Browse Source

Merge branch 'dev'

master
Guillaume Bourgeois 5 years ago
parent
commit
84bb945bc0
41 changed files with 1039 additions and 227 deletions
  1. +62
    -0
      backend/assets/VuejsSubscriptionFormAsset.php
  2. +44
    -19
      backend/controllers/DistributionController.php
  3. +14
    -2
      backend/controllers/OrderController.php
  4. +53
    -1
      backend/controllers/SubscriptionController.php
  5. +25
    -8
      backend/views/distribution/index.php
  6. +19
    -15
      backend/views/distribution/report.php
  7. +55
    -40
      backend/views/product/_form.php
  8. +12
    -0
      backend/views/product/index.php
  9. +35
    -26
      backend/views/site/index.php
  10. +25
    -9
      backend/views/subscription/_form.php
  11. +8
    -2
      backend/views/subscription/index.php
  12. +85
    -17
      backend/web/css/screen.css
  13. +51
    -0
      backend/web/js/lechatdesnoisettes.js
  14. +7
    -4
      backend/web/js/vuejs/distribution-index.js
  15. +90
    -0
      backend/web/js/vuejs/subscription-form.js
  16. +26
    -4
      backend/web/sass/distribution/_index.scss
  17. +8
    -3
      backend/web/sass/product/_form.scss
  18. +1
    -0
      backend/web/sass/screen.scss
  19. +1
    -0
      backend/web/sass/site/_index.scss
  20. +29
    -0
      backend/web/sass/subscription/_form.scss
  21. +16
    -10
      common/models/Order.php
  22. +84
    -8
      common/models/Product.php
  23. +3
    -0
      common/models/ProductOrder.php
  24. +21
    -0
      common/models/Subscription.php
  25. +39
    -7
      common/models/SubscriptionForm.php
  26. +26
    -0
      console/migrations/m190508_155647_ajout_champs_vente_kilo.php
  27. +15
    -0
      console/migrations/m190510_074749_ajout_champs_product_order_step.php
  28. +17
    -0
      console/migrations/m190511_061123_ajout_champs_unit_product_subscription.php
  29. +27
    -0
      console/migrations/m190515_122438_ajout_champs_price_product_subscription.php
  30. +24
    -7
      producer/controllers/OrderController.php
  31. +17
    -11
      producer/controllers/SubscriptionController.php
  32. +4
    -1
      producer/views/order/_form.php
  33. +8
    -7
      producer/views/order/order.php
  34. +1
    -1
      producer/views/site/index.php
  35. +7
    -4
      producer/views/subscription/_form.php
  36. +2
    -1
      producer/views/subscription/index.php
  37. +41
    -13
      producer/web/css/screen.css
  38. +9
    -3
      producer/web/js/vuejs/order-order.js
  39. +1
    -1
      producer/web/js/vuejs/subscription-form.js
  40. +14
    -1
      producer/web/sass/order/_order.scss
  41. +13
    -2
      producer/web/sass/subscription/_form.scss

+ 62
- 0
backend/assets/VuejsSubscriptionFormAsset.php View File

<?php
/**
Copyright distrib (2018)

contact@opendistrib.net

Ce logiciel est un programme informatique servant à aider les producteurs
à distribuer leur production en circuits courts.

Ce logiciel est régi par la licence CeCILL soumise au droit français et
respectant les principes de diffusion des logiciels libres. Vous pouvez
utiliser, modifier et/ou redistribuer ce programme sous les conditions
de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
sur le site "http://www.cecill.info".

En contrepartie de l'accessibilité au code source et des droits de copie,
de modification et de redistribution accordés par cette licence, il n'est
offert aux utilisateurs qu'une garantie limitée. Pour les mêmes raisons,
seule une responsabilité restreinte pèse sur l'auteur du programme, le
titulaire des droits patrimoniaux et les concédants successifs.

A cet égard l'attention de l'utilisateur est attirée sur les risques
associés au chargement, à l'utilisation, à la modification et/ou au
développement et à la reproduction du logiciel par l'utilisateur étant
donné sa spécificité de logiciel libre, qui peut le rendre complexe à
manipuler et qui le réserve donc à des développeurs et des professionnels
avertis possédant des connaissances informatiques approfondies. Les
utilisateurs sont donc invités à charger et tester l'adéquation du
logiciel à leurs besoins dans des conditions permettant d'assurer la
sécurité de leurs systèmes et ou de leurs données et, plus généralement,
à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.

Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
pris connaissance de la licence CeCILL, et que vous en avez accepté les
termes.
*/

namespace backend\assets;

use yii\web\AssetBundle;
use yii ;

/**
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class VuejsSubscriptionFormAsset extends \common\components\MyAssetBundle
{
public $basePath = '@webroot';
public $baseUrl = '@web';
public $css = [];
public $js = [];
public $depends = [
'common\assets\CommonAsset'
];
public function __construct()
{
parent::__construct() ;
$this->addAsset('js','js/vuejs/subscription-form.js') ;
}
}

+ 44
- 19
backend/controllers/DistributionController.php View File

foreach($productsArray as &$theProduct) { foreach($productsArray as &$theProduct) {
$quantityOrder = Order::getProductQuantity($theProduct['id'], $ordersArray) ; $quantityOrder = Order::getProductQuantity($theProduct['id'], $ordersArray) ;
$theProduct['quantity_ordered'] = $quantityOrder ; $theProduct['quantity_ordered'] = $quantityOrder ;
$theProduct['quantity_remaining'] = $theProduct['quantity_max'] - $quantityOrder ;
if($theProduct['quantity_remaining'] < 0) $theProduct['quantity_remaining'] = 0 ;
if(!is_numeric($theProduct['productDistribution'][0]['quantity_max'])) {
$theProduct['quantity_remaining'] = null ;
}
else {
$theProduct['quantity_remaining'] = $theProduct['productDistribution'][0]['quantity_max'] - $quantityOrder ;
}
$theProduct['quantity_form'] = 0 ; $theProduct['quantity_form'] = 0 ;
if($theProduct['productDistribution'][0]['active'] && $theProduct['productDistribution'][0]['quantity_max']) { if($theProduct['productDistribution'][0]['active'] && $theProduct['productDistribution'][0]['quantity_max']) {
// orders as array // orders as array
if($ordersArray) { if($ordersArray) {
foreach($ordersArray as &$order) {
$productOrderArray = \yii\helpers\ArrayHelper::map($order->productOrder, 'id_product', 'quantity') ;
foreach($ordersArray as &$order) {
$productOrderArray = [] ;
foreach($order->productOrder as $productOrder) {
$productOrderArray[$productOrder->id_product] = [
'quantity' => $productOrder->quantity * Product::$unitsArray[$productOrder->unit]['coefficient'],
'unit' => $productOrder->unit,
] ;
}
/*$productOrderArray = \yii\helpers\ArrayHelper::map($order->productOrder, 'id_product', 'quantity') ;
*/
foreach($productsArray as $product) { foreach($productsArray as $product) {
if(!isset($productOrderArray[$product['id']])) { if(!isset($productOrderArray[$product['id']])) {
$productOrderArray[$product['id']] = 0 ;
$productOrderArray[$product['id']] = [
'quantity' => 0,
'unit' => $product['unit']
] ;
} }
} }
// order create // order create
$productOrderArray = [] ; $productOrderArray = [] ;
foreach($productsArray as $product) { foreach($productsArray as $product) {
$productOrderArray[$product['id']] = 0 ;
$productOrderArray[$product['id']] = [
'quantity' => 0,
'unit' => $product['unit']
] ;
} }
$json['order_create'] = [ $json['order_create'] = [
'id_point_sale' => $idPointSaleDefault, 'id_point_sale' => $idPointSaleDefault,
foreach($pointSale->orders as $order) { foreach($pointSale->orders as $order) {
$orderLine = [$order->getStrUser()] ; $orderLine = [$order->getStrUser()] ;
foreach($order->productOrder as $productOrder) { foreach($order->productOrder as $productOrder) {
$orderLine[$productsIndexArray[$productOrder->id_product]] = $productOrder->quantity ;
$orderLine[$productsIndexArray[$productOrder->id_product]] = $productOrder->quantity . ' '.Product::strUnit($productOrder->unit, 'wording_short', true);
} }
$datas[] = $this->_lineOrderReportCSV($orderLine, $cpt) ; $datas[] = $this->_lineOrderReportCSV($orderLine, $cpt) ;
} }
// total point de vente // total point de vente
$totalsPointSaleArray = ['Total'] ; $totalsPointSaleArray = ['Total'] ;
foreach ($productsArray as $product) { foreach ($productsArray as $product) {
$quantity = Order::getProductQuantity($product->id, $pointSale->orders);
if($quantity) {
$index = $productsIndexArray[$product->id] ;
if(!isset($totalsPointSaleArray[$index])) {
$totalsPointSaleArray[$index] = 0 ;
foreach(Product::$unitsArray as $unit => $dataUnit) {
$quantity = Order::getProductQuantity($product->id, $pointSale->orders, false, $unit);
if ($quantity) {
$index = $productsIndexArray[$product->id] ;
if(!isset($totalsPointSaleArray[$index])) {
$totalsPointSaleArray[$index] = '' ;
}
$totalsPointSaleArray[$index] .= $quantity . ' '.Product::strUnit($unit, 'wording_short', true).' ';
} }
$totalsPointSaleArray[$index] += $quantity ;
} }
} }
// global // global
$totalsGlobalArray = ['> Totaux'] ; $totalsGlobalArray = ['> Totaux'] ;
foreach ($productsArray as $product) { foreach ($productsArray as $product) {
$quantity = Order::getProductQuantity($product->id, $ordersArray);
if($quantity) {
$index = $productsIndexArray[$product->id] ;
if(!isset($totalsGlobalArray[$index])) {
$totalsGlobalArray[$index] = 0 ;
foreach(Product::$unitsArray as $unit => $dataUnit) {
$quantity = Order::getProductQuantity($product->id, $ordersArray, false, $unit);
if ($quantity) {
$index = $productsIndexArray[$product->id] ;
if(!isset($totalsGlobalArray[$index])) {
$totalsGlobalArray[$index] = '' ;
}
$totalsGlobalArray[$index] .= $quantity . ' '.Product::strUnit($unit, 'wording_short', true).' ';
} }
$totalsGlobalArray[$index] += $quantity ;
} }
} }



+ 14
- 2
backend/controllers/OrderController.php View File

$productOrder->id_product = $product->id; $productOrder->id_product = $product->id;
$productOrder->quantity = $quantity; $productOrder->quantity = $quantity;
$productOrder->price = $p->price; $productOrder->price = $p->price;
$productOrder->unit = $p->unit;
$productOrder->step = $p->step;
$productOrder->save(); $productOrder->save();
} }
} }
$productOrder->id_product = $product->id; $productOrder->id_product = $product->id;
$productOrder->quantity = $quantity; $productOrder->quantity = $quantity;
$productOrder->price = $p->price; $productOrder->price = $p->price;
$productOrder->unit = $p->unit;
$productOrder->step = $p->step;
$productOrder->save(); $productOrder->save();
} }
} }


$order->save(); $order->save();


