# ITA E-Invoice Registration (חשבונית ממוחשבת)

**Date:** 2026-06-01
**Status:** Draft
**Spec:** 165
**Tier:** All tiers
**Depends on:** `invoices-core`, `foundation-auth-rbac`, `settings-module`
**Referenced by:** `invoices-core`, `israeli-tax-reports`

---

## Overview

Israeli Tax Authority (רשות המסים — ITA) requires all VAT-registered businesses to register tax invoices (חשבונית מס) with the ITA's central registry before sending them to customers. This is the "חשבונית ממוחשבת" (computerized invoice) mandate, effective for businesses above a certain revenue threshold (ITA sets thresholds annually).

This spec defines the ITA e-invoice registration integration: the API calls to the ITA's "Shaba" system (שב"א — שירות בינה ארגונית), the registration flow within the invoice lifecycle, and the confirmation number storage.

---

## ITA Registration Requirements

At `TAX_ISSUED` transition:
1. Zync must call the ITA Shaba API with invoice details
2. ITA returns a **confirmation number** (מספר אישור / מספר הקצאה) — up to 8 digits
3. The confirmation number must appear on the invoice document
4. If the ITA API is unavailable: **invoice may not be issued** (business rule, not technical retry)

### Exemptions

The registration requirement applies when **invoice total (including VAT) ≥ threshold**. ITA sets this threshold annually:
- 2024: ₪5,000
- 2025: ₪20,000 (expected — confirm at implementation)

Invoices below threshold can be issued without ITA registration (confirmation number not required). Zync stores the threshold in `system_config` (admin-configurable, not hard-coded).

---

## ITA Shaba API

**Environment:**
- Production: `https://openapi.taxes.gov.il/shaam/tsandbox`  (sandbox for testing)
- Production live: `https://openapi.taxes.gov.il/shaam/openapi` (actual endpoint TBD — verify at implementation)

