From 7f6264c87f7e030bd9e122c92dd160e09c63e150 Mon Sep 17 00:00:00 2001 From: pbao9 Date: Wed, 11 Mar 2026 16:57:26 +0700 Subject: [PATCH] feat: Implement payment proof upload for orders, add payment method selection API, and seed bank transfer payment options. --- app/Console/Commands/UpdateExpiryDate.php | 54 +++++++ .../Api/Admin/Order/OrderController.php | 109 +++++++++---- .../Api/Admin/OsManager/OsController.php | 90 +++++++++++ .../Api/Admin/OsManager/OsGroupController.php | 152 ++++++++++++++++++ .../Api/Admin/Payment/PaymentController.php | 15 ++ .../Api/Admin/Product/ProductController.php | 72 ++++++++- app/Http/Requests/OrderRequest.php | 2 +- .../Resources/Api/Admin/OrderResource.php | 64 +++++++- .../Resources/Api/Admin/ProductResource.php | 5 + app/Models/Order.php | 4 +- app/Models/OrderItem.php | 3 +- app/Models/Payment.php | 4 + app/Models/Product.php | 9 ++ ...00002_add_payment_proof_to_order_table.php | 21 +++ ..._03_11_144540_rename_order_items_table.php | 24 +++ ...5_add_expiry_date_to_order_items_table.php | 28 ++++ .../seeders/BankTransferPaymentSeeder.php | 69 ++++++++ routes/api.php | 30 ++++ ...lYy8R6JFgbqasJy5dfSLxWR1miOvpUIjejIPle.jpg | Bin 0 -> 37529 bytes 19 files changed, 706 insertions(+), 49 deletions(-) create mode 100644 app/Console/Commands/UpdateExpiryDate.php create mode 100644 app/Http/Controllers/Api/Admin/OsManager/OsController.php create mode 100644 app/Http/Controllers/Api/Admin/OsManager/OsGroupController.php create mode 100644 database/migrations/2026_03_11_000002_add_payment_proof_to_order_table.php create mode 100644 database/migrations/2026_03_11_144540_rename_order_items_table.php create mode 100644 database/migrations/2026_03_11_153935_add_expiry_date_to_order_items_table.php create mode 100644 database/seeders/BankTransferPaymentSeeder.php create mode 100644 storage/app/public/payment-proofs/48lYy8R6JFgbqasJy5dfSLxWR1miOvpUIjejIPle.jpg diff --git a/app/Console/Commands/UpdateExpiryDate.php b/app/Console/Commands/UpdateExpiryDate.php new file mode 100644 index 0000000..743c0b7 --- /dev/null +++ b/app/Console/Commands/UpdateExpiryDate.php @@ -0,0 +1,54 @@ +option('dry-run'); + + $query = OrderItem::whereNull('expiry_date') + ->whereNotNull('months') + ->where('months', '>', 0); + + $count = $query->count(); + + if ($count === 0) { + $this->info('No order items need to be updated.'); + return 0; + } + + $this->info("Found {$count} order items to update."); + + if ($dryRun) { + $this->warn('DRY RUN - No changes will be made.'); + $query->chunk(100, function ($items) { + foreach ($items as $item) { + $expiryDate = Carbon::parse($item->created_at)->addMonths($item->months); + $this->line("Order #{$item->order_id}, Item #{$item->id}: would set expiry_date to {$expiryDate}"); + } + }); + } else { + $updated = 0; + $query->chunk(100, function ($items) use (&$updated) { + foreach ($items as $item) { + $expiryDate = Carbon::parse($item->created_at)->addMonths($item->months); + $item->update(['expiry_date' => $expiryDate]); + $updated++; + } + }); + $this->info("Updated {$updated} order items."); + } + + return 0; + } +} diff --git a/app/Http/Controllers/Api/Admin/Order/OrderController.php b/app/Http/Controllers/Api/Admin/Order/OrderController.php index 5d1e874..2739d32 100644 --- a/app/Http/Controllers/Api/Admin/Order/OrderController.php +++ b/app/Http/Controllers/Api/Admin/Order/OrderController.php @@ -22,11 +22,13 @@ use App\Repositories\Module\Vmware\VmwareRepositoryInterface; use App\Repositories\Order\OrderRepositoryInterface; use App\Repositories\Os\OsRepositoryInterface; use App\Repositories\VPS\VPSRepositoryInterface; +use App\Models\Customer; use Carbon\Carbon; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Validator; use Yajra\DataTables\Facades\DataTables; @@ -95,6 +97,14 @@ class OrderController extends Controller { $request->validated(); + $customer = Customer::find($request->customer_id); + if (!$customer || (int) $customer->status !== 1) { + return response()->json([ + 'error' => true, + 'message' => 'Tài khoản khách hàng không hoạt động. Không thể tạo đơn hàng.', + ], 422); + } + $total = 0; $product_item = []; $relid_data = []; @@ -167,28 +177,32 @@ class OrderController extends Controller $total += $item['quantity'] * ($item['price_custom'] ?? ($product->price_sale ?? $product->price)) * $billingDetails['billing']; } + $paymentRecord = \App\Models\Payment::find((int) $request->payment_method); + $isBankTransfer = $paymentRecord && (int) $paymentRecord->type === 1; + $order_item = [ - 'order_num' => Helper::generateUniqueCode(), - 'customer_id' => $request->customer_id, - 'invoice_id' => 0, - 'discount_id' => $request->discount_id ?: null, - 'amount' => $total + ($total * (10 / 100)), - 'payment_method' => $request->payment_method, - 'user_id' => Auth::guard('admin')->id(), - 'status' => $request->status, - 'client_ip' => $request->getClientIp() + 'order_num' => Helper::generateUniqueCode(), + 'customer_id' => $request->customer_id, + 'invoice_id' => 0, + 'discount_id' => $request->discount_id ?: null, + 'amount' => $total + ($total * (10 / 100)), + 'payment_id' => $paymentRecord?->id, + 'payment_proof' => $request->payment_proof ?? null, + 'user_id' => Auth::guard('admin')->id(), + 'status' => $request->status, + 'client_ip' => $request->getClientIp(), ]; $invoice_data = [ - 'date' => Carbon::today()->toDateString(), - 'due_date' => Carbon::today()->toDateString(), - 'date_paid' => (int)$request->status === 1 ? Carbon::today()->toDateString() : null, - 'sub_total' => $total, - 'tax' => $total * (10 / 100), - 'total' => $total + ($total * (10 / 100)), - 'payment_method' => $request->payment_method, + 'date' => Carbon::today()->toDateString(), + 'due_date' => Carbon::today()->toDateString(), + 'date_paid' => (int)$request->status === 1 ? Carbon::today()->toDateString() : null, + 'sub_total' => $total, + 'tax' => $total * (10 / 100), + 'total' => $total + ($total * (10 / 100)), + 'payment_id' => $paymentRecord?->id, 'customer_id' => $request->create_invoice ? $request->customer_id : 0, - 'status' => $request->status + 'status' => $request->status ]; DB::beginTransaction(); @@ -242,30 +256,39 @@ class OrderController extends Controller $this->invoiceItemRepository->insert($invoiceItemData); $now = now(); - \App\Models\OrderItem::insert(array_map(fn($item) => [ - 'order_id' => $order->id, - 'product_id' => $item['product_id'], - 'billing_cycle' => $item['billing_cycle'], - 'months' => $item['months'], - 'os_id' => $item['os_id'], - 'quantity' => $item['quantity'], - 'server_name' => $item['server_name'], - 'price' => $item['price'], - 'created_at' => $now, - 'updated_at' => $now, - ], $product_item)); + \App\Models\OrderItem::insert(array_map(function ($item) use ($now) { + $expiryDate = $now->copy()->addMonths($item['months']); + return [ + 'order_id' => $order->id, + 'product_id' => $item['product_id'], + 'billing_cycle' => $item['billing_cycle'], + 'months' => $item['months'], + 'expiry_date' => $expiryDate, + 'os_id' => $item['os_id'], + 'quantity' => $item['quantity'], + 'server_name' => $item['server_name'], + 'price' => $item['price'], + 'created_at' => $now, + 'updated_at' => $now, + ]; + }, $product_item)); // Dispatch VM creation job for VPS/cloud products $vpsTypes = ['vps', 'cloud']; $createdOrderItems = \App\Models\OrderItem::where('order_id', $order->id)->get(); foreach ($createdOrderItems as $orderItem) { - $pt = $orderItem->product?->product_type ?? $orderItem->billing_cycle; if (in_array($orderItem->product?->product_type, $vpsTypes)) { \App\Jobs\VirtualMachine\Create\CreateVirtualMachineJob::dispatch($orderItem) ->delay(now()->addSeconds(10)); } } + // Deduct customer balance for non-bank-transfer payments + if (!$isBankTransfer) { + Customer::where('id', $request->customer_id) + ->decrement('total', (int) ($total + ($total * (10 / 100)))); + } + DB::commit(); return response()->json([ @@ -284,10 +307,15 @@ class OrderController extends Controller public function show(int $id): JsonResponse { - $order = $this->orderRepository->findWithRelationships( - $id, - ['orderItem', 'user', 'customer.invoiceItem.type', 'payment', 'discount', 'invoice.invoiceItem'] - ); + $order = Order::with([ + 'orderItems.product', + 'orderItems.os', + 'user', + 'customer', + 'payment', + 'discount', + 'invoice' + ])->find($id); if (!$order) { return response()->json([ @@ -501,6 +529,19 @@ class OrderController extends Controller } } + public function uploadProof(Request $request): JsonResponse + { + $request->validate(['file' => 'required|image|max:5120']); + + $path = $request->file('file')->store('payment-proofs', 'public'); + + return response()->json([ + 'error' => false, + 'url' => Storage::disk('public')->url($path), + 'path' => $path, + ]); + } + private function calculateBillingDetails($billing): array { $months = match ($billing) { diff --git a/app/Http/Controllers/Api/Admin/OsManager/OsController.php b/app/Http/Controllers/Api/Admin/OsManager/OsController.php new file mode 100644 index 0000000..7ad38e9 --- /dev/null +++ b/app/Http/Controllers/Api/Admin/OsManager/OsController.php @@ -0,0 +1,90 @@ +validate([ + 'version' => 'required|string|max:100', + 'os_group_id' => 'required|integer|exists:os_group,id', + 'template_id' => 'nullable|string|max:255', + 'is_active' => 'boolean', + ]); + + $os = Os::create([ + 'version' => $validated['version'], + 'os_group_id' => $validated['os_group_id'], + 'template_id' => $validated['template_id'] ?? null, + 'status' => ($validated['is_active'] ?? true) ? 1 : 0, + ]); + + return response()->json([ + 'error' => false, + 'message' => 'Thêm OS thành công.', + 'data' => $this->formatOs($os), + ], 201); + } + + public function update(Request $request, int $id): JsonResponse + { + $os = Os::find($id); + + if (! $os) { + return response()->json(['error' => true, 'message' => 'Không tìm thấy OS.'], 404); + } + + $validated = $request->validate([ + 'version' => 'sometimes|required|string|max:100', + 'os_group_id' => 'sometimes|integer|exists:os_group,id', + 'template_id' => 'nullable|string|max:255', + 'is_active' => 'boolean', + ]); + + $os->update([ + 'version' => $validated['version'] ?? $os->version, + 'os_group_id' => $validated['os_group_id'] ?? $os->os_group_id, + 'template_id' => array_key_exists('template_id', $validated) ? $validated['template_id'] : $os->template_id, + 'status' => isset($validated['is_active']) ? ($validated['is_active'] ? 1 : 0) : $os->status, + ]); + + return response()->json([ + 'error' => false, + 'message' => 'Cập nhật OS thành công.', + 'data' => $this->formatOs($os->fresh()), + ]); + } + + public function destroy(int $id): JsonResponse + { + $os = Os::find($id); + + if (! $os) { + return response()->json(['error' => true, 'message' => 'Không tìm thấy OS.'], 404); + } + + $os->delete(); + + return response()->json(['error' => false, 'message' => 'Xoá OS thành công.']); + } + + // ── helpers ────────────────────────────────────────────────────────────── + + private function formatOs(Os $os): array + { + return [ + 'id' => $os->id, + 'version' => $os->version, + 'template_id' => $os->template_id, + 'is_active' => (bool) $os->status, + 'os_group_id' => $os->os_group_id, + ]; + } +} diff --git a/app/Http/Controllers/Api/Admin/OsManager/OsGroupController.php b/app/Http/Controllers/Api/Admin/OsManager/OsGroupController.php new file mode 100644 index 0000000..09c3204 --- /dev/null +++ b/app/Http/Controllers/Api/Admin/OsManager/OsGroupController.php @@ -0,0 +1,152 @@ +with(['os' => fn($q) => $q->orderBy('version')]) + ->when( + $request->get('filter'), + fn($q, $f) => $q->where('name', 'like', "%{$f}%") + ) + ->orderBy('name'); + + if ($request->boolean('all')) { + $items = $query->get(); + + return response()->json([ + 'error' => false, + 'data' => $items->map(fn($g) => $this->formatGroup($g)), + ]); + } + + $perPage = min((int) $request->get('per_page', 15), 100); + $paginator = $query->paginate($perPage); + + return response()->json([ + 'error' => false, + 'data' => [ + 'items' => collect($paginator->items())->map(fn($g) => $this->formatGroup($g)), + 'total' => $paginator->total(), + 'per_page' => $paginator->perPage(), + 'current_page' => $paginator->currentPage(), + 'last_page' => $paginator->lastPage(), + ], + ]); + } + + public function show(int $id): JsonResponse + { + $group = OsGroup::withCount('os') + ->with(['os' => fn($q) => $q->orderBy('version')]) + ->find($id); + + if (! $group) { + return response()->json(['error' => true, 'message' => 'Không tìm thấy nhóm OS.'], 404); + } + + return response()->json(['error' => false, 'data' => $this->formatGroup($group)]); + } + + public function store(Request $request): JsonResponse + { + $validated = $request->validate([ + 'name' => 'required|string|max:255', + 'is_active' => 'boolean', + ]); + + $group = OsGroup::create([ + 'name' => $validated['name'], + 'status' => ($validated['is_active'] ?? true) ? 1 : 0, + ]); + + return response()->json([ + 'error' => false, + 'message' => 'Tạo nhóm OS thành công.', + 'data' => $this->formatGroup($group->load('os')), + ], 201); + } + + public function update(Request $request, int $id): JsonResponse + { + $group = OsGroup::find($id); + + if (! $group) { + return response()->json(['error' => true, 'message' => 'Không tìm thấy nhóm OS.'], 404); + } + + $validated = $request->validate([ + 'name' => 'sometimes|required|string|max:255', + 'is_active' => 'boolean', + ]); + + $group->update([ + 'name' => $validated['name'] ?? $group->name, + 'status' => isset($validated['is_active']) ? ($validated['is_active'] ? 1 : 0) : $group->status, + ]); + + return response()->json([ + 'error' => false, + 'message' => 'Cập nhật nhóm OS thành công.', + 'data' => $this->formatGroup($group->fresh(['os'])), + ]); + } + + public function destroy(int $id): JsonResponse + { + $group = OsGroup::withCount('os')->find($id); + + if (! $group) { + return response()->json(['error' => true, 'message' => 'Không tìm thấy nhóm OS.'], 404); + } + + if ($group->os_count > 0) { + return response()->json([ + 'error' => true, + 'message' => "Không thể xoá nhóm đang chứa {$group->os_count} OS. Hãy xoá các OS trước.", + ], 422); + } + + $group->delete(); + + return response()->json(['error' => false, 'message' => 'Xoá nhóm OS thành công.']); + } + + // ── helpers ────────────────────────────────────────────────────────────── + + private function formatGroup(OsGroup $group): array + { + return [ + 'id' => $group->id, + 'name' => $group->name, + 'is_active' => (bool) $group->status, + 'os_count' => $group->os_count ?? $group->os->count(), + 'os_list' => $group->relationLoaded('os') + ? $group->os->map(fn($o) => $this->formatOs($o))->values() + : [], + ]; + } + + private function formatOs(\App\Models\Os $os): array + { + return [ + 'id' => $os->id, + 'version' => $os->version, + 'template_id' => $os->template_id, + 'is_active' => (bool) $os->status, + 'os_group_id' => $os->os_group_id, + ]; + } +} diff --git a/app/Http/Controllers/Api/Admin/Payment/PaymentController.php b/app/Http/Controllers/Api/Admin/Payment/PaymentController.php index 5441533..06901a0 100644 --- a/app/Http/Controllers/Api/Admin/Payment/PaymentController.php +++ b/app/Http/Controllers/Api/Admin/Payment/PaymentController.php @@ -26,6 +26,21 @@ class PaymentController extends Controller ]); } + public function forSelect(): JsonResponse + { + $payments = $this->paymentRepository->filter(['status' => 1]); + + return response()->json([ + 'error' => false, + 'data' => $payments->map(fn($p) => [ + 'id' => $p->id, + 'name' => $p->name, + 'type' => (int) $p->type, + 'status' => (int) $p->status, + ])->values(), + ]); + } + public function store(PaymentRequest $request): JsonResponse { $request->validated(); diff --git a/app/Http/Controllers/Api/Admin/Product/ProductController.php b/app/Http/Controllers/Api/Admin/Product/ProductController.php index 8d1cfd4..986d7f5 100644 --- a/app/Http/Controllers/Api/Admin/Product/ProductController.php +++ b/app/Http/Controllers/Api/Admin/Product/ProductController.php @@ -6,6 +6,7 @@ use App\Http\Controllers\Controller; use App\Http\Resources\Api\Admin\ProductResource; use App\Models\Product; use App\Repositories\Admin\Product\AdminProductRepositoryInterface; +use App\Models\Os; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Str; @@ -44,6 +45,9 @@ class ProductController extends Controller return response()->json(['error' => true, 'message' => 'Không tìm thấy sản phẩm.'], 404); } + // Load operatingSystems relationship (product_os table) + $product->load('operatingSystems'); + return response()->json([ 'error' => false, 'data' => new ProductResource($product), @@ -105,11 +109,13 @@ class ProductController extends Controller 'product_categories_id' => 'nullable|integer|exists:product_categories,id', 'product_type' => 'nullable|string', 'is_active' => 'boolean', - 'setup_fee' => 'nullable|numeric|min:0', - 'cpu' => 'nullable|numeric|min:0', - 'ram' => 'nullable|numeric|min:0', - 'disk' => 'nullable|numeric|min:0', - 'bandwidth' => 'nullable|numeric|min:0', + 'setup_fee' => 'nullable|numeric|min:0', + 'cpu' => 'nullable|numeric|min:0', + 'ram' => 'nullable|numeric|min:0', + 'disk' => 'nullable|numeric|min:0', + 'bandwidth' => 'nullable|numeric|min:0', + 'os_ids' => 'nullable|array', + 'os_ids.*' => 'integer|exists:os,id', ]); if (isset($validated['name']) && $validated['name'] !== $product->name) { @@ -121,8 +127,17 @@ class ProductController extends Controller $validated['product_type'] = $category?->type; } + // Handle OS associations via product_os table + $osIds = $validated['os_ids'] ?? null; + unset($validated['os_ids']); + $product->update($validated); + // Sync OS associations in product_os table + if ($osIds !== null) { + $product->operatingSystems()->sync($osIds); + } + return response()->json([ 'error' => false, 'message' => 'Cập nhật sản phẩm thành công.', @@ -151,7 +166,7 @@ class ProductController extends Controller $product = Product::find($id); if (!$product) { - return response()->json(['error' => true, 'message' => 'Không tìm thấy sản phẩm.'], 404); + return response()->json(['error' => true, 'message' => 'Không tìm thấy sản phờng trình.'], 404); } $osList = $product->osVersions() @@ -171,4 +186,49 @@ class ProductController extends Controller 'data' => $osList, ]); } + + public function getOsList(): JsonResponse + { + $osList = Os::with('osGroup') + ->where('status', 1) + ->get() + ->map(fn($os) => [ + 'id' => $os->id, + 'name' => $os->name, + 'version' => $os->version, + 'os_group_name' => $os->osGroup?->name, + 'template_id' => $os->template_id, + ]); + + return response()->json([ + 'error' => false, + 'data' => $osList, + ]); + } + + public function getOperatingSystems(int $id): JsonResponse + { + $product = Product::find($id); + + if (!$product) { + return response()->json(['error' => true, 'message' => 'Không tìm thấy sản phẩm.'], 404); + } + + $osList = $product->operatingSystems() + ->with('osGroup') + ->where('status', 1) + ->get() + ->map(fn($os) => [ + 'id' => $os->id, + 'name' => $os->name, + 'version' => $os->version, + 'os_group_name' => $os->osGroup?->name, + 'template_id' => $os->template_id, + ]); + + return response()->json([ + 'error' => false, + 'data' => $osList, + ]); + } } diff --git a/app/Http/Requests/OrderRequest.php b/app/Http/Requests/OrderRequest.php index 95907b3..71744b9 100644 --- a/app/Http/Requests/OrderRequest.php +++ b/app/Http/Requests/OrderRequest.php @@ -23,7 +23,7 @@ class OrderRequest extends FormRequest { return [ 'customer_id' => 'required', - 'payment_method' => 'required', + 'payment_method' => 'required|integer|min:1', 'status' => 'required', 'product.*.product_id' => 'required|numeric|not__in:0', 'product.*.os_id' => 'nullable|integer', diff --git a/app/Http/Resources/Api/Admin/OrderResource.php b/app/Http/Resources/Api/Admin/OrderResource.php index b9d246d..48aa0cd 100644 --- a/app/Http/Resources/Api/Admin/OrderResource.php +++ b/app/Http/Resources/Api/Admin/OrderResource.php @@ -10,17 +10,71 @@ class OrderResource extends JsonResource public function toArray(Request $request): array { return [ - 'id' => $this->id, + 'id' => $this->id, 'order_num' => $this->order_num, - 'customer' => $this->whenLoaded('customer', fn() => [ + 'customer' => $this->whenLoaded('customer', fn() => [ 'id' => $this->customer->id, 'fullname' => $this->customer->fullname, 'email' => $this->customer->email, + 'phone' => $this->customer->phone ?? null, ]), - 'amount' => $this->amount, - 'status' => $this->status, - 'date' => $this->date, + 'user' => $this->whenLoaded('user', fn() => [ + 'id' => $this->user->id, + 'firstname' => $this->user->firstname, + 'lastname' => $this->user->lastname, + 'email' => $this->user->email, + ]), + 'payment' => $this->whenLoaded('payment', fn() => [ + 'id' => $this->payment->id, + 'name' => $this->payment->name, + 'type' => $this->payment->type, + ]), + 'discount' => $this->whenLoaded('discount', fn() => [ + 'id' => $this->discount->id, + 'code' => $this->discount->code, + 'value' => $this->discount->value, + ]), + 'invoice' => $this->whenLoaded('invoice', fn() => [ + 'id' => $this->invoice->id, + 'order_num' => $this->invoice->order_num, + 'sub_total' => $this->invoice->sub_total, + 'tax' => $this->invoice->tax, + 'total' => $this->invoice->total, + 'status' => $this->invoice->status, + 'date' => $this->invoice->date, + 'due_date' => $this->invoice->due_date, + 'date_paid' => $this->invoice->date_paid, + ]), + 'amount' => $this->amount, + 'payment_method' => $this->whenLoaded('payment', fn() => $this->payment->name), + 'payment_proof' => $this->payment_proof, + 'note' => $this->note, + 'client_ip' => $this->client_ip, + 'status' => $this->status, + 'date' => $this->date, 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + 'order_items' => $this->whenLoaded('orderItems', fn() => $this->orderItems->map(function ($item) { + return [ + 'id' => $item->id, + 'product' => $item->product ? [ + 'id' => $item->product->id, + 'name' => $item->product->name, + 'product_type' => $item->product->product_type, + ] : null, + 'os' => $item->os ? [ + 'id' => $item->os->id, + 'name' => $item->os->name, + 'version' => $item->os->version, + ] : null, + 'billing_cycle' => $item->billing_cycle, + 'months' => $item->months, + 'expiry_date' => $item->expiry_date, + 'quantity' => $item->quantity, + 'server_name' => $item->server_name, + 'price' => $item->price, + ]; + })), ]; } } diff --git a/app/Http/Resources/Api/Admin/ProductResource.php b/app/Http/Resources/Api/Admin/ProductResource.php index a295171..316d6eb 100644 --- a/app/Http/Resources/Api/Admin/ProductResource.php +++ b/app/Http/Resources/Api/Admin/ProductResource.php @@ -13,6 +13,10 @@ class ProductResource extends JsonResource return $this->pricings->min('price'); }); + $osIds = $this->whenLoaded('operatingSystems', function () { + return $this->operatingSystems->pluck('id')->toArray(); + }); + return [ 'id' => $this->id, 'uuid' => $this->uuid, @@ -30,6 +34,7 @@ class ProductResource extends JsonResource 'min_price' => $minPrice, 'is_active' => $this->is_active, 'created_at' => $this->created_at, + 'os_ids' => $osIds, ]; } diff --git a/app/Models/Order.php b/app/Models/Order.php index 1c10913..5bc8292 100644 --- a/app/Models/Order.php +++ b/app/Models/Order.php @@ -46,7 +46,7 @@ class Order extends Model public function payment() { - return $this->belongsTo(\App\Models\Payment::class); + return $this->belongsTo(\App\Models\Payment::class, 'payment_method', 'id'); } public function user() @@ -56,7 +56,7 @@ class Order extends Model public function orderItem() { - return $this->belongsToMany(\App\Models\InvoiceItem::class, 'order__order_item', 'order_id', 'invoice_item_id'); + return $this->belongsToMany(\App\Models\InvoiceItem::class, 'order_items', 'order_id', 'invoice_item_id'); } public function orderItems() diff --git a/app/Models/OrderItem.php b/app/Models/OrderItem.php index 4b88bbd..40c20c1 100644 --- a/app/Models/OrderItem.php +++ b/app/Models/OrderItem.php @@ -9,9 +9,10 @@ class OrderItem extends Model { use HasFactory; - protected $table = 'order__order_item'; + protected $table = 'order_items'; protected $guarded = []; public $timestamps = false; + public function order() { return $this->belongsTo(Order::class, 'order_id'); diff --git a/app/Models/Payment.php b/app/Models/Payment.php index a6bd450..e19d5f5 100644 --- a/app/Models/Payment.php +++ b/app/Models/Payment.php @@ -24,6 +24,10 @@ class Payment extends Model { return $this->belongsTo(\App\Models\Invoice::class); } + public function order() + { + return $this->belongsTo(\App\Models\Order::class); + } public function getStatus() { return match ($this->attributes['status']) { diff --git a/app/Models/Product.php b/app/Models/Product.php index cc46a80..6b4bc6d 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -75,6 +75,15 @@ class Product extends Model ->withTimestamps(); } + /** + * Relationship: Product has many OS via product_os table (VPS OS selection) + */ + public function operatingSystems() + { + return $this->belongsToMany(Os::class, 'product_os', 'product_id', 'os_id') + ->withTimestamps(); + } + /** * Relationship: Product has many Control Panels (for Hosting) */ diff --git a/database/migrations/2026_03_11_000002_add_payment_proof_to_order_table.php b/database/migrations/2026_03_11_000002_add_payment_proof_to_order_table.php new file mode 100644 index 0000000..2d79eef --- /dev/null +++ b/database/migrations/2026_03_11_000002_add_payment_proof_to_order_table.php @@ -0,0 +1,21 @@ +string('payment_proof')->nullable()->after('payment_method'); + }); + } + + public function down(): void + { + Schema::table('order', function (Blueprint $table) { + $table->dropColumn('payment_proof'); + }); + } +}; diff --git a/database/migrations/2026_03_11_144540_rename_order_items_table.php b/database/migrations/2026_03_11_144540_rename_order_items_table.php new file mode 100644 index 0000000..de2224c --- /dev/null +++ b/database/migrations/2026_03_11_144540_rename_order_items_table.php @@ -0,0 +1,24 @@ +timestamp('expiry_date')->nullable()->after('months'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('order_items', function (Blueprint $table) { + $table->dropColumn('expiry_date'); + }); + } +}; diff --git a/database/seeders/BankTransferPaymentSeeder.php b/database/seeders/BankTransferPaymentSeeder.php new file mode 100644 index 0000000..3557fd8 --- /dev/null +++ b/database/seeders/BankTransferPaymentSeeder.php @@ -0,0 +1,69 @@ + 'Số dư tài khoản', + 'type' => 0, + 'accountNo' => '', + 'bankName' => '', + 'bankCode' => '', + 'logo' => '', + 'status' => 1, + ], + [ + 'name' => 'Chuyển khoản ngân hàng', + 'type' => 1, + 'accountNo' => '', + 'bankName' => '', + 'bankCode' => '', + 'logo' => '', + 'status' => 1, + ], + [ + 'name' => 'Chuyển khoản VNPAY', + 'type' => 1, + 'accountNo' => '', + 'bankName' => 'VNPAY', + 'bankCode' => 'VNPAY', + 'logo' => '', + 'status' => 1, + ], + [ + 'name' => 'Chuyển khoản ACB', + 'type' => 1, + 'accountNo' => '', + 'bankName' => 'Ngân hàng ACB', + 'bankCode' => 'ACB', + 'logo' => '', + 'status' => 1, + ], + [ + 'name' => 'Chuyển khoản MB', + 'type' => 1, + 'accountNo' => '', + 'bankName' => 'Ngân hàng MB', + 'bankCode' => 'MB', + 'logo' => '', + 'status' => 1, + ], + ]; + + foreach ($methods as $method) { + DB::table('payment') + ->where('name', $method['name']) + ->exists() ?: DB::table('payment')->insert(array_merge($method, [ + 'created_at' => now(), + 'updated_at' => now(), + ])); + } + } +} diff --git a/routes/api.php b/routes/api.php index 3d1aa91..7afc416 100644 --- a/routes/api.php +++ b/routes/api.php @@ -15,6 +15,8 @@ use App\Http\Controllers\Api\Admin\VPS\VPSController; use App\Http\Controllers\Api\Admin\VCenter\VCenterController; use App\Http\Controllers\Api\Admin\DnsManager\ServerController as DnsServerController; use App\Http\Controllers\Api\Admin\DnsManager\DomainController as DnsDomainController; +use App\Http\Controllers\Api\Admin\OsManager\OsGroupController; +use App\Http\Controllers\Api\Admin\OsManager\OsController; use App\Http\Services\Media; use App\Http\Services\vCenter as ServiceVCenter; use App\Models\ModuleVmware; @@ -92,8 +94,13 @@ Route::prefix('v1') Route::get('/', [ProductController::class, 'index'])->middleware('perm:product-service.view'); Route::post('/', [ProductController::class, 'store'])->middleware('perm:product-service.create'); Route::post('import', [ProductController::class, 'import'])->middleware('perm:product-service.create'); + + // OS list for product configuration - must be before {id} routes + Route::get('os', [ProductController::class, 'getOsList'])->middleware('perm:product-service.view'); + Route::get('{id}', [ProductController::class, 'show'])->middleware('perm:product-service.view'); Route::get('{id}/os', [ProductController::class, 'getOs'])->middleware('perm:product-service.view'); + Route::get('{id}/operating-systems', [ProductController::class, 'getOperatingSystems'])->middleware('perm:product-service.view'); Route::put('{id}', [ProductController::class, 'update'])->middleware('perm:product-service.edit'); Route::delete('{id}', [ProductController::class, 'destroy'])->middleware('perm:product-service.destroy'); Route::post('{id}/duplicate', [ProductController::class, 'duplicate'])->middleware('perm:product-service.create'); @@ -115,6 +122,7 @@ Route::prefix('v1') Route::prefix('orders')->group(function () { Route::get('/', [OrderController::class, 'index'])->middleware('perm:order.view'); Route::get('/datatable', [OrderController::class, 'datatable'])->middleware('perm:order.view'); + Route::post('/upload-proof', [OrderController::class, 'uploadProof'])->middleware('perm:order.create'); Route::post('/', [OrderController::class, 'store'])->middleware('perm:order.create'); Route::get('{id}', [OrderController::class, 'show'])->middleware('perm:order.view'); Route::put('{id}', [OrderController::class, 'update'])->middleware('perm:order.edit'); @@ -143,6 +151,7 @@ Route::prefix('v1') // Payments (module: payment) Route::prefix('payments')->group(function () { + Route::get('for-select', [PaymentController::class, 'forSelect']); Route::get('/', [PaymentController::class, 'index'])->middleware('perm:payment.view'); Route::post('/', [PaymentController::class, 'store'])->middleware('perm:payment.create'); Route::get('{id}', [PaymentController::class, 'show'])->middleware('perm:payment.view'); @@ -150,6 +159,27 @@ Route::prefix('v1') Route::delete('{id}', [PaymentController::class, 'destroy'])->middleware('perm:payment.destroy'); }); + // OS Groups & OS Entries (module: module-vcenter) + Route::prefix('os-groups') + ->controller(OsGroupController::class) + ->as('os-groups.') + ->group(function () { + Route::get('/', 'index')->middleware('perm:module-vcenter.manager')->name('index'); + Route::post('/', 'store')->middleware('perm:module-vcenter.manager')->name('store'); + Route::get('{id}', 'show')->middleware('perm:module-vcenter.manager')->name('show'); + Route::put('{id}', 'update')->middleware('perm:module-vcenter.manager')->name('update'); + Route::delete('{id}', 'destroy')->middleware('perm:module-vcenter.manager')->name('destroy'); + }); + + Route::prefix('os') + ->controller(OsController::class) + ->as('os.') + ->group(function () { + Route::post('/', 'store')->middleware('perm:module-vcenter.manager')->name('store'); + Route::put('{id}', 'update')->middleware('perm:module-vcenter.manager')->name('update'); + Route::delete('{id}', 'destroy')->middleware('perm:module-vcenter.manager')->name('destroy'); + }); + // VPS (module: module-vcenter) Route::prefix('vps')->group(function () { Route::get('/', [VPSController::class, 'index'])->middleware('perm:module-vcenter.view'); diff --git a/storage/app/public/payment-proofs/48lYy8R6JFgbqasJy5dfSLxWR1miOvpUIjejIPle.jpg b/storage/app/public/payment-proofs/48lYy8R6JFgbqasJy5dfSLxWR1miOvpUIjejIPle.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8322426f81a22f537e894664ad0a63a917f2c44c GIT binary patch literal 37529 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 U|42bG${Y=W(GVC8A@Khu07lJ{0ssI2 literal 0 HcmV?d00001