Add planning docs for trade-ins, returns, tax exemptions, cycle counts, POs, bundles, backorders, barcode labels, instrument sizing, warranties, maintenance schedules, gift cards, layaway, rental agreements, and in-home trials
This commit is contained in:
@@ -224,7 +224,19 @@ timestamptz
|
||||
|
||||
Individual physical units for serialized items and rental fleet instruments.
|
||||
|
||||
id, product_id, company_id, location_id, serial_number,condition (new|excellent|good|fair|poor),status (available|sold|rented|in_repair|retired),purchase_date, purchase_cost, notes,legacy_id, created_at
|
||||
id, product_id, company_id, location_id, serial_number,condition (new|excellent|good|fair|poor),status (available|sold|rented|on_trial|in_repair|layaway|lost|retired),purchase_date, purchase_cost, notes,legacy_id, created_at
|
||||
|
||||
### Status Values
|
||||
|
||||
Status | Description | Set By
|
||||
available | In stock, ready for sale or rental | Default, return, restock
|
||||
sold | Purchased by customer | Sale transaction
|
||||
rented | Out on active rental contract | Rental activation
|
||||
on_trial | Out with customer on in-home trial | In-home trial checkout (07_Domain_Sales_POS.md §9)
|
||||
in_repair | In repair shop for service | Repair intake
|
||||
layaway | Reserved for layaway customer, not available | Layaway creation (08_Domain_Payments_Billing.md §9)
|
||||
lost | Unrecovered — trial, rental, or inventory discrepancy | Overdue escalation or cycle count
|
||||
retired | Permanently removed from inventory | Manual retirement
|
||||
|
||||
|
||||
|
||||
@@ -924,4 +936,313 @@ timestamptz
|
||||
|
||||
- Parts cost always recorded at time of use — price changes do not affect historical records
|
||||
|
||||
- Negative qty_on_hand not permitted — system warns technician when stock is insufficient
|
||||
- Negative qty_on_hand not permitted — system warns technician when stock is insufficient
|
||||
|
||||
|
||||
|
||||
# 8. Instrument Sizing
|
||||
|
||||
String instruments come in fractional sizes — critical for rentals, school orders, and customer matching. Size is tracked on both the product catalog and individual inventory units.
|
||||
|
||||
## 8.1 Size Values
|
||||
|
||||
Size | Instruments | Notes
|
||||
4/4 (full) | Violin, viola, cello, bass, guitar | Default adult size
|
||||
3/4 | Violin, cello, bass, guitar | Older students, smaller adults
|
||||
1/2 | Violin, cello, bass | Intermediate students
|
||||
1/4 | Violin, cello | Younger students
|
||||
1/8 | Violin | Beginner young students
|
||||
1/10 | Violin | Smallest common size
|
||||
1/16 | Violin | Rare — very young students
|
||||
15" | Viola | Measured in inches for viola
|
||||
15.5" | Viola | Common intermediate
|
||||
16" | Viola | Full-size standard
|
||||
16.5" | Viola | Large full-size
|
||||
|
||||
## 8.2 Schema Changes
|
||||
|
||||
**product** — add column:
|
||||
|
||||
Column | Type | Notes
|
||||
instrument_size | varchar | Nullable — only set for sized instruments. Free text to support both fractional (1/2) and inch (15.5") formats.
|
||||
|
||||
**inventory_unit** — add column:
|
||||
|
||||
Column | Type | Notes
|
||||
instrument_size | varchar | Nullable — size of this specific physical unit. May differ from product default (e.g. product "Yamaha Model 5 Violin" has units in multiple sizes).
|
||||
|
||||
## 8.3 Business Rules
|
||||
|
||||
- Size is optional — only relevant for sized instruments (strings, some guitars)
|
||||
- Product-level size is the default/catalog size — inventory units can override per-unit
|
||||
- Size is searchable and filterable in product lists and POS lookup
|
||||
- Rental matching: when a student needs a size, search filters by instrument_size
|
||||
- Size changes on rental returns are common (student grew) — logged in rental history
|
||||
|
||||
|
||||
|
||||
# 9. Inventory Cycle Counts
|
||||
|
||||
Physical inventory reconciliation ensures system counts match actual shelf counts. Cycle counts can be full-store or targeted (by category, location area, or supplier).
|
||||
|
||||
## 9.1 count_session
|
||||
|
||||
Column | Type | Notes
|
||||
id | uuid PK |
|
||||
company_id | uuid FK | Tenant scoping
|
||||
location_id | uuid FK | Which location is being counted
|
||||
name | varchar | e.g. "Q1 2025 Full Count", "Guitar Room Spot Check"
|
||||
count_type | enum | full | category | spot
|
||||
status | enum | draft | in_progress | review | completed | cancelled
|
||||
category_id | uuid FK | Nullable — set if count_type = category
|
||||
started_by | uuid FK | Employee who initiated
|
||||
started_at | timestamptz | When counting began
|
||||
completed_at | timestamptz | When finalized
|
||||
notes | text |
|
||||
created_at | timestamptz |
|
||||
|
||||
## 9.2 count_entry
|
||||
|
||||
One row per product counted in a session. For serialized products, one row per unit.
|
||||
|
||||
Column | Type | Notes
|
||||
id | uuid PK |
|
||||
count_session_id | uuid FK |
|
||||
product_id | uuid FK |
|
||||
inventory_unit_id | uuid FK | Nullable — set for serialized items
|
||||
expected_qty | integer | System qty at time count started
|
||||
counted_qty | integer | Physical count entered by staff
|
||||
variance | integer | counted_qty - expected_qty (computed)
|
||||
counted_by | uuid FK | Employee who counted this item
|
||||
counted_at | timestamptz | When this entry was recorded
|
||||
notes | text | Explanation for variance
|
||||
created_at | timestamptz |
|
||||
|
||||
## 9.3 count_adjustment
|
||||
|
||||
When a count session is completed, variances are applied as inventory adjustments. Each adjustment is an auditable record.
|
||||
|
||||
Column | Type | Notes
|
||||
id | uuid PK |
|
||||
count_session_id | uuid FK |
|
||||
count_entry_id | uuid FK |
|
||||
product_id | uuid FK |
|
||||
inventory_unit_id | uuid FK | Nullable
|
||||
previous_qty | integer | qty_on_hand before adjustment
|
||||
adjusted_qty | integer | qty_on_hand after adjustment
|
||||
adjustment_reason | enum | cycle_count | damaged | stolen | found | data_entry_error
|
||||
approved_by | uuid FK | Manager who approved the adjustment
|
||||
approved_at | timestamptz |
|
||||
created_at | timestamptz |
|
||||
|
||||
## 9.4 Cycle Count Workflow
|
||||
|
||||
1. Manager creates count session — selects scope (full, category, or spot check)
|
||||
2. System snapshots expected quantities for all products in scope
|
||||
3. Staff count physical inventory — enter counts per product/unit
|
||||
4. System calculates variances and flags discrepancies
|
||||
5. Manager reviews variances — adds reason codes for each
|
||||
6. Manager approves adjustments — qty_on_hand updated, adjustment records created
|
||||
7. Session marked completed — immutable after completion
|
||||
|
||||
## 9.5 Business Rules
|
||||
|
||||
- Count sessions lock affected products from sale/receiving while in_progress — prevents count drift
|
||||
- Variances above a configurable threshold (default: 5% or $50 value) require manager approval
|
||||
- All adjustments are append-only audit records — never modified after creation
|
||||
- Serialized items: count confirms unit is present and in expected status
|
||||
- Completed sessions cannot be reopened — start a new session to re-count
|
||||
- Spot checks do not lock inventory — used for quick verification without disrupting sales
|
||||
|
||||
|
||||
|
||||
# 10. Purchase Orders
|
||||
|
||||
Formal purchase order workflow extends the existing stock_receipt flow. POs track what was ordered, what was received, and flag discrepancies.
|
||||
|
||||
## 10.1 purchase_order
|
||||
|
||||
Column | Type | Notes
|
||||
id | uuid PK |
|
||||
company_id | uuid FK | Tenant scoping
|
||||
location_id | uuid FK | Receiving location
|
||||
po_number | varchar | Human-readable PO number (auto-generated)
|
||||
supplier_id | uuid FK |
|
||||
status | enum | draft | submitted | partial | received | cancelled
|
||||
order_date | date | When PO was sent to supplier
|
||||
expected_date | date | Expected delivery date
|
||||
received_date | date | When fully received
|
||||
subtotal | numeric(10,2) | Sum of line totals
|
||||
shipping_cost | numeric(10,2) |
|
||||
tax | numeric(10,2) |
|
||||
total | numeric(10,2) |
|
||||
notes | text | Internal notes or special instructions
|
||||
created_by | uuid FK | Employee who created
|
||||
created_at | timestamptz |
|
||||
updated_at | timestamptz |
|
||||
|
||||
## 10.2 purchase_order_line
|
||||
|
||||
Column | Type | Notes
|
||||
id | uuid PK |
|
||||
purchase_order_id | uuid FK |
|
||||
product_id | uuid FK |
|
||||
supplier_sku | varchar | Supplier's SKU for this product
|
||||
description | varchar | Line item description
|
||||
qty_ordered | integer |
|
||||
qty_received | integer | Updated as items arrive — default 0
|
||||
unit_cost | numeric(10,2) | Agreed cost per unit
|
||||
line_total | numeric(10,2) | qty_ordered * unit_cost
|
||||
created_at | timestamptz |
|
||||
|
||||
## 10.3 Three-Way Match
|
||||
|
||||
When receiving against a PO, the system compares:
|
||||
1. **PO line** — what was ordered (qty_ordered, unit_cost)
|
||||
2. **Packing slip** — what supplier says they shipped (entered by staff at receiving)
|
||||
3. **Physical count** — what actually arrived (counted by staff)
|
||||
|
||||
Discrepancies flagged: short shipment, over shipment, wrong item, cost mismatch.
|
||||
|
||||
## 10.4 PO → Stock Receipt Flow
|
||||
|
||||
- Receiving against a PO auto-creates stock_receipt records for each line received
|
||||
- stock_receipt.purchase_order_id links receipt back to originating PO
|
||||
- Partial receives update PO status to "partial" — remaining lines stay open
|
||||
- Final receive updates PO status to "received"
|
||||
|
||||
## 10.5 Business Rules
|
||||
|
||||
- POs in draft status are editable — submitted POs are locked (create new revision if needed)
|
||||
- Supplier auto-populated from product's preferred supplier if set
|
||||
- Unit cost defaults to last stock_receipt cost for that product from that supplier
|
||||
- PO approval workflow optional — configurable threshold requiring manager sign-off
|
||||
- Cancelled POs retained for audit — soft cancel with reason code
|
||||
- Reorder report: products below qty_reorder_point generate suggested PO lines grouped by preferred supplier
|
||||
|
||||
|
||||
|
||||
# 11. Product Bundles & Kits
|
||||
|
||||
Bundles group multiple products into a single sellable item at a package price. Common in music retail: instrument + case + accessories starter packs.
|
||||
|
||||
## 11.1 product_bundle
|
||||
|
||||
Column | Type | Notes
|
||||
id | uuid PK |
|
||||
company_id | uuid FK | Tenant scoping
|
||||
product_id | uuid FK | The bundle's parent product record (is_bundle = true)
|
||||
created_at | timestamptz |
|
||||
|
||||
## 11.2 product_bundle_item
|
||||
|
||||
Column | Type | Notes
|
||||
id | uuid PK |
|
||||
bundle_id | uuid FK | References product_bundle
|
||||
component_product_id | uuid FK | The included product
|
||||
qty | integer | How many of this component per bundle (default 1)
|
||||
sort_order | integer | Display order in bundle breakdown
|
||||
created_at | timestamptz |
|
||||
|
||||
## 11.3 Schema Changes
|
||||
|
||||
**product** — add column:
|
||||
|
||||
Column | Type | Notes
|
||||
is_bundle | boolean | Default false. True = this product is a bundle of other products.
|
||||
|
||||
## 11.4 Pricing Models
|
||||
|
||||
Model | Description
|
||||
fixed | Bundle has a set price — component prices ignored at POS
|
||||
sum_discount | Bundle price = sum of component prices minus a bundle discount (percent or fixed)
|
||||
|
||||
Bundle price is stored on the parent product.price field. The pricing model determines how the discount is displayed on receipts (single line vs itemized with discount).
|
||||
|
||||
## 11.5 Inventory Behavior
|
||||
|
||||
- Bundle does not have its own qty_on_hand — availability derived from component stock
|
||||
- Bundle is "in stock" only if ALL components are in stock at required quantities
|
||||
- Selling a bundle decrements each component product's qty_on_hand (or marks serialized units as sold)
|
||||
- Stock receipt never targets a bundle directly — components are received individually
|
||||
- Low stock alert triggers if any component falls below its reorder point
|
||||
|
||||
## 11.6 Business Rules
|
||||
|
||||
- A bundle component cannot itself be a bundle (no nesting)
|
||||
- Bundles appear in POS search like any product — clearly labeled as bundle
|
||||
- Receipt shows bundle name and price, with component breakdown below
|
||||
- Returning a bundle returns all components — partial bundle returns handled as individual item returns
|
||||
- Bundle price must be less than or equal to sum of component prices (enforced at creation)
|
||||
|
||||
|
||||
|
||||
# 12. Barcode Label Printing
|
||||
|
||||
Bulk label printing for inventory receiving, repricing, and cycle count preparation.
|
||||
|
||||
## 12.1 Label Templates
|
||||
|
||||
Template | Use Case | Content
|
||||
standard_price | Shelf labels | SKU, name, price, barcode (UPC or SKU)
|
||||
serialized | Individual units | Serial number, SKU, name, barcode (serial)
|
||||
clearance | Sale items | SKU, name, original price (struck), sale price, barcode
|
||||
receiving | Incoming stock | SKU, name, price, received date, barcode
|
||||
bundle | Bundle items | Bundle name, bundle price, component list, barcode
|
||||
|
||||
## 12.2 Print Jobs
|
||||
|
||||
Label printing is triggered from:
|
||||
- **Stock receipt** — auto-prompt to print labels for received items
|
||||
- **Price change** — bulk print updated labels for repriced items
|
||||
- **Cycle count prep** — print labels for products missing barcodes
|
||||
- **Manual** — select products from inventory list, choose template, print
|
||||
|
||||
## 12.3 Technical
|
||||
|
||||
- Labels rendered server-side as PDF (ZPL support planned for thermal printers)
|
||||
- Standard label sizes: 1.25" x 0.875" (Dymo), 2" x 1" (thermal roll), 4" x 6" (shipping)
|
||||
- Barcode formats: Code 128 (SKU-based), UPC-A (manufacturer UPC), QR code (serial number)
|
||||
- Print queue supports batching — up to 500 labels per job
|
||||
|
||||
|
||||
|
||||
# 13. Backorders
|
||||
|
||||
Customer order queue for out-of-stock items. Tracks demand and notifies customers when stock arrives.
|
||||
|
||||
## 13.1 backorder
|
||||
|
||||
Column | Type | Notes
|
||||
id | uuid PK |
|
||||
company_id | uuid FK | Tenant scoping
|
||||
location_id | uuid FK |
|
||||
product_id | uuid FK | What was requested
|
||||
account_id | uuid FK | Who wants it
|
||||
member_id | uuid FK | Nullable — specific member on account
|
||||
qty | integer | Quantity requested
|
||||
status | enum | pending | ordered | received | fulfilled | cancelled
|
||||
purchase_order_line_id | uuid FK | Nullable — linked to PO when ordered from supplier
|
||||
deposit_transaction_id | uuid FK | Nullable — if deposit collected
|
||||
notes | text |
|
||||
requested_date | date | When customer placed backorder
|
||||
fulfilled_date | date | When customer received item
|
||||
created_by | uuid FK | Employee who took the order
|
||||
created_at | timestamptz |
|
||||
updated_at | timestamptz |
|
||||
|
||||
## 13.2 Workflow
|
||||
|
||||
1. Customer wants an out-of-stock product — staff creates backorder
|
||||
2. Optional: collect deposit via POS (deposit_transaction_id recorded)
|
||||
3. When creating next PO for that supplier, backorder quantities surfaced as suggested lines
|
||||
4. Stock receipt against PO triggers backorder match — status updated to "received"
|
||||
5. Staff notified to contact customer — notification sent via preferred channel
|
||||
6. Customer picks up item — backorder marked "fulfilled", deposit applied to sale
|
||||
|
||||
## 13.3 Business Rules
|
||||
|
||||
- Multiple backorders can exist for the same product — filled in request date order (FIFO)
|
||||
- Backorder quantities included in reorder report alongside reorder point calculations
|
||||
- Cancelled backorders refund any deposit collected
|
||||
- Backorder demand report: shows products with pending backorders — informs purchasing decisions
|
||||
Reference in New Issue
Block a user