Тариф, учитывающий время проезда по маршруту и километраж — различия между версиями

Материал из TaxiMaster
Перейти к: навигация, поиск
 
Строка 3151: Строка 3151:
 
Расчет времени происходит через онлайн карты (в данном примере используется 2GIS).
 
Расчет времени происходит через онлайн карты (в данном примере используется 2GIS).
  
Нужно '''выбрать карту как источник для расчета времени доезда экипажа''' ({{путь|[[Настройка карт|Файл - Настройки - Карта - Онлайн карты - Функционал - Расчет времени доезда экипажа до заказа в карточке заказа]]}}).
+
Нужно '''выбрать карту для расчета маршрута по заказу''' ({{путь|[[Настройка карт|Файл - Настройки - Карта - Онлайн карты - Функционал - Приоритет карт для расчета маршрута по заказу]]}}). Возвращать время маршрута с учетом пробок могут онлайн карты 2GIS и Яндекс.
  
 
[[Файл:Ветка Карта - онлайн карты - Функционал.png|центр]]
 
[[Файл:Ветка Карта - онлайн карты - Функционал.png|центр]]
  
 
{{info|
 
{{info|
Обратите внимание, для использования онлайн карт 2GIS, Яндекс и Google нужен API-ключ, который указывается в {{путь|[[Настройка карт|Файл - Настройки - Карта - Онлайн карты - Настройки - API-ключ для использования онлайн карт]]}}.
+
Обратите внимание, для использования онлайн карт 2GIS и Яндекс нужен API-ключ, который указывается в {{путь|[[Настройка карт|Файл - Настройки - Карта - Онлайн карты - Настройки - API-ключ для использования онлайн карт]]}}.
 
}}
 
}}
  

Текущая версия на 16:51, 19 июля 2022

В тарифе существуют параметры, которые позволяют учитывать не только километраж маршрута, но и предварительно рассчитанное время проезда по этому маршруту, которое может зависеть от загруженности дорог.

Интересная информация
Данный тариф будет корректно работать с версиями Такси-Мастер с 3.10 по 3.11.

Начиная с версии Такси-Мастер 3.12 нужные параметры уже присутствуют в расширенном тарифе по умолчанию.

Текст тарифа (Развернуть/Свернуть) →


//<?xml version="1.0" encoding="UTF-8"?>
//<xml_consts>
//  <param>
//    <code>PRIOR_ORDER_COST</code>
//    <type>currency</type>
//    <name>Наценка за предварительный заказ</name>
//  </param>

//  <group>
//    <name>Ожидание</name>
//    <items>
//      <param>
//        <code>WAITING_INCLUDED</code>
//        <type>boolean</type>
//        <name>Ожидание включено в минимальную стоимость</name>
//      </param>
//      <param>
//        <code>WAIT_MINUTE_COST</code>
//        <type>currency</type>
//        <name>Цена минуты ожидания</name>
//      </param>
//      <param>
//        <code>FREE_WAITING_TIME</code>
//        <type>integer</type>
//        <name>Бесплатное время ожидания, мин</name>
//      </param>
//      <param>
//        <code>PRIOR_FREE_WAITING_TIME</code>
//        <type>integer</type>
//        <name>Бесплатное время ожидания для предварительного заказа, мин</name>
//      </param>
//      <param>
//        <code>CLIENT_ABSENT_VISIBLE</code>
//        <type>boolean</type>
//        <name>Отображать кнопку "Не выходят"</name>
//      </param>
//      <param>
//        <code>CLIENT_ABSENT_ENABLED_TIME</code>
//        <type>integer</type>
//        <name>За сколько минут до начала платного ожидания делать доступной кнопку "Не выходят"</name>
//        <visible>
//          <code>CLIENT_ABSENT_VISIBLE</code>
//          <condition>equal</condition>
//          <value>true</value>
//        </visible>
//      </param>
//    </items>
//  </group>

//  <group>
//    <name>Обычный заказ (не почасовой)</name>
//    <items>
//      <group>
//        <name>По городу</name>
//        <items>
//          <param>
//            <code>MIN_CITY</code>
//            <type>currency</type>
//            <name>Минимальная стоимость</name>
//          </param>
//          <param>
//            <code>BOARDING_CITY</code>
//            <type>currency</type>
//            <name>Посадка</name>
//          </param>
//          <param>
//            <code>CITY_KM_INCLUDED</code>
//            <type>float</type>
//            <name>Сколько км включено в посадку</name>
//          </param>
//          <param>
//            <code>CITY_KM_COST</code>
//            <type>currency</type>
//            <name>Цена км далее</name>
//          </param>
//          <param>
//            <type>integer</type>
//            <name>На сколько % увеличивать сумму по городу при обратном пути в карте заказа</name>
//            <code>CITY_BACK_WAY_PERCENT</code>
//          </param>
//          <param>
//            <code>CITY_MINUTES_INCLUDED</code>
//            <type>float</type>
//            <name>Сколько минут включено в посадку</name>
//          </param>
//          <param>
//            <code>CITY_MINUTE_COST</code>
//            <type>currency</type>
//            <name>Цена минуты далее</name>
//          </param>
//        </items>
//      </group>

//      <group>
//        <name>За городом</name>
//        <items>
//          <param>
//            <code>MIN_COUNTRY</code>
//            <type>currency</type>
//            <name>Минимальная стоимость</name>
//          </param>
//          <param>
//            <code>BOARDING_COUNTRY</code>
//            <type>currency</type>
//            <name>Посадка</name>
//          </param>
//          <param>
//            <code>COUNTRY_KM_INCLUDED</code>
//            <type>float</type>
//            <name>Сколько км включено в посадку</name>
//          </param>
//          <param>
//            <code>COUNTRY_KM_COST</code>
//            <type>currency</type>
//            <name>Цена км далее</name>
//          </param>
//          <param>
//            <code>SOURCE_COUNTRY_KM_COST</code>
//            <type>currency</type>
//            <name>Цена км до адреса подачи за город</name>
//          </param>
//          <param>
//            <code>COUNTRY_BACK_WAY_PERCENT</code>
//            <type>integer</type>
//            <name>На сколько % увеличивать сумму за городом при обратном пути в карте заказа</name>
//          </param>
//          <param>
//            <code>COUNTRY_MINUTES_INCLUDED</code>
//            <type>float</type>
//            <name>Сколько минут включено в посадку</name>
//          </param>
//          <param>
//            <code>COUNTRY_MINUTE_COST</code>
//            <type>currency</type>
//            <name>Цена минуты далее</name>
//          </param>
//        </items>
//      </group>

//      <group>
//        <name>Простой</name>
//        <items>
//          <param>
//            <code>IDLE_MINUTE_COST</code>
//            <type>currency</type>
//            <name>Цена минуты простоя</name>
//          </param>
//          <param>
//            <code>FREE_IDLE_TIME</code>
//            <type>integer</type>
//            <name>Период между потерей скорости и началом простоя, сек</name>
//          </param>
//          <param>
//            <code>SPEED_LIMIT</code>
//            <type>integer</type>
//            <name>Скорость, ниже которой считается простой, км/ч</name>
//          </param>
//        </items>
//      </group>

//      <group>
//        <name>Остановки</name>
//        <items>
//          <param>
//            <code>MIN_STOP_COST</code>
//            <type>currency</type>
//            <name>Минимальная стоимость остановки</name>
//          </param>
//          <param>
//            <code>STOP_MINUTES_INCLUDED</code>
//            <type>integer</type>
//            <name>Сколько минут включено в минимальную стоимость остановки</name>
//          </param>
//          <param>
//            <code>STOP_MINUTE_COST</code>
//            <type>currency</type>
//            <name>Цена минуты остановки далее</name>
//          </param>
//        </items>
//      </group>

//      <group>
//        <name>Расчет километража для таксометра через ТМ</name>
//        <items>
//          <param>
//            <code>USE_CALC_TM_ROUTE</code>
//            <type>boolean</type>
//            <name>Использовать расчет километража для таксометра через ТМ</name>
//          </param>
//          <param>
//            <code>CALC_TM_ROUTE_PERIOD</code>
//            <type>integer</type>
//            <name>Через сколько секунд делать пересчет километража для таксометра через ТМ</name>
//          </param>
//        </items>
//      </group>

//    </items>
//  </group>

//  <group>
//    <name>Почасовой заказ</name>
//    <items>
//      <param>
//        <code>MIN_HOURLY</code>
//        <type>currency</type>
//        <name>Минимальная стоимость</name>
//      </param>
//      <param>
//        <code>BOARDING_HOURLY</code>
//        <type>currency</type>
//        <name>Посадка</name>
//      </param>
//      <param>
//        <code>HOURLY_MINUTES_INCLUDED</code>
//        <type>integer</type>
//        <name>Сколько минут включено в посадку</name>
//      </param>
//      <param>
//        <code>HOURLY_MINUTE_COST</code>
//        <type>currency</type>
//        <name>Цена минуты далее</name>
//      </param>
//      <param>
//        <code>HOURLY_PERIOD</code>
//        <type>integer</type>
//        <name>За сколько минут разом увеличивать сумму</name>
//      </param>
//      <param>
//        <code>HOURLY_TRIP_KM_INCLUDED</code>
//        <type>float</type>
//        <name>Количество км, включенных в поездку</name>
//        <visible>
//          <code>HOURLY_HOUR_KM_INCLUDED</code>
//          <condition>equal</condition>
//          <value>0</value>
//        </visible>
//      </param>
//      <param>
//        <code>HOURLY_HOUR_KM_INCLUDED</code>
//        <type>float</type>
//        <name>Количество км, включенных в час</name>
//        <visible>
//          <code>HOURLY_TRIP_KM_INCLUDED</code>
//          <condition>equal</condition>
//          <value>0</value>
//        </visible>
//      </param>
//      <param>
//        <code>HOURLY_KM_COST</code>
//        <type>currency</type>
//        <name>Цена км</name>
//      </param>
//    </items>
//  </group>

//  <group>
//    <name>Районы</name>
//    <items>
//      <param>
//        <code>USE_ZONE_COST</code>
//        <type>boolean</type>
//        <name>Учитывать посадку/высадку в районах</name>
//      </param>
//      <param>
//        <code>USE_ZONE_PATH</code>
//        <type>boolean</type>
//        <name>Учитывать проезды между районами</name>
//      </param>
//      <param>
//        <code>ZONES_PATH_GROUP_ID</code>
//        <type>zones_path_group</type>
//        <name>Группа проездов между районами</name>
//        <visible>
//          <code>USE_ZONE_PATH</code>
//          <condition>equal</condition>
//          <value>true</value>
//        </visible>
//      </param>
//      <param>
//        <code>ZONES_BACK_WAY_PERCENT</code>
//        <type>integer</type>
//        <name>На сколько % увеличивать сумму за проезды между районами при обратном пути в карте заказа</name>
//        <visible>
//          <code>USE_ZONE_PATH</code>
//          <condition>equal</condition>
//          <value>true</value>
//        </visible>
//      </param>
//      <param>
//        <code>USE_REAL_ZONES_IN_TAXM</code>
//        <type>boolean</type>
//        <name>Определять районы в таксометре по фактическому маршруту</name>
//      </param>
//    </items>
//  </group>

//  <group>
//    <name>Дополнительные параметры минимальной стоимости</name>
//    <items>
//      <param>
//        <code>DISCOUNT_AFTER_MIN</code>
//        <type>boolean</type>
//        <name>Считать скидки после минимальной стоимости</name>
//      </param>
//      <param>
//        <code>SERVICES_AFTER_MIN</code>
//        <type>boolean</type>
//        <name>Считать услуги после минимальной стоимости</name>
//      </param>
//      <param>
//        <code>MIN_SUM_AFTER_CALC</code>
//        <type>boolean</type>
//        <name>Применять минимальную стоимость после завершения расчета в таксометре</name>
//      </param>
//    </items>
//  </group>

//  <group>
//    <name>Округление</name>
//    <items>
//      <param>
//        <code>ROUNDING</code>
//        <type>currency</type>
//        <name>Округление</name>
//        <min>0</min>
//      </param>
//      <param>
//        <code>ROUND_UP</code>
//        <type>boolean</type>
//        <name>Округлять в большую сторону</name>
//      </param>
//    </items>
//  </group>

//  <group>
//    <name>Таксофон</name>
//    <items>
//      <param>
//        <code>TAXOPHONE_FIRST_DISCOUNT_SUM</code>
//        <type>currency</type>
//        <name>Скидка за первый заказ из TaxoPhone, руб</name>
//      </param>
//      <param>
//        <code>TAXOPHONE_FIRST_DISCOUNT_PERCENT</code>
//        <type>integer</type>
//        <name>Скидка за первый заказ из TaxoPhone, %</name>
//      </param>
//      <param>
//        <code>TAXOPHONE_CONSTANT_DISCOUNT_SUM</code>
//        <type>currency</type>
//        <name>Постоянная скидка для заказов из TaxoPhone, руб</name>
//      </param>
//      <param>
//        <code>TAXOPHONE_CONSTANT_DISCOUNT_PERCENT</code>
//        <type>integer</type>
//        <name>Постоянная скидка для заказов из TaxoPhone, %</name>
//      </param>
//      <param>
//        <code>TAXOPHONE_REGULAR_DISCOUNT_COUNT</code>
//        <type>integer</type>
//        <name>Через сколько заказов должна срабатывать регулярная скидка для заказов из TaxoPhone</name>
//      </param>
//      <param>
//        <code>TAXOPHONE_REGULAR_DISCOUNT_SUM</code>
//        <type>currency</type>
//        <name>Регулярная скидка для заказов из TaxoPhone, руб</name>
//      </param>
//      <param>
//        <code>TAXOPHONE_REGULAR_DISCOUNT_PERCENT</code>
//        <type>integer</type>
//        <name>Регулярная скидка для заказов из TaxoPhone, %</name>
//      </param>
//    </items>
//  </group>

