Bug Pnl packing

This commit is contained in:
2026-07-17 07:28:35 +02:00
parent 39afd9dc32
commit 0272fc70bc
3 changed files with 910 additions and 13 deletions

View File

@@ -0,0 +1,861 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>User Guide - Coffee Features</title>
<style>
:root {
--ink: #17333a;
--muted: #66797d;
--line: #d9e3e1;
--panel: #f6faf9;
--accent: #0d7180;
--accent-soft: #e8f4f3;
--warn: #8a5a00;
--bad: #8a1f17;
--good: #126a38;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: Arial, Helvetica, sans-serif;
color: var(--ink);
background: #fff;
line-height: 1.55;
}
header {
padding: 36px 44px 26px;
background: #f1f7f6;
border-bottom: 1px solid var(--line);
}
main {
max-width: 1180px;
padding: 28px 44px 56px;
}
h1 {
margin: 0 0 8px;
font-size: 32px;
line-height: 1.15;
}
h2 {
margin: 34px 0 12px;
padding-bottom: 8px;
font-size: 22px;
border-bottom: 1px solid var(--line);
}
h3 {
margin: 24px 0 8px;
font-size: 17px;
}
h4 {
margin: 18px 0 6px;
font-size: 15px;
}
p { margin: 8px 0 12px; }
ul, ol { margin: 8px 0 16px 24px; padding: 0; }
li { margin: 5px 0; }
table {
width: 100%;
border-collapse: collapse;
margin: 12px 0 20px;
font-size: 14px;
}
th, td {
border: 1px solid var(--line);
padding: 9px 10px;
vertical-align: top;
text-align: left;
}
th {
background: var(--panel);
color: #24484f;
}
code {
padding: 1px 4px;
border-radius: 4px;
background: var(--accent-soft);
color: #064d58;
font-family: Consolas, Monaco, monospace;
font-size: 0.95em;
}
.lead {
max-width: 920px;
color: var(--muted);
font-size: 16px;
}
.toc {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 10px;
margin: 22px 0 4px;
}
.toc a {
display: block;
padding: 10px 12px;
border: 1px solid var(--line);
border-radius: 6px;
color: var(--accent);
text-decoration: none;
background: #fff;
}
.note, .warning, .success {
margin: 14px 0;
padding: 12px 14px;
border-left: 4px solid var(--accent);
background: var(--panel);
}
.warning {
border-left-color: var(--warn);
background: #fff8e8;
}
.success {
border-left-color: var(--good);
background: #ecf8f1;
}
.badge {
display: inline-block;
margin: 0 4px 4px 0;
padding: 2px 7px;
border-radius: 999px;
background: var(--accent-soft);
color: #075662;
font-size: 12px;
font-weight: bold;
}
.path {
font-weight: bold;
color: #0a5965;
}
.small { color: var(--muted); font-size: 13px; }
@media print {
header, main { padding-left: 22px; padding-right: 22px; }
.toc a { break-inside: avoid; }
}
</style>
</head>
<body>
<header>
<h1>User Guide - Coffee Features</h1>
<p class="lead">
This guide explains how to activate and use the coffee extensions in the CTRM:
coffee specifications on purchase and sale lines, sample tracking, laboratory
analyses, cupping sessions with blind cups, scoring criteria and quality decisions.
</p>
<p class="small">Documented version: purchase_trade coffee implementation, updated 17/07/2026.</p>
</header>
<main>
<nav class="toc" aria-label="Table of contents">
<a href="#overview">1. Functional overview</a>
<a href="#activation">2. Activating coffee mode</a>
<a href="#line">3. Coffee Quality tab on trade lines</a>
<a href="#packing">4. Packing and market data on trade lines</a>
<a href="#samples">5. Coffee samples</a>
<a href="#lab">6. Laboratory analyses</a>
<a href="#cupping">7. Cupping sessions</a>
<a href="#scoring">8. Cupping results and criteria scoring</a>
<a href="#decisions">9. Quality status and recommended decisions</a>
<a href="#routine">10. Recommended daily routine</a>
<a href="#troubleshooting">11. Troubleshooting and common questions</a>
</nav>
<section id="overview">
<h2>1. Functional overview</h2>
<p>
The coffee functionality adds a quality workflow around physical purchase
and sale contracts. It is designed to keep the commercial contract, the
sample lifecycle, lab results and cupping decisions connected without
forcing non-coffee contracts to use any coffee-specific fields.
</p>
<p>The workflow is built around these objects:</p>
<table>
<thead>
<tr>
<th>Object</th>
<th>Purpose</th>
<th>Where users usually access it</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>purchase.line</code> / <code>sale.line</code></td>
<td>Stores the expected coffee profile and target quality thresholds.</td>
<td>Purchase or Sale line, tab <span class="path">Coffee Quality</span>.</td>
</tr>
<tr>
<td><code>coffee.sample</code></td>
<td>Tracks a physical or logical sample linked to a contract line.</td>
<td>From the line tab, or menu <span class="path">Coffee / Samples</span>.</td>
</tr>
<tr>
<td><code>coffee.lab.analysis</code></td>
<td>Tracks a laboratory analysis requested for a sample.</td>
<td>From a sample, or menu <span class="path">Coffee / Lab Analyses</span>.</td>
</tr>
<tr>
<td><code>coffee.cupping.session</code></td>
<td>Groups samples into a tasting session and generates blind cups.</td>
<td>Menu <span class="path">Coffee / Cupping Sessions</span>.</td>
</tr>
<tr>
<td><code>coffee.cupping.result</code></td>
<td>Stores one cupper's evaluation for one cup.</td>
<td>Inside a cup generated by a cupping session.</td>
</tr>
<tr>
<td><code>coffee.cupping.result.line</code></td>
<td>Stores one scored criterion within a cupping result.</td>
<td>Inside a result, in the <span class="path">Criteria</span> list.</td>
</tr>
</tbody>
</table>
<div class="note">
The coffee process is advisory by design. It calculates quality status,
recommended decisions and warnings, but the user remains responsible for
approving, rejecting or requesting another cup.
</div>
</section>
<section id="activation">
<h2>2. Activating coffee mode</h2>
<p>
Coffee features are optional. They are hidden until the trade configuration
enables them.
</p>
<ol>
<li>Open the trade configuration.</li>
<li>Go to the trade options area.</li>
<li>Enable <code>Active coffee compatibility</code>.</li>
<li>Save the configuration.</li>
</ol>
<p>Once enabled, the following menus are available under Purchase and Sale:</p>
<p>
<span class="badge">Coffee / Samples</span>
<span class="badge">Coffee / Lab Analyses</span>
<span class="badge">Coffee / Cupping Sessions</span>
<span class="badge">Coffee / Cupping Criteria</span>
</p>
<div class="warning">
If the option is disabled, coffee menus and the Coffee Quality page on
purchase and sale lines are removed from the UI. Existing data remains in
the database, but it is not shown to users.
</div>
</section>
<section id="line">
<h2>3. Coffee Quality tab on purchase and sale lines</h2>
<p>
The <span class="path">Coffee Quality</span> tab on a trade line stores
the expected quality profile for the coffee and the thresholds used to
evaluate samples.
</p>
<table>
<thead>
<tr>
<th>Area</th>
<th>Main fields</th>
<th>How it is used</th>
</tr>
</thead>
<tbody>
<tr>
<td>Quality summary</td>
<td><code>Coffee quality</code>, <code>Coffee origin</code>, <code>Coffee type</code>, <code>Coffee process</code></td>
<td>Identifies the coffee and displays the aggregated quality state of linked samples.</td>
</tr>
<tr>
<td>Coffee identity</td>
<td><code>Variety</code>, <code>Crop year</code>, <code>Screen size</code></td>
<td>Completes the commercial and physical description of the product.</td>
</tr>
<tr>
<td>Quality targets</td>
<td><code>Moisture max %</code>, <code>Defects max</code>, <code>Cup score min</code></td>
<td>Defines the thresholds used by lab analysis and sample quality evaluation.</td>
</tr>
<tr>
<td>Coffee samples</td>
<td>One2Many list of linked samples</td>
<td>Creates or reviews all samples linked to the trade line.</td>
</tr>
</tbody>
</table>
<p>
The line's <code>Attributes Name</code> can display the coffee attributes
in a compact way, for example origin, type, process, variety, crop,
screen, moisture, defects and cup score.
</p>
</section>
<section id="packing">
<h2>4. Packing and market data on trade lines</h2>
<p>
The trade line also contains coffee operational fields in the general tab.
These fields are not part of the sample workflow, but they are useful for
physical quantity tracking and market hedging.
</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Meaning</th>
<th>Operational effect</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Packages</code></td>
<td>Number of bags, sacks or packages.</td>
<td>Copied to the virtual lot as <code>lot_qt</code> when the line is validated.</td>
</tr>
<tr>
<td><code>Package unit</code></td>
<td>UoM used to describe the package, for example Bag60 or Bag90.</td>
<td>Copied to the virtual lot as <code>lot_unit</code>.</td>
</tr>
<tr>
<td><code>Packing description</code></td>
<td>Free text used for readable contract or logistics descriptions.</td>
<td>Informational only.</td>
</tr>
<tr>
<td><code>Market reference</code></td>
<td>Price curve used as the market reference.</td>
<td>Used by pricing, valuation and auto-hedging rules.</td>
</tr>
<tr>
<td><code>Market price</code> / <code>Market delta</code></td>
<td>Market entry data used for pricing or hedge generation.</td>
<td>May be copied to a generated derivative entry price.</td>
</tr>
</tbody>
</table>
<div class="note">
The default virtual lot is created when the line is validated. The package
values are synchronized after creation and are also refreshed if the
packing values are later changed and the line is saved again.
</div>
</section>
<section id="samples">
<h2>5. Coffee samples</h2>
<p>
A coffee sample can be created from the <span class="path">Coffee Quality</span>
tab of a purchase or sale line, or from the menu
<span class="path">Coffee / Samples</span>. Creating it from a line is
preferred because the direction and line link are filled automatically.
</p>
<h3>Sample identity</h3>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Reference</code></td>
<td>Internal or external sample reference.</td>
</tr>
<tr>
<td><code>Direction</code></td>
<td>Purchase or Sale. It is inferred when the sample is created from a trade line.</td>
</tr>
<tr>
<td><code>Sample Type</code></td>
<td>Offer, Pre-shipment, Shipment, Arrival, Stock, Customer or Retained sample.</td>
</tr>
<tr>
<td><code>Purchase Line</code> / <code>Sale Line</code></td>
<td>The contract line the sample belongs to.</td>
</tr>
<tr>
<td><code>Lot</code></td>
<td>Optional physical or virtual lot reference.</td>
</tr>
<tr>
<td><code>Product</code>, <code>Party</code>, <code>Origin</code>, <code>Coffee type</code></td>
<td>Computed from the linked trade line and used for sample review and cupping.</td>
</tr>
</tbody>
</table>
<h3>Storage and lifecycle</h3>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Requested Date</code></td>
<td>Defaults to today when a sample is created.</td>
</tr>
<tr>
<td><code>Received Date</code></td>
<td>Filled when the sample is received, or manually entered if needed.</td>
</tr>
<tr>
<td><code>Shelf life (days)</code></td>
<td>Defaults to 90 days.</td>
</tr>
<tr>
<td><code>Expiry Date</code></td>
<td>Calculated from requested or received date plus shelf life when not manually set.</td>
</tr>
<tr>
<td><code>Quantity</code>, <code>Unit</code>, <code>Sample location</code></td>
<td>Used to track the physical sample stored in the office, lab or warehouse.</td>
</tr>
</tbody>
</table>
<h3>Sample workflow</h3>
<p>
Sample buttons are displayed depending on the current state. The typical
workflow is:
</p>
<p>
<span class="badge">Requested</span>
<span class="badge">Received</span>
<span class="badge">Sent to lab</span>
<span class="badge">Under review</span>
<span class="badge">Approved / Rejected</span>
<span class="badge">Expired</span>
<span class="badge">Archived</span>
</p>
<table>
<thead>
<tr>
<th>Button</th>
<th>Available from</th>
<th>Effect</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Receive</code></td>
<td>Requested</td>
<td>Moves the sample to received and fills <code>Received Date</code> if it is empty.</td>
</tr>
<tr>
<td><code>Send to lab</code></td>
<td>Received</td>
<td>Moves the sample to sent to lab and creates a lab analysis if a lab is selected.</td>
</tr>
<tr>
<td><code>Review</code></td>
<td>Received or sent to lab</td>
<td>Moves the sample to quality review.</td>
</tr>
<tr>
<td><code>Evaluate quality</code></td>
<td>Received, sent to lab or under review</td>
<td>Computes the recommended decision from lab and cupping data.</td>
</tr>
<tr>
<td><code>Approve</code> / <code>Reject</code></td>
<td>Under review, or other reviewable states depending on the button visibility</td>
<td>Records the final decision and decision date.</td>
</tr>
<tr>
<td><code>Expire</code></td>
<td>Active states</td>
<td>Forces the sample into expired state.</td>
</tr>
<tr>
<td><code>Archive</code></td>
<td>Approved, rejected or expired</td>
<td>Moves the sample out of active follow-up.</td>
</tr>
</tbody>
</table>
<div class="warning">
A sample is considered "expiring soon" when its expiry date is within the
next 14 days. Expired samples continue to affect the line's coffee quality
status until they are handled.
</div>
</section>
<section id="lab">
<h2>6. Laboratory analyses</h2>
<p>
A lab analysis records the external or internal laboratory control of a
sample. It can be created from a sample or from the global lab analyses
menu.
</p>
<h3>Creating a lab analysis from a sample</h3>
<ol>
<li>Open the sample.</li>
<li>Enable <code>Delegated to lab</code> if needed.</li>
<li>Select a <code>Lab</code> and optionally a <code>Lab Due Date</code>.</li>
<li>Click <code>Send to lab</code>.</li>
</ol>
<p>
The system creates a new active lab analysis only if the sample does not
already have an active one.
</p>
<h3>Lab analysis workflow</h3>
<table>
<thead>
<tr>
<th>State</th>
<th>Meaning</th>
<th>Common action</th>
</tr>
</thead>
<tbody>
<tr>
<td>Draft</td>
<td>The request is being prepared.</td>
<td>Fill lab, due date and requested date.</td>
</tr>
<tr>
<td>Sent</td>
<td>The sample has been sent to the lab.</td>
<td>Wait for the report.</td>
</tr>
<tr>
<td>Received</td>
<td>The lab report has been received.</td>
<td>Enter moisture, defects, cup score and summary.</td>
</tr>
<tr>
<td>Accepted</td>
<td>The lab result is accepted.</td>
<td>Use it for sample evaluation.</td>
</tr>
<tr>
<td>Rejected</td>
<td>The lab result is rejected or invalid.</td>
<td>Request another analysis if needed.</td>
</tr>
</tbody>
</table>
<p>
The computed field <code>Compliant</code> compares the lab values against
the target thresholds on the linked purchase or sale line:
</p>
<ul>
<li><code>Moisture %</code> must be less than or equal to <code>Moisture max %</code>.</li>
<li><code>Defects count</code> must be less than or equal to <code>Defects max</code>.</li>
<li><code>Cup score</code> must be greater than or equal to <code>Cup score min</code>.</li>
</ul>
</section>
<section id="cupping">
<h2>7. Cupping sessions</h2>
<p>
A cupping session is a tasting event. It groups samples, prepares blind
cups and lets cuppers enter one or more results per cup.
</p>
<h3>Before creating a session: define criteria</h3>
<p>
Go to <span class="path">Coffee / Cupping Criteria</span> and create the
scoring criteria used by cuppers. Each criterion can have:
</p>
<ul>
<li>a sequence used for display order;</li>
<li>a name;</li>
<li>a criterion type, if used by your local process;</li>
<li>a maximum score;</li>
<li>a flag telling whether the score must be subtracted from the total.</li>
</ul>
<p>
Subtractive criteria are useful for defects or penalties. When a result
total is calculated, normal criteria add to the total and subtractive
criteria reduce it.
</p>
<h3>Creating the session</h3>
<ol>
<li>Open <span class="path">Coffee / Cupping Sessions</span>.</li>
<li>Create a new session.</li>
<li>Fill <code>Name</code>, <code>Date</code>, <code>Responsible</code> and <code>Location</code>.</li>
<li>Check <code>Cup capacity</code>. The default is 24.</li>
<li>Check <code>Cups per sample</code>. The default is 3.</li>
<li>Add samples to the <code>Samples</code> list.</li>
</ol>
<p>
A session line is a link between one session and one sample. The line
also stores the sample's cupping decision: pending, approved, re-cup or
rejected.
</p>
<h3>Adding a sample from a sample record</h3>
<p>
You can also open a coffee sample and go to the <span class="path">Cupping sessions</span>
tab. When you add a row there, the sample is already known, but you must
choose the session. This creates the same session/sample link as adding
the sample from the cupping session screen.
</p>
<h3>The session workflow</h3>
<table>
<thead>
<tr>
<th>State</th>
<th>Button</th>
<th>What happens</th>
</tr>
</thead>
<tbody>
<tr>
<td>Draft</td>
<td><code>Prepare</code></td>
<td>Checks capacity, sorts samples and creates missing cups with blind codes.</td>
</tr>
<tr>
<td>Prepared</td>
<td><code>Start</code></td>
<td>Moves the session to in progress. Cuppers can enter results.</td>
</tr>
<tr>
<td>In progress</td>
<td><code>Done</code></td>
<td>Marks the tasting session as completed.</td>
</tr>
<tr>
<td>Draft, Prepared or In progress</td>
<td><code>Cancel</code></td>
<td>Cancels the session.</td>
</tr>
<tr>
<td>Cancelled</td>
<td><code>Draft</code></td>
<td>Reopens the session in draft state.</td>
</tr>
</tbody>
</table>
<h3>What Prepare does</h3>
<p>
<code>Prepare</code> is the important operational step. The system:
</p>
<ul>
<li>calculates the requested number of cups from each session line;</li>
<li>blocks the preparation if the total exceeds <code>Cup capacity</code>;</li>
<li>sorts samples by coffee type, origin, reference and id;</li>
<li>assigns the sequence on each session/sample line;</li>
<li>creates any missing cups;</li>
<li>generates blind codes such as <code>S01-C1</code>, <code>S01-C2</code>, <code>S02-C1</code>.</li>
</ul>
<p>
The default tasting order proposes arabicas first, blends and other
coffees next, and robustas at the end.
</p>
<div class="note">
If you change the number of cups per sample after preparation, running
Prepare again creates only the missing cups. Existing cups are not deleted.
</div>
<h3>Blind information</h3>
<p>
Cuppers see operational tasting information such as origin and coffee type.
They do not need to see the full purchase/sale contract, supplier or
customer information during the tasting.
</p>
</section>
<section id="scoring">
<h2>8. Cupping results and criteria scoring</h2>
<p>
Once cups exist, users enter tasting results inside each cup.
</p>
<h3>Data hierarchy</h3>
<ol>
<li>A <code>Cupping Session</code> contains session/sample lines.</li>
<li>Each session/sample line contains generated <code>Cups</code>.</li>
<li>Each cup can contain one or more <code>Results</code>.</li>
<li>Each result contains scored <code>Criteria</code> lines.</li>
</ol>
<h3>Entering a result</h3>
<ol>
<li>Open a cupping session.</li>
<li>Open the relevant sample line.</li>
<li>Open the relevant cup.</li>
<li>Add a result.</li>
<li>Select the <code>Cupper</code>.</li>
<li>Enter the date if it differs from today.</li>
<li>Add criteria lines and enter scores.</li>
<li>Add notes if needed.</li>
</ol>
<p>
The field <code>Total score</code> is computed from the result lines. It
is empty until at least one criterion has a score.
</p>
<h3>Criteria lines</h3>
<table>
<thead>
<tr>
<th>Field</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Criterion</code></td>
<td>The scoring criterion configured in <span class="path">Coffee / Cupping Criteria</span>.</td>
</tr>
<tr>
<td><code>Score</code></td>
<td>The numeric score entered by the cupper.</td>
</tr>
<tr>
<td><code>Defect found</code></td>
<td>Marks the criterion as identifying a defect.</td>
</tr>
<tr>
<td><code>Notes</code></td>
<td>Free tasting notes for this criterion.</td>
</tr>
</tbody>
</table>
<h3>Score consolidation</h3>
<p>
Scores are consolidated upwards:
</p>
<ul>
<li>Result total = sum of its criteria, with subtractive criteria deducted.</li>
<li>Cup average = average of result totals entered for that cup.</li>
<li>Session/sample average = average of all result totals across the line's cups.</li>
<li>Sample cupping average = average of the cupping results linked to the sample.</li>
</ul>
<div class="warning">
A criteria line requires a criterion. If the Add button appears to do
nothing, check that the Criteria list is configured with both tree and
form views and that cupping criteria exist.
</div>
</section>
<section id="decisions">
<h2>9. Quality status and recommended decisions</h2>
<p>
Quality status is computed from direct sample values, lab analysis values
and cupping information. The system compares available values with the
thresholds stored on the purchase or sale line.
</p>
<table>
<thead>
<tr>
<th>Indicator</th>
<th>Calculation / logic</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Quality status</code></td>
<td>Pass, warning, fail or pending depending on available checks and cupping decisions.</td>
</tr>
<tr>
<td><code>Recommended decision</code></td>
<td>Pass recommends approve, fail recommends reject, warning recommends re-cup, otherwise pending.</td>
</tr>
<tr>
<td><code>Lab analysis status</code></td>
<td>Shows the status of the latest active lab analysis.</td>
</tr>
<tr>
<td><code>Cupping score</code></td>
<td>Uses the average cupping result when available.</td>
</tr>
</tbody>
</table>
<p>
Cupping session line decisions have a direct impact:
</p>
<ul>
<li>If a linked cupping line is marked <code>rejected</code>, the sample quality can fail.</li>
<li>If a linked cupping line is marked <code>re_cup</code>, the sample quality can become warning.</li>
<li>If the values are compliant and no warning/rejection exists, the recommended decision can be approve.</li>
</ul>
<p>
On purchase and sale lines, the computed coffee quality summarizes all
linked samples. Rejected, expired or warning samples are therefore visible
directly on the contract line.
</p>
</section>
<section id="routine">
<h2>10. Recommended daily routine</h2>
<ol>
<li>Review <span class="path">Coffee / Samples / Expiring soon</span>.</li>
<li>Review <span class="path">Coffee / Samples / Pending</span>.</li>
<li>Check lab analyses waiting for reports.</li>
<li>Create or prepare upcoming cupping sessions.</li>
<li>After cupping, enter result criteria and session/sample decisions.</li>
<li>After lab or cupping data is entered, use <code>Evaluate quality</code> on the sample.</li>
<li>Approve, reject or request a re-cup according to the quality process.</li>
<li>Archive completed samples when they no longer need active follow-up.</li>
</ol>
<div class="success">
A simple full flow is: create sample from the trade line, receive it,
send it to the lab and/or add it to a cupping session, enter results,
evaluate quality, then approve or reject.
</div>
</section>
<section id="troubleshooting">
<h2>11. Troubleshooting and common questions</h2>
<h3>The Coffee Quality tab is not visible</h3>
<p>
Check that <code>Active coffee compatibility</code> is enabled in trade
configuration and that the view has been reloaded after saving.
</p>
<h3>The Add button does nothing in a One2Many list</h3>
<p>
This usually means that the child record has another required parent
field that is not known from the current screen. Examples:
</p>
<ul>
<li>From a sample, adding a cupping session line requires selecting the <code>Session</code>.</li>
<li>From a result, adding a criteria line requires selecting the <code>Criterion</code>.</li>
</ul>
<p>
The current views provide form popups for these cases. If the popup does
not appear, reload the client after updating the module views.
</p>
<h3>No cups are created</h3>
<p>
Cups are created by the <code>Prepare</code> button on the cupping
session. Check that the session has sample lines and that the requested
number of cups does not exceed the session capacity.
</p>
<h3>A sample remains pending</h3>
<p>
The system can only recommend a decision when it has enough data to
compare against the trade line thresholds. Check moisture, defects,
cupping score and lab analysis status.
</p>
<h3>The result total does not match the simple sum</h3>
<p>
Some criteria may be configured as subtractive. In that case their score
is deducted from the total instead of added.
</p>
<h3>Prepare blocks the session</h3>
<p>
The total requested cups are above <code>Cup capacity</code>. Increase
capacity, reduce <code>Cups per sample</code>, or remove some samples.
</p>
</section>
</main>
</body>
</html>

