creneaux et exceptions liées à un point de retrait

BO, et selection en front
This commit is contained in:
Olivier Legat 2021-04-22 18:18:52 +02:00
parent 286a0fab2a
commit 6052dd2fd2
14 changed files with 253 additions and 86 deletions

View file

@ -3,30 +3,50 @@
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Models\CollecteException;
use App\Models\Magasin;
use App\Models\TimeslotSetting;
use App\Models\Setting;
use Illuminate\Http\Request;
class CreneauxController extends Controller
{
public function settings(Request $request)
public function settings(Request $request,$id)
{
$timeslotSettings = TimeslotSetting::all();
$magasin = Magasin::findOrFail($id);
$timeslotSettings = $magasin->timeslotSettings;
if ($timeslotSettings->count() === 0) {
//Aucun settings pour ce magasin
$timeslotSettings = $this->makeCreneauxSettings($id);
}
$plage_days = Setting::where('key', 'order_withdrawal_period')->firstOrFail()->value;
$exceptionsCollecte = CollecteException::orderBy('date')->get();
return view('bo.creneaux.settings', compact('timeslotSettings', 'plage_days', 'exceptionsCollecte'));
return view('bo.creneaux.settings', compact('timeslotSettings', 'plage_days', 'magasin'));
}
public function updateSettings(Request $request)
public function makeCreneauxSettings($id) {
for ($i=0;$i<7;$i++) {
$dayset = new TimeslotSetting();
$dayset->magasin_id = $id;
$dayset->day_index = ''.$i;
$dayset->save();
}
return Magasin::findOrFail($id)->timeslotSettings;
}
public function updateSettings(Request $request,$id)
{
$plage = $request->plage;
$plage_days = $request->plage_days;
$magasin_id = $id;
foreach($plage as $index => $day) {
$setting = TimeslotSetting::where('day_index', ''.$index)->firstOrFail();
$setting = TimeslotSetting::where('day_index', ''.$index)
->where('magasin_id', $magasin_id)
->firstOrFail();
//$setting->magasin_id = $magasin_id; ne doit pas changer
$setting->morning_start = $day['morning_start'];
$setting->morning_end = $day['morning_end'];
$setting->afternoon_start = $day['afternoon_start'];
@ -41,42 +61,6 @@ class CreneauxController extends Controller
$order_withdrawal_period->value = $plage_days;
$order_withdrawal_period->save();
return redirect()->route('bo.creneaux.settings')->with('success', 'Modifications enregistrées !');
}
public function saveExceptions(Request $request)
{
$collecteException = $request->collecte_exception;
for ($i = 0; $i < count($collecteException); $i++) {
$collecteExceptionToInsert = !empty($collecteException[$i]["date"]);
if (isset($collecteException[$i]["id"]) && $collecteException[$i]["id"] !== null && $collecteExceptionToInsert) {
$ce = CollecteException::findOrFail($collecteException[$i]["id"]);
$ce->date = $collecteException[$i]["date"];
$ce->comment = $collecteException[$i]["comment"];
$ce->save();
} else {
if ($collecteExceptionToInsert) {
$ce = CollecteException::updateOrCreate(['date' => $collecteException[$i]["date"], 'comment' => $collecteException[$i]["comment"]]);
$collecteException[$i]["id"] = $ce->id;
} else {
unset($collecteException[$i]);
$i--;
}
}
}
//remet dans l'ordre avant de renvoyer
$collecteException = CollecteException::orderBy('date')->get();
return $collecteException;
}
public function destroyExceptions(Request $request)
{
$collecteException = CollecteException::findOrFail($request->id);
$collecteException->delete();
return "Suppression terminée !";
return redirect()->route('bo.creneaux.settings', $magasin_id)->with('success', 'Modifications enregistrées !');
}
}

View file

@ -4,6 +4,7 @@ namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Models\Magasin;
use App\Models\CollecteException;
use Illuminate\Http\Request;
class MagasinsController extends Controller
@ -68,7 +69,8 @@ class MagasinsController extends Controller
public function edit($id)
{
$magasin = Magasin::findOrFail($id);
return view('bo.magasins.edit', compact('magasin'));
$exceptionsCollecte = $magasin->collecteExceptions()->orderBy('date')->get();
return view('bo.magasins.edit', compact('magasin', 'exceptionsCollecte'));
}
/**
@ -112,4 +114,58 @@ class MagasinsController extends Controller
$shop->delete();
return redirect()->route('bo.magasins.index');
}
public function saveExceptions(Request $request)
{
$collecteException = $request->collecte_exception;
for ($i = 0; $i < count($collecteException); $i++) {
$collecteExceptionToInsert = !empty($collecteException[$i]["date"]);
if (isset($collecteException[$i]["id"]) && $collecteException[$i]["id"] !== null && $collecteExceptionToInsert) {
$ce = CollecteException::findOrFail($collecteException[$i]["id"]);
$ce->date = $collecteException[$i]["date"];
$ce->comment = $collecteException[$i]["comment"];
$ce->magasin_id = $collecteException[$i]["magasin_id"];
$ce->save();
} else {
if ($collecteExceptionToInsert) {
$ce = CollecteException::updateOrCreate([
'date' => $collecteException[$i]["date"],
'comment' => $collecteException[$i]["comment"],
'magasin_id' => $collecteException[$i]["magasin_id"]
]);
$collecteException[$i]["id"] = $ce->id;
} else {
unset($collecteException[$i]);
$i--;
}
}
}
//remet dans l'ordre avant de renvoyer
//$collecteException = CollecteException::where('magasin_id',)->orderBy('date')->get();
$this->arraySortByColumn($collecteException, "date");
return $collecteException;
}
public function arraySortByColumn(&$array, $column, $direction = SORT_ASC) {
$reference_array = array();
foreach($array as $key => $row) {
$reference_array[$key] = $row[$column];
}
array_multisort($reference_array, $direction, $array);
}
public function destroyExceptions(Request $request)
{
$collecteException = CollecteException::findOrFail($request->id);
$collecteException->delete();
return "Suppression terminée !";
}
}

View file

@ -169,13 +169,31 @@ class OrdersController extends Controller
{
$activestep = 3;
$magasin = Magasin::first();
$availCreneaux = array();
//plus besoin car chargé par le composant vue.
//$availCreneaux = $this->getAvailableCreneauxByShop($magasin->id);
$order = Order::where('reference', '=', $reference)->firstOrFail();
return view('sales.pick-slot', compact('activestep', 'availCreneaux', 'order', 'magasin'));
}
/**
* @return mixed
*/
public function getAvailableCreneauxByShop($id)
{
//setting à stocker et réutiliser
$plage_days = 14;
$timeslotSettings = TimeslotSetting::all();
$magasin = Magasin::findOrFail($id);
$timeslotSettings = $magasin->timeslotSettings;
//$timeslotSettings = TimeslotSetting::all();
//fabriquons un tableau recap
$plage = array();
foreach($timeslotSettings as $day) {
foreach ($timeslotSettings as $day) {
$plage[$day->day_index] = array(
'morning_start' => $day->morning_start,
@ -190,29 +208,39 @@ class OrdersController extends Controller
//génération des plages
//start today
$today = CarbonImmutable::now()->startOfDay();
$today = CarbonImmutable::now()->startOfDay();
$weekDay = $today->weekday();
//final boundary (permet de limiter la liste des jours d'exception)
$finalday = $today->add($plage_days, 'day');
$now = CarbonImmutable::now();
$now = CarbonImmutable::now();
//jours exceptions
$noCollectDays = CollecteException::whereDate('date','>',$now)->whereDate('date','<=',$finalday)->orderBy('date')->get();
// $noCollectDays = CollecteException::where('magasin_id',$magasin->id)
// ->whereDate('date', '>', $now)
// ->whereDate('date', '<=', $finalday)
// ->orderBy('date')->get();
$noCollectDays = $magasin->collecteExceptions()
->whereDate('date', '>', $now)
->whereDate('date', '<=', $finalday)
->orderBy('date')->get();
$delaiPrepa = CarbonInterval::createFromFormat('h:i', $plage[$weekDay]['min_order_preparation'] ?? '01:00');
$availCreneaux = array();
for ($i = 0; $i < $plage_days; $i++) {
/** @var Carbon */
$current = $today->add($i, 'day');
$weekDay = $current->weekday();
$interval = CarbonInterval::createFromFormat('h:i', $plage[$weekDay]['interval'] ?? '00:15');
$delaiPrepa = CarbonInterval::createFromFormat('h:i', $plage[$weekDay]['min_order_preparation'] ?? '01:00');
$current = $today->add($i, 'day');
$weekDay = $current->weekday();
$interval = CarbonInterval::createFromFormat('h:i', $plage[$weekDay]['interval'] ?? '00:15');
//si c'est un jour exception skip
$skip = false;
foreach ($noCollectDays as $not_today) {
if($current->isSameDay($not_today->date)) {
if ($current->isSameDay($not_today->date)) {
$skip = true;
}
}
if($skip) {
if ($skip) {
continue;
}
//matinée
@ -221,14 +249,20 @@ class OrdersController extends Controller
$endTime = Carbon::createFromTimeString($plage[$weekDay]['morning_end']);
do {
$creneau = $currentTime->format('H:i');
$nbtakens = Timeslot::where('day',$current->format('d/m/Y'))->where('time',$creneau)->count();
if($nbtakens >= $plage[$weekDay]['max_orders_per_interval']) {
$nbtakens = Timeslot::where('day', $current->format('d/m/Y'))
->where('time', $creneau)->count();
if ($nbtakens >= $plage[$weekDay]['max_orders_per_interval']) {
$isDispo = false;
} else {
$isDispo = true;
}
if($current->addHours($currentTime->format('H'))->addMinutes($currentTime->format('i')) < $now->add($delaiPrepa)) {
$isDispo = false;
}
//attention si la date est passée (today mais avant maintenant) : pas de créneau
if ($current > $today || $currentTime >= $now->add($delaiPrepa)) {
if ($current > $today || $currentTime >= $now) {
$availCreneaux[$current->format('d/m/Y')][] = array($creneau,$isDispo);
}
$currentTime->add($interval);
@ -246,19 +280,25 @@ class OrdersController extends Controller
} else {
$isDispo = true;
}
if($current->addHours($currentTime->format('H'))->addMinutes($currentTime->format('i')) < $now->add($delaiPrepa)) {
$isDispo = false;
}
//attention si la date est passée (today mais avant maintenant) : pas de créneau
if ($current > $today || $currentTime >= $now->add($delaiPrepa)) {
if ($current > $today || $currentTime >= $now) {
$availCreneaux[$current->format('d/m/Y')][] = array($creneau,$isDispo);
}
$currentTime->add($interval);
} while ($currentTime <= $endTime);
}
}
return $availCreneaux;
}
$order = Order::where('reference', '=', $reference)->firstOrFail();
$magasin = Magasin::first();
return view('sales.pick-slot', compact('activestep', 'availCreneaux', 'order', 'magasin'));
//call ajax pour le composant vue de timeslots
public function getAvailableCreneaux(Request $request) {
return $this->getAvailableCreneauxByShop($request->id);
}
/**

View file

@ -10,7 +10,7 @@ class CollecteException extends Model
{
use HasFactory;
protected $fillable = ['date', 'comment'];
protected $fillable = ['date', 'comment', 'magasin_id'];
/**
* The attributes that should be cast to native types.
@ -25,4 +25,9 @@ class CollecteException extends Model
{
return $date->format('Y-m-d');
}
public function magasin()
{
return $this->belongsTo(Magasin::class);
}
}

View file

@ -14,4 +14,12 @@ class Magasin extends Model
'latitude', 'longitude',
'gmaps_placeid', 'gmaps_embed_href', 'gmaps_link_href',
];
public function timeslotSettings() {
return $this->hasMany(TimeslotSetting::class);
}
public function collecteExceptions() {
return $this->hasMany(CollecteException::class);
}
}

View file

@ -8,4 +8,9 @@ use Illuminate\Database\Eloquent\Model;
class TimeslotSetting extends Model
{
use HasFactory;
public function magasin()
{
return $this->belongsTo(Magasin::class);
}
}

View file

@ -0,0 +1,54 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class MakeChildOfMagasinsTimeSettingsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('timeslot_settings', function (Blueprint $table) {
$table->dropUnique('timeslot_settings_day_index_unique');
$table->foreignId('magasin_id')->after('id')->default(1)->constrained('magasins');
$table->unique(array('magasin_id', 'day_index'));
});
Schema::table('timeslot_settings', function (Blueprint $table) {
$table->unsignedBigInteger('magasin_id')->default(null)->change();
});
Schema::table('collecte_exceptions', function (Blueprint $table) {
$table->foreignId('magasin_id')->after('id')->default(1)->constrained('magasins');
});
Schema::table('collecte_exceptions', function (Blueprint $table) {
$table->unsignedBigInteger('magasin_id')->default(null)->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('timeslot_settings', function (Blueprint $table) {
$table->dropForeign(['magasin_id']);
$table->dropUnique('timeslot_settings_magasin_id_day_index_unique');
$table->dropColumn('magasin_id');
$table->unique('day_index');
});
Schema::table('collecte_exceptions', function (Blueprint $table) {
$table->dropForeign(['magasin_id']);
$table->dropColumn('magasin_id');
});
}
}

View file

@ -12270,7 +12270,7 @@ __webpack_require__.r(__webpack_exports__);
console.log('CreneauxExceptions Component mounted.');
this.datas = JSON.parse(this.dbdatas);
},
props: ['name', 'dbdatas'],
props: ['name', 'dbdatas', 'magasinId'],
data: function data() {
return {
datas: [],
@ -12283,7 +12283,8 @@ __webpack_require__.r(__webpack_exports__);
newItem: function newItem() {
this.datas.push({
date: '',
comment: ''
comment: '',
magasin_id: this.magasinId
});
},
removeItem: function removeItem(index) {

View file

@ -43,6 +43,7 @@
props: [
'name',
'dbdatas',
'magasinId'
],
data() {
return {
@ -56,7 +57,7 @@
},
methods: {
newItem() {
this.datas.push({date: '', comment: ''});
this.datas.push({date: '', comment: '', magasin_id: this.magasinId});
},
removeItem(index) {
if (this.datas[index]['id'] !== undefined) {

View file

@ -1,10 +1,10 @@
@extends('layouts.bo-layout')
@section('content')
<form action="{{route('bo.creneaux.settings.update')}}" method="POST" enctype="multipart/form-data">
<form action="{{route('bo.creneaux.settings.update',$magasin->id)}}" method="POST" enctype="multipart/form-data">
@csrf
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Créneaux <small class="text-muted">Règlages</small></h1>
<h1 class="h2"><a href="{{route('bo.magasins.edit',$magasin->id)}}" class="h2 text-decoration-none"><</a> Créneaux - {{$magasin->name}} <small class="text-muted">Règlages</small></h1>
@if (session('success'))
<span class="float-right text-success">{{ session('success') }}</span>
@endif
@ -21,7 +21,7 @@
</ul>
</div>
@endif
<div class="container">
<div class="">
<div class="form-row">
<div class="form-group col">
</div>
@ -110,10 +110,11 @@
<div class="form-row">
<div class="form-group col">
Delai de préparation de commande
<small class="text-muted">(durée en heures:minutes, exemple : 02:00 pour 2 heures)</small>
</div>
@foreach ($timeslotSettings as $zeDay)
<div class="form-group col">
<input type="time" id="min_order_preparation_{{$zeDay->day_index}}" name="plage[{{$zeDay->day_index}}][min_order_preparation]" class="form-control" value="{{ old('plage['.$zeDay->day_index.'][min_order_preparation]', $zeDay->min_order_preparation ?? '') }}">
<input type="text" pattern="\d*:\d{2}" placeholder="--:--" id="min_order_preparation_{{$zeDay->day_index}}" name="plage[{{$zeDay->day_index}}][min_order_preparation]" class="form-control" value="{{ old('plage['.$zeDay->day_index.'][min_order_preparation]', $zeDay->min_order_preparation ?? '') }}">
</div>
@endforeach
</div>
@ -129,16 +130,13 @@
</div>
<div class="form-row">
<div class="form-group col"></div>
<div class="form-group col"></div>
<div class="form-group col">
<div class="form-group col text-center">
<button type="submit" class="btn btn-sm btn-primary">Enregistrer les règlages</button>
</div>
<div class="form-group col"></div>
</div>
</div>
</form>
<creneaux-exceptions dbdatas='@json($exceptionsCollecte)'></creneaux-exceptions>
@endsection
@section('scripts')

View file

@ -22,9 +22,14 @@
</ul>
</div>
@endif
<div class="form-group">
<label for="name">Nom *</label>
<input type="text" id="name" name="name" class="form-control" placeholder="Nom *" value="{{ old('name',$magasin->name) }}" required>
<div class="form-row">
<div class="form-group col">
<label for="name">Nom *</label>
<input type="text" id="name" name="name" class="form-control" placeholder="Nom *" value="{{ old('name',$magasin->name) }}" required>
</div>
<div class="form-group col">
<a class="btn btn-sm btn-primary" href="{{route('bo.creneaux.settings', $magasin->id)}}">Editer Créneaux de collecte</a>
</div>
</div>
<div class="form-group">
<label>Adresse *</label>
@ -69,7 +74,15 @@
<label>placeID</label>
<input type="text" id="gmaps_placeid" name="gmaps_placeid" class="form-control" placeholder="placeID google maps (necessite une clé d'API)" value="{{ old('gmaps_placeid',$magasin->gmaps_placeid) }}">
</div>
<div class="form-row">
<div class="form-group col"></div>
<div class="form-group col text-center">
<button type="submit" class="btn btn-sm btn-primary">Enregistrer les règlages</button>
</div>
<div class="form-group col"></div>
</div>
</form>
<creneaux-exceptions dbdatas='@json($exceptionsCollecte)' magasin-id='{{$magasin->id}}'></creneaux-exceptions>
@endsection
@section('scripts')

View file

@ -17,6 +17,7 @@
<th scope="col">Nom</th>
<th scope="col">Adresse</th>
<th scope="col">Téléphone</th>
<th scope="col"></th>
</thead>
<tbody>
@foreach ($magasins as $magasin)
@ -27,6 +28,7 @@
<td>{{$magasin->phone}}</td>
<td class="text-center">
<a href="{{route('bo.magasins.edit', $magasin->id)}}" class="btn btn-sm btn-primary">Éditer</a>
<a href="{{route('bo.creneaux.settings', $magasin->id)}}" class="btn btn-sm btn-secondary">Editer Créneaux de collecte</a>
<form action="{{route('bo.magasins.destroy', $magasin->id)}}" method="POST" class="d-inline">
@method('DELETE')
@csrf

View file

@ -177,7 +177,7 @@
</h2>
</div>
<div id="collapseCollecte" class="collapse {{ ( request()->routeIs('bo.magasins*') ) ? 'show' : '' }}" aria-labelledby="headingCollecte" tata-parent="#accordionMenu">
<div id="collapseCollecte" class="collapse {{ ( request()->routeIs('bo.magasins*') || request()->routeIs('bo.creneaux*') ) ? 'show' : '' }}" aria-labelledby="headingCollecte" tata-parent="#accordionMenu">
<div class="card-body">
<ul class="nav flex-column mb-2">
<li class="nav-item">
@ -196,12 +196,11 @@
</h2>
</div>
<div id="collapseSettings" class="collapse {{ ( request()->routeIs('bo.*settings*') ) ? 'show' : '' }}" aria-labelledby="headingSettings" tata-parent="#accordionMenu">
<div id="collapseSettings" class="collapse {{ ( request()->routeIs('bo.settings*') ) ? 'show' : '' }}" aria-labelledby="headingSettings" tata-parent="#accordionMenu">
<div class="card-body">
<ul class="nav flex-column mb-2">
<li class="nav-item">
<a class="nav-link" href="{{route('bo.settings')}}">Paramètres généraux</a>
<a class="nav-link" href="{{route('bo.creneaux.settings')}}">Créneaux de collecte</a>
</li>
</ul>
</div>

View file

@ -62,6 +62,7 @@ Route::prefix('ajax')
Route::post('/get-cart', [BasketController::class, 'getCart'])->name('cart.get');
Route::post('/remove-from-cart', [BasketController::class, 'removeFromCart'])->name('cart.remove');
Route::post('/empty-cart', [BasketController::class, 'emptyCart'])->name('cart.empty');
Route::post('/get-time-slots', [OrdersController::class, 'getAvailableCreneaux'])->name('get-creneaux');
});
Route::namespace('App\Http\Controllers\Bo')
@ -78,11 +79,11 @@ Route::namespace('App\Http\Controllers\Bo')
Route::post('/ajax/delete-faq', [FaqController::class, 'destroy']);
//Creneaux
Route::get('/settings/creneaux', [CreneauxController::class, 'settings'])->name('creneaux.settings');
Route::post('/settings/creneaux', [CreneauxController::class, 'updateSettings'])->name('creneaux.settings.update');
Route::get('/magasins/{id}/creneaux', [CreneauxController::class, 'settings'])->name('creneaux.settings');
Route::post('/magasins/{id}/creneaux', [CreneauxController::class, 'updateSettings'])->name('creneaux.settings.update');
//exceptions de collecte
Route::post('/ajax/save-creneaux-exception', [CreneauxController::class, 'saveExceptions']);
Route::post('/ajax/delete-creneaux-exception', [CreneauxController::class, 'destroyExceptions']);
Route::post('/ajax/save-creneaux-exception', [\App\Http\Controllers\Bo\MagasinsController::class, 'saveExceptions']);
Route::post('/ajax/delete-creneaux-exception', [\App\Http\Controllers\Bo\MagasinsController::class, 'destroyExceptions']);
// Produits