foreach ($products as $key => $quantity) {
foreach ($products as $key => $dataProductOrder) {
$product = Product::findOne($key); $product = Product::findOne($key);
$quantity = $dataProductOrder->quantity / Product::$unitsArray[$dataProductOrder->unit]['coefficient'] ;
if ($product && $quantity) { if ($product && $quantity) {
$productOrder = new ProductOrder; $productOrder = new ProductOrder;
$productOrder->id_order = $order->id; $productOrder->id_order = $order->id;
$productOrder->id_product = $key; $productOrder->id_product = $key;
$productOrder->quantity = $quantity; $productOrder->quantity = $quantity;
$productOrder->unit = $product->unit;
$productOrder->step = $product->step;
$productOrder->price = $product->price; $productOrder->price = $product->price;
$productOrder->save(); $productOrder->save();
} }
$order->distribution->id_producer == Producer::getId()) { $order->distribution->id_producer == Producer::getId()) {
$products = json_decode($products); $products = json_decode($products);
foreach ($products as $key => $quantity) {
foreach ($products as $key => $dataProductOrder) {
$productOrder = ProductOrder::findOne([ $productOrder = ProductOrder::findOne([
'id_order' => $idOrder, 'id_order' => $idOrder,
'id_product' => $key 'id_product' => $key
]); ]);


$quantity = $dataProductOrder->quantity
/ Product::$unitsArray[$dataProductOrder->unit]['coefficient'] ;
if ($quantity) { if ($quantity) {
if ($productOrder) { if ($productOrder) {
$productOrder->quantity = $quantity; $productOrder->quantity = $quantity;
$productOrder->id_order = $idOrder; $productOrder->id_order = $idOrder;
$productOrder->id_product = $key; $productOrder->id_product = $key;
$productOrder->quantity = $quantity; $productOrder->quantity = $quantity;
$productOrder->unit = $product->unit;
$productOrder->step = $product->step;
$productOrder->price = $product->price; $productOrder->price = $product->price;
} }
} }

+ 53
- 1
backend/controllers/SubscriptionController.php View File

namespace backend\controllers; namespace backend\controllers;


use common\models\Order ; use common\models\Order ;
use common\models\Product ;
use common\models\SubscriptionSearch ;


class SubscriptionController extends BackendController class SubscriptionController extends BackendController
{ {
{ {
// form // form
$model = new SubscriptionForm; $model = new SubscriptionForm;
$model->isAdmin = true ;
$model->id_producer = Producer::getId(); $model->id_producer = Producer::getId();


if($idOrder) { if($idOrder) {
{ {
// form // form
$model = new SubscriptionForm; $model = new SubscriptionForm;
$model->isAdmin = true ;
$subscription = Subscription::findOne($id); $subscription = Subscription::findOne($id);
if ($subscription) { if ($subscription) {
$model->id = $id; $model->id = $id;
$model->auto_payment = $subscription->auto_payment; $model->auto_payment = $subscription->auto_payment;
$model->week_frequency = $subscription->week_frequency; $model->week_frequency = $subscription->week_frequency;



// produits // produits
$arrayProductsSubscription = ProductSubscription::searchAll([ $arrayProductsSubscription = ProductSubscription::searchAll([
'id_subscription' => $model->id 'id_subscription' => $model->id
'update' => $update 'update' => $update
]) ; ]) ;
} }
public function actionAjaxInfos($idSubscription = 0)
{
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
$productsQuery = Product::find()
->where(['id_producer' => Producer::getId(),]) ;
if($idSubscription) {
$productsQuery->joinWith(['productSubscription' => function($query) use($idSubscription) {
$query->andOnCondition('product_subscription.id_subscription = '.((int) $idSubscription)) ;
}]) ;
}
$productsArray = $productsQuery->asArray()->all() ;
foreach($productsArray as &$theProduct) {
$theProduct['unit_save'] = $theProduct['unit'] ;
$theProduct['units'] = [] ;
$theProduct['units'][] = [
'unit' => $theProduct['unit'],
'wording_unit' => Product::strUnit($theProduct['unit'], 'wording_short'),
'step' => $theProduct['step'],
'price' => $theProduct['price']
] ;
if(isset($theProduct['productSubscription'][0])) {
if($theProduct['productSubscription'][0]['unit'] != $theProduct['unit']) {
$theProduct['units'][] = [
'unit' => $theProduct['productSubscription'][0]['unit'],
'wording_unit' => Product::strUnit($theProduct['productSubscription'][0]['unit'], 'wording_short'),
'step' => $theProduct['productSubscription'][0]['step'],
'price' => $theProduct['productSubscription'][0]['price'],
] ;
$theProduct['unit'] = $theProduct['productSubscription'][0]['unit'] ;
$theProduct['step'] = $theProduct['productSubscription'][0]['step'] ;
$theProduct['price'] = $theProduct['productSubscription'][0]['price'] ;
}
$theProduct['quantity'] = $theProduct['productSubscription'][0]['quantity'] * Product::$unitsArray[$theProduct['unit']]['coefficient'] ;
}
else {
$theProduct['quantity'] = '' ;
}
}
return [
'products' => $productsArray
] ;
}
} }

+ 25
- 8
backend/views/distribution/index.php View File

<button class="btn btn-default" v-else data-active-product="0" :data-id-product="product.id" @click="productActiveClick"><span class="glyphicon glyphicon-remove"></span></button> <button class="btn btn-default" v-else data-active-product="0" :data-id-product="product.id" @click="productActiveClick"><span class="glyphicon glyphicon-remove"></span></button>
</td> </td>
<td>{{ product.name }}</td> <td>{{ product.name }}</td>
<td class="quantity-ordered">{{ product.quantity_ordered ? product.quantity_ordered : '-' }}</td>
<td class="quantity-max"><input type="text" class="form-control quantity-max" placeholder="&infin;" :data-id-product="product.id" v-model="product.productDistribution[0].quantity_max" @keyup="productQuantityMaxChange" /></td>
<td class="quantity-ordered">{{ product.quantity_ordered ? product.quantity_ordered + ' '+ ((product.unit == 'piece') ? ' p.' : ' '+(product.unit == 'g' || product.unit == 'kg') ? 'kg' : 'litre(s)') : '&empty;' }}</td>
<td class="quantity-max">
<div class="input-group">
<input type="text" class="form-control quantity-max" placeholder="&infin;" :data-id-product="product.id" v-model="product.productDistribution[0].quantity_max" @keyup="productQuantityMaxChange" />
<span class="input-group-addon">{{ (product.unit == 'piece') ? 'p.' : ' '+((product.unit == 'g' || product.unit == 'kg') ? 'kg' : 'litre(s)') }}</span>
</div>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<td colspan="6"> <td colspan="6">
<strong><span class="glyphicon glyphicon-menu-right"></span> Produits</strong> <strong><span class="glyphicon glyphicon-menu-right"></span> Produits</strong>
<ul> <ul>
<li v-for="product in products" v-if="order.productOrder[product.id] > 0">
{{ order.productOrder[product.id] }} x {{ product.name }} <span v-if="product.productDistribution[0].active == 0" class="glyphicon glyphicon-warning-sign" title="Ce produit n'est pas activé"></span>
<li v-for="product in products" v-if="order.productOrder[product.id].quantity > 0">
{{ product.name }} : {{ order.productOrder[product.id].quantity }} {{ order.productOrder[product.id].unit == 'piece' ? ' pièce(s)' : ' '+order.productOrder[product.id].unit }} <span v-if="product.productDistribution[0].active == 0" class="glyphicon glyphicon-warning-sign" title="Ce produit n'est pas activé"></span>
</li> </li>
</ul> </ul>
<div v-if="order.comment && order.comment.length > 0" class="comment"> <div v-if="order.comment && order.comment.length > 0" class="comment">
<div class="col-md-8"> <div class="col-md-8">
<label class="control-label">Produits</label> <label class="control-label">Produits</label>
<table class="table table-condensed table-bordered table-hover table-products"> <table class="table table-condensed table-bordered table-hover table-products">
<thead>
<tr>
<th></th>
<th>Nom</th>
<th>Quantité</th>
<th>Reste</th>
</tr>
</thead>
<tbody> <tbody>
<tr v-for="product in products" :class="(order.productOrder[product.id] > 0) ? 'product-ordered' : ''"> <tr v-for="product in products" :class="(order.productOrder[product.id] > 0) ? 'product-ordered' : ''">
<td> <td>
<td class="quantity"> <td class="quantity">
<div class="input-group"> <div class="input-group">
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn btn-default btn-moins" type="button" @click="productQuantityClick(product.id, -1)"><span class="glyphicon glyphicon-minus"></span></button>
<button class="btn btn-default btn-moins" type="button" @click="productQuantityClick(product.id, order.productOrder[product.id].unit == 'piece' ? -1 : -parseFloat(product.step))"><span class="glyphicon glyphicon-minus"></span></button>
</span> </span>
<input type="text" v-model="order.productOrder[product.id]" class="form-control" />
<input type="text" v-model="order.productOrder[product.id].quantity" class="form-control" />
<span class="input-group-addon">{{ order.productOrder[product.id].unit == 'piece' ? 'p.' : order.productOrder[product.id].unit }}</span>
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn btn-default btn-plus" type="button" @click="productQuantityClick(product.id, 1)"><span class="glyphicon glyphicon-plus"></span></button>
<button class="btn btn-default btn-plus" type="button" @click="productQuantityClick(product.id, order.productOrder[product.id].unit == 'piece' ? 1 : parseFloat(product.step))"><span class="glyphicon glyphicon-plus"></span></button>
</span> </span>
</div> </div>
</td> </td>
<td class="quantity-remaining">/ {{ product.quantity_remaining }}</td>
<td class="quantity-remaining infinite" v-if="product.quantity_remaining === null || order.productOrder[product.id].unit != product.unit">&infin;</td>
<td class="quantity-remaining negative" v-else-if="product.quantity_remaining <= 0">{{ product.quantity_remaining }} {{ order.productOrder[product.id].unit == 'piece' ? ' p.' : ' '+(order.productOrder[product.id].unit == 'g' || order.productOrder[product.id].unit == 'kg') ? 'kg' : 'litre(s)' }}</td>
<td class="quantity-remaining has-quantity" v-else>{{ product.quantity_remaining }} {{ order.productOrder[product.id].unit == 'piece' ? ' p.' : ' '+(order.productOrder[product.id].unit == 'g' || order.productOrder[product.id].unit == 'kg') ? 'kg' : 'litre(s)' }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

+ 19
- 15
backend/views/distribution/report.php View File

*/ */


use common\models\Order ; use common\models\Order ;
use common\models\Product ;


$dayWeek = date('w', strtotime($date)); $dayWeek = date('w', strtotime($date));
$dayWeekArray = [0 => 'sunday', 1 => 'monday', 2 => 'tuesday', 3 => 'wednesday', 4 => 'thursday', 5 => 'friday', 6 => 'saturday']; $dayWeekArray = [0 => 'sunday', 1 => 'monday', 2 => 'tuesday', 3 => 'wednesday', 4 => 'thursday', 5 => 'friday', 6 => 'saturday'];
$add = false; $add = false;
foreach ($order->productOrder as $productOrder) { foreach ($order->productOrder as $productOrder) {
if ($product->id == $productOrder->id_product) { if ($product->id == $productOrder->id_product) {
$strProducts .= $productOrder->quantity . '&nbsp;' . $product->name . ', ';
$strProducts .= $product->name . ' (' .$productOrder->quantity . '&nbsp;'.Product::strUnit($productOrder->unit, 'wording_short', true).'), ';
$add = true; $add = true;
} }
} }
$strProducts = ''; $strProducts = '';
foreach ($productsArray as $product) { foreach ($productsArray as $product) {
$quantity = Order::getProductQuantity($product->id, $pointSale->orders);
$strQuantity = '';
if ($quantity) {
$strQuantity = $quantity;
$strProducts .= $strQuantity .'&nbsp;'. $product->name . ', ';
foreach(Product::$unitsArray as $unit => $dataUnit) {
$quantity = Order::getProductQuantity($product->id, $pointSale->orders, false, $unit);
if ($quantity) {
$strProducts .= $product->name . ' (' .$quantity . '&nbsp;'.Product::strUnit($unit, 'wording_short', true).'), ';
}
} }
} }
if (count($pointSale->orders)) if (count($pointSale->orders))
{ {
$html .= '<tr><td>'.$pointSale->name.'</td><td>' ; $html .= '<tr><td>'.$pointSale->name.'</td><td>' ;
foreach ($productsArray as $product) { foreach ($productsArray as $product) {
$quantity = Order::getProductQuantity($product->id, $pointSale->orders);
$strQuantity = ($quantity) ? $quantity : '' ;
if(strlen($strQuantity)) {
$html .= $strQuantity . '&nbsp;'.$product->name.', ' ;
foreach(Product::$unitsArray as $unit => $dataUnit) {
$quantity = Order::getProductQuantity($product->id, $pointSale->orders, false, $unit);
if ($quantity) {
$html .= $product->name . ' (' .$quantity . '&nbsp;'.Product::strUnit($unit, 'wording_short', true).'), ';
}
} }
} }
$html = substr($html, 0, strlen($html) - 2) ; $html = substr($html, 0, strlen($html) - 2) ;


// total // total
$html .= '<tr><td><strong>Total</strong></td><td>' ; $html .= '<tr><td><strong>Total</strong></td><td>' ;

foreach ($productsArray as $product) { foreach ($productsArray as $product) {
$quantity = Order::getProductQuantity($product->id, $ordersArray);
if($quantity) {
$html .= $quantity . '&nbsp;'.$product->name.', ' ;
foreach(Product::$unitsArray as $unit => $dataUnit) {
$quantity = Order::getProductQuantity($product->id, $ordersArray, false, $unit);
if ($quantity) {
$html .= $product->name . ' (' .$quantity . '&nbsp;'.Product::strUnit($unit, 'wording_short', true).'), ';
}
} }
} }



+ 55
- 40
backend/views/product/_form.php View File

*/ */


use yii\helpers\Html; use yii\helpers\Html;
use yii\widgets\ActiveForm;
use yii\bootstrap\ActiveForm;
use common\models\Product;
use yii\helpers\ArrayHelper ;


/* @var $this yii\web\View */ /* @var $this yii\web\View */
/* @var $model app\models\Produit */ /* @var $model app\models\Produit */


<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]); ?> <?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]); ?>


<?= $form->field($model, 'active')->radioList([1 => 'Oui',0 => 'Non' ]) ?>
<?= $form->field($model, 'name')->textInput(['maxlength' => 255]) ?>
<?= $form->field($model, 'description')->textInput(['maxlength' => 255]) ?>
<?= $form->field($model, 'recipe')->textarea() ?>
<?= $form->field($model, 'price')->textInput() ?>
<?= $form->field($model, 'weight')->textInput() ?>
<?= $form->field($model, 'quantity_max')
->hint('Renseignez ce champs si vous souhaitez limiter la quantité commandable pour ce produit.')
->textInput() ?>
<?= $form->field($model, 'photo')->fileInput() ?>
<?php
if(strlen($model->photo)) {
$url = Yii::$app->urlManagerProducer->getHostInfo().'/'.Yii::$app->urlManagerProducer->baseUrl ;
echo '<img class="photo-product" src="'.$url.'/uploads/'.$model->photo.'" width="200px" /><br />' ;
echo '<input type="checkbox" name="delete_photo" id="delete_photo" /> <label for="delete_photo">Supprimer la photo</label><br /><br />' ;
}
?>
<h2>Jours de distribution</h2>
<div id="days-production">
<?= $form->field($model, 'monday')->checkbox() ?>
<?= $form->field($model, 'tuesday')->checkbox() ?>
<?= $form->field($model, 'wednesday')->checkbox() ?>
<?= $form->field($model, 'thursday')->checkbox() ?>
<?= $form->field($model, 'friday')->checkbox() ?>
<?= $form->field($model, 'saturday')->checkbox() ?>
<?= $form->field($model, 'sunday')->checkbox() ?>
<div>
<div class="col-md-8">
<?= $form->field($model, 'active')->radioList([1 => 'Oui',0 => 'Non' ]) ?>
<?= $form->field($model, 'name')->textInput(['maxlength' => 255]) ?>
<?= $form->field($model, 'description')->textInput(['maxlength' => 255]) ?>
<?= $form->field($model, 'recipe')->textarea()->label('Description longue') ?>
<?= $form->field($model, 'unit')
->dropDownList(ArrayHelper::map(Product::$unitsArray, 'unit', 'wording'))
->label('Unité (pièce, poids ou volume)'); ?>
<?= $form->field($model, 'price',[
'inputTemplate' => '<div class="input-group">{input}<span class="input-group-addon"><span class="glyphicon glyphicon-euro"></span></span></div>',
]) ?>
<?= $form->field($model, 'step')->textInput()->hint('Définit ce qui est ajouté ou enlevé lors des changements de quantité.') ?>
<?= $form->field($model, 'weight')->textInput()->label('Poids (g)') ?>
<?= $form->field($model, 'quantity_max')
->hint('Renseignez ce champs si vous souhaitez limiter la quantité commandable pour une distribution.')
->textInput() ?>
<?php
if(!$model->isNewRecord) {
echo $form->field($model, 'apply_distributions')
->checkbox()
->hint('Sélectionnez cette option si vous souhaitez que ces modifications soient répercutées dans les distributions à venir déjà initialisées.');
}
?>
</div>
<div class="col-md-4">
<?= $form->field($model, 'photo')->fileInput() ?>
<?php
if(strlen($model->photo)) {
$url = Yii::$app->urlManagerProducer->getHostInfo().'/'.Yii::$app->urlManagerProducer->baseUrl ;
echo '<img class="photo-product" src="'.$url.'/uploads/'.$model->photo.'" width="200px" /><br />' ;
echo '<input type="checkbox" name="delete_photo" id="delete_photo" /> <label for="delete_photo">Supprimer la photo</label><br /><br />' ;
}
?>

<div id="days-production">
<h2>Jours de distribution</h2>
<?= $form->field($model, 'monday')->checkbox() ?>
<?= $form->field($model, 'tuesday')->checkbox() ?>
<?= $form->field($model, 'wednesday')->checkbox() ?>
<?= $form->field($model, 'thursday')->checkbox() ?>
<?= $form->field($model, 'friday')->checkbox() ?>
<?= $form->field($model, 'saturday')->checkbox() ?>
<?= $form->field($model, 'sunday')->checkbox() ?>
</div>
<div class="clr"></div>
</div>
<div class="clr"></div>
</div> </div>
<div class="clr"></div>
<?= $form->field($model, 'id_producer')->hiddenInput()->label('') ?>
<?php
if(!$model->isNewRecord) {
echo $form->field($model, 'apply_distributions')
->checkbox()
->hint('Sélectionnez cette option si vous souhaitez que ces modifications soient répercutées dans les distributions à venir déjà initialisées.');
}
?>
<?= $form->field($model, 'id_producer')->hiddenInput()->label('') ?>

<div class="form-group"> <div class="form-group">
<?= Html::submitButton($model->isNewRecord ? 'Ajouter' : 'Modifier', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?> <?= Html::submitButton($model->isNewRecord ? 'Ajouter' : 'Modifier', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div> </div>

+ 12
- 0
backend/views/product/index.php View File

use yii\helpers\Html; use yii\helpers\Html;
use yii\grid\GridView; use yii\grid\GridView;
use common\helpers\Url ; use common\helpers\Url ;
use common\models\Product ;


$this->setTitle('Produits') ; $this->setTitle('Produits') ;
$this->addBreadcrumb($this->getTitle()) ; $this->addBreadcrumb($this->getTitle()) ;
], ],
'name', 'name',
'description', 'description',
[
'attribute' => 'price',
'value' => function($model) {
$return = '' ;
if($model->price) {
$return = Price::format($model->price).' ('.Product::strUnit($model->unit, 'wording_unit', true).')' ;
}
return $return ;
}
],
[ [
'attribute' => 'active', 'attribute' => 'active',
'headerOptions' => ['class' => 'active'], 'headerOptions' => ['class' => 'active'],

+ 35
- 26
backend/views/site/index.php View File

<?php endif; ?> <?php endif; ?>
<div id="distributions"> <div id="distributions">
<!-- distributions -->
<?php if(count($distributionsArray)): ?>
<?php foreach($distributionsArray as $distribution): ?>
<div class="col-md-4 col-sm-12 col-xs-12">
<div class="info-box">
<span class="info-box-icon bg-green date">
<span class="day"><?= strftime('%A', strtotime($distribution->date)) ?></span>
<span class="num"><?= date('d', strtotime($distribution->date)) ?></span>
<span class="month"><?= strftime('%B', strtotime($distribution->date)) ?></span>
</span>
<div class="info-box-content">
<span class="info-box-text">
<?php if(count($distribution->order)): ?>
<strong><?= count($distribution->order); ?></strong> COMMANDES
<?php else: ?>
AUCUNE COMMANDE
<?php endif; ?>
</span>
<span class="info-box-number"></span>
<div class="buttons">
<?= Html::a('<span class="fa fa-eye"></span>', ['distribution/index', 'date' => $distribution->date], ['class' => 'btn btn-default']); ?>
<?php if(count($distribution->order)): ?><?= Html::a('<span class="fa fa-download"></span>', ['distribution/report', 'date' => $distribution->date], ['class' => 'btn btn-default']); ?><?php endif; ?>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
Prochaines distributions
</h3>
</div>
<div class="panel-body">
<!-- distributions -->
<?php if(count($distributionsArray)): ?>
<?php foreach($distributionsArray as $distribution): ?>
<div class="col-md-4 col-sm-12 col-xs-12">
<div class="info-box">
<span class="info-box-icon bg-green date">
<span class="day"><?= strftime('%A', strtotime($distribution->date)) ?></span>
<span class="num"><?= date('d', strtotime($distribution->date)) ?></span>
<span class="month"><?= strftime('%B', strtotime($distribution->date)) ?></span>
</span>
<div class="info-box-content">
<span class="info-box-text">
<?php if(count($distribution->order)): ?>
<strong><?= count($distribution->order); ?></strong> COMMANDES
<?php else: ?>
AUCUNE COMMANDE
<?php endif; ?>
</span>
<span class="info-box-number"></span>
<div class="buttons">
<?= Html::a('<span class="fa fa-eye"></span>', ['distribution/index', 'date' => $distribution->date], ['class' => 'btn btn-default']); ?>
<?php if(count($distribution->order)): ?><?= Html::a('<span class="fa fa-download"></span>', ['distribution/report', 'date' => $distribution->date], ['class' => 'btn btn-default']); ?><?php endif; ?>
</div>
</div>
</div> </div>
</div> </div>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</div> </div>
<div class="clr"></div> <div class="clr"></div>

+ 25
- 9
backend/views/subscription/_form.php View File

use common\models\User ; use common\models\User ;
use common\models\PointSale ; use common\models\PointSale ;


\backend\assets\VuejsSubscriptionFormAsset::register($this);

?> ?>


<div class="subscription-form">
<div class="subscription-form" id="app-subscription-form">
<?php $form = ActiveForm::begin(['enableClientValidation' => false]); ?> <?php $form = ActiveForm::begin(['enableClientValidation' => false]); ?>
<?php if($model->id): ?>
<?= $form->field($model, 'id')->hiddenInput() ?>
<?php endif; ?>
<div class="col-md-5" id="bloc-select-user"> <div class="col-md-5" id="bloc-select-user">
<?= $form->field($model, 'id_user')->dropDownList( ArrayHelper::map(User::find()->joinWith('userProducer')->where('user_producer.id_producer = '.Producer::getId())->andWhere('user_producer.active = 1')->orderBy('lastname ASC, name ASC')->all(), 'id', function($model, $defaultValue) { <?= $form->field($model, 'id_user')->dropDownList( ArrayHelper::map(User::find()->joinWith('userProducer')->where('user_producer.id_producer = '.Producer::getId())->andWhere('user_producer.active = 1')->orderBy('lastname ASC, name ASC')->all(), 'id', function($model, $defaultValue) {
return $model['lastname'].' '.$model['name']; return $model['lastname'].' '.$model['name'];
echo '<div class="alert alert-danger">'.$model->errors['products'][0].'</div>' ; echo '<div class="alert alert-danger">'.$model->errors['products'][0].'</div>' ;
} }
?> ?>
<table class="table table-bordered table-condensed table-hover">
<?php foreach ($productsArray as $p) : ?>
<tr>
<td><?= Html::encode($p->name) ?></td>
<table class="table table-bordered table-condensed table-hover" id="products">
<tr v-for="product in products">
<td>{{ product.name }}</td>
<td> <td>
<input type="hidden" :value="product.step" :name="'product_step_'+product.step" />
<input type="hidden" :value="product.price" :name="'product_price_'+product.price" />
<div class="input-group"> <div class="input-group">
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn btn-default btn-moins" type="button"><span class="glyphicon glyphicon-minus"></span></button>
<button class="btn btn-default" type="button" @click="changeQuantityProductSubscription(false, product)"><span class="glyphicon glyphicon-minus"></span></button>
</span> </span>
<?= Html::input('text', 'SubscriptionForm[products][product_'.$p->id.']', (isset($model->products['product_'.$p->id])) ? $model->products['product_'.$p->id] : '', ['class' => 'form-control quantity']) ?>
<input v-model="product.quantity" :name="'SubscriptionForm[products][product_'+product.id+']'" class="form-control input-quantity" />
<div class="input-group-addon">
<select class="form-control select-unit" :name="'product_unit_'+product.id" v-model="product.unit" v-if="product.units.length > 1" @change="changeUnitProductSubscription" :data-id-product="product.id">
<option v-for="unit in product.units" :value="unit.unit">{{ unit.wording_unit }}</option>
</select>
<span v-else>
{{ product.units[0].wording_unit }}
</span>
</div>
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn btn-default btn-plus" type="button"><span class="glyphicon glyphicon-plus"></span></button>
<button class="btn btn-default" type="button" @click="changeQuantityProductSubscription(true, product)"><span class="glyphicon glyphicon-plus"></span></button>
</span> </span>
</div> </div>
</td> </td>
<td>
<span v-if="product.units.length > 1 && product.unit != product.unit_save" class="glyphicon glyphicon-warning-sign" title="Unité différente du produit"></span>
</td>
</tr> </tr>
<?php endforeach; ?>
</table> </table>
</div> </div>

+ 8
- 2
backend/views/subscription/index.php View File



use yii\helpers\Html; use yii\helpers\Html;
use yii\grid\GridView; use yii\grid\GridView;
use common\models\Product ;




$this->setTitle('Abonnements') ; $this->setTitle('Abonnements') ;
foreach($model->productSubscription as $productSubscription) foreach($model->productSubscription as $productSubscription)
{ {
if(isset($productSubscription->product)) { if(isset($productSubscription->product)) {
$html .= $productSubscription->quantity . '&nbsp;x&nbsp;'.Html::encode($productSubscription->product->name).'<br />' ;
$html .= Html::encode($productSubscription->product->name).' ('.($productSubscription->quantity * Product::$unitsArray[$productSubscription->unit]['coefficient']).'&nbsp;'.Product::strUnit($productSubscription->unit, 'wording_short').')<br />' ;
} }
else { else {
$html .= 'Produit non défini<br />' ; $html .= 'Produit non défini<br />' ;
// aucun produit // aucun produit
if(!count($model->productSubscription)) if(!count($model->productSubscription))
{ {
$html .= '<span class="glyphicon glyphicon-warning-sign"></span> Aucun produit' ;
$html .= '<span class="glyphicon glyphicon-warning-sign"></span> Aucun produit<br />' ;
}
// des unités ne correspondent pas
if($model->hasUnitsNotMatch()) {
$html .= '<span class="glyphicon glyphicon-warning-sign" title="Des unités ne correspondent pas"></span>' ;
} }
return $html ; return $html ;

+ 85
- 17
backend/web/css/screen.css View File

color: #ff8c1a; color: #ff8c1a;
} }


/* line 5, ../sass/site/_index.scss */
/* line 4, ../sass/site/_index.scss */
.site-index #distributions .info-box {
border: solid 1px #e0e0e0;
}
/* line 6, ../sass/site/_index.scss */
.site-index #distributions .info-box .date { .site-index #distributions .info-box .date {
text-transform: uppercase; text-transform: uppercase;
font-size: 12px; font-size: 12px;
line-height: 20px; line-height: 20px;
padding-top: 10px; padding-top: 10px;
} }
/* line 11, ../sass/site/_index.scss */
/* line 12, ../sass/site/_index.scss */
.site-index #distributions .info-box .date span { .site-index #distributions .info-box .date span {
display: block; display: block;
} }
/* line 17, ../sass/site/_index.scss */
/* line 18, ../sass/site/_index.scss */
.site-index #distributions .info-box .date .num { .site-index #distributions .info-box .date .num {
font-size: 30px; font-size: 30px;
padding-top: 5px; padding-top: 5px;
padding-bottom: 5px; padding-bottom: 5px;
} }
/* line 29, ../sass/site/_index.scss */
/* line 30, ../sass/site/_index.scss */
.site-index #distributions .info-box-content .buttons { .site-index #distributions .info-box-content .buttons {
margin-top: 10px; margin-top: 10px;
} }
width: 50px; width: 50px;
} }


/* line 3, ../sass/subscription/_form.scss */
.subscription-form .field-subscriptionform-id {
display: none;
}
/* line 7, ../sass/subscription/_form.scss */
.subscription-form table#products .input-quantity {
text-align: center;
border-right: 0px none;
}
/* line 11, ../sass/subscription/_form.scss */
.subscription-form table#products .input-group-addon {
padding: 5px;
padding-left: 0px;
margin: 0px;
border-left: 0px none;
border-right: 0px none;
}
/* line 18, ../sass/subscription/_form.scss */
.subscription-form table#products .select-unit {
padding: 0px;
width: 58px;
text-align: center;
height: 22px;
}
/* line 24, ../sass/subscription/_form.scss */
.subscription-form table#products .glyphicon-warning-sign {
position: relative;
top: 10px;
}

/* line 3, ../sass/product/_index.scss */ /* line 3, ../sass/product/_index.scss */
.product-index .td-photo { .product-index .td-photo {
max-width: 100px; max-width: 100px;
background-color: white; background-color: white;
} }


/* line 5, ../sass/product/_form.scss */
/* line 6, ../sass/product/_form.scss */
.product-create #product-active label, .product-create #product-active label,
.product-update #product-active label { .product-update #product-active label {
margin-right: 15px; margin-right: 15px;
} }
/* line 10, ../sass/product/_form.scss */ /* line 10, ../sass/product/_form.scss */
.product-create #days-production .form-group,
.product-update #days-production .form-group {
float: left;
margin-right: 15px;
.product-create #days-production,
.product-update #days-production {
margin-top: 30px;
}
/* line 13, ../sass/product/_form.scss */
.product-create #days-production h2,
.product-update #days-production h2 {
font-size: 20px;
}
/* line 16, ../sass/product/_form.scss */
.product-create #days-production label,
.product-update #days-production label {
font-weight: normal;
} }


