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

Материал из TaxiMaster
Перейти к: навигация, поиск

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

Интересная информация
Данный тариф будет корректно работать с версиями Такси-Мастер с 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