EXZOTRON knowledge base
ECOM TTelegram MMAX Вход
Умный инженер Exzotron knowledge
Здравствуйте. Задайте вопрос по оборудованию, настройке или документации Exzotron.

Exzotron СТРОЙ: алгоритмы, интеграции и модель данных

10.2026 · Ответственный компонент: заявки с составом техники и «План выезда», расчёт факта и контроль маршрута, закрытие дня, выгрузка в 1С, отчёт по объектам строительства, REST API и модель данных. Примеры — данные стенда «Тест строительной техники». Статья для интегратора, технического специалиста и службы поддержки. Она описывает, как устроены модель данных и REST API блока Exzotron СТРОЙ, по каким правилам делится груз в «Плане выезда», как строится факт спецтехники и транспорта, как засчитывается проезд по участку дороги, как считается…

Версия ПО: Exzotron Server (сборка от 15.10.2026) · Проверено: 15.10.2026 · Ответственный компонент: заявки с составом техники и «План выезда», расчёт факта и контроль маршрута, закрытие дня, выгрузка в 1С, отчёт по объектам строительства, REST API и модель данных. Примеры — данные стенда «Тест строительной техники».

Статья для интегратора, технического специалиста и службы поддержки. Она описывает, как устроены модель данных и REST API блока Exzotron СТРОЙ, по каким правилам делится груз в «Плане выезда», как строится факт спецтехники и транспорта, как засчитывается проезд по участку дороги, как считается «Перевезено», как формируются закрытие дня, файл для 1С и отчёт по объектам строительства. Обзорное, не техническое описание — в статье «Архитектура и устройство Exzotron СТРОЙ». Общая для транспорта часть (окно расчёта, пороги отрезков, планировщик автонарядов, конструктор маршрута, маршрутизация, геокодер, нормализатор трека) подробно изложена в комплекте Exzotron ТРАНС, в статье «Алгоритмы и интеграции Exzotron ТРАНС (техническое руководство)»; здесь она повторяется только там, где у СТРОЯ есть отличия. Адреса серверов, учётные данные и ключи не приводятся.

Соглашения. Типы техники окна «Наряды»: SPECIAL — закладка «Спецтехника», ROAD — «Автотранспорт», LIGHT_ROAD — «Легковой», AGRICULTURE — «Сельхозтехника» (комплект АГРО). Роли: A — ROLE_ADMIN, V — ROLE_ADVANCED_USER, U — ROLE_USER, C — ROLE_CUSTOMER. Время в API и в расчётах — местное время предприятия (смещение «UTC компании», по умолчанию +3); сырые данные приборов хранятся в UTC. Помета «по аудиту, не проверено в коде» означает, что утверждение взято из аудитов комплекта (_audit/01–03) и при подготовке статьи кодом не перепроверялось; всё остальное сверено с кодом на HEAD fcf23f77 (15.10.2026).

1. Общая схема#

Блок работает поверх общих слоёв Exzotron:

  1. Телематика объекта. Приёмник пишет «сырые» точки в таблицу объекта (ex_d_<imei>): время, координаты, скорость, спутники, показания датчиков. Расчётов приёмник не делает.
  2. Расчётный конвейер датчиков (общий для всего Exzotron) дополняет таблицу производными колонками (зажигание, уровень топлива, пробег, скорость) и событиями заправок и сливов (ex_fuel_level_events). Без включённого конвейера факт нулевой.
  3. Расчёт факта блока (AgroFactService, ExObjectService.getActionSegments) строит из отрезков телеметрии операции наряда и итоги; контроль маршрута (AgroRouteCheck) сверяет план с посещениями геозон. Планировщик (AgroFactScheduler) каждые 15 минут создаёт и досчитывает автонаряды; ручные наряды считаются по событиям.
  4. Диспетчерский слой: заявки, состав, комплекты, «План выезда», наряды, простои, ручной факт, закрытие дня (AgroWorkService, AgroPlanDao, AgroDayCloseService).
  5. Результаты: печатные формы, выгрузка в 1С (AgroExportService), отчёт по объектам строительства (ConstructionReportService, страница report_construction).

Доступ по подразделениям (AgroAccess) применяется в слое 4 и в выгрузке; отчёт его не применяет (раздел 5.14).

2. Модель данных#

2.1. Связи#

Внешних ключей между таблицами блока почти нет (только в миграциях 017, 200, 201: каскад вложенных геозон, привязка видов работ к модели, интервал смены); целостность держится кодом, поэтому «висячие» ссылки возможны.

контрагент (ex_company) ← ex_work_objects.partner_id
ex_work_objects ─ parent_id → ex_work_objects (объект → участок)
ex_work_objects ─ geozone_id → ex_company_geozones (point | line | polygon | group; parent_id — группа)

ex_agro_work_request ─ work_object_id → ex_work_objects
        │ ─ geozone_id (куда), from_geozone_id (откуда) → ex_company_geozones
        │ ─ work_type_id → ex_work_types ─ unit_id → ex_units_of_measure
        │ ─ cargo_nomenclature_id → ex_nomenclature
        │ ─ combo_id → ex_agro_work_combo (только ссылка-запоминание)
        ├── ex_agro_work_request_unit (request_id): состав — brand_model_id → ex_object_brand_models
        ├── ex_agro_work_request_point (request_id): точки маршрута
        └── ex_agro_work_order (request_id, request_unit_id → строка состава)
                 ├── ex_agro_work_order_op (order_id): операции факта
                 ├── ex_agro_work_order_point (order_id): точки маршрута наряда
                 └── ex_agro_work_order_downtime (order_id) → ex_downtime_reasons

ex_agro_work_order ─ object_id, imei → ex_objects;  imei + компания → ex_vehicles (карточка «Автотранспорт»)
ex_agro_work_order ─ employee_id → ex_employee
ex_agro_work_order ─ export_batch_id → ex_agro_export_batch
ex_agro_day_close: по (company_id, day, division_id) блокирует наряды дня

Карточка ex_vehicles связывается с объектом мониторинга только по паре «IMEI + предприятие» (ex_vehicles.imei = ex_objects.imei AND ex_vehicles.ex_users_company = ex_objects.company_id); для «Плана выезда» и колонки «Модель» отчёта нужна именно эта карточка.

2.2. Заявка и состав#

ex_agro_work_request (миграции 191, 195, 196, 197, 215, 236):

Группа Столбцы
Идентификация id, company_id INT, division_id INT, status VARCHAR(20), object_type VARCHAR(20) (195; NULL = сельхоз)
Место geozone_id (место работ / «куда»), from_geozone_id (197), work_object_id (196)
Работа и груз work_type_id, cargo_nomenclature_id, cargo_quantity DOUBLE (197), planned_area_ha DOUBLE (плановый объём в единице вида работ)
Срок required_date DATE, required_time TIME (215), window_from TIME, window_to TIME (236), priority TINYINT (1–3, по умолчанию 2)
План перевозки (215) transport_model VARCHAR(10) (SINGLE/SHUTTLE/MULTI, по умолчанию SINGLE), planned_trips INT, route_km DECIMAL(10,2) (за ходку), planned_km DECIMAL(10,1), round_trip TINYINT(1) (по умолчанию 1)
Прочее external_ref VARCHAR(100) (№ заявки заказчика), vehicle_requirement VARCHAR(150), comment VARCHAR(1000), combo_id (236), shift_id (236, не используется)
Служебные created_at, created_by_user_id NOT NULL, updated_at, updated_by_user_id

Индексы: idx_awr_company, idx_awr_status, idx_awr_geozone, idx_awr_work_object. Столбец planned_area_ha без единицы в названии (исторически «га»): единицу определяет вид работ. У грузовой заявки при пустом значении подставляется количество груза.

ex_agro_work_request_unit — состав заявки (236): id, request_id NOT NULL, seq INT, brand_model_id BIGINT NOT NULL (ex_object_brand_models.id_object_brand_model), qty INT (по умолчанию 1; 1–99), work_type_id, planned_volume DOUBLE («плановый объём на одну единицу техники в единице вида работ»), note VARCHAR(200); индекс idx_awru_request. При правке заявки строки удаляются и создаются заново (новые id).

ex_agro_work_combo (236): id, company_id, name VARCHAR(200), description VARCHAR(500), active TINYINT(1), external_guid VARCHAR(36) (при создании UUID()), created_at, created_by_user_id; ex_agro_work_combo_item: id, combo_id, seq, brand_model_id, qty, work_type_id, note. Столбца объёма на единицу в комплекте нет: введённое в окне комплекта значение теряется.

ex_agro_work_request_point / ex_agro_work_order_point (215): id, request_id / order_id, seq, kind VARCHAR(10) (BASE/LOAD/UNLOAD/STOP/WORK), geozone_id NOT NULL, cargo_quantity DECIMAL(12,3), contact VARCHAR(200). План выезда копирует точки заявки в каждый наряд целиком, включая количества.

2.3. Наряд, операции, простои#

ex_agro_work_order (191, 192, 193, 196–199, 202, 204, 207, 215, 236):

Группа Столбцы
Идентификация id, company_id, division_id, status (NEW/IN_PROGRESS/REVIEW/DONE), source VARCHAR(10) (AUTO/MANUAL, по умолчанию MANUAL), request_id, request_unit_id (236; строка состава — по ней считается охват)
Техника object_id, imei, object_name, object_type VARCHAR(32), trailer_id, employee_id (198), shift_id INT, shift_name
Место, груз geozone_id, from_geozone_id, work_object_id, work_type_id, cargo_nomenclature_id, cargo_quantity
План planned_start, planned_end (204), planned_area_ha, planned_trips, route_km, planned_km, round_trip, transport_model
Окно автонаряда period_start, period_end (местное время); уникальность uq_awo_shift (object_id, shift_id, period_start)
Факт fact_area_ha (объём в единице вида работ: м², м³, т, ч…; для транспорта — «объём»), engine_seconds (193), work_seconds, transfer_seconds, idle_seconds, distance_km, refuel_liters, op_count, calc_at, started_at, completed_at
Ручной факт fact_source VARCHAR(12) (NULL — телеметрия, MANUAL), fact_reason VARCHAR(255), fact_warnings VARCHAR(500)
Контроль маршрута route_status VARCHAR(12) (OK/PARTIAL/NONE/NO_DATA/NULL), trips_done INT, route_note VARCHAR(500)
Выгрузка export_batch_id, exported_at (199)
Служебные note VARCHAR(1000), created_at, created_by_user_id (у автонарядов — 0), updated_at, updated_by_user_id

