Skip to content

[FIX] purchase_ux: complete the pending criterion in purchase matching - #360

Open
mav-adhoc wants to merge 1 commit into
ingadhoc:19.0from
adhoc-dev:19.0-t-72358-mav
Open

[FIX] purchase_ux: complete the pending criterion in purchase matching#360
mav-adhoc wants to merge 1 commit into
ingadhoc:19.0from
adhoc-dev:19.0-t-72358-mav

Conversation

@mav-adhoc

@mav-adhoc mav-adhoc commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Port of #359 to 19.0, plus the two fixes of #351 and #353 that never reached this branch. The criterion here was still product_qty > qty_invoiced for every document type.

1. Credit notes (#351, #353)

product_qty does not drop with a return, so a line that is already fully billed but has a pending credit (qty_to_invoice < 0) was hidden from the matcher of a credit note. Use qty_to_invoice < 0 on in_refund.

2. Over receipt on bills (#359)

When the vendor delivers more than ordered, the line ends up ordered = billed but received > billed, so there is a legitimate quantity left to bill and the line disappeared from the matcher (ordered 40, received 41, billed 40 → 1 pending). Fixed by adding qty_to_invoice > 0 as a second term, the same criterion the native view uses (purchase/models/purchase_bill_line_match.py, _select_po_line()). The first term is still needed: on products controlled on received quantities qty_to_invoice is qty_received - qty_invoiced, so a confirmed purchase order with no receipt yet gives 0 and every line would be hidden.

3. Orders set as Nothing to Bill

The view already excludes orders forced as No Bill to Receive, but not the other value of force_invoiced_status, even though both mean the order is closed for billing. The exclusion now covers any forced status. Unlike #359, this branch does not need it in the action: the filter lives in the view, which is the better place, so the action only carries the quantity criterion.

Test plan

purchase_ux/tests/test_purchase_matching.py, 7 cases. Bill criterion: confirmed order with no receipt (offered), partial receipt not billed (offered), over receipt 40/41/40 (offered), fully received and billed (not offered), forced invoice status, both values (not offered). Credit note criterion: pending refund from a return 600/500/600 (offered), return already credited 600/500/500 (not offered).

Verified locally on 19.0: 0 failed, 0 errors of 10 tests. Reverting purchase_ux/models/ fails test_bill_over_receipt, test_refund_pending_from_return, test_refund_already_credited and the 'no' subtest of test_bill_forced_invoiced_status, and leaves the rest green.

Known limitation, out of scope

A line returned and already credited (ordered 150, net billed 0) still shows as 150 pending on bills. It happens with the previous criterion and with the native one too; whether it is tolerated is a product decision.

https://www.adhoc.inc/odoo/project.task/72358

4. Orders closed with the Set Invoiced button

button_set_invoiced() stamped invoice_status on the order and zeroed qty_to_invoice on its lines. Both are stored computed fields, so the next recompute undid it. Verified on 19.0: right after pressing the button the order reads Nothing to Bill instead of No Bill to Receive, and any later recompute — writing on a line is enough — brings it back to Waiting Bills with the full quantity pending. Neither the matcher nor the line status ever treated those orders as closed, since both read force_invoiced_status, which the button never set. It now writes that field, which is durable and is what the rest of the module reads.

Its invisible domain never matched on this branch either: purchase orders no longer have the done state on 19.0 (it is the locked boolean now), so the button was unreachable in the form. It is gated on locked now.

test_bill_set_invoiced_button covers it: after the button the order keeps force_invoiced_status and No Bill to Receive, the matcher does not offer its lines, and a later change of the received quantity does not bring it back. Verified locally on 19.0: 0 failed, 0 errors of 11 tests; reverting only purchase_order.py fails it with False != 'invoiced'.

Note for the merge: this now touches a view, so it needs bump, not nobump. The line level status of those orders only becomes correct together with #362.

@roboadhoc

Copy link
Copy Markdown
Contributor

Pull request status dashboard

The lines offered by the 'Match purchase lines' button were filtered
with product_qty > qty_invoiced for every document type, which misses
three cases. The first two were already fixed on 18.0 and never reached
this branch.

* Credit notes: product_qty does not drop with a return, so a fully
  billed line with a pending credit gave 0 and was hidden. Use
  qty_to_invoice < 0 on in_refund.

* Over receipt on bills: when the vendor delivers more than ordered the
  line ends up fully ordered but not fully billed (ordered 40, received
  41, billed 40), and the quantity left to bill was hidden. Add
  qty_to_invoice > 0 as a second term, the same criterion the native
  view uses. The first term is still needed for a confirmed purchase
  order with no receipt yet, where qty_to_invoice is 0 on products
  controlled on received quantities.

* Orders set as 'Nothing to Bill': the matching view already excludes
  orders forced as 'No Bill to Receive', but not the other forced
  status, even though both mean the order is closed for billing.
  Exclude any forced invoice status.

Add tests for the bill criterion (no receipt, partial receipt, over
receipt, fully billed, forced status) and for the credit note one
(pending refund from a return, return already credited).

* Orders closed with the 'Set Invoiced' button: the button stamped
  invoice_status on the order and zeroed qty_to_invoice on the lines, both
  stored computed fields, so the next recompute undid it and neither the
  matcher nor the lines ever saw the order as closed for billing. Write
  force_invoiced_status instead, which is what everything else reads. Its
  domain in the form also never matched on 19.0, where purchase orders no
  longer have the 'done' state, so the button was unreachable: use locked.
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.

2 participants