2022-09-20 16:33:05 +02:00
< ? php
namespace App\Http\Controllers ;
2025-02-10 15:18:10 +00:00
use App\Enums\ExceptionDayType ;
2022-09-20 16:33:05 +02:00
use App\Http\Controllers\Controller ;
use App\Models\Order ;
use App\Models\OrderLine ;
use App\Models\Timeslot ;
use App\Models\Magasin ;
use Illuminate\Support\Facades\Mail ;
use App\Mail\OrderConfirmMail ;
use App\Mail\OrderConfirmAdminMail ;
use App\Models\Setting ;
2024-11-29 16:49:59 +01:00
use App\Notifications\Regate\AnOrderWasPlaced ;
2022-09-20 16:33:05 +02:00
use Carbon\Carbon ;
use Carbon\CarbonImmutable ;
use Carbon\CarbonInterval ;
use Gloudemans\Shoppingcart\Facades\Cart ;
use Illuminate\Support\Facades\Auth ;
use Illuminate\Http\Request ;
2024-11-29 16:49:59 +01:00
use Illuminate\Support\Facades\Notification ;
2022-09-20 16:33:05 +02:00
use Illuminate\Support\Facades\Session ;
class OrderTunnelController extends Controller
{
/**
* front
* récap panier du tunnel de commande
*
* @ return \Illuminate\Contracts\View\View
*/
public function recapOrder ()
{
2023-08-02 16:26:25 +02:00
if ( auth () -> user () -> isNoLongerEmployed ()) {
auth () -> logout ();
return redirect () -> route ( 'auth.connexion' ) -> withErrors (
'Compte bloqué. Veuillez contacter votre RH ou le standard St Laurent au 02 43 62 70 00'
);
}
2022-10-06 12:26:13 +02:00
$activestep = 1 ;
2022-09-20 16:33:05 +02:00
Cart :: instance ( 'panier' );
$cart = Cart :: content ();
$cartInfos = array (
" totalTTC " => Cart :: total (),
// Total des taxes.
'totalTVA' => Cart :: tax (),
// Total sans les taxes.
'totalHT' => Cart :: subtotal (),
// Poids total des items.
'totalweight' => Cart :: weight (),
// Total avant taxes et remises.
'totalHTbefore' => Cart :: priceTotal (),
// Total des remises.
'totaldiscount' => Cart :: discount (),
// Nb de produits dans le cart.
'countItems' => Cart :: count (),
);
return view ( 'sales.recap' , compact ( 'activestep' , 'cart' , 'cartInfos' ));
}
/**
* front
* Storing order from sales tunnel
*
* @ return \Illuminate\Http\RedirectResponse
*/
public function store ()
{
Cart :: instance ( 'panier' );
2022-12-23 09:59:06 +01:00
$cart = Cart :: content ();
2023-08-16 16:35:47 +02:00
2022-12-23 09:59:06 +01:00
// Check that we have products in cart before saving.
2023-08-16 16:35:47 +02:00
if ( Cart :: count () <= 0 ) {
2022-12-23 09:59:06 +01:00
// Then redirect to empty recap.
2023-08-16 16:35:47 +02:00
return redirect () -> route ( 'sales.recap' ) -> with (
'message' ,
'Le nombre total de produits dans cette commande ne peut être négatif.'
);
2022-12-23 09:59:06 +01:00
}
// Save.
2022-09-20 16:33:05 +02:00
$order = new Order ();
// Generate order ref.
$order -> reference = $this -> generateOrderRef ();
$order -> date = Carbon :: now ();
$order -> status_id = 1 ;
$order -> total = str_replace ( ',' , '.' , str_replace ( ' ' , '' , Cart :: total ()));
$order -> customer_id = Auth :: user () -> id ;
$order -> save ();
// Order_id.
foreach ( $cart as $cartItem ) {
$order_line = new OrderLine ();
$order_line -> order_id = $order -> id ;
$order_line -> product_id = $cartItem -> model -> id ;
$order_line -> quantity = $cartItem -> qty ;
// Gross unit price.
$order_line -> unit_price = $cartItem -> model -> priceHt ;
// Discount per unit.
$order_line -> unit_discount = $cartItem -> model -> discountAmount ;
// Tax rate for this line.
$order_line -> tax_rate = $cartItem -> taxRate ;
// Tax amount par unit.
$order_line -> unit_tax = round (
(( $order_line -> unit_price - $order_line -> unit_discount ) * ( $order_line -> tax_rate / 100 )) * 100
) / 100 ;
// Discount amount for the line.
$order_line -> total_discount = $order_line -> unit_discount * $order_line -> quantity ;
// Gross amount for the line.
$order_line -> total_ht = ( $order_line -> unit_price - $order_line -> unit_discount ) * $order_line -> quantity ;
// Tax amount for the line.
$order_line -> total_tax = $order_line -> unit_tax * $order_line -> quantity ;
// Total TTC avec remises de la ligne.
$order_line -> total_ttc = $order_line -> total_ht + $order_line -> total_tax ;
$order_line -> save ();
} //end foreach
// Then redirect.
return redirect () -> route ( 'sales.pick-slot' , [ 'reference' => $order -> reference ]);
}
/**
* generate order Reference
* for a new order
*
* @ return string
*/
public function generateOrderRef ()
{
// Format : YYYYMMDDHHMM000.
$now = Carbon :: now ();
// Count todays orders.
$countorders = Order :: whereDate ( 'created_at' , Carbon :: today ()) -> count ();
// Add one.
$countorders ++ ;
$ref = $now -> isoFormat ( 'YYYYMMDDHHmm' ) . str_pad ( $countorders , 3 , " 0 " , STR_PAD_LEFT );
return $ref ;
}
/**
* front tunnel de commande étape 3 : choix de créneau
*
* @ param string $reference
* @ return \Illuminate\Contracts\View\View
*/
public function timeSlotPick ( string $reference )
{
2022-10-06 12:26:13 +02:00
$activestep = 2 ;
2022-09-20 16:33:05 +02:00
/*
* Il faut diriger les salariés vers le bon magasin sans leur
* laisser le choix
2022-09-21 16:12:03 +02:00
* Dans la gestion des thèmes antérieure il était possible d ' avoir 2 cas :
* theme . multi_relay : on ou off , ici cela ne sera plus le cas
* ( thème non interchangeable pour cette boutique spécifique )
2022-09-20 16:33:05 +02:00
*/
2022-09-21 16:12:03 +02:00
// Si Magasin n'est pas référencé, on ne sait pas ou va le salarié.
if ( Auth :: user () -> salarie -> magasin ) {
$magasin = Auth :: user () -> salarie -> magasin ;
}
2022-09-20 16:33:05 +02:00
$availCreneaux = array ();
$order = Order :: where ( 'reference' , '=' , $reference ) -> firstOrFail ();
return view ( 'sales.pick-slot' , compact ( 'activestep' , 'availCreneaux' , 'order' , 'magasin' ));
}
/**
* add timeslot by interval between start and end hour
*
* @ param array $plage
* @ param integer $weekDay
* @ param CarbonImmutable $current
* @ param Magasin $magasin
* @ param CarbonImmutable $now
* @ param CarbonImmutable $today
* @ param CarbonInterval $delaiPrepa
* @ param CarbonInterval $interval
* @ param Carbon $currentTime
* @ param Carbon $endTime
* @ return array
*/
private function addCreneaux (
array $plage ,
int $weekDay ,
CarbonImmutable $current ,
Magasin $magasin ,
CarbonImmutable $now ,
CarbonImmutable $today ,
CarbonInterval $delaiPrepa ,
CarbonInterval $interval ,
Carbon $currentTime ,
2025-02-10 15:18:10 +00:00
Carbon $endTime ,
bool $hasPublicHolidayNextWeekButNoFriday ,
bool $afterThursdayNight
2022-09-20 16:33:05 +02:00
) {
$retArray = array ();
do {
$creneau = $currentTime -> format ( 'H:i' );
$nbtakens = $magasin -> timeslots ()
-> where ( 'day' , $current -> format ( 'd/m/Y' ))
-> where ( 'time' , $creneau ) -> count ();
if ( $nbtakens >= $plage [ $weekDay ][ 'max_orders_per_interval' ]) {
$isDispo = false ;
} else {
$isDispo = true ;
}
2023-08-11 09:38:33 +02:00
/**
* On utilise les méthodes de Carbon pour la comparaison de date , parce que c ' est fait pour ...
*/
2022-09-20 16:33:05 +02:00
if (
2024-11-28 11:53:40 +01:00
$current -> addHours ( $currentTime -> format ( 'H' ))
-> addMinutes ( $currentTime -> format ( 'i' ))
-> lessThan ( $now -> add ( $delaiPrepa ))
2022-09-20 16:33:05 +02:00
) {
$isDispo = false ;
}
// Attention si la date est passée (today mais avant maintenant) : pas de créneau.
if ( $current > $today || $currentTime >= $now ) {
2025-02-10 15:18:10 +00:00
// Si un jour de la semaine prochaine est férié et qu'on passe commande un jeudi après 23h59...alors le vendredi de la semaine suivante n'est pas dispo.
$nextFriday = $now -> addWeek () -> startOfWeek ( Carbon :: MONDAY ) -> addDays ( 4 );
if ( $hasPublicHolidayNextWeekButNoFriday && $afterThursdayNight && $current -> isSameDay ( $nextFriday )) {
$isDispo = false ;
}
2022-09-20 16:33:05 +02:00
$retArray [] = array ( $creneau , $isDispo );
}
$currentTime -> add ( $interval );
} while ( $currentTime <= $endTime );
return $retArray ;
}
/**
* give array of available crenaux
*
* @ param integer $id
* @ return array
*/
public function getAvailableCreneauxByShop ( int $id )
{
// Setting à stocker et réutiliser.
$plage_days = Setting :: where ( 'key' , 'order_withdrawal_period' ) -> firstOrFail () -> value ;
$magasin = Magasin :: findOrFail ( $id );
$timeslotSettings = $magasin -> timeslotSettings ;
// $timeslotSettings = TimeslotSetting::all();
// Fabriquons un tableau recap.
$plage = array ();
foreach ( $timeslotSettings as $day ) {
$plage [ $day -> day_index ] = array (
'morning_start' => $day -> morning_start ,
'morning_end' => $day -> morning_end ,
'afternoon_start' => $day -> afternoon_start ,
'afternoon_end' => $day -> afternoon_end ,
'interval' => $day -> interval ,
'min_order_preparation' => $day -> min_order_preparation ,
'max_orders_per_interval' => $day -> max_orders_per_interval
);
}
// Génération des plages.
2024-11-28 11:53:40 +01:00
// On commence le calcul à aujourd'hui + 1
// car on ne va pas afficher de créaneaux pour aujourd'hui dans l'interface...
2023-08-11 09:38:33 +02:00
$today = CarbonImmutable :: now () -> addDay () -> startOfDay ();
2022-09-20 16:33:05 +02:00
$weekDay = $today -> weekday ();
// Final boundary (permet de limiter la liste des jours d'exception).
$finalday = $today -> add ( $plage_days , 'day' );
$now = CarbonImmutable :: now ();
2025-02-10 15:18:10 +00:00
2022-09-20 16:33:05 +02:00
// Jours exceptions.
$noCollectDays = $magasin -> collecteExceptions ()
-> whereDate ( 'date' , '>' , $now )
-> whereDate ( 'date' , '<=' , $finalday )
-> orderBy ( 'date' ) -> get ();
2025-02-10 15:18:10 +00:00
// On effectue des vérifications pour récupérer des jours de la semaine suivante
// =/= de vendredi qui seraient fériés.
$thursdayNight = $now -> startOfWeek ( Carbon :: MONDAY ) -> addDays ( 3 ) -> setTime ( 23 , 59 );
$afterThursdayNight = $now -> greaterThanOrEqualTo ( $thursdayNight );
$currentweekStartDate = $now -> startOfWeek ();
$currentweekEndDate = $now -> endOfWeek ();
$nextWeekStartDate = $currentweekStartDate -> addWeek ();
$nextWeekEndDate = $currentweekEndDate -> addWeek ();
$hasPublicHolidayNextWeekButNoFriday = $magasin -> collecteExceptions ()
-> whereDate ( 'date' , '>=' , $nextWeekStartDate )
-> whereDate ( 'date' , '<=' , $nextWeekEndDate )
-> where ( 'type' , '=' , ExceptionDayType :: ferie ())
-> whereRaw ( 'DAYOFWEEK(date) != ?' , 6 )
-> count () > 0 ;
2022-09-20 16:33:05 +02:00
$delaiPrepa = CarbonInterval :: createFromFormat ( 'h:i' , $plage [ $weekDay ][ 'min_order_preparation' ] ? ? '01:00' );
$availCreneaux = array ();
for ( $i = 0 ; $i < $plage_days ; $i ++ ) {
2024-11-28 11:53:40 +01:00
$current = $today -> add ( $i , 'day' );
2023-08-11 09:38:33 +02:00
/**
* On gère le décalage par rapport au tableau de settings ...
* Dans le tableau lundi = 0
* Dans Carbon dimanche = 0
*/
$weekDay = $current -> weekday () - 1 !== - 1 ? $current -> weekday () - 1 : 6 ;
2022-09-20 16:33:05 +02:00
$interval = CarbonInterval :: createFromFormat ( 'h:i' , $plage [ $weekDay ][ 'interval' ] ? ? '00:15' );
// Si c'est un jour exception skip.
2025-02-10 15:18:10 +00:00
// ah ? mais on skip quoi et ou ? et pourquoi ?
2022-09-20 16:33:05 +02:00
$skip = false ;
foreach ( $noCollectDays as $not_today ) {
if ( $current -> isSameDay ( $not_today -> date )) {
$skip = true ;
}
}
if ( $skip ) {
continue ;
}
2025-02-10 15:18:10 +00:00
2022-09-20 16:33:05 +02:00
$availCreneaux [ $current -> format ( 'd/m/Y' )] = array ();
2025-02-10 15:18:10 +00:00
2022-09-20 16:33:05 +02:00
// Matinée.
if ( ! empty ( $plage [ $weekDay ][ 'morning_start' ])) {
$currentTime = Carbon :: createFromTimeString ( $plage [ $weekDay ][ 'morning_start' ]);
$endTime = Carbon :: createFromTimeString ( $plage [ $weekDay ][ 'morning_end' ]);
$morningCreneaux = $this -> addCreneaux (
$plage ,
$weekDay ,
$current ,
$magasin ,
$now ,
$today ,
$delaiPrepa ,
$interval ,
$currentTime ,
2025-02-10 15:18:10 +00:00
$endTime ,
$hasPublicHolidayNextWeekButNoFriday ,
$afterThursdayNight ,
2022-09-20 16:33:05 +02:00
);
2023-08-11 09:38:33 +02:00
// Cela sert à rien d'afficher un jour avec des créneaux sur lesquels le conso ne peut pas cliquer...
if ( $this -> hasCreneauAvailableTime ( $morningCreneaux )) {
$availCreneaux [ $current -> format ( 'd/m/Y' )] = array_merge (
$availCreneaux [ $current -> format ( 'd/m/Y' )],
$morningCreneaux
);
}
2022-09-20 16:33:05 +02:00
} //end if
// Aprem.
if ( ! empty ( $plage [ $weekDay ][ 'afternoon_start' ])) {
$currentTime = Carbon :: createFromTimeString ( $plage [ $weekDay ][ 'afternoon_start' ]);
$endTime = Carbon :: createFromTimeString ( $plage [ $weekDay ][ 'afternoon_end' ]);
$afternoonCreneaux = $this -> addCreneaux (
$plage ,
$weekDay ,
$current ,
$magasin ,
$now ,
$today ,
$delaiPrepa ,
$interval ,
$currentTime ,
2025-02-10 15:18:10 +00:00
$endTime ,
$hasPublicHolidayNextWeekButNoFriday ,
$afterThursdayNight ,
2022-09-20 16:33:05 +02:00
);
2023-08-11 09:38:33 +02:00
// Cela sert à rien d'afficher un jour avec des créneaux sur lesquels le conso ne peut pas cliquer...
if ( $this -> hasCreneauAvailableTime ( $afternoonCreneaux )) {
$availCreneaux [ $current -> format ( 'd/m/Y' )] = array_merge (
$availCreneaux [ $current -> format ( 'd/m/Y' )],
$afternoonCreneaux
);
}
2022-09-20 16:33:05 +02:00
} //end if
} //end for
return $availCreneaux ;
}
2024-11-28 11:53:40 +01:00
/**
* hasCreneauAvailableTime
*
* @ param array $creneaux
*
* @ return boolean
*/
private function hasCreneauAvailableTime ( array $creneaux ) : bool
2023-08-11 09:38:33 +02:00
{
foreach ( $creneaux as $creneau ) {
if ( $creneau [ 1 ]) {
return true ;
}
}
return false ;
}
2022-09-20 16:33:05 +02:00
/**
* Call ajax pour le composant vue de timeslots .
*
* @ param Request $request
* @ return array
*/
public function getAvailableCreneaux ( Request $request )
{
return $this -> getAvailableCreneauxByShop ( $request -> id );
}
/**
* front
* enregistrement du créneau de collect
* et validation de la commande
*
* @ param Request $request
* @ return \Illuminate\Http\RedirectResponse
*/
public function setTimeSlot ( Request $request )
{
$request -> validate ([
'selectedmagasin' => 'required' ,
'selectedcreneau' => 'required' ,
'ok_cgv' => 'accepted' ,
]);
$tsday = explode ( '-' , $request -> selectedcreneau )[ 0 ];
$tstime = explode ( '-' , $request -> selectedcreneau )[ 1 ];
$magasin_id = $request -> selectedmagasin ;
$reference = $request -> order_ref ;
$order = Order :: where ( 'reference' , '=' , $reference ) -> firstOrFail ();
2022-10-17 17:20:37 +02:00
// Check total price of this customer commands for a specific delivery day.
2022-10-27 17:20:38 +02:00
$date = CarbonImmutable :: create (
explode ( '/' , $tsday )[ 2 ],
explode ( '/' , $tsday )[ 1 ],
explode ( '/' , $tsday )[ 0 ]
);
$start = $date -> startOfWeek ( Carbon :: MONDAY );
$end = $date -> endOfWeek ( Carbon :: SUNDAY );
2022-10-17 17:20:37 +02:00
$customerOrders = Order :: where ( 'customer_id' , '=' , $order -> customer_id )
-> where ( 'status_id' , '=' , 3 )
-> get ();
$totalPrice = 0 ;
foreach ( $customerOrders as $key => $customerOrder ) {
2023-08-30 08:03:04 +00:00
$timeslot_day = $customerOrder -> timeslot -> day ;
2022-10-17 17:20:37 +02:00
if (
2022-10-27 17:20:38 +02:00
$timeslot_day >= $start &&
$timeslot_day <= $end
2022-10-17 17:20:37 +02:00
) {
$totalPrice += $customerOrder -> total ;
}
}
if ( $totalPrice > 200 ) {
return redirect () -> route ( 'account.order-detail' , [ 'reference' => $order -> reference ])
2022-10-27 17:20:38 +02:00
-> with ( 'message' , ' Le montant total pour cette date de livraison est dépassé ( 200 € maximum ) .
Veuillez modifier cette dernière pour la valider . ' );
}
if ( $order -> total > 200 ) {
return redirect () -> route ( 'account.order-detail' , [ 'reference' => $order -> reference ])
-> with ( 'message' , ' Le montant total de cette commande est dépassé ( 200 € maximum ) .
2022-10-17 17:20:37 +02:00
Veuillez modifier cette dernière pour la valider . ' );
}
2022-09-20 16:33:05 +02:00
if ( $order -> status_id == 1 ) {
// On ne traite l'enregistrement que pour les commandes en attente venant de l'étape de récap.
// Save timeslot.
$timeslot = Timeslot :: firstOrNew ([ 'order_id' => $order -> id ]);
$timeslot -> order_id = $order -> id ;
$timeslot -> magasin_id = $magasin_id ;
2023-08-30 08:03:04 +00:00
$timeslot -> day = $date ;
2022-09-20 16:33:05 +02:00
$timeslot -> time = $tstime ;
$timeslot -> save ();
// Save order status.
$order -> status_id = 3 ;
$order -> save ();
// Send confirm mail.
Mail :: to ( $order -> customer -> email ) -> queue ( new OrderConfirmMail ( $order ));
// Envoi du mail de confirmation aux admins site.
$toMailSetting = Setting :: where ( 'key' , 'send_order_confirm_mail_to' ) -> first ();
if ( empty ( $toMailSetting -> value )) {
$toMail = config ( 'mail.from.address' );
} else {
$toMail = $toMailSetting -> value ;
}
Mail :: to ( $toMail ) -> queue ( new OrderConfirmAdminMail ( $order ));
// Export commande.
if ( config ( 'bosettings.regate_orders' )) {
2024-11-29 16:49:59 +01:00
Notification :: route ( 'mail' , config ( 'mail.to.regate' )) -> notify ( new AnOrderWasPlaced ( $order ));
2022-09-20 16:33:05 +02:00
}
} //end if
// Redirect to final confirmation.
return redirect () -> route ( 'sales.confirm' , [ 'reference' => $order -> reference ]);
}
/**
* front
* page de confirmation de commande
*
* @ param string $reference
* @ return \Illuminate\Contracts\View\View
*/
public function confirmOrder ( string $reference )
{
2022-10-06 12:26:13 +02:00
$activestep = 3 ;
2022-09-20 16:33:05 +02:00
$idcommande = Session :: get ( 'idcommande' );
$order = Order :: where ( 'reference' , '=' , $reference ) -> firstOrFail ();
// 1 seul magasin en base pour le moment
$magasin = Magasin :: find ( $order -> timeslot -> magasin -> id );
2023-07-11 13:23:27 +02:00
// Empty cart.
Cart :: instance ( 'panier' ) -> destroy ();
2022-09-20 16:33:05 +02:00
return view ( 'sales.confirm' , compact ( 'activestep' , 'order' , 'magasin' ));
}
}