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

# Company settings

<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: "Company Settings"
description: "Konfigurasi company profile 12 tab: informasi umum, members & roles, pembayaran, WhatsApp, struk, absensi, landing page, printer, menu access, modul, backup, dan audit trail."
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

# Company Settings

<img src="https://mintcdn.com/quinnofspicy/e4f_upKhVWcjsUmM/docs/mintlify/screenshots/core/company-settings.png?fit=max&auto=format&n=e4f_upKhVWcjsUmM&q=85&s=105c6b556e61c9eb909e8705ca872ef6" alt="Company Settings" width="1920" height="1080" data-path="docs/mintlify/screenshots/core/company-settings.png" />

Company Settings adalah halaman konfigurasi level company yang hanya bisa diakses oleh owner. CompanySettings.jsx (812 baris) mengelola 12 tab konfigurasi yang mencakup seluruh aspek operasional company — dari profil dasar, member management, payment accounts, WhatsApp integration, receipt templates, attendance settings, landing page, Bluetooth printer, menu access control, module toggles, backup & security, hingga audit trail.

Setiap perubahan di-save melalui server function `updateCompanyProfile` dan di-broadcast ke semua tab browser yang terbuka via `BroadcastChannel('snishop_company_change')` dan `CustomEvent('companyChanged')`, memastikan konsistensi state secara real-time.

Owner verification menggunakan dual check: `company.owner_email === currentUser.email` DAN CompanyMember record dengan `role='owner'`.

## Arsitektur Komponen

```mermaid theme={null}
graph TD
    A[CompanySettings.jsx<br/>812 lines] --> B[12 Tab Configuration]
    
    B --> C[Informasi Umum<br/>Company Profile]
    B --> D[Members & Roles<br/>RBAC Management]
    B --> E[Pembayaran Customer<br/>Bank & E-Wallet]
    B --> F[WhatsApp<br/>Wablas Integration]
    B --> G[Struk/Receipt<br/>Template Config]
    B --> H[Pengaturan Absensi<br/>Attendance Settings]
    B --> I[Landing Page<br/>Company Landing]
    B --> J[Printer Kasir<br/>Bluetooth Printer]
    B --> K[Kontrol Menu<br/>Menu Access]
    B --> L[Modul & Fitur<br/>Module Toggles]
    B --> M[Backup & Keamanan<br/>Data Backup]
    B --> N[Audit Trail<br/>Security Logs]
    
    D --> O[CompanyAccessControl.jsx<br/>273 lines]
    D --> P[RoleManager.jsx<br/>1398 lines]
    
    H --> Q[AttendanceSettingsForm]
    J --> R[BluetoothPrinterManager]
    M --> S[CompanyBackupManager]
    F --> T[WablasSettingsForm]
    G --> U[ReceiptSettingsForm]
    I --> V[LandingPageSettings]
    K --> W[MenuAccessManager]
    L --> X[CompanyModulesForm]
    N --> Y[AuditTrailViewer.jsx<br/>388 lines]
```

## Entity & Model

### Company

| Field | Tipe | Deskripsi |
| - | - | - |
| `id` | UUID | Primary key |
| `name` | String | Nama company |
| `description` | Text | Deskripsi company |
| `industry` | Enum | `retail`, `manufacturing`, `services`, `technology`, `food_beverage`, `healthcare`, `education`, `other` |
| `address` | Text | Alamat lengkap |
| `phone` | String | Nomor telepon |
| `email` | String | Email company |
| `website` | String | Website URL |
| `tax_id` | String | NPWP |
| `logo_url` | String | URL logo (Cloudinary) |
| `owner_id` | UUID | ID owner perusahaan |
| `owner_email` | String | Email owner (untuk verifikasi) |
| `owner_subscription_plan` | Enum | `free`, `pro`, `business`, `advanced`, `enterprise` |
| `employee_count` | Number | Jumlah karyawan (default: 0) |
| `metadata` | Object | Data tambahan / legacy |
| `landing_page_config` | Object | Konfigurasi landing page perusahaan |
| `business_type` | String | Kategori bisnis saat onboarding |
| `active_modules` | String | JSON string array ID modul aktif |
| `settings` | Object | Konfigurasi global company (jam kerja, leave policy, expiry thresholds, batch strategy, pajak, spoilage) |
| `created_at` | Timestamp | Waktu pembuatan |
| `updated_at` | Timestamp | Waktu update |

### CompanyMember

| Field | Tipe | Deskripsi |
| - | - | - |
| `id` | UUID | Primary key |
| `user_id` | UUID | Reference ke User |
| `company_id` | UUID | Reference ke Company |
| `user_email` | String | Email user member |
| `user_name` | String | Nama lengkap member |
| `role` | Enum | `owner`, `admin`, `supervisor`, `store_admin`, `stock_admin`, `finance_admin`, `hr_admin`, `transaction_admin`, `employee`, `production_operator`, `qc_inspector`, `sales_marketing`, `partner_distributor` |
| `employee_id` | UUID | Link ke entitas Employee |
| `department` | String | Departemen member |
| `position` | String | Jabatan/posisi member |
| `permissions` | JSON | 35+ granular permission flags |
| `assigned_locations` | Array | Daftar ID lokasi gudang/outlet yang diizinkan (RBAC-01) |
| `working_hours` | Object | Jam kerja karyawan (start, end) |
| `salary` | Number | Gaji karyawan (opsional) |
| `status` | Enum | `active`, `inactive`, `pending` |
| `joined_date` | Date | Tanggal bergabung |
| `invited_by` | String | Email yang mengundang |
| `notes` | String | Catatan tambahan |

## 12 Tab Konfigurasi

### 1. Informasi Umum

Profile company dengan field lengkap:

| Field | Deskripsi |
| - | - |
| Company Name | Nama resmi perusahaan |
| Description | Deskripsi bisnis |
| Industry | Sektor industri |
| Address | Alamat lengkap |
| Phone | Nomor telepon |
| Email | Email perusahaan |
| Website | Website URL |
| Tax ID (NPWP) | Nomor NPWP untuk faktur pajak |
| Logo | Upload logo via Cloudinary |
| Modal Awal | Modal awal perusahaan |

### 2. Members & Roles

RBAC management via `CompanyAccessControl.jsx` (273 baris) dan `RoleManager.jsx` (1398 baris):

**9 Role Templates**:

| Role | Emoji | Akses Utama |
| - | - | - |
| Owner | 👑 | Full access — semua permissions |
| Admin | 🛡️ | Semua kecuali settings edit dan role management |
| Supervisor | 👁️ | Read-only finance/inventory/HR |
| Cashier | 💰 | POS, orders, customers |
| Inventory Staff | 📦 | Stock management, warehouse ops |
| Production Operator | 🏭 | Manufacturing, BOM, production orders |
| QC Inspector | 🔬 | Quality control, batch verification |
| Finance Staff | 💳 | Invoicing, expenses, bank, reports |
| CS Agent | 🎧 | POS view-only, notes, CRM via AI |

**Permission Groups** (\~40+ individual permissions):

| Group | Permissions |
| - | - |
| Dashboard & Tasks | View dashboard, manage tasks |
| Notes | Create, edit, delete notes |
| HR & Payroll | View employees, manage attendance, process payroll |
| Finance | View invoices, create expenses, manage bank accounts |
| Inventory | View stock, create movements, approve adjustments |
| POS/Kasir | Create orders, process payments, view reports |
| Manufacturing | Create production orders, verify batches |
| Stock Opname | Participate, approve opname |
| Reports & Management | View reports, export data |
| AI Agent & Automation | Use AI features, manage automation |

### 3. Pembayaran Customer

Kelola akun pembayaran untuk customer top-up:

| Field | Deskripsi |
| - | - |
| Account Type | Bank / E-Wallet |
| Bank Name | Nama bank (BCA, Mandiri, BNI, dll) |
| Account Number | Nomor rekening |
| Account Holder | Nama pemilik rekening |
| QRIS | Upload QRIS image |

### 4. WhatsApp

Integrasi WhatsApp via Wablas:

