diff --git a/app/Enums/Role.php b/app/Enums/Role.php index 17d44f0..075a482 100644 --- a/app/Enums/Role.php +++ b/app/Enums/Role.php @@ -274,7 +274,6 @@ public function permissions(): array Permission::RAW_MATERIALS_VIEW, Permission::RAW_MATERIALS_CREATE, Permission::RAW_MATERIALS_UPDATE, - Permission::RAW_MATERIALS_DELETE, Permission::RAW_MATERIALS_TOGGLE_STATUS, Permission::OWNER_VERIFICATIONS_VIEW, diff --git a/app/Http/Controllers/Admin/Master/RawMaterialController.php b/app/Http/Controllers/Admin/Master/RawMaterialController.php index bd98589..688d9d4 100644 --- a/app/Http/Controllers/Admin/Master/RawMaterialController.php +++ b/app/Http/Controllers/Admin/Master/RawMaterialController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Admin\Master; +use App\Enums\Permission; use App\Enums\RawMaterialUnit; use App\Http\Controllers\Concerns\FlashesEntityMessage; use App\Http\Controllers\Concerns\ParsesDataTableQuery; @@ -10,6 +11,8 @@ use App\Http\Requests\Admin\ToggleStatusRequest; use App\Models\RawMaterial; use App\Services\Master\RawMaterialService; +use App\Support\Media\MediaPresenter; +use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Inertia\Inertia; @@ -52,7 +55,11 @@ public function store(RawMaterialRequest $request): RedirectResponse { $this->rawMaterialService->create($request->validated(), $request->user()); - $this->flashSuccess('Bahan baku berhasil diajukan dan menunggu verifikasi owner.'); + if ($request->user()->can(Permission::OWNER_VERIFICATIONS_VERIFY->value)) { + $this->flashCreated('Bahan baku'); + } else { + $this->flashSuccess('Bahan baku berhasil diajukan dan menunggu verifikasi owner.'); + } return redirect()->route('admin.master.raw_materials.index'); } @@ -69,7 +76,11 @@ public function update(RawMaterialRequest $request, RawMaterial $rawMaterial): R { $this->rawMaterialService->update($rawMaterial, $request->validated(), $request->user()); - $this->flashSuccess('Perubahan bahan baku berhasil diajukan dan menunggu verifikasi owner.'); + if ($request->user()->can(Permission::OWNER_VERIFICATIONS_VERIFY->value)) { + $this->flashUpdated('Bahan baku'); + } else { + $this->flashSuccess('Perubahan bahan baku berhasil diajukan dan menunggu verifikasi owner.'); + } return redirect()->route('admin.master.raw_materials.index'); } @@ -78,7 +89,11 @@ public function destroy(Request $request, RawMaterial $rawMaterial): RedirectRes { $this->rawMaterialService->delete($rawMaterial, $request->user()); - $this->flashSuccess('Penghapusan bahan baku berhasil diajukan dan menunggu verifikasi owner.'); + if ($request->user()->can(Permission::OWNER_VERIFICATIONS_VERIFY->value)) { + $this->flashDeleted('Bahan baku'); + } else { + $this->flashSuccess('Penghapusan bahan baku berhasil diajukan dan menunggu verifikasi owner.'); + } return redirect()->route('admin.master.raw_materials.index'); } @@ -87,8 +102,27 @@ public function toggleStatus(ToggleStatusRequest $request, RawMaterial $rawMater { $this->rawMaterialService->toggleStatus($rawMaterial, $request->validated(), $request->user()); - $this->flashSuccess('Perubahan status bahan baku berhasil diajukan dan menunggu verifikasi owner.'); + if ($request->user()->can(Permission::OWNER_VERIFICATIONS_VERIFY->value)) { + $this->flashStatusUpdated('bahan baku'); + } else { + $this->flashSuccess('Perubahan status bahan baku berhasil diajukan dan menunggu verifikasi owner.'); + } return back(); } + + public function show(RawMaterial $rawMaterial): JsonResponse + { + $rawMaterial->load([ + 'prices' => fn ($query) => $query->orderBy('created_at')->with('media'), + ]); + + $rawMaterial->prices->each(function ($price) use ($rawMaterial): void { + $price->setAttribute('images', MediaPresenter::collection($price, 'images')); + $price->setAttribute('unit_abbreviation', $rawMaterial->unit->abbreviation()); + $price->unsetRelation('rawMaterial'); + }); + + return response()->json($rawMaterial); + } } diff --git a/app/Services/Master/RawMaterialService.php b/app/Services/Master/RawMaterialService.php index 1e29f90..2173b37 100644 --- a/app/Services/Master/RawMaterialService.php +++ b/app/Services/Master/RawMaterialService.php @@ -208,13 +208,49 @@ function () use ($validated, $rawMaterial, $user, $isOwner): void { } if (! $isOwner) { - $this->notifyForPendingRequest( - $user, - 'Ubah Bahan Baku', - "Pengajuan ubah bahan baku '{$rawMaterial->name}' menunggu verifikasi owner.", - route('admin.master.raw_materials.index', ['search' => $rawMaterial->name]), - $rawMaterial->name, - ); + $changedVariants = []; + foreach ($validated['prices'] as $priceData) { + if (! empty($priceData['id'])) { + $originalPrice = $rawMaterial->prices->firstWhere('id', $priceData['id']); + if ($originalPrice) { + $isChanged = false; + if ($originalPrice->variant !== $priceData['variant']) { + $isChanged = true; + } + if ($originalPrice->price !== (int) $priceData['price']) { + $isChanged = true; + } + if (rtrim(rtrim(number_format((float) $originalPrice->stock, 4, '.', ''), '0'), '.') !== rtrim(rtrim(number_format((float) $priceData['stock'], 4, '.', ''), '0'), '.')) { + $isChanged = true; + } + + if ($isChanged) { + $changedVariants[] = $priceData['variant']; + } + } + } else { + $changedVariants[] = $priceData['variant']; + } + } + + if (! empty($changedVariants)) { + $variantsStr = implode(', ', $changedVariants); + $this->notifyForPendingRequest( + $user, + 'Ubah Varian Bahan Baku', + "Pengajuan ubah varian '{$variantsStr}' pada bahan baku '{$rawMaterial->name}' menunggu verifikasi owner.", + route('admin.master.raw_materials.index', ['search' => $rawMaterial->name]), + $rawMaterial->name, + ); + } else { + $this->notifyForPendingRequest( + $user, + 'Ubah Bahan Baku', + "Pengajuan ubah bahan baku '{$rawMaterial->name}' menunggu verifikasi owner.", + route('admin.master.raw_materials.index', ['search' => $rawMaterial->name]), + $rawMaterial->name, + ); + } } } diff --git a/resources/js/pages/admin/master/raw-materials/form/RawMaterialVariantEditModal.vue b/resources/js/pages/admin/master/raw-materials/form/RawMaterialVariantEditModal.vue new file mode 100644 index 0000000..4a6c18b --- /dev/null +++ b/resources/js/pages/admin/master/raw-materials/form/RawMaterialVariantEditModal.vue @@ -0,0 +1,184 @@ + + + diff --git a/resources/js/pages/admin/master/raw-materials/table/RawMaterialGroupedTable.vue b/resources/js/pages/admin/master/raw-materials/table/RawMaterialGroupedTable.vue index 20d1161..f8c8d22 100644 --- a/resources/js/pages/admin/master/raw-materials/table/RawMaterialGroupedTable.vue +++ b/resources/js/pages/admin/master/raw-materials/table/RawMaterialGroupedTable.vue @@ -1,5 +1,6 @@ diff --git a/routes/web.php b/routes/web.php index 926da91..528f499 100644 --- a/routes/web.php +++ b/routes/web.php @@ -176,6 +176,7 @@ ]) ->name('destroy'); + Route::get('{rawMaterial}', [RawMaterialController::class, 'show'])->name('show'); Route::get('/', [RawMaterialController::class, 'index'])->name('index'); }); diff --git a/tests/Feature/Admin/Master/RawMaterialTest.php b/tests/Feature/Admin/Master/RawMaterialTest.php index bd8aac6..479f63f 100644 --- a/tests/Feature/Admin/Master/RawMaterialTest.php +++ b/tests/Feature/Admin/Master/RawMaterialTest.php @@ -738,8 +738,8 @@ function createRawMaterialVerifierUser(): User RawMaterialPrice::factory()->create(['raw_material_id' => $rawMaterial->id, 'stock' => 10.5]); RawMaterialPrice::factory()->create(['raw_material_id' => $rawMaterial->id, 'stock' => 5.25]); - expect($rawMaterial->fresh()->total_stock_formatted)->toContain('15,75'); - expect($rawMaterial->fresh()->total_stock_formatted)->toContain('m'); + expect($rawMaterial->fresh(['prices'])->total_stock_formatted)->toContain('15,75'); + expect($rawMaterial->fresh(['prices'])->total_stock_formatted)->toContain('m'); }); }); @@ -907,3 +907,56 @@ function createRawMaterialVerifierUser(): User expect($price->fresh()->variant)->toBe($originalVariant); }); }); + +// ─── Show JSON ──────────────────────────────────────────── + +describe('Raw Material Show JSON', function () { + test('authenticated user with permission can get raw material details in JSON', function () { + $user = createRawMaterialUserWithPermission(PermissionEnum::RAW_MATERIALS_VIEW); + + $rawMaterial = createRawMaterialWithPrices(); + + $response = $this->actingAs($user) + ->get(route('admin.master.raw_materials.show', $rawMaterial)) + ->assertOk(); + + $response->assertJsonStructure([ + 'id', + 'name', + 'unit', + 'prices' => [ + '*' => [ + 'id', + 'variant', + 'price', + 'price_formatted', + 'price_input', + 'stock_formatted', + 'stock_input', + 'images', + ], + ], + ]); + + $data = $response->json(); + expect($data['id'])->toBe($rawMaterial->id); + expect($data['prices'])->toHaveCount(2); + }); + + test('guest cannot get raw material details in JSON', function () { + $rawMaterial = createRawMaterialWithPrices(); + + $this->get(route('admin.master.raw_materials.show', $rawMaterial)) + ->assertRedirect(route('login')); + }); + + test('user without view permission cannot get raw material details in JSON', function () { + $user = User::factory()->create(); + + $rawMaterial = createRawMaterialWithPrices(); + + $this->actingAs($user) + ->get(route('admin.master.raw_materials.show', $rawMaterial)) + ->assertForbidden(); + }); +});