194 lines
5.8 KiB
Markdown
194 lines
5.8 KiB
Markdown
# Fees
|
|
|
|
Language: `en`
|
|
Mirror page: [fees.md](fees.md)
|
|
Status: `partial migration`
|
|
|
|
See also the historical technical page: `../fees.md`.
|
|
|
|
> **Operational summary**
|
|
> A 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
|
|
|
|
```text
|
|
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:
|
|
- [sql/fee_quantity_consistency_checks.sql](sql/fee_quantity_consistency_checks.sql)
|
|
- 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.
|