| Field | Deskripsi |
| - | - |
| Token | Wablas API token |
| Phone Number | Nomor WhatsApp bisnis |
| Webhook URL | URL untuk menerima pesan masuk |
| Auto Reply | Template balasan otomatis |

### 5. Struk/Receipt

Template struk kasir:

| Field | Deskripsi |
| - | - |
| Template | Compact / Standard / Detailed |
| Header Text | Teks header struk |
| Footer Text | Teks footer struk |
| Show Logo | Tampilkan logo di struk |
| Show QRIS | Tampilkan QRIS di struk |
| Paper Size | 58mm / 80mm |

### 6. Pengaturan Absensi

Konfigurasi attendance via `AttendanceSettingsForm`:

| Field | Deskripsi |
| - | - |
| Work Hours | Jam kerja (start, end) |
| Late Tolerance | Toleransi keterlambatan (menit) |
| Location Required | Wajib GPS saat absen |
| Selfie Required | Wajib selfie saat absen |
| Overtime Rules | Aturan lembur |

### 7. Landing Page

Konfigurasi landing page company via `LandingPageSettings`:

| Field | Deskripsi |
| - | - |
| Hero Title | Judul hero section |
| Hero Subtitle | Subjudul hero |
| Hero Image | Gambar hero |
| Features | Daftar fitur yang ditampilkan |
| Testimonials | Testimonial customer |
| CTA Button | Call-to-action button |

### 8. Printer Kasir

Manajemen Bluetooth printer via `BluetoothPrinterManager`:

| Field | Deskripsi |
| - | - |
| Printer Name | Nama printer |
| MAC Address | MAC address Bluetooth |
| Paper Size | 58mm / 80mm |
| Auto Connect | Auto-connect saat buka kasir |
| Test Print | Cetak test struk |

### 9. Kontrol Menu

Kontrol visibilitas menu via `MenuAccessManager`:

| Menu | Toggle | Deskripsi |
| - | - | - |
| POS | ✅/❌ | Tampilkan/sembunyikan POS |
| Inventory | ✅/❌ | Tampilkan/sembunyikan Inventory |
| Manufacturing | ✅/❌ | Tampilkan/sembunyikan Manufacturing |
| Finance | ✅/❌ | Tampilkan/sembunyikan Finance |
| HR | ✅/❌ | Tampilkan/sembunyikan HR |
| CRM | ✅/❌ | Tampilkan/sembunyikan CRM |
| AI | ✅/❌ | Tampilkan/sembunyikan AI |

### 10. Modul & Fitur

Toggle modul dan fitur via `CompanyModulesForm`:

| Modul | Fitur yang Bisa Di-toggle |
| - | - |
| POS | Multi-payment, order queue, customer display |
| Inventory | Barcode, stock opname, multi-location |
| Manufacturing | BOM, QC, production scheduling |
| Finance | Invoice, expense, budget planning |
| HR | Attendance, payroll, performance review |

### 11. Backup & Keamanan

Data backup dan restore via `CompanyBackupManager`:

| Fitur | Deskripsi |
| - | - |
| Export Data | Export semua data company (JSON) |
| Import Data | Import data dari backup |
| Scheduled Backup | Backup otomatis (harian/mingguan) |
| Backup History | Riwayat backup sebelumnya |

### 12. Audit Trail

View audit logs via `AuditTrailViewer.jsx` (388 baris):

**16+ Action Types**:

| Category | Actions |
| - | - |
| Inventory | `stock_in`, `stock_out`, `transfer_inventory`, `approve_stock_opname` |
| Manufacturing | `start_production_batch`, `verify_release_batch`, `approve_quality_check`, `reject_quality_check` |
| Finance | `record_invoice_payment`, `verify_online_payment`, `finalize_cashier_sale` |
| CRM | `create_pos_customer` |
| RBAC | `update_member_role`, `delete_company_member`, `create_company_invitation` |
| Distribution | `create_distribution_shipment`, `confirm_distribution_shipment` |

**Audit Log Detail**:

| Field | Deskripsi |
| - | - |
| Actor | User yang melakukan aksi |
| Action | Jenis aksi |
| Entity | Entitas yang diubah |
| Old Value | Nilai sebelum perubahan |
| New Value | Nilai setelah perubahan |
| Timestamp | Waktu aksi |
| IP Address | IP address user |
| Correlation ID | ID untuk trace related operations |

## Cara Akses

Menu: **Settings** > **Company Settings**

**Akses terbatas**: Hanya company owner yang bisa mengakses halaman ini.

## Owner Verification

```mermaid theme={null}
flowchart TD
    A[User Access CompanySettings] --> B{company.owner_email === user.email?}
    B -->|No| C[Access Denied]
    B -->|Yes| D{CompanyMember role = 'owner'?}
    D -->|No| C
    D -->|Yes| E[Access Granted]
    E --> F[Show 12 Tabs]
```

**Dual Check**: Email match + member role verification untuk keamanan maksimal.

## Cross-Tab Synchronization

```mermaid theme={null}
sequenceDiagram
    participant A as Tab 1
    participant BC as BroadcastChannel
    participant CE as CustomEvent
    participant B as Tab 2
    participant C as Tab 3

    A->>A: User edit company profile
    A->>A: Call updateCompanyProfile()
    A->>BC: Broadcast 'snishop_company_change'
    A->>CE: Dispatch 'companyChanged'
    
    BC-->>B: Receive broadcast
    B->>B: Refresh company data
    
    BC-->>C: Receive broadcast
    C->>C: Refresh company data
```

## Integrasi Cross-Module

```mermaid theme={null}
graph LR
    A[Company Settings] --> B[CompanyAccessControl<br/>RBAC]
    A --> C[AttendanceSettingsForm<br/>HR]
    A --> D[BluetoothPrinterManager<br/>POS]
    A --> E[CompanyBackupManager<br/>Data]
    A --> F[WablasSettingsForm<br/>WhatsApp]
    A --> G[ReceiptSettingsForm<br/>POS]
    A --> H[LandingPageSettings<br/>Marketing]
    A --> I[MenuAccessManager<br/>Navigation]
    A --> J[CompanyModulesForm<br/>Features]
    A --> K[AuditTrailViewer<br/>Security]
```

## Tips

* Dual owner verification memastikan hanya owner asli yang bisa mengubah konfigurasi company
* Gunakan BroadcastChannel sync untuk memastikan semua tab mendapat update real-time
* Audit trail bersifat append-only — tidak bisa dihapus atau diubah, memastikan forensic integrity
* Grace period 3 hari untuk membership expired — user masih bisa akses read-only sebelum benar-benar terblokir
* Test Bluetooth printer connection sebelum digunakan di produksi untuk menghindari masalah saat transaksi
* Review audit trail secara berkala untuk mendeteksi aktivitas mencurigakan

***

## Diagram Relasi Entitas (ERD)

