offdataai
Retail & E-commerce · data model template

E-commerce Returns & RMA data model

Third-normal-form mirror of an e-commerce returns / RMA stack as it lands from the OMS, returns platform and 3PL. Sales order header/line shape is mirrored by return_request → return_line; carrier-tracked return shipments are isolated for in-transit visibility; inspection_grade joins to a disposition lookup to drive reverse-logistics economics and recommerce; refund vs exchange settlement is split so revenue and COGS impact remain queryable. Customer name, email, phone and addresses are PII-masked under GDPR/CCPA, and payment processor references are PCI-scoped.

Normalized replica of e-commerce reverse-logistics operations: customer initiates a return request against a sales order, an RMA is approved and a return label is issued via a carrier, the parcel is tracked in transit, received and graded at the returns facility, assigned a disposition (restock/repair/refurbish/liquidate/scrap), and settled via refund or exchange. Models the full Loop/Returnly/Narvar/AfterShip-style RMA flow with carrier tracking, inspection grading, fraud scoring and split returns. It is a normalized (3NF) relational model that mirrors the source system, suited to operational databases and to landing source data faithfully.

3NFRetail & E-commerceGDPRCCPAPCI-DSS
17
tables
165
columns
25
relationships
7
PII columns

Opening the template creates a project with the ERD, platform-native DDL, a dbt project and documentation already generated (recommended platform: PostgreSQL). Edit it in plain English or on the canvas, then ship it to any supported warehouse.

Subject areas

master_dataorders_customerrma_flowinspection_dispositionsettlement

Design decisions

  • RMA header → line mirrors sales order header → line

    Returns are issued against specific order lines with their own quantities, reasons and refund amounts; modelling return_request → return_line at the same grain as sales_order → sales_order_line keeps reconciliation lossless and lets partial returns (subset of lines / quantities) be expressed natively without denormalization.

  • Return shipments separated from the RMA header

    A single RMA can be split across multiple parcels, dropped off at a locker, or re-shipped after a failed pickup; isolating return_shipment with its own tracking number, label cost and carrier-status timeline preserves in-transit visibility without bloating the RMA header.

  • Inspection grading joined to a disposition lookup

    Reverse-logistics economics depend on what actually happens to the item — restock, refurbish, liquidate or scrap each carry different recovery percentages; pairing inspection_grade with a disposition lookup makes recommerce yield and shrink directly queryable per returned unit.

  • Refund and exchange settlement modelled as separate entities

    Refunds hit the original tender (revenue reversal) while exchanges spawn a downstream sales order (revenue retention); keeping refund_event and exchange_order as separate entities means finance can attribute revenue / COGS impact cleanly without untangling a polymorphic settlement table.

  • Customer return risk and parent RMA self-reference

    Serial returners and split / re-opened RMAs are core fraud and ops signals; denormalizing lifetime_return_rate_pct + return_risk_score onto customer and carrying parent_return_request_key on return_request lets fraud and support teams trace related returns without an extra bridge table.

Tables in the E-commerce Returns & RMA model

Every table with its grain, columns, types and keys. PK marks a primary key, BK a business key and PII a column classified as personal data, which gets a masking strategy by default.

Tables (9)

customer

Grain: one row per customer account

Customer master with returns-behavior denormalizations.

ColumnTypeKeys
customer_keybigintPK
customer_idvarcharBK
store_keybigint
full_namevarcharPII
emailvarcharPII
phonevarcharPII
shipping_addressvarcharPII
billing_addressvarcharPII
countryvarchar
registered_attimestamp
lifetime_orders_countinteger
lifetime_return_countinteger
lifetime_return_rate_pctdecimal
return_risk_scoredecimal
is_blocked_from_returnsboolean

sales_order

Grain: one row per sales order header

Originating sales order header referenced by return requests.

ColumnTypeKeys
sales_order_keybigintPK
sales_order_idvarcharBK
customer_keybigint
store_keybigint
order_datedate
order_subtotaldecimal
shipping_costdecimal
tax_amountdecimal
order_totaldecimal
currencyvarchar
sales_channelvarchar
payment_methodvarchar

sales_order_line

Grain: one row per sales order line item

Sales order line items — the grain that return lines reference.

ColumnTypeKeys
sales_order_line_keybigintPK
order_line_idvarcharBK
sales_order_keybigint
product_keybigint
quantitydecimal
unit_pricedecimal
line_subtotaldecimal
discount_amountdecimal
line_totaldecimal
currencyvarchar

return_request

Grain: one row per RMA / return request header

RMA header — mirrors the originating sales order header.

ColumnTypeKeys
return_request_keybigintPK
return_request_idvarcharBK
sales_order_keybigint
customer_keybigint
store_keybigint
return_method_keybigint
return_status_keybigint
requested_attimestamp
approved_attimestamp
expires_attimestamp
return_window_daysinteger
total_refund_estimatedecimal
restocking_fee_amountdecimal
currencyvarchar
return_origin_addressvarcharPII
channel_initiatedvarchar
fraud_scoredecimal
parent_return_request_keybigint

return_line

Grain: one row per RMA line item

RMA line item — mirrors sales_order_line at the return grain.

ColumnTypeKeys
return_line_keybigintPK
return_line_idvarcharBK
return_request_keybigint
sales_order_line_keybigint
product_keybigint
return_reason_keybigint
requested_quantitydecimal
received_quantitydecimal
item_refund_amountdecimal
restocking_fee_amountdecimal
currencyvarchar
line_statusvarchar
evidence_photos_countinteger

return_shipment

Grain: one row per return shipment / label

Carrier-tracked return shipment for in-transit visibility.

