ipdate
This commit is contained in:
@@ -23,6 +23,7 @@ use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Validator as ValidatorInstance;
|
||||
use Illuminate\Support\Facades\Response;
|
||||
use Illuminate\Support\Facades\Validator as FacadesValidator;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ServiceController extends Controller
|
||||
{
|
||||
@@ -628,78 +629,7 @@ class ServiceController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renew service for selected months
|
||||
*/
|
||||
public function renew(Request $request)
|
||||
{
|
||||
try {
|
||||
$user = customer();
|
||||
if (!$user) {
|
||||
return response()->json([
|
||||
'error' => true,
|
||||
'message' => __('Chưa đăng nhập. Vui lòng đăng nhập để sử dụng tính năng này.'),
|
||||
], 401);
|
||||
}
|
||||
|
||||
$validated = FacadesValidator::make($request->all(), [
|
||||
'product_id' => 'required|string|max:255',
|
||||
'plan' => 'required|string|in:3,6,12,24,other',
|
||||
'months' => 'nullable|integer|min:1|max:60',
|
||||
]);
|
||||
|
||||
if ($validated->fails()) {
|
||||
return response()->json([
|
||||
'error' => true,
|
||||
'message' => __('Dữ liệu không hợp lệ'),
|
||||
'errors' => $validated->errors(),
|
||||
], 422);
|
||||
}
|
||||
|
||||
$months = $request->plan === 'other' ? (int) ($request->months ?? 1) : (int) $request->plan;
|
||||
$productId = $this->sanitizeInput($request->product_id);
|
||||
|
||||
// Fetch product price based on invoice item link
|
||||
$invoiceItem = InvoiceItem::whereHas('invoice', function ($q) use ($user) {
|
||||
$q->where('customer_id', $user->id);
|
||||
})
|
||||
->where('product_type', 4) // VPS
|
||||
->whereHas('invoice', fn($q) => $q->where('status', '!=', 0))
|
||||
->first();
|
||||
|
||||
$product = $invoiceItem?->getProductAttribute();
|
||||
$unitPrice = (int) ($product?->price_sale ?? $product?->price ?? 0);
|
||||
$total = $unitPrice * max($months, 1);
|
||||
|
||||
Log::info('Khách yêu cầu gia hạn dịch vụ', [
|
||||
'user_id' => $user->id,
|
||||
'product_vmware_id' => $productId,
|
||||
'months' => $months,
|
||||
'unit_price' => $unitPrice,
|
||||
'total' => $total,
|
||||
]);
|
||||
|
||||
// TODO: implement actual renewal billing/workflow
|
||||
return response()->json([
|
||||
'error' => false,
|
||||
'message' => __('Yêu cầu gia hạn đã được ghi nhận'),
|
||||
'data' => [
|
||||
'unit_price' => $unitPrice,
|
||||
'months' => $months,
|
||||
'total' => $total,
|
||||
],
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Lỗi renew', [
|
||||
'user_id' => customer()?->id,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
return response()->json([
|
||||
'error' => true,
|
||||
'message' => __('Có lỗi xảy ra khi gia hạn'),
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Secure remove snapshot using session authentication - NO BEARER TOKEN
|
||||
@@ -1316,4 +1246,238 @@ class ServiceController extends Controller
|
||||
|
||||
return $processed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate renewal price (AJAX)
|
||||
*/
|
||||
public function renewPrice(Request $request)
|
||||
{
|
||||
try {
|
||||
$user = customer();
|
||||
if (!$user) {
|
||||
return response()->json(['error' => true, 'message' => __('Vui lòng đăng nhập.')], 401);
|
||||
}
|
||||
|
||||
$vmware_id = $this->sanitizeInput($request->product_id);
|
||||
$plan = $request->input('plan');
|
||||
$months = $plan === 'other' ? (int) $request->input('months') : (int) $plan;
|
||||
|
||||
if ($months < 1)
|
||||
$months = 1;
|
||||
|
||||
$vm = ModuleVmware::where('vmware_id', $vmware_id)
|
||||
->where('customer_id', $user->id)
|
||||
->first();
|
||||
|
||||
if (!$vm) {
|
||||
return response()->json(['error' => true, 'message' => __('Không tìm thấy dịch vụ.')], 404);
|
||||
}
|
||||
|
||||
$order = Order::find($vm->order_id);
|
||||
if (!$order) {
|
||||
return response()->json(['error' => true, 'message' => __('Không tìm thấy đơn hàng gốc.')], 404);
|
||||
}
|
||||
|
||||
// Find OrderItem in order__order_item
|
||||
// Rule: product_type = 4 (VPS) or 3 (Hosting)
|
||||
// Ideally we match the product_id too, but ModuleVmware may not store original product_id directly except via relation?
|
||||
// ModuleVmware has no 'product_id' column in the schema we saw, but it has 'order_id'.
|
||||
// Let's assume we look for the item that created this VM.
|
||||
// Since we are in ServiceController for VM, we look for Type 4 (VPS).
|
||||
|
||||
$orderItem = \App\Models\OrderItem::where('order_id', $order->id)
|
||||
->where('product_type', 4) // VPS
|
||||
->first();
|
||||
|
||||
if (!$orderItem) {
|
||||
// Fallback or error
|
||||
return response()->json(['error' => true, 'message' => __('Không tìm thấy thông tin sản phẩm trong đơn hàng.')], 404);
|
||||
}
|
||||
|
||||
// Get Product Price
|
||||
$product = null;
|
||||
if ($orderItem->product_type == 4) {
|
||||
$product = \App\Models\VPS::find($orderItem->product_id);
|
||||
} elseif ($orderItem->product_type == 3) {
|
||||
$product = \App\Models\Hosting::find($orderItem->product_id);
|
||||
}
|
||||
|
||||
if (!$product) {
|
||||
return response()->json(['error' => true, 'message' => __('Sản phẩm không còn tồn tại.')], 404);
|
||||
}
|
||||
|
||||
// Priority: Price Sale -> Price
|
||||
$base_unit_price = (float) ($product->price_sale > 0 ? $product->price_sale : $product->price);
|
||||
|
||||
// Calculate Addons Price
|
||||
// Look into order__order_addons via OrderItem relationship
|
||||
$addons_unit_price = 0;
|
||||
if ($orderItem->addons && $orderItem->addons->count() > 0) {
|
||||
foreach ($orderItem->addons as $addonPivot) {
|
||||
// $addonPivot is OrderAddon model
|
||||
// We need current price of the addon? User said "lấy giá đã lưu" (take stored price)
|
||||
// BUT for renewal, usually we take CURRENT price of the addon or the STORED price?
|
||||
// User said: "lấy sản order hiện tại kèm các addon nếu có lấy giá đã lưu * với số tháng"
|
||||
// "giá đã lưu" implies the price stored in the order/product table?
|
||||
// But then user said: "nếu product có price_sale thì ưu tiên lấy price_sale còn lại thì lấy price".
|
||||
// This implies looking at the ACTUAL Product table for the base Item.
|
||||
|
||||
// For Addons: "kèm các addon nếu có".
|
||||
// Let's look at the Addon model (Current Price).
|
||||
if ($addonPivot->addonItems) {
|
||||
$addons_unit_price += (float) ($addonPivot->addonItems->price);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$total_unit_price = $base_unit_price + $addons_unit_price;
|
||||
$total = $total_unit_price * $months;
|
||||
|
||||
return response()->json([
|
||||
'error' => false,
|
||||
'data' => [
|
||||
'unit_price' => $total_unit_price,
|
||||
'months' => $months,
|
||||
'total' => $total
|
||||
]
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error("Error calculating renew price: " . $e->getMessage());
|
||||
return response()->json(['error' => true, 'message' => $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renew VPS Service
|
||||
*/
|
||||
public function renew(Request $request)
|
||||
{
|
||||
try {
|
||||
$user = customer();
|
||||
if (!$user) {
|
||||
return redirect()->route('user.login')->with('error', __('Vui lòng đăng nhập.'));
|
||||
}
|
||||
|
||||
// 1. Validate Input
|
||||
$validator = FacadesValidator::make($request->all(), [
|
||||
'product_id' => 'required|string',
|
||||
'plan' => 'required',
|
||||
'months' => 'nullable|integer|min:2|max:60'
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return redirect()->back()->withErrors($validator)->withInput()->with('error', __('Dữ liệu không hợp lệ.'));
|
||||
}
|
||||
|
||||
$vmware_id = $this->sanitizeInput($request->product_id);
|
||||
$plan = $request->input('plan');
|
||||
$months = $plan === 'other' ? (int) $request->input('months') : (int) $plan;
|
||||
|
||||
if ($months < 2) {
|
||||
return redirect()->back()->with('error', __('Thời hạn gia hạn tối thiểu là 2 tháng.'));
|
||||
}
|
||||
|
||||
// 2. Find Service & Verify Ownership
|
||||
$vm = ModuleVmware::where('vmware_id', $vmware_id)
|
||||
->where('customer_id', $user->id)
|
||||
->first();
|
||||
|
||||
if (!$vm) {
|
||||
return redirect()->back()->with('error', __('Không tìm thấy dịch vụ hoặc bạn không có quyền truy cập.'));
|
||||
}
|
||||
|
||||
// 3. Calculate Price based on Original Order -> OrderItem
|
||||
$order = Order::find($vm->order_id);
|
||||
if (!$order) {
|
||||
return redirect()->back()->with('error', __('Không tìm thấy đơn hàng gốc của dịch vụ này.'));
|
||||
}
|
||||
|
||||
$orderItem = \App\Models\OrderItem::where('order_id', $order->id)
|
||||
->where('product_type', 4) // VPS
|
||||
->first();
|
||||
|
||||
if (!$orderItem) {
|
||||
return redirect()->back()->with('error', __('Không tìm thấy thông tin sản phẩm trong đơn hàng.'));
|
||||
}
|
||||
|
||||
// Get Product Price
|
||||
$product = null;
|
||||
if ($orderItem->product_type == 4) {
|
||||
$product = \App\Models\VPS::find($orderItem->product_id);
|
||||
} elseif ($orderItem->product_type == 3) {
|
||||
$product = \App\Models\Hosting::find($orderItem->product_id);
|
||||
}
|
||||
|
||||
if (!$product) {
|
||||
return redirect()->back()->with('error', __('Sản phẩm không còn tồn tại.'));
|
||||
}
|
||||
|
||||
// Priority: Price Sale -> Price
|
||||
$base_unit_price = (float) ($product->price_sale > 0 ? $product->price_sale : $product->price);
|
||||
|
||||
// Calculate Addons Price
|
||||
$addons_unit_price = 0;
|
||||
if ($orderItem->addons && $orderItem->addons->count() > 0) {
|
||||
foreach ($orderItem->addons as $addonPivot) {
|
||||
if ($addonPivot->addonItems) {
|
||||
$addons_unit_price += (float) ($addonPivot->addonItems->price);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$total_unit_price = $base_unit_price + $addons_unit_price;
|
||||
$total_cost = $total_unit_price * $months;
|
||||
|
||||
// 4. Check Balance
|
||||
if ($user->credit < $total_cost) {
|
||||
return redirect()->back()->with('error', __('Số dư tài khoản không đủ để gia hạn (Cần: :amount). Vui lòng nạp thêm tiền.', ['amount' => number_format($total_cost) . ' VNĐ']));
|
||||
}
|
||||
|
||||
// 5. Process Renewal
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
// Deduct Credit
|
||||
$user->decrement('credit', $total_cost);
|
||||
|
||||
// Create Renewal Invoice (Paid)
|
||||
$newInvoice = Invoice::create([
|
||||
'customer_id' => $user->id,
|
||||
'status' => 1, // Paid
|
||||
'date' => now()->timestamp, // Using timestamp as per schema
|
||||
'due_date' => now()->timestamp,
|
||||
'total' => $total_cost,
|
||||
// 'payment_method' => 'credit_balance', // Schema doesn't have this, maybe note?
|
||||
'note' => "Gia hạn dịch vụ VPS #{$vm->name} ({$months} tháng)"
|
||||
]);
|
||||
|
||||
// Update VM Expiry
|
||||
$current_due = $vm->next_due_date ? \Carbon\Carbon::parse($vm->next_due_date) : now();
|
||||
$new_due_date = $current_due->copy()->addMonths($months);
|
||||
|
||||
$vm->update([
|
||||
'next_due_date' => $new_due_date,
|
||||
'status' => 1,
|
||||
]);
|
||||
|
||||
// TODO: Update OrderItem duration/dates if needed? Or just create new invoice record?
|
||||
// The prompt was about "gia hạn thêm sản phẩm" (add renewal product?), but generally it just extends the service.
|
||||
|
||||
DB::commit();
|
||||
|
||||
// Clear cache
|
||||
Cache::forget("vm_info_{$vm->vmware_id}");
|
||||
|
||||
return redirect()->back()->with('success', __('Gia hạn dịch vụ thành công! Hạn sử dụng mới: :date', ['date' => $new_due_date->format('d/m/Y')]));
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error("Error renewing VM: " . $e->getMessage());
|
||||
return redirect()->back()->with('error', __('Có lỗi xảy ra khi gia hạn dịch vụ. Vui lòng thử lại sau.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user