metallkart-erp/CDC.md
louis 27c36375b1 feat: SPEC-37A settings multi-tabs + notification preferences + fix budget byCategory
- Fix budget byCategory: use PurchaseOrderLine (qty×price) instead of PR.budgetAmount (null)
- UserNotificationPreference model (7 categories, role defaults, @@unique userId+category)
- GET/PUT /notification-preferences routes (upsert transactional)
- NotificationHelper: shouldNotifyInApp() filters by category before creating
- PaymentRemindersService: category PAYMENT_DUE on notifyRole calls
- Frontend: SettingsPage 3 tabs (Profile/Notifications/Categories)
- ProfileTab, NotificationsTab (Switch inApp, Email/Telegram disabled)
- CDC updated session 37

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 17:32:40 +03:00

20 KiB
Raw Blame History

МеталлКарт ERP — Document de continuité Session 27

Date: 22 mars 2026


1. RÈGLES INDISPENSABLES DE TRAVAIL

Architecture agentique

  • claude.ai = ARCHITECTE + PROMPTEUR uniquement. NE JAMAIS piloter le VPS commande par commande.
  • Si tenté de lancer des commandes complexes → STOP → écrire une spec → send_task.sh
  • VPS Hostinger Ubuntu 24.04, IP 76.13.55.81, SSH: ssh root@76.13.55.81

Pattern d'envoi des specs (VALIDÉ, NE PAS IMPROVISER)

1. bash_tool: encoder la spec en base64
2. CLAUDE_TERMINAL (n8n workflow Y5kkG8IIFTxSSlDs):
   - Heredoc B64END pour écrire le b64 dans /tmp/sb.txt
   - node -e POST vers HOST_TERMINAL (172.17.0.1:9999) pour décoder et écrire /tmp/spec_XXX.md
3. CLAUDE_TERMINAL:
   - node -e POST vers HOST_TERMINAL: launch script qui lit /tmp/spec_XXX.md, encode b64, appelle send_task.sh
4. Vérifier les logs: via HOST_TERMINAL tail /opt/erp/logs/spec_XXX_*.log

Secrets:

  • CLAUDE_TERMINAL: mk_terminal_2026
  • HOST_TERMINAL: mk_host_2026

Règles absolues

  • Ne jamais sleep >60s dans CLAUDE_TERMINAL (timeout)
  • curl absent du container n8n → toujours node http.request()
  • Ne jamais mettre du b64 en argument shell ni dans JS template literals
  • /tmp dans CLAUDE_TERMINAL = container n8n, PAS le host VPS
  • CLAUDE_TERMINAL a un timeout task runner n8n, POST HOST_TERMINAL pour commandes longues
  • prisma migrate dev IMPOSSIBLE en non-interactif → db push
  • EADDRINUSE port 3001 → fuser -k 3001/tcp
  • Toujours utiliser db push (pas migrate dev) après modification schema

2. ÉTAT DU PROJET — Session 27

Stack

  • Backend : Fastify + Prisma + PostgreSQL 16 + TypeScript, port 3001
  • Frontend : Vite 8 + React 19 + Ant Design 6 + TypeScript, port 5173 (nginx 5180)
  • Repo : /opt/erp/metallkart-erp/
  • Services systemd : erp-backend.service, erp-frontend.service
  • PG staging : 172.19.0.5, user metallkart, DB erp_staging

