# Invoice Receipt Document (קבלה)

**Date:** 2026-06-01
**Status:** Draft
**Spec:** 179
**Tier:** All tiers
**Depends on:** `invoices-core`, `partial-payment-recording`, `multi-currency`, `ita-einvoice`, `invoice-pdf-customization`, `hebrew-locale-dates`
**Referenced by:** `invoices-core`, `partial-payment-recording`, `payment-reconciliation`, `uniform-format-export`

---

## Overview

Israeli tax law requires a **receipt (קבלה)** to be issued whenever a business receives payment — this is a separate legal document from the tax invoice (חשבונית מס). Today `invoices-core` issues only `type 'invoice' | 'proforma'`, and recording a payment (`partial-payment-recording`, `invoice_payments`) produces only a *payment-confirmation email* — **not a legal receipt**. This spec closes that gap.

Two missing document types, both legally mandated:

1. **Standalone קבלה (receipt)** — issued when payment is received against an already-issued חשבונית מס. Own sequential number; references the originating invoice + payment.
2. **Combined חשבונית מס/קבלה (tax-invoice-receipt)** — a single document that is both invoice and receipt, issued when goods/services are billed and paid in the same act (the common cash-sale / immediate-payment SMB case). One number from the combined sequence.

`invoices-core` is the **native system of record** (issues its own sequences; `ita-einvoice` spec 165 registers natively with the ITA), so the receipt obligation falls on Zync, not on external adapters.

---

## Data Model

```sql
CREATE TABLE receipts (
  id                 UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id          UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
  customer_id        UUID NOT NULL REFERENCES customers(id) ON DELETE RESTRICT,
  -- Document identity
  doc_type           TEXT NOT NULL CHECK (doc_type IN ('receipt', 'invoice_receipt')),
                       -- 'receipt' = standalone קבלה; 'invoice_receipt' = combined חשבונית מס/קבלה
  receipt_number     TEXT,                 -- sequential; assigned at ISSUE (never in draft)
  status             TEXT NOT NULL DEFAULT 'DRAFT'
                       CHECK (status IN ('DRAFT','ISSUED','VOIDED')),
                       -- v1: receipts are issued directly (ISSUED); DRAFT is reserved — no creation flow yet; list DRAFT filter is forward-compatible
  -- Links
  invoice_id         UUID REFERENCES invoices(id) ON DELETE RESTRICT,
                       -- standalone receipt: the paid invoice. invoice_receipt: the combined invoice row (may be self/NULL — see notes)
  -- Money
  currency           TEXT NOT NULL DEFAULT 'ILS',
  amount             NUMERIC(12,2) NOT NULL,           -- total received on this receipt
  ils_exchange_rate  NUMERIC(10,4),                    -- snapshot at ISSUE (multi-currency)
  amount_ils         NUMERIC(12,2),                    -- amount in ILS at ISSUE (IL law)
  -- Issue metadata
  issued_at          TIMESTAMPTZ,
  issued_by          UUID REFERENCES users(id) ON DELETE SET NULL,
  pdf_r2_key         TEXT,
  -- Void
  void_reason        TEXT,
  voided_at          TIMESTAMPTZ,
  voided_by          UUID REFERENCES users(id) ON DELETE SET NULL,
  created_at         TIMESTAMPTZ DEFAULT NOW(),
  updated_at         TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_receipts_tenant ON receipts(tenant_id, status, issued_at);
CREATE INDEX idx_receipts_invoice ON receipts(invoice_id);

-- Each payment line on a receipt (cash, bank transfer, cheque, card). IL law requires payment-method detail.
CREATE TABLE receipt_payment_lines (
  id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  receipt_id      UUID NOT NULL REFERENCES receipts(id) ON DELETE CASCADE,
  method          TEXT NOT NULL CHECK (method IN ('cash','bank_transfer','cheque','credit_card','other')),
  amount          NUMERIC(12,2) NOT NULL,
  -- method-specific (IL מבנה אחיד D120 fields)
  cheque_number   TEXT,
  cheque_bank     TEXT,
  cheque_branch   TEXT,
  cheque_account  TEXT,
  cheque_due_date DATE,
  card_last_four  TEXT,
  card_brand      TEXT,
  reference       TEXT,                 -- bank ref / confirmation no.
  invoice_payment_id UUID REFERENCES invoice_payments(id) ON DELETE SET NULL
);

-- Dedicated sequence per doc_type per tenant (separate from invoice_sequences).
CREATE TABLE receipt_sequences (
  tenant_id   UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
  doc_type    TEXT NOT NULL,            -- 'receipt' | 'invoice_receipt'
  prefix      TEXT NOT NULL DEFAULT '',
  next_number INTEGER NOT NULL DEFAULT 1,
  PRIMARY KEY (tenant_id, doc_type)
);
```

