fix(spp_drims): stop backorder dispatches bypassing the DRIMS request - #390
Draft
emjay0921 wants to merge 1 commit into
Draft
fix(spp_drims): stop backorder dispatches bypassing the DRIMS request#390emjay0921 wants to merge 1 commit into
emjay0921 wants to merge 1 commit into
Conversation
Validating a dispatch short of its demand and choosing "Create Backorder" left the request reading as fully dispatched, told no one, and attributed the parent shipment's per-shipment facts to goods still in the warehouse. Odoo builds a backorder with picking.copy(), so every field left at the default copy=True was inherited. Mark the per-shipment facts copy=False: beneficiary count, departure/arrival, the pod_* block, transport and driver details, discrepancy notes and drims_return_id. This also stops the Duplicate action producing a dispatch that claims a delivery, and it makes the beneficiary guard in button_validate() fire on the backorder instead of being pre-satisfied by the inherited value — which had let one 100-unit distribution to 500 people report 1000 beneficiaries served on spp.hazard.incident.drims_beneficiaries_served. Announce the backorder on the request: an internal note plus a to-do activity for the coordinators of the destination area, resolved by mirroring rule_request_coordinator_scope so it reaches exactly those permitted to see the request. Reopen the request at Ready for Dispatch while a backorder is pending, and re-advance to Dispatched once nothing is outstanding. Because action_create_dispatch counts a quantity as dispatched when it is committed to a picking rather than when it ships, that running total is now rebuilt from the moves that still stand whenever moves are validated or cancelled. A single reconciliation covers a cancelled backorder, a cancelled dispatch, and declining Create Backorder — the last of which cancels no move at all, it just drops the excess demand, so a cancellation hook alone would have missed it. Odoo already carries drims_request_id and drims_request_line_id onto the backorder and its split move, so the request link and per-line attribution were correct and are covered by a regression test. OP#1087
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
Comment on lines
+798
to
+799
| self.env["res.users"] | ||
| .sudo() |
Comment on lines
+798
to
+799
| self.env["res.users"] | ||
| .sudo() |
| edit. | ||
| """ | ||
| self.ensure_one() | ||
| request = self.sudo() |
| part of it was cancelled rather than shipped has to become actionable | ||
| again. | ||
| """ | ||
| for rec in self.sudo(): |
| Counterpart to ``_on_dispatch_backorder_created``: when the outstanding | ||
| backorder is finally validated, the request returns to ``dispatched``. | ||
| """ | ||
| for rec in self.sudo(): |
| have write access to the request under the area record rules, and this is | ||
| system bookkeeping rather than a user edit. | ||
| """ | ||
| Move = self.env["stock.move"].sudo() |
| system bookkeeping rather than a user edit. | ||
| """ | ||
| Move = self.env["stock.move"].sudo() | ||
| for line in self.sudo(): |
| ): | ||
| dispatched += move.quantity if move.state == "done" else move.product_uom_qty | ||
| line.quantity_dispatched = dispatched | ||
| self.sudo().request_id._reopen_if_not_fully_dispatched() |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## 19.0 #390 +/- ##
==========================================
+ Coverage 72.27% 72.30% +0.03%
==========================================
Files 1010 1008 -2
Lines 60741 60814 +73
==========================================
+ Hits 43900 43974 +74
+ Misses 16841 16840 -1
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
emjay0921
marked this pull request as draft
August 4, 2026 02:14
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why is this change needed?
Validating a DRIMS dispatch short of its demand and choosing Create Backorder processed the remainder entirely outside the request workflow. Reproduced on a dev instance before changing anything:
message_postappeared nowhere in the module).picking.copy(), so every field left at the defaultcopy=Truewas inherited. Three consequences, all measured:beneficiary_countwas inherited andhazard_incident.py:341sums it over every done dispatch. Validating the 10-unit backorder moveddrims_beneficiaries_served1500 → 2000 — one 100-unit distribution to 500 people reporting 1000 served.button_validate()returnedTruewith no prompt, because the inherited value already satisfied it. Nobody ever confirmed where the remaining units went.date_departedanddriver_name— a departure timestamp predating its own existence, on goods still in the warehouse.In a humanitarian context these are false coverage figures and undocumented aid movements, which is what the ticket flags as the real risk.
Not broken, and verified rather than assumed:
waybill_numberis alreadycopy=Falseand regenerates, anddrims_request_id/drims_request_line_idalready carry onto the backorder and its split move — so the request link and per-line attribution were correct. Both are now covered by a regression test instead of being "fixed".How was the change implemented?
copy=Falseon the per-shipment facts (stock_picking.py) — beneficiary count, departure/arrival, thepod_*block, transport and driver details, discrepancy notes,drims_return_id. Declarative, so it fixes every copy path rather than just backorders, including the Duplicate action producing a dispatch that claims a delivery. It also makes the existing beneficiary guard fire on the backorder.Coordinator notification (
request.py) —_create_backorder()routes DRIMS dispatch backorders to their request, which posts an internal note and schedules a to-do activity. Coordinators are resolved by mirroringrule_request_coordinator_scope(security/rules.xml), matching users whosedrims_area_idscover the destination area or any ancestor, so the notification reaches exactly those permitted to see the request. Runssudo()because a warehouse officer may sit outside the request's area scope and this is system bookkeeping, not a user edit.Request state — reopens at
allocated(Ready for Dispatch) while a backorder is pending, and re-advances todispatchedonce nothing is outstanding.Quantity reconciliation (
request_line.py,stock_move.py) —action_create_dispatchcounts a quantity as dispatched when it is committed to a picking rather than when it ships, so that running total is rebuilt from the moves that still stand on_action_done/_action_cancel. One reconciliation covers a cancelled backorder, a cancelled dispatch, and declining Create Backorder. That last path cancels no move at all — Odoo just drops the excess demand — so a cancellation hook alone would have missed it.quantity_dispatchedkeeps its existing "committed to a picking" meaning, so OP#1033's partial-dispatch behaviour is untouched.New unit tests
spp_drims/tests/test_dispatch_backorder.py— 12 tests:dispatchedwhile the backorder is pending, and returns todispatchedonce it ships.Unit tests executed by the author
Full module suite, run after merging the current
19.0:All 12 new tests confirmed executing by name in the log rather than inferred from the total. OP#1033's existing partial-dispatch assertions still pass.
./spp lint(ruff + ruff-format) passes on all six files.Also verified in the running app, not only in tests: the backorder shows
beneficiary_count=0, no departure, no driver and its own waybill while keeping request/type/incident/area; the request reopens toallocated; the coordinator receives both the note and an assigned activity; the guard fires; the incident counts 540; and on the cancel pathtotal_dispatcheddrops to 90 with Create Dispatch succeeding for the remaining 10.How to test manually
Requires a DRIMS warehouse with stock, and a user in DRIMS District Coordinator whose DRIMS Areas include the request's destination area.
Related links
Notes for the reviewer
readme/HISTORY.mdand the manifest version are deliberately untouched — per our convention those land on19.0after merge, to avoid conflicts between concurrent PRs against the same module. The entry for this change would befix(spp_drims): stop backorder dispatches bypassing the DRIMS requestat19.0.2.0.1.Two adjacent gaps found while investigating, both left alone as out of scope:
quantity_deliveredis never written by any production code in the module — only by tests. Sototal_deliveredandfulfillment_pctare permanently 0, andalert.py:276-282's "quantity needed" always reports the full requested amount. That is the delivered side, which OP#1088's Confirm Delivery popup owns.🤖 Generated with Claude Code