diff --git a/app/Services/Admin/Manage/CuttingService.php b/app/Services/Admin/Manage/CuttingService.php index 397ac34..c4b0799 100644 --- a/app/Services/Admin/Manage/CuttingService.php +++ b/app/Services/Admin/Manage/CuttingService.php @@ -254,12 +254,15 @@ public function getForEdit(Cutting $cutting): array public function store(array $data): Cutting { $cutting = DB::transaction(function () use ($data) { + $priceIds = collect($data['materials'])->pluck('raw_material_price_id')->unique()->values(); + $pricesMap = RawMaterialPrice::whereIn('id', $priceIds)->get()->keyBy('id'); + foreach ($data['materials'] as $materialData) { $usage = (int) ($materialData['material_usage'] ?? 0); if ($usage <= 0) { continue; } - $price = RawMaterialPrice::find($materialData['raw_material_price_id']); + $price = $pricesMap[$materialData['raw_material_price_id']] ?? null; if (! $price) { continue; } @@ -293,8 +296,10 @@ public function store(array $data): Cutting $combinationMap[$index] = $combination->id; } + $stockDecrementMap = []; + foreach ($data['materials'] as $materialData) { - $price = RawMaterialPrice::find($materialData['raw_material_price_id']); + $price = $pricesMap[$materialData['raw_material_price_id']] ?? null; $materialCost = $price ? $price->price * ($materialData['material_usage'] ?? 0) : 0; $totalMaterialCost += $materialCost; @@ -314,11 +319,16 @@ public function store(array $data): Cutting 'updated_at' => $now, ]); - if ($price && ($materialData['material_usage'] ?? 0) > 0) { - $price->decrement('stock', (int) $materialData['material_usage']); + $usage = (int) ($materialData['material_usage'] ?? 0); + if ($price && $usage > 0) { + $stockDecrementMap[$materialData['raw_material_price_id']] = ($stockDecrementMap[$materialData['raw_material_price_id']] ?? 0) + $usage; } } + foreach ($stockDecrementMap as $priceId => $totalUsage) { + RawMaterialPrice::where('id', $priceId)->decrement('stock', $totalUsage); + } + $costPerUnit = 0; if (($data['cutting_result'] ?? 0) > 0) { $costPerUnit = (int) ($totalMaterialCost / $data['cutting_result']); @@ -364,20 +374,29 @@ public function store(array $data): Cutting public function update(Cutting $cutting, array $data): Cutting { $cutting = DB::transaction(function () use ($cutting, $data) { - $cutting->load(['cuttingMaterials.rawMaterialPrice']); + $oldMaterialUsages = $cutting->cuttingMaterials() + ->select('raw_material_price_id', 'material_usage') + ->where('material_usage', '>', 0) + ->get() + ->filter(fn ($m) => $m->raw_material_price_id) + ->groupBy('raw_material_price_id') + ->map(fn ($group) => $group->sum('material_usage')); - foreach ($cutting->cuttingMaterials as $oldMaterial) { - if ($oldMaterial->material_usage > 0 && $oldMaterial->rawMaterialPrice) { - $oldMaterial->rawMaterialPrice->increment('stock', (int) $oldMaterial->material_usage); + if ($oldMaterialUsages->isNotEmpty()) { + foreach ($oldMaterialUsages as $priceId => $totalUsage) { + RawMaterialPrice::where('id', $priceId)->increment('stock', $totalUsage); } } + $priceIds = collect($data['materials'])->pluck('raw_material_price_id')->unique()->values(); + $pricesMap = RawMaterialPrice::whereIn('id', $priceIds)->get()->keyBy('id'); + foreach ($data['materials'] as $materialData) { $usage = (int) ($materialData['material_usage'] ?? 0); if ($usage <= 0) { continue; } - $price = RawMaterialPrice::find($materialData['raw_material_price_id']); + $price = $pricesMap[$materialData['raw_material_price_id']] ?? null; if (! $price) { continue; } @@ -388,8 +407,6 @@ public function update(Cutting $cutting, array $data): Cutting } } - $cutting->load(['cuttingMaterials', 'cuttingMaterialCombinations', 'cuttingResults']); - $cutting->cuttingResults()->delete(); $cutting->cuttingMaterials()->delete(); $cutting->cuttingMaterialCombinations()->delete(); @@ -411,8 +428,10 @@ public function update(Cutting $cutting, array $data): Cutting $combinationMap[$index] = $combination->id; } + $stockDecrementMap = []; + foreach ($data['materials'] as $materialData) { - $price = RawMaterialPrice::find($materialData['raw_material_price_id']); + $price = $pricesMap[$materialData['raw_material_price_id']] ?? null; $materialCost = $price ? $price->price * ($materialData['material_usage'] ?? 0) : 0; $totalMaterialCost += $materialCost; @@ -432,11 +451,16 @@ public function update(Cutting $cutting, array $data): Cutting 'updated_at' => $now, ]); - if ($price && ($materialData['material_usage'] ?? 0) > 0) { - $price->decrement('stock', (int) $materialData['material_usage']); + $usage = (int) ($materialData['material_usage'] ?? 0); + if ($price && $usage > 0) { + $stockDecrementMap[$materialData['raw_material_price_id']] = ($stockDecrementMap[$materialData['raw_material_price_id']] ?? 0) + $usage; } } + foreach ($stockDecrementMap as $priceId => $totalUsage) { + RawMaterialPrice::where('id', $priceId)->decrement('stock', $totalUsage); + } + $costPerUnit = 0; if (($data['cutting_result'] ?? 0) > 0) { $costPerUnit = (int) ($totalMaterialCost / $data['cutting_result']); @@ -477,12 +501,15 @@ public function update(Cutting $cutting, array $data): Cutting public function destroy(Cutting $cutting): bool { $result = DB::transaction(function () use ($cutting) { - $cutting->load('cuttingMaterials.rawMaterialPrice'); + $stockRestoreMap = $cutting->cuttingMaterials() + ->select('raw_material_price_id', 'material_usage') + ->where('material_usage', '>', 0) + ->get() + ->groupBy('raw_material_price_id') + ->map(fn ($group) => $group->sum('material_usage')); - foreach ($cutting->cuttingMaterials as $material) { - if ($material->material_usage > 0 && $material->rawMaterialPrice) { - $material->rawMaterialPrice->increment('stock', (int) $material->material_usage); - } + foreach ($stockRestoreMap as $priceId => $totalUsage) { + RawMaterialPrice::where('id', $priceId)->increment('stock', $totalUsage); } $cutting->cuttingResults()->delete(); diff --git a/app/Services/Admin/Manage/PurchaseService.php b/app/Services/Admin/Manage/PurchaseService.php index d13d691..2f8a64c 100644 --- a/app/Services/Admin/Manage/PurchaseService.php +++ b/app/Services/Admin/Manage/PurchaseService.php @@ -94,15 +94,7 @@ public function getForCreate(): array 'rawMaterialPrices:id,raw_material_id,variant,price,stock', ]) ->orderBy('name') - ->get() - ->each(function (RawMaterial $rawMaterial) { - $rawMaterial->rawMaterialPrices->each(function (RawMaterialPrice $price) { - $media = $price->getFirstMedia('images'); - $price->photo_url = $media - ? $this->s3Service->getTemporaryUrl($media->getPath()) - : null; - }); - }), + ->get(), ]; } @@ -226,9 +218,12 @@ private function storeFromExisting(array $data): Purchase } DB::table('purchase_items')->insert($itemRows); - foreach ($data['existing_items'] as $item) { - RawMaterialPrice::whereKey($item['raw_material_price_id']) - ->increment('stock', (int) $item['quantity']); + $stockIncrementMap = collect($data['existing_items']) + ->groupBy('raw_material_price_id') + ->map(fn ($group) => $group->sum('quantity')); + + foreach ($stockIncrementMap as $priceId => $totalQty) { + RawMaterialPrice::where('id', $priceId)->increment('stock', $totalQty); } if (! empty($data['photo_keys']) && is_array($data['photo_keys'])) { @@ -277,19 +272,23 @@ private function storeNew(array $data): Purchase DB::table('raw_material_prices')->insert($priceRows); - $insertedPrices = RawMaterialPrice::where('raw_material_id', $rawMaterial->id)->get(); - $variantMap = $insertedPrices->mapWithKeys(fn ($p) => [$p->variant => $p->id]); + $priceIdMap = DB::table('raw_material_prices') + ->where('raw_material_id', $rawMaterial->id) + ->pluck('id', 'variant') + ->toArray(); foreach ($data['variants'] as $variantData) { if (! empty($variantData['photo_key'])) { - $priceId = $variantMap[$variantData['variant']]; - $priceModel = RawMaterialPrice::find($priceId); - $this->registerMedia( - model: $priceModel, - s3Key: $variantData['photo_key'], - collectionName: 'images', - orderColumn: 1, - ); + $priceId = $priceIdMap[$variantData['variant']] ?? null; + if ($priceId) { + $priceModel = RawMaterialPrice::find($priceId); + $this->registerMedia( + model: $priceModel, + s3Key: $variantData['photo_key'], + collectionName: 'images', + orderColumn: 1, + ); + } } } @@ -307,16 +306,20 @@ private function storeNew(array $data): Purchase 'notes' => $data['notes'] ?? null, ]); - $purchaseItems = $insertedPrices->map(fn ($price) => [ - 'purchase_id' => $purchase->id, - 'raw_material_price_id' => $price->id, - 'user_id' => auth()->id(), - 'quantity' => $price->stock, - 'unit_price' => $price->price, - 'subtotal' => (int) ($price->price * $price->stock), - 'created_at' => $now, - 'updated_at' => $now, - ])->toArray(); + $purchaseItems = collect($data['variants'])->map(function ($v) use ($purchase, $priceIdMap, $now) { + $priceId = $priceIdMap[$v['variant']] ?? null; + + return [ + 'purchase_id' => $purchase->id, + 'raw_material_price_id' => $priceId, + 'user_id' => auth()->id(), + 'quantity' => $v['stock'], + 'unit_price' => $v['price'], + 'subtotal' => (int) ($v['price'] * $v['stock']), + 'created_at' => $now, + 'updated_at' => $now, + ]; + })->toArray(); DB::table('purchase_items')->insert($purchaseItems); @@ -342,31 +345,28 @@ private function storeNew(array $data): Purchase public function update(Purchase $purchase, array $data): Purchase { $purchase = DB::transaction(function () use ($purchase, $data) { - $purchase->load('purchaseItems.rawMaterialPrice.rawMaterial'); + $oldStockMap = $purchase->purchaseItems() + ->select('raw_material_price_id', 'quantity') + ->get() + ->groupBy('raw_material_price_id') + ->map(fn ($group) => $group->sum('quantity')); - $oldItems = $purchase->purchaseItems; + foreach ($oldStockMap as $priceId => $totalQty) { + RawMaterialPrice::where('id', $priceId)->decrement('stock', $totalQty); + } - // 1. Reverse the stock increments of the old items, so prices - // get adjusted by the difference instead of being reset. - $oldItems->each(function (PurchaseItem $item) { - if ($item->rawMaterialPrice) { - $item->rawMaterialPrice->decrement('stock', (int) $item->quantity); - } - }); - - $oldMaterial = $oldItems - ->map(fn (PurchaseItem $item) => $item->rawMaterialPrice?->rawMaterial) - ->filter() - ->unique(fn (RawMaterial $material) => $material->id) + $oldMaterial = $purchase->purchaseItems() + ->join('raw_material_prices', 'raw_material_prices.id', '=', 'purchase_items.raw_material_price_id') + ->join('raw_materials', 'raw_materials.id', '=', 'raw_material_prices.raw_material_id') + ->select('raw_materials.*') ->first(); - $oldItems->each->delete(); + $purchase->purchaseItems()->delete(); $now = now(); $subtotal = 0; if (($data['mode'] ?? 'new') === 'existing') { - // 2a. Reference existing prices and add their new stock. $itemRows = collect($data['existing_items'])->map(function ($item) use ($now, &$subtotal) { $itemSubtotal = (int) ($item['unit_price'] * $item['quantity']); $subtotal += $itemSubtotal; @@ -383,14 +383,14 @@ public function update(Purchase $purchase, array $data): Purchase ]; })->toArray(); - foreach ($data['existing_items'] as $item) { - RawMaterialPrice::whereKey($item['raw_material_price_id']) - ->increment('stock', (int) $item['quantity']); + $stockIncrementMap = collect($data['existing_items']) + ->groupBy('raw_material_price_id') + ->map(fn ($group) => $group->sum('quantity')); + + foreach ($stockIncrementMap as $priceId => $totalQty) { + RawMaterialPrice::where('id', $priceId)->increment('stock', $totalQty); } } else { - // 2a. Always reuse the purchase's existing material in place; - // a fresh material is only created when the purchase has - // no items yet. if ($oldMaterial) { $rawMaterial = $oldMaterial; $rawMaterial->update([ @@ -405,8 +405,6 @@ public function update(Purchase $purchase, array $data): Purchase ]); } - // 2b. Adjust the stock of existing variant prices, create - // prices for new variants, but never delete variants. $itemRows = collect($data['variants'])->map(function ($v) use ($purchase, $rawMaterial, $now, &$subtotal) { $price = null; @@ -493,14 +491,15 @@ public function update(Purchase $purchase, array $data): Purchase public function destroy(Purchase $purchase): bool { $result = DB::transaction(function () use ($purchase) { - $purchase->load('purchaseItems.rawMaterialPrice'); + $stockDecrementMap = $purchase->purchaseItems() + ->select('raw_material_price_id', 'quantity') + ->get() + ->groupBy('raw_material_price_id') + ->map(fn ($group) => $group->sum('quantity')); - // Remove the stock the purchase added, keep the variants. - $purchase->purchaseItems->each(function (PurchaseItem $item) { - if ($item->rawMaterialPrice) { - $item->rawMaterialPrice->decrement('stock', (int) $item->quantity); - } - }); + foreach ($stockDecrementMap as $priceId => $totalQty) { + RawMaterialPrice::where('id', $priceId)->decrement('stock', $totalQty); + } $purchase->purchaseItems()->delete(); $purchase->delete(); diff --git a/app/Services/Admin/Master/RawMaterial/RawMaterialVariantService.php b/app/Services/Admin/Master/RawMaterial/RawMaterialVariantService.php index 7268f97..c1f25f9 100644 --- a/app/Services/Admin/Master/RawMaterial/RawMaterialVariantService.php +++ b/app/Services/Admin/Master/RawMaterial/RawMaterialVariantService.php @@ -28,14 +28,6 @@ public function getForCutting(): array ->active() ->orderBy('name') ->get() - ->each(function (RawMaterial $rawMaterial) { - $rawMaterial->rawMaterialPrices->each(function (RawMaterialPrice $price) { - $media = $price->getFirstMedia('images'); - $price->photo_url = $media - ? $this->s3Service->getTemporaryUrl($media->getPath()) - : null; - }); - }) ->toArray(); }