/** /**
} }
/* line 155, ../sass/distribution/_index.scss */ /* line 155, ../sass/distribution/_index.scss */
.distribution-index #modal-products table.table td.quantity-max { .distribution-index #modal-products table.table td.quantity-max {
width: 70px;
width: 120px;
} }
/* line 158, ../sass/distribution/_index.scss */ /* line 158, ../sass/distribution/_index.scss */
.distribution-index #modal-products table.table td.quantity-max input { .distribution-index #modal-products table.table td.quantity-max input {
} }
/* line 267, ../sass/distribution/_index.scss */ /* line 267, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products td.quantity { .distribution-index .modal-form-order table.table-products td.quantity {
width: 150px;
width: 165px;
} }
/* line 270, ../sass/distribution/_index.scss */ /* line 270, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products td.quantity input { .distribution-index .modal-form-order table.table-products td.quantity input {
text-align: center; text-align: center;
color: gray;
color: black;
} }
/* line 276, ../sass/distribution/_index.scss */
/* line 274, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products td.quantity .form-control {
border-right: 0px none;
padding-right: 4px;
}
/* line 278, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products td.quantity .input-group-addon {
padding: 5px;
padding-left: 0px;
margin: 0px;
border-left: 0px none;
border-right: 0px none;
}
/* line 286, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products td.quantity-remaining { .distribution-index .modal-form-order table.table-products td.quantity-remaining {
text-align: right; text-align: right;
} }
/* line 282, ../sass/distribution/_index.scss */
/* line 289, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products td.quantity-remaining.quantity-remaining, .distribution-index .modal-form-order table.table-products td.quantity-remaining.infinite {
color: #00A65A;
}
/* line 293, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products td.quantity-remaining.negative {
color: #DD4B39;
}
/* line 297, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products td.quantity-remaining.infinite, .distribution-index .modal-form-order table.table-products td.quantity-remaining.empty {
font-size: 18px;
}
/* line 304, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order .actions-form button { .distribution-index .modal-form-order .actions-form button {
margin-left: 15px; margin-left: 15px;
} }
/* line 290, ../sass/distribution/_index.scss */
/* line 312, ../sass/distribution/_index.scss */
.distribution-index .modal-payment .info-box .info-box-icon { .distribution-index .modal-payment .info-box .info-box-icon {
width: 50px; width: 50px;
} }
/* line 292, ../sass/distribution/_index.scss */
/* line 314, ../sass/distribution/_index.scss */
.distribution-index .modal-payment .info-box .info-box-icon i { .distribution-index .modal-payment .info-box .info-box-icon i {
font-size: 30px; font-size: 30px;
} }
/* line 296, ../sass/distribution/_index.scss */
/* line 318, ../sass/distribution/_index.scss */
.distribution-index .modal-payment .info-box .info-box-content { .distribution-index .modal-payment .info-box .info-box-content {
margin-left: 50px; margin-left: 50px;
} }

