> ## Documentation Index
> Fetch the complete documentation index at: https://docs.quinnsambal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Marketplace

<style>
  {`
    [class*="max-w-none"][class*="table"] {
      display: block !important;
      overflow-x: auto !important;
      max-width: 100% !important;
      width: 100% !important;
      flex-grow: 0 !important;
    }
    [class*="max-w-none"][class*="table"] > table {
      width: 100% !important;
      max-width: 100% !important;
      table-layout: fixed !important;
    }
    .mermaid {
      max-width: 100% !important;
      overflow-x: auto !important;
    }
    article svg[role="img"] {
      max-width: 100% !important;
      height: auto !important;
    }
    article img, .prose img {
      max-width: 100% !important;
      height: auto !important;
    }
    img[src*="LOGO"], img[src*="logo"] {
      max-width: 120px !important;
      max-height: 40px !important;
      width: auto !important;
      height: auto !important;
      object-fit: contain !important;
    }
    `}
</style>

***

title: "Marketplace"
description: "Multi-marketplace storefront dengan product catalog, cart system, Shopee sales sync, admin CMS, dan order management di SNISHOP ERP."
---------------------------------------------------------------------------------------------------------------------------------------------------

# Marketplace

<img src="https://mintcdn.com/quinnofspicy/ny1xnfpEa_OBdv6T/docs/mintlify/screenshots/marketing/marketplace.png?fit=max&auto=format&n=ny1xnfpEa_OBdv6T&q=85&s=ba359ffd407c6cbe54f5c778edd79c6c" alt="Marketplace" width="1920" height="1080" data-path="docs/mintlify/screenshots/marketing/marketplace.png" />

Halaman Marketplace adalah storefront digital multi-channel yang menampilkan produk dari berbagai seller di platform SNISHOP. Dibangun di atas `Marketplace.jsx` (811 baris) dengan katalog produk dari `POSProduct` (produk fisik) dan `DigitalProduct` (produk digital), sistem cart terintegrasi melalui `MarketplaceCart`, banner carousel, diskon & voucher, serta admin CMS untuk pengaturan commission dan tampilan.

Sistem ini juga dilengkapi **Shopee Sales Sync Engine** (`shopeeSalesSync.js`, 187 baris) yang mensinkronisasi data penjualan Shopee secara realtime dengan algoritma offset otomatis, serta mendukung multi-channel pricing untuk Shopee, Tokopedia, TikTok Shop, WhatsApp, B2B, dan channel lainnya.

## Arsitektur Komponen

```mermaid theme={null}
graph TD
    A[Marketplace.jsx<br/>811 lines] --> B[Product Grid]
    A --> C[Banner Carousel]
    A --> D[Category Filter]
    A --> E[Sort Options]
    A --> F[Cart System]
    B --> G[POSProduct<br/>Produk Fisik]
    B --> H[DigitalProduct<br/>Produk Digital]
    B --> I[CompanyPOSInventory<br/>stock aggregation]
    B --> J[StockMovement<br/>multi-channel tracking]
    A --> K[Company Entity<br/>seller info]
    A --> L[MarketplaceProductDetail<br/>Modal]
    A --> M[MarketplaceSettingsTab<br/>320 lines]
    A --> N[MarketplaceOrdersTab<br/>188 lines]
    A --> O[shopeeSalesSync<br/>187 lines]
    A --> P[Discount & Voucher<br/>Engine]
    M --> Q[MarketplaceCommissionSettings]
    F --> R[MarketplaceCart]
    N --> S[MarketplaceOrder]
    H --> T[ProductOrder]
    P --> U[Voucher / ProductVoucher]
    P --> V[Discount]
    A --> W[ShopSettings]
    A --> X[LiveStreamingActivity]
```

## Entity & Data Sources

### POSProduct (Produk Fisik)

Entity `POSProduct` merepresentasikan produk fisik yang dijual melalui marketplace. Setiap produk memiliki dukungan multi-channel pricing, varian, dan sinkronisasi dengan `DigitalProduct`.

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `id` | string | Ya | ID unik produk (auto-generated) |
| `name` | string | Ya | Nama produk |
| `sku` | string | Tidak | SKU/Barcode produk |
| `category` | string | Tidak | Kategori produk |
| `description` | string | Tidak | Deskripsi produk |
| `price` | number | Ya | Harga jual dasar |
| `cost` | number | Tidak | Harga modal/beli |
| `stock` | number | Tidak | Stok saat ini (default: 0) |
| `min_stock` | number | Tidak | Minimum stok untuk alert (default: 5) |
| `image_url` | string | Tidak | URL gambar produk |
| `variants` | array | Tidak | Varian produk (ukuran, warna, dll) |
| `is_active` | boolean | Tidak | Status aktif produk (default: true) |
| `tax_rate` | number | Tidak | Persentase pajak 0-100 (default: 0) |
| `supplier` | string | Tidak | Nama supplier |
| `unit` | string | Tidak | Satuan produk (default: "pcs") |
| `digital_product_id` | string | Tidak | ID dari DigitalProduct jika sinkronisasi |
| `is_synced_from_digital` | boolean | Tidak | Apakah produk disinkronkan dari DigitalProduct (default: false) |
| `last_synced` | date-time | Tidak | Waktu terakhir sinkronisasi |

### DigitalProduct (Produk Digital)

Entity `DigitalProduct` merepresentasikan produk digital seperti jasa desain, video editing, Zoom session, dan lainnya. Mendukung custom order form, varian harga, dan fitur produk.

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `id` | string | Ya | ID unik produk (auto-generated) |
| `name` | string | Ya | Nama produk digital |
| `description` | string | Ya | Deskripsi produk |
| `category` | enum | Ya | Kategori: `zoom`, `design`, `video_editing`, `plagiarism_check`, `other` |
| `price` | number | Ya | Harga produk dasar |
| `images` | string\[] | Tidak | Array URL gambar produk (rasio 1:1) |
| `is_active` | boolean | Tidak | Status aktif produk (default: true) |
| `stock_available` | boolean | Tidak | Ketersediaan stok (default: true) |
| `order_form_fields` | array | Tidak | Form fields yang harus diisi customer saat order |
| `variants` | array | Tidak | Varian produk dengan harga berbeda |
| `delivery_time` | string | Tidak | Estimasi waktu pengerjaan |
| `features` | string\[] | Tidak | Fitur-fitur produk |
| `commission_rate` | number | Tidak | Persentase komisi untuk admin basic 0-1 (default: 0) |

