# CRM + Customer Account Foundation v41

v41 introduces the first CRM business capability bundle.

## Customer master

`crm.customers` is tenant-scoped and stores:
- display/first/last name
- raw phone/email
- normalized phone/email for search
- optional customer group
- active/inactive lifecycle
- notes and metadata
- audit actor references

Anonymous selling remains valid: cart and sale customer fields stay nullable.

## Customer groups

`crm.customer_groups.group_key` is the canonical group key.

When a customer is attached to a cart, the group's key is copied into
`sales.carts.customer_group_key`.

Existing Pricing and Promotions already consume that string, so customer/group pricing
and targeting work without introducing a hard CRM dependency into those schemas.

## Cart customer assignment

Changing a customer on an open cart:
- requires expected cart version
- locks the cart
- assigns or clears customer
- copies canonical public ID + group key
- recalculates pricing/promotions
- increments cart version
- audits the change

This is important because customer and group scopes can change both price-list selection
and promotion eligibility.

## Sale snapshot

Checkout copies:
- internal `customer_id` link
- public customer ID
- customer group key
- immutable JSON customer snapshot

The snapshot preserves the commercial identity used at sale time even if the customer
profile changes later.

## Privacy

Standard CRM customer responses mask phone/email.

Unmasked phone/email/notes are exposed only through a separate endpoint protected by
`crm.customer.pii.view`.

This keeps field-level privacy explicit rather than relying on UI convention.

## Timeline

Customer timeline derives:
- completed sale count
- gross completed sales
- posted returns
- net sales after returns
- captured payments
- completed refunds
- last purchase
- chronological sale / return / payment / refund events

The timeline is derived reporting data; Sales/Payments remain sources of truth.

## API

- GET  `/api/v1/crm/customers`
- POST `/api/v1/crm/customers`
- GET  `/api/v1/crm/customers/{customerPublicId}`
- PATCH `/api/v1/crm/customers/{customerPublicId}`
- GET  `/api/v1/crm/customers/{customerPublicId}/sensitive`
- GET  `/api/v1/crm/customers/{customerPublicId}/timeline`
- GET  `/api/v1/crm/customer-groups`
- POST `/api/v1/crm/customer-groups`
- PUT  `/api/v1/sales/carts/{cartPublicId}/customer`

## Search path

The PostgreSQL test search path must include `crm`, because RefreshDatabase / migrate:fresh
must be able to clean cross-schema foreign-key dependencies.