Индексы: idx_awo_company, _status, _object, _request, _geozone, _work_object, _employee, _export, idx_awo_company_period (company_id, period_start), idx_awo_company_planned (company_id, planned_start) (205), idx_awo_company_imei (company_id, imei, work_type_id) (206), idx_awo_request_unit (236).

ex_agro_work_order_op — операции факта (192, 193): id, order_id, seq, kind (WORK, TRANSFER, VISIT, REFUEL, а также IDLE), start_dt, end_dt, duration_seconds, work_seconds, idle_seconds, engine_seconds, distance_km, avg_speed_kmh, area_ha, refuel_liters, level_before, level_after, zone_id, zone_name, from_zone_name, to_zone_name, no_nav TINYINT. Строки пересоздаются при каждом пересчёте и при ручном факте очищаются. Признака «проезд по участку» (pass) в таблице нет: он существует только в памяти на время расчёта. При удалении наряда его операции остаются (нет внешнего ключа).

ex_agro_work_order_downtime (198): order_id, reason_id, minutes INT, note VARCHAR(500), created_at, created_by_user_id; ex_downtime_reasons: company_id, name VARCHAR(150), code VARCHAR(50), external_guid CHAR(36), is_active, sort_order.

2.4. Закрытие дня и выгрузка#

ex_agro_day_close (203): company_id, division_id (NULL — вся компания), day DATE, closed_at, closed_by_user_id, summary MEDIUMTEXT (JSON-снимок), reopened_at, reopened_by_user_id; индекс (company_id, day, reopened_at). Действующая запись — с reopened_at IS NULL.

ex_agro_export_batch (199): company_id, created_at, created_by_user_id, date_from, date_to, include_exported, order_count, document_count, payload LONGTEXT (сам файл).

2.5. Объекты работ, геозоны, справочники#

ex_work_objects (196, 214): id, company_id, parent_id, name VARCHAR(255) NOT NULL, code VARCHAR(50), address VARCHAR(500), customer VARCHAR(255) (текст), contract VARCHAR(255) (текст), partner_id INT (контрагент, ex_company.id), geozone_id, external_guid CHAR(36), is_active, sort_order, description VARCHAR(500), created_by_user_id, created_at, updated_at. При выборе контрагента customer заполняется его названием; переименование контрагента обновляет customer у его объектов.

ex_company_geozones (017): id, company_id BIGINT, parent_id (внешний ключ на себя, ON DELETE CASCADE), zone_type VARCHAR(32), name VARCHAR(255), color, radius_meters DOUBLE, width_meters DOUBLE, geometry_json JSON (массив [lon, lat]; у точки — одна пара), show_on_map, use_for_address, description. Значения zone_type: point (круг: центр + радиус, подпись «Точка»; значения circle в коде нет), line, polygon, group.

Справочники-основания (создаются не этим блоком): ex_work_types (company_id, name, code, unit_id, is_active, sort_order, external_guid), ex_units_of_measure (name, short_name), ex_nomenclature (грузы; общие записи — company_id IS NULL), ex_object_brand_models (модели техники: id_object_brand_model, ex_users_company_id, name, code, equipment_kind ENUM(BUS,TRUCK,COMBINE,CAR,TRAILER,SPECIAL,AGRI_MACHINE,TRACTOR), tractor_conversion_coef, implement_width_m, load_capacity_t DECIMAL(10,1), is_active, sort_order, external_guid) с привязкой видов работ ex_object_brand_model_work_types (model_id, work_type_id), ex_vehicles (карточка «Автотранспорт»: id_vehicles, imei, ex_users_company, name, license_plate, garage_number, inventory_number, ex_object_brand_model_id, shift_id, driver_employee_id, parking_stop_minutes, external_guid), ex_vehicle_shift_drivers (201; уникальность (vehicle_id, shift_interval_id)), ex_employee (id_employee, id_code — табельный номер, external_guid), ex_shifts и ex_shift_intervals, ex_division_company (external_guid), ex_objects (поля, значимые для блока: object_type по умолчанию ROAD, ex_shift_id, ex_division_id, parking_stop_minutes по умолчанию 5, work_width_m — только для сельхоза, ex_object_brand_model_id, track_normalize (216), last_time).

Создаётся не миграциями: пункт меню «Наряды» (данные меню), ex_user_allowed_divisions, колонки датчиков ex_d_<imei>.

2.6. Перечисления#

Поле Значения
Статус заявки NEW «Новая», PLANNED «Запланирована», DONE «Выполнена», CANCELLED «Отменена»
Статус наряда NEW «Новый», IN_PROGRESS «На исполнении», REVIEW «На проверке», DONE «Завершён»
Источник наряда AUTO — создан по смене, MANUAL — ручной (в том числе из заявки и плана выезда)
Источник факта NULL — телеметрия, MANUAL — введён вручную
Контроль маршрута OK, PARTIAL, NONE, NO_DATA; NULL — плана «откуда → куда» нет
Модель перевозки SINGLE, SHUTTLE («Ходки (челнок)»), MULTI («Развоз на несколько точек»)
Операции WORK, TRANSFER, VISIT, IDLE, REFUEL
Замечания закрытия дня NOT_CLOSED, NO_EXECUTOR, NO_FACT, MANUAL_FACT, SUSPECT_GPS, DUPLICATE, FUEL_DRAIN, ROUTE_NONE, ROUTE_PARTIAL, ROUTE_NODATA
Вид документа в 1С MECHANISM_REPORT (SPECIAL), TRUCK_WAYBILL (ROAD), CAR_WAYBILL (LIGHT_ROAD), AGRO_WAYBILL (прочее)
Тип техники объекта → закладка equipment_kind модели: SPECIAL → SPECIAL; TRUCK, BUS → ROAD; CAR → LIGHT_ROAD; TRACTOR, COMBINE, AGRI_MACHINE → AGRICULTURE; TRAILER — не привязывается (для списка видов работ формы)

2.7. Идентификаторы и GUID#

Для сопоставления с 1С в файл выгрузки уходят ссылки вида {id, guid, code, name}. GUID (external_guid) заводится при первой выгрузке у видов работ предприятия, объектов работ (на которые ссылаются выгружаемые наряды и на которых наряд отработал по рабочим операциям), сотрудников, карточек транспорта, подразделений и причин простоев (UUID()); общие виды работ (company_id IS NULL) не трогаются. Геозоны и грузы GUID не имеют: в файле они передаются как {id, name}. Естественные ключи: табельный номер сотрудника (code), инвентарный и гаражный номер и госномер машины, коды видов работ и объектов работ. Уникальность табельного номера и кодов не проверяется.

3. REST API#

