5.2 KiB
5.2 KiB
Спринт 5 — оптовые продажи, email, аудит, надёжность import-job
Автономная работа. После каждого пункта: dotnet build (SDK 8.0.126),
unit + integration тесты, коммит порцией, отметка [x], коммит прогресса.
Multi-tenant: все новые сущности — TenantEntity с OrganizationId +
query filter. E2E на изоляцию A vs B где применимо.
Чек-лист
- P1-5 Оптовая отгрузка (Demand) — Domain
Demand+DemandLine(CounterpartyId юрлица, способ оплаты нал/безнал, цена опт., НДС). EF + миграция. Контроллер CRUD + Post/Unpost. Web/sales/demands.StockMovementтипWholesaleSale. Multi-tenant. Тесты. ✅ Зеркалит RetailSale без RetailPoint/Cashier;DemandPayment.Credit(постоплата/дебиторка),PaidAmountдля отслеживания. Permissions переиспользуют существующиеDemandsEdit/Post. Метрикиdocuments_posted{type="demand"}. 3 интеграционных теста. - P1-18 Аудит мутаций tenant'а — Domain
OrgAuditLog(какSuperAdminAuditLog, но per-org). Hook через EF SaveChangesInterceptor на Supply/Sale/Demand/Product/Counterparty. UI:/audit-logдля админа. Multi-tenant строго. Тесты. ✅OrgAuditInterceptorснимает diff наSavingChanges(до commit) — атомарно с мутацией. ChangesJson:{"field":{"before":X,"after":Y}}. Белый список типов: Supply/SupplierReturn/RetailSale/Demand/Product/ ProductPrice/ProductBarcode/Counterparty. Web/audit-logс фильтрами и diff-viewer'ом. Tenant-isolation через query-filter. 3 интеграционных. - P1-22 Email-шаблоны — расширить MailKit-сервис:
Resources/EmailTemplates/*.html. Шаблоны: приглашение сотрудника с временным паролем (sendInvite=true), еженедельный summary владельцу (Hangfire recurring), low-stock alert (Hangfire daily). Тесты рендеринга. ✅IEmailSender.SendHtmlAsync(multipart/alternative с plain fallback);EmailTemplateRenderer(mustache-light:{{key}}escape,{{{raw}}},{{#key}}…{{/key}}условие);EmailTemplatesзагружает embeddedResources/EmailTemplates/*.html(Subject: первой строкой). Шаблоны: invite/weekly-summary/low-stock. Hangfire: weekly понедельник 07:00, low-stock ежедневно 08:00. 8 unit-тестов. - TD-5 ImportJobRegistry в БД — сейчас in-memory
ConcurrentDictionary, теряется при рестарте. Перевод на таблицуImportJobs(Id, OrgId, Status, Progress, Total, StartedAt, FinishedAt, Errors JSON). Миграция.MoySkladImportControllerиспользует. Тесты. ✅Domain.Integrations.ImportJob(TenantEntity); миграцияPhase8c_ImportJobs.ImportJobRegistryтеперь IServiceScopeFactory-backed:Createпишет строку немедленно,SaveAsyncобновляет,Get/RecentlyFinishedчитают из БД. КонтроллерMoySkladImportController.RunInBackgroundAsyncдополнен periodic flush через Timer (каждые 2 сек) + финальный flush в finally — UI видит реальный прогресс, terminal-state сохраняется через рестарт. 3 интеграционных теста (survives across scope, RecentlyFinished, tenant-isolation).
Итог
Все 4 пункта выполнены. Спринт 5 завершён 2026-05-28.
Сводка:
- P1-5 Demand — оптовая отгрузка контрагенту-юрлицу с
DemandPayment.CreditиPaidAmount-полем; 3 интеграционных. - P1-18 OrgAuditLog — per-tenant журнал через
SaveChangesInterceptor, atomic с мутацией, diff в jsonb; 3 интеграционных. - P1-22 Email —
IEmailSender.SendHtmlAsync(multipart) + mustache-light renderer + 3 шаблона + 2 recurring Hangfire-джоба; 8 unit. - TD-5 ImportJob — persistence в БД, переживает рестарт, periodic flush для live-прогресса; 3 интеграционных.
Сборка: зелёная. Тесты: 35 unit + 68 integration = 103 зелёных.
Что осталось вне scope
- ОФД-оператор (нужен внешний участник:
Транском/Касса24); - MoySklad webhook-токены прод;
- WPF/POS UI (Windows-SDK);
- Стейдж→прод-деплой;
- Real SMTP-сервер для прод (Mailgun/Sendgrid/etc).
Лог
- Каждый пункт: build + тесты + коммит порцией + отметка [x] + коммит прогресса.
- Все правки на
main(origin Forgejo), без коммитаglobal.json.