diff --git a/app/Enums/RawMaterialUnit.php b/app/Enums/RawMaterialUnit.php index eb8a912..7d712d0 100644 --- a/app/Enums/RawMaterialUnit.php +++ b/app/Enums/RawMaterialUnit.php @@ -12,11 +12,11 @@ enum RawMaterialUnit: string case METER = 'meter'; case KILOGRAM = 'kilogram'; - private const MIN_STOCK_YARD = 5.0; + private const MIN_STOCK_YARD = 20; - private const MIN_STOCK_METER = 5.0; + private const MIN_STOCK_METER = 10; - private const MIN_STOCK_KILOGRAM = 2.0; + private const MIN_STOCK_KILOGRAM = 5; private const CM_PER_YARD = 91.44; diff --git a/app/Http/Controllers/Admin/Master/RawMaterialController.php b/app/Http/Controllers/Admin/Master/RawMaterialController.php index 6d144f2..c1477f5 100644 --- a/app/Http/Controllers/Admin/Master/RawMaterialController.php +++ b/app/Http/Controllers/Admin/Master/RawMaterialController.php @@ -7,6 +7,7 @@ use App\Http\Controllers\Concerns\ParsesDataTableQuery; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Master\RawMaterialRequest; +use App\Http\Requests\Admin\ToggleStatusRequest; use App\Models\RawMaterial; use App\Models\RawMaterialPrice; use App\Services\Master\RawMaterialService; @@ -28,13 +29,13 @@ public function index(Request $request): Response { $tableQuery = $this->parseDataTableQuery($request); $isActive = $request->string('is_active')->toString(); + $stockStatus = $request->string('stock_status')->toString(); return Inertia::render('admin/master/raw-materials/Index', [ - 'rawMaterials' => $this->rawMaterialService->paginateForIndex($tableQuery, $isActive), - 'outOfStockGroups' => $this->rawMaterialService->outOfStockGroups(), - 'stockWarningGroups' => $this->rawMaterialService->stockWarningGroups(), + 'rawMaterials' => $this->rawMaterialService->paginateForIndex($tableQuery, $isActive, $stockStatus), 'filters' => $this->dataTableFilters($tableQuery, [ 'is_active' => $isActive, + 'stock_status' => $stockStatus, ]), ]); } @@ -80,13 +81,9 @@ public function update(RawMaterialRequest $request, RawMaterial $rawMaterial): R return redirect()->route('admin.master.raw-materials.index'); } - public function toggleStatus(Request $request, RawMaterial $rawMaterial): RedirectResponse + public function toggleStatus(ToggleStatusRequest $request, RawMaterial $rawMaterial): RedirectResponse { - $validated = $request->validate([ - 'is_active' => ['required', 'boolean'], - ]); - - $this->rawMaterialService->toggleStatus($rawMaterial, $validated['is_active']); + $this->rawMaterialService->toggleStatus($rawMaterial, $request->validated()); $this->flashStatusUpdated('bahan baku'); diff --git a/app/Services/Master/RawMaterialService.php b/app/Services/Master/RawMaterialService.php index b4f67a3..1505d59 100644 --- a/app/Services/Master/RawMaterialService.php +++ b/app/Services/Master/RawMaterialService.php @@ -2,6 +2,7 @@ namespace App\Services\Master; +use App\Enums\RawMaterialUnit; use App\Models\RawMaterial; use App\Models\RawMaterialPrice; use App\Services\Media\MediaService; @@ -18,119 +19,10 @@ public function __construct( private readonly MediaService $mediaService, ) {} - /** - * @return list>, - * }>, - * }> - */ - public function outOfStockGroups(): array - { - return $this->inventoryAlertGroups( - fn (RawMaterialPrice $price): bool => (float) $price->stock <= 0, - ); - } - - /** - * @return list>, - * }>, - * }> - */ - public function stockWarningGroups(): array - { - return $this->inventoryAlertGroups(function (RawMaterialPrice $price): bool { - $stock = (float) $price->stock; - - if ($stock <= 0) { - return false; - } - - $minStock = $price->rawMaterial->unit->minStock(); - - return $stock < $minStock; - }); - } - - /** - * @return list>, - * }>, - * }> - */ - private function inventoryAlertGroups(callable $filter): array - { - return RawMaterialPrice::query() - ->whereHas('rawMaterial', fn (Builder $query) => $query->where('is_active', true)) - ->with(['rawMaterial', 'media']) - ->get() - ->filter($filter) - ->sortBy([ - fn (RawMaterialPrice $price) => $price->rawMaterial->name, - fn (RawMaterialPrice $price) => $price->variant, - ]) - ->groupBy('raw_material_id') - ->map(function ($prices, $rawMaterialId) { - $material = $prices->first()->rawMaterial; - - return [ - 'id' => (int) $rawMaterialId, - 'name' => $material->name, - 'unit_label' => $material->unit->label(), - 'edit_url' => route('admin.master.raw-materials.edit', $rawMaterialId), - 'variants' => $prices->map(function (RawMaterialPrice $price) use ($material): array { - return [ - 'id' => $price->id, - 'name' => $price->variant, - 'stock_formatted' => $this->formatStockAmount((float) $price->stock, $material->unit->abbreviation()), - 'images' => MediaPresenter::collection($price, 'images'), - ]; - })->values()->all(), - ]; - }) - ->values() - ->all(); - } - - private function formatStockAmount(float $amount, ?string $unit = null): string - { - $formatted = rtrim(rtrim(number_format($amount, 4, ',', '.'), '0'), ','); - - if ($unit === null) { - return $formatted; - } - - return "{$formatted} {$unit}"; - } - /** * @param array{search: string, sort: string, direction: 'asc'|'desc'} $tableQuery */ - public function paginateForIndex(array $tableQuery, string $isActive): LengthAwarePaginator + public function paginateForIndex(array $tableQuery, string $isActive, string $stockStatus = ''): LengthAwarePaginator { $query = RawMaterial::query() ->with([ @@ -145,10 +37,22 @@ public function paginateForIndex(array $tableQuery, string $isActive): LengthAwa ->orWhereHas('prices', fn (Builder $query) => $query->where('variant', 'like', "%{$search}%")); }); }) - ->when( - $isActive !== '', - fn (Builder $query) => $query->where('is_active', $isActive === '1') - ); + ->when($isActive !== '', fn (Builder $query) => $query->where('is_active', $isActive === '1')) + ->when($stockStatus === 'out_of_stock', function (Builder $query): void { + $query->whereHas('prices', fn (Builder $q) => $q->where('stock', '<=', 0)); + }) + ->when($stockStatus === 'low_stock', function (Builder $query): void { + $query->whereHas('prices', function (Builder $q): void { + $q->where('stock', '>', 0)->where(function (Builder $q): void { + foreach (RawMaterialUnit::cases() as $unit) { + $q->orWhere(function (Builder $q) use ($unit): void { + $q->whereHas('rawMaterial', fn (Builder $rm) => $rm->where('unit', $unit)) + ->where('stock', '<', $unit->minStock()); + }); + } + }); + }); + }); $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']); @@ -224,9 +128,9 @@ public function update(RawMaterial $rawMaterial, array $validated): void }); } - public function toggleStatus(RawMaterial $rawMaterial, bool $isActive): void + public function toggleStatus(RawMaterial $rawMaterial, array $validated): void { - $rawMaterial->is_active = $isActive; + $rawMaterial->is_active = $validated['is_active']; $rawMaterial->save(); } diff --git a/resources/js/pages/admin/master/raw-materials/Create.vue b/resources/js/pages/admin/master/raw-materials/Create.vue index d999e80..88c0a61 100644 --- a/resources/js/pages/admin/master/raw-materials/Create.vue +++ b/resources/js/pages/admin/master/raw-materials/Create.vue @@ -1,10 +1,10 @@