# 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 `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.