Add lot_hist readonly

This commit is contained in:
2026-05-13 15:35:23 +02:00
parent de78da846d
commit 343fc8527c
10 changed files with 479 additions and 159 deletions

View File

@@ -24,8 +24,14 @@ class Lot(ModelSQL, ModelView):
lot_name = fields.Char("Lot") lot_name = fields.Char("Lot")
number = fields.Char("Number", readonly=True) number = fields.Char("Number", readonly=True)
lot_qt = fields.Float("Quantity",required=False) lot_qt = fields.Float("Quantity", required=False,
lot_unit = fields.Many2One('product.uom', "Unit",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_product = fields.Many2One('product.product', "Product")
lot_type = fields.Selection([ lot_type = fields.Selection([
('virtual', 'Open'), ('virtual', 'Open'),

View File

@@ -36,7 +36,7 @@
<notebook colspan="4"> <notebook colspan="4">
<page string="General" id="general"> <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_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>
<page string="Accounting" id="accounting"> <page string="Accounting" id="accounting">
<field name="pivot" widget="html_viewer" height="600" colspan="4"/> <field name="pivot" widget="html_viewer" height="600" colspan="4"/>

View File

@@ -1,11 +1,11 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<form col="4"> <form col="4">
<label name="lot"/> <label name="lot"/>
<field name="lot"/> <field name="lot" readonly="1"/>
<label name="quantity_type"/> <label name="quantity_type"/>
<field name="quantity_type"/> <field name="quantity_type" readonly="1"/>
<label name="quantity"/> <label name="quantity"/>
<field name="quantity"/> <field name="quantity" readonly="1"/>
<label name="gross_quantity"/> <label name="gross_quantity"/>
<field name="gross_quantity"/> <field name="gross_quantity" readonly="1"/>
</form> </form>

View File

@@ -1,4 +1,4 @@
<tree editable="1"> <tree>
<field name="lot"/> <field name="lot"/>
<field name="quantity_type"/> <field name="quantity_type"/>
<field name="quantity"/> <field name="quantity"/>

View File

@@ -1,4 +1,4 @@
<tree editable="1"> <tree>
<field name="lot"/> <field name="lot"/>
<field name="quantity_type"/> <field name="quantity_type"/>
<field name="quantity"/> <field name="quantity"/>

View File

@@ -5,7 +5,7 @@ Statut: `migration partielle`
## Comment chercher une regle ## Comment chercher une regle
- Contrats, dates, lieux, banques: [contracts.md](contracts.md) - 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) - Matching, Create Contracts, back-to-back: [matching.md](matching.md)
- Shipments, controllers, SLA, weight reports: [shipments-execution.md](shipments-execution.md) - Shipments, controllers, SLA, weight reports: [shipments-execution.md](shipments-execution.md)
- Pricing manuel, basis, premium, linked currency: [pricing.md](pricing.md) - Pricing manuel, basis, premium, linked currency: [pricing.md](pricing.md)

View File

@@ -1,35 +1,53 @@
# Guide de lecture des regles business # Guide de lecture des règles business
Statut: `migration partielle` 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`. module `purchase_trade`.
Chaque page doit rester lisible par deux publics: 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; de code inutile;
- les developpeurs, qui ont besoin des champs, modeles, fichiers et tests - les développeurs, qui ont besoin des champs, modèles, fichiers et tests
concernes pour appliquer la regle sans l'interpreter. 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 ```md
### BR-PT-THEME-001 - Titre court ### BR-PT-THEME-001 - Titre court
Statut: active 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: - Fichiers:
- Tests: - Tests:
- Points de vigilance: - Points de vigilance:
@@ -37,9 +55,9 @@ Texte fonctionnel, sans nom de champ si ce n'est pas necessaire.
## Sources pendant la migration ## Sources pendant la migration
Les anciennes pages ne sont pas supprimees a cette etape. Elles restent des Les anciennes pages ne sont pas supprimées à cette étape. Elles restent des
sources de verification jusqu'a ce que chaque decision soit promue dans une sources de vérification jusqu'à ce que chaque décision soit promue dans une
page thematique: page thématique:
- `modules/purchase_trade/docs/business-rules.md` - `modules/purchase_trade/docs/business-rules.md`
- `modules/purchase_trade/docs/fees.md` - `modules/purchase_trade/docs/fees.md`
@@ -48,4 +66,3 @@ page thematique:
- `modules/purchase_trade/docs/template-properties.md` - `modules/purchase_trade/docs/template-properties.md`
- `notes/business_rules.md` - `notes/business_rules.md`
- `notes/template_business_rules.md` - `notes/template_business_rules.md`

View 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`.

View File

@@ -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` 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`, Cette page consolide les anciennes règles `BR-PT-LOT-001`,
`BR-PT-LOT-002` et `BR-PT-LOT-003` autour d'une regle fonctionnelle unique: `BR-PT-LOT-002` et `BR-PT-LOT-003` autour d'une règle fonctionnelle unique :
le cycle de vie des quantites ouvertes, forecastees et physiques. 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 Lors de la création d'une ligne d'achat ou de vente, le système crée un lot
virtuel associe a cette ligne. Ce lot virtuel represente la quantite encore virtuel associé à cette ligne. Ce lot virtuel représente la quantité encore
ouverte du contrat. Sa quantite initiale reprend la quantite contractuelle ou ouverte du contrat. Sa quantité initiale reprend la quantité contractuelle ou
theorique de la ligne. théorique de la ligne.
En parallele, le systeme ajoute une ligne ouverte dans `lot.qt`. Cette ligne En parallèle, le système ajoute une ligne ouverte dans `lot.qt`. Cette ligne
sert de base aux previsions commerciales et logistiques: vente previsionnelle, sert de base aux prévisions commerciales et logistiques : vente prévisionnelle,
matching futur, transport planifie, shipment, etc. `lot.qt` porte donc le matching futur, transport planifié, shipment, etc. `lot.qt` porte donc le
forecast operationnel, tandis que le lot virtuel reste la representation forecast opérationnel, tandis que le lot virtuel reste la représentation
globale du solde ouvert dans `lot.lot`. globale du solde ouvert dans `lot.lot`.
Exemple: une quantite ouverte `P1` peut etre progressivement subdivisee dans Exemple : une quantité ouverte `P1` peut être progressivement subdivisée dans
`lot.qt` pour prevoir plusieurs ventes ou transports: `lot.qt` pour prévoir plusieurs ventes ou transports :
- `P1S1T1` - `P1S1T1`
- `P1S1T2` - `P1S1T2`
- `P1S2T3` - `P1S2T3`
- `P1S2T4` - `P1S2T4`
Ces subdivisions ne creent pas plusieurs lots virtuels pour `P1`. Elles Ces subdivisions ne créent pas plusieurs lots virtuels pour `P1`. Elles
decrivent seulement la repartition previsionnelle de la quantite ouverte. 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 de Lots Management, donc depuis une ligne `lot.qt`. Si l'utilisateur choisit
`P1S1T1`, le lot physique cree consomme ce forecast particulier. La ligne `P1S1T1`, le lot physique créé consomme ce forecast particulier. La ligne
`lot.qt` choisie est reduite, puis le lot virtuel de la ligne est recalcule `lot.qt` choisie est réduite, puis le lot virtuel de la ligne est recalculé
pour representer seulement le reliquat encore ouvert. pour représenter seulement le reliquat encore ouvert.
La regle immuable de coherence est: La règle immuable de cohérence est :
```text ```text
somme des lots physiques de la ligne + lot virtuel de la ligne 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 Cette cohérence doit rester vraie même si la quantité contractuelle est
ajustee en cours de route. La quantite contractuelle/theorique est la seule ajustée en cours de route. La quantité contractuelle/théorique est la seule
quantite saisie par l'utilisateur sur la ligne. La quantite standard de la quantité saisie par l'utilisateur sur la ligne. La quantité standard de la
ligne est un compteur: elle reprend la quantite contractuelle tant qu'il n'y a ligne est un compteur : elle reprend la quantité contractuelle tant qu'il n'y a
pas de lot physique, puis elle reflete la somme des lots physiques executes. 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 Le retrait d'un lot physique est autorisé seulement tant que son mouvement
stock n'est pas finalise. Si ce lot etait deja matche ou rattache a un 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 shipment, l'utilisateur doit confirmer l'action. Le retrait restaure la
quantite dans la ligne `lot.qt` qui portait le contexte forecast, matching ou quantité dans la ligne `lot.qt` qui portait le contexte forecast, matching ou
transport ayant permis de creer le lot physique. 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, ### Tolérance et quantités physiques
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.
Si un lot physique consomme plus ou moins que son forecast, cela doit reduire Un lot physique peut avoir une quantité différente de la prévision initiale,
ou augmenter dynamiquement la tolerance restante pour les prochains lots dans les limites de tolérance du contrat. La tolérance doit se lire comme une
physiques de la meme ligne. Aucune action d'ajout, de weighing, de suppression tolérance globale sur la ligne ou le contrat, pas comme une tolérance
ou d'ajustement contractuel ne doit permettre de sortir de la coherence globale indépendante par transport.
des quantites.
## 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`. ### Modèles et champs principaux
- Ligne vente: `sale.line`.
- Lot: `lot.lot`. - Ligne achat : `purchase.line`.
- Forecast / quantite ouverte: `lot.qt`. - Ligne vente : `sale.line`.
- Historique des quantites de lot: `lot.qt.hist`. - Lot : `lot.lot`.
- Quantite contractuelle achat: `purchase.line.quantity_theorical` - Forecast / quantité ouverte : `lot.qt`.
- Historique des quantités de lot : `lot.qt.hist`.
- Quantité contractuelle achat : `purchase.line.quantity_theorical`
(`Contractual Qt`). (`Contractual Qt`).
- Quantite contractuelle vente: `sale.line.quantity_theorical` - Quantité contractuelle vente : `sale.line.quantity_theorical`
(`Th. quantity`). (`Th. quantity`).
- Quantite compteur achat/vente: `quantity`. - Quantité compteur achat/vente : `quantity`.
- Type de lot: `lot.lot.lot_type`, valeurs `virtual` et `physic`. - Type de lot : `lot.lot.lot_type`, valeurs `virtual` et `physic`.
- Lien achat: `lot.lot.line`. - Lien achat : `lot.lot.line`.
- Lien vente: `lot.lot.sale_line`. - Lien vente : `lot.lot.sale_line`.
- Liens shipment: `lot_shipment_in`, `lot_shipment_internal`, - Liens shipment : `lot_shipment_in`, `lot_shipment_internal`,
`lot_shipment_out`. `lot_shipment_out`.
- Matching forecast achat/vente: `lot.qt.lot_p`, `lot.qt.lot_s`. - Matching forecast achat/vente : `lot.qt.lot_p`, `lot.qt.lot_s`.
- Quantite forecast: `lot.qt.lot_quantity`. - Quantité forecast : `lot.qt.lot_quantity`.
- Tolerances contrat/ligne: `tol_min`, `tol_max`, `tol_min_qt`, - Tolérances contrat/ligne : `tol_min`, `tol_max`, `tol_min_qt`,
`tol_max_qt`, `tol_min_v`, `tol_max_v`. `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`. - Achat : `modules/purchase_trade/purchase.py`, `Line.validate`.
- Vente: `modules/purchase_trade/sale.py`, `SaleLine.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 - 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 le produit n'est pas un service et que `quantity != 0`, un lot `virtual` est
cree. créé.
- Le lot virtuel recoit une premiere entree `lot.qt.hist`. - Le lot virtuel reçoit une première entrée `lot.qt.hist`.
- La creation du `lot.qt` ouvert est declenchee par - La création du `lot.qt` ouvert est déclenchée par
`modules/purchase_trade/lot.py`, `Lot.validate`, via `createVirtualPart` `modules/purchase_trade/lot.py`, `Lot.validate`, via `createVirtualPart`
quand aucun `lot.qt` n'existe encore pour le lot virtuel. quand aucun `lot.qt` n'existe encore pour le lot virtuel.
- Pour l'achat, le lot virtuel alimente `lot.qt.lot_p`. - 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`. - 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`. - Achat : `purchase.py`, `Line.write`.
- Vente: `sale.py`, `SaleLine.write`. - Vente : `sale.py`, `SaleLine.write`.
- La modification de `quantity_theorical` recalcule une quantite virtuelle - La modification de `quantity_theorical` recalcule une quantité virtuelle
cible: cible :
```text ```text
target_quantity = quantity_theorical - somme(lots physiques convertis) 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`. `Please unlink or unmatch lot`.
- Le `lot.qt` libre est ensuite resynchronise en tenant compte des lignes - Le `lot.qt` libre est ensuite resynchronisé en tenant compte des lignes
`lot.qt` deja allouees, c'est-a-dire deja matchees ou rattachees a un `lot.qt` déjà allouées, c'est-à-dire déjà matchées ou rattachées à un
shipment: shipment :
```text ```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`. `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)`. `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 - Si aucun `lot.qt` libre n'existe et que `free_quantity > 0`, un nouveau
`lot.qt` libre est cree. `lot.qt` libre est créé.
- Les fees de la ligne sont resynchronises apres modification. - 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 - Si `quantity_theorical` était vide au moment de l'initialisation, le code
prend comme baseline la quantite courante du lot virtuel pour eviter de prend comme baseline la quantité courante du lot virtuel pour éviter de
doubler le `lot.qt` ouvert. doubler le `lot.qt` ouvert.
Point specifique vente: Point spécifique vente :
- Si l'ancienne `quantity_theorical` est vide, `SaleLine.write` ne lance pas - Si l'ancienne `quantity_theorical` est vide, `SaleLine.write` ne lance pas
encore cette resynchronisation. encore cette resynchronisation.
### Ajout de lots physiques ### Ajout de lots physiques
- Wizard: `modules/purchase_trade/lot.py`, `lot.add`. - Wizard : `modules/purchase_trade/lot.py`, `lot.add`.
- Methode principale: `LotQt.add_physical_lots`. - Méthode principale : `LotQt.add_physical_lots`.
- Creation unitaire: `LotQt.add_physical_lot`. - Création unitaire : `LotQt.add_physical_lot`.
- L'ajout part obligatoirement d'une ligne `lot.qt` issue de Lots Management. - 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 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`. d'utiliser `Apply matching`.
- Le nouveau lot physique reprend le contexte de la ligne `lot.qt`: - Le nouveau lot physique reprend le contexte de la ligne `lot.qt` :
ligne achat, vente matchee si presente, shipment, produit, unite, ligne achat, vente matchée si présente, shipment, produit, unité,
quantites, premium et chunk key. quantités, premium et chunk key.
- Apres creation, le code reduit la quantite de la ligne `lot.qt` source du - Après création, le code réduit la quantité de la ligne `lot.qt` source du
total physique cree et ne laisse pas la ligne descendre sous zero. total physique créé et ne laisse pas la ligne descendre sous zéro.
- La sauvegarde/validation du lot physique recalcule: - La sauvegarde/validation du lot physique recalcule :
- le lot virtuel de la ligne via `_recompute_virtual_lot`; - le lot virtuel de la ligne via `_recompute_virtual_lot` ;
- la quantite compteur de la ligne via `_recalc_line_quantity`; - la quantité compteur de la ligne via `_recalc_line_quantity` ;
- les mouvements stock lies si necessaire; - les mouvements stock liés si nécessaire ;
- les fees rattaches au lot, a la ligne ou au shipment. - les fees rattachés au lot, à la ligne ou au shipment.
### Retrait de lots physiques ### 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. - 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`. `draft`.
- Si le lot est matche ou shippe, un warning confirmable est affiche. - Si le lot est matché ou shippé, un warning confirmable est affiché.
- Le code supprime d'abord le move draft si present. - Le code supprime d'abord le move draft si présent.
- Le code restaure la quantite physique dans `lot.qt` en reutilisant le - Le code restaure la quantité physique dans `lot.qt` en réutilisant le
contexte: contexte :
- shipment d'origine via `lot.lot_shipment_origin`; - shipment d'origine via `lot.lot_shipment_origin` ;
- lot virtuel sale via `getVlot_s()` si le lot etait matche; - lot virtuel sale via `getVlot_s()` si le lot était matché ;
- lot virtuel purchase via `getVlot_p()` dans `updateVirtualPart`. - lot virtuel purchase via `getVlot_p()` dans `updateVirtualPart`.
- Si une ligne `lot.qt` compatible existe deja, elle est incrementee. - Si une ligne `lot.qt` compatible existe déjà, elle est incrémentée.
- Sinon une nouvelle ligne `lot.qt` est creee. - Sinon une nouvelle ligne `lot.qt` est créée.
- La suppression du lot physique declenche ensuite le recalcul du lot virtuel, - La suppression du lot physique déclenche ensuite le recalcul du lot virtuel,
de la quantite compteur de ligne et des fees. 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`. - L'historique est porté par `lot.qt.hist`.
- Si la ligne n'a qu'un seul lot, `quantity` reprend la quantite courante de ce - `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. lot.
- Si la ligne a plusieurs lots, `quantity` somme uniquement les lots physiques. - Si la ligne a plusieurs lots, `quantity` somme uniquement les lots physiques.
- Cette logique correspond a la regle fonctionnelle: - Cette logique correspond à la règle fonctionnelle :
- sans physique: `quantity` suit le virtuel; - sans physique : `quantity` suit le virtuel ;
- avec physiques: `quantity` reflete l'execute physique. - 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`. `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`. `sale.line.tol_max`.
- Fonctions d'affichage: `get_tol_min`, `get_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 - Wizard d'ajout : `lot.add.line.tol_min`, `lot.add.line.tol_max`, avec
depuis le contrat source. 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 - 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 encore de contrôle complet qui calcule une tolérance restante globale en
tenant compte des lots physiques deja crees. tenant compte des lots physiques déjà créés.
- La regle consultant ci-dessus de tolerance globale dynamique doit donc etre - La règle consultant ci-dessus de tolérance globale dynamique doit donc être
consideree comme cible metier a verifier/implementer avant de la marquer considérée comme cible métier à vérifier/implémenter avant de la marquer
`active`. `active`.
### Tests proches ### 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_open_lot_qt_with_physical_lots`
- `test_purchase_line_write_syncs_virtual_fee_quantity` - `test_purchase_line_write_syncs_virtual_fee_quantity`
- `test_purchase_line_write_initial_theorical_qty_does_not_double_open_lot` - `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`.

View File

@@ -16,7 +16,7 @@ regles lisibles par les consultants des notes utiles aux developpeurs.
## Regles par domaine ## Regles par domaine
- [Contrats achat / vente](business/contracts.md) - [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) - [Matching achat / vente](business/matching.md)
- [Shipments et execution](business/shipments-execution.md) - [Shipments et execution](business/shipments-execution.md)
- [Pricing, basis, premium](business/pricing.md) - [Pricing, basis, premium](business/pricing.md)