### MarketplaceCart (Keranjang Belanja)

Entity `MarketplaceCart` menyimpan item keranjang belanja per user. Setiap record merepresentasikan satu item dalam cart.

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `id` | string | Ya | ID unik cart item (auto-generated) |
| `user_id` | string | Ya | ID user pemilik keranjang |
| `company_id` | string | Ya | ID company/seller |
| `product_id` | string | Ya | ID produk yang dibeli |
| `product_name` | string | Tidak | Nama produk (denormalized) |
| `product_price` | number | Tidak | Harga produk saat dimasukkan ke cart |
| `product_image` | string | Tidak | URL gambar produk |
| `description` | string | Tidak | Catatan/instruksi khusus dari pembeli (max 1000 karakter) |
| `quantity` | number | Ya | Jumlah item (default: 1) |
| `subtotal` | number | Tidak | Subtotal (price x quantity) |

### MarketplaceOrder (Order Marketplace)

Entity `MarketplaceOrder` merepresentasikan order fisik yang masuk melalui marketplace SNISHOP. Mencakup informasi lengkap dari data customer, item order, komisi, pengiriman, hingga tracking.

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `id` | string | Ya | ID unik order (auto-generated) |
| `order_number` | string | Tidak | Nomor order unik |
| `customer_id` | string | Ya | ID user yang membeli |
| `customer_email` | string | Tidak | Email customer |
| `customer_name` | string | Tidak | Nama customer |
| `company_id` | string | Ya | ID company yang menjual |
| `company_name` | string | Tidak | Nama company |
| `items` | array | Ya | List produk yang dibeli (product\_id, product\_name, quantity, price, subtotal) |
| `total_amount` | number | Ya | Total pembayaran |
| `commission_rate` | number | Tidak | Persentase komisi marketplace (0-100) |
| `commission_amount` | number | Tidak | Jumlah komisi dalam rupiah |
| `company_amount` | number | Tidak | Jumlah yang diterima company setelah komisi |
| `status` | enum | Tidak | Status pesanan (default: "pending") |
| `payment_status` | enum | Tidak | Status pembayaran (default: "paid") |
| `shipping_address` | object | Tidak | Alamat pengiriman (name, phone, address, city, province, postal\_code) |
| `notes` | string | Tidak | Catatan customer |
| `tracking_number` | string | Tidak | Nomor resi pengiriman |
| `processed_at` | date-time | Tidak | Waktu order diproses |
| `shipped_at` | date-time | Tidak | Waktu order dikirim |
| `delivered_at` | date-time | Tidak | Waktu order sampai |

### ProductOrder (Order Produk Digital)

Entity `ProductOrder` merepresentasikan order untuk produk digital. Mendukung sistem assignment admin, quota-based ordering, voucher, dan reseller discount.

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `id` | string | Ya | ID unik order (auto-generated) |
| `product_id` | string | Ya | ID produk digital |
| `product_name` | string | Ya | Nama produk |
| `product_category` | string | Tidak | Kategori produk |
| `product_price` | number | Ya | Harga produk saat order |
| `product_commission_rate` | number | Tidak | Commission rate saat order dibuat (default: 0) |
| `selected_variant` | object | Tidak | Varian yang dipilih customer |
| `customer_id` | string | Ya | ID customer |
| `customer_email` | string | Ya | Email customer |
| `customer_name` | string | Tidak | Nama customer |
| `order_data` | object | Tidak | Data form order (jawaban order\_form\_fields) |
| `description` | string | Tidak | Catatan pelanggan (max 1000 karakter) |
| `used_balance` | number | Tidak | Saldo yang digunakan (default: 0) |
| `used_commission` | number | Tidak | Komisi yang digunakan (default: 0) |
| `reseller_discount` | number | Tidak | Persentase diskon reseller (default: 0) |
| `reseller_discount_amount` | number | Tidak | Nominal diskon reseller (default: 0) |
| `voucher_code` | string | Tidak | Kode voucher yang digunakan |
| `voucher_discount_amount` | number | Tidak | Potongan dari voucher (default: 0) |
| `tip_amount` | number | Tidak | Tip dari customer (default: 0) |
| `subtotal` | number | Tidak | Harga sebelum discount |
| `final_price` | number | Tidak | Harga final setelah semua diskon |
| `status` | enum | Tidak | Status order (default: "pending") |
| `admin_notes` | string | Tidak | Catatan admin |
| `result_files` | string\[] | Tidak | URL file hasil pengerjaan |
| `completed_at` | date-time | Tidak | Waktu order selesai |
| `processed_by_admin` | string | Tidak | Email admin yang memproses |
| `assigned_to_admin` | string | Tidak | Email admin yang mengambil order |
| `assigned_at` | date-time | Tidak | Waktu order diambil admin |
| `admin_commission_amount` | number | Tidak | Komisi admin (default: 0) |
| `admin_commission_paid` | boolean | Tidak | Status pembayaran komisi admin (default: false) |
| `quota_total` | number | Tidak | Total kuota (default: 0) |
| `quota_used` | number | Tidak | Kuota terpakai (default: 0) |
| `quota_remaining` | number | Tidak | Sisa kuota (default: 0) |
| `is_quota_based` | boolean | Tidak | Apakah order berbasis kuota (default: false) |
| `parent_order_id` | string | Tidak | ID order induk (untuk sub-order) |

### MarketplaceCommissionSettings (Pengaturan Komisi)

