# Database Reference > Format: `table_name` → `model_name` | columns | relationships --- ## Auth & User ### `users` → User `id` `email`(unique) `username`(unique) `password` `is_active`(bool) `last_login_at`(datetime) `created_at` `updated_at` `deleted_at` - Casts: email_verified_at(datetime), is_active(bool), last_login_at(datetime), password(hashed), two_factor_confirmed_at(datetime) - Implements: HasMedia (Spatie Media Library) - Media Collections: photos (single photo, with thumb conversion) - Scopes: active() - Relations: userProfile(HasOne→UserProfile), employee(HasOne→Employee), attendances(HasMany→Attendance), cashAccounts(HasMany→CashAccount,created_by_id), cashTransactions(HasMany→CashTransaction,created_by_id), createdCuttings(HasMany→Cutting,created_by_id), createdExpenses(HasMany→Expense,created_by_id), createdOrders(HasMany→Order,created_by_id), marketingOrders(HasMany→Order,marketing_id), orderItems(HasMany→OrderItem), createdPurchases(HasMany→Purchase,created_by_id), createdRestocks(HasMany→Restock,created_by_id), stokOpnamesCreated(HasMany→StokOpname,created_by_id), stokOpnamesVerified(HasMany→StokOpname,verified_by_id), employeeAdvancesPaid(HasMany→EmployeeAdvance,paid_by_id), employeeAdvancesVerified(HasMany→EmployeeAdvance,verified_by_id), paidPayrolls(HasMany→Payroll,paid_by_id), payrollPeriodsClosed(HasMany→PayrollPeriod,closed_by_id), notifications(HasMany→AppNotification), rejections(HasMany→Rejection,rejected_by_id), pushSubscriptions(HasMany→PushSubscription,morph) - Accessor: avatar → temporary S3 URL dari media 'photos' (atau null) ### `user_profiles` → UserProfile `id` `user_id`(FK→users,unique) `full_name`(200) `phone_number`(20,null) `gender`(enum,null) `birth_date`(date,null) `address`(text,null) `created_at` `updated_at` `deleted_at` - Casts: gender(Gender), birth_date(date:Y-m-d) - Scopes: female(), male(), hasPhoneNumber() - Relations: user(BelongsTo→User) ### `push_subscriptions` → PushSubscription `id` `subscribable_type` `subscribable_id` `endpoint`(500,unique) `public_key`(null) `auth_token`(null) `content_encoding`(null) `created_at` `updated_at` - Relations: user(MorphTo) ### `notifications` → AppNotification `id` `user_id`(FK→users) `title` `body`(longText,null) `url`(null) `is_read`(bool,default:false) `read_at`(datetime,null) `created_at` `updated_at` - Casts: is_read(bool), read_at(datetime) - Accessor: formatted_read_at → 'l, d F Y H:i' - Relations: user(BelongsTo→User) --- ## Master Data ### `categories` → Category `id` `name`(50) `slug`(50,unique) `created_at` `updated_at` `deleted_at` - Relations: products(BelongsToMany→Product via product_categories) - Accessor: formattedName → ucfirst(name) ### `product_categories` → Category (Pivot) `product_id`(FK→products) `category_id`(FK→categories) - No id, no timestamps - Relations: category(BelongsTo→Category), product(BelongsTo→Product) ### `products` → Product `id` `name`(200) `slug`(200,unique) `description`(text,null) `status`(enum,default:active) `is_featured`(bool,default:false) `rejection_reason`(text,null) `created_at` `updated_at` `deleted_at` - Casts: status(ProductStatus), is_featured(bool) - Scopes: active(), draft(), featured(), inactive(), pending(), rejected() - Relations: categories(BelongsToMany→Category), productVariants(HasMany→ProductVariant) ### `product_variants` → ProductVariant `id` `product_id`(FK→products) `name`(200) `stock`(uint,default:0) `reject_stock`(uint,default:0) `retail_stock`(uint,default:0) `created_at` `updated_at` `deleted_at` - Casts: stock(int), reject_stock(int), retail_stock(int) - GlobalScope: orderBy(name) - Relations: product(BelongsTo→Product), productPrices(HasMany→ProductPrice,variant_id), orderItems(HasMany→OrderItem), restockItems(HasMany→RestockItem), retailStockHistories(HasMany→RetailStockHistory), stokOpnameItems(HasMany→StokOpnameItem), stockMutations(HasMany→StockMutation,morph) ### `product_prices` → ProductPrice `id` `variant_id`(FK→product_variants) `type`(enum) `price`(uint) `created_at` `updated_at` - UNIQUE(variant_id, type) - Casts: type(PriceType), price(int) - Scopes: retail(), wholesale() - Relations: variant(BelongsTo→ProductVariant) ### `customers` → Customer `id` `name`(200) `phone_number`(20,null) `address`(text,null) `created_at` `updated_at` `deleted_at` - Relations: orders(HasMany→Order) - Accessor: formattedPhoneNumber (get: "0821 2121 2121", set: strip whitespace) ### `suppliers` → Supplier `id` `name`(200) `phone_number`(20,null) `address`(text,null) `created_at` `updated_at` `deleted_at` - Relations: purchases(HasMany→Purchase) - Accessor: formattedPhoneNumber (get: "0821 2121 2121", set: strip whitespace) ### `raw_materials` → RawMaterial `id` `name`(200) `unit`(enum) `is_active`(bool,default:true) `created_at` `updated_at` `deleted_at` - Casts: unit(RawMaterialUnit), is_active(bool) - Scopes: active(), nonactive(), kg(), meter(), yard() - Relations: rawMaterialPrices(HasMany→RawMaterialPrice) ### `raw_material_prices` → RawMaterialPrice `id` `raw_material_id`(FK→raw_materials) `variant`(200) `price`(uint) `stock`(uint,default:0) `created_at` `updated_at` `deleted_at` - Casts: price(int), stock(int) - GlobalScope: orderBy(variant) - Relations: rawMaterial(BelongsTo→RawMaterial,withTrashed), cuttingMaterials(HasMany→CuttingMaterial), purchaseItems(HasMany→PurchaseItem) - Accessor: photo_url → first media presigned S3 URL --- ## Finance ### `cash_accounts` → CashAccount `id` `created_by_id`(FK→users) `name`(200) `balance`(ubig,default:0) `created_at` `updated_at` `deleted_at` - Casts: balance(int) - Relations: cashTransactions(HasMany→CashTransaction), createdBy(BelongsTo→User) ### `cash_transactions` → CashTransaction `id` `cash_account_id`(FK→cash_accounts) `created_by_id`(FK→users) `reference_id`(ubig,null,morph) `reference_type`(string,null,morph) `amount`(ubig) `balance_after`(ubig) `type`(enum,default:deposit) `description`(100) `created_at` `updated_at` `deleted_at` - Casts: type(CashTransactionType), amount(int), balance_after(int) - Scopes: deposit(), expenseType(), transfer(), withdrawal() - Relations: cashAccount(BelongsTo→CashAccount), createdBy(BelongsTo→User), reference(MorphTo), expense(HasOne→Expense), order(HasOne→Order), employeeAdvances(HasMany→EmployeeAdvance), payrolls(HasMany→Payroll) ### `expenses` → Expense `id` `cash_transaction_id`(FK→cash_transactions,unique,null) `created_by_id`(FK→users) `amount`(ubig) `description`(100) `created_at` `updated_at` `deleted_at` - Casts: amount(int), date(date:Y-m-d) - Relations: cashTransaction(BelongsTo→CashTransaction), createdBy(BelongsTo→User) ### `employee_advances` → EmployeeAdvance `id` `paid_by_id`(FK→users,null) `employee_id`(FK→employees) `verified_by_id`(FK→users,null) `repayment_cash_transaction_id`(FK→cash_transactions,unique,null) `cash_transaction_id`(FK→cash_transactions,unique,null) `amount`(ubig) `paid_amount`(ubig,default:0) `description`(100) `due_date`(date) `status`(enum,default:pending) `verified_at`(datetime,null) `paid_at`(datetime,null) `created_at` `updated_at` `deleted_at` - Casts: status(EmployeeAdvanceStatus), amount(int), paid_amount(int), due_date(date:Y-m-d), verified_at(datetime), paid_at(datetime) - Scopes: approved(), cancelled(), paid(), pending(), rejected() - Relations: employee(BelongsTo→Employee), cashTransaction(BelongsTo→CashTransaction), repaymentCashTransaction(BelongsTo→CashTransaction), paidBy(BelongsTo→User), verifiedBy(BelongsTo→User), payments(HasMany→EmployeeAdvancePayment) ### `employee_advance_payments` → EmployeeAdvancePayment `id` `employee_advance_id`(FK→employee_advances) `paid_by_id`(FK→users,null) `cash_transaction_id`(FK→cash_transactions,unique,null) `amount`(ubig) `description`(100,null) `paid_at`(datetime) `created_at` `updated_at` - No soft deletes - Casts: amount(int), paid_at(datetime) - Accessor: formatted_amount → 'Rp X.XXX', formatted_paid_at → 'l, d F Y H:i' - Relations: employeeAdvance(BelongsTo→EmployeeAdvance), paidBy(BelongsTo→User), cashTransaction(BelongsTo→CashTransaction) --- ## HR ### `employees` → Employee `id` `user_id`(FK→users,unique) `join_date`(date) `resign_date`(date,null) `employment_status`(enum,default:full_time) `base_salary`(uint) `created_at` `updated_at` `deleted_at` - Casts: employment_status(EmploymentStatus), base_salary(int), join_date(date:Y-m-d), resign_date(date:Y-m-d) - Scopes: contract(), fullTime(), internship(), partTime(), resigned() - Relations: user(BelongsTo→User), attendances(HasMany→Attendance), employeeAdvances(HasMany→EmployeeAdvance), leaveRequests(HasMany→LeaveRequest), payrolls(HasMany→Payroll) ### `attendances` → Attendance `id` `employee_id`(FK→employees) `attendance_date`(date) `check_in_at`(datetime) `check_out_at`(datetime,null) `check_in_latitude`(decimal(10,7),null) `check_in_longitude`(decimal(10,7),null) `check_out_latitude`(decimal(10,7),null) `check_out_longitude`(decimal(10,7),null) `work_duration_minutes`(uint,null) `created_at` `updated_at` - No soft deletes - Casts: attendance_date(date:Y-m-d), check_in_at(datetime), check_out_at(datetime), check_in/out_latitude/longitude(decimal:7), work_duration_minutes(int) - Accessor: formatted_attendance_date → 'l, d F Y', formatted_check_in_at → 'l, d F Y H:i', formatted_check_out_at → 'l, d F Y H:i' - Relations: employee(BelongsTo→Employee), payrollAdjustments(HasMany→PayrollAdjustment) ### `payroll_periods` → PayrollPeriod `id` `closed_by_id`(FK→users,null) `year`(usmall) `month`(utiny) `status`(enum,default:open) `closed_at`(datetime,null) `created_at` `updated_at` `deleted_at` - Casts: status(PayrollPeriodStatus), year(int), month(int), closed_at(datetime) - Accessor: formatted_closed_at → 'l, d F Y H:i', month_name → 'Januari', status_label - Scopes: closed(), open() - Relations: closedBy(BelongsTo→User), payrolls(HasMany→Payroll) ### `payrolls` → Payroll `id` `payroll_period_id`(FK→payroll_periods) `employee_id`(FK→employees) `cash_transaction_id`(FK→cash_transactions,unique,null) `paid_by_id`(FK→users,null) `base_salary`(uint) `bonus_amount`(ubig,default:0) `deduction_amount`(ubig,default:0) `total_amount`(ubig) `status`(enum,default:unpaid) `paid_at`(datetime,null) `created_at` `updated_at` `deleted_at` - Casts: status(PayrollStatus), base_salary(int), bonus_amount(int), deduction_amount(int), total_amount(int), paid_at(datetime) - Scopes: cancelled(), paid(), unpaid() - Relations: payrollPeriod(BelongsTo→PayrollPeriod), employee(BelongsTo→Employee), cashTransaction(BelongsTo→CashTransaction), paidBy(BelongsTo→User), payrollAdjustments(HasMany→PayrollAdjustment) ### `payroll_adjustments` → PayrollAdjustment `id` `payroll_id`(FK→payrolls) `attendance_id`(FK→attendances,null) `created_by_id`(FK→users) `type`(enum) `amount`(ubig) `description`(100) `created_at` `updated_at` `deleted_at` - Casts: type(PayrollAdjustmentType), amount(int) - Scopes: bonus(), deduction() - Relations: payroll(BelongsTo→Payroll), attendance(BelongsTo→Attendance), createdBy(BelongsTo→User) ### `leave_requests` → LeaveRequest `id` `employee_id`(FK→employees) `verified_by_id`(FK→users,null) `start_date`(date) `end_date`(date) `total_days`(uint) `status`(enum,default:pending) `verified_at`(datetime,null) `created_at` `updated_at` `deleted_at` - Casts: status(LeaveRequestStatus), start_date(date:Y-m-d), end_date(date:Y-m-d), total_days(int), verified_at(datetime) - Scopes: approved(), cancelled(), pending(), rejected() - Relations: employee(BelongsTo→Employee), verifiedBy(BelongsTo→User) --- ## Sales ### `orders` → Order `id` `customer_id`(FK→customers,null) `marketing_id`(FK→users,null) `cash_transaction_id`(FK→cash_transactions,unique,null) `created_by_id`(FK→users) `order_number`(30,unique) `channel`(enum) `price_type`(enum) `status`(enum,default:pending) `payment_type`(enum,default:cash) `tiktok_order_id`(100,null) `shopee_order_id`(100,null) `subtotal`(ubig) `discount`(ubig,default:0) `nego_price`(ubig,null) `total_amount`(ubig) `cogs`(ubig,default:0) `notes`(text,null) `created_at` `updated_at` `deleted_at` - Casts: channel(OrderChannel), price_type(PriceType), status(OrderStatus), payment_type(PaymentType), subtotal(int), discount(int), nego_price(int), total_amount(int), cogs(int) - Scopes: cancelled(), cash(), completed(), pending(), processing(), qris(), refunded(), retail(), shopee(), store(), tiktok(), transfer(), wholesale() - Relations: cashTransaction(BelongsTo→CashTransaction), createdBy(BelongsTo→User), customer(BelongsTo→Customer), marketing(BelongsTo→User), orderItems(HasMany→OrderItem) ### `order_items` → OrderItem `id` `order_id`(FK→orders,null) `user_id`(FK→users,null) `product_variant_id`(FK→product_variants) `stock_quality`(enum,default:good) `quantity`(uint) `unit_price`(ubig) `subtotal`(ubig) `created_at` `updated_at` `deleted_at` - Casts: stock_quality(ProductStockQuality), quantity(int), unit_price(int), subtotal(int) - Scopes: good(), reject() - Relations: order(BelongsTo→Order), productVariant(BelongsTo→ProductVariant), user(BelongsTo→User) ### `rejections` → Rejection `id` `rejectable_type`(string,null,morph) `rejectable_id`(ubig,null,morph) `rejected_by_id`(FK→users) `reason`(500) `created_at` `updated_at` `deleted_at` - Relations: rejectable(MorphTo), rejectedBy(BelongsTo→User) --- ## Production ### `cuttings` → Cutting `id` `created_by_id`(FK→users) `status`(enum,default:in_progress) `description`(100,null) `total_material_cost`(ubig,null) `cost_per_unit`(ubig,null) `created_at` `updated_at` `deleted_at` - Casts: status(CuttingStatus), total_material_cost(int), cost_per_unit(int) - Scopes: cancelled(), completed(), inProgress() - Relations: createdBy(BelongsTo→User), cuttingMaterialCombinations(HasMany→CuttingMaterialCombination), cuttingMaterials(HasMany→CuttingMaterial), cuttingResults(HasMany→CuttingResult) ### `cutting_material_combinations` → CuttingMaterialCombination `id` `user_id`(FK→users,null) `cutting_id`(FK→cuttings,null) `material_result`(int,null) `created_at` `updated_at` `deleted_at` - Casts: material_result(int) - Accessor: formatted_material_result → 'X.XXX' - Relations: cutting(BelongsTo→Cutting), cuttingMaterials(HasMany→CuttingMaterial,combination_id), user(BelongsTo→User) ### `cutting_materials` → CuttingMaterial `id` `user_id`(FK→users,null) `cutting_id`(FK→cuttings,null) `raw_material_price_id`(FK→raw_material_prices) `combination_id`(FK→cutting_material_combinations,null) `material_usage`(int) `material_result`(int,null) `created_at` `updated_at` `deleted_at` - Casts: material_usage(int), material_result(int) - Accessor: formatted_material_result → 'X.XXX', formatted_material_usage → 'X.XXX' - Relations: cutting(BelongsTo→Cutting), rawMaterialPrice(BelongsTo→RawMaterialPrice), combination(BelongsTo→CuttingMaterialCombination), user(BelongsTo→User) ### `cutting_results` → CuttingResult `id` `user_id`(FK→users,null) `cutting_id`(FK→cuttings,null) `product_name`(255,null) `cutting_result`(uint,null) `sample`(uint,null) `original_outside_sample`(uint,null) `created_at` `updated_at` `deleted_at` - Casts: cutting_result(int), sample(int), original_outside_sample(int) - Accessor: formatted_cutting_result → 'X.XXX', formatted_original_outside_sample → 'X.XXX', formatted_sample → 'X.XXX' - Relations: cutting(BelongsTo→Cutting), user(BelongsTo→User) --- ## Inventory ### `purchases` → Purchase `id` `supplier_id`(FK→suppliers) `created_by_id`(FK→users) `subtotal`(ubig) `discount`(ubig,default:0) `shipping_cost`(ubig,default:0) `total`(ubig) `notes`(100,null) `created_at` `updated_at` `deleted_at` - Casts: subtotal(int), discount(int), shipping_cost(int), total(int) - Relations: createdBy(BelongsTo→User), supplier(BelongsTo→Supplier), purchaseItems(HasMany→PurchaseItem) ### `purchase_items` → PurchaseItem `id` `purchase_id`(FK→purchases,null) `user_id`(FK→users,null) `raw_material_price_id`(FK→raw_material_prices) `quantity`(uint) `unit_price`(ubig) `subtotal`(ubig) `created_at` `updated_at` `deleted_at` - Casts: quantity(int), unit_price(int), subtotal(int) - Relations: purchase(BelongsTo→Purchase), rawMaterialPrice(BelongsTo→RawMaterialPrice,withTrashed), user(BelongsTo→User) ### `restocks` → Restock `id` `created_by_id`(FK→users) `total`(ubig) `notes`(100,null) `stock_type`(enum,default:good) `created_at` `updated_at` `deleted_at` - Casts: stock_type(ProductStockQuality), total(int) - Scopes: good(), reject() - Relations: createdBy(BelongsTo→User), restockItems(HasMany→RestockItem) ### `restock_items` → RestockItem `id` `restock_id`(FK→restocks,null) `user_id`(FK→users,null) `product_variant_id`(FK→product_variants) `quantity`(int) `unit_price`(ubig) `subtotal`(ubig) `created_at` `updated_at` `deleted_at` - Casts: quantity(int), unit_price(int), subtotal(int) - Relations: restock(BelongsTo→Restock), productVariant(BelongsTo→ProductVariant), user(BelongsTo→User) ### `stok_opnames` → StokOpname `id` `created_by_id`(FK→users) `verified_by_id`(FK→users,null) `opname_date`(date) `status`(enum,default:draft) `notes`(text,null) `verification_notes`(text,null) `created_at` `updated_at` `deleted_at` - Casts: status(StokOpnameStatus), opname_date(date:Y-m-d) - Scopes: cancelled(), completed(), draft(), inProgress(), verified() - Relations: createdBy(BelongsTo→User), verifiedBy(BelongsTo→User), stokOpnameItems(HasMany→StokOpnameItem) ### `stok_opname_items` → StokOpnameItem `id` `stok_opname_id`(FK→stok_opnames) `product_variant_id`(FK→product_variants) `stock_quality`(enum,default:good) `system_stock`(uint,default:0) `physical_stock`(uint,default:0) `difference`(int,default:0) `notes`(text,null) `created_at` `updated_at` - No soft deletes - Casts: stock_quality(ProductStockQuality), system_stock(int), physical_stock(int), difference(int) - Scopes: good(), reject() - Relations: stokOpname(BelongsTo→StokOpname), productVariant(BelongsTo→ProductVariant) ### `retail_stock_histories` → RetailStockHistory `id` `product_variant_id`(FK→product_variants) `user_id`(FK→users) `quantity`(uint) `stock_before`(uint) `retail_stock_before`(uint) `stock_after`(uint) `retail_stock_after`(uint) `notes`(255,null) `created_at` - No soft deletes, no updated_at - $timestamps = false - Casts: quantity(int), stock_before(int), retail_stock_before(int), stock_after(int), retail_stock_after(int) - Accessor: formatted_quantity → 'X.XXX', formatted_retail_stock_before → 'X.XXX', formatted_retail_stock_after → 'X.XXX', formatted_stock_before → 'X.XXX', formatted_stock_after → 'X.XXX' - Relations: productVariant(BelongsTo→ProductVariant), user(BelongsTo→User) ### `stock_mutations` → StockMutation `id` `user_id`(FK→users) `stockable_type`(string) `stockable_id`(ubig) `type`(string) `source_type`(string,null) `source_id`(ubig,null) `quantity`(int) `stock_before`(int) `stock_after`(int) `stock_quality`(string,null) `description`(string,null) `created_at` `updated_at` - No soft deletes - Casts: quantity(int), stock_before(int), stock_after(int) - Accessor: formatted_quantity → 'X.XXX', formatted_stock_after → 'X.XXX', formatted_stock_before → 'X.XXX' - Relations: stockable(MorphTo), source(MorphTo), user(BelongsTo→User) --- ## Settings ### `system_configurations` → SystemConfiguration `id` `created_at` `updated_at` - Singleton table ### `homepage_configurations` → HomepageConfiguration `id` `created_at` `updated_at` - Singleton table ### `settings` (laravel-settings) `id` `group`(string) `name`(string) `locked`(bool,default:false) `payload`(json) `created_at` `updated_at` - UNIQUE(group, name) --- ## Enums | Enum | Values | Used In | |------|--------|---------| | `CashTransactionType` | deposit, expense, transfer, withdrawal | cash_transactions.type | | `CuttingStatus` | in_progress, completed, cancelled | cuttings.status | | `EmployeeAdvanceStatus` | pending, disbursed, partial, repaid, rejected | employee_advances.status | | `EmploymentStatus` | full_time, part_time, contract, internship, resigned | employees.employment_status | | `Gender` | male, female | user_profiles.gender | | `LeaveRequestStatus` | pending, approved, rejected, cancelled | leave_requests.status | | `Modules` | — | Permission module names | | `OrderChannel` | store, shopee, tiktok | orders.channel | | `OrderStatus` | pending, processing, completed, cancelled, refunded | orders.status | | `PaymentType` | cash, transfer, qris | orders.payment_type | | `PayrollAdjustmentType` | bonus, deduction | payroll_adjustments.type | | `PayrollPeriodStatus` | open, closed | payroll_periods.status | | `PayrollStatus` | unpaid, paid, cancelled | payrolls.status | | `Permission` | — | Permission action names | | `PriceType` | retail, wholesale, capital | product_prices.type, orders.price_type | | `ProductStatus` | active, draft, inactive, pending, rejected | products.status | | `ProductStockQuality` | good, reject | order_items.stock_quality, restocks.stock_type, stok_opname_items.stock_quality | | `RawMaterialUnit` | kg, meter, yard | raw_materials.unit | | `Role` | — | Role names | | `StokOpnameStatus` | draft, in_progress, completed, verified, cancelled | stok_opnames.status | ## Service Concerns | Trait | Methods | Used By | |-------|---------|---------| | `HandlesCashTransactions` | getCashAccount(), creditCash(), debitCash() | CashAccountService, ExpenseService, EmployeeAdvanceService, PayrollPeriodService | | `HasStockAdjustment` | adjustStock(), adjustVariantStock(), applyStock(), reverseStock() | TransactionService, RestockService | | `RegistersMedia` | registerMedia(), syncPhoto() | CashAccountService, CuttingService, ExpenseService, PurchaseService, RestockService, TransactionService | --- ## Form Request Validation Rules > Validasi harus SESUAI dengan DB schema (type + max length). ### Master | Request | Field | Rules | DB Match | |---------|-------|-------|----------| | `CategoryRequest` | `name` | `required, string, max:50, unique:categories,name` | ✅ varchar(50) | | `CustomerRequest` | `name` | `required, string, max:200, unique:customers,name` | ✅ varchar(200) | | `CustomerRequest` | `phone_number` | `nullable, string, max:20` | ✅ varchar(20) | | `CustomerRequest` | `address` | `nullable, string` | ✅ text | | `SupplierRequest` | `name` | `required, string, max:200, unique:suppliers,name` | ✅ varchar(200) | | `SupplierRequest` | `phone_number` | `nullable, string, max:20` | ✅ varchar(20) | | `RawMaterialRequest` | `name` | `required, string, max:200` | ✅ varchar(200) | | `RawMaterialRequest` | `unit` | `required, in:kg,meter,yard` | ✅ enum | | `RawMaterialVariantRequest` | `variant` | `required, string, max:200` | ✅ varchar(200) | | `RawMaterialVariantRequest` | `price` | `required, integer, min:0` | ✅ uint | | `ProductRequest` | `name` | `required, string, max:200` | ✅ varchar(200) | | `ProductRequest` | `is_featured` | `nullable, boolean` | ✅ bool | | `ProductVariantRequest` | `name` | `required, string, max:200` | ✅ varchar(200) | ### Finance | Request | Field | Rules | DB Match | |---------|-------|-------|----------| | `CashAccountRequest` | `name` | `required, string, max:200, unique:cash_accounts,name` | ✅ varchar(200) | | `CashTransactionRequest` | `amount` | `required, integer, min:1` | ✅ ubig | | `CashTransactionRequest` | `description` | `required, string, max:100` | ✅ varchar(100) | | `ExpenseRequest` | `amount` | `required, integer, min:1` | ✅ ubig | | `ExpenseRequest` | `description` | `required, string, max:100` | ✅ varchar(100) | | `EmployeeAdvanceRequest` | `amount` | `required, integer, min:1` | ✅ ubig | | `EmployeeAdvanceRequest` | `description` | `required, string, max:100` | ✅ varchar(100) | | `EmployeeAdvanceRequest` | `due_date` | `required, date, after_or_equal:today` | ✅ date | | `EmployeeAdvancePaymentRequest` | `amount` | `required, integer, min:1` | ✅ ubig | | `EmployeeAdvancePaymentRequest` | `description` | `nullable, string, max:100` | ✅ varchar(100) nullable | | `PayrollAdjustmentRequest` | `type` | `required, in:bonus,deduction` | ✅ enum | | `PayrollAdjustmentRequest` | `amount` | `required, integer, min:1` | ✅ ubig | | `PayrollAdjustmentRequest` | `description` | `required, string, max:100` | ✅ varchar(100) | ### HR | Request | Field | Rules | DB Match | |---------|-------|-------|----------| | `EmployeeRequest` | `email` | `required, email, max:100, unique:users,email` | ✅ varchar(100) | | `EmployeeRequest` | `username` | `required, string, max:20, alpha_dash, unique:users,username` | ✅ varchar(20) | | `EmployeeRequest` | `full_name` | `required, string, max:200` | ✅ varchar(200) | | `EmployeeRequest` | `phone_number` | `nullable, string, max:20` | ✅ varchar(20) | | `EmployeeRequest` | `gender` | `nullable, in:male,female` | ✅ enum | | `EmployeeRequest` | `base_salary` | `required, integer, min:0` | ✅ uint | | `AttendanceRequest` | `latitude` | `required, numeric, min:-90, max:90` | ✅ decimal(10,7) | | `AttendanceRequest` | `longitude` | `required, numeric, min:-180, max:180` | ✅ decimal(10,7) | | `LeaveRequestRequest` | `start_date` | `required, date, after:today` | ✅ date | | `LeaveRequestRequest` | `end_date` | `required, date, after_or_equal:start_date` | ✅ date | ### Sales | Request | Field | Rules | DB Match | |---------|-------|-------|----------| | `TransactionRequest` | `channel` | `required, in:store,shopee,tiktok` | ✅ enum | | `TransactionRequest` | `price_type` | `required, in:retail,wholesale` | ✅ enum | | `TransactionRequest` | `payment_type` | `required, in:cash,transfer,qris` | ✅ enum | | `TransactionRequest` | `tiktok_order_id` | `nullable, string, max:100` | ✅ varchar(100) | | `TransactionRequest` | `shopee_order_id` | `nullable, string, max:100` | ✅ varchar(100) | | `TransactionRequest` | `notes` | `nullable, string, max:100` | ✅ text (UI limit) | ### Production | Request | Field | Rules | DB Match | |---------|-------|-------|----------| | `CuttingRequest` | `description` | `nullable, string, max:100` | ✅ varchar(100) | | `CuttingRequest` | `product_name` | `required, string, max:255` | ✅ varchar(255) | | `CuttingRequest` | `cutting_result` | `required, integer, min:1` | ✅ uint | | `CuttingRequest` | `sample` | `required, integer` | ✅ uint | | `CuttingRequest` | `original_outside_sample` | `required, integer` | ✅ uint | ### Inventory | Request | Field | Rules | DB Match | |---------|-------|-------|----------| | `PurchaseRequest` | `supplier_id` | `required, integer, exists:suppliers,id` | ✅ FK | | `PurchaseRequest` | `notes` | `nullable, string, max:100` | ✅ varchar(100) | | `RestockRequest` | `stock_type` | `required, in:good,reject` | ✅ enum | | `RestockRequest` | `notes` | `nullable, string, max:100` | ✅ varchar(100) |