Skip to main content

Data Seeding

Strategi seeding data untuk initial setup merchant, branch, roles, users, dan master data di MStore Backend.

πŸ“Š Seed Data Categories

1. Lookup Tables (Static Reference Data)

Data statis yang jarang berubah, biasanya enum values. Examples:
  • business_level: L0, L1, L2, L3, L4
  • transaction_status: draft, completed, cancelled, refunded
  • branch_type: outlet, warehouse, distribution, hq
  • user_role: owner, manager, cashier, auditor, etc.
Location: third_party/migrations/seeder/

2. Master Data (Initial Setup)

Data yang dibuat saat merchant/branch baru didaftarkan. Examples:
  • Roles & Permissions (multi-level RBAC)
  • Users (admin, owner-manager, cashier, etc.)
  • Chart of Accounts (COA) untuk accounting
  • Tax Configurations
  • Default Settings per merchant/branch
Location: internal/domains/excel_import/excel_import_data_seeder.go

3. Configuration Data

Sistem configuration yang bisa di-override per merchant. Examples:
  • Log level, timezone, currency
  • Merchant defaults (payment method, tax rate)
  • Branch defaults (warehouse, POS terminal)

πŸ”„ Seed Data Flow

1. Lookup tables (enums, status codes)
   ↓
2. Master data (roles, permissions, users)
   ↓
3. Configuration (system settings)
   ↓
4. Optional: Demo data (for development)

πŸ’» Seeder Implementation (Go)

User Seeder

// internal/domains/excel_import/excel_import_data_seeder.go
func (s *excel_import_service) UserInitiateDataSeeder(
    payload dto.WarehouseSetupNewMerchant,
) []User {
    return []User{
        {
            UserCode:          fmt.Sprintf("USR-%s-ADM-000001", payload.Merchant.MerchantPrefix),
            Username:          fmt.Sprintf("admin.%s", payload.Merchant.MerchantPrefix),
            Email:             fmt.Sprintf("admin.%s", payload.Merchant.MerchantEmail),
            Phone:             payload.Branch.BranchPhone,
            Password:          hashPassword(fmt.Sprintf("admin.%s", payload.Merchant.MerchantPrefix)),
            IsActive:          true,
            IsLocked:          false,
            Lang:              "id",
            Theme:             "light",
            Tz:                "Asia/Jakarta",
            IsEmailVerified:   false,
        },
        {
            UserCode:          fmt.Sprintf("USR-%s-OWN-MGR-000001", payload.Merchant.MerchantPrefix),
            Username:          fmt.Sprintf("owner-manager.%s", payload.Merchant.MerchantPrefix),
            Email:             fmt.Sprintf("owner-manager.%s", payload.Merchant.MerchantEmail),
            Phone:             payload.Branch.BranchPhone,
            Password:          hashPassword(fmt.Sprintf("owner.%s", payload.Merchant.MerchantPrefix)),
            IsActive:          true,
            IsLocked:          false,
            Lang:              "id",
            Theme:             "light",
            Tz:                "Asia/Jakarta",
            IsEmailVerified:   false,
        },
        // ... more users
    }
}

Role Seeder (Multi-Level RBAC)

// internal/domains/excel_import/excel_import_data_seeder_multilevel.go
func (s *excel_import_service) RoleInitiateDataSeederMultiLevel() []Role {
    return []Role{
        // =====================================================
        // L0 β€” MICRO / SOLO
        // =====================================================
        {
            RoleCode:        "OWN",
            Name:            "Owner",
            Description:     "Pemilik tunggal dengan akses penuh ke semua modul",
            AvailableLevels: "L0",
        },
        {
            RoleCode:        "CSH",
            Name:            "Cashier",
            Description:     "Kasir untuk input transaksi harian",
            AvailableLevels: "L0,L1,L2,L3,L4",
        },
        {
            RoleCode:        "VWR",
            Name:            "Viewer",
            Description:     "Hanya dapat melihat laporan (read-only)",
            AvailableLevels: "L0,L1,L2,L3,L4",
        },

        // =====================================================
        // L1 β€” SME (Small–Medium Enterprise)
        // =====================================================
        {
            RoleCode:        "OWN-MGR",
            Name:            "Owner & Manager",
            Description:     "Pemilik usaha dengan akses ke semua modul",
            AvailableLevels: "L1,L2,L3,L4",
        },
        {
            RoleCode:        "FIN-MGR",
            Name:            "Finance Manager",
            Description:     "Keuangan, laporan, dan manajemen kas",
            AvailableLevels: "L1,L2,L3,L4",
        },
        {
            RoleCode:        "INV-MGR",
            Name:            "Inventory Manager",
            Description:     "Manajemen stok dan gudang",
            AvailableLevels: "L1,L2,L3,L4",
        },
        {
            RoleCode:        "HR-MGR",
            Name:            "HR Manager",
            Description:     "Manajemen HR dan payroll dasar",
            AvailableLevels: "L1,L2,L3,L4",
        },
        {
            RoleCode:        "AUD",
            Name:            "Auditor",
            Description:     "Read-only untuk audit internal",
            AvailableLevels: "L1,L2,L3,L4",
        },

        // =====================================================
        // L2 β€” ENTERPRISE
        // =====================================================
        {
            RoleCode:        "ADM-BIZ",
            Name:            "Business Admin",
            Description:     "Konfigurasi bisnis, tax, COA, workflow",
            AvailableLevels: "L2,L3,L4",
        },
        {
            RoleCode:        "ADM-SYS",
            Name:            "System Admin",
            Description:     "Deployment, backup, server, monitoring",
            AvailableLevels: "L2,L3,L4",
        },
        {
            RoleCode:        "ACC-MGR",
            Name:            "Accounting Manager",
            Description:     "Review dan approval jurnal keuangan",
            AvailableLevels: "L2,L3,L4",
        },
        // ... more roles
    }
}

