Files
tradon/modules/purchase_trade/docs/padding-invoice-accounting.md
2026-04-26 14:35:07 +02:00

9.7 KiB

Padding facture provisoire vente - notes comptables

Statut: draft Derniere mise a jour: 2026-04-26 Scope: lot.invoice -> account.invoice -> validation comptable.

1) Objectif metier

Le padding sert a augmenter temporairement la quantite facturee sur une facture provisoire cote vente, afin de constituer une provision avant la facture finale.

Exemple:

  • 2 lots factures ensemble
  • padding global saisi dans le wizard: 1000
  • repartition actuelle: 500 par lot
  • chaque ligne facture affiche:
    • quantity = quantite reelle du lot + padding du lot
    • Inc. padding = padding du lot

Le padding ne modifie pas la quantite physique du lot. Il doit rester visible et traçable comme ecart entre la facture provisoire et le lot.

2) Donnees creees ou utilisees

lot.lot

  • Champ: sale_invoice_padding
  • Role: memoire de la part de padding appliquee au lot pour la facture provisoire vente.
  • UI: readonly sur le lot.

account.invoice.line

  • Champ fonctionnel: included_padding
  • Label UI: Inc. padding
  • Role: affichage de line.lot.sale_invoice_padding sur la ligne facture.
  • La valeur n'est pas stockee sur la ligne; elle lit le padding du lot.

Wizard lot.invoice

  • Champ: sale_padding (Global padding)
  • Visible seulement pour:
    • type = sale
    • action = prov
  • Repartition actuelle:
    • padding global / nombre de lots selectionnes

3) Flux de creation de facture avec padding

  1. L'utilisateur ouvre le wizard lot.invoice depuis les lots physiques.
  2. Le wizard prepare deux listes:
    • lot_p pour achat
    • lot_s pour vente
  3. En mode vente provisoire, l'utilisateur saisit Global padding.
  4. Au clic Invoice, le wizard:
    • calcule la part de padding par lot
    • ecrit cette part dans lot.sale_invoice_padding
    • appelle sale.sale._process_invoice(..., action='prov')
  5. sale.line.get_invoice_line(lots, action) cree une ligne facture par lot physique.
  6. Pour chaque ligne positive out / Pro forma, le module ajoute le padding du lot a invoice_line.quantity.
  7. L'amount de la ligne augmente naturellement via:
    • quantity * unit_price

4) Ce qui se passe actuellement lors de Validate d'une facture

Bouton UI: Validate sur account.invoice.

Code principal:

  • modules/account_invoice/invoice.py
    • Invoice.validate_invoice
    • Invoice.get_move
    • InvoiceLine.get_move_lines
    • Invoice.do_lot_invoicing
    • Invoice.cleanMoves

Etapes de validate_invoice

  1. Verifie les taxes avec _check_taxes.
  2. Attribue le numero via set_number.
  3. Stocke les caches de montants via _store_cache.
  4. Pour chaque facture:
    • appelle invoice.get_move()
    • affecte le move a invoice.move s'il est nouveau
    • appelle invoice.do_lot_invoicing()
  5. Sauvegarde les moves crees.
  6. Nettoie certaines lignes de move via cleanMoves.
  7. Sauvegarde les factures.

Note importante deja actee dans le projet:

  • Validate cree deja le account.move et attribue le number aussi pour les factures client (type = out).
  • Post ne doit plus etre le premier moment ou ces donnees apparaissent cote client.

get_move()

get_move() construit le move comptable principal de la facture:

  • appelle update_taxes(exception=True)
  • parcourt invoice.lines
  • pour chaque ligne, appelle line.get_move_lines()
  • ajoute les lignes de taxes via tax.get_move_lines()
  • ajoute la ligne tiers / receivable-payable via _get_move_line
  • ajoute eventuellement une ligne de change via _get_exchange_move_line
  • cree un account.move avec:
    • journal = invoice.journal
    • period trouve depuis la date comptable
    • origin = invoice
    • lines = move_lines

account.invoice.line.get_move_lines()

Pour une ligne facture type = line:

  • cree une ligne account.move.line
  • rattache line.lot = self.invoice.lines[0].lot
    • attention: comportement existant, pas forcement la ligne courante
  • convertit le montant si devise differente
  • pour une facture client (type = out):
    • montant positif => credit du compte de revenu de la ligne
    • montant negatif => debit
  • met line.account = self.account
  • met line.origin = self
  • calcule les lignes de taxe

Pour le padding actuel, comme il augmente invoice_line.quantity, il est deja inclus dans le montant de revenu du move principal.

do_lot_invoicing()

do_lot_invoicing() traite des ajustements lies aux lots:

  • groupe les lignes facture par i.lot
  • calcule:
    • var_price
    • var_qt
  • retrouve le mouvement stock:
    • fournisseur pour type = in
    • client pour type = out
  • declenche des actions stock / shipment si necessaire
  • calcule le COG du lot
  • prepare des lignes d'ajustement via _get_move_lines
  • actuellement, le move d'ajustement n'est cree/poste que si:
    • adjust_move_lines existe
    • self.type == 'in'

