This commit is contained in:
baodev0
2026-10-08 11:25:28 +07:00
parent 1544629a1f
commit 8651dde6b1
45 changed files with 1543 additions and 357 deletions
@@ -3,6 +3,7 @@
namespace App\Http\Controllers\Api\Admin\Customer\Concerns;
use App\Models\Service;
use App\Support\AutoEmailGate;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -34,6 +35,7 @@ trait ManagesCustomerServices
'requirements' => 'nullable|string',
'feedback' => 'nullable|string',
'price' => 'nullable|numeric|min:0',
'virtual_machine_id' => 'nullable|integer|exists:virtual_machines,id',
'os_id' => 'nullable|integer|exists:os,id',
'vm_cpu' => 'nullable|integer|min:1',
'vm_ram_mb' => 'nullable|integer|min:1',
@@ -74,6 +76,7 @@ trait ManagesCustomerServices
public function notifyServiceRenewal(string $uuid, int $serviceId): JsonResponse
{
AutoEmailGate::markManual();
$this->serviceProvisioning->notifyRenewal($uuid, $serviceId);
return response()->json(['error' => false, 'message' => 'Đã gửi thông báo gia hạn đến khách hàng.']);
@@ -238,6 +238,8 @@ class EmailTemplateController extends Controller
'news_template_id' => 'nullable|integer|exists:template,id',
'invoice_template_id' => 'nullable|integer|exists:template,id',
'mail_enabled' => 'sometimes|boolean',
'auto_disabled' => 'sometimes|array',
'auto_disabled.*' => ['string', Rule::in(array_keys(EmailScenarioRegistry::SCENARIOS))],
]);
$setting = Setting::firstOrNew(['key' => 'email_settings']);
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api\Admin\Invoice\Concerns;
use App\Mail\InvoiceNotificationMail;
use App\Models\Customer;
use App\Services\InvoiceReminderService;
use App\Support\AutoEmailGate;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
@@ -36,6 +37,7 @@ trait SendsInvoiceNotifications
}
try {
AutoEmailGate::markManual();
$reminders->remind($invoice->load('customer'), force: true);
} catch (\Throwable $e) {
return response()->json([
@@ -79,6 +81,7 @@ trait SendsInvoiceNotifications
);
$paymentUrl = url('/client/invoices/' . $invoice->invoice_number . '/payment');
try {
AutoEmailGate::markManual();
$invoicePdf = $this->invoicePdfService->generate($invoice);
$invoicePdfFileName = $this->invoicePdfService->generateFileName($invoice);
@@ -44,6 +44,30 @@ class ServiceController extends Controller
return response()->json(['error' => false, 'data' => $service]);
}
public function bulkAction(Request $request): JsonResponse
{
$data = $request->validate([
'ids' => 'required|array|min:1',
'ids.*' => 'integer|distinct|exists:services,id',
'action' => 'required|in:activate,suspend,cancel,detach-customer',
]);
$updates = match ($data['action']) {
'activate' => ['status' => 'active', 'suspended_at' => null, 'cancelled_at' => null],
'suspend' => ['status' => 'suspended', 'suspended_at' => now()],
'cancel' => ['status' => 'cancelled', 'cancelled_at' => now()],
'detach-customer' => ['customer_id' => null],
};
$updated = Service::whereIn('id', $data['ids'])->update($updates);
return response()->json([
'error' => false,
'message' => "Đã cập nhật {$updated} dịch vụ.",
'data' => ['updated' => $updated],
]);
}
public function detachCustomer(int $id): JsonResponse
{
$service = Service::findOrFail($id);
@@ -129,7 +129,7 @@ class ServiceController extends Controller
{
$customer = $request->user();
$service = Service::with(['orderItem.order.vmWare.vCenter'])
$service = Service::with(['vmware.vCenter', 'orderItem.order.vmWare.vCenter'])
->where('uuid', $uuid)
->where('customer_id', $customer->id)
->first();
@@ -138,7 +138,7 @@ class ServiceController extends Controller
return response()->json(['error' => true, 'message' => 'Không tìm thấy dịch vụ'], 404);
}
$vm = $service->orderItem?->order?->vmWare?->first();
$vm = $service->resolveVmware();
if (!$vm) {
return response()->json(['error' => true, 'message' => 'Dịch vụ chưa được cấp phát VM'], 404);
@@ -31,7 +31,7 @@ trait ManagesVmNetwork
return $service;
}
$vm = $service->orderItem?->order?->vmWare?->first();
$vm = $service->resolveVmware();
if (!$vm) {
return response()->json(['success' => false, 'message' => 'Không tìm thấy VM cho dịch vụ này.'], 404);
}
@@ -77,7 +77,7 @@ trait ManagesVmNetwork
return $service;
}
$vm = $service->orderItem?->order?->vmWare?->first();
$vm = $service->resolveVmware();
if (!$vm) {
return response()->json(['success' => false, 'message' => 'Không tìm thấy VM cho dịch vụ này.'], 404);
@@ -219,8 +219,9 @@ trait ManagesVmNetwork
{
$customer = $request->user();
// VM chain: Service → orderItem → order → vmWare (same as ServiceClientDetailResource)
$service = Service::with([
'vmware.vCenter',
'vmware.ipAddress.host.datacenter.vCenter',
'orderItem.order.vmWare.vCenter',
'orderItem.order.vmWare.ipAddress.host.datacenter.vCenter',
'orderItem.os.osGroup',