```mermaid theme={null}
erDiagram
    User ||--o{ Company : "memiliki (owner)"
    User ||--o{ CompanyMember : "terdaftar sebagai"
    Company ||--o{ CompanyMember : "memiliki anggota"
    Company ||--o| CompanyAttendanceSettings : "memiliki konfigurasi"
    Company ||--o| CompanyModuleSettings : "memiliki konfigurasi"
    Company ||--o| CompanyReceiptSettings : "memiliki template"
    Company ||--o| CompanyWablasSettings : "memiliki integrasi"
    Company ||--o| CompanyPOSSettings : "memiliki konfigurasi"
    Company ||--o{ CompanyInvitation : "mengirim undangan"
    Company ||--o{ CompanyBackup : "memiliki riwayat"
    Company ||--o{ PrinterSettings : "memiliki printer"
    Company ||--o{ MenuAccessProfile : "memiliki profil"
    Company ||--o{ Subscription : "memiliki langganan"
    CompanyMember }o--|| User : "merujuk ke"
    CompanyInvitation }o--|| User : "diundang ke"
    MenuAccessProfile }o--|| CompanyMember : "mengatur akses menu untuk role"

    User {
        UUID id PK
        String email UK
        String full_name
        Enum role "admin | user"
        Enum subscription_plan "free | pro | business | advanced | enterprise"
        String active_company_id FK
        Enum admin_type "owner | basic"
        Enum admin_tier "none | business | advanced | enterprise"
        Number balance
        Number user_level
    }

    Company {
        UUID id PK
        String name
        String owner_id FK
        String owner_email
        Enum industry "retail | manufacturing | services | technology | food_beverage | healthcare | education | other"
        String address
        String phone
        String email
        String website
        String logo_url
        String tax_id
        Number employee_count
        Object settings
        Object landing_page_config
        String active_modules
    }

    CompanyMember {
        UUID id PK
        String company_id FK
        String user_id FK
        String user_email
        String user_name
        Enum role "owner | admin | supervisor | store_admin | stock_admin | finance_admin | hr_admin | transaction_admin | employee | production_operator | qc_inspector | sales_marketing | partner_distributor"
        String employee_id FK
        String department
        String position
        Object permissions
        Array assigned_locations
        Object working_hours
        Number salary
        Enum status "active | inactive | pending"
        Date joined_date
        String invited_by
    }

    CompanyAttendanceSettings {
        UUID id PK
        String company_id FK
        String description
        Object office_location
        Number office_radius_meters
        Boolean require_office_location
        Number default_accuracy_tolerance_meters
        Array locations
        Object working_hours
        Array shifts
        Object overtime_settings
        Number late_tolerance_minutes
        Array working_days
        Boolean auto_clock_out_enabled
        String auto_clock_out_time
        Boolean require_photo
        Boolean require_notes
    }

    CompanyModuleSettings {
        UUID id PK
        String company_id FK
        String description
        Object modules_enabled
        Enum business_type "retail | manufacturing | services | digital_products | food_beverage | healthcare | education | consulting | hybrid"
        Enum inventory_method "FIFO | LIFO | Average | Specific_Identification"
        Boolean multi_warehouse_enabled
        Boolean lot_tracking_enabled
        Boolean serial_tracking_enabled
        Boolean barcode_enabled
        Boolean auto_reorder_enabled
        Boolean production_planning_enabled
        Boolean quality_control_enabled
        Boolean attendance_integration
        Boolean invoice_auto_create
        Boolean pos_inventory_sync
        Boolean finance_auto_record
        Array custom_fields
        Array workflow_rules
        Object notification_settings
    }

    CompanyReceiptSettings {
        UUID id PK
        String company_id FK
        String description
        Boolean show_company_logo
        Boolean show_company_address
        Boolean show_customer_name
        Boolean show_customer_points
        Boolean show_points_earned
        Boolean show_payment_details
        Boolean show_change
        String custom_header_text
        String custom_footer_text
        Boolean show_promo_message
        String promo_message
    }

    CompanyWablasSettings {
        UUID id PK
        String company_id FK
        String description
        Boolean use_system_api
        String wablas_api_key
        String wablas_secret_key
        String wablas_domain
        Boolean is_active
        Boolean auto_send_receipt
        String receipt_message_template
    }

    CompanyPOSSettings {
        UUID id PK
        String company_id FK
        String description
        Object receipt_settings
    }

    CompanyInvitation {
        UUID id PK
        String company_id FK
        String company_name
        String invited_email
        String invited_by
        String invited_by_name
        Enum role "owner | admin | supervisor | store_admin | stock_admin | finance_admin | hr_admin | transaction_admin | employee"
        String department
        String position
        Object permissions
        String message
        Enum status "pending | accepted | rejected | expired | cancelled"
        DateTime expires_at
        DateTime accepted_at
        DateTime rejected_at
        DateTime cancelled_at
    }

    CompanyBackup {
        UUID id PK
        String company_id FK
        String company_name
        Enum backup_type "manual | auto_reminder | scheduled"
        Enum backup_scope "full | partial"
        DateTime backup_date
        String created_by_email
        String created_by_name
        Object data_summary
        String file_url
        Number file_size_mb
        String notes
        Enum status "completed | failed | processing"
        String checksum
        String schema_version
        String backup_version
        String error_log
        DateTime restore_tested_at
        Number restore_record_count
        Number retention_days
        String drive_file_id
        Enum drive_integration_status "not_configured | active | failed | disabled"
    }

    PrinterSettings {
        UUID id PK
        String company_id FK
        String user_id FK
        String printer_name
        String description
        String device_id
        String device_name
        Enum paper_width "58mm | 80mm"
        Enum encoding "UTF-8 | GB18030 | Big5 | Shift_JIS | EUC-KR"
        Boolean auto_cut
        Boolean beep_on_print
        Boolean auto_reconnect
        Boolean print_logo
        String logo_url
        String header_text
        String footer_text
        Enum font_size "small | normal | large"
        DateTime last_connected
        Number connection_count
        Boolean is_default
    }

    MenuAccessProfile {
        UUID id PK
        String company_id FK
        Enum role "owner | admin | supervisor | store_admin | stock_admin | finance_admin | hr_admin | transaction_admin | employee"
        String profile_name
        String description
        Array allowed_menus
        Enum dashboard_type "owner | admin | supervisor | employee | cashier | finance | hr | inventory"
        Boolean is_default
        String created_by
    }

    Subscription {
        UUID id PK
        String user_id FK
        String company_id FK
        String service_name
        Number cost
        String currency
        Enum billing_cycle "weekly | monthly | quarterly | yearly"
        Date next_due_date
        Date start_date
        Date end_date
        Enum status "active | paused | cancelled"
        String payment_method
        Boolean auto_renew
        String notes
    }
```

## Tabel Schema Entitas Lengkap

### Company

| Field | Tipe | Wajib | Default | Deskripsi |
| - | - | - | - | - |
| `id` | UUID | Ya | Auto-generate | Primary key |
| `name` | String | Ya | — | Nama perusahaan |
| `owner_id` | String | Ya | — | ID owner perusahaan |
| `owner_email` | String | Ya | — | Email owner untuk verifikasi |
| `owner_subscription_plan` | Enum | Tidak | — | Plan membership owner: `free`, `pro`, `business`, `advanced`, `enterprise` |
| `description` | String | Tidak | — | Deskripsi perusahaan |
| `industry` | Enum | Tidak | — | Sektor industri: `retail`, `manufacturing`, `services`, `technology`, `food_beverage`, `healthcare`, `education`, `other` |
| `address` | String | Tidak | — | Alamat lengkap perusahaan |
| `phone` | String | Tidak | — | Nomor telepon perusahaan |
| `email` | String | Tidak | — | Email perusahaan |
| `website` | String | Tidak | — | Website URL |
| `logo_url` | String | Tidak | — | URL logo (Cloudinary) |
| `tax_id` | String | Tidak | — | NPWP perusahaan |
| `employee_count` | Number | Tidak | `0` | Jumlah karyawan |
| `metadata` | Object | Tidak | — | Data tambahan / legacy |
| `landing_page_config` | Object | Tidak | — | Konfigurasi landing page (tema, konten, pembayaran) |
| `business_type` | String | Tidak | — | Kategori bisnis saat onboarding |
| `active_modules` | String | Tidak | — | JSON string berisi array ID modul aktif |
| `settings` | Object | Tidak | — | Konfigurasi global: jam kerja, hari kerja, leave policy, expiry thresholds, batch strategy, pajak, spoilage |
| `settings.working_hours_start` | String | Tidak | `"09:00"` | Jam mulai kerja |
| `settings.working_hours_end` | String | Tidak | `"17:00"` | Jam selesai kerja |
| `settings.working_days` | Array\[Number] | Tidak | `[1,2,3,4,5]` | Hari kerja (1=Senin, 7=Minggu) |
| `settings.leave_policy` | Object | Tidak | — | Kebijakan cuti |
| `settings.expiry_thresholds` | Object | Tidak | `{critical:7, warning:30, info:90}` | Ambang batas peringatan kedaluwarsa lot (hari) |
| `settings.batch_allocation_strategy` | Enum | Tidak | `"fifo"` | Strategi alokasi batch: `fifo` atau `fefo` |
| `settings.tax` | Object | Tidak | — | Konfigurasi pajak (enabled, rate, mode, rounding) |
| `settings.spoilage_default_treatment` | Enum | Tidak | `"absorbed_normal"` | Perlakuan spoilage: `absorbed_normal` atau `expense_abnormal` |

