product_type === 'domain'; $order = Order::create([ 'order_num' => OrderHelper::generateOrderNumber(), 'customer_id' => $customerId, 'user_id' => Auth::guard('admin')->id(), 'date' => now()->toDateString(), 'sub_total' => $price, 'discount_amount' => 0, 'total' => $price, 'status' => $paid ? OrderStatus::ACTIVE->value : OrderStatus::PENDING->value, 'payment_method' => $paymentMethod, 'client_ip' => $clientIp, ]); $orderItem = OrderItem::create([ 'order_id' => $order->id, 'product_id' => $product?->id, 'billing_cycle' => $billingCycle, 'months' => $months, 'server_name' => $serverName, 'domain_name' => $isDomain ? $serverName : null, 'quantity' => 1, 'price' => $price, ]); $invoice = Invoice::create([ 'invoice_number' => OrderHelper::generateOrderNumber('INV'), 'customer_id' => $customerId, 'order_id' => $order->id, 'invoice_type' => 1, 'date' => now()->toDateString(), 'sub_total' => $price, 'discount_amount' => 0, 'tax' => 0, 'total' => $price, 'status' => $paid ? InvoiceStatus::PAID->value : InvoiceStatus::UNPAID->value, 'payment_method' => $paymentMethod, 'paid_at' => $paid ? now() : null, 'due_date' => $paid ? now()->addMonths($months)->toDateString() : now()->toDateString(), ]); InvoiceItem::create([ 'invoice_id' => $invoice->id, 'product_id' => $product?->id, 'type' => 'service', 'description' => $this->describe($product, $serverName), 'months' => $months, 'quantity' => 1, 'unit_price' => $price, 'total_price' => $price, ]); return compact('order', 'orderItem', 'invoice'); } public function ensureForService(Service $service, ?int $price = null, ?string $billingCycle = null): void { if ($service->order_item_id || ! $service->customer_id) { return; } DB::transaction(function () use ($service, $price, $billingCycle) { $product = $service->product ?? $this->defaultProductFor($service->type); $isDomain = ($product?->product_type ?? $service->type) === 'domain'; $billingCycle ??= $isDomain ? 'annually' : 'monthly'; $price ??= $this->listPrice($product, $billingCycle, $service->server_name) ?? 0; $records = $this->create( (int) $service->customer_id, $product, $billingCycle, $price, $service->server_name, paid: false, ); $service->forceFill([ 'product_id' => $service->product_id ?? $product?->id, 'order_item_id' => $records['orderItem']->id, 'invoice_id' => $records['invoice']->id, ])->save(); }); } public function syncUnpaidInvoice(Service $service, int $price): void { $invoice = $service->invoice; if (! $invoice || (int) $invoice->status !== InvoiceStatus::UNPAID->value || (int) $invoice->invoice_type !== 1) { return; } $months = (int) ($service->orderItem?->months ?: 1); $invoice->update(['sub_total' => $price, 'total' => $price]); $invoice->invoiceItems()->where('type', 'service')->update([ 'months' => $months, 'unit_price' => $price, 'total_price' => $price, ]); $invoice->order?->update(['sub_total' => $price, 'total' => $price]); } public function cancelUnpaidInvoice(Service $service): void { $invoice = $service->invoice; if (! $invoice || (int) $invoice->status !== InvoiceStatus::UNPAID->value) { return; } $invoice->update(['status' => InvoiceStatus::CANCELLED->value]); $invoice->order?->update(['status' => OrderStatus::CANCELLED->value]); } public function discardOrder(?int $orderItemId): void { $order = $orderItemId ? OrderItem::find($orderItemId)?->order : null; if (! $order) { return; } if (Service::whereIn('order_item_id', $order->orderItems()->pluck('id'))->exists()) { return; } $order->invoices()->delete(); $order->delete(); } public function listPrice(?Product $product, string $billingCycle, ?string $serverName): ?int { if ($product?->product_type === 'domain') { return DomainPricing::matchDomain((string) $serverName) ?->orderPrice(DomainPricing::yearsForMonths(OrderHelper::cycleToMonths($billingCycle))); } $price = $product?->pricings()->where('billing_cycle', $billingCycle)->value('price'); return $price === null ? null : (int) $price; } private function defaultProductFor(?string $type): ?Product { return $type ? Product::where('product_type', $type)->where('is_active', true)->orderBy('id')->first() : null; } private function describe(?Product $product, ?string $serverName): string { $name = $product->name ?? 'Dịch vụ'; return $serverName ? "{$name} - {$serverName}" : $name; } }