Entity `MarketplaceCommissionSettings` menyimpan konfigurasi komisi global marketplace.

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `id` | string | Ya | ID unik settings (auto-generated) |
| `commission_rate` | number | Ya | Persentase komisi marketplace 0-100 (default: 10) |
| `description` | string | Tidak | Penjelasan kebijakan komisi (max 1000 karakter) |
| `min_withdrawal` | number | Tidak | Minimum penarikan saldo (default: 100000) |
| `withdrawal_fee` | number | Tidak | Biaya admin penarikan (default: 5000) |
| `is_active` | boolean | Tidak | Status pengaturan aktif (default: true) |

### CompanyPOSInventory (Pergerakan Stok Company)

Entity `CompanyPOSInventory` mencatat setiap pergerakan stok produk di level company.

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `id` | string | Ya | ID unik (auto-generated) |
| `company_id` | string | Ya | ID perusahaan |
| `product_id` | string | Ya | ID produk |
| `product_name` | string | Tidak | Nama produk (denormalized) |
| `type` | enum | Ya | Tipe pergerakan: `in`, `out`, `adjustment` |
| `quantity` | number | Ya | Jumlah perubahan |
| `stock_before` | number | Tidak | Stok sebelum perubahan |
| `stock_after` | number | Tidak | Stok setelah perubahan |
| `reason` | string | Tidak | Alasan (pembelian, penjualan, rusak, dll) |
| `reference_id` | string | Tidak | ID transaksi terkait |
| `notes` | string | Tidak | Catatan tambahan |
| `performed_by` | string | Tidak | ID user yang melakukan |

### StockMovement (Detail Pergerakan Stok Multi-Channel)

Entity `StockMovement` mencatat detail pergerakan stok dengan dukungan multi-channel, lot tracking, dan kalkulasi profit.

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `id` | string | Ya | ID unik (auto-generated) |
| `company_id` | string | Ya | ID perusahaan |
| `inventory_id` | string | Ya | ID inventory yang berubah |
| `product_id` | string | Tidak | ID produk |
| `product_sku` | string | Tidak | SKU produk |
| `product_name` | string | Tidak | Nama produk |
| `movement_type` | enum | Ya | Tipe: `in`, `out`, `transfer`, `adjustment`, `return`, `damaged`, `hold`, `hold_release` |
| `quantity` | number | Ya | Jumlah perubahan |
| `stock_before` | number | Tidak | Stok sebelum |
| `stock_after` | number | Tidak | Stok setelah |
| `reference_type` | enum | Tidak | Sumber: `purchase`, `sale`, `cashier_sale`, `production`, `transfer`, `adjustment`, `opname_adjustment`, `return`, `manual`, `distribution_shipment`, `quality_hold` |
| `reference_id` | string | Tidak | ID transaksi terkait (PO, SO, Transfer) |
| `lot_id` | string | Tidak | ID Lot/Batch untuk traceability |
| `lot_number` | string | Tidak | Nomor label Lot/Batch fisik |
| `sales_channel` | string | Tidak | Nama channel (Shopee, TikTok, WhatsApp, B2B, dll) |
| `channel_price_key` | string | Tidak | Key pricing channel (marketplace, whatsapp, b2b, reseller, dll) |
| `unit_cost` | number | Tidak | Harga modal per unit (default: 0) |
| `unit_price` | number | Tidak | Harga jual per unit per channel (default: 0) |
| `total_revenue` | number | Tidak | Total pendapatan (default: 0) |
| `total_cost` | number | Tidak | Total modal (default: 0) |
| `profit` | number | Tidak | Laba/rugi (default: 0) |
| `profit_margin` | number | Tidak | Margin laba % (default: 0) |
| `performed_by` | string | Tidak | Email user yang melakukan |
| `notes` | string | Tidak | Catatan |
| `idempotency_key` | string | Tidak | Retry identity for stock command |

### Discount (Diskon Produk)

Entity `Discount` mengelola berbagai jenis diskon yang bisa diterapkan pada produk marketplace.

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `id` | string | Ya | ID unik (auto-generated) |
| `company_id` | string | Ya | ID perusahaan |
| `discount_name` | string | Ya | Nama diskon |
| `discount_type` | enum | Ya | Tipe: `percentage`, `fixed_amount`, `buy_x_get_y`, `coupon_code` |
| `value` | number | Ya | Nilai diskon (% atau nominal) |
| `buy_quantity` | number | Tidak | Untuk buy\_x\_get\_y: jumlah beli |
| `get_quantity` | number | Tidak | Untuk buy\_x\_get\_y: jumlah gratis |
| `min_purchase_amount` | number | Tidak | Minimum pembelian (default: 0) |
| `max_discount_amount` | number | Tidak | Batas maksimal nilai diskon |
| `applies_to` | enum | Tidak | Berlaku untuk: `all_products`, `specific_products`, `specific_categories` (default: "all\_products") |
| `product_ids` | string\[] | Tidak | ID produk spesifik |
| `category_names` | string\[] | Tidak | Kategori spesifik |
| `coupon_code` | string | Tidak | Kode kupon unik |
| `usage_limit` | number | Tidak | Batas penggunaan (0 = unlimited) |
| `used_count` | number | Tidak | Jumlah sudah digunakan (default: 0) |
| `start_date` | date | Tidak | Tanggal mulai |
| `end_date` | date | Tidak | Tanggal berakhir |
| `is_active` | boolean | Tidak | Status aktif (default: true) |
| `description` | string | Tidak | Deskripsi diskon |

### Voucher & ProductVoucher

Entity `Voucher` mengelola voucher subscription, sedangkan `ProductVoucher` mengelola voucher khusus produk marketplace.

**Voucher (Subscription)**

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `id` | string | Ya | ID unik (auto-generated) |
| `code` | string | Ya | Kode voucher unik |
| `name` | string | Ya | Nama voucher |
| `description` | string | Tidak | Penjelasan detail (max 1000 karakter) |
| `discount_type` | enum | Ya | Tipe: `percentage`, `fixed_amount` |
| `discount_value` | number | Ya | Nilai potongan (persentase 0-100 atau rupiah) |
| `min_purchase` | number | Tidak | Minimum pembelian (default: 0) |
| `max_discount` | number | Tidak | Maksimum potongan (untuk persentase) |
| `usage_limit` | number | Tidak | Batas penggunaan (null = unlimited) |
| `usage_count` | number | Tidak | Jumlah sudah digunakan (default: 0) |
| `valid_from` | date-time | Ya | Tanggal mulai aktif |
| `valid_until` | date-time | Ya | Tanggal berakhir |
| `applicable_plans` | enum\[] | Tidak | Paket: `pro`, `business`, `advanced`, `enterprise` |
| `is_active` | boolean | Tidak | Status aktif (default: true) |
| `created_by` | string | Tidak | Admin yang membuat |