+ 51
- 0
backend/web/js/lechatdesnoisettes.js View File

chat_index_commandes_maj_points_vente() ; chat_index_commandes_maj_points_vente() ;
// admin // admin
chat_select_etablissement() ; chat_select_etablissement() ;
chat_products() ;
}) ; }) ;


var UrlManager = { var UrlManager = {
} }
}; };


function chat_products() {
if($('.product-create').size() || $('.product-update').size()) {
chat_products_event_unit(false) ;
$('#product-unit').change(function() {
chat_products_event_unit(true) ;
}) ;
}
}

function chat_products_event_unit(change) {
var unit = $('#product-unit').val() ;
if(unit == 'piece') {
$('.field-product-step').hide() ;
$('.field-product-weight').show() ;
}
else {
$('.field-product-step').show() ;
$('.field-product-weight').hide() ;
}
var label_price = $('.field-product-price .control-label') ;
var label_step = $('.field-product-step .control-label') ;
var label_quantity_max = $('.field-product-quantity_max .control-label') ;
if(unit == 'piece') {
label_price.html('Prix (la pièce)') ;
label_quantity_max.html('Quantité max par défaut (pièces)') ;
}
else if(unit == 'g' || unit == 'kg') {
label_price.html('Prix (au kg)') ;
label_quantity_max.html('Quantité max par défaut (kg)') ;
label_step.html('Pas ('+unit+')') ;
}
else if(unit == 'mL' || unit == 'L') {
label_price.html('Prix (au litre)') ;
label_quantity_max.html('Quantité max par défaut (litres)') ;
label_step.html('Pas ('+unit+')') ;
}
if(change) {
if(unit == 'piece') {
$('#product-step').val(1) ;
}
else {
$('#product-step').val('') ;
}
}
}

