Browse Source

Confirmation / paiement / création commande : première version,

dev
Guillaume Bourgeois 5 years ago
parent
commit
031c48c8a7
6 changed files with 278 additions and 174 deletions
  1. +5
    -0
      common/config/main.php
  2. +40
    -20
      producer/controllers/OrderController.php
  3. +168
    -147
      producer/views/order/order.php
  4. +17
    -7
      producer/web/css/screen.css
  5. +36
    -0
      producer/web/js/vuejs/order-order.js
  6. +12
    -0
      producer/web/sass/order/_order.scss

+ 5
- 0
common/config/main.php View File

'httpOnly' => true, 'httpOnly' => true,
], ],
], ],
'request' => [
'parsers' => [
'application/json' => 'yii\web\JsonParser',
]
],
'cache' => [ 'cache' => [
'class' => 'yii\caching\FileCache', 'class' => 'yii\caching\FileCache',
], ],

+ 40
- 20
producer/controllers/OrderController.php View File



class OrderController extends ProducerBaseController class OrderController extends ProducerBaseController
{ {
var $enableCsrfValidation = false;


public function behaviors() public function behaviors()
{ {
* *
* @return mixed * @return mixed
*/ */
public function actionCreate()
public function actionAjaxCreate()
{ {
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
$idProducer = $this->getProducer()->id ; $idProducer = $this->getProducer()->id ;
$order = new Order; $order = new Order;


$posts = Yii::$app->request->post(); $posts = Yii::$app->request->post();
if ($idProducer) { if ($idProducer) {
$this->_verifyProducerActive($idProducer); $this->_verifyProducerActive($idProducer);
} }


if ($order->load($posts)) { if ($order->load($posts)) {

$order = Order::find() $order = Order::find()
->where('id_distribution = ' . $posts['Order']['id_distribution'])
->andWhere('id_user = ' . User::getCurrentId())
->where('id_distribution = :id_distribution')
->andWhere('id_user = :id_user')
->params([
':id_distribution' => $posts['Order']['id_distribution'],
':id_user' => User::getCurrentId()
])
->one(); ->one();
if (!$order) { if (!$order) {
$order->origin = Order::ORIGIN_USER; $order->origin = Order::ORIGIN_USER;
} }
$this->processForm($order);
$errors = $this->processForm($order);
if(count($errors)) {
return ['status' => 'error', 'errors' => $errors] ;
}
} }


return $this->render('create', array_merge($this->initForm($order), [
'model' => $order
]));
return ['status' => 'success'] ;
} }


/** /**


$totalQuantity = 0; $totalQuantity = 0;


foreach ($posts['Product'] as $key => $quantity) {
$key = (int) str_replace('product_', '', $key);
$product = Product::find()->where(['id' => $key])->one();
foreach ($posts['products'] as $key => $quantity) {
$product = Product::find()->where(['id' => (int) $key])->one();
$totalQuantity += $quantity; $totalQuantity += $quantity;
if ($product && $quantity) { if ($product && $quantity) {
$productsArray[] = $product; $productsArray[] = $product;
$pointSale = PointSale::findOne($posts['Order']['id_point_sale']); $pointSale = PointSale::findOne($posts['Order']['id_point_sale']);


if ($pointSale) { if ($pointSale) {
if (strlen($pointSale->code) && !$pointSale->validateCode($posts['code_point_sale_' . $pointSale->id])) {
if (strlen($pointSale->code) && !$pointSale->validateCode($posts['code_point_sale'])) {
$errorPointSale = true; $errorPointSale = true;
} }
} else { } else {
} }
} }


$errors = [] ;
if ($order->validate() && count($productsArray) && !$errorDate && !$errorPointSale) { if ($order->validate() && count($productsArray) && !$errorDate && !$errorPointSale) {


// gestion point de vente // gestion point de vente


$productOrder->price = $product->price; $productOrder->price = $product->price;


$quantity = (int) $posts['Product']['product_' . $product->id];
$quantity = (int) $posts['products'][$product->id] ;
if ($availableProducts[$product->id]['quantity_max'] && $quantity > $availableProducts[$product->id]['quantity_remaining']) { if ($availableProducts[$product->id]['quantity_max'] && $quantity > $availableProducts[$product->id]['quantity_remaining']) {
$quantity = $availableProducts[$product->id]['quantity_remaining']; $quantity = $availableProducts[$product->id]['quantity_remaining'];
} }
} }


// credit // credit
$credit = isset($posts['credit']) && $posts['credit'];
$credit = isset($posts['use_credit']) && $posts['use_credit'];
$order = Order::searchOne([ $order = Order::searchOne([
'id' => $order->id 'id' => $order->id
]) ; ]) ;
} }


// redirection // redirection
$this->redirect(Yii::$app->urlManager->createUrl(['order/history', 'orderOk' => true]));
//$this->redirect(Yii::$app->urlManager->createUrl(['order/history', 'orderOk' => true]));
} }
else { else {
if (!count($productsArray)) { if (!count($productsArray)) {
Yii::$app->session->setFlash('error', "Vous n'avez choisi aucun produit");
$errors[] = "Vous n'avez choisi aucun produit" ;
} }
if ($errorDate) { if ($errorDate) {
Yii::$app->session->setFlash('error', "Vous ne pouvez pas commander pour cette date.");
$errors[] = "Vous ne pouvez pas commander pour cette date." ;
} }
if ($errorPointSale) { if ($errorPointSale) {
Yii::$app->session->setFlash('error', "Point de vente invalide.");
$errors[] = "Point de vente invalide." ;
} }
} }
return $errors ;
} }


/** /**
'order_infos' => $producer->order_infos 'order_infos' => $producer->order_infos
] ; ] ;
// User
$userProducer = UserProducer::searchOne([
'id_producer' => $producer->id,
'id_user' => User::getCurrentId()
]) ;
$json['credit'] = $userProducer->credit ;
if($dateObject && $dateObject->format($format) === $date) { if($dateObject && $dateObject->format($format) === $date) {
// distribution // distribution
$distribution = Distribution::initDistribution($date) ; $distribution = Distribution::initDistribution($date) ;
$json['distribution'] = $distribution ;
$pointsSaleArray = PointSale::find() $pointsSaleArray = PointSale::find()
->joinWith(['pointSaleDistribution' => function($query) use ($distribution) { ->joinWith(['pointSaleDistribution' => function($query) use ($distribution) {
$query->where(['id_distribution' => $distribution->id]); $query->where(['id_distribution' => $distribution->id]);

+ 168
- 147
producer/views/order/order.php View File

?> ?>


<div id="app-order-order"> <div id="app-order-order">
<div :class="(producer != null && producer.order_infos.length) ? 'col-md-9' : 'col-md-12'">
<div id="steps">
<ul>
<li id="step-date" :class="'col-md-3 '+((step == 'date') ? 'active' : '')">
<div class="info-step" v-if="dateFormat">
{{ dateFormat }}
</div>
<button @click="changeStep('date')" :class="'btn '+ (step == 'date' ? 'btn-primary' : 'btn-default')">
<span class="button-content"><span class="glyphicon glyphicon-time"></span> Date</span></span>
</button>
</li>
<li id="step-point-sale" :class="'col-md-3 '+((step == 'point-sale') ? 'active ' : '')">
<div class="info-step" v-if="pointSaleActive">
{{ pointSaleActive.name }}
</div>
<button @click="changeStep('point-sale')" :class="'btn '+ (step == 'point-sale' ? 'btn-primary' : 'btn-default')" :disabled="step == 'date'">
<span class="button-content"><span class="glyphicon glyphicon-map-marker"></span> Points de vente</span>
</button>
</li>
<li id="step-products" :class="'col-md-3 '+((step == 'products') ? 'active ' : '')">
<div class="info-step" v-if="oneProductOrdered()">
{{ countProductOrdered() }} produit{{ (countProductOrdered() > 1) ? 's' : '' }}
</div>
<button @click="changeStep('products')" :class="'btn '+ (step == 'products' ? 'btn-primary' : 'btn-default')" :disabled="step == 'date' || step == 'point-sale'">
<span class="button-content"><span class="glyphicon glyphicon-th-list"></span> Produits</span>
</button>
</li>
<li id="step-payment" :class="'col-md-3 '+((step == 'payment') ? 'active' : '')">
<button @click="changeStep('payment')" :class="'btn '+ (step == 'payment' ? 'btn-primary' : 'btn-default')" :disabled="step == 'date' || step == 'point-sale' || step == 'products'">
<span class="button-content"><span class="glyphicon glyphicon-ok"></span> Paiement</span>
</button>
</li>
</ul>
<div class="clr"></div>
</div>
<div class="content">
<div id="content-step-date" v-if="step == 'date'">
<div id="calendar">
<v-date-picker
is-inline
is-expanded
v-model="date"
mode="single"
:formats="calendar.formats"
:theme-styles="calendar.themeStyles"
:attributes="calendar.attrs"
@dayclick='dayClick'>
></v-date-picker>
</div>
<div v-if="orderSuccess" class="alert alert-success" id="order-success">
<span class="glyphicon glyphicon-ok"></span> Votre commande a bien été prise en compte.
</div>
<div v-else>
<div :class="(producer != null && producer.order_infos.length) ? 'col-md-9' : 'col-md-12'">
<div id="steps">
<ul>
<li id="step-date" :class="'col-md-3 '+((step == 'date') ? 'active' : '')">
<div class="info-step" v-if="dateFormat">
{{ dateFormat }}
</div>
<button @click="changeStep('date')" :class="'btn '+ (step == 'date' ? 'btn-primary' : 'btn-default')">
<span class="button-content"><span class="glyphicon glyphicon-time"></span> Date</span></span>
</button>
</li>
<li id="step-point-sale" :class="'col-md-3 '+((step == 'point-sale') ? 'active ' : '')">
<div class="info-step" v-if="pointSaleActive">
{{ pointSaleActive.name }}
</div>
<button @click="changeStep('point-sale')" :class="'btn '+ (step == 'point-sale' ? 'btn-primary' : 'btn-default')" :disabled="step == 'date'">
<span class="button-content"><span class="glyphicon glyphicon-map-marker"></span> Points de vente</span>
</button>
</li>
<li id="step-products" :class="'col-md-3 '+((step == 'products') ? 'active ' : '')">
<div class="info-step" v-if="oneProductOrdered()">
{{ countProductOrdered() }} produit{{ (countProductOrdered() > 1) ? 's' : '' }}
</div>
<button @click="changeStep('products')" :class="'btn '+ (step == 'products' ? 'btn-primary' : 'btn-default')" :disabled="step == 'date' || step == 'point-sale'">
<span class="button-content"><span class="glyphicon glyphicon-th-list"></span> Produits</span>
</button>
</li>
<li id="step-payment" :class="'col-md-3 '+((step == 'payment') ? 'active' : '')">
<button @click="changeStep('payment')" :class="'btn '+ (step == 'payment' ? 'btn-primary' : 'btn-default')" :disabled="step == 'date' || step == 'point-sale' || step == 'products'">
<span class="button-content"><span class="glyphicon glyphicon-ok"></span> Confirmation</span>
</button>
</li>
</ul>
<div class="clr"></div>
</div> </div>
<div id="content-step-point-sale" v-if="step == 'point-sale'">
<table class="table table-bordered" v-if="pointsSale.length">
<thead>
<tr>
<th>Nom</th>
<th>Localité</th>
<th></th>
</tr>
</thead>
<tbody>
<tr v-for="pointSale in pointsSale" v-if="pointSale.pointSaleDistribution.delivery">
<td class="name">
{{ pointSale.name }}
<div class="comment" v-if="pointSale.userPointSale">
{{ pointSale.userPointSale.comment }}
</div>
</td>
<td>{{ pointSale.locality }}</td>
<td>
<button class="btn btn-default" @click="pointSaleClick" :data-id-point-sale="pointSale.id">
<span class="glyphicon glyphicon-map-marker"></span>
Choisir
</button>
</td>
</tr>
</tbody>
</table>
<div class="alert alert-warning" v-else>
Aucun point de vente disponible pour ce jour de distribution.
<div class="content">
<div id="content-step-date" v-if="step == 'date'">
<div id="calendar">
<v-date-picker
is-inline
is-expanded
v-model="date"
mode="single"
:formats="calendar.formats"
:theme-styles="calendar.themeStyles"
:attributes="calendar.attrs"
@dayclick='dayClick'>
></v-date-picker>
</div>
</div> </div>
</div>
<div id="content-step-products" v-if="step == 'products'">
<div v-if="products.length">
<table id="products" class="table table-bordered" >
<div id="content-step-point-sale" v-if="step == 'point-sale'">
<table class="table table-bordered" v-if="pointsSale.length">
<thead> <thead>
<tr> <tr>
<th>Nom</th> <th>Nom</th>
<th>Prix unitaire</th>
<th>Quantité</th>
<th>Total</th>
<th>Localité</th>
<th></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="product in products" v-if="product.productDistribution[0].active == 1">
<tr v-for="pointSale in pointsSale" v-if="pointSale.pointSaleDistribution.delivery">
<td class="name"> <td class="name">
<span class="name">{{ product.name }}</span>
<span class="other">
<span v-if="product.description.length">/</span>
<span class="description">{{ product.description }}</span>
<span v-if="product.weight">({{ product.weight }}g)</span>
</span>
<span v-if="product.quantity_remaining - product.quantity_form == 0 && product.quantity_max > 0" class="label label-danger">
Épuisé
</span>
<div class="recipe" v-if="product.recipe.length">{{ product.recipe }}</div>
</td>
<td class="price-unit">
{{ formatPrice(product.price) }}
</td>
<td class="td-quantity">
<div class="input-group">
<span class="input-group-btn">
<button class="btn btn-default btn-moins" type="button" @click="productQuantityClick(product, -1)"><span class="glyphicon glyphicon-minus"></span></button>
</span>
<input type="text" v-model="product.quantity_form" class="form-control quantity" />
<span class="input-group-btn">
<button class="btn btn-default btn-plus" type="button" @click="productQuantityClick(product, 1)"><span class="glyphicon glyphicon-plus"></span></button>
</span>
<span class="glyphicon glyphicon-lock" v-if="pointSale.code.length > 0"></span> {{ pointSale.name }}
<div class="comment" v-if="pointSale.userPointSale">
{{ pointSale.userPointSale.comment }}
</div> </div>
</td> </td>
<td class="price-total">
{{ formatPrice(product.price * product.quantity_form) }}
<td>{{ pointSale.locality }}</td>
<td>
<input v-if="pointSale.code.length > 0" v-model="codePointSale" type="password" placeholder="Code" class="form-control input-code" />
<button class="btn btn-default" @click="pointSaleClick" :data-id-point-sale="pointSale.id">
<span class="glyphicon glyphicon-map-marker"></span>
Choisir
</button>
</td> </td>
</tr> </tr>
<tr>
<td colspan="3"></td>
<td class="price-total">{{ priceTotal() }}</td>
</tr>
</tbody> </tbody>
</table> </table>
<div class="block-actions">
<button class="btn btn-primary" @click="changeStep('payment')">Valider</button>
<div class="alert alert-warning" v-else>
Aucun point de vente disponible pour ce jour de distribution.
</div> </div>
</div> </div>
<div class="alert alert-warning" v-else>
Aucun produit disponible
<div id="content-step-products" v-if="step == 'products'">
<div v-if="products.length">
<table id="products" class="table table-bordered" >
<thead>
<tr>
<th>Nom</th>
<th>Prix unitaire</th>
<th>Quantité</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<tr v-for="product in products" v-if="product.productDistribution[0].active == 1">
<td class="name">
<span class="name">{{ product.name }}</span>
<span class="other">
<span v-if="product.description.length">/</span>
<span class="description">{{ product.description }}</span>
<span v-if="product.weight">({{ product.weight }}g)</span>
</span>
<span v-if="product.quantity_remaining - product.quantity_form == 0 && product.quantity_max > 0" class="label label-danger">
Épuisé
</span>
<div class="recipe" v-if="product.recipe.length">{{ product.recipe }}</div>
</td>
<td class="price-unit">
{{ formatPrice(product.price) }}
</td>
<td class="td-quantity">
<div class="input-group">
<span class="input-group-btn">
<button class="btn btn-default btn-moins" type="button" @click="productQuantityClick(product, -1)"><span class="glyphicon glyphicon-minus"></span></button>
</span>
<input type="text" v-model="product.quantity_form" class="form-control quantity" />
<span class="input-group-btn">
<button class="btn btn-default btn-plus" type="button" @click="productQuantityClick(product, 1)"><span class="glyphicon glyphicon-plus"></span></button>
</span>
</div>
</td>
<td class="price-total">
{{ formatPrice(product.price * product.quantity_form) }}
</td>
</tr>
<tr>
<td colspan="3"></td>
<td class="price-total">{{ priceTotal() }}</td>
</tr>
</tbody>
</table>
<div class="block-actions">
<button class="btn btn-primary" @click="changeStep('payment')">Valider</button>
</div>
</div>
<div class="alert alert-warning" v-else>
Aucun produit disponible
</div>
</div>
<div id="content-step-payment" v-if="step == 'payment'">
<div class="comment">
<label for="order-comment">Commentaire</label>
<textarea id="order-comment" v-model="comment" class="form-control"></textarea>
</div>
<div class="credit">
<input type="checkbox" id="checkbox-credit" v-model="creditCheckbox" />
<label for="checkbox-credit">Utiliser mon Crédit : {{ formatPrice(credit) }}</label>
<div class="info">
<span v-if="creditCheckbox == true">
{{ priceTotal() }} seront débités
</span>
</div>
</div>
<div class="block-actions">
<button class="btn btn-primary" @click="confirmClick">Je confirme ma commande</button>
</div>
</div> </div>
</div>
<div id="content-step-payment" v-if="step == 'payment'">
Paiement
</div> </div>
</div> </div>
</div>
<div id="infos" class="col-md-3" v-if="producer != null && producer.order_infos.length">
<div class="panel panel-default">
<div class="panel-heading">
Informations
</div>
<div class="panel-body">
{{ producer.order_infos }}
<!--<span class="glyphicon glyphicon-time"></span> {{ dateFormat }}
<div v-if="pointSaleActive">
<span class="glyphicon glyphicon-map-marker"></span>
{{ pointSaleActive.name }}
<div id="infos" class="col-md-3" v-if="producer != null && producer.order_infos.length">
<div class="panel panel-default">
<div class="panel-heading">
Informations
</div> </div>
<div class="products" v-if="products.length && oneProductOrdered()">
<span class="glyphicon glyphicon-th-list"></span>
<ul>
<li v-for="product in products" v-if="product.quantity_form">
{{ product.quantity_form }} x {{ product.name }}
</li>
</ul>
<div class="panel-body">
{{ producer.order_infos }}


<!--<span class="glyphicon glyphicon-time"></span> {{ dateFormat }}
<div v-if="pointSaleActive">
<span class="glyphicon glyphicon-map-marker"></span>
{{ pointSaleActive.name }}
</div>
<div class="products" v-if="products.length && oneProductOrdered()">
<span class="glyphicon glyphicon-th-list"></span>
<ul>
<li v-for="product in products" v-if="product.quantity_form">
{{ product.quantity_form }} x {{ product.name }}
</li>
</ul>
</div>
<div class="products" v-if="products.length && oneProductOrdered()">
<span class="glyphicon glyphicon-euro"></span>
{{ priceTotal() }}
</div>-->
</div> </div>
<div class="products" v-if="products.length && oneProductOrdered()">
<span class="glyphicon glyphicon-euro"></span>
{{ priceTotal() }}
</div>-->
</div> </div>
</div> </div>
</div> </div>

+ 17
- 7
producer/web/css/screen.css View File

/* line 116, ../sass/order/_order.scss */ /* line 116, ../sass/order/_order.scss */
.order-order #app-order-order .block-actions { .order-order #app-order-order .block-actions {
text-align: right; text-align: right;
margin-top: 20px;
} }
/* line 122, ../sass/order/_order.scss */
/* line 123, ../sass/order/_order.scss */
.order-order #app-order-order table#products td.name .name { .order-order #app-order-order table#products td.name .name {
text-transform: uppercase; text-transform: uppercase;
font-family: "myriadpro-regular"; font-family: "myriadpro-regular";
color: black; color: black;
font-size: 16px; font-size: 16px;
} }
/* line 128, ../sass/order/_order.scss */
/* line 129, ../sass/order/_order.scss */
.order-order #app-order-order table#products td.name .other { .order-order #app-order-order table#products td.name .other {
font-size: 14px; font-size: 14px;
color: #333; color: #333;
} }
/* line 132, ../sass/order/_order.scss */
/* line 133, ../sass/order/_order.scss */
.order-order #app-order-order table#products td.name .recipe { .order-order #app-order-order table#products td.name .recipe {
color: gray; color: gray;
} }
/* line 136, ../sass/order/_order.scss */
/* line 137, ../sass/order/_order.scss */
.order-order #app-order-order table#products .price-unit, .order-order #app-order-order table#products .price-total { .order-order #app-order-order table#products .price-unit, .order-order #app-order-order table#products .price-total {
width: 100px; width: 100px;
text-align: center; text-align: center;
} }
/* line 140, ../sass/order/_order.scss */
/* line 141, ../sass/order/_order.scss */
.order-order #app-order-order table#products .td-quantity { .order-order #app-order-order table#products .td-quantity {
width: 150px; width: 150px;
} }
/* line 142, ../sass/order/_order.scss */
/* line 143, ../sass/order/_order.scss */
.order-order #app-order-order table#products .td-quantity input.quantity { .order-order #app-order-order table#products .td-quantity input.quantity {
text-align: center; text-align: center;
} }
/* line 149, ../sass/order/_order.scss */
/* line 150, ../sass/order/_order.scss */
.order-order #app-order-order #content-step-payment .credit {
margin-top: 20px;
}
/* line 153, ../sass/order/_order.scss */
.order-order #app-order-order #content-step-payment .credit .info {
margin-left: 20px;
color: gray;
}
/* line 161, ../sass/order/_order.scss */
.order-order #app-order-order #infos .panel-body { .order-order #app-order-order #infos .panel-body {
padding-top: 0px; padding-top: 0px;
white-space: pre-line; white-space: pre-line;