### CompanyMember

| Field | Tipe | Wajib | Default | Deskripsi |
| - | - | - | - | - |
| `id` | UUID | Ya | Auto-generate | Primary key |
| `company_id` | String | Ya | — | Reference ke Company |
| `user_id` | String | Tidak | — | Reference ke User |
| `user_email` | String | Ya | — | Email user member |
| `user_name` | String | Tidak | — | Nama lengkap member |
| `role` | Enum | Tidak | `"employee"` | Role dalam perusahaan |
| `employee_id` | String | Tidak | — | Link ke entitas Employee |
| `department` | String | Tidak | — | Departemen |
| `position` | String | Tidak | — | Jabatan/posisi |
| `permissions` | Object | Tidak | — | 35+ granular permission flags (lihat tabel permissions di bawah) |
| `assigned_locations` | Array\[String] | Tidak | — | Daftar ID lokasi gudang/outlet yang diizinkan (RBAC-01) |
| `working_hours` | Object | Tidak | `{start:"09:00", end:"17:00"}` | Jam kerja karyawan |
| `salary` | Number | Tidak | — | Gaji karyawan (opsional) |
| `status` | Enum | Tidak | `"active"` | Status keanggotaan: `active`, `inactive`, `pending` |
| `joined_date` | Date | Tidak | — | Tanggal bergabung |
| `invited_by` | String | Tidak | — | Email yang mengundang |
| `notes` | String | Tidak | — | Catatan tambahan |

### CompanyMember Permissions (35+ Flags)

| Permission | Tipe | Default | Deskripsi |
| - | - | - | - |
| `can_view_dashboard` | Boolean | `true` | Lihat dashboard |
| `can_view_tasks` | Boolean | `true` | Lihat daftar tugas |
| `can_create_tasks` | Boolean | `true` | Buat tugas baru |
| `can_edit_tasks` | Boolean | `true` | Edit tugas |
| `can_delete_tasks` | Boolean | `false` | Hapus tugas |
| `can_view_notes` | Boolean | `true` | Lihat catatan |
| `can_create_notes` | Boolean | `true` | Buat catatan baru |
| `can_edit_notes` | Boolean | `true` | Edit catatan |
| `can_delete_notes` | Boolean | `false` | Hapus catatan |
| `can_view_hr` | Boolean | `false` | Lihat data HR |
| `can_edit_hr` | Boolean | `false` | Edit data HR |
| `can_view_finance` | Boolean | `false` | Lihat data keuangan |
| `can_edit_finance` | Boolean | `false` | Edit data keuangan |
| `can_view_inventory` | Boolean | `false` | Lihat data inventori |
| `can_edit_inventory` | Boolean | `false` | Edit data inventori |
| `can_view_projects` | Boolean | `false` | Lihat proyek |
| `can_edit_projects` | Boolean | `false` | Edit proyek |
| `can_view_pos` | Boolean | `false` | Lihat modul POS |
| `can_use_pos` | Boolean | `false` | Gunakan modul POS (kasir) |
| `can_view_reports` | Boolean | `false` | Lihat laporan |
| `can_manage_members` | Boolean | `false` | Kelola anggota company |
| `can_manage_roles` | Boolean | `false` | Kelola role & permission |
| `can_view_settings` | Boolean | `false` | Lihat pengaturan |
| `can_edit_settings` | Boolean | `false` | Edit pengaturan company |
| `can_manage_cashier_shift` | Boolean | `false` | Kelola shift kasir |
| `can_approve_stock_opname` | Boolean | `false` | Approve stock opname |
| `can_count_stock_opname` | Boolean | `false` | Hitung stock opname |
| `can_transfer_inventory` | Boolean | `false` | Transfer antar gudang |
| `can_create_production_batch` | Boolean | `false` | Buat batch produksi |
| `can_release_production_qc` | Boolean | `false` | Release batch dari QC |
| `can_view_hpp` | Boolean | `false` | Lihat HPP (Harga Pokok Produksi) |
| `can_manage_channel_pricing` | Boolean | `false` | Kelola harga per channel |
| `can_view_distribution` | Boolean | `false` | Lihat modul distribusi |
| `can_create_distribution_shipment` | Boolean | `false` | Buat shipment distribusi |
| `can_confirm_distribution_shipment` | Boolean | `false` | Konfirmasi shipment distribusi |
| `can_view_b2b_invoices` | Boolean | `false` | Lihat invoice B2B |
| `can_create_b2b_invoice` | Boolean | `false` | Buat invoice B2B |
| `can_verify_b2b_payment` | Boolean | `false` | Verifikasi pembayaran B2B |

### User

| Field | Tipe | Wajib | Default | Deskripsi |
| - | - | - | - | - |
| `id` | UUID | Ya | Auto-generate | Primary key |
| `email` | String | Ya | — | Email user (unique) |
| `full_name` | String | Ya | — | Nama lengkap |
| `role` | Enum | Tidak | `"user"` | Role aplikasi: `admin`, `user` |
| `subscription_plan` | Enum | Tidak | `"free"` | Plan langganan: `free`, `pro`, `business`, `advanced`, `enterprise` |
| `subscription_start` | DateTime | Tidak | — | Tanggal mulai langganan |
| `subscription_end` | DateTime | Tidak | — | Tanggal akhir langganan |
| `membership_duration_type` | Enum | Tidak | — | Durasi membership: `monthly`, `yearly`, `custom`, `lifetime` |
| `membership_start_date` | Date | Tidak | — | Tanggal mulai membership |
| `membership_end_date` | Date | Tidak | — | Tanggal berakhir membership |
| `is_readonly_mode` | Boolean | Tidak | `false` | Mode read-only saat expired dalam grace period 3 hari |
| `trial_end` | DateTime | Tidak | — | Tanggal berakhir trial |
| `active_company_id` | String | Tidak | — | ID company yang sedang aktif (Business+ users) |
| `company_slots_purchased` | Number | Tidak | `0` | Slot company tambahan di luar limit plan |
| `ai_monthly_usage` | Number | Tidak | `0` | Penggunaan AI bulan ini |
| `ai_credits` | Number | Tidak | `10` | Kredit AI bulanan |
| `ai_addon_quota` | Number | Tidak | `0` | Kuota addon AI |
| `ai_usage_period_start` | Date | Tidak | — | Awal periode penggunaan AI |
| `last_credit_reset` | DateTime | Tidak | — | Terakhir kali kredit di-reset |
| `storage_used` | Number | Tidak | `0` | Penyimpanan terpakai |
| `balance` | Number | Tidak | `0` | Saldo deposit untuk membership/addon |
| `commission_balance` | Number | Tidak | `0` | Saldo komisi referral (dapat ditarik) |
| `admin_commission_balance` | Number | Tidak | `0` | Saldo komisi admin basic |
| `total_earnings` | Number | Tidak | `0` | Total kumulasi komisi |
| `admin_type` | Enum | Tidak | — | Tipe admin aplikasi: `owner` (full access) atau `basic` (transaksi digital) |
| `admin_tier` | Enum | Tidak | `"none"` | Tier admin company: `none`, `business`, `advanced`, `enterprise` |
| `productivity_score` | Number | Tidak | `0` | Skor produktivitas |
| `current_streak` | Number | Tidak | `0` | Streak hari berturut-turut menyelesaikan tugas |
| `longest_streak` | Number | Tidak | `0` | Streak terpanjang |
| `last_active_date` | Date | Tidak | — | Tanggal terakhir aktif |
| `total_tasks_completed` | Number | Tidak | `0` | Total tugas selesai |
| `total_notes_created` | Number | Tidak | `0` | Total catatan dibuat |
| `achievement_points` | Number | Tidak | `0` | Poin achievement |
| `user_level` | Number | Tidak | `1` | Level berdasarkan achievement points |
| `preferences` | Object | Tidak | — | Preferensi user (theme, warna, wallpaper) |

### CompanyAttendanceSettings

