Перейти к основному содержимому

Аудит слаженности процессов

Документ фиксирует методику и результаты проверки того, как согласованы между собой документация, backend, frontend и фактические бизнес-сценарии.

Раздел не заменяет пользовательскую документацию. Он нужен как технический рабочий журнал: сначала здесь фиксируются ожидания и расхождения, затем подтвержденные изменения переносятся в пользовательские разделы и задачи на доработку.

Цель

Проверить, что рабочие процессы выполняются предсказуемо и согласованно:

  • документация описывает понятный ожидаемый сценарий;
  • backend реализует тот же порядок задач, статусов, валидаций и побочных эффектов;
  • frontend показывает пользователю те же действия, поля и ограничения;
  • данные не меняются неожиданно при сохранении, смене статуса, корректировках и повторных действиях;
  • процесс не зависает и не создает лишние или недостающие задачи.

Источники

Основной источник ожидаемого поведения - весь раздел 70. Рабочий процесс, включая вложенные папки и примеры.

Кодовые источники для сверки:

  • OlMag.Manufacture.Api/OlMag.Manufacture/Data/Enums/EnTaskProcessType.cs
  • OlMag.Manufacture.Api/OlMag.Manufacture/Data/Enums/EnTaskType.cs
  • OlMag.Manufacture.Api/OlMag.Manufacture/Data/Enums/EnTaskStatus.cs
  • OlMag.Manufacture.Api/OlMag.Manufacture/Services/UserTask/Services/TaskService.cs
  • OlMag.Manufacture.Api/OlMag.Manufacture/Services/UserTask/Services/TaskConditionHandler.cs
  • OlMag.Manufacture.Api/OlMag.Manufacture/Services/UserTask/Services/ProcessService.cs
  • frontend-страницы и формы задач в OlMag.Manufacture/src/pages/tasks и OlMag.Manufacture/src/entities/task

Документация считается ориентиром, но не абсолютной истиной: если она устарела, это фиксируется отдельно как расхождение документации, а не как ошибка кода.

Методика

Для каждого процесса и каждой задачи проверка идет в одном формате.

  1. Из документации выписываются ожидаемые роли, статусы, переходы, поля формы, проверки, создаваемые следующие задачи и особые сценарии.
  2. В backend проверяется фактическая state machine: доступные статусы, обработчики смены статуса, валидации, создание задач, изменение состояния заказа и связанные сущности.
  3. Во frontend проверяется пользовательский путь: загрузка данных, transformTaskData, defaultValues, видимые поля, скрытые поля, mapToTaskUpdate, сохранение и смена статуса.
  4. Отдельно проверяются побочные эффекты: документы, счета, планы оплат, платежи, отгрузки, Google Drive, уведомления, корректировки.
  5. Результат фиксируется в матрице: документация -> backend -> frontend -> вывод -> риск -> рекомендация -> нужен ли тест.

Классификация результатов

ТипКогда использовать
БагКод ведет себя не так, как должен работать бизнес-сценарий.
Рассинхрон frontend/backendUI разрешает или отправляет не то, что ожидает backend, либо backend делает больше/меньше, чем показывает UI.
Устаревшая документацияКод и текущий сценарий выглядят согласованно, но документация описывает старое поведение.
Спорное поведениеНужен продуктовый выбор: оба варианта возможны, но система должна выбрать один.
Технический долгРаботает сейчас, но реализация хрупкая и может ломаться при расширении сценария.
Нужен тестПоведение критично и должно быть закреплено автоматической проверкой.

Реестр документации

Общие страницы

ФайлНазначение
70. Рабочий процесс/workProcess.mdОбщая идея рабочих процессов и список процессов.
70. Рабочий процесс/fixTask.mdОбщие правила работы с корректировками.

Процессы

ФайлПроцесс
70. Рабочий процесс/Процессы/processBuyerOrder.mdЗаказ покупателя.
70. Рабочий процесс/Процессы/processCreateProduct.mdСоздание изделия.
70. Рабочий процесс/Процессы/processCreateSku.mdСоздание складской единицы.
70. Рабочий процесс/Процессы/processCustom.mdПользовательский процесс.
70. Рабочий процесс/Процессы/processOrder.mdЗаказ на производство.
70. Рабочий процесс/Процессы/Примеры/processBuyerOrderExample.mdПошаговый пример работы с заказом покупателя.

Задачи менеджера по продажам

ФайлЗадача
70. Рабочий процесс/Менеджер по продажам/beginBuyerOrder.mdЗаказ покупателя. Сбор первоначальной информации.
70. Рабочий процесс/Менеджер по продажам/createCommercialProposal.mdЗаказ покупателя. Создание КП.
70. Рабочий процесс/Менеджер по продажам/signingCommercialProposal.mdЗаказ покупателя. Согласование КП с покупателем.
70. Рабочий процесс/Менеджер по продажам/createSpecification.mdЗаказ покупателя. Создание спецификации.
70. Рабочий процесс/Менеджер по продажам/signingSpecification.mdЗаказ покупателя. Запуск.
70. Рабочий процесс/Менеджер по продажам/supportOrder.mdЗаказ покупателя. Сопровождение.
70. Рабочий процесс/Менеджер по продажам/createProduct.mdСоздание изделия.
70. Рабочий процесс/Менеджер по продажам/createSku.mdСоздание складской единицы.
70. Рабочий процесс/Менеджер по продажам/agreeDrawingForApprovalWithBuyer.mdЧертеж на согласование. Согласование с покупателем.
70. Рабочий процесс/Менеджер по продажам/notTask.mdРабочие процессы вне задач.

Задачи начальника менеджеров по продажам

ФайлЗадача
70. Рабочий процесс/Начальник менеджеров по продажам/calcBuyerOrderAmount.mdЗаказ покупателя. Расчет и согласование стоимости.
70. Рабочий процесс/Начальник менеджеров по продажам/agreeCommercialProposal.mdЗаказ покупателя. Согласование КП.
70. Рабочий процесс/Начальник менеджеров по продажам/agreeSpecification.mdЗаказ покупателя. Согласование спецификации.
70. Рабочий процесс/Начальник менеджеров по продажам/agreeCloseBuyerOrder.mdЗаказ покупателя. Согласование отмены заказа.
70. Рабочий процесс/Начальник менеджеров по продажам/agreeCreateDrawingForApproval.mdЗаказ покупателя. Согласование создания чертежа на согласование.
70. Рабочий процесс/Начальник менеджеров по продажам/agreeCreateProduct.mdСогласование создания изделия.
70. Рабочий процесс/Начальник менеджеров по продажам/agreeCreateSku.mdСогласование создания складской единицы.
70. Рабочий процесс/Начальник менеджеров по продажам/noTask.mdДополнительные сценарии без отдельной задачи.

Производство и конструкторы

ФайлЗадача
70. Рабочий процесс/Начальник производства/assignMaintainer.mdЗаказ покупателя. Назначить сопровождающего.
70. Рабочий процесс/Менеджер производства/supportOrder.mdЗаказ покупателя. Сопровождение.
70. Рабочий процесс/Менеджер производства/agreeDrawingForApprovalWithBuyer.mdЧертеж на согласование. Согласование с покупателем.
70. Рабочий процесс/Главный конструктор/assignPerformerDrawing.mdЗаказ. Назначить конструктора.
70. Рабочий процесс/Главный конструктор/agreeDrawing.mdЗаказ. Согласование КД.
70. Рабочий процесс/Главный конструктор/assignPerformerDrawingForApproval.mdЧертеж на согласование. Назначить конструктора.
70. Рабочий процесс/Главный конструктор/agreeDrawingForApprovalWithChief.mdЧертеж на согласование. Согласование с руководством.
70. Рабочий процесс/Конструктор/createDrawing.mdЗаказ. Создание КД.
70. Рабочий процесс/Конструктор/createDrawingForApproval.mdЧертеж на согласование. Создание.

Пользовательские задачи

ФайлЗадача
70. Рабочий процесс/Любая роль/custom.mdПользовательская задача.
70. Рабочий процесс/Любая роль/finalCustom.mdКонтроль пользовательской задачи.

Первый проход: заказ покупателя

Первый глубокий проход выполняется по процессу Заказ покупателя, потому что он связывает задачи, заказ, товары, КП, спецификацию, договор, оплату, счета, отгрузки и сопровождение.

На первом проходе боковая ветка по чертежам на согласование отмечается в реестре, но не проверяется как активный сценарий. Она будет вынесена в отдельный проход, если потребуется.

Ожидаемая основная цепочка из документации:

  1. Заказ покупателя. Сбор первоначальной информации.
  2. Заказ покупателя. Расчет и согласование стоимости.
  3. Заказ покупателя. Создание КП.
  4. Заказ покупателя. Согласование КП.
  5. Заказ покупателя. Согласование КП с покупателем.
  6. Заказ покупателя. Создание спецификации.
  7. Заказ покупателя. Согласование спецификации.
  8. Заказ покупателя. Запуск.
  9. Автоматический выбор сопровождающего при необходимости.
  10. Заказ покупателя. Сопровождение.

