217 lines
5.1 KiB
Markdown
217 lines
5.1 KiB
Markdown
# Data Model
|
|
|
|
## Core relationship
|
|
|
|
```text
|
|
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 (`PRODUCT` or `SEARCH`)
|
|
- 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.
|
|
|
|
Public contact methods also use the existing seller settings JSON. Explicit
|
|
empty strings in `general.publicContactEmail` or `general.publicContactPhone`
|
|
represent removal; omitted fields retain their values during partial updates.
|
|
Public detail projections normalize empty contacts to `null`. No migration is
|
|
required, and seller ownership and private settings boundaries are unchanged.
|