| Field | Tipe | Wajib | Default | Deskripsi |
| - | - | - | - | - |
| `id` | UUID | Ya | Auto-generate | Primary key |
| `company_id` | String | Ya | — | ID perusahaan |
| `description` | String | Tidak | — | Penjelasan kebijakan absensi (maks 1000 karakter) |
| `office_location` | Object | Tidak | — | Lokasi kantor utama (latitude, longitude, address) |
| `office_radius_meters` | Number | Tidak | `100` | Radius kantor dalam meter (legacy single-location fallback) |
| `require_office_location` | Boolean | Tidak | `false` | Wajib absen dari lokasi kantor |
| `default_accuracy_tolerance_meters` | Number | Tidak | `150` | Toleransi akurasi GPS dalam meter |
| `locations` | Array\[Object] | Tidak | `[]` | Daftar multi-lokasi geofence absensi |
| `working_hours` | Object | Tidak | — | Jam kerja (start\_time, end\_time, break\_duration\_minutes) |
| `shifts` | Array\[Object] | Tidak | `[]` | Daftar shift kerja (shift\_id, shift\_name, start\_time, end\_time) |
| `overtime_settings` | Object | Tidak | — | Pengaturan lembur (enabled, start\_after\_hours, max\_overtime\_hours\_per\_day) |
| `late_tolerance_minutes` | Number | Tidak | `15` | Toleransi terlambat dalam menit |
| `working_days` | Array\[Number] | Tidak | `[1,2,3,4,5]` | Hari kerja (1=Senin, 7=Minggu) |
| `auto_clock_out_enabled` | Boolean | Tidak | `false` | Otomatis clock out jika lupa |
| `auto_clock_out_time` | String | Tidak | `"18:00"` | Jam auto clock out |
| `require_photo` | Boolean | Tidak | `true` | Wajib foto selfie saat absen |
| `require_notes` | Boolean | Tidak | `false` | Wajib catatan saat absen |

### CompanyModuleSettings

| Field | Tipe | Wajib | Default | Deskripsi |
| - | - | - | - | - |
| `id` | UUID | Ya | Auto-generate | Primary key |
| `company_id` | String | Ya | — | ID perusahaan |
| `description` | String | Tidak | — | Catatan konfigurasi modul (maks 1000 karakter) |
| `modules_enabled` | Object | Tidak | — | Toggle modul: manufacturing, inventory\_advanced, pos, crm, hr\_payroll, projects, assets, documents, email\_marketing, appointments, team\_chat |
| `business_type` | Enum | Tidak | `"retail"` | Tipe bisnis: `retail`, `manufacturing`, `services`, `digital_products`, `food_beverage`, `healthcare`, `education`, `consulting`, `hybrid` |
| `inventory_method` | Enum | Tidak | `"Average"` | Metode inventori: `FIFO`, `LIFO`, `Average`, `Specific_Identification` |
| `multi_warehouse_enabled` | Boolean | Tidak | `false` | Aktifkan multi-gudang |
| `lot_tracking_enabled` | Boolean | Tidak | `false` | Aktifkan lot tracking |
| `serial_tracking_enabled` | Boolean | Tidak | `false` | Aktifkan serial tracking |
| `barcode_enabled` | Boolean | Tidak | `false` | Aktifkan barcode |
| `auto_reorder_enabled` | Boolean | Tidak | `false` | Aktifkan auto reorder |
| `production_planning_enabled` | Boolean | Tidak | `false` | Aktifkan production planning |
| `quality_control_enabled` | Boolean | Tidak | `false` | Aktifkan quality control |
| `attendance_integration` | Boolean | Tidak | `true` | Integrasi absensi ke payroll |
| `invoice_auto_create` | Boolean | Tidak | `false` | Auto-create invoice dari sales order |
| `pos_inventory_sync` | Boolean | Tidak | `true` | Sync otomatis POS ke inventory |
| `finance_auto_record` | Boolean | Tidak | `false` | Auto-record transaksi ke finance |
| `custom_fields` | Array\[Object] | Tidak | — | Custom fields per modul |
| `workflow_rules` | Array\[Object] | Tidak | — | Automation workflow rules |
| `notification_settings` | Object | Tidak | — | Pengaturan notifikasi (low\_stock\_alert, order\_approval\_required, production\_complete, invoice\_overdue, leave\_approval) |

### CompanyReceiptSettings

| Field | Tipe | Wajib | Default | Deskripsi |
| - | - | - | - | - |
| `id` | UUID | Ya | Auto-generate | Primary key |
| `company_id` | String | Ya | — | ID Company |
| `description` | String | Tidak | — | Catatan konfigurasi struk (maks 1000 karakter) |
| `show_company_logo` | Boolean | Tidak | `true` | Tampilkan logo di struk |
| `show_company_address` | Boolean | Tidak | `true` | Tampilkan alamat di struk |
| `show_customer_name` | Boolean | Tidak | `true` | Tampilkan nama customer |
| `show_customer_points` | Boolean | Tidak | `true` | Tampilkan total poin customer |
| `show_points_earned` | Boolean | Tidak | `true` | Tampilkan poin didapat dari transaksi |
| `show_payment_details` | Boolean | Tidak | `true` | Tampilkan detail pembayaran |
| `show_change` | Boolean | Tidak | `true` | Tampilkan kembalian |
| `custom_header_text` | String | Tidak | — | Teks custom header struk |
| `custom_footer_text` | String | Tidak | `"Terima kasih sudah berbelanja!"` | Teks custom footer struk |
| `show_promo_message` | Boolean | Tidak | `false` | Tampilkan pesan promo |
| `promo_message` | String | Tidak | — | Pesan promo/marketing |

### CompanyWablasSettings

| Field | Tipe | Wajib | Default | Deskripsi |
| - | - | - | - | - |
| `id` | UUID | Ya | Auto-generate | Primary key |
| `company_id` | String | Ya | — | ID Company |
| `description` | String | Tidak | — | Catatan integrasi Wablas (maks 1000 karakter) |
| `use_system_api` | Boolean | Tidak | `true` | Gunakan API Wablas default sistem |
| `wablas_api_key` | String | Tidak | — | API Key Wablas sendiri (jika use\_system\_api=false) |
| `wablas_secret_key` | String | Tidak | — | Secret Key Wablas sendiri |
| `wablas_domain` | String | Tidak | `"solo.wablas.com"` | Domain Wablas server |
| `is_active` | Boolean | Tidak | `true` | Status aktif integrasi |
| `auto_send_receipt` | Boolean | Tidak | `false` | Otomatis kirim struk via WhatsApp |
| `receipt_message_template` | String | Tidak | Template default | Template pesan struk WhatsApp (variabel: `{company_name}`, `{transaction_id}`, `{date}`, `{total}`) |

### CompanyPOSSettings

| Field | Tipe | Wajib | Default | Deskripsi |
| - | - | - | - | - |
| `id` | UUID | Ya | Auto-generate | Primary key |
| `company_id` | String | Ya | — | ID perusahaan |
| `description` | String | Tidak | — | Catatan konfigurasi POS (maks 1000 karakter) |
| `receipt_settings` | Object | Tidak | — | Konfigurasi struk POS (lihat sub-field di bawah) |
| `receipt_settings.logo_url` | String | Tidak | — | URL logo untuk struk |
| `receipt_settings.business_name` | String | Tidak | — | Nama bisnis di struk |
| `receipt_settings.business_address` | String | Tidak | — | Alamat di struk |
| `receipt_settings.business_phone` | String | Tidak | — | Nomor telepon di struk |
| `receipt_settings.business_email` | String | Tidak | — | Email bisnis di struk |
| `receipt_settings.tax_id` | String | Tidak | — | NPWP/Tax ID di struk |
| `receipt_settings.receipt_width` | Enum | Tidak | `"80mm"` | Ukuran kertas: `58mm`, `80mm` |
| `receipt_settings.show_logo` | Boolean | Tidak | `true` | Tampilkan logo |
| `receipt_settings.show_tax_id` | Boolean | Tidak | `false` | Tampilkan NPWP |
| `receipt_settings.show_cashier_name` | Boolean | Tidak | `true` | Tampilkan nama kasir |
| `receipt_settings.show_customer_info` | Boolean | Tidak | `true` | Tampilkan info customer |
| `receipt_settings.show_item_image` | Boolean | Tidak | `false` | Tampilkan gambar item |
| `receipt_settings.header_text` | String | Tidak | `"STRUK PEMBELIAN"` | Header struk |
| `receipt_settings.footer_message` | String | Tidak | Template default | Footer struk |
| `receipt_settings.print_auto_after_transaction` | Boolean | Tidak | `true` | Auto-print setelah transaksi |
| `receipt_settings.font_size` | Enum | Tidak | `"medium"` | Ukuran font: `small`, `medium`, `large` |

