diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..25138c5 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,98 @@ +# AGENTS.md + +Guide rapide pour les agents qui codent dans ce repository. + +## 1) Contexte du projet + +- Codebase Tryton monolithique (coeur + modules metier). +- Noyau serveur a la racine: `application.py`, `wsgi.py`, `admin.py`, `worker.py`, `cron.py`. +- Couches framework importantes: + - ORM: `model/` + - Meta/systeme (`ir`): `ir/` + - Protocoles RPC: `protocols/` + - Backend DB: `backend/` +- Modules metier: `modules//` (~220 modules). + +## 2) Regles de travail pour agent + +- Ne jamais toucher des fichiers sans rapport avec la demande. +- Limiter le scope de modif au minimum necessaire. +- Respecter le style existant du module cible. +- Ne pas supprimer du code legacy sans verifier les usages. +- Si comportement incertain: preferer un patch conservateur + test. + +## 3) Zones de bruit a ignorer pendant l'exploration + +- `.venv/` +- `__pycache__/` +- `build/` (quand present dans des sous-modules) +- Fichiers temporaires editeur (ex: `*.swp`) + +## 4) Comment choisir ou coder selon le besoin + +- Si bug ORM/champs: + - Lire `model/fields/*.py` et les tests `tests/test_field_*.py`. +- Si bug transaction/DB: + - Lire `transaction.py`, `backend/*/database.py`, `tests/test_backend.py`. +- Si bug API/RPC/HTTP: + - Lire `wsgi.py`, `rpc.py`, `protocols/*`, `tests/test_rpc.py`, `tests/test_wsgi.py`. +- Si bug metier: + - Modifier uniquement `modules//` + ses tests. + +## 5) Workflow de modification (obligatoire) + +1. Identifier le module et le flux impacte. +2. Localiser un test existant proche du comportement a changer. +3. Implementer le plus petit patch possible. +4. Ajouter/adapter les tests au plus pres du changement. +5. Lancer la validation ciblee (pas toute la suite si inutile). +6. Donner un resume du risque residuel. + +## 6) Checklist avant de rendre une modif + +- Le changement est-il limite au domaine demande ? +- Le comportement existant non cible est-il preserve ? +- Les droits/regles (`ir.rule`, acces) sont-ils impactes ? +- Les vues XML et labels sont-ils coherents si un champ change ? +- Les tests modifies couvrent-ils le bug/la feature ? +- Le message de commit (si demande) explique clairement le pourquoi ? + +## 7) Tests: point de depart pratique + +- Suite coeur: `tests/test_tryton.py` +- Tests coeur par domaine: `tests/test_*.py` +- Tests module: + - `modules//tests/test_module.py` + - `modules//tests/test_scenario.py` + - `modules//tests/scenario_*.rst` + +Quand possible, lancer d'abord la cible minimale: + +- fichier de test touche +- puis fichier voisin de regression +- puis suite plus large uniquement si necessaire + +## 8) Contrat de sortie attendu de l'agent + +Toujours fournir: + +- Liste des fichiers modifies +- Resume fonctionnel (ce qui change) +- Resume technique (pourquoi ce design) +- Tests executes + resultat +- Risques residuels et impacts potentiels + +## 9) Cas sensibles (demander confirmation humaine) + +- Changement schema/structure de donnees +- Changement de logique de securite/acces +- Changement de comportement transverse (transaction, pool, RPC, worker) +- Refactor multi-modules sans ticket explicite + +## 10) Raccourci de demarrage pour agent + +1. Lire ce fichier. +2. Lire le(s) fichier(s) touche(s) et leurs tests. +3. Proposer le patch minimal. +4. Implementer + tester cible. +5. Rendre avec le contrat de sortie (section 8). diff --git a/modules/account_invoice/invoice_ict.fodt b/modules/account_invoice/invoice_ict.fodt index 5694243..96a70e7 100644 --- a/modules/account_invoice/invoice_ict.fodt +++ b/modules/account_invoice/invoice_ict.fodt @@ -1,10 +1,10 @@ - Invoice nowillenRizza Deborah2018-12-09T16:20:002025-06-20T10:50:002007-08-28T18:19:00LibreOffice/7.6.0.3$Windows_X86_64 LibreOffice_project/69edd8b8ebc41d00b4de3915dc82f8f0fc3b6265 + Invoice nowillen2018-12-09T16:20:002026-03-23T20:46:35.3000000002007-08-28T18:19:00LibreOffice/7.6.0.3$Windows_X86_64 LibreOffice_project/69edd8b8ebc41d00b4de3915dc82f8f0fc3b6265PT22M49S1 - 0 + 9172 0 43623 21098 @@ -13,12 +13,12 @@ view2 - 11640 - 3900 + 19516 + 16919 0 - 0 + 9172 43621 - 21096 + 30268 0 0 false @@ -91,7 +91,7 @@ false false false - 84510 + 84841 84510 false false @@ -152,6 +152,7 @@ + @@ -161,14 +162,14 @@ - + - + - + @@ -285,14 +286,17 @@ + + + - + - + - + @@ -566,6 +570,12 @@ + + + + + + @@ -575,170 +585,138 @@ - + - + - + - + - - - - - - - - - + - + - + + + + + + + + + - + - + - + - - - - - - - - - + - + - + - - + + - + + + + + + + + + - + - + - + - + - + - + - + - + - + - - - - - - - - - + - - - - - + - - - - - - - - - - - - - - - - - - - - - + - + @@ -3797,8 +3775,6 @@ - <replace text:p=\'set_lang(invoice.party.lang)\'> - <replace text:p=\'invoice.set_lang(invoice.party.lang)\'> @@ -3806,23 +3782,25 @@ + <replace text:p=\'set_lang(invoice.party.lang)\'> + <replace text:p=\'invoice.set_lang(invoice.party.lang)\'> - <for each='line in invoice.invoice_address.full_address.split(chr(10))'> - <line> - </for> - + <for each='line in invoice.invoice_address.full_address.split('\n')"> + <line> + </for> + - + - <format_date(invoice.invoice_date or today, invoice.party.lang)> + <format_date(invoice.invoice_date or today, invoice.party.lang)> @@ -3831,238 +3809,244 @@ - - Invoice N° + + Invoice N° - - <invoice.number and invoice.number or ''> + + <invoice.number> - Contract N° + Contract N° - <invoice.origins or ''> + <invoice.origins or ''> - + - Name of the vessel + Name of the vessel - <invoice.description or ''> + <invoice.description or ''> - N° of Bill of Lading + N° of Bill of Lading - <invoice.reference or ''> + <invoice.reference or ''> - Shipped on board date - Port of loading + Shipped on board date + Port of loading - 14.06.2025 - SANTOS, BRAZIL + 14.06.2025 + SANTOS, BRAZIL - Port of discharge + Port of discharge - PORT QASIM, PAKISTAN + PORT QASIM, PAKISTAN - + - Goods description + Goods description - QUANTITY: 2,253,035 LBS (1,021.970 MTS) - OF BRAZILIAN GINNED COTTON BCI CROP 2024 - AT THE RATE OF USC 74.15 PER LB - H.S CODE 5201.0090 - CFR PORT QASIM, PAKISTAN - ALL DETAILS AND SPECIFICATIONS AS PER BENEFICIARY’S - PROFORMA INVOICE NO. 1411-1 DATED 20-05-2025. + QUANTITY: 2,253,035 LBS (1,021.970 MTS) + OF BRAZILIAN GINNED COTTON BCI CROP 2024 + AT THE RATE OF USC 74.15 PER LB + H.S CODE 5201.0090 + CFR PORT QASIM, PAKISTAN + ALL DETAILS AND SPECIFICATIONS AS PER BENEFICIARY’S + PROFORMA INVOICE NO. 1411-1 DATED 20-05-2025. + - - - - - - + + + + + - + - BALES + BALES - Gross KGS + Gross KGS - NET KGS + NET KGS - + - + - <format_number(invoice.lines[0].quantity, invoice.party.lang) if invoice.lines else ''> + <format_number(invoice.lines[0].quantity, invoice.party.lang) if invoice.lines else ''> - <invoice.lines[0].gross_weight if (invoice.lines and hasattr(invoice.lines[0], \'gross_weight\')) else ''> + <invoice.lines[0].gross_weight if (invoice.lines and hasattr(invoice.lines[0], \'gross_weight\')) else ''> - <format_number_symbol(invoice.lines[0].quantity, invoice.party.lang, invoice.lines[0].unit) if (invoice.lines and invoice.lines[0].unit) else (format_number(invoice.lines[0].quantity, invoice.party.lang) if invoice.lines else '')> - - - + <format_number_symbol(invoice.lines[0].quantity, invoice.party.lang, invoice.lines[0].unit) if (invoice.lines and invoice.lines[0].unit) else (format_number(invoice.lines[0].quantity, invoice.party.lang) if invoice.lines else '')> + + + - + - - - - - - - Equivalent to LBS - - - <format_number(invoice.lines[0].quantity * 2.20462, invoice.party.lang) if invoice.lines else ''> + + + Equivalent to LBS + + + <format_number(invoice.lines[0].quantity * 2.20462, invoice.party.lang) if invoice.lines else ''> + + + + - + - At <format_currency(invoice.lines[0].unit_price, invoice.party.lang, invoice.currency, digits=invoice.lines[0].__class__.unit_price.digits[1]) if invoice.lines else \'\'> PER LB - - FREIGHT VALUE: <invoice.currency.symbol if invoice.currency else 'USD'> <format_number(invoice.freight_amount, invoice.party.lang) if hasattr(invoice, \'freight_amount\') else \'\'> - - WE CERTIFY THAT THE MERCHANDISE IS OF BRAZIL ORIGIN - L/C NUMBER <invoice.reference or ''> - IMPORTER H.S. CODE NOS. <invoice.party_tax_identifier.type_string if invoice.party_tax_identifier else \'5201.0090\'> - IMPORTER’S NTN <invoice.party_tax_identifier.code if invoice.party_tax_identifier else \'\'> - + At <format_currency(invoice.lines[0].unit_price, invoice.party.lang, invoice.currency, digits=invoice.lines[0].__class__.unit_price.digits[1]) if invoice.lines else \'\'>PER LB + + FREIGHT VALUE: <invoice.currency.symbol if invoice.currency else 'USD'><format_number(invoice.freight_amount, invoice.party.lang) if hasattr(invoice, \'freight_amount\') else \'\'> + + WE CERTIFY THAT THE MERCHANDISE IS OF BRAZIL ORIGIN + L/C NUMBER <invoice.reference or ''> + IMPORTER H.S. CODE NOS. <invoice.party_tax_identifier.type_string if invoice.party_tax_identifier else \'5201.0090\'> + IMPORTER’S NTN <invoice.party_tax_identifier.code if invoice.party_tax_identifier else \'\'> + - <invoice.currency.symbol if invoice.currency else 'USD'> <format_currency(invoice.total_amount, invoice.party.lang, invoice.currency)> - - + <invoice.currency.symbol if invoice.currency else 'USD'><format_currency(invoice.total_amount, invoice.party.lang, invoice.currency)> + + - + - NET LANDED WEIGHTS, ACTUAL TARE, NO FRANCHISE - + NET LANDED WEIGHTS, ACTUAL TARE, NO FRANCHISE + - <for each='tax in invoice.taxes'>V.A.T. <tax.description or \'\'> RATE</for> + <for each='tax in invoice.taxes'> + V.A.T. <tax.description or \'\'>RATE + </for> - + - S/I - - Controller Name + S/I + + Controller Name - S/BR/55 - - INTERTEK + S/BR/55 + + INTERTEK - - - - - - Payment - - - <if test=\'invoice.payment_term and invoice.payment_term.description\'><for each=\'description in (invoice.payment_term.description or \'\').split(chr(10))\'><description></for></if> - - - ICT TRADING S.A. + - SB/DR + Payment - + <if test=\'invoice.payment_term and invoice.payment_term.description\'> + <for each=\'description in (invoice.payment_term.description or \'\').split(chr(10))\'> + <description> + </for> + </if> + + + ICT TRADING S.A. - + SB/DR - + + + + + + + + + - + \ No newline at end of file diff --git a/modules/purchase/AGENTS.md b/modules/purchase/AGENTS.md new file mode 100644 index 0000000..9e0a0a2 --- /dev/null +++ b/modules/purchase/AGENTS.md @@ -0,0 +1,105 @@ +# AGENTS.md - Module `purchase` + +Ce guide complete le `AGENTS.md` racine. +Pour ce module, les regles locales ci-dessous priment. + +## 1) Perimetre metier + +Le module `purchase` gere le cycle d'achat fournisseur: + +- commande d'achat (`purchase.purchase`, `purchase.line`) +- facturation fournisseur (`account.invoice` liee a l'achat) +- reception/retour de stock (`stock.move`, `stock.shipment.in`, `stock.shipment.in.return`) +- reporting achats (axes temporels, fournisseur, produit) + +## 2) Fichiers pivots + +- Logique coeur: + - `modules/purchase/purchase.py` +- Extensions metier connexes: + - `modules/purchase/product.py` + - `modules/purchase/stock.py` + - `modules/purchase/invoice.py` + - `modules/purchase/party.py` + - `modules/purchase/configuration.py` + - `modules/purchase/purchase_reporting.py` +- Vues et actions: + - `modules/purchase/purchase.xml` + - `modules/purchase/stock.xml` + - `modules/purchase/invoice.xml` + - `modules/purchase/purchase_reporting.xml` +- Manifest et dependances: + - `modules/purchase/tryton.cfg` +- Documentation metier: + - `modules/purchase/docs/business-rules.template.md` (template) + - `modules/purchase/docs/business-rules.md` (instance a remplir) + +## 3) Etats et flux critiques a preserver + +Workflow de commande (dans `purchase.py`): + +- `draft -> quotation -> confirmed -> processing -> done` +- transitions de retour existent aussi (`cancelled`, retour a `draft`, etc.) + +Invariants importants: + +- `invoice_state` et `shipment_state` doivent rester coherents apres `process()`. +- `process()` orchestre facture + stock + recalcul d'etats, ne pas contourner sans raison. +- `delete()` exige une commande annulee. +- Les methodes `create_invoice()` et `create_move()` sont sensibles (gestion `lots` et `action`). + +## 4) Couplages a surveiller + +- Facture: + - `purchase.py` <-> `invoice.py` + - gestion des exceptions facture (`purchase_exception_state`) +- Stock: + - `purchase.py` <-> `stock.py` + - liens `moves`, expeditions entrantes, retours +- Produit/fournisseur/prix: + - `product.py` impacte prix d'achat, UoM, fournisseurs +- Tiers: + - `party.py` impacte adresses/parametres fournisseur et contraintes d'effacement + +## 5) Convention de modification pour ce module + +1. Modifier d'abord le coeur minimal dans `purchase.py` ou le fichier specialise adequat. +2. Mettre a jour XML uniquement si comportement UI/action change. +3. Si regle metier impactee, mettre a jour `docs/business-rules.md`. +4. Ajouter un test proche du flux reel (scenario `.rst` prioritaire si possible). +5. Verifier les impacts transverses facture/stock avant rendu. + +## 6) Strategie de test recommandee + +Priorite 1 (rapide): + +- `modules/purchase/tests/test_module.py` + +Priorite 2 (comportement metier): + +- `modules/purchase/tests/test_scenario.py` +- Scenarios cibles selon la modif: + - `scenario_purchase.rst` + - `scenario_purchase_manual_invoice.rst` + - `scenario_purchase_line_cancelled.rst` + - `scenario_purchase_line_cancelled_on_shipment.rst` + - `scenario_purchase_return_wizard.rst` + - `scenario_purchase_reporting.rst` + +Si la modif touche prix/UoM/fournisseur: + +- ajouter un cas dans `test_module.py` ou un scenario dedie. + +## 7) Cas qui exigent validation humaine + +- Changement du workflow d'etats +- Changement des regles de creation facture/mouvement +- Changement de logique sur retours fournisseur +- Changement qui altere les ecritures comptables ou le statut de paiement + +## 8) Definition of done (module `purchase`) + +- Le flux metier cible fonctionne de bout en bout. +- Les etats `state`, `invoice_state`, `shipment_state` restent coherents. +- Les tests du module pertinents passent. +- Le patch est limite aux fichiers necessaires. diff --git a/modules/purchase/docs/business-rules.template.md b/modules/purchase/docs/business-rules.template.md new file mode 100644 index 0000000..29f6e8a --- /dev/null +++ b/modules/purchase/docs/business-rules.template.md @@ -0,0 +1,122 @@ +# Business Rules Template - Purchase + +Statut: `draft` | `reviewed` | `approved` +Version: `v0.1` +Derniere mise a jour: `YYYY-MM-DD` +Owner metier: `Nom / Equipe` +Owner technique: `Nom / Equipe` + +## 1) Scope + +- Domaine: `ex: achats fournisseur` +- Hors scope: `ex: achats intercompany` +- Modules impactes: + - `purchase` + - `stock` (si applicable) + - `account_invoice` (si applicable) + +## 2) Glossaire + +- `Purchase`: commande d'achat fournisseur. +- `Line`: ligne de commande. +- `Invoice State`: etat facture calcule. +- `Shipment State`: etat reception calcule. +- Ajouter ici les termes metier propres a ton contexte. + +## 3) Regles metier (source de verite) + +### BR-001 - [Titre court] + +- Intent: `Pourquoi cette regle existe` +- Description: + - `Enonce clair et testable` +- Conditions d'entree: + - `Etat` + - `Type de ligne (goods/service)` + - `Contexte (societe, devise, fournisseur, lot, etc.)` +- Resultat attendu: + - `Etat/valeur/action attendue` +- Exceptions: + - `Cas ou la regle ne s'applique pas` +- Priorite: + - `bloquante | importante | informative` +- Source: + - `Ticket / spec / decision metier` + +### BR-002 - [Titre court] + +- Intent: +- Description: +- Conditions d'entree: +- Resultat attendu: +- Exceptions: +- Priorite: +- Source: + +## 4) Matrice d'etats (optionnel mais recommande) + +| Regle | Etat initial | Evenement | Etat attendu | Notes | +|---|---|---|---|---| +| BR-001 | `draft` | `quote` | `quotation` | | +| BR-002 | `quotation` | `confirm` | `confirmed/processing` | | + +## 5) Exemples concrets + +### Exemple E1 - Cas nominal + +- Donnees: + - `fournisseur = X` + - `produit = Y` + - `quantite = 10` +- Attendu: + - `invoice_state = pending` + - `shipment_state = waiting` + +### Exemple E2 - Cas limite + +- Donnees: +- Attendu: + +## 6) Impact code attendu + +- Fichiers Python potentiellement concernes: + - `modules/purchase/purchase.py` + - `modules/purchase/stock.py` + - `modules/purchase/invoice.py` + - `modules/purchase/product.py` +- Fichiers XML potentiellement concernes: + - `modules/purchase/purchase.xml` + - `modules/purchase/stock.xml` + - `modules/purchase/invoice.xml` + +## 7) Strategie de tests + +- Unitaires: + - `modules/purchase/tests/test_module.py` +- Scenarios: + - `modules/purchase/tests/scenario_purchase.rst` + - `modules/purchase/tests/scenario_purchase_manual_invoice.rst` + - `modules/purchase/tests/scenario_purchase_return_wizard.rst` + +Pour chaque regle BR-xxx, lister le test associe: + +| Regle | Test existant | Nouveau test a ajouter | Statut | +|---|---|---|---| +| BR-001 | `...` | `...` | `todo` | + +## 8) Compatibilite et migration + +- Effet retroactif sur commandes existantes: `oui/non` +- Migration necessaire: `oui/non` +- Plan de rollback: + - `comment revenir en arriere sans corruption metier` + +## 9) Validation + +- Valide par metier: + - `Nom` - `date` +- Valide par technique: + - `Nom` - `date` +- Decision finale: + - `approved / rejected / needs update` + diff --git a/modules/purchase_trade/purchase.py b/modules/purchase_trade/purchase.py index d97e269..cea3eea 100755 --- a/modules/purchase_trade/purchase.py +++ b/modules/purchase_trade/purchase.py @@ -983,7 +983,7 @@ class QualityAnalysis(ModelSQL,ModelView): class Line(metaclass=PoolMeta): __name__ = 'purchase.line' - quantity_theorical = fields.Numeric("Th. quantity", digits='unit', readonly=True) + quantity_theorical = fields.Numeric("Th. quantity", digits='unit', readonly=False) price_type = fields.Selection([ ('cash', 'Cash Price'), ('priced', 'Priced'), @@ -1210,6 +1210,26 @@ class Line(metaclass=PoolMeta): return round(d.price_index.get_price(Date.today(),self.unit,self.purchase.currency,True),4) return self.unit_price + @classmethod + def write(cls, *args): + old_values = {} + + for records, values in zip(args[::2], args[1::2]): + if 'quantity_theorical' in values: + for record in records: + old_values[record.id] = record.quantity_theorical + + super().write(*args) + + lines = sum(args[::2], []) + for line in lines: + if line.id in old_values: + old = old_values[line.id] + new = line.quantity_theorical + delta = Decimal(new) - Decimal(old) + if delta > 0: + continue + @classmethod def copy(cls, lines, default=None): if default is None: @@ -1292,16 +1312,7 @@ class Line(metaclass=PoolMeta): fl.lot = lot.id fl.line = line.id FeeLots.save([fl]) - - #update inherit fee qt - # if line.fees: - # Fee = Pool().get('fee.fee') - # for f in line.fees: - # if f.inherit_qt: - # f.quantity = round(line.quantity_theorical,4) - # f.unit = line.unit - # Fee.save([f]) - #check if fee purchase is filled on fee + if line.fee_: if not line.fee_.purchase: Fee = Pool().get('fee.fee')