ColumnTypeKeys
return_shipment_keybigintPK
return_shipment_idvarcharBK
return_request_keybigint
carrier_keybigint
label_issued_attimestamp
label_costdecimal
tracking_numbervarchar
picked_up_attimestamp
received_attimestamp
shipping_statusvarchar
currencyvarchar

inspection_grade

Grain: one row per inspection grading event on a return line

Reverse-logistics grading and disposition assignment.

ColumnTypeKeys
inspection_grade_keybigintPK
inspection_grade_idvarcharBK
return_line_keybigint
disposition_keybigint
inspected_attimestamp
gradevarchar
condition_notesvarchar
restocked_quantitydecimal
scrapped_quantitydecimal
sent_to_vendor_quantitydecimal
evidence_photos_countinteger
inspector_namevarcharPII

refund_event

Grain: one row per refund event posted against an RMA

Refund settlement event — separate from exchanges to keep revenue/COGS impact queryable.

ColumnTypeKeys
refund_event_keybigintPK
refund_event_idvarcharBK
return_request_keybigint
customer_keybigint
refund_method_keybigint
sales_order_keybigint
refund_amountdecimal
refund_currencyvarchar
processed_attimestamp
payment_processor_referencevarchar
refund_statusvarchar
refund_breakdown_itemsdecimal
refund_breakdown_shippingdecimal
refund_breakdown_taxdecimal

exchange_order

Grain: one row per exchange order issued in lieu of refund

Exchange settlement — separated from refund_event for clean recommerce attribution.

ColumnTypeKeys
exchange_order_keybigintPK
exchange_order_idvarcharBK
return_request_keybigint
customer_keybigint
product_keybigint
exchange_quantitydecimal
new_sales_order_idvarchar
exchange_statusvarchar
currencyvarchar
price_differencedecimal

Lookup tables (8)

store

Grain: one row per retail store / brand storefront

Storefront / banner reference table.

ColumnTypeKeys
store_keybigintPK
store_idvarcharBK
store_namevarchar
brandvarchar
countryvarchar
currencyvarchar
is_activeboolean

product

Grain: one row per sellable product / SKU

Product / SKU reference table.

ColumnTypeKeys
product_keybigintPK
product_idvarcharBK
product_namevarchar
categoryvarchar
brandvarchar
is_perishableboolean
is_high_valueboolean
is_oversizedboolean
base_pricedecimal
currencyvarchar

carrier

Grain: one row per parcel carrier

Parcel carrier reference table for return shipments.

ColumnTypeKeys
carrier_keybigintPK
carrier_idvarcharBK
carrier_namevarchar
countryvarchar
services_offeredvarchar

return_reason

Grain: one row per return reason code

Return reason catalog driving downstream root-cause analysis.

ColumnTypeKeys
return_reason_keybigintPK
return_reason_idvarcharBK
descriptionvarchar
reason_categoryvarchar
is_customer_faultboolean
requires_evidenceboolean

return_method

Grain: one row per return delivery method

Channel through which the customer returns goods.

ColumnTypeKeys
return_method_keybigintPK
return_method_idvarcharBK
descriptionvarchar
average_costdecimal
currencyvarchar
average_lead_daysdecimal

disposition

Grain: one row per disposition outcome

Reverse-logistics disposition outcomes after grading.

ColumnTypeKeys
disposition_keybigintPK
disposition_idvarcharBK
descriptionvarchar
is_sellable_againboolean
recovery_pctdecimal
channelvarchar

refund_method

Grain: one row per refund settlement method

Settlement method for refund events.

ColumnTypeKeys
refund_method_keybigintPK
refund_method_idvarcharBK
descriptionvarchar
processing_daysdecimal
is_electronicboolean

return_status

Grain: one row per return lifecycle status

Return request lifecycle status catalog.

ColumnTypeKeys
return_status_keybigintPK
return_status_idvarcharBK
descriptionvarchar
is_terminalboolean
is_openboolean

Relationships

Each relationship is a foreign key on the first table referencing the second.

  • customer.store_key → store (many-to-one)
  • sales_order.customer_key → customer (many-to-one)
  • sales_order.store_key → store (many-to-one)
  • sales_order_line.sales_order_key → sales_order (many-to-one)
  • sales_order_line.product_key → product (many-to-one)
  • return_request.sales_order_key → sales_order (many-to-one)
  • return_request.customer_key → customer (many-to-one)
  • return_request.store_key → store (many-to-one)
  • return_request.return_method_key → return_method (many-to-one)
  • return_request.return_status_key → return_status (many-to-one)
  • return_line.return_request_key → return_request (many-to-one)
  • return_line.sales_order_line_key → sales_order_line (many-to-one)
  • return_line.product_key → product (many-to-one)
  • return_line.return_reason_key → return_reason (many-to-one)
  • return_shipment.return_request_key → return_request (many-to-one)
  • return_shipment.carrier_key → carrier (many-to-one)
  • inspection_grade.return_line_key → return_line (many-to-one)
  • inspection_grade.disposition_key → disposition (many-to-one)
  • refund_event.return_request_key → return_request (many-to-one)
  • refund_event.customer_key → customer (many-to-one)
  • refund_event.refund_method_key → refund_method (many-to-one)
  • refund_event.sales_order_key → sales_order (many-to-one)
  • exchange_order.return_request_key → return_request (many-to-one)
  • exchange_order.customer_key → customer (many-to-one)
  • exchange_order.product_key → product (many-to-one)

Related templates

All templates →
Start for $1

From requirements to AI-ready context, in one platform.

Describe your business in a conversation, start from a template or connect your own database. Your first model is ready in minutes.

Get startedBook a demo

Plans from $1/month · cancel any time