Files
portal-api/app/Http/Controllers/frontend/CartController.php
T
pbao9 057353a2e1 feat: Enhance product support in Cart and OrderItem models
- Added new fields 'billing_cycle' and 'product_pricing_id' to Cart and OrderItem models.
- Updated product retrieval logic to support both new and legacy systems.
- Introduced relationships for Product and ProductPricings in Cart and OrderItem models.
- Created migration to add new fields to the carts and order_item tables.
- Updated frontend service index to handle new pricing structure and billing cycles.
- Improved UI components for better user experience in product selection and pricing display.
- Refactored action buttons in various views for consistency and improved styling.
2026-02-09 08:04:56 +07:00

714 lines
27 KiB
PHP

<?php
namespace App\Http\Controllers\frontend;
use App\Http\Controllers\Controller;
use App\Http\Helpers\Helper;
use App\Http\Services\Cart;
use App\Models\Addon;
use App\Models\Cart as CartModel;
use App\Models\Discount;
use App\Models\Hosting;
use App\Models\Invoice;
use App\Models\InvoiceItem;
use App\Models\ModuleHosting;
use App\Models\ModuleVmware;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Os;
use App\Models\Product;
use App\Models\ProductPricings;
use App\Models\VPS;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
class CartController extends Controller
{
public function index(Cart $cart)
{
$cartItems = $cart->cart(customer()?->id);
$title = "Giỏ hàng";
return view("frontend.cart.index", compact('cartItems', 'title'));
}
public function addCart(Request $request)
{
$userId = customer()?->id;
if (!$userId) {
return back()->with('error', 'Bạn cần đăng nhập để thêm sản phẩm vào giỏ hàng.');
}
// Get request data
$productId = $request->product_id;
$typeId = $request->type_id; // Can be null for new Product system
$months = $request->input('months');
$billingCycle = $request->input('billing_cycle'); // New: billing cycle
$productPrice = $request->input('product_price'); // Price from form
// Validate basic input
if (!$productId) {
return back()->with('error', 'Sản phẩm không hợp lệ.');
}
try {
// Check if this is new Product system or legacy (Hosting/VPS)
$product = Product::find($productId);
if ($product) {
// NEW FLOW: Using Product table
$pricingId = null;
$finalPrice = $productPrice;
// Find the pricing based on billing_cycle
if ($billingCycle) {
$pricing = ProductPricings::where('product_id', $productId)
->where('billing_cycle', $billingCycle)
->where('is_active', 1)
->first();
if ($pricing) {
$pricingId = $pricing->id;
$finalPrice = $pricing->price_sale ?: $pricing->price;
}
}
// Add to cart
CartModel::updateOrCreate(
['customer_id' => $userId],
[
'product_id' => $productId,
'type_product' => null, // New system doesn't use type_product
'months' => $months,
'billing_cycle' => $billingCycle,
'product_pricing_id' => $pricingId,
'price' => $finalPrice,
'quantity' => 1,
]
);
return redirect()->route('cart.index')->with('success', 'Sản phẩm đã được thêm vào giỏ hàng!');
} else {
// LEGACY FLOW: Using old Hosting/VPS system
if (!$typeId || !$months) {
return back()->with('error', 'Dữ liệu không hợp lệ.');
}
CartModel::updateOrCreate(
['customer_id' => $userId],
[
'product_id' => $productId,
'type_product' => $typeId,
'months' => $months,
'price' => $productPrice,
]
);
return redirect()->route('cart.index')->with('success', 'Sản phẩm đã được thêm vào giỏ hàng!');
}
} catch (\Exception $e) {
Log::error('Cart add error: ' . $e->getMessage(), [
'user_id' => $userId,
'product_id' => $productId,
'type_id' => $typeId,
'billing_cycle' => $billingCycle ?? null,
'trace' => $e->getTraceAsString()
]);
return back()->with('error', 'Có lỗi xảy ra khi thêm sản phẩm vào giỏ hàng.');
}
}
public function store(Request $request, Cart $cart)
{
$validator = null;
$allOrderItem = [];
if ($request->vps) {
$validator = Validator::make(
$request->all(),
[
'vps.*.os_id' => 'required',
'vps.*.os_version_id' => 'required',
'vps.*.time_buy' => 'required',
'vps.*.hostname' => 'required'
],
[
'vps.*.os_id' => [
'required' => 'Vui lòng chọn hệ điều hành cần cài đặt!'
],
'vps.*.os_version_id' => [
'required' => 'Vui lòng chọn version cần cài đặt!'
],
'vps.*.hostname' => [
'required' => 'Vui lòng nhập hostname!'
]
]
);
foreach ($request->vps as $_key => $_item) {
$product = $cart->getItem(customer()?->id, $_key);
if ($product) {
$allOrderItem[] = [
'data' => $_item,
'product' => $product
];
}
}
}
if ($request->hosting) {
$validator = Validator::make(
$request->all(),
[
'hosting.*.time_buy' => 'required',
'hosting.*.domain' => 'required'
],
[
'hosting.*.domain' => [
'required' => 'Vui lòng nhập tên miền để kích hoạt Hosting!'
],
'hosting.*.time_buy' => [
'required' => 'Vui lòng chọn thời gian thanh toán!'
],
]
);
$regexFilterDomain = '/(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]/m';
foreach ($request->hosting as $_key => $_item) {
$filter = filter_var(
$request->hosting[$_key]['domain'],
FILTER_VALIDATE_REGEXP,
[
'options' => [
'regexp' => $regexFilterDomain
]
]
);
if ($filter === false) {
return back()->with($request->except(['_token']))->withErrors("Tên miền không hợp lệ");
}
}
foreach ($request->hosting as $_key => $_item) {
$product = $cart->getItem(customer()?->id, $_key);
if ($product) {
$allOrderItem[] = [
'data' => $_item,
'product' => $product
];
}
}
}
if ($validator && $validator->fails()) {
return back()->with($request->except(['_token']))->withErrors($validator);
}
if (!count($allOrderItem)) {
return back()->with($request->except('_token'))->withErrors("Yêu cầu không hợp lệ");
}
$relid_data = [];
foreach ($allOrderItem as $key => $item) {
$relid_code = Helper::generateUniqueCode();
$product = null;
$relid_data[$key]['code'] = $relid_code;
$relid_data[$key]['data'] = [
'customer_id' => customer()?->id,
'status' => 3
];
switch ($item['product']['product_type']) {
case 3:
$product = Hosting::find($item['product']['product']);
$relid_data[$key]['data']['name'] = $item['data']['domain'];
$relid_data[$key]['type'] = 3;
break;
case 4:
$product = VPS::find($item['product']['product']);
$relid_data[$key]['data']['hostname'] = $item['data']['hostname'];
$relid_data[$key]['data']['os_id'] = $item['data']['os_version_id'];
$relid_data[$key]['type'] = 4;
break;
}
$subMessage = "";
$billing = 0;
switch ($item['data']['time_buy']) {
case 1:
$billing = 2;
$subMessage = " ( " . Carbon::today()->format('d/m/Y') . " - " . Carbon::today()->addMonths(1)->format('d/m/Y') . " )";
$relid_data[$key]['data']['next_due_date'] = Carbon::today()->addMonths(1)->timestamp;
$relid_data[$key]['data']['next_invoice'] = Carbon::today()->addMonths(1)->timestamp;
break;
case 3:
$billing = 3;
$subMessage = " ( " . Carbon::today()->format('d/m/Y') . " - " . Carbon::today()->addMonths(3)->format('d/m/Y') . " )";
$relid_data[$key]['data']['next_due_date'] = Carbon::today()->addMonths(3)->timestamp;
$relid_data[$key]['data']['next_invoice'] = Carbon::today()->addMonths(3)->timestamp;
break;
case 6:
$billing = 4;
$subMessage = " ( " . Carbon::today()->format('d/m/Y') . " - " . Carbon::today()->addMonths(6)->format('d/m/Y') . " )";
$relid_data[$key]['data']['next_due_date'] = Carbon::today()->addMonths(6)->timestamp;
$relid_data[$key]['data']['next_invoice'] = Carbon::today()->addMonths(6)->timestamp;
break;
case 12:
$billing = 5;
$subMessage = " ( " . Carbon::today()->format('d/m/Y') . " - " . Carbon::today()->addMonths(12)->format('d/m/Y') . " )";
$relid_data[$key]['data']['next_due_date'] = Carbon::today()->addMonths(12)->timestamp;
$relid_data[$key]['data']['next_invoice'] = Carbon::today()->addMonths(12)->timestamp;
break;
}
$totalAddon = 0;
$message = $product->name;
$product_item[] = [
'customer_id' => customer()?->id,
'type_id' => $item['product']['product_type'],
'product_id' => $item['product']['product'],
'description' => $message . $subMessage,
'amount' => $item['product']['price'] * $item['data']['time_buy'],
'billing' => $billing,
'invoice_type' => 1,
'tax' => 1,
'parent_id' => 0,
'code' => $relid_code,
'due_date' => Carbon::today()->hour(23)->minute(59)->second(59)->timestamp,
];
if (isset($item['data']['addon'])) {
foreach ($item['data']['addon'] as $_key => $_sub) {
$productAddon = Addon::where('name', 'like', "%$_key%")->first();
$message = "Addon " . $productAddon->name;
if ($_sub['value']) {
$totalAddon += $_sub['price'] * $_sub['value'] * $item['data']['time_buy'];
$product_item[$key]['addon'][] = [
'customer_id' => customer()?->id,
'type_id' => $productAddon->type_id,
'product_id' => $productAddon->id,
'description' => $message . $subMessage,
'amount' => $_sub['price'] * $_sub['value'] * $item['data']['time_buy'],
'billing' => $billing,
'invoice_type' => 1,
'tax' => 1,
'due_date' => Carbon::today()->hour(23)->minute(59)->second(59)->timestamp,
];
}
}
}
$relid_data[$key]['data']['first_payment'] = $item['product']['price'] * $item['data']['time_buy'] + $totalAddon;
$relid_data[$key]['data']['next_payment'] = $item['product']['price'] * $item['data']['time_buy'] + $totalAddon;
}
$allOrderItemWithAddon = array_map(function ($item) {
$codeCheckAddon = Helper::generateUniqueCode();
$item['addon_code'] = $codeCheckAddon;
return $item;
}, $product_item);
$addonArray = [];
foreach ($allOrderItemWithAddon as $item) {
if (isset($item['addon'])) {
foreach ($item['addon'] as $_Sub) {
$_Sub['parent'] = $item['addon_code'];
$addonArray[] = $_Sub;
}
}
}
$allOrderItemWithAddon = array_map(function ($item) {
if (isset($item['addon'])) {
unset($item['addon']);
}
return $item;
}, array_merge($addonArray, $allOrderItemWithAddon));
$total = array_sum(array_map(function ($item) {
return $item['amount'];
}, $allOrderItemWithAddon));
$discount = 0;
$discount_value = 0;
if (session('coupon')) {
$discount = session('coupon')['coupon_id'];
switch (session('coupon')['coupon_type']) {
case 1:
$discount_value = (int) ((session('coupon')['coupon_value'] / 100) * $total);
$total -= $discount_value;
break;
case 2:
$discount_value = (int) session('coupon')['coupon_value'];
$total -= $discount_value;
break;
}
}
try {
DB::beginTransaction();
$invoice_data = [
'date' => Carbon::today()->timestamp,
'due_date' => Carbon::today()->hour(23)->minute(59)->second(59)->timestamp,
'sub_total' => $total,
'tax' => $total * (10 / 100),
'total' => $total + ($total * (10 / 100)),
'payment_id' => 1,
'customer_id' => customer()?->id,
'status' => 0
];
$invoice_id = Invoice::insertGetId($invoice_data);
$allOrderItemWithAddon = array_map(function ($array) use ($invoice_id) {
$array['invoice_id'] = $invoice_id;
return $array;
}, $allOrderItemWithAddon);
$order_item = [
'order_num' => Helper::generateUniqueCode(),
'customer_id' => customer()?->id,
'date' => Carbon::today()->timestamp,
'invoice_id' => $invoice_id,
'discount_id' => $discount,
'discount_value' => $discount_value,
'amount' => $total + ($total * (10 / 100)),
'payment_id' => 1,
'user_id' => 0,
'status' => 0,
'client_ip' => $request->getClientIp()
];
$order = Order::insertGetId($order_item);
$relid_data = array_map(function ($array) use ($order) {
$array['data']['order_id'] = $order;
return $array;
}, $relid_data);
$relid = [];
//insert + get id for item auto created by module
foreach ($relid_data as $item) {
$code = $item['code'];
switch ($item['type']) {
case 3:
$relid_id = ModuleHosting::insertGetId($item['data']);
$relid[$code] = $relid_id;
break;
case 4:
$relid_id = ModuleVmware::insertGetId($item['data']);
$relid[$code] = $relid_id;
break;
}
}
$allInvoiceItem = array_map(function ($array) use ($relid) {
if (isset($array['code']) && array_key_exists($array['code'], $relid)) {
$array['relid'] = $relid[$array['code']];
} else {
$array['relid'] = 0;
}
unset($array['code']);
return $array;
}, $allOrderItemWithAddon);
$productNoAddon = array_values(array_filter($allInvoiceItem, function ($item) {
if (isset($item['addon_code'])) {
return $item;
}
}));
$productAddon = array_values(array_filter($allInvoiceItem, function ($item) {
if (!isset($item['addon_code'])) {
return $item;
}
}));
$allProduct = [];
foreach ($productNoAddon as $item) {
foreach ($productAddon as $_sub) {
if ($item['addon_code'] === $_sub['parent']) {
$_Sub['parent_id'] = $item['relid'];
$_sub['parent_id'] = $item['relid'];
unset($_sub['parent']);
$allProduct[] = $_sub;
}
}
if (isset($item['addon_code'])) {
unset($item['addon_code']);
}
$allProduct[] = $item;
}
InvoiceItem::insert($allProduct);
$last_id = InvoiceItem::latest('id')->limit(count($allProduct))->select('id')->get()->toArray();
$Order__order_item = array_map(function ($array) use ($order) {
return [
'order_id' => $order,
'InvoiceItem_id' => $array['id']
];
}, $last_id);
DB::table('order__order_item')->insert($Order__order_item);
Log::create([
'agent' => $request->userAgent(),
'client_ip' => $request->getClientIp(),
'username' => "System",
'description' => customer()?->firstname . " created new Order with Order id: " . $order,
'customer_id' => customer()?->id
]);
if (session('coupon')) {
Discount::find(session('coupon')['coupon_id'])->increment('use', 1);
}
DB::commit();
} catch (Exception $exception) {
DB::rollBack();
return back()->with($request->except(['_token']))->withErrors($exception->getMessage());
}
session()->forget('cart');
session()->forget('coupon');
return redirect()->route('invoice.show', ['invoice_id' => $invoice_id])->with('sucess', 'Đặt hàng thành công');
}
public function destroy(Request $request, Cart $cart)
{
$validator = Validator::make(
$request->all(),
[
'cart_id' => 'required'
],
[
'cart_id' => [
'required' => 'Yêu cầu không hợp lệ'
]
]
);
if ($validator->fails()) {
return back()->withErrors($validator);
}
$status = $cart->removeCartItem(customer()?->id, $request->cart_id);
if ($status) {
return back()->with('success', 'Xoá thông tin đơn hàng thành công');
}
return back()->withErrors('Yêu cầu không hợp lệ');
}
public function applyCoupon(Request $request)
{
$validator = Validator::make(
$request->all(),
[
'promotion_code' => 'required'
],
[
'promotion_code' => [
'required' => 'Mã giảm giá không được để trống'
]
]
);
if ($validator->fails()) {
return redirect()
->route('cart')
->with($request->except(['_token']))
->withErrors($validator);
}
$Coupon = Discount::where('code', $request->promotion_code)->first();
if (!$Coupon) {
return redirect()
->route('cart')
->with($request->except(['_token']))
->withErrors("Mã giảm giá không hợp lệ");
}
if ($Coupon->status === 0) {
return redirect()
->route('cart')
->with($request->except(['_token']))
->withErrors("Mã giảm giá không hợp lệ");
}
if ($Coupon->end < Carbon::today()->timestamp) {
return redirect()
->route('cart')
->with($request->except(['_token']))
->withErrors("Mã giảm giá đã hết hạn sử dụng");
}
if ($Coupon->start > Carbon::today()->timestamp) {
return redirect()
->route('cart')
->with($request->except(['_token']))
->withErrors("Mã giảm giá không hợp lệ");
}
if ($Coupon->use === $Coupon->count) {
return redirect()
->route('cart')
->with($request->except(['_token']))
->withErrors("Mã giảm giá đã đạt giới hạn số lượng cho phép");
}
$couponAdded = [
'coupon_value' => $Coupon->discount,
'coupon_type' => $Coupon->type,
'coupon_code' => $Coupon->code,
'coupon_id' => $Coupon->id
];
session()->put('coupon', $couponAdded);
return redirect()
->route('cart')
->with($request->except(['_token']))
->with('success', 'Áp dụng mã giảm thành công!');
}
public function addon(Request $request)
{
$validator = Validator::make($request->all(), [
'product' => 'required|not__in:0'
], ['product.required' => 'Yêu cầu không hợp lệ']);
if ($validator->fails()) {
return response()->json(['error' => true, 'message' => $validator]);
}
$count = 0;
$sumAddon = 0;
$addOn = [];
$billing = 0;
if ($request->addon) {
$i = 0;
foreach ($request->addon as $key => $item) {
if ($item['quantity'] != 0) {
$product = Addon::where('name', 'like', "%$key%")->first();
$addOn[$i] = [
'customer_id' => customer()?->id,
'type_id' => 1,
'due_date' => Carbon::today()->hour(23)->minute(59)->second(59)->timestamp,
'product_id' => $product->id,
'parent_id' => $request->product,
'relid' => 0,
'invoice_type' => "1",
'billing' => $request->billing,
'amount' => $item['price'] * $item['quantity'],
'tax' => 1,
];
$message = "Addon " . strtoupper($key);
switch ($request->billing) {
case 2:
$billing = 1;
$message .= " ( " . Carbon::today()->format('d/m/Y') . " - " . Carbon::today()->addMonths(1)->format('d/m/Y') . " )";
break;
case 3:
$billing = 3;
$message .= " ( " . Carbon::today()->format('d/m/Y') . " - " . Carbon::today()->addMonths(3)->format('d/m/Y') . " )";
break;
case 4:
$billing = 6;
$message .= " ( " . Carbon::today()->format('d/m/Y') . " - " . Carbon::today()->addMonths(6)->format('d/m/Y') . " )";
break;
case 5:
$billing = 12;
$message .= " ( " . Carbon::today()->format('d/m/Y') . " - " . Carbon::today()->addMonths(12)->format('d/m/Y') . " )";
break;
}
$addOn[$i]['description'] = $message;
}
if ($item['quantity'] == 0) {
++$count;
}
$sumAddon += $item['price'] * $item['quantity'];
$i++;
}
}
$sumAddon *= $billing;
if ($count === 3) {
return response()->json(['error' => true, 'message' => 'Vui lòng chọn một addon để nâng cấp cấu hình!']);
}
$product = ModuleVmware::with('Order')->find($request->product);
if (!$product) {
return response()->json(['error' => true, 'message' => 'Yêu cầu không hợp lệ']);
}
$invoice_data = [
'date' => Carbon::today()->timestamp,
'due_date' => Carbon::today()->hour(23)->minute(59)->second(59)->timestamp,
'sub_total' => $sumAddon,
'tax' => $sumAddon * (10 / 100),
'total' => $sumAddon + ($sumAddon * (10 / 100)),
'payment_id' => 1,
'customer_id' => customer()?->id,
'status' => 0
];
try {
DB::beginTransaction();
$invoice = Invoice::insertGetId($invoice_data);
$order = [
'order_num' => Helper::generateUniqueCode(),
'customer_id' => customer()?->id,
'date' => Carbon::today()->timestamp,
'invoice_id' => $invoice,
'discount_id' => 0,
'discount_value' => 0,
'amount' => $sumAddon + ($sumAddon * (10 / 100)),
'payment_id' => 1,
'user_id' => 0,
'status' => 0,
'client_ip' => $request->getClientIp()
];
$order_id = Order::insertGetId($order);
$AddonItem = array_map(function ($item) use ($invoice) {
$item['invoice_id'] = $invoice;
return $item;
}, $addOn);
InvoiceItem::insert($AddonItem);
$Order_item = InvoiceItem::select('id')->latest('id')->limit(count($AddonItem))->get();
foreach ($Order_item as $item) {
DB::table('order__order_item')->insert([
'InvoiceItem_id' => $item->id,
'order_id' => $order_id
]);
}
DB::commit();
} catch (Exception $exception) {
DB::rollBack();
return response()->json(['error' => true, 'message' => $exception->getMessage()]);
}
return response()->json(['error' => false, 'message' => 'Order thành công. Vui lòng thanh toán để tiến hành nâng cấp!']);
}
}