**ProductVoucher (Produk Marketplace)**

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `id` | string | Ya | ID unik (auto-generated) |
| `code` | string | Ya | Kode voucher unik |
| `name` | string | Ya | Nama voucher |
| `description` | string | Tidak | Penjelasan detail (max 1000 karakter) |
| `discount_type` | enum | Ya | Tipe: `percentage`, `fixed_amount` |
| `discount_value` | number | Ya | Nilai discount (% atau Rp) |
| `min_purchase` | number | Tidak | Minimum pembelian (default: 0) |
| `max_discount` | number | Tidak | Maksimum potongan |
| `usage_limit` | number | Tidak | Batas penggunaan |
| `usage_count` | number | Tidak | Jumlah sudah dipakai (default: 0) |
| `valid_from` | date-time | Ya | Tanggal mulai aktif |
| `valid_until` | date-time | Ya | Tanggal berakhir |
| `applicable_products` | string\[] | Tidak | ID produk yang berlaku (kosong = semua) |
| `applicable_categories` | string\[] | Tidak | Kategori yang berlaku |
| `is_active` | boolean | Tidak | Status aktif (default: true) |

### ShopSettings (Pengaturan Toko)

Entity `ShopSettings` menyimpan konfigurasi tampilan dan operasional toko marketplace.

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `id` | string | Ya | ID unik (auto-generated) |
| `setting_key` | string | Ya | Kunci pengaturan unik |
| `description` | string | Tidak | Penjelasan konfigurasi toko (max 1000 karakter) |
| `banner_image_url` | string | Tidak | URL banner toko |
| `banner_title` | string | Tidak | Judul banner |
| `banner_subtitle` | string | Tidak | Subtitle banner |
| `featured_categories` | string\[] | Tidak | Kategori yang ditampilkan |
| `promo_banner` | object | Tidak | Banner promo (active, title, description, discount\_percentage, valid\_until) |
| `reseller_discounts` | object | Tidak | Diskon per tier: free (0), pro (5%), business (10%), advanced (15%), enterprise (20%) |

### WarehouseLocation (Lokasi Gudang)

Entity `WarehouseLocation` mengelola lokasi gudang dan toko untuk mendukung multi-location inventory.

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `id` | string | Ya | ID unik (auto-generated) |
| `company_id` | string | Ya | ID perusahaan |
| `location_name` | string | Ya | Nama lokasi (Gudang Utama, Toko Cabang A, dll) |
| `location_code` | string | Ya | Kode unik lokasi |
| `location_type` | enum | Tidak | Tipe: `warehouse`, `store`, `transit`, `virtual` (default: "warehouse") |
| `description` | string | Tidak | Penjelasan lokasi (max 1000 karakter) |
| `address` | string | Tidak | Alamat lengkap |
| `city` | string | Tidak | Kota |
| `manager_name` | string | Tidak | Nama penanggung jawab |
| `manager_contact` | string | Tidak | Kontak penanggung jawab |
| `capacity` | number | Tidak | Kapasitas maksimal (unit atau m2) |
| `current_utilization` | number | Tidak | Utilisasi saat ini % (default: 0) |
| `is_active` | boolean | Tidak | Status aktif (default: true) |
| `coordinates` | object | Tidak | Koordinat GPS (latitude, longitude) |

### LiveStreamingActivity (Aktivitas Live Streaming)

Entity `LiveStreamingActivity` mencatat aktivitas live streaming di berbagai platform untuk tracking penjualan dan engagement.

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `id` | string | Ya | ID unik (auto-generated) |
| `company_id` | string | Ya | ID perusahaan |
| `sales_person_id` | string | Tidak | ID user sales yang live |
| `sales_person_name` | string | Tidak | Nama sales person |
| `activity_date` | date | Ya | Tanggal live streaming |
| `platform` | enum | Ya | Platform: `shopee_live`, `tiktok`, `instagram`, `facebook`, `youtube`, `other` |
| `platform_label` | string | Tidak | Label platform lain (jika platform=other) |
| `start_time` | date-time | Ya | Waktu mulai live |
| `end_time` | date-time | Tidak | Waktu selesai live |
| `duration_minutes` | number | Tidak | Durasi live (auto: end - start, default: 0) |
| `peak_viewers` | number | Tidak | Peak viewers (default: 0) |
| `avg_viewers` | number | Tidak | Rata-rata viewers (default: 0) |
| `promoted_product_ids` | string\[] | Tidak | ID produk yang dipromosikan |
| `promoted_product_names` | string\[] | Tidak | Nama produk yang dipromosikan |
| `orders_count` | number | Tidak | Jumlah order dari live (default: 0) |
| `revenue` | number | Tidak | Revenue dari live (default: 0) |
| `screenshot_url` | string | Tidak | URL screenshot live |
| `recording_link` | string | Tidak | Link recording live |
| `conversion_rate` | number | Tidak | Conversion rate % (default: 0) |
| `status` | enum | Tidak | Status: `ongoing`, `completed`, `cancelled` (default: "completed") |
| `notes` | string | Tidak | Catatan tambahan |

## Entity Relationship Diagram

