ultimatepos/Modules/IndustrialEngineering/Services/ErpItemLinkService.php

142 lines
4.4 KiB
PHP

<?php
namespace Modules\IndustrialEngineering\Services;
use App\Product;
use App\Variation;
use Illuminate\Support\Facades\DB;
use Modules\IndustrialEngineering\Models\ItemMaster;
/**
* Links IE item masters to core ERP products/variations for stock, purchase, sales, costing.
*/
class ErpItemLinkService
{
/**
* Try to match item to ERP product by SKU or name.
*
* @return array{linked: bool, message: string, variation_id?: int}
*/
public function autoMatchProduct(ItemMaster $item): array
{
if ($item->variation_id) {
return ['linked' => true, 'message' => __('industrialengineering::lang.auto_erp_already_linked')];
}
$variation = Variation::query()
->whereHas('product', fn ($q) => $q->where('business_id', $item->business_id))
->where(function ($q) use ($item) {
$q->where('sub_sku', $item->item_code)
->orWhere('sub_sku', 'like', $item->item_code.'%');
})
->with('product')
->first();
if (! $variation) {
$variation = Variation::query()
->whereHas('product', function ($q) use ($item) {
$q->where('business_id', $item->business_id)
->where('name', 'like', '%'.$item->name.'%');
})
->with('product')
->first();
}
if ($variation) {
$this->linkItem($item, ['variation_id' => $variation->id]);
return [
'linked' => true,
'message' => __('industrialengineering::lang.auto_erp_matched', ['name' => $this->variationLabel($variation)]),
'variation_id' => $variation->id,
];
}
return [
'linked' => false,
'message' => __('industrialengineering::lang.auto_erp_not_found', ['code' => $item->item_code]),
];
}
public function linkItem(ItemMaster $item, array $data): ItemMaster
{
$updates = [];
if (! empty($data['variation_id'])) {
$variation = Variation::with('product')->find($data['variation_id']);
if ($variation) {
$updates['variation_id'] = $variation->id;
$updates['product_id'] = $variation->product_id;
if (empty($data['standard_cost']) && $variation->dpp_inc_tax) {
$updates['standard_cost'] = $variation->dpp_inc_tax;
}
if (empty($item->name) || $item->name === $item->item_code) {
$updates['name'] = $this->variationLabel($variation);
}
}
} elseif (! empty($data['product_id'])) {
$product = Product::find($data['product_id']);
if ($product) {
$updates['product_id'] = $product->id;
$variation = $product->variations()->first();
if ($variation) {
$updates['variation_id'] = $variation->id;
}
}
}
if ($updates) {
$item->update($updates);
}
return $item->fresh(['product', 'variation']);
}
public function erpLabel(?ItemMaster $item): ?string
{
if (! $item) {
return null;
}
if ($item->variation_id) {
$variation = $item->variation ?? Variation::with('product')->find($item->variation_id);
if ($variation) {
return $this->variationLabel($variation);
}
}
if ($item->product_id && ($product = $item->product ?? Product::find($item->product_id))) {
return $product->name;
}
return null;
}
public function variationLabel(?Variation $variation): string
{
if (! $variation) {
return '';
}
$variation->loadMissing('product');
$label = $variation->product->name ?? 'محصول';
if (($variation->product->type ?? '') === 'variable') {
$label .= ' — '.$variation->name;
}
return $label;
}
public function stockSummary(ItemMaster $item): array
{
if (! $item->variation_id) {
return ['linked' => false, 'qty' => 0];
}
$qty = \DB::table('variation_location_details')
->where('variation_id', $item->variation_id)
->sum('qty_available');
return ['linked' => true, 'qty' => (float) $qty];
}
}