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

# Orders

<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: "POS Orders"
description: "Manajemen pesanan POS SNISHOP ERP — status workflow, multi-channel order, split payment, void management, dan integrasi inventory lintas lokasi."
---------------------------------------------------------------------------------------------------------------------------------------------------------------

# POS Orders

<img src="https://mintcdn.com/quinnofspicy/e4f_upKhVWcjsUmM/docs/mintlify/screenshots/pos/orders.png?fit=max&auto=format&n=e4f_upKhVWcjsUmM&q=85&s=24a05e37b0d7b771815df188d3879710" alt="POS Orders" width="1920" height="1080" data-path="docs/mintlify/screenshots/pos/orders.png" />

Halaman **POS Orders** menampilkan daftar seluruh transaksi POS yang telah dibuat, baik dari kasir offline maupun channel online. Halaman ini terintegrasi penuh dengan sistem inventory, membership loyalty, dan financial recording.

## Arsitektur Komponen

```mermaid theme={null}
graph TB
    subgraph ORDERS["POS Orders Page"]
        LIST["Order List<br/>CompanyPOSTransaction.filter"]
        FILTER["Multi-Filter<br/>Period, Channel, Status, Cashier"]
        DETAIL["Order Detail Modal<br/>Items, Payment, Customer"]
        VOID["Void Action<br/>voidCompanyPOSTransaction"]
    end

    subgraph ENTITIES["Entities"]
        T["CompanyPOSTransaction<br/>50+ fields"]
        P["CompanyPOSProduct<br/>Channel pricing"]
        C["Customer<br/>Loyalty tracking"]
        FR["FinancialRecord<br/>Auto-created"]
    end

    subgraph INTEGRATIONS["Cross-Module"]
        INV["Inventory<br/>Stock deduction"]
        FIN["Finance<br/>Income recording"]
        CRM["CRM<br/>Customer loyalty"]
        RPT["POS Reports<br/>Analytics data"]
    end

    LIST --> T
    FILTER --> T
    DETAIL --> T
    VOID --> T
    T --> P
    T --> C
    T --> FR
    T --> INV
    T --> FIN
    T --> CRM
    T --> RPT
```

## Status Workflow Pesanan

```mermaid theme={null}
stateDiagram-v2
    [*] --> pending: Order dibuat
    pending --> processing: Mulai persiapan
    processing --> shipped: Dikirim / siap ambil
    shipped --> delivered: Diterima customer
    delivered --> completed: Transaksi selesai
    pending --> cancelled: Dibatalkan
    processing --> cancelled: Dibatalkan
    completed --> refunded: Retur / refund
```

| Status | Warna | Deskripsi | Aksi yang Tersedia |
| - | - | - | - |
| **Pending** | Kuning | Order baru diterima, belum diproses | Process, Cancel, Detail |
| **Processing** | Biru | Sedang disiapkan (dapur/kemasan) | Ship, Cancel, Detail |
| **Shipped** | Ungu | Sudah dikirim / siap diambil | Mark Delivered, Detail |
| **Delivered** | Hijau | Sudah diterima customer | Complete, Detail |
| **Completed** | Hijau Tua | Transaksi selesai (final) | Void (PRD-07), Detail |
| **Cancelled** | Merah | Dibatalkan | Detail only |
| **Refunded** | Abu-abu | Retur/dana dikembalikan | Detail only |

## Dual POS System

SNISHOP ERP memiliki **dua sistem POS paralel** yang melayani kebutuhan berbeda:

| Aspek | Personal POS | Company POS |
| - | - | - |
| **Entity** | `POSTransaction` | `CompanyPOSTransaction` |
| **Scope** | Owner-only (RLS) | Company-scoped (multi-tenant) |
| **Multi-cashier** | Tidak | Ya |
| **Multi-location** | Tidak | Ya |
| **Channel** | Offline only | 8 channel (offline, marketplace, website, WhatsApp, reseller, B2B, Grab, social media) |
| **Split payment** | Tidak | Ya |
| **Hold/Recall** | Tidak | Ya (localStorage) |
| **Bluetooth printer** | Basic | Full integration |
| **Loyalty** | Basic tier | Full scheme (5 types) |
| **Void management** | Tidak | Ya (PRD-07) |

## CompanyPOSTransaction Entity

Entity utama untuk transaksi POS company dengan **50+ field** yang mencakup seluruh aspek transaksi:

