URD: Customer
| Module | Version | Updated |
|---|---|---|
CORE-09 | v0.6 | 2026-06-04 |
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 (Organizer) | 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.
| Feature ID | Feature | Phase | Epic | 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 | E5 | 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 Organizer, 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 (Organizer) |
| 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 (Organizer), 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 | Phát Nguyễn | PNT made code-accurate (spend-per-point divisor); added ledger requirements PNT-006/007 + AC-PNT-02; linked PRD-PNT-001 | v0.6 |