View File

@@ -3268,6 +3268,46 @@ class PurchaseTradeTestCase(ModuleTestCase):
'shipment_in': shipment.id,
}])
def test_ppack_fee_valuation_uses_fee_packing_not_lot_packing(self):
'per packing fee valuation keeps the fee quantity and amount'
Valuation = valuation_module.ValuationBase
fee = SimpleNamespace(
mode='ppack',
quantity=Decimal('2'),
price=Decimal('2800'),
amount=Decimal('5600'),
)
lot = Mock(lot_qt=Decimal('440'))
segment = {'quantity': Decimal('26.4'), 'share': None}
qty = Valuation._fee_segment_quantity(fee, lot, segment)
price, amount = Valuation._fee_segment_amount(
fee, lot, qty, Decimal('-1'), segment)
self.assertEqual(qty, Decimal('2'))
self.assertEqual(price, Decimal('2800'))
self.assertEqual(amount, Decimal('-5600'))
def test_ppack_fee_valuation_splits_fee_packing_by_segment_share(self):
'per packing fee valuation prorates fee amount on segment shares'
Valuation = valuation_module.ValuationBase
fee = SimpleNamespace(
mode='ppack',
quantity=Decimal('2'),
price=Decimal('2800'),
amount=Decimal('5600'),
)
lot = Mock(lot_qt=Decimal('440'))
segment = {'quantity': Decimal('13.2'), 'share': Decimal('0.5')}
qty = Valuation._fee_segment_quantity(fee, lot, segment)
price, amount = Valuation._fee_segment_amount(
fee, lot, qty, Decimal('-1'), segment)
self.assertEqual(qty, Decimal('1.00000'))
self.assertEqual(price, Decimal('2800'))
self.assertEqual(amount, Decimal('-2800.0'))
def test_snapshot_identity_prefers_sale_line_over_purchase_line(self):
'valuation snapshot identity ignores purchase line when sale line exists'
Valuation = Pool().get('valuation.valuation')

