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); } } }