### Core Transaction Fields

| Field | Type | Deskripsi |
| - | - | - |
| `company_id` | string | ID perusahaan (multi-tenant isolation) |
| `location_id` | string | ID lokasi/gudang tempat transaksi |
| `location_name` | string | Nama lokasi untuk display |
| `transaction_number` | string | Format: `TRX-YYYYMMDD-XXXXXX` |
| `invoice_number` | string | Nomor faktur (jika ada) |
| `transaction_date` | date-time | Tanggal dan waktu transaksi |

### Item & Pricing Fields

| Field | Type | Deskripsi |
| - | - | - |
| `items` | array | Detail item: product\_id, product\_name, sku, quantity, price, discount, subtotal, unit, unit\_cost, total\_cost, note |
| `subtotal` | number | Total sebelum diskon |
| `discount_amount` | number | Nominal diskon |
| `discount_percentage` | number | Persentase diskon |
| `tax_amount` | number | Jumlah pajak |
| `total` | number | Total akhir |
| `total_amount` | number | Total amount (alias) |

### Payment Fields

| Field | Type | Deskripsi |
| - | - | - |
| `payment_method` | enum | cash, card, transfer, ewallet, qris, saldo, mayar, debt, manual\_transfer, midtrans, tripay, stripe, paypal |
| `payment_amount` | number | Jumlah bayar |
| `payments` | array | **Split payment**: method, amount, change\_amount, account\_id, reference, verified\_by, verified\_at |
| `payment_status` | enum | pending, pending\_verification, partially\_paid, paid, failed, refunded, rejected |
| `paid_amount` | number | Total sudah dibayar |
| `remaining_amount` | number | Sisa belum dibayar |
| `excess_amount` | number | Kelebihan bayar |
| `change_amount` | number | Kembalian |
| `payment_reference` | string | Referensi pembayaran non-tunai |
| `payment_verified_by` | string | Verifikator pembayaran |
| `payment_verified_at` | date-time | Waktu verifikasi |

### Customer & Loyalty Fields

| Field | Type | Deskripsi |
| - | - | - |
| `customer_id` | string | ID customer |
| `customer_name` | string | Nama customer |
| `customer_phone` | string | Nomor telepon |
| `customer_address` | string | Alamat pengiriman |
| `points_earned` | number | Poin yang didapat dari transaksi ini |
| `points_used` | number | Poin yang digunakan untuk diskon |

### Operational Fields

| Field | Type | Deskripsi |
| - | - | - |
| `cashier_id` | string | ID kasir yang memproses |
| `cashier_name` | string | Nama kasir |
| `assigned_to_id` | string | ID staff/therapist yang ditugaskan |
| `assigned_to_name` | string | Nama staff/therapist |
| `source` | enum | pos, online\_catalog, website, marketplace, landing\_page, whatsapp, reseller, b2b, grab, social\_media |
| `sales_channel` | enum | offline\_pos, offline, website, online\_catalog, marketplace, landing\_page, whatsapp, reseller, b2b, grab, social\_media |
| `order_status` | enum | pending, processing, shipped, delivered, completed, cancelled, rejected |
| `status` | enum | pending, processing, shipped, delivered, completed, failed, refunded, cancelled |
| `tracking_number` | string | Nomor resi/tracking |
| `notes` | string | Catatan tambahan |
| `metadata` | object | Data tambahan (cogs\_total, dll) |

### Timestamp Fields

| Field | Type | Deskripsi |
| - | - | - |
| `verified_at` | date-time | Waktu verifikasi pembayaran |
| `verified_by` | string | User yang memverifikasi |
| `processed_at` | date-time | Waktu mulai diproses |
| `shipped_at` | date-time | waktu dikirim |
| `delivered_at` | date-time | Waktu diterima |
| `cancelled_at` | date-time | Waktu dibatalkan |

## Split Payment (Multi-Tender)

Transaksi POS mendukung **split payment** — customer bisa membayar dengan beberapa metode sekaligus:

```mermaid theme={null}
graph LR
    A["Total: Rp 150.000"] --> B["Payment 1<br/>Cash: Rp 100.000"]
    A --> C["Payment 2<br/>E-Wallet: Rp 50.000"]
    B --> D["account_id: cash_register"]
    C --> E["account_id: gopay"]
    D --> F["Change: Rp 0"]
    E --> F
```

**Implementasi:**

