Bug check qt
This commit is contained in:
@@ -191,8 +191,8 @@ them with Python guards and SQL diagnostics.
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>sum(physical lots) + virtual lot = quantity_theorical</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Open forecast</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>sum(non-zero lot.qt) = max(virtual lot, 0)</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Minimum open forecast</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>sum(non-zero lot.qt) >= max(virtual lot, 0)</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -218,6 +218,14 @@ them with Python guards and SQL diagnostics.
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Ignored by checks; possible memory of a consumed forecast.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Forecast overconsumption</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Allowed after tolerance confirmation: <code>lot.qt</code> stops at zero, but the virtual lot absorbs the whole physical lot.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>lot.qt</code> greater than virtual lot</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Accepted when remaining open forecast exceeds the positive virtual lot because of physical overconsumption.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Negative virtual lot</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Allowed to compensate theoretical / executed gap; expected forecast = zero.</td>
|
||||
</tr>
|
||||
|
||||
@@ -190,8 +190,8 @@ puis les sécuriser par des checks Python et des diagnostics SQL.
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>somme(lots physiques) + lot virtuel = quantity_theorical</code></td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Forecast ouvert</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>somme(lot.qt non zéro) = max(lot virtuel, 0)</code></td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Forecast ouvert minimal</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>somme(lot.qt non zéro) >= max(lot virtuel, 0)</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -217,6 +217,14 @@ puis les sécuriser par des checks Python et des diagnostics SQL.
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Ignoré par les checks ; mémoire possible d'une prévision vidée.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Surconsommation forecast</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Autorisée après confirmation de tolérance : <code>lot.qt</code> s'arrête à zéro, mais le lot virtuel absorbe tout le physique.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;"><code>lot.qt</code> supérieur au virtuel</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Accepté si le forecast ouvert restant dépasse le virtuel positif à cause d'une surconsommation physique.</td>
|
||||
</tr>
|
||||
<tr style="border-bottom:1px solid #e0e0e0;">
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Lot virtuel négatif</td>
|
||||
<td style="vertical-align:top; padding:0.5rem 0.7rem;">Autorisé pour compenser l'écart théorique / exécuté ; forecast attendu = zéro.</td>
|
||||
</tr>
|
||||
|
||||
@@ -23,6 +23,12 @@ lot may become negative to compensate the difference between theoretical and
|
||||
executed quantity. A non-zero `lot_qt` row without both `lot_p` and `lot_s` is
|
||||
reported as anomalous.
|
||||
|
||||
When a physical lot overconsumes the selected forecast after a tolerance
|
||||
confirmation, `lot_qt` stops at zero but the virtual lot absorbs the full
|
||||
physical quantity. The SQL diagnostic therefore reports only open forecast
|
||||
lower than the positive virtual lot. Open forecast greater than the virtual lot
|
||||
is accepted as a consequence of this overconsumption rule.
|
||||
|
||||
Run it on a restored test database:
|
||||
|
||||
<pre style="background:#263238; color:#eef7ff; padding:1rem; border-radius:0.35rem; overflow:auto;"><code>\i modules/purchase_trade/docs/business/sql/quantity_consistency_checks.sql</code></pre>
|
||||
|
||||
@@ -12,12 +12,15 @@ Rules checked:
|
||||
|
||||
2. For each virtual purchase lot:
|
||||
sum(non-zero lot_qt.lot_quantity where lot_p = virtual lot)
|
||||
= virtual lot current quantity.
|
||||
must not be lower than the positive virtual lot current quantity.
|
||||
A higher sum is accepted because lot_qt stops at zero when a physical lot
|
||||
overconsumes a forecast, while the virtual lot absorbs the full physical
|
||||
quantity.
|
||||
Matched and unmatched lot_qt rows are both included.
|
||||
|
||||
3. For each virtual sale lot:
|
||||
sum(non-zero lot_qt.lot_quantity where lot_s = virtual lot)
|
||||
= virtual lot current quantity.
|
||||
must not be lower than the positive virtual lot current quantity.
|
||||
Rows linked to a purchase lot and rows without purchase lot are both
|
||||
included.
|
||||
|
||||
@@ -349,7 +352,7 @@ FROM (
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'purchase_lot_qt_equals_virtual' AS check_name,
|
||||
'purchase_lot_qt_below_virtual' AS check_name,
|
||||
'purchase.purchase' AS contract_model,
|
||||
b.contract_id,
|
||||
b.contract_number,
|
||||
@@ -360,18 +363,18 @@ FROM (
|
||||
ROUND(
|
||||
b.lot_qt_quantity
|
||||
- GREATEST(COALESCE(b.virtual_quantity, 0), 0), 5) AS diff,
|
||||
'sum non-zero lot_qt where lot_p = virtual lot must equal positive virtual lot quantity'
|
||||
'sum non-zero lot_qt where lot_p = virtual lot must not be lower than positive virtual lot quantity'
|
||||
AS detail
|
||||
FROM purchase_lot_qt_balance b, params p
|
||||
WHERE b.lot_qt_cross_category_count = 0
|
||||
AND ABS(
|
||||
AND (
|
||||
b.lot_qt_quantity
|
||||
- GREATEST(COALESCE(b.virtual_quantity, 0), 0)) > p.epsilon
|
||||
- GREATEST(COALESCE(b.virtual_quantity, 0), 0)) < -p.epsilon
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'sale_lot_qt_equals_virtual' AS check_name,
|
||||
'sale_lot_qt_below_virtual' AS check_name,
|
||||
'sale.sale' AS contract_model,
|
||||
b.contract_id,
|
||||
b.contract_number,
|
||||
@@ -382,13 +385,13 @@ FROM (
|
||||
ROUND(
|
||||
b.lot_qt_quantity
|
||||
- GREATEST(COALESCE(b.virtual_quantity, 0), 0), 5) AS diff,
|
||||
'sum non-zero lot_qt where lot_s = virtual lot must equal positive virtual lot quantity'
|
||||
'sum non-zero lot_qt where lot_s = virtual lot must not be lower than positive virtual lot quantity'
|
||||
AS detail
|
||||
FROM sale_lot_qt_balance b, params p
|
||||
WHERE b.lot_qt_cross_category_count = 0
|
||||
AND ABS(
|
||||
AND (
|
||||
b.lot_qt_quantity
|
||||
- GREATEST(COALESCE(b.virtual_quantity, 0), 0)) > p.epsilon
|
||||
- GREATEST(COALESCE(b.virtual_quantity, 0), 0)) < -p.epsilon
|
||||
|
||||
UNION ALL
|
||||
|
||||
|
||||
@@ -86,13 +86,15 @@ quantity_theorical
|
||||
| Invariant | Formula |
|
||||
| --- | --- |
|
||||
| Conservation | `sum(physical lots) + virtual lot = quantity_theorical` |
|
||||
| Open forecast | `sum(non-zero lot.qt) = max(virtual lot, 0)` |
|
||||
| Minimum open forecast | `sum(non-zero lot.qt) >= max(virtual lot, 0)` |
|
||||
|
||||
| Case | Rule |
|
||||
| --- | --- |
|
||||
| Purchase virtual lot | Sum all `lot.qt` where `lot_p = virtual lot`, with or without `lot_s`. |
|
||||
| Sale virtual lot | Sum all `lot.qt` where `lot_s = virtual lot`, with or without `lot_p`. |
|
||||
| `lot.qt = 0` | Ignored by checks; possible memory of a consumed forecast. |
|
||||
| Forecast overconsumption | Allowed after tolerance confirmation: `lot.qt` stops at zero, but the virtual lot absorbs the whole physical lot. |
|
||||
| `lot.qt` greater than virtual lot | Accepted when remaining open forecast exceeds the positive virtual lot because of physical overconsumption. |
|
||||
| Negative virtual lot | Allowed to compensate theoretical / executed gap; expected forecast = zero. |
|
||||
| Non-zero orphan `lot.qt` | Forbidden if neither `lot_p` nor `lot_s` is set. |
|
||||
|
||||
|
||||
@@ -85,13 +85,15 @@ quantity_theorical
|
||||
| Invariant | Formule |
|
||||
| --- | --- |
|
||||
| Conservation | `somme(lots physiques) + lot virtuel = quantity_theorical` |
|
||||
| Forecast ouvert | `somme(lot.qt non zéro) = max(lot virtuel, 0)` |
|
||||
| Forecast ouvert minimal | `somme(lot.qt non zéro) >= max(lot virtuel, 0)` |
|
||||
|
||||
| Cas | Règle |
|
||||
| --- | --- |
|
||||
| Lot virtuel achat | Sommer tous les `lot.qt` où `lot_p = lot virtuel`, avec ou sans `lot_s`. |
|
||||
| Lot virtuel vente | Sommer tous les `lot.qt` où `lot_s = lot virtuel`, avec ou sans `lot_p`. |
|
||||
| `lot.qt = 0` | Ignoré par les checks ; mémoire possible d'une prévision vidée. |
|
||||
| Surconsommation forecast | Autorisée après confirmation de tolérance : `lot.qt` s'arrête à zéro, mais le lot virtuel absorbe tout le physique. |
|
||||
| `lot.qt` supérieur au virtuel | Accepté si le forecast ouvert restant dépasse le virtuel positif à cause d'une surconsommation physique. |
|
||||
| Lot virtuel négatif | Autorisé pour compenser l'écart théorique / exécuté ; forecast attendu = zéro. |
|
||||
| `lot.qt` non zéro orphelin | Interdit si ni `lot_p` ni `lot_s` n'est renseigné. |
|
||||
|
||||
|
||||
@@ -21,6 +21,12 @@ lot may become negative to compensate the difference between theoretical and
|
||||
executed quantity. A non-zero `lot_qt` row without both `lot_p` and `lot_s` is
|
||||
reported as anomalous.
|
||||
|
||||
When a physical lot overconsumes the selected forecast after a tolerance
|
||||
confirmation, `lot_qt` stops at zero but the virtual lot absorbs the full
|
||||
physical quantity. The SQL diagnostic therefore reports only open forecast
|
||||
lower than the positive virtual lot. Open forecast greater than the virtual lot
|
||||
is accepted as a consequence of this overconsumption rule.
|
||||
|
||||
Run it on a restored test database:
|
||||
|
||||
```sql
|
||||
|
||||
@@ -182,12 +182,12 @@ class Lot(metaclass=PoolMeta):
|
||||
diff = (
|
||||
cls._round_consistency_quantity(open_quantity)
|
||||
- cls._round_consistency_quantity(expected_open_quantity))
|
||||
if diff:
|
||||
if diff < 0:
|
||||
raise UserError(
|
||||
cls._format_consistency_error(
|
||||
line,
|
||||
"lot.qt forecast quantity must equal virtual lot "
|
||||
"positive current quantity",
|
||||
"lot.qt forecast quantity must not be lower than "
|
||||
"virtual lot positive current quantity",
|
||||
[
|
||||
('lot_qt', open_quantity),
|
||||
('virtual', virtual_quantity),
|
||||
|
||||
Reference in New Issue
Block a user