URD: Customer
| Module | CORE-09 | Version | v0.6 |
|---|---|---|---|
| Status | In-progress | Date | 2026-06-04 |
Business documentation. This URD is Customer's feature list - each feature below is one Functional Area (
<AREA>). The same<AREA>keys the feature's PRDs (PRD-<AREA>-NNN) and tests (TC-<AREA>-NNN), and each feature is listed in the Delivery feature catalog. See the Feature Spine convention.
1. Purpose
Define user-facing requirements for Customer Relationship Management - keeping customer profiles under a brand, rewarding repeat buyers with loyalty points, growing a newsletter mailing list, and capturing sales inquiries for follow-up. The goal is a single place to know who a business's customers are and to engage them.
2. Scope
| Included | Excluded |
|---|---|
| Customer profiles scoped to the brand | Customer segmentation (Planned) |
| Customer-to-user account linking | Email / SMS campaign engine (Planned) |
| Loyalty point earning on completed orders | Point redemption on orders (Planned) |
| Newsletter subscribe / unsubscribe / statistics | Loyalty tiers (Planned) |
| Sales-inquiry capture and lifecycle | Lifetime-value analytics (Planned) |
| Technical API specifications (see developer docs) |
3. Definitions
| Term | Definition |
|---|---|
| Customer | A person known to a brand; modelled as a User with the fixed customer role, with no sign-in credentials by default |
| Brand (Organization) | The virtual brand layer a customer belongs to; customers are scoped to it, not to a single point-of-sale unit |
| Loyalty points | A point balance a customer earns when an order completes payment |
| Conversion rate | The per-merchant spend-per-point factor - how much order total equals one point (order total is divided by it) |
| Subscriber | An email address opted into the newsletter, with topics, locale, and a unique unsubscribe token |
| Inquiry | A lead captured from the public site, tracked through a NEW → assigned → replied → converted/lost lifecycle |
4. Conceptual Model
Conceptual only. Full schema lives in the developer docs: Identity domain model, Sale - Customer Points, and Outreach domain model.
5. Feature Catalog
The feature list of this module. Each row is one feature (a Functional Area). Detail in §6. Mirrored in the Delivery feature catalog.
| Feature ID | Feature | Phase | Status | Priority |
|---|---|---|---|---|
CUS | Customer Profiles | P1 | Built | High |
PNT | Loyalty Points | P2 | Built | Medium |
SUB | Newsletter Subscribers | P2 | Built | Medium |
INQ | Sales Inquiries | P2 | Built | Medium |
BIZ | Business customers & groups | P2 | Planned | High |
Status: live from Plane where mapped, otherwise registry-declared. Vocabulary mirrors Plane (state-group / phase).
6. Features
One sub-section per feature, in catalog order. Each feature keeps its description, requirements, and acceptance together. Priority = MoSCoW (Must / Should / Could / Won't).
CUS - Customer Profiles Built
Feature ID: crm/CUS · Phase: P1 · PRDs: - · Dev: @nx/identity
What it does for users: staff keep a directory of customers under their brand - name, emails, phones, birthday, locale - scoped to the Organization, attachable to orders, and soft-deleted while preserving history.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-CUS-001 | M | Create a customer with name, at least one email and one phone |
| URD-CUS-002 | M | Scope every customer to the brand (Organization) |
| URD-CUS-003 | M | Update a customer's profile (name, emails, phones, birthday, locale) |
| URD-CUS-004 | M | Soft-delete a customer, preserving history |
| URD-CUS-005 | S | Staff see and manage only customers within their own brand |
| URD-CUS-006 | S | Attach a customer to a sale order at checkout |
| URD-CUS-007 | C | Promote a customer to a full sign-in user account |
Acceptance
AC-CUS-01: Customer profile lifecycle
| Given | When | Then |
|---|---|---|
| A staff member of a brand | Creates a customer with name + phone + email | Customer is created and scoped to that brand |
| A customer of another brand | The staff member searches/opens it | It is not visible (brand isolation) |
| A customer with linked orders | The staff member soft-deletes it | Customer leaves the active list; record and orders are preserved |
PNT - Loyalty Points Built
Feature ID: crm/PNT · Phase: P2 · PRDs: PRD-PNT-001 · Dev: @nx/sale
What it does for users: customers automatically earn a point balance when their order completes payment, computed by dividing the order total by a per-merchant conversion rate (spend-per-point) and flooring to whole points, awarded at most once per order, with every award recorded as an immutable ledger entry.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-PNT-001 | S | Award points when an order completes payment and a customer is attached |
| URD-PNT-002 | S | Compute the award as floor(order total ÷ the per-merchant conversion rate) - the rate is spend-per-point |
| URD-PNT-003 | S | Track a running point balance per customer, incremented atomically |
| URD-PNT-004 | S | Award points at most once per order, even on retry (idempotent by order) |
| URD-PNT-005 | C | Award no points when the rate is unset / not positive, computed points ≤ 0, or no customer is attached |
| URD-PNT-006 | S | Record every award as an immutable ledger entry (type, points, applied rate, links to customer / order / merchant), written atomically with the balance increment |
| URD-PNT-007 | C | Browse a customer's point history via a merchant-scoped, read-only ledger |
Acceptance
AC-PNT-01: Loyalty points earn
| Given | When | Then |
|---|---|---|
| A customer attached to an order; rate configured | The order completes payment | Points = floor(order total ÷ rate) are added to the balance and one ledger entry is written |
| The same order payment event is redelivered | Points would be awarded again | No double award - the balance and ledger are unchanged |
| No customer attached, or rate is not positive / unset | The order completes payment | No points are awarded; no entry is written (safe no-op, not an error) |
| The order total is smaller than the rate | The order completes payment | Computed points floor to zero, so nothing is awarded |
AC-PNT-02: Point ledger is read-only and merchant-scoped
| Given | When | Then |
|---|---|---|
| Awarded point entries for a merchant | Staff open the point ledger with the merchant scope | They see the customer's earn entries (type, points, applied rate, order link) |
| A staff member without the merchant scope | They request the ledger | Entries of that merchant are not returned |
| Any external client | It attempts to create / edit / delete an entry | The ledger exposes no such operation - entries are written only by the earn seam |
SUB - Newsletter Subscribers Built
Feature ID: crm/SUB · Phase: P2 · PRDs: - · Dev: @nx/outreach
What it does for users: site visitors opt into the newsletter by email with topics and locale; subscription is idempotent, one-click unsubscribe works via a unique token, and admins see subscriber statistics.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-SUB-001 | S | Subscribe by email with topics and locale |
| URD-SUB-002 | S | Keep email globally unique among subscribers |
| URD-SUB-003 | S | Unsubscribe via a unique one-click token link |
| URD-SUB-004 | S | Resubscribe reactivates a deactivated subscriber (idempotent) |
| URD-SUB-005 | S | View subscriber statistics - totals, monthly new, counts by status |
Acceptance
AC-SUB-01: Newsletter subscribe / unsubscribe
| Given | When | Then |
|---|---|---|
| A new email | Subscribe | Subscriber is created with topics, locale, and an unsubscribe token |
| An already-active email | Subscribe again | Returns the existing subscriber (idempotent) |
| A deactivated subscriber | Subscribe again | The subscriber is reactivated |
| A valid unsubscribe token | The link is clicked | The subscriber is deactivated |
INQ - Sales Inquiries Built
Feature ID: crm/INQ · Phase: P2 · PRDs: - · Dev: @nx/outreach
What it does for users: leads from the public site submit inquiries with contact, business info, and a message; admins are notified in real time, and sales reps move each inquiry through a NEW → assigned → replied → converted/lost lifecycle.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-INQ-001 | S | Capture an inquiry with contact info, business info, and message |
| URD-INQ-002 | S | Notify admins in real time when a new inquiry is submitted |
| URD-INQ-003 | S | Track assignment, reply, conversion, and lost reason |
| URD-INQ-004 | S | Move an inquiry through NEW → assigned → replied → converted/lost |
Acceptance
AC-INQ-01: Sales inquiry
| Given | When | Then |
|---|---|---|
| A site visitor | Submits an inquiry | Status is NEW and admins are notified in real time |
| A sales rep | Assigns it to self | The inquiry shows the assignee |
| A sales rep | Replies | Reply author and timestamp are recorded |
| A sales rep | Marks converted (or lost with reason) | Outcome and timestamp are recorded |
BIZ - Business Customers & Groups Planned
Feature ID: customer/BIZ · Phase: P2 (Jul) · PRDs: PRD-BIZ-001 · Dev: @nx/identity
What it does for users: merchants manage company customers - with tax code (MST) - so company VAT invoices carry the buyer's MST, and place customers into groups (VIP, member) for pricing and targeting.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-BIZ-001 | M | A customer record can carry business fields: tax code (MST), company name |
| URD-BIZ-002 | M | Business and individual customers are clearly distinguished |
| URD-BIZ-003 | M | The company's MST flows onto its VAT invoice |
| URD-BIZ-004 | M | Customer groups: create groups, assign customers |
| URD-BIZ-005 | S | A customer's group is available as a pricing condition (consumed by Pricing in Aug) |
| URD-BIZ-006 | S | Customer management screens exist in client and BO |
| URD-BIZ-007 | C | Group rules (automatic membership) |
Acceptance
AC-BIZ-01: Company VAT invoice
| Given | When | Then |
|---|---|---|
| A business customer with a valid MST | A B2B sale issues a VAT invoice | The invoice carries the buyer's MST and company name |
| A customer in group "VIP" | Listed/filtered | Group membership is visible and filterable |
7. Constraints & Non-Goals
Constraints
| ID | Constraint |
|---|---|
| C-01 | Customers are scoped to a brand (Organization), not a single point-of-sale unit |
| C-02 | A customer has the fixed customer role and no sign-in credentials by default |
| C-03 | Point award is idempotent - at most one award per order |
| C-04 | Subscriber email is globally unique; unsubscribe token is globally unique |
| C-05 | Subscribe is idempotent (returns existing or reactivates) |
| C-06 | All records use soft-delete |
Non-Goals
- Customer segmentation and targeting
- Email / SMS campaign engine
- Point redemption on orders, and loyalty tiers
- Customer lifetime-value analytics
8. Version History
| Date | Author | Description | Ver |
|---|---|---|---|
| 2026-02-26 | P. Do - Product Owner | Initial customer profiles, points, subscribers, inquiries | v0.1 |
| 2026-04-16 | P. Do - Product Owner | Reworked around subscribers and inquiries | v0.3 |
| 2026-05-30 | Docs migration | Restructured to module convention; areas CUS/PNT/SUB/INQ aligned to built behavior; status-honest priorities | v0.4 |
| 2026-06-04 | Claude (AI pair) | Reorganize by feature (Feature Spine) | v0.5 |
| 2026-06-15 | Sale squad | PNT made code-accurate (spend-per-point divisor); added ledger requirements PNT-006/007 + AC-PNT-02; linked PRD-PNT-001 | v0.6 |