```mermaid theme={null}
erDiagram
    User ||--o{ MarketplaceCart : "memiliki"
    User ||--o{ MarketplaceOrder : "membuat"
    User ||--o{ ProductOrder : "membuat"
    User ||--o{ VoucherUsage : "menggunakan"

    POSProduct ||--o{ MarketplaceCart : "dimasukkan ke"
    POSProduct ||--o{ StockMovement : "memiliki"
    POSProduct ||--o{ CompanyPOSInventory : "dicatat di"
    POSProduct ||--o{ Discount : "mendapat"
    POSProduct ||--o{ StockMovement : "terjual di channel"

    DigitalProduct ||--o{ ProductOrder : "dipesan melalui"
    DigitalProduct ||--o{ POSProduct : "disinkronkan ke"
    DigitalProduct ||--o{ ProductVoucher : "mendapat"

    MarketplaceOrder }o--|| POSProduct : "berisi items"
    MarketplaceOrder }o--|| Company : "dijual oleh"

    ProductOrder }o--|| DigitalProduct : "untuk produk"

    MarketplaceCart }o--|| Company : "dari seller"

    Company ||--o{ POSProduct : "menjual"
    Company ||--o{ CompanyPOSCategory : "menggunakan"
    Company ||--o{ CompanyPOSInventory : "mencatat"
    Company ||--o{ WarehouseLocation : "memiliki"
    Company ||--o{ Discount : "membuat"
    Company ||--o{ LiveStreamingActivity : "melakukan"
    Company ||--o{ MarketplaceOrder : "menerima"

    Voucher ||--o{ VoucherUsage : "direkam di"
    ProductVoucher ||--o{ ProductOrder : "diterapkan pada"

    WarehouseLocation ||--o{ StockMovement : "lokasi pergerakan"

    StockMovement }o--o{ WarehouseLocation : "dari-ke (transfer)"

    MarketplaceCommissionSettings ||--|| MarketplaceOrder : "mengatur komisi"
    ShopSettings ||--|| Company : "konfigurasi toko"
```

## Multi-Channel Pricing System

Sistem marketplace SNISHOP mendukung pricing berbeda untuk setiap channel penjualan. Mapping channel ke pricing key dikelola melalui shared module `channelPricing.js`:

| Channel Penjualan | Pricing Key | Deskripsi |
| - | - | - |
| Shopee | `marketplace` | Semua pesanan dari Shopee |
| Shopee Live | `marketplace` | Penjualan via live streaming Shopee |
| TikTok Shop | `marketplace` | Semua pesanan dari TikTok Shop |
| Tokopedia | `marketplace` | Semua pesanan dari Tokopedia |
| WhatsApp | `whatsapp` | Pesanan via WhatsApp |
| Indomaret | `b2b` | Distribusi ke Indomaret |
| K3Mart | `b2b` | Distribusi ke K3Mart |
| Karya Prima | `b2b` | Distribusi B2B |
| Smarco | `b2b` | Distribusi B2B |
| Suzuya | `b2b` | Distribusi B2B |
| Dekranasda | `b2b` | Distribusi B2B |
| WhSmith | `b2b` | Distribusi B2B |
| Reseller | `reseller` | Penjualan ke reseller |
| Grab | `grab` | Pesanan via Grab |
| Website (SNISHOP) | `website` | Pesanan via website |
| POS / Kasir Offline | `offline_pos` | Transaksi kasir offline |
| Instagram / Facebook / YouTube | `social_media` | Pesanan dari social media |

### Fungsi Channel Pricing

```
resolveChannelPriceKey(salesChannel, metadata)
  → Mengembalikan pricing key dari nama channel

getChannelPrice(product, pricingKey)
  → Mengembalikan harga untuk channel tertentu, fallback ke product.price

calculateSalesData(product, salesChannel, quantity, metadata)
  → Menghitung total_revenue, total_cost, profit, profit_margin per channel
```

## 9 Kategori Produk

| Kategori | Deskripsi |
| - | - |
| `all` | Semua produk |
| `Retail` | Produk retail umum |
| `F&B` | Makanan dan minuman |
| `Fashion` | Pakaian dan aksesoris |
| `Technology` | Produk teknologi |
| `Services` | Jasa dan layanan |
| `Health` | Kesehatan dan kecantikan |
| `Education` | Pendidikan dan kursus |
| `Other` | Lainnya |

### Kategori Produk Digital

| Kategori | Deskripsi |
| - | - |
| `zoom` | Sesi Zoom / meeting online |
| `design` | Jasa desain grafis |
| `video_editing` | Jasa editing video |
| `plagiarism_check` | Cek plagiarisme |
| `other` | Produk digital lainnya |

## 5 Opsi Sorting

| Sort | Deskripsi |
| - | - |
| `newest` | Produk terbaru |
| `price_low` | Harga terendah dulu |
| `price_high` | Harga tertinggi dulu |
| `name_asc` | Nama A-Z |
| `popular` | Paling populer (sold\_count) |

## "For You" Scoring Algorithm

Produk di tab "For You" diurutkan berdasarkan skor yang menggabungkan popularitas dan recency:

```
scoreA = (sold_count x 0.5) + (created_date.getTime() / 1e12)
```

```mermaid theme={null}
flowchart LR
    A[sold_count] --> B[x 0.5]
    C[created_date] --> D[getTime / 1e12]
    B --> E[Sum]
    D --> E
    E --> F[Sort descending]
    F --> G[For You Feed]
```

## Stock Resolution

Stok ditampilkan dari entity **CompanyPOSInventory** yang di-aggregate per produk, dengan dukungan multi-location dari `WarehouseLocation`:

```mermaid theme={null}
flowchart LR
    A[CompanyPOSInventory Records] --> B[aggregateAvailableStockByProduct]
    B --> C[Stock per product_id]
    C --> D[Display di Product Card]
    E[WarehouseLocation] --> B
```

Setiap pergerakan stok dicatat di `StockMovement` dengan detail channel penjualan, lot tracking, dan kalkulasi profit otomatis.

## Banner Carousel

Banner auto-rotate setiap **5 detik** dengan konfigurasi dari admin CMS (max 5 banner). Konfigurasi banner disimpan di `ShopSettings`:

| Field | Deskripsi |
| - | - |
| `banner_image_url` | URL gambar banner |
| `banner_title` | Judul banner |
| `banner_subtitle` | Subtitle banner |
| `promo_banner` | Banner promo aktif (title, description, discount\_percentage, valid\_until) |

