L'assistant pouvait conclure « prenez plutot un conteneur complet » avec
l'autorite de la marque, et repondre sur du transport interieur. Il ne pouvait
pas non plus combler un trou de documentation qu'il venait de rencontrer.
Perimetre (openai-trade.adapter)
- SCOPE_RULES : LCL uniquement. Le FCL reste explicable — c'est du vocabulaire
metier — mais jamais recommande, chiffre, ni presente comme la meilleure
option. Un cas hors LCL part vers support@xpeditis.com.
- SCOPE_RULES : transport international uniquement. Le transport interieur, le
demenagement et le transport de personnes sont refuses, pas traites « un peu ».
- La page wiki « LCL vs FCL » ne conseille plus le FCL : elle le decrit, et
liste les cas hors perimetre a signaler au support. Corpus reconstruit.
- L'amorce « LCL ou FCL ? » devient une question sur le calcul du fret LCL.
Documentation interne d'abord (KNOWLEDGE_RULES)
- Les extraits du wiki sont dits source de reference, avant les connaissances
generales du modele.
Entretien du wiki global
- Nouvelle capacite `contribute_wiki_page` (scope write) : quand le wiki ne
couvre pas un sujet d'information generale sur le transport international,
l'assistant ecrit la page. Un titre deja pris est mis a jour, pas duplique.
- `wiki-contribution-policy` (domaine) decide ce qui entre : refus du contenu
qui conseille le FCL, du cas client (dossier, tarif, coordonnees) et du hors
perimetre. Le prompt oriente, cette fonction empeche.
- Un sujet deja couvert par le wiki publie (score >= 0,62) est refuse.
- `WikiRetriever` indexe les contributions a cote du corpus fige, avec un index
en memoire invalide par la revision du jeu ; une panne de ce cote ne coute pas
la reponse.
- Page `/dashboard/wiki/complements`, etiquetee comme ecrite par l'assistant,
et carte sur l'index du wiki.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le statut PENDING_PAYMENT ne designe plus un paiement en attente mais un devis :
une reservation construite dont les frais de booking ne sont pas encore regles.
L'enum backend devient QUOTE, converti en place par migration.
Les deux onglets se partagent desormais la meme liste :
- « Reservations » n'affiche que PENDING, ACCEPTED et REJECTED ;
- « Historique des devis » regroupe QUOTE, PENDING_BANK_TRANSFER et CANCELLED,
avec les actions propres a un devis (modifier, payer, supprimer).
La table, l'export et les filtres sont extraits dans BookingListView pour eviter
de dupliquer la page entre les deux vues.
Tableau de bord :
- l'alerte « Paiement en attente » disparait des points a traiter, les devis non
regles ne bloquant aucun envoi en cours ;
- « Sans reponse du transporteur » devient « En attente du transporteur ».
Migration verifiee en transaction annulee sur la base de dev : les lignes
PENDING_PAYMENT deviennent QUOTE, l'enum et le defaut de colonne suivent.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NZJ5aowZJVAcyPZw5TiNEm
- Nouvel endpoint GET /csv-bookings/reservation-quota: limite = vrai plan de
l'org (pas l'override ADMIN PLATINIUM), used = bookings PAYES de l'annee
(PENDING_BANK_TRANSFER/PENDING/ACCEPTED), limitReached.
- ShipmentCounter: countPaidShipmentsForOrganizationInYear.
- create-check compte desormais les payes (au lieu de tous).
- Frontend useReservationQuota consomme l'endpoint (fiable meme pour un admin
dont l'overview renvoie PLATINIUM).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Le plan gratuit (BRONZE) est limite a maxShipmentsPerYear (5/an). Cote frontend,
rien ne le refletait: le bouton 'Nouvelle Reservation' restait actif. Ajout d'un
hook useReservationQuota (limite du plan vs reservations de l'annee) qui:
- remplace le bouton 'Nouvelle Reservation' par un bouton 'Ameliorer mon
abonnement' + banniere sur la liste des reservations quand la limite est
atteinte;
- bloque la page de recherche (nouvelle resa) avec un ecran d'upgrade, sauf en
mode edition d'une resa existante.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
L'email n'etait pas normalise, or la contrainte chk_users_email exige des
minuscules. Un email avec majuscules faisait echouer l'INSERT utilisateur APRES
la creation de l'organisation (flux non transactionnel), laissant une org
orpheline -> 'An organization with this name already exists' aux tentatives
suivantes.
- register/login: email.trim().toLowerCase()
- register: rollback de l'organisation nouvellement creee si la creation de
l'utilisateur echoue (anti-orphelin)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
La reprise reconstruisait un cube (cbrt du volume) au lieu de la base palette
standard, d'ou des longueur/largeur/hauteur incorrectes. Pour une palette on
reconstitue desormais 120x80 cm et on deduit la hauteur du volume (ex: 0.96 CBM
-> 120x80x100). Les colis restent en cube equivalent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Les options du formulaire de recherche etaient collectees mais jamais stockees.
Ajout d'une colonne jsonb options sur csv_bookings (+ migration), stockee a la
creation et a l'edition (editFromRate), renvoyee dans la reponse, et re-pre-
remplie a la reprise (search-advanced <- URL <- booking.options).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
toOrmUpdate ne mappait que status/notes/commission, donc les modifications de
volume/poids/palettes/prix/transporteur/route via editDetails/editFromRate
n'etaient jamais persistees (repository.update). Ajout de tous les champs
editables au mapping de mise a jour.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remplace la page d'édition autonome par une reprise du parcours de réservation :
"Modifier" ré-ouvre la recherche pré-remplie (origine/destination/volume/poids/
palettes reconstruits), l'utilisateur ajuste si besoin, choisit une compagnie
(résultats) et passe au paiement — la réservation existante est mise à jour au
lieu d'en créer une nouvelle.
- Backend: PATCH /csv-bookings/:id/rate + UpdateCsvBookingRateDto + service
updateBookingRate + domaine editFromRate (carrier/route/conteneur/transit/
cargo/prix modifiables avant paiement).
- Front: search-advanced pré-rempli via query (+ editBookingId, démarre à
l'étape colis) ; results en mode édition met à jour puis redirige vers /pay ;
liens "Modifier" (liste, modale, paiement) pointent vers le parcours ;
suppression de la page /booking/:id/edit autonome.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
La recherche de tarif renvoie le transporteur en slug ("ssc-consolidation")
alors que la réservation stocke le nom d'affichage ("SSC Consolidation"), donc
le matching exact companyName === carrierName ne trouvait jamais l'offre →
"aucun tarif trouvé" et prix jamais recalculé.
Matching robuste : comparaison normalisée du transporteur (minuscules, sans
séparateurs) + conteneur + transit, avec fallbacks. Vérifié en conditions
réelles (GET booking, search-csv-offers, PATCH details renvoient 200 ; le prix
change bien quand le volume change).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Suppression de documents: cause = deleteDocument restreint aux statuts
PENDING/PENDING_PAYMENT + blocage du dernier document. Alignement sur
replaceDocument (aucune restriction de statut) et autorisation de 0 document
(validation domaine assouplie ; creation exige toujours >=1 doc cote service).
- Edition avant paiement: recalcul auto du prix. La page /booking/:id/edit
relance la recherche de tarif (meme transporteur/conteneur/transit) pour le
nouveau volume/poids et met a jour fret/FOB/total ; nouveaux champs de prix
dans UpdateCsvBookingDetailsDto + editDetails.
- Liste reservations: actions regroupees dans un menu 3 points (kebab) en
positionnement fixed (anti-clipping), desktop + mobile.
- Responsive (<=1280px): tables documents & blog admin en overflow-x-auto,
menus documents en fixed, grilles de la page edition en sm:.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>