orderRepository->getOrders($customerId, $filters->toArray()); } public function checkServerName(string $name): array { $exists = OrderItem::where('server_name', $name)->exists() || Service::where('server_name', $name)->exists() || ModuleVmware::where('name', $name)->exists() || ModuleVmware::where('hostname', $name)->exists() || VirtualMachines::where('name', $name)->exists(); return [ 'available' => ! $exists, 'message' => $exists ? 'Tên server này đã được sử dụng, vui lòng chọn tên khác.' : null, ]; } public function findCustomerOrder(int $customerId, int $id): ?Order { return Order::query() ->with(['invoice', 'orderItems.product', 'orderItems.service', 'customer']) ->where('customer_id', $customerId) ->where('id', $id) ->first(); } public function getCustomerInvoices(int $customerId) { return Invoice::query() ->with(['order.orderItems.product']) ->where('customer_id', $customerId) ->orderByDesc('created_at') ->get(); } public function findCustomerInvoice(int $customerId, int $id): ?Invoice { return Invoice::query() ->with([ 'order.orderItems.product', 'order.vmWare.ipAddress', 'order.vmWare.os', 'customer', 'attachments', ]) ->where('customer_id', $customerId) ->where('id', $id) ->first(); } public function findCustomerInvoiceByOrderNum(int $customerId, string $orderNumber): ?Order { return Order::query() ->with([ 'invoice.invoiceItems', 'invoice.attachments', 'invoice.customer', 'orderItems.product', 'customer', 'vmWare.ipAddress', 'vmWare.os', ]) ->where('customer_id', $customerId) ->where('order_num', $orderNumber) ->first(); } public function getCustomerServices(int $customerId) { return Service::query() ->with(['product', 'addons', 'invoice', 'orderItem', 'vmware.ipAddress', 'vmware.os']) ->where('customer_id', $customerId) ->orderByDesc('created_at') ->get(); } public function findCustomerService(int $customerId, string $uuid): ?Service { return Service::query() ->with(['product', 'addons', 'invoice', 'orderItem', 'vmware.ipAddress', 'vmware.os', 'vmware.vCenter']) ->where('uuid', $uuid) ->where('customer_id', $customerId) ->first(); } public function canRetryProvisioning(Service $service): bool { if ($service->status !== 'pending' || $service->retry_requested_at) { return false; } $paidAt = $service->invoice?->paid_at; return $paidAt ? Carbon::parse($paidAt)->diffInMinutes(now()) >= 180 : false; } public function retryProvisioning(Order $order, int $customerId): array { $order = Order::where('id', $order->id) ->with(['invoice', 'orderItems.service']) ->where('customer_id', $customerId) ->first(); if (! $order) { return ['ok' => false, 'message' => 'Đơn hàng không tồn tại.', 'code' => 404]; } if (! $order->invoice || (int) $order->invoice->status !== InvoiceStatus::PAID->value) { return ['ok' => false, 'message' => 'Đơn hàng chưa được thanh toán.', 'code' => 400]; } $paidAt = $order->invoice->paid_at ? Carbon::parse($order->invoice->paid_at) : null; if (! $paidAt || $paidAt->diffInMinutes(now()) < 180) { return ['ok' => false, 'message' => 'Vui lòng chờ khoảng 3 tiếng sau khi thanh toán mới có thể khởi tạo lại dịch vụ.', 'code' => 400]; } $service = $order->orderItems->first()?->service; if (! $service || (int) $service->customer_id !== $customerId) { return ['ok' => false, 'message' => 'Không tìm thấy dịch vụ hợp lệ để khởi tạo lại.', 'code' => 400]; } if ($service->status !== 'pending') { return ['ok' => false, 'message' => 'Dịch vụ đã được khởi tạo hoặc không ở trạng thái chờ.', 'code' => 400]; } if ($service->retry_requested_at) { return ['ok' => false, 'message' => 'Dịch vụ đã được yêu cầu khởi tạo lại một lần. Vui lòng liên hệ kỹ thuật qua mailto:kythuat@tgs.com.vn', 'code' => 400]; } $service->update(['retry_requested_at' => now()]); RetryProvisioningOrchestrator::dispatch($order->id, $customerId); return ['ok' => true, 'message' => 'Đã yêu cầu khởi tạo lại dịch vụ. Hệ thống sẽ kiểm tra và xử lý trong vài phút.']; } public function renewService(int $serviceId, int $customerId, int $months): array { $customer = \App\Models\Customer::find($customerId); $service = Service::query() ->with(['orderItem.product']) ->where('id', $serviceId) ->where('customer_id', $customerId) ->whereNotIn('status', ['terminated', 'cancelled']) ->first(); if (! $service) { return ['ok' => false, 'message' => 'Không tìm thấy dịch vụ hoặc dịch vụ đã bị hủy.', 'code' => 404]; } $orderItem = $service->orderItem; if (! $orderItem || (int) $orderItem->price <= 0) { return ['ok' => false, 'message' => 'Không thể xác định giá gia hạn dịch vụ.', 'code' => 400]; } $price = (int) $orderItem->price * $months; if ($customer->total < $price) { return ['ok' => false, 'message' => sprintf('Số dư không đủ. Hiện có: %s VNĐ, cần: %s VNĐ.', number_format($customer->total), number_format($price)), 'code' => 400]; } $needsVmStart = $service->vm_id !== null; $invoice = DB::transaction(function () use ($customer, $service, $orderItem, $price, $months) { $customer->total -= $price; $customer->save(); \App\Models\Log::create([ 'agent' => 'api', 'client_ip' => request()?->ip(), 'customer_id' => $customer->id, 'description' => sprintf('Gia hạn dịch vụ #%s. Số tiền: %s VNĐ. Số dư còn: %s VNĐ.', $service->server_name ?? $service->id, number_format($price), number_format($customer->total)), ]); $invoice = Invoice::create([ 'invoice_number' => OrderHelper::generateOrderNumber('INV'), 'customer_id' => $customer->id, 'order_id' => null, 'invoice_type' => 2, 'sub_total' => $price, 'discount_amount' => 0, 'tax' => 0, 'total' => $price, 'status' => InvoiceStatus::PAID->value, 'paid_at' => now(), 'payment_method' => 'credit', ]); \App\Models\InvoiceItem::create([ 'invoice_id' => $invoice->id, 'product_id' => $service->product_id, 'type' => 'renewal', 'description' => 'Gia hạn dịch vụ: ' . ($orderItem->product?->name ?? $service->server_name ?? ''), 'months' => $months, 'quantity' => 1, 'unit_price' => $price, 'total_price' => $price, ]); $baseDate = $service->expires_at && $service->expires_at->isFuture() ? $service->expires_at : now(); $expiresAt = Carbon::parse($baseDate)->addMonths($months); $service->update([ 'status' => 'active', 'expires_at' => $expiresAt, 'suspended_at' => null, 'invoice_id' => $invoice->id, ]); return $invoice; }); if ($needsVmStart) { dispatch(new VMJobs((int) $service->vm_id, 'Start', false))->delay(now()->addSeconds(5)); } return [ 'ok' => true, 'message' => 'Gia hạn thành công. Dịch vụ đã được kích hoạt lại.', 'data' => [ 'invoice_number' => $invoice->invoice_number, 'total' => (float) $invoice->total, 'expires_at' => $service->fresh()?->expires_at?->toISOString(), ], ]; } }