Modules backend complets

  • auth (JWT, RBAC 15 rôles, seed 15 users)
  • suppliers (CRUD, INN, pagination)
  • products (CRUD, SKU, relations)
  • orders (state machine XState 10 statuts, ref auto, transitions RBAC)
  • revisions (workflow conditionnel, double approbation)
  • photos (upload multipart, storage abstraction)
  • passport (agrégation complète commande JSON)
  • dashboard (summary, alertes, vue financière)
  • payment-requests (XState, 4 catégories, budget checks)
  • estimates (workflow 2 validateurs, versioning, import Excel)
  • contract-tracking (XState relances, escalade)
  • supplier-database (CRUD enrichi, tags, contacts)
  • purchases (PR CRUD, génération auto smeta, consolidé)
  • steel-references (52 profils, import bulk)
  • tenders (6 statuts, offres, compare, award multi-fournisseur)
  • purchase-orders (from-tender, TLT, QR, email, PDF)
  • budget (double validation FIN+OPS, blocage PO)
  • financial (suivi coûts, FinancialTracking, ExpenseCode, MonthlyBudget)
  • cashflow (ДДС, RecurringPayment, BankStatement, forecast, export Excel)
  • profitability (marge, hook cashflow→tracking, export)
  • notifications (centralisées, NotificationHelper)
  • tasks (centre de tâches unifié, escalade auto)
  • audit (journal, stats, export CSV)
  • admin (users CRUD, config système, health check)
  • email (SMTP+IMAP, cron inbox poll)
  • consultation (drafts KP, envoi email groupé)
  • onec (import 1С OData, sync contractors/contracts/financial)
  • smeta-revision (comparison engine, validate+apply, AdditionalCost)
  • clients (CRUD, stats)
  • document-files (upload, versioning, checksum)

Frontend complet

  • Документооборот : 5 onglets (Оплаты, Сметы, Контракты, Поставщики, Заказчики)
  • Заказы : liste + détail 5 onglets (Général, Ревизии, Фото, Паспорт, Ревизии смет)
  • Закупки : 5 onglets (Потребности, Консультации, Заказы поставщикам, Бюджет, Планирование)
  • Финансы : 2 onglets (Календарь ДДС, Рентабельность)
  • Уведомления : 2 onglets (Уведомления, Задачи)
  • Администрирование : 3 onglets (Пользователи, Журнал аудита, Настройки)
  • Настройки : Category-Tag Configurator

3. NOUVEAU WORKFLOW ACHATS (référence)

Flux complet :

  1. Smeta : créer → import Excel → versions/révisions → soumettre → valider (2 validateurs)
  2. Lancement : commande LAUNCHED → PR auto-générées depuis specData
  3. Consultations : matrice PR×Fournisseurs → envoi email → réponses → analyse AI
  4. Tenders : compare matriciel → award ligne par ligne multi-fournisseur → PO DRAFT
  5. Budget : vue consolidée → double validation Максим (FIN) + Никита (OPS)
  6. PO : confirmation → email PDF+QR → expédition → réception QR scan

4. INFRASTRUCTURE

Composant Détail
VPS Hostinger Ubuntu 24.04, 76.13.55.81
PostgreSQL 16, staging 172.19.0.5
n8n container n8n-n8n-1, SQLite
SMTP purchase@metdesigntver.ru, beget.com:465
IMAP purchase@metdesigntver.ru, beget.com:993
GitHub cifemvostok-png/metallkart-erp (privé)
Пугина PG 9.6, 85.91.121.250:5432
1С OData scloud.ru (désactivé session 18)

5. HISTORIQUE DES SESSIONS

Sessions 1-7 (mars 2026)

Infrastructure, modules backend core (auth, suppliers, products, orders, payment-requests, estimates, contract-tracking, supplier-database, purchases, tenders, purchase-orders, financial, cashflow, profitability, notifications, tasks, audit, admin).

Session 8

Frontend scaffolding (Vite+React+AntDesign), Документооборот 4 tabs, Заказы pages.

Sessions 10-12

Phase 1.5 (filtres, ContractAttachment→APPENDIX, clients, archive PR), EstimatesTab enrichi, ДДС dashboard v2, bugfixes documents.

Sessions 13-16

Smeta import Excel (parser dynamique, 13 MaterialCategory), Document Files module, frontend finance+notifications+admin, Phase 1.5B/C (traductions, améliorations frontend).

Sessions 17-18

Email service SMTP+IMAP, consultation fournisseurs, QR codes PO, Planning Gantt, configurable scoring, 2-validator estimate workflow, bugfixes critiques.

Sessions 19-20

Consultation matrice PR×Fournisseurs, 1C OData import, tender cleanup+offer lines, compare modal matriciel, award lines multi-fournisseur, offer analyzer AI, tender responses multipart.

Sessions 21-22

Editable drawers, batch confirm, partial award, TLT intégration (auto-PO, exclude tenders), category-tag configurator, tender dispatch fix, visual validation tool, 18+ specs corrections/améliorations.

