# Invoice Adapter Setup UI

**Date:** 2026-05-31  
**Status:** Draft  
**Spec:** 127  
**Tier:** All tiers  
**Depends on:** `invoices-adapters`, `settings-module`, `foundation-auth-rbac`  
**Referenced by:** `invoices-adapters`, `settings-module`

---

## Overview

Spec 16 (`invoices-adapters`) defines the adapter interface and `adapter_credentials` table for Morning, iCount, Rivhit, Invoice4u, and Easycount — but has zero UI. Spec 25 (`settings-module`) mentions "connect flow: enter API credentials → test connection → save encrypted" in one sentence. This spec defines the full per-adapter setup wizard UI at `/settings/integrations/invoicing`.

---

## Integration Hub Entry Point

In `/settings/integrations` (spec 25), the invoicing section:

```
┌──────────────────────────────────────────────────────────────┐
│  Invoicing Adapters                                          │
│                                                              │
│  ┌──────────────────┐  ┌──────────────────┐                  │
│  │  🟢 Morning       │  │  ○ iCount         │                  │
│  │  Connected        │  │  Not connected    │                  │
│  │  [Configure →]    │  │  [Connect →]      │                  │
│  └──────────────────┘  └──────────────────┘                  │
│  ┌──────────────────┐  ┌──────────────────┐  ┌────────────┐  │
│  │  ○ Rivhit         │  │  ○ Invoice4u      │  │ ○ Easycount│  │
│  │  Not connected    │  │  Not connected    │  │ Not conn.  │  │
│  │  [Connect →]      │  │  [Connect →]      │  │ [Connect]  │  │
│  └──────────────────┘  └──────────────────┘  └────────────┘  │
│                                                              │
│  ⓘ Only one adapter can be active at a time.                │
└──────────────────────────────────────────────────────────────┘
```

One active adapter at a time. Connecting a new one deactivates the previous.

---

## Setup Wizard — Per-Adapter Sheet

**[Connect →]** → right-side sheet with 3 steps:

### Step 1: Credentials

Different fields per adapter:

**Morning (Green Invoice):**
```
│  Morning API Key                                             │
│  [API key_____________________________]                      │
│  API Secret                                                  │
│  [API secret__________________________]                      │
│                                                              │
│  Find these at: green.co.il → Settings → API                │
│  [Help: Morning API setup ↗]                                 │
```

**iCount:**
```
│  Company ID   [____________]                                 │
│  Username     [____________]                                 │
│  Password     [____________]  (stored encrypted)            │
│                                                              │
│  Find these at: icount.co.il → Admin → API Access           │
```

**Rivhit:**
```
│  API Token    [____________]                                 │
│  Company Code [____________]                                 │
```

**Invoice4u:**
```
│  API Key      [____________]                                 │
│  User Token   [____________]                                 │
```

**Easycount:**
```
│  API Key      [____________]                                 │
│  Account ID   [____________]                                 │
```

All credential fields: `type="password"` with show/hide toggle. Never shown after initial entry.

---

### Step 2: Test Connection

After entering credentials → **[Test connection]** button:

```
│  Testing connection to Morning...                            │
│  ✓ Connected — Morning API v3 (company: Acme Ltd)            │
│  ✓ Create invoice: OK                                        │
│  ✓ Create credit note: OK                                    │
│                                                              │
│  [Back]                      [Save & activate →]            │
```

On failure:
```
│  ✗ Connection failed                                         │
│  Invalid API key. Check your Morning credentials.            │
│                                                              │
│  [Try again]                                                 │
```

`testConnection()` from adapter interface (spec 16 line 23).

---

### Step 3: Automation Settings

```
│  When to sync invoices                                       │
│                                                              │
│  ☑ Sync on TAX_ISSUED (when invoice status = tax invoice)    │
│  ☐ Sync on SENT (sync proforma invoices too)                 │
│  ☑ Sync credit notes automatically                           │
│                                                              │
│  Webhook (optional — auto-receive payment status)           │
│  Endpoint URL: https://app.zync.is/api/webhooks/billing/morning │
│  [Copy URL]                                                  │
│                                                              │
│  Set this URL in Morning: Settings → Webhooks                │
│  [Help: Morning webhook setup ↗]                             │
│                                                              │
│  [Back]              [Complete setup]                        │
```

---

## Active Adapter Configuration Page

After connecting, **[Configure →]** opens the same sheet in edit mode:

```
┌──────────────────────────────────────────────────────────────┐
│  Morning — Connected                                  [✕]    │
│                                                              │
│  Status: ✓ Active · Last sync: Today 14:23                   │
│  Company: Acme Ltd (confirmed via API)                        │
│                                                              │
│  Automation settings                                         │
│  ☑ Sync on TAX_ISSUED                                        │
│  ☐ Sync on SENT                                              │
│  ☑ Sync credit notes                                         │
│                                                              │
│  [Update credentials]  [Test connection]  [Disconnect]       │
└──────────────────────────────────────────────────────────────┘
```

