feat: add domain registration form and email notifications
- Create a new Blade view for domain registration (`registrant-tochuc.blade.php`) to capture organization and manager details. - Implement email templates for new domain orders for both admin (`domain-order-new.blade.php`) and customers (`domain-order-received.blade.php`). - Add CSV exports for client and domain data. - Introduce tests for customer mass assignment, password reset, login throttling, and permission gating. - Ensure service renewals use current pricing and validate ownership for virtual machines. - Validate mass assignment behavior in service model.
This commit is contained in:
@@ -33,14 +33,13 @@ class OrderControllerRefactor extends Controller
|
||||
private readonly OrderQueryService $orderQueryService,
|
||||
private readonly ActivityLogger $activityLogger,
|
||||
private readonly InvoicePaymentService $invoicePaymentService,
|
||||
) {
|
||||
}
|
||||
) {}
|
||||
|
||||
public function getList(Request $request): JsonResponse
|
||||
{
|
||||
$orders = $this->orderQueryService->getOrdersForCustomer(
|
||||
(int) $request->user()->id,
|
||||
ClientOrderFilters::fromArray($request->only(['total_price', 'pending']))
|
||||
ClientOrderFilters::fromArray($request->only(['total_price', 'pending'])),
|
||||
);
|
||||
|
||||
return response()->json(['success' => true, 'data' => $orders]);
|
||||
@@ -67,7 +66,7 @@ class OrderControllerRefactor extends Controller
|
||||
{
|
||||
$order = $this->orderQueryService->findCustomerOrder((int) $request->user()->id, $id);
|
||||
|
||||
if (! $order) {
|
||||
if (!$order) {
|
||||
return response()->json(['success' => false, 'message' => 'Đơn hàng không tồn tại.'], 404);
|
||||
}
|
||||
|
||||
@@ -81,8 +80,11 @@ class OrderControllerRefactor extends Controller
|
||||
$data = $request->validated();
|
||||
|
||||
$product = \App\Models\Product::where('uuid', $data['product_id'])->first();
|
||||
if (! $product) {
|
||||
return response()->json(['success' => false, 'message' => 'Sản phẩm không tìm thấy hoặc đã bị xóa.'], 404);
|
||||
if (!$product) {
|
||||
return response()->json(
|
||||
['success' => false, 'message' => 'Sản phẩm không tìm thấy hoặc đã bị xóa.'],
|
||||
404,
|
||||
);
|
||||
}
|
||||
|
||||
$data['product_id'] = $product->id;
|
||||
@@ -90,16 +92,19 @@ class OrderControllerRefactor extends Controller
|
||||
|
||||
$order = $result['order']->load(['orderItems.product', 'invoice', 'customer']);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Tạo đơn hàng thành công.',
|
||||
'notice' => 'Dịch vụ VPS sẽ được khởi tạo trong 5-10 phút sau khi thanh toán.',
|
||||
'data' => [
|
||||
'order' => new OrderDetailResource($order),
|
||||
'invoice' => new InvoiceDetailResource($result['invoice']->load(['customer', 'order'])),
|
||||
'items' => OrderItemResource::collection($order->orderItems),
|
||||
return response()->json(
|
||||
[
|
||||
'success' => true,
|
||||
'message' => 'Tạo đơn hàng thành công.',
|
||||
'notice' => 'Dịch vụ VPS sẽ được khởi tạo trong 5-10 phút sau khi thanh toán.',
|
||||
'data' => [
|
||||
'order' => new OrderDetailResource($order),
|
||||
'invoice' => new InvoiceDetailResource($result['invoice']->load(['customer', 'order'])),
|
||||
'items' => OrderItemResource::collection($order->orderItems),
|
||||
],
|
||||
],
|
||||
], 201);
|
||||
201,
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
return response()->json(['success' => false, 'message' => $e->getMessage()], 400);
|
||||
}
|
||||
@@ -109,7 +114,7 @@ class OrderControllerRefactor extends Controller
|
||||
{
|
||||
$order = $this->orderRepository->findByOrderNum($orderNum, (int) $request->user()->id);
|
||||
|
||||
if (! $order) {
|
||||
if (!$order) {
|
||||
return response()->json(['success' => false, 'message' => 'Đơn hàng không tồn tại.'], 404);
|
||||
}
|
||||
|
||||
@@ -131,13 +136,13 @@ class OrderControllerRefactor extends Controller
|
||||
$customerId = (int) $request->user()->id;
|
||||
$order = $this->orderRepository->findForCustomer($id, $customerId);
|
||||
|
||||
if (! $order) {
|
||||
if (!$order) {
|
||||
return response()->json(['success' => false, 'message' => 'Đơn hàng không tồn tại.'], 404);
|
||||
}
|
||||
|
||||
$result = $this->orderQueryService->retryProvisioning($order, $customerId);
|
||||
|
||||
if (! $result['ok']) {
|
||||
if (!$result['ok']) {
|
||||
return response()->json(['success' => false, 'message' => $result['message']], $result['code']);
|
||||
}
|
||||
|
||||
@@ -162,7 +167,7 @@ class OrderControllerRefactor extends Controller
|
||||
{
|
||||
$invoice = $this->orderQueryService->findCustomerInvoice((int) $request->user()->id, $id);
|
||||
|
||||
if (! $invoice) {
|
||||
if (!$invoice) {
|
||||
return response()->json(['success' => false, 'message' => 'Hóa đơn không tồn tại.'], 404);
|
||||
}
|
||||
|
||||
@@ -173,20 +178,23 @@ class OrderControllerRefactor extends Controller
|
||||
{
|
||||
$order = $this->orderQueryService->findCustomerInvoiceByOrderNum((int) $request->user()->id, $orderNumber);
|
||||
|
||||
if (! $order?->invoice) {
|
||||
if (!$order?->invoice) {
|
||||
return response()->json(['success' => false, 'message' => 'Hóa đơn không tồn tại.'], 404);
|
||||
}
|
||||
|
||||
$order->load('vmWare.os.osGroup', 'vmWare.virtualMachine', 'vmWare.service');
|
||||
|
||||
return response()->json(['success' => true, 'data' => new InvoiceDetailResource($order->invoice->setRelation('order', $order))]);
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => new InvoiceDetailResource($order->invoice->setRelation('order', $order)),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getClientInvoiceDetailByNumber(Request $request, string $invoiceNumber): JsonResponse
|
||||
{
|
||||
$invoice = $this->orderQueryService->findCustomerInvoiceByNumber((int) $request->user()->id, $invoiceNumber);
|
||||
|
||||
if (! $invoice) {
|
||||
if (!$invoice) {
|
||||
return response()->json(['success' => false, 'message' => 'Hóa đơn không tồn tại.'], 404);
|
||||
}
|
||||
|
||||
@@ -201,7 +209,7 @@ class OrderControllerRefactor extends Controller
|
||||
{
|
||||
$invoice = $this->orderQueryService->findCustomerInvoiceByNumber((int) $request->user()->id, $invoiceNumber);
|
||||
|
||||
if (! $invoice) {
|
||||
if (!$invoice) {
|
||||
return response()->json(['success' => false, 'message' => 'Hóa đơn không tồn tại.'], 404);
|
||||
}
|
||||
|
||||
@@ -230,26 +238,29 @@ class OrderControllerRefactor extends Controller
|
||||
{
|
||||
$order = $this->orderQueryService->findCustomerInvoiceByOrderNum((int) $request->user()->id, $orderNum);
|
||||
|
||||
if (! $order) {
|
||||
if (!$order) {
|
||||
return response()->json(['success' => false, 'message' => 'Đơn hàng không tồn tại.'], 404);
|
||||
}
|
||||
|
||||
return response()->json(['success' => true, 'data' => new OrderDetailResource($order)]);
|
||||
}
|
||||
|
||||
public function downloadInvoicePdfByOrderNum(Request $request, string $orderNum, InvoicePdfService $pdfService): Response
|
||||
{
|
||||
public function downloadInvoicePdfByOrderNum(
|
||||
Request $request,
|
||||
string $orderNum,
|
||||
InvoicePdfService $pdfService,
|
||||
): Response {
|
||||
$order = $this->orderQueryService->findCustomerInvoiceByOrderNum((int) $request->user()->id, $orderNum);
|
||||
|
||||
if (! $order?->invoice) {
|
||||
if (!$order?->invoice) {
|
||||
abort(404, 'Hóa đơn không tồn tại.');
|
||||
}
|
||||
|
||||
$pdf = $pdfService->generate($order->invoice);
|
||||
$pdf = $pdfService->generate($order->invoice);
|
||||
$filename = $pdfService->generateFileName($order->invoice);
|
||||
|
||||
return response($pdf, 200, [
|
||||
'Content-Type' => 'application/pdf',
|
||||
'Content-Type' => 'application/pdf',
|
||||
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
|
||||
]);
|
||||
}
|
||||
@@ -265,7 +276,7 @@ class OrderControllerRefactor extends Controller
|
||||
{
|
||||
$service = $this->orderQueryService->findCustomerService((int) $request->user()->id, $uuid);
|
||||
|
||||
if (! $service) {
|
||||
if (!$service) {
|
||||
return response()->json(['success' => false, 'message' => 'Không tìm thấy dịch vụ.'], 404);
|
||||
}
|
||||
|
||||
@@ -276,12 +287,9 @@ class OrderControllerRefactor extends Controller
|
||||
{
|
||||
$validated = $request->validate(['auto_renew' => ['required', 'boolean']]);
|
||||
|
||||
$service = \App\Models\Service::query()
|
||||
->where('id', $id)
|
||||
->where('customer_id', $request->user()->id)
|
||||
->first();
|
||||
$service = \App\Models\Service::query()->where('id', $id)->where('customer_id', $request->user()->id)->first();
|
||||
|
||||
if (! $service) {
|
||||
if (!$service) {
|
||||
return response()->json(['success' => false, 'message' => 'Không tìm thấy dịch vụ.'], 404);
|
||||
}
|
||||
|
||||
@@ -290,19 +298,29 @@ class OrderControllerRefactor extends Controller
|
||||
$this->activityLogger->log(
|
||||
event: 'updated',
|
||||
logName: 'Service',
|
||||
description: "Customer {$request->user()->email} " . ($validated['auto_renew'] ? 'bật' : 'tắt') . " tự động gia hạn dịch vụ #{$service->id}",
|
||||
description: "Customer {$request->user()->email} " .
|
||||
($validated['auto_renew'] ? 'bật' : 'tắt') .
|
||||
" tự động gia hạn dịch vụ #{$service->id}",
|
||||
subject: $service,
|
||||
newValues: ['auto_renew' => (bool) $validated['auto_renew']],
|
||||
);
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Cập nhật tự động gia hạn thành công.', 'data' => ['id' => $service->id, 'auto_renew' => (bool) $service->auto_renew]]);
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Cập nhật tự động gia hạn thành công.',
|
||||
'data' => ['id' => $service->id, 'auto_renew' => (bool) $service->auto_renew],
|
||||
]);
|
||||
}
|
||||
|
||||
public function renewService(RenewServiceRequest $request, int $id): JsonResponse
|
||||
{
|
||||
$result = $this->orderQueryService->renewService($id, (int) $request->user()->id, (int) $request->validated()['months']);
|
||||
$result = $this->orderQueryService->renewService(
|
||||
$id,
|
||||
(int) $request->user()->id,
|
||||
(int) $request->validated()['months'],
|
||||
);
|
||||
|
||||
if (! $result['ok']) {
|
||||
if (!$result['ok']) {
|
||||
return response()->json(['success' => false, 'message' => $result['message']], $result['code']);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user