function chat_tooltip() { function chat_tooltip() {
$('[data-toggle="tooltip"]').tooltip({container:'body'}); $('[data-toggle="tooltip"]').tooltip({container:'body'});
} }

+ 7
- 4
backend/web/js/vuejs/distribution-index.js View File

var countProducts = 0 ; var countProducts = 0 ;
for(var key in this.order.productOrder) { for(var key in this.order.productOrder) {
if(this.order.productOrder[key] > 0) {
if(this.order.productOrder[key].quantity > 0) {
countProducts ++ ; countProducts ++ ;
} }
} }
} }
}, },
productQuantityClick: function(id_product, quantity) { productQuantityClick: function(id_product, quantity) {
if(this.order.productOrder[id_product] + quantity >= 0) {
var theQuantity = this.order.productOrder[id_product] + quantity ;
Vue.set(this.order.productOrder, id_product, theQuantity);
if(!this.order.productOrder[id_product].quantity) {
this.order.productOrder[id_product].quantity = 0 ;
}
if(parseFloat(this.order.productOrder[id_product].quantity) + quantity >= 0) {
var theQuantity = parseFloat(this.order.productOrder[id_product].quantity) + parseFloat(quantity) ;
Vue.set(this.order.productOrder, id_product, {quantity: theQuantity, unit: this.order.productOrder[id_product].unit});
} }
} }
} }

+ 90
- 0
backend/web/js/vuejs/subscription-form.js View File


/**
Copyright distrib (2018)

contact@opendistrib.net

Ce logiciel est un programme informatique servant à aider les producteurs
à distribuer leur production en circuits courts.

Ce logiciel est régi par la licence CeCILL soumise au droit français et
respectant les principes de diffusion des logiciels libres. Vous pouvez
utiliser, modifier et/ou redistribuer ce programme sous les conditions
de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
sur le site "http://www.cecill.info".

En contrepartie de l'accessibilité au code source et des droits de copie,
de modification et de redistribution accordés par cette licence, il n'est
offert aux utilisateurs qu'une garantie limitée. Pour les mêmes raisons,
seule une responsabilité restreinte pèse sur l'auteur du programme, le
titulaire des droits patrimoniaux et les concédants successifs.

A cet égard l'attention de l'utilisateur est attirée sur les risques
associés au chargement, à l'utilisation, à la modification et/ou au
développement et à la reproduction du logiciel par l'utilisateur étant
donné sa spécificité de logiciel libre, qui peut le rendre complexe à
manipuler et qui le réserve donc à des développeurs et des professionnels
avertis possédant des connaissances informatiques approfondies. Les
utilisateurs sont donc invités à charger et tester l'adéquation du
logiciel à leurs besoins dans des conditions permettant d'assurer la
sécurité de leurs systèmes et ou de leurs données et, plus généralement,
à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.

Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
pris connaissance de la licence CeCILL, et que vous en avez accepté les
termes.
*/

var app = new Vue({
el: '#app-subscription-form',
data: {
showLoading: false,
loading: true,
products: [],
units: []
},
mounted: function() {
this.init() ;
this.loading = false ;
},
methods: {
init: function() {
var app = this ;
this.showLoading = true ;
axios.get("ajax-infos",{params: {idSubscription: $('#subscriptionform-id').val()}})
.then(function(response) {
app.products = response.data.products ;
app.units = response.data.units ;
app.showLoading = false ;
}) ;
},
changeUnitProductSubscription: function(event) {
for(var i = 0; i < this.products.length ; i++) {
if(this.products[i].id == event.currentTarget.getAttribute('data-id-product')) {
this.products[i].quantity = '' ;
for(var j = 0; j < this.products[i].units.length ; j ++) {
if(this.products[i].unit == this.products[i].units[j].unit) {
this.products[i].step = this.products[i].units[j].step ;
this.products[i].price = this.products[i].units[j].price ;
}
}
}
}
},
changeQuantityProductSubscription: function(increase, product) {
var step = parseFloat(product.step) ;
if(!product.quantity) product.quantity = 0 ;
var quantity = parseFloat(product.quantity) ;
if(!increase) {
step = -step ;
}
if(quantity + step >= 0) {
product.quantity = quantity + step ;
}
if(!product.quantity) product.quantity = '' ;
}
}
});

+ 26
- 4
backend/web/sass/distribution/_index.scss View File

width: 50px ; width: 50px ;
} }
td.quantity-max { td.quantity-max {
width: 70px ;
width: 120px ;
input { input {
text-align: center ; text-align: center ;
} }
td.quantity { td.quantity {
width: 150px ;
width: 165px ;
input { input {
text-align: center ; text-align: center ;
color: gray ;
color: black ;
}
.form-control {
border-right: 0px none ;
padding-right: 4px ;
}
.input-group-addon {
padding: 5px ;
padding-left: 0px ;
margin: 0px ;
border-left: 0px none ;
border-right: 0px none ;
} }
} }
td.quantity-remaining { td.quantity-remaining {
text-align: right ; text-align: right ;
&.quantity-remaining, &.infinite {
color: #00A65A ;
}
&.negative {
color: #DD4B39 ;
}
&.infinite, &.empty {
font-size: 18px ;
}
} }
} }

+ 8
- 3
backend/web/sass/product/_form.scss View File



.product-create, .product-create,
.product-update { .product-update {
#product-active { #product-active {
label { label {
margin-right: 15px ; margin-right: 15px ;
} }
} }
#days-production { #days-production {
.form-group {
float: left ;
margin-right: 15px ;
margin-top: 30px ;
h2 {
font-size: 20px ;
}
label {
font-weight: normal ;
} }
} }
} }

+ 1
- 0
backend/web/sass/screen.scss View File

@import "_adminlte.scss" ; @import "_adminlte.scss" ;
@import "site/_index.scss" ; @import "site/_index.scss" ;
@import "subscription/_index.scss" ; @import "subscription/_index.scss" ;
@import "subscription/_form.scss" ;
@import "product/_index.scss" ; @import "product/_index.scss" ;
@import "product/_form.scss" ; @import "product/_form.scss" ;
@import "stats/_products.scss" ; @import "stats/_products.scss" ;

+ 1
- 0
backend/web/sass/site/_index.scss View File

