126 lines
5.6 KiB
Markdown
126 lines
5.6 KiB
Markdown
<!-- Generated from docs_source/business by docs/tools/render_business_docs.py. -->
|
|
|
|
# SQL diagnostics for purchase_trade business rules
|
|
|
|
These scripts are read-only diagnostics for a PostgreSQL test database.
|
|
|
|
They exist to support the same business rules enforced by Python guards. The
|
|
expected workflow is:
|
|
|
|
1. write the consultant/developer rule in the thematic documentation;
|
|
2. enforce the invariant in the application code when feasible;
|
|
3. provide a read-only SQL diagnostic to audit existing data.
|
|
|
|
## quantity_consistency_checks.sql
|
|
|
|
Checks the two core lot quantity invariants documented in
|
|
`lots-and-quantities.md` and `lots-and-quantities.en.md`.
|
|
|
|
Zero `lot_qt` rows are ignored completely. They are treated as legitimate
|
|
memory of an open quantity consumed by a physical lot. This is required because
|
|
`lot_qt` represents usable open forecast and stops at zero, while the virtual
|
|
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>
|
|
|
|
The script returns rows only when it finds a potential issue.
|
|
|
|
Main columns:
|
|
|
|
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
|
<li style="margin:0.38rem 0;"><code>check_name</code>: invariant or diagnostic that failed.
|
|
</li>
|
|
<li style="margin:0.38rem 0;"><code>contract_model</code>: <code>purchase.purchase</code> or <code>sale.sale</code>.
|
|
</li>
|
|
<li style="margin:0.38rem 0;"><code>contract_id</code>: database id of the contract.
|
|
</li>
|
|
<li style="margin:0.38rem 0;"><code>contract_number</code>: purchase or sale contract number.
|
|
</li>
|
|
<li style="margin:0.38rem 0;"><code>line_id</code>: <code>purchase.line</code> or <code>sale.line</code> id depending on the check.
|
|
</li>
|
|
<li style="margin:0.38rem 0;"><code>virtual_lot_id</code>: virtual lot involved in the inconsistency.
|
|
</li>
|
|
<li style="margin:0.38rem 0;"><code>observed_value</code>: value found in the database.
|
|
</li>
|
|
<li style="margin:0.38rem 0;"><code>expected_value</code>: value required by the business rule.
|
|
</li>
|
|
<li style="margin:0.38rem 0;"><code>diff</code>: observed minus expected.
|
|
</li>
|
|
<li style="margin:0.38rem 0;"><code>detail</code>: human-readable explanation.
|
|
</li>
|
|
</ul>
|
|
|
|
Cross-category UoM rows are reported as manual-review diagnostics because the
|
|
Python code may pass explicit conversion factors that cannot be inferred safely
|
|
from SQL alone.
|
|
|
|
## fee_quantity_consistency_checks.sql
|
|
|
|
Checks the fee quantity invariant documented in `fees.md` and `fees.en.md`.
|
|
|
|
The script ignores `mode = 'ppack'` because Per packing follows a packing
|
|
quantity, not the lot net/gross weight.
|
|
|
|
For every other fee, it recomputes the expected quantity from the effective
|
|
`fee.lots`:
|
|
|
|
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
|
<li style="margin:0.38rem 0;">physical lots if at least one physical lot is linked;
|
|
</li>
|
|
<li style="margin:0.38rem 0;">otherwise virtual lots;
|
|
</li>
|
|
<li style="margin:0.38rem 0;"><code>fee.qt_state</code> when filled, capped by the contract <code>weight basis</code>;
|
|
</li>
|
|
<li style="margin:0.38rem 0;">otherwise the contract <code>weight basis</code>;
|
|
</li>
|
|
<li style="margin:0.38rem 0;">closest previous <code>lot.qt.hist</code> state by <code>lot.qt.type.sequence</code> when the exact state is missing;
|
|
</li>
|
|
<li style="margin:0.38rem 0;"><code>lot.qt.hist.quantity</code> for net fees and <code>lot.qt.hist.gross_quantity</code> for gross fees.
|
|
</li>
|
|
</ul>
|
|
|
|
The diagnostic is grouped by `fee_id`. Purchase and sale columns are displayed
|
|
only to help identify related contracts; they do not split the expected
|
|
quantity. This matters for purchase fees whose linked lots may also be matched
|
|
to several sale lines.
|
|
|
|
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/fee_quantity_consistency_checks.sql</code></pre>
|
|
|
|
The script returns rows only when it finds a potential issue.
|
|
|
|
Main columns:
|
|
|
|
<ul style="margin:0.65rem 0 1rem 1.1rem; padding-left:1rem; list-style-type:disc;">
|
|
<li style="margin:0.38rem 0;"><code>check_name</code>: failed invariant or manual-review diagnostic.
|
|
</li>
|
|
<li style="margin:0.38rem 0;"><code>fee_id</code>: <code>fee.fee</code> id.
|
|
</li>
|
|
<li style="margin:0.38rem 0;"><code>purchase_id</code> / <code>purchase_number</code>: related purchase contract when available.
|
|
</li>
|
|
<li style="margin:0.38rem 0;"><code>sale_id</code> / <code>sale_number</code>: related sale contract when available.
|
|
</li>
|
|
<li style="margin:0.38rem 0;"><code>observed_value</code>: current <code>fee.quantity</code>.
|
|
</li>
|
|
<li style="margin:0.38rem 0;"><code>expected_value</code>: recomputed quantity from effective lots.
|
|
</li>
|
|
<li style="margin:0.38rem 0;"><code>diff</code>: observed minus expected.
|
|
</li>
|
|
<li style="margin:0.38rem 0;"><code>detail</code>: human-readable explanation.
|
|
</li>
|
|
</ul>
|
|
|
|
Rows reported as `fee_quantity_uom_manual_review` need manual analysis because
|
|
SQL cannot safely infer cross-category conversion factors.
|