Ключевые правила, которые надо проверить в коде:

  • смена статуса задачи сначала сохраняет данные задачи;
  • при смене статуса выполняются проверки корректности заполненных данных;
  • следующая задача создается только после успешного завершения текущей;
  • пропущенные задачи остаются в процессе и должны корректно участвовать в связях PreviousTask/NextTask;
  • корректировки должны возвращать процесс к нужной предыдущей задаче и затем позволять продолжить цепочку;
  • состояние заказа покупателя должно соответствовать текущему этапу процесса;
  • frontend не должен показывать пользователю действия, которые backend не поддерживает;
  • backend не должен принимать действия, которые не разрешены текущей ролью, типом задачи или статусом;
  • повторное сохранение и повторная смена статуса не должны создавать дубли задач, счетов, документов или связей.

Проверка 1. Сбор первоначальной информации

Задача: BeginBuyerOrder, "Заказ покупателя. Сбор первоначальной информации".

Ожидание из документации:

  • задача создается при создании заказа покупателя;
  • исполнитель - ответственный менеджер по заказу;
  • стартовый статус - FillingOrderInformation;
  • редактируются основные данные заказа, товары, комментарий, оплаты и отгрузка;
  • для завершения нужен хотя бы один товар;
  • для завершения должна быть создана папка для документов по заказу;
  • если условие запуска требует согласования чертежа, среди товаров должно быть хотя бы одно изделие;
  • после завершения создается задача CalcBuyerOrderAmount;
  • в полной схеме документации завершение идет через статус FillingOrderInformation.

Фактическое поведение backend:

  • первая задача создается в BuyerOrderService.CreateBuyerOrderTask со статусом FillingOrderInformation, исполнителем и автором buyerOrder.ResponsibleId;
  • допустимые переходы в EnTaskType для BeginBuyerOrder: ReadyForWork -> FillingOrderInformation, FillingOrderInformation -> ReadyForWork, FillingOrderInformation -> Completed;
  • переход из ожидания ведет обратно к заполнению данных; завершение доступно только из статуса заполнения;
  • TaskService.UpdateData для BeginBuyerOrder требует BuyerOrder в TaskUpdateRequest и передает его в BuyerOrderService.Update;
  • при сохранении BuyerOrderService.Update обновляет заказ целиком через BuyerOrderCustomRequest, корректирует НДС, организацию, адреса, доставку и пересчитывает плановые даты отгрузки/доставки;
  • при завершении TaskConditionHandler.ValidateBeginBuyerOrderFinalData проверяет не только товары и условие запуска, но и наличие FilesFolder;
  • при завершении SetBeginBuyerOrderTaskDone создает CalcBuyerOrderAmount, либо помечает ее Skipped и сразу создает CreateCommercialProposal, если расчет стоимости можно пропустить.

Фактическое поведение frontend:

  • страница task-begin-buyer-order.tsx строит форму из transformBuyerOrderData(currentTaskData.buyerOrder);
  • форма недоступна для редактирования в статусе ReadyForWork; для редактирования пользователь должен перевести задачу в FillingOrderInformation;
  • BeginBuyerOrderForm.mapToTaskUpdate отправляет в backend полный объект buyerOrder;
  • в интерфейсе есть блоки основных данных, товаров, оплаты, отгрузки и кнопка создания папки файлов, если папка отсутствует;
  • сумма advancePaymentPercent + paymentBeforeShipmentPercent > 100 показывается пользователю как ошибка, но общая buyerOrderValidationSchema для этой формы проверяет только отдельные проценты в диапазоне 0..100. Backend для BeginBuyerOrder тоже не отклоняет такую сумму при сохранении или завершении; строгая проверка суммы появляется позже в обработке CreateCommercialProposal.

Выводы по первой задаче:

  • основной путь создания, заполнения и завершения задачи совпадает с текущей backend/frontend-реализацией;
  • правило обязательной папки документов находится на правильном этапе: backend не дает завершить BeginBuyerOrder без FilesFolder, а frontend дает создать папку из формы первой задачи;
  • документация, backend и frontend согласованы по маршруту: из ожидания задача возвращается в FillingOrderInformation, завершение доступно только после этого статуса;
  • контракт сохранения хрупкий: frontend сейчас отправляет полный buyerOrder, поэтому сценарий работает, но если будущая форма начнет отправлять частичный объект, BuyerOrderService.Update может непреднамеренно очистить nullable-поля;
  • проверка суммы процентов оплаты больше 100 сейчас выглядит как frontend-подсказка без блокировки на первом шаге. Нужно решить, допустимо ли сохранять такую комбинацию до задачи создания КП, или правило должно быть единым для заказа покупателя.

Проверка 2. Расчет и согласование стоимости

Задача: CalcBuyerOrderAmount, "Заказ покупателя. Расчет и согласование стоимости".

Ожидание из документации:

  • задача создается после завершения BeginBuyerOrder, если расчет стоимости не может быть пропущен;
  • исполнитель - начальник менеджеров по продажам;
  • стартовый статус - ReadyForWork;
  • доступные переходы: ReadyForWork -> InProgress, ReadyForWork -> Completed, InProgress -> ReadyForWork, InProgress -> Completed;
  • задача может быть создана сразу в статусе Skipped, если товары уже согласованы, цены указаны, РРЦ актуальны и скидка не превышает разрешенную;
  • для завершения у всех товаров должна быть цена больше 0, а несогласованных изделий быть не должно;
  • после завершения или пропуска создается задача CreateCommercialProposal;
  • в форме можно менять стоимость товаров, РРЦ изделия, стоимость доставки и комментарий к заказу.

Фактическое поведение backend:

  • задача создается в SetBeginBuyerOrderTaskDone со статусом ReadyForWork, исполнителем ChiefSalesManager, наблюдателем и автором ответственным менеджером;
  • решение о пропуске принимается там же через ShouldSkipCalcBuyerOrderAmountTask;
  • пропущенная задача остается в цепочке: CreateCommercialProposal.PreviousTaskId указывает на пропущенную CalcBuyerOrderAmount;
  • допустимые переходы в EnTaskType совпадают с документацией: из ReadyForWork доступны InProgress и Completed, из InProgress доступны ReadyForWork и Completed;
  • TaskService.UpdateData разрешает редактирование только роли ChiefSalesManager и принимает узкий BuyerOrderCalcAmountRequest, а не полный заказ;
  • BuyerOrderService.UpdateAmount может обновить цену конкретного товара, РРЦ связанной складской единицы, стоимость доставки и непустой Note;
  • TaskConditionHandler.ValidateCalcBuyerOrderAmountFinalData перед завершением проверяет отсутствие ApprovalInProgress у товаров и наличие цены больше 0;
  • при финализации SetBeginBuyerOrderOrCalcAmountFinalData копирует РРЦ из складской единицы в товары заказа и создает CommercialProposal, если его еще нет;
  • при завершении SetCalcBuyerOrderAmountTaskDone переводит заказ в состояние CreateCommercialProposal и создает следующую задачу исполнителю предыдущей задачи, то есть ответственному менеджеру.

Фактическое поведение frontend:

  • страница task-calculate-goods-cost.tsx загружает задачу, buyerOrderForTask, orderPayment и товары;
  • форма становится редактируемой только после выхода из ReadyForWork;
  • основная форма отправляет buyerOrderCalcAmount через FormTaskCalculateGoodsCost.mapToTaskUpdate;
  • стоимость доставки редактируется отдельным полем, если доставка за наш счет;
  • цена товара и РРЦ редактируются через модальное окно GoodFormPriceRRP;
  • таблица товаров после сохранения может показывать актуальную РРЦ из StockKeepingUnit, потому что GoodTableResponse берет Good.RecommendedRetailPrice ?? StockKeepingUnit.RecommendedRetailPrice;
  • комментарий к заказу в UI этой задачи не найден, хотя поле note есть в BuyerOrderCalcAmountRequest, а backend его частично поддерживает.

Выводы по второй задаче:

  • основной сценарий создания, пропуска, завершения и создания следующей задачи согласован между документацией, backend и frontend;
  • переход ReadyForWork -> Completed поддержан и документацией, и backend. Так как форма в ReadyForWork readonly, этот путь подходит только для уже корректно заполненных данных; иначе финальная проверка блокирует завершение;
  • комментарий к заказу описан как редактируемый, но frontend его не показывает. Backend при этом обновляет Note только если пришла непустая строка, поэтому даже после добавления поля очистить комментарий таким request не получится;
  • backend-валидация UpdateAmount слабее frontend-ожиданий: финал проверяет цену товара, но сохранение не запрещает отрицательную доставку или отрицательную РРЦ, если такой payload придет не из текущего UI;
  • правило пропуска строже обычного завершения: для пропуска требуются актуальная РРЦ и скидка в пределах лимита менеджера, а для ручного завершения начальником проверяются только цена и отсутствие несогласованных изделий. Это выглядит логично, но должно быть явно закреплено в тестах, потому что это важное бизнес-различие.

Проверка 3. Создание коммерческого предложения

Задача: CreateCommercialProposal, "Заказ покупателя. Создание КП".

