This is the small plan. Back-Office (BO) returns only sales that were made in BO. A sale made at POS (POPC) is returned at POS (POPC). It can replace the big plan (v3) for now. Built on 14 Oct 2026 in the working tree (not committed, not deployed). Tests: backend sales-return 156 passed, frontend sales pages 213 passed, typecheck 372 errors (was 375). B2 note added. The POS-return code (SOURCES_RETURNABLE, _till_line_refund, canReturnFromSource and the till-only tests) was removed, so it is no longer "dormant" as section 1 says.
14 Oct 2026 · Branch Redy-20261005-Sales-Return · Big plan for comparison: Plan v3 · Facts below were read from the code on 14 Oct.
sales_commit_repository.py:262), so it stays useful.| Where | Today | Plan B |
|---|---|---|
apps/v2-sellable-bo/backend/app/repositories/sales_commit_repository.py:40 | SOURCES_RETURNABLE = (SOURCE_TYPE_BO, "POS") | SOURCES_RETURNABLE = (SOURCE_TYPE_BO,). This one value feeds the invoice search (:892) and the server check (sales_commit_service.py:1717), so both follow it. |
apps/v2-sellable-bo/frontend/src/lib/constants/sales-lifecycle.ts:143 | canReturnFromSource accepts BO and "POS" | Accepts BO only. It hides the Return button in the invoice list (salesListColumns.tsx:434) and on the invoice page (SalesDocumentViewPage.tsx:537). Fix the comment above it too. |
apps/v2-sellable-bo/backend/tests/unit/services/test_sales_return_qa_sr.py:104-133 | Tests say the POS invoice is returnable | Change them: the switch is ("BO",); a POS invoice is refused with ORIGINAL_NOT_RETURNABLE; search gets ["BO"]. The "if switched off" test becomes the normal test. |
The till-line refund code (_till_line_refund, sales_return_rules.py:153) and its tests | Used for POS sales | Leave it in. It is dormant. Turning POS returns on later is the same two lines back. (Deleting it is cleaner but costs a rewrite later.) |
The server already has the right words for BO-only: "Only a completed sales invoice made in the Back-Office can be returned here." (sales_commit_service.py:1719-1722). It shows when the switch is BO only.
ORIGINAL_NOT_RETURNABLE with the message above.SalesDocumentViewPage.tsx. I would add it: without it, people ask where the button went.| Case | What happens | What to do |
|---|---|---|
Returns of POS sales already made (dev company OHY33941 has 4 BO returns; one is of a POS sale: CI-8/BO/261013052505369. I did not check the other 3) | They stay. They are completed documents. With the server fix they sync. The POS (POPC) app can still crash when opening one (the app change is dropped). | Leave them (my pick), or delete the test rows. I will not touch data without your OK. |
| A Draft or For Approval return of a POS sale | It is refused at the next submit or approve (ORIGINAL_NOT_RETURNABLE). It keeps holding quantity until voided. | Void it. Dev has 0 such (checked 13 Oct). Production: the switch was never committed, so this code made none there. Not checked on the production data. |
| Returns of BO-made sales | No change. | None. |
| Plan B (this page) | Plan v3 | |
|---|---|---|
| BO returns a POS (POPC) sale | No | Yes, with limits (no Cash) |
| Code to build | 2 lines + tests + optional note | Server rules, return-form changes, store picker, link ids, POS (POPC) app |
| New POS (POPC) build | No | Yes |
| End Shift / cash risk | None from BO returns | Handled by rule R1; card totals still off |
| Double-refund risk | None | Needs the link-id fix |
| Customer returns a POS sale | At POS (POPC) only | At POS (POPC) or in BO |
| Can go live | Right after tests | After build, device test, new POS build |
pytest tests/unit/services -k sales_return in apps/v2-sellable-bo/backend (last known: 162 passed). The edited qa_sr tests must pass.npm run typecheck:ratchet (last known 372 errors; it must not go up).fix[sales]: allow back-office returns of back-office sales only. No Claude footer.v2-sellable-api for the register fix.| # | Question | Option 1 | Option 2 | My pick |
|---|---|---|---|---|
| B1 | Which plan? | Plan B now: BO only, small | Plan v3: BO also returns POS sales | Plan B. It is small and safe. Plan v3 can come later. |
| B2 | Tell the user why the button is missing? | Yes, one grey note on the invoice page | No, button just hidden | Yes |
| B3 | The dev return(s) of POS sales | Leave them | Delete the test rows (needs your OK) | Leave them |
Official docs answer this for only 2 products. Cash refunds are tied to a register or drawer. No brand I found allows a back-office cash refund with a clean shift count. This does not clearly choose Plan B or Plan v3.
Nothing in code until you say "go". On "go" I make the two changes, edit the tests, run them, and report. I do not commit or deploy the code without your word.
Searched 14 Oct 2026 by a research agent. UNVERIFIED = no official page found. Only Toast and Dynamics 365 have official answers. The evidence is thin.
| Product | Can back-office refund a POS sale? | Cash and shift rule | Other store | Source |
|---|---|---|---|---|
| Toast | Card: yes. Cash: no. "You cannot refund cash payments from Toast Web". | A cash refund is done on a POS device tied to a cash drawer, so it shows in that drawer's report. With no drawer it shows in no report. | Not stated | Toast doc |
| Dynamics 365 Commerce | A return is a POS action. Return from head office or call centre: UNVERIFIED. | The return policy limits refund methods by the original payment. A cash sale can go back to cash, gift card or customer account, not credit card. A manager can override (and from version 10.0.44 a permission "Allow using all payment methods for refund"). | Not stated | refund policy · POS returns |
| Dynamics AX 2012 (old) | Not stated | A cash return in a different shift needs a disbursement slip, made at POS or registered in advance. | Not stated | TechNet |
| Lightspeed X-Series | You open the original sale in Retail POS (Sales history, Return items). A refund made only in the Financial services tab is not in other reports. Card refunds go to the original card. | Register closing records the cash counted. How a cash refund affects closing: UNVERIFIED. | UNVERIFIED | refund · closures |
| Square | Dashboard handles card refunds. Interac Debit in person only and cash from the Dashboard are UNVERIFIED (community posts only). | UNVERIFIED | UNVERIFIED | Square help |
| Shopify POS | UNVERIFIED. The official page did not say whether the admin can refund. | A cash tracking report shows the refund total per cash session. Effect of an admin refund: UNVERIFIED. | UNVERIFIED | changelog |
| Odoo POS | UNVERIFIED. A forum thread says refunds made in the Orders tab, outside an open session, stayed "New" and blocked closing the session. No confirmed fix. | UNVERIFIED | UNVERIFIED | forum only |
| Clover, Loyverse, SAP B1, Oracle Xstore, NCR, Zoho | No official source found. UNVERIFIED. | |||