Permission Seeder

func (s *excel_import_service) PermissionInitiateDataSeederMultiLevel() []Permission {
    return []Permission{
        {
            PermissionCode: "user.create",
            Name:           "Create User",
            Description:    "Dapat membuat user baru",
            Module:         "user_management",
        },
        {
            PermissionCode: "user.read",
            Name:           "Read User",
            Description:    "Dapat melihat data user",
            Module:         "user_management",
        },
        {
            PermissionCode: "transaction.create",
            Name:           "Create Transaction",
            Description:    "Dapat membuat transaksi baru",
            Module:         "transaction",
        },
        // ... more permissions
    }
}

Role-Permission Mapping

func (s *excel_import_service) RolePermissionInitiateDataSeederMultiLevel() []RolePermission {
    return []RolePermission{
        // Owner (L0) β€” Full access
        {RoleCode: "OWN", PermissionCode: "user.create"},
        {RoleCode: "OWN", PermissionCode: "user.read"},
        {RoleCode: "OWN", PermissionCode: "transaction.create"},
        
        // Cashier (L0-L4) β€” Limited access
        {RoleCode: "CSH", PermissionCode: "transaction.create"},
        {RoleCode: "CSH", PermissionCode: "transaction.read"},
        
        // Finance Manager (L1+) β€” Finance access
        {RoleCode: "FIN-MGR", PermissionCode: "transaction.read"},
        {RoleCode: "FIN-MGR", PermissionCode: "report.financial"},
        {RoleCode: "FIN-MGR", PermissionCode: "approval.payment"},
        
        // ... more mappings
    }
}

πŸš€ Running Seeders

Method 1: Via CLI Command

# Seed all data
go run cmd/tools/seeder.go

# Seed specific data
go run cmd/tools/seeder.go --type=roles
go run cmd/tools/seeder.go --type=users
go run cmd/tools/seeder.go --type=permissions

Method 2: Via API Endpoint

# Trigger seeding via API
curl -X POST http://localhost:8080/api/admin/seed \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "types": ["roles", "permissions", "users"],
    "merchant_id": 1
  }'

Method 3: Via Database Migration

-- Insert lookup data
INSERT INTO lookup_codes (code, name, category) VALUES
  ('L0', 'Micro', 'business_level'),
  ('L1', 'SME', 'business_level'),
  ('L2', 'Enterprise', 'business_level'),
  ('L3', 'Holding', 'business_level'),
  ('L4', 'MNC', 'business_level');

-- Insert roles
INSERT INTO roles (role_code, name, description, available_levels) VALUES
  ('OWN', 'Owner', 'Pemilik tunggal', 'L0'),
  ('CSH', 'Cashier', 'Kasir', 'L0,L1,L2,L3,L4'),
  ('FIN-MGR', 'Finance Manager', 'Keuangan', 'L1,L2,L3,L4');

βœ… Seeding Checklist

  • Backup database before seeding
  • Verify lookup tables exist
  • Check role & permission definitions
  • Validate user credentials
  • Test seeding on staging first
  • Verify seeded data integrity
  • Check for duplicate entries
  • Validate foreign key constraints
  • Monitor application logs

πŸš€ Best Practices

DO βœ…

  • Seed lookup tables first
  • Use transactions for atomic seeding
  • Validate data before inserting
  • Document seed data changes
  • Version control seed scripts
  • Test seeding on staging
  • Use idempotent seeders (safe to run multiple times)

DON’T ❌

  • Seed production data directly
  • Skip validation
  • Use hardcoded IDs (use natural keys)
  • Seed without backup
  • Mix seed & migration scripts
  • Seed sensitive data (passwords, tokens)
  • Ignore foreign key constraints