Add lot_hist readonly
This commit is contained in:
@@ -24,8 +24,14 @@ class Lot(ModelSQL, ModelView):
|
||||
|
||||
lot_name = fields.Char("Lot")
|
||||
number = fields.Char("Number", readonly=True)
|
||||
lot_qt = fields.Float("Quantity",required=False)
|
||||
lot_unit = fields.Many2One('product.uom', "Unit",required=False)
|
||||
lot_qt = fields.Float("Quantity", required=False,
|
||||
states={
|
||||
'readonly': Eval('lot_type') == 'virtual',
|
||||
})
|
||||
lot_unit = fields.Many2One('product.uom', "Unit", required=False,
|
||||
states={
|
||||
'readonly': Eval('lot_type') == 'virtual',
|
||||
})
|
||||
lot_product = fields.Many2One('product.product', "Product")
|
||||
lot_type = fields.Selection([
|
||||
('virtual', 'Open'),
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<notebook colspan="4">
|
||||
<page string="General" id="general">
|
||||
<field name="lot_childs" colspan="4" mode="tree,form" view_ids="lot.lot_view_tree_sequence2,lot.lot_view_form"/>
|
||||
<field name="lot_hist" colspan="4"/>
|
||||
<field name="lot_hist" colspan="4" readonly="1"/>
|
||||
</page>
|
||||
<page string="Accounting" id="accounting">
|
||||
<field name="pivot" widget="html_viewer" height="600" colspan="4"/>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0"?>
|
||||
<form col="4">
|
||||
<label name="lot"/>
|
||||
<field name="lot"/>
|
||||
<field name="lot" readonly="1"/>
|
||||
<label name="quantity_type"/>
|
||||
<field name="quantity_type"/>
|
||||
<field name="quantity_type" readonly="1"/>
|
||||
<label name="quantity"/>
|
||||
<field name="quantity"/>
|
||||
<field name="quantity" readonly="1"/>
|
||||
<label name="gross_quantity"/>
|
||||
<field name="gross_quantity"/>
|
||||
<field name="gross_quantity" readonly="1"/>
|
||||
</form>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<tree editable="1">
|
||||
<tree>
|
||||
<field name="lot"/>
|
||||
<field name="quantity_type"/>
|
||||
<field name="quantity"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<tree editable="1">
|
||||
<tree>
|
||||
<field name="lot"/>
|
||||
<field name="quantity_type"/>
|
||||
<field name="quantity"/>
|
||||
|
||||
@@ -5,7 +5,7 @@ Statut: `migration partielle`
|
||||
## Comment chercher une regle
|
||||
|
||||
- Contrats, dates, lieux, banques: [contracts.md](contracts.md)
|
||||
- Lots virtuels, lots physiques, `lot.qt`, weighing: [lots-and-quantities.md](lots-and-quantities.md)
|
||||
- Lots virtuels, lots physiques, `lot.qt`, weighing: [FR](lots-and-quantities.md) / [EN](lots-and-quantities.en.md)
|
||||
- Matching, Create Contracts, back-to-back: [matching.md](matching.md)
|
||||
- Shipments, controllers, SLA, weight reports: [shipments-execution.md](shipments-execution.md)
|
||||
- Pricing manuel, basis, premium, linked currency: [pricing.md](pricing.md)
|
||||
|
||||
@@ -1,35 +1,53 @@
|
||||
# Guide de lecture des regles business
|
||||
# Guide de lecture des règles business
|
||||
|
||||
Statut: `migration partielle`
|
||||
Derniere mise a jour: `2026-05-13`
|
||||
Dernière mise à jour: `2026-05-13`
|
||||
|
||||
Ce dossier devient la source de lecture thematique pour les regles business du
|
||||
Ce dossier devient la source de lecture thématique pour les règles business du
|
||||
module `purchase_trade`.
|
||||
|
||||
Chaque page doit rester lisible par deux publics:
|
||||
|
||||
- les consultants, qui ont besoin d'une regle fonctionnelle stable sans detail
|
||||
- les consultants, qui ont besoin d'une règle fonctionnelle stable sans détail
|
||||
de code inutile;
|
||||
- les developpeurs, qui ont besoin des champs, modeles, fichiers et tests
|
||||
concernes pour appliquer la regle sans l'interpreter.
|
||||
- les développeurs, qui ont besoin des champs, modèles, fichiers et tests
|
||||
concernés pour appliquer la règle sans l'interpréter.
|
||||
|
||||
## Convention de redaction
|
||||
## Convention de langues
|
||||
|
||||
Pour chaque regle durable, utiliser autant que possible ce format:
|
||||
Chaque page thématique durable doit exister en deux versions maintenues
|
||||
ensemble:
|
||||
|
||||
- une page française, rédigée en français correct avec accents, typographie et
|
||||
formulations naturelles pour le wiki consultant;
|
||||
- une page anglaise miroir, portant le même contenu fonctionnel et technique.
|
||||
|
||||
Convention de nommage:
|
||||
|
||||
- page française principale: `theme.md`;
|
||||
- page anglaise miroir: `theme.en.md`.
|
||||
|
||||
Toute modification d'une règle business, d'un statut, d'un champ technique ou
|
||||
d'un point de vigilance doit être reportée dans les deux pages au même moment.
|
||||
Les deux pages doivent indiquer leur page miroir en en-tête.
|
||||
|
||||
## Convention de rédaction
|
||||
|
||||
Pour chaque règle durable, utiliser autant que possible ce format:
|
||||
|
||||
```md
|
||||
### BR-PT-THEME-001 - Titre court
|
||||
|
||||
Statut: active
|
||||
Source: business-rules.md / note de session / decision projet
|
||||
Source: business-rules.md / note de session / décision projet
|
||||
|
||||
#### Regle consultant
|
||||
#### Règle consultant
|
||||
|
||||
Texte fonctionnel, sans nom de champ si ce n'est pas necessaire.
|
||||
Texte fonctionnel, sans nom de champ si ce n'est pas nécessaire.
|
||||
|
||||
#### Notes developpeur
|
||||
#### Notes développeur
|
||||
|
||||
- Modeles/champs:
|
||||
- Modèles/champs:
|
||||
- Fichiers:
|
||||
- Tests:
|
||||
- Points de vigilance:
|
||||
@@ -37,9 +55,9 @@ Texte fonctionnel, sans nom de champ si ce n'est pas necessaire.
|
||||
|
||||
## Sources pendant la migration
|
||||
|
||||
Les anciennes pages ne sont pas supprimees a cette etape. Elles restent des
|
||||
sources de verification jusqu'a ce que chaque decision soit promue dans une
|
||||
page thematique:
|
||||
Les anciennes pages ne sont pas supprimées à cette étape. Elles restent des
|
||||
sources de vérification jusqu'à ce que chaque décision soit promue dans une
|
||||
page thématique:
|
||||
|
||||
- `modules/purchase_trade/docs/business-rules.md`
|
||||
- `modules/purchase_trade/docs/fees.md`
|
||||
@@ -48,4 +66,3 @@ page thematique:
|
||||
- `modules/purchase_trade/docs/template-properties.md`
|
||||
- `notes/business_rules.md`
|
||||
- `notes/template_business_rules.md`
|
||||
|
||||
|
||||
259
modules/purchase_trade/docs/business/lots-and-quantities.en.md
Normal file
259
modules/purchase_trade/docs/business/lots-and-quantities.en.md
Normal file
@@ -0,0 +1,259 @@
|
||||
# Lots and Quantities
|
||||
|
||||
Language: `en`
|
||||
Mirror page: [lots-and-quantities.md](lots-and-quantities.md)
|
||||
Status: `partial migration`
|
||||
Last code verification: `2026-05-13`
|
||||
|
||||
This page consolidates the former rules `BR-PT-LOT-001`,
|
||||
`BR-PT-LOT-002`, and `BR-PT-LOT-003` around one functional rule: the life
|
||||
cycle of open, forecasted, and physical quantities.
|
||||
|
||||
## Consultant Business Rule
|
||||
|
||||
### BR-PT-LOT-001 - Lot and quantity life cycle
|
||||
|
||||
When a purchase line or sale line is created, the system creates one virtual
|
||||
lot linked to that line. This virtual lot represents the contract quantity that
|
||||
is still open. Its initial quantity is the contractual or theoretical quantity
|
||||
defined on the line.
|
||||
|
||||
At the same time, the system adds one open line in `lot.qt`. This line is the
|
||||
basis for commercial and logistics forecasts: forecast sale, future matching,
|
||||
planned transport, shipment, and so on. `lot.qt` therefore carries the
|
||||
operational forecast, while the virtual lot remains the global representation
|
||||
of the open balance in `lot.lot`.
|
||||
|
||||
Example: an open quantity `P1` can be progressively subdivided in `lot.qt` to
|
||||
forecast several sales or transports:
|
||||
|
||||
- `P1S1T1`
|
||||
- `P1S1T2`
|
||||
- `P1S2T3`
|
||||
- `P1S2T4`
|
||||
|
||||
These subdivisions do not create several virtual lots for `P1`. They only
|
||||
describe the forecast allocation of the open quantity.
|
||||
|
||||
When physical lots are added, they are created from a specific Lots Management
|
||||
line, therefore from a `lot.qt` line. If the user selects `P1S1T1`, the
|
||||
physical lot created consumes that specific forecast. The selected `lot.qt`
|
||||
line is reduced, then the line's virtual lot is recalculated so that it only
|
||||
represents the remaining open balance.
|
||||
|
||||
The immutable quantity consistency rule is:
|
||||
|
||||
```text
|
||||
sum of the line's physical lots + the line's virtual lot
|
||||
= the line's contractual/theoretical quantity
|
||||
```
|
||||
|
||||
This consistency must remain true even if the contractual quantity is adjusted
|
||||
along the way. The contractual/theoretical quantity is the only quantity that
|
||||
the user enters on the line. The standard line quantity is a counter: it
|
||||
follows the contractual quantity as long as there is no physical lot, then it
|
||||
reflects the sum of the executed physical lots.
|
||||
|
||||
Removing a physical lot is allowed only while its stock move is not finalized.
|
||||
If the lot was already matched or linked to a shipment, the user must confirm
|
||||
the action. The removal restores the quantity into the `lot.qt` line that
|
||||
carried the forecast, matching, or transport context used to create the
|
||||
physical lot.
|
||||
|
||||
The quantity history of a lot is never a direct data-entry area. The different
|
||||
quantity states of a lot must be changed only through a dedicated business
|
||||
action, currently `Do weighing`. This rule ensures that open quantity, virtual
|
||||
lot, fee, and stock move recalculations remain synchronized.
|
||||
|
||||
### Tolerance and physical quantities
|
||||
|
||||
A physical lot may have a quantity that differs from the initial forecast,
|
||||
within the contract tolerance limits. Tolerance must be read as a global
|
||||
tolerance on the line or contract, not as an independent tolerance per
|
||||
transport.
|
||||
|
||||
If a physical lot consumes more or less than its forecast, this must
|
||||
dynamically reduce or increase the remaining tolerance for the next physical
|
||||
lots of the same line. No add, weighing, deletion, or contractual adjustment
|
||||
action may break the global quantity consistency rule.
|
||||
|
||||
## Developer Section
|
||||
|
||||
### Main models and fields
|
||||
|
||||
- Purchase line: `purchase.line`.
|
||||
- Sale line: `sale.line`.
|
||||
- Lot: `lot.lot`.
|
||||
- Forecast / open quantity: `lot.qt`.
|
||||
- Lot quantity history: `lot.qt.hist`.
|
||||
- Purchase contractual quantity: `purchase.line.quantity_theorical`
|
||||
(`Contractual Qt`).
|
||||
- Sale contractual quantity: `sale.line.quantity_theorical`
|
||||
(`Th. quantity`).
|
||||
- Purchase/sale quantity counter: `quantity`.
|
||||
- Lot type: `lot.lot.lot_type`, values `virtual` and `physic`.
|
||||
- Purchase link: `lot.lot.line`.
|
||||
- Sale link: `lot.lot.sale_line`.
|
||||
- Shipment links: `lot_shipment_in`, `lot_shipment_internal`,
|
||||
`lot_shipment_out`.
|
||||
- Purchase/sale forecast matching: `lot.qt.lot_p`, `lot.qt.lot_s`.
|
||||
- Forecast quantity: `lot.qt.lot_quantity`.
|
||||
- Contract/line tolerances: `tol_min`, `tol_max`, `tol_min_qt`,
|
||||
`tol_max_qt`, `tol_min_v`, `tol_max_v`.
|
||||
- Quantity state history: `lot.lot.lot_hist` to `lot.qt.hist`.
|
||||
- History fields: `lot.qt.hist.quantity_type`, `lot.qt.hist.quantity`,
|
||||
`lot.qt.hist.gross_quantity`.
|
||||
- Lot packing fields: `lot.lot.lot_qt`, `lot.lot.lot_unit`.
|
||||
|
||||
### Line and virtual lot creation
|
||||
|
||||
- Purchase: `modules/purchase_trade/purchase.py`, `Line.validate`.
|
||||
- Sale: `modules/purchase_trade/sale.py`, `SaleLine.validate`.
|
||||
- If the line is not `created_by_code`, has no lot yet, the product is not a
|
||||
service, and `quantity != 0`, a `virtual` lot is created.
|
||||
- The virtual lot receives its first `lot.qt.hist` entry.
|
||||
- The open `lot.qt` creation is triggered by
|
||||
`modules/purchase_trade/lot.py`, `Lot.validate`, through
|
||||
`createVirtualPart` when no `lot.qt` exists yet for the virtual lot.
|
||||
- On purchase, the virtual lot feeds `lot.qt.lot_p`.
|
||||
- On sale, the virtual lot feeds `lot.qt.lot_s` with `lot_p = None`.
|
||||
|
||||
### Contractual quantity update
|
||||
|
||||
Verified code state:
|
||||
|
||||
- Purchase: `purchase.py`, `Line.write`.
|
||||
- Sale: `sale.py`, `SaleLine.write`.
|
||||
- Updating `quantity_theorical` recalculates a target virtual quantity:
|
||||
|
||||
```text
|
||||
target_quantity = quantity_theorical - sum(converted physical lots)
|
||||
```
|
||||
|
||||
- If this target quantity becomes negative, the code blocks with
|
||||
`Please unlink or unmatch lot`.
|
||||
- The free `lot.qt` is then resynchronized while taking into account `lot.qt`
|
||||
lines that are already allocated, meaning already matched or linked to a
|
||||
shipment:
|
||||
|
||||
```text
|
||||
free_quantity = target_quantity - sum(already matched or shipped lot.qt)
|
||||
```
|
||||
|
||||
- If `free_quantity` becomes negative, the code blocks with
|
||||
`Please unlink or unmatch lot`.
|
||||
- If the virtual lot does not already carry the target quantity, the code calls
|
||||
`vlot.set_current_quantity(target_quantity, target_quantity, 1)`.
|
||||
- If a free `lot.qt` exists, its quantity is replaced with `free_quantity`.
|
||||
- If no free `lot.qt` exists and `free_quantity > 0`, a new free `lot.qt` is
|
||||
created.
|
||||
- The line fees are resynchronized after the update.
|
||||
|
||||
Purchase-specific point:
|
||||
|
||||
- If `quantity_theorical` was empty during initialization, the code uses the
|
||||
current virtual lot quantity as the baseline to avoid doubling the open
|
||||
`lot.qt`.
|
||||
|
||||
Sale-specific point:
|
||||
|
||||
- If the previous `quantity_theorical` is empty, `SaleLine.write` does not yet
|
||||
run this resynchronization.
|
||||
|
||||
### Adding physical lots
|
||||
|
||||
- Wizard: `modules/purchase_trade/lot.py`, `lot.add`.
|
||||
- Main method: `LotQt.add_physical_lots`.
|
||||
- Unit creation method: `LotQt.add_physical_lot`.
|
||||
- Adding starts from a `lot.qt` line selected in Lots Management.
|
||||
- The code refuses direct addition from a physical lot.
|
||||
- The code refuses physical addition on the sale side through this wizard and
|
||||
asks the user to use `Apply matching`.
|
||||
- The new physical lot inherits the context of the `lot.qt` line: purchase
|
||||
line, matched sale if present, shipment, product, unit, quantities, premium,
|
||||
and chunk key.
|
||||
- After creation, the code reduces the source `lot.qt` quantity by the total
|
||||
physical quantity created and never lets the line go below zero.
|
||||
- Saving/validating the physical lot recalculates:
|
||||
- the line's virtual lot through `_recompute_virtual_lot`;
|
||||
- the line quantity counter through `_recalc_line_quantity`;
|
||||
- linked stock moves when needed;
|
||||
- fees linked to the lot, line, or shipment.
|
||||
|
||||
### Removing physical lots
|
||||
|
||||
- Wizard: `modules/purchase_trade/lot.py`, `lot.remove`.
|
||||
- Removing an open lot is forbidden.
|
||||
- If the physical lot has a `stock.move`, that move must be in `draft` state.
|
||||
- If the lot is matched or shipped, a confirmable warning is displayed.
|
||||
- The code first deletes the draft move when present.
|
||||
- The code restores the physical quantity into `lot.qt` while reusing the
|
||||
context:
|
||||
- original shipment through `lot.lot_shipment_origin`;
|
||||
- sale virtual lot through `getVlot_s()` if the lot was matched;
|
||||
- purchase virtual lot through `getVlot_p()` in `updateVirtualPart`.
|
||||
- If a compatible `lot.qt` line already exists, it is incremented.
|
||||
- Otherwise, a new `lot.qt` line is created.
|
||||
- Deleting the physical lot then triggers recalculation of the virtual lot,
|
||||
the line quantity counter, and fees.
|
||||
|
||||
### Quantity history and weighing
|
||||
|
||||
- History is carried by `lot.qt.hist`.
|
||||
- `lot.qt.hist` represents the different quantity states of a lot.
|
||||
- Historical quantities must not be changed directly from the `lot.lot` form.
|
||||
- The `lot.lot` view displays `lot_hist` as read-only.
|
||||
- `lot.qt.hist` views are consultative: no direct editing from tree or form.
|
||||
- The functional change path is the `lot.weighing` wizard, exposed by the
|
||||
`Do weighing` action.
|
||||
- The wizard writes or updates the `lot.qt.hist` entry for the selected
|
||||
`lot_state`, then synchronizes the lot, open quantities, and fees.
|
||||
- A virtual lot must not receive packing data entry from the lot form:
|
||||
`lot_qt` and `lot_unit` are not editable for `lot_type = virtual`.
|
||||
|
||||
### Line quantity counter
|
||||
|
||||
- Method: `lot.py`, `Lot._recalc_line_quantity`.
|
||||
- If the line has only one lot, `quantity` takes the current quantity of that
|
||||
lot.
|
||||
- If the line has several lots, `quantity` sums physical lots only.
|
||||
- This matches the functional rule:
|
||||
- without a physical lot: `quantity` follows the virtual lot;
|
||||
- with physical lots: `quantity` reflects executed physical quantity.
|
||||
|
||||
### Tolerance: current state and point to confirm
|
||||
|
||||
The code already exposes tolerances on contracts and lines:
|
||||
|
||||
- Purchase: `purchase.purchase.tol_min`, `purchase.purchase.tol_max`,
|
||||
`purchase.line.tol_min`, `purchase.line.tol_max`.
|
||||
- Sale: `sale.sale.tol_min`, `sale.sale.tol_max`, `sale.line.tol_min`,
|
||||
`sale.line.tol_max`.
|
||||
- Display functions: `get_tol_min`, `get_tol_max`.
|
||||
- Add wizard: `lot.add.line.tol_min`, `lot.add.line.tol_max`, with defaults
|
||||
from the source contract.
|
||||
|
||||
To confirm / potential gap:
|
||||
|
||||
- In `LotQt.add_physical_lots` and `LotQt.add_physical_lot`, I do not yet see
|
||||
a complete control that computes remaining global tolerance while taking
|
||||
already created physical lots into account.
|
||||
- The consultant rule above about dynamic global tolerance must therefore be
|
||||
considered a target business rule to verify/implement before marking it
|
||||
`active`.
|
||||
|
||||
### Nearby tests
|
||||
|
||||
- `modules/purchase_trade/tests/test_module.py`
|
||||
- `test_sale_line_write_updates_virtual_lot_when_theorical_qty_increases`
|
||||
- `test_sale_line_write_blocks_theorical_qty_decrease_when_no_open_quantity`
|
||||
- `test_purchase_line_write_syncs_open_lot_qt_with_physical_lots`
|
||||
- `test_purchase_line_write_syncs_virtual_fee_quantity`
|
||||
- `test_purchase_line_write_initial_theorical_qty_does_not_double_open_lot`
|
||||
|
||||
To add if this rule becomes regression-sensitive:
|
||||
|
||||
- verify that `lot_hist` is read-only on the `lot.lot` form;
|
||||
- verify that `Do weighing` can still create or update a quantity state;
|
||||
- verify that a virtual lot does not allow direct entry of `lot_qt` and
|
||||
`lot_unit`.
|
||||
@@ -1,224 +1,254 @@
|
||||
# Lots et quantites
|
||||
# Lots et quantités
|
||||
|
||||
Langue: `fr`
|
||||
Page miroir: [lots-and-quantities.en.md](lots-and-quantities.en.md)
|
||||
Statut: `migration partielle`
|
||||
Derniere verification code: `2026-05-13`
|
||||
Dernière vérification code: `2026-05-13`
|
||||
|
||||
Cette page consolide les anciennes regles `BR-PT-LOT-001`,
|
||||
`BR-PT-LOT-002` et `BR-PT-LOT-003` autour d'une regle fonctionnelle unique:
|
||||
le cycle de vie des quantites ouvertes, forecastees et physiques.
|
||||
Cette page consolide les anciennes règles `BR-PT-LOT-001`,
|
||||
`BR-PT-LOT-002` et `BR-PT-LOT-003` autour d'une règle fonctionnelle unique :
|
||||
le cycle de vie des quantités ouvertes, forecastées et physiques.
|
||||
|
||||
## Regle business consultant
|
||||
## Règle business consultant
|
||||
|
||||
### BR-PT-LOT-001 - Cycle de vie des lots et des quantites
|
||||
### BR-PT-LOT-001 - Cycle de vie des lots et des quantités
|
||||
|
||||
Lors de la creation d'une ligne d'achat ou de vente, le systeme cree un lot
|
||||
virtuel associe a cette ligne. Ce lot virtuel represente la quantite encore
|
||||
ouverte du contrat. Sa quantite initiale reprend la quantite contractuelle ou
|
||||
theorique de la ligne.
|
||||
Lors de la création d'une ligne d'achat ou de vente, le système crée un lot
|
||||
virtuel associé à cette ligne. Ce lot virtuel représente la quantité encore
|
||||
ouverte du contrat. Sa quantité initiale reprend la quantité contractuelle ou
|
||||
théorique de la ligne.
|
||||
|
||||
En parallele, le systeme ajoute une ligne ouverte dans `lot.qt`. Cette ligne
|
||||
sert de base aux previsions commerciales et logistiques: vente previsionnelle,
|
||||
matching futur, transport planifie, shipment, etc. `lot.qt` porte donc le
|
||||
forecast operationnel, tandis que le lot virtuel reste la representation
|
||||
En parallèle, le système ajoute une ligne ouverte dans `lot.qt`. Cette ligne
|
||||
sert de base aux prévisions commerciales et logistiques : vente prévisionnelle,
|
||||
matching futur, transport planifié, shipment, etc. `lot.qt` porte donc le
|
||||
forecast opérationnel, tandis que le lot virtuel reste la représentation
|
||||
globale du solde ouvert dans `lot.lot`.
|
||||
|
||||
Exemple: une quantite ouverte `P1` peut etre progressivement subdivisee dans
|
||||
`lot.qt` pour prevoir plusieurs ventes ou transports:
|
||||
Exemple : une quantité ouverte `P1` peut être progressivement subdivisée dans
|
||||
`lot.qt` pour prévoir plusieurs ventes ou transports :
|
||||
|
||||
- `P1S1T1`
|
||||
- `P1S1T2`
|
||||
- `P1S2T3`
|
||||
- `P1S2T4`
|
||||
|
||||
Ces subdivisions ne creent pas plusieurs lots virtuels pour `P1`. Elles
|
||||
decrivent seulement la repartition previsionnelle de la quantite ouverte.
|
||||
Ces subdivisions ne créent pas plusieurs lots virtuels pour `P1`. Elles
|
||||
décrivent seulement la répartition prévisionnelle de la quantité ouverte.
|
||||
|
||||
Quand des lots physiques sont ajoutes, ils sont crees depuis une ligne precise
|
||||
Quand des lots physiques sont ajoutés, ils sont créés depuis une ligne précise
|
||||
de Lots Management, donc depuis une ligne `lot.qt`. Si l'utilisateur choisit
|
||||
`P1S1T1`, le lot physique cree consomme ce forecast particulier. La ligne
|
||||
`lot.qt` choisie est reduite, puis le lot virtuel de la ligne est recalcule
|
||||
pour representer seulement le reliquat encore ouvert.
|
||||
`P1S1T1`, le lot physique créé consomme ce forecast particulier. La ligne
|
||||
`lot.qt` choisie est réduite, puis le lot virtuel de la ligne est recalculé
|
||||
pour représenter seulement le reliquat encore ouvert.
|
||||
|
||||
La regle immuable de coherence est:
|
||||
La règle immuable de cohérence est :
|
||||
|
||||
```text
|
||||
somme des lots physiques de la ligne + lot virtuel de la ligne
|
||||
= quantite contractuelle/theorique de la ligne
|
||||
= quantité contractuelle/théorique de la ligne
|
||||
```
|
||||
|
||||
Cette coherence doit rester vraie meme si la quantite contractuelle est
|
||||
ajustee en cours de route. La quantite contractuelle/theorique est la seule
|
||||
quantite saisie par l'utilisateur sur la ligne. La quantite standard de la
|
||||
ligne est un compteur: elle reprend la quantite contractuelle tant qu'il n'y a
|
||||
pas de lot physique, puis elle reflete la somme des lots physiques executes.
|
||||
Cette cohérence doit rester vraie même si la quantité contractuelle est
|
||||
ajustée en cours de route. La quantité contractuelle/théorique est la seule
|
||||
quantité saisie par l'utilisateur sur la ligne. La quantité standard de la
|
||||
ligne est un compteur : elle reprend la quantité contractuelle tant qu'il n'y a
|
||||
pas de lot physique, puis elle reflète la somme des lots physiques exécutés.
|
||||
|
||||
Le retrait d'un lot physique est autorise seulement tant que son mouvement
|
||||
stock n'est pas finalise. Si ce lot etait deja matche ou rattache a un
|
||||
Le retrait d'un lot physique est autorisé seulement tant que son mouvement
|
||||
stock n'est pas finalisé. Si ce lot était déjà matché ou rattaché à un
|
||||
shipment, l'utilisateur doit confirmer l'action. Le retrait restaure la
|
||||
quantite dans la ligne `lot.qt` qui portait le contexte forecast, matching ou
|
||||
transport ayant permis de creer le lot physique.
|
||||
quantité dans la ligne `lot.qt` qui portait le contexte forecast, matching ou
|
||||
transport ayant permis de créer le lot physique.
|
||||
|
||||
### Tolerance et quantites physiques
|
||||
L'historique des quantités d'un lot n'est jamais une zone de saisie directe.
|
||||
Les différents états de quantité d'un lot doivent être modifiés uniquement par
|
||||
une action métier dédiée, aujourd'hui `Do weighing`. Cette règle garantit que
|
||||
les recalculs de quantité ouverte, de lot virtuel, de fees et de mouvements
|
||||
stock restent synchronisés.
|
||||
|
||||
Un lot physique peut avoir une quantite differente de la prevision initiale,
|
||||
dans les limites de tolerance du contrat. La tolerance doit se lire comme une
|
||||
tolerance globale sur la ligne ou le contrat, pas comme une tolerance
|
||||
independante par transport.
|
||||
### Tolérance et quantités physiques
|
||||
|
||||
Si un lot physique consomme plus ou moins que son forecast, cela doit reduire
|
||||
ou augmenter dynamiquement la tolerance restante pour les prochains lots
|
||||
physiques de la meme ligne. Aucune action d'ajout, de weighing, de suppression
|
||||
ou d'ajustement contractuel ne doit permettre de sortir de la coherence globale
|
||||
des quantites.
|
||||
Un lot physique peut avoir une quantité différente de la prévision initiale,
|
||||
dans les limites de tolérance du contrat. La tolérance doit se lire comme une
|
||||
tolérance globale sur la ligne ou le contrat, pas comme une tolérance
|
||||
indépendante par transport.
|
||||
|
||||
## Section developpeur
|
||||
Si un lot physique consomme plus ou moins que son forecast, cela doit réduire
|
||||
ou augmenter dynamiquement la tolérance restante pour les prochains lots
|
||||
physiques de la même ligne. Aucune action d'ajout, de weighing, de suppression
|
||||
ou d'ajustement contractuel ne doit permettre de sortir de la cohérence globale
|
||||
des quantités.
|
||||
|
||||
### Modeles et champs principaux
|
||||
## Section développeur
|
||||
|
||||
- Ligne achat: `purchase.line`.
|
||||
- Ligne vente: `sale.line`.
|
||||
- Lot: `lot.lot`.
|
||||
- Forecast / quantite ouverte: `lot.qt`.
|
||||
- Historique des quantites de lot: `lot.qt.hist`.
|
||||
- Quantite contractuelle achat: `purchase.line.quantity_theorical`
|
||||
### Modèles et champs principaux
|
||||
|
||||
- Ligne achat : `purchase.line`.
|
||||
- Ligne vente : `sale.line`.
|
||||
- Lot : `lot.lot`.
|
||||
- Forecast / quantité ouverte : `lot.qt`.
|
||||
- Historique des quantités de lot : `lot.qt.hist`.
|
||||
- Quantité contractuelle achat : `purchase.line.quantity_theorical`
|
||||
(`Contractual Qt`).
|
||||
- Quantite contractuelle vente: `sale.line.quantity_theorical`
|
||||
- Quantité contractuelle vente : `sale.line.quantity_theorical`
|
||||
(`Th. quantity`).
|
||||
- Quantite compteur achat/vente: `quantity`.
|
||||
- Type de lot: `lot.lot.lot_type`, valeurs `virtual` et `physic`.
|
||||
- Lien achat: `lot.lot.line`.
|
||||
- Lien vente: `lot.lot.sale_line`.
|
||||
- Liens shipment: `lot_shipment_in`, `lot_shipment_internal`,
|
||||
- Quantité compteur achat/vente : `quantity`.
|
||||
- Type de lot : `lot.lot.lot_type`, valeurs `virtual` et `physic`.
|
||||
- Lien achat : `lot.lot.line`.
|
||||
- Lien vente : `lot.lot.sale_line`.
|
||||
- Liens shipment : `lot_shipment_in`, `lot_shipment_internal`,
|
||||
`lot_shipment_out`.
|
||||
- Matching forecast achat/vente: `lot.qt.lot_p`, `lot.qt.lot_s`.
|
||||
- Quantite forecast: `lot.qt.lot_quantity`.
|
||||
- Tolerances contrat/ligne: `tol_min`, `tol_max`, `tol_min_qt`,
|
||||
- Matching forecast achat/vente : `lot.qt.lot_p`, `lot.qt.lot_s`.
|
||||
- Quantité forecast : `lot.qt.lot_quantity`.
|
||||
- Tolérances contrat/ligne : `tol_min`, `tol_max`, `tol_min_qt`,
|
||||
`tol_max_qt`, `tol_min_v`, `tol_max_v`.
|
||||
- Historique des états de quantité : `lot.lot.lot_hist` vers `lot.qt.hist`.
|
||||
- Champs d'historique : `lot.qt.hist.quantity_type`,
|
||||
`lot.qt.hist.quantity`, `lot.qt.hist.gross_quantity`.
|
||||
- Champs de packing du lot : `lot.lot.lot_qt`, `lot.lot.lot_unit`.
|
||||
|
||||
### Creation de la ligne et du lot virtuel
|
||||
### Création de la ligne et du lot virtuel
|
||||
|
||||
- Achat: `modules/purchase_trade/purchase.py`, `Line.validate`.
|
||||
- Vente: `modules/purchase_trade/sale.py`, `SaleLine.validate`.
|
||||
- Achat : `modules/purchase_trade/purchase.py`, `Line.validate`.
|
||||
- Vente : `modules/purchase_trade/sale.py`, `SaleLine.validate`.
|
||||
- Si la ligne n'est pas `created_by_code`, qu'elle n'a pas encore de lot, que
|
||||
le produit n'est pas un service et que `quantity != 0`, un lot `virtual` est
|
||||
cree.
|
||||
- Le lot virtuel recoit une premiere entree `lot.qt.hist`.
|
||||
- La creation du `lot.qt` ouvert est declenchee par
|
||||
créé.
|
||||
- Le lot virtuel reçoit une première entrée `lot.qt.hist`.
|
||||
- La création du `lot.qt` ouvert est déclenchée par
|
||||
`modules/purchase_trade/lot.py`, `Lot.validate`, via `createVirtualPart`
|
||||
quand aucun `lot.qt` n'existe encore pour le lot virtuel.
|
||||
- Pour l'achat, le lot virtuel alimente `lot.qt.lot_p`.
|
||||
- Pour la vente, le lot virtuel alimente `lot.qt.lot_s` avec `lot_p = None`.
|
||||
|
||||
### Modification de la quantite contractuelle
|
||||
### Modification de la quantité contractuelle
|
||||
|
||||
Etat du code verifie:
|
||||
État du code vérifié :
|
||||
|
||||
- Achat: `purchase.py`, `Line.write`.
|
||||
- Vente: `sale.py`, `SaleLine.write`.
|
||||
- La modification de `quantity_theorical` recalcule une quantite virtuelle
|
||||
cible:
|
||||
- Achat : `purchase.py`, `Line.write`.
|
||||
- Vente : `sale.py`, `SaleLine.write`.
|
||||
- La modification de `quantity_theorical` recalcule une quantité virtuelle
|
||||
cible :
|
||||
|
||||
```text
|
||||
target_quantity = quantity_theorical - somme(lots physiques convertis)
|
||||
```
|
||||
|
||||
- Si cette quantite cible devient negative, le code bloque avec
|
||||
- Si cette quantité cible devient négative, le code bloque avec
|
||||
`Please unlink or unmatch lot`.
|
||||
- Le `lot.qt` libre est ensuite resynchronise en tenant compte des lignes
|
||||
`lot.qt` deja allouees, c'est-a-dire deja matchees ou rattachees a un
|
||||
shipment:
|
||||
- Le `lot.qt` libre est ensuite resynchronisé en tenant compte des lignes
|
||||
`lot.qt` déjà allouées, c'est-à-dire déjà matchées ou rattachées à un
|
||||
shipment :
|
||||
|
||||
```text
|
||||
free_quantity = target_quantity - somme(lot.qt deja matches ou shippes)
|
||||
free_quantity = target_quantity - somme(lot.qt déjà matchés ou shippés)
|
||||
```
|
||||
|
||||
- Si `free_quantity` devient negative, le code bloque avec
|
||||
- Si `free_quantity` devient négative, le code bloque avec
|
||||
`Please unlink or unmatch lot`.
|
||||
- Si le lot virtuel ne porte pas deja la quantite cible, le code appelle
|
||||
- Si le lot virtuel ne porte pas déjà la quantité cible, le code appelle
|
||||
`vlot.set_current_quantity(target_quantity, target_quantity, 1)`.
|
||||
- Si un `lot.qt` libre existe, sa quantite est remplacee par `free_quantity`.
|
||||
- Si un `lot.qt` libre existe, sa quantité est remplacée par `free_quantity`.
|
||||
- Si aucun `lot.qt` libre n'existe et que `free_quantity > 0`, un nouveau
|
||||
`lot.qt` libre est cree.
|
||||
- Les fees de la ligne sont resynchronises apres modification.
|
||||
`lot.qt` libre est créé.
|
||||
- Les fees de la ligne sont resynchronisés après modification.
|
||||
|
||||
Point specifique achat:
|
||||
Point spécifique achat :
|
||||
|
||||
- Si `quantity_theorical` etait vide au moment de l'initialisation, le code
|
||||
prend comme baseline la quantite courante du lot virtuel pour eviter de
|
||||
- Si `quantity_theorical` était vide au moment de l'initialisation, le code
|
||||
prend comme baseline la quantité courante du lot virtuel pour éviter de
|
||||
doubler le `lot.qt` ouvert.
|
||||
|
||||
Point specifique vente:
|
||||
Point spécifique vente :
|
||||
|
||||
- Si l'ancienne `quantity_theorical` est vide, `SaleLine.write` ne lance pas
|
||||
encore cette resynchronisation.
|
||||
|
||||
### Ajout de lots physiques
|
||||
|
||||
- Wizard: `modules/purchase_trade/lot.py`, `lot.add`.
|
||||
- Methode principale: `LotQt.add_physical_lots`.
|
||||
- Creation unitaire: `LotQt.add_physical_lot`.
|
||||
- Wizard : `modules/purchase_trade/lot.py`, `lot.add`.
|
||||
- Méthode principale : `LotQt.add_physical_lots`.
|
||||
- Création unitaire : `LotQt.add_physical_lot`.
|
||||
- L'ajout part obligatoirement d'une ligne `lot.qt` issue de Lots Management.
|
||||
- Le code refuse l'ajout direct depuis un lot physique.
|
||||
- Le code refuse l'ajout physique cote vente par ce wizard et demande
|
||||
- Le code refuse l'ajout physique côté vente par ce wizard et demande
|
||||
d'utiliser `Apply matching`.
|
||||
- Le nouveau lot physique reprend le contexte de la ligne `lot.qt`:
|
||||
ligne achat, vente matchee si presente, shipment, produit, unite,
|
||||
quantites, premium et chunk key.
|
||||
- Apres creation, le code reduit la quantite de la ligne `lot.qt` source du
|
||||
total physique cree et ne laisse pas la ligne descendre sous zero.
|
||||
- La sauvegarde/validation du lot physique recalcule:
|
||||
- le lot virtuel de la ligne via `_recompute_virtual_lot`;
|
||||
- la quantite compteur de la ligne via `_recalc_line_quantity`;
|
||||
- les mouvements stock lies si necessaire;
|
||||
- les fees rattaches au lot, a la ligne ou au shipment.
|
||||
- Le nouveau lot physique reprend le contexte de la ligne `lot.qt` :
|
||||
ligne achat, vente matchée si présente, shipment, produit, unité,
|
||||
quantités, premium et chunk key.
|
||||
- Après création, le code réduit la quantité de la ligne `lot.qt` source du
|
||||
total physique créé et ne laisse pas la ligne descendre sous zéro.
|
||||
- La sauvegarde/validation du lot physique recalcule :
|
||||
- le lot virtuel de la ligne via `_recompute_virtual_lot` ;
|
||||
- la quantité compteur de la ligne via `_recalc_line_quantity` ;
|
||||
- les mouvements stock liés si nécessaire ;
|
||||
- les fees rattachés au lot, à la ligne ou au shipment.
|
||||
|
||||
### Retrait de lots physiques
|
||||
|
||||
- Wizard: `modules/purchase_trade/lot.py`, `lot.remove`.
|
||||
- Wizard : `modules/purchase_trade/lot.py`, `lot.remove`.
|
||||
- Le retrait d'un lot ouvert est interdit.
|
||||
- Si le lot physique possede un `stock.move`, ce move doit etre en etat
|
||||
- Si le lot physique possède un `stock.move`, ce move doit être en état
|
||||
`draft`.
|
||||
- Si le lot est matche ou shippe, un warning confirmable est affiche.
|
||||
- Le code supprime d'abord le move draft si present.
|
||||
- Le code restaure la quantite physique dans `lot.qt` en reutilisant le
|
||||
contexte:
|
||||
- shipment d'origine via `lot.lot_shipment_origin`;
|
||||
- lot virtuel sale via `getVlot_s()` si le lot etait matche;
|
||||
- Si le lot est matché ou shippé, un warning confirmable est affiché.
|
||||
- Le code supprime d'abord le move draft si présent.
|
||||
- Le code restaure la quantité physique dans `lot.qt` en réutilisant le
|
||||
contexte :
|
||||
- shipment d'origine via `lot.lot_shipment_origin` ;
|
||||
- lot virtuel sale via `getVlot_s()` si le lot était matché ;
|
||||
- lot virtuel purchase via `getVlot_p()` dans `updateVirtualPart`.
|
||||
- Si une ligne `lot.qt` compatible existe deja, elle est incrementee.
|
||||
- Sinon une nouvelle ligne `lot.qt` est creee.
|
||||
- La suppression du lot physique declenche ensuite le recalcul du lot virtuel,
|
||||
de la quantite compteur de ligne et des fees.
|
||||
- Si une ligne `lot.qt` compatible existe déjà, elle est incrémentée.
|
||||
- Sinon une nouvelle ligne `lot.qt` est créée.
|
||||
- La suppression du lot physique déclenche ensuite le recalcul du lot virtuel,
|
||||
de la quantité compteur de ligne et des fees.
|
||||
|
||||
### Quantite compteur de ligne
|
||||
### Historique de quantité et weighing
|
||||
|
||||
- Methode: `lot.py`, `Lot._recalc_line_quantity`.
|
||||
- Si la ligne n'a qu'un seul lot, `quantity` reprend la quantite courante de ce
|
||||
- L'historique est porté par `lot.qt.hist`.
|
||||
- `lot.qt.hist` représente les différents états de quantité d'un lot.
|
||||
- Les quantités historiques ne doivent pas être modifiées directement depuis
|
||||
la fiche `lot.lot`.
|
||||
- La vue `lot.lot` affiche `lot_hist` en lecture seule.
|
||||
- Les vues `lot.qt.hist` sont consultatives: pas d'édition directe depuis
|
||||
l'arbre ou le formulaire.
|
||||
- Le chemin fonctionnel de modification est le wizard `lot.weighing`, exposé
|
||||
par l'action `Do weighing`.
|
||||
- Le wizard écrit ou met à jour l'entrée `lot.qt.hist` correspondant au
|
||||
`lot_state` choisi, puis synchronise le lot, les quantités ouvertes et les
|
||||
fees.
|
||||
- Un lot virtuel ne doit pas recevoir de saisie de packing depuis la fiche
|
||||
lot: les champs `lot_qt` et `lot_unit` sont non éditables pour
|
||||
`lot_type = virtual`.
|
||||
|
||||
### Quantité compteur de ligne
|
||||
|
||||
- Méthode : `lot.py`, `Lot._recalc_line_quantity`.
|
||||
- Si la ligne n'a qu'un seul lot, `quantity` reprend la quantité courante de ce
|
||||
lot.
|
||||
- Si la ligne a plusieurs lots, `quantity` somme uniquement les lots physiques.
|
||||
- Cette logique correspond a la regle fonctionnelle:
|
||||
- sans physique: `quantity` suit le virtuel;
|
||||
- avec physiques: `quantity` reflete l'execute physique.
|
||||
- Cette logique correspond à la règle fonctionnelle :
|
||||
- sans physique : `quantity` suit le virtuel ;
|
||||
- avec physiques : `quantity` reflète l'exécuté physique.
|
||||
|
||||
### Tolerance: etat actuel et point a confirmer
|
||||
### Tolérance : état actuel et point à confirmer
|
||||
|
||||
Le code expose deja les tolerances sur contrats et lignes:
|
||||
Le code expose déjà les tolérances sur contrats et lignes :
|
||||
|
||||
- Achat: `purchase.purchase.tol_min`, `purchase.purchase.tol_max`,
|
||||
- Achat : `purchase.purchase.tol_min`, `purchase.purchase.tol_max`,
|
||||
`purchase.line.tol_min`, `purchase.line.tol_max`.
|
||||
- Vente: `sale.sale.tol_min`, `sale.sale.tol_max`, `sale.line.tol_min`,
|
||||
- Vente : `sale.sale.tol_min`, `sale.sale.tol_max`, `sale.line.tol_min`,
|
||||
`sale.line.tol_max`.
|
||||
- Fonctions d'affichage: `get_tol_min`, `get_tol_max`.
|
||||
- Wizard d'ajout: `lot.add.line.tol_min`, `lot.add.line.tol_max`, avec defauts
|
||||
depuis le contrat source.
|
||||
- Fonctions d'affichage : `get_tol_min`, `get_tol_max`.
|
||||
- Wizard d'ajout : `lot.add.line.tol_min`, `lot.add.line.tol_max`, avec
|
||||
défauts depuis le contrat source.
|
||||
|
||||
A confirmer / gap potentiel:
|
||||
À confirmer / gap potentiel :
|
||||
|
||||
- Dans `LotQt.add_physical_lots` et `LotQt.add_physical_lot`, je ne vois pas
|
||||
encore de controle complet qui calcule une tolerance restante globale en
|
||||
tenant compte des lots physiques deja crees.
|
||||
- La regle consultant ci-dessus de tolerance globale dynamique doit donc etre
|
||||
consideree comme cible metier a verifier/implementer avant de la marquer
|
||||
encore de contrôle complet qui calcule une tolérance restante globale en
|
||||
tenant compte des lots physiques déjà créés.
|
||||
- La règle consultant ci-dessus de tolérance globale dynamique doit donc être
|
||||
considérée comme cible métier à vérifier/implémenter avant de la marquer
|
||||
`active`.
|
||||
|
||||
### Tests proches
|
||||
@@ -229,3 +259,11 @@ A confirmer / gap potentiel:
|
||||
- `test_purchase_line_write_syncs_open_lot_qt_with_physical_lots`
|
||||
- `test_purchase_line_write_syncs_virtual_fee_quantity`
|
||||
- `test_purchase_line_write_initial_theorical_qty_does_not_double_open_lot`
|
||||
|
||||
À ajouter si cette règle devient sensible côté régression:
|
||||
|
||||
- vérifier que `lot_hist` est en lecture seule sur la fiche `lot.lot`;
|
||||
- vérifier que `Do weighing` reste capable de créer ou mettre à jour un état de
|
||||
quantité;
|
||||
- vérifier qu'un lot virtuel ne permet pas la saisie directe de `lot_qt` et
|
||||
`lot_unit`.
|
||||
|
||||
@@ -16,7 +16,7 @@ regles lisibles par les consultants des notes utiles aux developpeurs.
|
||||
## Regles par domaine
|
||||
|
||||
- [Contrats achat / vente](business/contracts.md)
|
||||
- [Lots et quantites](business/lots-and-quantities.md)
|
||||
- [Lots et quantités](business/lots-and-quantities.md) / [Lots and Quantities](business/lots-and-quantities.en.md)
|
||||
- [Matching achat / vente](business/matching.md)
|
||||
- [Shipments et execution](business/shipments-execution.md)
|
||||
- [Pricing, basis, premium](business/pricing.md)
|
||||
|
||||
Reference in New Issue
Block a user