//  <group>
//    <name>Услуги, доступные водителю в таксометре</name>
//    <items>
//      <param>
//        <code>SERVICES_BUTTON_COUNT</code>
//        <type>integer</type>
//        <name>Количество услуг</name>
//        <min>0</min>
//        <max>10</max>
//      </param>
//      <param>
//        <code>SERVICE_1</code>
//        <type>order_param</type>
//        <name>Услуга 1</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>greater_or_equal</condition>
//          <value>1</value>
//        </visible>
//      </param>
//      <param>
//        <code>CAN_REPEAT_SERVICE_1</code>
//        <type>boolean</type>
//        <name>Услугу 1 можно использовать повторно</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>greater_or_equal</condition>
//          <value>1</value>
//        </visible>
//      </param>
//      <param>
//        <code>SERVICE_2</code>
//        <type>order_param</type>
//        <name>Услуга 2</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>greater_or_equal</condition>
//          <value>2</value>
//        </visible>
//      </param>
//      <param>
//        <code>CAN_REPEAT_SERVICE_2</code>
//        <type>boolean</type>
//        <name>Услугу 2 можно использовать повторно</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>greater_or_equal</condition>
//          <value>2</value>
//        </visible>
//      </param>
//      <param>
//        <code>SERVICE_3</code>
//        <type>order_param</type>
//        <name>Услуга 3</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>greater_or_equal</condition>
//          <value>3</value>
//        </visible>
//      </param>
//      <param>
//        <code>CAN_REPEAT_SERVICE_3</code>
//        <type>boolean</type>
//        <name>Услугу 3 можно использовать повторно</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>greater_or_equal</condition>
//          <value>3</value>
//        </visible>
//      </param>
//      <param>
//        <code>SERVICE_4</code>
//        <type>order_param</type>
//        <name>Услуга 4</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>greater_or_equal</condition>
//          <value>4</value>
//        </visible>
//      </param>
//      <param>
//        <code>CAN_REPEAT_SERVICE_4</code>
//        <type>boolean</type>
//        <name>Услугу 4 можно использовать повторно</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>greater_or_equal</condition>
//          <value>4</value>
//        </visible>
//      </param>
//      <param>
//        <code>SERVICE_5</code>
//        <type>order_param</type>
//        <name>Услуга 5</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>greater_or_equal</condition>
//          <value>5</value>
//        </visible>
//      </param>
//      <param>
//        <code>CAN_REPEAT_SERVICE_5</code>
//        <type>boolean</type>
//        <name>Услугу 5 можно использовать повторно</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>greater_or_equal</condition>
//          <value>5</value>
//        </visible>
//      </param>
//      <param>
//        <code>SERVICE_6</code>
//        <type>order_param</type>
//        <name>Услуга 6</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>greater_or_equal</condition>
//          <value>6</value>
//        </visible>
//      </param>
//      <param>
//        <code>CAN_REPEAT_SERVICE_6</code>
//        <type>boolean</type>
//        <name>Услугу 6 можно использовать повторно</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>greater_or_equal</condition>
//          <value>6</value>
//        </visible>
//      </param>
//      <param>
//        <code>SERVICE_7</code>
//        <type>order_param</type>
//        <name>Услуга 7</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>greater_or_equal</condition>
//          <value>7</value>
//        </visible>
//      </param>
//      <param>
//        <code>CAN_REPEAT_SERVICE_7</code>
//        <type>boolean</type>
//        <name>Услугу 7 можно использовать повторно</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>greater_or_equal</condition>
//          <value>7</value>
//        </visible>
//      </param>
//      <param>
//        <code>SERVICE_8</code>
//        <type>order_param</type>
//        <name>Услуга 8</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>greater_or_equal</condition>
//          <value>8</value>
//        </visible>
//      </param>
//      <param>
//        <code>CAN_REPEAT_SERVICE_8</code>
//        <type>boolean</type>
//        <name>Услугу 8 можно использовать повторно</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>greater_or_equal</condition>
//          <value>8</value>
//        </visible>
//      </param>
//      <param>
//        <code>SERVICE_9</code>
//        <type>order_param</type>
//        <name>Услуга 9</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>greater_or_equal</condition>
//          <value>9</value>
//        </visible>
//      </param>
//      <param>
//        <code>CAN_REPEAT_SERVICE_9</code>
//        <type>boolean</type>
//        <name>Услугу 9 можно использовать повторно</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>greater_or_equal</condition>
//          <value>9</value>
//        </visible>
//      </param>
//      <param>
//        <code>SERVICE_10</code>
//        <type>order_param</type>
//        <name>Услуга 10</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>greater_or_equal</condition>
//          <value>10</value>
//        </visible>
//      </param>
//      <param>
//        <code>CAN_REPEAT_SERVICE_10</code>
//        <type>boolean</type>
//        <name>Услугу 10 можно использовать повторно</name>
//        <visible>
//          <code>SERVICES_BUTTON_COUNT</code>
//          <condition>equal</condition>
//          <value>10</value>
//        </visible>
//      </param>
//    </items>
//  </group>

//  <param>
//    <code>CALC_SUM_BY_TAXIMETER</code>
//    <type>boolean</type>
//    <name>Считать сумму по таксометру</name>
//  </param>
//  <param>
//    <code>USE_STOPS_WAITING_IDLE</code>
//    <type>boolean</type>
//    <name>Считать простой, ожидание и остановки независимо от настройки "Считать сумму по таксометру"</name>
//    <visible>
//      <code>CALC_SUM_BY_TAXIMETER</code>
//      <condition>equal</condition>
//      <value>false</value>
//    </visible>
//  </param>
//  <param>
//    <code>USE_GPS_MISSED_DISTANCE_RECOVERY</code>
//    <type>boolean</type>
//    <name>Использовать восстановление километража по прямой при потере координат</name>
//    <visible>
//      <code>CALC_SUM_BY_TAXIMETER</code>
//      <condition>equal</condition>
//      <value>true</value>
//    </visible>
//  </param>
//  <param>
//    <code>VALUTA</code>
//    <type>string</type>
//    <name>Валюта</name>
//  </param>

//  <group>
//    <name>Параметры для печати чека</name>
//    <items>
//      <param>
//        <code>NEED_DETAILS_IN_CHECK</code>
//        <type>boolean</type>
//        <name>Печатать детализацию поездки</name>
//      </param>
//      <param>
//        <code>TAXI_NAME</code>
//        <type>string</type>
//        <name>Название такси</name>
//      </param>
//      <param>
//        <code>TAXI_PHONE</code>
//        <type>string</type>
//        <name>Телефон такси</name>
//      </param>
//      <param>
//        <code>TAXI_INN</code>
//        <type>string</type>
//        <name>ИНН</name>
//      </param>
//    </items>
//  </group>
//</xml_consts>

const
  MIN_CITY = 0;
  MIN_COUNTRY = 0;
  MIN_HOURLY = 0;
  BOARDING_CITY = 0;
  CITY_KM_INCLUDED = 0;
  CITY_KM_COST = 0;
  CITY_MINUTES_INCLUDED = 0;
  CITY_MINUTE_COST = 0;
  BOARDING_COUNTRY = 0;
  COUNTRY_KM_INCLUDED = 0;
  COUNTRY_KM_COST = 0;
  COUNTRY_MINUTES_INCLUDED = 0;
  COUNTRY_MINUTE_COST = 0;
  SOURCE_COUNTRY_KM_COST = 0;
  BOARDING_HOURLY = 0;
  HOURLY_MINUTES_INCLUDED = 0;
  HOURLY_MINUTE_COST = 0;
  HOURLY_PERIOD = 0;
  HOURLY_TRIP_KM_INCLUDED = 0;
  HOURLY_HOUR_KM_INCLUDED = 0;
  HOURLY_KM_COST = 0;
  WAITING_INCLUDED = True;
  WAIT_MINUTE_COST = 0;
  FREE_WAITING_TIME = 0;
  PRIOR_FREE_WAITING_TIME = 0;
  CLIENT_ABSENT_VISIBLE = True;
  CLIENT_ABSENT_ENABLED_TIME = 0;
  IDLE_MINUTE_COST = 0;
  FREE_IDLE_TIME = 0;
  SPEED_LIMIT = 5;
  USE_ZONE_PATH = False;
  USE_ZONE_COST = False;
  ZONES_PATH_GROUP_ID = 0;
  USE_REAL_ZONES_IN_TAXM = False;
  MIN_STOP_COST = 0;
  STOP_MINUTES_INCLUDED = 0;
  STOP_MINUTE_COST = 0;
  PRIOR_ORDER_COST = 0;
  CITY_BACK_WAY_PERCENT = 0;
  COUNTRY_BACK_WAY_PERCENT = 0;
  ZONES_BACK_WAY_PERCENT = 0;
  ROUNDING = 1;
  ROUND_UP = False;
  MIN_SUM_AFTER_CALC = False;
  DISCOUNT_AFTER_MIN = False;
  SERVICES_AFTER_MIN = False;
  TAXOPHONE_FIRST_DISCOUNT_SUM = 0;
  TAXOPHONE_FIRST_DISCOUNT_PERCENT = 0;
  TAXOPHONE_CONSTANT_DISCOUNT_SUM = 0;
  TAXOPHONE_CONSTANT_DISCOUNT_PERCENT = 0;
  TAXOPHONE_REGULAR_DISCOUNT_COUNT = 0;
  TAXOPHONE_REGULAR_DISCOUNT_SUM = 0;
  TAXOPHONE_REGULAR_DISCOUNT_PERCENT = 0;
  CALC_SUM_BY_TAXIMETER = True;
  USE_STOPS_WAITING_IDLE = False;
  USE_GPS_MISSED_DISTANCE_RECOVERY = False;
  USE_CALC_TM_ROUTE = False;
  CALC_TM_ROUTE_PERIOD = 0;
  CALC_TM_ROUTE_FINISH_TIMEOUT = 15;
  VALUTA = 'р';
  NEED_DETAILS_IN_CHECK = False;
  TAXI_NAME = 'ООО "Название Такси"';
  TAXI_PHONE = '(495) 123-45-67';
  TAXI_INN = '123456789012';

  SERVICES_BUTTON_COUNT = 0;
  SERVICE_1 = 0;
  CAN_REPEAT_SERVICE_1 = False;
  SERVICE_2 = 0;
  CAN_REPEAT_SERVICE_2 = False;
  SERVICE_3 = 0;
  CAN_REPEAT_SERVICE_3 = False;
  SERVICE_4 = 0;
  CAN_REPEAT_SERVICE_4 = False;
  SERVICE_5 = 0;
  CAN_REPEAT_SERVICE_5 = False;
  SERVICE_6 = 0;
  CAN_REPEAT_SERVICE_6 = False;
  SERVICE_7 = 0;
  CAN_REPEAT_SERVICE_7 = False;
  SERVICE_8 = 0;
  CAN_REPEAT_SERVICE_8 = False;
  SERVICE_9 = 0;
  CAN_REPEAT_SERVICE_9 = False;
  SERVICE_10 = 0;
  CAN_REPEAT_SERVICE_10 = False;

// Округлить сумму.
function RoundSum(Sum: Single): Single;
var
  F, R: Single;
begin
  if ROUNDING = 0 then
    Result := Sum
  else
  begin
    R := 1 / ROUNDING;
    if ROUND_UP then
      Result := Ceil(Sum * R) / R
    else
    begin
      F := Trunc(Sum * R) / R;
      if Sum - F >= ROUNDING / 2 then
        F := F + ROUNDING;
      Result := F;
    end;
  end;
end;


function FloatToStrFixed(Value: Single): String;
var
  R, SymbolCount: Integer;
begin
  R := Floor(ROUNDING * 100);
  if R mod 100 = 0 then
    SymbolCount := 0
  else
  if R mod 10 = 0 then
    SymbolCount := 1
  else
    SymbolCount := 2;
  Result := FloatToStr(Round(Value, SymbolCount));
end;


procedure TM_AddZonesSum(var TaxmZonesSum: Single; var ZonePathSum: Single;
  NeedPrintToBill: Boolean);
var
  F: Single;
  s: String;
  NeedZoneOutCost: Boolean;
  StopCount, i: Integer;
