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")
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'),

View File

@@ -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"/>

View File

@@ -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>

View File

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

View File

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

View File

@@ -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)

View File

@@ -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`

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

View File

@@ -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)