Consequence importante pour le padding:

  • les ajustements de quantite/prix existants sont principalement effectifs cote achat (type = in)
  • une facture client provisoire avec padding augmente deja le move principal de vente, mais ne genere pas encore d'ecritures specifiques padding separees

_get_move_lines(gl, amount, drop, IsUsd=False, stock_move=None)

Helper existant pour creer des paires de lignes de move liees au stock/COG:

  • rattache lot, fee, origin
  • gere la conversion devise / second currency
  • choisit les comptes stock / stock out / stock in / COG selon le signe, fee et contexte
  • peut creer une variante drop

Ce helper est un candidat possible pour reutilisation partielle, mais il est aujourd'hui pense pour des ajustements stock/COG, pas pour isoler un padding de facture provisoire vente.

5) Question comptable a trancher pour le padding

Le besoin restant est: au Validate d'une facture provisoire vente avec padding, generer des ecritures propres a ce padding.

Points a definir avant implementation:

  • Le move padding doit-il etre:
    • integre dans le move principal de facture ?
    • cree comme additional_move ?
    • cree comme move separe avec origin = invoice ?
  • Quels comptes utiliser ?
    • compte de revenu temporaire / provision ?
    • compte de contrepartie padding ?
    • compte lie au produit ?
    • compte de configuration dedie ?
  • Faut-il neutraliser une partie du revenu principal deja augmente par la quantity, ou seulement ajouter une ecriture analytique/stock parallele ?
  • Le padding doit-il impacter:
    • revenu client ?
    • stock out ?
    • COG ?
    • PnL / valuation ?
  • Que doit faire la facture finale ?
    • reprendre le padding provisoire ?
    • le contrepasser ?
    • ne rien faire si la finale facture la vraie quantite ?
  • Le montant padding a utiliser est:
    • lot.sale_invoice_padding * invoice_line.unit_price
    • dans la devise de facture
    • a convertir si devise societe differente selon la logique standard

5.1) Decision comptable cible

Comptes de configuration ajoutes sur account.configuration:

  • default_sale_padding_account
    • label UI: Default Sale Padding
    • compte attendu metier: 80021
    • nature: revenu
  • default_accrual_padding_account
    • label UI: Default Accrual Padding
    • compte attendu metier: 42021
    • nature: bilan / accrual

Facture provisoire vente:

  • condition:
    • invoice.type = out
    • invoice.reference = Provisional
    • invoice_line.description = Pro forma
    • invoice_line.lot.sale_invoice_padding > 0
  • montant:
    • padding_amount = lot.sale_invoice_padding * invoice_line.unit_price
    • le prix est celui de la facture provisoire, pas un prix recalcule plus tard
  • ecriture attendue:
    • couple 80021 / 42021
    • rattachement au lot obligatoire sur les lignes si possible

Facture finale vente:

  • condition:
    • lot deja facture en provisoire avec padding
    • la finale doit inverser le padding deja provisionne
  • montant:
    • exactement le montant comptabilise lors de la provisoire
    • ne pas recalculer avec le prix de la finale
  • ecriture attendue:
    • couple inverse 42021 / 80021

Decision technique retenue:

  • ne pas ajouter de nouveau champ montant pour l'instant
  • utiliser le lien deja ecrit sur le lot:
    • lot.sale_invoice_line_prov
  • au moment de la finale, retrouver la ligne provisoire via ce champ
  • calculer le montant a extourner avec:
    • lot.sale_invoice_padding * lot.sale_invoice_line_prov.unit_price
  • utiliser la devise, le taux et la date de la facture provisoire pour obtenir le meme montant societe que l'ecriture initiale
  • garder le rattachement comptable par account.move.line.lot

Raison:

  • evite les ecarts si le prix final change
  • reutilise le lien direct deja maintenu entre le lot et la ligne provisoire
  • rend la reprise finale audit-friendly
  • garde le pivot comptable lot coherent

6) Invariants a preserver

  • Le padding ne change pas la quantite physique du lot.
  • Le padding est uniquement pour facture provisoire vente:
    • invoice.type = out
    • invoice.reference = Provisional
    • lignes description = Pro forma
  • L'utilisateur doit voir clairement l'ecart:
    • Quantity
    • Inc. padding
  • Le lien principal reste:
    • account.invoice.line.lot
    • lot.sale_invoice_padding
  • Les futures ecritures doivent etre rattachees au lot autant que possible pour rester visibles dans le pivot comptable du lot.

7) Tests a prevoir pour la suite

  • Validation d'une facture provisoire vente avec padding:
    • move principal existe
    • montant facture inclut le padding
    • ecritures specifiques padding creees selon la regle retenue
    • lignes rattachees au bon lot
  • Facture provisoire vente sans padding:
    • aucun move padding specifique
  • Facture finale vente:
    • aucun nouveau padding applique automatiquement
    • comportement de reprise/contrepassation selon decision metier
  • Facture achat:
    • aucun impact padding vente
  • Multi-lots:
    • padding reparti correctement
    • ecritures detaillees par lot si la regle comptable l'exige