Sessions 23-24

TLT schema+auto-PO+frontend, data cleanup, SPEC_DETAIL_CATEGORIES, smeta revision engine (comparison+validate+apply+AdditionalCost), PR cascade cleanup, order delete/archive.

Sessions 25-26

SmetaLot prévisionnel (créé puis supprimé — lotissement déplacé au niveau PO), refonte lotissement backend/frontend, test infrastructure (E2E+Playwright), dropdown/expand fixes.

Session 27 (22/03/2026)

27A — Infrastructure Consolidation

  • CLAUDE.md : 4 nouvelles sections (tests obligatoires, validation frontend, skills, workflow achats)
  • dev_task.sh : --allowedTools étendu avec 5 outils MCP Playwright
  • Skills nettoyées : suppression tailwindcss, redis, mcp-server-guide (15 restantes)
  • Test E2E workflow : 18/18 passés (auth→client→order→estimate→submit→approve→launch→PRs(9)→tender→dashboard)
  • tests/test_workflow_e2e.sh créé

27B — Purchase Orders API Routes

  • Nouveaux fichiers : src/modules/purchase-orders/po-pdf.service.ts
  • Routes CRUD : POST/DELETE /purchase-orders (avec auto orderCode PO-YYYYMMDD-NNN, auto totalAmount)
  • Routes workflow : confirm (QR), send-email (PDF+QR attachés), mark-shipped, receive, cancel
  • Route génération : POST /purchase-orders/generate-from-tender/:tenderId (groupé par supplier)
  • Route PDF : GET /purchase-orders/:id/pdf (pdfkit avec header, lignes, QR, signature)
  • SMTP configuré : SMTP_FROM ajouté dans .env
  • Dépendances : nodemailer, pdfkit, qrcode

27C — Budget Consolidé + Double Validation

  • Nouveau modèle Prisma : BudgetValidation (@@unique orderId, enum PENDING/FIN_APPROVED/TECH_APPROVED/FULLY_APPROVED/REJECTED)
  • Relations ajoutées : Order.budgetValidation, User.budgetFinValidations, User.budgetTechValidations
  • Nouveaux fichiers : src/modules/budget/budget.service.ts, budget.routes.ts
  • 6 routes /api/v1/budget : GET order/:orderId (vue consolidée), GET dashboard (KPIs), POST validate-financial, POST validate-technical, POST reject, POST reset
  • BudgetService : calculs automatiques totalEstimated (smeta), totalAwarded (TenderLineAward), totalPO (PurchaseOrder non CANCELLED), variance, bySupplier, byCategory
  • Guard PO confirm : vérifie FULLY_APPROVED avant DRAFT→CONFIRMED (sauf clientOrderId null)
  • Rôles : DIRECTION_FIN pour financier, DIRECTION_OPS pour technique, ADMIN pour reset

27D — Frontend PO + Budget Tabs

  • PurchaseOrdersTab : modal création (supplier, lignes éditables, auto-total, DatePicker), génération depuis tender AWARDED, suppression DRAFT, PDF download, actions contextuelles par status
  • BudgetTab (280 lignes) : 4 KPI cards (estimé, PO, variance colorée, validé count), tableau commandes, drawer détail (bySupplier, byCategory, validation RBAC)
  • Nouveaux fichiers : frontend/src/api/budget.ts, frontend/src/pages/purchases/BudgetTab.tsx
  • Modifiés : PurchaseOrdersTab.tsx, PurchasesPage.tsx, App.tsx, types/purchases.ts, api/purchaseOrders.ts
  • Route /purchases/budget ajoutée

27E — MEMORY.md Mémoire Étendue

  • Créé /opt/erp/metallkart-erp/MEMORY.md : contexte complet projet (équipe, infra, credentials, bots TG, pipeline, règles métier smeta, n8n gotchas, skills, Пугина, historique sessions, DNS)
  • CLAUDE.md : section "Mémoire étendue" ajoutée en référence
  • Solution au problème de limite 30 mémoires claude.ai

