ultimatepos/Modules/Maintenance/Services/SparePartInventoryService.php

131 lines
3.8 KiB
PHP

<?php
namespace Modules\Maintenance\Services;
use App\Product;
use App\Utils\ProductUtil;
use App\Variation;
use App\VariationLocationDetails;
use Modules\Maintenance\Models\SparePart;
use Modules\Maintenance\Utils\MaintenanceUtil;
use InvalidArgumentException;
class SparePartInventoryService
{
public function __construct(
protected MaintenanceUtil $maintenanceUtil,
protected ProductUtil $productUtil
) {}
public function posStockSyncEnabled(int $businessId): bool
{
$settings = $this->maintenanceUtil->getMaintenanceSettings($businessId);
return ! empty($settings['sync_pos_stock']);
}
public function posStockDeductEnabled(int $businessId): bool
{
$settings = $this->maintenanceUtil->getMaintenanceSettings($businessId);
return ! array_key_exists('deduct_pos_stock_on_consume', $settings)
|| ! empty($settings['deduct_pos_stock_on_consume']);
}
public function resolveLocationId(SparePart $part, int $businessId): ?int
{
if ($part->location_id) {
return (int) $part->location_id;
}
$settings = $this->maintenanceUtil->getMaintenanceSettings($businessId);
return ! empty($settings['default_stock_location_id'])
? (int) $settings['default_stock_location_id']
: null;
}
public function resolveVariationId(SparePart $part): ?int
{
if ($part->variation_id) {
return (int) $part->variation_id;
}
if (! $part->product_id) {
return null;
}
$variation = Variation::where('product_id', $part->product_id)->orderBy('id')->first();
return $variation?->id;
}
public function getPosAvailableQty(SparePart $part, int $businessId): ?float
{
if (! $part->product_id) {
return null;
}
$locationId = $this->resolveLocationId($part, $businessId);
$variationId = $this->resolveVariationId($part);
if (! $locationId || ! $variationId) {
return null;
}
$details = VariationLocationDetails::where('product_id', $part->product_id)
->where('variation_id', $variationId)
->where('location_id', $locationId)
->first();
return $details ? (float) $details->qty_available : 0.0;
}
public function syncSparePartQuantityFromPos(SparePart $part, int $businessId): void
{
if (! $this->posStockSyncEnabled($businessId) || ! $part->product_id) {
return;
}
$qty = $this->getPosAvailableQty($part, $businessId);
if ($qty === null) {
return;
}
$part->update(['quantity' => $qty]);
}
public function deductPosStock(SparePart $part, float $quantity, int $businessId): void
{
if (! $this->posStockDeductEnabled($businessId) || ! $part->product_id) {
return;
}
$locationId = $this->resolveLocationId($part, $businessId);
$variationId = $this->resolveVariationId($part);
if (! $locationId || ! $variationId) {
return;
}
$product = Product::where('business_id', $businessId)->find($part->product_id);
if (! $product || (int) $product->enable_stock !== 1) {
return;
}
$available = $this->getPosAvailableQty($part, $businessId) ?? 0;
if ($available < $quantity) {
throw new InvalidArgumentException('موجودی انبار POS کافی نیست. موجودی: '.$available);
}
$this->productUtil->decreaseProductQuantity(
$part->product_id,
$variationId,
$locationId,
$quantity
);
if ($this->posStockSyncEnabled($businessId)) {
$part->refresh();
$this->syncSparePartQuantityFromPos($part, $businessId);
}
}
}