# Customer Settings

**Date:** 2026-06-01
**Status:** Draft
**Spec:** 152
**Tier:** All tiers
**Depends on:** `customers-module`, `customer-portal-access-control`, `tenant-portals`, `foundation-auth-rbac`
**Referenced by:** `customers-module`, `tenant-portals`

---

## Overview

Spec 9 (`customers-module`) defines customer and portal user CRUD. Spec 82 (`customer-portal-access-control`) defines portal visibility rules per entity. No spec defines tenant-level *defaults* for customer management: default payment terms, automatic portal invitation behavior, and what new customers see by default. This spec adds `/settings/customers`.

---

## Route

`/settings/customers` — requires `customers:write`.

---

## Page Layout

```
┌──────────────────────────────────────────────────────────────┐
│  Settings > Customers                                        │
│                                                              │
│  ┌── Customer Defaults ─────────────────────────────────┐  │
│  │                                                        │  │
│  │  Default payment terms                                 │  │
│  │  [30___] days  (pre-fills new invoice payment terms)   │  │
│  │  Note: also editable in Settings > Invoices            │  │
│  │                                                        │  │
│  │  Default customer currency                             │  │
│  │  [ILS ▾]                                               │  │
│  │                                                        │  │
│  └───────────────────────────────────────────────────────┘  │
│                                                              │
│  ┌── Portal Access Defaults ─────────────────────────────┐  │
│  │                                                        │  │
│  │  Automatically invite contacts to portal               │  │
│  │  ○ Never (manual invitation only)                      │  │
│  │  ● On customer creation (invite primary contact)       │  │
│  │  ○ On first invoice sent                               │  │
│  │                                                        │  │
│  │  Default portal role for new invites                   │  │
│  │  customer_viewer (portal access is read-only)          │  │
│  │                                                        │  │
│  └───────────────────────────────────────────────────────┘  │
│                                                              │
│  ┌── Portal Visibility Defaults ─────────────────────────┐  │
│  │  What new customers can see in their portal            │  │
│  │                                                        │  │
│  │  ☑ Invoices                                            │  │
│  │  ☑ Projects (name + status only)                       │  │
│  │  ☑ Support tickets (their own)                         │  │
│  │  ☑ Contracts (their own)                               │  │
│  │  ☑ Proposals                                           │  │
│  │  ☑ Shared files                                        │  │
│  │  ☑ Time summary                                        │  │
│  │                                                        │  │
│  │  (Applies tenant-wide; no per-customer override)       │  │
│  │                                                        │  │
│  └───────────────────────────────────────────────────────┘  │
│                                                              │
│  [Save changes]                                              │
└──────────────────────────────────────────────────────────────┘
```

---

## Schema Delta

`default_payment_terms_days` already exists in `tenant_settings` (owned by `invoices-core`). The new columns:

```sql
ALTER TABLE tenant_settings
  ADD COLUMN IF NOT EXISTS customer_default_currency TEXT NOT NULL DEFAULT 'ILS',
  ADD COLUMN IF NOT EXISTS customer_auto_invite_portal TEXT NOT NULL DEFAULT 'never'
    CHECK (customer_auto_invite_portal IN ('never', 'on_creation', 'on_first_invoice')),
  ADD COLUMN IF NOT EXISTS customer_default_portal_role TEXT NOT NULL DEFAULT 'customer_viewer'
    CHECK (customer_default_portal_role IN ('customer_viewer'));
```

Portal section visibility defaults are **tenant-level**: stored in the `tenant_settings.portal_visibility` JSONB column (owned by spec 82 / `customer-portal-access-control`), using the canonical `PortalVisibility` shape from `@zync/types` — the 7 keys `show_invoices`, `show_projects`, `show_tickets`, `show_contracts`, `show_proposals`, `show_files`, `show_time_summary`. This page edits those tenant defaults through spec 136's `/api/settings/portal` route; it defines **no** per-customer override column and adds **no** `portal_visibility` / `portal_show_*` columns of its own. (There is no per-customer visibility override in the corpus — visibility is enforced tenant-wide by spec 82's middleware.)

---

## Auto-invite Behavior

When `customer_auto_invite_portal` is `'on_creation'`:
- After `POST /api/customers` succeeds and the customer has at least one contact with an email address, enqueue a portal invitation for the primary contact (first contact or the one marked `is_primary = true`).
- Invitation email uses the standard portal invite template (spec 9).

When `customer_auto_invite_portal` is `'on_first_invoice'`:
- When `POST /api/invoices/:id/send` transitions an invoice to `SENT`, check if the invoice's customer has any `customer_portal_users`. If none, enqueue a portal invitation for the primary contact.

When `customer_auto_invite_portal` is `'never'`: no automatic invitations; staff must invite manually via customer detail.

---

## API

```
GET  /api/settings/customers
     → get customer defaults
       Returns: { default_payment_terms_days, customer_default_currency,
                  customer_auto_invite_portal, customer_default_portal_role }
       Requires: customers:read

PATCH /api/settings/customers
      → update customer defaults
        body: { ...any settable fields above }
        Requires: customers:write
```

---

## Architecture Decisions

| Decision | Choice | Reason |
|----------|--------|--------|
| Extend `tenant_settings` | Not new `customer_settings` table | Same pattern as contractor/invoice/project settings — scalar per-tenant config; no benefit from a separate table |
| Portal visibility stored tenant-level in `tenant_settings.portal_visibility` JSONB (spec 82) | Not per-customer columns, not flat booleans | Visibility is enforced tenant-wide by spec 82's middleware; a single JSONB keyed by the canonical `PortalVisibility` shape avoids per-customer drift and divergent column sets |
| Auto-invite modes as TEXT enum | Not booleans | Three states with clear semantics; a boolean `auto_invite` would lose the trigger distinction (on creation vs on first invoice) |
