refactor: remove owner verification index functionality and related components, streamline approval and rejection processes
This commit is contained in:
parent
2b51f42a7a
commit
1f27acc63a
@ -2,10 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers\Admin\Manage;
|
||||
|
||||
use App\Enums\OwnerVerificationAction;
|
||||
use App\Enums\OwnerVerificationStatus;
|
||||
use App\Http\Controllers\Concerns\FlashesEntityMessage;
|
||||
use App\Http\Controllers\Concerns\ParsesDataTableQuery;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\Manage\ApproveVerificationRequest;
|
||||
use App\Http\Requests\Admin\Manage\RejectVerificationRequest;
|
||||
@ -14,45 +11,16 @@
|
||||
use App\Services\Manage\OwnerVerificationService;
|
||||
use App\Services\Manage\StockService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class OwnerVerificationController extends Controller
|
||||
{
|
||||
use FlashesEntityMessage, ParsesDataTableQuery;
|
||||
use FlashesEntityMessage;
|
||||
|
||||
public function __construct(
|
||||
private readonly OwnerVerificationService $ownerVerificationService,
|
||||
private readonly StockService $stockService,
|
||||
) {}
|
||||
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$tableQuery = $this->parseDataTableQuery($request);
|
||||
$status = $request->string('status')->toString();
|
||||
$subjectType = $request->string('subject_type')->toString();
|
||||
$action = $request->string('action')->toString();
|
||||
|
||||
return Inertia::render('admin/manage/owner-verifications/Index', [
|
||||
'verificationRequests' => $this->ownerVerificationService->paginateForIndex(
|
||||
$request->user(),
|
||||
$tableQuery,
|
||||
$status,
|
||||
$subjectType,
|
||||
$action,
|
||||
),
|
||||
'filters' => $this->dataTableFilters($tableQuery, [
|
||||
'status' => $status,
|
||||
'subject_type' => $subjectType,
|
||||
'action' => $action,
|
||||
]),
|
||||
'statusOptions' => OwnerVerificationStatus::selectOptions(),
|
||||
'subjectOptions' => $this->ownerVerificationService->subjectTypeOptions($request->user()),
|
||||
'actionOptions' => OwnerVerificationAction::selectOptions(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function approveCutting(ApproveVerificationRequest $request, Cutting $cutting): RedirectResponse
|
||||
{
|
||||
$this->stockService->approveVerification(
|
||||
@ -63,9 +31,7 @@ public function approveCutting(ApproveVerificationRequest $request, Cutting $cut
|
||||
|
||||
$this->flashSuccess('Verifikasi cutting berhasil disetujui. Stok produk telah ditambahkan ke toko.');
|
||||
|
||||
$fallback = route('admin.manage.owner_verifications.index');
|
||||
|
||||
return back(302, [], $fallback);
|
||||
return back(302);
|
||||
}
|
||||
|
||||
public function rejectCutting(RejectVerificationRequest $request, Cutting $cutting): RedirectResponse
|
||||
@ -78,9 +44,7 @@ public function rejectCutting(RejectVerificationRequest $request, Cutting $cutti
|
||||
|
||||
$this->flashSuccess('Verifikasi cutting berhasil ditolak.');
|
||||
|
||||
$fallback = route('admin.manage.owner_verifications.index');
|
||||
|
||||
return back(302, [], $fallback);
|
||||
return back(302);
|
||||
}
|
||||
|
||||
public function approveRequest(
|
||||
@ -95,9 +59,7 @@ public function approveRequest(
|
||||
|
||||
$this->flashSuccess('Verifikasi berhasil disetujui.');
|
||||
|
||||
$fallback = route('admin.manage.owner_verifications.index');
|
||||
|
||||
return back(302, [], $fallback);
|
||||
return back(302);
|
||||
}
|
||||
|
||||
public function rejectRequest(
|
||||
@ -112,8 +74,6 @@ public function rejectRequest(
|
||||
|
||||
$this->flashSuccess('Verifikasi berhasil ditolak.');
|
||||
|
||||
$fallback = route('admin.manage.owner_verifications.index');
|
||||
|
||||
return back(302, [], $fallback);
|
||||
return back(302);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
use App\Models\Cutting;
|
||||
use App\Models\EmployeeAdvance;
|
||||
use App\Models\LeaveRequest;
|
||||
use App\Services\Manage\OwnerVerificationService;
|
||||
use App\Services\System\Setting\SystemService;
|
||||
use App\Settings\SystemSettings;
|
||||
use Illuminate\Http\Request;
|
||||
@ -72,7 +71,6 @@ public function share(Request $request): array
|
||||
'pendingLeaveRequests' => fn () => $this->pendingLeaveRequests($request),
|
||||
'pendingEmployeeAdvances' => fn () => $this->pendingEmployeeAdvances($request),
|
||||
'pendingCuttings' => fn () => $this->pendingCuttings($request),
|
||||
'pendingOwnerVerifications' => fn () => $this->pendingOwnerVerifications($request),
|
||||
];
|
||||
}
|
||||
|
||||
@ -114,15 +112,4 @@ private function pendingCuttings(Request $request): int
|
||||
->completed()
|
||||
->count();
|
||||
}
|
||||
|
||||
private function pendingOwnerVerifications(Request $request): int
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
if ($user === null || ! $user->can('owner_verifications.view')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return app(OwnerVerificationService::class)->pendingCountForUser($user);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,19 +2,15 @@
|
||||
|
||||
namespace App\Http\Requests\Admin\Manage;
|
||||
|
||||
use App\Enums\Permission;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ApproveVerificationRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return $this->user()?->can(Permission::OWNER_VERIFICATIONS_VERIFY->value) ?? false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
|
||||
@ -2,19 +2,15 @@
|
||||
|
||||
namespace App\Http\Requests\Admin\Manage;
|
||||
|
||||
use App\Enums\Permission;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class RejectVerificationRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return $this->user()?->can(Permission::OWNER_VERIFICATIONS_REJECT->value) ?? false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
|
||||
@ -265,7 +265,7 @@ public function rejectRequest(
|
||||
'❌ Pengajuan Ditolak',
|
||||
"Pengajuan {$request->action->label()} {$subjectLabel} '{$title}' ditolak dengan alasan: '{$reason}'.",
|
||||
['owner', 'developer'],
|
||||
route('admin.manage.owner_verifications.index'),
|
||||
route('admin.dashboard'),
|
||||
);
|
||||
|
||||
$this->notifyRequestSubmitter(
|
||||
@ -527,7 +527,7 @@ private function notifyRequestSubmitter(
|
||||
RawMaterial::class => route('admin.master.raw_materials.index'),
|
||||
Purchase::class => route('admin.manage.purchases.index'),
|
||||
MarketplaceSettings::class => route('admin.system.settings.index'),
|
||||
default => route('admin.manage.owner_verifications.index'),
|
||||
default => route('admin.dashboard'),
|
||||
};
|
||||
|
||||
$this->pushNotificationService->sendToUser(
|
||||
|
||||
@ -544,7 +544,7 @@ private function notifyForPendingRequest(User $user, string $typeLabel, string $
|
||||
"📦 {$typeLabel} Menunggu Persetujuan Owner",
|
||||
$body,
|
||||
['owner', 'developer'],
|
||||
route('admin.manage.owner_verifications.index'),
|
||||
route('admin.manage.purchases.index'),
|
||||
);
|
||||
|
||||
$this->pushNotificationService->sendToUser(
|
||||
|
||||
@ -134,7 +134,7 @@ public function submitVerification(
|
||||
'📦 Verifikasi Stok Menunggu Persetujuan',
|
||||
"Cutting dengan deskripsi '{$description}' telah diajukan verifikasi dan menunggu persetujuan owner.",
|
||||
['owner', 'developer'],
|
||||
route('admin.manage.owner_verifications.index'),
|
||||
route('admin.manage.stocks.index'),
|
||||
);
|
||||
}
|
||||
|
||||
@ -187,7 +187,7 @@ public function approveVerification(
|
||||
'📦 Stok Cutting Diverifikasi',
|
||||
"Cutting dengan deskripsi '{$description}' telah disetujui owner dan stok produk telah ditambahkan ke toko.",
|
||||
['owner', 'developer'],
|
||||
route('admin.manage.owner_verifications.index'),
|
||||
route('admin.manage.stocks.index'),
|
||||
);
|
||||
}
|
||||
|
||||
@ -232,7 +232,7 @@ public function rejectVerification(
|
||||
'📦 Verifikasi Cutting Ditolak Owner',
|
||||
"Cutting dengan deskripsi '{$description}' ditolak oleh owner dengan alasan: '{$reason}'.",
|
||||
['owner', 'developer'],
|
||||
route('admin.manage.owner_verifications.index'),
|
||||
route('admin.manage.stocks.index'),
|
||||
);
|
||||
|
||||
if ($cutting->submitted_by_id) {
|
||||
@ -240,7 +240,7 @@ public function rejectVerification(
|
||||
'📦 Verifikasi Cutting Ditolak Owner',
|
||||
"Cutting dengan deskripsi '{$description}' yang Anda verifikasi ditolak oleh owner dengan alasan: '{$reason}'.",
|
||||
$cutting->submitted_by_id,
|
||||
route('admin.manage.owner_verifications.index'),
|
||||
route('admin.manage.stocks.index'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -432,7 +432,7 @@ private function notifyForPendingRequest(User $user, string $typeLabel, string $
|
||||
"📦 {$typeLabel} Menunggu Persetujuan Owner",
|
||||
$body,
|
||||
['owner', 'developer'],
|
||||
route('admin.manage.owner_verifications.index'),
|
||||
route('admin.master.products.index'),
|
||||
);
|
||||
|
||||
$this->pushNotificationService->sendToUser(
|
||||
|
||||
@ -307,7 +307,7 @@ private function notifyForPendingRequest(User $user, string $typeLabel, string $
|
||||
"📦 {$typeLabel} Menunggu Persetujuan Owner",
|
||||
$body,
|
||||
['owner', 'developer'],
|
||||
route('admin.manage.owner_verifications.index'),
|
||||
route('admin.master.raw_materials.index'),
|
||||
);
|
||||
|
||||
$this->pushNotificationService->sendToUser(
|
||||
|
||||
@ -125,7 +125,7 @@ public function updateMarketplace(array $validated, User $user): void
|
||||
'⚙️ Pengaturan Marketplace Menunggu Persetujuan Owner',
|
||||
"Pengajuan ubah pengaturan marketplace oleh '{$user->username}' menunggu verifikasi owner.",
|
||||
['owner', 'developer'],
|
||||
route('admin.manage.owner_verifications.index'),
|
||||
route('admin.system.settings.index'),
|
||||
);
|
||||
|
||||
$this->pushNotificationService->sendToUser(
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { Link, usePage } from '@inertiajs/vue3';
|
||||
import { Banknote, CalendarDays, CheckCircle, Clock, FolderTree, History, Layers, LayoutDashboard, Package, Receipt, Scissors, Settings2, Shield, ShoppingBag, ShoppingCart, TrendingUp, User, UserCheck, Users, Wallet, WalletCards, Warehouse } from '@lucide/vue';
|
||||
import { Banknote, CalendarDays, Clock, FolderTree, History, Layers, LayoutDashboard, Package, Receipt, Scissors, Settings2, Shield, ShoppingBag, ShoppingCart, TrendingUp, User, UserCheck, Users, Wallet, WalletCards, Warehouse } from '@lucide/vue';
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
@ -25,7 +25,7 @@ interface MenuItem {
|
||||
href: string;
|
||||
icon: any;
|
||||
permission?: string;
|
||||
badgeKey?: 'pendingLeaveRequests' | 'pendingEmployeeAdvances' | 'pendingCuttings' | 'pendingOwnerVerifications';
|
||||
badgeKey?: 'pendingLeaveRequests' | 'pendingEmployeeAdvances' | 'pendingCuttings';
|
||||
}
|
||||
|
||||
interface MenuGroup {
|
||||
@ -57,7 +57,6 @@ const menuGroups: MenuGroup[] = [
|
||||
{ title: 'Belanja', href: admin.manage.purchases.index.url(), icon: ShoppingBag, permission: 'purchases.view' },
|
||||
{ title: 'Cutting', href: admin.manage.cuttings.index.url(), icon: Scissors, permission: 'cuttings.view' },
|
||||
{ title: 'Stok Gudang', href: admin.manage.stocks.index.url(), icon: Warehouse, permission: 'stocks.view', badgeKey: 'pendingCuttings' },
|
||||
{ title: 'Verifikasi Owner', href: admin.manage.owner_verifications.index.url(), icon: CheckCircle, permission: 'owner_verifications.view', badgeKey: 'pendingOwnerVerifications' },
|
||||
{ title: 'Pesanan', href: admin.manage.orders.index.url(), icon: ShoppingCart, permission: 'orders.view' },
|
||||
],
|
||||
},
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { Link, usePage } from '@inertiajs/vue3';
|
||||
import { CalendarDays, CheckCircle, Clock, ShoppingCart } from '@lucide/vue';
|
||||
import { CalendarDays, Clock, ShoppingCart } from '@lucide/vue';
|
||||
import { useCan } from '@/composables/useCan';
|
||||
import admin from '@/routes/admin';
|
||||
|
||||
@ -12,7 +12,7 @@ interface NavItem {
|
||||
href: string;
|
||||
icon: any;
|
||||
permission?: string;
|
||||
badgeKey?: 'pendingLeaveRequests' | 'pendingOwnerVerifications' | 'pendingCuttings';
|
||||
badgeKey?: 'pendingLeaveRequests' | 'pendingCuttings';
|
||||
}
|
||||
|
||||
const navItems: NavItem[] = [
|
||||
@ -22,13 +22,6 @@ const navItems: NavItem[] = [
|
||||
icon: ShoppingCart,
|
||||
permission: 'orders.view',
|
||||
},
|
||||
{
|
||||
title: 'Verifikasi',
|
||||
href: admin.manage.owner_verifications.index.url(),
|
||||
icon: CheckCircle,
|
||||
permission: 'owner_verifications.view',
|
||||
badgeKey: 'pendingOwnerVerifications',
|
||||
},
|
||||
{
|
||||
title: 'Presensi',
|
||||
href: admin.hr.attendances.index.url(),
|
||||
|
||||
@ -1,143 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { Head } from '@inertiajs/vue3';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { DataTable } from '@/components/data-table';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { useDataTableQuery, useDataTableQuerySync } from '@/composables/useDataTableQuery';
|
||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||
import type {
|
||||
OwnerVerificationItem,
|
||||
PaginatedOwnerVerificationRequests,
|
||||
SelectOption,
|
||||
} from '@/types/owner-verification';
|
||||
import type { DataTableSort } from '@/types/data-table';
|
||||
import OwnerVerificationDetailModal from './table/OwnerVerificationDetailModal.vue';
|
||||
import { createColumns } from './table/columns';
|
||||
import { index } from '@/routes/admin/manage/owner_verifications';
|
||||
|
||||
const props = defineProps<{
|
||||
verificationRequests: PaginatedOwnerVerificationRequests;
|
||||
filters: {
|
||||
search: string;
|
||||
sort?: string;
|
||||
direction?: 'asc' | 'desc';
|
||||
status?: string;
|
||||
subject_type?: string;
|
||||
action?: string;
|
||||
};
|
||||
statusOptions: SelectOption[];
|
||||
subjectOptions: SelectOption[];
|
||||
actionOptions: SelectOption[];
|
||||
}>();
|
||||
|
||||
const search = ref(props.filters.search ?? '');
|
||||
const detailModalOpen = ref(false);
|
||||
const selectedItem = ref<OwnerVerificationItem | null>(null);
|
||||
|
||||
const { query, setSearch, setSort, setFilter, resetFilters, syncFromServer } = useDataTableQuery({
|
||||
url: index.url(),
|
||||
initial: { ...props.filters },
|
||||
filterKeys: ['status', 'subject_type', 'action'],
|
||||
});
|
||||
|
||||
useDataTableQuerySync(() => props.filters, syncFromServer);
|
||||
|
||||
const columns = computed(() => createColumns(openDetailModal));
|
||||
|
||||
const filterDefs = computed(() => [
|
||||
{
|
||||
key: 'status',
|
||||
label: 'Status',
|
||||
type: 'select' as const,
|
||||
options: props.statusOptions,
|
||||
},
|
||||
{
|
||||
key: 'subject_type',
|
||||
label: 'Modul',
|
||||
type: 'select' as const,
|
||||
options: props.subjectOptions,
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
label: 'Aksi',
|
||||
type: 'select' as const,
|
||||
options: props.actionOptions,
|
||||
},
|
||||
]);
|
||||
|
||||
const filterValues = computed(() => ({
|
||||
status: query.value.status ?? '',
|
||||
subject_type: query.value.subject_type ?? '',
|
||||
action: query.value.action ?? '',
|
||||
}));
|
||||
|
||||
const currentSort = computed<DataTableSort | null>(() => {
|
||||
if (!query.value.sort || !query.value.direction) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
column: query.value.sort,
|
||||
direction: query.value.direction,
|
||||
};
|
||||
});
|
||||
|
||||
const pagination = computed(() => ({
|
||||
currentPage: props.verificationRequests.current_page,
|
||||
perPage: props.verificationRequests.per_page,
|
||||
lastPage: props.verificationRequests.last_page,
|
||||
total: props.verificationRequests.total,
|
||||
}));
|
||||
|
||||
function openDetailModal(item: OwnerVerificationItem) {
|
||||
selectedItem.value = item;
|
||||
detailModalOpen.value = true;
|
||||
}
|
||||
|
||||
watch(search, (value) => {
|
||||
setSearch(value);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.filters.search,
|
||||
(value) => {
|
||||
search.value = value ?? '';
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Head title="Verifikasi Owner" />
|
||||
|
||||
<AdminLayout>
|
||||
<div class="space-y-1">
|
||||
<h2 class="text-2xl font-bold tracking-tight">
|
||||
Verifikasi Owner
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<Card class="min-w-0">
|
||||
<CardContent class="min-w-0">
|
||||
<DataTable
|
||||
v-model:search="search"
|
||||
:columns="columns"
|
||||
:data="verificationRequests.data"
|
||||
:pagination="pagination"
|
||||
:pagination-links="verificationRequests.links"
|
||||
:sort="currentSort"
|
||||
:filter-defs="filterDefs"
|
||||
:filter-values="filterValues"
|
||||
search-placeholder="Cari verifikasi..."
|
||||
@sort-change="setSort"
|
||||
@filter-change="setFilter"
|
||||
@filters-reset="resetFilters"
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<OwnerVerificationDetailModal
|
||||
v-model:open="detailModalOpen"
|
||||
:item="selectedItem"
|
||||
/>
|
||||
</AdminLayout>
|
||||
</template>
|
||||
@ -1,120 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import type { OwnerVerificationItem } from '@/types/owner-verification';
|
||||
|
||||
const open = defineModel<boolean>('open', { default: false });
|
||||
|
||||
const props = defineProps<{
|
||||
item?: OwnerVerificationItem | null;
|
||||
}>();
|
||||
|
||||
const hasChanges = computed(() => (props.item?.changes.length ?? 0) > 0);
|
||||
|
||||
function formatValue(value: unknown): string {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return '-';
|
||||
}
|
||||
|
||||
if (typeof value === 'boolean') {
|
||||
return value ? 'Aktif' : 'Nonaktif';
|
||||
}
|
||||
|
||||
if (typeof value === 'object') {
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
return String(value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog v-model:open="open">
|
||||
<DialogContent class="max-h-[85vh] overflow-y-auto sm:max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Detail Verifikasi</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div v-if="item" class="space-y-4 text-sm">
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<div>
|
||||
<p class="text-muted-foreground">Waktu</p>
|
||||
<p class="font-medium">{{ item.created_at_formatted ?? '-' }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-muted-foreground">Diajukan Oleh</p>
|
||||
<p class="font-medium">{{ item.submitted_by_name }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-muted-foreground">Aksi</p>
|
||||
<p class="font-medium">{{ item.action_label }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-muted-foreground">Modul</p>
|
||||
<p class="font-medium">{{ item.subject_label }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-muted-foreground">Item</p>
|
||||
<p class="font-medium">{{ item.title }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-muted-foreground">Status</p>
|
||||
<Badge
|
||||
:variant="item.status === 'approved' ? 'default' : item.status === 'rejected' ? 'destructive' : 'outline'"
|
||||
>
|
||||
{{ item.status_label }}
|
||||
</Badge>
|
||||
</div>
|
||||
<div v-if="item.verified_by_name">
|
||||
<p class="text-muted-foreground">Diverifikasi Oleh</p>
|
||||
<p class="font-medium">{{ item.verified_by_name }}</p>
|
||||
</div>
|
||||
<div v-if="item.verified_at_formatted">
|
||||
<p class="text-muted-foreground">Waktu Verifikasi</p>
|
||||
<p class="font-medium">{{ item.verified_at_formatted }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="item.summary" class="rounded-md border p-3">
|
||||
<p class="text-muted-foreground">Ringkasan</p>
|
||||
<p class="font-medium">{{ item.summary }}</p>
|
||||
</div>
|
||||
|
||||
<div v-if="item.rejection_reason" class="rounded-md border border-destructive/30 bg-destructive/5 p-3">
|
||||
<p class="text-muted-foreground">Alasan Penolakan</p>
|
||||
<p class="font-medium">{{ item.rejection_reason }}</p>
|
||||
</div>
|
||||
|
||||
<div v-if="hasChanges" class="space-y-2">
|
||||
<p class="font-medium">Perubahan Data</p>
|
||||
<div class="overflow-hidden rounded-md border">
|
||||
<table class="w-full text-sm">
|
||||
<thead class="bg-muted/50">
|
||||
<tr>
|
||||
<th class="px-3 py-2 text-left font-medium">Field</th>
|
||||
<th class="px-3 py-2 text-left font-medium">Sebelum</th>
|
||||
<th class="px-3 py-2 text-left font-medium">Sesudah</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="change in item.changes" :key="change.field" class="border-t">
|
||||
<td class="px-3 py-2 align-top font-medium">{{ change.field }}</td>
|
||||
<td class="px-3 py-2 align-top text-muted-foreground">
|
||||
{{ formatValue(change.old) }}
|
||||
</td>
|
||||
<td class="px-3 py-2 align-top">{{ formatValue(change.new) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
@ -1,70 +0,0 @@
|
||||
import type { ColumnDef } from '@tanstack/vue-table';
|
||||
import { h } from 'vue';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { DataTableColumnHeader } from '@/components/data-table';
|
||||
import type { OwnerVerificationItem } from '@/types/owner-verification';
|
||||
import DataTableActions from './data-table-actions.vue';
|
||||
|
||||
function statusBadgeVariant(status: string): 'default' | 'destructive' | 'outline' | 'secondary' {
|
||||
if (status === 'approved') {
|
||||
return 'default';
|
||||
}
|
||||
|
||||
if (status === 'rejected') {
|
||||
return 'destructive';
|
||||
}
|
||||
|
||||
return 'outline';
|
||||
}
|
||||
|
||||
export function createColumns(
|
||||
onView: (item: OwnerVerificationItem) => void,
|
||||
): ColumnDef<OwnerVerificationItem>[] {
|
||||
return [
|
||||
{
|
||||
accessorKey: 'created_at_formatted',
|
||||
enableSorting: true,
|
||||
header: () => h(DataTableColumnHeader, { title: 'Waktu', column: 'created_at' }),
|
||||
cell: ({ row }) => row.original.created_at_formatted ?? '-',
|
||||
},
|
||||
{
|
||||
accessorKey: 'submitted_by_name',
|
||||
enableSorting: false,
|
||||
header: 'Diajukan Oleh',
|
||||
},
|
||||
{
|
||||
accessorKey: 'action_label',
|
||||
enableSorting: true,
|
||||
header: () => h(DataTableColumnHeader, { title: 'Aksi', column: 'action' }),
|
||||
},
|
||||
{
|
||||
accessorKey: 'subject_label',
|
||||
enableSorting: false,
|
||||
header: 'Modul',
|
||||
},
|
||||
{
|
||||
accessorKey: 'title',
|
||||
enableSorting: false,
|
||||
header: 'Item',
|
||||
},
|
||||
{
|
||||
accessorKey: 'status_label',
|
||||
enableSorting: true,
|
||||
header: () => h(DataTableColumnHeader, { title: 'Status', column: 'status' }),
|
||||
cell: ({ row }) => h(
|
||||
Badge,
|
||||
{ variant: statusBadgeVariant(row.original.status) },
|
||||
() => row.original.status_label,
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
cell: ({ row }) => h(DataTableActions, {
|
||||
item: row.original,
|
||||
onView: () => onView(row.original),
|
||||
}),
|
||||
},
|
||||
];
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { RowDetailAction } from '@/components/button';
|
||||
import OwnerVerificationRowActions from '@/components/owner-verification/OwnerVerificationRowActions.vue';
|
||||
import type { OwnerVerificationItem } from '@/types/owner-verification';
|
||||
|
||||
const props = defineProps<{
|
||||
item: OwnerVerificationItem;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
view: [];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-center justify-end gap-1">
|
||||
<RowDetailAction tooltip="Lihat" @click="emit('view')" />
|
||||
|
||||
<OwnerVerificationRowActions v-if="item.is_pending" :type="item.source === 'cutting' ? 'cutting' : 'request'"
|
||||
:id="item.id" :title="item.title" :subject-label="item.subject_label" :action-label="item.action_label" />
|
||||
</div>
|
||||
</template>
|
||||
@ -1,51 +0,0 @@
|
||||
export type OwnerVerificationSource = 'cutting' | 'request';
|
||||
|
||||
export interface VerificationChange {
|
||||
field: string;
|
||||
old: unknown;
|
||||
new: unknown;
|
||||
}
|
||||
|
||||
export interface OwnerVerificationItem {
|
||||
id: number;
|
||||
source: OwnerVerificationSource;
|
||||
subject_type: string | null;
|
||||
subject_label: string;
|
||||
subject_id: number | null;
|
||||
action: string;
|
||||
action_label: string;
|
||||
status: string;
|
||||
status_label: string;
|
||||
title: string;
|
||||
summary?: string;
|
||||
submitted_by_name: string;
|
||||
verified_by_name?: string | null;
|
||||
verified_at_formatted?: string | null;
|
||||
created_at?: string | null;
|
||||
created_at_formatted: string | null;
|
||||
changes: VerificationChange[];
|
||||
rejection_reason?: string | null;
|
||||
is_pending: boolean;
|
||||
detail?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/** @deprecated Use OwnerVerificationItem */
|
||||
export type OwnerVerificationRequestItem = OwnerVerificationItem;
|
||||
|
||||
export interface SelectOption {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface PaginatedOwnerVerificationRequests {
|
||||
data: OwnerVerificationItem[];
|
||||
current_page: number;
|
||||
per_page: number;
|
||||
last_page: number;
|
||||
total: number;
|
||||
links: Array<{
|
||||
url: string | null;
|
||||
label: string;
|
||||
active: boolean;
|
||||
}>;
|
||||
}
|
||||
@ -356,10 +356,7 @@
|
||||
});
|
||||
|
||||
Route::prefix('owner-verifications')->name('owner_verifications.')
|
||||
->middleware('permission:'.Permission::OWNER_VERIFICATIONS_VIEW->value)
|
||||
->group(function () {
|
||||
Route::get('/', [OwnerVerificationController::class, 'index'])->name('index');
|
||||
|
||||
Route::post('cuttings/{cutting}/approve', [OwnerVerificationController::class, 'approveCutting'])
|
||||
->middleware('permission:'.Permission::OWNER_VERIFICATIONS_VERIFY->value)
|
||||
->name('approve');
|
||||
|
||||
@ -1,354 +0,0 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\CuttingStatus;
|
||||
use App\Enums\OwnerVerificationAction;
|
||||
use App\Enums\OwnerVerificationStatus;
|
||||
use App\Enums\Permission as PermissionEnum;
|
||||
use App\Enums\Role as RoleEnum;
|
||||
use App\Models\Cutting;
|
||||
use App\Models\OwnerVerificationRequest;
|
||||
use App\Models\Product;
|
||||
use App\Models\Purchase;
|
||||
use App\Models\User;
|
||||
use App\Support\ActivityLog\ModelLabel;
|
||||
use Database\Seeders\RolePermissionSeeder;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
$this->seed(RolePermissionSeeder::class);
|
||||
});
|
||||
|
||||
function createOwnerVerificationRequestFor(User $submitter, ?Product $product = null): OwnerVerificationRequest
|
||||
{
|
||||
$product ??= Product::factory()->create(['is_active' => false]);
|
||||
|
||||
return OwnerVerificationRequest::query()->create([
|
||||
'action' => OwnerVerificationAction::CREATE,
|
||||
'status' => OwnerVerificationStatus::PENDING,
|
||||
'subject_type' => Product::class,
|
||||
'subject_id' => $product->id,
|
||||
'submitted_by_id' => $submitter->id,
|
||||
'payload' => ['old' => null, 'new' => ['name' => $product->name]],
|
||||
]);
|
||||
}
|
||||
|
||||
function createOwnerVerifierUser(): User
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$user->assignRole(RoleEnum::OWNER->value);
|
||||
$user->forgetCachedPermissions();
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
function createPendingCutting(?User $submitter = null): Cutting
|
||||
{
|
||||
$submitter ??= User::factory()->create();
|
||||
|
||||
return Cutting::factory()->create([
|
||||
'status' => CuttingStatus::PENDING_VERIFICATION,
|
||||
'submitted_by_id' => $submitter->id,
|
||||
'created_by_id' => $submitter->id,
|
||||
'description' => 'Cutting test pending',
|
||||
]);
|
||||
}
|
||||
|
||||
describe('Owner Verification Index', function () {
|
||||
test('submitter with view permission only sees own verification requests', function () {
|
||||
$submitter = User::factory()->create();
|
||||
$submitter->assignRole(RoleEnum::ADMIN_TOKO->value);
|
||||
$submitter->forgetCachedPermissions();
|
||||
|
||||
$otherUser = User::factory()->create();
|
||||
$otherUser->assignRole(RoleEnum::ADMIN_TOKO->value);
|
||||
$otherUser->forgetCachedPermissions();
|
||||
|
||||
$ownRequest = createOwnerVerificationRequestFor($submitter);
|
||||
createOwnerVerificationRequestFor($otherUser);
|
||||
|
||||
$response = $this->actingAs($submitter)
|
||||
->get(route('admin.manage.owner_verifications.index'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('admin/manage/owner-verifications/Index')
|
||||
->has('verificationRequests.data', 1)
|
||||
->where('verificationRequests.data.0.id', $ownRequest->id)
|
||||
->where('verificationRequests.data.0.source', 'request')
|
||||
);
|
||||
});
|
||||
|
||||
test('verifier sees all verification requests', function () {
|
||||
$submitter = User::factory()->create();
|
||||
$submitter->assignRole(RoleEnum::ADMIN_TOKO->value);
|
||||
$submitter->forgetCachedPermissions();
|
||||
|
||||
$verifier = createOwnerVerifierUser();
|
||||
|
||||
createOwnerVerificationRequestFor($submitter);
|
||||
createOwnerVerificationRequestFor($submitter);
|
||||
|
||||
$response = $this->actingAs($verifier)
|
||||
->get(route('admin.manage.owner_verifications.index'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('admin/manage/owner-verifications/Index')
|
||||
->has('verificationRequests.data', 2)
|
||||
);
|
||||
});
|
||||
|
||||
test('submitter cannot approve verification request', function () {
|
||||
$submitter = User::factory()->create();
|
||||
$submitter->assignRole(RoleEnum::ADMIN_TOKO->value);
|
||||
$submitter->forgetCachedPermissions();
|
||||
|
||||
$request = createOwnerVerificationRequestFor($submitter);
|
||||
|
||||
$this->actingAs($submitter)
|
||||
->post(route('admin.manage.owner_verifications.approve_request', $request))
|
||||
->assertForbidden();
|
||||
});
|
||||
|
||||
test('admin toko role has owner verification view permission', function () {
|
||||
$user = User::factory()->create();
|
||||
$user->assignRole(RoleEnum::ADMIN_TOKO->value);
|
||||
$user->forgetCachedPermissions();
|
||||
|
||||
expect($user->can(PermissionEnum::OWNER_VERIFICATIONS_VIEW->value))->toBeTrue();
|
||||
expect($user->can(PermissionEnum::OWNER_VERIFICATIONS_VERIFY->value))->toBeFalse();
|
||||
});
|
||||
|
||||
test('admin bahan baku role has owner verification view permission', function () {
|
||||
$user = User::factory()->create();
|
||||
$user->assignRole(RoleEnum::ADMIN_BAHAN_BAKU->value);
|
||||
$user->forgetCachedPermissions();
|
||||
|
||||
expect($user->can(PermissionEnum::OWNER_VERIFICATIONS_VIEW->value))->toBeTrue();
|
||||
expect($user->can(PermissionEnum::OWNER_VERIFICATIONS_VERIFY->value))->toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Owner Verification Unified Cutting List', function () {
|
||||
test('verifier sees pending cuttings in the same verification table', function () {
|
||||
$verifier = createOwnerVerifierUser();
|
||||
$cutting = createPendingCutting();
|
||||
$request = createOwnerVerificationRequestFor(User::factory()->create());
|
||||
|
||||
$response = $this->actingAs($verifier)
|
||||
->get(route('admin.manage.owner_verifications.index'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('admin/manage/owner-verifications/Index')
|
||||
->has('verificationRequests.data', 2)
|
||||
->where('verificationRequests.data.0.source', 'cutting')
|
||||
->where('verificationRequests.data.0.id', $cutting->id)
|
||||
->where('verificationRequests.data.0.action', OwnerVerificationAction::STOCK_VERIFY->value)
|
||||
->where('verificationRequests.data.0.subject_type', Cutting::class)
|
||||
->where('verificationRequests.data.1.source', 'request')
|
||||
->where('verificationRequests.data.1.id', $request->id)
|
||||
);
|
||||
});
|
||||
|
||||
test('submitter does not see pending cuttings in verification table', function () {
|
||||
$submitter = User::factory()->create();
|
||||
$submitter->assignRole(RoleEnum::ADMIN_TOKO->value);
|
||||
$submitter->forgetCachedPermissions();
|
||||
|
||||
createPendingCutting();
|
||||
createOwnerVerificationRequestFor($submitter);
|
||||
|
||||
$response = $this->actingAs($submitter)
|
||||
->get(route('admin.manage.owner_verifications.index'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->component('admin/manage/owner-verifications/Index')
|
||||
->has('verificationRequests.data', 1)
|
||||
->where('verificationRequests.data.0.source', 'request')
|
||||
);
|
||||
});
|
||||
|
||||
test('filtering by cutting module shows only pending cuttings', function () {
|
||||
$verifier = createOwnerVerifierUser();
|
||||
$cutting = createPendingCutting();
|
||||
createOwnerVerificationRequestFor(User::factory()->create());
|
||||
|
||||
$response = $this->actingAs($verifier)
|
||||
->get(route('admin.manage.owner_verifications.index', [
|
||||
'subject_type' => Cutting::class,
|
||||
]));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->has('verificationRequests.data', 1)
|
||||
->where('verificationRequests.data.0.source', 'cutting')
|
||||
->where('verificationRequests.data.0.id', $cutting->id)
|
||||
);
|
||||
});
|
||||
|
||||
test('filtering by stock verify action shows only pending cuttings', function () {
|
||||
$verifier = createOwnerVerifierUser();
|
||||
$cutting = createPendingCutting();
|
||||
createOwnerVerificationRequestFor(User::factory()->create());
|
||||
|
||||
$response = $this->actingAs($verifier)
|
||||
->get(route('admin.manage.owner_verifications.index', [
|
||||
'action' => OwnerVerificationAction::STOCK_VERIFY->value,
|
||||
]));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->has('verificationRequests.data', 1)
|
||||
->where('verificationRequests.data.0.source', 'cutting')
|
||||
->where('verificationRequests.data.0.id', $cutting->id)
|
||||
);
|
||||
});
|
||||
|
||||
test('filtering by approved status hides pending cuttings', function () {
|
||||
$verifier = createOwnerVerifierUser();
|
||||
createPendingCutting();
|
||||
|
||||
OwnerVerificationRequest::query()->create([
|
||||
'action' => OwnerVerificationAction::CREATE,
|
||||
'status' => OwnerVerificationStatus::APPROVED,
|
||||
'subject_type' => Product::class,
|
||||
'subject_id' => Product::factory()->create()->id,
|
||||
'submitted_by_id' => User::factory()->create()->id,
|
||||
'payload' => ['old' => null, 'new' => ['name' => 'Approved Product']],
|
||||
'verified_by_id' => $verifier->id,
|
||||
'verified_at' => now(),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($verifier)
|
||||
->get(route('admin.manage.owner_verifications.index', [
|
||||
'status' => OwnerVerificationStatus::APPROVED->value,
|
||||
]));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->has('verificationRequests.data', 1)
|
||||
->where('verificationRequests.data.0.source', 'request')
|
||||
->where('verificationRequests.data.0.status', OwnerVerificationStatus::APPROVED->value)
|
||||
);
|
||||
});
|
||||
|
||||
test('subject options include cutting when pending cuttings exist', function () {
|
||||
$verifier = createOwnerVerifierUser();
|
||||
createPendingCutting();
|
||||
|
||||
$response = $this->actingAs($verifier)
|
||||
->get(route('admin.manage.owner_verifications.index'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->where('subjectOptions', fn ($options) => collect($options)->contains(
|
||||
fn (array $option) => $option['value'] === Cutting::class
|
||||
&& $option['label'] === ModelLabel::for(Cutting::class),
|
||||
))
|
||||
);
|
||||
});
|
||||
|
||||
test('verifier can approve pending cutting from verification page', function () {
|
||||
$verifier = createOwnerVerifierUser();
|
||||
$cutting = createPendingCutting();
|
||||
|
||||
$this->actingAs($verifier)
|
||||
->post(route('admin.manage.owner_verifications.approve', $cutting))
|
||||
->assertRedirect(route('admin.manage.owner_verifications.index'));
|
||||
|
||||
expect($cutting->fresh()->status)->toBe(CuttingStatus::VERIFIED);
|
||||
});
|
||||
|
||||
test('verifier can reject pending cutting from verification page', function () {
|
||||
$verifier = createOwnerVerifierUser();
|
||||
$cutting = createPendingCutting();
|
||||
|
||||
$this->actingAs($verifier)
|
||||
->post(route('admin.manage.owner_verifications.reject', $cutting), [
|
||||
'reason' => 'Data tidak sesuai',
|
||||
])
|
||||
->assertRedirect(route('admin.manage.owner_verifications.index'));
|
||||
|
||||
$cutting->refresh();
|
||||
|
||||
expect($cutting->status)->toBe(CuttingStatus::COMPLETED);
|
||||
expect($cutting->rejection?->reason)->toBe('Data tidak sesuai');
|
||||
expect($cutting->rejection?->rejected_by_id)->toBe($verifier->id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Owner Verification Purchase Module', function () {
|
||||
test('verifier sees purchase verification requests in index', function () {
|
||||
$verifier = createOwnerVerifierUser();
|
||||
$submitter = User::factory()->create();
|
||||
$purchase = Purchase::factory()->create();
|
||||
|
||||
OwnerVerificationRequest::query()->create([
|
||||
'action' => OwnerVerificationAction::CREATE,
|
||||
'status' => OwnerVerificationStatus::PENDING,
|
||||
'subject_type' => Purchase::class,
|
||||
'subject_id' => $purchase->id,
|
||||
'submitted_by_id' => $submitter->id,
|
||||
'payload' => [
|
||||
'old' => null,
|
||||
'new' => ['supplier_name' => $purchase->supplier?->name ?? 'Supplier'],
|
||||
],
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($verifier)
|
||||
->get(route('admin.manage.owner_verifications.index', [
|
||||
'subject_type' => Purchase::class,
|
||||
]));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->has('verificationRequests.data', 1)
|
||||
->where('verificationRequests.data.0.source', 'request')
|
||||
->where('verificationRequests.data.0.subject_type', Purchase::class)
|
||||
->where('verificationRequests.data.0.subject_id', $purchase->id)
|
||||
);
|
||||
});
|
||||
|
||||
test('purchase submitter sees only own purchase verification requests', function () {
|
||||
$submitter = User::factory()->create();
|
||||
$submitter->assignRole(RoleEnum::ADMIN_BAHAN_BAKU->value);
|
||||
$submitter->forgetCachedPermissions();
|
||||
|
||||
$otherUser = User::factory()->create();
|
||||
$ownPurchase = Purchase::factory()->create(['created_by_id' => $submitter->id]);
|
||||
$otherPurchase = Purchase::factory()->create(['created_by_id' => $otherUser->id]);
|
||||
|
||||
OwnerVerificationRequest::query()->create([
|
||||
'action' => OwnerVerificationAction::CREATE,
|
||||
'status' => OwnerVerificationStatus::PENDING,
|
||||
'subject_type' => Purchase::class,
|
||||
'subject_id' => $ownPurchase->id,
|
||||
'submitted_by_id' => $submitter->id,
|
||||
'payload' => ['old' => null, 'new' => ['supplier_name' => 'Own']],
|
||||
]);
|
||||
|
||||
OwnerVerificationRequest::query()->create([
|
||||
'action' => OwnerVerificationAction::CREATE,
|
||||
'status' => OwnerVerificationStatus::PENDING,
|
||||
'subject_type' => Purchase::class,
|
||||
'subject_id' => $otherPurchase->id,
|
||||
'submitted_by_id' => $otherUser->id,
|
||||
'payload' => ['old' => null, 'new' => ['supplier_name' => 'Other']],
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($submitter)
|
||||
->get(route('admin.manage.owner_verifications.index', [
|
||||
'subject_type' => Purchase::class,
|
||||
]));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertInertia(fn ($page) => $page
|
||||
->has('verificationRequests.data', 1)
|
||||
->where('verificationRequests.data.0.subject_id', $ownPurchase->id)
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -56,7 +56,7 @@ function approveLatestPurchaseVerificationRequest(User $verifier): OwnerVerifica
|
||||
|
||||
test()->actingAs($verifier)
|
||||
->post(route('admin.manage.owner_verifications.approve_request', $verificationRequest))
|
||||
->assertRedirect(route('admin.manage.owner_verifications.index'));
|
||||
->assertRedirect();
|
||||
|
||||
return $verificationRequest->fresh();
|
||||
}
|
||||
@ -263,7 +263,7 @@ function approveLatestPurchaseVerificationRequest(User $verifier): OwnerVerifica
|
||||
->post(route('admin.manage.owner_verifications.reject_request', $verificationRequest), [
|
||||
'reason' => 'Tidak sesuai',
|
||||
])
|
||||
->assertRedirect(route('admin.manage.owner_verifications.index'));
|
||||
->assertRedirect();
|
||||
|
||||
expect(Purchase::query()->whereKey($purchase->id)->exists())->toBeFalse();
|
||||
expect(PurchaseItem::query()->where('purchase_id', $purchase->id)->exists())->toBeFalse();
|
||||
|
||||
@ -76,7 +76,7 @@ function approveLatestOwnerVerificationRequest(User $verifier): OwnerVerificatio
|
||||
|
||||
test()->actingAs($verifier)
|
||||
->post(route('admin.manage.owner_verifications.approve_request', $verificationRequest))
|
||||
->assertRedirect(route('admin.manage.owner_verifications.index'));
|
||||
->assertRedirect();
|
||||
|
||||
return $verificationRequest->fresh();
|
||||
}
|
||||
@ -431,7 +431,7 @@ function variantWithImage(string $name = 'All Size', int $stock = 10): array
|
||||
->post(route('admin.manage.owner_verifications.reject_request', $verificationRequest), [
|
||||
'reason' => 'Tidak sesuai standar',
|
||||
])
|
||||
->assertRedirect(route('admin.manage.owner_verifications.index'));
|
||||
->assertRedirect();
|
||||
|
||||
$this->assertSoftDeleted('products', ['id' => $product->id]);
|
||||
});
|
||||
|
||||
@ -84,7 +84,7 @@ function approveLatestOwnerVerificationRequest(User $verifier): OwnerVerificatio
|
||||
|
||||
test()->actingAs($verifier)
|
||||
->post(route('admin.manage.owner_verifications.approve_request', $verificationRequest))
|
||||
->assertRedirect(route('admin.manage.owner_verifications.index'));
|
||||
->assertRedirect();
|
||||
|
||||
return $verificationRequest->fresh();
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user