Ожидание из документации:

  • задача создается после завершения или пропуска CalcBuyerOrderAmount;
  • исполнитель - менеджер по продажам;
  • стартовый статус - ReadyForWork;
  • основной путь: ReadyForWork -> FillingData -> CreateFile -> Completed;
  • короткий путь без КП: ReadyForWork -> FillingData -> NotRequired;
  • при переходе в CreateFile и при завершении должны быть заполнены условия оплаты, НДС, наша организация, условие запуска, базис поставки, сроки изготовления и приемки;
  • при сумме предоплаты и оплаты перед отгрузкой меньше 100 должна быть заполнена отсрочка;
  • при доставке за наш счет должна быть заполнена стоимость доставки;
  • для завершения с созданием КП должна быть заполнена ссылка на КП;
  • папка для документов к этому этапу уже должна быть создана и проверена в BeginBuyerOrder;
  • при NotRequired должны создаваться пропущенные AgreeCommercialProposal и SigningCommercialProposal, затем активная CreateSpecification;
  • в статусе создания файла пользователь должен формировать КП через функционал генерации документа; ручное указание ссылки на КП остается как возможность замены/корректировки; ссылка на папку документов доступна как уже созданная папка заказа.

Фактическое поведение backend:

  • EnTaskType.CreateCommercialProposal реализует переходы ReadyForWork -> FillingData, FillingData -> ReadyForWork/CreateFile/NotRequired, CreateFile -> FillingData/Completed;
  • данные задачи возвращаются вместе с BuyerOrder, BuyerOrderForTask и отдельным FilesFolder;
  • TaskService.UpdateData разрешает сохранение только ролям SalesManager и AssistantSalesManager;
  • сохранение принимает BuyerOrderCommercialProposal и через Adapt обновляет заказ, оплату, отгрузку, необходимые документы, дату следующего контакта, ссылку на чат, договор, заказчика, нашу организацию, условие запуска и комментарий;
  • сохранение отдельно создает CommercialProposal, если его нет, и обновляет FileLink/IsStandard;
  • при сохранении проверяется сумма AdvancePaymentPercent + PaymentBeforeShipmentPercent <= 100;
  • при сохранении запрещено ShippingIncludedInPrice = true, если доставка не за наш счет;
  • перед переходом в CreateFile проверяются обязательные коммерческие поля; FilesFolder здесь повторно не проверяется, потому что обязательность папки вынесена в BeginBuyerOrder;
  • финальная проверка для Completed требует ссылку на КП и обязательные коммерческие поля; FilesFolder повторно не проверяется;
  • финальная проверка для NotRequired не требует ссылку на КП, но требует обязательные коммерческие поля;
  • SetCreateCommercialProposalTaskDone при NotRequired создает AgreeCommercialProposal и SigningCommercialProposal в Skipped, затем CreateSpecification в ReadyForWork; при обычном Completed создает AgreeCommercialProposal в ReadyForWork;
  • старая ветка пропуска согласования типового КП закомментирована, поэтому IsStandard = true сейчас не влияет на маршрут.

Фактическое поведение frontend:

  • страница task-commercial-proposal.tsx строит форму из transformTaskData, где buyerOrderCommercialProposal берется из полного buyerOrder, а commercialProposal - из документа КП;
  • форма readonly в ReadyForWork; для заполнения данных пользователь сначала переводит задачу в FillingData;
  • при смене статуса из ReadyForWork сохранение данных не вызывается, что согласовано с readonly-формой;
  • в FillingData показываются коммерческие поля оплаты, поставки, сроков, НДС, организации и условия запуска;
  • в CreateFile, Completed и NotRequired форма показывает ссылку на КП, признак типового документа и комментарий;
  • кнопка формирования КП доступна только в статусе CreateFile;
  • при успешной генерации frontend заполняет commercialProposal.fileLink и устанавливает commercialProposal.isStandard = true;
  • mapToTaskUpdate отправляет только buyerOrderCommercialProposal и commercialProposal;
  • filesFolder в payload задачи создания КП не отправляется, потому что задача не отвечает за создание или изменение папки;
  • ссылка на папку документов показывается в задачах согласования/подписания КП и создания спецификации как уже созданная папка заказа.

Выводы по третьей задаче:

  • основной маршрут статусов и создание следующих задач согласованы между документацией, backend и frontend;
  • папка документов корректно относится к первой задаче процесса: BeginBuyerOrder создает и проверяет FilesFolder, а CreateCommercialProposal только использует уже подготовленную папку;
  • устаревшее описание задачи создания КП, где папка была обязательным редактируемым полем этого этапа, исправлено в пользовательской документации;
  • IsStandard сохраняется и выставляется при генерации файла, но не влияет на процесс: задача согласования КП создается всегда. Пользовательская документация актуализирована под этот маршрут;
  • как и в предыдущих задачах, форма в ReadyForWork readonly. Это не ломает текущий маршрут, но в документации лучше явно писать, что заполнение начинается после перехода в FillingData.

Проверка 4. Согласование коммерческого предложения

Задача: AgreeCommercialProposal, "Заказ покупателя. Согласование КП".

Ожидание из документации:

  • задача создается после успешного создания КП;
  • исполнитель - начальник менеджеров по продажам;
  • стартовый статус активной задачи - ReadyForWork;
  • основной путь: ReadyForWork -> Completed или ReadyForWork -> InProgress -> Completed;
  • при завершении нужно установить флаг и дату согласования КП;
  • после завершения создается задача SigningCommercialProposal;
  • если КП не требуется, задача согласования создается как Skipped предыдущим этапом;
  • типовой документ КП не пропускает согласование автоматически: текущий основной маршрут все равно проходит через эту задачу;
  • форма задачи предназначена для просмотра КП и данных заказа, а не для редактирования коммерческих условий.

Фактическое поведение backend:

  • EnTaskType.AgreeCommercialProposal разрешает переходы ReadyForWork -> InProgress/Completed, InProgress -> ReadyForWork/Completed;
  • TaskService.GetDataById для задачи возвращает BuyerOrder, BuyerOrderForTask и FilesFolder;
  • отдельной финальной проверки для AgreeCommercialProposal нет: CheckFinalData возвращает успех по умолчанию;
  • при завершении TaskConditionHandler.SetAgreeCommercialProposalFinalData устанавливает CommercialProposal.Approved = true, ApproveDate = DateTime.UtcNow, DateOfLastEdit = DateTime.UtcNow;
  • SetAgreeCommercialProposalTaskDone переводит заказ в состояние SigningCommercialProposal и создает следующую задачу SigningCommercialProposal в ReadyForWork;
  • исполнитель следующей задачи берется из PreviousTask.ExecutorId, то есть из задачи создания КП, поэтому подписание возвращается ответственному менеджеру/помощнику;
  • активная задача согласования не имеет доступного перехода в Skipped; Skipped создается только из CreateCommercialProposal при NotRequired;
  • ветка пропуска согласования типового КП в SetCreateCommercialProposalTaskDone закомментирована, поэтому CommercialProposal.IsStandard не влияет на маршрут.

Фактическое поведение frontend:

  • страница task-commercial-proposal.tsx для типа ApprovalCommercialProposal показывает CommercialProposalApproval;
  • форма согласования показывает документ КП, папку документов и коммерческие данные заказа на чтение;
  • кнопка сохранения скрыта через hideSaveButton;
  • при смене статуса из InProgress общий TaskStatusSelector все равно вызывает UpdateData, но для AgreeCommercialProposal backend выполняет no-op и возвращает актуальные данные;
  • корректировка доступна через общий механизм запросов на корректировку, а не через отдельные поля формы.

Выводы по четвертой задаче:

  • активный маршрут согласования КП согласован между backend и frontend;
  • установка флага и даты согласования реализована не в TaskService, а в TaskConditionHandler.SetFinalData; это важно учитывать в тестах;
  • пользовательская документация была устаревшей по пропуску типового КП и общей схеме процесса: маршрут CreateCommercialProposal -> SigningCommercialProposal, если КП типовое, сейчас не работает. Документация исправлена под текущую реализацию;
  • отсутствие финальной проверки ссылки на КП в самой задаче согласования допустимо для штатного процесса, потому что CreateCommercialProposal.Completed уже проверяет ссылку. Остаточный риск остается только для старых или ручных неконсистентных данных;
  • no-op сохранение при смене статуса из InProgress не ломает сценарий, но это технический шум: для readonly-задач можно рассмотреть возможность не передавать mapToTaskUpdate.

Проверка 5. Подписание коммерческого предложения

Задача: SigningCommercialProposal, "Заказ покупателя. Согласование КП с покупателем".

Ожидание из документации:

  • задача создается после завершения AgreeCommercialProposal;
  • исполнитель - ответственный менеджер или помощник, который создавал КП;
  • стартовый статус активной задачи - ReadyForWork;
  • основной путь: ReadyForWork -> AgreeWithBuyer -> Completed;
  • при переходе на согласование у покупателя ссылка на КП должна быть заполнена;
  • в задаче можно обновить ссылку на КП, указать дату отправки клиенту, дату подписания клиентом и крайний срок задачи;
  • при завершении система выставляет флаги отправки и подписания КП;
  • после завершения создается задача CreateSpecification;
  • если КП не требуется, задача подписания создается как Skipped предыдущим этапом, а активной следующей задачей сразу становится CreateSpecification.

