# МеталлКарт ERP — Document de continuité Session 31 ## Date: 24 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 ### Session 30 — Revision impacts + budget fix + cancel revision (24/03/2026) #### 30A — API revision-impact - GET /orders/:id/revision-impact — agrégation impacts revision achats par commande - Types SURPLUS/RACHAT/MODIFIED, totaux par type, statut acquittement #### 30B — Frontend onglet Изменения Закупок - 3 cartes stats SURPLUS/RACHAT/MODIFIED, tableau impacts avec ack individuel+bulk - Badge pendingCount sur onglet, visible post-AWAITING_ESTIMATE #### 30C — Budget fix quantity - totalEstimated×order.quantity dans computeTotals+getDashboard - variancePercent Decimal(10,2) overflow fix #### 30D — Cancel revision - DELETE /estimates/revisions/:id annule REV_PENDING+supprime newSmeta cascade - Bouton Отменить ревизию frontend, historique revisions sorti du Collapse en Card visible #### 30E — Refonte SmetaRevisionTab - Fusionné 2 sections (Доп. затраты Card + История ревизий Timeline) en 1 Table AntD 8 colonnes - Colonnes: Версия, Статус, Дата, Автор, Изменений, Дельта бюджета, Доп. затраты, Действия - Expandable rows pour доп. затраты par revision, orphan costs en bas ### Session 31 — PurchaseLots, Factures, Conditions paiement (24/03/2026) #### TLT-C1a — Backend PurchaseLots + Factures + Conditions paiement - 2 enums: PaymentType (PREPAYMENT_FULL/INSTALLMENT/POST_DELIVERY), InvoiceStatus (PENDING_VALIDATION/APPROVED_AWAITING_PAYMENT/PARTIALLY_PAID/PAID) - 3 modèles: POInvoice, POPaymentSchedule, POInstallment - `createLotsForPO` helper intégré dans 4 méthodes création PO (createManual, generateFromTender, createFromTender, createTlt) - 15+ routes: lots (GET /:poId/lots, POST /:poId/lots/split), invoices (POST multipart, validate, trigger-payment, confirm-payment, GET file, DELETE), payment schedule (PUT, GET), backfill-lots - 3 guards CONFIRM: facture obligatoire + conditions paiement + dates lots (skip TLT) - Facture validée → auto-crée PaymentRequest code PAY- statut APPROVED - Cycle: APPROVED → trigger-payment → AWAITING_PAYMENT → confirm-payment → PAID - Prefix PAY- (ex PR-) pour nouveaux payment requests - PO_INCLUDE enrichi: lots, invoices, paymentSchedule #### TLT-C1b-1 — Frontend Drawer PO enrichi - 6 types: POLotItem, POInvoice, InvoiceStatus, PaymentType, POInstallment, POPaymentSchedule - 11 API functions dans purchaseOrders.ts - Drawer PO: 3 nouvelles sections + 3 modals: - Лоты: table + modal split (qty + DatePicker) - Счёт-фактура: table Tags colorés (jaune/bleu/rouge/violet/vert + overdue), upload Dragger, RBAC actions - Условия оплаты: Radio 3 types, installments éditables DRAFT, read-only sinon, Alert récap - Code couleur: PENDING_VALIDATION=jaune, APPROVED_AWAITING_PAYMENT=bleu (OVERDUE=rouge si dueDate10M ₽ - sendPOEmail() → transition CONFIRMED→SENT après envoi email réussi - ship() n'accepte que SENT (plus CONFIRMED directement) - Frontend: boutons contextuels par statut dans liste et drawer PO - Migration SQL: UPDATE purchase_orders SET status = 'SENT' WHERE status = 'CONFIRMED' AND email_sent_at IS NOT NULL #### TLT-C1b-2 — Onglet Счета (ex-Оплаты) - Onglet Документооборот renommé "Оплаты" → "Счета" - Code couleur fond lignes par statut (jaune PENDING, bleu APPROVED, rouge REJECTED, vert PAID, violet INFO_REQUESTED) - Colonne date paiement: vert=réelle (paidAt), rouge=prévisionnelle (dueDate) - Bouton "Файл" pour consulter facture fournisseur (POInvoice file download) - Drawer détail enrichi: section infos facture + conditions paiement (via payment-schedule API) - Backend: PaymentRequest list enrichi avec relation poInvoice - Types: POInvoiceSummary, InvoiceStatus ajoutés #### 32B — Offer Analyzer Refonte + Steel References - Seed 51 profils acier dans steel_references (SQUARE_TUBE, RECT_TUBE, ROUND_TUBE, ANGLE, CHANNEL, SHEET, FLAT_BAR, ROUND_BAR) avec poids théoriques ГОСТ - SteelConversionService: findWeightPerMeter (regex cyrillique), metersToTonnes, tonnesToMeters, pricePerTonneToPerMeter, removeVAT/addVAT - Route GET /api/v1/steel-references/convert - Offer analyzer refonte: nouveau prompt AI extraction données brutes (supplierQuantity, supplierUnit, supplierUnitPrice, vatIncluded, vatRate) - Post-processing déterministe normalizeAnalysis(): conversion unités via SteelConversion, retrait НДС, détection offre partielle - rawSupplierData conservé pour audit, transportCost ajouté #### 32C — Cashflow Prévisionnel PO - Forecast enrichi avec échéances PO (installments non payés des PO CONFIRMED/SENT/SHIPPED) - Champ poExpenses par semaine dans le forecast - KPI poCommitments: totalUnpaid, poCount, installmentsDue7d, overdueInstallments - Frontend: 5ème carte KPI "Обязательства по закупкам" dans CashflowTab - Live: 2 PO actifs, 63.6M total non payé #### 32D — Calcul auto Сумма ДЗ - Bannière orange temps réel dans SmetaRevisionTab quand ADDITIONAL_COST sélectionné - Affiche: nombre positions + montant total ₽ + impact budget - Visible uniquement pendant révision REV_PENDING active #### État technique post-session 32 - ~225+ routes API, 1000+ tests (18 e2e + 6 Playwright), tous verts - 5 specs livrées, 0 échec - steel_references: 51 profils seedés - PurchaseOrderStatus: 7 valeurs (DRAFT, CONFIRMED, SENT, SHIPPED, RECEIVED, PARTIAL, CANCELLED) - PurchaseLot.quantity: Decimal(15,2) --- ## 6. BUGS CONNUS NON RÉSOLUS - 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 33-34 (06-08/04/2026) Audit 33A (PDF OK, workflow OK, cashflow OK, Gantt OK). Session 34: 22 items (18 fixes + 4 features). Fixes: PDF blob, cashflow, quantités PR, tender async, doublons, close() libère PR, updateSuppliers, Zod, ADMIN bypass, tags, Gantt, montants, PR résiduelle, lots min 1. Features: lien tender PR, Podtverdit vse, filtre commande Tendery, arkhivirovat. ### Session 35 (08-09/04/2026) 15 specs. Workflow paiement refondu PENDING→APPROVED→PAID. Fixes: git push PAT, CashflowTab off-by-one, award compare, revertToSent, UI drawer PO, navigation tab, PR ORDERED, fournisseurs filtrés, TLT renommé. Features: recherche tenders, archivage PO, installments liés, toggle Инициировать/Отозвать, bouton ТЛТ, dates installments éditables. ### Session 36 (09-10/04/2026) 36A: Comparaison révision smeta specData (compareSpecItems, LASER_TUBE/TUBE_BENDING). 36B: Ближайшие платежи vraies données. 36C: Revert PO CONFIRMED→DRAFT. 36D: PaymentRemindersService cron J-3/J-1/J-0/overdue, idempotent. 36E: Lotissement PO par ligne (PurchaseLotLine, validation 100%). 36E-FIX: Backfill items→PurchaseOrderLine. 36F: LotSplitModal plein écran matrice ligne×lot. 36G: Fix révision smeta compare dernière version. 36H: Navigation cashflow + highlight blink-red + filtres statut + tri Просроченные↑ + colonne Просрочка. Fix lot split deliveryDate + auto-remplissage supplier. Backfill PurchaseOrderLine.material (48 lignes fixées). ### Session 37 (10/04/2026) Fix budget byCategory (PurchaseOrderLine au lieu de PR.budgetAmount null). 37A: Refonte Настройки multi-onglets (Профиль/Уведомления/Категории). Modèle UserNotificationPreference (7 catégories, defaults par rôle). Routes GET/PUT /notification-preferences. NotificationHelper filtrage par catégorie (shouldNotifyInApp). PaymentRemindersService category PAYMENT_DUE. Frontend ProfileTab + NotificationsTab (Switch inApp, Email/Telegram disabled). 37B: Budget drawer enrichi (totaux + 3 colonnes Бюджет/Факт/Разница). 37C: Catégories notification sur tous notifyRole existants (estimates, tenders, tasks) + lot order date reminders (checkLotOrderDates). 37D: notifyByCategory remplace notifyRole dans reminders (ADMIN recoit les notifs) + budgetAmount peuple a la creation PR. 37E: Budget par categorie depuis smeta specData (materiaux) + SmetaLine (services via SERVICE_TO_CATEGORY) — LASER_FLAT/LASER_TUBE/ROD_BENDING maintenant avec budget. --- ## 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)