Ver código fonte

Merge branch 'dev'

prodstable
Guillaume Bourgeois 2 anos atrás
pai
commit
9740a1425d
55 arquivos alterados com 5039 adições e 4221 exclusões
  1. +11
    -0
      CHANGELOG.md
  2. +35
    -3
      backend/controllers/DevelopmentController.php
  3. +42
    -18
      backend/controllers/DistributionController.php
  4. +4
    -0
      backend/controllers/DocumentController.php
  5. +1011
    -966
      backend/controllers/OrderController.php
  6. +269
    -259
      backend/controllers/ProductController.php
  7. +1
    -3
      backend/controllers/SiteController.php
  8. +479
    -481
      backend/controllers/UserController.php
  9. +2
    -1
      backend/models/CreditForm.php
  10. +194
    -0
      backend/views/development/development.php
  11. +16
    -139
      backend/views/development/index.php
  12. +51
    -11
      backend/views/distribution/index.php
  13. +2
    -2
      backend/views/document/_download_product_line.php
  14. +252
    -241
      backend/views/document/_form.php
  15. +156
    -152
      backend/views/document/download.php
  16. +14
    -1
      backend/views/invoice/index.php
  17. +5
    -1
      backend/views/layouts/content.php
  18. +1
    -1
      backend/views/layouts/header.php
  19. +91
    -76
      backend/views/layouts/left.php
  20. +1
    -1
      backend/views/layouts/main.php
  21. +333
    -348
      backend/views/producer/update.php
  22. +103
    -97
      backend/views/product/index.php
  23. +1
    -1
      backend/views/site/index.php
  24. +2
    -6
      backend/views/user/index.php
  25. +96
    -70
      backend/views/user/orders.php
  26. +76
    -38
      backend/web/css/screen.css
  27. +231
    -199
      backend/web/js/backend.js
  28. +208
    -108
      backend/web/js/vuejs/distribution-index.js
  29. +11
    -11
      backend/web/js/vuejs/producer-update.js
  30. +1
    -0
      backend/web/sass/_alerts.scss
  31. +392
    -367
      backend/web/sass/distribution/_index.scss
  32. +11
    -1
      backend/web/sass/producer/_update.scss
  33. +162
    -141
      common/components/ActiveRecordCommon.php
  34. +1
    -0
      common/config/params.php
  35. +33
    -32
      common/helpers/GlobalParam.php
  36. +2
    -1
      common/models/CreditHistory.php
  37. +18
    -0
      common/models/Distribution.php
  38. +8
    -8
      common/models/Document.php
  39. +42
    -34
      common/models/InvoiceSearch.php
  40. +7
    -1
      common/models/LoginForm.php
  41. +3
    -1
      common/models/Order.php
  42. +74
    -0
      common/models/OrderSearch.php
  43. +5
    -3
      common/models/Producer.php
  44. +17
    -0
      common/models/ProductPrice.php
  45. +384
    -378
      common/models/Subscription.php
  46. +45
    -0
      common/versions/22.10.A.php
  47. +2
    -1
      composer.json
  48. +70
    -2
      composer.lock
  49. +30
    -0
      console/migrations/m221010_124540_document_is_sent.php
  50. +26
    -0
      console/migrations/m221013_085349_producer_add_field_latest_version.php
  51. +4
    -11
      frontend/views/site/contact.php
  52. +0
    -1
      info.php
  53. +1
    -2
      producer/controllers/SiteController.php
  54. +2
    -2
      producer/views/layouts/main.php
  55. +1
    -1
      producer/web/js/vuejs/subscription-form.js

+ 11
- 0
CHANGELOG.md Ver arquivo

@@ -0,0 +1,11 @@
# Change Log
Tous les ajouts, modifications et correctifs de ce projet sont listés dans ce fichier.

## [22.10.A] - 25/10/2022

### Ajouts
- [Producer] Ajout d'un captcha sur le formulaire de contact #228

### Modifications

### Correctifs

+ 35
- 3
backend/controllers/DevelopmentController.php Ver arquivo

@@ -79,7 +79,39 @@ class DevelopmentController extends Controller
*
* @return mixed
*/
public function actionIndex($status = Development::STATUS_OPEN)
public function actionIndex()
{
$versionsArray = [];
$pathVersions = Yii::getAlias('@common/versions');

if(is_dir($pathVersions))
{
$directory = opendir($pathVersions);
while( $child = readdir($directory) ){
if($child != '.' && $child != '..'){
$version = str_replace('.php', '', $child);
$versionsArray[$version] = [
'version' => str_replace('.php', '', $child),
'content' => $this->renderFile('@common/versions/'.$child)
];
}
}
}

krsort($versionsArray);

// Producer : set latest version d'Opendistrib
$producer = GlobalParam::getCurrentProducer();
$producer->latest_version_opendistrib = array_values($versionsArray)[0]['version'];
$producer->save();

return $this->render('index', [
'versionsArray' => $versionsArray
]);
}


public function actionDevelopment($status = Development::STATUS_OPEN)
{
$dataProvider = new ActiveDataProvider([
'query' => Development::find()
@@ -88,10 +120,10 @@ class DevelopmentController extends Controller
->orderBy('date DESC'),
]);

return $this->render('index', [
return $this->render('development', [
'dataProvider' => $dataProvider,
'status' => $status
]);
]);
}

/**

+ 42
- 18
backend/controllers/DistributionController.php Ver arquivo

@@ -82,7 +82,7 @@ class DistributionController extends BackendController
];
}

public function actionIndex($date = '')
public function actionIndex($date = '', $idOrderUpdate = 0)
{
$this->checkProductsPointsSale();

@@ -94,8 +94,14 @@ class DistributionController extends BackendController
$theDate = $date;
}

$orderUpdate = null;
if($idOrderUpdate) {
$orderUpdate = Order::searchOne(['id' => $idOrderUpdate]);
}

return $this->render('index', [
'date' => $theDate
'date' => $theDate,
'orderUpdate' => $orderUpdate
]);
}

@@ -176,20 +182,19 @@ class DistributionController extends BackendController
$json['distribution']['weight'] = number_format($weight, 2);

// products
$productsArray = Product::find()
->orWhere(['id_producer' => GlobalParam::getCurrentProducerId(),])
->joinWith([
'taxRate',
'productDistribution' => function ($query) use ($distribution) {
$query->andOnCondition(
'product_distribution.id_distribution = ' . $distribution->id
);
}
])
->orderBy('product_distribution.active DESC, order ASC')
->asArray()
->all();
$productsQuery = Product::find()
->orWhere(['id_producer' => GlobalParam::getCurrentProducerId(),])
->joinWith([
'taxRate',
'productDistribution' => function ($query) use ($distribution) {
$query->andOnCondition(
'product_distribution.id_distribution = ' . $distribution->id
);
}
])
->orderBy('product_distribution.active DESC, order ASC');

$productsArray = $productsQuery->asArray()->all();
$potentialRevenues = 0;
$potentialWeight = 0;

@@ -229,12 +234,14 @@ class DistributionController extends BackendController
$ordersArrayObject = $ordersArray;
if ($ordersArray) {
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,
'price' => Price::getPriceWithTax($productOrder->price, $productOrder->taxRate->value)
'price' => number_format($productOrder->price, 3),
'price_with_tax' => Price::getPriceWithTax($productOrder->price, $productOrder->taxRate->value),
];
}

@@ -243,7 +250,8 @@ class DistributionController extends BackendController
$productOrderArray[$product['id']] = [
'quantity' => 0,
'unit' => $product['unit'],
'price' => Price::getPriceWithTax($product['price'], $product['taxRate']['value']),
'price' => number_format($product['price'], 3),
'price_with_tax' => Price::getPriceWithTax($product['price'], $product['taxRate']['value']),
];
}
}
@@ -253,6 +261,7 @@ class DistributionController extends BackendController
foreach ($order->creditHistory as $creditHistory) {
$creditHistoryArray[] = [
'date' => date('d/m/Y H:i:s', strtotime($creditHistory->date)),
'user' => $creditHistory->user->getUsername(),
'user_action' => $creditHistory->strUserAction(),
'wording' => $creditHistory->getStrWording(),
'debit' => ($creditHistory->isTypeDebit() ? '- ' . $creditHistory->getAmount(
@@ -282,6 +291,7 @@ class DistributionController extends BackendController

$order = array_merge($order->getAttributes(), [
'selected' => false,
'weight' => $order->weight,
'amount' => Price::numberTwoDecimals($order->getAmountWithTax(Order::AMOUNT_TOTAL)),
'amount_paid' => Price::numberTwoDecimals($order->getAmount(Order::AMOUNT_PAID)),
'amount_remaining' => Price::numberTwoDecimals($order->getAmount(Order::AMOUNT_REMAINING)),
@@ -446,7 +456,6 @@ class DistributionController extends BackendController
$productsArray = Product::find()
->where([
'id_producer' => GlobalParam::getCurrentProducerId(),
'product.active' => 1,
])->joinWith([
'productPrice',
'productDistribution' => function ($q) use ($distribution) {
@@ -1459,6 +1468,7 @@ class DistributionController extends BackendController

if ($order && $order->distribution->id_producer == GlobalParam::getCurrentProducerId(
) && $order->id_user) {

$deliveryNote = null;
$idDeliveryNote = $order->id_delivery_note;
if ($idDeliveryNote) {
@@ -1491,6 +1501,20 @@ class DistributionController extends BackendController
if ($deliveryNote) {
$order->id_delivery_note = $deliveryNote->id;
$order->save();

// init invoice prices
$user = User::searchOne([
'id' => $deliveryNote->id_user
]);
$userProducer = UserProducer::searchOne([
'id_user' => $deliveryNote->id_user,
'id_producer' => GlobalParam::getCurrentProducerId()
]);
$order->initInvoicePrices([
'user' => $user,
'user_producer' => $userProducer,
'point_sale' => $order->pointSale
]);
}
}
}

+ 4
- 0
backend/controllers/DocumentController.php Ver arquivo

@@ -320,6 +320,10 @@ class DocumentController extends BackendController
{
$document = $this->findModel($id);
if ($document->send()) {

$document->is_sent = true;
$document->save();

Yii::$app->getSession()->setFlash('success', $this->getFlashMessage('send', $document));
} else {
Yii::$app->getSession()->setFlash('danger', $this->getFlashMessage('send', $document));

+ 1011
- 966
backend/controllers/OrderController.php
Diferenças do arquivo suprimidas por serem muito extensas
Ver arquivo


+ 269
- 259
backend/controllers/ProductController.php Ver arquivo

@@ -62,75 +62,75 @@ use common\helpers\Upload;
*/
class ProductController extends BackendController
{
var $enableCsrfValidation = false;
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
],
],
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'allow' => true,
'roles' => ['@'],
'matchCallback' => function ($rule, $action) {
return User::hasAccessBackend();
}
]
],
],
];
}
var $enableCsrfValidation = false;
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
],
],
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'allow' => true,
'roles' => ['@'],
'matchCallback' => function ($rule, $action) {
return User::hasAccessBackend();
}
]
],
],
];
}