### CompanyInvitation

| Field | Tipe | Wajib | Default | Deskripsi |
| - | - | - | - | - |
| `id` | UUID | Ya | Auto-generate | Primary key |
| `company_id` | String | Ya | — | ID perusahaan |
| `company_name` | String | Tidak | — | Nama perusahaan |
| `invited_email` | String | Ya | — | Email yang diundang |
| `invited_by` | String | Ya | — | Email pengundang |
| `invited_by_name` | String | Tidak | — | Nama pengundang |
| `role` | Enum | Tidak | `"employee"` | Role yang ditawarkan |
| `department` | String | Tidak | — | Departemen |
| `position` | String | Tidak | — | Jabatan |
| `description` | String | Tidak | — | Pesan undangan (maks 1000 karakter) |
| `permissions` | Object | Tidak | — | Hak akses awal saat join |
| `message` | String | Tidak | — | Pesan tambahan |
| `status` | Enum | Tidak | `"pending"` | Status: `pending`, `accepted`, `rejected`, `expired`, `cancelled` |
| `expires_at` | DateTime | Tidak | — | Tanggal kadaluarsa undangan |
| `accepted_at` | DateTime | Tidak | — | Tanggal diterima |
| `rejected_at` | DateTime | Tidak | — | Tanggal ditolak |
| `cancelled_at` | DateTime | Tidak | — | Tanggal dibatalkan |

### CompanyBackup

| Field | Tipe | Wajib | Default | Deskripsi |
| - | - | - | - | - |
| `id` | UUID | Ya | Auto-generate | Primary key |
| `company_id` | String | Ya | — | ID perusahaan |
| `company_name` | String | Tidak | — | Nama perusahaan (snapshot) |
| `backup_type` | Enum | Tidak | `"manual"` | Jenis backup: `manual`, `auto_reminder`, `scheduled` |
| `backup_scope` | Enum | Tidak | `"full"` | Cakupan: `full`, `partial` |
| `backup_date` | DateTime | Ya | — | Tanggal backup dilakukan |
| `created_by_email` | String | Ya | — | Email pembuat backup |
| `created_by_name` | String | Tidak | — | Nama pembuat backup |
| `data_summary` | Object | Tidak | — | Ringkasan data (total\_members, total\_tasks, total\_notes, total\_transactions, total\_inventory, total\_customers, total\_employees, total\_projects, total\_assets, total\_size\_mb) |
| `file_url` | String | Tidak | — | URL file backup (JSON) |
| `file_size_mb` | Number | Tidak | — | Ukuran file backup (MB) |
| `notes` | String | Tidak | — | Catatan backup |
| `status` | Enum | Tidak | `"completed"` | Status: `completed`, `failed`, `processing` |
| `checksum` | String | Tidak | — | SHA-256 checksum file backup (hex) — BKP-03 |
| `schema_version` | String | Tidak | — | Versi schema saat backup dibuat — BKP-04 |
| `backup_version` | String | Tidak | — | Platform version string — BKP-04 |
| `error_log` | String | Tidak | — | Log error jika parsial/gagal — BKP-03 |
| `restore_tested_at` | DateTime | Tidak | — | Timestamp terakhir restore diverifikasi — BKP-04 |
| `restore_record_count` | Number | Tidak | — | Jumlah record saat restore test — BKP-04 |
| `retention_days` | Number | Tidak | `30` | Retensi backup (hari) — BKP-03 |
| `drive_file_id` | String | Tidak | — | ID file di Google Drive — BKP-02 |
| `drive_integration_status` | Enum | Tidak | `"not_configured"` | Status Drive: `not_configured`, `active`, `failed`, `disabled` — BKP-02 |

### PrinterSettings

| Field | Tipe | Wajib | Default | Deskripsi |
| - | - | - | - | - |
| `id` | UUID | Ya | Auto-generate | Primary key |
| `company_id` | String | Tidak | — | ID perusahaan (null untuk personal) |
| `user_id` | String | Ya | — | ID user pemilik |
| `printer_name` | String | Tidak | — | Nama printer tersimpan |
| `description` | String | Tidak | — | Catatan konfigurasi printer (maks 1000 karakter) |
| `device_id` | String | Tidak | — | Bluetooth device ID |
| `device_name` | String | Tidak | — | Nama device Bluetooth |
| `paper_width` | Enum | Tidak | `"80mm"` | Lebar kertas: `58mm`, `80mm` |
| `encoding` | Enum | Tidak | `"UTF-8"` | Encoding: `UTF-8`, `GB18030`, `Big5`, `Shift_JIS`, `EUC-KR` |
| `auto_cut` | Boolean | Tidak | `true` | Auto cut kertas setelah print |
| `beep_on_print` | Boolean | Tidak | `false` | Beep saat print |
| `auto_reconnect` | Boolean | Tidak | `true` | Auto reconnect jika disconnect |
| `print_logo` | Boolean | Tidak | `true` | Print logo di struk |
| `logo_url` | String | Tidak | — | URL logo untuk print |
| `header_text` | String | Tidak | `"STRUK PEMBELIAN"` | Header struk |
| `footer_text` | String | Tidak | `"Terima kasih atas kunjungan Anda"` | Footer struk |
| `font_size` | Enum | Tidak | `"normal"` | Ukuran font: `small`, `normal`, `large` |
| `last_connected` | DateTime | Tidak | — | Terakhir terhubung |
| `connection_count` | Number | Tidak | `0` | Jumlah koneksi berhasil |
| `is_default` | Boolean | Tidak | `false` | Printer default |

### MenuAccessProfile

| Field | Tipe | Wajib | Default | Deskripsi |
| - | - | - | - | - |
| `id` | UUID | Ya | Auto-generate | Primary key |
| `company_id` | String | Ya | — | ID perusahaan (isolasi data) |
| `role` | Enum | Ya | — | Role yang menggunakan profil ini |
| `profile_name` | String | Ya | — | Nama profil akses menu |
| `description` | String | Tidak | — | Penjelasan hak akses menu (maks 1000 karakter) |
| `allowed_menus` | Array\[String] | Ya | — | Daftar ID menu yang diizinkan |
| `dashboard_type` | Enum | Tidak | `"employee"` | Tipe dashboard: `owner`, `admin`, `supervisor`, `employee`, `cashier`, `finance`, `hr`, `inventory` |
| `is_default` | Boolean | Tidak | `true` | Apakah profil default untuk role ini |
| `created_by` | String | Tidak | — | Email owner yang membuat profil |

### Subscription

| Field | Tipe | Wajib | Default | Deskripsi |
| - | - | - | - | - |
| `id` | UUID | Ya | Auto-generate | Primary key |
| `user_id` | String | Ya | — | ID pemilik langganan |
| `company_id` | String | Tidak | — | ID perusahaan (null untuk personal) |
| `service_name` | String | Ya | — | Nama layanan/produk berlangganan |
| `cost` | Number | Ya | — | Biaya per periode |
| `currency` | String | Tidak | `"IDR"` | Mata uang |
| `billing_cycle` | Enum | Tidak | `"monthly"` | Siklus tagihan: `weekly`, `monthly`, `quarterly`, `yearly` |
| `next_due_date` | Date | Ya | — | Jatuh tempo pembayaran berikutnya |
| `start_date` | Date | Tidak | — | Tanggal mulai |
| `end_date` | Date | Tidak | — | Tanggal akhir (jika ada) |
| `status` | Enum | Tidak | `"active"` | Status: `active`, `paused`, `cancelled` |
| `payment_method` | String | Tidak | — | Metode pembayaran |
| `auto_renew` | Boolean | Tidak | `true` | Auto perpanjang |
| `notes` | String | Tidak | — | Catatan |