begin
  // == Районы.
  if USE_ZONE_COST then
  begin
    // Посадка.
    F := ReadFloat('ZoneInCost', -1);
    if F <> 0 then
    begin
      s := ReadStr('ZoneName', -1);
      TaxmZonesSum := TaxmZonesSum + F;
      if NeedPrintToBill then
      begin
        WriteStr('Bill', 'Text', 'Посадка в ''' + s + '''');
        WriteStr('Bill', 'Sum', FloatToStr(F, 2));
      end;
    end;
    // Высадка.
    if ReadStr('ZoneName', -1) <> ReadStr('ZoneName', -2) then
      NeedZoneOutCost := True
    else
      if ReadInt('StopCount') > 0 then
      begin
        NeedZoneOutCost := False;
        for i := 0 to ReadInt('StopCount') - 1 do
          if ReadStr('ZoneName', i) <> ReadStr('ZoneName', -1) then
            NeedZoneOutCost := True;
      end
      else
        NeedZoneOutCost := False;
    if NeedZoneOutCost then
    begin
      F := ReadFloat('ZoneOutCost', -2);
      if F <> 0 then
      begin
        s := ReadStr('ZoneName', -2);
        TaxmZonesSum := TaxmZonesSum + F;
        if NeedPrintToBill then
        begin
          WriteStr('Bill', 'Text', 'Высадка в ''' + s + '''');
          WriteStr('Bill', 'Sum', FloatToStr(F, 2));
        end;
      end;
    end;
    // Остановки.
    for i := 0 to ReadInt('StopCount') - 1 do
    begin
      F := ReadFloat('ZoneStopCost', i);
      if F <> 0 then
      begin
        s := ReadStr('ZoneName', i);
        TaxmZonesSum := TaxmZonesSum + F;
        if NeedPrintToBill then
        begin
          WriteStr('Bill', 'Text', 'Остановка в ''' + s + '''');
          WriteStr('Bill', 'Sum', FloatToStr(F, 2));
        end;
      end;
    end;
  end;

  // Проезды.
  if USE_ZONE_PATH then
  begin
    StopCount := ReadInt('StopCount');
    if StopCount > 0 then
    begin
      F := ReadFloat('ZonePathCost', -1, 0);
      if F <> 0 then
      begin
        ZonePathSum := F;
        if NeedPrintToBill then
        begin
          WriteStr('Bill', 'Text', 'Проезд ''' +
            ReadStr('ZoneName', -1) + ''' -> ''' +
            ReadStr('ZoneName', 0) + '''');
          WriteStr('Bill', 'Sum', FloatToStr(F, 2));
        end;
      end;
      for i := 0 to StopCount - 2 do
      begin
        F := ReadFloat('ZonePathCost', i, i + 1);
        if F <> 0 then
        begin
          ZonePathSum := ZonePathSum + F;
          if NeedPrintToBill then
          begin
            WriteStr('Bill', 'Text', 'Проезд ''' +
              ReadStr('ZoneName', i) + ''' -> ''' +
              ReadStr('ZoneName', i + 1) + '''');
            WriteStr('Bill', 'Sum', FloatToStr(F, 2));
          end;
        end;
      end;
      F := ReadFloat('ZonePathCost', StopCount - 1, -2);
      if F <> 0 then
      begin
        ZonePathSum := ZonePathSum + F;
        if NeedPrintToBill then
        begin
          WriteStr('Bill', 'Text', 'Проезд ''' +
            ReadStr('ZoneName', StopCount - 1) + ''' -> ''' +
            ReadStr('ZoneName', -2) + '''');
          WriteStr('Bill', 'Sum', FloatToStr(F, 2));
        end;
      end;
    end
    else
    begin
      F := ReadFloat('ZonePathCost', -1, -2);
      if F <> 0 then
      begin
        ZonePathSum := F;
        if NeedPrintToBill then
        begin
          WriteStr('Bill', 'Text', 'Проезд ''' +
            ReadStr('ZoneName', -1) + ''' -> ''' +
            ReadStr('ZoneName', -2) + '''');
          WriteStr('Bill', 'Sum', FloatToStr(F, 2));
        end;
      end;
    end;

    if ReadBool('BackFree') and (ZonePathSum <> 0) and
       (ZONES_BACK_WAY_PERCENT <> 0) then
    begin
      F := ZonePathSum * ZONES_BACK_WAY_PERCENT / 100;
      ZonePathSum := ZonePathSum + F;
      if NeedPrintToBill then
      begin
        WriteStr('Bill', 'Text', 'Обратный проезд между районами');
        WriteStr('Bill', 'Sum', FloatToStr(F, 2));
      end;
    end;
  end;
end;

// Заказ пришёл из TaxoPhone.
function TM_IsFromTaxophone: Boolean;
begin
  Result := (ReadInt('CreationWay') = 6) or
    (ReadInt('CreationWay') = 9) or
    (ReadInt('CreationWay') = 11);
end;


// Рассчитать стоимость за ожидание.
function TM_GetWaitingCost: Single;
var
  FreeWaitingTime: Integer;
begin
  if not ReadBool('IsPrior') then
    FreeWaitingTime := FREE_WAITING_TIME
  else
    FreeWaitingTime := PRIOR_FREE_WAITING_TIME;
  if ReadInt('WaitTime') div 60 > FreeWaitingTime then
    Result := WAIT_MINUTE_COST *
      (ReadInt('WaitTime') div 60 - FreeWaitingTime)
  else
    Result := 0;
end;


// Получить минималку.
function TM_GetMinSum: Single;
begin
  if ReadBool('IsHourly') then
    Result := MIN_HOURLY
  else
  if ReadBool('IsCountry') then
    Result := MIN_COUNTRY
  else
    Result := MIN_CITY;
end;


// Добавить услуги (атрибуты/параметры) к сумме.
procedure TM_AddServices(var Sum: Single; NeedPrintToBill: Boolean);
var
  F, ServicePercentSum, ServicesSum: Single;
  i: Integer;
begin
  // Услуги.
  ServicesSum := 0;
  for i := 0 to ReadInt('ServiceCount') - 1 do
  begin
    F := ReadFloat('ServiceSum', i);
    if F <> 0 then
    begin
      ServicesSum := ServicesSum + F;
      if NeedPrintToBill then
      begin
        WriteStr('Bill', 'Text',
          'Услуга ''' + ReadStr('ServiceName', i) + '''');
        WriteStr('Bill', 'Sum', FloatToStr(F, 2));
      end;
    end;
  end;

  ServicePercentSum := 0;
  for i := 0 to ReadInt('ServiceCount') - 1 do
  begin
    F := ReadFloat('ServicePercent', i);
    if F <> 0 then
    begin
      ServicePercentSum := ServicePercentSum + F;
      if NeedPrintToBill then
      begin
        WriteStr('Bill', 'Text',
          'Услуга ''' + ReadStr('ServiceName', i) + '''');
        WriteStr('Bill', 'Sum', FloatToStr(F, 0) + '%');
      end;
    end;
  end;

  Sum := Sum + Sum * ServicePercentSum / 100 + ServicesSum;
end;


// Рассчитать сумму со скидкой.
procedure TM_AddDiscounts(var Sum: Single;
  var TotalDiscSum: Single;
  var TotalDiscPercent: Single;
  NeedPrintToBill: Boolean);
var
  F: Single;
  i: Integer;
  S: String;
begin
  // == Скидки.

  TotalDiscSum := 0;
  TotalDiscPercent := 0;
  // Скидка/наценка.
  for i := 0 to ReadInt('DiscMarkupCount') - 1 do
  begin
    F := ReadFloat('DiscMarkupSum', i);
    if F <> 0 then
    begin
      if NeedPrintToBill then
      begin
        if F > 0 then
          S := 'Наценка'
        else
          S := 'Скидка';
        WriteStr('Bill', 'Text', S + ' ''' +
          ReadStr('DiscMarkupName', i) + '''');
        WriteStr('Bill', 'Sum', FloatToStr(Abs(F), 2));
      end;
      // + это наценка, - это скидка.
      TotalDiscSum := TotalDiscSum - F;
    end;
    F := ReadFloat('DiscMarkupPercent', i);
    if F <> 0 then
    begin
      if NeedPrintToBill then
      begin
        if F > 0 then
          S := 'Наценка'
        else
          S := 'Скидка';
        WriteStr('Bill', 'Text', S + ' ''' +
          ReadStr('DiscMarkupName', i) + '''');
        WriteStr('Bill', 'Sum', FloatToStr(Abs(F), 0) + '%');
      end;
      // + это наценка, - это скидка.
      TotalDiscPercent := TotalDiscPercent - F;
    end;
  end;

  if TM_IsFromTaxophone then
  begin
    // Постоянная скидка для заказов из TaxoPhone.
    TotalDiscSum := TotalDiscSum + TAXOPHONE_CONSTANT_DISCOUNT_SUM;
    TotalDiscPercent := TotalDiscPercent + TAXOPHONE_CONSTANT_DISCOUNT_PERCENT;
    if NeedPrintToBill then
    begin
      if TAXOPHONE_CONSTANT_DISCOUNT_SUM > 0 then
      begin
        WriteStr('Bill', 'Text', 'Постоянная скидка за заказ из TaxoPhone');
        WriteStr('Bill', 'Sum', FloatToStr(TAXOPHONE_CONSTANT_DISCOUNT_SUM, 2));
      end;
      if TAXOPHONE_CONSTANT_DISCOUNT_PERCENT > 0 then
      begin
        WriteStr('Bill', 'Text', 'Постоянная скидка за заказ из TaxoPhone');
        WriteStr('Bill', 'Sum', FloatToStr(TAXOPHONE_CONSTANT_DISCOUNT_PERCENT, 0) + '%');
      end;
    end;

    // Скидка за первый заказ из TaxoPhone.
    if ReadInt('ClientTaxoPhoneOrdersCount') = 0 then
    begin
      TotalDiscSum := TotalDiscSum + TAXOPHONE_FIRST_DISCOUNT_SUM;
      TotalDiscPercent := TotalDiscPercent + TAXOPHONE_FIRST_DISCOUNT_PERCENT;
      if NeedPrintToBill then
      begin
        if TAXOPHONE_FIRST_DISCOUNT_SUM > 0 then
        begin
          WriteStr('Bill', 'Text', 'Скидка за первый заказ из TaxoPhone');
          WriteStr('Bill', 'Sum', FloatToStr(TAXOPHONE_FIRST_DISCOUNT_SUM, 2));
        end;
        if TAXOPHONE_FIRST_DISCOUNT_PERCENT > 0 then
        begin
          WriteStr('Bill', 'Text', 'Скидка за первый заказ из TaxoPhone');
          WriteStr('Bill', 'Sum', FloatToStr(TAXOPHONE_FIRST_DISCOUNT_PERCENT, 0) + '%');
        end;
      end;
    end;

    // Регулярная скидка за заказ из TaxoPhone.
    if TAXOPHONE_REGULAR_DISCOUNT_COUNT > 0 then
      if (ReadInt('ClientTaxoPhoneOrdersCount') + 1) mod TAXOPHONE_REGULAR_DISCOUNT_COUNT = 0 then
      begin
        TotalDiscSum := TotalDiscSum + TAXOPHONE_REGULAR_DISCOUNT_SUM;
        TotalDiscPercent := TotalDiscPercent + TAXOPHONE_REGULAR_DISCOUNT_PERCENT;
        if NeedPrintToBill then
        begin
          if TAXOPHONE_REGULAR_DISCOUNT_SUM > 0 then
          begin
            WriteStr('Bill', 'Text', 'Регулярная скидка за каждый ' +
              IntToStr(TAXOPHONE_REGULAR_DISCOUNT_COUNT) + '-й заказ из TaxoPhone');
            WriteStr('Bill', 'Sum', FloatToStr(TAXOPHONE_REGULAR_DISCOUNT_SUM, 2));
          end;
          if TAXOPHONE_FIRST_DISCOUNT_PERCENT > 0 then
          begin
            WriteStr('Bill', 'Text', 'Регулярная скидка за каждый ' +
              IntToStr(TAXOPHONE_REGULAR_DISCOUNT_COUNT) + '-й заказ из TaxoPhone');
            WriteStr('Bill', 'Sum', FloatToStr(TAXOPHONE_REGULAR_DISCOUNT_PERCENT, 0) + '%');
          end;
        end;
      end;
  end;

  // Скидка.
  S := ReadStr('DiscountName');
  F := ReadFloat('DiscountSum');
  if F <> 0 then
  begin
    if NeedPrintToBill then
    begin
      WriteStr('Bill', 'Text', 'Скидка ''' + S + '''');
      WriteStr('Bill', 'Sum', FloatToStr(F, 2));
    end;
    TotalDiscSum := TotalDiscSum + F;
  end;
  F := ReadFloat('DiscountPercent');
  if F <> 0 then
  begin
    if NeedPrintToBill then
    begin
      WriteStr('Bill', 'Text', 'Скидка ''' + S + '''');
      WriteStr('Bill', 'Sum', FloatToStr(F, 0) + '%');
    end;
    TotalDiscPercent := TotalDiscPercent + F;
  end;
  // Скидка по диск. карте.
  S := ReadStr('DiscCardName');
  F := ReadFloat('DiscCardSum');
  if F <> 0 then
  begin
    if NeedPrintToBill then
    begin
      WriteStr('Bill', 'Text', 'Скидка по ДК ''' + S + '''');
      WriteStr('Bill', 'Sum', FloatToStr(F, 2));
    end;
    TotalDiscSum := TotalDiscSum + F;
  end;
  F := ReadFloat('DiscCardPercent');
  if F <> 0 then
  begin
    if NeedPrintToBill then
    begin
      WriteStr('Bill', 'Text', 'Скидка по ДК ''' + S + '''');
      WriteStr('Bill', 'Sum', FloatToStr(F, 0) + '%');
    end;
    TotalDiscPercent := TotalDiscPercent + F;
  end;

  Sum := Sum - Sum * TotalDiscPercent / 100 - TotalDiscSum;
  if NeedPrintToBill then
  begin
    WriteStr('Bill', 'Text', 'Сумма со скидкой');
    WriteStr('Bill', 'Sum', FloatToStr(Sum, 2));
  end;
end;


// *****************
// Вычисление суммы.
// *****************
procedure CalcSum;
var
  Sum, F, CityDist, CountryDist, TotalDist, HourlyPayDist,
  ZonePathSum, TaxmZonesSum: Single;
  CityCost, CountryCost: Single;
  i, StopCount, TripTime: Integer;
  NeedZoneOutCost: Boolean;
  CityTripTime, CountryTripTime: Integer;
begin
  Sum := 0;
  WriteInt('UseZonesPathGroupId', ZONES_PATH_GROUP_ID);

  if ReadBool('IsPrior') and (PRIOR_ORDER_COST <> 0) then
  begin
    Sum := Sum + PRIOR_ORDER_COST;
    WriteStr('Bill', 'Text', 'Предварительный заказ');
    WriteStr('Bill', 'Sum', FloatToStr(PRIOR_ORDER_COST, 2));
  end;

  F := TM_GetWaitingCost;
  if F <> 0 then
  begin
    Sum := Sum + F;
    WriteStr('Bill', 'Text',
      'Ожидание ' + FloatToStr(WAIT_MINUTE_COST, 2) + ' ' + VALUTA + '/мин');
    WriteStr('Bill', 'Value',
      IntToStr(ReadInt('WaitTime') div 60) + ' мин');
    WriteStr('Bill', 'Sum', FloatToStr(F, 2));
  end;

  F := SOURCE_COUNTRY_KM_COST * ReadFloat('SourceDistCountry');
  if F <> 0 then
  begin
    Sum := Sum + F;
    WriteStr('Bill', 'Text',
      'До подачи за город ' + FloatToStr(SOURCE_COUNTRY_KM_COST, 2) + ' ' + VALUTA + '/км');
    WriteStr('Bill', 'Value',
    FloatToStr(ReadFloat('SourceDistCountry'), 2) + ' км');
    WriteStr('Bill', 'Sum', FloatToStr(F, 2));
  end;

  CityDist := ReadFloat('Distance');
  if ReadBool('IsCountry') then
    CountryDist := ReadFloat('DistCountry')
  else
    CountryDist := 0;
  TotalDist := CityDist + CountryDist;
  TripTime := ReadInt('TripTime');

  // Почасовой заказ.
  if ReadBool('IsHourly') then
  begin
    F := BOARDING_HOURLY;
    if TripTime div 60 > HOURLY_MINUTES_INCLUDED then
      if HOURLY_PERIOD = 0 then
        F := F + (TripTime div 60 - HOURLY_MINUTES_INCLUDED) *
          HOURLY_MINUTE_COST
      else
        F := F + Ceil((TripTime div 60 - HOURLY_MINUTES_INCLUDED) /
          HOURLY_PERIOD) * HOURLY_PERIOD * HOURLY_MINUTE_COST;
    if F <> 0 then
    begin
      Sum := Sum + F;
      WriteStr('Bill', 'Text', 'Продолжительность');
      WriteStr('Bill', 'Value',
        IntToStr(TripTime div 60) + ' мин');
      WriteStr('Bill', 'Sum', FloatToStr(F, 2));
    end;

    if HOURLY_KM_COST > 0 then
    begin
      F := 0;
      HourlyPayDist := 0;
      if HOURLY_HOUR_KM_INCLUDED > 0 then
      begin
        HourlyPayDist := (TotalDist - HOURLY_HOUR_KM_INCLUDED *
          Ceil(TripTime / 60 / 60));
        F := HourlyPayDist * HOURLY_KM_COST;
      end
      else
      if HOURLY_TRIP_KM_INCLUDED > 0 then
      begin
        HourlyPayDist := TotalDist - HOURLY_TRIP_KM_INCLUDED;
        F := HourlyPayDist * HOURLY_KM_COST;
      end
      else
      begin
        HourlyPayDist := TotalDist;
        F := TotalDist * HOURLY_KM_COST;
      end;
      if F > 0 then
      begin
        Sum := Sum + F;
        WriteStr('Bill', 'Text', 'Платный километраж');
        WriteStr('Bill', 'Value',
          FloatToStr(HourlyPayDist, 2) + ' км');
        WriteStr('Bill', 'Sum', FloatToStr(F, 2));
      end;
    end;
  end
  else
  begin
    // Загородный заказ.
    if ReadBool('IsCountry') then
    begin
      F := BOARDING_COUNTRY;
      if TotalDist > COUNTRY_KM_INCLUDED then
        if CityDist <= COUNTRY_KM_INCLUDED then
          F := F + (TotalDist - COUNTRY_KM_INCLUDED) * COUNTRY_KM_COST
        else
          F := F + (CityDist - COUNTRY_KM_INCLUDED) * CITY_KM_COST +
            CountryDist * COUNTRY_KM_COST;

      CityCost := 0;
      if CityDist > COUNTRY_KM_INCLUDED then
        CityCost := BOARDING_COUNTRY + (CityDist - COUNTRY_KM_INCLUDED) * CITY_KM_COST
      else
        if (TotalDist > COUNTRY_KM_INCLUDED) and (COUNTRY_KM_INCLUDED > 0) then
          CityCost := BOARDING_COUNTRY * CityDist / COUNTRY_KM_INCLUDED
        else
          if TotalDist > 0 then
            CityCost := BOARDING_COUNTRY * CityDist / TotalDist;
      CountryCost := F - CityCost;

      if F <> 0 then
      begin
        Sum := Sum + F;
        WriteStr('Bill', 'Text', 'Путь');
        WriteStr('Bill', 'Value',
          FloatToStr(TotalDist, 2) + ' км');
        WriteStr('Bill', 'Sum', FloatToStr(F, 2));
        if ReadBool('BackFree') then
        begin
          if (CityCost <> 0) and (CITY_BACK_WAY_PERCENT <> 0) then
          begin
            F := CityCost * CITY_BACK_WAY_PERCENT / 100;
            Sum := Sum + F;
            WriteStr('Bill', 'Text', 'Обратно по городу');
            WriteStr('Bill', 'Sum', FloatToStr(F, 2));
          end;
          if (CountryCost <> 0) and (COUNTRY_BACK_WAY_PERCENT <> 0) then
          begin
            F := CountryCost * COUNTRY_BACK_WAY_PERCENT / 100;
            Sum := Sum + F;
            WriteStr('Bill', 'Text', 'Обратно за городом');
            WriteStr('Bill', 'Sum', FloatToStr(F, 2));
          end;
        end;
      end;

      if ((COUNTRY_MINUTE_COST > 0) or (CITY_MINUTE_COST > 0)) and 
         (TripTime > COUNTRY_MINUTES_INCLUDED * 60) and
         (TotalDist > 0) then
      begin
        CountryTripTime := Floor(TripTime * (CountryDist / TotalDist));
        CityTripTime := Floor(TripTime - CountryTripTime);

        if CityTripTime <= COUNTRY_MINUTES_INCLUDED * 60 then
        begin
          CityCost := 0;
          CountryCost := (TripTime / 60 - COUNTRY_MINUTES_INCLUDED) * COUNTRY_MINUTE_COST
        end
        else
        begin
          CityCost := (CityTripTime / 60 - COUNTRY_MINUTES_INCLUDED) * CITY_MINUTE_COST;
          CountryCost := CountryTripTime / 60 * COUNTRY_MINUTE_COST;
        end;

        F := CityCost + CountryCost;
        if F <> 0 then
        begin
          Sum := Sum + F;

          WriteStr('Bill', 'Text', 'Длительность по городу');
          WriteStr('Bill', 'Value', TimeLenToStr(CityTripTime));
          WriteStr('Bill', 'Sum', FloatToStr(CityCost, 2));

          WriteStr('Bill', 'Text', 'Длительность за городом');
          WriteStr('Bill', 'Value', TimeLenToStr(CountryTripTime));
          WriteStr('Bill', 'Sum', FloatToStr(CountryCost, 2));
        end;
      end;
    end
    else
    // Городской заказ.
    begin
      F := BOARDING_CITY;
      if TotalDist > CITY_KM_INCLUDED then
        F := F + (TotalDist - CITY_KM_INCLUDED) * CITY_KM_COST;

      if F <> 0 then
      begin
        Sum := Sum + F;
        WriteStr('Bill', 'Text', 'Путь');
        WriteStr('Bill', 'Value',
          FloatToStr(TotalDist, 2) + ' км');
        WriteStr('Bill', 'Sum', FloatToStr(F, 2));
        if ReadBool('BackFree') then
          if (CITY_BACK_WAY_PERCENT <> 0) then
          begin
            F := F * CITY_BACK_WAY_PERCENT / 100;
            Sum := Sum + F;
            WriteStr('Bill', 'Text', 'Обратно по городу');
            WriteStr('Bill', 'Sum', FloatToStr(F, 2));
          end;
      end;

      if (CITY_MINUTE_COST > 0) and 
         (TripTime > CITY_MINUTES_INCLUDED * 60) then
      begin
        F := (TripTime / 60 - CITY_MINUTES_INCLUDED) * CITY_MINUTE_COST;
        if F <> 0 then
        begin
          Sum := Sum + F;
          WriteStr('Bill', 'Text', 'Длительность по городу');
          WriteStr('Bill', 'Value', TimeLenToStr(TripTime));
          WriteStr('Bill', 'Sum', FloatToStr(F, 2));
        end;
      end;
    end;
  end;

  // Остановки.
  if not ReadBool('IsHourly') then
  begin
    F := MIN_STOP_COST * ReadInt('StopCount');
    if F <> 0 then
    begin
      Sum := Sum + F;
      WriteStr('Bill', 'Text', 'Остановки');
      WriteStr('Bill', 'Value',
        IntToStr(ReadInt('StopCount')) + ' шт');
      WriteStr('Bill', 'Sum', FloatToStr(F, 2));
    end;
  end;

  TaxmZonesSum := 0;
  ZonePathSum := 0;
  TM_AddZonesSum(TaxmZonesSum, ZonePathSum, True);
  Sum := Sum + ZonePathSum + TaxmZonesSum;

  if not SERVICES_AFTER_MIN then
    TM_AddServices(Sum, True);

  WriteFloat('Sum', Sum);
end;


// ***********************************************
// Заполение полей райнов и скидок для таксометра.
// ***********************************************
procedure CalcTaxmSums;
var
  i, StopCount: Integer;
  TaxmZonesSum, TotalDiscPercent,
    TotalDiscSum, ZonePathSum, Sum: Single;
begin
  // == Районы.
  WriteInt('UseZonesPathGroupId', ZONES_PATH_GROUP_ID);
  TaxmZonesSum := 0;
  ZonePathSum := 0;
  TM_AddZonesSum(TaxmZonesSum, ZonePathSum, False);

  // Запись.
  WriteFloat('TaxmZonesSum', TaxmZonesSum + ZonePathSum);

  TotalDiscSum := 0;
  TotalDiscPercent := 0;
  TM_AddDiscounts(Sum, TotalDiscSum, TotalDiscPercent, False);

  // Запись.
  WriteFloat('TaxmTotalDiscSum', TotalDiscSum);
  WriteFloat('TaxmTotalDiscPercent', TotalDiscPercent);
end;


// ****************************
// Вычисление суммы со скидкой.
// ****************************
procedure CalcTotalSum;
var
  WaitingCost, F, MinSum, Sum: Single;
  i: Integer;
  TotalDiscSum, TotalDiscPercent: Single;
begin
  // Сумма без скидок.
  Sum := ReadFloat('Sum');

  TotalDiscSum := 0;
  TotalDiscPercent := 0;

  if not DISCOUNT_AFTER_MIN then
    TM_AddDiscounts(Sum, TotalDiscSum, TotalDiscPercent, True);

  MinSum := TM_GetMinSum;
  WriteStr('Bill', 'Text', 'Минимум');
  WriteStr('Bill', 'Sum', FloatToStr(MinSum, 2));

  WaitingCost := TM_GetWaitingCost;
  if WAITING_INCLUDED then
    Sum := Max(MinSum, Sum)
  else
    Sum := Max(MinSum + WaitingCost, Sum);

  if SERVICES_AFTER_MIN then
    TM_AddServices(Sum, True);

  if DISCOUNT_AFTER_MIN then
    TM_AddDiscounts(Sum, TotalDiscSum, TotalDiscPercent, True);

  Sum := RoundSum(Sum);

  WriteStr('Bill', 'Text', 'Итого');
  WriteStr('Bill', 'Sum', FloatToStr(Sum, 2) + ' ' + VALUTA);

  // Записать итоговую сумму.
  WriteFloat('TotalSum', Sum);
end;


// Использовать простой и остановки.
function TMD_UseCalcStopsIdle: Boolean;
begin
  Result := not ReadBool('IsHourly') and
    (CALC_SUM_BY_TAXIMETER or USE_STOPS_WAITING_IDLE)
end;


// **************
// Инициализация.
// **************
procedure ResetCalc;
begin
  if CLIENT_ABSENT_VISIBLE then
    WriteInt('ClientAbsentVisible', 1)
  else
    WriteInt('ClientAbsentVisible', 0);
end;


// *****************
// Ожидание клиента.
// *****************
procedure WaitCalc;
var
  Sum: Single;
  FreeWaitingTime, WaitTime: Integer;
begin
  if not ReadBool('IsPrior') then
    FreeWaitingTime := FREE_WAITING_TIME
  else
    FreeWaitingTime := PRIOR_FREE_WAITING_TIME;

  WaitTime := ReadInt('WaitTime');

  WriteInt('FreeWaitingTime', (FreeWaitingTime * 60 - WaitTime));

  if CALC_SUM_BY_TAXIMETER or USE_STOPS_WAITING_IDLE then
    if WaitTime > FreeWaitingTime * 60 then
    begin
      Sum := ReadFloat('Sum') + WAIT_MINUTE_COST / 60;
      WriteFloat('Temp', 'PayWaitTimeSum', Sum);
      WriteFloat('Sum', Sum);
      WriteStr('SumStr', FloatToStrFixed(RoundSum(Sum)));
      WriteFloat('CurrentSum', Sum);
    end;

  if CLIENT_ABSENT_VISIBLE and
     (WaitTime > (FreeWaitingTime - CLIENT_ABSENT_ENABLED_TIME) * 60) then
    WriteInt('ClientAbsentEnabled', 1)
  else
    WriteInt('ClientAbsentEnabled', 0);
end;


// Получить параметры услуги по ИД.
procedure TMD_GetServiceParams(ServiceId: Integer;
  var ServiceCost: Single;
  var ServicePercent: Integer;
  var ServiceName: String);
begin
  ServiceCost := ReadFloat('OrderParamSum', ServiceId);
  ServiceName := ReadStr('OrderParamName', ServiceId);
  ServicePercent := Trunc(ReadFloat('OrderParamPercent', ServiceId));
end;


// Получить параметры услуги по индексу.
procedure TMD_GetServiceParamsByIndex(Index: Integer;
  var ServiceCost: Single;
  var ServicePercent: Integer;
  var ServiceName: String;
  var ServiceId: Integer;
  var CanRepeatService: Boolean);
begin
  if Index = 1 then
  begin
    TMD_GetServiceParams(SERVICE_1, ServiceCost, ServicePercent, ServiceName);
    ServiceId := SERVICE_1;
    CanRepeatService := CAN_REPEAT_SERVICE_1;
  end
  else
  if Index = 2 then
  begin
    TMD_GetServiceParams(SERVICE_2, ServiceCost, ServicePercent, ServiceName);
    ServiceId := SERVICE_2;
    CanRepeatService := CAN_REPEAT_SERVICE_2;
  end
  else
  if Index = 3 then
  begin
    TMD_GetServiceParams(SERVICE_3, ServiceCost, ServicePercent, ServiceName);
    ServiceId := SERVICE_3;
    CanRepeatService := CAN_REPEAT_SERVICE_3;
  end
  else
  if Index = 4 then
  begin
    TMD_GetServiceParams(SERVICE_4, ServiceCost, ServicePercent, ServiceName);
    ServiceId := SERVICE_4;
    CanRepeatService := CAN_REPEAT_SERVICE_4;
  end
  else
  if Index = 5 then
  begin
    TMD_GetServiceParams(SERVICE_5, ServiceCost, ServicePercent, ServiceName);
    ServiceId := SERVICE_5;
    CanRepeatService := CAN_REPEAT_SERVICE_5;
  end
  else
  if Index = 6 then
  begin
    TMD_GetServiceParams(SERVICE_6, ServiceCost, ServicePercent, ServiceName);
    ServiceId := SERVICE_6;
    CanRepeatService := CAN_REPEAT_SERVICE_6;
  end
  else
  if Index = 7 then
  begin
    TMD_GetServiceParams(SERVICE_7, ServiceCost, ServicePercent, ServiceName);
    ServiceId := SERVICE_7;
    CanRepeatService := CAN_REPEAT_SERVICE_7;
  end
  else
  if Index = 8 then
  begin
    TMD_GetServiceParams(SERVICE_8, ServiceCost, ServicePercent, ServiceName);
    ServiceId := SERVICE_8;
    CanRepeatService := CAN_REPEAT_SERVICE_8;
  end
  else
  if Index = 9 then
  begin
    TMD_GetServiceParams(SERVICE_9, ServiceCost, ServicePercent, ServiceName);
    ServiceId := SERVICE_9;
    CanRepeatService := CAN_REPEAT_SERVICE_9;
  end
  else
  if Index = 10 then
  begin
    TMD_GetServiceParams(SERVICE_10, ServiceCost, ServicePercent, ServiceName);
    ServiceId := SERVICE_10;
    CanRepeatService := CAN_REPEAT_SERVICE_10;
  end;
end;


// Создать кнопку с услугой.
procedure TMD_CreateDriverServiceButton(Index: Integer; ButtonName: String);
var
  I, ServicePercent, ServiceId: Integer;
  ServiceCost: Single;
  ServiceName: String;
  CanRepeatService: Boolean;
begin
  TMD_GetServiceParamsByIndex(Index, ServiceCost, ServicePercent, ServiceName,
    ServiceId, CanRepeatService);
  if (ServiceId > 0) and
     ((ServiceCost <> 0) or (ServicePercent <> 0)) then
  begin
    WriteStr('Button', 'New', ButtonName);
    WriteStr('Button', 'Select', ButtonName);
    WriteInt('Button', 'Visible', 1);
    WriteInt('Button', 'Enabled', 1);
    WriteInt('Button', 'LongClick', 1);
    WriteStr('Button', 'Text', ServiceName);
  end;
end;


// Получить количество услуг, доступных водителю.
function TMD_GetDriverServicesCount: Integer;
begin
  if SERVICES_BUTTON_COUNT > 10 then
    Result := 10
  else
  if SERVICES_BUTTON_COUNT < 0 then
    Result := 0
  else
    Result := SERVICES_BUTTON_COUNT;
end;


// Инициализация доп. кнопок для водительских услуг.
procedure TMD_InitDriverServiceButtons;
var
  I: Integer;
  S: String;
begin
  for I := 1 to TMD_GetDriverServicesCount do
    TMD_CreateDriverServiceButton(I, 'DriverServiceButton' + IntToStr(I));
end;


// Перепроверить видимость услуг.
procedure TMD_RecheckDriverServiceButtons;
var
  I, J: Integer;
  ServicePercent, ServiceId: Integer;
  ServiceCost: Single;
  ServiceName: String;
  FoundService, CanRepeatService: Boolean;
  IsPressedUnrepeatableButton: Boolean;
begin
  for I := 1 to TMD_GetDriverServicesCount do
  begin
    TMD_GetServiceParamsByIndex(I, ServiceCost, ServicePercent, ServiceName,
      ServiceId, CanRepeatService);
    if ServiceId > 0 then
    begin
      WriteStr('Button', 'Select', 'DriverServiceButton' + IntToStr(I));
      // Кнопку нажали один раз, запрещено нажимать повторно.
      IsPressedUnrepeatableButton := not CanRepeatService and
        (ReadInt('Temp', 'DriverServiceButton' + IntToStr(I)) = 1);

      FoundService := False;
      for J := 0 to ReadInt('OrderParamsCount') - 1 do
        if ServiceId = ReadInt('OrderParamId', J) then
          FoundService := True;

      // В ТМ был добавлен атрибут с таким же ИД.
      // Нужно сделать соответствующую кнопку невидимой.
      if FoundService and
         ((ReadInt('Button', 'Visible') = 1) or IsPressedUnrepeatableButton) then
      begin
        WriteInt('Temp', 'DriverServiceButton' + IntToStr(I), 0);
        WriteInt('Button', 'Visible', 0);
      end
      else
      // В ТМ убрали атрибут.
      // Нужно вновь сделать кнопку видимой.
      if not FoundService and (ReadInt('Button', 'Visible') = 0) and
         not IsPressedUnrepeatableButton then
        WriteInt('Button', 'Visible', 1);
    end;
  end;
end;


// Взять сумму всех водительских услуг.
procedure TMD_GetDriverServicesSum(var DriverServicesSum: Single;
  var DriverServicesPercent: Integer);
var
  ServiceCost: Single;
  ServicePercent: Integer;
  ServiceName: String;
  ServiceId, I, ServicesCount: Integer;
  CanRepeatService: Boolean;
begin
  DriverServicesSum := 0;
  DriverServicesPercent := 0;
  for I := 1 to TMD_GetDriverServicesCount do
  begin
    ServicesCount := ReadInt('Temp', 'DriverServiceButton' + IntToStr(I));
    if ServicesCount > 0 then
    begin
      TMD_GetServiceParamsByIndex(I, ServiceCost, ServicePercent, ServiceName,
        ServiceId, CanRepeatService);
      DriverServicesSum := DriverServicesSum + ServicesCount * ServiceCost;
      DriverServicesPercent := DriverServicesPercent + ServicesCount * ServicePercent;
    end;
  end;
end;


// Добавить в чек стоимость за водительские услуги.
procedure TMD_AddDriverServices;
var
  ServiceName: String;
  ServiceCost: Single;
  I, ServicePercent, ServiceId, ServicesCount: Integer;
  CanRepeatService: Boolean;
begin
  for I := 1 to TMD_GetDriverServicesCount do
  begin
    ServicesCount := ReadInt('Temp', 'DriverServiceButton' + IntToStr(I));
    if ServicesCount > 0 then
    begin
      TMD_GetServiceParamsByIndex(I, ServiceCost, ServicePercent, ServiceName,
        ServiceId, CanRepeatService);

      if ServicePercent <> 0 then
      begin
        WriteStr('Bill', 'Code', 'DRIVER_SERVICE_PERCENT_' + IntToStr(I));
        WriteStr('Bill', 'Text', ServiceName);
        WriteStr('Bill', 'Value', IntToStr(ServicesCount));
        WriteStr('Bill', 'Sum', FloatToStr(ServicePercent * ServicesCount, 0) + '%');
      end;

      if ServiceCost <> 0 then
      begin
        WriteStr('Bill', 'Code', 'DRIVER_SERVICE_SUM_' + IntToStr(I));
        WriteStr('Bill', 'Text', ServiceName);
        WriteStr('Bill', 'Value', IntToStr(ServicesCount));
        WriteStr('Bill', 'Sum', FloatToStr(ServiceCost * ServicesCount, 2));
      end;
    end;
  end;
end;



// Добавить в чек стоимость за услуги (атрибуты/параметры).
procedure TMD_AddServices;
var
  F: Single;
begin
  F := ReadFloat('ServicesSum');
  if F <> 0 then
  begin
    WriteStr('Bill', 'Code', 'SERVICE_SUM');
    WriteStr('Bill', 'Text', 'Услуги');
    WriteStr('Bill', 'Value', ReadStr('ServicesName'));
    WriteStr('Bill', 'Sum', FloatToStr(F, 2));
  end;

  F := ReadFloat('ServicesPercent');
  if F <> 0 then
  begin
    WriteStr('Bill', 'Code', 'SERVICE_PERCENT');
    WriteStr('Bill', 'Text', 'Услуги');
    WriteStr('Bill', 'Value', ReadStr('ServicesName'));
    WriteStr('Bill', 'Sum', FloatToStr(F, 0) + '%');
  end;

  TMD_AddDriverServices;
end;


// Добавить в чек стоимость за скидки.
procedure TMD_AddDiscounts;
var
  Sum, DiscountSum, DiscountPercent: Single;
begin
  Sum := ReadFloat('Temp', 'SumBeforeDiscount');
  WriteStr('Bill', 'Code', 'BEFORE_DISC');
  WriteStr('Bill', 'Text', 'Сумма без скидки');
  WriteStr('Bill', 'Sum', FloatToStr(Sum, 2));

  DiscountSum := ReadFloat('TaxmTotalDiscSum');
  if DiscountSum <> 0 then
  begin
    WriteStr('Bill', 'Code', 'DISC_SUM');
    if DiscountSum < 0 then
      WriteStr('Bill', 'Text', 'Наценка')
    else
      WriteStr('Bill', 'Text', 'Скидка');
    WriteStr('Bill', 'Sum', FloatToStr(Abs(DiscountSum), 2));
  end;

  DiscountPercent := ReadFloat('TaxmTotalDiscPercent');
  if DiscountPercent <> 0 then
  begin
    WriteStr('Bill', 'Code', 'DISC_PERCENT');
    if DiscountPercent < 0 then
      WriteStr('Bill', 'Text', 'Наценка')
    else
      WriteStr('Bill', 'Text', 'Скидка');
    WriteStr('Bill', 'Sum', FloatToStr(Abs(DiscountPercent), 0) + '%');
  end;

  Sum := Sum - Sum * DiscountPercent / 100 - DiscountSum;

  WriteStr('Bill', 'Code', 'AFTER_DISC');
  WriteStr('Bill', 'Text', 'Сумма со скидкой');
  WriteStr('Bill', 'Sum', FloatToStr(Sum, 2));
end;


function TMD_UseCalcTMRoute: Boolean;
begin
  Result := USE_CALC_TM_ROUTE and (ReadInt('SupportedScriptVersion') >= 3);
end;


// Рассчитать стоимость за километраж с учётом маршрута из ТМ.
procedure TMD_RecalcDistSum;
var
  DistanceSum: Single;
begin
  if ReadInt('TMRouteReceiveTime') <> ReadInt('Temp', 'TMRouteReceiveTime') then
  begin
    WriteInt('Temp', 'TMRouteReceiveTime', ReadInt('TMRouteReceiveTime'));
    if (ReadInt('Now') - ReadInt('TMRouteSendTime') <= CALC_TM_ROUTE_FINISH_TIMEOUT) and
       (ReadFloat('TMRouteDistance') > 0) then
    begin
      if ReadFloat('TMRouteDistance') > ReadFloat('Temp', 'KmIncluded') then
      begin
        if ReadFloat('TMRouteCountryDistance') = 0 then
          DistanceSum := (ReadFloat('TMRouteDistance') - ReadFloat('Temp', 'KmIncluded')) * CITY_KM_COST
        else
        if ReadFloat('TMRouteCityDistance') > ReadFloat('Temp', 'KmIncluded') then
          DistanceSum := (ReadFloat('TMRouteCityDistance') - ReadFloat('Temp', 'KmIncluded')) * CITY_KM_COST +
            ReadFloat('TMRouteCountryDistance') * COUNTRY_KM_COST
        else
          DistanceSum := (ReadFloat('TMRouteDistance') - ReadFloat('Temp', 'KmIncluded')) * COUNTRY_KM_COST;
      end
      else
        DistanceSum := 0;

      // Перезаписать сумму и километраж.
      WriteFloat('Temp', 'DistanceSum', DistanceSum);
      WriteFloat('Temp', 'TotalDist', ReadFloat('TMRouteDistance'));
      WriteFloat('Temp', 'DistCity', ReadFloat('TMRouteCityDistance'));
      WriteFloat('Temp', 'DistCountry', ReadFloat('TMRouteCountryDistance'));
      WriteFloat('Temp', 'GPSLostDistSum', 0);
      WriteFloat('Temp', 'GpsLostDistCountry', 0);
      WriteFloat('Temp', 'GpsLostDistCity', 0);
      WriteFloat('Temp', 'GpsLostKmDist', 0);
    end;
  end;
end;


// Заполнить дополнительную информацию по тарифу в таксометре.
procedure TMD_FillMoreInfo;
var
  BoardingMoreInfo, CityMoreInfo, CountryMoreInfo: String;
  HasKmCost, HasMinutesCost: Boolean;
begin
  WriteStr('MoreInfo', 0, 'Тариф: ' + ReadStr('TariffName'));

  if CALC_SUM_BY_TAXIMETER then
  begin
    if not ReadBool('IsHourly') then
    begin
      // Если задан хоть один параметр города или загорода, нужно будет выводить оба, 
      // даже если цена будет 0.
      HasKmCost := (CITY_KM_COST <> 0) or (COUNTRY_KM_COST <> 0);
      HasMinutesCost := (CITY_MINUTE_COST <> 0) or (COUNTRY_MINUTE_COST <> 0);

      // Выводим подробный текст в поле посадки, если есть включенные км или минуты,
      // с учетом того, что есть соответствующие цены.
      if ((ReadFloat('Temp', 'KmIncluded') <> 0) and HasKmCost) or 
         ((ReadFloat('Temp', 'MinutesIncluded') <> 0) and HasMinutesCost) then
      begin
        BoardingMoreInfo := 'Первые';
        if HasKmCost then
          BoardingMoreInfo := BoardingMoreInfo + 
            ' ' + FloatToStr(ReadFloat('Temp', 'KmIncluded')) + ' км';
        if HasMinutesCost then
          BoardingMoreInfo := BoardingMoreInfo + 
            ' ' + FloatToStr(ReadFloat('Temp', 'MinutesIncluded')) + ' мин';

        CityMoreInfo := 'Далее город:';
        CountryMoreInfo := 'Далее загород:';
      end
      else
      // Иначе выводим обычный текст.
      begin
        BoardingMoreInfo := 'Посадка';
        CityMoreInfo := 'Город:';
        CountryMoreInfo := 'Загород:';
      end;

      // В конце строки посадки всегда пишем сумму посадки.
      BoardingMoreInfo := BoardingMoreInfo + 
        ': ' + FloatToStrFixed(ReadFloat('Temp', 'BoardingSum')) + ' ' + VALUTA;

      // Если есть цена за км, либо вообще нет ни одной цены, то выводим цену за км.
      if HasKmCost or not HasMinutesCost then
      begin
        CityMoreInfo := CityMoreInfo + 
          ' ' + FloatToStrFixed(CITY_KM_COST) + ' ' + VALUTA + '/км';
        CountryMoreInfo := CountryMoreInfo + 
          ' ' + FloatToStrFixed(COUNTRY_KM_COST) + ' ' + VALUTA + '/км';
      end;

      // Если есть цена за минуты, то выводим ее. 
      // Если при этом была цена за км, то цена за минуты выводится дополнительно.
      if HasMinutesCost then
      begin
        CityMoreInfo := CityMoreInfo + 
          ' ' + FloatToStrFixed(CITY_MINUTE_COST) + ' ' + VALUTA + '/мин';
        CountryMoreInfo := CountryMoreInfo + 
          ' ' + FloatToStrFixed(COUNTRY_MINUTE_COST) + ' ' + VALUTA + '/мин';
      end;

      if IDLE_MINUTE_COST <> 0 then
        WriteStr('MoreInfo', 1, 'Простой: ' +
          FloatToStrFixed(IDLE_MINUTE_COST) + ' ' + VALUTA + '/мин');
      if ReadFloat('Temp', 'MinSum') <> 0 then
        WriteStr('MoreInfo', 2, 'Минимум: ' +
          FloatToStrFixed(ReadFloat('Temp', 'MinSum')) + ' ' + VALUTA);
      WriteStr('MoreInfo', 3, BoardingMoreInfo);
      WriteStr('MoreInfo', 4, CityMoreInfo);
      WriteStr('MoreInfo', 5, CountryMoreInfo);
    end
    else
    begin
      if MIN_HOURLY <> 0 then
        WriteStr('MoreInfo', 1, 'Минимум: ' +
          FloatToStrFixed(MIN_HOURLY) + ' ' + VALUTA);
      if HOURLY_MINUTES_INCLUDED = 0 then
      begin
        WriteStr('MoreInfo', 3, 'Посадка: ' +
          FloatToStrFixed(BOARDING_HOURLY) + ' ' + VALUTA);
        if HOURLY_PERIOD > 0 then
          WriteStr('MoreInfo', 4, IntToStr(HOURLY_PERIOD) + ' мин: ' +
            FloatToStrFixed(HOURLY_MINUTE_COST * HOURLY_PERIOD) + ' ' + VALUTA)
        else
          WriteStr('MoreInfo', 4, '1 час: ' +
            FloatToStrFixed(HOURLY_MINUTE_COST * 60) + ' ' + VALUTA);
      end
      else
      begin
        WriteStr('MoreInfo', 3, 'Первые ' + IntToStr(HOURLY_MINUTES_INCLUDED) + ' мин: ' +
          FloatToStrFixed(BOARDING_HOURLY) + ' ' + VALUTA);
        if HOURLY_PERIOD > 0 then
          WriteStr('MoreInfo', 4, 'Далее ' + IntToStr(HOURLY_PERIOD) + ' мин: ' +
            FloatToStrFixed(HOURLY_MINUTE_COST * HOURLY_PERIOD) + ' ' + VALUTA)
        else
          WriteStr('MoreInfo', 4, 'Далее 1 час: ' +
            FloatToStrFixed(HOURLY_MINUTE_COST * 60) + ' ' + VALUTA);
      end;
    end;
  end;
end;


// Рассчитать полную стоимость поездки.
procedure TMD_CalcTaximeterSum;
var
  Sum, DriverServicesSum: Single;
  DriverServicesPercent: Integer;
  IsTripFinished: Boolean;
begin
  Sum := 0;
  TMD_RecheckDriverServiceButtons;
  // Стоимость всех водительских услуг.
  TMD_GetDriverServicesSum(DriverServicesSum, DriverServicesPercent);

  if CALC_SUM_BY_TAXIMETER then
  begin
    IsTripFinished := ReadInt('Temp', 'TripFinished') = 1;

    Sum := Sum + ReadFloat('Temp', 'PayWaitTimeSum') +
      ReadFloat('Temp', 'PriorOrderCost') +
      ReadFloat('Temp', 'SourceDistCountrySum') +
      ReadFloat('Temp', 'BoardingSum');

    if ReadBool('IsHourly') then
      Sum := Sum + ReadFloat('Temp', 'TripTimeSum') +
        ReadFloat('Temp', 'HourlyDistSum')
    else
    begin
      if not IsTripFinished and TMD_UseCalcTMRoute then
        TMD_RecalcDistSum;

      Sum := Sum + ReadFloat('Temp', 'DistanceSum') +
        ReadFloat('Temp', 'CityTripTimeSum') +
        ReadFloat('Temp', 'CountryTripTimeSum') +
        ReadFloat('Temp', 'GPSLostDistSum') +
        ReadFloat('Temp', 'IdleTimeSum') +
        ReadFloat('Temp', 'StopsSum') +
        ReadFloat('Temp', 'StopTimeSum');
    end;

    if not USE_REAL_ZONES_IN_TAXM then
      Sum := Sum + ReadFloat('TaxmZonesSum')
    else
      Sum := Sum + ReadFloat('Temp', 'ZoneStopSum') +
        ReadFloat('Temp', 'ZonePathSum') +
        ReadFloat('Temp', 'ZoneInCost') +
        ReadFloat('Temp', 'ZoneOutCost');

    if not SERVICES_AFTER_MIN then
    begin
      if IsTripFinished then
        TMD_AddServices;
      Sum := Sum + ReadFloat('ServicesSum') + DriverServicesSum +
        Sum * (DriverServicesPercent + ReadFloat('ServicesPercent')) / 100;
    end;

    // Сумма без скидки.
    // Работает только для Android.
    if ReadStr('Platform') = 'Android' then
      WriteFloat('Sum', RoundSum(Sum));

    if not DISCOUNT_AFTER_MIN then
    begin
      WriteFloat('Temp', 'SumBeforeDiscount', Sum);
      if IsTripFinished then
        TMD_AddDiscounts;
      Sum := Sum - Sum * ReadFloat('TaxmTotalDiscPercent') / 100 -
        ReadFloat('TaxmTotalDiscSum');
    end;

    if not ReadBool('IsHourly') and not ReadBool('IsCountry') then
      if (ReadInt('Temp', 'WasOutCity') = 0) and not ReadBool('InCity') then
      begin
        WriteInt('Temp', 'WasOutCity', 1);
        WriteFloat('Temp', 'MinSum', MIN_COUNTRY);
        WriteStr('MoreInfo', 2, 'Минимум: ' +
          FloatToStrFixed(MIN_COUNTRY) + ' ' + VALUTA);
      end;

    if IsTripFinished then
    begin
      WriteStr('Bill', 'Code', 'MINIMUM');
      WriteStr('Bill', 'Text', 'Минимум');
      WriteStr('Bill', 'Sum', FloatToStr(ReadFloat('Temp', 'MinSum'), 2));
    end;

    if (not MIN_SUM_AFTER_CALC) or (ReadInt('Temp', 'TripFinished') = 1) then
      if WAITING_INCLUDED then
        Sum := Max(ReadFloat('Temp', 'MinSum'), Sum)
      else
        Sum := Max(ReadFloat('Temp', 'MinSum') + ReadFloat('Temp', 'PayWaitTimeSum'), Sum);

    if SERVICES_AFTER_MIN then
    begin
      if IsTripFinished then
        TMD_AddServices;
      Sum := Sum + ReadFloat('ServicesSum') + DriverServicesSum +
        Sum * (DriverServicesPercent + ReadFloat('ServicesPercent')) / 100;
    end;

    if DISCOUNT_AFTER_MIN then
    begin
      WriteFloat('Temp', 'SumBeforeDiscount', Sum);
      if IsTripFinished then
        TMD_AddDiscounts;
      Sum := Sum - Sum * ReadFloat('TaxmTotalDiscPercent') / 100 -
        ReadFloat('TaxmTotalDiscSum');
    end;
  end
  else
  begin
    WriteStr('Caption', '');
    Sum := ReadFloat('OperSum');
    Sum := Sum + DriverServicesSum + Sum * DriverServicesPercent / 100;

    if USE_STOPS_WAITING_IDLE then
      Sum := Sum + ReadFloat('Temp', 'PayWaitTimeSum') +
        ReadFloat('Temp', 'IdleTimeSum') +
        ReadFloat('Temp', 'StopsSum') +
        ReadFloat('Temp', 'StopTimeSum');

    WriteFloat('Sum', RoundSum(Sum));
  end;

  Sum := RoundSum(Sum);

  // Итоговая сумма со скидкой.
  WriteFloat('TotalSum', Sum);

  // Для ios надо заполнить параметр Sum.
  if (ReadStr('Platform') <> 'Android') and CALC_SUM_BY_TAXIMETER then
    WriteFloat('Sum', Sum);

  WriteStr('SumStr', FloatToStrFixed(Sum));
  WriteFloat('CurrentSum', Sum);
end;


// ***************
// Начало расчета.
// ***************
procedure InitCalc;
var
  F, MinSum, Boarding, KmIncluded, MinutesIncluded: Single;
begin
  TMD_InitDriverServiceButtons;
  F := ReadFloat('Temp', 'PayWaitTimeSum');
  if F <> 0 then
  begin
    WriteStr('Bill', 'Code', 'WAITING_TIME');
    WriteStr('Bill', 'Text', 'Ожидание');
    WriteStr('Bill', 'Value',
      TimeLenToStr(ReadInt('WaitTime')));
    WriteStr('Bill', 'Sum', FloatToStr(F, 2));
  end;

  if CALC_SUM_BY_TAXIMETER then
  begin
    if ReadBool('IsPrior') and (PRIOR_ORDER_COST <> 0) then
    begin
      WriteStr('Bill', 'Code', 'PRIOR_ORDER');
      WriteStr('Bill', 'Text', 'Предварительный');
      WriteStr('Bill', 'Sum', FloatToStr(PRIOR_ORDER_COST, 2));
      WriteFloat('Temp', 'PriorOrderCost', PRIOR_ORDER_COST);
    end;

    F := ReadFloat('SourceDistCountry') * SOURCE_COUNTRY_KM_COST;
    if F <> 0 then
    begin
      WriteStr('Bill', 'Code', 'SOURCE_COUNTRY_DIST');
      WriteStr('Bill', 'Text', 'До подачи загород');
      WriteStr('Bill', 'Value',
        FloatToStr(ReadFloat('SourceDistCountry'), 2) + ' км');
      WriteStr('Bill', 'Sum', FloatToStr(F, 2));
      WriteFloat('Temp', 'SourceDistCountrySum', F);
    end;

    if not ReadBool('IsHourly') then
    begin
      if TMD_UseCalcTMRoute then
      begin
        WriteInt('UseCalcTMRoute', 1);
        WriteInt('CalcTMRoutePeriodSec', CALC_TM_ROUTE_PERIOD);
        ReadInt('Temp', 'TMRouteReceiveTime', 0);
      end;

      if ReadBool('IsCountry') or not ReadBool('InCity') then
      begin
        Boarding := BOARDING_COUNTRY;
        KmIncluded := COUNTRY_KM_INCLUDED;
        MinutesIncluded := COUNTRY_MINUTES_INCLUDED;
        MinSum := MIN_COUNTRY;
        WriteInt('Temp', 'WasOutCity', 1);
      end
      else
      begin
        Boarding := BOARDING_CITY;
        KmIncluded := CITY_KM_INCLUDED;
        MinutesIncluded := CITY_MINUTES_INCLUDED;
        MinSum := MIN_CITY;
        WriteInt('Temp', 'WasOutCity', 0);
      end;
      WriteFloat('Temp', 'KmIncluded', KmIncluded);
      WriteFloat('Temp', 'MinutesIncluded', MinutesIncluded);
    end
    else
    begin
      Boarding := BOARDING_HOURLY;
      MinSum := MIN_HOURLY;
    end;
    WriteFloat('Temp', 'BoardingSum', Boarding);
    WriteFloat('Temp', 'MinSum', MinSum);

    // Определяем район по фактическим координатам.
    if USE_REAL_ZONES_IN_TAXM then
    begin
      // Посадка в районе.
      if USE_ZONE_COST then
      begin
        F := ReadFloat('ZoneInCost', ReadInt('ZoneId'));
        if F <> 0 then
        begin
          WriteStr('Bill', 'Code', 'ZONE_IN');
          WriteStr('Bill', 'Text', 'Посадка ' +
            '"' + ReadStr('ZoneName') + '"');
          WriteStr('Bill', 'Sum', FloatToStr(F, 2));

          WriteFloat('Temp', 'ZoneInCost', F);
        end;
      end;

      // Сохраняем текущий район.
      WriteInt('Temp', 'LastZoneId', ReadInt('ZoneId'));
    end;
  end;

  TMD_CalcTaximeterSum;

  TMD_FillMoreInfo;

  WriteInt('Temp', 'AllStopTime', 0);
  WriteFloat('Temp', 'StopTimeSum', 0);
end;


// Использовать восстановление километража при потере GPS.
function TMD_UseGPSMissedDistanceRecovery: Boolean;
begin
  Result := USE_GPS_MISSED_DISTANCE_RECOVERY and
    (ReadInt('SupportedScriptVersion') > 0);
end;


// Пересчитать параметры, связанные с координатами.
procedure TMD_RecalcLostGPSParams;
var
  Lat, Lon, CurTotalDistance, KmCost, DistDelta: Single;
begin
  Lon := ReadFloat('Lon');
  Lat := ReadFloat('Lat');

  // Посчитать километраж по прямой, если появились координаты.
  if (ReadInt('Temp', 'IsLostGPS') = 1) and
     ((ReadFloat('Temp', 'OldLat') > 0) or
     (ReadFloat('Temp', 'OldLon') > 0)) and
     ((Lat > 0) or (Lon > 0)) then
  begin
    DistDelta := SegmentLength(ReadFloat('Temp', 'OldLon'),
      ReadFloat('Temp', 'OldLat'), Lon, Lat);

    WriteFloat('Temp', 'GpsLostKmDist',
      ReadFloat('Temp', 'GpsLostKmDist') + DistDelta);

    CurTotalDistance := ReadFloat('Temp', 'TotalDist') + DistDelta;

    // При восстанавлении координат не успевает правильно
    // определиться флаг ReadBool('InCity').
    if ReadInt('Temp', 'OldInCity') > 0 then
    begin
      WriteFloat('Temp', 'GpsLostDistCity',
        ReadFloat('Temp', 'GpsLostDistCity') + DistDelta);
      KmCost := CITY_KM_COST;
    end
    else
    begin
      WriteFloat('Temp', 'GpsLostDistCountry',
        ReadFloat('Temp', 'GpsLostDistCountry') + DistDelta);
      KmCost := COUNTRY_KM_COST;
    end;

    if CurTotalDistance > ReadFloat('Temp', 'KmIncluded') then
    begin
      // Если при восстановлении координат сразу закончился
      // включенный киломтраж.
      if ReadFloat('Temp', 'TotalDist') < ReadFloat('Temp', 'KmIncluded') then
        // Платный километраж.
        DistDelta := CurTotalDistance - ReadFloat('Temp', 'KmIncluded');

      WriteFloat('Temp', 'GPSLostDistSum',
        ReadFloat('Temp', 'GPSLostDistSum') + KmCost * DistDelta);
    end;
  end;

  // Пропал сигнал GPS.
  if (Lat = 0) and (Lon = 0) then
    WriteInt('Temp', 'IsLostGPS', 1)
  else
  // Есть сигнал GPS.
  begin
    WriteFloat('Temp', 'OldLat', Lat);
    WriteFloat('Temp', 'OldLon', Lon);
    if ReadBool('InCity') then
      WriteInt('Temp', 'OldInCity', 1)
    else
      WriteInt('Temp', 'OldInCity', 0);
    WriteInt('Temp', 'IsLostGPS', 0);
  end;
end;


// *******
// Расчёт.
// *******
procedure StepCalc;
var
  F, DistDelta, TripDistance: Single;
  City: String;
  Seconds: Integer;
begin
  if CALC_SUM_BY_TAXIMETER then
  begin
    if ReadBool('HasCities') then
    begin
      if ReadBool('InCity') then
        City := ' [' + ReadStr('CityName') + ']'
      else
        City := ' [загород]';
    end;

    DistDelta := ReadFloat('DistDelta');
    if ReadBool('IsHourly') then
    begin
      WriteStr('Caption', 'ЧАС' + City);

      if ReadInt('TripTime') > HOURLY_MINUTES_INCLUDED * 60 then
      begin
        if HOURLY_PERIOD = 0 then
          F := HOURLY_MINUTE_COST / 60
        else
          if Floor((ReadInt('TripTime') - HOURLY_MINUTES_INCLUDED * 60 - 1) /
             60 / HOURLY_PERIOD) * HOURLY_PERIOD * 60 =
             ReadInt('TripTime') - HOURLY_MINUTES_INCLUDED * 60 - 1 then
            F := HOURLY_PERIOD * HOURLY_MINUTE_COST
          else
            F := 0;
        if F <> 0 then
        begin
          WriteFloat('Temp', 'TripTimeSum',
            ReadFloat('Temp', 'TripTimeSum') + F);
        end;
      end;

      if HOURLY_KM_COST > 0 then
      begin
        F := 0;
        if HOURLY_HOUR_KM_INCLUDED > 0 then
        begin
          // Ещё один час закончился.
          if ReadInt('TripTime') mod (60 * 60) = 0 then
            WriteFloat('Temp', 'DistHour', 0);

          WriteFloat('Temp', 'DistHour',
            ReadFloat('Temp', 'DistHour') + DistDelta);

          if ReadFloat('Temp', 'DistHour') > HOURLY_HOUR_KM_INCLUDED then
            F := DistDelta * HOURLY_KM_COST;
        end
        else
        if HOURLY_TRIP_KM_INCLUDED > 0 then
        begin
          if ReadFloat('TripDistance') > HOURLY_TRIP_KM_INCLUDED then
            F := DistDelta * HOURLY_KM_COST;
        end
        else
          F := DistDelta * HOURLY_KM_COST;

        if F <> 0 then
        begin
          WriteFloat('Temp', 'HourlyDist',
            ReadFloat('Temp', 'HourlyDist') + DistDelta);
          WriteFloat('Temp', 'HourlyDistSum',
            ReadFloat('Temp', 'HourlyDistSum') + F);
        end;
      end;
    end
    else
    begin
      WriteFloat('Temp', 'TotalDist',
        ReadFloat('Temp', 'TotalDist') + DistDelta);
      // Если скорость достаточная, считать по километражу.
      if ReadFloat('Speed') > SPEED_LIMIT then
      begin
        WriteStr('Caption', 'КМ' + City);
        TripDistance := ReadFloat('Temp', 'TotalDist') + ReadFloat('Temp', 'GpsLostDistCountry') +
          ReadFloat('Temp', 'GpsLostDistCity');
        if not ReadBool('InCity') then
        begin
          WriteFloat('Temp', 'DistCountry',
            ReadFloat('Temp', 'DistCountry') + DistDelta);
          if TripDistance > ReadFloat('Temp', 'KmIncluded') then
          begin
            WriteFloat('Temp', 'DistanceSum',
              ReadFloat('Temp', 'DistanceSum') + COUNTRY_KM_COST * DistDelta);
          end;
        end
        else
        begin
          WriteFloat('Temp', 'DistCity',
            ReadFloat('Temp', 'DistCity') + DistDelta);
          if TripDistance > ReadFloat('Temp', 'KmIncluded') then
          begin
            WriteFloat('Temp', 'DistanceSum',
              ReadFloat('Temp', 'DistanceSum') + CITY_KM_COST * DistDelta);
          end;
        end;
      end;

      if (COUNTRY_MINUTE_COST <> 0) or (CITY_MINUTE_COST <> 0) then
        if not ReadBool('InCity') then
        begin
          WriteInt('Temp', 'CountryTripTime', 
            ReadInt('Temp', 'CountryTripTime') + 1);
          if ReadInt('TripTime') > ReadFloat('Temp', 'MinutesIncluded') * 60 then
            WriteFloat('Temp', 'CountryTripTimeSum', 
              ReadFloat('Temp', 'CountryTripTimeSum') + COUNTRY_MINUTE_COST / 60);
        end
        else
        begin
          WriteInt('Temp', 'CityTripTime', 
            ReadInt('Temp', 'CityTripTime') + 1);
          if ReadInt('TripTime') > ReadFloat('Temp', 'MinutesIncluded') * 60 then
            WriteFloat('Temp', 'CityTripTimeSum', 
              ReadFloat('Temp', 'CityTripTimeSum') + CITY_MINUTE_COST / 60);
        end;

      if TMD_UseGPSMissedDistanceRecovery then
        TMD_RecalcLostGPSParams;
    end;
  end;

  if TMD_UseCalcStopsIdle then
  begin
    if ReadFloat('Speed') > SPEED_LIMIT then
      WriteInt('Temp', 'LastKMTime', ReadInt('TripTime'))
    else
    if ReadInt('TripTime') - ReadInt('Temp', 'LastKMTime') > FREE_IDLE_TIME then
    begin
      WriteStr('Caption', 'Простой');
      WriteInt('Temp', 'IdleTime',
        ReadInt('Temp', 'IdleTime') + 1);
      WriteFloat('Temp', 'IdleTimeSum',
        ReadFloat('Temp', 'IdleTimeSum') + IDLE_MINUTE_COST / 60);
    end
    else
    begin
      Seconds := FREE_IDLE_TIME -
        (ReadInt('TripTime') - ReadInt('Temp', 'LastKMTime'));
      WriteStr('Caption', 'Ожидание (' + IntToStr(Seconds) + ')' + City);
    end;
  end;

  TMD_CalcTaximeterSum;
end;


// *****************
// Начало остановки.
// *****************
procedure StartStop;
var
  F: Single;
begin
  if CALC_SUM_BY_TAXIMETER then
  begin
    WriteStr('Caption', 'Остановка');
    WriteInt('UseZonesPathGroupId', ZONES_PATH_GROUP_ID);

    // Определяем район по фактическим координатам.
    if USE_REAL_ZONES_IN_TAXM then
    begin
      if ReadInt('ZoneId') <> ReadInt('Temp', 'LastZoneId') then
        // Сохраняем признак, что покидали район посадки.
        WriteInt('Temp', 'NeedZoneOutCost', 1);

      // Проезды между районами
      if (USE_ZONE_PATH) and (ReadInt('ZoneId') > 0) then
      begin
        F := ReadFloat('ZonePathCost',
          ReadInt('Temp', 'LastZoneId'), ReadInt('ZoneId'));
        if F <> 0 then
        begin
          WriteStr('Bill', 'Code', 'ZONES_PATH');
          WriteStr('Bill', 'Text', 'Проезд ' + '"' +
            ReadStr('ZoneName', ReadInt('Temp', 'LastZoneId')) +
            '"-"' + ReadStr('ZoneName') + '"');
          WriteStr('Bill', 'Sum', FloatToStr(F, 2));

          WriteFloat('Temp', 'ZonePathSum',
            ReadFloat('Temp', 'ZonePathSum') + F);
        end;
      end;

      // Сохраняем текущий район.
      WriteInt('Temp', 'LastZoneId', ReadInt('ZoneId'));

      // Остановка в районе.
      if USE_ZONE_COST then
      begin
        F := ReadFloat('ZoneStopCost', ReadInt('ZoneId'));
        if F <> 0 then
        begin
          WriteStr('Bill', 'Code', 'ZONES_STOP');
          WriteStr('Bill', 'Text', 'Остановка ' +
            '"' + ReadStr('ZoneName') + '"');
          WriteStr('Bill', 'Sum', FloatToStr(F, 2));

          WriteFloat('Temp', 'ZoneStopSum',
            ReadFloat('Temp', 'ZoneStopSum') + F);
        end;
      end;
    end;
  end;

  if TMD_UseCalcStopsIdle then
  begin
    WriteInt('Temp', 'StopTime', 0);
    WriteInt('Temp', 'StopsCount',
      ReadInt('Temp', 'StopsCount') + 1);
    WriteFloat('Temp', 'StopsSum',
      ReadFloat('Temp', 'StopsSum') + MIN_STOP_COST);
  end;

  TMD_CalcTaximeterSum;
end;


// **********
// Остановка.
// **********
procedure StepCalcStop;
var
  StopTime: Integer;
  Sum: Single;
begin
  if not TMD_UseCalcStopsIdle then
    StepCalc
  else
  begin
    WriteFloat('Temp', 'TotalDist',
      ReadFloat('Temp', 'TotalDist') + ReadFloat('DistDelta'));
    if TMD_UseGPSMissedDistanceRecovery then
      TMD_RecalcLostGPSParams;

    if STOP_MINUTE_COST <> 0 then
    begin
      StopTime := ReadInt('Temp', 'StopTime') + 1;

      if StopTime > (STOP_MINUTES_INCLUDED * 60) then
      begin
        WriteFloat('Temp', 'StopTimeSum',
          ReadFloat('Temp', 'StopTimeSum') + STOP_MINUTE_COST / 60);
        TMD_CalcTaximeterSum;
      end;

      WriteInt('Temp', 'StopTime', StopTime);
      WriteInt('Temp', 'AllStopTime',
        ReadInt('Temp', 'AllStopTime') + 1);
    end;
  end;
end;


// ****************
// Конец остановки.
// ****************
procedure EndStop;
begin
  WriteStr('Caption', '');
end;


// ****************
// Обработка дополнительных функциональных кнопок.
// ****************
procedure ButtonClick;
var
  ButtonName, ServiceName: String;
  Sum, ServiceSum, PercentSum: Single;
  I, ServicePercent, ServiceId: Integer;
  CanRepeatService: Boolean;
begin
  ButtonName := ReadStr('Button', 'Clicked');
  for I := 1 to TMD_GetDriverServicesCount do
    if ButtonName = 'DriverServiceButton' + IntToStr(I) then
    begin
      TMD_GetServiceParamsByIndex(I, ServiceSum, ServicePercent, ServiceName,
        ServiceId, CanRepeatService);
      if CanRepeatService then
        WriteInt('Temp', 'DriverServiceButton' + IntToStr(I),
          ReadInt('Temp', 'DriverServiceButton' + IntToStr(I)) + 1)
      else
      begin
        WriteInt('Temp', 'DriverServiceButton' + IntToStr(I), 1);
        WriteStr('Button', 'Select', ButtonName);
        WriteInt('Button', 'Visible', 0);
      end;
    end;

  TMD_CalcTaximeterSum;
end;


// Добавить в чек стоимость за восстановленный километраж.
procedure TMD_AddGPSParams;
begin
  if TMD_UseGPSMissedDistanceRecovery and
     (ReadFloat('Temp', 'GpsLostKmDist') > 0) then
  begin
    if ReadFloat('Temp', 'GPSLostDistCity') > 0 then
    begin
      WriteStr('Bill', 'Code', 'CITY_LOST_GPS_DIST');
      WriteStr('Bill', 'Text', 'По городу при отсутствии GPS');
      WriteStr('Bill', 'Value',
        FloatToStr(ReadFloat('Temp', 'GPSLostDistCity'), 2) + ' км');
    end;

    if ReadFloat('Temp', 'GPSLostDistCountry') > 0 then
    begin
      WriteStr('Bill', 'Code', 'COUNTRY_LOST_GPS_DIST');
      WriteStr('Bill', 'Text', 'За городом при отсутствии GPS');
      WriteStr('Bill', 'Value',
        FloatToStr(ReadFloat('Temp', 'GPSLostDistCountry'), 2) + ' км');
    end;
  end;
end;


// Добавить в чек стоимость за простой, ожидание, остановки.
procedure TMD_AddTimeParams;
begin
  WriteStr('Bill', 'Code', 'IDLE_TIME');
  WriteStr('Bill', 'Text', 'Простой');
  WriteStr('Bill', 'Value',
    TimeLenToStr(ReadInt('Temp', 'IdleTime')));
  WriteStr('Bill', 'Sum',
    FloatToStr(ReadFloat('Temp', 'IdleTimeSum'), 2));

  if ReadInt('Temp', 'StopsCount') > 0 then
  begin
    WriteStr('Bill', 'Code', 'STOPS');
    WriteStr('Bill', 'Text', 'Остановки');
    WriteStr('Bill', 'Value',
      IntToStr(ReadInt('Temp', 'StopsCount')) + ' шт');
    WriteStr('Bill', 'Sum',
      FloatToStr(ReadFloat('Temp', 'StopsSum'), 2));

    if (STOP_MINUTE_COST <> 0) and (ReadFloat('Temp', 'StopTimeSum') <> 0) then
    begin
      WriteStr('Bill', 'Code', 'STOPS_TIME');
      WriteStr('Bill', 'Text', 'Длит. остановок');
      WriteStr('Bill', 'Value',
        TimeLenToStr(ReadInt('Temp', 'AllStopTime')));
      WriteStr('Bill', 'Sum',
        FloatToStr(ReadFloat('Temp', 'StopTimeSum'), 2));
    end;
  end;
end;


// *******************
// Завершение расчета.
// *******************
procedure TermCalc;
var
  F: Single;
begin
  WriteInt('Temp', 'TripFinished', 1);
  if CALC_SUM_BY_TAXIMETER then
  begin
    WriteInt('UseZonesPathGroupId', ZONES_PATH_GROUP_ID);

    if not USE_REAL_ZONES_IN_TAXM then
    begin
      F := ReadFloat('TaxmZonesSum');
      if F <> 0 then
      begin
        WriteStr('Bill', 'Code', 'ZONES');
        WriteStr('Bill', 'Text', 'Районы');
        WriteStr('Bill', 'Sum', FloatToStr(F, 2));
      end;
    end
    else
    begin
      // Перед высадкой проверяем проезды между районами
      if (USE_ZONE_PATH) and (ReadInt('ZoneId') > 0) then
      begin
        F := ReadFloat('ZonePathCost',
          ReadInt('Temp', 'LastZoneId'), ReadInt('ZoneId'));
        if F <> 0 then
        begin
          WriteStr('Bill', 'Code', 'ZONES_PATH');
          WriteStr('Bill', 'Text', 'Проезд ' + '"' +
            ReadStr('ZoneName', ReadInt('Temp', 'LastZoneId')) +
            '"-"' + ReadStr('ZoneName') + '"');
          WriteStr('Bill', 'Sum', FloatToStr(F, 2));

          WriteFloat('Temp', 'ZonePathSum',
            ReadFloat('Temp', 'ZonePathSum') + F);
        end;
      end;

      // Высадка в районе.
      if USE_ZONE_COST and (ReadInt('ZoneId') > 0) and
         ((ReadInt('ZoneId') <> ReadInt('Temp', 'LastZoneId')) or
         (ReadInt('Temp', 'NeedZoneOutCost') = 1)) then
      begin
        F := ReadFloat('ZoneOutCost', ReadInt('ZoneId'));
        if F <> 0 then
        begin
          WriteStr('Bill', 'Code', 'ZONES_OUT');
          WriteStr('Bill', 'Text', 'Высадка ' +
            '"' + ReadStr('ZoneName') + '"');
          WriteStr('Bill', 'Sum', FloatToStr(F, 2));

          WriteFloat('Temp', 'ZoneOutCost', F);
        end;
      end;
    end;

    if ReadBool('IsHourly') then
    begin
      WriteStr('Bill', 'Code', 'TRIP_TIME');
      WriteStr('Bill', 'Text', 'Общее время');
      WriteStr('Bill', 'Value',
        TimeLenToStr(ReadInt('TripTime')));
      WriteStr('Bill', 'Sum',
        FloatToStr(ReadFloat('Temp', 'TripTimeSum') + ReadFloat('Temp', 'BoardingSum'), 2));

      WriteStr('Bill', 'Code', 'TRIP_DIST');
      WriteStr('Bill', 'Text', 'Общее расстояние');
      WriteStr('Bill', 'Value',
        FloatToStr(ReadFloat('TripDistance'), 2) + ' км');

      F := ReadFloat('Temp', 'HourlyDistSum');
      if F > 0 then
      begin
        WriteStr('Bill', 'Code', 'HOURLY_PAY_DIST');
        WriteStr('Bill', 'Text', 'Платный километраж');
        WriteStr('Bill', 'Value',
          FloatToStr(ReadFloat('Temp', 'HourlyDist'), 2) + ' км');
        WriteStr('Bill', 'Sum', FloatToStr(F, 2));
      end;
    end
    else
    begin
      if TMD_UseCalcTMRoute then
        TMD_RecalcDistSum;

      WriteStr('Bill', 'Code', 'TRIP_TIME');
      WriteStr('Bill', 'Text', 'Общее время');
      WriteStr('Bill', 'Value',
        TimeLenToStr(ReadInt('TripTime')));

      if CITY_MINUTE_COST <> 0 then
      begin
        WriteStr('Bill', 'Code', 'CITY_TRIP_TIME');
        WriteStr('Bill', 'Text', 'Время по городу');
        WriteStr('Bill', 'Value',
          TimeLenToStr(ReadInt('Temp', 'CityTripTime')));
        WriteStr('Bill', 'Sum', 
          FloatToStr(ReadFloat('Temp', 'CityTripTimeSum'), 2));
      end;

      if COUNTRY_MINUTE_COST <> 0 then
      begin
        WriteStr('Bill', 'Code', 'COUNTRY_TRIP_TIME');
        WriteStr('Bill', 'Text', 'Время за городом');
        WriteStr('Bill', 'Value',
          TimeLenToStr(ReadInt('Temp', 'CountryTripTime')));
        WriteStr('Bill', 'Sum', 
          FloatToStr(ReadFloat('Temp', 'CountryTripTimeSum'), 2));  
      end;

      WriteStr('Bill', 'Code', 'TRIP_DIST');
      WriteStr('Bill', 'Text', 'Общее расстояние');
      WriteStr('Bill', 'Value',
        FloatToStr(ReadFloat('Temp', 'TotalDist'), 2) + ' км');
      WriteStr('Bill', 'Sum',
        FloatToStr(ReadFloat('Temp', 'BoardingSum') + 
          ReadFloat('Temp', 'DistanceSum') + 
          ReadFloat('Temp', 'GPSLostDistSum'), 2));

      WriteStr('Bill', 'Code', 'CITY_DIST');
      WriteStr('Bill', 'Text', 'По городу');
      WriteStr('Bill', 'Value',
        FloatToStr(ReadFloat('Temp', 'DistCity'), 2) + ' км');

      WriteStr('Bill', 'Code', 'COUNTRY_DIST');
      WriteStr('Bill', 'Text', 'За городом');
      WriteStr('Bill', 'Value',
        FloatToStr(ReadFloat('Temp', 'DistCountry'), 2) + ' км');

      TMD_AddGPSParams;

      TMD_AddTimeParams;
    end;

    TMD_CalcTaximeterSum;
  end
  else
  begin
    WriteStr('Bill', 'Code', 'TRIP_TIME');
    WriteStr('Bill', 'Text', 'Общее время');
    WriteStr('Bill', 'Value',
      TimeLenToStr(ReadInt('TripTime')));

    WriteStr('Bill', 'Code', 'TRIP_DIST');
    WriteStr('Bill', 'Text', 'Общее расстояние');
    WriteStr('Bill', 'Value',
      FloatToStr(ReadFloat('TripDistance'), 2) + ' км');

    WriteStr('Bill', 'Code', 'OPER_SUM');
    WriteStr('Bill', 'Text', 'Сумма оператора');
    WriteStr('Bill', 'Sum', FloatToStr(ReadFloat('OperSum'), 2));

    if USE_STOPS_WAITING_IDLE then
      TMD_AddTimeParams;

    TMD_AddDriverServices;

    TMD_CalcTaximeterSum;

    WriteStr('Bill', 'Code', 'AFTER_DISC');
    WriteStr('Bill', 'Text', 'Сумма');
    if ReadStr('Platform') = 'Android' then
       WriteStr('Bill', 'Sum', FloatToStr(ReadFloat('TotalSum'), 2))
    else
       WriteStr('Bill', 'Sum', FloatToStr(ReadFloat('Sum'), 2));
  end;
end;

// *****************************************************************
// Добавить строку в чек. В конец строки вставляется перенос строки.
// *****************************************************************
procedure AddLine(Line: String);
begin
  WriteStr('PrintCheck', 'Text', Line);
end;

// ******************************************
// Добавить в чек строку типа текст/значение.
// ******************************************
procedure AddLineTextValue(Line: String; Value: String);
begin
  WriteStr('PrintCheck', 'Text', Line);
  WriteStr('PrintCheck', 'Value', Value);
end;

// ******************************************
// Добавить в чек строку типа текст/значение для стоимости.
// ******************************************
procedure AddTextCostValue(Line: String; Value: Single);
begin
  WriteStr('PrintCheck', 'Text', Line);
  WriteStr('PrintCheck', 'Value', '=' + FloatToStr(Value, 2));
end;

// ******************************************
// Добавить в чек строку типа текст/значение с проверкой на 0.
// ******************************************
procedure AddTextNotNullValue(Line: String; Value: Single);
begin
  if Value <> 0 then
    AddTextCostValue(Line, Value);
end;

// ******************************
// Добавить перенос строки в чек.
// ******************************
procedure AddLineBreak;
begin
  WriteStr('PrintCheck', 'BreakLine');
end;

// ******************************
// Получить текущую дату и время в виде строки.
// ******************************
function GetDateTimeStr(DT: Integer): String;
var
  TmpStr: String;
  D, M, Y, DW, H, S, I: Integer;
begin
  TmpStr := '';
  ExtractDate(DT, D, M, Y, DW);
  if D < 10 then
    TmpStr := TmpStr + '0';
  TmpStr := TmpStr + IntToStr(D) + '.';
  if M < 10 then
    TmpStr := TmpStr + '0';
  TmpStr := TmpStr + IntToStr(M) + '.';
  Y := Y mod 100;
  if Y < 10 then
    TmpStr := TmpStr + '0';
  TmpStr := TmpStr + IntToStr(y) + ' ';
  ExtractTime(ReadInt('Now'), H, M, S);
  if H < 10 then
    TmpStr := TmpStr + '0';
  TmpStr := TmpStr + IntToStr(H) + ':';
  if M < 10 then
    TmpStr := TmpStr + '0';
  TmpStr := TmpStr + IntToStr(M);

  Result := TmpStr;
end;

// ******************************
// Сформировать QR-код.
// ******************************
function GetQrCode: String;
var
  D, M, Y, DW, H, MM, S, I: Integer;
  QR: String;
begin
  ExtractDate(ReadInt('FiscalInfo', 'CompleteTime'), D, M, Y, DW);
  ExtractTime(ReadInt('FiscalInfo', 'CompleteTime'), H, MM, S);
  QR := 't=' +IntToStr(Y) + IntToStr(M) + IntToStr(D) + 'T' + IntToStr(H) +
    IntToStr(MM);
  QR := QR + '&s=' + FloatToStr(ReadFloat('BankCardSum') + ReadFloat('CashSum'), 2);
  QR := QR + '&fn=' + ReadStr('FiscalInfo', 'FiscalNumber');
  QR := QR + '&i=' + IntToStr(ReadInt('FiscalInfo', 'FiscalDocumentNumber'));
  QR := QR + '&fp=' + ReadStr('FiscalInfo', 'FiscalDocumentSign');
  QR := QR + '&n=1';
  Result := QR;
end;

// ************
// Печать чека.
// ************
procedure PrintCheck;
var
  I: Integer;
  TmpStr, PrinterModel, Encoding: String;
  ServicePercent, SumServicePercent, NdsSum, FiscalSum, DiscountSum: Single;
begin
  WriteStr('PrintCheck', 'Align', '0');
  AddLine(TAXI_NAME);
  AddLine('т. ' + TAXI_PHONE);
  AddLineBreak;
  AddLine('БЛАГОДАРИМ ВАС');
  AddLine('ЗА ОБРАЩЕНИЕ В НАШУ КОМПАНИЮ!');
  AddLineBreak;

  // Детализация поездки
  if NEED_DETAILS_IN_CHECK then
  begin
    if not ReadBool('IsBorder') then
      AddLine('Заказ #' + IntToStr(ReadInt('OrderId')))
    else
      AddLine('Бордюрный заказ');

    AddLine('Тариф: ' + ReadStr('TariffName'));
    if not ReadBool('IsBorder') then
      AddLine('Адрес: ' + ReadStr('SourceAddress'));
    AddLine('Водитель: ' + ReadStr('DriverName'));
    AddLine('Машина: ' + ReadStr('CarColor') + ' ' +
      ReadStr('CarMark') + ' ' + ReadStr('CarModel') + ' ' + ReadStr('CarGosnumber'));
    AddLineBreak;
    AddLine('Километраж: ' + FloatToStr(ReadFloat('TripDistance'), 2) + ' км');
    AddLine('Длительность: ' + TimeLenToStr(ReadInt('TripTime')));
    AddLineBreak;

    if CALC_SUM_BY_TAXIMETER then
    begin
      AddLine('Услуга                     Сумма');
      AddLine('--------------------------------');

      AddTextNotNullValue('Доезд до адреса подачи',
        ReadFloat('Temp', 'SourceDistCountrySum'));
      AddTextNotNullValue('Ожидание', ReadFloat('Temp', 'PayWaitTimeSum'));
      AddTextNotNullValue('Посадка', ReadFloat('Temp', 'BoardingSum'));
      AddTextNotNullValue('Районы', ReadFloat('TaxmZonesSum'));

      if ReadBool('IsHourly') then
      begin
        AddTextNotNullValue('Поездка', ReadFloat('Temp', 'TripTimeSum'));
      end
      else
      begin
        AddTextNotNullValue('По городу', ReadFloat('Temp', 'DistanceSum'));
        AddTextNotNullValue('За городом', ReadFloat('Temp', 'DistCountrySum'));
        if ReadFloat('Temp', 'CityTripTimeSum') <> 0 then
          AddTextNotNullValue('Время по городу', ReadFloat('Temp', 'CityTripTimeSum'));
        if ReadFloat('Temp', 'CountryTripTimeSum') <> 0 then
          AddTextNotNullValue('Время за городом', ReadFloat('Temp', 'CountryTripTimeSum'));
        AddTextNotNullValue('Простой', ReadFloat('Temp', 'IdleTimeSum'));
        AddTextNotNullValue('Остановки', ReadFloat('Temp', 'StopsSum'));
      end;

      if ReadFloat('ServicesSum') <> 0 then
      begin
        for I := 0 to ReadInt('ServiceCount') - 1 do
          AddTextNotNullValue(ReadStr('ServiceName', ReadInt('OrderServiceId', I)),
            ReadFloat('ServiceSum', ReadInt('OrderServiceId', I)));
      end;

      if ReadFloat('ServicesPercent') <> 0 then
      begin
        for I := 0 to ReadInt('ServiceCount') - 1 do
          if ReadFloat('ServicePercent', ReadInt('OrderServiceId', I)) <> 0 then
          begin
            ServicePercent := ReadFloat('ServicePercent', ReadInt('OrderServiceId', I));
            SumServicePercent := ServicePercent * ReadFloat('Temp', 'Check_SumBeforeServices') / 100;
            AddTextNotNullValue(
              ReadStr('ServiceName', ReadInt('OrderServiceId', I)) + ' ' + FloatToStr(ServicePercent, 0) + '%',
              SumServicePercent);
          end;
      end;
    end;

    AddLineTextValue('',
      '==' + FloatToStr(ReadFloat('Temp', 'SumBeforeDiscount'), 2));

    if ReadFloat('Temp', 'Check_DiscountSum') <> 0 then
    begin
      AddLine('--------------------------------');
      if ReadFloat('Temp', 'Check_DiscountSum') > 0 then
        AddTextCostValue('Скидка', ReadFloat('Temp', 'Check_DiscountSum'))
      else
        AddTextCostValue('Наценка', -ReadFloat('Temp', 'Check_DiscountSum'));

      AddTextCostValue('', ReadFloat('Temp', 'Check_SumAfterDiscount'));
    end;
  end;

  AddLine( '================================');

  if CALC_SUM_BY_TAXIMETER and NEED_DETAILS_IN_CHECK then
    AddTextNotNullValue('Минимум', ReadFloat('Temp', 'MinSum'));

  if not NEED_DETAILS_IN_CHECK then
    AddLine('ПОЕЗДКА');
  AddLineTextValue('ИТОГО К ОПЛАТЕ',
    '==' + FloatToStr(ReadFloat('Temp', 'Check_TotalSum'), 2));
  AddLineTextValue('Наличными',
    '==' + FloatToStr(ReadFloat('CashSum'), 2));
  AddLineBreak;
  AddLine(GetDateTimeStr(ReadInt('Now')) + '  ИНН ' + TAXI_INN);
  AddLineBreak;
  AddLineBreak;

  // Чек с фискальными данными.
  if not ReadBool('PrintCheck', 'IsFiscal') and ReadBool('FiscalInfo', 'Success') then
  begin
    WriteStr('PrintCheck', 'Align', '0');
    AddLine('КАССОВЫЙ ЧЕК');

    WriteStr('PrintCheck', 'Align', '-1');
    AddLine('ПРИХОД');
    
    FiscalSum := ReadFloat('BankCardSum') + ReadFloat('CashSum');
    AddLineTextValue(ReadStr('FiscalInfo', 'CommodityName'),
      FloatToStr(FiscalSum, 2) + ' x 1 = ' + FloatToStr(FiscalSum, 2));
    AddTextNotNullValue('  ' + ReadStr('FiscalInfo', 'TaxRate'), ReadFloat('FiscalInfo', 'TaxSum'));
    
    DiscountSum := ReadFloat('TotalSum') - FiscalSum;
    if DiscountSum > 0 then
      AddTextCostValue('СКИДКА', DiscountSum);

    AddLine('УСЛУГА');
    if ReadStr('FiscalInfo', 'TaxRate') = 'без НДС' then
      NdsSum := FiscalSum
    else
      NdsSum := ReadFloat('FiscalInfo', 'TaxSum');
    WriteStr('PrintCheck', 'FontSize', '1');  
    AddTextCostValue('ИТОГ', FiscalSum);
    WriteStr('PrintCheck', 'FontSize', '0');
    
    AddTextNotNullValue('  Сумма ' + ReadStr('FiscalInfo', 'TaxRate'), NdsSum);
    AddTextNotNullValue('НАЛИЧНЫМИ', ReadFloat('CashSum'));
    AddTextNotNullValue('БАНК. КАРТОЙ', ReadFloat('BankCardSum'));

    AddTextCostValue('ПОЛУЧЕНО', FiscalSum);
    AddLineTextValue('СНО:', ReadStr('FiscalInfo', 'Taxation'));
    if ReadStr('FiscalInfo', 'ClientPhone') <> '' then
      AddLineTextValue('Тел.покупателя', ReadStr('FiscalInfo', 'ClientPhone'));
    if (ReadStr('FiscalInfo', 'ClientPhone') = '') and (ReadStr('FiscalInfo', 'ClientEmail') <> '') then
      AddLineTextValue('Эл.адр.покупателя', ReadStr('FiscalInfo', 'ClientEmail'));
    AddLineTextValue('Пользователь:', ReadStr('FiscalInfo', 'OrganizationName'));
    AddLineTextValue('Адрес:', ReadStr('FiscalInfo', 'OrganizationAddress'));
    AddLineTextValue('Место расчетов:', ReadStr('FiscalInfo', 'OrganizationAddress'));
    AddLineTextValue('Кассир:', ReadStr('FiscalInfo', 'OperatorName'));
    AddLineTextValue('Сайт ФНС:', ReadStr('FiscalInfo', 'FnsUrl'));
    AddLineTextValue('ЗН ККТ:', ReadStr('FiscalInfo', 'SerialNumber'));
    AddLineTextValue('Смена №', IntToStr(ReadInt('FiscalInfo', 'ShiftNumber')));
    AddLineTextValue('Чек №', IntToStr(ReadInt('FiscalInfo', 'CheckNumber')));
    AddLineTextValue('Дата Время', GetDateTimeStr(ReadInt('FiscalInfo', 'CompleteTime')));
    AddLineTextValue('ОФД:', ReadStr('FiscalInfo', 'OfdName'));
    AddLineTextValue('ИНН:', ReadStr('FiscalInfo', 'OfdVatin'));
    AddLineTextValue('РН ККТ:', ReadStr('FiscalInfo', 'RegistrationNumber'));
    AddLineTextValue('ФН №', ReadStr('FiscalInfo', 'FiscalNumber'));
    AddLineTextValue('ФД №', IntToStr(ReadInt('FiscalInfo', 'FiscalDocumentNumber')));
    AddLineTextValue('ФП:', ReadStr('FiscalInfo', 'FiscalDocumentSign'));

    WriteStr('PrintCheck','QR', GetQrCode);
    AddLineBreak;
    AddLineBreak;
  end;
end;

Параметры, которые позволяют учитывать время проезда по маршруту в тарифе (справочник "Тарифы"):

  • Обычный заказ - По городу - Цена минуты далее.
  • Обычный заказ - За городом - Цена минуты далее.
Тариф время плюс км.png

Расчет времени происходит через онлайн карты (в данном примере используется 2GIS).

Нужно выбрать карту для расчета маршрута по заказу («Файл - Настройки - Карта - Онлайн карты - Функционал - Приоритет карт для расчета маршрута по заказу»). Возвращать время маршрута с учетом пробок могут онлайн карты 2GIS и Яндекс.

Ветка Карта - онлайн карты - Функционал.png
Интересная информация

Обратите внимание, для использования онлайн карт 2GIS и Яндекс нужен API-ключ, который указывается в «Файл - Настройки - Карта - Онлайн карты - Настройки - API-ключ для использования онлайн карт».

Пример расчета стоимости поездки с учетом времени проезда по маршруту и километража в разное время.

Пример расчета по тарифу время плюс км.png
Пример второй расчета по тарифу время плюс км.png