Repository navigation
C2-3: invoices and credit memos on the contract, gapless numbering, void and restock - #46
Merged
Merged
Conversation
… 4.1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ters, numbers, terms by id, credit memo lines) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…count: credit memo and reversal row types Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ng credit memo) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…pository, void, credit memo lifecycle, gapless numbering), callers moved; WIP tests
…erlocks, seed, portal readers, document print wall kept Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…s gain the credit memo and reversal values
…oid, numbering, credit memo lifecycle, tax cap, restock COGS) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… wall through serve, exposure lock, relieved cost and discount carried items Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…it memo pages, portal invoices, Playwright flows Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…oo; test for each posting
…_id, deposit applications, closed short orders, live piece billing after a void, restock cost telescoping, stale PENDING reader Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…invoices # Conflicts: # web/packages/api-client/test/usage.ts
The sync merge of refactor/v1 brought in C3-1 (the product list) and C3-1b (the inventory levels list), both now the cursor envelope with available as a decimal string. stockedProducts still read bare arrays and threw "list is not iterable"; it now walks the seed's products oldest first as fulfilment.spec.ts does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n pieces round down With the remainder branch of creditTax replaced by case false the new test fails (tax credited 45 of 47, balance 2); with it, green. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…s it A void committed while the charge was at the gateway made the in-transaction lockInvoice refuse, leaving the customer charged with no document. The charge is now voided (same day) or refunded (settled) through the existing gateway interface before the refusal returns, and both outcomes are logged. No new gateway method was needed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…retryable 409 voidCredit decided the finance role and the credit serialization from an unlocked read. If a post committed before the row lock, the void ran on the open memo with both skipped. After the lock, a draft read that is no longer a draft is now a 409 credit_memo_changed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…S(amount) A legacy memo with a negative amount aborted the whole file on the nonpositive CHECK. The totals and the ADJUST line now read the absolute value. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…e counter sale void An origin pos invoice is 409 counter_sale: its stock moved with the sale, so voiding the invoice alone left the sale standing. The legacy entry test now reclassifies its invoice as an order's. CONTRACT-CHANGES row added. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Three messages sent the user to each other while all refusing. Every kit refusal now says a kit cannot be returned in v1 and to credit its price with a free line that names no product; a text line keeps its own message. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…erse on a detached context P2-1: reverseCharge returns its outcome (voided, refunded, failed). The 409 keeps ErrInvoiceVoid and names the reversal in its message (the handler now writes that message to the body). When the void and the refund both fail the service returns ChargeNotReversedError (ErrChargeNotReversed) and the handler answers 502 CHARGE_NOT_REVERSED: the card was charged, finance reconciles the gateway transaction id (the id only). Every reversal writes a payment.charge_reversal audit row on the invoice after the rolled back transaction ended, no card data. P3-1: the reversal runs on context.WithoutCancel(ctx) with a 30 second timeout. A transaction that never opened (the request ended during the gateway call) now reverses too, since nothing was recorded. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…invoices # Conflicts: # docs/refactor/CONTRACT-CHANGES.md
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.
Summary
C2-3 of the Gable v1 goal (money class): invoices and credit memos on the wire contract (ADR 0001) as ADR 0005 sections 4, 6 and 8 build them. Migration 097.
status,customer_id,job_id,ship_to_id,order_id,overdue), the detail with its lines in the shared sales line shape (IN-1.8), a lowercase status,_centsmoney, the gaplessIN-number, a revision with its ETag, the job and ship-to the invoice carries, the customer's terms snapshot (due date, early pay discount),open_centsand the computedis_overdue.OVERDUEis gone from storage, the wire and every reader.POST /invoices/{id}/transitions, roles admin, owner, finance): locks the order row and the customer's credit serialization first, reverses the invoice's whole entry (dated the void date), returns the billed stock to on hand, reduces the order lines' fulfilled quantities, re-runs allocation for exactly those quantities (whole kits for kits), re-derives the order status and credits the subledger back. Refused withhas_applications(a payment recorded or an applied credit memo names it) orhas_credit_memos./credit-memos, new): a draft with no number (minted at post, so a voided draft consumes none), lines that return invoice lines in proportion to what was charged (the credit memos of a line sum to its total to the cent, never more than was billed:exceeds_billed) or free lines (a price given back, a fee, a note), tax at the invoice's rate and never more than the invoice charged (the last memo takes the remainder), the post with restock and its balanced entry (COGS reversed at the cost that LEFT), and the void (reverses the entry and the restock).document_counters,httpx.NextGaplessNumber, the two SQL functions as column DEFAULTs (the seed numbers through the same counter), the number minted late (after the customer row lock, just before the insert).gl.PostReversal,inventory.UnrestockQty,salesdoc.BilledDiscount, the payment module refusing a void invoice under the invoice row lock,period_closedas a 409 on every posting, the desk invoice and credit memo pages, the portal invoice page, Playwright flows, 16 CONTRACT-CHANGES rows.Postings take the same interim path as C2-2's (
gl.PostEntry, today'sPostTransaction) until C2-4.Carried items
order/repository.go; testTestRelievedCostReadExcludesVoidInvoices, which fails when the exclusion is removed).discount_centson partial pieces is the gross piece less the net piece, so total plus discount equals the gross to the cent and the shares sum to the order line's discount exactly (salesdoc.BilledDiscount, a property test and a wire test; the same wire test written against the base fails: 334 and 666 against the gross pieces 333 and 667).TestBranchWall_DocumentRoutes, now throughGetInvoiceRecord).TestExposureAddingActsTakeTheCustomerCreditLock).customers.payment_termsandinvoices.payment_termsare dropped by 097 with 091's mapping (TestMigration097_BackfillsRowsThatExist).TestBranchWall_InvoiceAndCreditMemoRoutesthrough serve's wiring).Exit test
cd core && DATABASE_URL=<url> GABLE_TEST_REQUIRE_DB=1 CI=true go test -race ./...: 64 packages ok, no race.make contract-goandcd web/packages/api-client && npm run drift && npm run typecheck: fresh, 0 pending, 368 covered, 1092 golden steps conform.bash scripts/check-shape.sh,python3 .github/scripts/reuse_gate.py: pass.cd web && npm run lint && npm run typecheck && npm test && npm run build: pass (543 desk tests). Playwright on the real stack withVITE_AUTH_DEV_MODE=true: 27 passed.TestBaseInvoiceWireFactswritten with the base's request shapes and the new outcomes fails 8 ways on 7cda840 (status casing, no number, no_cents, lines withoutline_total_cents,overduesilently ignored, the offset page, no credit memo route).Not built here (ADR 0005 assigns it elsewhere)
AR aging and statements by job (C2-4, ADR 0005 section 10); invoice
amount_open, applications and the write off (C2-4); a kit or component line is not credited by line (a 400 asks for the invoice line that billed the kit as a free line): kit credit memos need the component explosion the ADR leaves unwritten.🤖 Generated with Claude Code