tablesReady()) { return false; } return app(ConfigService::class)->getBool(self::SETTING_KEY, false); } /** * @return array{package_id:int,title:string,profit_buy:int,profit_sell:int,show_product:bool}|null */ public function ruleForCustomerProduct(int $customerId, int $productId): ?array { if (! $this->isEnabled() || $customerId <= 0 || $productId <= 0) { return null; } $row = CustomerSpecialPricePackage::query() ->active() ->whereHas('customers', fn ($q) => $q->where('customers.id', $customerId)) ->whereHas('products', fn ($q) => $q->where('products.id', $productId)) ->orderByDesc('updated_at') ->orderByDesc('id') ->first(['id', 'title', 'profit_buy', 'profit_sell', 'show_product', 'updated_at']); if (! $row) { return null; } return [ 'package_id' => (int) $row->id, 'title' => (string) $row->title, 'profit_buy' => (int) ($row->profit_buy ?? 0), 'profit_sell' => (int) ($row->profit_sell ?? 0), 'show_product' => (bool) $row->show_product, ]; } /** * قوانین مؤثر برای مشتری — هر محصول یک قانون (آخرین پکیج). * * @return array */ public function rulesMapForCustomer(int $customerId): array { if (! $this->isEnabled() || $customerId <= 0) { return []; } $packages = CustomerSpecialPricePackage::query() ->active() ->whereHas('customers', fn ($q) => $q->where('customers.id', $customerId)) ->with(['products:id']) ->orderByDesc('updated_at') ->orderByDesc('id') ->get(['id', 'title', 'profit_buy', 'profit_sell', 'show_product', 'updated_at']); $map = []; foreach ($packages as $package) { foreach ($package->products as $product) { $pid = (int) $product->id; if (isset($map[$pid])) { continue; } $map[$pid] = [ 'package_id' => (int) $package->id, 'title' => (string) $package->title, 'profit_buy' => (int) ($package->profit_buy ?? 0), 'profit_sell' => (int) ($package->profit_sell ?? 0), 'show_product' => (bool) $package->show_product, ]; } } return $map; } public function profitForDirection(?array $rule, int $direction): int { if (! $rule) { return 0; } return $direction === 2 ? (int) ($rule['profit_sell'] ?? 0) : (int) ($rule['profit_buy'] ?? 0); } /** * اعمال روی payload تابلو last (buy/sell/price_last) برای محصول اصلی یا اولین قانون آبشده. */ public function applyToLastPayload(array $payload, int $customerId): array { if (! $this->isEnabled() || $customerId <= 0) { return $payload; } $mainId = app(ConfigService::class)->mainProductId(); $rule = null; if ($mainId) { $rule = $this->ruleForCustomerProduct($customerId, (int) $mainId); } if (! $rule) { $map = $this->rulesMapForCustomer($customerId); $rule = $map ? reset($map) : null; if (is_array($rule) && ! ($rule['show_product'] ?? true)) { // فقط برای اختلاف قیمت؛ مخفی‌سازی روی لیست محصول است } } if (! $rule) { return $payload; } $buyExtra = (int) ($rule['profit_buy'] ?? 0); $sellExtra = (int) ($rule['profit_sell'] ?? 0); if (isset($payload['buy']) && (int) $payload['buy'] > 0 && $buyExtra !== 0) { $payload['buy'] = max(0, (int) $payload['buy'] + $buyExtra); } if (isset($payload['sell']) && (int) $payload['sell'] > 0 && $sellExtra !== 0) { $payload['sell'] = max(0, (int) $payload['sell'] - $sellExtra); } if (isset($payload['price_last']) && (int) $payload['price_last'] > 0 && $buyExtra !== 0) { $payload['price_last'] = max(0, (int) $payload['price_last'] + $buyExtra); } if (isset($payload['priceLast']) && (int) $payload['priceLast'] > 0 && $buyExtra !== 0) { $payload['priceLast'] = max(0, (int) $payload['priceLast'] + $buyExtra); } $payload['special_pricing'] = [ 'package_id' => $rule['package_id'], 'title' => $rule['title'], 'profit_buy' => $buyExtra, 'profit_sell' => $sellExtra, ]; return $payload; } /** * روی کالکشن محصول: ویژه را روی مدل می‌نشاند و در صورت نیاز show را false می‌کند. * * @param Collection|iterable $products */ public function applyToProducts($products, int $customerId): void { if (! $this->isEnabled() || $customerId <= 0) { return; } $map = $this->rulesMapForCustomer($customerId); if ($map === []) { return; } $collection = $products instanceof Collection ? $products : collect($products); foreach ($collection as $product) { if (! $product instanceof Product) { continue; } $pid = (int) ($product->id ?? 0); $rule = $map[$pid] ?? null; if (! $rule) { continue; } $product->setAttribute('special_profit_buy', (int) $rule['profit_buy']); $product->setAttribute('special_profit_sell', (int) $rule['profit_sell']); $product->setAttribute('special_package_id', (int) $rule['package_id']); $product->setAttribute('special_package_title', (string) $rule['title']); if (! ($rule['show_product'] ?? true)) { $product->setAttribute('show', false); } } } /** * هشدار تداخل: مشتری+محصول در پکیج دیگری هم هست. * * @param list $customerIds * @param list $productIds * @return list */ public function overlapWarnings(array $customerIds, array $productIds, ?int $excludePackageId = null): array { if (! $this->tablesReady() || $customerIds === [] || $productIds === []) { return []; } $customerIds = array_values(array_unique(array_map('intval', $customerIds))); $productIds = array_values(array_unique(array_map('intval', $productIds))); $q = DB::table('customer_special_price_packages as p') ->join('customer_special_price_package_customer as pc', 'pc.package_id', '=', 'p.id') ->join('customer_special_price_package_product as pp', 'pp.package_id', '=', 'p.id') ->join('customers as c', 'c.id', '=', 'pc.customer_id') ->join('products as pr', 'pr.id', '=', 'pp.product_id') ->where('p.is_active', true) ->whereIn('pc.customer_id', $customerIds) ->whereIn('pp.product_id', $productIds) ->select([ 'p.id as package_id', 'p.title as package_title', 'c.id as customer_id', 'c.name as customer_name', 'pr.id as product_id', 'pr.title as product_title', ]); if ($excludePackageId) { $q->where('p.id', '!=', $excludePackageId); } $rows = $q->limit(50)->get(); if ($rows->isEmpty()) { return []; } $warnings = []; foreach ($rows as $row) { $warnings[] = sprintf( 'تداخل: مشتری «%s» (#%d) و محصول «%s» (#%d) در پکیج «%s» (#%d) هم هست — آخرین پکیج اعمال می‌شود.', $row->customer_name ?: '-', (int) $row->customer_id, $row->product_title ?: '-', (int) $row->product_id, $row->package_title ?: '-', (int) $row->package_id ); } return $warnings; } /** payload سبک برای SSE / me */ public function clientPayloadForCustomer(int $customerId): array { $map = $this->rulesMapForCustomer($customerId); $products = []; foreach ($map as $productId => $rule) { $products[] = array_merge(['product_id' => (int) $productId], $rule); } $mainId = app(ConfigService::class)->mainProductId(); $board = null; if ($mainId && isset($map[(int) $mainId])) { $board = $map[(int) $mainId]; } elseif ($products !== []) { $board = $products[0]; } return [ 'enabled' => $this->isEnabled(), 'products' => $products, 'board' => $board, ]; } }