From 5a5d2c6f78cba1eef7dba4a2515051f721a7fde6 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Sun, 5 Jul 2026 13:27:52 +0700 Subject: [PATCH] feat: enhance cutting materials display in stock management components; implement grouping logic for combinations and improve UI for material usage and results presentation --- app/Services/Manage/StockService.php | 17 +++ .../cuttings/table/CuttingVerifyDialog.vue | 22 +++ .../table/StockPendingApprovalSection.vue | 127 ++++++++++++++++- .../stocks/table/StockPendingSection.vue | 132 +++++++++++++++++- .../manage/stocks/table/StockVerifyDialog.vue | 21 +++ 5 files changed, 316 insertions(+), 3 deletions(-) diff --git a/app/Services/Manage/StockService.php b/app/Services/Manage/StockService.php index 2525593..35577f6 100644 --- a/app/Services/Manage/StockService.php +++ b/app/Services/Manage/StockService.php @@ -31,6 +31,7 @@ public function getPendingVerificationCuttings(User $user): Collection 'rejection.rejectedBy.profile', 'materials.rawMaterialPrice.rawMaterial:id,name,unit', 'materials.rawMaterialPrice.media', + 'materials.combination', 'results.productVariant.product:id,name', 'results.productVariant.media', ]) @@ -53,6 +54,7 @@ public function getPendingApprovalCuttings(User $user): Collection 'rejection.rejectedBy.profile', 'materials.rawMaterialPrice.rawMaterial:id,name,unit', 'materials.rawMaterialPrice.media', + 'materials.combination', 'results.productVariant.product:id,name', 'results.productVariant.media', 'resultPrices.productVariant:id,product_id,name', @@ -236,19 +238,34 @@ private function breakMaterialCircularReference(CuttingMaterial $material): void { $price = $material->rawMaterialPrice; + // Always set these attributes regardless of price + $material->setAttribute('material_result', $material->material_result); + $material->setAttribute('material_result_input', $material->material_result); + $material->setAttribute('combination_id', $material->combination_id); + $material->setAttribute('combination_material_result', $material->combination?->material_result); + if ($price) { $rawMaterial = $price->rawMaterial; + $material->setAttribute('variant', $price->variant); + $material->setAttribute('stock_input', $price->stock_input); + $material->setAttribute('images', $price->getAttribute('images') ?? []); + if ($rawMaterial) { $unitAbbreviation = $rawMaterial->unit->abbreviation(); $price->setAttribute('unit_abbreviation', $unitAbbreviation); $material->setAttribute('unit_abbreviation', $unitAbbreviation); + $material->setAttribute('unit', $rawMaterial->unit->value); + $material->setAttribute('raw_material_id', $rawMaterial->id); + $material->setAttribute('raw_material_name', $rawMaterial->name); + $material->setAttribute('raw_material_unit_label', $rawMaterial->unit->label()); } $price->unsetRelation('rawMaterial'); } $material->unsetRelation('rawMaterialPrice'); + $material->unsetRelation('combination'); } private function applyProductStockOnVerify(Cutting $cutting): void diff --git a/resources/js/pages/admin/manage/cuttings/table/CuttingVerifyDialog.vue b/resources/js/pages/admin/manage/cuttings/table/CuttingVerifyDialog.vue index 6fbeb21..f412358 100644 --- a/resources/js/pages/admin/manage/cuttings/table/CuttingVerifyDialog.vue +++ b/resources/js/pages/admin/manage/cuttings/table/CuttingVerifyDialog.vue @@ -3,6 +3,7 @@ import { useForm } from '@inertiajs/vue3'; import { watch } from 'vue'; import { toast } from 'vue-sonner'; import { RupiahInput } from '@/components/form/rupiah-input'; +import MediaThumbnailCell from '@/components/media/MediaThumbnailCell.vue'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { @@ -194,6 +195,27 @@ function submitVerify() { +
+

Bahan Baku Terpakai

+
+
+
+ +
+
+

+ {{ material.raw_material_name || material.raw_material_price?.raw_material?.name }} - {{ material.variant || material.raw_material_price?.variant }} +

+

+ Pemakaian: {{ material.material_usage_formatted }} + Kombinasi +

+
+
+
+
+ diff --git a/resources/js/pages/admin/manage/stocks/table/StockPendingApprovalSection.vue b/resources/js/pages/admin/manage/stocks/table/StockPendingApprovalSection.vue index bedb0e1..a1b3038 100644 --- a/resources/js/pages/admin/manage/stocks/table/StockPendingApprovalSection.vue +++ b/resources/js/pages/admin/manage/stocks/table/StockPendingApprovalSection.vue @@ -12,9 +12,73 @@ import { } from '@/components/ui/table'; import type { CuttingListItem } from '@/types/cutting'; +interface GroupedCuttingMaterials { + rawMaterialId: number; + rawMaterialName: string; + unitLabel?: string; + items: any[]; + isCombination?: boolean; +} + defineProps<{ cuttings: CuttingListItem[]; }>(); + +function getGroupedMaterials(materials: any[]): GroupedCuttingMaterials[] { + const combinationGroups: Record = {}; + const nonCombinationItems: any[] = []; + + materials.forEach((item) => { + if (item.combination_id) { + if (!combinationGroups[item.combination_id]) { + combinationGroups[item.combination_id] = []; + } + + combinationGroups[item.combination_id].push(item); + } else { + nonCombinationItems.push(item); + } + }); + + const result: GroupedCuttingMaterials[] = []; + + Object.entries(combinationGroups).forEach(([combinationId, items]) => { + const firstItem = items[0]; + const rawMaterialName = firstItem.raw_material_name || 'Bahan Baku Tidak Diketahui'; + const unitLabel = firstItem.raw_material_unit_label; + + result.push({ + rawMaterialId: Number(combinationId) * -1, + rawMaterialName: `Kombinasi`, + unitLabel, + items, + isCombination: true, + }); + }); + + const groups: Record = {}; + + nonCombinationItems.forEach((item) => { + const rawMaterialId = item.raw_material_id ?? 0; + const rawMaterialName = item.raw_material_name || 'Bahan Baku Tidak Diketahui'; + const unitLabel = item.raw_material_unit_label; + + if (!groups[rawMaterialId]) { + groups[rawMaterialId] = { + rawMaterialId, + rawMaterialName, + unitLabel, + items: [], + }; + } + + groups[rawMaterialId].items.push(item); + }); + + result.push(...Object.values(groups)); + + return result; +}