Фактическое поведение backend:

  • EnTaskType.SigningCommercialProposal разрешает переходы ReadyForWork -> AgreeWithBuyer, InProgress -> AgreeWithBuyer, AgreeWithBuyer -> ReadyForWork/Completed;
  • перехода из ReadyForWork в InProgress для обычного пути нет, хотя InProgress -> AgreeWithBuyer поддержан для возврата после корректировок;
  • перед переходом в AgreeWithBuyer проверяется, что заполнена ссылка на КП;
  • при завершении ValidateSigningCommercialProposalFinalData проверяет ссылку на КП, если задача создания КП не была NotRequired;
  • при сохранении TaskService.UpdateData для SigningCommercialProposal обновляет ссылку на КП, даты отправки/подписания, флаги документа и Deadline;
  • если дата отправки не передана, Sent выставляется по текущему статусу SigningWithBuyer. Для этой задачи фактический статус - AgreeWithBuyer, поэтому без ручной даты перед завершением SentDate останется пустой;
  • если дата подписания клиентом не передана, при сохранении SignedByClient становится false, но при финализации SetSigningCommercialProposalFinalData снова выставляет SignedByClient = true;
  • финализация выставляет CommercialProposal.Sent = true, SignedByManagement = true, SignedByClient = true, создает Specification, если ее еще нет;
  • финализация не проставляет даты SentDate, SignedByManagementDate, SignedByClientDate, если пользователь не заполнил их ранее;
  • SetSigningCommercialProposalTaskDone переводит заказ в состояние CreateSpecification и создает следующую задачу CreateSpecification в ReadyForWork.

Фактическое поведение frontend:

  • CommercialProposalSinging показывает документ КП, папку документов, ссылку на КП, крайний срок, дату отправки клиенту и дату согласования клиентом;
  • форма readonly в ReadyForWork; редактирование доступно после перехода в AgreeWithBuyer;
  • mapToTaskUpdate отправляет commercialProposal и deadline;
  • кнопка отмены заказа доступна в редактируемом статусе;
  • в компоненте осталась ветка для TaskStatus.SigningWithOur: кнопка создания поручения на подпись руководству. По текущим backend-переходам для КП этот статус недостижим;
  • task-description.tsx содержит описание для SigningCommercialProposal в статусах SigningWithOur и SigningWithBuyer, но фактический статус задачи - AgreeWithBuyer. Поэтому подсказка для текущего основного статуса может не показываться.

Выводы по пятой задаче:

  • основной маршрут подписания КП согласован между backend и frontend: пользователь переводит задачу на согласование у покупателя, при необходимости обновляет ссылку/даты и завершает задачу;
  • пользовательская документация была устаревшей относительно измененного маршрута: раньше после согласования КП руководителем менеджера отдельным этапом в этой задаче шло подписание КП у руководства, а затем у клиента. Сейчас подписание у руководства из маршрута убрано: предыдущая задача отвечает за согласование руководителем, а эта задача - за согласование КП с покупателем. Документация исправлена под текущий маршрут;
  • флаги отправки и подписания выставляются автоматически при завершении, но даты остаются пустыми, если пользователь их не заполнил. Это стоит явно учитывать в отчетах и тестах;
  • финальная проверка не требует даты подписания клиентом. Если бизнесу дата обязательна, нужно добавить backend-валидацию SignedByClientDate; если дата необязательна, текущая документация должна оставаться в формулировке "можно указать";
  • frontend содержит недостижимую ветку SigningWithOur для КП и описание не того статуса (SigningWithBuyer вместо AgreeWithBuyer). Это не ломает сценарий, но создает шум и может скрывать подсказку пользователю.

Повторная проверка после обновления frontend/backend:

  • backend-переходы, проверки, сохранение дат и финализация SigningCommercialProposal содержательно не изменились;
  • frontend-форма CommercialProposalSinging теперь получает задачу и данные задачи через RTK Query (useGetTaskByIdQuery, useGetTaskDataByIdQuery) и показывает загрузчик, но бизнес-поля формы и mapToTaskUpdate остались прежними;
  • остаточные выводы остаются актуальными: фактический рабочий статус задачи - AgreeWithBuyer, тогда как в подсказках остались SigningWithOur/SigningWithBuyer; кнопка для SigningWithOur по текущим backend-переходам недостижима; даты отправки и подписания не обязательны и не проставляются финализацией автоматически.

Проверка 6. Создание спецификации

Задача: CreateSpecification, "Заказ покупателя. Создание спецификации".

Ожидание из документации:

  • задача создается после завершения SigningCommercialProposal или сразу после CreateCommercialProposal.NotRequired;
  • стартовый статус активной задачи - ReadyForWork;
  • основной путь: ReadyForWork -> FillingData -> CreateFile -> Completed;
  • альтернативный путь: ReadyForWork -> FillingData -> NotRequired;
  • в статусе FillingData менеджер выбирает договор, заполняет адрес базиса поставки, необходимую документацию, грузополучателя и дополнительные данные заказа;
  • при переходе в CreateFile должны быть выбраны договор и адрес базиса поставки;
  • в статусе CreateFile менеджер формирует или прикрепляет спецификацию и договор;
  • при завершении должны быть заполнены ссылка на спецификацию и ссылка на файл договора, если договор выбран;
  • при NotRequired создается AgreeSpecification в Skipped, затем SigningSpecification в ReadyForWork.

Фактическое поведение backend:

  • EnTaskType.CreateSpecification разрешает переходы ReadyForWork -> FillingData, FillingData -> ReadyForWork/CreateFile/NotRequired, CreateFile -> FillingData/Completed;
  • TaskServiceInternal.GetAvailableStatuses скрывает NotRequired, если условие запуска - AfterSigningSpecification или AfterSigningSpecificationAndApproval;
  • при переходе в CreateFile ValidateCreateSpecificationInterimData проверяет ContractId и SupplyBaseAddress;
  • при сохранении TaskService.UpdateData требует роль SalesManager или AssistantSalesManager, требует BuyerOrderSpecification, затем обновляет данные заказа через requestBuyerOrder.Adapt(buyerOrder, mapper.Config);
  • RequiredDocumentationIds сохраняются, если пришли в request, но обязательность этого поля backend не проверяет;
  • в статусе CreateFile дополнительно сохраняются ссылка и признак типового договора, если договор еще не подписан клиентом, а также ссылка и признак типовой спецификации;
  • при завершении не в NotRequired ValidateCreateSpecificationFinalData требует ContractId, ссылку на файл договора и ссылку на спецификацию;
  • при завершении в NotRequired финальная проверка требует только SupplyBaseAddress;
  • SetCreateSpecificationTaskDone при Completed переводит заказ в AgreeSpecification и создает AgreeSpecification в ReadyForWork; исполнитель берется из task.PreviousTask!.PreviousTask!.ExecutorId;
  • SetCreateSpecificationTaskDone при NotRequired создает AgreeSpecification в Skipped, затем SigningSpecification в ReadyForWork, переводит заказ в состояние SigningSpecification.

Фактическое поведение frontend:

  • CreateSpecification и FormCreateSpecification получают задачу и данные через RTK Query и показывают загрузчик во время загрузки;
  • форма редактируется только после выхода из ReadyForWork;
  • для ReadyForWork, FillingData и NotRequired показывается блок заполнения данных заказа;
  • для CreateFile и Completed показываются документы спецификации/договора, ссылка на папку документов, комментарий и блок просмотра деталей заказа;
  • mapToTaskUpdate отправляет buyerOrder, buyerOrderSpecification, contract, specification и дополнительно buyerOrderNote = values.buyerOrderSpecification?.note;
  • backend в этой ветке использует BuyerOrderSpecification.Note, поэтому отдельное поле buyerOrderNote для CreateSpecification выглядит лишним, но данные комментария не теряются;
  • подсказки в task-description.tsx покрывают FillingData, CreateFile, Completed, NotRequired.

Выводы по шестой задаче:

  • основной маршрут создания спецификации согласован между документацией, backend и frontend;
  • ветка NotRequired корректно ограничена backend по условию запуска: если запуск требует подписания спецификации, статус пользователю не доступен;
  • документация была уточнена: NotRequired доступен не всегда, при нем проверяется только адрес базиса поставки, а "Необходимые документы" не являются обязательным полем по backend-валидации;
  • важный контракт сохранения: frontend отправляет полный buyerOrderSpecification, backend применяет его как обновление данных заказа. Это удобно для текущей формы, но остается хрупким при будущих частичных payload;
  • использование task.PreviousTask!.PreviousTask!.ExecutorId для назначения согласования спецификации опирается на строгую цепочку процесса. В штатном процессе цепочка есть, но это место стоит держать под интеграционными тестами, особенно для корректировок и пропусков;
  • интеграционные тесты уже покрывают основной путь CreateSpecification, ошибки пустого договора/адреса, пустых ссылок договора/спецификации, а также сценарии корректировок с CreateSpecification.NotRequired.

Проверка 7. Согласование спецификации

Задача: AgreeSpecification, "Заказ покупателя. Согласование спецификации".

Ожидание из документации:

  • задача создается после завершения CreateSpecification.Completed;
  • если спецификация не требуется, задача создается предыдущим этапом как Skipped;
  • стартовый статус активной задачи - ReadyForWork;
  • основной путь: ReadyForWork -> Completed или ReadyForWork -> InProgress -> Completed;
  • руководитель менеджеров проверяет спецификацию и, если к заказу прикреплен неподписанный договор, файл договора;
  • данные в этой задаче не редактируются; при замечаниях создается запрос на корректировку;
  • при завершении сопровождающему менеджеру создается SigningSpecification.