**[Disconnect]** → confirmation: "This will stop syncing invoices to Morning. Existing Morning invoices will not be deleted."

---

## Sync Log

The configuration page's status line (`Last sync: Today 14:23`) is a summary; **[View sync log]** beside it opens a browsable per-row log so staff can drill into individual push attempts and diagnose errors. It reads the existing `integration_sync_logs` table (spec 16) — no new table.

```
┌──────────────────────────────────────────────────────────────────────────┐
│  Morning — Sync log                                                [✕]   │
│  [Status: All ▾]  [Type: All ▾]  [Last 30 days ▾]                         │
│                                                                          │
│  Time              Entity            Dir    Status    Detail             │
│  ──────────────────────────────────────────────────────────────────────  │
│  06-01 14:23  invoice  INV-0042      push   ✓ success  ext #320-8841     │
│  06-01 14:21  credit_note CN-00007   push   ✓ success  ext #305-112      │
│  06-01 09:02  invoice  INV-0039      push   ✗ error    Invalid VAT id ›  │
│  05-31 18:40  payment  INV-0031      push   ✓ success  —                 │
│                                                                          │
│  Showing 1–50 of 612                              [‹ Prev]  [Next ›]      │
└──────────────────────────────────────────────────────────────────────────┘
```

Columns: `created_at`, `entity_type` + entity number, `direction`, `status` badge, and a detail cell (external id on success, `error_message` on failure). An errored row expands (`›`) to show `request_payload` / `response_payload` for debugging. Filters (`status`, `entity_type`, date range) map to the query params below. Failed rows offer **[Retry push]** → `POST /api/invoices/:id/push` (spec 16). Requires `settings:read` (matching spec 16's "View sync logs" permission).

---

## Schema Delta

No new tables. Uses `adapter_credentials` (spec 16) with existing columns. Add columns:

```sql
ALTER TABLE adapter_credentials
  ADD COLUMN sync_on_sent BOOLEAN NOT NULL DEFAULT false,
  ADD COLUMN sync_on_tax_issued BOOLEAN NOT NULL DEFAULT true,
  ADD COLUMN sync_credit_notes BOOLEAN NOT NULL DEFAULT true,
  ADD COLUMN last_sync_at TIMESTAMPTZ,
  ADD COLUMN last_sync_status TEXT;  -- 'ok'|'error'
```

---

## API

```
GET /api/settings/integrations/invoicing
    → list adapter statuses (without credential values)
      Returns: [{ adapter_id, status: 'active'|'inactive', last_sync_at, last_sync_status, company_name? }]
      Requires: admin

POST /api/settings/integrations/invoicing/:adapterId/test
     → test adapter credentials
       body: { credentials: { [key]: string } }
       Returns: { ok: boolean, error?: string, company_name?: string }
       Requires: admin

POST /api/settings/integrations/invoicing/:adapterId/connect
     → save credentials + activate adapter
       body: { credentials, sync_on_sent?, sync_on_tax_issued?, sync_credit_notes? }
       Requires: admin

PATCH /api/settings/integrations/invoicing/:adapterId
      → update automation settings only (not credentials)
        body: { sync_on_sent?, sync_on_tax_issued?, sync_credit_notes? }
        Requires: admin

DELETE /api/settings/integrations/invoicing/:adapterId
       → disconnect adapter (clear credentials, set inactive)
         Requires: admin

GET /api/settings/integrations/invoicing/:adapterId/sync-logs
    → browse per-row push/pull attempts from integration_sync_logs (spec 16)
      Query: status (success|error), entity_type (invoice|credit_note|payment),
             from, to, page, per_page
      Returns: [{ id, created_at, entity_type, entity_id, entity_number,
                  direction, status, external_id?, error_message?,
                  request_payload?, response_payload? }]
      Requires: settings:read
```

This is a thin browse view over `integration_sync_logs` (defined in `invoices-adapters`, spec 16). Spec 16 exposes the account-wide `GET /api/settings/integrations/invoicing/logs`; the `/invoicing/:adapterId/sync-logs` form here is the per-adapter, filterable variant used by this UI.

---

## Architecture Decisions

| Decision | Choice | Reason |
|----------|--------|--------|
| Wizard with test step | Not save-then-test | Bad credentials silently failing at sync time is worse UX; test-first ensures setup is correct |
| One active adapter | Not multiple active | Duplicate invoices in two systems cause reconciliation problems; one source of truth for external accounting |
| Credentials never shown after save | Not viewable | Security: re-entry is the right UX for credential rotation; showing encrypted then decrypted is risky |
| Per-adapter help links | Not generic docs | Each adapter has different credential locations; direct links to provider docs reduce support requests |
