# Fees

Langue : `fr`
Page miroir : [fees.en.md](fees.en.md)
Statut : `migration partielle`

Voir aussi la page technique historique : `../fees.md`.

> **Résumé opérationnel**
> Un fee porte un montant, mais sa quantité doit rester alignée avec les lots
> qui le composent. Le mode `Per packing` est la seule exception : il suit une
> quantité de colisage, pas le poids du lot.

| Repère | Sujet | Règle courte |
| --- | --- | --- |
| Quantité | `fee.quantity` | Somme des lots liés dans `fee.lots`. |
| État | `fee.qt_state` vide | Utilise le `weight basis` du contrat. |
| État | `fee.qt_state` renseigné | Utilise cet état, plafonné au `weight basis`. |
| Fallback | état absent sur le lot | Prend l'état antérieur le plus proche par `sequence`. |
| Net / brut | `fee.weight_type` | `net` lit `quantity`, `brut` lit `gross_quantity`. |
| Packing | `ppack` | Quantité de packing, décorrélée du poids. |
| Contrôle | Python + SQL | Check applicatif et diagnostic SQL. |

## Règles consultant

### BR-PT-FEE-001 - Freight value depuis fee shipment

Source : `BR-PT-003`

#### Règle consultant

La valeur de fret affichée sur les documents facture vient du fee maritime du
shipment, pas d'un champ direct de la facture.

#### Notes développeur

- Retrouver le lot physique depuis la facture.
- Retrouver son `shipment_in`.
- Chercher le `fee.fee` avec `product.name = 'Maritime freight'`.
- Utiliser `fee.get_amount()`.

### BR-PT-FEE-002 - Lots effectifs des fees

Source : `BR-PT-021`

#### Règle consultant

Un fee suit le lot virtuel tant qu'aucun lot physique n'est lié. Dès qu'un lot
physique est lié, les lots physiques deviennent la base de calcul du fee.

#### Notes développeur

- Ne pas supprimer le lien virtuel : il reste le fallback.
- Les lots effectifs sont :
  - les physiques si au moins un physique est lié ;
  - sinon les virtuels.
- La même sélection s'applique au PnL fee.
- Points de synchronisation :
  - création fee ;
  - lien `fee.lots` ;
  - changement de `quantity_theorical` ;
  - weighing ;
  - suppression de physique.

### BR-PT-FEE-003 - Quantité du fee

#### Règle consultant

La quantité d'un fee suit la vie de la quantité des lots qui lui sont liés. Elle
doit représenter la somme des quantités de ces lots dans l'état contractuel
autorisé.

#### Règle courte

```text
fee.quantity = somme(quantité applicable des lots effectifs fee.lots)
```

#### Sélection de l'état de quantité

| Cas | État cible |
| --- | --- |
| `fee.qt_state` vide | `weight basis` du contrat porteur du fee |
| `fee.qt_state` renseigné | `fee.qt_state`, sauf s'il est postérieur au `weight basis` |
| `fee.qt_state` postérieur au `weight basis` | `weight basis` |
| état cible absent du lot | état antérieur le plus proche par `lot.qt.type.sequence` |
| aucun état cible disponible | poids courant du lot, seulement si aucun `weight basis` ne s'applique |

#### Source du `weight basis`

| Fee | `weight basis` |
| --- | --- |
| Fee achat | `fee.line.purchase.wb.qt_type` |
| Fee vente | `fee.sale_line.sale.wb.qt_type` |
| Fee shipment | weight basis achat du lot si présent |
| Fee shipment sans achat | weight basis vente du lot si présent |

#### Net / brut

- Si `fee.weight_type = net` :
  - lire `lot.qt.hist.quantity`.
- Si `fee.weight_type = brut` :
  - lire `lot.qt.hist.gross_quantity`.

#### Per packing

- `mode = ppack` est exclu de la règle poids.
- `fee.quantity` représente une quantité de packing.
- Cette quantité peut être décorrélée du poids net ou brut.

#### Lump sum

- `mode = lumpsum` suit aussi la règle de quantité.
- Le montant reste forfaitaire.
- La quantité permet de calculer un prix par tonne plus précis.

### BR-PT-FEE-004 - Fees `% rate` via delta de financement

Source : `BR-PT-016` historique et notes `2026-04-30`

#### Règle consultant

Les frais financiers en pourcentage se calculent avec le delta de financement
de la ligne d'estimation `BL date`, pas avec la date du jour.

#### Notes développeur

- Formule : `amount = unit_price * quantity * (price / 100) * fin_int_delta / 360`.
- Source du delta : ligne `Estimated date` avec `trigger = bldate`.
- Si aucune ligne `bldate` n'existe, ne pas calculer de montant `% rate`.

## Section développeur

### Champs clés

- Fee : `fee.fee`
- Lots du fee : `fee.lots`
- Quantité du fee : `fee.fee.quantity`
- Mode : `fee.fee.mode`
- État de quantité optionnel : `fee.fee.qt_state`
- Net / brut : `fee.fee.weight_type`
- État de quantité lot : `lot.qt.hist.quantity_type`
- Quantité nette : `lot.qt.hist.quantity`
- Quantité brute : `lot.qt.hist.gross_quantity`
- Ordre des états : `lot.qt.type.sequence`
- Weight basis achat / vente : `purchase.weight.basis.qt_type`

### Fonctions de calcul

- `Fee._get_effective_fee_lots()` :
  - sélectionne physiques puis virtuels.
- `Fee._target_qt_type_for_lot()` :
  - choisit `fee.qt_state` ou le `weight basis` ;
  - plafonne l'état au `weight basis`.
- `Fee._select_lot_qt_type()` :
  - prend l'état exact s'il existe ;
  - sinon prend l'état antérieur le plus proche par `sequence`.
- `Fee._get_lot_fee_quantity()` :
  - lit net ou brut selon `weight_type`.
- `Fee.sync_quantity_from_lots()` :
  - resynchronise `fee.quantity`.

### Garde-fous Python

- Check central :
  - `fee.fee.assert_quantity_consistency()`
  - `fee.fee.assert_quantities_consistency()`
- Points de déclenchement :
  - création de fee ;
  - modification de fee ;
  - création / modification / suppression de `fee.lots` ;
  - weighing via resynchronisation des fees liés.
- Exception :
  - `mode = ppack` n'est pas contrôlé comme un poids.

### Gap connu

- Split / merge de lots :
  - le flux peut cloner ou créer des lots physiques ;
  - le recâblage de `fee.lots` vers les nouveaux lots n'est pas encore garanti ;
  - les fees concernés doivent donc être contrôlés par le diagnostic SQL après
    utilisation de ce flux.

### Diagnostic SQL

- Script :
  - [sql/fee_quantity_consistency_checks.sql](sql/fee_quantity_consistency_checks.sql)
- Usage :
  - audit des bases de test ;
  - audit des données historiques ;
  - qualification avant correction.
- Le script remonte les fees non `ppack` dont `fee.quantity` ne correspond pas
  à la somme des lots effectifs selon l'état de quantité applicable.
