diff --git a/app/Livewire/Forms/Studio/Manage/PurchaseForm.php b/app/Livewire/Forms/Studio/Manage/PurchaseForm.php index 0b1ed38..9d6ec00 100644 --- a/app/Livewire/Forms/Studio/Manage/PurchaseForm.php +++ b/app/Livewire/Forms/Studio/Manage/PurchaseForm.php @@ -87,14 +87,30 @@ public function store(): void $purchaseItems = $this->loadPurchaseItems(); - DB::transaction(function () use ($purchaseItems) { - $purchase = Purchase::create($this->prepareSavedData()); + // Recalculate total to ensure data integrity + $calculatedTotal = $purchaseItems->sum(function ($item) { + return $item->unit_price * $item->quantity; + }); + + // Override the total input with the calculated one to prevent mismatches + $this->total = formatCurrencyNumber($calculatedTotal); + + DB::transaction(function () use ($purchaseItems, $calculatedTotal) { + $data = $this->prepareSavedData(); + $data['total'] = $calculatedTotal; // Use strict calculated total + + $purchase = Purchase::create($data); $purchase->load('outlet'); $this->uploadMedia($this->image, $purchase, 'image'); foreach ($purchaseItems as $item) { + // Ensure item integrity before linking + if ($item->quantity <= 0 || $item->unit_price <= 0) { + continue; // Or throw exception + } + $item->update(['purchase_id' => $purchase->id]); $this->increaseOutletStock($purchase->outlet, $item); @@ -106,8 +122,15 @@ public function update(): void { $this->validate(); - DB::transaction(function () { - $this->purchase->update($this->prepareSavedData()); + // Ensure total calculation integrity from existing items + $calculatedTotal = $this->purchase->items()->sum(DB::raw('unit_price * quantity')); + $this->total = formatCurrencyNumber($calculatedTotal); + + DB::transaction(function () use ($calculatedTotal) { + $data = $this->prepareSavedData(); + $data['total'] = $calculatedTotal; // Enforce calculated total + + $this->purchase->update($data); $this->syncMedia($this->image, $this->purchase, 'image'); $this->uploadMedia($this->image, $this->purchase, 'image'); diff --git a/app/Livewire/Studio/Manage/Purchase/Create.php b/app/Livewire/Studio/Manage/Purchase/Create.php index b2bb6b5..e78a155 100644 --- a/app/Livewire/Studio/Manage/Purchase/Create.php +++ b/app/Livewire/Studio/Manage/Purchase/Create.php @@ -40,11 +40,11 @@ public function mount(): void { $this->outlets = auth()->user()->outlets->pluck('name', 'id')->toArray(); - $this->perfumes = Perfume::orderBy('name')->pluck('name', 'id')->toArray(); + $this->perfumes = Perfume::whereHas('outlets', fn ($query) => $query->where('outlets.id', $this->form->outlet_id))->orderBy('name')->pluck('name', 'id')->toArray(); - $this->bottles = Bottle::orderBy('size')->pluck('name', 'id')->toArray(); + $this->bottles = Bottle::whereHas('outlets', fn ($query) => $query->where('outlets.id', $this->form->outlet_id))->orderBy('size')->pluck('name', 'id')->toArray(); - $this->products = Product::orderBy('name')->pluck('name', 'id')->toArray(); + $this->products = Product::whereHas('outlets', fn ($query) => $query->where('outlets.id', $this->form->outlet_id))->orderBy('name')->pluck('name', 'id')->toArray(); $this->purchaseItems = $this->loadPurchaseItems(); @@ -70,6 +70,13 @@ public function save(): void $this->redirectRoute('studio.manage.purchase.index'); } + public function updatedFormOutletId($value): void + { + $this->perfumes = Perfume::whereHas('outlets', fn ($query) => $query->where('outlets.id', $value))->orderBy('name')->pluck('name', 'id')->toArray(); + $this->bottles = Bottle::whereHas('outlets', fn ($query) => $query->where('outlets.id', $value))->orderBy('size')->pluck('name', 'id')->toArray(); + $this->products = Product::whereHas('outlets', fn ($query) => $query->where('outlets.id', $value))->orderBy('name')->pluck('name', 'id')->toArray(); + } + public function render(): View { return view('livewire.studio.manage.purchase.form', [ diff --git a/app/Livewire/Studio/Manage/Purchase/Index.php b/app/Livewire/Studio/Manage/Purchase/Index.php index ad9c8f4..6659d78 100644 --- a/app/Livewire/Studio/Manage/Purchase/Index.php +++ b/app/Livewire/Studio/Manage/Purchase/Index.php @@ -11,6 +11,7 @@ use App\Traits\Purchase\WithUpdateStock; use Flux\Flux; use Illuminate\Contracts\View\View; +use Illuminate\Support\Facades\DB; use Livewire\Attributes\Title; use Livewire\Component; @@ -23,11 +24,13 @@ public function delete(Purchase $purchase): void { $this->canOrAbort('delete purchase'); - foreach ($purchase->items as $item) { - $this->decreaseOutletStock($purchase->outlet, $item); - } + DB::transaction(function () use ($purchase) { + foreach ($purchase->items as $item) { + $this->decreaseOutletStock($purchase->outlet, $item); + } - $purchase->delete(); + $purchase->delete(); + }); $this->dispatch('refreshDatatable'); diff --git a/app/Traits/Purchase/WithAddItem.php b/app/Traits/Purchase/WithAddItem.php index 5fc1d31..fba535f 100644 --- a/app/Traits/Purchase/WithAddItem.php +++ b/app/Traits/Purchase/WithAddItem.php @@ -84,7 +84,7 @@ public function addItem(string $type): void 'total_price' => $costPrice * $quantity, ]); - $this->purchaseItems->push($item); + $this->purchaseItems->prepend($item); $this->toast(Str::ucfirst($type).' ditambahkan ke keranjang.', 'Berhasil'); } diff --git a/app/Traits/Purchase/WithUpdateItem.php b/app/Traits/Purchase/WithUpdateItem.php index 231276b..91e4b4c 100644 --- a/app/Traits/Purchase/WithUpdateItem.php +++ b/app/Traits/Purchase/WithUpdateItem.php @@ -27,11 +27,25 @@ public function closeModal() public function updateItem(): void { + $quantity = parseRupiahToInt($this->form->quantity_edit); + + if ($quantity <= 0) { + $this->toast('Jumlah item tidak valid.', 'Gagal', 'danger'); + + return; + } + $item = PurchaseItem::find($this->form->item_id); + if (! $item) { + $this->toast('Item tidak found.', 'Gagal', 'danger'); + + return; + } + $item->update([ - 'quantity' => parseRupiahToInt($this->form->quantity_edit), - 'total_price' => (int) $item->purchasable->cost_price * (int) parseRupiahToInt($this->form->quantity_edit), + 'quantity' => $quantity, + 'total_price' => (int) $item->purchasable->cost_price * $quantity, ]); $item->refresh(); diff --git a/app/Traits/Purchase/WithUpdateStock.php b/app/Traits/Purchase/WithUpdateStock.php index 307a599..56ab602 100644 --- a/app/Traits/Purchase/WithUpdateStock.php +++ b/app/Traits/Purchase/WithUpdateStock.php @@ -27,7 +27,12 @@ public function increaseOutletStock($outlet, $item): void return; } - $existing = $outlet->{$relation}()->withPivot('stock')->where("{$relation}.id", $item->purchasable_id)->first(); + // Lock the row to prevent race conditions + $existing = $outlet->{$relation}() + ->where("{$relation}.id", $item->purchasable_id) + ->lockForUpdate() + ->withPivot('stock') + ->first(); if ($existing) { $previousStock = $existing->pivot->stock ?? 0; @@ -76,7 +81,11 @@ public function decreaseOutletStock($outlet, $item): void return; } - $existing = $outlet->{$relation}()->withPivot('stock')->where("{$relation}.id", $item->purchasable_id)->first(); + $existing = $outlet->{$relation}() + ->where("{$relation}.id", $item->purchasable_id) + ->lockForUpdate() + ->withPivot('stock') + ->first(); if ($existing) { $currentStock = $existing->pivot->stock ?? 0; @@ -84,6 +93,11 @@ public function decreaseOutletStock($outlet, $item): void $outlet->{$relation}()->updateExistingPivot($item->purchasable_id, [ 'stock' => $newStock, ]); + } else { + // Handle case where stock doesn't exist but we are decreasing? + // Should not happen in normal flow, but good to handle. + $currentStock = 0; + $newStock = 0; } StockActivityLogService::log( diff --git a/resources/views/livewire/studio/manage/order/form.blade.php b/resources/views/livewire/studio/manage/order/form.blade.php index 7d5bc78..708ea40 100644 --- a/resources/views/livewire/studio/manage/order/form.blade.php +++ b/resources/views/livewire/studio/manage/order/form.blade.php @@ -15,7 +15,7 @@
Outlet * - + @foreach ($outlets as $key => $value) {{ $value }} diff --git a/resources/views/livewire/studio/manage/purchase/form.blade.php b/resources/views/livewire/studio/manage/purchase/form.blade.php index b2e5050..9a77f46 100644 --- a/resources/views/livewire/studio/manage/purchase/form.blade.php +++ b/resources/views/livewire/studio/manage/purchase/form.blade.php @@ -50,7 +50,7 @@ Outlet * + wire:model.live="form.outlet_id"> @foreach ($outlets as $key => $value) {{ $value }}