## Diagram State — Status CompanyMember

```mermaid theme={null}
stateDiagram-v2
    [*] --> pending: Undangan dikirim (CompanyInvitation)
    pending --> active: Undangan diterima (accepted)
    pending --> inactive: Undangan ditolak (rejected)
    pending --> inactive: Undangan kadaluarsa (expired)
    pending --> inactive: Undangan dibatalkan (cancelled)
    active --> inactive: Owner menonaktifkan member
    inactive --> active: Owner mengaktifkan kembali
    active --> inactive: Membership expired (grace period 3 hari)
    inactive --> active: Perpanjangan membership berhasil
    inactive --> [*]: Member dihapus permanen
    active --> [*]: Member dihapus oleh owner
```

## Diagram State — Status CompanyInvitation

```mermaid theme={null}
stateDiagram-v2
    [*] --> pending: Owner mengirim undangan
    pending --> accepted: Email undangan di-klik dan diterima
    pending --> rejected: Invitee menolak undangan
    pending --> expired: Melebihi expires_at
    pending --> cancelled: Owner membatalkan undangan
    accepted --> [*]: CompanyMember dibuat otomatis
    rejected --> [*]: Tidak ada tindak lanjut
    expired --> [*]: Tidak ada tindak lanjut
    cancelled --> [*]: Tidak ada tindak lanjut
```

## Diagram State — Status CompanyBackup

```mermaid theme={null}
stateDiagram-v2
    [*] --> processing: Backup dimulai (manual/scheduled/auto_reminder)
    processing --> completed: Backup berhasil, checksum terverifikasi
    processing --> failed: Error saat backup (disk full, timeout)
    failed --> processing: Retry backup
    completed --> completed: Restore test diverifikasi (restore_tested_at diupdate)
    completed --> [*]: Backup dihapus setelah retention_days habis
    failed --> [*]: Backup gagal dihapus dari riwayat
```

## Diagram State — Status Subscription

```mermaid theme={null}
stateDiagram-v2
    [*] --> active: Langganan dimulai
    active --> paused: User menjeda langganan
    paused --> active: User melanjutkan langganan
    active --> cancelled: User membatalkan atau jatuh tempo terlewat
    cancelled --> active: User memperpanjang langganan baru
    cancelled --> [*]: Langganan berakhir permanen
```

## Diagram Sequence — Alur Undangan Member Baru

```mermaid theme={null}
sequenceDiagram
    participant O as Owner
    participant CS as CompanySettings.jsx
    participant CI as CompanyInvitation
    participant WA as Wablas (WhatsApp)
    participant I as Invitee
    participant CM as CompanyMember

    O->>CS: Input email & role invitee
    CS->>CS: Validasi email belum terdaftar
    CS->>CI: Create invitation (status=pending)
    CI-->>CS: Return invitation data
    CS->>WA: Kirim pesan undangan via WhatsApp
    WA-->>I: Notifikasi undangan diterima
    I->>CS: Klik link undangan
    CS->>CS: Verifikasi token undangan
    CS->>CM: Create CompanyMember (status=active)
    CM-->>CS: Return member data
    CS->>CI: Update status=accepted, accepted_at=now()
    CS->>O: Broadcast: member baru bergabung
    CS->>CS: Audit trail: create_company_invitation
```

## Diagram Sequence — Alur Backup & Restore

```mermaid theme={null}
sequenceDiagram
    participant O as Owner
    participant BM as CompanyBackupManager
    participant BE as Backup Engine
    participant GDR as Google Drive
    participant DB as Database

    O->>BM: Klik "Export Data" (backup_type=manual)
    BM->>DB: Query semua data company
    DB-->>BE: Return data (members, tasks, inventory, dll)
    BE->>BE: Serialisasi ke JSON
    BE->>BE: Hitung SHA-256 checksum
    BE->>BE: Set schema_version & backup_version
    BE->>GDR: Upload file backup JSON
    GDR-->>BE: Return drive_file_id
    BE->>DB: Simpan CompanyBackup record
    DB-->>BM: Return backup record
    BM-->>O: Tampilkan "Backup berhasil"

    Note over O,DB: Proses Restore
    O->>BM: Pilih backup & klik "Import"
    BM->>GDR: Download file backup via drive_file_id
    GDR-->>BM: Return JSON data
    BM->>BM: Verifikasi checksum (SHA-256)
    BM->>BM: Validasi schema_version kompatibilitas
    BM->>DB: Restore data ke database
    DB-->>BM: Return jumlah record di-restore
    BM->>DB: Update restore_tested_at & restore_record_count
    BM-->>O: Tampilkan "Restore berhasil"
```

## Diagram Sequence — Konfigurasi Multi-Tab & Sinkronisasi

```mermaid theme={null}
sequenceDiagram
    participant O as Owner
    participant T1 as Tab Browser 1
    participant BC as BroadcastChannel
    participant SF as Server Function
    participant DB as Database
    participant T2 as Tab Browser 2

    O->>T1: Edit konfigurasi di Tab Absensi
    T1->>SF: updateCompanyProfile(attendanceSettings)
    SF->>DB: Upsert CompanyAttendanceSettings
    DB-->>SF: Return updated record
    SF-->>T1: Return success
    T1->>BC: Broadcast 'snishop_company_change'
    T1->>T1: Dispatch CustomEvent('companyChanged')

    BC-->>T2: Receive broadcast
    T2->>T2: Refresh company data dari DB
    T2->>T2: Update UI tab absensi

    Note over T1,T2: Owner pindah ke Tab Modul
    O->>T1: Toggle manufacturing=true
    T1->>SF: updateCompanyProfile(moduleSettings)
    SF->>DB: Upsert CompanyModuleSettings
    DB-->>SF: Return updated record
    SF-->>T1: Return success
    T1->>BC: Broadcast 'snishop_company_change'
    BC-->>T2: Receive broadcast
    T2->>T2: Refresh & tampilkan modul Manufacturing
```

## Tabel Enum Lengkap

### Enum: Company.industry

| Nilai | Deskripsi |
| - | - |
| `retail` | Usaha ritel (toko, minimarket, supermarket) |
| `manufacturing` | Usaha manufaktur/pabrik |
| `services` | Usaha jasa/layanan |
| `technology` | Usaha teknologi/IT |
| `food_beverage` | Usaha makanan & minuman (F\&B) |
| `healthcare` | Usaha kesehatan (klinik, apotek) |
| `education` | Usaha pendidikan (kursus, pelatihan) |
| `other` | Industri lainnya |

### Enum: Company.owner\_subscription\_plan & User.subscription\_plan

| Nilai | Deskripsi |
| - | - |
| `free` | Plan gratis — fitur dasar terbatas |
| `pro` | Plan profesional — fitur lengkap untuk UKM |
| `business` | Plan bisnis — multi-company, fitur lanjutan |
| `advanced` | Plan advanced — fitur enterprise menengah |
| `enterprise` | Plan enterprise — full access, unlimited |

### Enum: CompanyMember.role & CompanyInvitation.role

| Nilai | Deskripsi |
| - | - |
| `owner` | Pemilik perusahaan — full access |
| `admin` | Administrator — semua akses kecuali settings edit & role management |
| `supervisor` | Supervisor — read-only finance/inventory/HR |
| `store_admin` | Admin toko — kelola operasional toko |
| `stock_admin` | Admin stok — kelola inventori & gudang |
| `finance_admin` | Admin keuangan — kelola invoice, expense, bank |
| `hr_admin` | Admin HR — kelola karyawan, absensi, payroll |
| `transaction_admin` | Admin transaksi — kelola transaksi & kasir |
| `employee` | Karyawan biasa — akses terbatas sesuai permission |
| `production_operator` | Operator produksi — kelola batch manufaktur |
| `qc_inspector` | Inspector QC — verifikasi kualitas batch |
| `sales_marketing` | Sales & marketing — kelola channel penjualan |
| `partner_distributor` | Partner/distributor — akses distribusi |

### Enum: CompanyMember.status

| Nilai | Deskripsi |
| - | - |
| `active` | Member aktif, memiliki akses penuh sesuai role |
| `inactive` | Member nonaktif, akses dibekukan |
| `pending` | Menunggu konfirmasi undangan |

