12 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
848e747379 |
fix: внутренний ШК 2x (не 02/ведущий-0) + разовый прогон «Проверки данных» при старте
Найдено Mac-Claude на стенде: 1. Генератор внутреннего EAN-13 давал «02…» (ведущий 0 → зона UPC-A, может совпасть с реальным товаром). InternalBarcode.PickPrefix теперь берёт 2x из restricted- circulation 20–29 (без ведущего нуля), не пересекаясь с весовыми префиксами орг; «02» — только крайний резерв. Юнит-тест обновлён. 2. «Проверка данных» на Демо показывала все 0 и lastCheckedAt=null — ночной cron ещё не отрабатывал после пересева. Добавлен разовый BackgroundJob.Enqueue(DataQualityJob) при старте: после деплоя/пересева счётчики считаются сразу по всем организациям. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
261a720b52 |
feat(data-quality): фоновая «Проверка данных» — бэкенд (п.1,3,4)
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions
Hangfire DataQualityJob (ночью 02:20 UTC) считает по орг. счётчики проблем: без группы
(корень/«Без группы»), без цены продажи, без штрихкода (кроме услуг), дубль штрихкода,
возможный дубль имя+единица; товары в минусе — ссылкой на центр (не дублируем). Результаты
в data_quality_results (Phase43), живые детальные списки — по запросу.
API /api/data-quality: сводка, {check}/products, recompute, dismiss («это разные товары»),
bulk set-group/set-price/generate-barcodes, PUT settings (отключение проверок). unknown-cost
отложен до FIFO; PLU убран (поправка владельца). Integration-тест: счётчики/список/bulk/dismiss.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
210e8171d3 |
feat(money): автооткрытие смены на POS-синке + уведомление о сменах >24ч (п.1)
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions
POS: чек с точки без открытой смены — сервер автооткрывает смену (Cash-счёт точки, размен 0) и принимает чек (как в вебе). Фоновый ShiftWatchJob (раз в час) уведомляет кассира и админов о сменах, открытых дольше 24ч (персистентное уведомление + SignalR, дедуп раз в сутки). Тест POS-автооткрытия. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
29a951a6ff |
feat(notifications): центр уведомлений + агрегация/эскалация минусов + каналы
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions
Docker Web / Build + push Web (push) Waiting to run
Docker Web / Deploy Web on stage (push) Blocked by required conditions
Персистентные уведомления (таблица notifications, Phase41) + REST
/api/notifications (список/счётчик/read/read-all) → центр уведомлений в вебе
(колокольчик, API-backed) и мобильном API.
Фоновый джоб NegativeStockNotificationJob (cron */5): агрегирует новые минусы
в ОДНО уведомление на склад (дебаунс по NotifiedAt + интервал орг., по умолч.
15 мин), уважает тихие часы (Asia/Almaty); эскалация просроченных (>N часов,
по умолч. 24) владельцу/админам. Получатели — админы, владелец, кладовщики
(право InventoryEdit). Каналы: SignalR (живой колокольчик), Telegram (владельцу),
push-заглушка.
Настройки организации: NegStockNotify{Enabled,IntervalMinutes,QuietFrom/ToHour,
EscalateHours} + GET/PUT /api/organization/settings + секция в настройках веба.
Блок «Просроченные минусы» на дашборде.
Тесты: unit тихих часов 3/3; integration — джоб создаёт уведомление админу,
дебаунс, read-all 3/3 (вся группа NegativeStockTests зелёная).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
c69114f8ab |
feat(s30): PruneAutoTestEntities + одноразовая зачистка стейджа (шаг 7/9)
HousekeepingJobs.PruneAutoTestEntitiesAsync — Hangfire-recurring (02:45 UTC
daily, Cron:PruneAutoTestEntities). Удаляет:
- orgs по regex autotest-паттерна в имени (quality-, soak-, UI-a11y-,
SmokeOrgA/B-, PerfBaseline-, Reg-, RT-ratetest-, Probe Org, cat3*,
visual-, factory-smoke-, mt6*, doc4*, auth2*, rep5*, rt8*, wiz9*,
i18n*, twofa*, iso*, mob1*, axdet-, load-, s19-test- + любое имя
с [0-9]{10,}) старше 3 дней + все их таблицы с OrganizationId;
- orphan autotest-юзеров (без удалённой org) по regex в email и доменам
@e.kz / @test-fm.local.
Защищены: SuperAdmin (OrganizationId IS NULL), admin@food-market.local,
admin@demo-market.local, demo-*@food-market.local, market@email.com,
«Demo Market» и «Демо-магазин» — явные исключения.
deploy/stage-prune-autotest.sql — ровно та же логика как SQL-скрипт для
ручного запуска. Прогнал на stage: было 8653 user'ов / 8634 orgs →
стало 300 users / 305 orgs. SuperAdmin/demo/market остались.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
019c57ae3b |
feat(s25): autonomous continuous quality monitoring (8/8)
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions
Hourly smoke watchdog + auto-fix loop + dashboard + multi-tenant guard + perf regression + cleanup job + README badge. 1. ~/quality-watchdog.sh (cron 5 * * * *) — 8 checks (~60s): /health/ready, signup→login→/api/me, GET products, Playwright UI smoke (3.1 product CRUD), /metrics format, /hubs/notifications negotiate with token, multi-tenant isolation, perf p95. 2. Auto-fix loop: 2× consecutive red → ~/.fm-watchdog/incident-*.txt + queue/0000-incident-* to bump it ahead of Server-Claude's sprint queue. fm-watchdog.sh sees prefix 0000- as next. 3. scripts/quality-dashboard.py — renders docs/quality-status.md (current emoji, 8-step table, perf baseline, 7-day history, 24-run sparkline) + injects README badge 🟢/🟡/🔴. 4. Multi-tenant smoke: signup 2 orgs `quality-{epoch}-A/B`, create product in A, verify B sees 404/403 + total=0. 5. Perf regression: p95 over 10 reqs for /api/me, products, sales/retail/stats. Baseline = median of last 10 samples (robust to noise). >50% from baseline → alert. First 5 runs always green (warm-up). 6. HousekeepingJobs.PruneQualityTestOrgsAsync (cron 30 2 * * * UTC): finds orgs `quality-%` older than 24h, dynamically scans information_schema for tables with OrganizationId, iteratively DELETEs with FK-violation retry (up to 10 passes), then cleans AspNetUser*/OpenIddict* by email pattern `quality-%@test-fm.local`, finally users + organizations. 7. README badge: <!-- quality-badge --> marker updated each run. Validated: stage deploy ✓, Hangfire job registered ✓, dry-run SQL on 24 stage candidates → 0 remaining ✓, 3 cron-triggered runs all 8/8 green (12:42/12:45/12:48 +05) ✓. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
72d0a71307 |
docs(s24): docs cross-check + auto-gen + onboarding + test gap-fill (8/8 ✓)
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions
1. Docs cross-check — обновил performance-baseline.md (Sprint 18/20/23 фиксы), secrets.md (16 новых env-vars из Sprint 20+ — Authentication Google/Microsoft, Monitoring, Cleanup, Hangfire:Cron, Telegram, Maintenance, App, Storage, PUBLIC_GA_ID/YM_ID). 2. Auto-gen api-reference — ApiReferenceDocsJob (Hangfire weekly вс 05:30 UTC) + Python-эквивалент `/tmp/gen-api-ref.py` для commit actual snapshot. docs/api-reference.md = 195 endpoints, 57 controllers. 3. Coverage gap-fill — Sprint18To23FeaturesTests.cs (16 Facts): - bulk-update + cross-tenant isolation - UserPresets CRUD - inline-edit price PATCH - CSV import 2 строки транзакцией - OrgExport create + list isolation - 1C-CSV import с русскими заголовками - audit-log export CSV streaming + BOM check - MoySklad sync-status stub - SSO providers + 503 unconfigured + 400 unknown provider - bug-001 NUL byte → 400 - bug-004 tiny price → 400 - export CSV BOM Покрывает все новые контроллеры Sprint 18-23 + regression-protect для критичных багов. 4. Contract tests — deploy/swagger-diff.sh: pull /swagger/v1/swagger.json с двух URL, diff endpoints+schemas через python3. Exit 0/1/2 для blue-green safety gate. Multi-path auto-detect. 5. docs/error-codes.md — каталог HTTP-кодов API (200-503) + humanizeError pattern для фронта + retry-policy таблица. 6. docs/glossary.md — 50+ доменных терминов (Tenant/Organization/Stock/ StockMovement/RetailSale/Counterparty/Owner/Employee/Role/Permission/ advisory lock/Serializable/…) с ссылками на code-сущности. 7. docs/ONBOARDING.md — first 3 days для нового разработчика (install → запуск → структура → первый PR + FAQ). 8. README.md — обновил под текущее состояние: React 19, Sprint-history 1-24, ссылки на ключевые docs, корректный 5-min quick start. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
aa83f82dc5 |
feat(s22): data tooling — export/import + schema docs + anon dump (7 пунктов)
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions
1. GDPR org export — domain OrgExport + Phase22a миграция, OrgExportJob
собирает ZIP с JSON по каждой сущности через IObjectStorage,
DownloadToken 64-hex + 24h TTL + email-notify.
POST /api/org/export, GET /api/org/export[/{id}], GET download/{token}.
2. 1C CSV import — POST /api/catalog/products/import/1c-csv:
Windows-1251/UTF-8 BOM auto-detect, разделитель ;/, русские заголовки
(Артикул/Наименование/Единица/Цена/Группа/Штрихкод) или английские.
Нормализация unit-кодов (шт/кг/г/л/мл/упак). Делегирует на ImportCsv
(транзакция, multi-tenant). docs/imports.md.
3. deploy/anonymize-prod.sh — pg_dump прода → restore во временную БД →
UPDATE PII (email→user{N}@example.kz, phone→+7700111{N:04}, password→
тестовый hash, BIN/IIN синтетические, MoySkladToken=NULL, аудиты
TRUNCATE) → pg_dump → gz файл.
4. DbSchemaDocsJob (weekly вс 05:00 UTC) — information_schema → md с
таблицами + колонками + FK + mermaid ER-диаграммой (топ-20 таблиц).
Сохраняет в content-root db-schema-generated.md.
5. POST /api/admin/audit-log/export?format=csv|jsonl — streaming через
AsAsyncEnumerable. UTF-8 BOM для CSV, JSONL для grep'a. Multi-tenant.
6. GET /api/moysklad/sync-status — агрегат по import_jobs:
{ configured, lastSuccessAt, errorCountLast7Days, pendingCount,
byKind: { products: KindStatus, counterparties: KindStatus } }.
Stub если MoySkladToken=null.
7. docs/ARCHITECTURE.md — финальный итог 22 спринтов:
- Sprint 13-22 changes-сводка
- «Реализовано полностью» секция
- «Scaffolding» таблица с указанием что нужно от user'а
- «Не реализовано» секция (прод, SSO callback, KZ-перевод, POS-тест)
- Актуальная файловая структура
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
346b7bfd48 |
feat(s20): Mapster + SSO scaffold + maintenance automation (7 пунктов)
Some checks failed
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions
Docker Public / Build + push Public (push) Has been cancelled
Docker Public / Deploy Public on stage (push) Has been cancelled
1. TD-3 Mapster — Application/Mapping/MapsterConfig.cs с
TypeAdapterConfig для Product, Counterparty + collections.
ProductsController.List/Get/GetInternalAsync + CounterpartiesController.
List/Get переведены на .ProjectToType<TDto>(MapsterConfig.Config).
Inline Projection-Expression удалён.
2. SSO scaffold — Microsoft.AspNetCore.Authentication.Google + .MicrosoftAccount
пакеты, условная регистрация в Program.cs (только если ClientId задан).
ExternalAuthController с GET /api/auth/external/{provider} (Challenge или
503 если не настроено), /callback (501 с email — invite-flow TODO),
/providers (булевый список). docs/sso.md инструкция.
3. Stale-data cleanup — HousekeepingJobs расширен:
PruneOrgAuditLogAsync (>90д из Cleanup:OrgAuditLogDays),
PruneDraftsAsync (Supply/RetailSale/Demand старше 30д),
PruneRevokedRefreshTokensAsync (raw SQL DELETE из OpenIddictTokens).
3 новых cron'a в HangfireJobsConfigurator (03:00-03:20 UTC).
4. DB VACUUM automation — DatabaseMaintenanceJobs.VacuumTopTablesAsync:
pg_total_relation_size → топ-5 таблиц → VACUUM (ANALYZE) per table
с замером времени. Default cron еженедельно вс 04:00 UTC.
5. Disk usage monitoring — DiskMonitoringJob ежечасно: DriveInfo.AvailableFreeSpace
на пути из Monitoring:DiskPaths (default "/opt,/var/lib/docker").
<1GB → Telegram-alert на Monitoring:SuperAdminTelegramChatIds.
Anti-spam cooldown 6h. Gauge food_market_disk_free_bytes{mount}.
6. Performance regression detection — ~/nightly-perf-check.sh после
nightly-verify. Парсит /metrics, считает db_avg_ms, сравнивает с
baseline в ~/.fm-watchdog/perf-baseline.json. Δ>30% → Telegram alert
+ baseline НЕ обновляется (sliding window).
7. Public-site analytics placeholder — Astro BaseLayout рендерит
gtag/Yandex.Metrika только если задан PUBLIC_GA_ID / PUBLIC_YM_ID;
иначе <script data-id="REPLACE_ME" data-doc="docs/analytics.md">
маркер. docs/analytics.md с инструкцией подключения.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
3088237ea7 |
feat(telegram): OwnerDailySummaryJob + bot binding (P2-14)
Some checks are pending
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions
Docker Web / Build + push Web (push) Waiting to run
Docker Web / Deploy Web on stage (push) Blocked by required conditions
Backend: - Organization.OwnerTelegramChatId (long?) — миграция Phase9a. - TelegramOptions / TelegramBotClient (Telegram Bot API sendMessage). Disabled-mode когда токен пустой (Dev/CI). HTML parse_mode. - OwnerDailySummaryJob.RunAsync — пробегает по org с привязанным chatId, рендерит сводку (выручка вчера, чеков, средний чек, топ-3 по выручке, low-stock 5 строк) и шлёт. Best-effort на каждой org. RenderSummaryAsync — publicный для тестов. - HangfireJobsConfigurator: cron "0 6 * * *" UTC = 09:00 МСК. - TelegramBindingController: GET /status (botEnabled, username, chatId, deepLink), PUT /bind (тестовое сообщение → проверка chatId → save), DELETE (unbind). Конфиг: - Telegram:BotToken — env Telegram__BotToken. - Telegram:BotUsername — для deep-link. UI: - OrganizationSettings.TelegramSection: показывает статус (bot enabled? bound?), deep-link к боту, пошаговая инструкция (start → userinfobot → ввести chat_id → проверка). Toast на привязку/отвязку через meta.successMessage. Тесты: - TelegramOwnerSummaryTests: рендер содержит org_name, метрики, HTML. ✓ 1/1. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
25d92c989a |
feat(email): HTML-шаблоны MailKit + invite/weekly/low-stock джобы (P1-22)
Application:
- IEmailSender.SendHtmlAsync(html, textFallback) - multipart/alternative
с plain-text для клиентов без HTML;
- EmailTemplateRenderer - минимальный mustache-light:
{{key}} (HTML-escape), {{{raw}}} (без escape), {{#key}}...{{/key}}
(условный блок, truthy если не-null/не-пусто/не-"0"/не-"false");
- EmailTemplates - загрузчик embedded HTML-шаблонов из
Resources/EmailTemplates/*.html, кеш after-first-read, parse
"Subject: <тема>" из первой строки, плюс plain-text strip для fallback.
Шаблоны (embedded в food-market.api.dll):
- invite.html - приглашение сотрудника с временным паролем и кнопкой
«Открыть Food Market».
- weekly-summary.html - выручка/чеки/средний чек за неделю + топ-товары.
- low-stock.html - таблица товаров с stock < MinStock.
EmployeesController.Create принимает SendInvite (требует CreateAccount):
формирует payload из orgName/loginUrl/role и шлёт через SendHtmlAsync.
SMTP-ошибки логируются warning'ом, не блокируют создание (showOnce
tempPassword фронту всё равно отдаётся).
Hangfire recurring jobs (EmailNotificationJobs):
- weekly-summary: cron "0 7 * * 1" (понедельник 07:00 UTC) - по каждой
активной орге считает revenue/tx/avgTicket/top-5, шлёт Admin'ам;
- low-stock-alert: cron "0 8 * * *" - товары с sum(stock)<MinStock,
шлёт Admin'ам. AsyncLocal tenant override на каждую орг чтобы
query-filter работал корректно.
Тесты: 8 unit на EmailTemplateRenderer + EmailTemplates (escape, raw,
условные блоки, invite/low-stock-шаблон-loaders). Все 35 unit
зелёные (27 + 8 новых).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
a74fa114d8 |
feat(hangfire): dashboard + scheduled cleanup джобы (P1-16)
Hangfire.PostgreSql storage (тот же ConnectionString:Default). Сервер стартует только когда Hangfire:Enabled (true по умолчанию) — в интеграционных тестах выключаем через env Hangfire__Enabled=false, чтобы тесты не плодили служебные таблицы в одноразовом контейнере. Dashboard на /hangfire с авторизационным фильтром SuperAdminHangfireFilter — требует роли SuperAdmin (стандартный OpenIddict-токен валидируется аутентификационным middleware'ом перед этим). Recurring jobs (HangfireJobsConfigurator): • prune-stock-movements — ежедневно 03:30 UTC, удаляет StockMovement старше 730 дней (Hangfire:Retention:StockMovementDays). За 30 минут до бэкапа, чтобы pg_dump не цеплял временные блокировки. • prune-audit-log — ежедневно 03:45 UTC, удаляет super_admin_audit_log старше 90 дней (Hangfire:Retention:AuditLogDays). Логика очистки в HousekeepingJobs (scoped, использует AppDbContext с IgnoreQueryFilters — это межтенантная задача). Тесты: 1 unit (PruneStockMovements удаляет только старые), 1 интеграционный (dashboard не отвечает без Hangfire-сервера). Полный прогон: 24 unit + 32 integration = 56 зелёных. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |