diff --git a/app/Livewire/Forms/Studio/Catalog/BottleForm.php b/app/Livewire/Forms/Studio/Catalog/BottleForm.php index ab9c856..ecd5ac6 100644 --- a/app/Livewire/Forms/Studio/Catalog/BottleForm.php +++ b/app/Livewire/Forms/Studio/Catalog/BottleForm.php @@ -6,6 +6,7 @@ use App\Models\Outlet; use App\Rules\UnsignedInteger; use App\Services\StockActivityLogService; +use App\Traits\Forms\WithFormStockAdjustment; use App\Traits\Media\WithMediaHandler; use Illuminate\Support\Facades\DB; use Illuminate\Validation\Rule; @@ -13,7 +14,7 @@ class BottleForm extends Form { - use WithMediaHandler; + use WithFormStockAdjustment, WithMediaHandler; public ?Bottle $bottle = null; @@ -70,6 +71,7 @@ public function setBottle(Bottle $bottle): void $this->description = $bottle->description; $this->outlet_ids = $this->bottle->outlets->pluck('id')->toArray(); $this->image = $this->mapMediaCollection($bottle->getMedia('image')); + $this->loadStockAdjustments($bottle); } public function store(): void @@ -129,6 +131,8 @@ public function update(): void } } + $this->saveStockAdjustments($this->bottle, 'bottle'); + $this->syncMedia($this->image, $this->bottle, 'image'); $this->uploadMedia($this->image, $this->bottle, 'image'); }); diff --git a/app/Livewire/Forms/Studio/Catalog/PerfumeForm.php b/app/Livewire/Forms/Studio/Catalog/PerfumeForm.php index c1b4537..69bf159 100644 --- a/app/Livewire/Forms/Studio/Catalog/PerfumeForm.php +++ b/app/Livewire/Forms/Studio/Catalog/PerfumeForm.php @@ -7,6 +7,7 @@ use App\Models\Perfume; use App\Rules\UnsignedInteger; use App\Services\StockActivityLogService; +use App\Traits\Forms\WithFormStockAdjustment; use App\Traits\Media\WithMediaHandler; use Illuminate\Support\Facades\DB; use Illuminate\Validation\Rule; @@ -14,7 +15,7 @@ class PerfumeForm extends Form { - use WithMediaHandler; + use WithFormStockAdjustment, WithMediaHandler; public ?Perfume $perfume = null; @@ -96,6 +97,7 @@ public function setPerfume(Perfume $perfume): void $this->category_ids = $perfume->categories->pluck('id')->toArray(); $this->outlet_ids = $this->perfume->outlets->pluck('id')->toArray(); $this->image = $this->mapMediaCollection($perfume->getMedia('image')); + $this->loadStockAdjustments($perfume); } public function store(): void @@ -163,6 +165,8 @@ public function update(): void } } + $this->saveStockAdjustments($this->perfume, 'perfume'); + $this->syncMedia($data['image'], $this->perfume, 'image'); $this->uploadMedia($data['image'], $this->perfume, 'image'); }); diff --git a/app/Livewire/Forms/Studio/Catalog/ProductForm.php b/app/Livewire/Forms/Studio/Catalog/ProductForm.php index 91ad1d5..458ca33 100644 --- a/app/Livewire/Forms/Studio/Catalog/ProductForm.php +++ b/app/Livewire/Forms/Studio/Catalog/ProductForm.php @@ -6,6 +6,7 @@ use App\Models\Product; use App\Rules\UnsignedInteger; use App\Services\StockActivityLogService; +use App\Traits\Forms\WithFormStockAdjustment; use App\Traits\Media\WithMediaHandler; use Illuminate\Support\Facades\DB; use Illuminate\Validation\Rule; @@ -13,7 +14,7 @@ class ProductForm extends Form { - use WithMediaHandler; + use WithFormStockAdjustment, WithMediaHandler; public ?Product $product = null; @@ -67,6 +68,7 @@ public function setProduct(Product $product): void $this->description = $product->description; $this->outlet_ids = $this->product->outlets->pluck('id')->toArray(); $this->image = $this->mapMediaCollection($product->getMedia('image')); + $this->loadStockAdjustments($product); } public function store(): void @@ -130,6 +132,8 @@ public function update(): void } } + $this->saveStockAdjustments($this->product, 'product'); + $this->syncMedia($data['image'], $this->product, 'image'); $this->uploadMedia($data['image'], $this->product, 'image'); }); diff --git a/app/Traits/Forms/WithFormStockAdjustment.php b/app/Traits/Forms/WithFormStockAdjustment.php new file mode 100644 index 0000000..238bf57 --- /dev/null +++ b/app/Traits/Forms/WithFormStockAdjustment.php @@ -0,0 +1,75 @@ +stock_adjustments = [ + 'outlet' => [], + ]; + + $user = auth()->user(); + $userOutletIds = $user->outlets->pluck('id')->toArray(); + + $itemOutlets = $item->outlets() + ->whereIn('outlets.id', $userOutletIds) + ->withPivot('stock') + ->get(); + + foreach ($itemOutlets as $outlet) { + $stock = $outlet->pivot->stock ?? 0; + $this->stock_adjustments['outlet'][$outlet->id] = [ + 'name' => $outlet->name, + 'stock' => $stock, + 'new_stock' => (string) $stock, + 'note' => '', + ]; + } + } + + protected function saveStockAdjustments(Model $item, string $itemType): void + { + $itemName = $item->name.($itemType === 'bottle' ? " ({$item->size}ml)" : ''); + + if (isset($this->stock_adjustments['outlet'])) { + foreach ($this->stock_adjustments['outlet'] as $id => $adj) { + $newStock = (int) str_replace(['.', ','], '', (string) ($adj['new_stock'] ?: '0')); + $oldStock = (int) $adj['stock']; + + if ($newStock === $oldStock) { + continue; + } + + $outlet = Outlet::find($id); + if ($outlet) { + $item->outlets()->syncWithoutDetaching([$id => ['stock' => $newStock]]); + + StockActivityLogService::logOutlet( + logName: 'adjustment', + event: 'update', + description: "Penyesuaian manual untuk {$itemName}: berubah dari ".formatCurrencyNumber($oldStock).' menjadi '.formatCurrencyNumber($newStock).'. Catatan: '.($adj['note'] ?: '-'), + performedOn: $item, + outlet: $outlet, + quantityChange: $newStock - $oldStock, + previousStock: $oldStock, + newStock: $newStock, + extraProperties: [ + 'note' => $adj['note'], + 'adjustment_type' => 'manual', + 'item_type' => $itemType, + 'item_id' => $item->id, + ] + ); + } + } + } + } +} diff --git a/resources/views/components/forms/stock-management.blade.php b/resources/views/components/forms/stock-management.blade.php new file mode 100644 index 0000000..dc6b8ce --- /dev/null +++ b/resources/views/components/forms/stock-management.blade.php @@ -0,0 +1,59 @@ +@props(['form']) + +
+
+ + Manajemen Stok Outlet +
+ + @if (count($form->stock_adjustments['outlet'] ?? []) > 0) + +
+
+ Outlet Penjualan +
+ +
+ @foreach ($form->stock_adjustments['outlet'] as $id => $adj) +
+
+
+ {{ $adj['name'] }} +
+ Stok: + {{ number_format($adj['stock']) }} +
+ +
+ + Penyesuaian Stok Baru + + + + + + + + + + Catatan Perubahan + + +
+
+ @if (!$loop->last) + + @endif + @endforeach +
+
+ @else + + Tidak ada outlet yang terhubung atau Anda tidak memiliki akses ke outlet manapun. + + @endif +
diff --git a/resources/views/livewire/studio/catalog/bottle/form.blade.php b/resources/views/livewire/studio/catalog/bottle/form.blade.php index d6353ef..2e2a05b 100644 --- a/resources/views/livewire/studio/catalog/bottle/form.blade.php +++ b/resources/views/livewire/studio/catalog/bottle/form.blade.php @@ -63,6 +63,10 @@ class="col-span-{{ auth()->user()->hasRole(['Developer', 'Owner'])? '1': '2' }}" + + @if ($form->bottle?->exists) + + @endif diff --git a/resources/views/livewire/studio/catalog/perfume/form.blade.php b/resources/views/livewire/studio/catalog/perfume/form.blade.php index bb003ab..10d4a2d 100644 --- a/resources/views/livewire/studio/catalog/perfume/form.blade.php +++ b/resources/views/livewire/studio/catalog/perfume/form.blade.php @@ -87,6 +87,10 @@ class="grid grid-cols-1 md:grid-cols-{{ auth()->user()->hasRole(['Developer', 'O + + @if ($form->perfume?->exists) + + @endif diff --git a/resources/views/livewire/studio/catalog/product/form.blade.php b/resources/views/livewire/studio/catalog/product/form.blade.php index e60d7c5..b13a78a 100644 --- a/resources/views/livewire/studio/catalog/product/form.blade.php +++ b/resources/views/livewire/studio/catalog/product/form.blade.php @@ -58,6 +58,10 @@ class="col-span-{{ auth()->user()->hasRole(['Developer', 'Owner'])? '1': '2' }}" + + @if ($form->product?->exists) + + @endif