**Schema delta on `invoice_payments`** (spec 80):
```sql
-- invoice_payments.receipt_id already exists as a plain nullable UUID (created by
-- partial-payment-recording, spec 80). This spec only wires the FK now that `receipts` exists:
ALTER TABLE invoice_payments
  ADD CONSTRAINT invoice_payments_receipt_id_fkey
  FOREIGN KEY (receipt_id) REFERENCES receipts(id) ON DELETE SET NULL;
-- Set when a קבלה is issued for this payment. A payment recorded without an issued receipt is flagged in the UI.
```

---

## Flows

### A. Standalone receipt on payment

When a payment is recorded against an issued invoice (`partial-payment-recording`), the payment-record modal gains an **"Issue receipt (קבלה)"** toggle (default ON):

```
┌──────────────────────────────────────────────────────────┐
│  Record payment — Invoice #2026-0042                [✕]  │
│  Amount        ₪ [1,170.00]                              │
│  Date          [2026-06-01]                              │
│  Method        [Bank transfer ▾]   Ref [TX-88231____]   │
│  ☑ Issue receipt (קבלה) now                             │
│  [Cancel]                            [Record + issue]    │
└──────────────────────────────────────────────────────────┘
```

On confirm (single DB transaction):
1. Insert `invoice_payments` row (existing flow), advance invoice status (`PARTIALLY_PAID` / `PAID`).
2. Insert `receipts(doc_type='receipt', status='ISSUED')`, assign `receipt_number` from `receipt_sequences`, snapshot ILS rate.
3. Insert `receipt_payment_lines` from the method detail.
4. Set `invoice_payments.receipt_id`.
5. Render + store PDF (`pdf_r2_key`); enqueue customer email.
6. Write audit record (same transaction, per spec 28).

### B. Combined חשבונית מס/קבלה

On the invoice editor, a **"Mark paid + issue invoice-receipt"** action (visible on DRAFT/SENT invoices) issues a `TAX_ISSUED` invoice **and** a `receipts(doc_type='invoice_receipt')` atomically, with the receipt number drawn from the `invoice_receipt` sequence. Used for immediate-payment sales. The invoice's `type` stays `'invoice'`; the paired receipt row records the payment.

### C. Void

Receipts are immutable once issued. Correction = **void** (`status='VOIDED'`, reason required) which also reverses the linked `invoice_payments` row (re-opening invoice balance) within one transaction. Voided numbers are retained (gap-free sequence requirement).

---

## Receipts List & Detail Pages

### `/receipts` — Receipts List

Standalone list of all issued/draft receipts, linked from the invoices sidebar (defined in `payment-reconciliation`, spec 157). Requires `invoices:read`. Backed by `GET /api/receipts`.

```
┌────────────────────────────────────────────────────────────────────────────┐
│  Receipts                                                                  │
│                                                                            │
│  [Type: All ▾] [Status: All ▾] [Customer…] [Date range ▾]   [Export ▾]    │
│                                                                            │
│  Number       Type         Customer      Date        Amount     Status     │
│  ──────────────────────────────────────────────────────────────────────   │
│  REC-00042    קבלה          Acme Corp     2026-06-01  ₪1,170.00  ISSUED     │
│  TIR-00018    חשבונית/קבלה   Beta Ltd      2026-05-30  ₪3,510.00  ISSUED     │
│  REC-00041    קבלה          Gamma Inc     2026-05-29  ₪900.00    VOIDED     │
│  —            קבלה          Delta Co      2026-05-28  ₪450.00    DRAFT      │
│                                                       ──────────           │
│  Showing 1–25 of 138                            [‹ Prev]  [Next ›]         │
└────────────────────────────────────────────────────────────────────────────┘
```