Фактическое поведение backend:

  • EnTaskType.AgreeSpecification разрешает переходы ReadyForWork -> InProgress/Completed, InProgress -> ReadyForWork/Completed;
  • активная задача не имеет пользовательского перехода в Skipped; Skipped создается только из CreateSpecification.NotRequired;
  • отдельной финальной валидации для AgreeSpecification нет: CheckFinalData возвращает успех по умолчанию;
  • TaskService.GetData возвращает BuyerOrder, BuyerOrderForTask, Specification, Contract и FilesFolder; документы помечаются как не редактируемые;
  • TaskConditionHandler.SetAgreeSpecificationFinalData проставляет у спецификации Approved = true и ApproveDate = DateTime.UtcNow;
  • если к заказу выбран договор и его документ еще не согласован, финализация проставляет Approved/ApproveDate и у договора;
  • SetAgreeSpecificationTaskDone переводит заказ в состояние SigningSpecification и создает следующую задачу SigningSpecification в ReadyForWork, исполнитель - task.PreviousTask!.ExecutorId, то есть исполнитель создания спецификации.

Фактическое поведение frontend:

  • SpecificationApproval загружает данные через useGetTaskDataByIdQuery;
  • форма показывает краткую информацию по заказу, документ спецификации и документ договора в режиме просмотра;
  • TaskHeaderControls вызывается с hideSaveButton и без mapToTaskUpdate, поэтому отдельное сохранение данных при смене статуса не выполняется;
  • подсказки в task-description.tsx покрывают InProgress, Skipped и технически недостижимый для этой задачи NotRequired; подсказки для Completed нет, что не влияет на основной путь.

Выводы по седьмой задаче:

  • основной маршрут согласования спецификации согласован между backend и frontend;
  • документация была уточнена: пропуск согласования спецификации сейчас связан с CreateSpecification.NotRequired, а не с типовой спецификацией или типовым договором;
  • пример процесса был исправлен: после завершения согласования обновляется статус спецификации и, при наличии несогласованного договора, договора, а не КП;
  • отсутствие финальной проверки в самой задаче допустимо для штатного процесса, потому что ссылки на спецификацию и договор проверяются на CreateSpecification.Completed, но ручные или старые неконсистентные данные могут привести к ошибке в SetAgreeSpecificationFinalData;
  • интеграционные тесты покрывают основной переход и косвенно проверяют Approved/ApproveDate у спецификации и договора на следующем шаге SigningSpecification;
  • во frontend остается небольшой технический долг: описание TaskStatus.NotRequired для TaskType.ApprovalSpecification недостижимо по текущим backend-переходам.

Проверка 8. Запуск

Задача: SigningSpecification, "Заказ покупателя. Запуск".

Ожидание из документации:

  • задача создается после завершения AgreeSpecification.Completed или после CreateSpecification.NotRequired, когда согласование спецификации пропускается;
  • стартовый статус активной задачи - ReadyForWork;
  • основной путь: ReadyForWork -> SigningWithOur -> Invoicing -> SigningWithBuyer -> AwaitingPayment -> CreatingOrders -> Completed;
  • если предоплаты нет, статус Invoicing не нужен;
  • если условие запуска не требует предоплаты, статус AwaitingPayment не нужен;
  • на шаге создается счет на предоплату, ожидается оплата, затем создаются заказы на производство для изделий;
  • при завершении создается следующая задача сопровождения заказа;
  • боковая ветка по чертежам на согласование сейчас не рассматривается как активный сценарий.

Фактическое поведение backend:

  • EnTaskType.SigningSpecification задает переходы между ReadyForWork, InProgress, SigningWithOur, Invoicing, SigningWithBuyer, AwaitingPayment, CreatingOrders, Completed;
  • TaskServiceInternal.GetAvailableStatuses динамически скрывает Invoicing, если предоплаты нет, и скрывает AwaitingPayment, если условие запуска не требует предоплаты;
  • при переходе после подписи руководством SetSigningSpecificationInterimData проставляет флаги и даты подписи руководством у спецификации и договора, если они есть;
  • при переходе к покупателю счет предоплаты переводится из Created в Sent;
  • при переходе в ожидание оплаты или создание заказов проставляется подпись покупателем у спецификации и договора;
  • при переходе в CreatingOrders рассчитывается ActualLaunchDate и пересчитываются плановые даты отгрузки/доставки;
  • SetSigningSpecificationTaskDone создает недостающие заказы на производство и Google-файлы для изделий, выбирает сопровождающего при необходимости, создает следующие планы оплат и задачу SupportOrder;
  • при оплате счета предоплаты HandleSigningSpecificationTaskDone автоматически переводит задачу в CreatingOrders и завершает ее.

Фактическое поведение frontend:

  • SpecificationSigning показывает разные блоки формы для подписания, выставления счета, ожидания оплаты и создания заказов;
  • mapToTaskUpdate отправляет specification, contract, buyerOrderNote, а в статусе Invoicing дополнительно invoicingForPrepayment;
  • исходные данные формы создаются через transformTaskData, поэтому в обычном пути даты документов не теряются при сохранении;
  • CreatingOrders показывает таблицу товаров и позволяет вручную создать заказ на производство для изделия;
  • повторное создание заказа на производство безопасно: backend возвращает существующий заказ, если он уже связан с товаром;
  • интеграционные тесты покрывают основной prepayment-путь, создание счета, ожидание оплаты, автоматическое завершение после банковской выписки и создание заказов на производство.

Выводы по восьмой задаче:

  • основной путь запуска согласован между backend и frontend;
  • пользовательская документация была уточнена: создание заказов на производство уже реализовано, отдельная задача назначения сопровождающего не создается, активная ветка чертежей не описывается как выполняющаяся автоматически;
  • важный баг: ветка CreateSpecification.NotRequired -> SigningSpecification создается backend, но TaskService.UpdateData для SigningSpecification требует request.Specification и затем обращается к buyerOrder.Specification!. Frontend в этой ветке может отправить specification: null, потому что спецификации нет. Такой сценарий приведет к ошибке сохранения или NullReferenceException;
  • тот же риск есть при смене статуса в AwaitingPayment или CreatingOrders: SetSigningSpecificationInterimData обращается к buyerOrder.Specification!.SignedByClientDate, хотя спецификация может отсутствовать после CreateSpecification.NotRequired;
  • ValidateSigningSpecificationFinalData читает t.BuyerOrder!.Specification!.FileLink даже до проверки CreateSpecificationTaskStatusId, поэтому финальная проверка тоже потенциально падает на старых или ручных данных без спецификации;
  • автозавершение по оплате ищет задачу запуска только по BuyerOrderId и типу задачи, без фильтра по состоянию/статусу. Если счет будет обработан повторно, метод может попытаться повторно перевести и завершить уже завершенную задачу;
  • финальная проверка предоплаты смотрит последний неотмененный счет, а промежуточная проверка перехода в CreatingOrders проверяет все неотмененные счета. Это различие стоит выровнять, чтобы сценарии с несколькими счетами были предсказуемыми;
  • SetSigningSpecificationTaskDone создает заказы на производство до финального сохранения задачи. OrderService.AddOrderData сам сохраняет заказ, номер и Google-ссылку, поэтому при ошибке на одном из товаров возможны частичные побочные эффекты по предыдущим товарам;
  • сообщение об ошибке в catch SetSigningSpecificationTaskDone говорит о задаче "согласование спецификации", хотя фактически это запуск/подписание спецификации.

Проверка 9. Сопровождение заказа

Задача: SupportOrder, "Заказ покупателя. Сопровождение".

Ожидание из документации:

  • задача создается после завершения запуска заказа;
  • если в заказе есть изделия, исполнитель - сопровождающий/менеджер производства; если изделий нет, исполнитель - ответственный менеджер по продажам;
  • основной путь: ReadyForWork -> PreparingDocumentation -> AwaitingProduction -> ReadyToShip -> Finalizing -> Completed;
  • из AwaitingProduction можно перейти сразу в Finalizing;
  • при подготовке документации должна быть создана или найдена папка отгрузочных документов;
  • в финальной работе создаются отгрузка, необходимые отгрузочные документы и строки в таблице отгрузок;
  • для завершения должна быть заполнена фактическая дата отгрузки; фактическая дата доставки нужна только если доставка выполняется за наш счет; товары должны быть полностью отгружены;
  • при завершении заказ покупателя переводится в состояние Completed, процесс завершается.

Фактическое поведение backend:

  • EnTaskType.SupportOrder реализует переходы ReadyForWork -> PreparingDocumentation, PreparingDocumentation -> AwaitingProduction, AwaitingProduction -> ReadyToShip/Finalizing, ReadyToShip -> Finalizing, Finalizing -> AwaitingResponse/Completed, AwaitingResponse -> Completed;
  • SetSupportOrderInterimData при переходе в PreparingDocumentation вызывает BuyerOrderService.SetShipmentsFolder;
  • при переходе в Finalizing вызывается BuyerOrderService.AddShipmentDocuments: если активной отгрузки нет, создается отгрузка на все товары; затем по необходимой документации создаются недостающие документы и обновляется таблица отгрузок;
  • UpdateData для SupportOrder сохраняет только ActualShipmentDate и ActualDeliveryDate;
  • ValidateSupportOrderFinalData требует фактическую дату отгрузки, фактическую дату доставки, наличие отгрузок по каждому товару и отгруженное количество не меньше количества товара в заказе; при этом DeliveryOurExpense не загружается и не учитывается, поэтому фактическая дата доставки сейчас обязательна всегда;
  • SetSupportOrderTaskDone переводит заказ покупателя в Completed и ставит Process.Completed = true.

