Skip to content

feat(spp_drims): Dispatch page and waybill review — locked lines, one-page waybill - #433

Open
emjay0921 wants to merge 8 commits into
19.0from
feat/1151-1150-drims-waybill-and-dispatch-page
Open

feat(spp_drims): Dispatch page and waybill review — locked lines, one-page waybill#433
emjay0921 wants to merge 8 commits into
19.0from
feat/1151-1150-drims-waybill-and-dispatch-page

Conversation

@emjay0921

Copy link
Copy Markdown
Contributor

Why is this change needed?

Two children of the Stock Allocation & Dispatch review (OP#1077), both returned from QA as passing:

  • OP#1150 — Improvements to Dispatch page. The DRIMS tab sat last on the dispatch form, behind Additional Info and Note, although it is where a dispatch is actually worked. The form also showed a Delivery Address that is meaningless for relief goods and no destination at all, and it left every field the request had already decided — operation type, source location, source document, products — editable for the dispatch's whole working life.
  • OP#1151 — Corrections to Waybill PDF. The waybill printed over two pages with columns stacked and the signature row pushed onto the second, the TO box printed blank, and any report containing a barcode or QR code answered HTTP 500 in this image.

How was the change implemented?

  • Waybill rendered, not read. The template was fixed against the rendered output rather than the source, so it fits one page with the signature row intact and states what is on the vehicle. rlPyCairo was added to docker/requirements.txt: every barcode and QR in every Odoo report goes through renderPM, reportlab 4.1 defaults that backend to rlPyCairo, and without it /report/barcode/ returns 500. Odoo's own requirements only pin a backend for win32, assuming Debian's python3-renderpm — this image does not install it.
  • Dispatch & Delivery leads the form. The tab moved to second position, right after Operations, and is labelled for what it does instead of "DRIMS". Delivery Address is replaced on a dispatch by the destination location, which is what spp.drims.request.action_create_dispatch actually populates; partner_id stays blank, and that emptiness is why the waybill's TO box printed blank.
  • A dispatch is locked to what its request approved. The header fields and everything under DRIMS Information are read-only for request dispatches only — ordinary transfers, donation receipts and returns keep core's behaviour. Quantity stays editable on purpose: entering less than Demand is how a partial dispatch and its backorder are produced (OP#1087).
  • Source Location is inert, not merely read-only. Making a many2one read-only turns it into a link to the record, which QA spotted. no_open cannot be conditional, so core's copy is hidden on a dispatch and an inert copy shown in its place — the same shape as OP#1158's hazard category.
  • Both header xpaths are qualified: core declares picking_type_id twice and location_id three times, and position="attributes" takes the first match — which for location_id is the copy nobody can see, so an unqualified xpath would have looked like nothing happened.

New unit tests

  • spp_drims/tests/test_waybill_report.py — 13 tests, asserting against the rendered PDF/HTML rather than the template.
  • spp_drims/tests/test_dispatch_page.py — 11 tests, evaluating the view's own invisible expressions per kind of picking so a change scoped to dispatches is proven not to touch transfers, receipts or returns.

Unit tests executed by the author

Full spp_drims suite on this branch, against a fresh database: 287 tests, 0 failed, 0 errors.

Worth knowing for anyone testing locally: two of the barcode tests need the rlPyCairo this PR adds, so a container image built before this branch fails them with ModuleNotFoundError: No module named 'rlPyCairo', and pip install rlPyCairo inside the runtime image cannot fix it — pycairo needs the dev headers that only the builder stage has. Rebuild the image (docker compose build openspp-dev) and they pass. CI builds the test image from docker/Dockerfile at this checkout, so it picks the dependency up on its own.

How to test manually

  1. ./spp start, then open a DRIMS request and allocate it.
  2. Create a dispatch from the request. On the dispatch form:
    • Dispatch & Delivery is the second tab, right after Operations.
    • Operation type, source document, Source Location and the DRIMS Information fields are read-only; Quantity is still editable.
    • Source Location is plain text — clicking it does not open the location record.
    • There is a destination location, and no Delivery Address field.
  3. Print the waybill: one page, signature row intact, TO box filled in, and the barcode renders instead of a 500.
  4. Repeat step 2 on a plain internal transfer, a donation receipt and a return — all keep core's editable behaviour.

Related links

Rendered the waybill and read the result rather than the template. It came
out two pages, with every column stacked and the signature row pushed onto
page 2; the TO box empty; "Total Items" reading 1 for a 200-unit
consignment; the picking's internal note printed; the quantity column
showing demand rather than what was picked; and no barcode.

Lay it out with tables instead of Bootstrap's grid. Reports are rendered by
wkhtmltopdf 0.12.6, a WebKit build with no flexbox, so row/col-* and card
collapse and each column becomes a row. A test fails if any of those
classes reappear, because the damage is invisible in the browser preview
and only shows in the PDF.

Fill the TO box from location_dest_id. partner_id is blank on a dispatch —
relief goods go to a location, not a customer — which is why the box was
empty. Print an address only when a street is recorded, since the contact
widget on a bare partner renders its name wrapped in dashes.

Show Shipped alongside Demand. A waybill accompanies the goods, so it has
to state what is actually on the vehicle; demand stays for reconciliation
at the receiving end. Drop the "Total Items" footer, which counted move
lines, and the note, which is internal. Always print the Vehicle and
Driver rows so there is somewhere to write, and print whichever of
Request or Donation matches the transaction type. Add the transaction
type, distribution area, estimated beneficiaries and distribution type.

The barcode needed two fixes and neither was in the template. reportlab
4.1 defaults renderPMBackend to rlPyCairo, which the image does not
install, so /report/barcode/ answered HTTP 500 for every URL form — every
barcode and QR code in every Odoo report, not just this one. Odoo's own
requirements only pin a backend for win32, assuming the Debian package
covers Linux. Adding rlPyCairo fixes the route. The image still did not
appear, because an <img> with a relative URL needs wkhtmltopdf to fetch it
from inside the rendering process, where web.base.url points at an
external host and port that cannot be reached; embedding it as a data URI
removes that dependency, verified by rendering with web.base.url
deliberately pointed at a dead port. A failure to build the barcode is
logged and skipped rather than raised, so it cannot stop a waybill
printing.

Also expose stock.move's drims_request_line_id and drims_donation_line_id,
which appeared in no view, so it is possible to see which request or
donation line a move fulfils. Read-only, since reassigning one by hand
would misattribute dispatched and delivered quantities. Kept in its own
file: the picking form's Operations list is defined inline in
stock.view_picking_form and cannot be extended from a separate view.

Note the ticket lists a third linkage field, drims_allocation_id, which
does not exist on the model.

OP#1151
The DRIMS tab sat last, behind Additional Info and Note, although it is
where a dispatch is actually worked. It now sits second, right after
Operations, and is labelled "Dispatch & Delivery" rather than "DRIMS".

Replace "Delivery Address" with the destination location on a dispatch.
partner_id is left blank because relief goods go to a location rather than
a customer, so the field only invited someone to fill in something
meaningless — and its emptiness is why the waybill's TO box printed blank
(OP#1151). Core hides location_dest_id for outgoing pickings and keeps an
invisible="1" copy behind stock.group_stock_multi_locations, which is why a
dispatch showed no destination at all; rather than un-hide core's copy,
which would stay invisible wherever that group is off, add a read-only one
scoped to dispatches. It is populated by
spp.drims.request.action_create_dispatch from the request's destination
warehouse.

Both changes are scoped to request dispatches, so donation receipts,
transfers, returns and plain stock pickings are untouched — asserted per
picking kind by evaluating the view's own invisible expressions.

The ticket's fourth item, removing the ability to delete a line, is already
implemented for request dispatches in OP#1057; repeating it here would mean
two xpaths setting the same attribute on the same field.

OP#1150
…waybill-and-dispatch-page

# Conflicts:
#	spp_drims/views/stock_picking_views.xml
A request dispatch is generated by action_create_dispatch, which sets the
operation type, the source location, the source document, the products and
every field under DRIMS Information. None of them is the dispatcher's to
change, but core only locks the header fields once the picking is done or
cancelled, so they stayed editable for a dispatch's whole working life.

Lock them for request dispatches only. Ordinary transfers, donation
receipts and returns keep core's behaviour. Quantity is deliberately left
editable: entering less than Demand is how a partial dispatch and its
backorder are produced (OP#1087).

Both header xpaths need qualifying. Core declares picking_type_id twice —
once in the header and once column_invisible in the moves list — and
location_id three times: a copy hidden behind
!stock.group_stock_multi_locations, the visible Source Location behind that
same group, and one in the list. position="attributes" takes the first
match, which for location_id is the invisible copy, so an unqualified xpath
would have locked a field nobody can see and looked like nothing happened.
A test asserts the lock lands on the visible field for that reason.

Also converts a round-1 xpath from @Class to hasclass(), which the UI hook
requires, and moves the test fixtures onto OP#1079's per-warehouse
allocation model.
…ad-only

QA asked whether Source Location had become clickable. It had, and round 2
is what did it: making a many2one read-only turns it into an internal link
to the record. Core sets no_open on picking_type_id already, which is why
Operation Type is inert - it does not on location_id.

no_open cannot be made conditional, because options is parsed as a static
dict and cannot reference drims_type. So core's copy is hidden on a dispatch
and an inert one shown in its place, the same shape as OP#1158's hazard
category. The replacement repeats the multi-locations group, or a
single-location installation would start seeing a Source Location field it
is not meant to have.

The view tests now ask which copy a given kind of picking actually renders,
rather than assuming one node per field.
view_stock_move_operations_drims fails the XML ID hook - "operations" is not
a view type - so it would block this branch's PR. Renamed to
view_move_form_operations_drims, matching its siblings in the same file
(view_move_form_drims, view_move_tree_drims).

Introduced by the waybill commit on this branch; never on 19.0, and
referenced nowhere else, so the rename is contained.
@emjay0921
emjay0921 marked this pull request as ready for review August 18, 2026 06:15
@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.27%. Comparing base (0820667) to head (6125146).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             19.0     #433      +/-   ##
==========================================
+ Coverage   72.24%   72.27%   +0.02%     
==========================================
  Files         419      419              
  Lines       29813    29829      +16     
==========================================
+ Hits        21539    21558      +19     
+ Misses       8274     8271       -3     
Flag Coverage Δ
spp_base_common 91.07% <ø> (ø)
spp_drims 82.41% <100.00%> (+0.16%) ⬆️
spp_drims_sl_demo 69.15% <ø> (+0.14%) ⬆️
spp_programs 65.27% <ø> (ø)
spp_registry 87.22% <ø> (+0.07%) ⬆️
spp_security 69.56% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
spp_drims/models/stock_picking.py 78.86% <100.00%> (+2.07%) ⬆️

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant