feat: implement permission-based messaging for purchase and stock verification actions, enhancing user feedback based on roles
This commit is contained in:
parent
fa4f6d4737
commit
b87203d6ab
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Admin\Manage\Purchase;
|
namespace App\Http\Controllers\Admin\Manage\Purchase;
|
||||||
|
|
||||||
|
use App\Enums\Permission;
|
||||||
use App\Http\Controllers\Concerns\FlashesEntityMessage;
|
use App\Http\Controllers\Concerns\FlashesEntityMessage;
|
||||||
use App\Http\Controllers\Concerns\ParsesDataTableQuery;
|
use App\Http\Controllers\Concerns\ParsesDataTableQuery;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
@ -46,7 +47,11 @@ public function store(PurchaseRequest $request): RedirectResponse
|
|||||||
{
|
{
|
||||||
$this->purchaseService->create($request->validated(), $request->user());
|
$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');
|
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->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');
|
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->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');
|
return redirect()->route('admin.manage.purchases.index');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ public function transfer(RetailStockTransferRequest $request): JsonResponse
|
|||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'message' => 'Pengajuan transfer stok ecer berhasil dikirim dan menunggu verifikasi owner.',
|
'message' => 'Transfer stok ecer berhasil dilakukan.',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Admin\Manage\Stock;
|
namespace App\Http\Controllers\Admin\Manage\Stock;
|
||||||
|
|
||||||
|
use App\Enums\Permission;
|
||||||
use App\Http\Controllers\Concerns\FlashesEntityMessage;
|
use App\Http\Controllers\Concerns\FlashesEntityMessage;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\Admin\Manage\StockVerifyRequest;
|
use App\Http\Requests\Admin\Manage\StockVerifyRequest;
|
||||||
@ -40,7 +41,11 @@ public function verify(StockVerifyRequest $request, Cutting $cutting): RedirectR
|
|||||||
$request->validated('result_prices'),
|
$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');
|
return redirect()->route('admin.manage.stocks.index');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
namespace App\Services\Manage;
|
namespace App\Services\Manage;
|
||||||
|
|
||||||
use App\Enums\CuttingStatus;
|
use App\Enums\CuttingStatus;
|
||||||
|
use App\Enums\Permission;
|
||||||
use App\Models\Cutting;
|
use App\Models\Cutting;
|
||||||
use App\Models\CuttingMaterial;
|
use App\Models\CuttingMaterial;
|
||||||
use App\Models\CuttingResult;
|
use App\Models\CuttingResult;
|
||||||
@ -85,8 +86,10 @@ public function submitVerification(
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$isOwner = $user->can(Permission::OWNER_VERIFICATIONS_VERIFY->value);
|
||||||
|
|
||||||
try {
|
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']);
|
$cutting->load(['materials.rawMaterialPrice', 'results']);
|
||||||
|
|
||||||
if ($results !== null) {
|
if ($results !== null) {
|
||||||
@ -111,7 +114,15 @@ public function submitVerification(
|
|||||||
}
|
}
|
||||||
|
|
||||||
$cutting->submitted_by_id = $user->id;
|
$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();
|
$cutting->save();
|
||||||
});
|
});
|
||||||
} catch (ValidationException $e) {
|
} catch (ValidationException $e) {
|
||||||
@ -128,14 +139,26 @@ public function submitVerification(
|
|||||||
|
|
||||||
$description = $cutting->description ?? '-';
|
$description = $cutting->description ?? '-';
|
||||||
|
|
||||||
$this->pushNotificationService->sendToRoles(
|
if ($isOwner) {
|
||||||
'📦 Verifikasi Stok Menunggu Persetujuan',
|
$this->pushNotificationService->sendToRoles(
|
||||||
"Cutting dengan deskripsi '{$description}' telah diajukan verifikasi dan menunggu persetujuan owner.",
|
'📦 Stok Cutting Diverifikasi',
|
||||||
['owner', 'developer', 'direktur'],
|
"Cutting dengan deskripsi '{$description}' telah disetujui owner dan stok produk telah ditambahkan ke toko.",
|
||||||
route('admin.manage.stocks.index'),
|
['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:*');
|
$this->cacheForgetByPattern('manage:stocks:*');
|
||||||
|
if ($isOwner) {
|
||||||
|
$this->cacheForgetByPattern('prices:*');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function approveVerification(
|
public function approveVerification(
|
||||||
|
|||||||
@ -313,4 +313,52 @@ function createCompletedCuttingSetup(): array
|
|||||||
])
|
])
|
||||||
->assertForbidden();
|
->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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user