Аудит слаженности процессов
Документ фиксирует методику и результаты проверки того, как согласованы между собой документация, backend, frontend и фактические бизнес-сценарии.
Раздел не заменяет пользовательскую документацию. Он нужен как технический рабочий журнал: сначала здесь фиксируются ожидания и расхождения, затем подтвержденные изменения переносятся в пользовательские разделы и задачи на доработку.
Цель
Проверить, что рабочие процессы выполняются предсказуемо и согласованно:
- документация описывает понятный ожидаемый сценарий;
- backend реализует тот же порядок задач, статусов, валидаций и побочных эффектов;
- frontend показывает пользователю те же действия, поля и ограничения;
- данные не меняются неожиданно при сохранении, смене статуса, корректировках и повторных действиях;
- процесс не зависает и не создает лишние или недостающие задачи.
Источники
Основной источник ожидаемого поведения - весь раздел 70. Рабочий процесс, включая вложенные папки и примеры.
Кодовые источники для сверки:
OlMag.Manufacture.Api/OlMag.Manufacture/Data/Enums/EnTaskProcessType.csOlMag.Manufacture.Api/OlMag.Manufacture/Data/Enums/EnTaskType.csOlMag.Manufacture.Api/OlMag.Manufacture/Data/Enums/EnTaskStatus.csOlMag.Manufacture.Api/OlMag.Manufacture/Services/UserTask/Services/TaskService.csOlMag.Manufacture.Api/OlMag.Manufacture/Services/UserTask/Services/TaskConditionHandler.csOlMag.Manufacture.Api/OlMag.Manufacture/Services/UserTask/Services/ProcessService.cs- frontend-страницы и формы задач в
OlMag.Manufacture/src/pages/tasksиOlMag.Manufacture/src/entities/task
Документация считается ориентиром, но не абсолютной истиной: если она устарела, это фиксируется отдельно как расхождение документации, а не как ошибка кода.
Методика
Для каждого процесса и каждой задачи проверка идет в одном формате.
- Из документации выписываются ожидаемые роли, статусы, переходы, поля формы, проверки, создаваемые следующие задачи и особые сценарии.
- В backend проверяется фактическая state machine: доступные статусы, обработчики смены статуса, валидации, создание задач, изменение состояния заказа и связанные сущности.
- Во frontend проверяется пользовательский путь: загрузка данных,
transformTaskData,defaultValues, видимые поля, скрытые поля,mapToTaskUpdate, сохранение и смена статуса. - Отдельно проверяются побочные эффекты: документы, счета, планы оплат, платежи, отгрузки, Google Drive, уведомления, корректировки.
- Результат фиксируется в матрице: документация -> backend -> frontend -> вывод -> риск -> рекомендация -> нужен ли тест.
Классификация результатов
| Тип | Когда использовать |
|---|---|
| Баг | Код ведет себя не так, как должен работать бизнес-сценарий. |
| Рассинхрон frontend/backend | UI разрешает или отправляет не то, что ожидает 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 | Контроль пользовательской задачи. |
Первый проход: заказ покупателя
Первый глубокий проход выполняется по процессу Заказ покупателя, потому что он связывает задачи, заказ, товары, КП, спецификацию, договор, оплату, счета, отгрузки и сопровождение.
На первом проходе боковая ветка по чертежам на согласование отмечается в реестре, но не проверяется как активный сценарий. Она будет вынесена в отдельный проход, если потребуется.
Ожидаемая основная цепочка из документации:
- Заказ покупателя. Сбор первоначальной информации.
- Заказ покупателя. Расчет и согласование стоимости.
- Заказ покупателя. Создание КП.
- Заказ покупателя. Согласование КП.
- Заказ покупателя. Согласование КП с покупателем.
- Заказ покупателя. Создание спецификации.
- Заказ покупателя. Согласование спецификации.
- Заказ покупателя. Запуск.
- Автоматический выбор сопровождающего при необходимости.
- Заказ покупателя. Сопровождение.
Ключевые правила, которые надо проверить в коде:
- смена статуса задачи сначала сохраняет данные задачи;
- при смене статуса выполняются проверки корректности заполненных данных;
- следующая задача создается только после успешного завершения текущей;
- пропущенные задачи остаются в процессе и должны корректно участвовать в связях 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. Так как форма вReadyForWorkreadonly, этот путь подходит только для уже корректно заполненных данных; иначе финальная проверка блокирует завершение; - комментарий к заказу описан как редактируемый, но 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сохраняется и выставляется при генерации файла, но не влияет на процесс: задача согласования КП создается всегда. Пользовательская документация актуализирована под этот маршрут;- как и в предыдущих задачах, форма в
ReadyForWorkreadonly. Это не ломает текущий маршрут, но в документации лучше явно писать, что заполнение начинается после перехода в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, но дляAgreeCommercialProposalbackend выполняет 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;- при переходе в
CreateFileValidateCreateSpecificationInterimDataпроверяетContractIdиSupplyBaseAddress; - при сохранении
TaskService.UpdateDataтребует рольSalesManagerилиAssistantSalesManager, требуетBuyerOrderSpecification, затем обновляет данные заказа черезrequestBuyerOrder.Adapt(buyerOrder, mapper.Config); RequiredDocumentationIdsсохраняются, если пришли в request, но обязательность этого поля backend не проверяет;- в статусе
CreateFileдополнительно сохраняются ссылка и признак типового договора, если договор еще не подписан клиентом, а также ссылка и признак типовой спецификации; - при завершении не в
NotRequiredValidateCreateSpecificationFinalDataтребует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; при этом frontendPermissionContext.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-защиты лучше явно запрещать нулевые/некорректные даты.
Матрица проверки
| Сценарий | Документация | Backend | Frontend | Вывод | Риск | Рекомендация | Тест |
|---|---|---|---|---|---|---|---|
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. | Нужен. |
Следующие шаги
- Выписать из документации по каждой задаче заказа покупателя роли, статусы, переходы, поля формы и проверки.
- Сверить эти ожидания с
EnTaskType,EnTaskStatusи обработчиками статусов вTaskService. - Сверить каждую задачу с frontend-формой и
mapToTaskUpdate. - Заполнить матрицу расхождений подтвержденными выводами.