* Field `payments[]` menyimpan array tender
* Setiap tender memiliki: `method`, `amount`, `change_amount`, `account_id`, `reference`, `verified_by`, `verified_at`
* `resolvePaymentTenders()` menormalisasi single vs split payment untuk reporting
* Total semua tender harus sama dengan `total` transaksi

## 8 Channel Penjualan

| Channel | Kode | Deskripsi | Pricing Source |
| - | - | - | - |
| **Offline POS** | `offline_pos` | Kasir langsung | Base price |
| **Website** | `website` | Toko online sendiri | `channel_pricing.website` |
| **Online Catalog** | `online_catalog` | Katalog digital | `channel_pricing.website` |
| **Marketplace** | `marketplace` | Shopee, Tokopedia, dll | `channel_pricing.marketplace` |
| **WhatsApp** | `whatsapp` | Order via WhatsApp | `channel_pricing.whatsapp` |
| **Reseller** | `reseller` | Order dari reseller | `channel_pricing.reseller` |
| **B2B** | `b2b` | Order bisnis ke bisnis | `channel_pricing.b2b` |
| **Grab** | `grab` | GrabFood | `channel_pricing.grab` |
| **Social Media** | `social_media` | Instagram, TikTok, dll | `channel_pricing.social_media` |

**Channel Pricing Gate (A3):**

* Channel online **wajib** punya harga di `channel_pricing{}`
* Jika harga channel tidak ada, item di-drop dengan notifikasi
* Offline POS fallback ke base price

## Void Management (PRD-07)

Transaksi yang sudah completed bisa di-void dengan mekanisme keamanan:

```mermaid theme={null}
sequenceDiagram
    participant Cashier
    participant Supervisor
    participant API
    participant Inventory
    participant Finance

    Cashier->>API: voidCompanyPOSTransaction(txId, reason)
    API->>API: Validate: only completed transactions
    API->>API: Check permission: can_edit_pos_transactions
    API->>Inventory: Reverse stock deduction
    Inventory->>Inventory: Restore stock levels
    API->>Finance: Reverse FinancialRecord
    Finance->>Finance: Create reversal entry
    API->>API: Update transaction status = 'refunded'
    API->>Cashier: Void confirmed
```

**Syarat Void:**

* Status harus `completed`
* User harus punya permission `can_edit_pos_transactions`
* Alasan void wajib diisi
* Stock otomatis dikembalikan
* Financial record dibuat reversal entry

## Discount Approval Flow (A1)

Diskon di atas threshold memerlukan approval supervisor:

```mermaid theme={null}
graph LR
    A["Cashier input diskon 25%"] --> B{"> 20%?"}
    B -->|Ya| C["Minta PIN Supervisor"]
    C --> D["approveCashierDiscount()"]
    D --> E{PIN Valid?}
    E -->|Ya| F["Discount disetujui"]
    E -->|Tidak| G["Ditolak"]
    B -->|Tidak| H["Auto-approve"]
```

**Threshold:** `DISCOUNT_APPROVAL_THRESHOLD = 20%`

**Implementasi:**

1. Cashier input diskon > 20%
2. Dialog approval muncul minta PIN supervisor
3. Server function `approveCashierDiscount` validasi PIN
4. Jika valid, `discountApprovedBy` dicatat
5. Jika tidak valid, error ditampilkan

## Transaction Integrity (DQ-01)

Sistem mencegah double-submission transaksi melalui beberapa mekanisme:

| Mekanisme | Implementasi | Fungsi |
| - | - | - |
| **Module-level lock** | `isTransactionInProgress` flag | Prevent concurrent submissions |
| **Safety timeout** | 60 detik auto-reset | Prevent permanent lock on crash |
| **Idempotency key** | `txq_{timestamp36}_{random6}` | Prevent duplicate transactions |
| **Write-Ahead Log** | `TransactionQueue` di localStorage | Crash recovery |

### Write-Ahead Log Lifecycle

```mermaid theme={null}
stateDiagram-v2
    [*] --> begin: generateIdempotencyKey()
    begin --> checkpoint_tx: CompanyPOSTransaction.created
    checkpoint_tx --> checkpoint_stock: Inventory.deducted
    checkpoint_stock --> checkpoint_finance: FinancialRecord.created
    checkpoint_finance --> complete: All steps done
    complete --> [*]

    begin --> fail: Error at any step
    checkpoint_tx --> fail: Error at any step
    checkpoint_stock --> fail: Error at any step
    checkpoint_finance --> fail: Error at any step
    fail --> [*]: Retry or cleanup
```