## Admin CMS

### MarketplaceSettingsTab (320 baris)

Menggunakan entity `MarketplaceCommissionSettings`:

| Setting | Deskripsi |
| - | - |
| `commission_rate` | Persentase komisi platform (default: 10%) |
| `min_withdrawal` | Minimum penarikan dana (default: Rp 100.000) |
| `withdrawal_fee` | Biaya penarikan (default: Rp 5.000) |
| `description` | Penjelasan kebijakan komisi |
| `is_active` | Status pengaturan aktif |

### MarketplaceOrdersTab (188 baris)

Admin order management via `listAdminMarketplaceOrders`:

| Tab | Filter |
| - | - |
| All | Semua order |
| Pending | Order belum diproses |
| Delivered | Order sudah sampai |

Stats: total orders, total revenue, total commission.

### ShopSettings CMS

| Setting | Deskripsi |
| - | - |
| `featured_categories` | Kategori yang ditampilkan di halaman utama |
| `promo_banner` | Banner promo aktif dengan diskon persentase |
| `reseller_discounts` | Diskon per tier membership reseller |

## Shopee Sales Sync Engine

`shopeeSalesSync.js` (187 baris) mensinkronisasi data penjualan Shopee Mall Official Store Quinn of Spicy dengan data transaksi realtime dari sistem ERP:

```mermaid theme={null}
flowchart TD
    A[Shopee API<br/>quinn_kitchen] --> B[Fetch base sales per SKU]
    B --> C{SKU base sales tier}
    C -->|">= 9000<br/>Produk Favorit"| D["100% weight<br/>Full offset sistem"]
    C -->|">= 5000<br/>Produk Populer"| E["65% weight<br/>Partial offset"]
    C -->|"< 5000<br/>Produk Reguler"| F["35% weight<br/>Minor offset"]
    D --> G[Auto-increment offset<br/>1 transaksi / 15-45 detik]
    E --> G
    F --> G
    G --> H[Combined Sales Display<br/>Shopee + Realtime Sistem]
```

### Detail Shopee Store

| Parameter | Nilai |
| - | - |
| Shop Name | Quinn of Spicy Official Store |
| Username | quinn\_kitchen |
| Badge | Shopee Mall |
| Rating | 4.8 |
| Total Ratings | 33,4RB |
| Followers | 17,7RB |
| Chat Performance | 95% |
| Location | Kota Medan |
| Products | 35 |
| Joined | 6 Tahun |

### Base Sales per SKU (Contoh)

| SKU | Produk | Base Sales Shopee |
| - | - | - |
| QOS-BCJ-150 | Baby Cumi Judes | 10.450 |
| QOS-ASP-150 | Ayam Suwir Petir | 9.860 |
| BND-01 | Paket Icip Double Isi 2 | 9.450 |
| BND-02 | Paket Trio Best Seller | 7.820 |
| QOS-STH-150 | Sambel Teri Hijau | 7.120 |
| BND-03 | Paket Family 4 Varian | 6.140 |
| QOS-SKK-150 | Kentang Korek Kriuk Jutek | 5.340 |
| QOS-STM-150 | Sambal Tuna Mercon | 4.280 |
| QOS-SBJ-150 | Sambal Bawang Judes | 3.920 |
| QOS-BCA-150 | Baby Cumi Andaliman | 3.420 |

### Algoritma Offset Realtime

```
1. Offset awal: 3.835 (BASE_SYSTEM_REALTIME_OFFSET)
2. Setiap 15-45 detik: +1 transaksi (max 15 per tick)
3. Bobot per tier:
   - >= 9.000 base sales → 100% offset
   - >= 5.000 base sales → 65% offset
   - < 5.000 base sales  → 35% offset
4. Total = Shopee Base Sales + (System Offset x Weight)
5. Persisted di localStorage (quinn_realtime_sales_live_offset)
6. Event: quinn-sales-updated (CustomEvent)
```

## Marketplace Integration (Shopee & Tokopedia)

Panel integrasi marketplace (`ShopeeTokopediaIntegrationAddon`) menampilkan status koneksi ke Shopee Open Platform dan Tokopedia Open API:

| Marketplace | Kebutuhan Integrasi | Status |
| - | - | - |
| Shopee | Partner ID, Shop ID, otorisasi dari Shopee Open Platform | Perlu konfigurasi |
| Tokopedia | App ID, Seller ID, otorisasi dari Tokopedia Open API | Perlu konfigurasi |

Integrasi ini memungkinkan:

* Sinkronisasi stok realtime antara SNISHOP dan marketplace
* Import order otomatis dari marketplace ke SNISHOP
* Update harga dan stok produk ke marketplace
* Tracking penjualan multi-channel dalam satu dashboard

## Discount & Voucher System

### Jenis Diskon

| Tipe | Deskripsi |
| - | - |
| `percentage` | Potongan persentase (0-100%) |
| `fixed_amount` | Potongan nominal tetap (rupiah) |
| `buy_x_get_y` | Beli X gratis Y |
| `coupon_code` | Kupon dengan kode khusus |

### Cakupan Diskon

| Berlaku Untuk | Deskripsi |
| - | - |
| `all_products` | Semua produk |
| `specific_products` | Produk tertentu (berdasarkan product\_ids) |
| `specific_categories` | Kategori tertentu (berdasarkan category\_names) |

### Reseller Discount per Tier

| Tier | Diskon |
| - | - |
| Free | 0% |
| Pro | 5% |
| Business | 10% |
| Advanced | 15% |
| Enterprise | 20% |

## Order Status

### MarketplaceOrder (Produk Fisik)

| Status | Deskripsi |
| - | - |
| `pending` | Order baru, belum diproses |
| `processing` | Sedang diproses/dikemas |
| `shipped` | Sudah dikirim (ada tracking\_number) |
| `delivered` | Sampai di tujuan |
| `cancelled` | Dibatalkan |

### ProductOrder (Produk Digital)

