'tên miền', 'hosting' => 'hosting', 'vps' => 'Cloud VPS', 'ssl' => 'SSL', ]; /** * @param array $customerIds * @return Collection */ public static function dueServices(array $customerIds, int $days): Collection { return Service::query() ->with(['customer:id,uuid,fullname,email', 'orderItem', 'product', 'customProduct']) ->whereIn('customer_id', $customerIds) ->whereNotIn('status', ['terminated', 'cancelled']) ->whereNotNull('expires_at') ->where('expires_at', '<=', now()->addDays($days)->endOfDay()) ->orderBy('expires_at') ->get(); } public static function forService(Service $service, ?int $months = null): ?array { $orderItem = $service->orderItem; $product = $service->customProduct ?? $service->product; if (! $orderItem || ! $product) { return null; } $baseMonths = OrderHelper::cycleToMonths($orderItem->billing_cycle); $options = RenewalPricing::options($product); $base = RenewalPricing::option($product, $baseMonths); if (! $base) { return null; } $rate = VatSettings::rateFor($service->product?->product_type); $start = ($service->expires_at ?? now())->copy(); $target = $service->server_name ?: $product->name; $label = self::TYPE_LABELS[$service->type] ?? null; $terms = fn (array $option) => self::terms($service, $option, $baseMonths, $start, $rate); return [ 'service_id' => $service->id, 'service_name' => 'Gia hạn ' . ($label ? $label . ' - ' : '') . $target, 'period_start' => $start->format('d/m/Y'), 'expires_date' => $start->format('d/m/Y'), 'vat_rate' => rtrim(rtrim(number_format($rate, 2, '.', ''), '0'), '.'), 'payment_content' => Str::limit('GIA HAN ' . Str::upper(Str::ascii($target)), 50, ''), ...$terms(RenewalPricing::option($product, (int) $months) ?? $base), 'cycles' => array_map($terms, $options), ]; } /** * @param array{months: int, price: int} $option * @return array{months: int, billing_cycle: string, period_end: string, total_amount: string, vat_amount: string} */ private static function terms(Service $service, array $option, int $baseMonths, Carbon $start, float $rate): array { $months = $option['months']; $amount = $option['price'] + RenewalPricing::addonTotal($service, $months / $baseMonths); return [ 'months' => $months, 'billing_cycle' => sprintf('%02d %s', $months % 12 === 0 ? $months / 12 : $months, $months % 12 === 0 ? 'năm' : 'tháng'), 'period_end' => $start->copy()->addMonthsNoOverflow($months)->subDay()->format('d/m/Y'), 'total_amount' => number_format($amount, 0, ',', '.'), 'vat_amount' => number_format((int) round($amount * $rate / (100 + $rate)), 0, ',', '.'), ]; } }