feat: enhance validation rules for various requests and update attribute names

This commit is contained in:
Yoga Pangestu 2026-08-08 01:52:16 +07:00
parent cd0995f04e
commit f7ce2f1620
30 changed files with 239 additions and 66 deletions

View File

@ -826,6 +826,42 @@ ### Stock Adjustment Pattern
## Form Request
### Validasi harus SESUAI dengan DB Schema
```php
// ✅ SELALU samakan max length dengan DB column
// DB: varchar(50) → max:50
'name' => ['required', 'string', 'max:50'],
// DB: varchar(200) → max:200
'name' => ['required', 'string', 'max:200'],
// DB: varchar(100) → max:100
'description' => ['required', 'string', 'max:100'],
// DB: text → tidak perlu max (atau max sesuai UI)
'address' => ['nullable', 'string'],
// DB: decimal(10,7) → numeric + min/max range
'latitude' => ['required', 'numeric', 'min:-90', 'max:90'],
'longitude' => ['required', 'numeric', 'min:-180', 'max:180'],
// DB: uint → integer, min:0
'base_salary' => ['required', 'integer', 'min:0'],
// DB: ubig → integer, min:1 (untuk amount)
'amount' => ['required', 'integer', 'min:1'],
// DB: enum → Rule::in(Enum::values()) atau Rule::in(['val1', 'val2'])
'status' => ['required', Rule::in(OrderStatus::values())],
'gender' => ['nullable', 'in:male,female'],
// DB: date → 'date'
'join_date' => ['required', 'date'],
// DB: datetime → 'date' (Laravel handle)
'paid_at' => ['nullable', 'date'],
```
### Struktur Dasar
```php
class ProductRequest extends FormRequest
@ -860,6 +896,38 @@ ### Struktur Dasar
}
```
### Rule Classes — Gunakan `Illuminate\Validation\Rule`
```php
use Illuminate\Validation\Rule;
// ✅ SELALU gunakan Rule classes, JANGAN string-based rules
// exists → Rule::exists('table', 'column')
'category_ids.*' => [Rule::exists('categories', 'id')],
'customer_id' => ['nullable', 'integer', Rule::exists('customers', 'id')],
'existing_items.*.raw_material_price_id' => ['required', 'integer', Rule::exists('raw_material_prices', 'id')],
// required_if → Rule::requiredIf(fn () => ...)
'shared_prices' => [Rule::requiredIf(fn () => $useSamePrice), 'nullable', 'array'],
'existing_items' => [Rule::requiredIf(fn () => $this->input('mode') === 'existing'), 'array', 'min:1'],
// required_unless → Rule::requiredUnless(fn () => ...)
'name' => [Rule::requiredUnless(fn () => $this->input('mode') === 'existing'), 'string', 'max:200'],
'unit' => [$this->isMethod('post') ? Rule::requiredUnless(fn () => $this->input('mode') === 'existing') : 'nullable', Rule::in(RawMaterialUnit::values())],
// in → Rule::in(Enum::values()) atau Rule::in(['val1', 'val2'])
'status' => ['required', Rule::in(OrderStatus::values())],
'gender' => ['nullable', 'in:male,female'],
// unique → Rule::unique('table')->ignore($id)
'name' => ['required', 'string', 'max:100', Rule::unique('categories')->ignore($this->route('category')?->id)],
// ❌ JANGAN pakai string-based rules
'category_ids.*' => ['exists:categories,id'], // JANGAN
'shared_prices' => ['required_if:use_same_price,true', ...], // JANGAN
'name' => ['required_unless:mode,existing', ...], // JANGAN
```
### Currency Stripping
```php
use App\Concerns\CurrencyStripping;
@ -874,11 +942,11 @@ ### Currency Stripping
### Unique Ignore
```php
// ✅ Saat update, ignore ID sendiri
Rule::unique('products')->ignore($this->route('product')?->id)
// ✅ Saat update, ignore ID sendiri — gunakan Rule::unique()
'name' => ['required', 'string', 'max:100', Rule::unique('categories')->ignore($this->route('category')?->id)],
// ✅ Saat store, tidak perlu ignore
Rule::unique('products')
'name' => ['required', 'string', 'max:100', Rule::unique('categories')],
```
### Shared Store/Update Request
@ -1468,6 +1536,7 @@ ### ✅ Do
- Tidak perlu accessor untuk label — Enum sudah handle via `->label()`
- Selalu select kolom yang dibutuhkan + eager load relasi
- Selalu strip currency di FormRequest sebelum validasi
- Selalu pakai `Rule::exists()`, `Rule::requiredIf()`, `Rule::requiredUnless()` (bukan string-based rules)
- Selalu pakai constructor promotion di controller (`public function __construct(private XService $service) {}`)
- Selalu kirim enum options dari controller ke view (`Enum::toSelect()`)
- Selalu pakai `handleAction()` untuk 2+ query
@ -1495,6 +1564,7 @@ ### ❌ Don't
- Jangan hardcode enum values di view (ambil dari controller)
- Jangan pakai `= new XService` (pakai dependency injection)
- Jangan lupa strip currency saat input Rupiah
- Jangan pakai string-based rules seperti `'exists:table,id'`, `'required_if:field,value'`, `'required_unless:field,value'` (pakai Rule classes)
- Jangan lupa `authorize()` di FormRequest (return true)
- Jangan lupa `attributes()` di FormRequest (label Bahasa Indonesia)
- JANGAN ada logic di controller (SEKECIL APAPUN)

View File

@ -312,3 +312,89 @@ ## Service Concerns
| `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) |
| `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) |

View File

@ -9,7 +9,7 @@ class CashAccountRequest extends FormRequest
{
public function authorize(): bool
{
return true;
return $this->user()->can('cash.update');
}
public function rules(): array

View File

@ -12,7 +12,8 @@ class CashTransactionRequest extends FormRequest
public function authorize(): bool
{
return true;
return $this->user()->can('cash.deposit')
|| $this->user()->can('cash.withdraw');
}
#[Override]

View File

@ -12,7 +12,7 @@ class EmployeeAdvancePaymentRequest extends FormRequest
public function authorize(): bool
{
return true;
return $this->user()->can('employee_advances.pay');
}
#[Override]

View File

@ -12,7 +12,8 @@ class EmployeeAdvanceRequest extends FormRequest
public function authorize(): bool
{
return true;
return $this->user()->can('employee_advances.create')
|| $this->user()->can('employee_advances.update');
}
#[Override]

View File

@ -12,7 +12,8 @@ class ExpenseRequest extends FormRequest
public function authorize(): bool
{
return true;
return $this->user()->can('expenses.create')
|| $this->user()->can('expenses.update');
}
#[Override]

View File

@ -14,7 +14,7 @@ class PayrollAdjustmentRequest extends FormRequest
public function authorize(): bool
{
return true;
return $this->user()->can('payroll.adjust');
}
#[Override]
@ -29,7 +29,6 @@ public function rules(): array
'type' => ['required', Rule::in(PayrollAdjustmentType::values())],
'amount' => ['required', 'integer', 'min:1'],
'description' => ['required', 'string', 'max:100'],
'attendance_id' => ['nullable', 'integer', 'exists:attendances,id'],
];
}
@ -39,7 +38,6 @@ public function attributes(): array
'type' => 'jenis',
'amount' => 'jumlah',
'description' => 'keterangan',
'attendance_id' => 'presensi',
];
}
}

View File

@ -8,15 +8,15 @@ class AttendanceRequest extends FormRequest
{
public function authorize(): bool
{
return true;
return $this->user()->can('attendances.create');
}
public function rules(): array
{
return [
'photo' => ['required'],
'latitude' => ['required', 'numeric'],
'longitude' => ['required', 'numeric'],
'latitude' => ['required', 'numeric', 'min:-90', 'max:90'],
'longitude' => ['required', 'numeric', 'min:-180', 'max:180'],
];
}

View File

@ -2,6 +2,8 @@
namespace App\Http\Requests\Admin\HR;
use App\Enums\EmploymentStatus;
use App\Enums\Gender;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
@ -9,7 +11,8 @@ class EmployeeRequest extends FormRequest
{
public function authorize(): bool
{
return true;
return $this->user()->can('employees.create')
|| $this->user()->can('employees.update');
}
public function rules(): array
@ -34,7 +37,7 @@ public function rules(): array
'role' => ['required', 'string', Rule::exists('roles', 'name')],
'full_name' => ['required', 'string', 'max:200'],
'phone_number' => ['nullable', 'string', 'max:20'],
'gender' => ['nullable', 'in:male,female'],
'gender' => ['nullable', Rule::in(Gender::values())],
'birth_date' => ['nullable', 'date'],
'address' => ['nullable', 'string'],
'join_date' => [
@ -48,7 +51,7 @@ public function rules(): array
],
'employment_status' => [
Rule::requiredIf(! $isOwner),
Rule::in(['full_time', 'part_time', 'contract', 'internship', 'resigned']),
Rule::in(EmploymentStatus::values()),
],
'base_salary' => [
Rule::requiredIf(! $isOwner),

View File

@ -9,7 +9,8 @@ class LeaveRequestRequest extends FormRequest
{
public function authorize(): bool
{
return true;
return $this->user()->can('leave_requests.create')
|| $this->user()->can('leave_requests.update');
}
public function rules(): array
@ -30,7 +31,7 @@ function ($attribute, $value, $fail) use ($leaveRequest) {
$query = LeaveRequest::where('employee_id', $employee->id)
->where('start_date', $value)
->where('status', '!=', 'cancelled');
->where('status', '!=', LeaveRequest::cancelled());
if ($leaveRequest) {
$query->where('id', '!=', $leaveRequest->id);

View File

@ -3,12 +3,14 @@
namespace App\Http\Requests\Admin\Manage;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class CuttingRequest extends FormRequest
{
public function authorize(): bool
{
return true;
return $this->user()->can('cuttings.create')
|| $this->user()->can('cuttings.update');
}
public function rules(): array
@ -20,7 +22,7 @@ public function rules(): array
'original_outside_sample' => ['required', 'integer'],
'cutting_result' => ['required', 'integer', 'min:1'],
'materials' => ['required', 'array', 'min:1'],
'materials.*.raw_material_price_id' => ['required', 'integer', 'exists:raw_material_prices,id'],
'materials.*.raw_material_price_id' => ['required', 'integer', Rule::exists('raw_material_prices', 'id')],
'materials.*.material_usage' => ['required', 'integer', 'min:1'],
'materials.*.material_result' => ['required', 'integer'],
'materials.*.combination_index' => ['nullable', 'integer'],

View File

@ -13,7 +13,8 @@ class PurchaseRequest extends FormRequest
public function authorize(): bool
{
return true;
return $this->user()->can('purchases.create')
|| $this->user()->can('purchases.update');
}
public function prepareForValidation(): void
@ -25,18 +26,18 @@ public function rules(): array
{
return [
'mode' => ['sometimes', 'required', 'in:new,existing'],
'name' => ['required_unless:mode,existing', 'string', 'max:200'],
'unit' => [$this->isMethod('post') ? 'required_unless:mode,existing' : 'nullable', Rule::in(RawMaterialUnit::values())],
'variants' => ['required_unless:mode,existing', 'array', 'min:1'],
'variants.*.variant' => ['required_unless:mode,existing', 'string', 'max:200'],
'variants.*.price' => ['required_unless:mode,existing', 'integer', 'min:0'],
'variants.*.stock' => ['required_unless:mode,existing', 'integer', 'min:0'],
'variants.*.photo_key' => ['required_unless:mode,existing', 'string', 'max:500'],
'existing_items' => ['required_if:mode,existing', 'array', 'min:1'],
'existing_items.*.raw_material_price_id' => ['required', 'integer', 'exists:raw_material_prices,id'],
'name' => [Rule::requiredUnless(fn () => $this->input('mode') === 'existing'), 'string', 'max:200'],
'unit' => [$this->isMethod('post') ? Rule::requiredUnless(fn () => $this->input('mode') === 'existing') : 'nullable', Rule::in(RawMaterialUnit::values())],
'variants' => [Rule::requiredUnless(fn () => $this->input('mode') === 'existing'), 'array', 'min:1'],
'variants.*.variant' => [Rule::requiredUnless(fn () => $this->input('mode') === 'existing'), 'string', 'max:200'],
'variants.*.price' => [Rule::requiredUnless(fn () => $this->input('mode') === 'existing'), 'integer', 'min:0'],
'variants.*.stock' => [Rule::requiredUnless(fn () => $this->input('mode') === 'existing'), 'integer', 'min:0'],
'variants.*.photo_key' => [Rule::requiredUnless(fn () => $this->input('mode') === 'existing'), 'string', 'max:500'],
'existing_items' => [Rule::requiredIf(fn () => $this->input('mode') === 'existing'), 'array', 'min:1'],
'existing_items.*.raw_material_price_id' => ['required', 'integer', Rule::exists('raw_material_prices', 'id')],
'existing_items.*.quantity' => ['required', 'integer', 'min:1'],
'existing_items.*.unit_price' => ['required', 'integer', 'min:0'],
'supplier_id' => ['required', 'integer', 'exists:suppliers,id'],
'supplier_id' => ['required', 'integer', Rule::exists('suppliers', 'id')],
'discount' => ['nullable', 'integer', 'min:0'],
'shipping_cost' => ['nullable', 'integer', 'min:0'],
'notes' => ['nullable', 'string', 'max:100'],

View File

@ -10,7 +10,8 @@ class RestockRequest extends FormRequest
{
public function authorize(): bool
{
return true;
return $this->user()->can('restocks.create')
|| $this->user()->can('restocks.update');
}
public function rules(): array
@ -18,7 +19,7 @@ public function rules(): array
return [
'stock_type' => ['sometimes', 'required', Rule::in(ProductStockQuality::values())],
'items' => ['required', 'array', 'min:1'],
'items.*.product_variant_id' => ['required', 'integer', 'exists:product_variants,id'],
'items.*.product_variant_id' => ['required', 'integer', Rule::exists('product_variants', 'id')],
'items.*.quantity' => ['required', 'integer', 'min:1'],
'notes' => ['nullable', 'string', 'max:100'],
'photo_key' => ['nullable', 'string', 'max:500'],

View File

@ -16,7 +16,8 @@ class TransactionRequest extends FormRequest
public function authorize(): bool
{
return true;
return $this->user()->can('orders.create')
|| $this->user()->can('orders.update');
}
public function prepareForValidation(): void
@ -26,15 +27,13 @@ public function prepareForValidation(): void
public function rules(): array
{
$sellingPriceTypes = array_diff(PriceType::values(), [PriceType::CAPITAL->value]);
return [
'stock_type' => ['sometimes', 'required', Rule::in(ProductStockQuality::values())],
'channel' => ['sometimes', 'required', Rule::in(OrderChannel::values())],
'price_type' => ['sometimes', 'required', Rule::in($sellingPriceTypes)],
'price_type' => ['sometimes', 'required', Rule::in(array_diff(PriceType::values(), [PriceType::CAPITAL->value]))],
'payment_type' => ['sometimes', 'required', Rule::in(PaymentType::values())],
'customer_id' => ['nullable', 'integer', 'exists:customers,id'],
'marketing_id' => ['nullable', 'integer', 'exists:users,id'],
'customer_id' => ['nullable', 'integer', Rule::exists('customers', 'id')],
'marketing_id' => ['nullable', 'integer', Rule::exists('users', 'id')],
'discount' => ['nullable', 'integer', 'min:0'],
'nego_price' => ['nullable', 'integer'],
'is_completed' => ['sometimes', 'boolean'],
@ -42,7 +41,7 @@ public function rules(): array
'tiktok_order_id' => ['nullable', 'string', 'max:100'],
'shopee_order_id' => ['nullable', 'string', 'max:100'],
'items' => ['required', 'array', 'min:1'],
'items.*.product_variant_id' => ['required', 'integer', 'exists:product_variants,id'],
'items.*.product_variant_id' => ['required', 'integer', Rule::exists('product_variants', 'id')],
'items.*.quantity' => ['required', 'integer', 'min:1'],
'notes' => ['nullable', 'string', 'max:100'],
'photo_key' => [

View File

@ -9,7 +9,8 @@ class CategoryRequest extends FormRequest
{
public function authorize(): bool
{
return true;
return $this->user()->can('categories.create')
|| $this->user()->can('categories.update');
}
public function rules(): array
@ -17,7 +18,7 @@ public function rules(): array
$category = $this->route('category');
return [
'name' => ['required', 'string', 'max:100', Rule::unique('categories', 'name')->ignore($category)],
'name' => ['required', 'string', 'max:50', Rule::unique('categories', 'name')->ignore($category)],
];
}

View File

@ -9,7 +9,8 @@ class CustomerRequest extends FormRequest
{
public function authorize(): bool
{
return true;
return $this->user()->can('customers.create')
|| $this->user()->can('customers.update');
}
public function rules(): array

View File

@ -4,6 +4,7 @@
use App\Concerns\CurrencyStripping;
use App\Enums\PriceType;
use App\Enums\ProductStatus;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use Override;
@ -14,7 +15,8 @@ class ProductRequest extends FormRequest
public function authorize(): bool
{
return true;
return $this->user()->can('products.create')
|| $this->user()->can('products.update');
}
#[Override]
@ -34,13 +36,13 @@ public function rules(): array
'max:200',
],
'description' => ['nullable', 'string'],
'status' => ['nullable', Rule::in(['active', 'inactive', 'draft', 'pending', 'rejected'])],
'status' => ['nullable', Rule::in(ProductStatus::values())],
'category_ids' => ['required', 'array', 'min:1'],
'category_ids.*' => ['exists:categories,id'],
'category_ids.*' => [Rule::exists('categories', 'id')],
'use_same_price' => ['nullable', 'boolean'],
'shared_prices' => ['required_if:use_same_price,true', 'nullable', 'array', ...($useSamePrice ? ['size:9'] : [])],
'shared_prices.*.type' => ['required_if:use_same_price,true', 'nullable', Rule::in(PriceType::values())],
'shared_prices.*.price' => ['required_if:use_same_price,true', 'nullable', 'integer', 'min:0'],
'shared_prices' => [Rule::requiredIf(fn() => $useSamePrice), 'nullable', 'array', ...($useSamePrice ? ['size:9'] : [])],
'shared_prices.*.type' => [Rule::requiredIf(fn() => $useSamePrice), 'nullable', Rule::in(PriceType::values())],
'shared_prices.*.price' => [Rule::requiredIf(fn() => $useSamePrice), 'nullable', 'integer', 'min:0'],
'variants' => ['required', 'array', 'min:1'],
'variants.*.id' => ['nullable', 'integer'],
'variants.*.name' => ['required', 'string', 'max:200'],
@ -49,10 +51,10 @@ public function rules(): array
'variants.*.retail_stock' => ['required', 'integer', 'min:0'],
'variants.*.photo_keys' => ['required', 'array', 'min:1', 'max:5'],
'variants.*.photo_keys.*' => ['required', 'string', 'max:500'],
'variants.*.prices' => ['required_if:use_same_price,false', 'nullable', 'array', ...(! $useSamePrice ? ['size:9'] : [])],
'variants.*.prices' => [Rule::requiredIf(fn() => ! $useSamePrice), 'nullable', 'array', ...(! $useSamePrice ? ['size:9'] : [])],
'variants.*.prices.*.id' => ['nullable', 'integer'],
'variants.*.prices.*.type' => ['required_if:use_same_price,false', 'nullable', Rule::in(PriceType::values())],
'variants.*.prices.*.price' => ['required_if:use_same_price,false', 'nullable', 'integer', 'min:0'],
'variants.*.prices.*.type' => [Rule::requiredIf(fn() => ! $useSamePrice), 'nullable', Rule::in(PriceType::values())],
'variants.*.prices.*.price' => [Rule::requiredIf(fn() => ! $useSamePrice), 'nullable', 'integer', 'min:0'],
];
}

View File

@ -13,7 +13,8 @@ class ProductVariantRequest extends FormRequest
public function authorize(): bool
{
return true;
return $this->user()->can('products.create')
|| $this->user()->can('products.update');
}
public function prepareForValidation(): void

View File

@ -8,7 +8,7 @@ class TransferStockRequest extends FormRequest
{
public function authorize(): bool
{
return true;
return $this->user()->can('products.transfer_stock');
}
public function rules(): array

View File

@ -13,7 +13,8 @@ class RawMaterialRequest extends FormRequest
public function authorize(): bool
{
return true;
return $this->user()->can('raw_materials.create')
|| $this->user()->can('raw_materials.update');
}
public function prepareForValidation(): void

View File

@ -11,7 +11,8 @@ class RawMaterialVariantRequest extends FormRequest
public function authorize(): bool
{
return true;
return $this->user()->can('raw_materials.create')
|| $this->user()->can('raw_materials.update');
}
public function prepareForValidation(): void

View File

@ -9,7 +9,8 @@ class SupplierRequest extends FormRequest
{
public function authorize(): bool
{
return true;
return $this->user()->can('suppliers.create')
|| $this->user()->can('suppliers.update');
}
public function rules(): array

View File

@ -9,7 +9,8 @@ class RoleRequest extends FormRequest
{
public function authorize(): bool
{
return true;
return $this->user()->can('roles.create')
|| $this->user()->can('roles.update');
}
public function rules(): array
@ -24,7 +25,7 @@ public function rules(): array
Rule::unique('roles', 'name')->ignore($roleId, 'id'),
],
'permissions' => ['present', 'array'],
'permissions.*' => ['string', 'exists:permissions,name'],
'permissions.*' => ['string', Rule::exists('permissions', 'name')],
];
}

View File

@ -12,7 +12,7 @@ class UpdateHRRequest extends FormRequest
public function authorize(): bool
{
return true;
return $this->user()->can('settings.update_hr');
}
#[Override]

View File

@ -8,7 +8,7 @@ class UpdateHomepageRequest extends FormRequest
{
public function authorize(): bool
{
return true;
return $this->user()->can('settings.update_homepage');
}
public function rules(): array

View File

@ -8,7 +8,7 @@ class UpdateMarketplaceRequest extends FormRequest
{
public function authorize(): bool
{
return true;
return $this->user()->can('settings.update_marketplace');
}
private function feeRuleRules(): array

View File

@ -8,7 +8,7 @@ class UpdateSocialMediaRequest extends FormRequest
{
public function authorize(): bool
{
return true;
return $this->user()->can('settings.update_social_media');
}
public function rules(): array

View File

@ -8,7 +8,7 @@ class UpdateSystemRequest extends FormRequest
{
public function authorize(): bool
{
return true;
return $this->user()->can('settings.update_system');
}
public function rules(): array

View File

@ -27,7 +27,7 @@ public function rules(): array
'required',
'string',
'email',
'max:255',
'max:100',
Rule::unique('users', 'email')->ignore($userId),
],
'username' => [