Fees
Language: en
Mirror page: fees.md
Status: partial migration
See also the historical technical page: ../fees.md.
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.feewithproduct.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.lotslink;quantity_theoricalchange;- 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.
- read
- If
fee.weight_type = brut:- read
lot.qt.hist.gross_quantity.
- read
Per Packing
mode = ppackis excluded from the weight rule.fee.quantityrepresents a packing quantity.- This quantity may be decoupled from net or gross weight.
Lump Sum
mode = lumpsumalso 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 dateline withtrigger = bldate. - If no
bldateline exists, do not calculate a% rateamount.
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_stateor theweight basis; - caps the state at the
weight basis.
- chooses
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.
- reads net or gross depending on
Fee.sync_quantity_from_lots():- resynchronizes
fee.quantity.
- resynchronizes
Python Guards
- Central check:
fee.fee.assert_quantity_consistency()fee.fee.assert_quantities_consistency()
- Trigger points:
- fee creation;
- fee modification;
fee.lotscreation / modification / deletion;- weighing through linked fee resynchronization.
- Exception:
mode = ppackis not controlled as weight.
Known Gap
- Lot split / merge:
- the workflow may clone or create physical lots;
- rewiring
fee.lotsto 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-
ppackfees wherefee.quantitydoes not match the sum of effective lots according to the applicable quantity state.