feat: implement permission-based messaging for purchase and stock verification actions, enhancing user feedback based on roles

This commit is contained in:
Yoga Pangestu 2026-07-10 11:26:06 +07:00
parent fa4f6d4737
commit b87203d6ab
5 changed files with 102 additions and 13 deletions

View File

@ -2,6 +2,7 @@
namespace App\Http\Controllers\Admin\Manage\Purchase;
use App\Enums\Permission;
use App\Http\Controllers\Concerns\FlashesEntityMessage;
use App\Http\Controllers\Concerns\ParsesDataTableQuery;
use App\Http\Controllers\Controller;
@ -46,7 +47,11 @@ public function store(PurchaseRequest $request): RedirectResponse
{
$this->purchaseService->create($request->validated(), $request->user());
$this->flashSuccess('Belanja berhasil diajukan dan menunggu verifikasi owner.');
if ($request->user()->can(Permission::OWNER_VERIFICATIONS_VERIFY->value)) {
$this->flashCreated('Belanja');
} else {
$this->flashSuccess('Belanja berhasil diajukan dan menunggu verifikasi owner.');
}
return redirect()->route('admin.manage.purchases.index');
}
@ -64,7 +69,11 @@ public function update(PurchaseRequest $request, Purchase $purchase): RedirectRe
{
$this->purchaseService->update($purchase, $request->validated(), $request->user());
$this->flashSuccess('Perubahan belanja berhasil diajukan dan menunggu verifikasi owner.');
if ($request->user()->can(Permission::OWNER_VERIFICATIONS_VERIFY->value)) {
$this->flashUpdated('Belanja');
} else {
$this->flashSuccess('Perubahan belanja berhasil diajukan dan menunggu verifikasi owner.');
}
return redirect()->route('admin.manage.purchases.index');
}
@ -73,7 +82,11 @@ public function destroy(Request $request, Purchase $purchase): RedirectResponse
{
$this->purchaseService->delete($purchase, $request->user());
$this->flashSuccess('Penghapusan belanja berhasil diajukan dan menunggu verifikasi owner.');
if ($request->user()->can(Permission::OWNER_VERIFICATIONS_VERIFY->value)) {
$this->flashDeleted('Belanja');
} else {
$this->flashSuccess('Penghapusan belanja berhasil diajukan dan menunggu verifikasi owner.');
}
return redirect()->route('admin.manage.purchases.index');
}

View File

@ -26,7 +26,7 @@ public function transfer(RetailStockTransferRequest $request): JsonResponse
return response()->json([
'success' => true,
'message' => 'Pengajuan transfer stok ecer berhasil dikirim dan menunggu verifikasi owner.',
'message' => 'Transfer stok ecer berhasil dilakukan.',
]);
}
}

View File

@ -2,6 +2,7 @@
namespace App\Http\Controllers\Admin\Manage\Stock;
use App\Enums\Permission;
use App\Http\Controllers\Concerns\FlashesEntityMessage;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\Manage\StockVerifyRequest;
@ -40,7 +41,11 @@ public function verify(StockVerifyRequest $request, Cutting $cutting): RedirectR
$request->validated('result_prices'),
);
$this->flashSuccess('Verifikasi berhasil diajukan. Menunggu persetujuan owner.');
if ($request->user()->can(Permission::OWNER_VERIFICATIONS_VERIFY->value)) {
$this->flashSuccess('Verifikasi berhasil disimpan. Stok produk telah ditambahkan ke toko.');
} else {
$this->flashSuccess('Verifikasi berhasil diajukan. Menunggu persetujuan owner.');
}
return redirect()->route('admin.manage.stocks.index');
}

View File

@ -3,6 +3,7 @@
namespace App\Services\Manage;
use App\Enums\CuttingStatus;
use App\Enums\Permission;
use App\Models\Cutting;
use App\Models\CuttingMaterial;
use App\Models\CuttingResult;
@ -85,8 +86,10 @@ public function submitVerification(
]);
}
$isOwner = $user->can(Permission::OWNER_VERIFICATIONS_VERIFY->value);
try {
DB::transaction(function () use ($cutting, $user, $verificationNote, $results, $resultPrices): void {
DB::transaction(function () use ($cutting, $user, $verificationNote, $results, $resultPrices, $isOwner): void {
$cutting->load(['materials.rawMaterialPrice', 'results']);
if ($results !== null) {
@ -111,7 +114,15 @@ public function submitVerification(
}
$cutting->submitted_by_id = $user->id;
$cutting->status = CuttingStatus::PENDING_VERIFICATION;
if ($isOwner) {
$this->applyProductStockOnVerify($cutting);
$this->applyResultPricesToProducts($cutting);
$cutting->status = CuttingStatus::VERIFIED;
} else {
$cutting->status = CuttingStatus::PENDING_VERIFICATION;
}
$cutting->save();
});
} catch (ValidationException $e) {
@ -128,14 +139,26 @@ public function submitVerification(
$description = $cutting->description ?? '-';
$this->pushNotificationService->sendToRoles(
'📦 Verifikasi Stok Menunggu Persetujuan',
"Cutting dengan deskripsi '{$description}' telah diajukan verifikasi dan menunggu persetujuan owner.",
['owner', 'developer', 'direktur'],
route('admin.manage.stocks.index'),
);
if ($isOwner) {
$this->pushNotificationService->sendToRoles(
'📦 Stok Cutting Diverifikasi',
"Cutting dengan deskripsi '{$description}' telah disetujui owner dan stok produk telah ditambahkan ke toko.",
['owner', 'developer', 'direktur'],
route('admin.manage.stocks.index'),
);
} else {
$this->pushNotificationService->sendToRoles(
'📦 Verifikasi Stok Menunggu Persetujuan',
"Cutting dengan deskripsi '{$description}' telah diajukan verifikasi dan menunggu persetujuan owner.",
['owner', 'developer', 'direktur'],
route('admin.manage.stocks.index'),
);
}
$this->cacheForgetByPattern('manage:stocks:*');
if ($isOwner) {
$this->cacheForgetByPattern('prices:*');
}
}
public function approveVerification(

View File

@ -313,4 +313,52 @@ function createCompletedCuttingSetup(): array
])
->assertForbidden();
});
test('owner can verify stock directly without pending verification', function () {
$user = createStockUserWithPermission(
PermissionEnum::STOCKS_VIEW,
PermissionEnum::CUTTINGS_VERIFY,
PermissionEnum::OWNER_VERIFICATIONS_VERIFY
);
$setup = createCompletedCuttingSetup();
$cutting = $setup['cutting'];
$payload = [
'verification_note' => 'Verified directly by owner',
'results' => [
[
'product_variant_id' => $setup['variant']->id,
'good' => 6,
'reject' => 1,
],
],
'result_prices' => [
[
'product_variant_id' => $setup['variant']->id,
'prices' => [
[
'type' => 'harga_modal',
'price' => 50000,
],
[
'type' => 'retail',
'price' => 75000,
],
],
],
],
];
$this->actingAs($user)
->post(route('admin.manage.stocks.verify', $cutting), $payload)
->assertRedirect(route('admin.manage.stocks.index'));
$cutting->refresh();
$this->assertEquals(CuttingStatus::VERIFIED, $cutting->status);
$setup['variant']->refresh();
// Base stock was 10. Added good stock is 6.
$this->assertEquals(16, $setup['variant']->stock);
});
});