# Service payment, receipt and settlement — Phase 3C.3

Phase 3C.3 extends the frozen `55effbe` baseline (1,872 tests). Invoice liability,
received money, allocation, receipt evidence and delivery clearance remain separate.
Only finalized invoice CUSTOMER responsibility creates customer debt. Warranty and
company responsibility never require artificial payments or receipts.

## Evidence and fields

All evidence records use the existing protected Record UUID and created/updated
timestamps. Foreign keys use PROTECT. Company and Customer are derived through
the immutable invoice's ServiceCase rather than accepting independent payer inputs.

| Model | Additional fields |
| --- | --- |
| ServicePayment | invoice, service_center, amount, currency, method, reference, status, received_by, received_at, note |
| PaymentAllocation | payment (one-to-one), invoice, amount, allocated_by; created_at records allocation time |
| ServicePaymentReceipt | payment (one-to-one), service_center, number, amount, currency, method, reference, customer_snapshot, center_snapshot, invoice_number, issued_by, issued_at |
| PaymentReversal | payment (one-to-one), amount, reversed_by, reversed_at, reason |
| ServiceFinancialRelease | invoice, outstanding_amount, authorized_by, authorized_at, reason, reference |
| ReceiptSequence | service_center (one-to-one primary key), next_value |

Amounts reuse commercial decimal validation and numeric(14,2), never floats.
Methods are CASH, CARD, BANK_TRANSFER, MOBILE_FINANCIAL_SERVICE and OTHER.
Non-cash methods require a reference; OTHER also requires an explanation. Trimmed,
nonblank references are case-sensitive and unique within center and method, including
after reversal. These are transaction identifiers, never payment credentials.

## Posting, allocation and receipts

Posting requires a consistent FINALIZED invoice and a positive amount no greater
than its remaining CUSTOMER liability. Each payment is strictly invoice-specific
and immediately, fully allocated through its own immutable PaymentAllocation.
Multiple payments and partial settlement are supported; split allocations across
invoices, unapplied money, deposits, wallets, credits and cash-change accounting are
not. Currency, customer, company and center come from the authoritative invoice.

Payment creation, allocation, numbering and receipt creation are one transaction.
The locked center ReceiptSequence allocates RCT-00000001 onwards, with database
center/number uniqueness. Aborted transactions roll back the counter; committed
numbers are never reused after voiding. No MAX()+1 is used. A deferred PostgreSQL
constraint trigger requires each committed payment to have its matching allocation
and receipt, and requires VOIDED state to correspond to a full reversal record.

Receipts snapshot customer UUID, number and display name, center UUID/code/name,
invoice number, amount, currency, method and reference. Customer contact changes,
deactivation, Device deactivation and subsequent center metadata changes do not
rewrite receipts. Historical debt can be collected for inactive Customers/Devices;
the actor and organizational path must still be active.

## Settlement and reversal

Settlement is computed from finalized customer liability less allocations whose
payments remain POSTED. States are NO_CUSTOMER_DUE, UNPAID, PARTIALLY_PAID and PAID.
There is no editable invoice payment status and no payment write to invoice lines,
quotation approval, inventory, ownership or technical history.

Reversal is a full correction void: append actor/time/reason/amount evidence and
transition POSTED to VOIDED atomically. Original payment, allocation and receipt
remain. Partial reversals, processor refunds and accounting journal entries are
outside this phase. PostgreSQL guards reject deletion, evidence edits and arbitrary
payment transitions. Cross-row balance limits are protected by serialized services,
not an unsafe aggregate database check.

After DELIVERED/CLOSED, a reversal is rejected if it would create an uncovered
outstanding balance. A previously authorized release covering that resulting balance
allows reversal. Further post-handover correction workflows are deferred; history
must not silently invalidate the financial basis of handover.

## Due release and handover

A distinct authorized operation records the current positive outstanding amount,
actor, time, mandatory reason and optional reference after QC and before handover
(QC_PASSED or READY_FOR_DELIVERY). It is immutable, invoice-specific authorization
up to that amount, without expiry or revocation in this phase. It does not reduce
debt. Duplicate authorization while an existing release covers the balance is rejected.
A later reversal that raises debt above the release limit removes financial clearance.

Immediately before handover, while holding the ServiceCase write lock, the existing
handover service checks the current finalized invoice. Zero CUSTOMER due, full
settlement or an adequate release permits delivery; otherwise delivery is rejected.
No finalized invoice (including draft-only cases) preserves the frozen workflow.
This does not label invoice-free work free or covered. Readiness and terminal closure
rules remain unchanged, and collection can continue after authorized due-release delivery.

## Services and queries

`apps.commercial.payment_services` exports keyword-only operations:

- `receive_service_payment(actor, invoice, expected_revision, amount, method, reference="", note="")`
- `reverse_service_payment(actor, payment, expected_revision, reason)`
- `authorize_due_release(actor, invoice, expected_revision, reason, reference="")`

The trusted `require_delivery_financial_clearance(service_case=...)` hook is called
only inside handover's locked transaction. It is not an independent delivery API.
Revision tokens combine invoice UUID with the count of immutable payment, reversal
and release events. Every accepted operation advances this monotonic revision;
stale operations fail after acquiring locks, including changes that restore a balance.