| Status | Deskripsi |
| - | - |
| `pending` | Order baru, menunggu admin |
| `processing` | Sedang dikerjakan admin |
| `completed` | Selesai dan file dikirim |
| `cancelled` | Dibatalkan |

### Payment Status

| Status | Deskripsi |
| - | - |
| `pending` | Pembayaran belum diterima |
| `paid` | Sudah dibayar |
| `refunded` | Dana dikembalikan |

## SEO & Demo Mode

* `usePageSEO` hook untuk metadata SEO otomatis
* Demo mode (`isDemoMode`) menampilkan `DEMO_PRODUCTS` dan `DEMO_COMPANY`
* Cart memerlukan user authentication

## Cara Akses

Dari sidebar, klik menu **Marketing** > **Marketplace**. Halaman ini publik — tidak memerlukan login untuk browsing, tetapi cart dan checkout memerlukan login.

## Flow Penggunaan

1. Buka Marketplace dari sidebar — lihat banner carousel dan produk unggulan
2. Filter produk berdasarkan kategori atau urutkan berdasarkan harga/popularitas
3. Klik produk untuk melihat detail di modal `MarketplaceProductDetail`
4. Tambahkan ke cart — cart count terupdate realtime
5. Checkout memerlukan login — sistem memvalidasi stok dari Inventory
6. Admin bisa mengelola commission, banner, dan order melalui tab admin

## Diagram Alur Proses

### Sinkronisasi Produk ke Marketplace

```mermaid theme={null}
sequenceDiagram
    participant Admin as Admin SNISHOP
    participant ERP as SNISHOP ERP
    participant CP as channelPricing.js
    participant MP as Marketplace (Shopee/Tokopedia)

    Admin->>ERP: Update produk (harga, stok, deskripsi)
    ERP->>CP: resolveChannelPriceKey(channel)
    CP-->>ERP: pricing_key (marketplace/b2b/whatsapp)
    ERP->>CP: getChannelPrice(product, pricing_key)
    CP-->>ERP: harga per channel
    ERP->>MP: Push produk via API
    MP-->>ERP: Konfirmasi listing berhasil
    ERP->>ERP: Catat di StockMovement (reference: distribution_shipment)
```

### Proses Order Marketplace

```mermaid theme={null}
sequenceDiagram
    participant Cust as Customer
    participant MKT as Marketplace UI
    participant CART as MarketplaceCart
    participant ORD as MarketplaceOrder
    participant INV as CompanyPOSInventory
    participant SM as StockMovement
    participant ADM as Admin CMS

    Cust->>MKT: Browse & pilih produk
    MKT->>CART: Tambah ke cart (user_id, product_id, qty)
    Cust->>MKT: Checkout
    MKT->>ORD: Buat order (status: pending, payment: paid)
    ORD->>INV: Validasi stok tersedia
    INV-->>ORD: Stok confirmed
    ORD->>SM: Catat pergerakan stok (type: out, ref: sale)
    SM->>SM: Hitung profit & margin per channel
    ORD-->>Cust: Konfirmasi order
    ADM->>ORD: Update status (pending → processing → shipped → delivered)
    ADM->>ORD: Input tracking_number
    ORD-->>Cust: Notifikasi status & resi
```

### Update Stok Multi-Channel

```mermaid theme={null}
sequenceDiagram
    participant POS as Kasir POS
    participant WEB as Web Order
    participant SP as Shopee
    participant TT as TikTok Shop
    participant ERP as SNISHOP ERP
    participant SM as StockMovement
    participant INV as Inventory

    POS->>ERP: Transaksi offline (channel: offline_pos)
    ERP->>SM: Catat movement (type: out, ref: cashier_sale)
    SM->>INV: Update stock_after

    WEB->>ERP: Web order (channel: website)
    ERP->>SM: Catat movement (type: out, ref: sale)
    SM->>INV: Update stock_after

    SP->>ERP: Shopee order (channel: marketplace)
    ERP->>SM: Catat movement (type: out, ref: sale, channel: shopee)
    SM->>SM: calculateSalesData(product, shopee, qty)
    SM->>INV: Update stock_after

    TT->>ERP: TikTok order (channel: marketplace)
    ERP->>SM: Catat movement (type: out, ref: sale, channel: tiktok)
    SM->>SM: calculateSalesData(product, tiktok, qty)
    SM->>INV: Update stock_after
```

## State Diagram - Order Lifecycle

### MarketplaceOrder Status Lifecycle

```mermaid theme={null}
stateDiagram-v2
    [*] --> pending: Order dibuat
    pending --> processing: Admin mulai proses
    pending --> cancelled: Customer batalkan / timeout
    processing --> shipped: Barang dikirim + tracking
    processing --> cancelled: Admin batalkan
    shipped --> delivered: Konfirmasi terima
    delivered --> [*]
    cancelled --> [*]

    state pending {
        [*] --> payment_check
        payment_check --> paid: Pembayaran OK
        payment_check --> pending: Menunggu bayar
    }
```

### ProductOrder Status Lifecycle

```mermaid theme={null}
stateDiagram-v2
    [*] --> pending: Order digital masuk
    pending --> processing: Admin ambil order (assigned_to_admin)
    pending --> cancelled: Customer batalkan
    processing --> completed: Admin selesai + upload result_files
    processing --> cancelled: Admin batalkan
    completed --> [*]
    cancelled --> [*]
```

### StockMovement Types

```mermaid theme={null}
stateDiagram-v2
    [*] --> in: Stok masuk (purchase/production)
    [*] --> out: Stok keluar (sale/cashier_sale)
    [*] --> transfer: Transfer antar gudang
    [*] --> adjustment: Penyesuaian stok
    [*] --> return: Retur barang
    [*] --> damaged: Barang rusak
    [*] --> hold: Hold untuk QC
    hold --> hold_release: QC selesai, stok dirilis
    hold_release --> [*]
    in --> [*]
    out --> [*]
    transfer --> [*]
    adjustment --> [*]
    return --> [*]
    damaged --> [*]
```

## Enum Reference Tables

### MarketplaceOrder.status

