feat: add stock adjustment functionality for warehouse items via a modal form.
This commit is contained in:
parent
5b8f89f693
commit
6477684540
@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Forms\Studio\Master\Warehouse;
|
||||||
|
|
||||||
|
use App\Models\Warehouse;
|
||||||
|
use App\Services\StockActivityLogService;
|
||||||
|
use Livewire\Form;
|
||||||
|
|
||||||
|
class AdjustmentStockForm extends Form
|
||||||
|
{
|
||||||
|
public string $itemId = '';
|
||||||
|
|
||||||
|
public string $type = '';
|
||||||
|
|
||||||
|
public string $name = '';
|
||||||
|
|
||||||
|
public string $oldStock = '0';
|
||||||
|
|
||||||
|
public string $newStock = '0';
|
||||||
|
|
||||||
|
public ?string $note = null;
|
||||||
|
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'newStock' => 'required',
|
||||||
|
'note' => 'nullable',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validationAttributes(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'newStock' => 'stok baru',
|
||||||
|
'note' => 'keterangan',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAdjustment(string $itemId, string $type, string $name, int $stock): void
|
||||||
|
{
|
||||||
|
$this->itemId = $itemId;
|
||||||
|
$this->type = $type;
|
||||||
|
$this->name = $name;
|
||||||
|
$this->oldStock = formatCurrencyNumber($stock);
|
||||||
|
$this->newStock = formatCurrencyNumber($stock);
|
||||||
|
$this->note = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function save(Warehouse $warehouse): void
|
||||||
|
{
|
||||||
|
$this->validate();
|
||||||
|
|
||||||
|
$newStockValue = (int) str_replace(['.', ','], '', (string) $this->newStock);
|
||||||
|
$oldStockValue = (int) str_replace(['.', ','], '', (string) $this->oldStock);
|
||||||
|
|
||||||
|
$typeId = explode('_', $this->itemId);
|
||||||
|
$type = $typeId[0];
|
||||||
|
$id = $typeId[1];
|
||||||
|
|
||||||
|
$relation = match ($type) {
|
||||||
|
'perfume' => 'perfumes',
|
||||||
|
'product' => 'products',
|
||||||
|
'bottle' => 'bottles',
|
||||||
|
default => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (! $relation) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = $warehouse->{$relation}()->find($id);
|
||||||
|
|
||||||
|
if (! $item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$warehouse->{$relation}()->updateExistingPivot($id, [
|
||||||
|
'stock' => $newStockValue,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Log the activity
|
||||||
|
StockActivityLogService::logWarehouse(
|
||||||
|
logName: 'adjustment',
|
||||||
|
event: 'update',
|
||||||
|
description: "Manual adjustment for {$this->name}: changed from {$oldStockValue} to {$newStockValue}. Note: ".($this->note ?? '-'),
|
||||||
|
performedOn: $item,
|
||||||
|
warehouse: $warehouse,
|
||||||
|
quantityChange: $newStockValue - $oldStockValue,
|
||||||
|
previousStock: $oldStockValue,
|
||||||
|
newStock: $newStockValue,
|
||||||
|
extraProperties: [
|
||||||
|
'note' => $this->note,
|
||||||
|
'adjustment_type' => 'manual',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,16 +2,26 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Studio\Master\Warehouse;
|
namespace App\Livewire\Studio\Master\Warehouse;
|
||||||
|
|
||||||
|
use App\Livewire\Forms\Studio\Master\Warehouse\AdjustmentStockForm;
|
||||||
use App\Models\Warehouse;
|
use App\Models\Warehouse;
|
||||||
use App\Traits\Authorization\WithAuthorization;
|
use App\Traits\Authorization\WithAuthorization;
|
||||||
|
use App\Traits\Components\WithCloseModal;
|
||||||
|
use App\Traits\Components\WithConfirmation;
|
||||||
|
use App\Traits\Components\WithToast;
|
||||||
|
use App\Traits\Notification\WithSubscribeNotification;
|
||||||
|
use App\Traits\Utilities\WithUpdatedData;
|
||||||
|
use Flux\Flux;
|
||||||
use Illuminate\Contracts\View\View;
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Livewire\Attributes\On;
|
||||||
use Livewire\Attributes\Title;
|
use Livewire\Attributes\Title;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
#[Title('Detail Gudang')]
|
#[Title('Detail Gudang')]
|
||||||
class Show extends Component
|
class Show extends Component
|
||||||
{
|
{
|
||||||
use WithAuthorization;
|
use WithAuthorization, WithCloseModal, WithConfirmation, WithSubscribeNotification, WithToast, WithUpdatedData;
|
||||||
|
|
||||||
|
public AdjustmentStockForm $form;
|
||||||
|
|
||||||
public Warehouse $warehouse;
|
public Warehouse $warehouse;
|
||||||
|
|
||||||
@ -19,6 +29,10 @@ class Show extends Component
|
|||||||
|
|
||||||
public array $type = [];
|
public array $type = [];
|
||||||
|
|
||||||
|
public string $method = 'saveAdjustment';
|
||||||
|
|
||||||
|
public string $modalTitle = '';
|
||||||
|
|
||||||
public function mount(Warehouse $warehouse): void
|
public function mount(Warehouse $warehouse): void
|
||||||
{
|
{
|
||||||
$this->warehouse = $warehouse;
|
$this->warehouse = $warehouse;
|
||||||
@ -121,6 +135,29 @@ public function getItemsProperty()
|
|||||||
return $perfumes->concat($products)->concat($bottles);
|
return $perfumes->concat($products)->concat($bottles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[On('modal:open')]
|
||||||
|
public function openModal(string $method, string $modalTitle, string $itemId, string $type, string $name, int $stock): void
|
||||||
|
{
|
||||||
|
$this->resetValidation();
|
||||||
|
$this->resetErrorBag();
|
||||||
|
|
||||||
|
$this->method = $method;
|
||||||
|
$this->modalTitle = $modalTitle;
|
||||||
|
|
||||||
|
$this->form->setAdjustment($itemId, $type, $name, $stock);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saveAdjustment(): void
|
||||||
|
{
|
||||||
|
$this->canOrAbort('manage adjustment');
|
||||||
|
|
||||||
|
$this->form->save($this->warehouse);
|
||||||
|
|
||||||
|
$this->toast('Stok berhasil diperbarui.');
|
||||||
|
|
||||||
|
Flux::modals()->close();
|
||||||
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
{
|
{
|
||||||
return view('livewire.studio.master.warehouse.show', [
|
return view('livewire.studio.master.warehouse.show', [
|
||||||
|
|||||||
@ -46,9 +46,9 @@
|
|||||||
<flux:table.column>Tipe</flux:table.column>
|
<flux:table.column>Tipe</flux:table.column>
|
||||||
<flux:table.column>Item</flux:table.column>
|
<flux:table.column>Item</flux:table.column>
|
||||||
<flux:table.column>Stok</flux:table.column>
|
<flux:table.column>Stok</flux:table.column>
|
||||||
@can('audit stock warehouse')
|
@canAny(['manage adjustment', 'audit stock warehouse'])
|
||||||
<flux:table.column>Aksi</flux:table.column>
|
<flux:table.column>Aksi</flux:table.column>
|
||||||
@endcan
|
@endcanAny
|
||||||
</flux:table.columns>
|
</flux:table.columns>
|
||||||
|
|
||||||
|
|
||||||
@ -57,12 +57,14 @@
|
|||||||
<flux:table.row :key="$item['id']">
|
<flux:table.row :key="$item['id']">
|
||||||
<flux:table.cell>
|
<flux:table.cell>
|
||||||
<flux:badge size="sm"
|
<flux:badge size="sm"
|
||||||
:color="$item['type'] === 'Parfum' ? 'fuchsia' : ($item['type'] === 'Produk' ? 'cyan' :
|
:color="$item['type'] === 'Parfum' ? 'fuchsia' : ($item['type'] === 'Produk' ?
|
||||||
|
'cyan' :
|
||||||
'zinc')">
|
'zinc')">
|
||||||
{{ $item['type'] }}</flux:badge>
|
{{ $item['type'] }}</flux:badge>
|
||||||
</flux:table.cell>
|
</flux:table.cell>
|
||||||
<flux:table.cell class="font-medium">{{ $item['name'] }}</flux:table.cell>
|
<flux:table.cell class="font-medium">{{ $item['name'] }}</flux:table.cell>
|
||||||
<flux:table.cell>{{ number_format($item['stock'], 0, ',', '.') }}</flux:table.cell>
|
<flux:table.cell>{{ number_format($item['stock'], 0, ',', '.') }}</flux:table.cell>
|
||||||
|
@canAny(['manage adjustment', 'audit stock warehouse'])
|
||||||
<flux:table.cell>
|
<flux:table.cell>
|
||||||
@can('audit stock warehouse')
|
@can('audit stock warehouse')
|
||||||
<flux:tooltip content="Audit">
|
<flux:tooltip content="Audit">
|
||||||
@ -72,7 +74,18 @@
|
|||||||
</flux:button>
|
</flux:button>
|
||||||
</flux:tooltip>
|
</flux:tooltip>
|
||||||
@endcan
|
@endcan
|
||||||
|
@can('manage adjustment')
|
||||||
|
<flux:modal.trigger name="form-modal">
|
||||||
|
<flux:tooltip content="Ubah">
|
||||||
|
<flux:button variant="primary" color="yellow" icon="pencil-square"
|
||||||
|
size="sm"
|
||||||
|
wire:click="$dispatch('modal:open', { method: 'saveAdjustment', modalTitle: 'Ubah Stok', itemId: '{{ $item['id'] }}', type: '{{ $item['type'] }}', name: '{{ $item['name'] }}', stock: {{ $item['stock'] }} })">
|
||||||
|
</flux:button>
|
||||||
|
</flux:tooltip>
|
||||||
|
</flux:modal.trigger>
|
||||||
|
@endcan
|
||||||
</flux:table.cell>
|
</flux:table.cell>
|
||||||
|
@endcanAny
|
||||||
</flux:table.row>
|
</flux:table.row>
|
||||||
@empty
|
@empty
|
||||||
<flux:table.row>
|
<flux:table.row>
|
||||||
@ -85,4 +98,41 @@
|
|||||||
</flux:table>
|
</flux:table>
|
||||||
</flux:card>
|
</flux:card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<flux:modal name="form-modal" class="w-[95%] max-w-sm md:max-w-xl mx-auto" @close="closeModal('form-modal')">
|
||||||
|
<div class="p-4 space-y-6">
|
||||||
|
<div>
|
||||||
|
<flux:heading size="lg">{{ $modalTitle }}</flux:heading>
|
||||||
|
<flux:subheading>Sesuaikan stok untuk {{ $form?->name ?? '-' }} ({{ $form?->type ?? '-' }})
|
||||||
|
</flux:subheading>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<flux:field>
|
||||||
|
<flux:label>Stok Saat Ini</flux:label>
|
||||||
|
<flux:input wire:model="form.oldStock" disabled />
|
||||||
|
</flux:field>
|
||||||
|
|
||||||
|
<flux:field>
|
||||||
|
<flux:label>Stok Baru <span class="text-red-500 ms-1">*</span></flux:label>
|
||||||
|
<flux:input placeholder="Masukkan stok" x-mask:dynamic="$money($input, ',')"
|
||||||
|
wire:model="form.newStock" autocomplete="off" />
|
||||||
|
<flux:error name="form.newStock" />
|
||||||
|
</flux:field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<flux:field>
|
||||||
|
<flux:label>Keterangan</flux:label>
|
||||||
|
<flux:textarea placeholder="Contoh: Koreksi stok fisik, Rusak, dsb." wire:model="form.note" />
|
||||||
|
<flux:error name="form.note" />
|
||||||
|
</flux:field>
|
||||||
|
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<flux:spacer />
|
||||||
|
<flux:button variant="primary" wire:click="{{ $method }}">
|
||||||
|
Simpan
|
||||||
|
</flux:button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</flux:modal>
|
||||||
</flux:main>
|
</flux:main>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user