View File

@@ -1278,11 +1278,8 @@ class ValuationBase(ModelSQL):
@classmethod
def _fee_amount_for_lot_or_zero(cls, fee, lot):
if fee.mode == 'ppack' and getattr(lot, 'lot_qt', None):
return abs(round(
Decimal(fee.price or 0)
* Decimal(str(lot.lot_qt or 0)),
2))
if fee.mode == 'ppack':
return cls._fee_amount_or_zero(fee)
if fee.mode == 'rate':
line = fee.line or getattr(fee, 'sale_line', None)
if line and line.estimated_date:
@@ -1305,16 +1302,15 @@ class ValuationBase(ModelSQL):
@classmethod
def _fee_segment_quantity(cls, fee, lot, segment):
quantity = segment.get('quantity')
if quantity is None:
qty = round(lot.get_current_quantity_converted(), 5)
if fee.mode == 'ppack' and getattr(lot, 'lot_qt', None):
qty = Decimal(str(lot.lot_qt or 0))
return qty
if fee.mode == 'ppack' and getattr(lot, 'lot_qt', None):
if fee.mode == 'ppack':
fee_quantity = Decimal(str(getattr(fee, 'quantity', None) or 0))
share = segment.get('share')
if share is not None:
return round(Decimal(str(lot.lot_qt or 0)) * share, 5)
return Decimal(str(lot.lot_qt or 0))
return round(fee_quantity * share, 5)
return fee_quantity
if quantity is None:
qty = round(lot.get_current_quantity_converted(), 5)
return qty
return round(Decimal(str(quantity)), 5)
@classmethod