Фактическое поведение frontend:

  • TaskSupportOrder загружает задачу и данные, показывает вкладки сопровождения, просмотра заказа, процесса, истории статусов и корректировок;
  • SupportOrder показывает необходимую документацию, папку отгрузочных документов, ссылку на таблицу отгрузок, фактические даты, кнопку создания всех отгрузочных документов, таблицы заказов на производство, складских товаров, отгрузок и планов оплат;
  • при сохранении и смене статуса frontend отправляет только actualShipmentDate и actualDeliveryDate;
  • значения формы после загрузки данных сбрасываются через общий useFormTaskDraft, поэтому обычный путь не должен сохранять дефолтные значения до прихода sourceValues.

Проверка доступа менеджера продаж на этапе SupportOrder:

  • если в заказе нет изделий, SupportOrder создается на ответственного менеджера продаж. Backend-доступ к редактированию задачи в этом случае есть, потому что TaskServiceInternal.HasAccessToEditing разрешает редактирование исполнителю задачи;
  • просмотр заказа покупателя, данных задачи и заказов на производство для менеджера продаж проходит через доступ к заказу покупателя: BuyerOrderService.HasAccess разрешает ответственному менеджеру, а OrderService.GetForTableByBuyerOrder дополнительно проверяет этот доступ;
  • просмотр таблицы отгрузок на frontend доступен менеджеру продаж через PermissionContext.SHIPMENT.table/view, и backend-методы ShipmentController.GetForTable/GetById также разрешают роль SalesManager;
  • ручное создание и редактирование отгрузки расходится между frontend и backend: ShipmentController.Add/Delete и генерация отдельных документов разрешают SalesManager, но дополнительно требуют редактирование активной задачи SupportOrder; при этом frontend PermissionContext.SHIPMENT.edit выдан только ProductionManager, а маршрут /shipments/:id доступен только ProductionManager и ChiefOfProduction. Поэтому менеджер продаж-исполнитель задачи не увидит кнопку создания отгрузки и не сможет открыть карточку отгрузки на редактирование из UI;
  • кнопка "Создать все отгрузочные документы" в SupportOrder вызывает POST /Order/BuyerOrder/{buyerOrderId}/shipment/documents. На backend этот маршрут помечен как тестовый и разрешен только Tester; рабочий маршрут для пользователей - POST /Order/BuyerOrder/shipment/documents?taskId={taskId}, где сервис проверяет, что это задача SupportOrder, и вызывает HasAccessToEditing(taskId, user). Поэтому для менеджера продаж кнопка генерации документов будет получать отказ по роли, несмотря на корректный доступ к задаче.

Выводы по девятой задаче:

  • основной маршрут сопровождения согласован между backend и frontend;
  • пользовательская документация была уточнена: ReadyToShip добавлен в список статусов, создание отгрузочных документов перенесено на переход в Finalizing, а финальная проверка описывает фактическую дату отгрузки, условную обязательность фактической даты доставки и отгруженное количество;
  • потенциальный баг: ValidateSupportOrderFinalData требует ActualDeliveryDate всегда, хотя по бизнес-правилу дата доставки должна быть обязательной только при доставке за наш счет;
  • для сценария без изделий, где SupportOrder исполняет менеджер продаж, есть frontend/backend-рассинхрон прав: backend допускает действия через доступ исполнителя задачи, но UI не дает менеджеру продаж создать/редактировать отгрузку, а массовая генерация документов вызывает тестовый endpoint по buyerOrderId;
  • автоматический переход в ReadyToShip при завершении заказов на производство в активном backend-коде не найден; статус доступен как ручной переход;
  • потенциальный баг: ValidateSupportOrderFinalData берет BuyerOrder.Goods без фильтра DateArchived == null, поэтому архивный товар может блокировать завершение сопровождения;
  • похожий риск есть в BuyerOrderService.AddShipmentDocuments: при автосоздании отгрузки используется b.Goods!.ToArray() без фильтра архивных товаров, значит в отгрузку могут попасть архивные товары;
  • GetDataById для SupportOrder выбирает OrderShipments без фильтра DateArchived == null, поэтому в данных задачи могут вернуться архивные отгрузки;
  • финальная проверка не проверяет наличие файлов отгрузочных документов по RequiredDocumentations. Это может быть допустимо, если создание документов гарантируется переходом в Finalizing, но ручные/старые данные после удаления или архивации документов не будут пойманы финальной валидацией;
  • backend принимает 0 как unix-date для фактических дат. Текущий frontend после загрузки должен сбрасывать форму на реальные значения, но для API-защиты лучше явно запрещать нулевые/некорректные даты.

Матрица проверки