`apps.commercial.payment_queries` provides actor-scoped `settlement_invoices`,
`invoice_settlement_summary`, `service_payments`, `payments_for_invoice`,
`payment_detail`, `receipt_history`, `receipt_lookup`, `financial_release_history`,
`unsettled_invoices`, `financially_cleared_delivery_queue` and
`outstanding_customer_balance(actor, customer, currency)`. List queries remain lazy
and deterministically ordered. Outstanding balances never mix currencies. Delivery
queues here concern finalized commercial invoices; frozen technical queues remain.

Separate correlated SQL aggregates prevent payment/reversal/release join fan-out.
Related displays use select_related. Representative list, summary, receipt, history
and balance queries have one-query budgets; Admin display tests check bounded query
counts that do not grow with added payments.

## Authorization, Admin and locking

Business permissions are `commercial.receive_servicepayment`,
`commercial.view_servicepayment`, `commercial.reverse_servicepayment` and
`commercial.authorize_due_release`. Frozen same-path role/assignment scope and active
superuser semantics apply. Staff, direct permissions, Groups and engineer assignment
do not establish business scope. Admin additionally requires native Django view and
add/change permissions for the relevant operation.

Finalized invoice Admin links to settlement, receipt/payment/reversal history and
separate receive/reverse/release forms. Mutations require POST, CSRF and signed
invoice/revision/user tokens. Payment choices are invoice- and actor-scoped; service
checks remain authoritative. Evidence Admin is readonly with deletion disabled.
Free text is escaped. No PAN, CVV, bank credentials, wallet secrets, OTPs or gateway
secrets belong in these records; forms state this explicitly.

Lock order follows the existing authorized-case dependency order: actor and
organizational/role dependencies, Customer SHARE, ServiceCase FOR UPDATE, invoice
FOR UPDATE, then payment FOR UPDATE for reversal or center receipt sequence for
posting. Customer precedes Case as in handover. The gate acquires no earlier
dependency after Case. All financial operations for one case therefore serialize
with invoice finalization and handover. Different-center receipts are independent;
same-center receipt allocation serializes. Authorization revocation uses compatible
locks and is rechecked before writes. A stale UI snapshot never authorizes delivery.

## Verification and baseline exception

The only approved modification to the 1,872-test baseline is the fixture in
`InvoiceConcurrencyTests.test_finalization_before_handover`: `self.billed()` (whose
default is CUSTOMER) becomes `self.billed(responsibility="WARRANTY")`. Its assertions
are unchanged. The test continues to verify invoice finalization versus handover
concurrency; unpaid CUSTOMER delivery is now covered by dedicated rejection tests.

New tests cover unpaid/partial delivery rejection, payment and due-release delivery,
unchanged outstanding debt after release, WARRANTY/COMPANY-only invoices, invoice-free
compatibility, immutable evidence, authorization, Admin, exact money, rollback and
query budgets. PostgreSQL races check balances and persisted histories after competing
payments/allocations, receipt numbering, posting/reversal, payment/release versus
handover, duplicate references, stale forms, rollback and permission revocation.

Final post-change regression: `python manage.py test --noinput` passed all 1,943
tests in 3,181.551 seconds, with zero failures/errors and zero skips. This comprises
the 1,872-test baseline and 71 new tests, including 19 new PostgreSQL payment
concurrency tests. The complete suite contains 519 PostgreSQL transactional tests.
The run created and migrated a fresh PostgreSQL test database and destroyed it
successfully. Development migrations 0005 and 0006 are also applied. Django system
checks, migration drift checks and Git whitespace checks passed. The bounded scan
of all 17 changed/new files found no secret-pattern matches or unrelated artifacts;
`.env` remains ignored and untracked. No files were staged, committed or pushed.

## Migrations and changed files

New migrations:

- `apps/commercial/migrations/0005_receiptsequence_servicepayment_paymentreversal_and_more.py`
- `apps/commercial/migrations/0006_payment_history_integrity.py`

Historical migrations are unchanged. Fresh PostgreSQL test database creation applies
the complete migration chain, in addition to upgrading the development database.

Other new files:

- `apps/commercial/payment_models.py`
- `apps/commercial/payment_services.py`
- `apps/commercial/payment_queries.py`
- `apps/commercial/payment_admin.py`
- `apps/commercial/test_payment.py`
- `apps/commercial/test_payment_security.py`
- `apps/commercial/test_payment_concurrency.py`
- `apps/commercial/templates/admin/commercial/payment_workflow.html`
- `docs/SERVICE_PAYMENT.md`

Modified files:

- `apps/commercial/models.py` — model registration import
- `apps/commercial/admin.py` — Admin registration import
- `apps/commercial/invoice_admin.py` — settlement link and URL
- `apps/service/handover_services.py` — locked financial-clearance hook
- `apps/commercial/test_invoice_concurrency.py` — single approved fixture change
- `docs/ARCHITECTURE.md` — payment-domain link

General ledger, bank reconciliation, deposits, wallets, payables, tax filing, fiscal
devices, payment gateways and external refunds remain outside Phase 3C.3.


## Phase 6D operational presentation

The Commercial workspace adds scoped payment, outstanding, receipt and due-release
views over these unchanged queries/services. Batch display uses the authoritative
settlement summary; no UI debt calculation exists. Receipt printing reuses the
issued record and snapshots, preserves reversal evidence, and applies independent
customer/Service Job disclosure. Due release != debt reduction; payment reversal
!= deletion. Dedicated financial-manager permissions remain separate from
collection. See OPERATIONAL_UI.md for navigation, security and verification.
