Аудит импорта данных из 1С
Документ фиксирует план проверки импорта данных из 1С и рабочую матрицу для последующего аудита.
Раздел не заменяет пользовательскую инструкцию по импорту. Его задача - проверить, что Excel-выгрузки из 1С, backend-обработка, frontend-интерфейс, права доступа и связанные бизнес-сущности работают согласованно и предсказуемо.
Цель
Проверить импорт из 1С как связанный системный сценарий:
- пользователь понимает, какой файл и в каком порядке нужно загрузить;
- frontend вызывает правильный endpoint и корректно показывает результат;
- backend валидирует файл, обязательные колонки и данные строк;
- импорт не создает дубли при повторной загрузке того же файла;
- существующие данные обновляются только ожидаемым образом;
- связанные сущности заказа покупателя, товаров, счетов, оплат, задач и статусов не расходятся;
- ошибки по отдельным строкам не скрываются и не выглядят как успешный импорт;
- права доступа соответствуют бизнес-ролям.
Ограничение по объему работ
Функционал импорта из 1С нужен для единственного запуска, максимум для двух запусков. Поэтому аудит и последующие задачи должны быть прагматичными.
Главная цель - безопасно провести импорт и не повредить данные. Не нужно превращать этот сценарий в полноценную долгосрочную подсистему синхронизации, если для этого нет отдельного решения.
Приоритеты:
- Найти ошибки, которые могут испортить данные, создать дубли или сломать связи.
- Проверить, что повторный запуск того же файла не ухудшит состояние данных.
- Проверить, что пользователь увидит критичные ошибки импорта.
- Исправлять только то, что реально влияет на безопасный разовый запуск.
- Перед боевым импортом сделать backup базы и сохранить исходный файл импорта.
Не тратить время без отдельного решения:
- на красивый preview-режим импорта;
- на сложную историю запусков и журналирование исходных файлов;
- на расширенный UI управления импортами;
- на универсальный механизм маппинга колонок;
- на долгосрочную поддержку разных форматов выгрузки из 1С;
- на глубокую оптимизацию производительности, если объем файла спокойно обрабатывается текущим способом.
Если найденное поведение неудобное, но не опасное для одного-двух запусков, его нужно фиксировать как низкий приоритет или не выносить в задачу.
Область проверки
Текущая документация описывает импорт через Excel-выгрузки из 1С. В первый проход входят:
- Импорт заказов покупателя.
- Импорт товаров в заказах покупателя.
- Импорт счетов на оплату.
- Корректность создания задач процесса заказа покупателя для импортированных заказов.
- Импорт/синхронизация заказов на производство для импортированных заказов покупателя. Эта часть пока в разработке и проверяется по текущей ветке.
- Связанный сценарий банковских выписок и платежей, если при проверке счетов понадобится подтвердить оплату и автозавершение задач.
Не входят в первый проход:
- Google-таблицы, кроме текущего сценария синхронизации заказов на производство, если он используется для импортированных заказов покупателя;
- ручное создание заказов, товаров и счетов вне связи с импортом;
- полная проверка процесса заказа покупателя, кроме точек, на которые импорт влияет напрямую.
Источники
Документация
| Файл | Назначение |
|---|---|
40. Синхронизация данных/1C.md | Краткое описание импорта из 1С. |
2.Заказы/buyerOrder.md | Пользовательское описание заказа покупателя. |
2.Заказы/good.md | Пользовательское описание товара в заказе. |
2.Заказы/paymentInvoice.md | Пользовательское описание счетов на оплату. |
2.Заказы/bankStatement.md | Пользовательское описание банковских выписок. |
2.Заказы/Внутренние сущности/invoicePayment.md | Платежи по счетам. |
10-architecture/process-audit.md | Проверка влияния импорта на процесс заказа покупателя. |
Документация считается ориентиром, но не абсолютной истиной: если она устарела, это фиксируется отдельно.
Backend
| Файл | Что проверить |
|---|---|
Controllers/OrderControllers/BuyerOrderController.cs | Endpoint POST /Order/BuyerOrder/1C, права и входной request. |
Controllers/OrderControllers/GoodController.cs | Endpoint POST /Order/Good/1C, права и входной request. |
Controllers/OrderControllers/PaymentInvoiceController.cs | Endpoint POST /Order/PaymentInvoice/1C, права и входной request. |
Controllers/OrderControllers/OrderController.cs | Endpoint синхронизации заказов на производство из текущей ветки. |
Services/Order/Services/BuyerOrderService.cs | Создание/обновление заказов покупателя из файла. |
Services/Order/Services/GoodService.cs | Создание/обновление товаров заказа из файла. |
Services/Order/Services/PaymentInvoiceService.cs | Создание/обновление счетов на оплату из файла. |
Services/Order/Services/OrderService.cs | Создание/обновление заказов на производство и привязка к товарам заказа покупателя. |
Services/UserTask/Services/TaskService.cs | Сохранение данных задач и смена статусов процесса заказа покупателя. |
Services/UserTask/Services/TaskConditionHandler.cs | Финальные проверки задач процесса заказа покупателя. |
Services/UserTask/Services/ProcessService.cs | Создание и связность процесса заказа покупателя. |
Helpers/FileGenerators/FileBuyerOrderReader.cs | Разбор Excel-файла заказов покупателя. |
Helpers/FileGenerators/FileGoodReader.cs | Разбор Excel-файла товаров. |
Helpers/FileGenerators/FilePaymentInvoiceReader.cs | Разбор Excel-файла счетов. |
Services/Shared/Services/GoogleTableService.cs | Чтение заказов на производство из Google-таблицы в текущей ветке. |
Helpers/SynchronizeHelper.cs | Общая обработка результатов синхронизации. |
Models/Responses/Shared/SynchronizeResponse.cs | Контракт результата импорта и сообщений пользователю. |
Frontend
| Файл | Что проверить |
|---|---|
src/pages/buyer-orders/buyer-orders.tsx | Кнопки импорта, права видимости, обновление таблицы после импорта. |
src/shared/ui/import/import-1C.tsx | Диалог выбора файла, запуск импорта, вывод результата и сообщений. |
src/entities/buyer-orders/api/buyer-order.api.ts | RTK endpoints импорта и инвалидация кеша. |
src/entities/production-orders | Отображение заказов на производство и связь с заказом покупателя. |
src/entities/task/ui/specification/specification-creating-orders.tsx | Сценарий создания/проверки заказов на производство из задачи запуска. |
src/entities/task/ui/support-order/support-order.tsx | Просмотр заказов на производство на этапе сопровождения. |
src/shared/config/field-permissions.config.ts | Доступность импорта по ролям и правам. |
Методика
Проверка идет по одному формату для каждого вида импорта.
- Выписать ожидаемый пользовательский сценарий: кто запускает импорт, какой файл загружает, что должен получить в результате.
- Проверить frontend: доступность кнопки, выбор файла, ограничения по формату, вызов endpoint, обработку успеха и ошибок.
- Проверить backend-контракт: endpoint, авторизацию, request, формат ответа, коды ошибок.
- Проверить парсер Excel: обязательные колонки, типы значений, пустые строки, дубли, некорректные даты и суммы.
- Проверить бизнес-логику: создание новых данных, обновление существующих, связи с заказами, товарами, счетами, оплатами и задачами.
- Проверить базовую идемпотентность: повторная загрузка того же файла не должна создавать дубли и ломать связи.
- Проверить частичные ошибки: одна плохая строка не должна незаметно портить общий результат.
- Зафиксировать вывод в матрице: ожидание -> frontend -> backend -> данные -> риск -> рекомендация -> тест.
Рекомендации формулировать с учетом разового характера импорта: сначала предлагать быстрые защитные проверки и ручной контроль, а полноценные архитектурные доработки выносить только для критичных рисков.
Тестовый прогон на копии базы не предполагается: файл с заказами будет загружаться сразу на прод. Поэтому контроль переносится в три точки:
- Предварительная ручная проверка файла до загрузки.
- Backup базы непосредственно перед импортом.
- Контрольная сверка результата сразу после импорта.
Классификация результатов
| Тип | Когда использовать |
|---|---|
| Баг | Импорт создает неверные данные, дубли, ломает связи или ошибочно меняет статус. |
| Рассинхрон frontend/backend | Frontend показывает действие или успех иначе, чем реально отработал backend. |
| Устаревшая документация | Код и интерфейс согласованы, но документация описывает старый порядок. |
| Спорное поведение | Нужен продуктовый выбор: например, перетирать поле импортом или сохранять ручное изменение. |
| Технический долг | Работает сейчас, но хрупко к изменению формата 1С или расширению импорта. |
| Нужен тест | Поведение критично и должно быть закреплено автоматической проверкой. |
Порядок аудита
Шаг 1. Карта текущего импорта
Цель: зафиксировать фактические endpoint, роли, frontend-кнопки, request/response и типы файлов.
Проверить:
- какие импорты доступны пользователю;
- где расположены кнопки импорта;
- какие роли видят кнопки;
- какие backend endpoints вызываются;
- какой response возвращается пользователю;
- какие счетчики и сообщения показывает интерфейс.
Результат: таблица фактических импортов и точек входа.
Результат шага 1, выполнено 09.07.2026:
| Сценарий | Frontend-точка входа | Frontend API | Backend endpoint | Права на frontend | Права на backend | Response | Обновление данных на frontend |
|---|---|---|---|---|---|---|---|
| Импорт заказов покупателя из 1С | Страница заказов покупателя, кнопка Импорт заказов из 1С | useImportBuyerOrders1CMutation | POST /Order/BuyerOrder/1C | PermissionContext.BUYER_ORDERS, поле import1C, роль ChiefSalesManager | EnRole.ChiefSalesManager | SynchronizeResponse | Инвалидируется BuyerOrders |
| Импорт товаров из 1С | Страница заказов покупателя, кнопка Импорт товаров из 1С | useImportGoods1CMutation | POST /Order/Good/1C | PermissionContext.BUYER_ORDERS, поле import1C, роль ChiefSalesManager | EnRole.ChiefSalesManager | SynchronizeResponse | Инвалидируются Goods, BuyerOrders, Task/LIST |
| Импорт счетов на оплату из 1С | Страница заказов покупателя, кнопка Импорт счетов для оплаты из 1С | useImportPaymentInvoice1CMutation | POST /Order/PaymentInvoice/1C | PermissionContext.BUYER_ORDERS, поле import1C, роль ChiefSalesManager | EnRole.ChiefSalesManager | SynchronizeResponse | Инвалидируется BuyerOrders |
| Синхронизация заказов на производство | Frontend-точки запуска не будет: endpoint запускает внешняя программа по расписанию | Не используется | POST /Order/Order/google | Не требуется | EnRole.ChiefSalesManager | SynchronizeResponse | Не требуется, запуск выполняется вне frontend |
Фактический SynchronizeResponse:
readed/Readed- количество считанных строк;updated/Updated- количество обновленных сущностей;added/Added- количество добавленных сущностей;syncMessages/SyncMessages- сообщения с уровнямиLog = 1,Warning = 2,Error = 3.
Диалог импорта из 1С:
- принимает только
.xlsx; - после успешного ответа показывает строку
Считано: ..., обновлено: ..., добавлено: ...; - показывает каждое сообщение
syncMessagesотдельной строкой с иконкой по уровню; - после успешного HTTP-ответа дополнительно показывает toast об успешном импорте;
- не закрывает автоматически результат импорта, пока пользователь не закроет диалог.
Наблюдения для следующих шагов:
- frontend и backend по трем Excel-импортам из 1С согласованы по роли
ChiefSalesManager; - синхронизация заказов на производство запускается не из frontend, а внешней программой по расписанию один раз в день; на шаге синхронизации заказов на производство нужно проверить безопасность ежедневного повторного вызова endpoint;
- success-toast показывается по факту успешного HTTP-ответа, даже если внутри
syncMessagesесть предупреждения или ошибки. На шаге UX нужно проверить, не вводит ли это пользователя в заблуждение при частично проблемном импорте.
Шаг 2. Импорт заказов покупателя
Цель: проверить создание и обновление заказов покупателя из 1С.
Проверить:
- обязательные колонки файла;
- ключ, по которому определяется существующий заказ;
- создание нового заказа;
- обновление существующего заказа;
- поведение при изменении покупателя, организации, номера, даты, суммы, валюты и ответственного;
- что происходит с заказом, у которого уже есть активный рабочий процесс;
- что происходит с заказом, который был изменен вручную после предыдущего импорта;
- повторный импорт того же файла;
- импорт файла с дублями строк по одному заказу;
- сообщения пользователю по добавленным, обновленным и ошибочным строкам.
Особое внимание:
- не должен создаваться второй процесс по тому же заказу покупателя, если процесс уже есть;
- для нового импортированного заказа должен создаваться ожидаемый процесс
BuyerOrderи стартовая задача; - повторный импорт того же заказа не должен создавать повторный процесс или повторную стартовую задачу;
- импорт не должен незаметно очищать поля, которые отсутствуют в выгрузке;
- ручные данные должны перетираться только по заранее определенным правилам.
Результат шага 2, выполнено 09.07.2026:
Фактическая схема импорта:
- файл читается через
FileBuyerOrderReader; - заголовки ищутся в строке 7, данные начинаются со строки 8;
- обязательные колонки проверяются по точному названию;
- заказ определяется по ссылке вида
Заказ покупателя N от dd.MM.yyyy; - существующий заказ ищется по ключу
Number + OurOrganizationId; - в БД есть уникальный индекс по
Number + OurOrganizationIdдля неархивных заказов; - новые заказы добавляются через
AddBuyerOrders; - существующие заказы обновляются через
UpdateBuyerOrders; - для новых не завершенных и не отмененных заказов создается процесс
BuyerOrderи стартовая задачаBeginBuyerOrder; - повторный импорт уже созданного заказа не должен создавать новый процесс, так как заказ попадает в ветку обновления.
Как обрабатываются строки файла:
| Ситуация | Поведение |
|---|---|
| Не найден номер заказа | Строка пропускается. |
| Не указан или не разобран автор | Строка пропускается. |
| Не указан ИНН покупателя | Строка пропускается. |
| Не указана или не распознана наша организация | Строка пропускается. |
| Не найден покупатель по ИНН/КПП | Строка пропускается. Если по ИНН найден ровно один покупатель, он используется с предупреждением. |
| Контрагент найден, но не является покупателем | Строка пропускается. |
| Не указан адрес доставки | Заказ не пропускается, адрес заменяется на ???, в примечание добавляется сообщение. |
| Не указана дата отгрузки | Заказ не пропускается, добавляется предупреждение. |
| Не указана валюта | Используется рубль, добавляется предупреждение. |
| Не указано контактное лицо | Заказ не пропускается, контактное лицо заменяется на ???. |
| Не указан или не разобран ответственный | Заказ не пропускается, задача может быть создана без исполнителя. |
Фактическое обновление существующего заказа:
- всегда обновляются автор, ответственный, контактное лицо, валюта, наша организация;
- договор обновляется только если в импортированной строке он найден;
- адрес доставки перезаписывается всегда;
- плановая дата отгрузки перезаписывается только если она есть в импортированной строке;
- примечание перезаписывается только если оно есть в импортированной строке;
- состояние заказа обновляется только при наличии причины отмены: заказ переводится в
Cancelled; - остальные статусы из 1С для существующего заказа не переносятся в
StateId.
Найденные риски:
| Риск | Критичность | Почему важно для разового запуска | Рекомендация |
|---|---|---|---|
| Дубли заказов внутри одного файла не отфильтровываются до добавления. | Высокая | Если в одном файле есть две новые строки с одинаковым Number + OurOrganizationId, импорт может упасть на уникальном индексе и оставить уже сохраненные подготовительные сущности. | Перед запуском проверить файл на дубли. На backend желательно добавить быстрый guard: дубли в файле отдавать в syncMessages и не запускать сохранение. |
| Импорт не выполняется одной транзакцией. | Средняя | Контактные лица, телефоны, сотрудники, подписанты или договоры могут сохраниться до того, как создание заказов упадет. Для одного запуска это терпимо, но повторный запуск будет идти уже по измененной базе. | Перед боевым запуском сделать backup базы. Критичные ошибки файла лучше ловить до сохранения. |
Если в файле указан подписант покупателя, у SignerEntity не заполняется PartnerId. | Высокая | PartnerId обязателен. Строки с подписантом могут привести к ошибке сохранения подписантов или создать некорректную подготовительную сущность, если в базе есть неожиданные данные. | Перед запуском проверить строки с колонкой Контактное лицо подписант. Быстрое исправление: при импорте ставить BuyerSigner.PartnerId равным покупателю заказа. |
| Банковские реквизиты нашей организации из договора фактически могут не привязаться к договору. | Средняя | В SetContracts договор создается без банковских реквизитов, а последующее обновление реквизитов построено хрупко и, судя по коду, не сработает для нового договора. | Для разового запуска решить, нужны ли банковские реквизиты договора сразу после импорта. Если нужны - исправить привязку или проверить вручную после импорта. |
| Дата договора из файла при создании нового договора не используется. | Средняя | Новый договор создается как импортированный, но дата из 1С не переносится в создаваемый ContractEntity. | Проверить на тестовом запуске, какая дата оказывается в договоре. Если дата нужна для дальнейших документов - исправить перед боевым импортом. |
| При повторном импорте отмененного заказа каждый раз добавляется новая причина отмены. | Средняя | Повторная загрузка файла может засорить историю причин отмены и изменить PreviousStateId на уже отмененный статус. | Добавить guard: не добавлять такую же причину повторно для уже отмененного заказа. Минимум - не загружать повторно файл отмен после успешного импорта. |
| Новая активная строка с отсутствующим ответственным не блокируется. | Высокая | Для такого заказа создается стартовая задача без исполнителя, и процесс может зависнуть сразу после импорта. | Перед запуском проверить, что у всех активных заказов есть распознанный ответственный. Лучше сделать отсутствие ответственного блокирующей ошибкой для активных заказов. |
| Статус заказа из 1С и созданная стартовая задача могут расходиться. | Средняя | Например, строка со статусом В работе получит состояние ProductionStarted, но процесс будет создан со стартовой задачей BeginBuyerOrder. | На следующем шаге отдельно проверить правила создания задач для импортированных заказов. Для разового запуска решить: импортировать только заказы, которые должны стартовать с первой задачи, или принудительно нормализовать статус. |
Возможен пропуск последней строки файла, если sheet.LastRow в Spire означает номер последней непустой строки. | Средняя | Цикл читает rowNumber < sheet.LastRow. Если библиотека возвращает именно последнюю строку, последняя строка выгрузки не попадет в импорт. | Проверить на тестовом файле с одной контрольной последней строкой. Если строка пропускается - заменить условие на <= sheet.LastRow. |
Вывод по шагу:
- базовый повторный импорт уже существующих заказов защищен ключом
Number + OurOrganizationIdи не должен создавать повторные процессы; - для боевого разового запуска главный риск не в долгой архитектуре, а в качестве файла: дубли, отсутствующий ответственный, строки с подписантом и договорными реквизитами;
- перед запуском обязательно сделать backup базы, сохранить исходный файл импорта и вручную проверить файл на опасные случаи;
- сразу после импорта нужна ручная сверка количества строк: сколько заказов прочитано, сколько добавлено, сколько обновлено, сколько пропущено;
- быстрые исправления, которые стоит рассмотреть до запуска: guard на дубли в файле, блокировка активных заказов без ответственного, корректное заполнение
PartnerIdподписанта, защита от повторного добавления причины отмены.
Шаг 3. Задачи процесса заказа покупателя после импорта
Цель: проверить, что импортированные заказы покупателя корректно попадают в рабочий процесс.
Проверить:
- создается ли процесс
BuyerOrderдля нового импортированного заказа; - создается ли стартовая задача процесса;
- корректно ли заполнены автор, исполнитель, наблюдатели,
BuyerOrderId,ProcessId,PreviousTaskIdиNextTaskId; - соответствует ли стартовый статус задачи ожидаемому сценарию;
- не создается ли дубль процесса при повторном импорте того же заказа;
- не создается ли дубль задачи при повторном импорте или обновлении существующего заказа;
- можно ли открыть импортированный заказ и его задачу на frontend;
- не теряются ли импортированные поля заказа при первом сохранении задачи;
- корректно ли процесс проходит хотя бы до этапа, на котором должны быть созданы или импортированы заказы на производство.
Особое внимание:
- для разового запуска достаточно проверить основной путь и повторный импорт, не нужно полностью заново аудировать весь процесс заказа покупателя;
- критичны только ошибки, которые оставляют импортированный заказ без задачи, создают дубли процесса или ломают переход к этапу запуска.
Результат шага 3, выполнено 09.07.2026:
Фактическая схема создания процесса:
- процесс и стартовая задача для импортированного заказа создаются в
BuyerOrderService.AddBuyerOrders, а не через общий метод обычного создания заказа; - процесс создается только для нового заказа, если состояние заказа не
Cancelledи неCompleted; - тип процесса:
BuyerOrder; - стартовая задача:
BeginBuyerOrder; - стартовый статус задачи:
FillingOrderInformation; BuyerOrderIdзаполняется через связьBuyerOrder = buyerOrder;ProcessIdзаполняется через связьProcess = process;PreviousTaskIdу стартовой задачи пустой, что корректно для первой задачи процесса;ExecutorIdравенbuyerOrder.ResponsibleId;- запись истории статуса создается сразу,
EmployeeIdберется изResponsibleId, а если его нет - изAuthorId; - повторный импорт уже существующего заказа идет через ветку обновления и не создает повторный процесс или повторную стартовую задачу.
Отличия импортной стартовой задачи от обычной:
| Поле | Обычное создание заказа | Импорт из 1С | Риск |
|---|---|---|---|
AuthorId | Ответственный менеджер | Не заполняется | Ниже информативность задачи; автор не участвует в доступе к задаче. |
Deadline | Заполняется настройкой | Не заполняется | Импортированные стартовые задачи могут выпадать из контроля сроков. |
ExecutorId | Ответственный менеджер | Ответственный из файла 1С | Если ответственный не распознан, задача создается без исполнителя. |
TaskStatusId | FillingOrderInformation | FillingOrderInformation | Совпадает. |
DateOfStatusChanges | Создается запись | Создается запись | При пустом ответственном история статуса записывается на автора заказа, не на исполнителя. |
Найденные риски:
| Риск | Критичность | Почему важно для разового запуска | Рекомендация |
|---|---|---|---|
| Активный импортированный заказ может получить задачу без исполнителя. | Высокая | Если ответственный в файле пустой или не распознан, ExecutorId у BeginBuyerOrder будет null. Доступ к редактированию задачи проверяется через исполнителя. | Перед боевым импортом проверить всех ответственных. Быстрое backend-исправление: для активных заказов отсутствие распознанного ответственного делать блокирующей ошибкой строки. |
Импортная стартовая задача создается без AuthorId и Deadline. | Средняя | Обычная задача имеет автора и дедлайн, а импортная - нет. Это может ухудшить отображение, фильтрацию и контроль сроков импортированных задач. | Использовать для импорта тот же helper создания стартовой задачи или синхронизировать поля: AuthorId = ResponsibleId, Deadline = GetTaskDeadline(BeginBuyerOrder). |
| Состояние заказа из 1С может не соответствовать стартовой задаче процесса. | Средняя | Строки со статусами В работе или Отгрузка получают состояние ProductionStarted, но процесс все равно стартует с BeginBuyerOrder. | Для разового запуска заранее решить допустимые статусы файла. Надежнее импортировать активные незавершенные заказы в состояние BeginBuyerOrder или явно пропускать поздние этапы. |
| Повторный импорт не восстанавливает процесс, если заказ уже существует, но процесс был утерян. | Низкая | Ветка обновления не создает недостающую задачу. Для штатного повторного импорта это нормально, но ручная правка БД или старые данные останутся без процесса. | Для одного запуска достаточно контрольной сверки: у каждого добавленного активного заказа есть BeginBuyerOrder. Полноценный repair-механизм не обязателен. |
Вывод по шагу:
- основной путь создания процесса для нового активного заказа есть;
- дубль процесса и стартовой задачи при повторном импорте того же заказа не создается;
- самый опасный крайний случай - активная строка без распознанного ответственного: заказ будет создан, но задача окажется без исполнителя;
- перед прод-импортом нужен ручной контроль ответственных и статусов 1С;
- быстрые исправления, которые стоит рассмотреть до запуска: блокировать активные строки без ответственного и заполнять у импортной стартовой задачи
AuthorId/Deadlineтак же, как при обычном создании заказа.
Шаг 4. Импорт товаров заказов покупателя
Цель: проверить создание и обновление товаров внутри заказов.
Проверить:
- как строка товара связывается с заказом покупателя;
- как определяется существующий товар;
- создание нового товара;
- обновление количества, цены, номенклатуры, характеристик и сроков;
- поведение, если заказ покупателя не найден;
- поведение, если товар уже участвует в задачах, заказах на производство, отгрузках или документах;
- повторный импорт того же файла;
- импорт с дублями строк товара;
- блокирующее ограничение на дубли изделий внутри одного заказа покупателя;
- влияние на расчет стоимости, КП, спецификацию и запуск.
Особое внимание:
- импорт товара не должен ломать уже согласованные данные без явного правила;
- изменения товаров должны быть согласованы с текущим этапом процесса заказа покупателя;
- дубли строк допускаются только для складских единиц, которые не являются изделиями; если внутри одного заказа покупателя есть несколько строк одного изделия, товары этого заказа нельзя импортировать, нужно вернуть ошибку;
- пользователь должен видеть строки, которые не удалось импортировать.
Результат шага 4, выполнено 09.07.2026:
Фактическая схема импорта:
- endpoint:
POST /Order/Good/1C, доступен ролиChiefSalesManager; - файл читается через
FileGoodReader; - заголовки ищутся в строке 7, данные начинаются со строки 8;
- обязательные колонки:
Ссылка.Номер,Тип номенклатуры: запас,Номенклатура,Дата отгрузки,Цена,Количество,РРЦ; - заказ покупателя определяется по значению
Ссылка.Номер: из префикса берется наша организация, из второй части - номер заказа; - заказ ищется по ключу
Number + OurOrganization.Name; - складская единица ищется по точному
StockKeepingUnit.Name; - существующий товар определяется по ключу
BuyerOrderId + StockKeepingUnitId; - одинаковые SKU в одном заказе могут быть отдельными позициями 1С с разным количеством и ценой, но безопасно допускать такие дубли только для складских единиц, которые не являются изделиями;
- дубли изделий внутри одного заказа покупателя должны быть блокирующей ошибкой: товары такого заказа не импортируются, пользователю возвращается сообщение с номером заказа и номенклатурой изделия;
- текущий код при первом импорте может сохранить одинаковые SKU отдельными строками, так как уникального индекса на
BuyerOrderId + StockKeepingUnitIdнет; - для существующего товара обновляются
Count,Price, аRecommendedRetailPriceобновляется только если в файле есть непустое значение; - для нового товара создается запись
Good; - если в файле указана дата отгрузки товара, импорт обновляет у заказа покупателя
PlannedShipmentDateиActualShipmentDate; - если тип складской единицы из файла отличается от текущего типа SKU, импорт может обновить тип складской единицы;
- frontend после импорта инвалидирует
Goods,BuyerOrdersи список задач.
Как обрабатываются строки файла:
| Ситуация | Поведение |
|---|---|
| Не указана номенклатура | Строка пропускается. |
| Не указан номер заказа покупателя | Строка пропускается. |
| Не указана или не распознана наша организация | Строка пропускается. |
| Не указано количество | Строка пропускается. |
| Не указан тип номенклатуры | Строка пропускается. |
| Не найден заказ покупателя | Строка удаляется из импорта, добавляется предупреждение. |
| Не найдена складская единица | Строка удаляется из импорта, добавляется предупреждение. |
| Не найден подходящий тип складской единицы | Строка удаляется из импорта, добавляется предупреждение. |
| В одном заказе покупателя есть несколько строк одного изделия | Требуемое поведение: товары этого заказа не импортируются, возвращается ошибка с номером заказа и изделием. |
| В одном заказе покупателя есть несколько строк одной складской единицы, которая не является изделием | Допускается, строки импортируются как отдельные позиции. |
| Цена пустая или не распознана | Товар не пропускается, Price становится null; позднее задача расчета стоимости заблокирует финал. |
| РРЦ пустая или не распознана | Товар не пропускается, существующая RecommendedRetailPrice не перетирается пустым значением. |
Найденные риски:
| Риск | Критичность | Почему важно для разового запуска | Рекомендация |
|---|---|---|---|
| Повторный импорт товаров с несколькими одинаковыми SKU в одном заказе работает хрупко. | Высокая | Дубли не-изделий должны сохраниться отдельными позициями, но обновление ищет существующий товар только по BuyerOrderId + StockKeepingUnitId и берет первый найденный товар. При повторном импорте несколько строк файла могут обновлять одну и ту же запись. | Дубли изделий сделать блокирующей ошибкой на уровне заказа. Для дублей не-изделий разовый запуск лучше выполнять один раз и после этого сверять строки. Если нужен повторный импорт, нужен устойчивый ключ позиции: идентификатор строки 1С, номер строки или порядковый номер внутри группы BuyerOrder + SKU. |
| Дубли изделий внутри одного заказа покупателя могут сломать дальнейший импорт заказов на производство. | Высокая | Заказ на производство должен быть привязан к конкретному изделию. Если в заказе покупателя несколько строк одного изделия, дальнейшая синхронизация по номенклатуре не сможет выбрать правильную позицию. | Добавить предварительную валидацию файла товаров: сгруппировать строки по BuyerOrder + StockKeepingUnit, определить категорию SKU и, если это изделие и строк больше одной, не импортировать товары этого заказа, вернуть ошибку. |
У товаров, созданных или обновленных импортом, не пересчитывается FinalPrice. | Высокая | Ручное добавление/изменение товара вызывает RecalculateGoodPrices, а импорт меняет только Price и Count. Суммы заказа, КП и планы оплат используют FinalPrice. | После добавления/обновления товаров импортом пересчитать FinalPrice по каждому затронутому заказу тем же методом, что ручной сценарий, либо после импорта выполнить контрольную сверку сумм. |
| Импорт может менять товары независимо от текущего этапа процесса заказа покупателя. | Средняя | Ручной сценарий проверяет CanModifyOrderGoods, а импорт эту проверку не использует. Повторная загрузка файла после согласований может изменить количество и цену. | Для разового запуска зафиксировать порядок: товары импортировать до активной работы по задачам и не перезагружать файл после согласования без ручного решения. Backend-guard можно добавить позже. |
| Импорт товаров обновляет дату отгрузки заказа покупателя без проверки этапа процесса. | Средняя | Файл товаров может перезаписать PlannedShipmentDate и ActualShipmentDate, даже если дата уже была уточнена в системе. | Перед загрузкой проверить колонку даты отгрузки. Если данные из 1С не считаются источником истины после старта работы, отключить это обновление или делать его только для новых заказов. |
| Импорт может менять тип существующей складской единицы. | Средняя | Тип SKU является общей справочной характеристикой, а не свойством товара в конкретном заказе. Ошибка в файле может повлиять не только на импортируемый заказ. | Для разового запуска проверить строки, где тип номенклатуры из файла отличается от типа SKU. Без отдельного решения лучше не менять типы SKU автоматически из файла товаров. |
| Возможен пропуск последней строки файла. | Средняя | FileGoodReader использует цикл rowNumber < sheet.LastRow, как и импорт заказов. Если LastRow - номер последней строки, последняя строка не импортируется. | Проверить контрольную последнюю строку в файле. Если проблема подтверждается - заменить условие на <= sheet.LastRow во всех Excel-reader импортах. |
Вывод по шагу:
- базовая привязка товара к заказу и складской единице есть;
- одинаковые SKU в одном заказе нужно разделить по типу: дубли не-изделий переносить как отдельные позиции, дубли изделий блокировать ошибкой по заказу;
- повторный импорт обычного товара обновляет его по ключу
BuyerOrderId + StockKeepingUnitId, но для нескольких одинаковых SKU этот ключ недостаточен; - для безопасного запуска критично вручную сверить количество строк товаров по заказам и соответствие существующим SKU;
- главный backend-риск - отсутствие пересчета
FinalPriceпосле импорта; - до запуска нужна backend-валидация дублей изделий, чтобы заказ с несколькими одинаковыми изделиями не попал в дальнейший процесс и синхронизацию заказов на производство;
- порядок запуска должен быть строгим: сначала импорт заказов покупателя, затем импорт товаров, затем сверка количества товаров и сумм, и только после этого продолжение задач процесса.
Шаг 5. Синхронизация заказов на производство для импортированных заказов
Цель: проверить сценарий текущей ветки, где заказы на производство подтягиваются для уже импортированных заказов покупателя.
Проверить:
- откуда берутся заказы на производство в текущей ветке и какой endpoint запускает синхронизацию;
- как внешняя программа авторизуется и вызывает endpoint по расписанию;
- что ежедневный повторный запуск не создает дубли и не затирает ручные уточнения;
- обновляются ли статус заказа на производство и дата согласования чертежа так же, как в
PUT /Order/Order/google/{number}; - какой ключ используется для поиска заказа покупателя;
- какой ключ используется для поиска товара внутри заказа покупателя;
- что происходит, если заказ покупателя еще не импортирован;
- что происходит, если товар заказа покупателя еще не импортирован;
- что происходит, если товар найден, но это не изделие;
- что синхронизация запускается только после проверки отсутствия дублей изделий внутри заказов покупателя;
- создается ли заказ на производство только один раз;
- обновляются ли существующие заказы на производство без потери важных данных;
- корректно ли заполняются ссылка на Google-документ, примечание и местоположение производства;
- показывает ли
SynchronizeResponseпредупреждения по пропущенным строкам; - видны ли созданные заказы на производство в карточке заказа покупателя и на этапе сопровождения;
- не конфликтует ли импорт заказов на производство с ручным созданием заказа на производство из задачи запуска.
Особое внимание:
- порядок запуска важен: сначала импорт заказов покупателя, затем импорт товаров, затем синхронизация заказов на производство;
- перед синхронизацией заказов на производство импорт товаров должен заблокировать заказы с дублями изделий; дубли не-изделий не должны участвовать в создании заказов на производство;
- синхронизация заказов на производство будет запускаться каждый день внешней программой, поэтому идемпотентность этого endpoint критична даже при разовом импорте из 1С;
- если строка заказа на производство пропущена из-за отсутствующего заказа покупателя или товара, это должно быть явно видно пользователю;
- для одного-двух запусков допустим ручной контроль пропущенных строк, но недопустимо молчаливое создание заказов без связанного товара.
Результат шага 5, выполнено 09.07.2026:
Фактическая схема синхронизации:
- синхронизация запускается через
POST /Order/Order/google, доступна ролиChiefSalesManager; - в логе метода указано
Order/Order/google/synchronize, поэтому внешней программе запуска нужно ориентироваться на фактический маршрут контроллера, а не на строку лога; - frontend-точки запуска не планируется: endpoint будет вызывать внешняя программа по расписанию;
- данные читаются из Google Sheets через service account, используются
GoogleConfig.OrdersSpreadsheetIdиGoogleConfig.OrdersSheetName; - из таблицы читаются номер заказа на производство, номенклатура, номер заказа покупателя, ссылка на Google-документ, примечание и местоположение производства;
- ключ идемпотентности для заказа на производство -
Order.Number; - существующие заказы на производство в массовой синхронизации сейчас обновляют только
GoogleDocumentLink,NoteиLocationManufactureId; - отдельно существует
PUT /Order/Order/google/{number}для вызова из Google Script: он обновляетStatusIdпоStatusиDates.DateOfApprovalOfDrawingпоDateOfApprovalOfDrawing; - для полной синхронизации массовый endpoint должен обновлять статус и дату согласования чертежа так же, как
PUT /Order/Order/google/{number}; - новые заказы на производство проходят через
FilterAndSetGoods, где им пытаются найтиGoodId, после чего сохраняются черезAddOrders; - предупреждения по пропущенным или подозрительным строкам возвращаются как
SyncMessageс уровнемWarning.
Как обрабатываются строки Google-таблицы:
| Ситуация | Поведение |
|---|---|
| Не указан или не разобран номер заказа на производство | Строка пропускается, добавляется предупреждение. |
| Не указана номенклатура | Строка пропускается, добавляется предупреждение. |
| Не указан или не разобран номер заказа покупателя | Строка пропускается, добавляется предупреждение. |
| Не указана ссылка на Google-документ | Добавляется предупреждение, но строка продолжает обрабатываться. |
| Не найдено местоположение производства | Добавляется предупреждение, LocationManufactureId остается пустым, строка продолжает обрабатываться. |
| В таблице есть несколько строк с одним номером заказа на производство | Добавляется предупреждение, но в обработку попадает последняя строка с этим номером. |
| Заказ покупателя не найден | Заказ на производство пропускается с предупреждением. |
| Товар заказа покупателя не найден | Заказ на производство пропускается с предупреждением. |
| Товар найден, но SKU не является изделием | Заказ на производство пропускается с предупреждением. |
| Заказ на производство уже есть в базе | Сейчас массово обновляются только ссылка на документ, примечание и местоположение производства; статус и дата согласования чертежа не подтягиваются. |
| Заказа на производство еще нет в базе | Текущий код пытается добавить новый OrderEntity, но не заполняет часть обязательных полей. |
Найденные риски:
| Риск | Критичность | Почему важно для разового запуска | Рекомендация |
|---|---|---|---|
| Новые заказы на производство из Google-таблицы могут не создаться. | Высокая | При ручном создании заказа на производство заполняются ProductId, StatusId, TypeId, Dates и дефолтное местоположение. В ветке AddOrders для Google-синхронизации эти поля не заполняются, хотя OrderEntity требует StatusId, DatesId, TypeId и ProductId. Сохранение новых строк может упасть на ограничениях БД. | Перед запуском исправить AddOrders: заполнять заказ теми же обязательными полями, что и ручное создание (ProductId, StatusId = New, TypeId = External, Dates = new OrderDatesEntity(), дефолтное местоположение при необходимости). |
| Массовая синхронизация не обновляет статус и дату согласования чертежа. | Высокая | PUT /Order/Order/google/{number} обновляет StatusId и Dates.DateOfApprovalOfDrawing, а POST /Order/Order/google сейчас обновляет только ссылку, примечание и местоположение. Если ежедневный endpoint должен заменить или дополнить работу Google Script, заказы могут остаться с устаревшим статусом и датой согласования чертежа. | В чтение Google-таблицы и UpdateOrders добавить те же поля и правила, что в UpdateOrderGoogle: парсинг Status через OrderStatusExt.FromText, парсинг DateOfApprovalOfDrawing, понятные warnings/errors при некорректных значениях. |
| Дубли изделий внутри одного заказа покупателя ломают поиск товара для заказа на производство. | Высокая | Заказ на производство создается только для изделия. Если в заказе покупателя есть несколько строк одного изделия, FilterAndSetGoods не может однозначно выбрать позицию по StockKeepingUnit.Name + BuyerOrderId, а ToDictionaryAsync может упасть до формирования понятного предупреждения. | В импорте товаров запретить дубли изделий на уровне заказа: товары такого заказа не импортировать и возвращать ошибку. Дубли не-изделий можно оставить, так как они не должны превращаться в заказы на производство. |
| Заказ покупателя ищется только по номеру без учета нашей организации. | Высокая | Импорт заказов покупателя использует ключ Number + OurOrganizationId, а синхронизация заказов на производство строит словарь только по BuyerOrder.Number. Если одинаковый номер встречается у разных наших организаций, ToDictionaryAsync упадет или будет невозможно однозначно выбрать заказ. | Добавить в Google-таблицу и синхронизацию нашу организацию или внутренний BuyerOrderId. Минимум перед запуском проверить, что в импортируемом наборе нет одинаковых номеров заказов покупателя по разным нашим организациям. |
| Существующий заказ на производство с тем же номером, но другой позицией товара, будет обновлен молча. | Средняя | Для существующих заказов ключом является только номер заказа на производство. Если в таблице номер совпал, но строка указывает на другой заказ покупателя или другой товар, код обновит ссылку, примечание и местоположение существующего заказа, не проверяя соответствие GoodId. | При обновлении сверять найденный GoodId с existsOrder.GoodId. При расхождении не обновлять заказ, а возвращать предупреждение или ошибку. |
| Дубли номеров заказов на производство в Google-таблице не блокируют синхронизацию. | Средняя | Parser добавляет предупреждение, но затем перезаписывает значение в Dictionary<int, OrderEntity>, поэтому в обработку попадет последняя строка. Для ежедневного запуска это может незаметно менять ссылку, примечание или местоположение производства. | Перед сохранением считать дубли номеров блокирующей ошибкой для этих строк или для всей синхронизации. Минимум перед запуском вручную проверить таблицу на дубли номеров заказов на производство. |
| В БД не видно уникального индекса по номеру заказа на производство. | Средняя | Код считает Order.Number уникальным и строит ToDictionary. Если в БД окажутся два заказа с одним номером, синхронизация упадет. | Проверить фактическую схему БД. Если номер заказа на производство должен быть уникальным, добавить уникальный индекс для непустого Number или предварительную проверку дублей перед запуском синхронизации. |
Вывод по шагу:
- ежедневный запуск безопасен только для уже существующих заказов на производство с уникальными номерами и при отсутствии дублей изделий внутри заказа покупателя;
- для импортированных из 1С заказов с одинаковыми изделиями текущий механизм привязки заказа на производство к товару недостаточен;
- перед боевым запуском нужно добавить в импорт товаров блокировку дублей изделий; дубли не-изделий можно оставить как отдельные позиции;
- создание новых заказов на производство из Google-таблицы требует backend-исправления обязательных полей, иначе endpoint может упасть на сохранении;
- массовую синхронизацию нужно довести до поведения
PUT /Order/Order/google/{number}по статусу и дате согласования чертежа; - после синхронизации нужна сверка: количество строк Google-таблицы, количество добавленных/обновленных заказов, предупреждения, связь каждого заказа на производство с конкретным товаром заказа покупателя.
Шаг 6. Импорт счетов на оплату
Цель: проверить создание и обновление счетов на оплату из 1С.
Проверить:
- как счет связывается с заказом покупателя и планом оплат;
- как определяется существующий счет;
- создание нового счета;
- обновление существующего счета;
- поведение при изменении суммы, даты, покупателя, организации, статуса и номера;
- поведение при отсутствии плана оплат;
- поведение, если по счету уже есть платежи;
- повторный импорт того же файла;
- импорт с дублями счетов.
Особое внимание:
- импорт не должен отвязывать счет от плана оплат без явного правила;
- счет с оплатами нельзя изменять так, чтобы платежи стали некорректными;
- изменение счета не должно повторно запускать побочные эффекты, которые уже выполнены.
Результат шага 6, выполнено 09.07.2026:
Фактическая схема импорта:
- endpoint:
POST /Order/PaymentInvoice/1C, доступен ролиChiefSalesManager; - frontend запускает импорт со страницы заказов покупателя через кнопку
Импорт счетов для оплаты из 1Си общий диалог 1С; диалог показываетreaded,updated,added,syncMessages; - файл читается через
FilePaymentInvoiceReader; - заголовки ищутся в строке 7, данные начинаются со строки 8;
- обязательные колонки:
Номер,Дата,Автор,Банковский счет.Номер счета,Банковский счет.Банк.БИК,Договор.№ договора,Договор.Дата,Основание,Комментарий,Покупатель.ИНН,Покупатель.КПП,Организация,Сумма; - номер счета формируется как
{год из даты счета % 100}-{номер из 1С}; - существующий счет определяется по ключу
Number + OurOrganizationId; - заказ покупателя берется из колонки
ОснованиечерезParseBuyerOrderLink; - покупатель определяется по
ИНН + КПП; если точного совпадения нет, но по ИНН найден ровно один покупатель, он используется с предупреждением; - наши банковские реквизиты ищутся по
AccountNumber + BIC, новые реквизиты могут добавляться через DaData; - для нового счета создается новый
PaymentScheduleс этапомOther, датой счета и суммой счета; - для существующего счета
PaymentScheduleне меняется; - статус из файла не импортируется: новый счет остается
Created, существующий сохраняет текущийStatusId; - предупреждения возвращаются как
SyncMessage.Warning, служебные сообщения о добавленных сотрудниках/реквизитах - какSyncMessage.Log.
Как обрабатываются строки файла:
| Ситуация | Поведение |
|---|---|
| Не указан или не распознан номер счета | Строка пропускается. |
| Не указана или не распознана наша организация | Строка пропускается. |
| Не указана дата счета | Строка пропускается. |
В Основание не найден номер заказа покупателя | Строка пропускается. |
| Не указан ИНН покупателя | Строка пропускается. |
| Не указан или не разобран автор | Строка пропускается. |
| Не указана сумма | Строка пропускается. |
| Не указан счет или БИК наших банковских реквизитов | Строка пропускается. |
| Покупатель не найден по ИНН/КПП | Строка удаляется из импорта, добавляется предупреждение. |
| По ИНН найден ровно один покупатель, но КПП другой | Используется найденный покупатель, добавляется предупреждение. |
| Заказ покупателя не найден по номеру и организации | Строка удаляется из импорта, добавляется предупреждение. |
| Новые банковские реквизиты не удалось получить | Счета с этими реквизитами удаляются из импорта, добавляется предупреждение. |
| Счет уже есть в БД | Обновляются дата, банковские реквизиты, основание, сумма, НДС, покупатель, договор, автор и непустая заметка. |
| Счета еще нет в БД | Создается новый счет и новый план оплаты Other. |
Найденные риски:
| Риск | Критичность | Почему важно для разового запуска | Рекомендация |
|---|---|---|---|
| Импорт обновляет существующие счета без ограничений ручного сценария. | Высокая | Ручное обновление запрещено для счетов не в статусах Created/Sent, а импорт меняет Total, BuyerId, ContractId, OurBankingDetailId, AuthorId, Reason, InvoiceDate независимо от статуса и наличия оплат. | Для существующих счетов со статусами Paid, PartiallyPaid, Overpaid, Cancelled не менять финансовые и связующие поля. Минимум - пропускать такие строки с предупреждением. |
| Изменение суммы существующего счета не пересчитывает статус и не сверяет уже привязанные платежи. | Высокая | Статус счета рассчитывается в сервисах платежей от Total и суммы связей InvoicePaymentPaymentInvoice.InvoiceAmount. Если импорт изменит Total уже оплаченного счета, статус может остаться старым и стать неверным. | При импорте существующего счета с платежами блокировать изменение Total, BuyerId, OurBankingDetailId, PaymentPercentage/смысла оплаты. Если изменение разрешено, сразу пересчитывать статус тем же правилом, что платежи. |
Для существующего счета не проверяется соответствие PaymentSchedule новому основанию. | Высокая | Счет ищется только по Number + OurOrganizationId. Если строка файла с тем же номером указывает на другой заказ покупателя, импорт обновит покупателя/сумму/договор, но оставит старый PaymentScheduleId, а значит счет останется привязан к старому заказу. | При обновлении сверять заказ покупателя из файла с existsInvoice.PaymentSchedule.BuyerOrderId. При расхождении не обновлять счет, а возвращать ошибку/предупреждение. |
| Дубли счетов внутри одного файла не блокируются до сохранения. | Высокая | Если в файле две новые строки с одинаковым Number + OurOrganizationId, AddRange может упасть на уникальном индексе. Если счет уже есть в БД, одна и та же запись будет обновлена несколько раз последней строкой. | До сохранения валидировать дубли Number + OurOrganizationId внутри файла. Для разового запуска сделать дубли блокирующей ошибкой или хотя бы пропускать все повторяющиеся строки. |
Новый импортированный счет всегда создает новый план оплаты Other. | Средняя | Импорт не пытается связать счет с существующим планом предоплаты/оплаты перед отгрузкой/финальной оплаты. В результате в заказе могут появиться дополнительные планы Other, не совпадающие с исходным планом оплат. | Для разового импорта решить правило: либо импортированные счета сознательно создают Other, либо нужно подбирать существующий план по заказу, этапу/сумме/дате. После импорта сверить планы оплат по заказам. |
У нового импортированного счета DueDate остается 0. | Средняя | В entity поле обязательное, но парсер его не заполняет. Счет сохранится со сроком оплаты 0 дней, тогда как ручное создание использует значение по умолчанию 10 дней. | В импорте выставлять DueDate = 10 или брать срок из файла, если такая колонка появится. |
| Наши банковские реквизиты могут не соответствовать нашей организации. | Средняя | SetOurBankingDetails добавляет предупреждение, если найденные реквизиты принадлежат другой нашей организации, но счет не блокирует и все равно получает OurBankingDetailId. | Сделать несоответствие банковских реквизитов нашей организации блокирующей ошибкой строки. |
| Договор ищется только по номеру. | Средняя | contractsDb.ToDictionary(c => c.Number) упадет при дублях номеров договоров или выберет недостаточно точный ключ для разных покупателей/организаций. | Искать договор по более точному ключу: номер + покупатель + наша организация/дата, либо при дублях не привязывать договор и возвращать предупреждение. |
| Возможен пропуск последней строки файла. | Средняя | FilePaymentInvoiceReader использует цикл rowNumber < sheet.LastRow, как и другие reader'ы. Если LastRow - номер последней строки, последняя строка не импортируется. | Проверить контрольную последнюю строку в файле. Если проблема подтверждается - заменить условие на <= sheet.LastRow во всех Excel-reader импортах. |
Frontend после импорта инвалидирует только BuyerOrders. | Низкая | Таблицы счетов и планов оплат живут в отдельном redux-slice и могут остаться со старыми данными, если были открыты до импорта. | После успешного импорта очищать/перезагружать paymentInvoices и paymentSchedules либо инвалидировать соответствующие данные, если их перевести на RTK Query tags. |
Вывод по шагу:
- базовая идемпотентность по ключу
Number + OurOrganizationIdесть, и в БД есть уникальный индекс; - самый опасный сценарий - повторный импорт уже оплаченного или частично оплаченного счета: импорт может изменить сумму/покупателя/реквизиты без пересчета статуса и без проверки платежей;
- второй критичный сценарий - тот же номер счета у той же нашей организации, но другое основание:
PaymentScheduleIdостанется старым, а остальные поля могут обновиться по новой строке; - перед боевым запуском стоит добавить guards для существующих счетов с оплатами, дублей счетов внутри файла и несоответствия заказа покупателя существующему
PaymentSchedule; - после импорта нужна сверка: количество считанных/добавленных/обновленных счетов, список предупреждений, планы оплат
Other, суммы счетов против платежей и статусы счетов.
Шаг 7. Связь счетов, платежей и задач
Цель: проверить, что импорт счетов корректно работает вместе с оплатами и процессом заказа покупателя.
Проверить:
- создание платежей из банковской выписки;
- автоматическую привязку платежей к счетам;
- пересчет статусов счетов;
- влияние оплаты на задачу запуска;
- повторную обработку оплаты или счета;
- частичную оплату;
- переплату;
- оплату без найденного счета;
- оплату по нескольким счетам.
Особое внимание:
- автоматические переходы задач должны быть идемпотентными;
- повторный импорт или повторная обработка не должны повторно завершать задачу или создавать дубли планов оплат.
Результат шага 7, выполнено 09.07.2026:
Фактическая схема работы:
- банковская выписка загружается через
POST /Order/BankStatement, endpoint доступен бухгалтеру; - файл читается как
Windows-1251, платежи выделяются из секцийСекцияДокумент ... КонецДокумента; - дубли уже загруженных платежей фильтруются по ключу
Number + Date + PayerAccountNumber; - счет ищется по номеру из назначения платежа и ИНН покупателя;
- если счет найден, платеж связывается через
InvoicePaymentPaymentInvoice, а статус счета пересчитывается по сумме всех связей; - если счет не найден или есть ошибка по счету, платеж все равно сохраняется с текстом ошибки;
- после сохранения выписки для счетов, которые стали
PaidилиOverpaid, вызываетсяHandleSigningSpecificationTaskDone; - ручная перепривязка платежа к счету также пересчитывает статус счета и при полной оплате вызывает
HandleSigningSpecificationTaskDone.
Как обрабатываются платежи:
| Ситуация | Поведение |
|---|---|
| Файл без документов | Импорт отклоняется с ошибкой В файле нет документов. |
| Платеж уже был загружен | Платеж отфильтровывается; если новых платежей нет, импорт отклоняется с ошибкой Данная выгрузка уже добавлена. |
| В назначении платежа не найден номер счета | Платеж сохраняется без связи со счетом и с ошибкой. |
| Счет с таким номером не найден | Платеж сохраняется без связи со счетом и с ошибкой. |
| ИНН плательщика не совпадает с покупателем в счете | Платеж сохраняется без связи со счетом и с ошибкой. |
| Банковский счет получателя не совпадает со счетом | Платеж привязывается к найденному счету, но сохраняется ошибка. |
| Счет отменен | Платеж привязывается к найденному счету, но сохраняется ошибка. |
| Сумма оплат меньше суммы к оплате | Счет получает статус PartiallyPaid. |
| Сумма оплат равна сумме к оплате | Счет получает статус Paid. |
| Сумма оплат больше суммы к оплате | Счет получает статус Overpaid; в таблице платежей дополнительно показывается ошибка о превышении суммы. |
| Оплачен счет предоплаты | Backend пытается автоматически перевести задачу SigningSpecification в запуск и завершить ее. |
| Оплачен счет не по предоплате | Автозавершение задачи подписания спецификации не выполняется. |
Повторная проверка ветки stage:
- основная проблема идемпотентности автозавершения по предоплате закрыта;
HandleSigningSpecificationTaskDoneне выполняет автопереход для счетов не-предоплаты;- автопереход выполняется только когда все активные счета предоплаты по заказу оплачены;
- если задача
SigningSpecificationуже завершена, повторная обработка пропускается; - перевод в
CreatingOrdersвыполняется только из состоянийAwaitingCurrentилиInProgress; - перед завершением
SigningSpecificationпроверяется, что задачаSupportOrderеще не создана; - планы
BeforeShippingиFinalсоздаются только если активного плана такого этапа еще нет; - добавлено уведомление исполнителю/ассистентам об автоматической смене статуса задачи.
Остаточные риски:
| Риск | Критичность | Почему важно для разового запуска | Рекомендация |
|---|---|---|---|
| Результат автозавершения игнорируется вызывающими сервисами. | Средняя | BankStatementService и InvoicePaymentService вызывают HandleSigningSpecificationTaskDone, но не проверяют OperationResult. Платеж или выписка могут сохраниться успешно, а пользователь не увидит, что автопереход задачи не выполнен. | Проверять результат HandleSigningSpecificationTaskDone. Для выписки можно не откатывать платежи, но нужно вернуть/показать предупреждение о том, что оплата сохранена, а задача не переведена автоматически. |
Проверка существования SupportOrder и создание следующей задачи не защищены от параллельной гонки. | Средняя | При обычном повторном вызове дубль не создается. Но два параллельных вызова могут одновременно увидеть отсутствие SupportOrder и оба перейти к созданию следующей задачи. | Для надежности добавить уникальную защиту на уровне данных или транзакционную проверку при создании следующей задачи SupportOrder по заказу покупателя. Для разового запуска риск ниже, но тест на параллельность полезен. |
Результат paymentScheduleService.AddByStage при создании BeforeShipping и Final не проверяется. | Средняя | Повторные планы теперь не создаются за счет предварительной проверки существующих этапов. Но если создание нового плана упадет по другой причине, метод может продолжить и создать SupportOrder. | Проверять результат AddByStage и прекращать завершение задачи, если обязательный план оплаты не создан. |
| Импорт выписки сохраняет проблемные платежи как успешный импорт банковской выписки. | Средняя | Платежи без найденного счета или с ошибкой по счету остаются в системе, а сама выписка создается. Это правильно для ручной разборки, но для разового запуска легко пропустить строки с ошибками, если пользователь смотрит только общий успех. | После загрузки выписки обязательно открыть список платежей по выписке и проверить поле Error. Для UI шага 8 отдельно проверить, что ошибки платежей хорошо заметны. |
| Повторный импорт того же платежа не повторяет автозавершение задачи. | Низкая | Дубли платежей фильтруются до привязки. Если первый запуск сохранил оплату и пересчитал счет, но автопереход задачи не выполнился, повторная загрузка той же выписки не станет повторной попыткой автозавершения. | После загрузки выписки сверять состояние задачи SigningSpecification. Если нужен ручной повтор автоперехода, лучше делать его отдельной идемпотентной операцией по счету/заказу. |
Импортированные из 1С новые счета создаются на плане Other, поэтому их оплата не запускает задачу по предоплате. | Средняя | Автозавершение работает только для счетов, чей PaymentSchedule.PaymentStageId = Prepayment. Если счет предоплаты пришел из 1С как новый счет и получил план Other, оплата такого счета не переведет задачу запуска автоматически. | До боевого сценария определить правило: счета предоплаты должны создаваться из задачи/плана предоплаты или импорт должен уметь привязать их к существующему плану Prepayment. После импорта счетов сверить, что предоплатные счета не в Other. |
Поиск номера счета в назначении платежа поддерживает только один шаблон число-число. | Низкая | Если банк/1С выгрузит назначение платежа с другим форматом номера или несколькими номерами счетов, платеж останется без связи или будет связан только с первым найденным номером. | Для разового запуска заранее проверить несколько реальных строк назначения платежа. Не расширять парсер без необходимости, если формат файла стабилен. |
Покрытие тестами:
- есть интеграционные проверки загрузки выписки, дубля платежа, частичной оплаты, полной оплаты, переплаты, отвязки/перепривязки платежей и платежа без найденного счета;
- есть сценарий, где банковская выписка оплачивает предоплату и завершает задачу
SigningSpecification; - не найден тест повторного вызова автозавершения по уже завершенной задаче и проверки, что не создается дубль
SupportOrder; - не найден тест параллельной обработки одной оплаченной предоплаты;
- не найден тест, что ошибка автоперехода возвращается пользователю при загрузке выписки или ручной перепривязке платежа;
- не найден тест уведомления исполнителю при автоматическом изменении статуса задачи.
Вывод по шагу:
- механизм привязки платежей к счетам и пересчета статусов в целом согласован и уже покрыт интеграционными тестами;
- в ветке
stageосновная идемпотентность автозавершения по оплате предоплаты закрыта: повторная обработка завершенной задачи пропускается, существующаяSupportOrderпроверяется, планы оплат не создаются повторно; - перед боевым импортом и загрузкой оплат стоит доработать/проверить возврат ошибок автоперехода из
BankStatementServiceиInvoicePaymentService, а также добавить тест повторной и, при необходимости, параллельной обработки; - после загрузки выписки обязательно сверить платежи с ошибками, статусы счетов, состояние задачи
SigningSpecificationи отсутствие дублей задачиSupportOrder.
Шаг 8. Frontend UX и сообщения пользователю
Цель: проверить, что пользователь понимает результат импорта и может исправить ошибки.
Проверить:
- запрет загрузки неподдерживаемых файлов;
- поведение при ошибке сети или backend-ошибке;
- отображение
readed,updated,added; - отображение
syncMessages; - разделение логов, предупреждений и ошибок;
- возможность повторить импорт после исправления файла;
- обновление таблиц после успешного импорта;
- не вводит ли успех toast в заблуждение, если часть строк завершилась ошибками.
Результат шага 8, выполнено 09.07.2026:
Фактическая схема frontend:
- импорты заказов, товаров и счетов из 1С запускаются с таблицы заказов покупателя через общий диалог
ImportDialog; - диалог принимает только
.xlsx; при выборе другого файла показывает ошибкуПожалуйста, выберите файл excel в формате .xlsx; - кнопка импорта заблокирована, пока файл не выбран или идет загрузка;
- после успешного HTTP-ответа диалог показывает счетчики
Считано,обновлено,добавленои списокsyncMessages; - сообщения показываются отдельными строками с иконками уровней
Log,Warning,Error; - после успешного HTTP-ответа дополнительно показывается success-toast, даже если в
syncMessagesесть предупреждения или ошибки; - при backend-ошибке RTK Query возвращает текст ошибки через общий error-listener и
showError; - после успешного импорта заказов инвалидируются
BuyerOrders; - после успешного импорта товаров инвалидируются
Goods,BuyerOrders, список задач; - после успешного импорта счетов инвалидируются только
BuyerOrders.
Фактическая схема банковских выписок:
- банковская выписка загружается на отдельной странице через
.txt; - при выборе файла другого формата показывается ошибка
Недопустимый формат файла. Загрузите файл в формате .txt.; - после успешного создания выписки показывается success-toast
Банковская выгрузка успешно создана!; - таблица выписок показывает флаг
Требуется корректировка; - подробные ошибки конкретных платежей видны только после перехода в платежи по выписке;
- в таблице платежей есть колонка
Ошибка, ячейка с ошибкой подсвечивается красным текстом; - бухгалтер может открыть редактирование платежа и перепривязать его к другому счету.
Отдельно по обходному сценарию с импортными счетами на Other:
- если импорт создал счет и план
Other, а в процессе нужен счет предоплаты, можно позже создать нормальный счет на этапеPrepayment; - затем оплату можно вручную перепривязать с импортного счета
Otherна счетPrepaymentчерез редактирование платежа; - это рабочий обходной путь, но он требует ручной сверки: старый счет/план
Otherсам по себе не исчезнет и может остаться лишним в картине оплат.
Повторная проверка сценария Импорт счетов для оплаты из 1С:
- сценарий начинается на странице заказов покупателя, кнопка доступна только
ChiefSalesManager; - это импорт счетов из Excel-файла 1С, а не загрузка платежей бухгалтером;
- импорт вызывает
POST /Order/PaymentInvoice/1C, счет связывается с заказом через номер заказа в колонкеОснованиеи нашу организацию; - новый импортированный счет всегда получает новый план оплаты с этапом
Other; - существующий счет определяется по
Number + OurOrganizationId, а его существующийPaymentScheduleIdне меняется; - импорт сам не создает платежи, не пересчитывает задачу и не завершает
SigningSpecification; - если импортированный счет фактически является счетом предоплаты, автоматическое продвижение задачи после оплаты не сработает, пока счет/платеж не будут приведены к этапу
Prepayment; - после импорта менеджер должен сверить созданные счета в карточке заказа и планы оплат; если вкладки уже были открыты, их нужно обновить вручную, потому что frontend очищает только
BuyerOrders.
Найденные UX-риски:
| Риск | Критичность | Почему важно для разового запуска | Рекомендация |
|---|---|---|---|
| Success-toast показывается при частичных ошибках импорта. | Средняя | Backend может вернуть SynchronizeResponse с syncMessages уровня Error, но HTTP-ответ успешный, поэтому пользователь видит сообщение об успешном импорте. | Для разового запуска в чек-листе явно требовать читать все syncMessages. В интерфейсе лучше показывать warning/error-toast, если в ответе есть ошибки. |
Диалог импорта не выделяет итоговый статус есть ошибки. | Средняя | Красная иконка у строки есть, но общий заголовок результата остается нейтральным. При большом списке сообщений пользователь может не понять, что импорт завершился частично. | Добавить сверху краткий итог: Есть ошибки, Есть предупреждения, Без ошибок; для ошибок можно не скрывать кнопку закрытия до подтверждения. |
| После импорта счетов обновляется только список заказов покупателя. | Средняя | Сценарий запускается со страницы заказов покупателя, и менеджер ожидает увидеть созданные счета/планы в карточке заказа. Если вкладки счетов или планов оплат уже открыты, они могут остаться со старым состоянием до ручного обновления. | Инвалидировать/очищать данные счетов и планов оплат после импорта счетов. Для разового запуска включить в чек-лист ручное обновление карточки заказа после импорта. |
| Ошибки платежей по выписке видны не сразу на странице выписки. | Средняя | Страница выписок показывает только Требуется корректировка, но не раскрывает список проблемных платежей. После success-toast пользователь может не открыть детали. | После загрузки выписки обязательно открыть платежи по выписке и проверить колонку Ошибка. В UI можно добавить быстрый переход/счетчик проблемных платежей. |
Ручная перепривязка оплаты с Other на Prepayment не оформлена как сценарий. | Средняя | Технически платеж можно перепривязать, но пользователь должен понимать, какой новый счет создать и какой старый импортный счет останется как след от импорта. | Для боевого запуска описать ручной порядок: создать счет предоплаты, открыть платеж, перепривязать оплату, проверить статус счета и задачу SigningSpecification. |
| Диалог импорта очищает выбранный файл после попытки импорта. | Низкая | После backend-ошибки пользователь должен выбрать файл заново. Для одного запуска это неудобство, но не риск порчи данных. | Можно оставить как есть; если импорт будут повторять часто, сохранять выбранный файл при ошибке. |
Вывод по шагу:
- frontend показывает основные счетчики и сообщения импорта достаточно прозрачно для ручной сверки;
- критичных UX-блокеров для одного-двух запусков не найдено;
- главный риск - пользователь может принять success-toast за полный успех и не разобрать
syncMessages; - для сценария счетов главный пользовательский контроль: после импорта открыть карточки нескольких заказов из файла и сверить вкладки счетов и планов оплат;
- перед боевым импортом нужен короткий ручной чек-лист: прочитать все сообщения диалога, отдельно проверить строки с error/warning, после импорта счетов обновить открытые карточки заказов и сверить счета/планы; если отдельно загружается банковская выписка, после нее открыть платежи и проверить колонку
Ошибка.
Шаг 9. Права доступа
Цель: проверить согласованность frontend-видимости и backend-авторизации.
Проверить:
- кто видит кнопки импорта;
- кто может вызвать endpoint напрямую;
- совпадают ли frontend permissions с backend
[AuthorizeRoles]; - есть ли пользователи, которым импорт нужен по бизнес-сценарию, но endpoint недоступен;
- есть ли пользователи, которым импорт показывается на frontend, но backend его запрещает.
Результат шага 9, выполнено 09.07.2026:
Фактическая схема прав:
| Сценарий | Frontend | Backend | Вывод |
|---|---|---|---|
| Импорт заказов покупателя из 1С | Кнопка видна только ChiefSalesManager | POST /Order/BuyerOrder/1C доступен только ChiefSalesManager | Согласовано. |
| Импорт товаров из 1С | Кнопка видна только ChiefSalesManager | POST /Order/Good/1C доступен только ChiefSalesManager | Согласовано. |
| Импорт счетов на оплату из 1С | Страница заказов покупателя, кнопка Импорт счетов для оплаты из 1С видна только ChiefSalesManager | POST /Order/PaymentInvoice/1C доступен только ChiefSalesManager | Согласовано. Это менеджерский сценарий импорта счетов, не загрузка платежей бухгалтером. |
| Просмотр банковских выписок | Маршрут /bank-statements доступен Accountant, Admin | GET /Order/BankStatement/table доступен Accountant, Admin | Согласовано. |
| Загрузка банковской выписки | Кнопка Добавить видна только Accountant | POST /Order/BankStatement доступен только Accountant | Согласовано. Это отдельный сценарий загрузки оплат, а не импорт счетов из 1С. |
| Просмотр платежей по конкретной выписке | Маршрут доступен Accountant, Admin | GET /BankStatement/{id}/InvoicePayment/table доступен Accountant, Admin | Согласовано. |
| Страница всех платежей | Маршрут /invoice-payments доступен Accountant, ChiefSalesManager, ProductionManager, Admin | GET /Order/InvoicePayment/table доступен ChiefSalesManager, Accountant, Admin | Рассинхрон: ProductionManager видит маршрут, но получит ошибку доступа при загрузке таблицы. |
| Перепривязка платежа к счету | В общих платежах кнопка редактирования только у Accountant; в планах оплат поле changeInvoicePayment доступно Accountant, ChiefSalesManager, ProductionManager, Admin | PATCH /Order/InvoicePayment/{id} доступен ChiefSalesManager, SalesManager, AssistantSalesManager, ProductionManager, Accountant | Рассинхрон: frontend может показать действие Admin, но backend его запретит; backend разрешает SalesManager/AssistantSalesManager, но frontend в планах оплат им действие не дает. |
| Просмотр счетов на оплату | Страницы/вкладки доступны по контекстным permissions | Таблицы счетов доступны разным наборам ролей; общий список GET /PaymentInvoice/table не доступен ProductionManager | В основном согласовано, но нужно отдельно проверить, нужен ли ProductionManager общий список счетов. |
| Daily/внешний запуск синхронизации заказов на производство | Frontend-точки нет, endpoint вызывает внешняя программа по расписанию | В ветке разработки есть массовый endpoint POST /Order/Order/google с ролью ChiefSalesManager, но рядом оставлен старый POST /Order/Order/google для создания пустой таблицы с ролью Tester | Сценарий по правам понятен, но маршруты конфликтуют: два метода имеют один HTTP-метод и один route. |
| Обновление заказа на производство из Google | Frontend-точки нет | PUT /Order/Order/google/{number} анонимный, но проверяет AccessCode | Для Google script сценария отдельная защита через код доступа есть. |
Найденные риски:
| Риск | Критичность | Почему важно для разового запуска | Рекомендация |
|---|---|---|---|
ProductionManager видит страницу всех платежей, но backend не дает общий список оплат. | Средняя | Пользователь может открыть раздел Платежи, получить ошибку загрузки и не понять, что это проблема прав, а не данных. | Либо убрать ProductionManager из RouteRoles.invoicePayment, либо добавить его в GET /Order/InvoicePayment/table, если доступ нужен. |
| Права на перепривязку платежа различаются между frontend и backend. | Средняя | Для обходного сценария Other -> Prepayment важно, кто реально может перепривязать оплату. Сейчас Admin может увидеть действие в некоторых местах, но backend запретит; sales-роли наоборот разрешены backend, но не всегда frontend. | Согласовать один список ролей для changeInvoicePayment и PATCH /Order/InvoicePayment/{id}. Для боевого запуска определить ответственного за перепривязку, лучше Accountant. |
В ветке разработки конфликтуют два POST /Order/Order/google. | Высокая | ASP.NET Core не выбирает action по роли пользователя. При двух одинаковых маршрутах POST /Order/Order/google возможна неоднозначная маршрутизация вместо запуска массовой синхронизации. | Развести маршруты: например, массовую синхронизацию сделать POST /Order/Order/google/synchronize, а создание пустой таблицы оставить отдельным tester/dev endpoint или удалить, если он больше не нужен. |
Импорт из 1С доступен только ChiefSalesManager. | Низкая | Это согласовано frontend/backend и подходит для контролируемого разового запуска, но импорт не сможет выполнить бухгалтер или админ без смены роли/токена. | Для боевого запуска заранее определить пользователя с ролью ChiefSalesManager, который выполнит Excel-импорты. |
Загрузить банковскую выписку может только Accountant, не Admin. | Низкая | Это не относится к импорту счетов из 1С, но важно для отдельного сценария подтверждения оплат: админ не сможет заменить бухгалтера при загрузке выписки. | Если после импорта счетов планируется загружать оплаты, заранее определить бухгалтера или временно выдать нужную роль ответственному пользователю. |
Вывод по шагу:
- основные Excel-импорты из 1С по правам согласованы: frontend показывает кнопки на странице заказов покупателя только
ChiefSalesManager, backend принимает толькоChiefSalesManager; - импорт счетов из 1С - это менеджерский сценарий через кнопку
Импорт счетов для оплаты из 1С, а не бухгалтерская загрузка платежей; - банковские выписки по правам согласованы как отдельный сценарий оплат: просмотр доступен
Accountant/Admin, загрузка толькоAccountant; - для основного разового импорта нужен пользователь/токен
ChiefSalesManager;Accountantнужен только если в этом же прогоне будут загружать банковскую выписку или вручную перепривязывать оплаты; - нужно исправить или хотя бы учесть рассинхроны вокруг страницы всех платежей и перепривязки платежа;
- для daily-сценария заказов на производство нужно развести конфликтующие маршруты
POST /Order/Order/googleи заранее подготовить пользователя/токен с рольюChiefSalesManagerдля внешней программы запуска.
Шаг 10. Матрица тестов
Цель: сформировать набор ручных и автоматических проверок.
Для разового импорта основной упор делается на ручные сценарные проверки и несколько точечных backend-тестов для самых опасных мест. Полный набор автотестов нужен только там, где ошибка может незаметно испортить данные.
Минимальные сценарии:
- успешный импорт новых заказов;
- повторный импорт тех же заказов;
- импорт заказов с ошибочной строкой;
- проверка, что для нового импортированного заказа создан ровно один процесс и ровно одна стартовая задача;
- повторный импорт заказа не создает дубль процесса и задачи;
- успешный импорт товаров к существующим заказам;
- импорт товара без найденного заказа;
- повторный импорт товаров;
- успешная синхронизация заказа на производство к импортированному заказу и товару;
- синхронизация заказа на производство до импорта заказа покупателя дает понятное предупреждение;
- синхронизация заказа на производство до импорта товара дает понятное предупреждение;
- повторная синхронизация заказа на производство не создает дубль;
- успешный импорт счетов к существующим заказам;
- импорт счета с основанием на заказ покупателя, запущенный кнопкой
Импорт счетов для оплаты из 1Сна странице заказов покупателя; - импорт счета без найденного заказа покупателя в
Основание; - повторный импорт счетов;
- импорт счета, по которому уже есть платеж;
- импорт счета предоплаты из 1С и проверка, что он попал в
Otherи не продвигает задачу без ручного сценария; - проверка прав доступа на frontend и backend;
- проверка отображения ошибок и предупреждений в диалоге импорта.
Для одного-двух запусков допустимо заменить часть автоматических тестов чек-листом ручной проверки, если сценарий легко воспроизводится и не будет поддерживаться постоянно.
Матрица проверки
| Участок | Ожидание | Frontend | Backend | Данные | Риск | Рекомендация | Тест |
|---|---|---|---|---|---|---|---|
| Заказы покупателя | Импорт должен создать или обновить заказ без дублей, не теряя важные ручные данные. | Три кнопки импорта доступны начальнику менеджеров по продажам через общий диалог. | POST /Order/BuyerOrder/1C, ключ Number + OurOrganizationId, новые заказы создают процесс и стартовую задачу. | Есть уникальный индекс по Number + OurOrganizationId; подготовительные сущности сохраняются до финального создания заказов. | Дубли в файле, отсутствие ответственного, подписант без PartnerId, повторные причины отмены, возможный пропуск последней строки. | Перед боевым запуском проверить файл на дубли и ответственных, сделать backup базы, сохранить исходный файл, рассмотреть быстрые guards. | Нужен ручной чек-лист перед прод-импортом и контрольная сверка результата после импорта. |
| Задачи процесса заказа покупателя | Новый активный импортированный заказ должен получить один процесс BuyerOrder и одну стартовую задачу BeginBuyerOrder. | Открытие задачи идет через CreateBuyerOrderTaskId; редактирование зависит от доступа к задаче и исполнителя. | AddBuyerOrders создает процесс и задачу только для новых не завершенных и не отмененных заказов; повторный импорт идет через обновление. | ExecutorId берется из ResponsibleId; AuthorId и Deadline у импортной стартовой задачи не заполняются. | Активный заказ без распознанного ответственного получает задачу без исполнителя; состояние ProductionStarted может сочетаться со стартовой задачей. | Перед запуском проверить ответственных и статусы 1С; на backend желательно блокировать активные строки без ответственного и заполнять AuthorId/Deadline. | Контрольная сверка после импорта: у каждого добавленного активного заказа есть одна задача BeginBuyerOrder с исполнителем. |
| Товары заказов | Импорт должен сохранить дубли не-изделий как отдельные позиции, но блокировать заказ с дублями изделий. | Кнопка импорта доступна через общий диалог; после успеха инвалидируются Goods, BuyerOrders и список задач. | POST /Order/Good/1C; нужен guard по BuyerOrder + StockKeepingUnit: дубли изделий делают заказ ошибочным, дубли не-изделий допускаются. | Файл может обновить Count, Price, RecommendedRetailPrice, даты отгрузки заказа и тип SKU; уникального индекса нет. | Повторный импорт одинаковых SKU хрупкий; дубли изделий ломают дальнейшую привязку заказов на производство; нет пересчета FinalPrice; возможна перезапись дат отгрузки. | Добавить предварительную валидацию дублей изделий, для дублей не-изделий при повторном импорте нужен ключ позиции; пересчитывать FinalPrice после импорта. | Контрольная сверка: дубли изделий возвращают ошибку и не импортируют заказ; дубли не-изделий сохраняются отдельными позициями; суммы и последняя строка файла корректны. |
| Заказы на производство | Синхронизация должна создать или обновить заказ на производство один раз и привязать его к конкретной позиции изделия. | Frontend-точки запуска нет; endpoint вызывает внешняя программа по расписанию. | POST /Order/Order/google; ключ заказа на производство - Number; заказ покупателя ищется по номеру, изделие - по SKU name + BuyerOrderId. | Новые заказы требуют GoodId, ProductId, StatusId, TypeId, Dates; дубли изделий должны быть заблокированы раньше. | Новые заказы могут не сохраниться из-за незаполненных обязательных полей; массово не обновляются статус и дата согласования чертежа; без guard дубли изделий ломают ToDictionary. | Добавить guard дублей изделий в импорт товаров, заполнение обязательных полей в AddOrders, обновление статуса/даты чертежа как в PUT /Order/Order/google/{number}. | Проверить таблицу на дубли номеров; прогнать синхронизацию после импорта без дублей изделий; сверить статус, дату чертежа и связи с товарами. |
| Счета на оплату | Импорт должен создать или обновить счет из файла 1С со страницы заказов покупателя и не ломать план оплат заказа. | Кнопка Импорт счетов для оплаты из 1С доступна на странице заказов покупателя; общий диалог показывает readed/updated/added и сообщения; после импорта очищается только BuyerOrders. | POST /Order/PaymentInvoice/1C; ключ счета - Number + OurOrganizationId; заказ ищется по номеру из Основание и нашей организации; новый счет создает план оплаты Other, существующий план не меняет. | Есть уникальный индекс по Number + OurOrganizationId; новые счета получают PaymentStage = Other; платежи связаны через InvoicePaymentPaymentInvoice.InvoiceAmount. | Импорт обновляет оплаченные счета без ручных guards; изменение суммы не пересчитывает статус; при новом основании старый PaymentScheduleId сохраняется; счет предоплаты из 1С попадает в Other; дубли в файле опасны. | Добавить guards для оплаченных/частично оплаченных счетов, сверку заказа с PaymentSchedule, проверку дублей файла, DueDate = 10 для новых импортных счетов; для предоплаты определить ручной или автоматический перевод из Other в Prepayment. | Запустить импорт именно кнопкой на buyer-orders; повторный импорт того же файла; файл с дублем счета; счет без найденного заказа; существующий оплаченный счет с измененной суммой; счет предоплаты из 1С и сверка планов Other. |
| Платежи и выписки | Выписка должна быть отдельным сценарием после импорта счетов: создать платежи, связать их со счетами, пересчитать статус счета и безопасно продвинуть задачу по оплаченной предоплате. | Страница банковских выписок загружает файл и обновляет таблицу; ошибки отдельных платежей видны в таблице платежей по выписке. | POST /Order/BankStatement; дубли платежей фильтруются по Number + Date + PayerAccountNumber; оплата Prepayment вызывает HandleSigningSpecificationTaskDone; в stage автопереход защищен guard'ами. | Платежи связаны через InvoicePaymentPaymentInvoice; статус счета зависит от суммы связей и PaymentPercentage; задача ищется по BuyerOrderId + SigningSpecification; повторные планы оплат не создаются. | Остаточный риск: результат автоперехода игнорируется вызывающими сервисами; возможна параллельная гонка при создании SupportOrder; оплата счета на плане Other не запускает предоплату; ошибки платежей легко пропустить. | Проверять результат HandleSigningSpecificationTaskDone, добавить тест повторной/параллельной обработки, ручную сверку платежей с ошибками после выписки, сверку предоплатных счетов не на Other перед загрузкой оплат. | Тест повторной обработки оплаченной предоплаты: не создаются дубли SupportOrder и планов оплат; тест ошибки автоперехода; тест уведомления; ручная сверка ошибок платежей после загрузки выписки; тест оплаты импортного счета Other. |
| Права доступа | Пользователь должен видеть только те действия, которые backend реально разрешает, а ответственные роли должны покрывать боевой запуск. | Excel-импорты заказов, товаров и счетов видны ChiefSalesManager на странице заказов покупателя; выписки видны Accountant/Admin, загрузка только Accountant; /invoice-payments доступен также ProductionManager; frontend-точки daily-синхронизации заказов на производство нет. | Excel-импорты доступны только ChiefSalesManager; выписку загружает только Accountant; общий список оплат не доступен ProductionManager; перепривязка платежа не доступна Admin; массовая синхронизация заказов на производство доступна ChiefSalesManager, но конфликтует маршрутом со старым tester endpoint. | Для основного импорта нужен ChiefSalesManager; Accountant нужен только для отдельного сценария выписки/перепривязки оплат; внешней программе нужен токен пользователя с ролью ChiefSalesManager. | Рассинхрон страницы всех платежей для ProductionManager; рассинхрон ролей перепривязки платежа; конфликт двух POST /Order/Order/google может сломать внешний daily-запуск. | Согласовать роли invoicePayment и changeInvoicePayment; заранее подготовить пользователя/токен ChiefSalesManager; для выписки отдельно подготовить Accountant; развести маршруты endpoint заказов на производство. | Ручная проверка входа под ChiefSalesManager, Accountant, ProductionManager, Admin; прямой вызов endpoints импорта/выписки; проверка перепривязки платежа; прямой вызов daily endpoint после разведения маршрутов. |
| UX результата импорта | Пользователь должен понять, какие строки импортированы, какие пропущены и какие требуют ручной проверки. | Диалог показывает счетчики и syncMessages; success-toast появляется при любом успешном HTTP-ответе; после импорта счетов карточка заказа может требовать ручного обновления вкладок счетов/планов. | Backend возвращает SynchronizeResponse; HTTP-ошибки показываются через общий error-listener; частичные ошибки остаются внутри успешного ответа. | После импорта счетов инвалидируется только BuyerOrders; ошибки платежей хранятся в InvoicePayment.Error; импортные счета на Other можно исправлять ручной перепривязкой оплаты. | Пользователь может принять частично проблемный импорт за полный успех; открытые таблицы счетов/планов могут быть не обновлены; ошибки платежей по выписке нужно открывать отдельно, если выписка используется отдельным шагом. | Для разового запуска сделать чек-лист сверки; в UI желательно показывать общий статус есть ошибки/предупреждения, инвалидировать счета/планы после импорта счетов, добавить быстрый контроль ошибок выписки. | Ручной сценарий: импорт с warning/error в syncMessages; импорт счетов при открытой карточке заказа; сверка вкладок счетов/планов после обновления; загрузка выписки с ошибочным платежом; перепривязка оплаты с Other на Prepayment. |
Шаг 11. Итоговая приоритизация
Цель: превратить найденные риски в рабочий набор задач и предбоевой чек-лист.
Для одного-двух запусков не все найденные проблемы нужно исправлять кодом. В код стоит выносить только то, что может испортить данные, создать дубли, сломать связи или остановить запуск без понятной ошибки. Остальное можно закрыть ручной проверкой файла, backup базы и контрольной сверкой результата.
Задачи, которые стоит выполнить до боевого запуска:
| Заголовок | Тип | Критичность | Описание |
|---|---|---|---|
| Защитить импорт заказов покупателя от опасных строк файла | Бек | Высокая | Добавить guard на дубли Number + OurOrganizationId внутри файла, блокировать активные заказы без распознанного ответственного, заполнить BuyerSigner.PartnerId для подписанта покупателя. |
| Привести импортную стартовую задачу к обычному созданию задачи | Бек | Средняя | Для BeginBuyerOrder, созданной импортом, заполнять AuthorId и Deadline; для активного заказа без исполнителя возвращать ошибку строки, а не создавать задачу без ExecutorId. |
| Защитить импорт товаров от дублей изделий и пересчитать суммы | Бек | Высокая | При импорте товаров блокировать заказ, если в нем есть дубли изделий; дубли не-изделий оставить допустимыми. После создания/обновления товаров пересчитать FinalPrice по затронутым заказам. |
| Исправить синхронизацию заказов на производство из Google-таблицы | Бек | Высокая | Развести конфликтующие POST /Order/Order/google, заполнить обязательные поля новых заказов на производство, обновлять статус и дату согласования чертежа так же, как PUT /Order/Order/google/{number}. |
| Защитить импорт счетов на оплату от порчи существующих связей | Бек | Высокая | Не обновлять финансовые и связующие поля оплаченных/частично оплаченных/отмененных счетов без явного правила; при обновлении сверять заказ из файла с existsInvoice.PaymentSchedule.BuyerOrderId; блокировать дубли счетов в файле. |
| Определить правило для импортных счетов предоплаты | Бек/процесс | Средняя | Решить, остаются ли счета из 1С всегда на Other или предоплатные счета нужно привязывать к Prepayment. Без этого оплата импортного счета Other не запустит автопереход задачи. |
| Обновлять видимые данные после импорта счетов | Фронт | Средняя | После успешного Импорт счетов для оплаты из 1С очищать/перезагружать не только BuyerOrders, но и открытые данные счетов/планов оплат, либо явно включить ручное обновление в чек-лист запуска. |
| Улучшить отображение частичного успеха импорта | Фронт | Средняя | Если в SynchronizeResponse.syncMessages есть warning/error, показывать общий статус Есть предупреждения/ошибки, чтобы success-toast не воспринимался как полный успех. |
| Согласовать права на платежи и перепривязку оплат | Фронт/бек | Средняя | Синхронизировать роли /invoice-payments, GET /Order/InvoicePayment/table, changeInvoicePayment и PATCH /Order/InvoicePayment/{id}. Особенно важно для ручного сценария Other -> Prepayment. |
Минимальный ручной чек-лист перед импортом:
- Сделать backup базы и сохранить исходные файлы импорта.
- Проверить файлы на дубли заказов, дубль счетов и дубли изделий внутри одного заказа.
- Проверить, что у всех активных заказов распознан ответственный.
- Проверить строки с подписантом покупателя и договорными реквизитами.
- Проверить контрольную последнюю строку в каждом Excel-файле.
- Проверить, что товары импортируются до активной работы по задачам и до синхронизации заказов на производство.
- Проверить Google-таблицу заказов на производство на дубли номеров заказов и дубли номеров заказов покупателя по разным нашим организациям.
- Подготовить пользователя/токен
ChiefSalesManagerдля Excel-импортов и внешней синхронизации заказов на производство. - Если загружается банковская выписка, подготовить пользователя
Accountantи заранее определить ответственного за разбор проблемных платежей.
Контроль после запуска:
- Сверить счетчики
readed,added,updatedи всеsyncMessagesпосле каждого импорта. - Проверить, что у каждого нового активного заказа есть один процесс
BuyerOrderи одна задачаBeginBuyerOrderс исполнителем. - Сверить количество товаров в нескольких контрольных заказах, итоговые суммы и отсутствие дублей изделий.
- После синхронизации заказов на производство сверить связи с конкретными товарами, статус и дату согласования чертежа.
- После импорта счетов открыть карточки нескольких заказов из файла и сверить вкладки счетов и планов оплат.
- Проверить предоплатные счета: они не должны случайно остаться на
Other, если по ним ожидается автоматический запуск процесса. - Если загружалась банковская выписка, открыть платежи по выписке и проверить колонку
Ошибка. - Проверить, что повторный запуск того же файла не создал дубли и не ухудшил состояние данных.
Что можно отложить, если запуск остается разовым:
- полноценный preview-режим импорта;
- историю запусков с хранением исходных файлов в системе;
- универсальный маппинг колонок;
- глубокую оптимизацию производительности;
- полный repair-механизм для старых заказов без процесса.
Формат найденной проблемы
Каждую найденную проблему фиксировать в одном формате:
Участок:
Тип:
Критичность:
Ожидание:
Фактическое поведение:
Где найдено:
Чем опасно:
Рекомендация:
Нужен тест:
Открытые вопросы
- Должен ли импорт перетирать ручные изменения в заказе покупателя, товаре или счете?
- Какие поля считаются источником истины из 1С, а какие ведутся только в системе?
- Должен ли импорт быть полностью транзакционным: весь файл откатывается при ошибке одной строки?
- Нужно ли хранить исходный файл после запуска? Для разового сценария может быть достаточно сохранить его вручную рядом с задачей/релизом.
- Нужен ли preview-режим перед применением импорта? Для одного-двух запусков предпочтительнее ручная проверка файла до импорта и backup базы перед запуском.
- Какие роли, кроме начальника менеджеров по продажам, должны иметь право на импорт?
- Какой порядок запуска считать обязательным: заказы покупателя -> товары -> заказы на производство -> счета?
- Должны ли заказы на производство, подтянутые из текущей ветки, заменять ручное создание заказов на производство в задаче запуска или только дополнять его?