feat: enhance cutting management by adding total result pieces and material usage display in Cutting components and updating related services

This commit is contained in:
Yoga Pangestu 2026-06-20 10:47:21 +07:00
parent 3cdabfae30
commit 9df8fe1ce2
6 changed files with 63 additions and 5 deletions

View File

@ -92,6 +92,8 @@ public function permissions(): array
self::ADMIN_TOKO => [
Permission::DASHBOARD_VIEW,
Permission::CUTTINGS_VERIFY,
Permission::ATTENDANCES_VIEW,
Permission::ATTENDANCES_CREATE,
Permission::ATTENDANCES_DELETE,

View File

@ -98,6 +98,7 @@ public function getInProgressCuttings(User $user): Collection
$cutting->setAttribute('available_actions', $actions);
$cutting->setAttribute('is_editable', $cutting->status->isEditable());
$this->appendCostPreview($cutting);
});
}
@ -678,6 +679,9 @@ private function applySorting(Builder $query, string $sort, string $direction):
private function appendCostPreview(Cutting $cutting): void
{
$cutting->setAttribute('total_result_pieces', (int) $cutting->results->sum('cutting_result'));
$cutting->setAttribute('total_material_usage', (float) $cutting->materials->sum('material_usage'));
if (! in_array($cutting->status, [CuttingStatus::COMPLETED, CuttingStatus::VERIFIED], true)) {
return;
}
@ -698,7 +702,6 @@ private function appendCostPreview(Cutting $cutting): void
$cutting->setAttribute('total_production_cost_formatted', 'Rp '.number_format($totalProductionCost, 0, ',', '.'));
$cutting->setAttribute('estimated_cost_per_unit', $costPerUnit);
$cutting->setAttribute('estimated_cost_per_unit_formatted', 'Rp '.number_format($costPerUnit, 0, ',', '.'));
$cutting->setAttribute('total_result_pieces', (int) $cutting->results->sum('cutting_result'));
}
public function calculateTotalMaterialCost(Cutting $cutting): int

View File

@ -93,6 +93,21 @@ const totalCompleted = computed(() => props.cuttings.length);
</ul>
</div>
<div v-if="cutting.results.length" class="space-y-1.5 pt-2 border-t">
<div class="flex items-center justify-between text-[11px]">
<span class="text-muted-foreground">Total Hasil Cutting:</span>
<span class="font-semibold tabular-nums">{{ cutting.total_result_pieces ?? 0 }} pcs</span>
</div>
<div class="flex items-center justify-between text-[11px]">
<span class="text-muted-foreground">Total Pemakaian Bahan:</span>
<span class="font-semibold tabular-nums">{{ cutting.total_material_usage ?? 0 }}</span>
</div>
<div v-if="cutting.estimated_cost_per_unit_formatted" class="flex items-center justify-between text-[11px]">
<span class="text-muted-foreground">Harga Modal:</span>
<span class="font-semibold">{{ cutting.estimated_cost_per_unit_formatted }}/pcs</span>
</div>
</div>
<div class="text-[11px] text-muted-foreground pt-2 border-t flex items-center justify-between">
<span>Pembuat:</span>
<span class="font-medium text-foreground">

View File

@ -132,6 +132,11 @@ function getGroupedResults(results: any[]): GroupedCuttingResults[] {
return Object.values(groups);
}
function getMaterialUnit(materials: any[]): string | undefined {
return materials[0]?.raw_material_price?.raw_material?.unit_abbreviation;
}
</script>
<template>
@ -165,12 +170,24 @@ function getGroupedResults(results: any[]): GroupedCuttingResults[] {
<p v-if="cutting.rejection?.reason" class="text-destructive text-sm">
Alasan ditolak: {{ cutting.rejection.reason }}
</p>
<div class="flex flex-wrap gap-x-4 gap-y-1 text-sm">
<span>Total Hasil Cutting <strong class="text-primary">{{ cutting.total_result_pieces ??
0 }} pcs</strong></span>
<span>Total Pemakaian Bahan <strong class="text-primary">{{ cutting.total_material_usage
??
0 }} {{ getMaterialUnit(cutting.materials) }}</strong></span>
<span>Total Biaya Produksi <strong class="text-primary">{{
cutting.total_production_cost_formatted ?? 0 }}</strong></span>
<span>Harga Modal <strong class="text-primary">{{
cutting.estimated_cost_per_unit_formatted ??
0 }}</strong></span>
</div>
</div>
</div>
<div class="flex shrink-0 items-center justify-end gap-2 sm:pt-0.5">
<DataTableActions :cutting="cutting" />
</div>
</div>
</div>
<div class="space-y-6 p-4">
@ -191,11 +208,13 @@ function getGroupedResults(results: any[]): GroupedCuttingResults[] {
Belum ada bahan baku
</TableCell>
</TableRow>
<template v-else v-for="group in getGroupedMaterials(cutting.materials)" :key="group.rawMaterialId">
<template v-else v-for="group in getGroupedMaterials(cutting.materials)"
:key="group.rawMaterialId">
<TableRow class="bg-muted/20 hover:bg-muted/20">
<TableCell colspan="3" class="font-semibold text-foreground">
{{ group.rawMaterialName }}
<Badge v-if="group.unitLabel" variant="secondary" class="ml-2 font-normal">
<Badge v-if="group.unitLabel" variant="secondary"
class="ml-2 font-normal">
{{ group.unitLabel }}
</Badge>
</TableCell>
@ -241,7 +260,8 @@ function getGroupedResults(results: any[]): GroupedCuttingResults[] {
Belum ada hasil produk
</TableCell>
</TableRow>
<template v-else v-for="group in getGroupedResults(cutting.results)" :key="group.productId">
<template v-else v-for="group in getGroupedResults(cutting.results)"
:key="group.productId">
<TableRow class="bg-muted/20 hover:bg-muted/20">
<TableCell colspan="4" class="font-semibold text-foreground">
{{ group.productName }}

View File

@ -93,6 +93,17 @@ const totalInProgress = computed(() => props.cuttings.length);
</ul>
</div>
<div v-if="cutting.results.length" class="space-y-1.5 pt-2 border-t">
<div class="flex items-center justify-between text-[11px]">
<span class="text-muted-foreground">Total Hasil Cutting:</span>
<span class="font-semibold tabular-nums">{{ cutting.total_result_pieces ?? 0 }} pcs</span>
</div>
<div class="flex items-center justify-between text-[11px]">
<span class="text-muted-foreground">Total Pemakaian Bahan:</span>
<span class="font-semibold tabular-nums">{{ cutting.total_material_usage ?? 0 }}</span>
</div>
</div>
<div class="text-[11px] text-muted-foreground pt-2 border-t flex items-center justify-between">
<span>Pembuat:</span>
<span class="font-medium text-foreground">

View File

@ -14,12 +14,18 @@ export type CuttingMaterialListItem = {
id: number;
material_usage_formatted: string;
remaining_material_formatted: string;
uses_length_unit?: boolean;
material_usage_cm_formatted?: string;
remaining_material_cm_formatted?: string;
raw_material_price?: {
id: number;
variant: string;
raw_material?: {
id: number;
name: string;
unit?: string;
unit_label?: string;
unit_abbreviation?: string;
};
};
};
@ -56,6 +62,7 @@ export type CuttingListItem = {
total_production_cost?: number;
total_production_cost_formatted?: string;
total_result_pieces?: number;
total_material_usage?: number;
estimated_cost_per_unit?: number;
estimated_cost_per_unit_formatted?: string;
created_by?: {