Columns: `receipt_number` (— when DRAFT), `doc_type` (Hebrew label: קבלה / חשבונית מס/קבלה), customer name, `issued_at` (or `created_at` for drafts), `amount`, `status` badge (DRAFT grey / ISSUED green / VOIDED struck-through red). Filters map directly to `GET /api/receipts` query params (`doc_type`, `status`, `customer`, date range). Default sort: `issued_at DESC`. Row click → receipt detail. `[Export ▾]` reuses the standard list-export menu (PDF list / CSV).

### `/receipts/:id` — Receipt Detail

Read-only document view, backed by `GET /api/receipts/:id`. Requires `invoices:read`.

```
┌────────────────────────────────────────────────────────────────────────────┐
│  קבלה  REC-00042                                          [Download PDF] [⋯] │
│  Status: ISSUED · Issued 2026-06-01 by Alex K.                             │
│                                                                            │
│  Customer       Acme Corp                                                  │
│  For invoice    #2026-0042  →  (link to invoice detail)                    │
│  Currency       ILS    Rate@issue  1.0000                                  │
│                                                                            │
│  ── Payment lines ───────────────────────────────────────────────────────  │
│  Method          Amount      Reference / detail                            │
│  Bank transfer   ₪1,170.00   TX-88231                                       │
│                  ──────────                                                 │
│  Total received  ₪1,170.00                                                  │
│                                                                            │
│  [Download PDF]                          ⋯ menu: [Void receipt]            │
└────────────────────────────────────────────────────────────────────────────┘
```

Shows document header (Hebrew type label + number), status/issue metadata, linked customer and originating invoice (deep-link to invoice detail), currency + `ils_exchange_rate` snapshot, and the `receipt_payment_lines` breakdown (method + amount + reference/cheque/card detail). `[Download PDF]` → `GET /api/receipts/:id/pdf`. The `⋯` menu exposes **[Void receipt]** (`POST /api/receipts/:id/void`, reason required) on `ISSUED` receipts only; hidden on `DRAFT`/`VOIDED`. A `VOIDED` receipt shows a banner: "Voided 2026-06-02 by Alex K. — {reason}".

This standalone detail mirrors the per-row view in the invoice-detail **Receipts tab**; both render from the same `GET /api/receipts/:id`.

---

## PDF

HTML-to-PDF (same Worker as `invoice-pdf-customization`, reuses `tenant_invoice_pdf_config`). Hebrew RTL template. Header: **קבלה** / **חשבונית מס/קבלה**. Shows receipt number, date, customer, amount in words (Hebrew), payment-method breakdown, originating invoice number, business details. `window.print()` on public/portal view (`print-layouts`).

---

## API

```
POST   /api/invoices/:id/receipts            → issue standalone receipt for a payment (body: payment detail + method lines)
POST   /api/invoices/:id/invoice-receipt     → issue combined חשבונית מס/קבלה
GET    /api/receipts                          → list (filter: doc_type, status, customer, date range)
GET    /api/receipts/:id                      → detail + payment lines + PDF url
GET    /api/receipts/:id/pdf                  → signed PDF
POST   /api/receipts/:id/void                 → void (reason required); reverses linked payment
```

Issue/void require `invoices:write`. List/read require `invoices:read`.

Receipts surface on the **invoice detail → Receipts tab**, in `customer-statement` (spec 183), and as records in `uniform-format-export` (spec 180, C100/D120). They also have a **standalone `/receipts` list + `/receipts/:id` detail** (see *Receipts List & Detail Pages* above), linked from the invoices sidebar defined in `payment-reconciliation` (spec 157). `/receipts` is owned by this spec (179) and must be registered in the route registry.

---

## Architecture Decisions

| Decision | Choice | Reason |
|----------|--------|--------|
| Separate `receipts` table | Not a row in `invoices` | Receipts have distinct sequence, lifecycle, and legal meaning; mixing doc types in `invoices` would overload the status machine |
| Dedicated `receipt_sequences` | Not shared with `invoice_sequences` | IL law requires gap-free per-document-type numbering; each type gets its own counter |
| Issue-on-payment default ON | Not manual-only | Receipt issuance is legally tied to receiving money; defaulting ON prevents missed receipts |
| Void, never edit | Immutable issued docs | Same compliance posture as tax invoices and credit notes (spec 86) |
| Payment-method lines table | Not JSON | מבנה אחיד D120 export (spec 180) needs structured cheque/card/bank fields per payment |