Règles métier documentées (session 27)

  • Smeta 3 onglets : Техданные (fabrication), Расчет (chiffrage/budget), СПЕЦИФИКАЦИЯ (plan achats)
  • Пром-Лазер = placeholder historique → toujours ТЛТ pour Лазер et Труборез
  • Services = chaque fournisseur différent
  • Сетка : 2 modes (achat fini "комплект сетки" OU проволока + сварка сетки sous-traitée)
  • Техданные exclut services de transformation (galvanisation, traitement thermique)

État technique post-session 27

  • ~200+ routes API, 1000+ tests (18 e2e + 6 Playwright), tous verts
  • Modules complets : Orders, Clients, Documents, Estimates, PurchaseRequirements, Tenders, TenderOffers, PurchaseOrders, Budget, ДДС, Profitability, Notifications, Audit
  • Frontend : 6+ onglets page Achats (Сметы, Консультации, Заказы поставщикам, Бюджет)
  • Mémoire : claude.ai 20/30 + MEMORY.md sur VPS

Session 29 — Sync PRs on Smeta Revision (24/03/2026)

29A — Backend syncPRsOnRevision

  • Hook dans POST /revision/:revId/apply (estimates.routes.ts L470+)
  • SmetaRevisionService.syncPRsOnRevision(revisionId) dans smeta-revision.service.ts
  • Pour chaque decision APPLY: flag le PR via impactedPRIds ou description matching → revisionAlert=true + revisionNote
  • Pour chaque decision ADDITIONAL_COST (position ORDERED):
    • Si delta > 0: crée nouveau PR DRAFT (quantity = delta × order.quantity, estimatedPrice = unitPrice)
    • Si delta < 0: flag SURPLUS sur PR existant (note avec fournisseur + PO number)
  • Notification ACHETEUR via NotificationHelper.notifyRole si flagged > 0
  • Champs Prisma ajoutés: revisionAlert, revisionNote, revisionAckBy, revisionAckAt sur PurchaseRequirement
  • Routes: POST /purchase-requirements/:id/ack-revision, POST /purchase-requirements/ack-revision-bulk

29B — Frontend tag ПОМЕНЯТЬ + ack-revision

  • Types purchases.ts: revisionAlert, revisionNote, revisionAckBy, revisionAckAt
  • API purchases.ts: ackRevision(id), ackRevisionBulk(orderId)
  • PurchaseRequirementsTab.tsx: tag ⚠ ПОМЕНЯТЬ + Tooltip revisionNote + bouton Обработано + row highlight #f9f0ff + bulk ack button

29C — Corrections

  • Fix version collision: MAX(version)+1 au lieu de currentSmeta.version+1 (estimates.routes.ts L357)
  • PR DRAFT pour delta ORDERED dans syncPRsOnRevision (action ADDITIONAL_COST)
  • Historique révisions: déjà implémenté en 24J (bouton Просмотр diff)

29D — UX Notifications + Tasks

  • Modal notification: clic sur message → Modal avec texte complet (whiteSpace: pre-wrap)
  • Filtre rôle dans les tâches: Select "Фильтр по роли" + cartes cliquables

29E — Fix quantité delta PR

  • quantity = deltaQtyPerUnit × order.quantity (quantité totale)
  • estimatedPrice = unitPrice (prix unitaire inchangé)
  • budgetAmount = deltaQtyTotal × unitPrice
  • Arrondi toFixed(4) pour quantités, toFixed(2) pour prix
  • Nettoyage 4 PR orphelins

29F — Alerte surplus (diminution quantité)

  • Quand delta < 0 sur position ORDERED: flag PR existant avec revisionAlert + note SURPLUS
  • Note inclut: fournisseur, n° PO, surplus total, coût
  • Résolu via lineAwards → purchaseOrder → supplier

6. BUGS CONNUS NON RÉSOLUS

Session 30 — Revision Impact Report + Budget Fix (24/03/2026)

30A — Backend Revision Impact API

  • Nouveau fichier: src/modules/purchases/revision-impact.service.tsRevisionImpactService
  • Route: GET /orders/:id/revision-impact (RBAC: ADMIN, DIRECTION_FIN, DIRECTION_OPS, ACHETEUR, ADM_PRODUCTION)
  • Agrège PRs impactés par révisions: types SURPLUS/RACHAT/MODIFIED, totals, ack status
  • Données via sourceType='SMETA_REVISION' + revisionAlert=true

