Nouvelle API sous /api/ext/v1, authentifiée par clé (EXTERNAL_API_KEYS,
en-tête X-API-Key ou Authorization: Bearer), pour qu'un outil tiers
lise magasins, fournisseurs et livraisons livrées (n° BL) puis écrive
la référence, les montants et l'échéance de la facture, avec
validation/dévalidation optionnelle du rapprochement.
- normalizeDateString extrait dans server/dateUtils.ts pour être partagé
- /api/ext/ exempté du CSRF (pas de cookie de session)
- EXTERNAL_API_KEYS ajouté à .env.example et docker-compose.yml
- documentation : docs/API-RAPPROCHEMENT.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
POST /api/deliveries/:id/validate never set reconciled=true for suppliers in
automatic reconciliation mode, so their deliveries showed in neither the
Manuels tab (auto suppliers excluded) nor Validées (reconciled=false).
- validate endpoint now marks reconciled + validatedAt for auto suppliers
- Validées tab includes all deliveries from auto suppliers
- migration backfills reconciled for existing delivered deliveries with BL
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
La température de l'an dernier disparaissait alors que Visual Crossing
répondait correctement (tableau de bord : requêtes réussies, 0 échec,
et l'URL d'historique testée dans le navigateur renvoie bien la journée).
Quand l'enregistrement de la réponse en cache échouait (par exemple une
contrainte d'unicité sur la date déjà occupée par la météo relevée ce
jour-là l'an dernier), la route relisait le cache, n'y trouvait rien et
jetait la donnée reçue : previousYear restait à null et l'API était
réinterrogée à chaque affichage.
La route garde désormais la réponse de l'API pour l'affichage quand
l'enregistrement échoue (année en cours et année précédente), et
journalise une réponse d'historique vide. Reproduit sur un Postgres local
avec une contrainte UNIQUE (date, location) et une API simulée renvoyant
la réponse réelle : N-1 vide avant, 14,1° après ; sans contrainte, le
comportement est inchangé.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MsDJjQrAggcJwbbBtKhgyb
La comparaison avec l'année dernière disparaît quand Visual Crossing
refuse durablement l'historique (plan, quota) : le repli précédent ne
cherchait que des lignes N-1 déjà en cache (is_current_year = false) à
7 jours près, qui n'existent plus si l'historique échoue depuis plus
d'une semaine.
La table weather_data garde pourtant la météo relevée chaque jour l'an
dernier, quand ces dates étaient « aujourd'hui » (is_current_year =
true). La route l'utilise désormais en second repli, au jour le plus
proche de la date cible (7 jours max), et journalise l'absence totale de
donnée N-1.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MsDJjQrAggcJwbbBtKhgyb
La comparaison avec l'année dernière avait disparu du widget météo depuis
l'optimisation des chargements. La route /api/weather/current interrogeait
Visual Crossing pour aujourd'hui et pour l'année dernière en parallèle :
la seconde requête d'une même clé peut alors être refusée (limite de
requêtes simultanées), laissant previousYear à null. Les deux appels
redeviennent séquentiels comme avant ; seules les lectures du cache local
restent en parallèle.
Le widget retrouve son comportement d'origine (plus de cache de 30
minutes ni de retry désactivé), pour qu'un échec ponctuel ne masque plus
la comparaison pendant une demi-heure. Les erreurs de l'API journalisent
désormais la raison renvoyée par Visual Crossing (quota, plan, clé).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MsDJjQrAggcJwbbBtKhgyb
- Fichiers statiques servis avant la session (plus de requêtes SQL par
asset), /assets en cache immuable 1 an, index.html en no-cache, et
compression gzip/brotli des réponses (dépendance compression, externe
dans le bundle esbuild du Dockerfile).
- req.user (déjà chargé par deserializeUser) réutilisé dans les handlers
au lieu de relire l'utilisateur et ses magasins à chaque appel ;
GET /api/user ne refait plus de requête.
- Listes : le magasin joint est réduit aux champs lus par l'interface
(plus de logo base64 ni de configuration SMTP/NocoDB dans chaque ligne),
plus aucune empreinte de mot de passe dans les créateurs/auteurs ni dans
/api/users.
- N+1 supprimés (/api/users, annonces, historique SAV, caches de
vérification des factures), requêtes indépendantes en parallèle (stats,
analytics, météo, getDelivery, getUserWithGroups), jointure
multiplicative des statistiques par magasin corrigée.
- Échéancier limité au magasin demandé ; filtre status sur
GET /api/deliveries.
- Index de performance créés en arrière-plan au démarrage
(CREATE INDEX CONCURRENTLY, reliquats invalides purgés sans verrou
exclusif).
- La connexion n'attend plus la sauvegarde quotidienne ; purge du cache
des factures active en production ; logs volumineux retirés des
chemins chauds ; NODE_ENV fixé dans l'image.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MsDJjQrAggcJwbbBtKhgyb
The dashboard widget lost the previous-year temperature and the difference:
when the day's previous-year row is not yet cached and the Visual Crossing
history call fails (quota exhausted, outage, or a plan without history
access), the route silently returned previousYear: null and the widget
hid the comparison.
The weather cache accumulates one previous-year row per day, so the route
now falls back to the nearest cached previous-year entry within 7 days of
the weekday-aligned target date, keeping the comparison on screen through
API failures. The exact API error remains logged server-side
(🌤️ [ERROR] / [FETCH-HISTORY]) for diagnosis.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FHdE9nEh8zHhQNGpCgrtYe
Continues the cleanup from 164 errors down to 88, all of which now sit in
MemStorage — the in-memory dev mock — documented as known debt. Production
server code and the entire client are at zero errors.
Three genuine defects surfaced by the types and fixed:
- deleteAnnouncement returned void while routes check the result and answer
404 "not found" on falsy: deleting an announcement succeeded in DB but the
API reported failure on the fallback paths; it now returns a real boolean
- AnnouncementMemoryStorage declared getAnnouncement twice; the first
implementation was dead at runtime (second definition wins) and is removed
- one route called storage.getDeliveryById(), a method that does not exist,
crashing with a TypeError whenever hit; it now calls getDelivery()
Everything else is type-level only (annotations, null-to-undefined for
inline styles, honest signatures for markClientCalled's comment parameter
and the invoice verification result's invoiceAmountTTC field), verified
behavior-neutral: client build, production server bundle and the SMTP
end-to-end test all pass unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FHdE9nEh8zHhQNGpCgrtYe
- POST /api/emergency-admin-reset accepted a fallback secret hardcoded in
the repository, letting anyone who read the source reset the production
admin account to admin/admin (EMERGENCY_SECRET is not set in the shipped
docker-compose, so the fallback was live). The route now returns 404
unless EMERGENCY_SECRET is explicitly configured, and invalid attempts
are logged.
- POST /api/admin/emergency-migration ran database migrations with no
authentication at all; it now requires an authenticated admin.
A sweep of the remaining API surface found no other unauthenticated
mutation or read routes beyond /api/health. .env.example documents
EMERGENCY_SECRET and ENCRYPTION_KEY.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FHdE9nEh8zHhQNGpCgrtYe
- POST /api/groups logged the full request body, which now carries the
store's SMTP password; a redactBody() helper masks smtpPassword, apiToken
and password in the six log sites that print request bodies
- Groups.tsx logged a debug object on every render; removed
- BLReconciliation's auto-fill mutation logged five lines per verified
delivery; collapsed to one DEV-gated line, keeping console.error
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FHdE9nEh8zHhQNGpCgrtYe
Encryption (server/crypto.ts):
- AES-256-GCM with a key derived from ENCRYPTION_KEY, falling back to
SESSION_SECRET so existing deployments need no new configuration
- stored format enc:v1:<iv>:<tag>:<data>; encryption is idempotent and
decryption passes legacy plaintext through unchanged, so nothing breaks
mid-migration
- tampered data or a changed key raises an explicit error instead of
returning garbage
- encrypt/decrypt is confined to the storage layer: group writes encrypt
smtpPassword (decrypted only in emailService at connection time, never sent
to the client), NocoDB config writes encrypt apiToken and reads decrypt it
so the invoice verification and the admin page behave as before
- startup migration sweep encrypts secrets already stored in plaintext,
idempotently; the active-config log line no longer prints the token
Mail history (supplier_mail_logs):
- every send attempt is recorded: delivery, store, supplier, recipient,
subject, status sent/failed with error, message id, user id and name;
a logging failure never fails the send itself
- GET /api/supplier-mail-logs restricted to the user's stores (admin may
filter by store)
- on the reconciliation page the mail icon turns green once a request has
been sent, with the date and sender in the tooltip; clicking again resends
- table created in init.sql, versioned migrations and the production
startup migration, with delivery/group indexes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FHdE9nEh8zHhQNGpCgrtYe
Replaces the mailto: link with a server-side send, so the message carries the
store's signature and logo instead of depending on each workstation's Outlook.
Store record (groups):
- address, phone and logo (data URI, 200 KB cap) feed the mail signature
- per-store SMTP settings: host, port, SSL/STARTTLS, credentials, sender
address and display name, with an enable switch
- "test connection" button verifies the server without sending anything
- the empty-form literal, previously repeated five times, becomes one constant
Sending:
- nodemailer transport built per store from its own settings
- multipart mail: plain-text alternative plus HTML whose signature embeds the
logo as an inline CID attachment, which Outlook renders without the remote
image blocking that a data: URI would hit
- delivery details are HTML-escaped
- Reply-To set to the store address; the row shows a spinner and refuses a
second click while a send is in flight
Credentials:
- the SMTP password is never returned to the client; a response-layer
sanitizer strips it from every /api payload and replaces it with a
smtpPasswordSet flag, covering the ten-plus queries that join full group
rows into deliveries, orders and user relations
- an empty password field on save keeps the stored one rather than clearing it
Verified end-to-end against a local SMTP server: transport, auth, From/Reply-To,
multipart structure and the inline logo attachment.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FHdE9nEh8zHhQNGpCgrtYe
The app degraded progressively over a year of data growth. Four causes,
all cumulative:
1. No indexes. Apart from primary keys, unique constraints and
session.expire, no table carried an index. PostgreSQL does not index
foreign keys automatically, so every filter and join on group_id,
supplier_id, order_id and the date columns did a sequential scan.
Worst offender: user_groups.user_id, read by getUserWithGroups() on
every authenticated request.
2. N+1 in the order and delivery listings. getOrders,
getOrdersByDateRange, getDeliveries and getDeliveriesByDateRange
issued one to two queries per row to load relations. Relations are now
loaded in bulk and grouped in Node: three queries regardless of volume.
3. /api/sync-order-delivery-status reloaded the whole deliveries table on
every iteration of its loop over orders. It now uses the deliveries
getOrders() already attaches.
4. clearExpiredCache() was implemented but never called, so
invoice_verification_cache grew without bound. Now scheduled every 6h.
Also replaces the full-history downloads on the Groups and Suppliers
pages, which fetched every order and delivery with nested relations only
to count rows, with aggregate endpoints that count in the database.
Indexes are created via scripts/auto-migrate-production.sh, the script
that actually runs at deploy time, using CREATE INDEX CONCURRENTLY so no
write lock is taken. Note that server/migrations.ts explicitly ignores
the migrations/ directory and runs only hardcoded migrations; the SQL
file added there is for reference and manual application.
Verified: typecheck baseline 440 errors, 430 after, none new in the
changed code; vite build passes; server boots; functional test confirms
the aggregate endpoints match the source data including the delivered
count.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- New contacts table (group_id, name, role, phone, email, notes) per store
- Suppliers table: add email field with form and card display
- Page /contacts: two-column layout (suppliers read-only / free contacts CRUD)
- Permissions: read all roles, create/edit/delete admin + directeur + manager
- Admin can filter contacts by store; other roles see their own stores only
- Migration SQL: 20260515_add_contacts_and_supplier_email.sql
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Modify the CSV generation logic in server/routes.ts to use semicolons (;) instead of tabs (\t) as delimiters. This change addresses issues with CSV files not being properly tab-delimited in certain spreadsheet applications, particularly in French locales, ensuring each row is correctly parsed.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 1e4433b5-47ab-464c-b663-fea2e53367dd
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 833028cc-a84a-4658-a40c-0a89da3920e8
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/1e4433b5-47ab-464c-b663-fea2e53367dd/WZFf6za
Enhance the error handling and logging for the Excel export route, including detailed console logs for request parameters, user information, and potential errors during XLSX module import and workbook creation. Also adds conditional error stack tracing for development environments.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 1e4433b5-47ab-464c-b663-fea2e53367dd
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: c050afd1-ec0c-4486-a949-a2b54ef8f939
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/1e4433b5-47ab-464c-b663-fea2e53367dd/GjEDOGr
Enhance the delivery reconciliation process by implementing fallback mechanisms to fetch and update missing `dueDate` and `invoiceAmountTTC` from external invoice verification services when these details are absent in the existing delivery records. This involves modifications to `server/routes.ts` to handle these updates asynchronously and log the process.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 1e4433b5-47ab-464c-b663-fea2e53367dd
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 5af17562-f143-442a-a620-6e8a4b02ac1d
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/1e4433b5-47ab-464c-b663-fea2e53367dd/vfXE6SO
Introduces `nocodbInvoiceAmountTTCColumnName` to `Groups.tsx` and `amountTTC` to `PaymentSchedule` interface in `PaymentSchedulePage.tsx` to support TTC calculations and display. Updates backend routes in `routes.ts` to include `amountTTC` and modifies `PaymentSchedulePage.tsx` to show both HT and TTC totals.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 1e4433b5-47ab-464c-b663-fea2e53367dd
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 01a3488a-852e-4c10-8349-442dbf9fc720
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/1e4433b5-47ab-464c-b663-fea2e53367dd/vfXE6SO
Update invoice verification service and database storage to include and process `invoiceAmountTTC` (total invoice amount). Modify routes to conditionally save TTC amount and adjust related schemas and cache definitions. Remove commented-out code and unused imports related to SAV ticket history.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 1e4433b5-47ab-464c-b663-fea2e53367dd
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 87886fa2-6eb3-432f-a67f-dd9d21591981
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/1e4433b5-47ab-464c-b663-fea2e53367dd/5hnOUe4
Update data transformation and validation logic in server routes to allow null values for invoice reference, amount, and due date. This addresses issues where users could not clear these fields and ensures the due date is correctly displayed.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 1e4433b5-47ab-464c-b663-fea2e53367dd
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 693eaffa-e5f9-48b1-8bb4-034d4f47e3f4
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/1e4433b5-47ab-464c-b663-fea2e53367dd/gTQvEeF
Adjusted the auto-fill logic in `BLReconciliation.tsx` to populate `invoiceReference`, `invoiceAmount`, and `dueDate` if they exist in the `result`. Also modified `server/routes.ts` to correctly parse `dueDate` as a Date object.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 1e4433b5-47ab-464c-b663-fea2e53367dd
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 25b35a21-13c0-4deb-8f7d-88d73b7e8d28
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/1e4433b5-47ab-464c-b663-fea2e53367dd/Tx7N1IN
Add a date normalization function to server/routes.ts to handle various date formats from NocoDB and standardize them to YYYY-MM-DD, also refactor delivery retrieval logic to separate dated and undated deliveries.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 1e4433b5-47ab-464c-b663-fea2e53367dd
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: 4d0fa35d-6e0d-44c6-b7e9-f78186630c41
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/1e4433b5-47ab-464c-b663-fea2e53367dd/G9gVBac
Updates InvoiceVerificationService to include dueDate, modifies routes.ts to fetch deliveries with due dates from storage instead of NocoDB, and adds dueDate to the deliveries schema in shared/schema.ts.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 1e4433b5-47ab-464c-b663-fea2e53367dd
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: 45962ece-1470-4431-936b-016ef39720d8
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/1e4433b5-47ab-464c-b663-fea2e53367dd/G9gVBac