/**
* Liste les modèles Produit.
*
* @return mixed
*/
public function actionIndex()
{
$searchModel = new ProductSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Liste les modèles Produit.
*
* @return mixed
*/
public function actionIndex()
{
$searchModel = new ProductSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}

/**
* Crée un modèle Produit.
* Si la création réussit, le navigateur est redirigé vers la page 'index'.
*
* @return mixed
*/
public function actionCreate()
{
$model = new Product();
$model->active = 1;
$model->id_producer = GlobalParam::getCurrentProducerId();
$model->monday = 1 ;
$model->tuesday = 1 ;
$model->wednesday = 1 ;
$model->thursday = 1 ;
$model->friday = 1 ;
$model->saturday = 1 ;
$model->sunday = 1 ;
$model->available_on_points_sale = 1 ;
/**
* Crée un modèle Produit.
* Si la création réussit, le navigateur est redirigé vers la page 'index'.
*
* @return mixed
*/
public function actionCreate()
{
$model = new Product();
$model->active = 1;
$model->id_producer = GlobalParam::getCurrentProducerId();
$model->monday = 1;
$model->tuesday = 1;
$model->wednesday = 1;
$model->thursday = 1;
$model->friday = 1;
$model->saturday = 1;
$model->sunday = 1;
$model->available_on_points_sale = 1;

if ($model->load(Yii::$app->request->post()) && $model->save()) {
$lastProductOrder = Product::find()->where('id_producer = :id_producer')->params([':id_producer' => GlobalParam::getCurrentProducerId()])->orderBy('order DESC')->one() ;
if($lastProductOrder) {
$model->order = ++ $lastProductOrder->order ;
$lastProductOrder = Product::find()->where('id_producer = :id_producer')->params([':id_producer' => GlobalParam::getCurrentProducerId()])->orderBy('order DESC')->one();
if ($lastProductOrder) {
$model->order = ++$lastProductOrder->order;
}
Upload::uploadFile($model, 'photo');
$model->save();

@@ -138,244 +138,254 @@ class ProductController extends BackendController
$this->processAvailabilityPointsSale($model);

// link product / distribution
Distribution::linkProductIncomingDistributions($model) ;
Distribution::linkProductIncomingDistributions($model);

Yii::$app->getSession()->setFlash('success', 'Produit <strong>'.Html::encode($model->name).'</strong> ajouté');
Yii::$app->getSession()->setFlash('success', 'Produit <strong>' . Html::encode($model->name) . '</strong> ajouté');

return $this->redirect(['index']);
}
else {
} else {
return $this->render('create', [
'model' => $model,
]);
}
}

/**
* Modifie un modèle Produit existant.
* Si la modification réussit, le navigateur est redirigé vers la page 'index'.
*
* @param integer $id
* @return mixed
*/
public function actionUpdate($id)
{
$request = Yii::$app->request;

$model = $this->findModel($id);

foreach ($model->productPointSale as $productPointSale) {
$model->pointsSale[] = $productPointSale->id_point_sale;
}

$photoFilenameOld = $model->photo;
/**
* Modifie un modèle Produit existant.
* Si la modification réussit, le navigateur est redirigé vers la page 'index'.
*
* @param integer $id
* @return mixed
*/
public function actionUpdate($id)
{
$request = Yii::$app->request;

$model = $this->findModel($id);

foreach ($model->productPointSale as $productPointSale) {
$model->pointsSale[] = $productPointSale->id_point_sale;
}

if ($model->load(Yii::$app->request->post()) && $model->save()) {
$photoFilenameOld = $model->photo;

Upload::uploadFile($model, 'photo', $photoFilenameOld);
if ($model->load(Yii::$app->request->post()) && $model->save()) {

$deletePhoto = $request->post('delete_photo', 0);
if ($deletePhoto) {
$model->photo = '';
$model->save();
}
Upload::uploadFile($model, 'photo', $photoFilenameOld);

// availability on points sale
$this->processAvailabilityPointsSale($model);
$deletePhoto = $request->post('delete_photo', 0);
if ($deletePhoto) {
$model->photo = '';
$model->save();
}

if ($model->apply_distributions) {
// link product / distribution
Distribution::linkProductIncomingDistributions($model);
}
// availability on points sale
$this->processAvailabilityPointsSale($model);

Yii::$app->getSession()->setFlash('success', 'Produit <strong>' . Html::encode($model->name) . '</strong> modifié');
return $this->redirect(['index']);
}
if ($model->apply_distributions) {
// link product / distribution
Distribution::linkProductIncomingDistributions($model);
}

return $this->render('update/update', [
'model' => $model,
'action' => 'update',
]);
Yii::$app->getSession()->setFlash('success', 'Produit <strong>' . Html::encode($model->name) . '</strong> modifié');
return $this->redirect(['index']);
}

/**
* Traite les accès restreints d'un point de vente.
*/
public function processAvailabilityPointsSale($model)
{
ProductPointSale::deleteAll(['id_product' => $model->id]);

if (is_array($model->pointsSale) && count($model->pointsSale)) {
foreach ($model->pointsSale as $key => $val) {
$pointSale = PointSale::findOne($val);
if ($pointSale) {
$productPointSale = new ProductPointSale;
$productPointSale->id_product = $model->id;
$productPointSale->id_point_sale = $pointSale->id;
$productPointSale->available = ($model->available_on_points_sale) ? 0 : 1;
$productPointSale->save();
}
return $this->render('update/update', [
'model' => $model,
'action' => 'update',
]);
}

/**
* Traite les accès restreints d'un point de vente.
*/
public function processAvailabilityPointsSale($model)
{
ProductPointSale::deleteAll(['id_product' => $model->id]);

if (is_array($model->pointsSale) && count($model->pointsSale)) {
foreach ($model->pointsSale as $key => $val) {
$pointSale = PointSale::findOne($val);
if ($pointSale) {
$productPointSale = new ProductPointSale;
$productPointSale->id_product = $model->id;
$productPointSale->id_point_sale = $pointSale->id;
$productPointSale->available = ($model->available_on_points_sale) ? 0 : 1;
$productPointSale->save();
}
}
}
}

public function actionPricesList($id)
{
$request = Yii::$app->request;
$model = $this->findModel($id);

$searchModel = new ProductPriceSearch();
$searchModel->id_product = $id ;

$dataProvider = $searchModel->search(array_merge(Yii::$app->request->queryParams, [
'id_product' => $id
]));

$userProducerWithProductPercent = UserProducer::searchAll([], [
'join_with' => ['user'],
'conditions' => 'user_producer.product_price_percent != 0',
]) ;

$pointSaleWithProductPercent = PointSale::searchAll([], [
'conditions' => 'point_sale.product_price_percent != 0'
]) ;

return $this->render('update/prices/list', [
'model' => $model,
'action' => 'prices-list',
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
'userProducerWithProductPercent' => $userProducerWithProductPercent,
'pointSaleWithProductPercent' => $pointSaleWithProductPercent,
]);
}

public function actionPricesCreate($idProduct)
{
$model = new ProductPrice();
$model->id_product = $idProduct;
$modelProduct = $this->findModel($idProduct) ;
public function actionPricesList($id)
{
$request = Yii::$app->request;
$model = $this->findModel($id);

$searchModel = new ProductPriceSearch();
$searchModel->id_product = $id;

$dataProvider = $searchModel->search(array_merge(Yii::$app->request->queryParams, [
'id_product' => $id
]));

$userProducerWithProductPercent = UserProducer::searchAll([], [
'join_with' => ['user'],
'conditions' => 'user_producer.product_price_percent != 0',
]);

$pointSaleWithProductPercent = PointSale::searchAll([], [
'conditions' => 'point_sale.product_price_percent != 0'
]);

return $this->render('update/prices/list', [
'model' => $model,
'action' => 'prices-list',
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
'userProducerWithProductPercent' => $userProducerWithProductPercent,
'pointSaleWithProductPercent' => $pointSaleWithProductPercent,
]);
}

if ($model->load(Yii::$app->request->post())) {
public function actionPricesCreate($idProduct)
{
$model = new ProductPrice();
$model->id_product = $idProduct;
$modelProduct = $this->findModel($idProduct);

$conditionsProductPriceExist = [
'id_product' => $idProduct,
'id_user' => $model->id_user ? $model->id_user : null,
'id_user_group' => $model->id_user_group ? $model->id_user_group : null,
'id_point_sale' => $model->id_point_sale ? $model->id_point_sale : null,
'from_quantity' => $model->from_quantity ? $model->from_quantity : null,
] ;
if ($model->load(Yii::$app->request->post())) {

$productPriceExist = ProductPrice::findOne($conditionsProductPriceExist) ;
$conditionsProductPriceExist = [
'id_product' => $idProduct,
'id_user' => $model->id_user ? $model->id_user : null,
'id_user_group' => $model->id_user_group ? $model->id_user_group : null,
'id_point_sale' => $model->id_point_sale ? $model->id_point_sale : null,
'from_quantity' => $model->from_quantity ? $model->from_quantity : null,
];

if($productPriceExist) {
$productPriceExist->delete() ;
Yii::$app->getSession()->setFlash('warning', 'Un prix existait déjà pour cet utilisateur / point de vente, il a été supprimé.');
}
$productPriceExist = ProductPrice::findOne($conditionsProductPriceExist);

if($model->save()) {
Yii::$app->getSession()->setFlash('success', 'Le prix a bien été ajouté.');
return $this->redirect(['product/prices-list', 'id' => $idProduct]);
}
}
if ($productPriceExist) {
$productPriceExist->delete();
Yii::$app->getSession()->setFlash('warning', 'Un prix existait déjà pour cet utilisateur / point de vente, il a été supprimé.');
}

return $this->render('update/prices/create', [
'model' => $model,
'modelProduct' => $modelProduct,
]);
if ($model->save()) {
Yii::$app->getSession()->setFlash('success', 'Le prix a bien été ajouté.');
return $this->redirect(['product/prices-list', 'id' => $idProduct]);
}
}

public function actionPricesUpdate($id)
{
$request = Yii::$app->request;
return $this->render('update/prices/create', [
'model' => $model,
'modelProduct' => $modelProduct,
]);
}

$model = $this->findModelProductPrice($id);
$modelProduct = $this->findModel($model->id_product) ;
public function actionPricesUpdate($id)
{
$request = Yii::$app->request;

if ($model->load(Yii::$app->request->post()) && $model->save()) {
Yii::$app->getSession()->setFlash('success', 'Prix modifié');
return $this->redirect(['product/prices-list', 'id' => $model->id_product]);
}
$model = $this->findModelProductPrice($id);
$modelProduct = $this->findModel($model->id_product);

return $this->render('update/prices/update', [
'model' => $model,
'modelProduct' => $modelProduct,
'action' => 'prices-update',
]);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
Yii::$app->getSession()->setFlash('success', 'Prix modifié');
return $this->redirect(['product/prices-list', 'id' => $model->id_product]);
}

public function actionPricesDelete($id)
{
$productPrice = $this->findModelProductPrice($id);
$productPrice->delete();
Yii::$app->getSession()->setFlash('success', 'Prix supprimé');
return $this->redirect(['product/prices-list', 'id' => $productPrice->id_product]);
}
return $this->render('update/prices/update', [
'model' => $model,
'modelProduct' => $modelProduct,
'action' => 'prices-update',
]);
}

/**
* Supprime un modèle Produit.
* Si la suppression réussit, le navigateur est redirigé vers la page
* 'index'.
*
* @param integer $id
* @return mixed
*/
public function actionDelete($id, $confirm = false)
{
$product = $this->findModel($id);

if ($confirm) {
$product->delete();
ProductDistribution::deleteAll(['id_product' => $id]);
Yii::$app->getSession()->setFlash('success', 'Produit <strong>' . Html::encode($product->name) . '</strong> supprimé');
} else {
Yii::$app->getSession()->setFlash('info', 'Souhaitez-vous vraiment supprimer le produit <strong>' . Html::encode($product->name) . '</strong> ? '
. Html::a('Oui', ['product/delete', 'id' => $id, 'confirm' => 1], ['class' => 'btn btn-default']) . ' ' . Html::a('Non', ['product/index'], ['class' => 'btn btn-default']));
}
public function actionPricesDelete($id)
{
$productPrice = $this->findModelProductPrice($id);
$productPrice->delete();
Yii::$app->getSession()->setFlash('success', 'Prix supprimé');
return $this->redirect(['product/prices-list', 'id' => $productPrice->id_product]);
}

return $this->redirect(['index']);
/**
* Supprime un modèle Produit.
* Si la suppression réussit, le navigateur est redirigé vers la page
* 'index'.
*
* @param integer $id
* @return mixed
*/
public function actionDelete($id, $confirm = false)
{
$product = $this->findModel($id);

if ($confirm) {
$product->delete();
ProductDistribution::deleteAll(['id_product' => $id]);
Yii::$app->getSession()->setFlash('success', 'Produit <strong>' . Html::encode($product->name) . '</strong> supprimé');
} else {
Yii::$app->getSession()->setFlash('info', 'Souhaitez-vous vraiment supprimer le produit <strong>' . Html::encode($product->name) . '</strong> ? '
. Html::a('Oui', ['product/delete', 'id' => $id, 'confirm' => 1], ['class' => 'btn btn-default']) . ' ' . Html::a('Non', ['product/index'], ['class' => 'btn btn-default']));
}

/**
* Modifie l'ordre des produits.
*
* @param array $array
*/
public function actionOrder()
{
$array = Yii::$app->request->post('array');
$orderArray = json_decode(stripslashes($array));

foreach ($orderArray as $id => $order) {
$product = $this->findModel($id);
$product->order = $order;
$product->save();
}
return $this->redirect(['index']);
}

/**
* Modifie l'ordre des produits.
*
* @param array $array
*/
public function actionOrder()
{
$array = Yii::$app->request->post('array');
$orderArray = json_decode(stripslashes($array));

foreach ($orderArray as $id => $order) {
$product = $this->findModel($id);
$product->order = $order;
$product->save();
}
}

/**
* Recherche un produit en fonction de son ID.
*
* @param integer $id
* @return Produit
* @throws NotFoundHttpException si le modèle n'est pas trouvé
*/
protected function findModel($id)
{
if (($model = Product::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
public function actionAjaxToggleActive($id, $active)
{
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
$product = $this->findModel($id);
$product->active = (int) $active;

Distribution::linkProductIncomingDistributions($product);

return ['success', 'id' => $id, 'active' => $active];
}

/**
* Recherche un produit en fonction de son ID.
*
* @param integer $id
* @return Produit
* @throws NotFoundHttpException si le modèle n'est pas trouvé
*/
protected function findModel($id)
{
if (($model = Product::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}

protected function findModelProductPrice($id)
{
if (($model = ProductPrice::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
protected function findModelProductPrice($id)
{
if (($model = ProductPrice::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}

}

+ 1
- 3
backend/controllers/SiteController.php Ver arquivo

@@ -117,6 +117,7 @@ class SiteController extends BackendController
*/
public function actionIndex()
{

// commandes
$optionDashboardNumberDistributions = Producer::getConfig('option_dashboard_number_distributions') ;
$dashboardNumberDistributions = $optionDashboardNumberDistributions ? $optionDashboardNumberDistributions : 3 ;
@@ -175,8 +176,6 @@ class SiteController extends BackendController
]);

// clients
$nbUsers = User::searchCount();

$usersArray = User::findBy()
->orderBy('created_at DESC')
->limit(5)
@@ -196,7 +195,6 @@ class SiteController extends BackendController
'distributionsArray' => $distributionsArray,
'ordersArray' => $ordersArray,
'usersArray' => $usersArray,
'nbUsers' => $nbUsers,
'usersNegativeCredit' => $usersNegativeCredit,
'producer' => $producer,
'productsCount' => $productsCount,

+ 479
- 481
backend/controllers/UserController.php
Diferenças do arquivo suprimidas por serem muito extensas
Ver arquivo


+ 2
- 1
backend/models/CreditForm.php Ver arquivo

@@ -73,7 +73,8 @@ class CreditForm extends Model
[['id_user', 'id_user_action', 'id_producer'], 'integer'],
[['date','send_mail'], 'safe'],
[['amount'], 'double'],
[['type', 'mean_payment', 'comment'], 'string', 'max' => 255],
[['type', 'mean_payment'], 'string', 'max' => 255],
[['comment'], 'string', 'max' => 2048],
];
}


+ 194
- 0
backend/views/development/development.php Ver arquivo

@@ -0,0 +1,194 @@
<?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.
*/

use yii\helpers\Html;
use yii\grid\GridView;
use common\models\Development;
use common\models\DevelopmentPriority;
use common\models\User;
use common\helpers\Url;
use common\helpers\GlobalParam;

$this->setTitle('Développement');
$this->addButton(['label' => 'Nouveau développement <span class="glyphicon glyphicon-plus"></span>', 'url' => ['development/create'], 'class' => 'btn btn-primary']);
$this->addBreadcrumb($this->getTitle());

?>

<div class="development-index">
<div class="well">
Cette page liste les besoins recencés auprès des producteurs utilisant la plateforme. N'hésitez pas à me <a
href="<?= Yii::$app->urlManagerFrontend->createAbsoluteUrl(['site/contact']); ?>">contacter</a> pour la
faire évoluer. Les remontées de bugs sont également bienvenues.<br/>
Afin d'orienter de manière pertinente le développement de la plateforme, je vous invite à définir la priorité
des évolutions qui vous intéressent.
</div>

<ul id="tab-status-developments" class="nav nav-tabs" role="tablist">
<li role="presentation" class="<?php if ($status == Development::STATUS_OPEN): ?>active<?php endif; ?>"><a
href="<?= Yii::$app->urlManager->createUrl(['development/index', 'status' => Development::STATUS_OPEN]); ?>"
id="" aria-controls="" role="tab">Ouvert</a></li>
<li role="presentation" class="<?php if ($status == Development::STATUS_CLOSED): ?>active<?php endif; ?>"><a
href="<?= Yii::$app->urlManager->createUrl(['development/index', 'status' => Development::STATUS_CLOSED]); ?>"
id="" aria-controls="" role="tab">Fermé</a></li>
</ul>

<?php

$columns = [
[
'header' => '#',
'value' => function ($model) {
return '#' . $model->id;
}
],
[
'attribute' => 'type',
'header' => 'Type',
'format' => 'raw',
'value' => function ($model) {
if ($model->type == Development::TYPE_EVOLUTION) {
return '<span class="label label-success">Évolution</span>';
} else {
return '<span class="label label-danger">Anomalie</span>';
}
}
],
['attribute' => 'sujet',
'format' => 'raw',
'value' => function ($model) {
$html = '<strong>' . Html::encode($model->subject) . '</strong>';
if (strlen($model->description))
$html .= '<br />' . nl2br(Html::encode($model->description));
return $html;
}],
['attribute' => 'estimation_temps',
'header' => 'Estimation',
'format' => 'raw',
'value' => function ($model) {
return intval($model->time_estimate) . ' h';
}],
['attribute' => 'avancement',
'format' => 'raw',
'value' => function ($model) {
if ($model->progress)
return '<div class="progress">
<div class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="' . intval($model->progress) . '" aria-valuemin="0" aria-valuemax="100" style="width: ' . intval($model->progress) . '%;">
<span class="sr-only">' . intval($model->progress) . '% effectué</span>
</div>
</div> ';
else
return '';
}],
['attribute' => 'date_delivery',
'format' => 'raw',
'value' => function ($model) {
if (strlen($model->date_delivery))
return date('d/m/Y', strtotime($model->date_delivery));
else
return '';
}],
];


if (User::hasAccessBackend()) {

$columns[] = [
'header' => 'Priorité',
'format' => 'raw',
'value' => function ($model) {

$currentPriority = (isset($model->developmentPrioritYCurrentProducer)) ? $model->developmentPriorityCurrentProducer->getStrPriority() : 'Non';
$styleButton = (isset($model->developmentPriorityCurrentProducer)) ? $model->developmentPriorityCurrentProducer->getClassCssStyleButton() : 'default';

$html = '<div class="btn-group btn-group-priorite">
<button type="button" class="btn btn-priorite btn-sm btn-' . $styleButton . ' dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
' . $currentPriority . ' <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="' . Yii::$app->urlManager->createUrl(['development/priority', 'idDevelopment' => $model->id]) . '">Non</a></li>
<li><a href="' . Yii::$app->urlManager->createUrl(['development/priority', 'idDevelopment' => $model->id, 'priority' => DevelopmentPriority::PRIORITY_LOW]) . '">Basse</a></li>
<li><a href="' . Yii::$app->urlManager->createUrl(['development/priority', 'idDevelopment' => $model->id, 'priority' => DevelopmentPriority::PRIORITY_NORMAL]) . '">Normale</a></li>
<li><a href="' . Yii::$app->urlManager->createUrl(['development/priority', 'idDevelopment' => $model->id, 'priority' => DevelopmentPriority::PRIORITY_HIGH]) . '">Haute</a></li>
</ul>
</div><br />';

if (isset($model->developmentPriority) && count($model->developmentPriority)) {
foreach ($model->developmentPriority as $developmentPriority) {
if ($developmentPriority->id_producer != GlobalParam::getCurrentProducerId())
$html .= '<div class="label label-priorite label-sm label-' . $developmentPriority->getClassCssStyleButton() . '">' . Html::encode($developmentPriority->producer->name) . '</div> ';
}
}

return $html;
}
];
}

if (User::getCurrentStatus() == USER::STATUS_ADMIN) {
$columns[] = [
'class' => 'yii\grid\ActionColumn',
'template' => '{update}',
'headerOptions' => ['class' => 'actions'],
'buttons' => [
'update' => function ($url, $model) {
return '<div class="btn-group">
<a href="' . $url . '" class="btn btn-default"><span class="glyphicon glyphicon-pencil"></span> Modifier</a>
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li><a href="' . Yii::$app->urlManager->createUrl(['development/delete', 'id' => $model->id]) . '" class=""><span class="glyphicon glyphicon-trash"></span> Supprimer</a></li>
</ul>
</div>';
},
],
];
}

?>

<?=
GridView::widget([
'id' => 'tab-developments',
'dataProvider' => $dataProvider,
'columns' => $columns
]);
?>
</div>

+ 16
- 139
backend/views/development/index.php Ver arquivo

@@ -45,150 +45,27 @@ use common\helpers\Url;
use common\helpers\GlobalParam;

$this->setTitle('Développement');
$this->addButton(['label' => 'Nouveau développement <span class="glyphicon glyphicon-plus"></span>', 'url' => ['development/create'], 'class' => 'btn btn-primary']);
$this->addBreadcrumb($this->getTitle());

?>

<div class="development-index">
<div class="well">
Cette page liste les besoins recencés auprès des producteurs utilisant la plateforme. N'hésitez pas à me <a
href="<?= Yii::$app->urlManagerFrontend->createAbsoluteUrl(['site/contact']); ?>">contacter</a> pour la
faire évoluer. Les remontées de bugs sont également bienvenues.<br/>
Afin d'orienter de manière pertinente le développement de la plateforme, je vous invite à définir la priorité
des évolutions qui vous intéressent.
<div class="alert alert-info">
Cette page liste les dernières versions du logiciel Opendistrib. Me
<a href="mailto:contact@opendistrib.net">contacter</a> pour soumettre
vos suggestions d'évolutions ou remontées de bug.
</div>

<ul id="tab-status-developments" class="nav nav-tabs" role="tablist">
<li role="presentation" class="<?php if ($status == Development::STATUS_OPEN): ?>active<?php endif; ?>"><a
href="<?= Yii::$app->urlManager->createUrl(['development/index', 'status' => Development::STATUS_OPEN]); ?>"
id="" aria-controls="" role="tab">Ouvert</a></li>
<li role="presentation" class="<?php if ($status == Development::STATUS_CLOSED): ?>active<?php endif; ?>"><a
href="<?= Yii::$app->urlManager->createUrl(['development/index', 'status' => Development::STATUS_CLOSED]); ?>"
id="" aria-controls="" role="tab">Fermé</a></li>
</ul>

<?php

$columns = [
[
'header' => '#',
'value' => function ($model) {
return '#' . $model->id;
}
],
[
'attribute' => 'type',
'header' => 'Type',
'format' => 'raw',
'value' => function ($model) {
if ($model->type == Development::TYPE_EVOLUTION) {
return '<span class="label label-success">Évolution</span>';
} else {
return '<span class="label label-danger">Anomalie</span>';
}
}
],
['attribute' => 'sujet',
'format' => 'raw',
'value' => function ($model) {
$html = '<strong>' . Html::encode($model->subject) . '</strong>';
if (strlen($model->description))
$html .= '<br />' . nl2br(Html::encode($model->description));
return $html;
}],
['attribute' => 'estimation_temps',
'header' => 'Estimation',
'format' => 'raw',
'value' => function ($model) {
return intval($model->time_estimate) . ' h';
}],
['attribute' => 'avancement',
'format' => 'raw',
'value' => function ($model) {
if ($model->progress)
return '<div class="progress">
<div class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="' . intval($model->progress) . '" aria-valuemin="0" aria-valuemax="100" style="width: ' . intval($model->progress) . '%;">
<span class="sr-only">' . intval($model->progress) . '% effectué</span>
</div>
</div> ';
else
return '';
}],
['attribute' => 'date_delivery',
'format' => 'raw',
'value' => function ($model) {
if (strlen($model->date_delivery))
return date('d/m/Y', strtotime($model->date_delivery));
else
return '';
}],
];


if (User::hasAccessBackend()) {

$columns[] = [
'header' => 'Priorité',
'format' => 'raw',
'value' => function ($model) {

$currentPriority = (isset($model->developmentPrioritYCurrentProducer)) ? $model->developmentPriorityCurrentProducer->getStrPriority() : 'Non';
$styleButton = (isset($model->developmentPriorityCurrentProducer)) ? $model->developmentPriorityCurrentProducer->getClassCssStyleButton() : 'default';

$html = '<div class="btn-group btn-group-priorite">
<button type="button" class="btn btn-priorite btn-sm btn-' . $styleButton . ' dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
' . $currentPriority . ' <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="' . Yii::$app->urlManager->createUrl(['development/priority', 'idDevelopment' => $model->id]) . '">Non</a></li>
<li><a href="' . Yii::$app->urlManager->createUrl(['development/priority', 'idDevelopment' => $model->id, 'priority' => DevelopmentPriority::PRIORITY_LOW]) . '">Basse</a></li>
<li><a href="' . Yii::$app->urlManager->createUrl(['development/priority', 'idDevelopment' => $model->id, 'priority' => DevelopmentPriority::PRIORITY_NORMAL]) . '">Normale</a></li>
<li><a href="' . Yii::$app->urlManager->createUrl(['development/priority', 'idDevelopment' => $model->id, 'priority' => DevelopmentPriority::PRIORITY_HIGH]) . '">Haute</a></li>
</ul>
</div><br />';

if (isset($model->developmentPriority) && count($model->developmentPriority)) {
foreach ($model->developmentPriority as $developmentPriority) {
if ($developmentPriority->id_producer != GlobalParam::getCurrentProducerId())
$html .= '<div class="label label-priorite label-sm label-' . $developmentPriority->getClassCssStyleButton() . '">' . Html::encode($developmentPriority->producer->name) . '</div> ';
}
}

return $html;
}
];
}

if (User::getCurrentStatus() == USER::STATUS_ADMIN) {
$columns[] = [
'class' => 'yii\grid\ActionColumn',
'template' => '{update}',
'headerOptions' => ['class' => 'actions'],
'buttons' => [
'update' => function ($url, $model) {
return '<div class="btn-group">
<a href="' . $url . '" class="btn btn-default"><span class="glyphicon glyphicon-pencil"></span> Modifier</a>
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li><a href="' . Yii::$app->urlManager->createUrl(['development/delete', 'id' => $model->id]) . '" class=""><span class="glyphicon glyphicon-trash"></span> Supprimer</a></li>
</ul>
</div>';
},
],
];
}

?>

<?=
GridView::widget([
'id' => 'tab-developments',
'dataProvider' => $dataProvider,
'columns' => $columns
]);
?>
<?php foreach($versionsArray as $version): ?>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
<?= $version['version'] ?>
</h3>
</div>
<div class="panel-body">
<?= $version['content'] ?>
</div>
</div>
<?php endforeach; ?>
</div>

+ 51
- 11
backend/views/distribution/index.php Ver arquivo

@@ -43,6 +43,20 @@ $this->setTitle('Distributions') ;
$this->setPageTitle('Distributions') ;

?>

<?php if($orderUpdate):
$orderUpdateDistributionTimestamp = strtotime($orderUpdate->distribution->date);
?>
<script>
window.vueValues = {
date: new Date(<?= date('Y', $orderUpdateDistributionTimestamp) ?>, <?= date('m', $orderUpdateDistributionTimestamp) - 1 ?>, <?= date('d', $orderUpdateDistributionTimestamp) ?>),
dateFormat: '<?= date('d/m/Y', $orderUpdateDistributionTimestamp) ?>',
showModalFormOrderUpdate: true,
idOrderUpdate: <?= $orderUpdate->id ?>
};
</script>
<?php endif; ?>

<div id="app-distribution-index" class="app-vuejs">
<?php if(strlen($date)): ?>
<span id="distribution-date"><?= $date; ?></span>
@@ -225,6 +239,7 @@ $this->setPageTitle('Distributions') ;
:date="date"
:order="orderCreate"
:points-sale="pointsSale"
:id-active-point-sale="idActivePointSale"
:means-payment="meansPayment"
:users="users"
:products="products"
@@ -287,6 +302,11 @@ $this->setPageTitle('Distributions') ;
Attention, ce jour de distribution n'est pas activé et vous avez quand même des commandes enregistrées.
</div>

<div v-if="idActivePointSale > 0 && (totalActivePointSale() > 0 || weightActivePointSale() > 0)" class="point-sale-totals">
<span class="title">Totaux</span>
CA TTC : <strong>{{ totalActivePointSale() }} €</strong> / Poids : <strong>{{ weightActivePointSale() }} kg</strong>
</div>

<table class="table table-condensed table-bordered table-hover" v-if="countOrdersByPointSale[idActivePointSale] > 0 || (idActivePointSale == 0 && orders.length > 0)">
<thead>
<tr>
@@ -397,8 +417,9 @@ $this->setPageTitle('Distributions') ;
<order-form
v-if="showModalFormOrderUpdate && idOrderUpdate == order.id"
:date="date"
:id-point-sale="idActivePointSale"
:date-format="dateFormat"
:points-sale="pointsSale"
:id-active-point-sale="idActivePointSale"
:means-payment="meansPayment"
:users="users"
:products="products"
@@ -486,6 +507,7 @@ $this->setPageTitle('Distributions') ;
<td>Date</td>
<td>Utilisateur</td>
<td>Action</td>
<td>Origine action</td>
<td>- Débit</td>
<td>+ Crédit</td>
</tr>
@@ -493,8 +515,9 @@ $this->setPageTitle('Distributions') ;
<tbody>
<tr v-for="creditHistory in order.creditHistory">
<td>{{ creditHistory.date }}</td>
<td>{{ creditHistory.user_action }}</td>
<td>{{ creditHistory.user }}</td>
<td v-html="creditHistory.wording"></td>
<td>{{ creditHistory.user_action }}</td>
<td v-html="creditHistory.debit"></td>
<td v-html="creditHistory.credit"></td>
</tr>
@@ -532,11 +555,11 @@ $this->setPageTitle('Distributions') ;
</td>
</tr>
</template>
<tr v-if="idActivePointSale > 0">
<!--<tr v-if="idActivePointSale > 0">
<td colspan="4"><strong>Total (TTC)</strong></td>
<td><strong>{{ totalActivePointSale() }}</strong></td>
<td colspan="3"></td>
</tr>
</tr>-->
</tbody>
</table>
<div class="alert alert-warning" v-else>
@@ -550,7 +573,11 @@ $this->setPageTitle('Distributions') ;
<!-- template for the order-form component -->
<script type="text/x-template" id="order-form-template">
<modal class="modal-form-order" @close="$emit('close')">
<h3 slot="header">Ajouter une commande</h3>
<h3 v-if="order.id" slot="header">
<template v-if="order.id">Modifier</template>
<template v-else>Ajouter</template>
une commande - {{ dateFormat }}
</h3>
<div slot="body">
<div class="callout callout-warning" v-if="errors.length">
<ul>
@@ -608,15 +635,19 @@ $this->setPageTitle('Distributions') ;
</tr>
</thead>
<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].quantity > 0) ? 'product-ordered' : ''">
<td>
<span class="label label-success" v-if="loadingUpdateProductOrder || order.productOrder[product.id].active">Actif</span>
<span class="label label-danger" v-else>Inactif</span>
</td>
<td>{{ product.name }}</td>
<td class="price">
<div v-show="vatMode == 'all'" class="input-group">
<input type="text" v-model="order.productOrder[product.id].price" class="form-control input-sm" @change="productPriceChange" :data-with-tax="false" :data-id-product="product.id" />
<span class="input-group-addon" id="basic-addon2">€ HT&nbsp;&nbsp;</span>
</div>
<div class="input-group">
<input type="text" v-model="order.productOrder[product.id].price" class="form-control" @change="productPriceChange" :data-id-product="product.id" />
<input type="text" v-model="order.productOrder[product.id].price_with_tax" class="form-control input-sm" @change="productPriceChange" :data-with-tax="true" :data-id-product="product.id" />
<span class="input-group-addon" id="basic-addon2">€ TTC</span>
</div>
</td>
@@ -625,7 +656,7 @@ $this->setPageTitle('Distributions') ;
<span class="input-group-btn">
<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>
<input type="text" v-model="order.productOrder[product.id].quantity" class="form-control" />
<input type="text" v-model="order.productOrder[product.id].quantity" class="form-control input-quantity" />
<span class="input-group-addon">{{ order.productOrder[product.id].unit == 'piece' ? 'p.' : order.productOrder[product.id].unit }}</span>
<span class="input-group-btn">
<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>
@@ -633,8 +664,8 @@ $this->setPageTitle('Distributions') ;
</div>
</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>
<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>
</tbody>
</table>
@@ -648,7 +679,16 @@ $this->setPageTitle('Distributions') ;
<button class="modal-default-button btn btn-primary" @click="submitFormUpdate" v-if="order.id">Modifier</button>
<button class="modal-default-button btn btn-primary" @click="submitFormCreate" v-else>Créer</button>

<button class="modal-default-button btn btn-default" @click="$emit('close')">Annuler</button>
<button class="modal-default-button btn btn-danger" @click="$emit('close')">Annuler</button>

<div class="right">
<button class="modal-default-button btn btn-info" @click="updateProductOrderPrices(true)">Recharger les prix</button>
<button class="modal-default-button btn btn-info" @click="toggleVatMode()">
<template v-if="vatMode == 'all'">Cacher</template>
<template v-else>Afficher</template>
les prix HT
</button>
</div>
</div>
</div>
</modal>

+ 2
- 2
backend/views/document/_download_product_line.php Ver arquivo

@@ -20,7 +20,7 @@

<?php if($displayPrices): ?>
<td class="align-center">
<?= Price::format($price) ?>
<?= Price::format($price) ?>
</td>
<?php endif; ?>
<td class="align-center">
@@ -28,7 +28,7 @@
</td>
<td class="align-center"><?= Product::strUnit($productOrder->unit, 'wording') ?></td>
<?php if($displayPrices): ?>
<?php if(GlobalParam::getCurrentProducer()->taxRate->value != 0): ?>
<?php if($producer->taxRate->value != 0): ?>
<td class="align-center"><?= $productOrder->taxRate->value * 100 ?> %</td>
<?php endif; ?>
<td class="align-center">

+ 252
- 241
backend/views/document/_form.php Ver arquivo

@@ -50,272 +50,283 @@ use common\models\Producer;
<div class="document-form" id="app-document-form" data-class-document="<?= $model->getClass() ?>"
data-id-document="<?= ($model->id > 0) ? $model->id : $model->id ?>">

<div class="<?= ($action == 'update') ? 'col-md-6' : '' ?>">
<div class="panel panel-default">
<div class="panel-heading">
Général
</div>
<div class="panel-body">
<?php $form = ActiveForm::begin(); ?>
<?= Html::hiddenInput('classDocument',$model->getClass(), ['id' => 'class-document']) ?>
<?= Html::hiddenInput('typeAction',$action, ['id' => 'type-action']) ?>
<?php if ($action == 'update'): ?>
<?= Html::hiddenInput('idDocument',$model->id, ['id' => 'id-document']) ?>
<?php endif; ?>
<?= $form->field($model, 'name')->label('Nom du document') ?>
<div class="<?= ($action == 'update') ? 'col-md-6' : '' ?>">
<div class="panel panel-default">
<div class="panel-heading">
Général
</div>
<div class="panel-body">
<?php $form = ActiveForm::begin(); ?>
<?= Html::hiddenInput('classDocument', $model->getClass(), ['id' => 'class-document']) ?>
<?= Html::hiddenInput('typeAction', $action, ['id' => 'type-action']) ?>
<?php if ($action == 'update'): ?>
<?= Html::hiddenInput('idDocument', $model->id, ['id' => 'id-document']) ?>
<?php endif; ?>
<?= $form->field($model, 'name')->label('Nom du document') ?>

<?php if($action == 'update'): ?>
<?= $form->field($model, 'id_user', [
'template' => '{label} <div>{input}</div>'.$model->user->getUsername(),
])->hiddenInput(); ?>
<?php else: ?>
<?php
$usersArray = User::findBy()->orderBy('type DESC')->all();
?>
<?= $form->field($model, 'id_user', [
'template' => '{label} <a href="' . Yii::$app->urlManager->createUrl(['user/create']) . '" class="btn btn-xs btn-default">Nouvel utilisateur <span class="glyphicon glyphicon-plus"></span></a><div>{input}</div>{hint}',
])
->dropDownList(
ArrayHelper::map($usersArray, 'user_id', function ($model) {
if(isset($model['name_legal_person']) && strlen($model['name_legal_person'])) {
return 'Personne morale / '.$model['name_legal_person'] ;
}
else {
return $model['lastname'] . ' ' . $model['name'];
}
}),
[
'@change' => 'changeUser',
'prompt' => '--',
'v-model' => 'idUser',
]
); ?>
<?php endif; ?>
<?php if ($action == 'update'): ?>
<?= $form->field($model, 'id_user', [
'template' => '{label} <div>{input}</div>' . $model->user->getUsername(),
])->hiddenInput(); ?>
<?php else: ?>
<?php
$usersArray = User::findBy()->orderBy('type DESC, name_legal_person ASC, lastname ASC, name ASC')->all();
?>
<?= $form->field($model, 'id_user', [
'template' => '{label} <a href="' . Yii::$app->urlManager->createUrl(['user/create']) . '" class="btn btn-xs btn-default">Nouvel utilisateur <span class="glyphicon glyphicon-plus"></span></a><div>{input}</div>{hint}',
])
->dropDownList(
ArrayHelper::map($usersArray, 'user_id', function ($model) {
if (isset($model['name_legal_person']) && strlen($model['name_legal_person'])) {
return 'Personne morale / ' . $model['name_legal_person'];
} else {
return $model['lastname'] . ' ' . $model['name'];
}
}),
[
'@change' => 'changeUser',
'prompt' => '--',
'v-model' => 'idUser',
]
); ?>
<?php endif; ?>


<?= $form->field($model, 'address')->textarea(['rows' => 2, 'v-model' => 'document.address']) ?>
<?php if ($action == 'update'): ?>
<?= $form->field($model, 'comment')->textarea(['rows' => 2])->hint('Affiché en bas de la facture') ?>
<?php endif; ?>
<?= $form->field($model, 'address')->textarea(['rows' => 2, 'v-model' => 'document.address']) ?>
<?php if ($action == 'update'): ?>
<?= $form->field($model, 'comment')->textarea(['rows' => 2])->hint('Affiché en bas de la facture') ?>
<?php endif; ?>

<?php if($model->getClass() == 'Invoice'): ?>
<template v-if="idUser > 0">
<strong>Bons de livraison</strong>
<table v-if="deliveryNotes && deliveryNotes.length > 0" class="table table-bordered">
<thead>
<tr>
<?php if($action == 'create'): ?><th></th><?php endif; ?>
<th>Libellé</th>
<th v-if="taxRateProducer != 0">Montant (TTC)</th>
<th v-else>Montant</th>
</tr>
</thead>
<tbody>
<tr v-for="deliveryNote in deliveryNotes">
<?php if($action == 'create'): ?>
<td><input type="checkbox" name="Invoice[deliveryNotes][]" :value="deliveryNote.id" /></td>
<?php endif; ?>
<td>{{ deliveryNote.name }}</td>
<td>{{ formatPrice(deliveryNote.total) }}</td>
</tr>
</tbody>
</table>
<div v-else class="alert alert-warning">Aucun bon de livraison pour cet utilisateur.</div>
</template>
<?php if ($model->getClass() == 'Invoice'): ?>
<template v-if="idUser > 0">
<strong>Bons de livraison</strong>
<table v-if="deliveryNotes && deliveryNotes.length > 0" class="table table-bordered">
<thead>
<tr>
<?php if ($action == 'create'): ?>
<th></th><?php endif; ?>
<th>Libellé</th>
<th v-if="taxRateProducer != 0">Montant (TTC)</th>
<th v-else>Montant</th>
</tr>
</thead>
<tbody>
<tr v-for="deliveryNote in deliveryNotes">
<?php if ($action == 'create'): ?>
<td><input type="checkbox" name="Invoice[deliveryNotes][]"
:value="deliveryNote.id"/></td>
<?php endif; ?>
<td>{{ deliveryNote.name }}</td>
<td>{{ formatPrice(deliveryNote.total) }}</td>
</tr>
</tbody>
</table>
<div v-else class="alert alert-warning">Aucun bon de livraison pour cet utilisateur.</div>
</template>
<?php endif; ?>

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

<?php if ($action == 'update'): ?>
<?php if ($action == 'update'): ?>

<div class="col-md-6">
<div id="" class="info-box">
<span class="info-box-icon bg-green"><i class="fa fa-sticky-note-o"></i></span>
<div class="info-box-content">
<span class="info-box-text"><?= $typeDocument ?> <span v-html="document.html_label"></span></span>
<span class="info-box-number">{{ document.reference }}</span>
<span class="info-box-text">Date</span>
<span class="info-box-number">{{ document.date }}</span>
</div>
</div>
<div id="" class="info-box">
<span class="info-box-icon bg-yellow"><i class="fa fa-euro"></i></span>
<div class="info-box-content">
<span class="info-box-text">Total<span v-if="taxRateProducer != 0"> (TTC)</span></span>
<span class="info-box-number">{{ formatPrice(total_with_tax) }}</span>
</div>
</div>
<div id="" class="info-box">
<span class="info-box-icon bg-blue"><i class="fa fa-download"></i></span>
<div class="info-box-content">
<a href="<?= Yii::$app->urlManager->createUrl([Yii::$app->controller->getControllerUrl().'/download', 'id' => $model->id]) ?>" class="btn btn-default"><span class="glyphicon glyphicon-download-alt"></span> Télécharger (PDF)</a>
<?php if($model->getClass() == 'Invoice' && Producer::getConfig('option_export_evoliz')): ?>
<a href="<?= Yii::$app->urlManager->createUrl([Yii::$app->controller->getControllerUrl().'/export-csv-evoliz', 'id' => $model->id]) ?>" class="btn btn-default"><span class="glyphicon glyphicon-save-file"></span> Export Evoliz (CSV)</a>
<?php endif; ?>
</div>
</div>
<div v-if="document.status == 'draft'" id="" class="info-box">
<span class="info-box-icon bg-red"><i class="fa fa-flash"></i></span>
<div class="info-box-content">
<form action="<?= Yii::$app->urlManager->createUrl([Yii::$app->controller->getControllerUrl().'/validate']) ?>">
<?= Html::hiddenInput('id', $model->id); ?>
<button class="btn btn-default"><span class="glyphicon glyphicon-ok"></span> Valider le document</button>
</form>
<?php if(isset($model->user) && strlen($model->user->email) > 0): ?>
<a href="<?= Yii::$app->urlManager->createUrl([Yii::$app->controller->getControllerUrl().'/send', 'id' => $model->id]) ?>" class="btn btn-default"><span class="glyphicon glyphicon-send"></span> Envoyer le document</a>
<?php endif; ?>
</div>
</div>
<div class="col-md-6">
<div id="" class="info-box">
<span class="info-box-icon bg-green"><i class="fa fa-sticky-note-o"></i></span>
<div class="info-box-content">
<span class="info-box-text"><?= $typeDocument ?> <span v-html="document.html_label"></span></span>
<span class="info-box-number">{{ document.reference }}</span>
<span class="info-box-text">Date</span>
<span class="info-box-number">{{ document.date }}</span>
</div>
</div>
<div id="" class="info-box">
<span class="info-box-icon bg-yellow"><i class="fa fa-euro"></i></span>
<div class="info-box-content">
<span class="info-box-text">Total<span v-if="taxRateProducer != 0"> (TTC)</span></span>
<span class="info-box-number">{{ formatPrice(total_with_tax) }}</span>
</div>
</div>
<div id="" class="info-box">
<span class="info-box-icon bg-blue"><i class="fa fa-download"></i></span>
<div class="info-box-content">
<a href="<?= Yii::$app->urlManager->createUrl([Yii::$app->controller->getControllerUrl() . '/download', 'id' => $model->id]) ?>"
class="btn btn-default"><span class="glyphicon glyphicon-download-alt"></span> Télécharger (PDF)</a>
<?php if ($model->getClass() == 'Invoice' && Producer::getConfig('option_export_evoliz')): ?>
<a href="<?= Yii::$app->urlManager->createUrl([Yii::$app->controller->getControllerUrl() . '/export-csv-evoliz', 'id' => $model->id]) ?>"
class="btn btn-default"><span class="glyphicon glyphicon-save-file"></span> Export Evoliz
(CSV)</a>
<?php endif; ?>
</div>
<div class="clr"></div>
</div>
<div v-if="document.status == 'draft'" id="" class="info-box">
<span class="info-box-icon bg-red"><i class="fa fa-flash"></i></span>
<div class="info-box-content">
<form action="<?= Yii::$app->urlManager->createUrl([Yii::$app->controller->getControllerUrl() . '/validate']) ?>">
<?= Html::hiddenInput('id', $model->id); ?>
<button class="btn btn-default"><span class="glyphicon glyphicon-ok"></span> Valider le document
</button>
</form>
<?php if (isset($model->user) && strlen($model->user->email) > 0): ?>
<a href="<?= Yii::$app->urlManager->createUrl([Yii::$app->controller->getControllerUrl() . '/send', 'id' => $model->id]) ?>"
class="btn btn-default"><span class="glyphicon glyphicon-send"></span> Envoyer le
document</a>
<?php endif; ?>
</div>
</div>
</div>
<div class="clr"></div>

<div class="">
<div class="panel panel-default" id="block-add-product">
<div class="panel-heading">
Ajouter un produit
</div>
<div class="panel-body">
<div class="col-md-6">
<strong>Produit</strong>
<select class="form-control" v-model="productAddId"
@change="changeProductAdd">
<option value="0" selected="selected">--</option>
<option v-for="product in productsArray" :value="product.id">
{{ product.name }}
</option>
</select>
</div>
<template v-if="productAddId > 0">
<div class="col-md-3">
<strong>Prix unitaire</strong>
<div class="input-group">
<input type="text" class="form-control input-price"
v-model="productAddPrice" @change="formatProductAddPrice"/>
<span class="input-group-addon"><span
class="glyphicon glyphicon-euro"></span> <span v-if="taxRateProducer != 0">HT</span></span>
</div>
</div>
<div class="col-md-3 total">
<strong>Quantité</strong>
<div class="input-group input-group-quantity">
<div class="">
<div class="panel panel-default" id="block-add-product">
<div class="panel-heading">
Ajouter un produit
</div>
<div class="panel-body">
<div class="col-md-6">
<strong>Produit</strong>
<select class="form-control" v-model="productAddId"
@change="changeProductAdd">
<option value="0" selected="selected">--</option>
<option v-for="product in productsArray" :value="product.id">
{{ product.name }}
</option>
</select>
</div>
<template v-if="productAddId > 0">
<div class="col-md-3">
<strong>Prix unitaire</strong>
<div class="input-group">
<input type="text" class="form-control input-price"
v-model="productAddPrice" @change="formatProductAddPrice"/>
<span class="input-group-addon"><span
class="glyphicon glyphicon-euro"></span> <span v-if="taxRateProducer != 0">HT</span></span>
</div>
</div>
<div class="col-md-3 total">
<strong>Quantité</strong>
<div class="input-group input-group-quantity">
<span class="input-group-btn">
<button class="btn btn-default" type="button"
@click="changeQuantityProductAdd(-1)">-</button>
</span>
<input type="text" class="form-control input-quantity"
v-model="productAddQuantity" @change="formatProductAddQuantity"/>
<span class="input-group-addon">{{ getProductById(productAddId).wording_unit }}</span>
<span class="input-group-btn">
<input type="text" class="form-control input-quantity"
v-model="productAddQuantity" @change="formatProductAddQuantity"/>
<span class="input-group-addon">{{ getProductById(productAddId).wording_unit }}</span>
<span class="input-group-btn">
<button class="btn btn-default"
type="button"
@click="changeQuantityProductAdd(1)">+</button>
</span>
</div>
<button class="btn btn-primary" value="Ajouter"
@click="submitProductAdd">Ajouter
</button>
<div class="clr"></div>
</div>
<!--<div class="col-md-3 total">
<strong>Total</strong>
<div class="input-group">
<input type="text" class="form-control input-price" readonly
:value="formatPrice(productAddPrice * productAddQuantity)"/>
<span class="input-group-addon"><span
class="glyphicon glyphicon-euro"></span> <span v-if="taxRateProducer != 0">HT</span></span>
</div>
</div>-->
</template>
<div class="clr"></div>
</div>
</div>
<button class="btn btn-primary" value="Ajouter"
@click="submitProductAdd">Ajouter
</button>
<div class="clr"></div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
Produits
<!--<div class="col-md-3 total">
<strong>Total</strong>
<div class="input-group">
<input type="text" class="form-control input-price" readonly
:value="formatPrice(productAddPrice * productAddQuantity)"/>
<span class="input-group-addon"><span
class="glyphicon glyphicon-euro"></span> <span v-if="taxRateProducer != 0">HT</span></span>
</div>
<div class="panel-body">
<div id="block-list-products">
<table class="table table-bordered" v-if="total > 0">
<thead>
<tr>
<th>Nom</th>
<th>Prix (unité)</th>
<th>Quantité</th>
<th v-if="taxRateProducer != 0">TVA</th>
<th v-if="taxRateProducer != 0">Total HT</th>
<th v-else>Total</th>
<th>Supprimer</th>
</tr>
</thead>
<tbody>
<template v-for="order in ordersArray">
<tr v-for="productOrder in order.productOrder">
<td class="col-md-4">
<div class="product-name">{{ getProductById(productOrder.id_product).name }}</div>
<ul class="product-order-meta">
<li>{{ order.username }}</li>
<li v-if="order.distribution_date">{{ order.distribution_date }}</li>
<li v-if="order.point_sale_name">{{ order.point_sale_name }}</li>
</ul>
</td>
<td class="col-md-2">
{{ formatPrice(getProductOrderPrice(productOrder)) }}
</td>
<td class="col-md-2">{{ productOrder.quantity }}</td>
<td class="col-md-1" v-if="taxRateProducer != 0">
{{ getProductById(productOrder.id_product).tax_rate * 100 }} %
</td>
<td class="col-md-2">
{{ formatPrice(productOrder.quantity * getProductOrderPrice(productOrder)) }}
</td>
<td class="col-md-1">
<a class="btn btn-default" @click="deleteProductOrder(productOrder.id)">
<span class="glyphicon glyphicon-trash"></span>
</a>
</td>
</tr>
</template>
<template v-if="taxRateProducer != 0">
<tr>
<td colspan="4"><strong>Total HT</strong></td>
<td><strong>{{ formatPrice(total) }} HT</strong></td>
<td></td>
</tr>
<tr>
<td colspan="4"><strong>Montant TVA</strong></td>
<td><strong>{{ formatPrice(total_with_tax - total) }}</strong></td>
<td></td>
</tr>
<tr>
<td colspan="4"><strong>Total TTC</strong></td>
<td><strong>{{ formatPrice(total_with_tax) }} TTC</strong></td>
<td></td>
</tr>
</template>
<template v-else>
<tr>
<td colspan="3"><strong>Total</strong></td>
<td><strong>{{ formatPrice(total) }}</strong></td>
<td></td>
</tr>
</template>

</tbody>
</table>
<div v-else class="alert alert-info">
Aucun produit.
</div>
</div>-->
</template>
<div class="clr"></div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
Produits
</div>
<div class="panel-body">
<div id="block-list-products">
<table class="table table-bordered" v-if="total > 0">
<thead>
<tr>
<th>Nom</th>
<th>Prix (unité)</th>
<th>Quantité</th>
<th v-if="taxRateProducer != 0">TVA</th>
<th v-if="taxRateProducer != 0">Total HT</th>
<th v-else>Total</th>
<th>Supprimer</th>
</tr>
</thead>
<tbody>
<template v-for="order in ordersArray">
<tr v-for="productOrder in order.productOrder">
<td class="col-md-4">
<div class="product-name">{{ getProductById(productOrder.id_product).name }}
</div>
</div>
<ul class="product-order-meta">
<li>{{ order.username }}</li>
<li v-if="order.distribution_date">{{ order.distribution_date }}</li>
<li v-if="order.point_sale_name">{{ order.point_sale_name }}</li>
</ul>
</td>
<td class="col-md-2">
{{ formatPrice(getProductOrderPrice(productOrder)) }}
<template v-if="document.status == 'draft' && getProductOrderPrice(productOrder) != getBestProductPrice(productOrder.id_product, productOrder.quantity)">
<i class="fa fa-exclamation-triangle" title="Prix différent de celui défini au niveau du produit"></i>
</template>
</td>
<td class="col-md-2">{{ productOrder.quantity }}</td>
<td class="col-md-1" v-if="taxRateProducer != 0">
{{ getProductById(productOrder.id_product).tax_rate * 100 }} %
</td>
<td class="col-md-2">
{{ formatPrice(productOrder.quantity * getProductOrderPrice(productOrder)) }}
</td>
<td class="col-md-1">
<a class="btn btn-default" @click="deleteProductOrder(productOrder.id)">
<span class="glyphicon glyphicon-trash"></span>
</a>
</td>
</tr>
</template>
<template v-if="taxRateProducer != 0">
<tr>
<td colspan="4"><strong>Total HT</strong></td>
<td><strong>{{ formatPrice(total) }} HT</strong></td>
<td></td>
</tr>
<tr>
<td colspan="4"><strong>Montant TVA</strong></td>
<td><strong>{{ formatPrice(total_with_tax - total) }}</strong></td>
<td></td>
</tr>
<tr>
<td colspan="4"><strong>Total TTC</strong></td>
<td><strong>{{ formatPrice(total_with_tax) }} TTC</strong></td>
<td></td>
</tr>
</template>
<template v-else>
<tr>
<td colspan="3"><strong>Total</strong></td>
<td><strong>{{ formatPrice(total) }}</strong></td>
<td></td>
</tr>
</template>

</tbody>
</table>
<div v-else class="alert alert-info">
Aucun produit.
</div>
</div>
</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>


</div>

+ 156
- 152
backend/views/document/download.php Ver arquivo

@@ -1,182 +1,186 @@
<?php

$displayPrices = Yii::$app->controller->getClass() != 'DeliveryNote' || (Yii::$app->controller->getClass() == 'DeliveryNote' && Producer::getConfig('document_display_prices_delivery_note')) ;
$displayPrices = Yii::$app->controller->getClass() != 'DeliveryNote' || (Yii::$app->controller->getClass() == 'DeliveryNote' && Producer::getConfig('document_display_prices_delivery_note'));
$displayProductDescription = Producer::getConfig('document_display_product_description');

?>

<div class="document-download">

<div id="block-addresses">
<div class="producer">
<?php if(strlen($producer->logo)) : ?>
<div class="logo">
<img style="max-height: 80px;" src="<?= $producer->getUrlLogo() ?>" />
</div>
<?php endif; ?>
<div class="address"><?= $producer->getFullAddress(true) ; ?></div>
</div>
<div class="user">
<?php if($document->address && strlen($document->address) > 0): ?>
<?= nl2br($document->address) ?>
<?php else: ?>
<?= $document->user->getFullAddress(true) ; ?>
<?php endif; ?>
<div id="block-addresses">
<div class="producer">
<?php if (strlen($producer->logo)) : ?>
<div class="logo">
<img style="max-height: 80px;" src="<?= $producer->getUrlLogo() ?>"/>
</div>
<?php endif; ?>
<div class="address"><?= $producer->getFullAddress(true); ?></div>
</div>

<div id="block-infos-document">
<div class="date">
Le <?= strftime('%d %B %Y', strtotime($document->date)) ?>
</div>
<div class="reference">
<?php if(strlen($document->reference)) : ?>
<?= $document->getType(); ?> N°<?= $document->reference ; ?>
<?php else: ?>
<div class="block-is-draft"><?= $document->getType(); ?> non validé<?= ($document->getType() == 'Facture') ? 'e' : '' ?></div>
<?php endif; ?>
</div>
<div class="name">
<strong>Libellé : </strong><?= $document->name ; ?>
</div>
<div class="user">
<?php if ($document->address && strlen($document->address) > 0): ?>
<?= nl2br($document->address) ?>
<?php else: ?>
<?= $document->user->getFullAddress(true); ?>
<?php endif; ?>
</div>
</div>

<?php if(strlen($document->comment)): ?>
<div class="block-infos">
<strong>Commentaire</strong><br />
<?= Html::encode($document->comment) ?>
</div>
<?php endif; ?>

<div id="block-products">
<?php if(count($document->orders) > 0) : ?>
<table class="table table-bordered">
<thead>
<tr>
<th class="align-left">Produit</th>
<?php if($displayPrices): ?>
<?php if(GlobalParam::getCurrentProducer()->taxRate->value == 0): ?>
<th>Prix unitaire</th>
<?php else: ?>
<th>Prix unitaire HT</th>
<?php endif; ?>
<?php endif; ?>
<th>Quantité</th>
<th>Unité</th>
<?php if($displayPrices): ?>
<?php if(GlobalParam::getCurrentProducer()->taxRate->value == 0): ?>
<th>Prix</th>
<?php else: ?>
<th>TVA</th>
<th>Prix HT</th>
<?php endif; ?>
<?php endif; ?>
<div id="block-infos-document">
<div class="date">
Le <?= strftime('%d %B %Y', strtotime($document->date)) ?>
</div>
<div class="reference">
<?php if (strlen($document->reference)) : ?>
<?= $document->getType(); ?> N°<?= $document->reference; ?>
<?php else: ?>
<div class="block-is-draft"><?= $document->getType(); ?> non
validé<?= ($document->getType() == 'Facture') ? 'e' : '' ?></div>
<?php endif; ?>
</div>
<div class="name">
<strong>Libellé : </strong><?= $document->name; ?>
</div>
</div>

</tr>
</thead>
<tbody>
<?php if (strlen($document->comment)): ?>
<div class="block-infos">
<strong>Commentaire</strong><br/>
<?= Html::encode($document->comment) ?>
</div>
<?php endif; ?>

<?php if($document->isDisplayOrders()): ?>
<?php foreach($document->orders as $order): ?>
<tr>
<td>
<strong><?= Html::encode($order->getUsername()) ; ?></strong>
<?php if($order->distribution): ?>
le <?= date('d/m/Y', strtotime($order->distribution->date)) ?>
<?php endif; ?>
</td>
<?php if($displayPrices): ?>
<td class="align-center"></td>
<?php endif; ?>
<td></td>
<td></td>
<?php if($displayPrices): ?>
<?php if(GlobalParam::getCurrentProducer()->taxRate->value != 0): ?>
<td class="align-center"></td>
<?php endif; ?>
<td class="align-center"></td>
<?php endif; ?>
</tr>
<?php foreach($order->productOrder as $productOrder): ?>
<?= $this->render('_download_product_line', [
'document' => $document,
'productOrder' => $productOrder,
'displayOrders' => true,
'displayPrices' => $displayPrices,
'displayProductDescription' => $displayProductDescription
]) ?>
<?php endforeach; ?>
<?php endforeach; ?>
<div id="block-products">
<?php if (count($document->orders) > 0) : ?>
<table class="table table-bordered">
<thead>
<tr>
<th class="align-left">Produit</th>
<?php if ($displayPrices): ?>
<?php if ($producer->taxRate->value == 0): ?>
<th>Prix unitaire</th>
<?php else: ?>
<?php foreach($document->getProductsOrders() as $product): ?>
<?php foreach($product as $productOrder): ?>
<?= $this->render('_download_product_line', [
'document' => $document,
'productOrder' => $productOrder,
'displayPrices' => $displayPrices,
'displayProductDescription' => $displayProductDescription
]) ?>
<?php endforeach; ?>
<?php endforeach; ?>
<th>Prix unitaire HT</th>
<?php endif; ?>
<?php if($displayPrices): ?>
<?php $typeAmount = $document->isInvoicePrice() ? Order::INVOICE_AMOUNT_TOTAL : Order::AMOUNT_TOTAL ; ?>
<?php endif; ?>
<th>Quantité</th>
<th>Unité</th>
<?php if ($displayPrices): ?>
<?php if ($producer->taxRate->value == 0): ?>
<th>Prix</th>
<?php else: ?>
<th>TVA</th>
<th>Prix HT</th>
<?php endif; ?>
<?php endif; ?>

<?php if(GlobalParam::getCurrentProducer()->taxRate->value != 0): ?>
</tr>
</thead>
<tbody>

<tr>
<td class="align-right" colspan="5"><strong>Total HT</strong></td>
<td class="align-center">
<?= Price::format($document->getAmount($typeAmount)); ?>
</td>
</tr>
<?php if ($document->isDisplayOrders()): ?>
<?php foreach ($document->orders as $order): ?>
<tr>
<td>
<strong><?= Html::encode($order->getUsername()); ?></strong>
<?php if ($order->distribution): ?>
le <?= date('d/m/Y', strtotime($order->distribution->date)) ?>
<?php endif; ?>
</td>
<?php if ($displayPrices): ?>
<td class="align-center"></td>
<?php endif; ?>
<td></td>
<td></td>
<?php if ($displayPrices): ?>
<?php if ($producer->taxRate->value != 0): ?>
<td class="align-center"></td>
<?php endif; ?>
<td class="align-center"></td>
<?php endif; ?>
</tr>
<?php foreach ($order->productOrder as $productOrder): ?>
<?= $this->render('_download_product_line', [
'producer' => $producer,
'document' => $document,
'productOrder' => $productOrder,
'displayOrders' => true,
'displayPrices' => $displayPrices,
'displayProductDescription' => $displayProductDescription
]) ?>
<?php endforeach; ?>
<?php endforeach; ?>
<?php else: ?>
<?php foreach ($document->getProductsOrders() as $product): ?>
<?php foreach ($product as $productOrder): ?>
<?= $this->render('_download_product_line', [
'producer' => $producer,
'document' => $document,
'productOrder' => $productOrder,
'displayPrices' => $displayPrices,
'displayProductDescription' => $displayProductDescription
]) ?>
<?php endforeach; ?>
<?php endforeach; ?>
<?php endif; ?>
<?php if ($displayPrices): ?>
<?php $typeAmount = $document->isInvoicePrice() ? Order::INVOICE_AMOUNT_TOTAL : Order::AMOUNT_TOTAL; ?>

<?php
$taxRateArray = TaxRate::getTaxRateArray();
foreach($document->getTotalVatArray($typeAmount) as $idTaxRate => $totalVat): ?>
<tr>
<td class="align-right" colspan="5"><strong>TVA <?= $taxRateArray[$idTaxRate]->value * 100 ?> %</strong></td>
<td class="align-center">
<?= Price::format($totalVat); ?>
</td>
</tr>
<?php endforeach; ?>
<?php if ($producer->taxRate->value != 0): ?>

<!--<tr>
<tr>
<td class="align-right" colspan="5"><strong>Total HT</strong></td>
<td class="align-center">
<?= Price::format($document->getAmount($typeAmount)); ?>
</td>
</tr>

<?php
$taxRateArray = TaxRate::getTaxRateArray();
foreach ($document->getTotalVatArray($typeAmount) as $idTaxRate => $totalVat): ?>
<tr>
<td class="align-right" colspan="5">
<strong>TVA <?= $taxRateArray[$idTaxRate]->value * 100 ?> %</strong></td>
<td class="align-center">
<?= Price::format($totalVat); ?>
</td>
</tr>
<?php endforeach; ?>

<!--<tr>
<td class="align-right" colspan="5"><strong>TVA</strong></td>
<td class="align-center">
<?= Price::format($document->getAmountWithTax($typeAmount) - $document->getAmount($typeAmount)) ?>
</td>
</tr>-->
<tr>
<td class="align-right" colspan="5"><strong>Total TTC</strong></td>
<td class="align-center"><?= Price::format($document->getAmountWithTax($typeAmount)) ?></td>
</tr>
<?php else: ?>
<tr>
<td class="align-right" colspan="4">
<strong>Total</strong><br />
TVA non applicable
</td>
<td class="align-center"><?= Price::format($document->getAmount($typeAmount)) ?></td>
</tr>
<?php endif; ?>
<?php endif; ?>
</tbody>
</table>
<?php else : ?>
<div id="block-no-product">
<strong>Aucun produit</strong>
</div>
<tr>
<td class="align-right" colspan="5"><strong>Total TTC</strong></td>
<td class="align-center"><?= Price::format($document->getAmountWithTax($typeAmount)) ?></td>
</tr>
<?php else: ?>
<tr>
<td class="align-right" colspan="4">
<strong>Total</strong><br/>
TVA non applicable
</td>
<td class="align-center"><?= Price::format($document->getAmount($typeAmount)) ?></td>
</tr>
<?php endif; ?>
<?php endif; ?>
</div>
</tbody>
</table>
<?php else : ?>
<div id="block-no-product">
<strong>Aucun produit</strong>
</div>
<?php endif; ?>
</div>

<?php
$fieldProducerDocumentInfo = 'document_infos_'.str_replace('deliverynote','delivery_note',strtolower($document->getClass())) ; ?>
<?php if(strlen($producer->$fieldProducerDocumentInfo)): ?>
<?php
$fieldProducerDocumentInfo = 'document_infos_' . str_replace('deliverynote', 'delivery_note', strtolower($document->getClass())); ?>
<?php if (strlen($producer->$fieldProducerDocumentInfo)): ?>
<div class="block-infos">
<strong>Informations</strong><br />
<?= nl2br(Html::encode($producer->$fieldProducerDocumentInfo)) ?>
<strong>Informations</strong><br/>
<?= nl2br(Html::encode($producer->$fieldProducerDocumentInfo)) ?>
</div>
<?php endif; ?>
<?php endif; ?>
</div>

+ 14
- 1
backend/views/invoice/index.php Ver arquivo

@@ -79,7 +79,7 @@ $this->addButton(['label' => 'Nouvelle facture <span class="glyphicon glyphicon-
],
'name',
[
'attribute' => 'id_user',
'attribute' => 'username',
'header' => 'Utilisateur',
'value' => function($model) {
return $model->user->getUsername() ;
@@ -99,6 +99,19 @@ $this->addButton(['label' => 'Nouvelle facture <span class="glyphicon glyphicon-
return $invoice->getAmountWithTax(Order::INVOICE_AMOUNT_TOTAL, true) ;
}
],
[
'attribute' => 'is_sent',
'header' => 'Envoyé',
'format' => 'raw',
'value' => function($model) {
if($model->is_sent) {
return '<span class="label label-success">Oui</span>';
}
else {
return '<span class="label label-danger">Non</span>';
}
}
],
[
'class' => 'yii\grid\ActionColumn',
'template' => '{validate} {update} {delete} {send} {download} {export-csv-evoliz}',

+ 5
- 1
backend/views/layouts/content.php Ver arquivo

@@ -83,10 +83,14 @@ use dmstr\widgets\Alert;

<footer class="main-footer">
<div class="pull-right hidden-xs">
<strong>Version</strong> <?= GlobalParam::getOpendistribVersion(); ?>
</div>
<br />
<!--<div class="pull-right hidden-xs">
<b>Version</b> 2.0
</div>
<strong>Copyright &copy; 2014-2015 <a href="http://almsaeedstudio.com">Almsaeed Studio</a>.</strong> All rights
reserved.
reserved.-->
</footer>

<!-- Control Sidebar -->

+ 1
- 1
backend/views/layouts/header.php Ver arquivo

@@ -198,7 +198,7 @@ $producer = GlobalParam::getCurrentProducer();
<?php foreach ($usersNegativeCreditArray as $user): ?>
<li>
<a href="<?= Yii::$app->urlManagerBackend->createUrl(['user/credit', 'id' => $user['user_id']]); ?>">
<h5><?= Html::encode($user['name'] . ' ' . $user['lastname']); ?>
<h5><?= User::getUsernameFromArray($user); ?>
<small>
<i class="fa fa-euro"></i> <?= Price::format($user['credit']); ?>
</small>

+ 91
- 76
backend/views/layouts/left.php Ver arquivo

@@ -1,40 +1,40 @@
<?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.
*/
* 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.
*/

?>

@@ -42,65 +42,80 @@ termes.

<section class="sidebar">

<?php
$producer = GlobalParam::getCurrentProducer();
$newVersionOpendistribTemplate = '';
if($producer->latest_version_opendistrib != GlobalParam::getOpendistribVersion()) {
$newVersionOpendistribTemplate = '<span class="pull-right-container"><small class="label pull-right bg-orange">&nbsp;</small></span>';
}
?>

<?= dmstr\widgets\Menu::widget(
[
'options' => ['class' => 'sidebar-menu tree', 'data-widget'=> 'tree'],
'options' => ['class' => 'sidebar-menu tree', 'data-widget' => 'tree'],
'items' => [
['label' => 'Tableau de bord','icon' => 'dashboard','url' => ['/site/index'], 'visible' => User::isCurrentProducer()],
['label' => 'Distributions','icon' => 'calendar','url' => ['/distribution/index'], 'visible' => User::isCurrentProducer()],
[
'label' => 'Produits',
'icon' => 'clone',
'url' => '#',
'visible' => User::isCurrentProducer(),
'active' => Yii::$app->controller->id == 'product',
'items' => [
['label' => 'Liste','icon' => 'th-list','url' => ['/product/index'], 'visible' => User::isCurrentProducer()],
['label' => 'Catégories','icon' => 'book','url' => ['/product-category/index'], 'visible' => User::isCurrentProducer()],
]
],
['label' => 'Points de vente','icon' => 'map-marker','url' => ['/point-sale/index'], 'visible' => User::isCurrentProducer(), 'active' => Yii::$app->controller->id == 'point-sale'],
['label' => 'Tableau de bord', 'icon' => 'dashboard', 'url' => ['/site/index'], 'visible' => User::isCurrentProducer()],
['label' => 'Distributions', 'icon' => 'calendar', 'url' => ['/distribution/index'], 'visible' => User::isCurrentProducer()],
[
'label' => 'Utilisateurs',
'icon' => 'users',
'url' => '#',
'items' => [
['label' => 'Liste','icon' => 'th-list','url' => ['/user/index'], 'visible' => User::isCurrentProducer()],
['label' => 'Groupes','icon' => 'users','url' => ['/user-group/index'], 'visible' => User::isCurrentProducer()],
],
'label' => 'Produits',
'icon' => 'clone',
'url' => ['/product/index'],
'visible' => User::isCurrentProducer(),
'active' => Yii::$app->controller->id == 'product',
'items' => [
['label' => 'Liste', 'icon' => 'th-list', 'url' => ['/product/index'], 'visible' => User::isCurrentProducer()],
['label' => 'Catégories', 'icon' => 'book', 'url' => ['/product-category/index'], 'visible' => User::isCurrentProducer()],
]
],
['label' => 'Abonnements','icon' => 'repeat','url' => ['/subscription/index'], 'visible' => User::isCurrentProducer(), 'active' => Yii::$app->controller->id == 'subscription'],
['label' => 'Paramètres','icon' => 'cog','url' => ['/producer/update'], 'visible' => User::isCurrentProducer()],
['label' => 'Communiquer','icon' => 'bullhorn','url' => ['/communicate/index'], 'visible' => User::isCurrentProducer()],
['label' => 'Points de vente', 'icon' => 'map-marker', 'url' => ['/point-sale/index'], 'visible' => User::isCurrentProducer(), 'active' => Yii::$app->controller->id == 'point-sale'],
[
'label' => 'Statistiques',
'icon' => 'line-chart',
'url' => '#',
'label' => 'Utilisateurs',
'icon' => 'users',
'url' => ['/user/index'],
'items' => [
['label' => 'Rapports','icon' => 'pencil-square-o','url' => ['/report/index'], 'visible' => User::isCurrentProducer()],
['label' => 'Chiffre d\'affaire','icon' => 'line-chart','url' => ['/stats/index'], 'visible' => User::isCurrentProducer()],
['label' => 'Produits','icon' => 'table','url' => ['/stats/products'], 'visible' => User::isCurrentProducer()],
['label' => 'Liste', 'icon' => 'th-list', 'url' => ['/user/index'], 'visible' => User::isCurrentProducer()],
['label' => 'Groupes', 'icon' => 'users', 'url' => ['/user-group/index'], 'visible' => User::isCurrentProducer()],
],
],
['label' => 'Abonnements', 'icon' => 'repeat', 'url' => ['/subscription/index'], 'visible' => User::isCurrentProducer(), 'active' => Yii::$app->controller->id == 'subscription'],
['label' => 'Communiquer', 'icon' => 'bullhorn', 'url' => ['/communicate/index'], 'visible' => User::isCurrentProducer()],
[
'label' => 'Documents',
'icon' => 'clone',
'url' => '#',
'url' => ['/delivery-note/index'],
'items' => [
['label' => 'Devis','icon' => 'sticky-note-o','url' => ['/quotation/index'], 'visible' => User::isCurrentProducer()],
['label' => 'Bons de livraison','icon' => 'sticky-note-o','url' => ['/delivery-note/index'], 'visible' => User::isCurrentProducer()],
['label' => 'Factures','icon' => 'sticky-note-o','url' => ['/invoice/index'], 'visible' => User::isCurrentProducer()],
['label' => 'Bons de livraison', 'icon' => 'sticky-note-o', 'url' => ['/delivery-note/index'], 'visible' => User::isCurrentProducer()],
['label' => 'Factures', 'icon' => 'sticky-note-o', 'url' => ['/invoice/index'], 'visible' => User::isCurrentProducer()],
['label' => 'Devis', 'icon' => 'sticky-note-o', 'url' => ['/quotation/index'], 'visible' => User::isCurrentProducer()],
],
],
['label' => 'Développement','icon' => 'wrench','url' => ['/development/index'], 'visible' => User::isCurrentProducer(), 'active' => Yii::$app->controller->id == 'development'],
['label' => 'Tarifs','icon' => 'euro','url' => ['/producer/billing'], 'visible' => User::isCurrentProducer()],
['label' => 'Accès','icon' => 'lock','url' => ['/access/index'], 'visible' => User::isCurrentProducer()],
[
'label' => 'Statistiques',
'icon' => 'line-chart',
'url' => ['/stats/index'],
'items' => [
['label' => 'Chiffre d\'affaire', 'icon' => 'line-chart', 'url' => ['/stats/index'], 'visible' => User::isCurrentProducer()],
['label' => 'Rapports', 'icon' => 'pencil-square-o', 'url' => ['/report/index'], 'visible' => User::isCurrentProducer()],
['label' => 'Produits', 'icon' => 'table', 'url' => ['/stats/products'], 'visible' => User::isCurrentProducer()],
],
],
['label' => 'Paramètres', 'icon' => 'cog', 'url' => ['/producer/update'], 'visible' => User::isCurrentProducer()],
['label' => 'Accès', 'icon' => 'lock', 'url' => ['/access/index'], 'visible' => User::isCurrentProducer()],
[
'label' => 'Développement',
'icon' => 'code',
'url' => ['/development/index'],
'visible' => User::isCurrentProducer(),
'active' => Yii::$app->controller->id == 'development',
'template'=>'<a href="{url}">{icon} {label}'.$newVersionOpendistribTemplate.'</a>'
],
['label' => 'Tarifs', 'icon' => 'euro', 'url' => ['/producer/billing'], 'visible' => User::isCurrentProducer()],

['label' => 'Administration', 'options' => ['class' => 'header'], 'visible' => User::isCurrentAdmin()],
['label' => 'Producteurs','icon' => 'th-list','url' => ['/producer-admin/index'], 'visible' => User::isCurrentAdmin()],
['label' => 'Tranches de prix','icon' => 'eur','url' => ['/producer-price-range-admin/index'], 'visible' => User::isCurrentAdmin()],
['label' => 'Taxes','icon' => 'eur','url' => ['/tax-rate-admin/index'], 'visible' => User::isCurrentAdmin()],
['label' => 'Communiquer','icon' => 'bullhorn','url' => ['/communicate-admin/index'], 'visible' => User::isCurrentAdmin()],
['label' => 'Producteurs', 'icon' => 'th-list', 'url' => ['/producer-admin/index'], 'visible' => User::isCurrentAdmin()],
['label' => 'Tranches de prix', 'icon' => 'eur', 'url' => ['/producer-price-range-admin/index'], 'visible' => User::isCurrentAdmin()],
['label' => 'Taxes', 'icon' => 'eur', 'url' => ['/tax-rate-admin/index'], 'visible' => User::isCurrentAdmin()],
['label' => 'Communiquer', 'icon' => 'bullhorn', 'url' => ['/communicate-admin/index'], 'visible' => User::isCurrentAdmin()],

['label' => 'Outils', 'options' => ['class' => 'header'], 'visible' => User::isCurrentAdmin()],
['label' => 'Gii', 'icon' => 'file-code-o', 'url' => ['/gii'], 'visible' => User::isCurrentAdmin()],

+ 1
- 1
backend/views/layouts/main.php Ver arquivo

@@ -69,7 +69,7 @@ if (Yii::$app->controller->action->id === 'login') {
<meta name="baseurl" content="<?= Yii::$app->urlManagerBackend->baseUrl ; ?>">
<meta name="baseurl-absolute" content="<?= Yii::$app->urlManagerBackend->getHostInfo().Yii::$app->urlManagerBackend->baseUrl; ?>">
<?= Html::csrfMetaTags() ?>
<title><?= Html::encode($this->title) ?> | distrib</title>
<title><?= Html::encode($this->page_title) ?> | Opendistrib</title>
<link rel="icon" type="image/png" href="<?php echo Yii::$app->urlManagerBackend->getBaseUrl(); ?>/img/favicon-distrib.png" />
<?php $this->head() ?>
</head>

+ 333
- 348
backend/views/producer/update.php Ver arquivo

@@ -1,340 +1,332 @@
<?php

/**
Copyright La boîte à pain (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.
*/
/**
* Copyright La boîte à pain (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.
*/

use yii\helpers\Html;
use yii\widgets\ActiveForm;
use common\helpers\Url ;
use common\models\Producer ;
use common\helpers\Url;
use common\models\Producer;

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

$this->setTitle('Paramètres') ;
$this->addBreadcrumb($this->getTitle()) ;
$this->setTitle('Paramètres');
$this->addBreadcrumb($this->getTitle());

?>

<script>
var appInitValues = {
isAdmin: <?= (int) User::isCurrentAdmin() ?>
isAdmin: <?= (int)User::isCurrentAdmin() ?>
};
</script>

<div class="user-update" id="app-producer-update">
<div id="nav-params">
<button v-for="section in sectionsArray" v-if="!section.isAdminSection || (section.isAdminSection && isAdmin)" :class="'btn '+((currentSection == section.name) ? 'btn-primary' : 'btn-default')" @click="changeSection(section)">
<button v-for="section in sectionsArray" v-if="!section.isAdminSection || (section.isAdminSection && isAdmin)"
:class="'btn '+((currentSection == section.name) ? 'btn-primary' : 'btn-default')"
@click="changeSection(section)">
{{ section.nameDisplay }}
<span class="glyphicon glyphicon-triangle-bottom"></span>
</button>
</div>
<div class="user-form">
<?php $form = ActiveForm::begin([
'enableClientValidation' => false,
]); ?>
<div>
<div v-show="currentSection == 'general'" class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Général</h3>
</div>
<div class="panel-body">
<h4>Accès</h4>
<?= $form->field($model, 'active')
->dropDownList([
0 => 'Non',
1 => 'Oui'
], [])
->label('En ligne')
->hint('Activez cette option pour rendre votre établissement visible à vos clients.') ; ?>
->hint('Activez cette option pour rendre votre espace visible à vos clients.'); ?>
<?= $form->field($model, 'code')->hint("Saisissez ce champs si vous souhaitez protéger l'accès à votre espace par un code, sinon laissez-le vide.<br />"
. "Ce code est à communiquer à vos clients pour qu'ils puissent ajouter votre espace à leurs favoris.<br />"
. "<a href=\"" . Yii::$app->urlManager->createUrl(['communicate/index']) . "\">Cliquez ici</a> pour télécharger un mode d'emploi comprenant ce code à distribuer à vos clients.") ?>

<h4>Général</h4>
<?= $form->field($model, 'name') ?>
<?= $form->field($model, 'type') ?>
<?= $form->field($model, 'description')
->textarea(['rows' => 4])
->hint('Affiché sur la page d\'accueil') ?>
->textarea(['rows' => 4])
->hint('Affiché sur la page d\'accueil') ?>
<?= $form->field($model, 'address')
->textarea(['rows' => 4]) ?>
->textarea(['rows' => 4]) ?>
<?= $form->field($model, 'postcode') ?>
<?= $form->field($model, 'city') ?>

<?= $form->field($model, 'code')->hint("Saisissez ce champs si vous souhaitez protéger l'accès à votre boutique par un code, sinon laissez-le vide.<br />"
. "Ce code est à communiquer à vos client pour qu'ils puissent ajouter votre établissement à leur tableau de bord.<br />"
. "<a href=\"".Yii::$app->urlManager->createUrl(['communicate/index'])."\">Cliquez ici</a> pour télécharger un mode d'emploi comprenant ce code à distribuer à vos clients.") ?>
<h4>Apparence</h4>
<?= $form->field($model, 'background_color_logo') ?>
<?= $form->field($model, 'logo')->fileInput() ?>
<?php
if (strlen($model->logo)) {
echo '<img src="'.Yii::$app->urlManagerProducer->getHostInfo().'/'.Yii::$app->urlManagerProducer->baseUrl.'/uploads/' . $model->logo . '" width="200px" /><br />';
echo '<img src="' . Yii::$app->urlManagerProducer->getHostInfo() . '/' . Yii::$app->urlManagerProducer->baseUrl . '/uploads/' . $model->logo . '" width="200px" /><br />';
echo '<input type="checkbox" name="delete_logo" id="delete_logo" /> <label for="delete_logo">Supprimer le logo</label><br /><br />';
}
?>
<?= $form->field($model, 'photo')->fileInput()->hint('Format idéal : 900 x 150 px') ?>
<?php
if (strlen($model->photo)) {
echo '<img src="'.Yii::$app->urlManagerProducer->getHostInfo().'/'.Yii::$app->urlManagerProducer->baseUrl.'/uploads/' . $model->photo . '" width="400px" /><br />';
echo '<img src="' . Yii::$app->urlManagerProducer->getHostInfo() . '/' . Yii::$app->urlManagerProducer->baseUrl . '/uploads/' . $model->photo . '" width="400px" /><br />';
echo '<input type="checkbox" name="delete_photo" id="delete_photo" /> <label for="delete_photo">Supprimer la photo</label><br /><br />';
}
?>
<?= $form->field($model, 'behavior_home_point_sale_day_list')
->dropDownList([
Producer::BEHAVIOR_HOME_POINT_SALE_DAY_LIST_WEEK => 'Jours de la semaine',
Producer::BEHAVIOR_HOME_POINT_SALE_DAY_LIST_INCOMING_DISTRIBUTIONS => 'Distributions à venir',
]); ?>
->dropDownList([
Producer::BEHAVIOR_HOME_POINT_SALE_DAY_LIST_WEEK => 'Jours de la semaine',
Producer::BEHAVIOR_HOME_POINT_SALE_DAY_LIST_INCOMING_DISTRIBUTIONS => 'Distributions à venir',
]); ?>
</div>
</div>

<div v-show="currentSection == 'tableau-bord'" class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Tableau de bord</h3>
</div>
<div class="panel-body">
<?= $form->field($model, 'option_dashboard_number_distributions')
->dropDownList([
3 => '3',
6 => '6',
9 => '9',
12 => '12',
15 => '15',
18 => '18',
21 => '21',
24 => '24',
27 => '27',
30 => '30',
], []); ?>
<?= $form->field($model, 'option_dashboard_date_start')->textInput([
'class' => 'datepicker form-control'
]) ; ?>
<?= $form->field($model, 'option_dashboard_date_end')->textInput([
'class' => 'datepicker form-control'
]) ; ?>
<h4>Tableau de bord administration</h4>
<?= $form->field($model, 'option_dashboard_number_distributions')
->dropDownList([
3 => '3',
6 => '6',
9 => '9',
12 => '12',
15 => '15',
18 => '18',
21 => '21',
24 => '24',
27 => '27',
30 => '30',
], []); ?>
<?= $form->field($model, 'option_dashboard_date_start')->textInput([
'class' => 'datepicker form-control'
]); ?>
<?= $form->field($model, 'option_dashboard_date_end')->textInput([
'class' => 'datepicker form-control'
]); ?>
</div>
</div>

<div v-show="currentSection == 'prise-commande'" class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Prise de commande</h3>
</div>
<div v-show="currentSection == 'prise-commande'" class="panel panel-default">
<div class="panel-body">
<h4>Horaires</h4>
<?php
$delaysArray = [
1 => '1 jour',
2 => '2 jours',
3 => '3 jours',
4 => '4 jours',
5 => '5 jours',
6 => '6 jours',
7 => '7 jours',
];

$deadlinesArray = [
24 => 'Minuit',
23 => '23h',
22 => '22h',
21 => '21h',
20 => '20h',
19 => '19h',
18 => '18h',
17 => '17h',
16 => '16h',
15 => '15h',
14 => '14h',
13 => '13h',
12 => '12h',
11 => '11h',
10 => '10h',
9 => '9h',
8 => '8h',
];

$daysArray = [
'monday' => 'Lundi',
'tuesday' => 'Mardi',
'wednesday' => 'Mercredi',
'thursday' => 'Jeudi',
'friday' => 'Vendredi',
'saturday' => 'Samedi',
'sunday' => 'Dimanche'
];
?>

<?php

$delaysArray = [
1 => '1 jour',
2 => '2 jours',
3 => '3 jours',
4 => '4 jours',
5 => '5 jours',
6 => '6 jours',
7 => '7 jours',
] ;

$deadlinesArray = [
24 => 'Minuit',
23 => '23h',
22 => '22h',
21 => '21h',
20 => '20h',
19 => '19h',
18 => '18h',
17 => '17h',
16 => '16h',
15 => '15h',
14 => '14h',
13 => '13h',
12 => '12h',
11 => '11h',
10 => '10h',
9 => '9h',
8 => '8h',
] ;

$daysArray = [
'monday' => 'Lundi',
'tuesday' => 'Mardi',
'wednesday' => 'Mercredi',
'thursday' => 'Jeudi',
'friday' => 'Vendredi',
'saturday' => 'Samedi',
'sunday' => 'Dimanche'
] ;
?>

<div class="row">
<div class="col-md-2">
<strong>Par défaut</strong>
</div>
<div class="col-md-5">
<?= $form->field($model, 'order_delay')
->dropDownList($delaysArray, ['prompt' => '--'])
->hint('Si <strong>1 jour</strong> est sélectionné, le client pourra commander jusqu\'à la veille de la production.<br />'
. 'Si <strong>2 jours</strong> est sélectionné, le client pourra commander jusqu\'à l\'avant-veille de la production, etc.') ; ?>
</div>
<div class="col-md-5">
<?= $form->field($model, 'order_deadline')
->dropDownList($deadlinesArray, ['prompt' => '--'])
->hint('Heure limite jusqu\'à laquelle les clients peuvent commander pour satisfaire le délai de commande.<br />'
. 'Par exemple, si <strong>2 jours</strong> est sélectionné dans le délai de commande, le client devra commander l\'avant-veille de la production avant l\'heure précisée ici.') ; ?>
</div>
<div class="row">
<div class="col-md-2">
<strong>Par défaut</strong>
</div>
<div class="col-md-5">
<?= $form->field($model, 'order_delay')
->dropDownList($delaysArray, ['prompt' => '--'])
->hint('Si <strong>1 jour</strong> est sélectionné, le client pourra commander jusqu\'à la veille de la production.<br />'
. 'Si <strong>2 jours</strong> est sélectionné, le client pourra commander jusqu\'à l\'avant-veille de la production, etc.'); ?>
</div>
<div class="col-md-5">
<?= $form->field($model, 'order_deadline')
->dropDownList($deadlinesArray, ['prompt' => '--'])
->hint('Heure limite jusqu\'à laquelle les clients peuvent commander pour satisfaire le délai de commande.<br />'
. 'Par exemple, si <strong>2 jours</strong> est sélectionné dans le délai de commande, le client devra commander l\'avant-veille de la production avant l\'heure précisée ici.'); ?>
</div>
<?php foreach($daysArray as $day => $labelDay): ?>
</div>
<?php foreach ($daysArray as $day => $labelDay): ?>
<div class="row">
<div class="col-md-2">
<strong><?= $labelDay ?></strong>
</div>
<div class="col-md-5">
<?= $form->field($model, 'order_delay_'.$day, [
'template' => '{input}',
])->dropDownList($delaysArray, ['prompt' => '--'])->label(''); ?>
</div>
<div class="col-md-5">
<?= $form->field($model, 'order_deadline_'.$day, [
'template' => '{input}',
])->dropDownList($deadlinesArray, ['prompt' => '--'])->label(''); ?>
</div>
<div class="col-md-2">
<strong><?= $labelDay ?></strong>
</div>
<div class="col-md-5">
<?= $form->field($model, 'order_delay_' . $day, [
'template' => '{input}',
])->dropDownList($delaysArray, ['prompt' => '--'])->label(''); ?>
</div>
<div class="col-md-5">
<?= $form->field($model, 'order_deadline_' . $day, [
'template' => '{input}',
])->dropDownList($deadlinesArray, ['prompt' => '--'])->label(''); ?>
</div>
</div>
<?php endforeach; ?>
<?php endforeach; ?>

<h4>Informations</h4>
<?= $form->field($model, 'order_infos')
->textarea(['rows' => 6])
->hint('Affichées au client lors de sa commande')?>
->hint('Affichées au client lors de sa commande') ?>

<?= $form->field($model, 'option_payment_info')
->textarea(['rows' => 6])
->hint('Affichées au client à la fin de la prise de commande') ?>


<h4>Tunnel de commande</h4>
<?= $form->field($model, 'option_order_entry_point')
->dropDownList([
Producer::ORDER_ENTRY_POINT_DATE => 'Date',
Producer::ORDER_ENTRY_POINT_POINT_SALE => 'Point de vente',
], []); ?>
<?= $form->field($model, 'behavior_order_select_distribution')
->dropDownList([
Producer::BEHAVIOR_ORDER_SELECT_DISTRIBUTION_CALENDAR => 'Calendrier',
Producer::BEHAVIOR_ORDER_SELECT_DISTRIBUTION_LIST => 'Liste',
]); ?>
<?= $form->field($model, 'option_delivery')
->dropDownList([
0 => 'Non',
1 => 'Oui'
], []); ?>
<?php echo $form->field($model, 'option_allow_order_guest')
->dropDownList([
0 => 'Non',
1 => 'Oui'
], []); ?>


<h4>Notifications</h4>
<?= $form->field($model, 'option_notify_producer_order_summary')
->dropDownList([
0 => 'Non',
1 => 'Oui',
], []) ; ?>
<?= $form->field($model, 'option_behavior_cancel_order')
->dropDownList([
Producer::BEHAVIOR_DELETE_ORDER_DELETE => 'Suppression de la commande',
Producer::BEHAVIOR_DELETE_ORDER_STATUS => 'Passage de la commande en statut "supprimé"',
], []) ; ?>
], []); ?>
<?= $form->field($model, 'option_email_confirm')
->dropDownList([
0 => 'Non',
1 => 'Oui'
], []); ?>
<?= $form->field($model, 'option_email_confirm_producer')
->dropDownList([
0 => 'Non',
1 => 'Oui'
], []); ?>

<?= $form->field($model, 'behavior_order_select_distribution')

<h4>Exports</h4>
<?= $form->field($model, 'option_csv_export_all_products')
->dropDownList([
Producer::BEHAVIOR_ORDER_SELECT_DISTRIBUTION_CALENDAR => 'Calendrier',
Producer::BEHAVIOR_ORDER_SELECT_DISTRIBUTION_LIST => 'Liste',
]); ?>
0 => 'Non',
1 => 'Oui'
], []); ?>
<?= $form->field($model, 'option_csv_export_by_piece')
->dropDownList([
0 => 'Non',
1 => 'Oui'
], []); ?>
<?= $form->field($model, 'option_display_export_grid')
->dropDownList([
0 => 'Non',
1 => 'Oui'
], []); ?>
<?= $form->field($model, 'option_export_display_product_reference')
->dropDownList([
0 => 'Non',
1 => 'Oui'
], []); ?>

<?= $form->field($model, 'option_payment_info')
->textarea(['rows' => 6])
->hint('Affiché au client à la fin de la prise de commande')?>

<?= $form->field($model, 'option_email_confirm')
->dropDownList([
0 => 'Non',
1 => 'Oui'
], []); ?>
<?= $form->field($model, 'option_email_confirm_producer')
->dropDownList([
0 => 'Non',
1 => 'Oui'
], []); ?>

<?= $form->field($model, 'option_csv_export_all_products')
->dropDownList([
0 => 'Non',
1 => 'Oui'
], []); ?>
<?= $form->field($model, 'option_csv_export_by_piece')
->dropDownList([
0 => 'Non',
1 => 'Oui'
], []); ?>

<?= $form->field($model, 'option_display_export_grid')
->dropDownList([
0 => 'Non',
1 => 'Oui'
], []); ?>

<?= $form->field($model, 'option_order_reference_type')
->dropDownList([
Producer::ORDER_REFERENCE_TYPE_NONE => '--',
Producer::ORDER_REFERENCE_TYPE_YEARLY => 'Annuelle',
], []) ; ?>

<?= $form->field($model, 'option_export_display_product_reference')
->dropDownList([
0 => 'Non',
1 => 'Oui'
], []); ?>

<?php echo $form->field($model, 'option_allow_order_guest')
->dropDownList([
0 => 'Non',
1 => 'Oui'
], []); ?>

<?= $form->field($model, 'option_order_entry_point')
->dropDownList([
Producer::ORDER_ENTRY_POINT_DATE => 'Date',
Producer::ORDER_ENTRY_POINT_POINT_SALE => 'Point de vente',
], []); ?>

<?= $form->field($model, 'option_delivery')
->dropDownList([
0 => 'Non',
1 => 'Oui'
], []); ?>
<h4>Divers</h4>
<?= $form->field($model, 'option_order_reference_type')
->dropDownList([
Producer::ORDER_REFERENCE_TYPE_NONE => '--',
Producer::ORDER_REFERENCE_TYPE_YEARLY => 'Annuelle',
], []); ?>
<?= $form->field($model, 'option_behavior_cancel_order')
->dropDownList([
Producer::BEHAVIOR_DELETE_ORDER_DELETE => 'Suppression de la commande',
Producer::BEHAVIOR_DELETE_ORDER_STATUS => 'Passage de la commande en statut "supprimé"',
], []); ?>
</div>
</div>
</div>


<div v-show="currentSection == 'abonnements'" class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Abonnements</h3>
</div>
<div class="panel-body">
<h4>Abonnements</h4>
<?= $form->field($model, 'user_manage_subscription')
->dropDownList([
0 => 'Non',
1 => 'Oui',
], []) ; ?>
->dropDownList([
0 => 'Non',
1 => 'Oui',
], []); ?>
</div>
</div>

<div v-show="currentSection == 'credit-payment'" class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Crédit</h3>
</div>
<div class="panel-body">
<h4>Crédit</h4>
<?= $form->field($model, 'credit')
->dropDownList([
0 => 'Non',
@@ -342,142 +334,135 @@ $this->addBreadcrumb($this->getTitle()) ;
], [])
->label('Activer le système de Crédit')
->hint('Le système de Crédit permet à vos clients d\'avoir un compte prépayé sur le site <em>distrib</em>.<br />'
. 'Ils créditent leur compte en vous donnant la somme de leur choix et c\'est ensuite à vous de '.Html::a('mettre à jour', ['user/index']).' leur Crédit en ligne.<br />'
. 'Ceci fait, les clients paient leur commande directement via leur Crédit.') ; ?>
. 'Ils créditent leur compte en vous donnant la somme de leur choix et c\'est ensuite à vous de ' . Html::a('mettre à jour', ['user/index']) . ' leur Crédit en ligne.<br />'
. 'Ceci fait, les clients paient leur commande directement via leur Crédit.'); ?>

<?= $form->field($model, 'credit_functioning')
->dropDownList([
Producer::CREDIT_FUNCTIONING_OPTIONAL => Producer::$creditFunctioningArray[Producer::CREDIT_FUNCTIONING_OPTIONAL],
Producer::CREDIT_FUNCTIONING_MANDATORY => Producer::$creditFunctioningArray[Producer::CREDIT_FUNCTIONING_MANDATORY],
Producer::CREDIT_FUNCTIONING_USER => Producer::$creditFunctioningArray[Producer::CREDIT_FUNCTIONING_USER],
], [])->hint(Producer::HINT_CREDIT_FUNCTIONING) ; ?>
->dropDownList([
Producer::CREDIT_FUNCTIONING_OPTIONAL => Producer::$creditFunctioningArray[Producer::CREDIT_FUNCTIONING_OPTIONAL],
Producer::CREDIT_FUNCTIONING_MANDATORY => Producer::$creditFunctioningArray[Producer::CREDIT_FUNCTIONING_MANDATORY],
Producer::CREDIT_FUNCTIONING_USER => Producer::$creditFunctioningArray[Producer::CREDIT_FUNCTIONING_USER],
], [])->hint(Producer::HINT_CREDIT_FUNCTIONING); ?>
<?= $form->field($model, 'use_credit_checked_default')
->dropDownList([
0 => 'Non',
1 => 'Oui',
], [])->hint('Utilisation optionnelle du Crédit.') ; ?>
<?= $form->field($model, 'credit_limit_reminder',[
'template' => '{label}<div class="input-group">{input}<span class="input-group-addon"><span class="glyphicon glyphicon-euro"></span></span></div>{hint}',
])
->hint("Une relance est envoyé au client dès que ce seuil est dépassé.") ; ?>
<?= $form->field($model, 'credit_limit',[
'template' => '{label}<div class="input-group">{input}<span class="input-group-addon"><span class="glyphicon glyphicon-euro"></span></span></div>{hint}',
])->hint('Limite de crédit que l\'utilisateur ne pourra pas dépasser. Laisser vide pour permettre un crédit négatif et infini.'); ?>

<br />
<h3>Paiement en ligne</h3>
->dropDownList([
0 => 'Non',
1 => 'Oui',
], [])->hint('Utilisation optionnelle du Crédit.'); ?>

<?= $form->field($model, 'credit_limit_reminder', [
'template' => '{label}<div class="input-group">{input}<span class="input-group-addon"><span class="glyphicon glyphicon-euro"></span></span></div>{hint}',
])
->hint("Une relance est envoyé au client dès que ce seuil est dépassé."); ?>



<?= $form->field($model, 'credit_limit', [
'template' => '{label}<div class="input-group">{input}<span class="input-group-addon"><span class="glyphicon glyphicon-euro"></span></span></div>{hint}',
])->hint('Limite de crédit que l\'utilisateur ne pourra pas dépasser. Laisser vide pour permettre un crédit négatif et infini.'); ?>

<h4>Paiement en ligne</h4>

<?= $form->field($model, 'online_payment')
->dropDownList([
0 => 'Non',
1 => 'Oui',
], []); ?>
->dropDownList([
0 => 'Non',
1 => 'Oui',
], []); ?>
<?= $form->field($model, 'option_stripe_mode_test')->dropDownList([
0 => 'Non',
1 => 'Oui'
], []); ?>
0 => 'Non',
1 => 'Oui'
], []); ?>

<?= $form->field($model, 'option_online_payment_type')
->dropDownList([
'credit' => 'Alimentation du crédit',
'order' => 'Paiement à la commande',
], []); ?>
->dropDownList([
'credit' => 'Alimentation du crédit',
'order' => 'Paiement à la commande',
], []); ?>

<?= $form->field($model, 'option_stripe_public_key')->textInput(); ?>
<?= $form->field($model, 'option_stripe_private_key')->textInput(); ?>
<?= $form->field($model, 'option_stripe_endpoint_secret')->textInput(); ?>
</div>
</div>
<div v-show="currentSection == 'infos'" class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Informations légales</h3>
</div>
<div class="panel-body">
<h4>Informations légales</h4>
<?= $form->field($model, 'mentions')
->textarea(['rows' => 15])
->hint('') ?>
->textarea(['rows' => 15])
->hint('') ?>
<?= $form->field($model, 'gcs')
->textarea(['rows' => 15])
->hint('') ?>
->textarea(['rows' => 15])
->hint('') ?>
</div>
</div>
<div v-show="currentSection == 'logiciels-caisse'" class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Logiciels de caisse</h3>
</div>
<div class="panel-body">
<h4>Tiller</h4>
<?= $form->field($model, 'tiller')
->dropDownList([
0 => 'Non',
1 => 'Oui'
], [])
->label('Synchroniser avec Tiller'); ?>
<?= $form->field($model, 'tiller_provider_token') ; ?>
<?= $form->field($model, 'tiller_restaurant_token') ; ?>
<?= $form->field($model, 'tiller_provider_token'); ?>
<?= $form->field($model, 'tiller_restaurant_token'); ?>
</div>
</div>
<div v-show="currentSection == 'facturation'" class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Facturation</h3>
</div>
<div class="panel-body">
<?= $form->field($model, 'id_tax_rate_default')
->dropDownList(ArrayHelper::map(TaxRate::find()->all(), 'id', function($model) { return $model->name; }))
->label('TVA à appliquer par défaut'); ?>
<?= $form->field($model, 'option_tax_calculation_method')
->dropDownList(Document::$taxCalculationMethodArray); ?>
<?= $form->field($model, 'option_export_evoliz')->dropDownList([
0 => 'Non',
1 => 'Oui'
]) ; ?>
<?= $form->field($model, 'document_quotation_prefix') ; ?>
<?= $form->field($model, 'document_quotation_first_reference') ; ?>
<?= $form->field($model, 'document_quotation_duration') ; ?>
<?= $form->field($model, 'document_invoice_prefix') ; ?>
<?= $form->field($model, 'document_invoice_first_reference') ; ?>
<?= $form->field($model, 'document_delivery_note_prefix') ; ?>
<?= $form->field($model, 'document_delivery_note_first_reference') ; ?>
<?= $form->field($model, 'document_display_orders_invoice')->dropDownList([
0 => 'Non',
1 => 'Oui'
]) ; ?>
<?= $form->field($model, 'document_display_orders_delivery_note')->dropDownList([
0 => 'Non',
1 => 'Oui'
]) ; ?>
<?= $form->field($model, 'document_display_prices_delivery_note')->dropDownList([
0 => 'Non',
1 => 'Oui'
]) ; ?>
<?= $form->field($model, 'document_display_product_description')->dropDownList([
0 => 'Non',
1 => 'Oui'
]) ; ?>
<?= $form->field($model, 'document_infos_bottom')
->textarea(['rows' => 15]) ?>
<?= $form->field($model, 'document_infos_quotation')
->textarea(['rows' => 15]) ?>
<?= $form->field($model, 'document_infos_invoice')
->textarea(['rows' => 15]) ?>
<?= $form->field($model, 'document_infos_delivery_note')
->textarea(['rows' => 15]) ?>
<h4>Facturation</h4>
<?= $form->field($model, 'id_tax_rate_default')
->dropDownList(ArrayHelper::map(TaxRate::find()->all(), 'id', function ($model) {
return $model->name;
}))
->label('TVA à appliquer par défaut'); ?>
<?= $form->field($model, 'option_tax_calculation_method')
->dropDownList(Document::$taxCalculationMethodArray); ?>
<?= $form->field($model, 'option_export_evoliz')->dropDownList([
0 => 'Non',
1 => 'Oui'
]); ?>
<?= $form->field($model, 'document_quotation_prefix'); ?>
<?= $form->field($model, 'document_quotation_first_reference'); ?>
<?= $form->field($model, 'document_quotation_duration'); ?>
<?= $form->field($model, 'document_invoice_prefix'); ?>
<?= $form->field($model, 'document_invoice_first_reference'); ?>
<?= $form->field($model, 'document_delivery_note_prefix'); ?>
<?= $form->field($model, 'document_delivery_note_first_reference'); ?>
<?= $form->field($model, 'document_display_orders_invoice')->dropDownList([
0 => 'Non',
1 => 'Oui'
]); ?>
<?= $form->field($model, 'document_display_orders_delivery_note')->dropDownList([
0 => 'Non',
1 => 'Oui'
]); ?>
<?= $form->field($model, 'document_display_prices_delivery_note')->dropDownList([
0 => 'Non',
1 => 'Oui'
]); ?>
<?= $form->field($model, 'document_display_product_description')->dropDownList([
0 => 'Non',
1 => 'Oui'
]); ?>
<?= $form->field($model, 'document_infos_bottom')
->textarea(['rows' => 15]) ?>
<?= $form->field($model, 'document_infos_quotation')
->textarea(['rows' => 15]) ?>
<?= $form->field($model, 'document_infos_invoice')
->textarea(['rows' => 15]) ?>
<?= $form->field($model, 'document_infos_delivery_note')
->textarea(['rows' => 15]) ?>
</div>
</div>

<?php if(User::isCurrentAdmin()): ?>
<?php if (User::isCurrentAdmin()): ?>
<div v-show="currentSection == 'administration'" class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Administration</h3>
</div>
<div class="panel-body">
<h4>Administration</h4>
<?= $form->field($model, 'option_billing_type')
->dropDownList(Producer::getBillingTypePopulateDropdown()); ?>
<?= $form->field($model, 'option_billing_frequency')
@@ -490,7 +475,7 @@ $this->addBreadcrumb($this->getTitle()) ;
</div>
</div>
<?php endif; ?>
<div class="form-group">
<?= Html::submitButton('Mettre à jour', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div>

+ 103
- 97
backend/views/product/index.php Ver arquivo

@@ -43,6 +43,7 @@ use common\models\Product;
use common\models\TaxRate;
use common\models\Producer;
use common\helpers\GlobalParam;
use \lo\widgets\Toggle;

$this->setTitle('Produits');
$this->addBreadcrumb($this->getTitle());
@@ -53,107 +54,112 @@ $this->addButton(['label' => 'Nouveau produit <span class="glyphicon glyphicon-p

<span style="display: none;" id="page-size"><?= $dataProvider->pagination->pageSize; ?></span>
<div class="product-index">
<?= GridView::widget([
'filterModel' => $searchModel,
'dataProvider' => $dataProvider,
'columns' => [
[
'attribute' => 'order',
'headerOptions' => ['class' => 'order'],
'format' => 'raw',
'filter' => '',
'value' => function ($model) {
return '<a class="btn-order btn btn-default" href="javascript:void(0);"><span class="glyphicon glyphicon-resize-vertical"></span></a>';
}
],
[
'attribute' => 'photo',
'format' => 'raw',
'headerOptions' => ['class' => 'td-photo'],
'filter' => '',
'value' => function ($model) {
if (strlen($model->photo)) {
$url = Yii::$app->urlManagerProducer->getHostInfo() . '/' . Yii::$app->urlManagerProducer->baseUrl . '/uploads/'.$model->photo ;
$url = str_replace('//uploads','/uploads', $url) ;
return '<img class="photo-product" src="' . $url . '" />';
}
return '';
}
],
'name',
'description',
[
'attribute' => 'id_product_category',
'format' => 'raw',
'headerOptions' => ['class' => 'td-product-category'],
'filter' => '',
'value' => function ($model) {
if ($model->productCategory) {
return $model->productCategory->name ;
}
return '';
}
],
[
'attribute' => 'id_tax_rate',
'value' => function ($model) {
if ($model->id_tax_rate == 0 || $model->id_tax_rate == null) {
<?= GridView::widget([
'filterModel' => $searchModel,
'dataProvider' => $dataProvider,
'columns' => [
[
'attribute' => 'order',
'headerOptions' => ['class' => 'order'],
'format' => 'raw',
'filter' => '',
'value' => function ($model) {
return '<a class="btn-order btn btn-default" href="javascript:void(0);"><span class="glyphicon glyphicon-resize-vertical"></span></a>';
}
],
[
'attribute' => 'photo',
'format' => 'raw',
'headerOptions' => ['class' => 'td-photo'],
'filter' => '',
'value' => function ($model) {
if (strlen($model->photo)) {
$url = Yii::$app->urlManagerProducer->getHostInfo() . '/' . Yii::$app->urlManagerProducer->baseUrl . '/uploads/' . $model->photo;
$url = str_replace('//uploads', '/uploads', $url);
return '<img class="photo-product" src="' . $url . '" />';
}
return '';
}
],
'name',
'description',
[
'attribute' => 'id_product_category',
'format' => 'raw',
'headerOptions' => ['class' => 'td-product-category'],
'filter' => '',
'value' => function ($model) {
if ($model->productCategory) {
return $model->productCategory->name;
}
return '';
}
],
[
'attribute' => 'id_tax_rate',
'value' => function ($model) {
if ($model->id_tax_rate == 0 || $model->id_tax_rate == null) {

//Récupère la tva par défaut du producteur courant
$taxRateDefault = GlobalParam::getCurrentProducer()->taxRate;
//Récupère la tva par défaut du producteur courant
$taxRateDefault = GlobalParam::getCurrentProducer()->taxRate;

$return = $taxRateDefault->name;
} else {
$return = $taxRateDefault->name;
} else {

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

return $return;
}
],
[
'attribute' => 'active',
'headerOptions' => ['class' => 'active'],
'contentOptions' => ['class' => 'center'],
'format' => 'raw',
'filter' => [0 => 'Non', 1 => 'Oui'],
'value' => function ($model) {
if ($model->active) {
return '<span class="label label-success">oui</span>';
} else {
return '<span class="label label-danger">non</span>';
}
}
],
$return = $model->taxRate->name;
}
return $return;
}
],
[
'attribute' => 'price',
'value' => function ($model) {
$return = '';
if ($model->price) {
$return = Price::format($model->getPriceWithTax()) . ' (' . Product::strUnit($model->unit, 'wording_unit', true) . ')';
}

return $return;
}
],
[
'attribute' => 'active',
'headerOptions' => ['class' => 'active'],
'contentOptions' => ['class' => 'center'],
'format' => 'raw',
'filter' => [0 => 'Non', 1 => 'Oui'],
'value' => function ($model) {
return Toggle::widget(
[
'class' => 'yii\grid\ActionColumn',
'template' => '{update} {delete}',
'headerOptions' => ['class' => 'column-actions'],
'contentOptions' => ['class' => 'column-actions'],
'buttons' => [
'update' => function ($url, $model) {
return Html::a('<span class="glyphicon glyphicon-pencil"></span>', $url, [
'title' => Yii::t('app', 'Modifier'), 'class' => 'btn btn-default'
]);
},
'delete' => function ($url, $model) {
return Html::a('<span class="glyphicon glyphicon-trash"></span>', $url, [
'title' => Yii::t('app', 'Supprimer'), 'class' => 'btn btn-default'
]);
}
],
],
'name' => 'active',
'checked' => $model->active,
'options' => [
'data-id' => $model->id,
'data-on' => 'Oui',
'data-off' => 'Non',
],
]
);
}
],
[
'class' => 'yii\grid\ActionColumn',
'template' => '{update} {delete}',
'headerOptions' => ['class' => 'column-actions'],
'contentOptions' => ['class' => 'column-actions'],
'buttons' => [
'update' => function ($url, $model) {
return Html::a('<span class="glyphicon glyphicon-pencil"></span>', $url, [
'title' => Yii::t('app', 'Modifier'), 'class' => 'btn btn-default'
]);
},
'delete' => function ($url, $model) {
return Html::a('<span class="glyphicon glyphicon-trash"></span>', $url, [
'title' => Yii::t('app', 'Supprimer'), 'class' => 'btn btn-default'
]);
}
],
]); ?>
],
],
]); ?>
</div>

+ 1
- 1
backend/views/site/index.php Ver arquivo

@@ -38,7 +38,7 @@ termes.

use yii\helpers\Html ;

$this->title = 'Tableau de bord';
$this->setTitle('Tableau de bord');

?>
<div class="site-index">

+ 2
- 6
backend/views/user/index.php Ver arquivo

@@ -67,11 +67,7 @@ $this->render('_menu', [
'attribute' => 'username',
'label' => 'Nom',
'value' => function ($model) {
if (isset($model['name_legal_person']) && strlen($model['name_legal_person'])) {
return $model['name_legal_person'];
} else {
return $model['lastname'] . ' ' . $model['name'];
}
return User::getUsernameFromArray($model);
}
],
[
@@ -112,7 +108,7 @@ $this->render('_menu', [
'orders' => function ($url, $model) {
$url = Yii::$app->urlManager->createUrl(['user/orders', 'id' => $model['id']]);
$countOrders = Order::searchCount([
'id_user' => $model['id']
'id_user' => $model['id'],
], ['conditions' => 'date_delete IS NULL']);

$html = '';

+ 96
- 70
backend/views/user/orders.php Ver arquivo

@@ -1,79 +1,105 @@
<?php

/**
Copyright distrib (2018)
/**
* 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.
*/

contact@opendistrib.net
use yii\grid\GridView;

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.
*/

use yii\helpers\Html;
use yii\widgets\ActiveForm;
use common\models\CreditHistory;
use common\models\Producer;
use common\models\Order;

$this->setTitle('Commandes <small>'.Html::encode($user->lastname.' '.$user->name).'</small>', 'Commandes de '.Html::encode($user->lastname.' '.$user->name)) ;
$this->addBreadcrumb(['label' => 'Utilisateurs', 'url' => ['index']]) ;
$this->addBreadcrumb(['label' => Html::encode($user->lastname.' '.$user->name)]) ;
$this->addBreadcrumb('Commandes') ;
$this->setTitle('Commandes <small>' . Html::encode($user->getUsername()) . '</small>', 'Commandes de ' . Html::encode($user->getUsername()));
$this->addBreadcrumb(['label' => 'Utilisateurs', 'url' => ['index']]);
$this->addBreadcrumb(['label' => Html::encode($user->lastname . ' ' . $user->name)]);
$this->addBreadcrumb('Commandes');

?>

<div class="user-orders">
<?php if(count($ordersArray)): ?>
<table id="historique-commandes" class="table table-striped table-bordered">
<thead>
<tr>
<th>Date livraison</th>
<th>Historique</th>
<th>Résumé</th>
<th>Point de vente</th>
<th class="montant">Montant</th>
</tr>
</thead>
<tbody>
<?php foreach($ordersArray as $order): ?>
<tr class="<?= $order->getClassHistory() ; ?>">
<td><?php echo date('d/m/Y',strtotime($order->distribution->date)); ?></td>
<td class="historique"><?= $order->getStrHistory() ; ?></td>
<td class="resume"><?= $order->getCartSummary() ; ?></td>
<td><?= $order->getPointSaleSummary(); ?></td>
<td class="montant"><?= $order->getAmountSummary(); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<div class="alert alert-warning">Aucune commande passée par ce client.</div>
<?php endif; ?>
<?= GridView::widget([
'filterModel' => $searchModel,
'dataProvider' => $dataProvider,
'columns' => [
[
'attribute' => 'distribution.date',
'label' => 'Date de livraison',
'value' => function ($model) {
return date('d/m/Y',strtotime($model->distribution->date));
}
],
[
'label' => 'Historique',
'format' => 'raw',
'value' => function ($model) {
return $model->getStrHistory();
}
],
[
'label' => 'Résumé',
'format' => 'raw',
'value' => function ($model) {
return $model->getCartSummary();
}
],
[
'label' => 'Point de vente',
'format' => 'raw',
'value' => function ($model) {
return $model->getPointSaleSummary();
}
],
[
'label' => 'Montant',
'format' => 'raw',
'value' => function ($model) {
$model->init();
return $model->getAmountSummary();
}
],
[
'class' => 'yii\grid\ActionColumn',
'template' => '{update}',
'headerOptions' => ['class' => 'column-actions'],
'contentOptions' => ['class' => 'column-actions'],
'buttons' => [
'update' => function ($url, $model) {
$url = Yii::$app->urlManager->createUrl(['distribution/index', 'idOrderUpdate' => $model->id]);
return Html::a('<span class="glyphicon glyphicon-pencil"></span>', $url, [
'title' => Yii::t('app', 'Modifier'), 'class' => 'btn btn-default'
]);
},
],
]
],
]); ?>
</div>

+ 76
- 38
backend/web/css/screen.css Ver arquivo

@@ -1720,22 +1720,23 @@ body.login-page .login-box .login-box-body a:hover {
right: 15px;
width: 300px;
height: auto;
z-index: 9999;
}
/* line 9, ../sass/_alerts.scss */
/* line 10, ../sass/_alerts.scss */
#app-alerts .slide-fade-enter-active {
-moz-transition: all 0.3s ease;
-o-transition: all 0.3s ease;
-webkit-transition: all 0.3s ease;
transition: all 0.3s ease;
}
/* line 12, ../sass/_alerts.scss */
/* line 13, ../sass/_alerts.scss */
#app-alerts .slide-fade-leave-active {
-moz-transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1);
-o-transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1);
-webkit-transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1);
transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1);
}
/* line 15, ../sass/_alerts.scss */
/* line 16, ../sass/_alerts.scss */
#app-alerts .slide-fade-enter, #app-alerts .slide-fade-leave-to {
-moz-transform: translateX(10px);
-ms-transform: translateX(10px);
@@ -2075,11 +2076,28 @@ termes.
.distribution-index #orders #buttons-top-orders .dropdown {
display: inline-block;
}
/* line 242, ../sass/distribution/_index.scss */
/* line 240, ../sass/distribution/_index.scss */
.distribution-index #orders .point-sale-totals {
background-color: white;
padding: 10px 20px;
border: solid 1px #e0e0e0;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
margin-bottom: 20px;
}
/* line 247, ../sass/distribution/_index.scss */
.distribution-index #orders .point-sale-totals .title {
color: gray;
font-size: 13px;
margin-right: 13px;
text-transform: uppercase;
}
/* line 257, ../sass/distribution/_index.scss */
.distribution-index #orders table td.tiller {
width: 60px;
}
/* line 245, ../sass/distribution/_index.scss */
/* line 260, ../sass/distribution/_index.scss */
.distribution-index #orders table td.tiller label {
font-size: 12px;
cursor: pointer;
@@ -2087,68 +2105,68 @@ termes.
top: -2px;
font-weight: normal;
}
/* line 254, ../sass/distribution/_index.scss */
/* line 269, ../sass/distribution/_index.scss */
.distribution-index #orders table td.column-actions {
position: relative;
text-align: right;
}
/* line 258, ../sass/distribution/_index.scss */
/* line 273, ../sass/distribution/_index.scss */
.distribution-index #orders table td.column-actions .dropdown-menu {
top: 0px;
right: 0px;
}
/* line 263, ../sass/distribution/_index.scss */
/* line 278, ../sass/distribution/_index.scss */
.distribution-index #orders table td.column-actions .modal-form-order,
.distribution-index #orders table td.column-actions .modal-payment {
text-align: left;
}
/* line 268, ../sass/distribution/_index.scss */
/* line 283, ../sass/distribution/_index.scss */
.distribution-index #orders table td.column-actions .add-subscription {
position: relative;
}
/* line 271, ../sass/distribution/_index.scss */
/* line 286, ../sass/distribution/_index.scss */
.distribution-index #orders table td.column-actions .add-subscription .glyphicon-plus {
position: absolute;
top: 4px;
right: 4px;
font-size: 7px;
}
/* line 280, ../sass/distribution/_index.scss */
/* line 295, ../sass/distribution/_index.scss */
.distribution-index #orders table td.column-state-payment {
width: 120px;
}
/* line 285, ../sass/distribution/_index.scss */
/* line 300, ../sass/distribution/_index.scss */
.distribution-index #orders table td.column-payment div.btn-group {
width: 125px;
}
/* line 291, ../sass/distribution/_index.scss */
/* line 306, ../sass/distribution/_index.scss */
.distribution-index #orders table tr.view ul {
list-style-type: none;
margin-left: 0px;
padding-left: 15px;
}
/* line 301, ../sass/distribution/_index.scss */
/* line 316, ../sass/distribution/_index.scss */
.distribution-index #orders table tr.view .comment {
margin-top: 20px;
}
/* line 305, ../sass/distribution/_index.scss */
/* line 320, ../sass/distribution/_index.scss */
.distribution-index #orders table tr.view .delivery {
margin-top: 20px;
}
/* line 314, ../sass/distribution/_index.scss */
/* line 329, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order .modal-container {
width: 100%;
padding: 0px;
}
/* line 318, ../sass/distribution/_index.scss */
/* line 333, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order .modal-container .modal-body {
padding-right: 15px;
}
/* line 320, ../sass/distribution/_index.scss */
/* line 336, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order .modal-container .modal-body table {
margin-bottom: 150px;
}
/* line 325, ../sass/distribution/_index.scss */
/* line 341, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order .modal-container .modal-footer {
border-top-color: #f4f4f4;
position: fixed;
@@ -2160,46 +2178,54 @@ termes.
text-align: center;
border-top: solid 1px #e0e0e0;
}
/* line 337, ../sass/distribution/_index.scss */
/* line 353, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order .modal-container .modal-footer .actions-form button {
float: none;
}
/* line 344, ../sass/distribution/_index.scss */
/* line 357, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order .modal-container .modal-footer .actions-form div.right {
float: right;
}
/* line 364, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order .btn-credit {
float: right;
}
/* line 350, ../sass/distribution/_index.scss */
/* line 370, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products .product-ordered td {
background-color: #e9e9e9;
}
/* line 354, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products .product-ordered input {
/* line 374, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products .product-ordered input.input-quantity {
font-size: 16px;
font-weight: bold;
}
/* line 360, ../sass/distribution/_index.scss */
/* line 380, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products td.price {
width: 150px;
}
/* line 362, ../sass/distribution/_index.scss */
/* line 383, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products td.price input {
text-align: center;
}
/* line 367, ../sass/distribution/_index.scss */
/* line 387, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products td.price .input-group-addon {
background-color: #eee;
}
/* line 392, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products td.quantity {
width: 165px;
}
/* line 370, ../sass/distribution/_index.scss */
/* line 395, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products td.quantity input {
text-align: center;
color: black;
}
/* line 375, ../sass/distribution/_index.scss */
/* line 400, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products td.quantity .form-control {
border-right: 0px none;
padding-right: 4px;
}
/* line 380, ../sass/distribution/_index.scss */
/* line 405, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products td.quantity .input-group-addon {
padding: 5px;
padding-left: 0px;
@@ -2207,35 +2233,35 @@ termes.
border-left: 0px none;
border-right: 0px none;
}
/* line 389, ../sass/distribution/_index.scss */
/* line 414, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products td.quantity-remaining {
text-align: right;
}
/* line 392, ../sass/distribution/_index.scss */
/* line 417, ../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 396, ../sass/distribution/_index.scss */
/* line 421, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order table.table-products td.quantity-remaining.negative {
color: #DD4B39;
}
/* line 400, ../sass/distribution/_index.scss */
/* line 425, ../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 407, ../sass/distribution/_index.scss */
/* line 432, ../sass/distribution/_index.scss */
.distribution-index .modal-form-order .actions-form button {
margin-left: 15px;
}
/* line 415, ../sass/distribution/_index.scss */
/* line 440, ../sass/distribution/_index.scss */
.distribution-index .modal-payment .info-box .info-box-icon {
width: 50px;
}
/* line 418, ../sass/distribution/_index.scss */
/* line 443, ../sass/distribution/_index.scss */
.distribution-index .modal-payment .info-box .info-box-icon i {
font-size: 30px;
}
/* line 423, ../sass/distribution/_index.scss */
/* line 448, ../sass/distribution/_index.scss */
.distribution-index .modal-payment .info-box .info-box-content {
margin-left: 50px;
}
@@ -2272,6 +2298,18 @@ termes.
margin-left: 15px;
}

/* line 4, ../sass/producer/_update.scss */
.producer-update .panel h4 {
font-size: 23px;
margin-bottom: 20px;
text-transform: uppercase;
border-bottom: solid 1px gray;
}
/* line 10, ../sass/producer/_update.scss */
.producer-update .panel h4:not(:first-child) {
margin-top: 45px;
}

/* line 4, ../sass/point_sale/_index.scss */
.point-sale-index table .td-default {
text-align: center;

+ 231
- 199
backend/web/js/backend.js Ver arquivo

@@ -1,68 +1,104 @@
/**
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.
*/

$(document).ready(function() {
opendistrib_datepicker() ;
$('button[data-toggle=popover]').popover() ;
opendistrib_commandeauto() ;
opendistrib_points_vente_acces() ;
opendistrib_tooltip() ;
opendistrib_ordre_produits() ;
opendistrib_ordre_categories() ;
opendistrib_products() ;
opendistrib_product_prices() ;
opendistrib_confirm_delete() ;
opendistrib_product_availability_points_sale();
}) ;
/**
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.
*/

$(document).ready(function () {
opendistrib_datepicker();
$('button[data-toggle=popover]').popover();
opendistrib_commandeauto();
opendistrib_points_vente_acces();
opendistrib_tooltip();
opendistrib_ordre_produits();
opendistrib_ordre_categories();
opendistrib_products();
opendistrib_product_prices();
opendistrib_product_index();
opendistrib_confirm_delete();
opendistrib_product_availability_points_sale();
opendistrib_user_index();
opendistrib_menu_treeview();
});

var UrlManager = {
getBaseUrl: function() {
return $('meta[name=baseurl]').attr('content')+'/' ;
getBaseUrl: function () {
return $('meta[name=baseurl]').attr('content') + '/';
},
getBaseUrlAbsolute: function() {
return $('meta[name=baseurl-absolute]').attr('content')+'/' ;
getBaseUrlAbsolute: function () {
return $('meta[name=baseurl-absolute]').attr('content') + '/';
}
};

function opendistrib_user_index() {
if ($('body').hasClass('user-index')) {
$('#w0-filters input:first').focus();
}
}

function opendistrib_menu_treeview() {
$('li.treeview a').unbind('click').click(function () {
var href = $(this).attr('href');
if (href != '#') {
$(location).attr('href', href);
}
});
}

function opendistrib_product_index() {
$('body.product-index .toggle input').change(function() {
var id = $(this).data('id');
var checked = $(this).prop('checked');

var active = 0;
if(checked) {
active = 1;
}

$.get(UrlManager.getBaseUrl() + 'product/ajax-toggle-active', {
id: id,
active: active
});

})
}

function opendistrib_product_availability_points_sale() {
$('input[name="Product[available_on_points_sale]"]').change(function() {
$('input[name="Product[available_on_points_sale]"]').change(function () {
var available = parseInt($(this).val());
var label = 'disponible';
if(available == 1) {
if (available == 1) {
label = 'indisponible';
}

@@ -71,24 +107,24 @@ function opendistrib_product_availability_points_sale() {
}

function opendistrib_confirm_delete() {
$('.btn-confirm-delete').click(function(event) {
if(!confirm('Souhaitez-vous vraiment supprimer cette entrée ?')) {
event.stopPropagation() ;
return false ;
$('.btn-confirm-delete').click(function (event) {
if (!confirm('Souhaitez-vous vraiment supprimer cette entrée ?')) {
event.stopPropagation();
return false;
}
}) ;
});
}

function opendistrib_products() {
if($('.product-create').size() || $('.product-update').size()) {
opendistrib_products_event_unit(false) ;
if ($('.product-create').size() || $('.product-update').size()) {
opendistrib_products_event_unit(false);

$('#product-unit').change(function() {
opendistrib_products_event_unit(true) ;
}) ;
$('#product-unit').change(function () {
opendistrib_products_event_unit(true);
});


opendistrib_products_event_price_with_tax() ;
opendistrib_products_event_price_with_tax();

$('#product-price').change(opendistrib_products_event_price_with_tax);
$('#product-price-with-tax').change(opendistrib_products_event_price);
@@ -99,21 +135,22 @@ function opendistrib_products() {

function opendistrib_products_event_price_with_tax() {
taxRateSelected = $('#product-id_tax_rate').find('option:selected').data('tax-rate-value');
if(typeof taxRateSelected == 'undefined') {
taxRateSelected = 0 ;
if (typeof taxRateSelected == 'undefined') {
taxRateSelected = 0;
}

var price = $('#product-price').val() ;
if(price) {
var price = $('#product-price').val().replace(',', '.');
if (price) {
$('#product-price-with-tax').val(getPriceWithTax(price, taxRateSelected));
// formattage
$('#product-price').val(parseFloat(price).toFixed(3));
}
}
function opendistrib_products_event_price(){

function opendistrib_products_event_price() {
taxRateSelected = $('#product-id_tax_rate').find('option:selected').data('tax-rate-value');
var priceWithTax = $('#product-price-with-tax').val() ;
if(priceWithTax) {
var priceWithTax = $('#product-price-with-tax').val();
if (priceWithTax) {
$('#product-price').val(getPrice(priceWithTax, taxRateSelected));
// formattage
$('#product-price-with-tax').val(parseFloat(priceWithTax).toFixed(2));
@@ -122,55 +159,51 @@ function opendistrib_products_event_price(){


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

var label_price_ttc = $('.field-product-price .control-label.with-tax');
var label_price_ht = $('.field-product-price .control-label.without-tax');
var label_step = $('.field-product-step .control-label');
var label_quantity_max = $('.field-product-quantity_max .control-label');

if (unit == 'piece') {
label_price_ttc.html('Prix (la pièce) TTC');
label_price_ht.html('Prix (la pièce) HT');
label_quantity_max.html('Quantité max par défaut (pièces)');
} else if (unit == 'g' || unit == 'kg') {
label_price_ttc.html('Prix (au kg) TTC');
label_price_ht.html('Prix (au kg) HT');
label_quantity_max.html('Quantité max par défaut (kg)');
label_step.html('Pas (' + unit + ')');
} else if (unit == 'mL' || unit == 'L') {
label_price_ttc.html('Prix (au litre) TTC');
label_price_ht.html('Prix (au litre) HT');
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('') ;

if (change) {
if (unit == 'piece') {
$('#product-step').val(1);
} else {
$('#product-step').val('');
}
}
}

function opendistrib_product_prices() {
if($('.product-prices-create').size() || $('.product-prices-update').size()) {
opendistrib_product_prices_event_price_with_tax() ;
if ($('.product-prices-create').size() || $('.product-prices-update').size()) {
opendistrib_product_prices_event_price_with_tax();
$('#productprice-price').change(opendistrib_product_prices_event_price_with_tax);
$('#productprice-price-with-tax').change(opendistrib_product_prices_event_price);
}
@@ -178,8 +211,8 @@ function opendistrib_product_prices() {

function opendistrib_product_prices_event_price_with_tax() {
var taxRateValue = $('#productprice-price-with-tax').data('tax-rate-value');
var price = $('#productprice-price').val() ;
if(price) {
var price = $('#productprice-price').val().replace(',', '.');
if (price) {
$('#productprice-price-with-tax').val(getPriceWithTax(price, taxRateValue));
// formattage
$('#productprice-price').val(parseFloat(price).toFixed(3));
@@ -188,8 +221,8 @@ function opendistrib_product_prices_event_price_with_tax() {

function opendistrib_product_prices_event_price() {
var taxRateValue = $('#productprice-price-with-tax').data('tax-rate-value');
var priceWithTax = $('#productprice-price-with-tax').val() ;
if(priceWithTax) {
var priceWithTax = $('#productprice-price-with-tax').val().replace(',', '.');
if (priceWithTax) {
$('#productprice-price').val(getPrice(priceWithTax, taxRateValue));
// formattage
$('#productprice-price-with-tax').val(parseFloat(priceWithTax).toFixed(2));
@@ -197,87 +230,85 @@ function opendistrib_product_prices_event_price() {
}

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

function opendistrib_nl2br(str, is_xhtml) {
var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br />' : '<br>';
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ breakTag +'$2');
function opendistrib_nl2br(str, is_xhtml) {
var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br />' : '<br>';
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + breakTag + '$2');
}

function opendistrib_points_vente_acces() {
// affichage du bloc acces restreint
$('#pointsale-restricted_access').change(function() {
opendistrib_points_vente_acces_event() ;
}) ;
opendistrib_points_vente_acces_event() ;
$('#pointsale-restricted_access').change(function () {
opendistrib_points_vente_acces_event();
});
opendistrib_points_vente_acces_event();
// affichage du champs commentaire
$('#pointsale-users input[type=checkbox]').change(function() {
opendistrib_points_vente_commentaire_event() ;
}) ;
opendistrib_points_vente_commentaire_event() ;
$('#pointsale-users input[type=checkbox]').change(function () {
opendistrib_points_vente_commentaire_event();
});
opendistrib_points_vente_commentaire_event();
}

function opendistrib_points_vente_commentaire_event() {
$('#pointsale-users input[type=checkbox]').each(function() {
if($(this).prop('checked')) {
$(this).parent().find('.commentaire').fadeIn() ;
}
else {
$(this).parent().find('.commentaire').hide() ;
$('#pointsale-users input[type=checkbox]').each(function () {
if ($(this).prop('checked')) {
$(this).parent().find('.commentaire').fadeIn();
} else {
$(this).parent().find('.commentaire').hide();
}
}) ;
});
}

function opendistrib_points_vente_acces_event() {
if($('#pointsale-restricted_access').prop('checked')) {
$('#pointsale-users').fadeIn() ;
}
else {
$('#pointsale-users').hide() ;
if ($('#pointsale-restricted_access').prop('checked')) {
$('#pointsale-users').fadeIn();
} else {
$('#pointsale-users').hide();
}
}

function opendistrib_commandeauto() {
$('#subscriptionform-date_begin, #subscriptionform-date_end').datepicker() ;
$('#subscriptionform-date_begin, #subscriptionform-date_end').datepicker();
}

function opendistrib_sortable_list(element_selector, button_selector, route_ajax) {
var fixHelper = function(e, ui) {
ui.children().each(function() {
var fixHelper = function (e, ui) {
ui.children().each(function () {
$(this).width($(this).width());
});
return ui;
};

$(element_selector+" table tbody").sortable({
$(element_selector + " table tbody").sortable({
items: "> tr",
appendTo: "parent",
cursor: "move",
placeholder: "ui-state-highlight",
handle: button_selector,
helper: fixHelper,
stop: function(event, ui) {
var tab_ordre = {} ;
var ordre = 1 ;
if($('ul.pagination').size()) {
var page = parseInt($('ul.pagination li.active a').html()) ;
var nb_items_by_page = parseInt($('#page-size').html()) ;
if(page != 1) {
ordre = (page - 1) * nb_items_by_page ;
stop: function (event, ui) {
var tab_ordre = {};
var ordre = 1;
if ($('ul.pagination').size()) {
var page = parseInt($('ul.pagination li.active a').html());
var nb_items_by_page = parseInt($('#page-size').html());
if (page != 1) {
ordre = (page - 1) * nb_items_by_page;
}
}

$(element_selector+" table tbody tr").each(function() {
tab_ordre[$(this).attr('data-key')] = ordre ;
ordre++ ;
}) ;
$(element_selector + " table tbody tr").each(function () {
tab_ordre[$(this).attr('data-key')] = ordre;
ordre++;
});

$.post(UrlManager.getBaseUrl() + route_ajax,{
$.post(UrlManager.getBaseUrl() + route_ajax, {
array: JSON.stringify(tab_ordre)
}) ;
});
}
}).disableSelection();
}
@@ -287,7 +318,7 @@ function opendistrib_ordre_categories() {
'.product-category-index',
'.btn-position',
'product-category/position'
) ;
);
}

function opendistrib_ordre_produits() {
@@ -296,7 +327,7 @@ function opendistrib_ordre_produits() {
'.product-index',
'.btn-order',
'product/order'
) ;
);

/*var fixHelper = function(e, ui) {
ui.children().each(function() {
@@ -338,45 +369,46 @@ function opendistrib_ordre_produits() {
}

function opendistrib_datepicker() {
$('input.datepicker').datepicker({dateFormat:'dd/mm/yy'}) ;
$('input.datepicker').datepicker({dateFormat: 'dd/mm/yy'});
}

/* French initialisation for the jQuery UI date picker plugin. */
/* Written by Keith Wood (kbwood{at}iinet.com.au),
Stéphane Nahmani (sholby@sholby.net),
Stéphane Raimbault <stephane.raimbault@gmail.com> */
(function( factory ) {
if ( typeof define === "function" && define.amd ) {

// AMD. Register as an anonymous module.
define([ "../jquery.ui.datepicker" ], factory );
} else {

// Browser globals
factory( jQuery.datepicker );
}
}(function( datepicker ) {
datepicker.regional['fr'] = {
closeText: 'Fermer',
prevText: 'Précédent',
nextText: 'Suivant',
currentText: 'Aujourd\'hui',
monthNames: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',
'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],
monthNamesShort: ['janv.', 'févr.', 'mars', 'avril', 'mai', 'juin',
'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],
dayNames: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
dayNamesShort: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
dayNamesMin: ['D','L','M','M','J','V','S'],
weekHeader: 'Sem.',
dateFormat: 'dd/mm/yy',
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: ''};
datepicker.setDefaults(datepicker.regional['fr']);

return datepicker.regional['fr'];
(function (factory) {
if (typeof define === "function" && define.amd) {

// AMD. Register as an anonymous module.
define(["../jquery.ui.datepicker"], factory);
} else {

// Browser globals
factory(jQuery.datepicker);
}
}(function (datepicker) {
datepicker.regional['fr'] = {
closeText: 'Fermer',
prevText: 'Précédent',
nextText: 'Suivant',
currentText: 'Aujourd\'hui',
monthNames: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',
'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],
monthNamesShort: ['janv.', 'févr.', 'mars', 'avril', 'mai', 'juin',
'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],
dayNames: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
dayNamesShort: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
dayNamesMin: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
weekHeader: 'Sem.',
dateFormat: 'dd/mm/yy',
firstDay: 1,
isRTL: false,
showMonthAfterYear: false,
yearSuffix: ''
};
datepicker.setDefaults(datepicker.regional['fr']);

return datepicker.regional['fr'];

}));


+ 208
- 108
backend/web/js/vuejs/distribution-index.js Ver arquivo

@@ -37,90 +37,95 @@ termes.

var app = new Vue({
el: '#app-distribution-index',
data: {
UrlManager: UrlManager,
baseUrl: $('meta[name=baseurl]').attr('content'),
date: null,
dateFormat: null,
loading: true,
distribution: {
active: false,
},
producer: null,
oneDistributionWeekActive: false,
products: [],
countActiveProducts: 0,
pointsSale: [],
meansPayment: [],
idActivePointSale: 0,
idDefaultPointSale: 0,
countActivePointsSale: 0,
countOrdersByPointSale: [],
orders: [],
ordersUpdate: [],
countOrders: 0,
users: [],
deliveryNotes: [],
showModalProducts: false,
showModalPointsSale: false,
showModalFormOrderCreate: false,
orderCreate: null,
showModalFormOrderUpdate: false,
idOrderUpdate: 0,
showViewProduct: false,
idOrderView: 0,
showModalPayment: false,
idOrderPayment: 0,
showLoading: false,
tillerIsSynchro: false,
checkboxSelectAllOrders: false,
messageGenerateDeliveryNoteDisplayed: false,
missingSubscriptions: false,
loadingUpdateProductOrder: false,
calendar: {
mode: 'single',
attrs: [],
themeStyles: {
wrapper: {
background: '#F7F7F7',
color: '#333',
border: 'solid 1px #e0e0e0'
},
header: {
padding: '10px 10px',
},
headerHorizontalDivider: {
borderTop: 'solid rgba(255, 255, 255, 0.2) 1px',
width: '80%',
},
weekdays: {
color: '#e0e0e0',
fontWeight: '600',
padding: '10px 10px',
fontSize: '2rem'
},
weeks: {
padding: '0 15px 15px 15px',

data() {
return Object.assign(
{
UrlManager: UrlManager,
baseUrl: $('meta[name=baseurl]').attr('content'),
date: null,
dateFormat: null,
loading: true,
distribution: {
active: false,
},
dayContent: function(object) {
var style = {
fontSize: '2rem',
padding: '16px',
};
if(object.isHovered || object.isFocus) {
style.backgroundColor = '#F39C12' ;
producer: null,
oneDistributionWeekActive: false,
products: [],
countActiveProducts: 0,
pointsSale: [],
meansPayment: [],
idActivePointSale: 0,
idDefaultPointSale: 0,
countActivePointsSale: 0,
countOrdersByPointSale: [],
orders: [],
ordersUpdate: [],
countOrders: 0,
users: [],
deliveryNotes: [],
showModalProducts: false,
showModalPointsSale: false,
showModalFormOrderCreate: false,
orderCreate: null,
showModalFormOrderUpdate: false,
idOrderUpdate: 0,
showViewProduct: false,
idOrderView: 0,
showModalPayment: false,
idOrderPayment: 0,
showLoading: false,
tillerIsSynchro: false,
checkboxSelectAllOrders: false,
messageGenerateDeliveryNoteDisplayed: false,
missingSubscriptions: false,
loadingUpdateProductOrder: false,
calendar: {
mode: 'single',
attrs: [],
themeStyles: {
wrapper: {
background: '#F7F7F7',
color: '#333',
border: 'solid 1px #e0e0e0'
},
header: {
padding: '10px 10px',
},
headerHorizontalDivider: {
borderTop: 'solid rgba(255, 255, 255, 0.2) 1px',
width: '80%',
},
weekdays: {
color: '#e0e0e0',
fontWeight: '600',
padding: '10px 10px',
fontSize: '2rem'
},
weeks: {
padding: '0 15px 15px 15px',
},
dayContent: function(object) {
var style = {
fontSize: '2rem',
padding: '16px',
};

if(object.isHovered || object.isFocus) {
style.backgroundColor = '#F39C12' ;
}

return style ;
},
},
formats: {
dayPopover: 'DD/MM/YYYY'
}
return style ;
},
},
formats: {
dayPopover: 'DD/MM/YYYY'
}
},
, window.vueValues);
},
mounted: function() {
if($('#distribution-date').size()) {
this.date = new Date($('#distribution-date').html()) ;
@@ -128,7 +133,6 @@ var app = new Vue({
+ ('0' + (this.date.getMonth() +1)).slice(-2) + '/'
+ this.date.getFullYear() ;
}
this.init() ;
this.loading = false ;
},
@@ -220,6 +224,10 @@ var app = new Vue({
appAlerts.alert('info','Pour générer un bon de livraison, sélectionnez tout d\'abord un jour et un lieu de distribution.', 6000) ;
app.messageGenerateDeliveryNoteDisplayed = true ;
}

if(app.idOrderUpdate) {
app.updateOrderFromUrl();
}
}) ;
},
initCountActiveProducts: function() {
@@ -360,6 +368,10 @@ var app = new Vue({
app.init(app.idActivePointSale) ;
}) ;
},
updateOrderFromUrl: function() {
this.initModalFormOrder() ;
this.updateProductOrderPrices(false);
},
updateOrderClick: function(event) {
var idOrder = event.currentTarget.getAttribute('data-id-order') ;
this.idOrderUpdate = idOrder ;
@@ -446,10 +458,19 @@ var app = new Vue({
var total = 0 ;
for(var i = 0; i < this.orders.length ; i++) {
if(this.orders[i].id_point_sale == this.idActivePointSale) {
total += this.orders[i].amount ;
total += parseFloat(this.orders[i].amount) ;
}
}
return total.toFixed(2);
},
weightActivePointSale: function() {
var weight = 0 ;
for(var i = 0; i < this.orders.length ; i++) {
if(this.orders[i].id_point_sale == this.idActivePointSale) {
weight += parseFloat(this.orders[i].weight) ;
}
}
return formatPrice(total) ;
return weight.toFixed(2) ;
},
changeSynchroTiller: function(event) {
var app = this ;
@@ -598,11 +619,13 @@ var app = new Vue({
.then(function (response) {
if (response.data) {
for (idProduct in response.data) {

if (app.showModalFormOrderCreate) {
Vue.set(app.orderCreate.productOrder[idProduct], 'prices', response.data[idProduct].prices);
Vue.set(app.orderCreate.productOrder[idProduct], 'active', response.data[idProduct].active);
Vue.set(app.orderCreate.productOrder[idProduct], 'unit_coefficient', response.data[idProduct].unit_coefficient);
Vue.set(app.orderCreate.productOrder[idProduct], 'price', app.getBestProductPrice(app.orderCreate, idProduct, app.orderCreate.productOrder[idProduct].quantity));
Vue.set(app.orderCreate.productOrder[idProduct], 'price', app.getBestProductPrice(app.orderCreate, idProduct, app.orderCreate.productOrder[idProduct].quantity, false));
Vue.set(app.orderCreate.productOrder[idProduct], 'price_with_tax', app.getBestProductPrice(app.orderCreate, idProduct, app.orderCreate.productOrder[idProduct].quantity, true));
}

if (app.showModalFormOrderUpdate && app.idOrderUpdate) {
@@ -611,48 +634,80 @@ var app = new Vue({
Vue.set(app.ordersUpdate[keyOrderUpdate].productOrder[idProduct], 'prices', response.data[idProduct].prices);
Vue.set(app.ordersUpdate[keyOrderUpdate].productOrder[idProduct], 'active', response.data[idProduct].active);
Vue.set(app.ordersUpdate[keyOrderUpdate].productOrder[idProduct], 'unit_coefficient', response.data[idProduct].unit_coefficient);
Vue.set(app.ordersUpdate[keyOrderUpdate].productOrder[idProduct], 'prices', response.data[idProduct].prices);

if(updatePricesOnUpdateOrder) {
console.log('new price : ');
Vue.set(
app.ordersUpdate[keyOrderUpdate].productOrder[idProduct],
'price',
app.getBestProductPrice(app.ordersUpdate[keyOrderUpdate], idProduct, app.ordersUpdate[keyOrderUpdate].productOrder[idProduct].quantity));
app.getBestProductPrice(app.ordersUpdate[keyOrderUpdate], idProduct, app.ordersUpdate[keyOrderUpdate].productOrder[idProduct].quantity, false));
Vue.set(
app.ordersUpdate[keyOrderUpdate].productOrder[idProduct],
'price_with_tax',
app.getBestProductPrice(app.ordersUpdate[keyOrderUpdate], idProduct, app.ordersUpdate[keyOrderUpdate].productOrder[idProduct].quantity, true));
}
}
}
}
}

if(updatePricesOnUpdateOrder) {
appAlerts.alert('info','Prix rechargés') ;
}
}

app.loadingUpdateProductOrder = false;
});
}
},
getBestProductPrice: function(order, idProduct, theQuantity) {
var thePriceWithTax = 9999;
getBestProductPrice: function(order, idProduct, theQuantity, withTax) {
var thePrice = 9999;
var pricesArray = order.productOrder[idProduct].prices;
var unitCoefficient = order.productOrder[idProduct].unit_coefficient;
if(theQuantity) {
theQuantity = theQuantity / unitCoefficient;
}

for(var i = 0; i < pricesArray.length ; i++) {
var priceWithTax = pricesArray[i].price_with_tax;
var fromQuantity = pricesArray[i].from_quantity;
if(pricesArray) {
for(var i = 0; i < pricesArray.length ; i++) {
var price = pricesArray[i].price;
if(withTax) {
price = pricesArray[i].price_with_tax
}

var fromQuantity = pricesArray[i].from_quantity;

if(priceWithTax < thePriceWithTax && fromQuantity <= theQuantity) {
thePriceWithTax = priceWithTax;
if(price < thePrice && fromQuantity <= theQuantity) {
thePrice = price;
}
}
}
else {
var product = this.getProduct(idProduct);
if(withTax) {
thePrice = getPriceWithTax(product.price, product.taxRate.value);
}
else {
thePrice = product.price;
}
}

if(thePriceWithTax == 9999) {
if(thePrice == 9999) {
return 0;
}
else {
return thePriceWithTax;
return thePrice;
}
},
getProduct: function(idProduct) {
for(var i= 0; i < this.products.length; i++) {
if(this.products[i].id == idProduct) {
return this.products[i];
}
}

return false;
},
},
});

@@ -661,16 +716,17 @@ Vue.component('modal', {
})

Vue.component('order-form',{
props: ['date', 'pointsSale','meansPayment', 'users', 'products', 'order', 'producer', 'loadingUpdateProductOrder'],
props: ['date', 'dateFormat', 'pointsSale', 'idActivePointSale', 'meansPayment', 'users', 'products', 'order', 'producer', 'loadingUpdateProductOrder'],
emits: ['updateProductPrice'],
data: function() {
return {
errors: [],
idPointSale: 0,
idPointSale: 0,
idUser: 0,
username : '',
comment: '',
baseUrl: $('meta[name=baseurl]').attr('content'),
vatMode: 'with_tax'
} ;
},
template: '#order-form-template',
@@ -771,21 +827,55 @@ Vue.component('order-form',{
}

Vue.set(this.order.productOrder[id_product], 'quantity', theQuantity);
Vue.set(this.order.productOrder[id_product], 'price', app.getBestProductPrice(this.order, id_product, theQuantity));
Vue.set(this.order.productOrder[id_product], 'price', app.getBestProductPrice(this.order, id_product, theQuantity, false));
Vue.set(this.order.productOrder[id_product], 'price_with_tax', app.getBestProductPrice(this.order, id_product, theQuantity, true));
}
},
productPriceChange: function(event) {
var idProduct = event.currentTarget.getAttribute('data-id-product') ;
var price = parseFloat(event.currentTarget.value) ;
if(isNaN(price)) {
price = 0 ;
getProduct: function(idProduct) {
for(var i= 0; i < this.products.length; i++) {
if(this.products[i].id == idProduct) {
return this.products[i];
}
}

Vue.set(this.order.productOrder, idProduct, {
quantity: this.order.productOrder[idProduct].quantity,
unit: this.order.productOrder[idProduct].unit,
price: price
});
return false;
},
productPriceChange: function(event) {
var idProduct = event.currentTarget.getAttribute('data-id-product');
var product = this.getProduct(idProduct);

if(product) {
var taxRateValue = parseFloat(product.taxRate.value);
var withTax = event.currentTarget.getAttribute('data-with-tax');
var price = 0;
var priceWithTax = 0;
var priceValue = parseFloat(event.currentTarget.value.replace(',', '.'));

if(withTax) {
priceWithTax = priceValue.toFixed(2);
price = getPrice(priceWithTax, taxRateValue);
}
else {
price = priceValue.toFixed(3);
priceWithTax = getPriceWithTax(price, taxRateValue);
}

if(isNaN(price)) {
price = 0 ;
}

if(isNaN(priceWithTax)) {
priceWithTax = 0 ;
}

Vue.set(this.order.productOrder, idProduct, {
active: this.order.productOrder[idProduct].active,
quantity: this.order.productOrder[idProduct].quantity,
unit: this.order.productOrder[idProduct].unit,
price: price,
price_with_tax: priceWithTax
});
}
},
userChange: function(event) {
var app = this ;
@@ -793,7 +883,9 @@ Vue.component('order-form',{
idUser: app.order.id_user,
}})
.then(function(response) {
app.order.id_point_sale = response.data.id_favorite_point_sale ;
if(app.idActivePointSale == 0) {
app.order.id_point_sale = response.data.id_favorite_point_sale ;
}
app.updateProductOrderPrices(true) ;
}) ;
},
@@ -802,6 +894,14 @@ Vue.component('order-form',{
},
updateProductOrderPrices: function(updateProductOrderPrices) {
this.$emit('updateproductorderprices', updateProductOrderPrices) ;
},
toggleVatMode: function() {
if(this.vatMode == 'all') {
this.vatMode = 'with_tax';
}
else {
this.vatMode = 'all';
}
}
}
}) ;

+ 11
- 11
backend/web/js/vuejs/producer-update.js Ver arquivo

@@ -47,13 +47,18 @@ var app = new Vue({
isAdminSection: 0
},
{
name: 'tableau-bord',
nameDisplay: 'Tableau de bord',
name: 'prise-commande',
nameDisplay: 'Commandes',
isAdminSection: 0
},
{
name: 'prise-commande',
nameDisplay: 'Prise de commande',
name: 'credit-payment',
nameDisplay: 'Crédit',
isAdminSection: 0
},
{
name: 'facturation',
nameDisplay: 'Facturation',
isAdminSection: 0
},
{
@@ -62,8 +67,8 @@ var app = new Vue({
isAdminSection: 0
},
{
name: 'credit-payment',
nameDisplay: 'Crédit',
name: 'tableau-bord',
nameDisplay: 'Tableau de bord',
isAdminSection: 0
},
{
@@ -76,11 +81,6 @@ var app = new Vue({
nameDisplay: 'Logiciels de caisse',
isAdminSection: 0
},
{
name: 'facturation',
nameDisplay: 'Facturation',
isAdminSection: 0
},
{
name: 'administration',
nameDisplay: 'Administration',

+ 1
- 0
backend/web/sass/_alerts.scss Ver arquivo

@@ -5,6 +5,7 @@
right: 15px ;
width: 300px ;
height: auto ;
z-index: 9999;

.slide-fade-enter-active {
@include transition(all .3s ease);

+ 392
- 367
backend/web/sass/distribution/_index.scss Ver arquivo

@@ -36,394 +36,419 @@ termes.

.distribution-index {

.content-header {
.date {
font-weight: bold;
}
.content-header {
.date {
font-weight: bold;
}
}

#wrapper-app-distribution-index {
display: none;

&.loaded {
display: block;
}
}

#app-distribution-index {
position: relative;
}

#distribution-date {
display: none;
}

#calendar {
margin-bottom: 15px;

.c-header .c-title-layout .c-title-popover .c-title-anchor .c-title[data-v-2083cb72] {
font-size: 2rem;
}

.c-day-background {
//background-color: #F39C12;
//background-color: white ;
padding: 16px;
//border: solid 1px black !important ;
//opacity: 1 ;
}

.c-day-popover-content {
font-size: 1.3rem;
}

.c-title {
text-transform: uppercase;
font-family: 'capsuularegular';
}
}

#products {
td.quantities {
width: 100px;
text-align: right;
}

input.quantity-max {
width: 50px;
text-align: center;
display: inline;
}
}

#infos-top {
.col-md-4 {
padding: 0px;
}

$height-info-box: 96px ;

.info-box {
min-height: $height-info-box;
height: $height-info-box;

.info-box-icon {
height: $height-info-box;
width: 50px;
line-height: $height-info-box;

i.fa {
font-size: 30px;
}
}

#wrapper-app-distribution-index {
display: none;
.info-box-content {
margin-left: 55px;

.info-box-text {
font-size: 12px;

.btn {
font-size: 12px;
text-transform: uppercase;
}
}

.info-box-number {
font-size: 14px;
}
}
}

#info-box-distribution {
.btn-active-week {
float: right;
}
}

#summary-ca-weight {
.normal {
font-weight: normal;
}
}
}

#modal-products {
table.table {
thead {
tr {
td {
font-weight: bold;
}
}
}

td.quantity-ordered,
td.quantity-max {
text-align: center;
}

td.quantity-ordered {
width: 50px;
}

td.quantity-max {
width: 120px;

input {
text-align: center;
min-width: 50px;
}
}
}
}

#orders {
position: relative;

.panel-heading {
.buttons {
.btn {
position: relative;
top: -19px;
float: right;
margin-left: 10px;
}
}
}

#wrapper-nav-points-sale {
margin-bottom: 10px;

ul#nav-points-sale {
margin: 0px;
padding: 0px;
list-style-type: none;

li {
float: left;
margin-right: 10px;
margin-bottom: 10px;

a {
.label {
background-color: white;
border: solid 1px #e0e0e0;
@include border-radius(10px) ;
}
}

&.loaded {
display: block;
}
}
}
}

#buttons-top-orders {
background-color: #F5F5F5;
padding: 10px 20px;
border: solid 1px #e0e0e0;
@include border-radius(5px) ;
margin-bottom: 20px;

.left {

}

.right {
float: right;
}

.dropdown {
display: inline-block;
}
}

.point-sale-totals {
background-color: white;
padding: 10px 20px;
border: solid 1px #e0e0e0;
@include border-radius(5px) ;
margin-bottom: 20px;

.title {
color: gray;
font-size: 13px;
margin-right: 13px;
text-transform: uppercase;
}
}

table {

td.tiller {
width: 60px;

label {
font-size: 12px;
cursor: pointer;
position: relative;
top: -2px;
font-weight: normal;
}
}

td.column-actions {
position: relative;
text-align: right;

#app-distribution-index {
position: relative;
.dropdown-menu {
top: 0px;
right: 0px;
}

#distribution-date {
display: none;
.modal-form-order,
.modal-payment {
text-align: left;
}

#calendar {
margin-bottom: 15px;

.c-header .c-title-layout .c-title-popover .c-title-anchor .c-title[data-v-2083cb72] {
font-size: 2rem;
}

.c-day-background {
//background-color: #F39C12;
//background-color: white ;
padding: 16px;
//border: solid 1px black !important ;
//opacity: 1 ;
}

.c-day-popover-content {
font-size: 1.3rem;
}

.c-title {
text-transform: uppercase;
font-family: 'capsuularegular';
}
.add-subscription {
position: relative;

.glyphicon-plus {
position: absolute;
top: 4px;
right: 4px;
font-size: 7px;
}
}
}

td.column-state-payment {
width: 120px;
}

#products {
td.quantities {
width: 100px;
text-align: right;
}

input.quantity-max {
width: 50px;
text-align: center;
display: inline;
}
td.column-payment {
div.btn-group {
width: 125px;
}
}

tr.view {
ul {
list-style-type: none;
margin-left: 0px;
padding-left: 15px;

#infos-top {
.col-md-4 {
padding: 0px;
}

$height-info-box: 96px ;

.info-box {
min-height: $height-info-box;
height: $height-info-box;

.info-box-icon {
height: $height-info-box;
width: 50px;
line-height: $height-info-box;

i.fa {
font-size: 30px;
}
}

.info-box-content {
margin-left: 55px;

.info-box-text {
font-size: 12px;

.btn {
font-size: 12px;
text-transform: uppercase;
}
}

.info-box-number {
font-size: 14px;
}
}
}

#info-box-distribution {
.btn-active-week {
float: right;
}
}

#summary-ca-weight {
.normal {
font-weight: normal;
}
}
li {

}
}

#modal-products {
table.table {
thead {
tr {
td {
font-weight: bold;
}
}
}

td.quantity-ordered,
td.quantity-max {
text-align: center;
}

td.quantity-ordered {
width: 50px;
}

td.quantity-max {
width: 120px;

input {
text-align: center;
min-width: 50px;
}
}
}
.comment {
margin-top: 20px;
}

#orders {
position: relative;

.panel-heading {
.buttons {
.btn {
position: relative;
top: -19px;
float: right;
margin-left: 10px;
}
}
}

#wrapper-nav-points-sale {
margin-bottom: 10px;

ul#nav-points-sale {
margin: 0px;
padding: 0px;
list-style-type: none;

li {
float: left;
margin-right: 10px;
margin-bottom: 10px;

a {
.label {
background-color: white;
border: solid 1px #e0e0e0;
@include border-radius(10px) ;
}
}

}
}
}

#buttons-top-orders {
background-color: #F5F5F5;
padding: 10px 20px;
border: solid 1px #e0e0e0;
@include border-radius(5px) ;
margin-bottom: 20px;

.left {

}

.right {
float: right;
}

.dropdown {
display: inline-block ;
}
}

table {

td.tiller {
width: 60px;

label {
font-size: 12px;
cursor: pointer;
position: relative;
top: -2px;
font-weight: normal;
}
}

td.column-actions {
position: relative;
text-align: right;

.dropdown-menu {
top: 0px;
right: 0px;
}

.modal-form-order,
.modal-payment {
text-align: left;
}

.add-subscription {
position: relative;

.glyphicon-plus {
position: absolute;
top: 4px;
right: 4px;
font-size: 7px;
}
}
}

td.column-state-payment {
width: 120px;
}

td.column-payment {
div.btn-group {
width: 125px;
}
}

tr.view {
ul {
list-style-type: none;
margin-left: 0px;
padding-left: 15px;

li {

}
}

.comment {
margin-top: 20px;
}

.delivery {
margin-top: 20px;
}
}
}
.delivery {
margin-top: 20px;
}
}
}
}

.modal-form-order {

.modal-container {
width: 100%;
padding: 0px;

.modal-form-order {

.modal-container {
width: 100%;
padding: 0px;

.modal-body {
padding-right: 15px;
table {
margin-bottom: 150px;
}
}

.modal-footer {
border-top-color: #f4f4f4;
position: fixed;
bottom: 0;
right: 0;
z-index: 99999;
background-color: white;
width: 100%;
text-align: center;
border-top: solid 1px #e0e0e0;

.actions-form {
button {
float: none;
}
}
}
}

.btn-credit {
float: right;
}

table.table-products {
.product-ordered {
td {
background-color: #e9e9e9;
}

input {
font-size: 16px;
font-weight: bold;
}
}

td.price {
width: 150px ;
input {
text-align: center ;
}
}

td.quantity {
width: 165px;

input {
text-align: center;
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 {
text-align: right;

&.quantity-remaining, &.infinite {
color: #00A65A;
}

&.negative {
color: #DD4B39;
}

&.infinite, &.empty {
font-size: 18px;
}
}
}

.actions-form {
button {
margin-left: 15px;
}
}
.modal-body {
padding-right: 15px;

table {
margin-bottom: 150px;
}
}

.modal-footer {
border-top-color: #f4f4f4;
position: fixed;
bottom: 0;
right: 0;
z-index: 99999;
background-color: white;
width: 100%;
text-align: center;
border-top: solid 1px #e0e0e0;

.actions-form {
button {
float: none;
}

div.right {
float: right;
}
}
}
}

.modal-payment {
.info-box {
.info-box-icon {
width: 50px;

i {
font-size: 30px;
}
}

.info-box-content {
margin-left: 50px;
}
}
.btn-credit {
float: right;
}

table.table-products {
.product-ordered {
td {
background-color: #e9e9e9;
}

input.input-quantity {
font-size: 16px;
font-weight: bold;
}
}

td.price {
width: 150px;

input {
text-align: center;
}

.input-group-addon {
background-color: #eee;
}
}

td.quantity {
width: 165px;

input {
text-align: center;
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 {
text-align: right;

&.quantity-remaining, &.infinite {
color: #00A65A;
}

&.negative {
color: #DD4B39;
}

&.infinite, &.empty {
font-size: 18px;
}
}
}

.actions-form {
button {
margin-left: 15px;
}
}
}

.modal-payment {
.info-box {
.info-box-icon {
width: 50px;

i {
font-size: 30px;
}
}

.info-box-content {
margin-left: 50px;
}
}
}
}


+ 11
- 1
backend/web/sass/producer/_update.scss Ver arquivo

@@ -1,4 +1,14 @@

.producer-update {
.panel {
h4 {
font-size: 23px;
margin-bottom: 20px;
text-transform: uppercase;
border-bottom: solid 1px gray;
}
h4:not(:first-child) {
margin-top: 45px;
}
}
}

+ 162
- 141
common/components/ActiveRecordCommon.php Ver arquivo

@@ -42,168 +42,189 @@ use common\helpers\GlobalParam;

class ActiveRecordCommon extends \yii\db\ActiveRecord
{
const SEARCH_ALL = 'all';
const SEARCH_ONE = 'one';
const SEARCH_COUNT = 'count';

/**
* Méthode générique de recherche utilisée pour tous les modèles. Elle a
* pour but de construire la requête et de retourner le résultat.
*
* @param array $params
* @param array $options
* @return mixed
* @throws NotFoundHttpException
*/
public static function searchBy($params = [], $options = [])
{
$class = get_called_class();

if (is_callable([$class, 'defaultOptionsSearch'])) {
$default_options = $class::defaultOptionsSearch();
} else {
throw new \ErrorException('La méthode "defaultOptionsSearch" n\'est '
. 'pas définie dans la classe "' . $class . '"');
}

$options = array_merge($default_options, $options);

$pk = $class::primaryKey();
$pk = $class::tableName() . '.' . $pk[0];
const SEARCH_QUERY = 'query';
const SEARCH_ALL = 'all';
const SEARCH_ONE = 'one';
const SEARCH_COUNT = 'count';

/**
* Méthode générique de recherche utilisée pour tous les modèles. Elle a
* pour but de construire la requête et de retourner le résultat.
*
* @param array $params
* @param array $options
* @return mixed
* @throws NotFoundHttpException
*/
public static function searchBy($params = [], $options = [])
{
$class = get_called_class();

if (is_callable([$class, 'defaultOptionsSearch'])) {
$default_options = $class::defaultOptionsSearch();
} else {
throw new \ErrorException('La méthode "defaultOptionsSearch" n\'est '
. 'pas définie dans la classe "' . $class . '"');
}

if (isset($options['attribute_id_producer']) && strlen($options['attribute_id_producer'])
&& !isset($params[$options['attribute_id_producer']]) && !Yii::$app->user->isGuest) {
$params[$options['attribute_id_producer']] = GlobalParam::getCurrentProducerId();
}
$options = array_merge($default_options, $options);

if (!isset($options['type_search'])) {
$options['type_search'] = self::SEARCH_ALL;
}
$pk = $class::primaryKey();
$pk = $class::tableName() . '.' . $pk[0];

$records = $class::find();
if (isset($options['attribute_id_producer']) && strlen($options['attribute_id_producer'])
&& !isset($params[$options['attribute_id_producer']]) && !Yii::$app->user->isGuest) {
$params[$options['attribute_id_producer']] = GlobalParam::getCurrentProducerId();
}

// With
if (is_array($options['with']) && count($options['with'])) {
$records = $records->with($options['with']);
}
if (!isset($options['type_search'])) {
$options['type_search'] = self::SEARCH_ALL;
}

// Join with
if (is_array($options['join_with']) && count($options['join_with'])) {
$records = $records->joinWith($options['join_with']);
}
$records = $class::find();

// Conditions
if (isset($options['conditions'])) {
if (is_array($options['conditions'])) {
if (count($options['conditions'])) {
foreach ($options['conditions'] as $condition) {
$records = $records->andWhere($condition);
}
}
} else {
if (strlen($options['conditions'])) {
$records = $records->andWhere($options['conditions']);
}
}
}
// With
if (is_array($options['with']) && count($options['with'])) {
$records = $records->with($options['with']);
}

// Params
if (isset($options['params']) && is_array($options['params']) && count($options['params'])) {
$records = $records->params($options['params']);
}
// Join with
if (is_array($options['join_with']) && count($options['join_with'])) {
$records = $records->joinWith($options['join_with']);
}

// Paramètres
if (is_array($params) && count($params)) {
foreach ($params as $key => $val) {
if (strpos($key, '.') === false) {
unset($params[$key]);
$key = $class::tableName() . '.' . $key;
$params[$key] = $val;
}
$records = $records->andWhere([$key => $val]);
}
// Conditions
if (isset($options['conditions'])) {
if (is_array($options['conditions'])) {
if (count($options['conditions'])) {
foreach ($options['conditions'] as $condition) {
$records = $records->andWhere($condition);
}
}

if (!isset($params[$pk])) {
// Orderby
if (isset($options['orderby']) && strlen($options['orderby'])) {
$records = $records->orderBy($options['orderby']);
}
// Limit
if (isset($options['limit']) && is_numeric($options['limit'])
&& $options['limit'] > 0) {
$records = $records->limit($options['limit']);
}
} else {
if (strlen($options['conditions'])) {
$records = $records->andWhere($options['conditions']);
}
}
}

if (isset($options['as_array'])) {
$records = $records->asArray();
}
// Params
if (isset($options['params']) && is_array($options['params']) && count($options['params'])) {
$records = $records->params($options['params']);
}

if ($options['type_search'] == self::SEARCH_ALL) {
return $records->all();
} elseif ($options['type_search'] == self::SEARCH_ONE) {
$record = $records->one();
if ($record) {
return $record;
}
} elseif ($options['type_search'] == self::SEARCH_COUNT) {
return $records->count();
// Paramètres
if (is_array($params) && count($params)) {
foreach ($params as $key => $val) {
if (strpos($key, '.') === false) {
unset($params[$key]);
$key = $class::tableName() . '.' . $key;
$params[$key] = $val;
}
return false;
$records = $records->andWhere([$key => $val]);
}
}

/**
* Recherche un enregistrement.
*
* @param array $params
* @param array $options
* @return mixed
*/
public static function searchOne($params = [], $options = [])
{
$options['type_search'] = self::SEARCH_ONE;
return self::searchDispatch($params, $options);
if (!isset($params[$pk])) {
// Orderby
if (isset($options['orderby']) && strlen($options['orderby'])) {
$records = $records->orderBy($options['orderby']);
}
// Limit
if (isset($options['limit']) && is_numeric($options['limit'])
&& $options['limit'] > 0) {
$records = $records->limit($options['limit']);
}
}

/**
* Recherche tous les enregistrements.
*
* @param array $params
* @param array $options
* @return mixed
*/
public static function searchAll($params = [], $options = [])
{
$options['type_search'] = self::SEARCH_ALL;
return self::searchDispatch($params, $options);
if (isset($options['groupby'])) {
$records = $records->groupBy($options['groupby']);
}

/**
* Recherche et compte le nombre de résultats.
*
* @param array $params
* @param array $options
* @return integer
*/
public static function searchCount($params = [], $options = [])
{
$options['type_search'] = self::SEARCH_COUNT;
return self::searchDispatch($params, $options);
if (isset($options['as_array'])) {
$records = $records->asArray();
}

/**
* Appelle la méthode 'search' de la classe appellante.
*
* @param array $params
* @param array $options
* @return mixed
*/
public static function searchDispatch($params = [], $options = [])
{
$class = get_called_class();
return $class::searchBy($params, $options);
if ($options['type_search'] == self::SEARCH_QUERY) {
self::groupByPrimaryKey($class, $records);
return $records;
} elseif ($options['type_search'] == self::SEARCH_ALL) {
return $records->all();
} elseif ($options['type_search'] == self::SEARCH_ONE) {
$record = $records->one();
if ($record) {
return $record;
}
} elseif ($options['type_search'] == self::SEARCH_COUNT) {
self::groupByPrimaryKey($class, $records);
return $records->count();
}

return false;
}

public static function groupByPrimaryKey($class, $records)
{
$primaryKey = static::primaryKey();
$records = $records->groupBy($class::tableName() . '.' . $primaryKey[0]);
}

public static function searchQuery($params = [], $options = [])
{
$options['type_search'] = self::SEARCH_QUERY;
return self::searchDispatch($params, $options);
}

/**
* Recherche un enregistrement.
*
* @param array $params
* @param array $options
* @return mixed
*/
public static function searchOne($params = [], $options = [])
{
$options['type_search'] = self::SEARCH_ONE;
return self::searchDispatch($params, $options);
}

/**
* Recherche tous les enregistrements.
*
* @param array $params
* @param array $options
* @return mixed
*/
public static function searchAll($params = [], $options = [])
{
$options['type_search'] = self::SEARCH_ALL;
return self::searchDispatch($params, $options);
}

/**
* Recherche et compte le nombre de résultats.
*
* @param array $params
* @param array $options
* @return integer
*/
public static function searchCount($params = [], $options = [])
{
$options['type_search'] = self::SEARCH_COUNT;
return self::searchDispatch($params, $options);
}

/**
* Appelle la méthode 'search' de la classe appellante.
*
* @param array $params
* @param array $options
* @return mixed
*/
public static function searchDispatch($params = [], $options = [])
{
$class = get_called_class();
return $class::searchBy($params, $options);
}

}

+ 1
- 0
common/config/params.php Ver arquivo

@@ -37,6 +37,7 @@
*/

return [
'version' => '22.10.A',
'adminEmail' => 'contact@opendistrib.net',
'supportEmail' => 'contact@opendistrib.net',
'user.passwordResetTokenExpire' => 3600,

+ 33
- 32
common/helpers/GlobalParam.php Ver arquivo

@@ -42,43 +42,44 @@ use common\models\Producer;

class GlobalParam
{
public static function get($key)
{
if ($key == 'producer') {
return $this->getCurrentProducer();
} else {

public function get($key)
{
if($key == 'producer'){
return $this->getCurrentProducer();
}else{

return \Yii::$app->params[$key];
}
}

public static function getCurrentProducer()
{
if(\Yii::$app->params['producer'] == false){
\Yii::$app->params['producer'] = Producer::searchOne();
}
return \Yii::$app->params['producer'];
return \Yii::$app->params[$key];
}
}

/**
* Retourne l'ID du producteur courant.
*
* @return integer|boolean
*/
public static function getCurrentProducerId()
{
if(\Yii::$app->controller->module->id == 'app-backend') {
if(!\Yii::$app->user->isGuest) {
return Yii::$app->user->identity->id_producer ;
}
}
else {
return \Yii::$app->controller->getProducer()->id;
}
public static function getOpendistribVersion()
{
return self::get('version');
}

return false ;
public static function getCurrentProducer()
{
if (\Yii::$app->params['producer'] == false) {
\Yii::$app->params['producer'] = Producer::searchOne();
}
return \Yii::$app->params['producer'];
}

/**
* Retourne l'ID du producteur courant.
*
* @return integer|boolean
*/
public static function getCurrentProducerId()
{
if (\Yii::$app->controller->module->id == 'app-backend') {
if (!\Yii::$app->user->isGuest) {
return Yii::$app->user->identity->id_producer;
}
} else {
return \Yii::$app->controller->getProducer()->id;
}

return false;
}
}

+ 2
- 1
common/models/CreditHistory.php Ver arquivo

@@ -85,7 +85,8 @@ class CreditHistory extends ActiveRecordCommon
[['id_user', 'id_user_action', 'id_order', 'id_producer'], 'integer'],
[['date'], 'safe'],
[['amount'], 'double'],
[['type', 'mean_payment', 'comment'], 'string', 'max' => 255],
[['type', 'mean_payment'], 'string', 'max' => 255],
[['comment'], 'string', 'max' => 2048],
];
}


+ 18
- 0
common/models/Distribution.php Ver arquivo

@@ -336,6 +336,24 @@ class Distribution extends ActiveRecordCommon
foreach($order->productOrder as $productOrder) {
if($productOrder->id_product == $product->id) {
$productOrder->price = $product->price ;

$user = false;
$userProducer = false;
if(isset($order->user) && $order->user) {
$user = $order->user;
$userProducer = UserProducer::searchOne([
'id_user' => $user->id,
'id_producer' => $order->distribution->id_producer
]) ;
}

$productOrder->price = $product->getPrice([
'user' => $user,
'user_producer' => $userProducer,
'point_sale' => $order->pointSale,
'quantity' => $productOrder->quantity
]);

$productOrder->save() ;
}
}

+ 8
- 8
common/models/Document.php Ver arquivo

@@ -67,6 +67,7 @@ class Document extends ActiveRecordCommon
[['date'], 'safe'],
[['comment', 'address', 'tax_calculation_method'], 'string'],
[['id_user', 'id_producer'], 'integer'],
['is_sent', 'boolean'],
[['name', 'reference', 'status'], 'string', 'max' => 255],
[['deliveryNotes'], 'safe']
];
@@ -87,7 +88,8 @@ class Document extends ActiveRecordCommon
'address' => 'Adresse',
'id_producer' => 'Producteur',
'status' => 'Statut',
'tax_calculation_method' => 'Méthode de calcul de la TVA'
'tax_calculation_method' => 'Méthode de calcul de la TVA',
'is_sent' => 'Envoyé'
];
}

@@ -285,10 +287,9 @@ class Document extends ActiveRecordCommon
{
$filenameComplete = $this->getFilenameComplete();

/*if (!file_exists($filenameComplete)) {
if (!file_exists($filenameComplete)) {
$this->generatePdf(Pdf::DEST_FILE);
}*/
$this->generatePdf(Pdf::DEST_FILE);
}

if (file_exists($filenameComplete)) {
return Yii::$app->response->sendFile($filenameComplete, $this->getFilename(), ['inline' => true]);
@@ -299,7 +300,7 @@ class Document extends ActiveRecordCommon

public function generatePdf($destination)
{
$producer = GlobalParam::getCurrentProducer();
$producer = $this->producer;
$content = Yii::$app->controller->renderPartial('/document/download', [
'producer' => $producer,
'document' => $this
@@ -433,9 +434,8 @@ class Document extends ActiveRecordCommon
$productOrderMatch = false;
foreach ($productsOrdersArray[$indexProductOrder] as &$theProductOrder) {
if ($theProductOrder->unit == $productOrder->unit
&& ((!$this->isInvoicePrice() && $theProductOrder->price == $productOrder->price)
|| ($this->isInvoicePrice() && $theProductOrder->invoice_price == $productOrder->invoice_price)
)) {
&& $theProductOrder->price == $productOrder->price
&& $theProductOrder->invoice_price == $productOrder->invoice_price) {

$theProductOrder->quantity += $productOrder->quantity;
$productOrderMatch = true;

+ 42
- 34
common/models/InvoiceSearch.php Ver arquivo

@@ -44,44 +44,52 @@ use common\models\Invoice;
class InvoiceSearch extends Invoice
{

public function rules()
{
return [
[[], 'safe'],
[['comment', 'address', 'status'], 'string'],
[['id_user'], 'integer'],
[['name', 'reference'], 'string', 'max' => 255],
];
}

public function search($params)
{
$optionsSearch = self::defaultOptionsSearch();
var $username;

$query = Invoice::find()
->with($optionsSearch['with'])
->joinWith($optionsSearch['join_with'])
->where(['invoice.id_producer' => GlobalParam::getCurrentProducerId()])
->orderBy('invoice.status ASC, invoice.reference DESC')
->groupBy('invoice.id');
public function rules()
{
return [
[[], 'safe'],
[['comment', 'address', 'status', 'username'], 'string'],
[['name', 'reference'], 'string', 'max' => 255],
];
}

$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => ['attributes' => ['name', 'reference', 'date']],
'pagination' => [
'pageSize' => 20,
],
]);
public function search($params)
{
$optionsSearch = self::defaultOptionsSearch();

$this->load($params);
if (!$this->validate()) {
return $dataProvider;
}
$query = Invoice::find()
->with($optionsSearch['with'])
->joinWith($optionsSearch['join_with'])
->where(['invoice.id_producer' => GlobalParam::getCurrentProducerId()])
->orderBy('invoice.status ASC, invoice.reference DESC')
->groupBy('invoice.id');

$query->andFilterWhere(['like', 'invoice.name', $this->name]);
$query->andFilterWhere(['like', 'invoice.reference', $this->reference]);
$query->andFilterWhere(['like', 'invoice.status', $this->status]);
$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => ['attributes' => ['name', 'reference', 'date']],
'pagination' => [
'pageSize' => 20,
],
]);

return $dataProvider;
$this->load($params);
if (!$this->validate()) {
return $dataProvider;
}

$query->andFilterWhere(['like', 'invoice.name', $this->name]);
$query->andFilterWhere(['like', 'invoice.reference', $this->reference]);
$query->andFilterWhere(['like', 'invoice.status', $this->status]);

$query->andFilterWhere([
'or',
['like', 'user.lastname', $this->username],
['like', 'user.name', $this->username],
['like', 'user.name_legal_person', $this->username],
]);

return $dataProvider;
}
}

+ 7
- 1
common/models/LoginForm.php Ver arquivo

@@ -121,7 +121,13 @@ class LoginForm extends Model
public function getUser()
{
if ($this->_user === false) {
$this->_user = User::findOne(['email' => $this->email, 'type' => User::TYPE_INDIVIDUAL]);
$this->_user = User::searchOne(
['email' => $this->email],
[
'conditions' => 'type LIKE :type_individual OR type LIKE :type_legal_person',
'params' => [':type_individual' => User::TYPE_INDIVIDUAL, ':type_legal_person' => User::TYPE_LEGAL_PERSON]
]
);
}

return $this->_user;

+ 3
- 1
common/models/Order.php Ver arquivo

@@ -552,7 +552,7 @@ class Order extends ActiveRecordCommon
public function saveCreditHistory($type, $amount, $idProducer, $idUser, $idUserAction)
{
$creditHistory = new CreditHistory;
$creditHistory->id_user = $this->id_user;
$creditHistory->id_user = $idUser;
$creditHistory->id_order = $this->id;
$creditHistory->amount = $amount;
$creditHistory->type = $type;
@@ -573,6 +573,8 @@ class Order extends ActiveRecordCommon
if ($this->id_user) {
$paymentStatus = $this->getPaymentStatus();

echo $paymentStatus;

if ($paymentStatus == self::PAYMENT_PAID) {
return true;
} elseif ($paymentStatus == self::PAYMENT_SURPLUS) {

+ 74
- 0
common/models/OrderSearch.php Ver arquivo

@@ -0,0 +1,74 @@
<?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 common\models ;

use common\helpers\GlobalParam;
use common\models\Product ;

class OrderSearch extends Order
{
public function search($params)
{
$optionsSearch = self::defaultOptionsSearch() ;

$paramsSearch = [];
if(isset($params['id_user'])) {
$paramsSearch['id_user'] = $params['id_user'];
}

$query = Order::searchQuery($paramsSearch, [
'orderby' => 'distribution.date DESC'
]);
$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => ['attributes' => []],
'pagination' => [
'pageSize' => 20,
],
]);
$this->load($params);
if (!$this->validate()) {
return $dataProvider;
}

return $dataProvider;
}
}

+ 5
- 3
common/models/Producer.php Ver arquivo

@@ -224,7 +224,8 @@ class Producer extends ActiveRecordCommon
'option_stripe_private_key',
'option_stripe_endpoint_secret',
'option_online_payment_type',
'option_tax_calculation_method'
'option_tax_calculation_method',
'latest_version_opendistrib'
],
'string'
],
@@ -308,7 +309,7 @@ class Producer extends ActiveRecordCommon
'description' => 'Description',
'postcode' => 'Code postal',
'city' => 'Ville',
'code' => 'Code',
'code' => "Code d'accès",
'order_delay' => 'Délai de commande',
'order_deadline' => 'Heure limite de commande',
'order_delay_monday' => 'Délai de commande (lundi)',
@@ -390,7 +391,8 @@ class Producer extends ActiveRecordCommon
'option_billing_frequency' => 'Fréquence de facturation',
'option_billing_reduction' => 'Réduction appliquée au moment de la facturation',
'option_tax_calculation_method' => 'Méthode de calcul de la TVA',
'option_export_evoliz' => 'Activer l\'export vers Evoliz'
'option_export_evoliz' => 'Activer l\'export vers Evoliz',
'latest_version_opendistrib' => 'Dernière version d\'Opendistrib',
];
}


+ 17
- 0
common/models/ProductPrice.php Ver arquivo

@@ -202,9 +202,11 @@ class ProductPrice extends ActiveRecordCommon
if(self::hasMatchOfType($specificPriceArray, 'matchUser', $user, $pointSale)) {
return 'matchUser';
}

if(self::hasMatchOfType($specificPriceArray, 'matchUserGroup', $user, $pointSale)) {
return 'matchUserGroup';
}

if(self::hasMatchOfType($specificPriceArray, 'matchPointSale', $user, $pointSale)) {
return 'matchPointSale';
}
@@ -213,6 +215,10 @@ class ProductPrice extends ActiveRecordCommon
return 'matchUserPointSale';
}

if(self::hasMatchOfType($specificPriceArray, 'matchUserGroupPointSale', $user, $pointSale)) {
return 'matchUserGroupPointSale';
}

return null;
}

@@ -252,6 +258,17 @@ class ProductPrice extends ActiveRecordCommon
&& $this->id_user == $user->id;
}

public function matchUserGroupPointSale($user, $pointSale)
{
return $user
&& $pointSale
&& $this->id_user_group
&& $this->id_point_sale
&& !$this->id_user
&& $user->belongsToUserGroup($this->id_user_group)
&& $this->id_point_sale == $pointSale->id;
}

public function matchFromQuantityOnly()
{
return !$this->id_user

+ 384
- 378
common/models/Subscription.php Ver arquivo

@@ -71,428 +71,434 @@ use common\models\ProductOrder;
*/
class Subscription extends ActiveRecordCommon
{
/**
* @inheritdoc
*/
public static function tableName()
{
return 'subscription';
}

/**
* @inheritdoc
*/
public function rules()
{
return [
[['id_producer', 'id_point_sale'], 'required'],
[['id_user', 'id_producer', 'id_point_sale', 'monday', 'tuesday',
'wednesday', 'thursday', 'friday', 'saturday', 'sunday', 'week_frequency'], 'integer'],
[['auto_payment'], 'boolean'],
[['date_begin', 'date_end', 'username', 'comment'], 'safe'],
];
}

/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'id_user' => 'Utilisateur',
'id_producer' => 'Etablissement',
'id_point_sale' => 'Point de vente',
'date_begin' => 'Date de début',
'date_end' => 'Date de fin',
'monday' => 'Lundi',
'tuesday' => 'Mardi',
'wednesday' => 'Mercredi',
'thursday' => 'Jeudi',
'friday' => 'Vendredi',
'saturday' => 'Samedi',
'sunday' => 'Dimanche',
'week_frequency' => 'Périodicité',
'auto_payment' => 'Paiement automatique',
'comment' => 'Commentaire'
];
}

/*
* Relations
*/

public function getUser()
{
return $this->hasOne(User::className(), ['id' => 'id_user']);
}

public function getProducer()
{
return $this->hasOne(
Producer::className(),
['id' => 'id_producer']
);
}

public function getPointSale()
{
return $this->hasOne(
PointSale::className(),
['id' => 'id_point_sale']
);
}

public function getProductSubscription()
{
return $this->hasMany(
ProductSubscription::className(),
['id_subscription' => 'id']
)->with('product');
}

/**
* Retourne les options de base nécessaires à la fonction de recherche.
*
* @return array
*/
public static function defaultOptionsSearch()
{
return [
'with' => ['producer'],
'join_with' => ['user', 'productSubscription', 'productSubscription.product', 'pointSale'],
'orderby' => 'user.name ASC',
'attribute_id_producer' => 'subscription.id_producer'
];
}

/**
* Ajoute la commande pour une date donnée.
*
* @param string $date
*/
public function add($date)
{
// distribution
$distribution = Distribution::searchOne([
'distribution.date' => date('Y-m-d', strtotime($date))
]);

if ($distribution && $distribution->active && count($this->productSubscription) && $this->id_point_sale) {
// commande
$order = new Order;
if (strlen($this->username)) {
$order->username = $this->username;
$order->id_user = 0;
} else {
$order->id_user = $this->id_user;
/**
* @inheritdoc
*/
public static function tableName()
{
return 'subscription';
}

/**
* @inheritdoc
*/
public function rules()
{
return [
[['id_producer', 'id_point_sale'], 'required'],
[['id_user', 'id_producer', 'id_point_sale', 'monday', 'tuesday',
'wednesday', 'thursday', 'friday', 'saturday', 'sunday', 'week_frequency'], 'integer'],
[['auto_payment'], 'boolean'],
[['date_begin', 'date_end', 'username', 'comment'], 'safe'],
];
}

/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'id_user' => 'Utilisateur',
'id_producer' => 'Etablissement',
'id_point_sale' => 'Point de vente',
'date_begin' => 'Date de début',
'date_end' => 'Date de fin',
'monday' => 'Lundi',
'tuesday' => 'Mardi',
'wednesday' => 'Mercredi',
'thursday' => 'Jeudi',
'friday' => 'Vendredi',
'saturday' => 'Samedi',
'sunday' => 'Dimanche',
'week_frequency' => 'Périodicité',
'auto_payment' => 'Paiement automatique',
'comment' => 'Commentaire'
];
}

/*
* Relations
*/

public function getUser()
{
return $this->hasOne(User::className(), ['id' => 'id_user']);
}

public function getProducer()
{
return $this->hasOne(
Producer::className(),
['id' => 'id_producer']
);
}

public function getPointSale()
{
return $this->hasOne(
PointSale::className(),
['id' => 'id_point_sale']
);
}

public function getProductSubscription()
{
return $this->hasMany(
ProductSubscription::className(),
['id_subscription' => 'id']
)->with('product');
}

/**
* Retourne les options de base nécessaires à la fonction de recherche.
*
* @return array
*/
public static function defaultOptionsSearch()
{
return [
'with' => ['producer'],
'join_with' => ['user', 'productSubscription', 'productSubscription.product', 'pointSale'],
'orderby' => 'user.name ASC',
'attribute_id_producer' => 'subscription.id_producer'
];
}

/**
* Ajoute la commande pour une date donnée.
*
* @param string $date
*/
public function add($date, $force = false)
{
// distribution
$now = date('Y-m-d');
$distributionDate = date('Y-m-d', strtotime($date));
$distribution = Distribution::searchOne([
'distribution.date' => $distributionDate
]);

if ($distribution
&& $distribution->active
&& ($distributionDate > $now || $force)
&& count($this->productSubscription)
&& $this->id_point_sale) {

// commande
$order = new Order;
if (strlen($this->username)) {
$order->username = $this->username;
$order->id_user = 0;
} else {
$order->id_user = $this->id_user;
}

$user = false;
if ($this->id_user) {
$user = User::findOne($this->id_user);
}

$order->date = date('Y-m-d H:i:s');
$order->origin = Order::ORIGIN_AUTO;
$order->id_point_sale = $this->id_point_sale;
$order->id_distribution = $distribution->id;
$order->id_subscription = $this->id;
$order->status = 'tmp-order';
if (strlen($this->comment)) {
$order->comment = $this->comment;
}

$pointSale = PointSale::findOne($this->id_point_sale);

if ($pointSale) {
$creditFunctioning = $pointSale->getCreditFunctioning();

$order->auto_payment = 0;
if ($order->id_user && Producer::getConfig('credit') && $pointSale->credit) {
if ($creditFunctioning == Producer::CREDIT_FUNCTIONING_OPTIONAL) {
$order->auto_payment = $this->auto_payment;
} elseif ($creditFunctioning == Producer::CREDIT_FUNCTIONING_MANDATORY) {
$order->auto_payment = 1;
} elseif ($creditFunctioning == Producer::CREDIT_FUNCTIONING_USER) {
$user = User::findOne($order->id_user);
$userProducer = UserProducer::searchOne([
'id_user' => $order->id_user,
'id_producer' => $distribution->id_producer
]);
if ($userProducer) {
$order->auto_payment = $userProducer->credit_active;
}
}
}

$user = false;
if($this->id_user) {
$user = User::findOne($this->id_user);
}
$order->tiller_synchronization = $order->auto_payment;

$order->date = date('Y-m-d H:i:s');
$order->origin = Order::ORIGIN_AUTO;
$order->id_point_sale = $this->id_point_sale;
$order->id_distribution = $distribution->id;
$order->id_subscription = $this->id;
$order->status = 'tmp-order' ;
if (strlen($this->comment)) {
$order->comment = $this->comment;
}
$userPointSale = UserPointSale::searchOne([
'id_point_sale' => $this->id_point_sale,
'id_user' => $this->id_user
]);

$pointSale = PointSale::findOne($this->id_point_sale);

if($pointSale) {
$creditFunctioning = $pointSale->getCreditFunctioning();

$order->auto_payment = 0;
if ($order->id_user && Producer::getConfig('credit') && $pointSale->credit) {
if ($creditFunctioning == Producer::CREDIT_FUNCTIONING_OPTIONAL) {
$order->auto_payment = $this->auto_payment;
} elseif ($creditFunctioning == Producer::CREDIT_FUNCTIONING_MANDATORY) {
$order->auto_payment = 1;
} elseif ($creditFunctioning == Producer::CREDIT_FUNCTIONING_USER) {
$user = User::findOne($order->id_user);
$userProducer = UserProducer::searchOne([
'id_user' => $order->id_user,
'id_producer' => $distribution->id_producer
]);
if ($userProducer) {
$order->auto_payment = $userProducer->credit_active;
}
}
}

$order->tiller_synchronization = $order->auto_payment ;

$userPointSale = UserPointSale::searchOne([
'id_point_sale' => $this->id_point_sale,
'id_user' => $this->id_user
]);

if ($userPointSale && strlen($userPointSale->comment)) {
$order->comment_point_sale = $userPointSale->comment;
}

$order->save();

// liaison utilisateur / point de vente
if ($order->id_user) {
$pointSale = PointSale::findOne($this->id_point_sale);
$pointSale->linkUser($order->id_user);
}

// produits
$amountTotal = 0;
$productsAdd = false;
foreach ($this->productSubscription as $productSubscription) {
$productOrder = new ProductOrder;
$productOrder->id_order = $order->id;
$productOrder->id_product = $productSubscription->product->id;
$productOrder->quantity = $productSubscription->quantity;
$productOrder->price = $productSubscription->product->getPrice([
'user' => $user,
'point_sale' => $pointSale,
'quantity' => $productSubscription->quantity
]);
$productOrder->unit = $productSubscription->product->unit;
$productOrder->step = $productSubscription->product->step;
$productOrder->id_tax_rate = $productSubscription->product->taxRate->id;
$productOrder->save();
$productsAdd = true;
}

if (!$productsAdd) {
$order->delete();
}

$order->initReference() ;
}
if ($userPointSale && strlen($userPointSale->comment)) {
$order->comment_point_sale = $userPointSale->comment;
}
}

/**
* Ajoute les commandes pour une date donnée à partir des abonnements.
*
* @param string $date
* @param boolean $force
*/
public static function addAll($date, $force = false)
{
$distribution = Distribution::searchOne([
'date' => date('Y-m-d', strtotime($date)),
'id_producer' => GlobalParam::getCurrentProducerId(),
]);
$order->save();

if ($distribution) {
$arrayOrdersDistribution = Order::searchAll([
Order::tableName() . '.id_distribution' => $distribution->id
]);
$arraySubscriptions = self::searchByDate($date);
foreach ($arraySubscriptions as $subscription) {
if (!$subscription->hasOrderAlreadyExist($arrayOrdersDistribution)) {
$subscription->add($date);
}
}
// liaison utilisateur / point de vente
if ($order->id_user) {
$pointSale = PointSale::findOne($this->id_point_sale);
$pointSale->linkUser($order->id_user);
}

// produits
$amountTotal = 0;
$productsAdd = false;
foreach ($this->productSubscription as $productSubscription) {
$productOrder = new ProductOrder;
$productOrder->id_order = $order->id;
$productOrder->id_product = $productSubscription->product->id;
$productOrder->quantity = $productSubscription->quantity;
$productOrder->price = $productSubscription->product->getPrice([
'user' => $user,
'point_sale' => $pointSale,
'quantity' => $productSubscription->quantity
]);
$productOrder->unit = $productSubscription->product->unit;
$productOrder->step = $productSubscription->product->step;
$productOrder->id_tax_rate = $productSubscription->product->taxRate->id;
$productOrder->save();
$productsAdd = true;
}
}

/**
* Informe s'il existe une commande correspond à l'abonnement courant.
*
* @param array $arrayOrders
* @return boolean
*/
public function hasOrderAlreadyExist($arrayOrders)
{
if (is_array($arrayOrders) && count($arrayOrders) > 0) {
foreach ($arrayOrders as $order) {
if ((($order->id_user > 0 && $order->id_user == $this->id_user) ||
(!$order->id_user && $order->username == $this->username)) &&
$order->id_point_sale == $this->id_point_sale) {
return true;
}
}
if (!$productsAdd) {
$order->delete();
}

return false;
$order->initReference();
}
}

/**
* Retourne les abonnements pour une date donnée.
*
* @param string $date
* @return array
*/
public static function searchByDate($date)
{
$date = date('Y-m-d', strtotime($date));

$subscriptions = Subscription::searchAll();

$arrSubscriptions = [];

foreach ($subscriptions as $s) {
if ($date >= $s->date_begin &&
(!$s->date_end || $date <= $s->date_end) &&
$s->matchWith($date)) {
$arrSubscriptions[] = $s;
}
}

/**
* Ajoute les commandes pour une date donnée à partir des abonnements.
*
* @param string $date
* @param boolean $force
*/
public static function addAll($date, $force = false)
{
$distribution = Distribution::searchOne([
'date' => date('Y-m-d', strtotime($date)),
'id_producer' => GlobalParam::getCurrentProducerId(),
]);

if ($distribution) {
$arrayOrdersDistribution = Order::searchAll([
Order::tableName() . '.id_distribution' => $distribution->id
]);
$arraySubscriptions = self::searchByDate($date);
foreach ($arraySubscriptions as $subscription) {
if (!$subscription->hasOrderAlreadyExist($arrayOrdersDistribution)) {
$subscription->add($date, $force);
}

return $arrSubscriptions;
}
}

/**
* Valide le fait qu'un abonnement est bien compatible avec une date donnée.
*
* @param string $date
* @return boolean
*/
public function matchWith($date)
{
$arrayDays = [
1 => 'monday',
2 => 'tuesday',
3 => 'wednesday',
4 => 'thursday',
5 => 'friday',
6 => 'saturday',
7 => 'sunday'
];

$nbDays = (strtotime($date) - strtotime($this->date_begin)) / (24 * 60 * 60);
if (round($nbDays) % ($this->week_frequency * 7) < 7) {
$numDay = date('N', strtotime($date));
$day = $arrayDays[$numDay];
if ($this->$day) {
return true;
}
}

/**
* Informe s'il existe une commande correspond à l'abonnement courant.
*
* @param array $arrayOrders
* @return boolean
*/
public function hasOrderAlreadyExist($arrayOrders)
{
if (is_array($arrayOrders) && count($arrayOrders) > 0) {
foreach ($arrayOrders as $order) {
if ((($order->id_user > 0 && $order->id_user == $this->id_user) ||
(!$order->id_user && $order->username == $this->username)) &&
$order->id_point_sale == $this->id_point_sale) {
return true;
}
}
}

return false;
return false;
}

/**
* Retourne les abonnements pour une date donnée.
*
* @param string $date
* @return array
*/
public static function searchByDate($date)
{
$date = date('Y-m-d', strtotime($date));

$subscriptions = Subscription::searchAll();

$arrSubscriptions = [];

foreach ($subscriptions as $s) {
if ($date >= $s->date_begin &&
(!$s->date_end || $date <= $s->date_end) &&
$s->matchWith($date)) {
$arrSubscriptions[] = $s;
}
}

/**
* Recherche les distributions futures où l'abonnement peut s'appliquer.
*
* @return array
*/
public function searchMatchedIncomingDistributions()
{
$producer = GlobalParam::getCurrentProducer();
$params = [
':date_earliest_order' => date('Y-m-d'),
':date_begin' => date('Y-m-d', strtotime($this->date_begin)),
':id_producer' => GlobalParam::getCurrentProducerId()
];

$incomingDistributions = Distribution::find()
->where('id_producer = :id_producer')
->andWhere('date >= :date_begin')
->andWhere('date >= :date_earliest_order');

if ($this->date_end) {
$incomingDistributions->andWhere('date < :date_end');
$params[':date_end'] = date('Y-m-d', strtotime($this->date_end));
}
return $arrSubscriptions;
}

/**
* Valide le fait qu'un abonnement est bien compatible avec une date donnée.
*
* @param string $date
* @return boolean
*/
public function matchWith($date)
{
$arrayDays = [
1 => 'monday',
2 => 'tuesday',
3 => 'wednesday',
4 => 'thursday',
5 => 'friday',
6 => 'saturday',
7 => 'sunday'
];

$nbDays = (strtotime($date) - strtotime($this->date_begin)) / (24 * 60 * 60);
if (round($nbDays) % ($this->week_frequency * 7) < 7) {
$numDay = date('N', strtotime($date));
$day = $arrayDays[$numDay];
if ($this->$day) {
return true;
}
}

$incomingDistributions->orderBy('date ASC');
return false;
}

/**
* Recherche les distributions futures où l'abonnement peut s'appliquer.
*
* @return array
*/
public function searchMatchedIncomingDistributions()
{
$producer = GlobalParam::getCurrentProducer();
$params = [
':date_earliest_order' => date('Y-m-d'),
':date_begin' => date('Y-m-d', strtotime($this->date_begin)),
':id_producer' => GlobalParam::getCurrentProducerId()
];

$incomingDistributions = Distribution::find()
->where('id_producer = :id_producer')
->andWhere('date >= :date_begin')
->andWhere('date > :date_earliest_order');

if ($this->date_end) {
$incomingDistributions->andWhere('date < :date_end');
$params[':date_end'] = date('Y-m-d', strtotime($this->date_end));
}

$incomingDistributions->params($params);
$incomingDistributionsArray = $incomingDistributions->all();
$incomingDistributions->orderBy('date ASC');

$incomingDistributions = Distribution::filterDistributionsByDateDelay($incomingDistributionsArray) ;
$incomingDistributions->params($params);
$incomingDistributionsArray = $incomingDistributions->all();

$matchedIncomingDistributionsArray = [];
foreach ($incomingDistributionsArray as $incomingDistribution) {
if ($this->matchWith($incomingDistribution->date)) {
$matchedIncomingDistributionsArray[] = $incomingDistribution;
}
}
$incomingDistributions = Distribution::filterDistributionsByDateDelay($incomingDistributionsArray);

return $matchedIncomingDistributionsArray;
$matchedIncomingDistributionsArray = [];
foreach ($incomingDistributionsArray as $incomingDistribution) {
if ($this->matchWith($incomingDistribution->date)) {
$matchedIncomingDistributionsArray[] = $incomingDistribution;
}
}

public function deleteOrdersIncomingDistributions()
{
$params = [
':id_producer' => GlobalParam::getCurrentProducerId(),
':date_today' => date('Y-m-d'),
':date_begin' => $this->date_begin,
':id_subscription' => $this->id
];
return $matchedIncomingDistributionsArray;
}

public function deleteOrdersIncomingDistributions()
{
$params = [
':id_producer' => GlobalParam::getCurrentProducerId(),
':date_today' => date('Y-m-d'),
':date_begin' => $this->date_begin,
':id_subscription' => $this->id
];

$orderDeadline = Producer::getConfig('order_deadline');
$hour = date('G');

if ($hour >= $orderDeadline) {
$conditionDistributionDate = 'distribution.date > :date_today';
} else {
$conditionDistributionDate = 'distribution.date >= :date_today';
}
$orderDeadline = Producer::getConfig('order_deadline');
$hour = date('G');

$orders = Order::find()
->joinWith('distribution')
->where('distribution.id_producer = :id_producer')
->andWhere($conditionDistributionDate)
->andWhere('distribution.date >= :date_begin')
->andWhere('order.id_subscription = :id_subscription');
if ($hour >= $orderDeadline) {
$conditionDistributionDate = 'distribution.date > :date_today';
} else {
$conditionDistributionDate = 'distribution.date >= :date_today';
}

$orders->params($params);
$orders = Order::find()
->joinWith('distribution')
->where('distribution.id_producer = :id_producer')
->andWhere($conditionDistributionDate)
->andWhere('distribution.date >= :date_begin')
->andWhere('order.id_subscription = :id_subscription');

$ordersArray = $orders->all();
$configCredit = Producer::getConfig('credit');
$orders->params($params);

if ($ordersArray && count($ordersArray)) {
foreach ($ordersArray as $order) {
$ordersArray = $orders->all();
$configCredit = Producer::getConfig('credit');

$theOrder = Order::searchOne(['id' => $order->id]);
if ($ordersArray && count($ordersArray)) {
foreach ($ordersArray as $order) {

// remboursement de la commande
if ($theOrder->id_user && $theOrder->getAmount(Order::AMOUNT_PAID) && $configCredit) {
$theOrder->saveCreditHistory(
CreditHistory::TYPE_REFUND,
$theOrder->getAmount(Order::AMOUNT_PAID),
$theOrder->distribution->id_producer,
$theOrder->id_user,
User::getCurrentId()
);
}
$theOrder = Order::searchOne(['id' => $order->id]);

$order->delete();
}
// remboursement de la commande
if ($theOrder->id_user && $theOrder->getAmount(Order::AMOUNT_PAID) && $configCredit) {
$theOrder->saveCreditHistory(
CreditHistory::TYPE_REFUND,
$theOrder->getAmount(Order::AMOUNT_PAID),
$theOrder->distribution->id_producer,
$theOrder->id_user,
User::getCurrentId()
);
}

$order->delete();
}
}
}

public function updateIncomingDistributions($update = false)
{
$matchedDistributionsArray = $this->searchMatchedIncomingDistributions();
public function updateIncomingDistributions($update = false)
{
$matchedDistributionsArray = $this->searchMatchedIncomingDistributions();

if ($update) {
$this->deleteOrdersIncomingDistributions();
}

if (count($matchedDistributionsArray)) {
foreach ($matchedDistributionsArray as $distribution) {
$this->add($distribution->date);
}
}
if ($update) {
$this->deleteOrdersIncomingDistributions();
}

public function getUsername()
{
if($this->user) {
return $this->user->getUsername() ;
}
if (count($matchedDistributionsArray)) {
foreach ($matchedDistributionsArray as $distribution) {
$this->add($distribution->date);
}
}
}

return $this->username ;
public function getUsername()
{
if ($this->user) {
return $this->user->getUsername();
}

return $this->username;
}


}

+ 45
- 0
common/versions/22.10.A.php Ver arquivo

@@ -0,0 +1,45 @@

<h4>Date de sortie</h4>
<ul>
<li>25/10/2022</li>
</ul>

<h4>Évolutions</h4>
<ul>
<li>[Administration] Distributions > commandes : ajout ligne avec montant total et poids par point de vente</li>
<li>[Administration] Distributions > formulaire commande : ajout champs prix HT</li>
<li>[Administration] Produits, Utilisateurs, Statistiques et Documents : accès rapide en un clic</li>
<li>[Administration] Produits > liste : possibilité de modifier directement le champs "actif"</li>
<li>[Administration] Utilisateurs > commandes : ajout lien vers modification</li>
<li>
[Administration] Documents > modification : mise en évidence des prix incohérents par rapport à ceux définis au
niveau des produits
</li>
<li>[Administration] Documents > liste : ajout champs "Envoyé"</li>
<li>[Administration] Documents > factures > liste : filtre par utilisateur</li>
<li>[Administration] Paramètres : tri et réagencement</li>
<li>[Administration] Développement : page de suivi des versions du logiciel (liste des évolutions et correctifs)</li>
<li>[Espace producteur] Formulaire de contact avec protection par captcha</li>
</ul>

<h4>Correctifs</h4>
<ul>
<li>
[Administration] Produits > modification : prise en compte des prix spécifiques lors de la mise à jour automatique
des commandes des distributions futures
</li>
<li>
[Administration] Produits > prix spécifiques : prise en compte des prix par groupe d'utilisateur et point de vente
</li>
<li>[Administration] Utilisateurs > crédit : correctif champs "Commentaire" trop long</li>
<li>[Administration] Utilisateurs > liste : correctif nombre de commandes</li>
<li>[Administration] Distributions > modification commande : correctif modification prix</li>
<li>
[Administration] Distributions > modification commande : adaptation changement du point de vente lors de la
sélection d'un utilisateur
</li>
<li>[Administration] Haut de page : utilisateurs vides dans le bloc "Utilisateurs au crédit négatif"</li>
<li>[Administration] Abonnements : ne pas générer de commandes pour le jour même</li>
<li>[Backend] Facture > créer : tri des utilisateurs par ordre alphabétique</li>
<li>Correctif connexion personnes morales</li>
</ul>

+ 2
- 1
composer.json Ver arquivo

@@ -30,7 +30,8 @@
"yiisoft/yii2-jui": "^2.0",
"bower-asset/jquery": "^3.6",
"yidas/yii2-bower-asset": "2.0.13.1",
"stripe/stripe-php": "^7.95"
"stripe/stripe-php": "^7.95",
"loveorigami/yii2-bootstrap-toggle": "*"
},
"require-dev": {
"yiisoft/yii2-codeception": "*",

+ 70
- 2
composer.lock Ver arquivo

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "bbfd502bb99ed522c5f181c9e809790f",
"content-hash": "96d4dfea93de951d74c4d1d099ed27ed",
"packages": [
{
"name": "2amigos/yii2-chartjs-widget",
@@ -178,6 +178,24 @@
},
"time": "2019-08-29T08:20:20+00:00"
},
{
"name": "bower-asset/bootstrap-toggle",
"version": "2.2.2",
"source": {
"type": "git",
"url": "git@github.com:minhur/bootstrap-toggle.git",
"reference": "187e332b7431afe3d0c07eeef92782ce59a77d66"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/minhur/bootstrap-toggle/zipball/187e332b7431afe3d0c07eeef92782ce59a77d66",
"reference": "187e332b7431afe3d0c07eeef92782ce59a77d66"
},
"type": "bower-asset",
"license": [
"MIT"
]
},
{
"name": "bower-asset/chartjs",
"version": "v2.1.6",
@@ -1011,6 +1029,56 @@
},
"time": "2020-05-03T11:44:34+00:00"
},
{
"name": "loveorigami/yii2-bootstrap-toggle",
"version": "1.1",
"source": {
"type": "git",
"url": "https://github.com/loveorigami/yii2-bootstrap-toggle.git",
"reference": "27ee6469f784b9163c03ee760e34c7ef751be148"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/loveorigami/yii2-bootstrap-toggle/zipball/27ee6469f784b9163c03ee760e34c7ef751be148",
"reference": "27ee6469f784b9163c03ee760e34c7ef751be148",
"shasum": ""
},
"require": {
"bower-asset/bootstrap-toggle": "*",
"yiisoft/yii2": "2.0.*"
},
"require-dev": {
"yiisoft/yii2-debug": "2.0.*"
},
"type": "yii2-extension",
"autoload": {
"psr-4": {
"lo\\widgets\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-3.0+"
],
"authors": [
{
"name": "Andrey Lukyanov",
"email": "loveorigami@mail.ru"
}
],
"description": "Yii2 bootstrap-toggle widget",
"keywords": [
"bootstrap",
"toggle",
"widget",
"yii2"
],
"support": {
"issues": "https://github.com/loveorigami/yii2-bootstrap-toggle/issues",
"source": "https://github.com/loveorigami/yii2-bootstrap-toggle/tree/master"
},
"time": "2016-05-12T15:57:40+00:00"
},
{
"name": "mailjet/mailjet-apiv3-php",
"version": "v1.5.1",
@@ -6048,5 +6116,5 @@
"php": ">=5.4.0"
},
"platform-dev": [],
"plugin-api-version": "2.0.0"
"plugin-api-version": "2.3.0"
}

+ 30
- 0
console/migrations/m221010_124540_document_is_sent.php Ver arquivo

@@ -0,0 +1,30 @@
<?php

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

/**
* Class m221010_124540_document_is_sent
*/
class m221010_124540_document_is_sent extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->addColumn('quotation', 'is_sent', Schema::TYPE_BOOLEAN);
$this->addColumn('delivery_note', 'is_sent', Schema::TYPE_BOOLEAN);
$this->addColumn('invoice', 'is_sent', Schema::TYPE_BOOLEAN);
}

/**
* {@inheritdoc}
*/
public function safeDown()
{
$this->dropColumn('quotation', 'is_sent');
$this->dropColumn('delivery_note', 'is_sent');
$this->dropColumn('invoice', 'is_sent');
}
}

+ 26
- 0
console/migrations/m221013_085349_producer_add_field_latest_version.php Ver arquivo

@@ -0,0 +1,26 @@
<?php

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

/**
* Class m221013_085349_producer_add_field_latest_version
*/
class m221013_085349_producer_add_field_latest_version extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->addColumn('producer', 'latest_version_opendistrib', Schema::TYPE_STRING);
}

/**
* {@inheritdoc}
*/
public function safeDown()
{
$this->dropColumn('producer', 'latest_version_opendistrib');
}
}

+ 4
- 11
frontend/views/site/contact.php Ver arquivo

@@ -56,18 +56,11 @@ $this->params['breadcrumbs'][] = $this->title;

<div class="row">
<div class="col-lg-5">
<!--<h2>par téléphone</h2>
<p id="contact-phone">
Guillaume Bourgeois<br />
<span class="glyphicon glyphicon-phone"></span> 06 84 59 71 79

<br /><br />
Fabien Normand<br />
<span class="glyphicon glyphicon-phone"></span> 07 81 99 97 01
</p>-->
<h2>Par email</h2>
<div class="alert alert-info">
Ce formulaire de contact vous permet de joindre le développeur de la plateforme Opendistrib.
Si vous souhaitez joindre un producteur, merci de le faire directement depuis son espace producteur.
</div>
<?php $form = ActiveForm::begin(['id' => 'contact-form']); ?>
<?= $form->field($model, 'name') ?>
<?= $form->field($model, 'email') ?>

+ 0
- 1
info.php Ver arquivo

@@ -1 +0,0 @@
<?php phpinfo(); ?>

+ 1
- 2
producer/controllers/SiteController.php Ver arquivo

@@ -188,8 +188,6 @@ class SiteController extends ProducerBaseController
*/
public function actionContact()
{
return $this->redirect(['site/index']);

$model = new ContactForm();
$producer = $this->getProducer();

@@ -202,6 +200,7 @@ class SiteController extends ProducerBaseController
$email = $contact->email;
}

$email = 'guillaume@laclic.fr';
if (strlen($email) && $model->sendEmail($email)) {
$isSent = true;
}

+ 2
- 2
producer/views/layouts/main.php Ver arquivo

@@ -209,11 +209,11 @@ if (!Yii::$app->user->isGuest) {
'visible' => !Yii::$app->user->isGuest && $producer->credit,
'active' => $this->getControllerAction() == 'credit/history',
],
/*[
[
'label' => '<span class="glyphicon glyphicon-envelope"></span> Contact',
'url' => Yii::$app->urlManager->createUrl(['site/contact']),
'active' => $this->getControllerAction() == 'site/contact',
],*/
],
[
'label' => '<span class="glyphicon glyphicon-cog"></span> Administration',
'url' => Yii::$app->urlManagerBackend->createAbsoluteUrl(['site/index']),

+ 1
- 1
producer/web/js/vuejs/subscription-form.js Ver arquivo

@@ -263,7 +263,7 @@ var app = new Vue({
}

if (!this.monday && !this.tuesday && !this.wednesday && !this.thursday &&
!this.friday && !this.saturday) {
!this.friday && !this.saturday && !this.sunday) {
this.errors.push('Veuillez sélectionner un jour de distribution');
}


Carregando…
Cancelar
Salvar