30B — Frontend Onglet "Изменения Закупок"

  • Nouveau fichier: frontend/src/api/revisionImpact.ts
  • Nouveau fichier: frontend/src/pages/orders/tabs/RevisionImpactTab.tsx (~200 lignes)
  • 6ème onglet dans OrderDetailPage avec Badge pending count
  • 3 cartes stats (SURPLUS rouge, RACHAT vert, MODIFIED jaune)
  • Tableau 9 colonnes + ack individuel/bulk + row coloring + Tooltip revisionNote
  • Visible uniquement pour commandes post-AWAITING_ESTIMATE

30C — Fix Budget Computation

  • budget.service.ts: computeTotals() et getDashboard() — totalEstimated multiplié par order.quantity
  • schema.prisma: variancePercent élargi Decimal(5,2) → Decimal(10,2)
  • Résultat: order 50 totalEstimated=2 512 171₽ (×170), variancePercent=57.15% (plus d'overflow)

30D — Cancel Pending Revision + Visible History

  • Backend: route DELETE /estimates/revisions/:revId — annule REV_PENDING (transaction: delete decisions, costs, revision, new smeta)
  • Frontend: bouton "Отменить ревизию" avec Popconfirm dans SmetaRevisionTab
  • Historique: Collapse remplacé par Card — toujours visible
  • API: cancelRevision() ajoutée dans smetaRevisions.ts

État technique post-session 30

  • ~205+ routes API, 1000+ tests (18 e2e + 6 Playwright), tous verts

  • Nouveaux fichiers: revision-impact.service.ts, revisionImpact.ts, RevisionImpactTab.tsx

  • Onglet OrderDetailPage: 6 tabs (Информация, Ревизии, Фото, Ревизия сметы, Паспорт заказа, Изменения Закупок)

  • Validation toasts parfois apparaissent sur Закупки (à investiguer)

  • Puppeteer screenshot multi-page auth loss (contournement: screenshot-api.js)

  • 1С sync désactivé (routes 503) — données nettoyées session 18

Session 31 — PurchaseLots, Factures, Conditions paiement (24/03/2026)

TLT-C1a — Backend PurchaseLots + Factures + Conditions paiement

  • POInvoice, POPaymentSchedule, POInstallment models
  • createLotsForPO helper, 15+ routes, confirm guards, PaymentRequest auto-creation PAY-

TLT-C1b-1 — Frontend Drawer PO enrichi

  • 3 sections: Лоты, Счёт-фактура, Условия оплаты
  • 11 API functions, 6 types, RBAC actions

Session 32 — PO Workflow SENT, Onglet Счета, Steel Conversion, Cashflow PO, Доп затраты auto (25/03/2026)

32A — PO Workflow SENT + Lots Overflow Fix

  • PurchaseOrderStatus +SENT, PurchaseLot.quantity Decimal(15,2), sendPOEmail→SENT transition

TLT-C1b-2 — Onglet Счета (ex-Оплаты)

  • Code couleur lignes par statut, date paiement vert/rouge, bouton Файл facture, drawer enrichi

32B — Offer Analyzer Refonte + Steel References

  • 51 profils acier seed, SteelConversionService т↔м, offer analyzer refonte raw+deterministic post-processing

32C — Cashflow Prévisionnel PO

  • Forecast PO installments, KPI poCommitments, 5ème carte KPI frontend

32D — Calcul auto Сумма ДЗ

  • Bannière orange temps réel ADDITIONAL_COST dans SmetaRevisionTab

Session 34 (07-08/04/2026)

Dev local PC validé. 22 items livrés (18 fixes + 4 features). Fixes: PDF blob, cashflow, P0 quantités PR, tender async, PR doublons, close() libère PR, updateSuppliers, Zod Predoplata/Postoplata, ADMIN bypass, tags alignés, Gantt barres, montants transhi, PR résiduelle award partiel, lots min 1, doublon priorité. Features: lien tender dans statut PR, Podtverdit vse groupe, filtre commande Tendery, arkhivirovat zakrytye. Pipeline WIN_TERMINAL testé.

Session 35 (08-09/04/2026)

15 specs livrées, workflow paiement refondu. Fixes (8): git push 403 PAT, CashflowTab off-by-one, award global supprimé→compare, revertToSent COMPARING→SENT, 4 fixes UI drawer PO, navigation tab PO depuis Счета, PR ORDERED après award, fournisseurs tender filtrés par dispatches, TLT renommé ООО "ТЛТ". Features (5): recherche textuelle tenders serveur-side, archivage PO (CANCELLED auto + RECEIVED manuel + bulk), installments liés aux paiements (purchaseOrderId, paymentRequestId, cascade markPaid), refonte workflow paiement PENDING→APPROVED→PAID avec toggle Инициировать/Отозвать, bouton "Заказ ТЛТ" sur LASER_FLAT/LASER_TUBE/PIPE_CUT. Améliorations: dates installments éditables (DatePicker inline, dueDate propagé sur PaymentRequest), purge données ERP local.

Session 36 (09-10/04/2026) — complète

SPEC-36A: Comparaison révision smeta descend dans specData (compareSpecItems, SPEC_DETAIL_CATEGORIES +LASER_TUBE/+TUBE_BENDING, endpoint recalculate). Fix lots TLT/laser: requirementId nullable, splitLots corrigé, items enrichis requirementIds. SPEC-36B: Ближайшие платежи avec vraies données (POInstallment+PaymentRequest), 7 colonnes, tags statut. Fix fournisseur: Decimal→string coercé Number(). SPEC-36C: Revert PO CONFIRMED→DRAFT (unconfirm), rejette PR, bloque si paidAt. SPEC-36D: PaymentRemindersService cron J-3/J-1/J-0/overdue, notifyRole ACHETEUR+DIRECTION_FIN, idempotent, POST /check-reminders ADMIN. SPEC-36E: Refonte lotissement PO par ligne (PurchaseLotLine model, validation 100%, deliveryDays TenderOffer). SPEC-36E-FIX: Backfill items JSON→PurchaseOrderLine (12 POs migrés), fix createFromTender/createTlt. SPEC-36F: LotSplitModal plein écran (matrice ligne×lot, InputNumber/DatePicker, auto-calcul deliveryDate, validation Tag 100%). SPEC-36G: Fix révision smeta compare v2 pas v1 (suppression filtre statut currentSmeta). SPEC-36H: Fix navigation cashflow (liens Заказ/description/ЗП, highlight blink-red 10s), filtres statut Radio.Group, tri Просроченные↑, colonne Просрочка (N дн. rouge). Fix lot split: deliveryDate sauvegardée dans payload, auto-remplissage depuis supplier.deliveryDelayDays. Backfill PurchaseOrderLine.material: 48 lignes fixées (28 via requirementId, 20 TLT→LASER_FLAT).

Session 37 (10/04/2026)

Fix budget byCategory: remplacé PR.budgetAmount (null) par PurchaseOrderLine groupées par material (quantity×finalPrice). SPEC-37A: Refonte Настройки multi-onglets (Профиль/Уведомления/Категории). Modèle UserNotificationPreference (7 catégories: PAYMENT_DUE, PR_APPROVE, PR_CONFIRM, TENDER_OFFER, SMETA_REVISION, PO_DELIVERY, LOT_ORDER_DUE). Defaults par rôle (ROLE_DEFAULT_CATEGORIES). Routes GET/PUT /notification-preferences (upsert transactionnel). NotificationHelper: shouldNotifyInApp() filtre par catégorie avant création. PaymentRemindersService: category PAYMENT_DUE ajouté. Frontend: ProfileTab (GET /auth/me), NotificationsTab (Switch inApp actif, Email/Telegram disabled futur), SettingsPage 3 onglets.


7. PROCHAINES ÉTAPES

  • [P1] Cashflow prévisionnel (installments + PO confirmés + factures clients)
  • [P1] Refonte Планирование/Gantt
  • [P2] Erreurs TS backend (.test.ts)
  • invoices (facturation client)
  • reports (rapports financiers)
  • Intégration Пугина (vues SQL lecture seule)