## Cross-Module Integration

```mermaid theme={null}
graph TB
    subgraph POS["POS Transaction"]
        TX["CompanyPOSTransaction"]
    end

    subgraph INVENTORY["Inventory Module"]
        INV["Inventory.filter/update"]
        SM["StockMovement.create"]
        SA["StockAlert.create"]
        TL["transferInventoryLots"]
    end

    subgraph FINANCE["Finance Module"]
        FR["FinancialRecord.create<br/>Income + COGS + Shipping"]
        ACC["Account.filter/create<br/>Payment account"]
    end

    subgraph CRM["CRM Module"]
        CUST["Customer.filter/update"]
        MEM["CustomerMembership.filter"]
        PM["POSMember.filter"]
        MCL["manageCustomerLoyalty"]
    end

    subgraph COMMUNICATION["Communication"]
        WA["WhatsAppSession.filter"]
        TASK["Task.create"]
    end

    TX --> INV
    TX --> SM
    TX --> SA
    TX --> TL
    TX --> FR
    TX --> ACC
    TX --> CUST
    TX --> MEM
    TX --> PM
    TX --> MCL
    TX --> WA
    TX --> TASK
```

### Integrasi Detail

| Modul | Entity/Function | Aksi | Trigger |
| - | - | - | - |
| **Inventory** | `Inventory.filter/update` | Deduct stock | On transaction complete |
| **Inventory** | `StockMovement.create` | Audit trail | On stock change |
| **Inventory** | `StockAlert.create` | Reconciliation | On deduction failure |
| **Inventory** | `transferInventoryLots` | Inter-location transfer | When stock unavailable at location |
| **Finance** | `FinancialRecord.create` | Record income, COGS, shipping | On transaction complete |
| **Finance** | `Account.filter/create` | Payment account | On payment processing |
| **CRM** | `Customer.filter/update` | Update stats | On transaction complete |
| **CRM** | `manageCustomerLoyalty` | Process points/stamps | On transaction complete |
| **CRM** | `POSMember.filter` | Membership lookup | On customer selection |
| **Communication** | `WhatsAppSession.filter` | Send receipt | Auto after transaction |
| **Task** | `Task.create` | Assign to therapist/staff | On order with assignment |

## Real-Time Synchronization

POS Orders menggunakan **3-tier sync strategy** untuk memastikan data selalu terkini:

| Layer | Teknologi | Scope | Latency |
| - | - | - | - |
| **Cross-tab** | BroadcastChannel | Same browser, different tabs | \< 100ms |
| **Server push** | WebSocket subscribe | All devices | \< 500ms |
| **Polling** | setInterval fallback | All clients | 10-120s |

### BroadcastChannel Directory

| Channel | Posted By | Listened By | Event |
| - | - | - | - |
| `snishop_pos_updates` | Cashier (on transaction) | Reports, Queue | NEW\_TRANSACTION |
| `snishop_inventory_updates` | Cashier (on stock change) | Sync, Reports | TRANSACTION\_COMPLETED |
| `snishop_order_queue` | Cashier, OrderManagement | Queue, Cashier | NEW\_ORDER |
| `snishop_finance_updates` | Cashier (on payment) | Finance module | POS\_TRANSACTION |
| `snishop_reports_updates` | Cashier (on transaction) | Reports | NEW\_TRANSACTION |

## Best Practices

### Untuk Kasir

1. **Selalu cek stok** sebelum menambah item ke cart
2. **Pilih lokasi** yang benar untuk transaksi
3. **Verifikasi pembayaran non-tunai** dengan referensi yang valid
4. **Gunakan split payment** jika customer bayar dengan multiple methods
5. **Cetak struk** sebelum customer meninggalkan kasir

### Untuk Manager

1. **Review void transactions** setiap hari
2. **Monitor split payment** untuk anomali
3. **Cek discount approvals** untuk pola abuse
4. **Verify online orders** dalam 5 menit
5. **Review channel performance** di POS Reports

### Untuk Developer

1. **Selalu gunakan idempotency key** saat create transaction
2. **Handle stock deduction failure** dengan StockAlert
3. **Broadcast ke semua channel** setelah transaction complete
4. **Validate channel pricing** sebelum submit
5. **Log all void actions** untuk audit trail


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