+ 36
- 0
producer/web/js/vuejs/order-order.js View File

producer: null, producer: null,
date: null, date: null,
dateFormat: null, dateFormat: null,
distribution: null,
pointsSale: [], pointsSale: [],
pointSaleActive: null, pointSaleActive: null,
codePointSale: '',
products: [], products: [],
comment: '',
creditCheckbox: false,
credit: 0,
orderSuccess: false,
calendar: { calendar: {
mode: 'single', mode: 'single',
attrs: [], attrs: [],
axios.get("ajax-infos",{params: {date : this.getDate()}}) axios.get("ajax-infos",{params: {date : this.getDate()}})
.then(response => { .then(response => {
this.producer = response.data.producer ; this.producer = response.data.producer ;
this.credit = response.data.credit ;
this.calendar.attrs = [] ; this.calendar.attrs = [] ;
var distributions = response.data.distributions ; var distributions = response.data.distributions ;
} }
} }
if(response.data.distribution) {
this.distribution = response.data.distribution ;
}
if(response.data.points_sale) { if(response.data.points_sale) {
this.pointsSale = response.data.points_sale ; this.pointsSale = response.data.points_sale ;
} }
} }
return this.formatPrice(price) ; return this.formatPrice(price) ;
},
confirmClick: function() {
var productsArray = {} ;
for(var key in this.products) {
if( this.products[key].quantity_form != null &&
this.products[key].quantity_form > 0) {
productsArray[this.products[key].id] = this.products[key].quantity_form ;
}
}
axios.post('ajax-create', {
Order: {
id_distribution : this.distribution.id,
id_point_sale: this.pointSaleActive.id,
comment: this.comment
},
code_point_sale: this.codePointSale,
products: productsArray,
use_credit: this.creditCheckbox
}).then(response => {
if(response.data.status == 'success') {
this.orderSuccess = true ;
}
});
} }
} }
}); });

+ 12
- 0
producer/web/sass/order/_order.scss View File

.block-actions { .block-actions {
text-align: right ; text-align: right ;
margin-top: 20px ;
} }
table#products { table#products {
} }
} }
#content-step-payment {
.credit {
margin-top: 20px ;
.info {
margin-left: 20px ;
color: gray ;
}
}
}
#infos { #infos {
.panel-body { .panel-body {
padding-top: 0px ; padding-top: 0px ;

Loading…
Cancel
Save