4.8 KiB
Data Model
Core relationship
User → Seller → Product → Inventory Item → Listing
│
├── Images
├── Acquisition
├── Pricing Observations
├── Market Valuations
└── Activity
Sale → Sale Items → Inventory Items
Product
Represents what the item is, independent of physical ownership.
Examples:
- Charizard ex — SV151 — #199/165
- Pokémon Center Worlds 2025 Pikachu Plush
Products persist after all inventory is sold.
Inventory Item
Represents one physical copy.
Each copy has its own:
- condition
- images
- acquisition
- asking price/listing
- market valuation
- notes
- status
Do not model multiple physical copies as a quantity field on one Inventory Item.
Listing
Represents how an Inventory Item is currently offered publicly.
A Product can have many physical Inventory Items with different conditions and asking prices.
Sale
A manual external transaction. One Sale can contain multiple Sale Items.
Provider Product Mapping
Provider mappings are Product-level relationships.
Recommended fields:
- id
- product_id
- provider_id
- mapping_type (
PRODUCTorSEARCH) - external_identifier
- search_query / search parameters
- source_url
- status
- metadata
- created_at
- updated_at
- disabled_at
A provider can support one or both mapping types.
eBay supports both:
- specific product/catalog mappings where appropriate
- search/query mappings where no canonical product is appropriate
Mappings can be replaced without destroying historical records.
Only one active/default mapping per provider should be used for normal pricing unless a provider integration explicitly supports multiple active sources.
Suggested pricing tables
pricing_providers
id, name, code, is_enabled, priority, configuration, created_at, updated_at
product_pricing_providers
product_id, provider_id, is_enabled
pricing_provider_mappings
As described above.
pricing_observations
id, product_id, inventory_item_id, provider_id, source_identifier, source_url, condition_id, price_minor, currency, observed_at, quality_score, is_outlier, outlier_reason, raw_reference
market_valuations
id, inventory_item_id, value_minor, currency, strategy, confidence, source_count, calculated_at
market_value_overrides
id, inventory_item_id, value_minor, currency, reason, created_by, created_at, removed_at
pricing_jobs
id, job_type, status, requested_by, started_at, completed_at, total_items, processed_items, successful_items, failed_items, error_summary, created_at
Conditions
condition_profiles:
- id
- category_id
- name
- active
- sort
Seller Settings
seller_settings stores one JSON settings document per Seller. Supported
categories are general contact details, inventory image limits, pricing
strategy, and session idle duration. Settings are private to the Seller and
are validated through the admin API allowlist.
conditions:
- id
- condition_profile_id
- name
- code
- quality_rank
- description
- active
- sort
Images
inventory_images:
- id
- inventory_item_id
- storage_key
- original_filename
- mime_type
- file_size
- width
- height
- alt_text
- sort_order
- is_primary
- processing_status
- created_at
- updated_at
Acquisitions
acquisitions:
- id
- inventory_item_id
- method
- cost_minor
- currency
- date
- source
- location
- shipping_minor
- tax_minor
- other_fees_minor
- estimated_trade_value_minor
- notes
- created_at
- updated_at
Sales
sales:
- id
- seller_id
- sale_date
- sale_location
- sold_price_minor
- currency
- platform_fees_minor
- payment_processing_fees_minor
- shipping_cost_minor
- taxes_minor
- other_fees_minor
- buyer_name
- buyer_contact
- notes
- created_by
- created_at
- updated_at
sale_items:
- id
- sale_id
- inventory_item_id
- sold_price_minor
- cost_basis_minor
Activity
Activity records retain:
- affected Product/Inventory Item
- event
- previous value where applicable
- new value
- actor
- timestamp
Security events are stored separately.
Money
Store money as integer minor units. Negative values are prohibited; zero is valid. USD is canonical for stored/calculated market values unless a documented external representation requires another currency.
Public seller descriptions use optional general.publicDescription in seller settings: trimmed plain text, at most 2,000 characters. An empty string clears the description. Public listing and product detail DTOs expose only this field as sellerDescription, alongside the existing public seller name and contact fields; other settings remain private. No database migration is required.