| Nilai | Deskripsi |
| - | - |
| `pending` | Order baru, menunggu pemrosesan |
| `processing` | Sedang diproses/dikemas |
| `shipped` | Dalam pengiriman |
| `delivered` | Sudah diterima customer |
| `cancelled` | Order dibatalkan |

### MarketplaceOrder.payment\_status

| Nilai | Deskripsi |
| - | - |
| `pending` | Belum dibayar |
| `paid` | Sudah dibayar |
| `refunded` | Dana dikembalikan |

### ProductOrder.status

| Nilai | Deskripsi |
| - | - |
| `pending` | Menunggu admin |
| `processing` | Sedang dikerjakan |
| `completed` | Selesai |
| `cancelled` | Dibatalkan |

### StockMovement.movement\_type

| Nilai | Deskripsi |
| - | - |
| `in` | Stok masuk |
| `out` | Stok keluar |
| `transfer` | Transfer antar lokasi |
| `adjustment` | Penyesuaian manual |
| `return` | Retur dari customer |
| `damaged` | Barang rusak |
| `hold` | Hold untuk quality control |
| `hold_release` | Rilis dari hold QC |

### StockMovement.reference\_type

| Nilai | Deskripsi |
| - | - |
| `purchase` | Pembelian dari supplier |
| `sale` | Penjualan |
| `cashier_sale` | Penjualan kasir POS |
| `production` | Hasil produksi |
| `raw_material_outbound` | Bahan baku keluar |
| `qc_release` | Rilis dari QC |
| `transfer` | Transfer stok |
| `adjustment` | Penyesuaian manual |
| `opname_adjustment` | Penyesuaian stock opname |
| `return` | Retur |
| `manual` | Input manual |
| `distribution_shipment` | Pengiriman distribusi |
| `quality_hold` | Hold untuk quality |

### CompanyPOSInventory.type

| Nilai | Deskripsi |
| - | - |
| `in` | Stok masuk |
| `out` | Stok keluar |
| `adjustment` | Penyesuaian |

### DigitalProduct.category

| Nilai | Deskripsi |
| - | - |
| `zoom` | Sesi Zoom online |
| `design` | Jasa desain |
| `video_editing` | Editing video |
| `plagiarism_check` | Cek plagiarisme |
| `other` | Lainnya |

### Discount.discount\_type

| Nilai | Deskripsi |
| - | - |
| `percentage` | Potongan persentase |
| `fixed_amount` | Potongan nominal tetap |
| `buy_x_get_y` | Beli X gratis Y |
| `coupon_code` | Kode kupon |

### Discount.applies\_to

| Nilai | Deskripsi |
| - | - |
| `all_products` | Semua produk |
| `specific_products` | Produk tertentu |
| `specific_categories` | Kategori tertentu |

### WarehouseLocation.location\_type

| Nilai | Deskripsi |
| - | - |
| `warehouse` | Gudang |
| `store` | Toko/cabang |
| `transit` | Lokasi transit |
| `virtual` | Gudang virtual |

### LiveStreamingActivity.platform

| Nilai | Deskripsi |
| - | - |
| `shopee_live` | Live streaming Shopee |
| `tiktok` | TikTok |
| `instagram` | Instagram Live |
| `facebook` | Facebook Live |
| `youtube` | YouTube Live |
| `other` | Platform lain |

### LiveStreamingActivity.status

| Nilai | Deskripsi |
| - | - |
| `ongoing` | Sedang berlangsung |
| `completed` | Selesai |
| `cancelled` | Dibatalkan |

## RBAC Permission Table

Berikut adalah hak akses berdasarkan role user dalam sistem marketplace:

| Operasi | Admin (Owner) | Admin (Basic) | User (Customer) | Guest (Publik) |
| - | - | - | - | - |
| Browse produk marketplace | Ya | Ya | Ya | Ya |
| Lihat detail produk | Ya | Ya | Ya | Ya |
| Tambah ke cart | Ya | Ya | Ya | Tidak |
| Checkout / buat order | Ya | Ya | Ya | Tidak |
| Kelola commission settings | Ya | Tidak | Tidak | Tidak |
| Kelola banner & tampilan | Ya | Tidak | Tidak | Tidak |
| Lihat semua orders (admin) | Ya | Ya (digital only) | Tidak | Tidak |
| Update status order | Ya | Ya (digital only) | Tidak | Tidak |
| Input tracking number | Ya | Tidak | Tidak | Tidak |
| Kelola diskon & voucher | Ya | Tidak | Tidak | Tidak |
| Kelola produk (POS/Digital) | Ya | Ya (create only) | Tidak | Tidak |
| Kelola gudang/warehouse | Ya | Tidak | Tidak | Tidak |
| Lihat laporan live streaming | Ya | Ya | Tidak | Tidak |
| Catat stock movement | Ya | Ya | Tidak | Tidak |
| Kelola integrasi marketplace | Ya | Tidak | Tidak | Tidak |
| Assign order ke admin | Ya | Ya (ambil order) | Tidak | Tidak |
| Upload result files (digital) | Ya | Ya | Tidak | Tidak |

## Tips

* Pastikan foto dan deskripsi produk sesuai standar setiap marketplace untuk approval yang lebih cepat
* Monitor stok secara berkala terutama saat periode promo besar (Harbolnas, Ramadan)
* Manfaatkan tab "For You" untuk menampilkan produk dengan scoring terbaik ke pengunjung
* Gunakan Shopee Sales Sync untuk menjaga konsistensi data antara SNISHOP dan Shopee
* Gunakan `channel_pricing` untuk mengatur harga berbeda per channel (marketplace, B2B, reseller, dll)
* Aktifkan `min_stock` alert agar tidak kehabisan stok tanpa pemberitahuan
* Manfaatkan `LiveStreamingActivity` untuk track performa live di Shopee, TikTok, dan platform lainnya
* Gunakan `ProductVoucher` untuk promo produk spesifik dan `Discount` untuk diskon kategori atau seluruh produk
* Untuk transfer antar gudang, gunakan `StockMovement` dengan type `transfer` dan isi `from_location_id` / `to_location_id`


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.