store/resources/js/pages/admin/manage/stock/History.vue
Yoga Pangestu 3052398ad8 feat: add stock mutation tracking and history feature
- Introduced StockMutation model and service to handle stock changes.
- Created migration for stock_mutations table.
- Implemented stock mutation recording in various services (CuttingService, OrderService, PurchaseService, RestockService, RetailStockService).
- Added StockHistoryController to manage stock history views.
- Developed frontend components for displaying stock history and actions.
- Updated routes to include stock history access with appropriate permissions.
- Enhanced ProductVariant and RawMaterialPrice models to support stock mutations.
- Added RowHistoryAction button for accessing stock history in product and raw material tables.
2026-07-27 22:45:49 +07:00

130 lines
4.3 KiB
Vue

<script setup lang="ts">
import { Head } from '@inertiajs/vue3';
import type { ColumnDef } from '@tanstack/vue-table';
import { h } from 'vue';
import BackButton from '@/components/button/BackButton.vue';
import DataTable from '@/components/data-table/DataTable.vue';
import { Badge } from '@/components/ui/badge';
import { Card, CardContent } from '@/components/ui/card';
import AdminLayout from '@/layouts/AdminLayout.vue';
import { index as productsIndex } from '@/routes/admin/master/products';
import { index as rawMaterialsIndex } from '@/routes/admin/master/raw_materials';
import type { PaginatedStockMutations, StockMutation } from '@/types/stock-mutation';
const props = defineProps<{
title: string;
stockableType: string;
stockableId: number;
mutations: PaginatedStockMutations;
}>();
const backUrl = props.stockableType === 'product-variant'
? productsIndex.url()
: rawMaterialsIndex.url();
const pagination = {
currentPage: props.mutations.current_page,
perPage: props.mutations.per_page,
lastPage: props.mutations.last_page,
total: props.mutations.total,
};
const typeBadgeVariant = (type: string): string => {
const map: Record<string, string> = {
in: 'success',
out: 'destructive',
transfer: 'info',
adjustment: 'warning',
};
return map[type] ?? 'secondary';
};
const columns: ColumnDef<StockMutation>[] = [
{
accessorKey: 'created_at_formatted',
header: 'Tanggal',
cell: ({ row }) => row.original.created_at_formatted ?? '-',
},
{
accessorKey: 'type_label',
header: 'Tipe',
cell: ({ row }) => h(Badge, {
variant: typeBadgeVariant(row.original.type),
}, () => row.original.type_label),
},
{
accessorKey: 'description',
header: 'Keterangan',
cell: ({ row }) => {
const mutation = row.original;
const parts = [mutation.description ?? '-'];
if (mutation.stock_quality) {
const qualityLabels: Record<string, string> = {
good: 'Stok Bagus',
reject: 'Stok Reject',
retail: 'Stok Ecer',
};
parts.push(qualityLabels[mutation.stock_quality] ?? mutation.stock_quality);
}
return parts.join(' — ');
},
},
{
accessorKey: 'quantity',
header: 'Qty',
cell: ({ row }) => {
const qty = Number(row.original.quantity);
const prefix = qty >= 0 ? '+' : '';
const cls = qty > 0 ? 'text-green-600 font-medium tabular-nums'
: qty < 0 ? 'text-red-600 font-medium tabular-nums'
: 'tabular-nums';
return h('span', { class: cls }, `${prefix}${qty.toLocaleString('id-ID')}`);
},
},
{
accessorKey: 'stock_before',
header: 'Stok Sebelum',
cell: ({ row }) => h('span', { class: 'tabular-nums' }, Number(row.original.stock_before).toLocaleString('id-ID')),
},
{
accessorKey: 'stock_after',
header: 'Stok Sesudah',
cell: ({ row }) => h('span', { class: 'tabular-nums' }, Number(row.original.stock_after).toLocaleString('id-ID')),
},
{
accessorKey: 'user.profile.full_name',
id: 'user',
header: 'User',
cell: ({ row }) => row.original.user?.profile?.full_name ?? 'Sistem',
},
];
</script>
<template>
<Head :title="`Riwayat Stok - ${title}`" />
<AdminLayout>
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div class="space-y-1">
<h2 class="text-2xl font-bold tracking-tight">Riwayat Stok</h2>
<p class="text-sm text-muted-foreground">{{ title }}</p>
</div>
<BackButton :href="backUrl" />
</div>
<Card class="min-w-0">
<CardContent class="min-w-0">
<DataTable
:columns="columns"
:data="mutations.data"
:pagination="pagination"
:pagination-links="mutations.links"
:show-row-number="true"
pagination-item-label="mutasi"
/>
</CardContent>
</Card>
</AdminLayout>
</template>