dstpabuaran.com/app/Services/StockMutationService.php

267 lines
9.8 KiB
PHP

<?php
namespace App\Services;
use App\Models\Product;
use App\Models\ProductVariant;
use App\Models\StockMutation;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class StockMutationService
{
private const QUALITY_MAP = [
'stock' => 'good',
'reject_stock' => 'reject',
'retail_stock' => 'retail',
];
public function paginated(Model $model, int $perPage = 20): LengthAwarePaginator
{
return StockMutation::query()
->where('stockable_type', get_class($model))
->where('stockable_id', $model->id)
->with('user:id,username,email')
->latest()
->paginate($perPage);
}
public function paginatedAll(int $perPage = 25, string $search = '', string $sort = 'created_at', string $direction = 'desc', array $filters = []): LengthAwarePaginator
{
return StockMutation::query()
->where('stockable_type', ProductVariant::class)
->with([
'user:id,username,email',
'user.userProfile:id,user_id,full_name',
'stockable' => fn ($q) => $q->withTrashed()->select('id', 'product_id', 'name'),
'stockable.product' => fn ($q) => $q->withTrashed()->select('id', 'name'),
])
->when($search, function ($q) use ($search) {
$q->where(function ($sq) use ($search) {
$sq->whereHasMorph('stockable', [ProductVariant::class], function ($vq) use ($search) {
$vq->withTrashed()
->where('name', 'like', "%{$search}%")
->orWhereHas('product', fn ($pq) => $pq->withTrashed()->where('name', 'like', "%{$search}%"));
})->orWhere('description', 'like', "%{$search}%");
});
})
->when($filters['product_id'] ?? null, function ($q, $productId) {
$q->whereHasMorph('stockable', [ProductVariant::class], fn ($vq) => $vq->withTrashed()->where('product_id', $productId));
})
->when($filters['type'] ?? null, fn ($q, $type) => $q->where('type', $type))
->when($filters['stock_quality'] ?? null, fn ($q, $quality) => $q->where('stock_quality', $quality))
->when($filters['date_from'] ?? null, fn ($q, $dateFrom) => $q->whereDate('created_at', '>=', $dateFrom))
->when($filters['date_to'] ?? null, fn ($q, $dateTo) => $q->whereDate('created_at', '<=', $dateTo))
->orderBy($sort, $direction)
->paginate($perPage);
}
public function getFilterOptions(): array
{
return [
'products' => Product::query()
->select(['id', 'name'])
->whereHas('productVariants.stockMutations')
->orderBy('name')
->get(),
'typeOptions' => [
['value' => 'in', 'label' => 'Stok Masuk'],
['value' => 'out', 'label' => 'Stok Keluar'],
],
'stockQualityOptions' => [
['value' => 'good', 'label' => 'Bagus'],
['value' => 'reject', 'label' => 'Reject'],
['value' => 'retail', 'label' => 'Ecer'],
],
];
}
public static function qualityFromField(string $field): string
{
return self::QUALITY_MAP[$field] ?? 'good';
}
public function record(
Model $model,
string $type,
int $quantity,
int $stockBefore,
int $stockAfter,
string $stockQuality,
string $description,
?Model $source = null,
): StockMutation {
return StockMutation::create([
'user_id' => auth()->id(),
'stockable_type' => get_class($model),
'stockable_id' => $model->id,
'type' => $type,
'quantity' => $quantity,
'stock_before' => $stockBefore,
'stock_after' => $stockAfter,
'stock_quality' => $stockQuality,
'description' => $description,
'source_type' => $source ? get_class($source) : null,
'source_id' => $source?->id,
]);
}
public function recordInitial(Model $model, array $stockData, string $description = 'Stok awal'): void
{
$userId = auth()->id();
foreach (self::QUALITY_MAP as $field => $quality) {
$quantity = (int) ($stockData[$field] ?? 0);
if ($quantity > 0) {
StockMutation::create([
'user_id' => $userId,
'stockable_type' => get_class($model),
'stockable_id' => $model->id,
'type' => 'in',
'quantity' => $quantity,
'stock_before' => 0,
'stock_after' => $quantity,
'stock_quality' => $quality,
'description' => $description,
]);
}
}
}
public function recordAdjustment(Model $model, array $oldData, array $newData, string $description = 'Penyesuaian stok'): void
{
$userId = auth()->id();
foreach (self::QUALITY_MAP as $field => $quality) {
$old = (int) ($oldData[$field] ?? 0);
$new = (int) ($newData[$field] ?? 0);
$diff = $new - $old;
if ($diff !== 0) {
StockMutation::create([
'user_id' => $userId,
'stockable_type' => get_class($model),
'stockable_id' => $model->id,
'type' => $diff > 0 ? 'in' : 'out',
'quantity' => $diff,
'stock_before' => $old,
'stock_after' => $new,
'stock_quality' => $quality,
'description' => $description,
]);
}
}
}
public function recordBulkInitial(Collection $variants, array $stockDataMap, string $description = 'Stok awal'): void
{
$userId = auth()->id();
$now = now();
$mutations = [];
foreach ($variants as $variant) {
$stockData = $stockDataMap[$variant->id] ?? [];
foreach (self::QUALITY_MAP as $field => $quality) {
$quantity = (int) ($stockData[$field] ?? 0);
if ($quantity > 0) {
$mutations[] = [
'user_id' => $userId,
'stockable_type' => ProductVariant::class,
'stockable_id' => $variant->id,
'type' => 'in',
'quantity' => $quantity,
'stock_before' => 0,
'stock_after' => $quantity,
'stock_quality' => $quality,
'description' => $description,
'created_at' => $now,
'updated_at' => $now,
];
}
}
}
if ($mutations !== []) {
DB::table('stock_mutations')->insert($mutations);
}
}
public function recordBulkAdjustment(Collection $variants, array $oldDataMap, array $newDataMap, string $description = 'Penyesuaian stok'): void
{
$userId = auth()->id();
$now = now();
$mutations = [];
foreach ($variants as $variant) {
$oldData = $oldDataMap[$variant->id] ?? [];
$newData = $newDataMap[$variant->id] ?? [];
foreach (self::QUALITY_MAP as $field => $quality) {
$old = (int) ($oldData[$field] ?? 0);
$new = (int) ($newData[$field] ?? 0);
$diff = $new - $old;
if ($diff !== 0) {
$mutations[] = [
'user_id' => $userId,
'stockable_type' => ProductVariant::class,
'stockable_id' => $variant->id,
'type' => $diff > 0 ? 'in' : 'out',
'quantity' => $diff,
'stock_before' => $old,
'stock_after' => $new,
'stock_quality' => $quality,
'description' => $description,
'created_at' => $now,
'updated_at' => $now,
];
}
}
}
if ($mutations !== []) {
DB::table('stock_mutations')->insert($mutations);
}
}
public function recordTransfer(
Model $model,
int $quantity,
string $fromQuality,
string $toQuality,
int $fromBefore,
int $toBefore,
string $description = 'Transfer stok',
): void {
DB::transaction(function () use ($model, $quantity, $fromQuality, $toQuality, $fromBefore, $toBefore, $description) {
$userId = auth()->id();
StockMutation::create([
'user_id' => $userId,
'stockable_type' => get_class($model),
'stockable_id' => $model->id,
'type' => 'out',
'quantity' => -$quantity,
'stock_before' => $fromBefore,
'stock_after' => $fromBefore - $quantity,
'stock_quality' => $fromQuality,
'description' => $description,
]);
StockMutation::create([
'user_id' => $userId,
'stockable_type' => get_class($model),
'stockable_id' => $model->id,
'type' => 'in',
'quantity' => $quantity,
'stock_before' => $toBefore,
'stock_after' => $toBefore + $quantity,
'stock_quality' => $toQuality,
'description' => $description,
]);
});
}
}