.site-index { .site-index {
#distributions { #distributions {
.info-box { .info-box {
border: solid 1px #e0e0e0 ;
.date { .date {
text-transform: uppercase ; text-transform: uppercase ;
font-size: 12px ; font-size: 12px ;

+ 29
- 0
backend/web/sass/subscription/_form.scss View File


.subscription-form {
.field-subscriptionform-id {
display: none;
}
table#products {
.input-quantity {
text-align: center ;
border-right: 0px none ;
}
.input-group-addon {
padding: 5px ;
padding-left: 0px ;
margin: 0px ;
border-left: 0px none ;
border-right: 0px none ;
}
.select-unit {
padding: 0px;
width: 58px;
text-align: center;
height: 22px;
}
.glyphicon-warning-sign {
position: relative;
top: 10px ;
}
}
}

+ 16
- 10
common/models/Order.php View File

*/ */
public static function defaultOptionsSearch() { public static function defaultOptionsSearch() {
return [ return [
'with' => ['productOrder', 'creditHistory','creditHistory.userAction' , 'pointSale'],
'with' => ['productOrder','productOrder.product','creditHistory','creditHistory.userAction' , 'pointSale'],
'join_with' => ['distribution', 'user', 'user.userProducer'], 'join_with' => ['distribution', 'user', 'user.userProducer'],
'orderby' => 'order.date ASC', 'orderby' => 'order.date ASC',
'attribute_id_producer' => 'distribution.id_producer' 'attribute_id_producer' => 'distribution.id_producer'
public function initAmount() { public function initAmount() {
if (isset($this->productOrder)) { if (isset($this->productOrder)) {
foreach ($this->productOrder as $productOrder) { foreach ($this->productOrder as $productOrder) {
if ($productOrder->sale_mode == Product::SALE_MODE_UNIT) {
$this->amount += $productOrder->price * $productOrder->quantity ;
$this->amount += $productOrder->price * $productOrder->quantity ;
if ($productOrder->unit == 'piece') {
if(isset($productOrder->product)) { if(isset($productOrder->product)) {
$this->weight += ($productOrder->quantity * $productOrder->product->weight) / 1000 ; $this->weight += ($productOrder->quantity * $productOrder->product->weight) / 1000 ;
} }
} }
elseif ($productOrder->sale_mode == Product::SALE_MODE_WEIGHT) {
$this->amount += $productOrder->price * $productOrder->quantity / 1000;
else {
$this->weight += $productOrder->quantity ;
} }
} }
} }
$i = 0; $i = 0;
foreach ($this->productOrder as $p) { foreach ($this->productOrder as $p) {
if (isset($p->product)) { if (isset($p->product)) {
$html .= $p->quantity . ' x ' . Html::encode($p->product->name);
$html .= Html::encode($p->product->name) .' ('. $p->quantity .'&nbsp;'.Product::strUnit($p->unit, 'wording_short', true).')';
if (++$i != $count) { if (++$i != $count) {
$html .= '<br />'; $html .= '<br />';
} }
* *
* @return integer * @return integer
*/ */
public static function getProductQuantity($idProduct, $orders, $ignoreCancel = false)
{
public static function getProductQuantity($idProduct, $orders, $ignoreCancel = false, $unit = null)
{
$quantity = 0; $quantity = 0;
if (isset($orders) && is_array($orders) && count($orders)) { if (isset($orders) && is_array($orders) && count($orders)) {
foreach ($orders as $c) { foreach ($orders as $c) {
if(is_null($c->date_delete) || $ignoreCancel) { if(is_null($c->date_delete) || $ignoreCancel) {
foreach ($c->productOrder as $po) { foreach ($c->productOrder as $po) {
if ($po->id_product == $idProduct) {
if ($po->id_product == $idProduct &&
((is_null($unit) && $po->product->unit == $po->unit) || (!is_null($unit) && strlen($unit) && $po->unit == $unit))) {
$quantity += $po->quantity ; $quantity += $po->quantity ;
} }
} }
$count = 0 ; $count = 0 ;
if($this->productOrder && is_array($this->productOrder)) { if($this->productOrder && is_array($this->productOrder)) {
foreach($this->productOrder as $productOrder) { foreach($this->productOrder as $productOrder) {
$count += $productOrder->quantity ;
if($productOrder->unit == 'piece') {
$count ++ ;
}
else {
$count += $productOrder->quantity ;
}
} }
} }
return $count ; return $count ;

+ 84
- 8
common/models/Product.php View File

* @property double $price * @property double $price
* @property double $pweight * @property double $pweight
* @property string $recipe * @property string $recipe
* @property string $unit
* @property double $step
*/ */
class Product extends ActiveRecordCommon class Product extends ActiveRecordCommon
{ {
var $total = 0; var $total = 0;
var $apply_distributions = false ; var $apply_distributions = false ;
const SALE_MODE_UNIT = 'unit' ;
const SALE_MODE_WEIGHT = 'weight' ;
public static $unitsArray = [
'piece' => [
'unit' => 'piece',
'wording_unit' => 'la pièce',
'wording' => 'pièce(s)',
'wording_short' => 'p.',
'coefficient' => 1
],
'g' => [
'unit' => 'g',
'wording_unit' => 'le g',
'wording' => 'g',
'wording_short' => 'g',
'coefficient' => 1000
],
'kg' => [
'unit' => 'kg',
'wording_unit' => 'le kg',
'wording' => 'kg',
'wording_short' => 'kg',
'coefficient' => 1
],
'mL' => [
'unit' => 'mL',
'wording_unit' => 'le mL',
'wording' => 'mL',
'wording_short' => 'mL',
'coefficient' => 1000
],
'L' => [
'unit' => 'L',
'wording_unit' => 'le litre',
'wording' => 'L',
'wording_short' => 'L',
'coefficient' => 1
],
];
/** /**
* @inheritdoc * @inheritdoc
[['name', 'id_producer'], 'required'], [['name', 'id_producer'], 'required'],
[['active', 'order', 'quantity_max', 'id_producer'], 'integer'], [['active', 'order', 'quantity_max', 'id_producer'], 'integer'],
[['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday', 'unavailable','apply_distributions'], 'boolean'], [['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday', 'unavailable','apply_distributions'], 'boolean'],
[['price', 'weight'], 'number'],
[['price', 'weight', 'step'], 'number'],
[[ 'photo'], 'file'], [[ 'photo'], 'file'],
[['name', 'description', 'photo'], 'string', 'max' => 255],
[['name', 'description', 'photo', 'unit'], 'string', 'max' => 255],
[['recipe'], 'string', 'max' => 1000], [['recipe'], 'string', 'max' => 1000],
['step', 'required', 'message' => 'Champs obligatoire', 'when' => function($model) {
if($model->unit == 'piece') {
return true ;
}
return false ;
}],
]; ];
} }


'description' => 'Description', 'description' => 'Description',
'active' => 'Actif', 'active' => 'Actif',
'photo' => 'Photo', 'photo' => 'Photo',
'price' => 'Prix',
'weight' => 'Poids (g)',
'price' => 'Prix (€)',
'weight' => 'Poids',
'recipe' => 'Recette', 'recipe' => 'Recette',
'monday' => 'Lundi', 'monday' => 'Lundi',
'tuesday' => 'Mardi', 'tuesday' => 'Mardi',
'order' => 'Ordre', 'order' => 'Ordre',
'quantity_max' => 'Quantité max par défaut', 'quantity_max' => 'Quantité max par défaut',
'unavailable' => 'Épuisé', 'unavailable' => 'Épuisé',
'apply_distributions' => 'Appliquer ces modifications dans les distributions futures'
'apply_distributions' => 'Appliquer ces modifications dans les distributions futures',
'unit' => 'Unité',
'step' => 'Pas'
]; ];
} }
return $this->hasMany(ProductDistribution::className(), ['id_product' => 'id']); return $this->hasMany(ProductDistribution::className(), ['id_product' => 'id']);
} }
public function getProductSubscription()
{
return $this->hasMany(ProductSubscription::className(), ['id_product' => 'id']);
}
/** /**
* Retourne les options de base nécessaires à la fonction de recherche. * Retourne les options de base nécessaires à la fonction de recherche.
* *
return $productGift ; return $productGift ;
} }

/**
* Retourne le libellé d'une unité.
*
* @param $format wording_unit, wording, short
* @param $unitInDb Unité stockée en base de données (ex: si g > kg, si mL > L)
* @return $string Libellé de l'unité
*/
public static function strUnit($unit, $format = 'wording_short', $unitInDb = false)
{
$strUnit = '' ;
if($unitInDb) {
if($unit == 'g') {
$unit = 'kg' ;
}
if($unit == 'mL') {
$unit = 'L' ;
}
}
if(isset(self::$unitsArray[$unit]) && isset(self::$unitsArray[$unit][$format])) {
$strUnit = self::$unitsArray[$unit][$format] ;
}
return $strUnit ;
}
} }

+ 3
- 0
common/models/ProductOrder.php View File

* @property integer $id_order * @property integer $id_order
* @property integer $id_product * @property integer $id_product
* @property double $quantity * @property double $quantity
* @property string $unit
*/ */
class ProductOrder extends ActiveRecordCommon class ProductOrder extends ActiveRecordCommon
{ {
return [ return [
[['id_order', 'id_product', 'quantity'], 'required'], [['id_order', 'id_product', 'quantity'], 'required'],
[['id_order', 'id_product'], 'integer'], [['id_order', 'id_product'], 'integer'],
[['unit'], 'string', 'max' => 255],
[['quantity'], 'number', 'min' => 0] [['quantity'], 'number', 'min' => 0]
]; ];
} }
'id_order' => 'Commande', 'id_order' => 'Commande',
'id_product' => 'Product', 'id_product' => 'Product',
'quantity' => 'Quantité', 'quantity' => 'Quantité',
'unit' => 'Unité'
]; ];
} }

+ 21
- 0
common/models/Subscription.php View File

$productOrder->id_product = $productSubscription->product->id; $productOrder->id_product = $productSubscription->product->id;
$productOrder->quantity = $productSubscription->quantity; $productOrder->quantity = $productSubscription->quantity;
$productOrder->price = $productSubscription->product->price; $productOrder->price = $productSubscription->product->price;
$productOrder->unit = $productSubscription->unit;
$productOrder->step = $productSubscription->step;
$productOrder->save(); $productOrder->save();
$productsAdd = true; $productsAdd = true;
} }
} }
} }
} }
/**
* Retourne true si des unités des ProductSubscription ne correspondent pas
* aux Product.
*
* @return boolean
*/
public function hasUnitsNotMatch()
{
if(isset($this->productSubscription) && is_array($this->productSubscription)) {
foreach($this->productSubscription as $productSubscription) {
if(isset($productSubscription->product) && $productSubscription->unit != $productSubscription->product->unit) {
return true ;
}
}
}
return false ;
}
} }

+ 39
- 7
common/models/SubscriptionForm.php View File

*/ */
class SubscriptionForm extends Model class SubscriptionForm extends Model
{ {
public $isAdmin = false ;
public $id; public $id;
public $id_user; public $id_user;
public $username; public $username;


// produits // produits
if ($this->id) { if ($this->id) {
$productsSubscriptionsArray = ProductSubscription::findAll(['id_subscription' => $this->id]) ;
ProductSubscription::deleteAll(['id_subscription' => $this->id]); ProductSubscription::deleteAll(['id_subscription' => $this->id]);
} }


foreach ($this->products as $nameInput => $quantity) { foreach ($this->products as $nameInput => $quantity) {
if ($quantity) { if ($quantity) {
$idProduct = str_replace('product_', '', $nameInput);
$subscriptionProduct = new ProductSubscription;
$subscriptionProduct->id_subscription = $subscription->id;
$subscriptionProduct->id_product = $idProduct;
$subscriptionProduct->quantity = $quantity;
$subscriptionProduct->save();
$idProduct = (int) str_replace('product_', '', $nameInput);
$product = Product::findOne($idProduct) ;

$newProductSubscription = new ProductSubscription;
$newProductSubscription->id_subscription = $subscription->id;
$newProductSubscription->id_product = $idProduct;
if($this->isAdmin) {
$unit = (Yii::$app->getRequest()->post('product_unit_'.$idProduct)) ? Yii::$app->getRequest()->post('product_unit_'.$idProduct) : $product->unit ;
$newProductSubscription->unit = $unit;
$step = (Yii::$app->getRequest()->post('product_step_'.$idProduct)) ? Yii::$app->getRequest()->post('product_step_'.$idProduct) : $product->step ;
$newProductSubscription->step = $step;
$price = (Yii::$app->getRequest()->post('product_price_'.$idProduct)) ? Yii::$app->getRequest()->post('product_price_'.$idProduct) : $product->price ;
$newProductSubscription->price = $price;
}
else {
foreach($productsSubscriptionsArray as $productSubscription) {
if($productSubscription->id_product == $idProduct) {
$newProductSubscription->unit = $productSubscription->unit ;
$newProductSubscription->step = $productSubscription->step ;
$newProductSubscription->price = $productSubscription->price ;
}
}
if(!$newProductSubscription->unit) {
$newProductSubscription->unit = $product->unit ;
}
if(!$newProductSubscription->step) {
$newProductSubscription->step = $product->step;
}
if(!$newProductSubscription->price) {
$newProductSubscription->price = $product->price;
}
}
$newProductSubscription->quantity = $quantity / Product::$unitsArray[$newProductSubscription->unit]['coefficient'];

$newProductSubscription->save();
} }
} }

+ 26
- 0
console/migrations/m190508_155647_ajout_champs_vente_kilo.php View File

<?php

use yii\db\Migration;
use yii\db\mysql\Schema;

class m190508_155647_ajout_champs_vente_kilo extends Migration {

public function up() {
$this->dropColumn('product', 'sale_mode') ;
$this->addColumn('product', 'unit', Schema::TYPE_STRING.' DEFAULT \'piece\'') ;
$this->addColumn('product', 'step', Schema::TYPE_FLOAT.' DEFAULT 1') ;
$this->dropColumn('product_order', 'sale_mode') ;
$this->addColumn('product_order', 'unit', Schema::TYPE_STRING.' DEFAULT \'piece\'') ;
}

public function down() {
$this->addColumn('product', 'sale_mode', Schema::TYPE_STRING.' DEFAULT \'unit\'') ;
$this->dropColumn('product', 'unit') ;
$this->dropColumn('product', 'step') ;
$this->addColumn('product_order', 'sale_mode', Schema::TYPE_STRING.' DEFAULT \'unit\'') ;
$this->dropColumn('product_order', 'unit') ;
}

}

+ 15
- 0
console/migrations/m190510_074749_ajout_champs_product_order_step.php View File

<?php

use yii\db\Migration;
use yii\db\mysql\Schema;

class m190510_074749_ajout_champs_product_order_step extends Migration {

public function up() {
$this->addColumn('product_order', 'step', Schema::TYPE_FLOAT.' DEFAULT 1') ;
}

public function down() {
$this->dropColumn('product_order','step') ;
}
}

+ 17
- 0
console/migrations/m190511_061123_ajout_champs_unit_product_subscription.php View File

<?php

use yii\db\Migration;
use yii\db\mysql\Schema;

class m190511_061123_ajout_champs_unit_product_subscription extends Migration {

public function up() {
$this->addColumn('product_subscription', 'unit', Schema::TYPE_STRING . ' DEFAULT \'piece\'');
$this->addColumn('product_subscription', 'step', Schema::TYPE_FLOAT . ' DEFAULT 1');
}

public function down() {
$this->dropColumn('product_subscription', 'unit');
$this->dropColumn('product_subscription', 'step');
}
}

+ 27
- 0
console/migrations/m190515_122438_ajout_champs_price_product_subscription.php View File

<?php

use yii\db\Migration;
use yii\db\mysql\Schema;

class m190515_122438_ajout_champs_price_product_subscription extends Migration {

public function up() {
$this->addColumn('product_subscription', 'price', Schema::TYPE_FLOAT. ' DEFAULT 0') ;
$productsSubscriptionsArray = common\models\ProductSubscription::find()->all() ;
$productsArray = common\models\Product::find()->all() ;
foreach($productsSubscriptionsArray as $productSubscription) {
foreach($productsArray as $product) {
if($productSubscription->id_product == $product->id) {
$productSubscription->price = $product->price ;
$productSubscription->save() ;
}
}
}
}

public function down() {
$this->dropColumn('product_subscription', 'price') ;
}
}

+ 24
- 7
producer/controllers/OrderController.php View File

use common\models\Producer ; use common\models\Producer ;
use common\models\Order ; use common\models\Order ;
use common\models\UserPointSale ; use common\models\UserPointSale ;
use common\models\Product ;
use DateTime; use DateTime;


class OrderController extends ProducerBaseController class OrderController extends ProducerBaseController
} }
} }

// date // date
$errorDate = false; $errorDate = false;
if (isset($order->id_distribution)) { if (isset($order->id_distribution)) {
// suppression de tous les enregistrements ProductOrder // suppression de tous les enregistrements ProductOrder
if (!is_null($order)) { if (!is_null($order)) {
ProductOrder::deleteAll(['id_order' => $order->id]); ProductOrder::deleteAll(['id_order' => $order->id]);
$stepsArray = [] ;
if(isset($order->productOrder)) {
foreach($order->productOrder as $productOrder) {
$unitsArray[$productOrder->id_product] = $productOrder->unit;
}
}
} }


// produits dispos // produits dispos


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


$quantity = (int) $posts['products'][$product->id] ;
$unit = (!is_null($order) && isset($unitsArray[$product->id])) ? $unitsArray[$product->id] : $product->unit ;
$coefficient = Product::$unitsArray[$unit]['coefficient'] ;
$quantity = ((int) $posts['products'][$product->id]) / $coefficient ;
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'];
} }
$productOrder->quantity = $quantity; $productOrder->quantity = $quantity;
$productOrder->sale_mode = Product::SALE_MODE_UNIT ;
$productOrder->unit = $product->unit ;
$productOrder->step = $product->step ;
$productOrder->save(); $productOrder->save();
} }
} }
$indexProduct = 0 ; $indexProduct = 0 ;
foreach($productsArray as &$product) { foreach($productsArray as &$product) {
$coefficient_unit = Product::$unitsArray[$product['unit']]['coefficient'] ;
if(is_null($product['photo'])) { if(is_null($product['photo'])) {
$product['photo'] = '' ; $product['photo'] = '' ;
} }
$quantityOrderUser = Order::getProductQuantity($product['id'], [$orderUser], true) ; $quantityOrderUser = Order::getProductQuantity($product['id'], [$orderUser], true) ;
$product['quantity_ordered'] = $quantityOrder ; $product['quantity_ordered'] = $quantityOrder ;
$product['quantity_remaining'] = $product['quantity_max'] - $quantityOrder + $quantityOrderUser ; $product['quantity_remaining'] = $product['quantity_max'] - $quantityOrder + $quantityOrderUser ;
$product['quantity_form'] = $quantityOrderUser ;
$product['quantity_form'] = $quantityOrderUser * $coefficient_unit ;
foreach($orderUser->productOrder as $productOrder) {
if($productOrder->id_product == $product['id']) {
$product['wording_unit'] = Product::strUnit($productOrder->unit,'wording_unit', true) ;
$product['step'] = $productOrder->step ;
}
}
} }
else { else {
$product['quantity_form'] = 0 ; $product['quantity_form'] = 0 ;
$product['wording_unit'] = Product::strUnit($product['unit'],'wording_unit', true) ;
} }
$product['coefficient_unit'] = $coefficient_unit ;
if($product['quantity_remaining'] < 0) $product['quantity_remaining'] = 0 ; if($product['quantity_remaining'] < 0) $product['quantity_remaining'] = 0 ;
$product['index'] = $indexProduct ++ ; $product['index'] = $indexProduct ++ ;
} }
$json['products'] = $productsArray; $json['products'] = $productsArray;
} }

