Triangular invoicing across group companies, safe to reprocess
The order starts as raw material, goes through cutting and is invoiced as a finished product by another company in the group. Idempotent step control and cascading reversal when an invoice is cancelled.
- Industry
- Textile manufacturing with cutting and retail
- Period
- 2026
- Role
- Sole author
- Scale
- 7 incremental releases in 4 weeks
2
group companies in the same flow
3
keys in the step control (packing list, item, step)
7
incremental releases in 4 weeks
Technologies
Challenge
A textile manufacturer sells cut fabric. The order starts with the raw material, goes through a cutting operation at headquarters, becomes a finished product through an equivalence table and is transferred to a branch, which invoices the end customer. The standard ERP does not know this design: to it, each company only sees its own half.
The first version worked on the happy path. Production brought the rest: a packing list closed twice, eleven seconds apart, duplicated the stock issue; cancelling the branch invoice deleted the mirror order instead of sending it back to the monitor; and the fulfilled quantity was reversed twice, once by the customisation and once by the standard routine.
Solution
Per-step idempotency. Closing the packing list issues the raw material and receives the finished product. I started recording progress per packing list, item and step: if the process dies halfway and runs again, it resumes where it stopped and does not repeat a movement it already made.
Cascading reversal. Cancelling the branch invoice or the transfer invoice now undoes, in the right order, the fulfilled quantity, the packing list postings, the committed stock and the acceptance, and recreates the mirror order at the branch. Each reversal checks whether the standard routine already did its part before doing its own. The double subtraction was diagnosed by reading the ERP’s native change log, node by node, until it was clear who wrote what.
Translation at the right point. Swapping raw material for finished product happened while the screen was being assembled, so merely opening the screen wrote data. I moved it to the extension point right before the invoice is generated, with a rollback when the selection is cancelled.
Branch taxes. The CFOP is recalculated and ICMS uses the branch’s state rate, not the rate of the order’s customer, which is what the standard routine would apply.
Records that had already drifted were fixed with scripts that ran a dry run, took a backup and checked their scope against the live data, one per incident.
Outcome
Reprocessing stopped being dangerous: a step that fails can run again without duplicating stock. Cancelling an invoice now leaves orders, stock and the mirror order consistent in both companies, with no manual data fixes. All of it went in through extension points, without changing product code.
What this case shows
Idempotency in a legacy ERP, with no distributed transactions, is built with explicit step control. Data bugs get solved by investigating the log, not by guessing. And a customisation can live alongside the standard routine when each reversal knows what the other has already done.
Stuck on a term? There is a glossary. Glossary