Общие правила: префикс /api/v1; JSON UTF-8; предприятие определяется по сессии (параметра «предприятие» нет); бизнес-ошибки — HTTP 400 {"message": "…"}; всё под /api/** требует входа (сессия веб-входа или HTTP Basic). Даты — ГГГГ-ММ-ДД, дата-время — ГГГГ-ММ-ДД ЧЧ:ММ[:СС]. Часть справочных контроллеров скрыта из Swagger (@ApiIgnore). Все методы доступны ролям A, V, U, если не сказано иное.

3.1. Заявки, состав, комплекты, план выезда — /agro_orders#

Метод и путь Назначение Роли
GET /form_data справочники форм: workTypes, workObjects, cargos, employees, downtimeReasons, objects (с load_capacity_t, driver_employee_id, shift_drivers), divisions, trailers, brandModels, combos (при отсутствии миграции 236 — пусто), company A,V,U
GET /requests?divisionId&dateFrom&dateTo&q журнал заявок; в каждой строке window_from, window_to («ЧЧ:ММ»), combo_id, order_count, order_id (наименьший номер наряда), units[], unit_total, unit_assigned, points[]; поиск q — по месту, виду работ, комментарию, объекту, заказчику, «откуда», грузу A,V,U
GET /requests/last?workObjectId&objectType последняя не отменённая заявка по объекту и типу либо {} («как в прошлый раз») A,V,U
GET /requests/{id} одна заявка; «Заявка не найдена» A,V,U
POST /requests создание: поля заявки + units[{brandModelId, qty, workTypeId, plannedVolume, note}], comboId, windowFrom, windowTo, shiftId; ответ {id} A,V,U
PUT /requests/{id} правка, только в статусе NEW; units не прислан — состав не меняется, пустой список — очищается; windowFrom, windowTo, comboId не присланы — обнуляются A,V,U
PUT /requests/{id}/status {status}: NEW и CANCELLED ставятся в любом состоянии; PLANNED/DONE — только если совпадают с текущим, иначе «Статус «PLANNED» ставится автоматически по наряду» A,V,U
DELETE /requests/{id} удаление вместе со строками состава; ошибка «По заявке уже есть наряд — сначала удалите наряд» A,V,U
POST /requests/{id}/form_order один наряд из заявки (objectId, employeeId, plannedStart, plannedEnd, plannedTrips, routeKm, plannedKm, note); только NEW; request_unit_id не заполняется A,V,U
GET /requests/{id}/plan_candidates подбор техники под строки состава (раздел 4.3) A,V,U
POST /requests/{id}/form_orders «План выезда»: наряды из состава (раздел 4.4) A,V,U
GET /requests/{id}/dispatch_sheet данные печатной разнарядки: {request, company{id,name}, workObject?, orders[]} A,V,U
GET /combos, POST /combos, PUT /combos/{id}, DELETE /combos/{id} комплекты {name, description, items[{brandModelId, qty, workTypeId, note}]}, при правке ещё active (по умолчанию true); удаление физическое A,V,U
POST /construction_template типовой набор справочников стройки (раздел 6.2) A,V

Пример тела POST /requests (идентификаторы условные):

{
  "objectType": "SPECIAL",
  "workObjectId": 51, "geozoneId": 847,
  "requiredDate": "2026-10-17", "windowFrom": "08:00", "windowTo": "20:00",
  "priority": 2, "externalRef": "Д-0417/26-Ф1", "comboId": 3,
  "units": [
    {"brandModelId": 11, "qty": 2, "workTypeId": 7, "plannedVolume": 3000},
    {"brandModelId": 16, "qty": 3, "workTypeId": 2, "plannedVolume": 120},
    {"brandModelId": 13, "qty": 1, "workTypeId": 9, "plannedVolume": 8}
  ]
}

3.2. Наряды, факт, простои — /agro_orders#

Метод и путь Назначение Роли
GET /?divisionId&dateFrom&dateTo&q журнал нарядов: не более 5 000 свежих; в строках drain_liters (слив), points[] A,V,U
GET /{id}, /{id}/ops, /{id}/track, /{id}/fuel карточка (с points, plan_points — посещения точек плана), операции, трек за окно наряда, топливо (только для автонаряда) A,V,U
POST /, PUT /{id} создание / правка наряда; у AUTO правятся только вид работ, агрегат, комментарий, подразделение, исполнитель (затем пересчёт) A,V,U
POST /{id}/recalc пересчёт факта (для ручного факта и закрытого дня — отказ/пропуск) A,V,U
PUT /{id}/status {status}; ручной наряд — не раньше планового дня; затем расчёт факта (для REVIEW/DONE ручного) и пересчёт статуса заявки A,V,U
DELETE /{id} удаление; статус заявки пересчитывается по оставшимся A,V,U
PUT /{id}/fact ручной факт: engineHours, workHours, idleHours, transferHours, factVolume, distanceKm, refuelLiters, tripsDone, reason; ответ {warnings[]} A,V,U
DELETE /{id}/fact вернуть расчёт по телеметрии A,V,U
POST /{id}/split {splitAt, employeeId} — разделить ручной наряд по времени A,V,U
GET/POST /{id}/downtimes, DELETE /{id}/downtimes/{downtimeId} простои {reasonId, minutes, note} A,V,U
GET/POST /downtime_reasons, PUT/DELETE /downtime_reasons/{id} причины простоев A,V,U
POST /backfill, GET /backfill/status «Обработать период» (до 32 дат); запуск — A,V; статус — A,V,U A,V (запуск)
PUT /objects/{objectId}/track_normalize признак нормализации трека на машине A,V,U
POST /route маршрут по дорогам (комплект ТРАНС) A,V,U

3.3. Закрытие дня — /agro_day_close#

Метод и путь Назначение Роли
GET /pending последние 14 суток: {today, notClosed, days[{date, orders, unfinished, closed}]} A,V,U
GET /check?date&divisionId замечания по нарядам дня: closed, orders[{id, machine, executor, workType, status, issues[], warnings, routeNote, overlaps, drain, needsDecision, canMove}], ordersTotal, withIssues, truncated, blocking A,V,U
POST / {date, divisionId, decisions{"<id наряда>": "CLOSE"|"MOVE"}, defaultDecision} (defaultDecision: CLOSE, MOVE, MOVE_OR_CLOSE); ответ {id, summary} A,V,U
POST /range {from, to, divisionId, defaultDecision}; допустимы CLOSE и MOVE_OR_CLOSE; ответ {closed[], errors[]} A,V,U
GET /{id} сводка для печати A,V,U
DELETE /{id} открыть день заново A,V

Для пользователя с несколькими доступными подразделениями check, close и range требуют явного divisionId («Выберите подразделение: у вас доступно несколько»).

3.4. Выгрузка в 1С и печать — /agro_export#

Метод и путь Назначение Роли
GET /preview?dateFrom&dateTo&includeExported {orders, documents, alreadyExported, warnings[]} A,V,U
POST / {dateFrom, dateTo, includeExported} → {id, orders, documents} A,V,U
GET / до 50 последних пакетов {id, created_at, date_from, date_to, include_exported, order_count, document_count, created_by_name} A,V,U
GET /{id}/file файл exzotron-agro-export-<id>.json, application/json A,V,U
GET /print/{orderId} данные печати: документ «машина + исполнитель + дата» из всех нарядов дня в любом статусе (страница agro_print.html?order=) A,V,U

3.5. Отчёт по объектам строительства — /report_construction#

Метод и путь Параметры Ответ Роли
GET /rows dateFrom, dateTo (обязательны, ГГГГ-ММ-ДД), divisionId, workObjectId {company, from, to, rows[], truncated} A,V,U,C

Ошибки: «Укажите начало периода», «Укажите конец периода», «Конец периода раньше начала», «Период не больше года». Метод скрыт из Swagger. Статус наряда на сервере не фильтруется.

3.6. Справочники, геозоны, объекты работ#

Метод и путь Роли Примечание
GET/POST/PUT /work-objects, PUT /work-objects/{id}/link, DELETE /work-objects/{id} A,V,U,C PUT без идентификатора в пути (id — в теле); link — {geozoneId, partnerId, contract}; DELETE → {deleted: true|false} (деактивация, если объект используется)
GET /partner; POST /partner/quick; POST/PUT/DELETE /partner; GET /partner/type чтение A,V,U,C; quick A,V,U; запись A; type A,V quick — {name, itn}: возвращает существующего контрагента (по названию без регистра) или создаёт тип «Покупатели»
GET/POST/PUT/DELETE /work-types, /units-of-measure, /nomenclature A,V,U,C общие записи — только чтение
GET/POST/PUT/DELETE /equipment-brands, GET/PUT /equipment-brands/{id}/work-types A,V,U,C модели техники; {loadCapacityT, equipmentKind, implementWidthM…}, {workTypeIds[]}
GET/POST/PUT/DELETE /employee, /employee/positions чтение A,V,U; запись A,V
GET/POST/PUT/DELETE /vehicle, /vehicle/{id}/shift-drivers чтение A,V,U; запись A,V
GET/POST/PUT/DELETE /shifts чтение A,V,U; запись A сумма смен = 1440 минут
GET/POST/PUT/DELETE /division чтение A,V,U,C; запись A
GET/POST /geozones, PUT/DELETE /geozones/{id}, GET /geozones/export_kml, POST /geozones/import_kml[/preview] любая вошедшая роль (аннотации ролей нет) {type: point|line|polygon|group, name, parentId, color, radiusMeters, widthMeters, geometry, visible, checked, useForAddress, description}; KML-загрузка создаёт только многоугольники
PUT /update_object_name A тип объекта, график смен, «Мин. стоянка», подразделение, модель

4. Алгоритмы заявок и плана выезда#

4.1. Состав заявки: проверки#

При сохранении заявки и комплекта строки состава проходят одну проверку (AgroWorkService.cleanUnits):

  • строка без brandModelId молча отбрасывается;
  • модель должна принадлежать предприятию (ex_object_brand_models.ex_users_company_id = :company; общая модель без предприятия не подходит): «Модель техники в составе заявки не найдена»;
  • qty — целое 1…99, пусто — 1: «Количество техники: допустимо от 0 до 99», «Количество техники в составе — не меньше 1»;
  • вид работ строки — вид работ своего предприятия: «Вид работ в составе заявки не найден»; пусто допускается;
  • plannedVolume — число 0…99 999 999, до двух знаков: «Плановый объём в составе: допустимо от 0 до 99999999»;
  • note — до 200 символов (в интерфейсе поля нет).

Для заявки в целом общий вид работ обязателен, если не у каждой строки состава задан свой вид («Выберите вид работ»); геозона обязательна («Выберите геозону» либо «У объекта работ не задана геозона — выберите геозону»). Состав сервер принимает у заявки любого типа, интерфейс отправляет его только для «Спецтехники» и «Автотранспорта». Серверной проверки окна работы нет (формат, «с < по»).

4.2. Статус заявки по нарядам (syncRequestStatus)#

Вызывается после смены статуса любого наряда заявки (в том числе закрытием дня, которое ставит DONE) и после удаления наряда; создание наряда кнопкой «Новый наряд» (без заявки) и правка наряда статус заявки не меняют.

total = число нарядов заявки; done = число нарядов в DONE; need = Σ qty строк состава (для заявки без состава 0)
total = 0                        → NEW
done = total И total ≥ need      → DONE
иначе                            → PLANNED

«Сформировать наряд» и «План выезда» ставят PLANNED напрямую. Из интерфейса CANCELLED ставится только из NEW, «Вернуть в работу» — только из CANCELLED; API разрешает NEW/CANCELLED в любом состоянии, поэтому «Запланированную» заявку с нарядами можно вручную вернуть в NEW (тогда «Редактировать» станет доступно, а замена состава осиротит request_unit_id нарядов). Охват в ответе заявки: unit_total = Σ qty, unit_assigned = Σ min(qty, число нарядов с request_unit_id = строке); в карточке по каждой строке — «наряды: assigned из qty».

4.3. Подбор техники (GET /requests/{id}/plan_candidates)#

Окно подбора (requestWindow): день — required_date (нет — сегодня по местному времени предприятия), начало — window_from (нет — 00:00), конец — window_to (нет — 23:59) с добавлением :59 секунд. Для каждой строки состава возвращаются все объекты предприятия, для которых карточка ex_vehicles (IMEI + предприятие) имеет ex_object_brand_model_id, равный модели строки, отсортированные по названию. Занятость (busy_count, busy_orders) — число и номера нарядов этой же машины, у которых одновременно: source <> 'AUTO', status <> 'DONE', задано planned_start, planned_start < конец окна и COALESCE(planned_end, planned_start + 12 ч) > начало окна. Тип техники (object_type) не фильтруется; машина без карточки или без модели в карточке в список не попадает, подсказки об этом нет.

{
  "windowStart": "2026-10-17 08:00:00", "windowEnd": "2026-10-17 20:00:59",
  "rows": [{
    "unit": {"id": 101, "request_id": 49, "seq": 1, "brand_model_id": 11, "brand_model_name": "Каток дорожный",
             "qty": 2, "work_type_id": 7, "work_type_name": "Уплотнение асфальтобетона (каток)",
             "unit_name": "м²", "planned_volume": 3000.0, "note": null, "assigned": 0},
    "candidates": [{"id": 3001, "name": "HAMM HD110 2658KB 82 (КАТОК)", "imei": "…", "object_type": "SPECIAL",
                    "busy_count": 0, "busy_orders": null}]
  }]
}

Клиентский «Подобрать автоматически»: сбрасывает выбор и идёт по строкам сверху вниз, для каждой берёт первую по названию машину, не занятую в окне и не выбранную в предыдущих строках; если подходящей нет, строка остаётся «не назначена». Выполняется автоматически при открытии окна. Плановое начало по умолчанию — required_date + window_from (нет окна — 08:00), плановое окончание — required_date + window_to либо пусто. Исполнитель для выбранной машины подставляется клиентом: исполнитель по смене, в которую попадает плановое начало (смена может переходить через полночь), иначе driver_employee_id карточки.

4.4. «План выезда» (POST /requests/{id}/form_orders)#

Тело: {items: [{unitId, objectId, employeeId}], plannedStart, plannedEnd, note}; ответ {ids: […]}. Метод выполняется одной транзакцией (AgroWorkService.formOrdersForPlan): любое исключение откатывает все созданные наряды.

Порядок проверок и действий:

  1. Статус заявки NEW или PLANNED, иначе «Наряды формируются по новой или запланированной заявке».
  2. items не пуст: «Выберите технику хотя бы для одной строки состава».
  3. plannedStart обязателен («Укажите плановое начало»); если задан plannedEnd, он строго позже начала («Плановое окончание должно быть позже планового начала»).
  4. K = Σ qty по всем строкам состава заявки (не по выбранным машинам); shareOf = max(1, K).
  5. Для каждого элемента: unitId обязателен («Не указана строка состава») и принадлежит заявке («Строка состава не найдена в заявке»); без objectId элемент пропускается; повтор objectId внутри запроса — «Одна и та же техника выбрана в плане выезда дважды».
  6. Значения наряда (см. таблицу ниже), assertDayOpen («…создать наряд на эту дату нельзя»), вставка наряда с request_id, request_unit_id, источником MANUAL, статусом NEW; копирование точек маршрута заявки.
  7. Ни одного наряда не создано: «Не выбрана ни одна единица техники».
  8. По каждой строке уже_сформировано + добавляется ≤ qty, иначе «По строке «Модель» нужно N, уже сформировано M, добавляется K».
  9. Заявка → PLANNED; для каждого нового наряда транспорта (ROAD/LIGHT_ROAD) отменяются суточные автонаряды этой машины, пересекающиеся с окном рейса (supersedeAutoOrders).
Поле наряда Правило
cargo_quantity K > 1 и груз заявки задан: round(груз ÷ K, 1) по HALF_UP; иначе весь груз заявки
planned_area_ha 1) planned_volume строки, если задан; 2) иначе, если есть груз заявки и вычислена его доля — доля груза; 3) иначе весь planned_area_ha заявки (не делится)
planned_trips ⌈planned_trips заявки ÷ K⌉; ходки не заданы — пусто
route_km как в заявке (км за ходку)
planned_km если есть ходки наряда и route_km: round(route_km × planned_trips наряда, 1); иначе planned_km заявки
work_type_id вид работ строки, иначе общий вид заявки
object_type берётся у машины, а не заявки: самосвал из заявки «Спецтехника» окажется на закладке «Автотранспорт»
точки маршрута копируются в каждый наряд целиком, включая cargo_quantity точек (не делятся)
planned_start, planned_end общие для всех нарядов плана
division_id, geozone_id, from_geozone_id, work_object_id, cargo_nomenclature_id, transport_model, round_trip как в заявке
note переданное note, иначе комментарий заявки
employee_id employeeId элемента, проверенный на принадлежность предприятию; пусто допустимо (исполнитель подставится при закрытии дня по закреплению смены)

Примеры (по формулам):

Заявка Состав Результат на каждый наряд
груз 100 т самосвал × 3, ходки не заданы 33,3 т (в сумме 99,9 т)
7 ходок самосвал × 3 по 3 ходки (всего 9)
заявка №47 стенда: 600 т, 1 рейс, 222,3 км самосвал × 3 по 200,0 т, 1 ходка, 222,3 км (в разнарядке «Плановый объём 200 т»)
600 т, 5 ходок, «Ходки (челнок)» 3 машины по 200,0 т, 2 ходки; «груз за рейс» 100 т вместо 120 т заявки (раздел 4.8)

Не проверяется сервером: занятость машины другими нарядами; соответствие модели машины строке состава; принадлежность машины типу техники; повторный выбор машины, по которой этой заявке уже сделан наряд в предыдущем запуске плана; окно «с — по» на форму и порядок. Через API можно назначить любую технику предприятия. POST /form_order (один наряд) на заявку с составом переводит её в PLANNED с охватом «0 из N».

Ошибки полей «План выезда» («Одна и та же техника…», «Выберите технику…») ищут на клиенте поле, которого нет в окне, — подсветки нет, сообщение выводится красным внизу окна.

4.5. Печать разнарядки (dispatch_sheet)#

getDispatchSheet возвращает заявку (с составом), предприятие, объект работ (только активный, из списка getWorkObjects; поля customer, contract, address берутся у самого объекта, без наследования от родителя) и наряды заявки (listRequestOrders): номер, статус, техника, госномер, плановые начало и окончание, request_unit_id, исполнитель, вид работ, единица, brand_model_name, номер строки состава. Плановый объём в запросе: cargo_quantity, если задан груз (cargo_nomenclature_id и cargo_quantity не пусты), иначе planned_area_ha — поэтому у нарядов, созданных до введения деления груза и хранящих весь объём заявки, разнарядка показывает груз наряда. Вёрстка (колонки, подписи, пустые строки «не назначено», альбомный A4) — клиентская (agro_request_print.js), по аудиту, не проверено в коде.

5. Факт, маршрут, закрытие дня, 1С, отчёт#

5.1. Окно и общий порядок расчёта#

Окно автонаряда — смена (period_start–period_end); ручного наряда — planned_start–planned_end; без планового окончания — смена машины, в которую попало плановое начало (допускается переход через полночь), иначе календарные сутки (00:00–23:59:59). Окно в будущем не считается (calcEnd = min(end, now); для ручного при calcEnd ≤ start — выход). recalcOrder: (1) замена автонарядов рейсовым (для транспорта); (2) выход, если факт ручной; (3) отрезки getActionSegments; (4) для автонаряда — проверка активности, пустой наряд удаляется; (5) applyFact: buildFact → контроль маршрута → replaceOps → updateOrderFact.

Параметры вызова getActionSegments: флаг «ход в зоне — работа» (fieldMovementIsWork) включён только для AGRICULTURE; флаг «холостой ход — работа» (idleIsWork) — только для SPECIAL. Подробности порогов — в комплекте ТРАНС.

5.2. Спецтехника (SPECIAL)#

  • Моточасы (engine_seconds) — сумма отрезков с работающим двигателем.
  • Работа. Если у объекта есть датчик механизма — отрезки «работа на месте» / «работа в движении». Если датчика механизма нет, но есть датчик зажигания, тип «холостой ход» заменяется на «работа на месте» (idleWork): комментарий кода — отличить копание от холостого хода по телеметрии нечем, поэтому время работы = моточасам. Движение без датчика механизма остаётся «Переездом» (для сельхоза движение в поле считается работой, для спецтехники — нет).
  • Простой — холостой ход, не ставший работой; строка IDLE создаётся при длительности не меньше «Мин. стоянки» объекта (по умолчанию 5 минут); холостой ход внутри операции «Работа» той же геозоны добавляется в её простой. Если датчика механизма нет, а зажигание есть, холостой ход превращается в «работу на месте» (см. выше), и строки IDLE практически не возникают.
  • Переезды — отрезки «Движение»; переезд короче MIN_TRANSFER_KM = 0,1 км удаляется как дрейф GPS (время и путь в итоги не идут).
  • Пробег — сумма путей TRANSFER и WORK в движении.
  • Заправка — операция REFUEL из событий REFUEL датчика уровня (слив DRAIN в операцию не попадает).
  • Площадь/объём не считаются. Ширина захвата передаётся в buildFact только для AGRICULTURE; у SPECIAL widthM = null, fact_area_ha = NULL. Объём вносится только ручным фактом («Выполнено, <ед.>»).
  • Посещения геозон. Операции VISIT у SPECIAL не создаются (тест idleInsideZoneOfTransportIsAVisit: у спецтехники — не VISIT). «Работа» привязывается к геозоне (zone_id); доминирующая зона записывается в geozone_id наряда, если тот пуст. Фильтра «работа только в рабочих зонах» для SPECIAL нет.
  • Подряд идущие операции одного вида и зоны склеиваются (mergeAdjacent) после удаления коротких переездов.

5.3. Транспорт (ROAD, LIGHT_ROAD)#

Операции: TRANSFER («Переезд»), VISIT («Стоянка в геозоне») — стоянка (двигатель заглушён) либо холостой ход с работающим двигателем в геозоне, IDLE («Двигатель на стоянке», только ROAD, вне геозон), REFUEL. Пробег — сумма TRANSFER. После сборки: VISIT без признака pass короче «Мин. стоянки» удаляется; переезд короче 0,1 км удаляется. «Работа» у транспорта всегда 0, fact_area_ha пусто.

5.4. Проезд по линейной зоне (участку дороги), PASS_MIN_KM = 0,3#

Назначение: на участке дороги самосвал едет, а не стоит, поэтому без специального правила рейс «карьер → участок» не подтверждался («была погрузка, но в пункте назначения машина не останавливалась»). Реализация (AgroFactService.applyFact и buildFact):

  1. Для ROAD и LIGHT_ROAD загружаются идентификаторы всех геозон предприятия с zone_type = 'line' (AgroWorkDao.getLineZoneIds, не только зона плана наряда). Для остальных типов набор пуст (passZones = null).
  2. Зона каждого отрезка определяется резолвером геозон (GeozoneShape): для линии точка принадлежит зоне, если расстояние до ломаной не больше width_meters / 2 (по умолчанию ширина 30 м, радиус круга 30 м); у линии меньше двух точек геометрия не используется. При пересекающихся зонах точка относится к первой подходящей по списку зон предприятия (порядок списка — по аудиту, не проверено в коде).
  3. Для отрезка типа «Движение» (а для транспорта и для отрезков «Работа»), зона которого входит в набор линейных, создаётся операция VISIT с pass = true и passKm = километры отрезка; подряд идущие отрезки той же линейной зоны склеиваются (passKm накапливается). Любой отрезок иного типа (стоянка, холостой ход, заправка, нет данных) либо смена зоны закрывает открытое посещение.
  4. В конце расчёта все pass-посещения с passKm < 0,3 удаляются («касание края зоны»). Порог «Мин. стоянки» к ним не применяется.
  5. Оставшиеся pass-посещения в контроле маршрута неотличимы от обычных VISIT: рейс «погрузка → разгрузка» засчитывается, когда после посещения пункта погрузки встречено посещение пункта назначения — в том числе проезд по участку. В route_note проезд описывается обычной формулой «разгрузка N мин (чч:мм–чч:мм)».

Ограничения: (а) признак pass в ex_agro_work_order_op не сохраняется, в интерфейсе строка выглядит как «Стоянка в геозоне», пробег по ней пуст; (б) порог — константа, из интерфейса не настраивается; (в) каждый разрыв непрерывного проезда (другая зона, стоянка) начинает новое посещение, и порог проверяется для каждого отдельно; (г) при недостоверной навигации зона точки не определяется, поэтому проезд не засчитывается; (д) линия, наложенная на площадку погрузки, может «перехватить» точку (первая подходящая зона). Правило не действует для SPECIAL. Подтверждение: тесты passThroughLineZoneCountsAsVisit (без линейных зон — PARTIAL, с зоной — VISIT с pass и OK, 1 рейс) и shortTouchOfLineZoneIsNotVisit (0,1 км — не посещение); стенд — наряды №983, 984, 988 (замечание 15 журнала эмуляции).

5.5. Контроль маршрута#

AgroFactService.applyFact вызывает контроль для ROAD/LIGHT_ROAD и для SPECIAL, если в наряде задана from_geozone_id («возит груз»). Выбор ветки: зон в плане наряда (getOrderPlanZones) больше двух — checkSequence, иначе check(from, to).

check(from, to): нет ни «откуда», ни «куда» — результата нет (NULL); нет активности — NO_DATA, trips = 0; учитываются только операции VISIT с зоной. Посещение «откуда» взводит признак «загружен»; посещение «куда» при загруженном (или при пустом «откуда») даёт рейс и снимает признак; если «откуда» = «куда», каждое посещение — рейс. Статус: при обеих зонах OK при trips > 0, иначе PARTIAL при любом посещении, иначе NONE; при одной зоне — OK при любом посещении. Число плановых ходок в статус не входит. checkSequence: рейс — прохождение всех зон плана по порядку; лишние посещения игнорируются; затем счёт начинается заново.

Недостоверная навигация: доля длительности операций с no_nav не меньше NO_NAV_SHARE = 0,3 — NONE превращается в NO_DATA (текст «навигация недостоверна на N% времени (подмена GPS)…»), к PARTIAL добавляется оговорка, OK не меняется. Тексты route_note и остальные детали — комплект ТРАНС.

Расхождение, спецтехника «возит груз». Контроль вызывается, но VISIT у SPECIAL не создаются: статус будет NONE или NO_DATA, и замечание ROUTE_NONE потребует решения при закрытии дня. На стенде не проверялось; для самосвалов используйте закладку «Автотранспорт».

5.6. Ручной факт#

PUT /{id}/fact (AgroWorkService.setManualFact, AgroWorkDao.setManualFact): только для source <> 'AUTO' («Факт автоматического наряда считается по телеметрии»); день не закрыт. Причина обязательна, до 255 символов («Укажите причину ввода факта вручную (например, «прибор неисправен»)»). Часы (engineHours, workHours, idleHours, transferHours) — 0…24, переводятся в секунды; factVolume, distanceKm, refuelLiters — неотрицательные числа; ошибки: «{Поле}: некорректное число», «{Поле}: значение не может быть отрицательным», «{Поле}: за сутки не может быть больше 24 часов». Запись: значения записываются в факт, fact_source = 'MANUAL', fact_reason, fact_warnings, calc_at = NOW(), операции удаляются. Рейсы: trips_done = COALESCE(:trips, trips_done); route_status = 'OK' только при trips > 0, иначе без изменений; route_note = 'Рейсы введены диспетчером по путевому листу', если поле заполнено (в том числе 0). Рейсы округляются до целого.

Предупреждения правдоподобия (без запрета; в fact_warnings до 500 символов): моточасы больше длительности окна; работа больше моточасов; работа + простой + переезды > моточасы + 6 мин. recalcOrder для fact_source = MANUAL выходит сразу, кнопка «Пересчитать» в интерфейсе неактивна. DELETE /{id}/fact снимает fact_source, fact_reason, fact_warnings и пересчитывает факт; ручные route_status, trips_done, route_note затирает только этот пересчёт.

5.7. Статусы наряда и их побочные эффекты#

Переходы между любыми статусами разрешены. started_at ставится один раз при IN_PROGRESS; completed_at — при DONE, любой иной статус его обнуляет. Для ручного наряда в REVIEW/DONE выполняется расчёт факта (сбой расчёта не срывает смену статуса), затем пересчитывается статус заявки. Ручной наряд с плановой датой в будущем не принимается: «Наряд запланирован на ДД.ММ.ГГГГ — завершить его до наступления даты нельзя. Измените плановое начало или дождитесь даты.» Автонаряды не ограничены. Изменение наряда закрытого дня — «День ДД.ММ.ГГГГ закрыт — наряд изменить нельзя. Руководитель может открыть день в окне «Закрыть день».» При удалении наряда его простои и точки удаляются, операции остаются.

5.8. «Перевезено» и груз за рейс#

Весов на машине нет: «перевезено» — расчётная величина «груз за рейс × число рейсов».

Место Формула Условия
Груз за рейс (все места) SINGLE: cargo_quantity наряда; SHUTTLE и MULTI при planned_trips > 0: cargo_quantity ÷ planned_trips груз задан
Карточка наряда («Перевезено (груз за рейс × рейсы)») груз за рейс × trips_done route_status = OK, trips_done > 0, задана единица груза; единицы груза и вида работ не сверяются; блок показывается только при расчёте по телеметрии (при ручном факте контроль маршрута в карточке не выводится)
Файл 1С (AgroExportService.routeFactVolume) round2(груз за рейс × trips_done), источник factVolumeSource = ROUTE_CONTROL route_status = OK, fact_source ≠ MANUAL, trips_done > 0, единица вида работ = единице груза (сравнение без регистра, после обрезки пробелов); иначе null и дальше factVolume по правилам 5.11
Отчёт по объектам строительства груз за рейс × trips, trips = trips_done > 0 только транспорт (по правилу 5.13); и для ручных рейсов; единицы не сверяются

Тест routeFactVolumeDividesShuttleCargoByPlannedTrips: 120 т, 2 ходки, 1 рейс → 60 т; SINGLE → 120 т. На стенде наряд №983: груз 120,0 т, «Ходки (челнок)», 2 ходки, всего 231,2 км (115,6 км за ходку), 1 подтверждённый рейс → «Перевезено» 60,0 т.

Следствие деления плана выезда. груз за рейс наряда = (груз ÷ K) ÷ ⌈ходки ÷ K⌉, что при неделимых ходках отличается от груз ÷ ходки заявки (600 т, 5 ходок, 3 машины: 200 т ÷ 2 ходки = 100 т за рейс вместо 120 т). Рекомендация: назначать число ходок кратным числу машин либо проверять «План перевозки» в карточке наряда.

Наряды, созданные до деления груза (№983–991, 992–994 на стенде), хранят planned_area_ha всей заявки; разнарядка, карточка и отчёт для грузового наряда берут cargo_quantity.

Расхождение отчёта и 1С при ручных рейсах. Отчёт посчитает груз × введённые рейсы; в файле 1С при ручном факте factVolume = введённое «Выполнено», а «перевезено» не вычисляется. Для 1С нужно вводить и «Рейсов по путевому листу», и «Выполнено, т».

5.9. Закрытие дня#

AgroDayCloseService; день определяется датой period_start (автонаряд) либо planned_start (ручной): period_start ≥ :d AND period_start < :d + 1 день, либо period_start IS NULL и то же по planned_start.

Проверка (check). Для каждого наряда дня собираются коды замечаний (issueCodes): NOT_CLOSED (статус не DONE); NO_EXECUTOR (нет employee_id и нет исполнителя по закреплению смены); MANUAL_FACT (факт ручной) либо NO_FACT (расчёта нет либо engine_seconds = 0 и work_seconds = 0); SUSPECT_GPS (не ручной факт, distance_km > 1, расчёт есть, engine_seconds = 0); ROUTE_NONE/ROUTE_PARTIAL/ROUTE_NODATA по сохранённому route_status — независимо от ручного факта; DUPLICATE (пересечение окон нарядов одной машины); FUEL_DRAIN (сливы по датчику уровня в окне наряда). Информационные (решение не требуется): MANUAL_FACT, SUSPECT_GPS, FUEL_DRAIN, ROUTE_PARTIAL, ROUTE_NODATA. Наряды без замечаний в список не попадают; список ограничен 300 нарядами (DISPLAY_LIMIT), остальные закрываются общим решением. Слив: событие DRAIN относится к наряду, если время его начала (с учётом смещения предприятия) попадает в [начало окна; конец окна); текст «слив топлива N,N л (k раз) — проверьте расход».

Закрытие (close), одна транзакция: (1) дата не позже сегодняшней по местному времени («День ещё не наступил…»); день не закрыт («День уже закрыт»); (2) подстановка исполнителя по закреплению смены в наряды дня без исполнителя; (3) нарядов нет — «В этом дне нет нарядов — закрывать нечего»; (4) для каждого наряда с требующим решения замечанием берётся решение из decisions[id], а при его отсутствии — общее defaultDecision: MOVE_OR_CLOSE → MOVE для ручного не завершённого наряда, иначе CLOSE; строгий MOVE оставляет решение пустым там, где перенос невозможен; нет решения — «Нет решения по нарядам: №…»; (5) MOVE допустим только для source <> 'AUTO' и status <> 'DONE' («Наряд №N нельзя перенести: автоматический или уже завершён»): плановые начало и окончание сдвигаются на +1 сутки; (6) CLOSE ставит статус DONE через setOrderStatus (у ручного — расчёт факта; заявка пересчитывается); (7) формируется сводка по нарядам, кроме перенесённых, и сохраняется в summary JSON; (8) запись закрытия блокирует правки.

Сводка (summary): company, date, division, totals {orders, engineHours, workHours, idleHours, transferHours, distanceKm, refuelLiters, downtimeMinutes, manualFacts}, machines[] (по паре «машина|исполнитель»), downtimes[] по причинам, manualFacts[], suspicious[] (сливы, маршрут, подозрительный пробег), exceptions[] (закрыто как есть), moved[]. В печатной форме сводки нет колонок пробега, рейсов и «Перевезено» (по аудиту).

Блокировка. isLocked(company, day, division): есть запись закрытия с reopened_at IS NULL и (division_id IS NULL или division_id <=> division наряда); закрытие всей компании блокирует все подразделения, закрытие подразделения — только его. Блокируются правка, пересчёт, смена статуса, удаление, ручной факт, простои, разделение, создание наряда (в том числе из заявки и плана выезда) и перенос на закрытую дату.

Диапазон (range). Допустимы CLOSE и MOVE_OR_CLOSE; конец ограничивается вчерашним днём; не более 32 дат («За один раз можно закрыть не больше 31 дня»); день без нарядов или уже закрытый пропускается; каждый день — своя транзакция, ошибка дня попадает в errors. Счётчик pending: последние DAYS_BACK = 14 суток, дни с нарядами и признак «закрыт» (закрытие по компании либо по доступному подразделению). Открыть заново (DELETE /{id}, A/V): ставит reopened_at и reopened_by_user_id; статусы нарядов не возвращаются; повтор — «День не найден или уже открыт». Сводка устаревшего закрытия помечается «день затем открыт заново».

5.10. Топливо в наряде#

Операции REFUEL строятся только из событий REFUEL (loadMechanismRefuels(…, includeDrains = false)); слив определяется отдельным запросом событий DRAIN по предприятию (AgroDayCloseDao.listDrains) — для красной строки в колонке «Заправка, л» журнала и для замечания FUEL_DRAIN. Вкладка «Топливо» (GET /{id}/fuel) строится только для автонарядов: нужен period_start. Удаление ложного события в «Отчёте по расходу топлива» (POST /report_fuel_consumption/delete_event?eventId=, только A) физически удаляет строку события (DELETE … WHERE id AND company_id); на закрытой сводке дня не отражается (снимок), слив из журнала и замечания исчезает сразу, а сохранённые в наряде операции «Заправка» и итог — только после пересчёта наряда (для закрытого дня — после «Открыть день заново»). Журнала удаления нет.

5.11. Выгрузка в 1С#

Условия попадания (AgroExportDao.listDoneOrders): status = 'DONE', дата документа DATE(COALESCE(period_start, planned_start, started_at)) входит в период включительно (разность дат не более 93, фактически до 94 календарных дат), по умолчанию export_batch_id IS NULL (флажок includeExported берёт и выгруженные и переносит их в новый пакет); для ограниченного пользователя — только его подразделения (filterRows). Пусто — «За выбранный период нет принятых нарядов для выгрузки». Ошибки периода: «Укажите период выгрузки», «Дата «по» раньше даты «с»», «Период выгрузки не больше 93 дней», «Неверный формат даты (ожидается ГГГГ-ММ-ДД)».

Предпросмотр (preview) добавляет предупреждения (выгрузка не блокируется — 1С получит пустые ссылки): «Наряд №N: не назначен исполнитель», «Наряд №N: не указан вид работ» (для LIGHT_ROAD не выдаётся), «Наряд №N: техника «…» не заведена в справочнике транспорта».

Файл — JSON, схема exzotron.agro.export, schemaVersion: 1, имя exzotron-agro-export-<id>.json, хранится целиком в payload; повторная загрузка отдаёт тот же файл. Перед построением заводятся GUID (раздел 2.7). Документ = дата + вид документа + машина + исполнитель (ключ группировки doc_date|kind|v<vehicle_id>|employee_id, а при отсутствии карточки машины — i<imei>).

Уровень Поля
Корень schema, schemaVersion, company {id, name}, period {from, to}, documents[]
Документ number (порядок в файле), kind, date, vehicle {id, guid, inventoryNumber, licensePlate, garageNumber, name}, employee {id, guid, code (табельный), name}, division {id, guid, code, name}, rows[], totals {orders, engineHours, workHours, idleHours, distanceKm, refuelLiters, downtimeMinutes}
Строка (наряд) orderId, source, shift, periodStart, periodEnd, workType {id, guid, code, name}, workObject {id, guid, code, name}, geozone {id, guid: null, code: null, name}, fromGeozone (так же), cargo {id, name, quantity, unit}, hours {engine, work, idle, transfer} (null, пока факт не рассчитан, а не 0), unit, planVolume, factVolume, factVolumeSource, factSource, factReason, routeCheck {status, tripsDone, note} (без плана — null), areaHa, fields[], distanceKm, refuelLiters, downtimes[{reason{id,guid,code,name}, minutes, note}], note

Правила значений строки:

  • periodEnd: автонаряд — конец смены; ручной — плановое окончание, иначе конец смены машины, в которую попадает плановое начало, иначе начало следующих суток. Момент «Завершён» не используется.
  • planVolume = planned_area_ha (у грузового наряда без заданного значения равен количеству груза).
  • factVolume: если routeFactVolume не null — он (factVolumeSource = "ROUTE_CONTROL"); иначе при ручном факте с заполненным fact_area_ha — введённое «Выполнено» (factVolumeSource не выставляется); иначе по единице вида работ: «га» — fact_area_ha; «ч», «час», «моточас», «мч» — часы работы (work_seconds ÷ 3600); «км» — пробег; единицы «м²», «м³», «т» по телеметрии дают null (следствие того, что объём спецтехники по телеметрии не считается).
  • factSource: MANUAL; NO_DATA — расчёт пуст (моточасы, работа, пробег и заправка нулевые; по самому наряду нельзя отличить «нет данных» от «не работала»); TELEMETRY.
  • fields[] — рабочие операции WORK с зоной, сгруппированные по геозоне: у SPECIAL с «Работой» в геозоне массив заполняется (areaHa = null, workHours, distanceKm, transferKm, transferHours), у транспорта пуст.
  • areaHa при ручном факте содержит «Выполнено» в единице вида работ, а не гектары.

Иллюстративная строка (значения условные; часть взята из наряда №983 стенда):

{
  "orderId": 983, "source": "MANUAL", "shift": null,
  "periodStart": "2026-10-14 07:00:00", "periodEnd": "2026-10-15 00:00:00",
  "workType": {"id": 2, "guid": "…", "code": null, "name": "Перевозка щебня и инертных материалов"},
  "workObject": {"id": 52, "guid": "…", "code": null, "name": "Подъездная дорога к логопарку, Новожиловка"},
  "geozone": {"id": 847, "guid": null, "code": null, "name": "Подъездная дорога к логопарку, Новожиловка"},
  "fromGeozone": {"id": 840, "guid": null, "code": null, "name": "Чернопольское 1"},
  "cargo": {"id": 12, "name": "Щебень фр. 20–40", "quantity": 120.0, "unit": "т"},
  "hours": {"engine": 6.57, "work": 0.0, "idle": 0.0, "transfer": 2.82},
  "unit": "т", "planVolume": 120.0,
  "factVolume": 60.0, "factVolumeSource": "ROUTE_CONTROL",
  "factSource": "TELEMETRY", "factReason": null,
  "routeCheck": {"status": "OK", "tripsDone": 1, "note": "погрузка 6 мин (10:10–10:16), разгрузка 1 мин (15:49–15:50)"},
  "areaHa": null, "distanceKm": 97.7, "refuelLiters": 171.6,
  "fields": [], "downtimes": [], "note": null
}

Загрузка файла в 1С при подготовке статьи не проверялась; документ EXPORT_1C_SCHEMA.md описывает схему неполно (нет factSource, factReason, factVolumeSource, routeCheck) — ориентируйтесь на настоящую статью. Сети, обратной загрузки, статуса «принято в 1С» и расценок нет.

5.12. Печатные формы наряда#

GET /agro_export/print/{orderId}: документ собирается по принципу «машина + исполнитель + дата» из всех нарядов этого дня в любом статусе (наряд-основание всегда включён: совпадают imei и employee_id, дата документа совпадает с датой наряда-основания); структура та же, что в файле выгрузки; номер условный. Формы (по аудиту, клиентский код печати не перечитывался): SPECIAL — «Рапорт о работе строительной машины (механизма)» (моточасы, работа, простой, «Выполнено» план/факт, заправка; пробега нет); ROAD — «Путевой лист грузового автомобиля» (откуда, куда, груз, контроль маршрута с числом рейсов, моточасы, пробег, заправка; без «Перевезено» и плановых ходок). Печатная сводка дня — страница agro_day_print.html.

5.13. Отчёт по объектам строительства#

Построение. Сервер (ConstructionReportDao.findRows) возвращает одну строку на наряд предприятия, дата наряда COALESCE(period_start, planned_start) которого лежит в [from; to + 1 день), с LIMIT 20000, сортировкой по дате и id; при divisionId — o.division_id = :id; при workObjectId — o.work_object_id = :id OR wo.parent_id = :id (объект и его прямые потомки). Соединения: объект мониторинга (госномер), карточка ex_vehicles (по IMEI и предприятию) → модель техники, исполнитель, подразделение, объект работ и его родитель, геозоны «куда»/«откуда», вид работ и его единица, груз и его единица. Поля строки: orderId, day, status, source, factSource, routeStatus, objectType, requestId, vehicleName, licensePlate, brandModelName, employeeName, divisionId, divisionName, workObjectId, workObjectName, workObjectParentId, workObjectParentName, customer, contract, geozoneName, fromGeozoneName, workTypeName, unitName, cargoName, cargoUnit, cargoQuantity, plannedVolume, factVolume, workSeconds, engineSeconds, idleSeconds, transferSeconds, distanceKm, refuelL, plannedTrips, tripsDone, plannedKm, routeKm, roundTrip, transportModel. truncated = (число строк ≥ 20000). Заказчик и договор берутся у самого объекта наряда (wo.customer, wo.contract), без наследования.

Сервер проверяет: даты заданы, to ≥ from, to − from ≤ 366 дней (то есть допустимо до 367 календарных дат включительно; сообщение «Период не больше года»). Статус на сервере не фильтруется, сортировка по возрастанию даты означает, что при превышении предела обрезаются самые поздние наряды периода.

Расчёт на клиенте (report_construction.js, prepare):

Показатель Правило
«Принятый» наряд status ∈ {REVIEW, DONE}
Транспорт objectType ∈ {ROAD, LIGHT_ROAD} либо задана геозона «откуда» либо задан груз; иначе спецтехника
Моточасы / работа / простой engine_seconds, work_seconds, idle_seconds ÷ 3600; простои с причинами в отчёте не используются
Пробег, заправлено distance_km, refuel_liters
Рейсы только у транспорта: trips_done, если > 0 (включая введённые вручную)
Груз за рейс SHUTTLE/MULTI при planned_trips > 0: груз ÷ ходки; иначе груз наряда
Перевезено груз за рейс × рейсы
т·км перевезено × гружёное плечо; плечо = route_km при round_trip 0/false, иначе route_km ÷ 2 (в том числе при NULL); только если единица груза — «т» (с учётом «т.»)
Объём спецтехники fact_area_ha (в единице вида работ); по телеметрии не считается, поэтому берётся из ручного факта
Плановый объём транспорт: груз наряда, если задан; иначе и для спецтехники — planned_area_ha
Суммы разных единиц не складываются: отдельными строками (5 600 м², 120 м³)

Вкладки (7): «Сводка по объектам» (7 плиток, «Сводка по объектам затрат» с итогом, «Объекты и виды работ»; предупреждение об обрезке «Показаны не все наряды периода (предел выдачи) — сузьте период или выберите объект.» — только здесь и только при наличии принятых нарядов); «Перевозки» (маршруты откуда|куда|груз|единица, плечо = т·км ÷ тонны); «Техника» (загрузка — полоса относительно машины с максимальными моточасами, не норматив); «По дням» (четыре матрицы «дата × объект»: моточасы спецтехники, рейсы, пробег транспорта, заправлено); «План / заявка и факт» (по всем нарядам периода: план — сумма по всем нарядам группы, факт — по принятым; сравнение идёт по нарядам, а не по заявкам: заявка без наряда не видна); «Конструктор» (PivotTable 2.23.0 с jQuery 1.12.4 и jQuery UI 1.12.1 из публичного CDN; по умолчанию строки «Объект строительства», «Вид работ», столбцы «Вид», значение «Моточасы», агрегатор «Сумма»; агрегаторы «Сумма», «Количество», «Среднее», «Минимум», «Максимум»; русской локализации оболочки нет); «Ведомость по объекту» (отдельный лист на каждый объект, разрыв страницы после объекта; подписи «Диспетчер», «Руководитель работ», «Представитель заказчика»; колонка «Факт» помечает «вручную»).

Поля конструктора — 29 (toPivotRow): 19 справочных — «Организация», «Объект строительства», «Заказчик», «Договор», «Вид» («Транспорт»/«Спецтехника»), «Вид работ», «Техника», «Модель техники», «Исполнитель», «Подразделение», «Дата», «Месяц», «Статус наряда», «Факт» («вручную»/«по телеметрии»), «Откуда», «Куда / место», «Груз», «Заявка», «Единица объёма»; 10 мерных — «Объём работ», «Плановый объём», «Моточасы», «Часы работы», «Простой, ч», «Пробег, км», «Рейсов», «Перевезено», «т·км», «Заправлено, л». Мерные поля у непринятых нарядов равны null (кроме «Планового объёма»); пустые текстовые заменяются на «—». Аудит и бриф называют 28 полей: по коду их 29.

Печать и Excel. Печать — window.print() с A4 альбомная и полями 10 мм (вкладки и панель скрыты, заголовки таблиц повторяются). Excel — кнопка формирует HTML-файл с типом application/vnd.ms-excel и именем construction_<вкладка>.xls из всех таблиц активной вкладки (summary, haul, vehicles, days, plan, pivot, sheet); плитки не экспортируются; это не настоящий XLSX.

Отчёт против файла 1С (схема 5 клиентской статьи): отчёт берёт REVIEW и DONE, 1С — только DONE; «перевезено» в 1С требует route_status = OK и совпадения единиц, в отчёте — нет; для ручных рейсов отчёт считает груз × рейсы, 1С — введённое «Выполнено». Фильтр «наряды без объекта» применяется на клиенте.

5.14. Доступ по подразделениям#

AgroAccess читает права пользователя из БД с кэшем 10 секунд: список ex_user_allowed_divisions плюс «Зафиксировать подразделение» (homeDivisionId). Правила: пустой набор — без ограничений; запись без подразделения ограниченному недоступна; единственное доступное подразделение подставляется автоматически, чужое — «Нет доступа к другому подразделению»; при нескольких доступных и пустом запросе журнал фильтруется по набору, а действия над целым днём требуют явного выбора («Выберите подразделение: у вас доступно несколько»); assertRow — «Нет доступа: запись относится к другому подразделению». Применяется в журналах заявок и нарядов, карточках, закрытии дня, выгрузке (предпросмотр, формирование, печать). Не применяется: отчёт /report_construction/rows (получает наряды всех подразделений предприятия), справочники (объекты работ, геозоны, комплекты, карточки техники), список пакетов выгрузки, сводка закрытого дня по номеру (по аудиту).

6. Права, справочники, типовой набор#

6.1. Роли#

Возможность A V U C
Заявки, комплекты, «План выезда», разнарядка, наряды, факт, простои, закрытие дня, выгрузка в 1С да да да нет
«Открыть день заново», «Обработать период», типовой набор для стройки да да нет нет
Отчёт по объектам строительства (метод API) да да да да (но справочники параметров /agro_orders/form_data для C закрыты)
Запись: объекты работ, виды работ, единицы, модели техники, номенклатура да да да да
Запись: сотрудники, карточки «Автотранспорт» да да нет нет
Запись: графики смен, подразделения, контрагенты (правка/удаление) да нет нет нет
Геозоны да да да да (аннотации ролей у контроллера нет)
Удаление события датчика уровня топлива да нет нет нет
Тип объекта, график смен, свойства объекта да нет нет нет

Кнопки «Открыть день заново», «Обработать период» и «Типовой набор» видны всем ролям окна; для U результат — сообщение об ошибке после нажатия (точный текст отказа под U не проверялся). Пункт меню «Наряды» — данные меню, а не миграция; кнопка «Отчеты» с пунктом отчёта доступна на странице «Объекты» (showObjectReportsButton), в окне «Наряды» её нет.

6.2. Типовой набор для стройки#

POST /agro_orders/construction_template (A, V; идемпотентно: добавляется только отсутствующее по названию или коду): единицы — «Кубический метр» (м³), «Квадратный метр» (м²), «Тонна» (т), «Час» (ч); виды работ (8, с кодом и единицей) — «Работа экскаватора» (EXCAV, ч), «Разработка и погрузка грунта» (GRUNT, м³), «Планировка площадки» (PLAN, м²), «Работа бульдозера» (BULL, ч), «Работа фронтального погрузчика» (LOADER, ч), «Работа автокрана» (CRANE, ч), «Перевозка грунта и материалов» (HAUL, т), «Переезд (перебазировка) техники» (RELOC, ч); причины простоев (8) — WAIT_FUEL, WAIT_LOAD, WAIT_FRONT, WAIT_MATERIAL, WEATHER, BREAKDOWN, CLIENT, BREAK; модели (5) — «Экскаватор» (EXCAVATOR), «Бульдозер» (BULLDOZER), «Фронтальный погрузчик» (WHEEL_LOADER), «Автокран» (MOBILE_CRANE) — все SPECIAL, и «Самосвал» (DUMP_TRUCK) — TRUCK, с привязкой видов работ только для моделей, созданных этой загрузкой. Ответ: {models, modelWorkTypeLinks, units, workTypes, downtimeReasons}. В набор не входят дорожные катки, асфальтоукладчик, виды работ «Укладка асфальта» и «Уплотнение…», грузы, объекты работ, геозоны, комплекты, сотрудники: на стенде они заведены вручную (12 видов работ, 4 комплекта).

7. Конфигурация#

Свойство / механизм По умолчанию Назначение и оговорки
scheduler.enabled false включает все @Scheduled, в том числе автонаряды (AgroFactScheduler: старт через 240 000 мс, период 900 000 мс). Включать только на одном процессе из пары
calc-pipeline.enabled / workers / batch-size / lease-minutes / profile-cache-ttl-ms true / 8 / 25 / 5 / 300 000 расчётный конвейер датчиков; без него факта нет
route.detour.factor и свойства маршрутизации, геокодера см. комплект ТРАНС в этой статье не повторяются
Окно автосоздания LOOKBACK_HOURS = 36, принудительный пересчёт окна раз в час старше — только «Обработка периода»
Лимиты кода журнал нарядов 5 000, отчёт 20 000, окно закрытия 300, диапазон 32 даты, выгрузка 93 дня, список пакетов 50, точки маршрута 12, состав 1–99, ходки до 999 константы кода, из интерфейса не настраиваются
Пороги расчёта MIN_TRANSFER_KM = 0,1; PASS_MIN_KM = 0,3; MIN_ACTIVE_KM = 0,3; ACTIVITY_MIN_SECONDS = 600; NO_NAV_SHARE = 0,3; SUSPECT_GPS_KM = 1,0 константы кода

Версии статических файлов окна и отчёта меняются при выкате; браузерный кэш окна — сутки, после обновления версии нужна перезагрузка страницы.

8. Миграции и версии#

Все файлы подключены в db.changelog-master.xml (проверка — тест MigrationChangelogTest); миграции идемпотентны (MARK_RAN, предусловия по information_schema). Номер 194 пропущен (файла нет).

№ Файл Содержание Нужна стройке
017 017_company_geozones.sql геозоны: zone_type, radius_meters, width_meters, geometry_json, группы да (линия-участок)
191 191_agro_work_orders.sql заявка и наряд да
192 192_agro_fact_orders.sql source, окна смен, итоги, операции; ex_objects.work_width_m да
193 193_agro_order_engine_seconds.sql моточасы да
195 195_agro_request_object_type.sql object_type заявки да
196 196_work_objects.sql объекты работ, work_object_id в заявке и наряде да
197 197_agro_route_cargo.sql «откуда», груз, количество да
198 198_agro_executor_downtime.sql исполнитель, причины простоев, простои да
199 199_agro_export_1c.sql пакеты выгрузки, GUID да
200 200_merge_equipment_brands_into_models.sql модели техники: load_capacity_t, equipment_kind, виды работ модели да
201 201_vehicle_shift_drivers.sql исполнители по сменам да
202 202_agro_order_manual_fact.sql ручной факт да
203 203_agro_day_close.sql закрытие дня да
204 204_agro_order_planned_end.sql плановое окончание, предупреждения ручного факта да
205–206 индексы журнал, вид работ по умолчанию, поиск автомобиля да
207 207_agro_order_route_check.sql route_status, trips_done, route_note да
213 213_fuel_level_sensor_double.sql дробные литры уровня топливо
214 214_work_object_partner.sql ex_work_objects.partner_id да
215 215_agro_plan_trips_km.sql ходки, км, модель перевозки, точки, время подачи да
216 216_object_track_normalize.sql признак нормализации на машине да
236 236_request_equipment_plan.sql 5 changeset: 236-01 комплекты, 236-02 строки комплектов, 236-03 окно и combo_id в заявке, 236-04 состав заявки, 236-05 request_unit_id в наряде да (состав, план выезда, охват)

Миграции 190, 208–212 (датчик механизма, технологический план, календарь культур), 217–235 (рассылки, прайс, партнёрский кабинет, карта) к блоку не относятся. Последняя в каталоге — 236 (db.changelog-master.xml, строка 242 по аудиту).

Версии блока (коммиты 15.10.2026, master): aacfda51 — заявка на спецтехнику v2: состав, окно, комплекты, «План выезда», миграция 236; a6830c7e — печать разнарядки; e2b72a69 — состав и план выезда у автотранспорта (деление груза, ходок и км); fd2619ba — отчёт по объектам строительства и поле «Рейсов по путевому листу»; bb81a990 — груз за рейс по ходкам и план транспорта из груза наряда в отчёте; 429adb91 — прочерки в пустых ячейках отчёта, разнарядка без пустых полей шапки; bfa5df13 — проезд по линейной зоне засчитывается посещением; d4617d61 — груз за рейс у «ходок» в карточке и в выгрузке; HEAD fcf23f77. Правки печати путевого листа грузового автомобиля и подсветки линии в окне «Подбор геозоны» подготовлены и ждут выката.

9. Тесты#

Покрытие — только синтетические данные; тесты в этой работе не запускались (описано по исходникам).

Класс Тестов Что проверяет
AgroFactServiceTest 14 окна смен; пропуск пересчёта без новых данных; фильтр рабочих зон и склейка для сельхоза; холостой ход транспорта в геозоне = посещение (у SPECIAL — нет); замена автонаряда рейсовым; passThroughLineZoneCountsAsVisit, shortTouchOfLineZoneIsNotVisit
AgroRouteCheckTest 13 нет плана, один и два рейса, частично, ничего, недостоверная навигация, нет данных, одна зона, склад → склад
RouteOptimizerTest 8 порядок точек развоза, checkSequence
AgroExportServiceTest 6 routeFactVolume (в том числе деление груза «ходок» на плановые ходки), factSourceOf, отнесение переездов
AgroDayCloseServiceTest 20 коды замечаний, решения (CLOSE, MOVE, MOVE_OR_CLOSE), общие решения, пересечение окон, диапазон, будущий день, блокировка закрытого дня
AgroWorkServiceTest 4 предупреждения правдоподобия ручного факта
AgroAccessTest 6 ограничения по подразделениям
MigrationChangelogTest 1 каждый файл миграции подключён в основной файл

Не покрыто тестами: состав и комплекты, подбор техники, деление в «Плане выезда» (формулы раздела 4.4), syncRequestStatus, печать разнарядки, поле tripsDone в SQL ручного факта, GeozoneShape для линий на реальной геометрии, haulingSpecial, отчёт по объектам строительства, удаление события топлива, интерфейс и проверки ролей. Подтверждение этих функций — живые прогоны на стенде.

10. Известные ограничения и расхождения#

Ограничения продукта.

  1. Объём спецтехники (м², м³) по телеметрии не считается; factVolume для единиц «м²», «м³», «т» в файле 1С равен null, если нет ручного факта.
  2. Проезд по участку (от 0,3 км) — только транспорт; признак не сохраняется; порог не настраивается.
  3. План выезда делит груз, ходки и км на число единиц всего состава; груз — до 0,1 т, ходки — вверх; точки развоза не делятся.
  4. Занятость и дубли — подсказки; сервер проверяет только дубль в запросе; окно работы не проверяется; окно через полночь не поддерживается.
  5. Подбор — только через карточку ex_vehicles с «Моделью ТС».
  6. Комплект не хранит объём на единицу и примечание строки; комплект нельзя пометить «не используется» из интерфейса (правка всегда ставит active = true); уникальность названия не проверяется; после удаления комплекта combo_id в заявках остаётся.
  7. Нет нормативной выработки и расчётных моточасов по норме; shift_id в заявке не используется; нет стоимости по тарифам.
  8. Отчёт: нет ограничения по подразделениям; 20 000 строк; «Excel» — HTML под именем .xls; вид объекта (объект/погрузка/база) не хранится; заказчик и договор берутся без наследования.
  9. Геозоны доступны любой вошедшей роли; удаление зоны не проверяет использование.
  10. Двойной щелчок по заявке открывает форму любого статуса, но сохранить можно только NEW.
  11. Предупреждение о дубле заявки при составе сравнивает только место, «откуда», дату, тип и общий вид работ (у заявок с составом он часто пуст).
  12. Системные alert() для ошибок смены статуса, пересчёта, удаления.
  13. Спецтехника «возит груз»: контроль маршрута вызывается, но не подтверждается телеметрией (раздел 5.5).

Расхождения кода и сопутствующих документов.

Что Источник расхождения Что верно
Число полей конструктора бриф и аудит 03: 28 в коде 29 (19 справочных + 10 мерных)
Тип геозоны «круг» ТЗ: circle в коде point (подпись «Точка»); circle нет
Срок отчёта «до 366 дней» ТЗ разность дат ≤ 366, то есть до 367 календарных дат
Выгрузка «до 93 дней», закрытие «до 31 дня» тексты ошибок разность дат ≤ 93 / ≤ 31, то есть до 94 / 32 календарных дат
Посещение геозоны у спецтехники схема 4 клиентского комплекта: «по обычному правилу» операции VISIT у SPECIAL в коде не создаются
Предприятие стенда ТЗ, части аудитов: «Автобаза №4» «Тест строительной техники»
Тесты аудит 02 AgroAccessTest (6 тестов) относится к блоку и не упомянут в аудите; AgroFactServiceTest — 14, AgroExportServiceTest — 6
Итоги эмуляции PLAN.md §7 и EMULATION_LOG.md №20 расходятся брать цифры стенда из свежей съёмки (STAND_FACTS)
Файл EXPORT_1C_SCHEMA.md описывает схему неполно см. раздел 5.11

11. Не подтверждено#

Что Статус
Контроль маршрута у SPECIAL с from_geozone_id в коде вызывается, посещений нет; на стенде не проверялся
Порядок резолвера геозон при пересечении по аудиту: «первая подходящая»; не проверено в коде
Печатные формы (колонки, подписи) по аудиту; клиентский код печати не перечитывался
Отображение pass-посещений в route_note и карточке по коду описывается как обычная «разгрузка…»; специальной проверки на стенде не было
Загрузка файла в 1С; формат с разработчиком 1С не согласован не выполнялась
Отказ роли U при «Открыть день заново», «Обработать период», «Типовой набор» по коду; текст не проверялся
Пересчёт нарядов спецтехники после удаления события топлива не проверялся
Поведение конструктора отчёта без доступа к CDN; открытие .xls в разных версиях Excel не проверялось
Окно «Закрыть все N дней»; печать и Excel отчёта не воспроизведены на стенде
Расхождение «пробег отчёта и пробег наряда» (в162хр: 492,8 км и 97,7 км) требует отдельной проверки расчёта; в треки стенда намеренно подмешаны помехи GPS
Нагрузка на боевых объёмах не проводилась

12. Что читать дальше#

  • «Архитектура и устройство Exzotron СТРОЙ»
  • «Заявки на спецтехнику и перевозку: состав, комплекты, План выезда»
  • «Управление нарядами на стройке: факт, рейсы, закрытие дня, печать и 1С»
  • «Карта, участки дорог и контроль маршрута на стройке»
  • «Топливо на стройке: заправки, сливы и расход»
  • «Отчёты и печатные формы Exzotron СТРОЙ»
  • «Справочники и подготовка данных Exzotron СТРОЙ»
  • «Первичная настройка и ввод в эксплуатацию Exzotron СТРОЙ»
  • Комплект Exzotron ТРАНС: «Алгоритмы и интеграции Exzotron ТРАНС (техническое руководство)», «Заявки на перевозку и конструктор маршрута Exzotron ТРАНС»