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:
@@ -228,4 +228,255 @@ Account charge
|
||||
|
||||
Internal
|
||||
|
||||
Charges to account balance — billed later
|
||||
Charges to account balance — billed later
|
||||
|
||||
|
||||
|
||||
# 6. Trade-In Workflow
|
||||
|
||||
Trade-ins are a core music retail workflow. A customer brings in a used instrument and receives credit toward a purchase. The trade-in creates both an inventory intake (new used product) and a credit applied to a transaction.
|
||||
|
||||
## 6.1 trade_in
|
||||
|
||||
Column | Type | Notes
|
||||
id | uuid PK |
|
||||
company_id | uuid FK | Tenant scoping
|
||||
location_id | uuid FK |
|
||||
account_id | uuid FK | Nullable — walk-in trade-ins allowed
|
||||
appraised_by | uuid FK | Employee who appraised
|
||||
instrument_description | varchar | What the customer is trading in
|
||||
serial_number | varchar | Nullable
|
||||
condition | enum | excellent | good | fair | poor
|
||||
appraisal_value | numeric(10,2) | What store offers the customer
|
||||
credit_applied | numeric(10,2) | Amount applied to transaction (may equal appraisal or less if partial)
|
||||
transaction_id | uuid FK | Nullable — the sale transaction where credit was applied
|
||||
product_id | uuid FK | Nullable — the product record created for the traded-in item
|
||||
inventory_unit_id | uuid FK | Nullable — the inventory unit created (if serialized)
|
||||
status | enum | appraised | accepted | declined | sold
|
||||
notes | text | Condition details, customer negotiation notes
|
||||
appraised_at | timestamptz |
|
||||
created_at | timestamptz |
|
||||
|
||||
## 6.2 Trade-In Flow
|
||||
|
||||
1. Customer brings instrument — staff appraises condition and fair market value
|
||||
2. Staff enters trade-in: description, serial, condition, appraisal value
|
||||
3. Customer accepts or declines the offer
|
||||
4. If accepted:
|
||||
a. New product/inventory_unit created for the traded-in instrument (used, condition noted)
|
||||
b. Trade-in credit applied to the current or future transaction
|
||||
c. Transaction shows trade-in credit as a line item (negative amount)
|
||||
5. Traded-in instrument enters inventory as available for resale
|
||||
|
||||
## 6.3 Business Rules
|
||||
|
||||
- Appraisal value is a negotiation — staff can adjust within min/max guidelines
|
||||
- Trade-in credit can exceed purchase price — difference paid as store credit (not cash refund)
|
||||
- Trade-in without immediate purchase: credit stored on account as store credit balance
|
||||
- Traded-in items default to "used" condition and get a new product record (not merged with new inventory)
|
||||
- Trade-in appraisal records are immutable — if value changes, create new appraisal
|
||||
- Trade-in value reported separately from sales discount for accounting purposes
|
||||
- Manager approval required for trade-in value above configurable threshold
|
||||
|
||||
|
||||
|
||||
# 7. Returns & Exchanges
|
||||
|
||||
Structured returns workflow beyond simple refund transactions. Handles RMA tracking, reason codes, restocking, and return window enforcement.
|
||||
|
||||
## 7.1 return_request
|
||||
|
||||
Column | Type | Notes
|
||||
id | uuid PK |
|
||||
company_id | uuid FK | Tenant scoping
|
||||
location_id | uuid FK |
|
||||
return_number | varchar | Human-readable RMA number (auto-generated)
|
||||
original_transaction_id | uuid FK | The sale being returned against
|
||||
account_id | uuid FK | Nullable — anonymous returns allowed with receipt
|
||||
status | enum | pending | approved | completed | denied
|
||||
return_type | enum | refund | exchange | store_credit
|
||||
initiated_by | uuid FK | Employee processing return
|
||||
approved_by | uuid FK | Nullable — manager if approval required
|
||||
reason_code | enum | defective | wrong_item | changed_mind | duplicate | other
|
||||
reason_detail | text | Free-text explanation
|
||||
restocking_fee | numeric(10,2) | Default 0 — configurable per category
|
||||
refund_amount | numeric(10,2) | Amount returned to customer (after restocking fee)
|
||||
refund_method | enum | original_payment | cash | store_credit
|
||||
refund_transaction_id | uuid FK | The refund transaction created
|
||||
created_at | timestamptz |
|
||||
updated_at | timestamptz |
|
||||
|
||||
## 7.2 return_line_item
|
||||
|
||||
Column | Type | Notes
|
||||
id | uuid PK |
|
||||
return_request_id | uuid FK |
|
||||
original_line_item_id | uuid FK | Line item from original transaction
|
||||
product_id | uuid FK |
|
||||
inventory_unit_id | uuid FK | Nullable — for serialized items
|
||||
qty_returned | integer |
|
||||
unit_price | numeric(10,2) | Price at time of original sale
|
||||
condition_returned | enum | new | excellent | good | fair | poor | defective
|
||||
restock | boolean | Whether item goes back to available inventory
|
||||
created_at | timestamptz |
|
||||
|
||||
## 7.3 Return Policies
|
||||
|
||||
return_policy — configurable per company, overridable per category:
|
||||
|
||||
Column | Type | Notes
|
||||
id | uuid PK |
|
||||
company_id | uuid FK |
|
||||
category_id | uuid FK | Nullable — null = company-wide default
|
||||
return_window_days | integer | Days after purchase returns are accepted (e.g. 30)
|
||||
restocking_fee_percent | numeric(5,2) | Default restocking fee (e.g. 15%)
|
||||
requires_receipt | boolean | Whether original receipt/transaction required
|
||||
requires_approval | boolean | Whether manager approval needed
|
||||
exchange_only | boolean | Some categories only allow exchange, not refund
|
||||
is_active | boolean |
|
||||
created_at | timestamptz |
|
||||
|
||||
## 7.4 Return Workflow
|
||||
|
||||
1. Customer requests return — staff looks up original transaction
|
||||
2. System checks return window — warns if outside policy (manager can override)
|
||||
3. Staff selects items being returned, enters reason code and condition
|
||||
4. System calculates refund amount (original price minus restocking fee if applicable)
|
||||
5. Manager approves if required by policy or amount threshold
|
||||
6. Refund processed: original payment method, cash, or store credit
|
||||
7. Returned items: restocked (condition updated) or marked defective (removed from available)
|
||||
|
||||
## 7.5 Exchange Flow
|
||||
|
||||
1. Return initiated with return_type = exchange
|
||||
2. Customer selects replacement item(s)
|
||||
3. System calculates difference — customer pays overage or receives credit for underage
|
||||
4. Single transaction captures both the return credit and the new sale
|
||||
5. Both the return and new sale are linked for reporting
|
||||
|
||||
## 7.6 Business Rules
|
||||
|
||||
- Return window enforced at POS — staff sees clear "within policy" / "outside policy" indicator
|
||||
- Outside-policy returns require manager override with reason
|
||||
- Defective items never restocked — routed to repair assessment or disposal
|
||||
- Serialized items: inventory unit status reverted from "sold" to "available" on restock
|
||||
- Non-serialized items: qty_on_hand incremented on restock
|
||||
- Refund to original payment method preferred — Stripe refund API called for card transactions
|
||||
- Store credit refunds create a credit balance on the account
|
||||
- No cash refunds above configurable threshold without manager approval
|
||||
- Return fraud tracking: system flags accounts with high return frequency
|
||||
|
||||
|
||||
|
||||
# 8. Tax Exemptions at POS
|
||||
|
||||
Tax-exempt customers (schools, churches, resellers) are common in music retail. Tax exemption is checked at transaction time and the exemption is recorded on each qualifying transaction.
|
||||
|
||||
## 8.1 Transaction Tax Flow
|
||||
|
||||
1. Transaction started — account attached (or anonymous)
|
||||
2. System checks account.tax_exempt_status:
|
||||
- If "approved": tax automatically set to $0.00 on all eligible lines
|
||||
- If "pending" or "none": standard tax calculation applies
|
||||
3. Tax exemption displayed on POS screen — staff sees "TAX EXEMPT" indicator
|
||||
4. Receipt and invoice show $0.00 tax with exemption reference number
|
||||
5. Tax-exempt transactions flagged in reporting for audit compliance
|
||||
|
||||
## 8.2 Manual Override
|
||||
|
||||
- Staff can manually mark a transaction as tax-exempt (e.g. walk-in school purchaser with certificate)
|
||||
- Manual exemptions require: certificate number entry and manager approval
|
||||
- Manual exemptions logged in tax_exemption_audit for compliance
|
||||
|
||||
## 8.3 Business Rules
|
||||
|
||||
- Tax exemption applies to the entire transaction, not individual line items
|
||||
- Resale certificates have expiration dates — system warns when nearing expiry
|
||||
- Expired certificates block auto-exemption until renewed — staff can override with manager approval
|
||||
- Tax-exempt transaction count and value reported monthly for state compliance
|
||||
- Rental transactions for schools are tax-exempt if the school account is exempt
|
||||
|
||||
|
||||
|
||||
# 9. In-Home Trials
|
||||
|
||||
In-home trials let a customer take an instrument home to try before committing to a purchase. Common for higher-value instruments (guitars, violins, horns) where feel, sound, and fit matter. The instrument leaves the store, so precise inventory tracking is critical.
|
||||
|
||||
## 9.1 instrument_trial
|
||||
|
||||
Column | Type | Notes
|
||||
id | uuid PK |
|
||||
company_id | uuid FK | Tenant scoping
|
||||
location_id | uuid FK | Originating location
|
||||
account_id | uuid FK | Customer taking the trial
|
||||
member_id | uuid FK | Nullable — specific member
|
||||
inventory_unit_id | uuid FK | The specific instrument being trialed
|
||||
trial_number | varchar | Human-readable ID (auto-generated)
|
||||
status | enum | active | returned | converted | overdue | cancelled
|
||||
checkout_date | date | When instrument left the store
|
||||
due_date | date | When instrument must be returned
|
||||
returned_date | date | Nullable — when actually returned
|
||||
duration_days | integer | Trial length (default configurable, e.g. 3-7 days)
|
||||
deposit_amount | numeric(10,2) | Nullable — optional hold or deposit collected
|
||||
deposit_transaction_id | uuid FK | Nullable — POS transaction for deposit
|
||||
condition_out | enum | new | excellent | good | fair | poor — condition at checkout
|
||||
condition_in | enum | Nullable — condition at return
|
||||
checked_out_by | uuid FK | Employee who processed checkout
|
||||
checked_in_by | uuid FK | Nullable — employee who processed return
|
||||
sale_transaction_id | uuid FK | Nullable — if trial converted to sale
|
||||
notes | text |
|
||||
created_at | timestamptz |
|
||||
updated_at | timestamptz |
|
||||
|
||||
## 9.2 Inventory Unit Status
|
||||
|
||||
**inventory_unit.status** — add value:
|
||||
|
||||
Value | Notes
|
||||
on_trial | Instrument is out with a customer on an in-home trial. Not available for sale, rental, or display.
|
||||
|
||||
This status ensures the instrument is accounted for in all inventory views. Staff can see exactly which instruments are out on trial, with whom, and when they're due back.
|
||||
|
||||
## 9.3 Trial Workflow
|
||||
|
||||
1. Customer interested in an instrument — staff creates trial, selects inventory unit
|
||||
2. Condition documented at checkout (condition_out)
|
||||
3. Optional: deposit collected via POS (hold against card or cash deposit)
|
||||
4. inventory_unit.status set to "on_trial"
|
||||
5. Customer takes instrument home
|
||||
6. Daily job checks for overdue trials — flags on dashboard, notifies staff
|
||||
7. Customer returns instrument:
|
||||
a. Condition assessed at return (condition_in)
|
||||
b. inventory_unit.status reverted to "available" (or "in_repair" if damaged)
|
||||
c. Deposit refunded if collected
|
||||
8. **Conversion**: customer decides to buy — trial marked "converted", sale transaction created, deposit applied as payment
|
||||
|
||||
## 9.4 Multiple Trials
|
||||
|
||||
- A customer may trial multiple instruments simultaneously (e.g. comparing two violins)
|
||||
- Each instrument gets its own trial record
|
||||
- Staff can see all active trials per customer on the account view
|
||||
- Common scenario: customer takes two, returns one, buys the other
|
||||
|
||||
## 9.5 Overdue Handling
|
||||
|
||||
- Trial due_date is firm — system escalates overdue trials:
|
||||
- Day of due date: reminder notification sent to customer
|
||||
- 1 day overdue: staff alerted on dashboard
|
||||
- 3 days overdue: manager notified, customer contacted directly
|
||||
- 7+ days overdue: escalation to store owner, deposit may be charged
|
||||
- Overdue trials appear prominently on the daily operations dashboard
|
||||
- If deposit was collected, store can charge the deposit after configurable overdue period
|
||||
|
||||
## 9.6 Business Rules
|
||||
|
||||
- Trial requires an account — no anonymous trials (need contact info for follow-up and liability)
|
||||
- Only serialized inventory units can go on trial (need serial number tracking)
|
||||
- Instrument must be in "available" status to start a trial
|
||||
- Maximum concurrent trials per account configurable (default: 3)
|
||||
- Maximum trial duration configurable per company (default: 7 days, max: 30 days)
|
||||
- Condition documented at checkout and return — discrepancies flagged for review
|
||||
- Deposit optional but recommended for high-value instruments — threshold configurable
|
||||
- Trial history visible on both the account record and the inventory unit record
|
||||
- Conversion rate report: trials started vs converted to sale, by instrument category and employee
|
||||
- Lost instrument on trial: if not returned and unresolved, inventory_unit.status set to "lost", deposit charged, account flagged
|
||||
Reference in New Issue
Block a user