Files
opensem/app/Http/Controllers/Shop/OrderController.php
Valentin Lab 63673117b3 fix: enforce stock limits on basket quantities
No stock validation existed in the ordering flow, allowing customers
to order more items than available.

Cap quantity to ``stock_current`` in ``Baskets::getBasketData()`` when
adding to cart. Add ``min=1`` and ``max=stock`` attributes on the
basket quantity input, with JS clamping in the change handler.
Verify stock again in ``Shop\OrderController::store()`` before saving
the order as a race-condition safeguard.
2026-02-20 13:05:31 +01:00

155 lines
5.2 KiB
PHP

<?php
namespace App\Http\Controllers\Shop;
use App\Datatables\Shop\CustomerOrdersDataTable;
use App\Http\Requests\Shop\StoreOrderPost;
use App\Repositories\Core\Auth\Users;
use App\Repositories\Core\User\ShopCart;
use App\Repositories\Shop\Baskets;
use App\Repositories\Shop\Contents;
use App\Repositories\Shop\Customers;
use App\Repositories\Shop\CustomerAddresses;
use App\Repositories\Shop\Deliveries;
use App\Repositories\Shop\DeliveryTypes;
use App\Repositories\Shop\Offers;
use App\Repositories\Shop\OrderMails;
use App\Repositories\Shop\Orders;
use App\Repositories\Shop\Paybox;
use App\Repositories\Shop\SaleChannels;
use Illuminate\Http\Request;
class OrderController extends Controller
{
public function index(CustomerOrdersDataTable $dataTable)
{
if (Users::isNotConnected() && Customers::isNotConnected()) {
abort(403);
}
return $dataTable->render('Shop.Orders.partials.list');
}
public function view($uuid)
{
$data = [
'order' => Orders::view($uuid),
];
return view('Shop.Orders.view', $data);
}
public function pdf($uuid)
{
$data = [
'order' => Orders::getByUUID($uuid),
];
return view('Shop.Orders.pdf', $data);
}
public function order()
{
if (ShopCart::count()) {
$customer = Customers::getWithAddresses();
$customerId = $customer ? $customer->id : false;
$defaultSaleChannelId = SaleChannels::getDefaultID($customerId);
$deliveries = $defaultSaleChannelId
? Deliveries::getBySaleChannels([$defaultSaleChannelId])
: Deliveries::getByCustomer($customerId);
$deliveries = $deliveries ? $deliveries->values() : collect();
$customerData = $customer ? $customer->toArray() : false;
if ($customerData) {
$customerData['delivery_address_id'] = optional(CustomerAddresses::getDeliveryAddress($customerId))->id;
$customerData['invoice_address_id'] = optional(CustomerAddresses::getInvoiceAddress($customerId))->id;
if (! $customerData['delivery_address_id'] && ! empty($customerData['delivery_addresses'])) {
$customerData['delivery_address_id'] = $customerData['delivery_addresses'][0]['id'] ?? null;
}
if (! $customerData['invoice_address_id'] && ! empty($customerData['invoice_addresses'])) {
$customerData['invoice_address_id'] = $customerData['invoice_addresses'][0]['id'] ?? null;
}
if ($defaultSaleChannelId) {
$customerData['default_sale_channel_id'] = $defaultSaleChannelId;
}
}
$data = [
'customer' => $customerData,
'basket' => Baskets::getBasketTotal(),
'deliveries' => $deliveries->toArray(),
'delivery_types' => DeliveryTypes::getWithPrice(Baskets::getWeight()),
];
return view('Shop.Orders.order', $data);
}
return redirect()->route('home');
}
public function store(StoreOrderPost $request)
{
$data = $request->all();
$data['customer_id'] = Customers::getId();
$data['sale_channel_id'] = $data['sale_channel_id'] ?? SaleChannels::getDefaultID();
$data['basket'] = Baskets::getBasketSummary($data['sale_channel_id'], $data['delivery_type_id'] ?? false);
// Vérifier le stock avant de valider la commande
$insufficients = [];
foreach ($data['basket']['detail'] as $item) {
$offer = Offers::get($item['offer_id']);
if ($offer && $offer->stock_current !== null && $item['quantity'] > $offer->stock_current) {
$offer->load('article');
$insufficients[] = ($offer->article->name ?? 'Offre #'.$offer->id)
.' (stock : '.(int) $offer->stock_current.', demandé : '.$item['quantity'].')';
}
}
if (! empty($insufficients)) {
return redirect()->back()->withInput()->with(
'growl',
['Stock insuffisant pour : '.implode(' ; ', $insufficients).'. Veuillez ajuster les quantités.', 'danger']
);
}
$order = Orders::saveOrder($data);
if ($order) {
if ($data['payment_type'] === '1') {
return Paybox::makeAuthorizationRequest($order);
}
OrderMails::sendOrderConfirmed($order->id);
return redirect()->route('Shop.Orders.confirmed', [
'payment_type' => $data['payment_type'],
]);
}
return view('Shop.Orders.order');
}
public function confirmed()
{
ShopCart::clear();
$paymentType = request('payment_type');
$content = Contents::getOrderConfirmedContent();
$paymentLabel = match ($paymentType) {
'2' => 'chèque',
'3' => 'virement',
default => null,
};
return view('Shop.Orders.confirmed', [
'content' => $content,
'payment_label' => $paymentLabel,
]);
}
public function getPdf($uuid)
{
return Orders::getPdfByUUID($uuid);
}
}