# Fees
Language: `en`
Mirror page: [fees.md](fees.md)
Status: `partial migration`
See also the historical technical page: `../fees.md`.
Operational summaryA fee carries an amount, but its quantity must remain aligned with the lots it applies to. Per packing is the only exception: it follows packing units, not lot weight.
| Marker |
Topic |
Short rule |
| Quantity |
fee.quantity |
Sum of linked lots in fee.lots. |
| State |
empty fee.qt_state |
Uses the contract weight basis. |
| State |
filled fee.qt_state |
Uses that state, capped by the weight basis. |
| Fallback |
state missing on lot |
Uses the closest previous state by sequence. |
| Net / gross |
fee.weight_type |
net reads quantity, brut reads gross_quantity. |
| Packing |
ppack |
Packing quantity, decoupled from weight. |
| Control |
Python + SQL |
Application guard and SQL diagnostic. |
## Consultant Rules
### BR-PT-FEE-001 - Freight value from shipment fee
Source: `BR-PT-003`
#### Consultant Rule
The freight value printed on invoice documents comes from the maritime freight
fee on the shipment, not from a direct invoice field.
#### Developer Notes
- Find the physical lot from the invoice.
- Find its
shipment_in.
- Search the
fee.fee with product.name = 'Maritime freight'.
- Use
fee.get_amount().
### BR-PT-FEE-002 - Effective lots for fees
Source: `BR-PT-021`
#### Consultant Rule
A fee follows the virtual lot until a physical lot is linked. As soon as a
physical lot is linked, physical lots become the calculation basis for the fee.
#### Developer Notes
- Do not remove the virtual lot link: it remains the fallback.
- Effective lots are:
- physical lots if at least one physical lot is linked;
- otherwise virtual lots.
- The same selection applies to fee PnL.
- Synchronization points:
- fee creation;
fee.lots link;
quantity_theorical change;
- weighing;
- physical lot removal.
### BR-PT-FEE-003 - Fee quantity
#### Consultant Rule
The quantity of a fee follows the quantity life cycle of its linked lots. It
must represent the sum of those lots in the authorized contractual quantity
state.
#### Short Rule
fee.quantity = sum(applicable quantity of effective fee.lots)
#### Quantity State Selection
| Case |
Target state |
empty fee.qt_state |
weight basis of the fee carrier contract |
filled fee.qt_state |
fee.qt_state, unless later than the weight basis |
fee.qt_state later than weight basis |
weight basis |
| target state missing on lot |
closest previous state by lot.qt.type.sequence |
| no target state available |
current lot weight, only when no weight basis applies |
#### Weight Basis Source
| Fee |
weight basis |
| Purchase fee |
fee.line.purchase.wb.qt_type |
| Sale fee |
fee.sale_line.sale.wb.qt_type |
| Shipment fee |
purchase weight basis of the lot when present |
| Shipment fee without purchase |
sale weight basis of the lot when present |
#### Net / Gross
- If
fee.weight_type = net:
- read
lot.qt.hist.quantity.
- If
fee.weight_type = brut:
- read
lot.qt.hist.gross_quantity.
#### Per Packing
mode = ppack is excluded from the weight rule.
fee.quantity represents a packing quantity.
- This quantity may be decoupled from net or gross weight.
#### Lump Sum
mode = lumpsum also follows the quantity rule.
- The amount remains fixed.
- The quantity gives a more accurate per-ton price.
### BR-PT-FEE-004 - `% rate` fees from financing delta
Source: historical `BR-PT-016` and `2026-04-30` notes
#### Consultant Rule
Percentage financial fees are calculated with the financing delta from the
`BL date` estimated line, not with the current date.
#### Developer Notes
- Formula:
amount = unit_price * quantity * (price / 100) * fin_int_delta / 360.
- Delta source:
Estimated date line with trigger = bldate.
- If no
bldate line exists, do not calculate a % rate amount.
## Developer Section
### Key Fields
- Fee:
fee.fee
- Fee lots:
fee.lots
- Fee quantity:
fee.fee.quantity
- Mode:
fee.fee.mode
- Optional quantity state:
fee.fee.qt_state
- Net / gross:
fee.fee.weight_type
- Lot quantity state:
lot.qt.hist.quantity_type
- Net quantity:
lot.qt.hist.quantity
- Gross quantity:
lot.qt.hist.gross_quantity
- State order:
lot.qt.type.sequence
- Purchase / sale weight basis:
purchase.weight.basis.qt_type
### Calculation Functions
Fee._get_effective_fee_lots():
- selects physical lots, then virtual lots.
Fee._target_qt_type_for_lot():
- chooses
fee.qt_state or the weight basis;
- caps the state at the
weight basis.
Fee._select_lot_qt_type():
- takes the exact state when it exists;
- otherwise takes the closest previous state by
sequence.
Fee._get_lot_fee_quantity():
- reads net or gross depending on
weight_type.
Fee.sync_quantity_from_lots():
- resynchronizes
fee.quantity.
### Python Guards
- Central check:
fee.fee.assert_quantity_consistency()
fee.fee.assert_quantities_consistency()
- Trigger points:
- fee creation;
- fee modification;
fee.lots creation / modification / deletion;
- weighing through linked fee resynchronization.
- Exception:
mode = ppack is not controlled as weight.
### Known Gap
- Lot split / merge:
- the workflow may clone or create physical lots;
- rewiring
fee.lots to the new lots is not guaranteed yet;
- affected fees must therefore be checked with the SQL diagnostic after this workflow is used.
### SQL Diagnostic
- Script:
- Usage:
- audit test databases;
- audit historical data;
- qualify data before correction.
- The script returns non-
ppack fees where fee.quantity does not match the sum of effective lots according to the applicable quantity state.