### Enum: CompanyInvitation.status

| Nilai | Deskripsi |
| - | - |
| `pending` | Undangan menunggu respon |
| `accepted` | Undangan diterima, CompanyMember dibuat |
| `rejected` | Undangan ditolak oleh invitee |
| `expired` | Undangan kadaluarsa (melebihi expires\_at) |
| `cancelled` | Undangan dibatalkan oleh owner |

### Enum: CompanyBackup.backup\_type

| Nilai | Deskripsi |
| - | - |
| `manual` | Backup manual yang dipicu owner |
| `auto_reminder` | Backup otomatis berdasarkan pengingat |
| `scheduled` | Backup terjadwal (harian/mingguan) |

### Enum: CompanyBackup.backup\_scope

| Nilai | Deskripsi |
| - | - |
| `full` | Backup seluruh data company |
| `partial` | Backup sebagian data (pilih modul tertentu) |

### Enum: CompanyBackup.status

| Nilai | Deskripsi |
| - | - |
| `completed` | Backup berhasil diselesaikan |
| `failed` | Backup gagal (error) |
| `processing` | Backup sedang berlangsung |

### Enum: CompanyBackup.drive\_integration\_status

| Nilai | Deskripsi |
| - | - |
| `not_configured` | Google Drive belum dikonfigurasi |
| `active` | Integrasi Drive aktif dan berfungsi |
| `failed` | Integrasi Drive gagal (token expired, dll) |
| `disabled` | Integrasi Drive dinonaktifkan |

### Enum: CompanyModuleSettings.business\_type

| Nilai | Deskripsi |
| - | - |
| `retail` | Ritel |
| `manufacturing` | Manufaktur |
| `services` | Jasa/layanan |
| `digital_products` | Produk digital |
| `food_beverage` | Makanan & minuman |
| `healthcare` | Kesehatan |
| `education` | Pendidikan |
| `consulting` | Konsultasi |
| `hybrid` | Kombinasi beberapa tipe |

### Enum: CompanyModuleSettings.inventory\_method

| Nilai | Deskripsi |
| - | - |
| `FIFO` | First In First Out — barang masuk pertama keluar pertama |
| `LIFO` | Last In First Out — barang masuk terakhir keluar pertama |
| `Average` | Rata-rata tertimbang (weighted average) |
| `Specific_Identification` | Identifikasi spesifik per unit |

### Enum: User.role

| Nilai | Deskripsi |
| - | - |
| `admin` | Admin aplikasi — akses penuh ke seluruh platform |
| `user` | User biasa — akses sesuai company membership |

### Enum: User.admin\_type

| Nilai | Deskripsi |
| - | - |
| `owner` | Owner aplikasi — full access ke seluruh aplikasi (BERBEDA dengan company owner) |
| `basic` | Admin basic — hanya transaksi produk digital |

### Enum: User.admin\_tier

| Nilai | Deskripsi |
| - | - |
| `none` | Bukan admin company management |
| `business` | Tier business — kelola company hingga plan business |
| `advanced` | Tier advanced — kelola company hingga plan advanced |
| `enterprise` | Tier enterprise — kelola company tanpa batas |

### Enum: User.membership\_duration\_type

| Nilai | Deskripsi |
| - | - |
| `monthly` | Membership bulanan |
| `yearly` | Membership tahunan |
| `custom` | Durasi kustom |
| `lifetime` | Membership seumur hidup |

### Enum: Subscription.billing\_cycle

| Nilai | Deskripsi |
| - | - |
| `weekly` | Tagihan mingguan |
| `monthly` | Tagihan bulanan |
| `quarterly` | Tagihan per kuartal (3 bulan) |
| `yearly` | Tagihan tahunan |

### Enum: Subscription.status

| Nilai | Deskripsi |
| - | - |
| `active` | Langganan aktif |
| `paused` | Langganan dijeda |
| `cancelled` | Langganan dibatalkan |

### Enum: PrinterSettings.paper\_width

| Nilai | Deskripsi |
| - | - |
| `58mm` | Kertas thermal 58mm (kecil) |
| `80mm` | Kertas thermal 80mm (standar) |

### Enum: PrinterSettings.encoding

| Nilai | Deskripsi |
| - | - |
| `UTF-8` | Unicode standar (default) |
| `GB18030` | Chinese Simplified |
| `Big5` | Chinese Traditional |
| `Shift_JIS` | Japanese |
| `EUC-KR` | Korean |

### Enum: PrinterSettings.font\_size

| Nilai | Deskripsi |
| - | - |
| `small` | Font kecil — lebih banyak item per struk |
| `normal` | Font normal (default) |
| `large` | Font besar — mudah dibaca |

### Enum: MenuAccessProfile.dashboard\_type

| Nilai | Deskripsi |
| - | - |
| `owner` | Dashboard pemilik — overview seluruh company |
| `admin` | Dashboard admin — overview operasional |
| `supervisor` | Dashboard supervisor — monitoring read-only |
| `employee` | Dashboard karyawan — tugas & aktivitas pribadi |
| `cashier` | Dashboard kasir — antrian & transaksi |
| `finance` | Dashboard keuangan — invoice, expense, laporan |
| `hr` | Dashboard HR — karyawan, absensi, payroll |
| `inventory` | Dashboard inventori — stok, gudang, pergerakan |

### Enum: Company.settings.batch\_allocation\_strategy

| Nilai | Deskripsi |
| - | - |
| `fifo` | First In First Out — batch paling lama yang digunakan duluan |
| `fefo` | First Expiry First Out — batch paling dekat kadaluarsa yang digunakan duluan |

### Enum: Company.settings.spoilage\_default\_treatment

| Nilai | Deskripsi |
| - | - |
| `absorbed_normal` | Spoilage masuk HPP unit baik (biaya normal produksi) |
| `expense_abnormal` | Spoilage dipisah sebagai beban periode (abnormal) |

### Enum: Company.settings.tax.mode

| Nilai | Deskripsi |
| - | - |
| `inclusive` | Pajak sudah termasuk dalam harga |
| `exclusive` | Pajak ditambah ke harga |

### Enum: Company.settings.tax.rounding

| Nilai | Deskripsi |
| - | - |
| `per_line` | Pembulatan per baris item |
| `per_transaction` | Pembulatan per total transaksi |

## Tabel RBAC — Hak Akses per Role

| Permission | Owner | Admin | Supervisor | Store Admin | Stock Admin | Finance Admin | HR Admin | Transaction Admin | Employee |
| - | - | - | - | - | - | - | - | - | - |
| `can_view_dashboard` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| `can_view_tasks` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| `can_create_tasks` | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| `can_edit_tasks` | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| `can_delete_tasks` | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ |
| `can_view_notes` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| `can_create_notes` | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| `can_edit_notes` | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| `can_delete_notes` | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ |
| `can_view_hr` | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
| `can_edit_hr` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
| `can_view_finance` | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| `can_edit_finance` | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| `can_view_inventory` | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ |
| `can_edit_inventory` | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ |
| `can_view_projects` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| `can_edit_projects` | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ |
| `can_view_pos` | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ | ❌ |
| `can_use_pos` | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ |
| `can_view_reports` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| `can_manage_members` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| `can_manage_roles` | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| `can_view_settings` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| `can_edit_settings` | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| `can_manage_cashier_shift` | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ |
| `can_approve_stock_opname` | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
| `can_count_stock_opname` | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ |
| `can_transfer_inventory` | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ |
| `can_create_production_batch` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| `can_release_production_qc` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| `can_view_hpp` | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| `can_manage_channel_pricing` | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
| `can_view_distribution` | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| `can_create_distribution_shipment` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| `can_confirm_distribution_shipment` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| `can_view_b2b_invoices` | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| `can_create_b2b_invoice` | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| `can_verify_b2b_payment` | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |

> **Catatan RBAC**: Owner memiliki SEMUA permission aktif. Role template di atas adalah default — owner dapat mengkustomisasi permission individual per member melalui `RoleManager.jsx` (1398 baris). Permission `can_edit_settings` dan `can_manage_roles` hanya aktif untuk Owner.


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