Files
Additional/app/Http/Controllers/CustomOrderController.php
T
2025-12-29 14:43:24 +02:00

317 lines
11 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\CustomOrder;
use App\Models\CustomOrderFile;
use App\Models\CustomOrderSpecification;
use App\Models\AppSetting;
use App\Models\PrintStock;
use Illuminate\Http\Request;
use Illuminate\View\View;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Http;
class CustomOrderController extends Controller
{
/**
* Show custom orders index/list
*/
public function index(): View
{
$customOrders = CustomOrder::where('user_id', auth()->id())
->orderBy('created_at', 'desc')
->get();
return view('custom-orders.index', [
'customOrders' => $customOrders,
]);
}
/**
* Show custom order creation form
*/
public function create(): View
{
$designFee = AppSetting::get('design_fee', 500);
return view('custom-orders.create', [
'printStocks' => PrintStock::all(),
'designFee' => $designFee,
]);
}
/**
* Store custom order and calculate quote
*/
public function store(Request $request)
{
error_log('=== CUSTOM ORDER STORE METHOD CALLED (error_log) ===');
error_log('Request method: ' . $request->method());
error_log('Request path: ' . $request->path());
error_log('Request all data: ' . json_encode($request->all()));
$userId = auth()->id();
error_log('User ID: ' . ($userId ?? 'NOT AUTHENTICATED'));
Log::warning('=== CUSTOM ORDER STORE METHOD CALLED ===');
Log::warning('Request all data', $request->all());
Log::warning('User ID', ['user_id' => auth()->id()]);
$validated = $request->validate([
'type' => 'required|in:wallpaper,mural,fabric',
'length' => 'nullable|numeric|min:0.1',
'width' => 'nullable|numeric|min:0.1',
'height' => 'nullable|numeric|min:0.1',
'print_stock_id' => 'required|exists:print_stocks,id',
'quantity' => 'required|integer|min:1',
'customer_brief' => 'required|string|min:50|max:5000',
'special_instructions' => 'nullable|string|max:1000',
'library_discount' => 'boolean',
'reference_images.*' => 'nullable|file|mimes:jpeg,png,jpg,gif,webp|max:5120',
]);
Log::warning('Validation passed', $validated);
// Get design fee from settings
$designFee = AppSetting::get('design_fee', 500);
$libraryDiscountApplied = $request->boolean('library_discount', false);
// Deduct library discount if selected
$finalDesignFee = $libraryDiscountApplied ? $designFee * 0.8 : $designFee;
// Get print stock for material cost calculation
$printStock = PrintStock::find($validated['print_stock_id']);
// Calculate material cost based on type and dimensions
$materialCost = $this->calculateMaterialCost(
$validated['type'],
$validated['length'] ?? 0,
$validated['width'] ?? 0,
$validated['height'] ?? 0,
$validated['quantity'],
$printStock
);
// Total cost = material cost + design fee
$totalCost = $materialCost + $finalDesignFee;
// Deposit is 20% of total, balance is 80%
$depositAmount = $totalCost * 0.2;
$balanceAmount = $totalCost * 0.8;
// Create custom order
$customOrder = CustomOrder::create([
'user_id' => auth()->id(),
'type' => $validated['type'],
'design_fee' => $designFee,
'library_discount_applied' => $libraryDiscountApplied,
'material_cost' => $materialCost,
'total_cost' => $totalCost,
'deposit_amount' => $depositAmount,
'balance_amount' => $balanceAmount,
'customer_brief' => $validated['customer_brief'],
]);
// Create specifications
CustomOrderSpecification::create([
'custom_order_id' => $customOrder->id,
'length' => $validated['length'] ?? null,
'width' => $validated['width'] ?? null,
'height' => $validated['height'] ?? null,
'print_stock_id' => $validated['print_stock_id'],
'quantity' => $validated['quantity'],
'special_instructions' => $validated['special_instructions'] ?? null,
]);
// Handle file uploads
if ($request->hasFile('reference_images')) {
foreach ($request->file('reference_images') as $file) {
$path = $file->store('custom-orders/references', 'local');
CustomOrderFile::create([
'custom_order_id' => $customOrder->id,
'file_type' => 'reference_image',
'file_path' => $path,
'original_filename' => $file->getClientOriginalName(),
'file_size' => $file->getSize(),
'mime_type' => $file->getMimeType(),
'uploaded_by' => auth()->id(),
]);
}
}
return redirect()->route('custom-orders.show', $customOrder)->with('success', 'Custom order created successfully. Please review the quote and proceed with deposit payment.');
}
/**
* Show custom order detail with quote
*/
public function show(CustomOrder $customOrder): View
{
// Check authorization
if ($customOrder->user_id !== auth()->id()) {
abort(403);
}
return view('custom-orders.show', [
'customOrder' => $customOrder,
]);
}
/**
* Process deposit payment
*/
public function depositPayment(Request $request)
{
$validated = $request->validate([
'custom_order_id' => 'required|exists:custom_orders,id',
]);
$customOrder = CustomOrder::findOrFail($validated['custom_order_id']);
// Check authorization
if ($customOrder->user_id !== auth()->id()) {
abort(403);
}
// Check if already paid
if ($customOrder->deposit_status === 'paid') {
return redirect()->route('custom-orders.show', $customOrder)
->with('info', 'Deposit already paid for this order.');
}
// Initiate Yoco payment for deposit
$yocoResponse = $this->initiateYocoPayment(
amount: (int)($customOrder->deposit_amount * 100), // Convert to cents
orderId: $customOrder->uuid,
orderType: 'custom_deposit',
description: "Deposit for Custom {$customOrder->type} Order #{$customOrder->order_number}"
);
if (!$yocoResponse) {
return redirect()->route('custom-orders.show', $customOrder)
->with('error', 'Failed to initiate payment. Please try again.');
}
return redirect($yocoResponse['checkout_url']);
}
/**
* Handle successful deposit payment
*/
public function depositSuccess(CustomOrder $customOrder)
{
// Check authorization
if ($customOrder->user_id !== auth()->id()) {
abort(403);
}
// Update order status
$customOrder->update([
'deposit_status' => 'paid',
'status' => 'submitted',
]);
return view('custom-orders.deposit-success', [
'customOrder' => $customOrder,
]);
}
/**
* Calculate material cost based on dimensions and print stock
*/
private function calculateMaterialCost($type, $length, $width, $height, $quantity, $printStock)
{
$costPerUnit = $printStock->cost_per_meter ?? $printStock->cost_per_m2 ?? 0;
if (!$costPerUnit) {
return 0;
}
$cost = 0;
if ($type === 'wallpaper' && $length > 0 && $width > 0) {
// Area-based: length x width in meters
$area = $length * $width;
$cost = $area * $costPerUnit * $quantity;
} elseif ($type === 'mural' && $width > 0 && $height > 0) {
// Area-based: width x height in meters
$area = $width * $height;
$cost = $area * $costPerUnit * $quantity;
} elseif ($type === 'fabric' && $length > 0) {
// Linear: length in meters
$cost = $length * $costPerUnit * $quantity;
}
return round($cost, 2);
}
/**
* Initiate Yoco payment
*/
private function initiateYocoPayment($amount, $orderId, $orderType, $description)
{
$yocoSecret = config('services.yoco.secret_key');
if (!$yocoSecret) {
Log::error('Yoco secret key not configured');
return null;
}
$payload = [
'amount' => $amount,
'currency' => 'ZAR',
'successUrl' => route('yoco-custom-deposit-success', ['customOrder' => $orderId]),
'failureUrl' => route('custom-orders.show', ['customOrder' => $orderId]),
'cancelUrl' => route('custom-orders.show', ['customOrder' => $orderId]),
'metadata' => [
'order_uuid' => $orderId,
'order_type' => $orderType,
],
'description' => $description,
];
try {
Log::info('Initiating Yoco payment', [
'amount' => $amount,
'orderId' => $orderId,
'description' => $description
]);
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . $yocoSecret,
'Content-Type' => 'application/json',
])->post('https://payments.yoco.com/api/checkouts', $payload);
Log::info('Yoco API response', [
'status' => $response->status(),
'body' => $response->body()
]);
if ($response->successful()) {
$data = $response->json();
Log::info('Yoco payment success', [
'checkout_url' => $data['redirectUrl'] ?? 'N/A',
'checkout_id' => $data['id'] ?? 'N/A'
]);
return [
'checkout_url' => $data['redirectUrl'],
'checkout_id' => $data['id'],
];
} else {
Log::error('Yoco payment API error', [
'status' => $response->status(),
'body' => $response->body()
]);
}
} catch (\Exception $e) {
Log::error('Yoco payment exception: ' . $e->getMessage(), [
'trace' => $e->getTraceAsString()
]);
}
return null;
}
}