+ 17
- 11
producer/controllers/SubscriptionController.php View File



use common\models\SubscriptionForm ; use common\models\SubscriptionForm ;
use common\models\SubscriptionSearch ; use common\models\SubscriptionSearch ;
use common\models\Product ;


class SubscriptionController extends ProducerBaseController class SubscriptionController extends ProducerBaseController
{ {
if($idSubscription > 0) { if($idSubscription > 0) {
// Quantités produit de l'abonnement
$productQuantitiesArray = [] ;
$arrayProductsSubscription = ProductSubscription::searchAll([ $arrayProductsSubscription = ProductSubscription::searchAll([
'id_subscription' => $idSubscription 'id_subscription' => $idSubscription
]) ; ]) ;
if(count($arrayProductsSubscription)) {
foreach ($arrayProductsSubscription as $productSubscription) {
$productQuantitiesArray[$productSubscription->id_product] = $productSubscription->quantity;
}
}
} }
// Produits // Produits
$productsArray = Product::searchAll() ; $productsArray = Product::searchAll() ;
$indexProduct = 0 ; $indexProduct = 0 ;
foreach($productsArray as &$product) { foreach($productsArray as &$product) {
$quantity = 0 ; $quantity = 0 ;
if(isset($productQuantitiesArray) && count($productQuantitiesArray) && isset($productQuantitiesArray[$product->id])) {
$quantity = $productQuantitiesArray[$product->id] ;
$coefficientUnit = Product::$unitsArray[$product->unit]['coefficient'] ;
if(isset($arrayProductsSubscription) && count($arrayProductsSubscription)) {
foreach ($arrayProductsSubscription as $productSubscription) {
if($product->id == $productSubscription->id_product) {
$coefficientUnit = Product::$unitsArray[$productSubscription->unit]['coefficient'] ;
$quantity = $productSubscription->quantity * $coefficientUnit;
$product->step = $productSubscription->step;
$product->unit = $productSubscription->unit;
$product->price = $productSubscription->price;
}
}
} }
$product = array_merge( $product = array_merge(
$product->getAttributes(), $product->getAttributes(),
[ [
'index' => $indexProduct ++, 'index' => $indexProduct ++,
'quantity_form' => $quantity
'quantity_form' => $quantity,
'coefficient_unit' => $coefficientUnit,
'wording_unit' => Product::strUnit($product->unit, 'wording_unit', true),
'wording_short' => Product::strUnit($product->unit, 'wording_short'),
] ]
) ; ) ;
} }

+ 4
- 1
producer/views/order/_form.php View File

*/ */


use yii\widgets\ActiveForm; use yii\widgets\ActiveForm;
use common\models\Product;


?> ?>
<div class="order-form"> <div class="order-form">
<span class="name"><?= Html::encode($product->name); ?></span> - <span class="description"><?= Html::encode($product->getDescription()); ?></span><br /> <span class="name"><?= Html::encode($product->name); ?></span> - <span class="description"><?= Html::encode($product->getDescription()); ?></span><br />
<span class="recipe"><?= Html::encode($product->recipe); ?></span> <span class="recipe"><?= Html::encode($product->recipe); ?></span>
</td> </td>
<td class="price-unit"><span class="price"><?= number_format($product->price, 2); ?></span> €</td>
<td class="price-unit">
<span class="price"><?= Price::format($product->price); ?></span> €
</td>
<td class="column-quantity"> <td class="column-quantity">
<div class="input-group" <?php if (isset($availableProducts[$product->id]) && $availableProducts[$product->id]['quantity_remaining'] == 0 && $quantity == 0): ?>style="display:none;"<?php endif; ?>> <div class="input-group" <?php if (isset($availableProducts[$product->id]) && $availableProducts[$product->id]['quantity_remaining'] == 0 && $quantity == 0): ?>style="display:none;"<?php endif; ?>>
<span class="input-group-btn"> <span class="input-group-btn">

+ 8
- 7
producer/views/order/order.php View File

<span class="other"> <span class="other">
<span v-if="product.description.length">/</span> <span v-if="product.description.length">/</span>
<span class="description">{{ product.description }}</span> <span class="description">{{ product.description }}</span>
<span v-if="product.weight">({{ product.weight }}g)</span>
<span v-if="product.unit == 'piece' && product.weight">({{ product.weight }}g)</span>
</span> </span>
<span v-if="product.quantity_form == product.quantity_remaining && product.quantity_max > 0" class="label label-danger">
<span v-if="product.quantity_max > 0 && ((product.quantity_form / product.coefficient_unit == product.quantity_remaining) || ((product.quantity_remaining * product.coefficient_unit) - product.quantity_form) < product.step)" class="label label-danger">
Épuisé Épuisé
</span> </span>
<div class="recipe" v-if="product.recipe.length">{{ product.recipe }}</div> <div class="recipe" v-if="product.recipe.length">{{ product.recipe }}</div>
</td> </td>
<td class="price-unit"> <td class="price-unit">
{{ formatPrice(product.price) }}
{{ formatPrice(product.price) }}<br /><span class="unit">{{ product.wording_unit }}</span>
</td> </td>
<td class="td-quantity"> <td class="td-quantity">
<div class="input-group"> <div class="input-group">
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn btn-default btn-moins" type="button" @click="productQuantityClick(product, -1)" :disabled="product.quantity_form == 0"><span class="glyphicon glyphicon-minus"></span></button>
<button class="btn btn-default btn-moins" type="button" @click="productQuantityClick(product, product.unit == 'piece' ? -1 : -parseFloat(product.step))" :disabled="product.quantity_form == 0"><span class="glyphicon glyphicon-minus"></span></button>
</span> </span>
<input type="text" v-model="product.quantity_form" class="form-control quantity" readonly="readonly" /> <input type="text" v-model="product.quantity_form" class="form-control quantity" readonly="readonly" />
<span class="input-group-addon">{{ product.unit == 'piece' ? 'p.' : product.unit }}</span>
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn btn-default btn-plus" type="button" @click="productQuantityClick(product, 1)" :disabled="product.quantity_form == product.quantity_remaining && product.quantity_max > 0"><span class="glyphicon glyphicon-plus"></span></button>
<button class="btn btn-default btn-plus" type="button" @click="productQuantityClick(product, product.unit == 'piece' ? 1 : parseFloat(product.step))" :disabled="product.quantity_form == product.quantity_remaining && product.quantity_max > 0"><span class="glyphicon glyphicon-plus"></span></button>
</span> </span>
</div> </div>
</td> </td>
<td class="price-total"> <td class="price-total">
{{ formatPrice(product.price * product.quantity_form) }}
{{ formatPrice(product.price * (product.quantity_form / product.coefficient_unit )) }}
</td> </td>
</tr> </tr>
<tr class="total"> <tr class="total">
<td colspan="3"></td>
<td colspan="4"></td>
<td class="price-total">{{ priceTotal(true) }}</td> <td class="price-total">{{ priceTotal(true) }}</td>
</tr> </tr>
</tbody> </tbody>

+ 1
- 1
producer/views/site/index.php View File

'attribute' => 'price', 'attribute' => 'price',
'value' => function($model) { 'value' => function($model) {
if($model->price) { if($model->price) {
return Price::format($model->price) ;
return Price::format($model->price).' ('.Product::strUnit($model->unit, 'wording_unit', true).')' ;
} }
return '' ; return '' ;
} }

+ 7
- 4
producer/views/subscription/_form.php View File

<div class="recipe" v-if="product.recipe.length">{{ product.recipe }}</div> <div class="recipe" v-if="product.recipe.length">{{ product.recipe }}</div>
</td> </td>
<td class="price-unit"> <td class="price-unit">
{{ formatPrice(product.price) }}
{{ formatPrice(product.price) }}<br /><span class="unit">{{ product.wording_unit }}</span>
</td> </td>
<td class="quantity"> <td class="quantity">
<div class="input-group"> <div class="input-group">
<input type="hidden" :value="product.step" :name="'product_step_'+product.step" />
<input type="hidden" :value="product.unit" :name="'product_unit_'+product.unit" />
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn btn-default" type="button" @click="productQuantityClick(product, -1)"><span class="glyphicon glyphicon-minus"></span></button>
<button class="btn btn-default" type="button" @click="productQuantityClick(product, -product.step)"><span class="glyphicon glyphicon-minus"></span></button>
</span> </span>
<input type="text" v-model="product.quantity_form" :class="'form-control '+((product.quantity_form > 0) ? 'has-quantity' : '')"> <input type="text" v-model="product.quantity_form" :class="'form-control '+((product.quantity_form > 0) ? 'has-quantity' : '')">
<span class="input-group-addon">{{ product.wording_short }}</span>
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn btn-default" type="button" @click="productQuantityClick(product, 1)"><span class="glyphicon glyphicon-plus"></span></button>
<button class="btn btn-default" type="button" @click="productQuantityClick(product, product.step)"><span class="glyphicon glyphicon-plus"></span></button>
</span> </span>
</div> </div>
</td> </td>
<td class="price-total"> <td class="price-total">
{{ formatPrice(product.price * product.quantity_form) }}
{{ formatPrice(product.price * (product.quantity_form / product.coefficient_unit )) }}
</td> </td>
</tr> </tr>
<tr class="total"> <tr class="total">

+ 2
- 1
producer/views/subscription/index.php View File



use yii\helpers\Html; use yii\helpers\Html;
use yii\grid\GridView; use yii\grid\GridView;
use common\models\Product ;


$this->setTitle('Abonnements') ; $this->setTitle('Abonnements') ;
$this->addButton(['label' => '<span class="glyphicon glyphicon-plus"></span> Ajouter', 'url' => 'subscription/form', 'class' => 'btn btn-primary']) ; $this->addButton(['label' => '<span class="glyphicon glyphicon-plus"></span> Ajouter', 'url' => 'subscription/form', 'class' => 'btn btn-primary']) ;
foreach($model->productSubscription as $productSubscription) foreach($model->productSubscription as $productSubscription)
{ {
if(isset($productSubscription->product)) { if(isset($productSubscription->product)) {
$html .= $productSubscription->quantity . '&nbsp;x&nbsp;'.Html::encode($productSubscription->product->name).'<br />' ;
$html .= Html::encode($productSubscription->product->name).' ('.($productSubscription->quantity * Product::$unitsArray[$productSubscription->unit]['coefficient']) . '&nbsp'.Product::strUnit($productSubscription->unit, 'wording_short').')<br />' ;
} }
else { else {
$html .= 'Produit non défini<br />' ; $html .= 'Produit non défini<br />' ;

+ 41
- 13
producer/web/css/screen.css View File

text-align: center; text-align: center;
} }
/* line 224, ../sass/order/_order.scss */ /* line 224, ../sass/order/_order.scss */
.order-order #app-order-order table#products .price-unit .unit, .order-order #app-order-order table#products .price-total .unit {
color: gray;
font-size: 13px;
}
/* line 229, ../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: 175px;
} }
/* line 226, ../sass/order/_order.scss */
/* line 231, ../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;
border-right: 0px none;
} }
/* line 232, ../sass/order/_order.scss */
/* line 235, ../sass/order/_order.scss */
.order-order #app-order-order table#products .td-quantity .input-group-addon {
padding: 5px;
padding-left: 0px;
margin: 0px;
border-left: 0px none;
border-right: 0px none;
}
/* line 245, ../sass/order/_order.scss */
.order-order #app-order-order table#products tr.total .price-total { .order-order #app-order-order table#products tr.total .price-total {
font-size: 23px; font-size: 23px;
} }
/* line 240, ../sass/order/_order.scss */
/* line 253, ../sass/order/_order.scss */
.order-order #app-order-order #content-step-payment .credit .info { .order-order #app-order-order #content-step-payment .credit .info {
margin-left: 20px; margin-left: 20px;
color: gray; color: gray;
} }
/* line 246, ../sass/order/_order.scss */
/* line 259, ../sass/order/_order.scss */
.order-order #app-order-order #content-step-payment .comment { .order-order #app-order-order #content-step-payment .comment {
margin-bottom: 20px; margin-bottom: 20px;
} }
/* line 251, ../sass/order/_order.scss */
/* line 264, ../sass/order/_order.scss */
.order-order #app-order-order #infos { .order-order #app-order-order #infos {
margin-top: 30px; margin-top: 30px;
} }
/* line 253, ../sass/order/_order.scss */
/* line 266, ../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;
/* line 73, ../sass/subscription/_form.scss */ /* line 73, ../sass/subscription/_form.scss */
.subscription-create .subscription-form .products td.quantity, .subscription-create .subscription-form .products td.quantity,
.subscription-update .subscription-form .products td.quantity { .subscription-update .subscription-form .products td.quantity {
width: 150px;
width: 180px;
} }
/* line 76, ../sass/subscription/_form.scss */ /* line 76, ../sass/subscription/_form.scss */
.subscription-create .subscription-form .products td.quantity input, .subscription-create .subscription-form .products td.quantity input,
.subscription-update .subscription-form .products td.quantity input { .subscription-update .subscription-form .products td.quantity input {
text-align: center; text-align: center;
border-right: 0px none;
} }
/* line 79, ../sass/subscription/_form.scss */ /* line 79, ../sass/subscription/_form.scss */
.subscription-create .subscription-form .products td.quantity input.has-quantity, .subscription-create .subscription-form .products td.quantity input.has-quantity,
.subscription-update .subscription-form .products td.quantity input.has-quantity { .subscription-update .subscription-form .products td.quantity input.has-quantity {
font-weight: bold; font-weight: bold;
} }
/* line 85, ../sass/subscription/_form.scss */
/* line 84, ../sass/subscription/_form.scss */
.subscription-create .subscription-form .products td.quantity .input-group-addon,
.subscription-update .subscription-form .products td.quantity .input-group-addon {
background-color: white;
padding-left: 0px;
border-left: 0px none;
}
/* line 91, ../sass/subscription/_form.scss */
.subscription-create .subscription-form .products .name, .subscription-create .subscription-form .products .name,
.subscription-update .subscription-form .products .name { .subscription-update .subscription-form .products .name {
font-family: "capsuularegular"; font-family: "capsuularegular";
font-size: 20px; font-size: 20px;
color: black; color: black;
} }
/* line 91, ../sass/subscription/_form.scss */
/* line 97, ../sass/subscription/_form.scss */
.subscription-create .subscription-form .products .description, .subscription-create .subscription-form .products .description,
.subscription-update .subscription-form .products .description { .subscription-update .subscription-form .products .description {
font-style: italic; font-style: italic;
} }
/* line 95, ../sass/subscription/_form.scss */
/* line 101, ../sass/subscription/_form.scss */
.subscription-create .subscription-form .products .recipe, .subscription-create .subscription-form .products .recipe,
.subscription-update .subscription-form .products .recipe { .subscription-update .subscription-form .products .recipe {
font-size: 12px; font-size: 12px;
} }
/* line 99, ../sass/subscription/_form.scss */
/* line 105, ../sass/subscription/_form.scss */
.subscription-create .subscription-form .products .price-unit, .subscription-create .subscription-form .products .price-total, .subscription-create .subscription-form .products .price-unit, .subscription-create .subscription-form .products .price-total,
.subscription-update .subscription-form .products .price-unit, .subscription-update .subscription-form .products .price-unit,
.subscription-update .subscription-form .products .price-total { .subscription-update .subscription-form .products .price-total {
text-align: center; text-align: center;
width: 150px; width: 150px;
} }
/* line 104, ../sass/subscription/_form.scss */
/* line 110, ../sass/subscription/_form.scss */
.subscription-create .subscription-form .products .unit,
.subscription-update .subscription-form .products .unit {
color: gray;
font-size: 13px;
}
/* line 115, ../sass/subscription/_form.scss */
.subscription-create .subscription-form .products tr.total .price-total, .subscription-create .subscription-form .products tr.total .price-total,
.subscription-update .subscription-form .products tr.total .price-total { .subscription-update .subscription-form .products tr.total .price-total {
text-align: center; text-align: center;

+ 9
- 3
producer/web/js/vuejs/order-order.js View File

this.changeStep('products') ; this.changeStep('products') ;
}, },
productQuantityClick: function(product, quantity) { productQuantityClick: function(product, quantity) {
console.log(this.products[product.index].quantity_remaining) ;
if( this.products[product.index].quantity_form + quantity >= 0 && if( this.products[product.index].quantity_form + quantity >= 0 &&
(this.products[product.index].quantity_form + quantity <= this.products[product.index].quantity_remaining ||
(this.products[product.index].quantity_form + quantity <= (this.products[product.index].quantity_remaining * this.products[product.index].coefficient_unit) ||
!this.products[product.index].quantity_max) !this.products[product.index].quantity_max)
) { ) {
this.products[product.index].quantity_form += quantity ; this.products[product.index].quantity_form += quantity ;
var count = 0 ; var count = 0 ;
for(var key in this.products) { for(var key in this.products) {
if(this.products[key].quantity_form > 0) { if(this.products[key].quantity_form > 0) {
count += this.products[key].quantity_form ;
if(this.products[key].unit != 'piece') {
count ++ ;
}
else {
count += this.products[key].quantity_form ;
}
} }
} }
return count ; return count ;
var price = 0 ; var price = 0 ;
for(var key in this.products) { for(var key in this.products) {
if(this.products[key].quantity_form > 0) { if(this.products[key].quantity_form > 0) {
price += this.products[key].quantity_form * this.products[key].price ;
price += (this.products[key].quantity_form / this.products[key].coefficient_unit) * this.products[key].price ;
} }
} }
if(format) { if(format) {

+ 1
- 1
producer/web/js/vuejs/subscription-form.js View File

var price = 0 ; var price = 0 ;
for(var key in this.products) { for(var key in this.products) {
if(this.products[key].quantity_form > 0) { if(this.products[key].quantity_form > 0) {
price += this.products[key].quantity_form * this.products[key].price ;
price += (this.products[key].quantity_form / this.products[key].coefficient_unit) * this.products[key].price ;
} }
} }
if(format) { if(format) {

+ 14
- 1
producer/web/sass/order/_order.scss View File

.price-unit, .price-total { .price-unit, .price-total {
width: 100px ; width: 100px ;
text-align: center ; text-align: center ;
.unit {
color: gray ;
font-size: 13px ;
}
} }
.td-quantity { .td-quantity {
width: 150px ;
width: 175px ;
input.quantity { input.quantity {
text-align: center ; text-align: center ;
border-right: 0px none ;
}
.input-group-addon {
padding: 5px ;
padding-left: 0px ;
margin: 0px ;
border-left: 0px none ;
border-right: 0px none ;
} }
} }

+ 13
- 2
producer/web/sass/subscription/_form.scss View File

} }
td.quantity { td.quantity {
width: 150px ;
width: 180px ;
input { input {
text-align: center ; text-align: center ;
border-right: 0px none ;
&.has-quantity { &.has-quantity {
font-weight: bold ; font-weight: bold ;
} }
} }
.input-group-addon {
background-color: white ;
padding-left: 0px ;
border-left: 0px none ;
}
} }
.name { .name {
width: 150px ; width: 150px ;
} }
.unit {
color: gray ;
font-size: 13px ;
}
tr.total .price-total { tr.total .price-total {
text-align: center ; text-align: center ;
font-size: 20px ; font-size: 20px ;

Loading…
Cancel
Save