СценарийДокументацияBackendFrontendВыводРискРекомендацияТест
BeginBuyerOrder: создание первой задачиСоздается при создании заказа, исполнитель - ответственный менеджер, стартовый статус - сбор информации.BuyerOrderService.CreateBuyerOrderTask создает BeginBuyerOrder со статусом FillingOrderInformation, ExecutorId = ResponsibleId.Страница задачи загружает buyerOrder и показывает форму первой задачи.Совпадает.Низкий.Закрепить интеграционным тестом создания заказа и первой задачи.Нужен.
BeginBuyerOrder: переходы статусовЗавершение идет через FillingOrderInformation; из ожидания задача возвращается к заполнению данных.EnTaskType разрешает из ReadyForWork только FillingOrderInformation; завершение доступно только из FillingOrderInformation.Форма в ReadyForWork readonly, пользователь сначала должен перейти в FillingOrderInformation.Совпадает после актуализации документации.Низкий.Закрепить тестом маршрут через ожидание, возврат к заполнению и завершение из FillingOrderInformation.Нужен.
BeginBuyerOrder: финальная проверкаОписаны товары, папка документов и наличие изделия при условии запуска через согласование чертежа.Проверяются товары, условие запуска через чертеж и FilesFolder.UI умеет создать папку из формы первой задачи.Совпадает.Низкий.Закрепить тестом невозможность завершить первую задачу без FilesFolder.Нужен.
BeginBuyerOrder: сохранение заказаДокументация описывает редактируемые блоки заказа.TaskService.UpdateData требует BuyerOrder, BuyerOrderService.Update обновляет заказ через полный BuyerOrderCustomRequest.mapToTaskUpdate отправляет полный buyerOrder, собранный из transformBuyerOrderData.Сейчас согласовано, но контракт хрупкий.Высокий при развитии форм и частичных payload.Явно зафиксировать контракт: полный replace или patch. Для полного replace добавить regression-тесты на сохранение nullable-полей.Нужен.
BeginBuyerOrder: проценты оплатыДокументация пока не фиксирует правило суммы процентов.На первом шаге нет проверки advancePaymentPercent + paymentBeforeShipmentPercent <= 100; строгая проверка есть позже в CreateCommercialProposal.UI показывает ошибку при сумме больше 100, но схема формы не блокирует сохранение.Рассинхрон frontend/backend и неявное бизнес-правило.Средний: некорректные условия оплаты можно сохранить до следующего этапа.Решить, где живет правило. Если сумма больше 100 недопустима всегда - добавить валидацию в общую схему и backend update/final check.Нужен.
CalcBuyerOrderAmount: создание и пропускЗадача создается после первой задачи или пропускается, если расчет не нужен.SetBeginBuyerOrderTaskDone создает CalcBuyerOrderAmount; при ShouldSkipCalcBuyerOrderAmountTask = true создает ее в Skipped и затем CreateCommercialProposal.Пользователь видит либо активную задачу расчета, либо следующую задачу создания КП.Совпадает.Низкий для обычного пути, средний для корректировок.Закрепить тестом обе ветки: расчет нужен и расчет пропущен.Нужен.
CalcBuyerOrderAmount: переходы статусовРазрешены ReadyForWork -> InProgress/Completed, InProgress -> ReadyForWork/Completed.EnTaskType реализует тот же набор переходов.В ReadyForWork форма readonly; для ручного редактирования нужен InProgress.Совпадает.Низкий.Оставить как есть, тестировать прямое завершение без редактирования.Нужен.
CalcBuyerOrderAmount: финальная проверкаУ всех товаров должна быть цена больше 0, товары и изделия должны быть согласованы.Проверяется ApprovalInProgress и Price > 0; при финале копируется РРЦ и создается CommercialProposal.UI дает редактировать цены и РРЦ через модальное окно товара.В целом совпадает.Средний: критично для следующего этапа КП.Закрепить тестом блокировки завершения при товаре без цены и при ApprovalInProgress.Нужен.
CalcBuyerOrderAmount: комментарий заказаКомментарий к заказу заявлен как редактируемое поле.BuyerOrderService.UpdateAmount обновляет Note только если строка непустая.Поле комментария на странице расчета стоимости не найдено.Рассинхрон документации/frontend/backend.Средний: пользователь не может выполнить описанное действие; очистка комментария невозможна даже при добавлении поля.Добавить поле комментария в UI и изменить backend-контракт: различать "не прислали" и "очистить", либо убрать поле из документации.Нужен.
CalcBuyerOrderAmount: валидация чиселСтоимость и РРЦ должны быть корректными бизнес-значениями.UpdateAmount не запрещает отрицательную доставку или отрицательную РРЦ; финал проверяет только цену товара и согласование.UI для доставки запрещает отрицательное значение, модальные числовые поля зависят от frontend-контролов.Backend принимает слишком доверенный payload.Средний: некорректные значения можно записать через API или регрессию UI.Добавить backend-валидацию диапазонов в UpdateAmount и/или ValidateCalcBuyerOrderAmountFinalData.Нужен.
CreateCommercialProposal: переходы статусовРазрешены ReadyForWork -> FillingData -> CreateFile -> Completed и FillingData -> NotRequired.EnTaskType реализует тот же набор переходов.Форма readonly в ReadyForWork; заполнение начинается после перехода в FillingData.Совпадает с уточнением по readonly-старту.Низкий.Уточнить документацию: в ReadyForWork задача ожидает начала работы, редактирование начинается в FillingData.Нужен.
CreateCommercialProposal: папка документовПапка создается и проверяется ранее, на этапе BeginBuyerOrder; здесь используется уже готовая папка заказа.Финальная проверка требует ссылку на КП и коммерческие поля; request задачи не обновляет FilesFolder.В форме создания КП нет создания/редактирования папки, filesFolder не отправляется.Совпадает после актуализации документации.Низкий для штатного процесса; остаточный риск только для старых или ручных данных.Проверять миграции/ручные данные отдельно; основной процесс закрепить тестом первой задачи.Нужен.
CreateCommercialProposal: генерация КПВ статусе создания файла пользователь формирует КП через генерацию документа, затем проверяет и при необходимости редактирует файл.BuyerOrderService.SetCommercialProposal требует FilesFolder и валидные данные КП, затем сохраняет CommercialProposal.FileLink.Кнопка генерации доступна только в CreateFile; после успеха заполняет commercialProposal.fileLink и isStandard.Совпадает для штатного процесса.Низкий: папка гарантируется предыдущим этапом.Закрепить сквозным тестом BeginBuyerOrder -> CreateCommercialProposal -> генерация КП.Нужен.
CreateCommercialProposal: NotRequiredПри отсутствии КП пропускаются согласование и подписание, затем создается создание спецификации.SetCreateCommercialProposalTaskDone создает AgreeCommercialProposal и SigningCommercialProposal в Skipped, затем CreateSpecification.Пользователь выбирает статус NotRequired; форма сохраняет коммерческие данные перед сменой статуса.Совпадает.Низкий для основного пути, средний для отчетов и корректировок с пропущенными задачами.Закрепить тестом цепочку NotRequired с проверкой PreviousTaskId у всех созданных задач.Нужен.
CreateCommercialProposal: типовой документТиповой документ не пропускает согласование КП автоматически.Ветка пропуска согласования типового КП закомментирована; IsStandard сохраняется, но маршрут всегда идет через AgreeCommercialProposal.Генерация КП выставляет isStandard = true, пользователь видит чекбокс.Совпадает после актуализации документации.Низкий, если бизнес подтвердил текущий маршрут.Проверить, нужен ли пользователю чекбокс Типовой документ, если он не влияет на маршрут, или оставить его как характеристику документа.Нужен.
AgreeCommercialProposal: переходы статусовАктивная задача проходит ReadyForWork -> Completed или ReadyForWork -> InProgress -> Completed.EnTaskType реализует эти переходы; Skipped не является пользовательским переходом активной задачи.Пользователь видит доступные backend-статусы через общий селектор.Совпадает после актуализации документации.Низкий.Закрепить тестом прямое завершение и путь через InProgress.Нужен.
AgreeCommercialProposal: финализация КППри завершении устанавливается дата согласования КП.SetAgreeCommercialProposalFinalData выставляет Approved = true и ApproveDate = DateTime.UtcNow; затем создается SigningCommercialProposal.Форма readonly; пользователь только завершает задачу или создает корректировку.Совпадает.Средний: важно для статусов документа и следующей задачи.Закрепить тестом, что после завершения КП получает Approved/ApproveDate, а следующая задача создана ответственному менеджеру.Нужен.
AgreeCommercialProposal: типовое КПТиповое КП не пропускает согласование автоматически.Ветка пропуска типового КП закомментирована, активная задача создается всегда после CreateCommercialProposal.Completed.После генерации типового КП пользователь все равно попадает на согласование.Совпадает после актуализации документации.Низкий, если бизнес подтвердил текущий маршрут.Если пропуск типового КП больше не нужен, убрать остаточные ожидания из UI/терминологии IsStandard; если нужен - вернуть логику и тесты.Нужен.
AgreeCommercialProposal: сохранение формыФорма предназначена для просмотра, без редактирования коммерческих данных.UpdateData для этого типа не имеет отдельной ветки и фактически выполняет no-op.hideSaveButton скрывает сохранение, но смена статуса из InProgress все равно вызывает UpdateData.Работает, но есть технический шум.Низкий: данные не теряются, но лишний запрос усложняет трассировку.Для readonly-задач рассмотреть режим без mapToTaskUpdate или условие в TaskStatusSelector, чтобы не дергать UpdateData.Опционально.
SigningCommercialProposal: переходы статусовОсновной путь ReadyForWork -> AgreeWithBuyer -> Completed; пропуск только при CreateCommercialProposal.NotRequired.EnTaskType реализует ReadyForWork -> AgreeWithBuyer, AgreeWithBuyer -> ReadyForWork/Completed; Skipped создается предыдущей задачей.Форма становится редактируемой после выхода из ReadyForWork.Совпадает после актуализации документации.Низкий.Закрепить тестом основной путь и ветку NotRequired.Нужен.
SigningCommercialProposal: даты и флаги КППользователь может указать дату отправки клиенту и дату подписания клиентом; при завершении проставляются флаги.UpdateData сохраняет даты, если они переданы; SetSigningCommercialProposalFinalData выставляет Sent/SignedByManagement/SignedByClient, но не даты.UI показывает дату отправки клиенту и дату согласования клиентом.Совпадает, если даты необязательны.Средний для отчетности: флаги могут быть true при пустых датах.Решить, обязательна ли дата подписания клиентом. Если да - добавить backend-валидацию; если нет - закрепить текущий контракт тестом.Нужен.
SigningCommercialProposal: следующая задачаПосле подписания КП создается CreateSpecification.SetSigningCommercialProposalTaskDone создает CreateSpecification в ReadyForWork, исполнитель - текущий исполнитель подписания КП.После завершения пользователь переходит к процессу и видит следующую задачу.Совпадает.Средний: важно для цепочки заказа.Закрепить интеграционным тестом создание спецификации и PreviousTaskId.Нужен.
SigningCommercialProposal: устаревший UIОтправка КП руководству не является частью текущего маршрута подписания КП.Статус SigningWithOur для SigningCommercialProposal недостижим по EnTaskType; используется AgreeWithBuyer.В форме и подсказках остались ветки для SigningWithOur/SigningWithBuyer.Технический долг frontend.Низкий: основной сценарий работает, но подсказки могут не показываться.Убрать недостижимую кнопку для SigningWithOur и добавить описание для AgreeWithBuyer в task-description.tsx.Опционально.
CreateSpecification: основной путьReadyForWork -> FillingData -> CreateFile -> Completed, далее создается согласование спецификации.EnTaskType, ValidateCreateSpecificationInterimData, ValidateCreateSpecificationFinalData и SetCreateSpecificationTaskDone реализуют этот путь.Форма разделяет заполнение данных и формирование файлов; при смене статуса сохраняет buyerOrderSpecification, contract, specification.Совпадает.Средний: шаг готовит документы для запуска заказа.Держать существующий интеграционный тест основного пути и ошибок обязательных данных.Есть.
CreateSpecification: NotRequiredСпецификация может не требоваться, тогда согласование спецификации пропускается и создается запуск.NotRequired скрывается для условий запуска после подписания спецификации; при финале проверяется только SupplyBaseAddress, создаются AgreeSpecification.Skipped и SigningSpecification.ReadyForWork.Статус приходит из backend-доступных статусов; форма показывает блок заполнения данных заказа.Совпадает после уточнения документации.Средний: ветка влияет на всю последующую цепочку запуска.Сохранять покрытие корректировок и пропуска CreateSpecification.NotRequired; отдельно проверять условия запуска, где статус должен быть скрыт.Частично есть.
CreateSpecification: обязательность необходимых документовДокументация раньше помечала "Необходимые документы" как обязательное поле.RequiredDocumentationIds сохраняются, но backend не проверяет обязательность при переходах.Поле доступно в форме, но обязательность не блокирует сохранение.Документация уточнена под текущий backend-контракт.Низкий, если поле действительно справочное; средний, если бизнес считает его обязательным.Если документация была права по бизнесу - добавить backend-валидацию; если нет - оставить поле необязательным.Нужен при изменении правила.
CreateSpecification: назначение следующей задачиСогласование спецификации должен получить руководитель менеджера.SetCreateSpecificationTaskDone берет исполнителя из task.PreviousTask!.PreviousTask!.ExecutorId, опираясь на строгую цепочку процесса.Пользователь видит следующую задачу в процессе.Работает для штатной цепочки.Средний для нестандартных корректировок или ручных данных.Закрепить тестами цепочки Completed и NotRequired с проверкой исполнителей и PreviousTaskId.Частично есть.
AgreeSpecification: переходы статусовАктивная задача проходит ReadyForWork -> Completed или через InProgress; Skipped создается только при отсутствии спецификации.EnTaskType реализует ReadyForWork -> InProgress/Completed, InProgress -> ReadyForWork/Completed; Skipped создается предыдущей задачей.Форма readonly, статус меняется через общий селектор без сохранения данных.Совпадает после уточнения документации.Низкий.Оставить текущий маршрут; держать тест прямого завершения и пути через InProgress.Есть.
AgreeSpecification: финализация документовПри завершении согласуется спецификация и, при необходимости, договор.SetAgreeSpecificationFinalData проставляет Approved/ApproveDate у спецификации и у несогласованного договора.Пользователь видит документы на просмотр; редактирования нет.Совпадает.Средний: важно для последующего подписания и отчетности.Добавить прямую проверку Approved/ApproveDate сразу после завершения AgreeSpecification, не только на следующем шаге.Частично есть.
AgreeSpecification: устаревшие описания пропускаРанее пропуск связывался с типовой спецификацией/договором.Автопропуск по IsStandard для спецификации не реализован; пропуск связан с CreateSpecification.NotRequired.В task-description.tsx есть недостижимое описание NotRequired для ApprovalSpecification.Документация исправлена, frontend-текст остается техническим долгом.Низкий.Убрать NotRequired из описаний ApprovalSpecification или оставить только если такой статус появится в backend.Опционально.
SigningSpecification: основной путьЗапуск проходит через подпись руководством, счет предоплаты, подпись покупателем, ожидание оплаты и создание заказов.Динамические статусы учитывают предоплату и условие запуска; финал создает заказы на производство, планы оплат и SupportOrder.Форма разделена на блоки подписания, счета, ожидания оплаты и создания заказов.Совпадает после уточнения документации.Средний: шаг запускает производство и оплату.Держать сквозной тест с предоплатой и без предоплаты, включая проверку SupportOrder и планов оплат.Частично есть.
SigningSpecification: создание заказовНа статусе создания заказов пользователь может создать заказы на производство для изделий.OrderService.AddOrderData не создает дубль, если заказ уже есть, и добавляет номер/Google-ссылку при необходимости; финал задачи проходит по всем изделиям.Таблица CreatingOrders дает кнопку создания заказа для изделий и просмотр товара.Совпадает.Средний: Google-интеграция и частичные ошибки могут оставлять неполный результат.При ошибке создания одного заказа явно показывать пользователю, какие товары уже созданы, а какие требуют повторного действия.Нужен.
SigningSpecification: CreateSpecification.NotRequiredСпецификация может не требоваться, но запуск все равно создается.UpdateData, SetSigningSpecificationInterimData и ValidateSigningSpecificationFinalData местами обращаются к Specification!, даже если предыдущая задача завершена NotRequired.transformTaskData вернет specification: null, и mapToTaskUpdate отправит null.Баг frontend/backend-сценария.Высокий для заказов, где спецификация не требуется: сохранение или смена статуса может падать.Сделать SigningSpecification null-safe для отсутствующей спецификации и покрыть тестом путь CreateSpecification.NotRequired -> SigningSpecification -> Completed.Нужен.
SigningSpecification: автозавершение по оплатеПосле корректной оплаты задача может закрыться автоматически.HandleSigningSpecificationTaskDone ищет задачу только по заказу и типу, без фильтра активного статуса, затем переводит ее в CreatingOrders и завершает.UI может уже видеть завершенную задачу или частично созданные заказы после банковской выписки.Работает в штатном тесте, но хрупко при повторной обработке.Средний: возможна повторная попытка завершения уже закрытой задачи.Добавить фильтр по активному состоянию/ожидаемому статусу и idempotency-тест повторной обработки оплаты.Нужен.
SigningSpecification: несколько счетов предоплатыВсе счета предоплаты должны быть в согласованном состоянии.Переход в CreatingOrders проверяет все неотмененные счета, финальная проверка смотрит последний неотмененный счет.Фронт показывает планы оплат и счета, но не решает конфликт правил.Спорное/несогласованное поведение.Средний при нескольких счетах или перевыставлении.Выбрать правило: проверять все активные счета или только актуальный счет, и применить одинаково в interim/final/auto paths.Нужен.
SupportOrder: переходы статусовСопровождение проходит через подготовку документации, ожидание производства/доставки, готовность к отправке, финальную работу и завершение.EnTaskType реализует эти переходы, включая прямой переход AwaitingProduction -> Finalizing и ветку AwaitingResponse -> Completed.Форма показывает блоки сопровождения начиная с AwaitingProduction, Finalizing, AwaitingResponse и Completed.Совпадает после уточнения документации.Низкий.Держать тест основного пути и отдельно проверить путь через ReadyToShip.Частично есть.
SupportOrder: папка и документы отгрузкиПри подготовке создается папка, при финальной работе создаются отгрузочные документы.PreparingDocumentation вызывает SetShipmentsFolder; Finalizing вызывает AddShipmentDocuments, создает отгрузку и недостающие документы.UI показывает папку/таблицу отгрузок и кнопку генерации документов.Совпадает после уточнения документации.Средний: завязано на Google Drive и генераторы документов.Закрепить тестом переход Finalizing, создание отгрузки и ссылок документов для выбранных RequiredDocumentations.Частично есть.
SupportOrder: фактическая дата доставкиФактическая дата доставки обязательна только если доставка выполняется за наш счет.ValidateSupportOrderFinalData проверяет ActualDeliveryDate всегда и не загружает DeliveryOurExpense.Форма показывает поле фактической даты доставки вместе с фактической датой отгрузки.Backend-валидация строже бизнес-правила.Средний: заказ без доставки за наш счет нельзя завершить без лишней даты доставки.В финальную проверку добавить DeliveryOurExpense и требовать ActualDeliveryDate только при доставке за наш счет; синхронизировать обязательность поля на frontend.Нужен.
SupportOrder: доступ менеджера продаж к отгрузкамЕсли в заказе нет изделий, сопровождение выполняет ответственный менеджер продаж, значит ему нужны действия по отгрузкам и документам.Backend разрешает SalesManager получать и создавать отгрузки, а редактирующие действия дополнительно проверяют HasAccessToEditing задачи SupportOrder. Рабочий endpoint массового создания документов принимает taskId.UI дает менеджеру продаж просмотр отгрузок и заказов на производство, но PermissionContext.SHIPMENT.edit и маршрут /shipments/:id не разрешают редактирование. Кнопка массовой генерации вызывает тестовый endpoint по buyerOrderId, доступный только Tester.Рассинхрон frontend/backend-доступа и неверный endpoint на кнопке.Высокий для заказов без изделий: менеджер продаж-исполнитель задачи не сможет штатно создать/редактировать отгрузку и сформировать документы из UI.Передавать taskId в POST /Order/BuyerOrder/shipment/documents?taskId=...; в UI разрешать создание/редактирование отгрузки исполнителю активной SupportOrder либо синхронизировать SHIPMENT.edit/route roles с backend-правами.Нужен.
SupportOrder: автоматический ReadyToShipРанее документация описывала автоматический статус при завершении заказов на производство.Активный backend-обработчик автоматического перевода в ReadyToShip не найден; статус доступен как ручной переход.UI получает доступные статусы от backend, отдельной автоматизации не видно.Устаревшая документация или нереализованное ожидание.Низкий, если ручной переход достаточен; средний, если пользователи ждут автоматизации.Решить, нужен ли автоматический переход. Если нет - оставить документацию в ручном варианте; если да - добавить обработчик и тест завершения всех заказов.Нужен при автоматизации.
SupportOrder: архивные товарыЗавершение проверяет актуальные товары заказа.ValidateSupportOrderFinalData и AddShipmentDocuments используют BuyerOrder.Goods без фильтра DateArchived == null.UI обычно показывает неархивные товары, но backend может учитывать архивные.Баг backend-валидации/автосоздания отгрузки.Высокий для заказов, где товар был удален/архивирован после запуска: закрытие может блокироваться или отгрузка создаст лишние строки.Фильтровать активные товары в финальной проверке и автосоздании отгрузки; добавить тест с архивным товаром.Нужен.
SupportOrder: архивные отгрузкиВ задаче должны отображаться актуальные отгрузки.GetDataById выбирает OrderShipments без фильтра DateArchived == null.Таблица отгрузок в UI может дополнительно загружать данные своим запросом, но task data уже содержит архивные отгрузки.Технический долг/потенциальный рассинхрон.Средний: пользователь может увидеть или использовать устаревшие отгрузки, если UI опирается на task data.Добавить фильтр активных отгрузок в GetDataById для SupportOrder.Нужен.
SupportOrder: финальная проверка документовОтгрузочные документы должны быть подготовлены перед закрытием.Финальная проверка не сверяет RequiredDocumentations с файлами документов в отгрузках; проверяет только даты и количество отгрузки.UI дает кнопку генерации документов и таблицу отгрузок.Работает при штатном переходе через Finalizing, но хрупко для ручных/старых данных.Средний.Либо зафиксировать, что документы гарантирует Finalizing, либо добавить финальную проверку файлов по RequiredDocumentations.Нужен.

Следующие шаги

  1. Выписать из документации по каждой задаче заказа покупателя роли, статусы, переходы, поля формы и проверки.
  2. Сверить эти ожидания с EnTaskType, EnTaskStatus и обработчиками статусов в TaskService.
  3. Сверить каждую задачу с frontend-формой и mapToTaskUpdate.
  4. Заполнить матрицу расхождений подтвержденными выводами.