**Authentication:** mTLS certificate per-business (registered at ITA for the tenant's business). Certificate stored encrypted in `adapter_credentials` (`adapter_id = 'ita_shaba'`).

**Request payload (simplified):**

```json
{
  "taxableEntityID": "123456789",      // tenant's VAT number (9 digits)
  "invoiceReferenceNumber": "INV-00042",
  "customerVatNumber": "987654321",    // if customer is VAT-registered
  "invoiceDate": "2026-06-01",
  "invoiceType": 305,                  // 305 = tax invoice; 320 = credit note (set from invoices.source)
  "currencyCode": "ILS",
  "invoiceTotal": 12700,               // total in smallest currency unit (agorot → × 100)
  "vatTotal": 1932,
  "lineItems": [
    {
      "lineSequenceNumber": 1,
      "description": "Consulting services",
      "quantity": 10,
      "unitPrice": 1000,
      "lineTotal": 10000
    }
  ]
}
```

**Response (success):**

```json
{
  "confirmationNumber": "12345678",    // 8-digit ITA confirmation number
  "status": "OK"
}
```

**Invoice type selection:** Credit notes transmitted to ITA use `invoiceType` 320 (as required by the Shaba API spec). The handler checks `invoices.source` to determine the correct type — `source = 'credit_note'` → 320, otherwise → 305 (regular tax invoice).

---

## Invoice Lifecycle Extension

Modified `POST /api/invoices/:id/issue-tax` handler:

```ts
// apps/zync-api/src/routes/invoices/issue-tax.ts
export async function issueInvoice(invoiceId: string, tenantId: string, env: Env) {
  const invoice = await getInvoice(invoiceId)
  const tenant = await getTenant(tenantId)

  // 1. Check if ITA registration required (amount threshold + tenant has VAT number)
  const requiresITA = invoice.total >= await getITAThreshold()
    && tenant.vat_number != null
    && tenant.ita_registration_enabled

  // Determine the ITA invoiceType: credit notes use 320, regular tax invoices use 305.
  // The handler checks invoices.source to branch.
  const itaInvoiceType = invoice.source === 'credit_note' ? 320 : 305

  if (requiresITA) {
    const itaResult = await registerWithITA(invoice, tenant, env, itaInvoiceType)
    if (!itaResult.success) {
      throw new ITARegistrationError(itaResult.error)
      // 503 returned to client with: { error: 'ita_registration_failed', details: itaResult.error }
    }
    // Store confirmation number before committing status change
    await db.query(
      `UPDATE invoices SET ita_confirmation_number = $1, ita_registered_at = NOW() WHERE id = $2`,
      [itaResult.confirmationNumber, invoiceId]
    )
  }

  // 2. Proceed with normal TAX_ISSUED transition
  await transitionToTaxIssued(invoiceId)
}
```

---

## Schema Delta

```sql
ALTER TABLE invoices ADD COLUMN ita_confirmation_number TEXT;
  -- Populated when ITA registration succeeds; NULL for exempt invoices or pre-feature invoices
ALTER TABLE invoices ADD COLUMN ita_registered_at TIMESTAMPTZ;
  -- Timestamp of successful ITA registration

ALTER TABLE tenant_settings ADD COLUMN ita_registration_enabled BOOLEAN NOT NULL DEFAULT false;
  -- Toggle: tenant must explicitly enable ITA registration (some tenants exempt by size)
ALTER TABLE tenant_settings ADD COLUMN ita_vat_number TEXT;
  -- Tenant's 9-digit VAT registration number (עוסק מורשה / ח.פ.)
  -- Separate from tenants.vat_number for settings isolation; synced at tenant profile update
```

System config table (admin-configurable):

```sql
CREATE TABLE IF NOT EXISTS system_config (
  key    TEXT PRIMARY KEY,
  value  TEXT NOT NULL,
  updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Seed:
INSERT INTO system_config (key, value) VALUES
  ('ita_threshold_ils', '20000');  -- ITA registration threshold in ILS
```

---

## Invoice Document Extension

When `ita_confirmation_number IS NOT NULL`, the invoice HTML template adds:

```html
<div class="ita-confirmation">
  <!-- Hebrew -->
  מספר אישור: {{ ita_confirmation_number }}
  <!-- English -->
  ITA Confirmation: {{ ita_confirmation_number }}
</div>
```

Positioned below the invoice totals section, above notes. Required by ITA.

---

## Settings UI

`/settings/invoicing` → "ITA E-Invoice Registration" section (shown only if `tenant.tier` is not `freelancer`, as Freelancer businesses are typically below threshold):

```
┌──────────────────────────────────────────────────────────────┐
│  ITA E-Invoice Registration (חשבונית ממוחשבת)               │
│                                                              │
│  ☐ Enable ITA registration for tax invoices                  │
│                                                              │
│  VAT number (עוסק מורשה):  [123456789_______]               │
│  Certificate file:          [Upload .p12 / .pfx] ✓ Uploaded  │
│  Certificate password:      [•••••••••••••••]               │
│                                                              │
│  [Test connection]  →  ✓ ITA Shaba API reachable             │
│                                                              │
│  Threshold: Invoices ≥ ₪20,000 (total) require registration. │
│  Updated annually by ITA. Current threshold: ₪20,000.        │
└──────────────────────────────────────────────────────────────┘
```

---

## Error Handling

| ITA API Error | Zync response |
|---------------|---------------|
| Certificate expired/invalid | 503 with `ita_auth_error`; prompt to re-upload certificate in settings |
| ITA service unavailable (5xx) | 503 with `ita_service_unavailable`; invoice NOT issued |
| Invalid invoice data (4xx) | 400 with `ita_validation_error` + ITA error message |
| Duplicate invoice number | 409 with `ita_duplicate`; staff must use a new invoice number |

Retrying after ITA service outage: invoice stays in `APPROVED` status; staff retries via the normal "Issue Tax Invoice" action once ITA is back up.

---

## API Extensions

```
POST /api/invoices/:id/issue-tax
     (existing endpoint — extended with ITA registration)
     New errors: 503 ita_service_unavailable, 503 ita_auth_error, 400 ita_validation_error

GET  /api/settings/ita/test
     → test ITA Shaba connection with stored certificate
       Response: { success: boolean, error?: string, endpoint: string }
       Requires: settings:write

PATCH /api/settings/ita
      → update ITA configuration
        body: { vat_number?, ita_registration_enabled? }
        Requires: settings:write
```

---

## Architecture Decisions

| Decision | Choice | Reason |
|----------|--------|--------|
| Synchronous ITA call at issue-tax | Not async queue | ITA confirmation number must appear on the invoice; can't issue without it; async would require a "pending ITA" status adding complexity |
| Block issue on ITA failure | Not allow with retry | ITA mandate — invoice without registration is legally invalid for qualifying amounts |
| Certificate stored in adapter_credentials | Same as other integration secrets | AES-256-GCM encrypted; consistent with Gmail/Telegram/SMTP pattern |
| Threshold in system_config | Not hard-coded | ITA changes threshold annually; admin can update without code deployment |
| Per-tenant enable flag | Not always-on | Not all tenants are VAT-registered or above threshold; opt-in prevents blocking small freelancers |
