소스 검색

Merge branch 'develop'

master
Guillaume Bourgeois 1 년 전
부모
커밋
4f62bbfc58
98개의 변경된 파일3338개의 추가작업 그리고 636개의 파일을 삭제
  1. +58
    -0
      backend/assets/VuejsSettingFormAsset.php
  2. +2
    -1
      backend/controllers/DistributionController.php
  3. +19
    -7
      backend/controllers/FeatureAdminController.php
  4. +3
    -0
      backend/controllers/ProducerController.php
  5. +86
    -0
      backend/controllers/SettingAdminController.php
  6. +1
    -1
      backend/controllers/UserController.php
  7. +57
    -0
      backend/forms/AdminSettingsForm.php
  8. +13
    -28
      backend/views/distribution/shopping-cart-labels.php
  9. +10
    -1
      backend/views/feature-admin/index.php
  10. +8
    -1
      backend/views/feature-admin/update.php
  11. +4
    -3
      backend/views/layouts/left.php
  12. +2
    -0
      backend/views/point-sale/_form.php
  13. +41
    -17
      backend/views/producer/update.php
  14. +112
    -0
      backend/views/setting-admin/index.php
  15. +7
    -2
      backend/views/support/index.php
  16. +44
    -1
      backend/web/css/screen.css
  17. +7
    -1
      backend/web/js/backend.js
  18. +63
    -0
      backend/web/js/vuejs/setting-form.js
  19. +2
    -1
      backend/web/sass/screen.scss
  20. +52
    -0
      backend/web/sass/setting/_form.scss
  21. +2
    -0
      common/assets/CommonAsset.php
  22. +2
    -1
      common/components/BusinessLogic.php
  23. +8
    -2
      common/components/BusinessLogicTrait.php
  24. +2
    -1
      common/components/DolibarrApi.php
  25. +1
    -1
      common/config/params.php
  26. +8
    -0
      common/logic/AbstractChecker.php
  27. +8
    -1
      common/logic/AbstractRepository.php
  28. +8
    -0
      common/logic/AbstractResolver.php
  29. +1
    -0
      common/logic/AbstractService.php
  30. +8
    -0
      common/logic/CheckerInterface.php
  31. +120
    -29
      common/logic/Distribution/Distribution/Export/DistributionShoppingCartLabelsPdfGenerator.php
  32. +7
    -1
      common/logic/Document/Document/Service/DocumentManager.php
  33. +8
    -4
      common/logic/Feature/Feature/Feature.php
  34. +2
    -4
      common/logic/Feature/Feature/FeatureBuilder.php
  35. +48
    -0
      common/logic/Feature/Feature/FeatureChecker.php
  36. +24
    -0
      common/logic/Feature/Feature/FeatureDefinition.php
  37. +3
    -6
      common/logic/Feature/Feature/FeatureImporter.php
  38. +43
    -0
      common/logic/Feature/Feature/FeatureManager.php
  39. +7
    -6
      common/logic/Feature/Feature/FeatureModule.php
  40. +9
    -3
      common/logic/Feature/Feature/FeatureRepository.php
  41. +9
    -2
      common/logic/Feature/Feature/FeatureRepositoryQuery.php
  42. +0
    -31
      common/logic/Feature/Feature/Service/FeatureDefinition.php
  43. +0
    -80
      common/logic/Feature/Feature/Service/FeatureManager.php
  44. +2
    -2
      common/logic/Feature/FeatureProducer/FeatureProducer.php
  45. +2
    -5
      common/logic/Feature/FeatureProducer/FeatureProducerBuilder.php
  46. +13
    -0
      common/logic/Feature/FeatureProducer/FeatureProducerDefinition.php
  47. +2
    -5
      common/logic/Feature/FeatureProducer/FeatureProducerModule.php
  48. +2
    -3
      common/logic/Feature/FeatureProducer/FeatureProducerRepository.php
  49. +3
    -4
      common/logic/Feature/FeatureProducer/FeatureProducerRepositoryQuery.php
  50. +0
    -25
      common/logic/Feature/FeatureProducer/Service/FeatureProducerDefinition.php
  51. +4
    -2
      common/logic/Order/Order/Service/TillerManager.php
  52. +3
    -2
      common/logic/PointSale/PointSale/Model/PointSale.php
  53. +14
    -4
      common/logic/Producer/Producer/Model/Producer.php
  54. +9
    -4
      common/logic/ProducerContextTrait.php
  55. +39
    -0
      common/logic/Setting/AdminSettingBag.php
  56. +108
    -0
      common/logic/Setting/Setting.php
  57. +84
    -0
      common/logic/Setting/SettingBuilder.php
  58. +47
    -0
      common/logic/Setting/SettingDefinition.php
  59. +170
    -0
      common/logic/Setting/SettingDetails/AbstractSettingDetail.php
  60. +67
    -0
      common/logic/Setting/SettingDetails/Admin/AdminSettingDefinition.php
  61. +17
    -0
      common/logic/Setting/SettingDetails/Admin/General/AdministratorEmailAdminSetting.php
  62. +17
    -0
      common/logic/Setting/SettingDetails/Admin/General/AdministratorPhoneNumberAdminSetting.php
  63. +30
    -0
      common/logic/Setting/SettingDetails/Producer/ProducerSettingDefinition.php
  64. +37
    -0
      common/logic/Setting/SettingImporter.php
  65. +53
    -0
      common/logic/Setting/SettingModule.php
  66. +50
    -0
      common/logic/Setting/SettingRepository.php
  67. +27
    -0
      common/logic/Setting/SettingRepositoryQuery.php
  68. +2
    -0
      common/mail/orderConfirm-html.php
  69. +1
    -1
      common/mail/paymentErrorProducer-text.php
  70. +32
    -0
      common/versions/23.11.A.php
  71. +31
    -0
      common/versions/23.11.B.php
  72. +386
    -0
      common/web/js/simple-lightbox/simpleLightbox.css
  73. +532
    -0
      common/web/js/simple-lightbox/simpleLightbox.js
  74. +1
    -0
      common/web/js/simple-lightbox/simpleLightbox.min.css
  75. +1
    -0
      common/web/js/simple-lightbox/simpleLightbox.min.js
  76. +2
    -1
      composer.json
  77. +104
    -2
      composer.lock
  78. +2
    -1
      console/commands/ImportFeaturesController.php
  79. +18
    -0
      console/commands/SettingController.php
  80. +25
    -0
      console/migrations/m231113_073008_add_column_feature_position.php
  81. +26
    -0
      console/migrations/m231113_084553_add_column_point_sale_minimum_order_amount.php
  82. +26
    -0
      console/migrations/m231113_131131_add_column_producer_export_shopping_cart_labels_format.php
  83. +37
    -0
      console/migrations/m231114_085352_create_table_setting.php
  84. +26
    -0
      console/migrations/m231115_083420_add_column_producer_document_image_bottom.php
  85. +4
    -1
      frontend/controllers/SiteController.php
  86. +7
    -6
      frontend/views/site/_prices_producer.php
  87. +4
    -1
      frontend/views/site/service.php
  88. +131
    -125
      producer/controllers/CreditController.php
  89. +33
    -35
      producer/controllers/OrderController.php
  90. +2
    -2
      producer/controllers/SiteController.php
  91. +4
    -1
      producer/views/credit/history.php
  92. +3
    -3
      producer/views/layouts/main.php
  93. +123
    -124
      producer/views/order/order.php
  94. +1
    -1
      producer/views/site/index.php
  95. +36
    -34
      producer/web/css/screen.css
  96. +5
    -3
      producer/web/js/producer.js
  97. +11
    -1
      producer/web/js/vuejs/order-order.js
  98. +3
    -2
      producer/web/sass/order/_order.scss

+ 58
- 0
backend/assets/VuejsSettingFormAsset.php 파일 보기

@@ -0,0 +1,58 @@
<?php
/**
Copyright distrib (2018)

contact@opendistrib.net

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

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

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

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

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

namespace backend\assets;

use yii\web\AssetBundle;
use yii ;

class VuejsSettingFormAsset extends \common\components\MyAssetBundle
{
public $basePath = '@webroot';
public $baseUrl = '@web';
public $css = [];
public $js = [];
public $depends = [
'common\assets\CommonAsset'
];
public function __construct()
{
parent::__construct() ;
$this->addAsset('js','js/vuejs/setting-form.js') ;
}
}

+ 2
- 1
backend/controllers/DistributionController.php 파일 보기

@@ -577,7 +577,8 @@ class DistributionController extends BackendController
return $distributionModule->getExportManager()->getGenerator($name)->generate($distribution);
}
catch(ErrorException $exception) {
$this->setFlash('error', "Une erreur est survenue lors de la génération de l'export.");
//$this->setFlash('error', "Une erreur est survenue lors de la génération de l'export.");
$this->setFlash('error', $exception->getMessage());
return $this->redirectReferer();
}
}

+ 19
- 7
backend/controllers/FeatureAdminController.php 파일 보기

@@ -71,7 +71,7 @@ class FeatureAdminController extends BackendController
public function actionIndex()
{
$featureModule = $this->getFeatureModule();
$dataProviderFeatures = $featureModule->getRepository()->queryAll()->getDataProvider(100);
$dataProviderFeatures = $featureModule->getRepository()->queryDefaultAll()->getDataProvider(100);

return $this->render('index', [
'producerCurrent' => $this->getProducerCurrent(),
@@ -85,30 +85,30 @@ class FeatureAdminController extends BackendController
$feature = $this->findModel($id);

if($status) {
$featureManager->enable($feature);
$featureManager->enableFeature($feature);
$messageResponse = 'La fonctionnalité "'.Html::encode($feature->name).'" a bien été activée';
}
else {
$featureManager->disable($feature);
$featureManager->disableFeature($feature);
$messageResponse = 'La fonctionnalité "'.Html::encode($feature->name).'" a bien été désactivée';
}

return Ajax::responseSuccess($messageResponse);
}

public function actionToggleStatusFeatureProducer(int $id, bool $status = null)
public function actionToggleStatusFeatureProducer(int $id, int $status = null)
{
$featureManager = $this->getFeatureModule()->getManager();
$feature = $this->findModel($id);

if(is_null($status)) {
$featureManager->defaultForProducer($feature);
$featureManager->defaultFeatureForProducer($feature);
}
elseif($status == 0) {
$featureManager->disableForProducer($feature);
$featureManager->disableFeatureForProducer($feature);
}
elseif($status == 1) {
$featureManager->enableForProducer($feature);
$featureManager->enableFeatureForProducer($feature);
}

return $this->redirectReferer();
@@ -128,6 +128,18 @@ class FeatureAdminController extends BackendController
}
}

public function actionPosition()
{
$array = \Yii::$app->request->post('array');
$positionArray = json_decode(stripslashes($array));

foreach ($positionArray as $id => $position) {
$feature = $this->findModel($id);
$feature->position = $position;
$feature->save();
}
}

protected function findModel($id)
{
$featureModule = $this->getFeatureModule();

+ 3
- 0
backend/controllers/ProducerController.php 파일 보기

@@ -94,17 +94,20 @@ class ProducerController extends BackendController

$logoFilenameOld = $model->logo;
$photoFilenameOld = $model->photo;
$documentImageBottomFilenameOld = $model->document_image_bottom;
$producerBuilder->initOptionDashboardDatesDisplay($model);

if ($model->load(\Yii::$app->request->post())) {

$model->logoFile = UploadedFile::getInstance($model, 'logoFile');
$model->photoFile = UploadedFile::getInstance($model, 'photoFile');
$model->document_image_bottomFile = UploadedFile::getInstance($model, 'document_image_bottomFile');

if($model->validate()) {

$producerBuilder->processUploadImage($model, 'logo', $logoFilenameOld, $request->post('delete_logo', 0));
$producerBuilder->processUploadImage($model, 'photo', $photoFilenameOld, $request->post('delete_photo', 0));
$producerBuilder->processUploadImage($model, 'document_image_bottom', $documentImageBottomFilenameOld, $request->post('delete_document_image_bottom', 0));
$producerBuilder->initOptionDashboardDatesBeforeSave($model);
$producerBuilder->savePrivateKeysStripe($model);
$model->save();

+ 86
- 0
backend/controllers/SettingAdminController.php 파일 보기

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

/**
* Copyright distrib (2018)
*
* contact@opendistrib.net
*
* Ce logiciel est un programme informatique servant à aider les producteurs
* à distribuer leur production en circuits courts.
*
* Ce logiciel est régi par la licence CeCILL soumise au droit français et
* respectant les principes de diffusion des logiciels libres. Vous pouvez
* utiliser, modifier et/ou redistribuer ce programme sous les conditions
* de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
* sur le site "http://www.cecill.info".
*
* En contrepartie de l'accessibilité au code source et des droits de copie,
* de modification et de redistribution accordés par cette licence, il n'est
* offert aux utilisateurs qu'une garantie limitée. Pour les mêmes raisons,
* seule une responsabilité restreinte pèse sur l'auteur du programme, le
* titulaire des droits patrimoniaux et les concédants successifs.
*
* A cet égard l'attention de l'utilisateur est attirée sur les risques
* associés au chargement, à l'utilisation, à la modification et/ou au
* développement et à la reproduction du logiciel par l'utilisateur étant
* donné sa spécificité de logiciel libre, qui peut le rendre complexe à
* manipuler et qui le réserve donc à des développeurs et des professionnels
* avertis possédant des connaissances informatiques approfondies. Les
* utilisateurs sont donc invités à charger et tester l'adéquation du
* logiciel à leurs besoins dans des conditions permettant d'assurer la
* sécurité de leurs systèmes et ou de leurs données et, plus généralement,
* à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.
*
* Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
* pris connaissance de la licence CeCILL, et que vous en avez accepté les
* termes.
*/

namespace backend\controllers;

use backend\forms\AdminSettingsForm;
use common\helpers\Ajax;
use yii\filters\AccessControl;
use yii\helpers\Html;
use yii\web\NotFoundHttpException;

/**
* UserController implements the CRUD actions for User model.
*/
class SettingAdminController extends BackendController
{
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::class,
'rules' => [
[
'allow' => true,
'roles' => ['@'],
'matchCallback' => function ($rule, $action) {
return $this->getUserModule()
->getAuthorizationChecker()
->isGrantedAsAdministrator($this->getUserCurrent());
}
]
],
],
];
}

public function actionIndex()
{
$settingModule = $this->getSettingModule();
$model = new AdminSettingsForm();
if($model->load(\Yii::$app->request->post()) && $model->validate()) {
foreach($settingModule->getAdminSettingDefinition()->getSettingDetailsFlat() as $settingDetail) {
$settingModule->getAdminSettingBag()->set($settingDetail->getName(), $model->{$settingDetail->getName()});
}
}

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

+ 1
- 1
backend/controllers/UserController.php 파일 보기

@@ -532,7 +532,7 @@ class UserController extends BackendController
}
}

$pointsSaleArray = PointSale::find()->where(['id_producer' => GlobalParam::getCurrentProducerId()])->all();
$pointsSaleArray = PointSale::find()->where(['id_producer' => GlobalParam::getCurrentProducerId(), 'status' => 1])->all();

$pointSale = null;
if ($idPointSale) {

+ 57
- 0
backend/forms/AdminSettingsForm.php 파일 보기

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

namespace backend\forms;

use common\logic\Setting\SettingModule;
use yii\base\Model;

class AdminSettingsForm extends Model
{
public function __get($name)
{
return SettingModule::getInstance()->getAdminSettingBag()->get($name);
}

public function __set($name, $value)
{
$this->$name = $value;
}

public function rules()
{
$rulesArray = [];
$typesArray = [
'string' => ['string', 'text'],
'date' => ['date'],
'boolean' => ['boolean'],
'integer' => ['integer'],
'double' => ['double', 'float'],
];
foreach($typesArray as $rule => $typesSettingArray) {
$rulesArray[] = [$this->getSettingNamesByTypeArray($typesSettingArray), $rule];
}
return $rulesArray;
}

public function attributeLabels()
{
$attributeLabelsArray = [];
foreach(SettingModule::getInstance()->getAdminSettingDefinition()->getSettingDetailsFlat() as $settingDetail) {
$attributeLabelsArray[$settingDetail->getName()] = $settingDetail->getLabel();
}
return $attributeLabelsArray;
}

public function getSettingNamesByTypeArray(array $typesSettingArray): array
{
$settingNamesTypeArray = [];
foreach(SettingModule::getInstance()->getAdminSettingDefinition()->getSettingDetailsFlat() as $settingDetail) {
if(in_array($settingDetail->getType(), $typesSettingArray)) {
$settingNamesTypeArray[] = $settingDetail->getName();
}
}
return $settingNamesTypeArray;
}
}

?>

+ 13
- 28
backend/views/distribution/shopping-cart-labels.php 파일 보기

@@ -1,35 +1,20 @@
<?php

use common\logic\Order\Order\Module\OrderModule;
use yii\helpers\Html;
use common\logic\Distribution\Distribution\Export\DistributionShoppingCartLabelsPdfGenerator;

$orderModule = OrderModule::getInstance();
$distributionShoppingCartLabelsPdfGenerator = DistributionShoppingCartLabelsPdfGenerator::getInstance();
$index = 0;

?>
<?php
$i = 0;
foreach($ordersArray as $key => $order):
foreach($ordersArray as $key => $order) {
$index ++;
?>
<div class="shopping-cart-label">
<div class="inner">
<div class="username">
<?= $orderModule->getOrderUsername($order); ?>
</div>
<div class="point-sale">
<?= date('d/m', strtotime($distribution->date)); ?> &bull;
<?= Html::encode($order->pointSale->name); ?>
</div>
<div class="products">
<?= $orderModule->getCartSummary($order); ?>
</div>
</div>
</div>
<?php if($index == $shoppingCartLabelsPerColumn) {

echo $distributionShoppingCartLabelsPdfGenerator->getShoppingCartLabelAsHtml($order);

if($index == $shoppingCartLabelsPerColumn) {
$index = 0;
} ?>
<?php if($index == 0 && $key !== array_key_last($ordersArray)): ?>
<columnbreak />
<?php endif; ?>
<?php endforeach; ?>
}
if(!$isSpecificFormat && $index == 0 && $key !== array_key_last($ordersArray)) {
echo '<columnbreak />';
}
}
?>

+ 10
- 1
backend/views/feature-admin/index.php 파일 보기

@@ -50,6 +50,15 @@ $this->addBreadcrumb($this->getTitle());
<?= GridView::widget([
'dataProvider' => $dataProviderFeatures,
'columns' => [
[
'attribute' => 'position',
'headerOptions' => ['class' => 'position'],
'format' => 'raw',
'filter' => '',
'value' => function ($model) {
return '<a class="btn-position btn btn-default" href="javascript:void(0);"><span class="glyphicon glyphicon-resize-vertical"></span></a>';
}
],
'name',
[
'attribute' => 'status',
@@ -121,7 +130,7 @@ $this->addBreadcrumb($this->getTitle());
'format' => 'raw',
'value' => function ($model) {
if($model->is_paid_feature && $model->price) {
return Price::format($model->price);
return Price::format($model->price, 0);
}

return '';

+ 8
- 1
backend/views/feature-admin/update.php 파일 보기

@@ -49,7 +49,14 @@ $this->addBreadcrumb('Modifier') ;
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'status')->radioList([1 => 'Oui', 0 => 'Non']) ?>
<?= $form->field($model, 'name') ?>
<?= $form->field($model, 'description')->textarea(['rows' => '4']) ; ?>
<?= $form->field($model, 'description')->widget(letyii\tinymce\Tinymce::class, [
'options' => [
'id' => 'testid',
],
'configs' => [ // Read more: https://www.tiny.cloud/docs/tinymce/6/full-featured-open-source-demo/
'plugins' => 'preview importcss searchreplace autolink autosave save directionality code visualblocks visualchars fullscreen image link media template codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount help charmap quickbars emoticons accordion' ,
]
]) ; ?>
<?= $form->field($model, 'only_for_selected_producers')->radioList([1 => 'Oui', 0 => 'Non']) ?>
<?= $form->field($model, 'is_paid_feature')->radioList([1 => 'Oui', 0 => 'Non']) ?>
<?= $form->field($model, 'price') ?>

+ 4
- 3
backend/views/layouts/left.php 파일 보기

@@ -37,14 +37,14 @@
*/

use common\helpers\GlobalParam;
use common\logic\Feature\Feature\Model\Feature;
use common\logic\Feature\Feature\Feature;
use common\logic\User\User\Module\UserModule;

$producerModule = $this->getProducerModule();
$userModule = UserModule::getInstance();
$userProducerModule = $this->getUserProducerModule();
$ticketModule = $this->getTicketModule();
$featureManager = $this->getFeatureModule()->getManager();
$featureChecker = $this->getFeatureModule()->getChecker();

$producer = GlobalParam::getCurrentProducer();
$userCurrent = GlobalParam::getCurrentUser();
@@ -106,7 +106,7 @@ $isUserCurrentGrantedAsProducer = $userModule->getAuthorizationChecker()->isGran
'items' => [
['label' => 'Liste', 'icon' => 'th-list', 'url' => ['/product/index'], 'visible' => $isUserCurrentGrantedAsProducer],
['label' => 'Catégories', 'icon' => 'book', 'url' => ['/product-category/index'], 'visible' => $isUserCurrentGrantedAsProducer],
['label' => 'Import prix', 'icon' => 'upload', 'url' => ['/product/price-import'], 'visible' => $isUserCurrentGrantedAsProducer && $featureManager->isEnabled(Feature::ALIAS_PRODUCT_PRICE_IMPORT)],
['label' => 'Import prix', 'icon' => 'upload', 'url' => ['/product/price-import'], 'visible' => $isUserCurrentGrantedAsProducer && $featureChecker->isEnabled(Feature::ALIAS_PRODUCT_PRICE_IMPORT)],
]
],
['label' => 'Points de vente', 'icon' => 'map-marker', 'url' => ['/point-sale/index'], 'visible' => $isUserCurrentGrantedAsProducer, 'active' => Yii::$app->controller->id == 'point-sale'],
@@ -182,6 +182,7 @@ $isUserCurrentGrantedAsProducer = $userModule->getAuthorizationChecker()->isGran
['label' => 'Commandes clients', 'icon' => 'calendar', 'url' => ['/stats-admin/customer-orders'], 'visible' => $isUserCurrentGrantedAsAdministrator],
],
],
['label' => 'Paramètres', 'icon' => 'cog', 'url' => ['/setting-admin/index'], 'visible' => $isUserCurrentGrantedAsAdministrator && $featureChecker->isEnabled(Feature::ALIAS_SETTINGS)],
['label' => 'Fonctionnalités', 'icon' => 'flag', 'url' => ['/feature-admin/index'], 'visible' => $isUserCurrentGrantedAsAdministrator],
['label' => 'Tranches de prix', 'icon' => 'eur', 'url' => ['/producer-price-range-admin/index'], 'visible' => $isUserCurrentGrantedAsAdministrator],
['label' => 'Taxes', 'icon' => 'eur', 'url' => ['/tax-rate-admin/index'], 'visible' => $isUserCurrentGrantedAsAdministrator],

+ 2
- 0
backend/views/point-sale/_form.php 파일 보기

@@ -88,6 +88,8 @@ $distributionModule = DistributionModule::getInstance();
->dropDownList( ProductPrice::percentValues(), [])->hint('Pourcentage appliqué aux prix de chaque produit dans ce point de vente.');*/ ?>

<?= $form->field($model, 'maximum_number_orders')->textInput() ?>
<?= $form->field($model, 'minimum_order_amount')->textInput() ?>


<div id="delivery-days">
<h2>Jours de livraison</h2>

+ 41
- 17
backend/views/producer/update.php 파일 보기

@@ -39,6 +39,9 @@
use common\helpers\Dropdown;
use common\helpers\GlobalParam;
use common\logic\Distribution\Distribution\Module\DistributionModule;
use common\logic\Distribution\Distribution\Service\ExportManager;
use common\logic\Feature\Feature\Feature;
use common\logic\Feature\Feature\FeatureModule;
use common\logic\User\User\Module\UserModule;
use common\logic\User\UserGroup\Module\UserGroupModule;
use yii\helpers\Html;
@@ -53,6 +56,7 @@ use yii\helpers\ArrayHelper;
$userModule = UserModule::getInstance();
$userGroupModule = UserGroupModule::getInstance();
$distributionExportManager = DistributionModule::getInstance()->getExportManager();
$featureChecker = FeatureModule::getInstance()->getChecker();

$userCurrent = GlobalParam::getCurrentUser();

@@ -304,8 +308,13 @@ $this->addBreadcrumb($this->getTitle());
->dropDownList(Dropdown::noYesChoices()); ?>
<?= $form->field($model, 'option_csv_export_by_piece')
->dropDownList(Dropdown::noYesChoices()); ?>
<?= $form->field($model, 'export_shopping_cart_labels_number_per_column')
->dropDownList(Dropdown::numberChoices(1, 20)); ?>
<?php if($featureChecker->isEnabled(Feature::ALIAS_EXPORT_SHOPPING_CART_LABELS_ADVANCED)): ?>
<?= $form->field($model, 'export_shopping_cart_labels_format')
->dropDownList($distributionExportManager->getGenerator(ExportManager::SHOPPING_CART_LABELS_PDF)->populateDropdownSpecificFormats()); ?>
<?php else: ?>
<?= $form->field($model, 'export_shopping_cart_labels_number_per_column')
->dropDownList(Dropdown::numberChoices(1, 20)); ?>
<?php endif; ?>
</div>
</div>

@@ -350,21 +359,27 @@ $this->addBreadcrumb($this->getTitle());
<?= $form->field($model, 'option_check_by_default_prevent_user_credit')
->dropDownList(Dropdown::noYesChoices()); ?>

<h4>Paiement en ligne</h4>
<?= $form->field($model, 'online_payment')
->dropDownList(Dropdown::noYesChoices()); ?>
<?= $form->field($model, 'option_online_payment_minimum_amount')
->hint('Valeur par défaut si non défini : ' . Producer::ONLINE_PAYMENT_MINIMUM_AMOUNT_DEFAULT . ' €')
->textInput(); ?>
<?= $form->field($model, 'option_stripe_mode_test')->dropDownList(Dropdown::noYesChoices()); ?>
<?= $form->field($model, 'option_online_payment_type')
->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(); ?>
<?php if($featureChecker->isEnabled(Feature::ALIAS_ONLINE_PAYMENT)): ?>
<h4>Paiement en ligne</h4>
<?php if($userModule->getAuthorizationChecker()->isGrantedAsAdministrator($userCurrent)): ?>
<?= $form->field($model, 'online_payment')
->dropDownList(Dropdown::noYesChoices()); ?>

<?= $form->field($model, 'option_stripe_mode_test')->dropDownList(Dropdown::noYesChoices()); ?>
<?= $form->field($model, 'option_online_payment_type')
->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(); ?>
<?php endif; ?>

<?= $form->field($model, 'option_online_payment_minimum_amount')
->hint('Valeur par défaut si non défini : ' . Producer::ONLINE_PAYMENT_MINIMUM_AMOUNT_DEFAULT . ' €')
->textInput(); ?>
<?php endif; ?>
</div>
</div>

@@ -431,6 +446,15 @@ $this->addBreadcrumb($this->getTitle());
->hint("Affichées juste en dessous de l'adresse"); ?>
<?= $form->field($model, 'document_infos_bottom')
->textarea(['rows' => 8]) ?>

<?= $form->field($model, 'document_image_bottomFile')->fileInput()->hint('') ?>
<?php
if (strlen($model->document_image_bottom)) {
echo '<img src="' . Yii::$app->urlManagerProducer->getHostInfo() . '/' . Yii::$app->urlManagerProducer->baseUrl . '/uploads/' . $model->document_image_bottom . '" width="400px" /><br />';
echo '<input type="checkbox" name="delete_document_image_bottom" id="delete_document_image_bottom" /> <label for="delete_document_image_bottom">Supprimer l\'image</label><br /><br />';
}
?>

<?= $form->field($model, 'document_infos_quotation')
->textarea(['rows' => 8]) ?>
<?= $form->field($model, 'document_infos_invoice')

+ 112
- 0
backend/views/setting-admin/index.php 파일 보기

@@ -0,0 +1,112 @@
<?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 common\logic\Setting\SettingModule;
use lo\widgets\Toggle;
use yii\bootstrap\ActiveForm;
use yii\helpers\Html;

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

$settingModule = SettingModule::getInstance();
$adminSettingDefinition = $settingModule->getAdminSettingDefinition();

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

?>

<script>
var appInitValues = {
sectionsArray: <?php echo json_encode($adminSettingDefinition->getSectionsArray()); ?>,
};
</script>

<div class="setting-admin-index setting-form" id="app-setting-admin">

<div id="nav-params">
<a v-for="section in sectionsArray" :class="'btn '+((currentSection == section.name) ? 'btn-primary' : 'btn-default')"
@click="changeSection(section)" :href="'#'+section.name">
{{ section.nameDisplay }}
</a>
</div>

<?php $form = ActiveForm::begin(); ?>
<?php foreach($adminSettingDefinition->getSettingDetails() as $sectionName => $sectionsArray): ?>
<div v-show="currentSection == '<?= $sectionName ?>'" class="panel panel-default">
<div class="panel-body">
<?php foreach($sectionsArray as $subSectionName => $subSectionsArray): ?>
<h4><?php echo $adminSettingDefinition->getSectionLabelBySectionName($subSectionName); ?></h4>
<?php foreach($subSectionsArray as $settingDetail): ?>
<?php echo field($form, $model, $settingDetail); ?>
<?php endforeach; ?>
<?php endforeach; ?>
</div>
</div>
<?php endforeach; ?>
<div class="form-group">
<?= Html::submitButton('Sauvegarder', ['class' => 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>

<?php

function field($form, $model, $settingDetail) {
$field = $form->field($model, $settingDetail->getName());
if($settingDetail->getFormType() == 'checkbox') {
return $field->checkbox();
}
elseif($settingDetail->getFormType() == 'toggle') {
return $form->field($model, $settingDetail->getName(), ['options' => ['class' => 'form-group form-toggle']])->widget(Toggle::class, ['options' => ['data-on' => 'Oui', 'data-off' => 'Non', 'data-offstyle' => 'default']]);
}
elseif($settingDetail->getFormType() == 'select') {
return $field->dropDownList($settingDetail->getOptions());
}
elseif($settingDetail->getFormType() == 'textarea') {
return $field->textarea(['rows' => 4]);
}
elseif($settingDetail->getFormType() == 'input') {
return $field->textInput();
}
else {
return '<div class="form-group"><span class="glyphicon glyphicon-alert"></span> Type de champ non défini pour le paramètre "'.$settingDetail->getName().'"</div>';
}
}

?>

+ 7
- 2
backend/views/support/index.php 파일 보기

@@ -41,7 +41,12 @@ use common\logic\Ticket\Ticket\Module\TicketModule;
use yii\helpers\Html;
use yii\grid\GridView;

/**
* @var $this common\components\ViewBackend
*/

$ticketModule = TicketModule::getInstance();
$adminSettingBag = $this->getSettingModule()->getAdminSettingBag();
$userCurrent = $this->getUserCurrent();
$this->setTitle('Support & contact');
$this->addBreadcrumb($this->getTitle());
@@ -62,7 +67,7 @@ $this->addBreadcrumb($this->getTitle());
<span class="info-box-text">Me contacter directement</span>
<span class="info-box-text">
<br/>
<strong><?= Yii::$app->parameterBag->get('adminPhoneNumber'); ?></strong>
<strong><?= $adminSettingBag->get('administratorPhoneNumber'); ?></strong>
</span>
</div>
</div>
@@ -89,7 +94,7 @@ $this->addBreadcrumb($this->getTitle());
<div class="info-box">
<span class="info-box-icon bg-yellow"><i class="fa fa-envelope"></i></span>
<div class="info-box-content">
<span class="info-box-text"><br/><?= Html::a("M'envoyer un email", 'mailto:'.Yii::$app->parameterBag->get('adminEmail'), ['class' => 'btn btn-sm btn-default']); ?></span>
<span class="info-box-text"><br/><?= Html::a("M'envoyer un email", 'mailto:'.$adminSettingBag->get('administratorEmail'), ['class' => 'btn btn-sm btn-default']); ?></span>
</div>
</div>
</div>

+ 44
- 1
backend/web/css/screen.css 파일 보기

@@ -428,7 +428,7 @@ a.btn.btn-primary .glyphicon-triangle-bottom, button.btn.btn-primary .glyphicon-

/* line 344, ../sass/screen.scss */
#nav-params {
margin-bottom: 30px;
margin-bottom: 20px;
}
/* line 347, ../sass/screen.scss */
#nav-params a {
@@ -2802,6 +2802,49 @@ termes.
width: 100px;
}

/* line 4, ../sass/setting/_form.scss */
.setting-form .panel h4 {
font-size: 23px;
margin-bottom: 20px;
text-transform: uppercase;
border-bottom: solid 1px gray;
}
/* line 10, ../sass/setting/_form.scss */
.setting-form .panel h4:not(:first-child) {
margin-top: 45px;
}
/* line 16, ../sass/setting/_form.scss */
.setting-form .form-group.has-success, .setting-form .form-group.has-success label {
color: #333;
}
/* line 21, ../sass/setting/_form.scss */
.setting-form .form-group .checkbox input {
position: relative;
top: -1px;
margin-right: 3px;
}
/* line 30, ../sass/setting/_form.scss */
.setting-form .form-group.form-toggle .control-label {
position: relative;
top: 7px;
left: 10px;
}
/* line 36, ../sass/setting/_form.scss */
.setting-form .form-group.form-toggle .toggle {
-moz-border-radius: 20px;
-webkit-border-radius: 20px;
border-radius: 20px;
float: left;
}
/* line 41, ../sass/setting/_form.scss */
.setting-form .form-group.form-toggle .toggle .toggle-group .btn.toggle-on {
color: white;
}
/* line 45, ../sass/setting/_form.scss */
.setting-form .form-group.form-toggle .toggle .toggle-group .btn.toggle-off {
border-color: white;
}

/**
Copyright distrib (2018)


+ 7
- 1
backend/web/js/backend.js 파일 보기

@@ -464,7 +464,13 @@ function opendistrib_features_index() {
.then(function (response) {
appAlerts.alertResponse(response);
});
})
});

opendistrib_sortable_list(
'.feature-admin-index',
'.btn-position',
'feature-admin/position'
);
}

function opendistrib_datepicker() {

+ 63
- 0
backend/web/js/vuejs/setting-form.js 파일 보기

@@ -0,0 +1,63 @@

/**
Copyright distrib (2018)

contact@opendistrib.net

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

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

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

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

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

var app = new Vue({
el: '#app-setting-admin',
data() {
return Object.assign({
currentSection: null,
sectionsArray: []
}, window.appInitValues);
},
methods: {
changeSection: function(section) {
this.currentSection = section.name ;
},
getInitialSection: function() {
var hash = window.location.hash.substring(1);
if(hash && hash.length) {
return hash;
}
return this.sectionsArray[0].name;
}
},
mounted: function() {
this.currentSection = this.getInitialSection();
}
});



+ 2
- 1
backend/web/sass/screen.scss 파일 보기

@@ -342,7 +342,7 @@ a.btn, button.btn {
}

#nav-params {
margin-bottom: 30px ;
margin-bottom: 20px ;

a {
margin-right: 10px ;
@@ -1535,4 +1535,5 @@ a.btn, button.btn {
@import "support/_view.scss";
@import "producer-admin/_index.scss";
@import "feature-admin/_index.scss";
@import "setting/_form.scss";
@import "_responsive.scss" ;

+ 52
- 0
backend/web/sass/setting/_form.scss 파일 보기

@@ -0,0 +1,52 @@

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

.form-group {
&.has-success, &.has-success label {
color: #333;
}

.checkbox {
input {
position: relative;
top: -1px;
margin-right: 3px;
}
}

&.form-toggle {

.control-label {
position: relative;
top: 7px;
left: 10px;
}

.toggle {
@include border-radius(20px);
float: left;

.toggle-group {
.btn.toggle-on {
//border-color: white;
color: white;
}
.btn.toggle-off {
border-color: white;
}
}
}
}
}
}

+ 2
- 0
common/assets/CommonAsset.php 파일 보기

@@ -62,6 +62,7 @@ class CommonAsset extends \common\components\MyAssetBundle
$this->addAsset('css','js/jquery-ui-1.11.4.custom/jquery-ui.min.css');
$this->addAsset('css','js/jquery-ui-1.11.4.custom/jquery-ui.theme.css');
$this->addAsset('css','js/vuejs/vcalendar/vcalendar.min.css') ;
$this->addAsset('css','js/simple-lightbox/simpleLightbox.min.css') ;
$this->addAsset('css','css/screen.css') ;
// js
@@ -69,6 +70,7 @@ class CommonAsset extends \common\components\MyAssetBundle
$this->addAsset('js','js/jquery-ui-1.11.4.custom/jquery-ui.min.js');
$this->addAsset('js','js/promise-polyfill/promise.min.js');
$this->addAsset('js','js/axios/axios.min.js');
$this->addAsset('js','js/simple-lightbox/simpleLightbox.min.js') ;
$this->addAsset('js','js/vuejs/vue.js');

// Documentation : https://vcalendar.io/

+ 2
- 1
common/components/BusinessLogic.php 파일 보기

@@ -15,6 +15,7 @@ class BusinessLogic
public function getModules()
{
return [
$this->getSettingModule(),
$this->getFeatureModule(),
$this->getFeatureProducerModule(),
$this->getUnitModule(),
@@ -54,7 +55,7 @@ class BusinessLogic
return $this->producerContext;
}

public function setProducerContext(Producer $producer)
public function setProducerContext(Producer $producer = null)
{
$this->producerContext = $producer;


+ 8
- 2
common/components/BusinessLogicTrait.php 파일 보기

@@ -11,8 +11,8 @@ use common\logic\Document\DeliveryNote\Module\DeliveryNoteModule;
use common\logic\Document\Document\Module\DocumentModule;
use common\logic\Document\Invoice\Module\InvoiceModule;
use common\logic\Document\Quotation\Module\QuotationModule;
use common\logic\Feature\Feature\Module\FeatureModule;
use common\logic\Feature\FeatureProducer\Module\FeatureProducerModule;
use common\logic\Feature\Feature\FeatureModule;
use common\logic\Feature\FeatureProducer\FeatureProducerModule;
use common\logic\Opinion\Module\OpinionModule;
use common\logic\Order\Order\Module\OrderModule;
use common\logic\Order\ProductOrder\Module\ProductOrderModule;
@@ -25,6 +25,7 @@ use common\logic\Product\Product\Module\ProductModule;
use common\logic\Product\ProductCategory\Module\ProductCategoryModule;
use common\logic\Product\ProductPointSale\Module\ProductPointSaleModule;
use common\logic\Product\ProductPrice\Module\ProductPriceModule;
use common\logic\Setting\SettingModule;
use common\logic\Subscription\ProductSubscription\Module\ProductSubscriptionModule;
use common\logic\Subscription\Subscription\Module\SubscriptionModule;
use common\logic\Ticket\Ticket\Module\TicketModule;
@@ -196,4 +197,9 @@ trait BusinessLogicTrait
{
return FeatureProducerModule::getInstance();
}

public function getSettingModule(): SettingModule
{
return SettingModule::getInstance();
}
}

+ 2
- 1
common/components/DolibarrApi.php 파일 보기

@@ -13,7 +13,8 @@ class DolibarrApi extends AbstractApi
public function createInvoice(int $idUser)
{
return $this->post(self::RESOURCE_INVOICES, [
'socid' => $idUser
'socid' => $idUser,
'cond_reglement_id' => 2
]);
}


+ 1
- 1
common/config/params.php 파일 보기

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

return [
'version' => '23.10.E',
'version' => '23.11.B',
'maintenanceMode' => false,
'siteName' => 'Opendistrib',
'adminEmail' => 'contact@opendistrib.net',

+ 8
- 0
common/logic/AbstractChecker.php 파일 보기

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

namespace common\logic;

abstract class AbstractChecker extends AbstractService implements CheckerInterface
{

}

+ 8
- 1
common/logic/AbstractRepository.php 파일 보기

@@ -67,7 +67,9 @@ abstract class AbstractRepository extends AbstractService implements RepositoryI
public function defaultFilterProducerContext(): void
{
$defaultOptions = $this->getDefaultOptionsSearch();
if(isset($defaultOptions['attribute_id_producer']) && $defaultOptions['attribute_id_producer']) {
if(isset($defaultOptions['attribute_id_producer'])
&& $defaultOptions['attribute_id_producer']
&& $this->getProducerContext()) {
$this->query->andWhere([$defaultOptions['attribute_id_producer'] => $this->getProducerContextId()]);
}
}
@@ -89,4 +91,9 @@ abstract class AbstractRepository extends AbstractService implements RepositoryI
{
return $this->createQuery();
}

public function queryDefaultAll()
{
return $this->createDefaultQuery();
}
}

+ 8
- 0
common/logic/AbstractResolver.php 파일 보기

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

namespace common\logic;

abstract class AbstractResolver extends AbstractService implements ResolverInterface
{

}

+ 1
- 0
common/logic/AbstractService.php 파일 보기

@@ -15,6 +15,7 @@ abstract class AbstractService extends AbstractSingleton implements ServiceInter
SolverInterface::class,
RepositoryQueryInterface::class,
RepositoryInterface::class,
CheckerInterface::class,
NotifierInterface::class,
BuilderInterface::class,
ResolverInterface::class,

+ 8
- 0
common/logic/CheckerInterface.php 파일 보기

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

namespace common\logic;

interface CheckerInterface
{

}

+ 120
- 29
common/logic/Distribution/Distribution/Export/DistributionShoppingCartLabelsPdfGenerator.php 파일 보기

@@ -4,34 +4,81 @@ namespace common\logic\Distribution\Distribution\Export;

use common\logic\AbstractGenerator;
use common\logic\Distribution\Distribution\Model\Distribution;
use common\logic\Feature\Feature\Feature;
use common\logic\Feature\Feature\FeatureChecker;
use common\logic\Feature\Feature\FeatureManager;
use common\logic\Order\Order\Model\Order;
use common\logic\Order\Order\Repository\OrderRepository;
use common\logic\Order\Order\Service\OrderSolver;
use common\logic\Producer\Producer\Service\ProducerSolver;
use kartik\mpdf\Pdf;
use yii\base\ErrorException;
use yii\helpers\BaseStringHelper;
use yii\helpers\Html;

class DistributionShoppingCartLabelsPdfGenerator extends AbstractGenerator implements DistributionExportGeneratorInterface
{
const FORMAT_70_42 = '70x42';

protected ProducerSolver $producerSolver;
protected OrderRepository $orderRepository;
protected OrderSolver $orderSolver;
protected FeatureChecker $featureChecker;

public function loadDependencies(): void
{
$this->producerSolver = $this->loadService(ProducerSolver::class);
$this->orderRepository = $this->loadService(OrderRepository::class);
$this->orderSolver = $this->loadService(OrderSolver::class);
$this->featureChecker = $this->loadService(FeatureChecker::class);
}

public function getSpecificFormatDetailsArray(): array
{
return [
self::FORMAT_70_42 => [70, 42]
];
}

public function generate(Distribution $distribution, bool $save = false)
{
$isSpecificFormat = false;
$exportShoppingCartLabelsFormat = $this->producerSolver->getConfig('export_shopping_cart_labels_format');
if($this->featureChecker->isEnabled(Feature::ALIAS_EXPORT_SHOPPING_CART_LABELS_ADVANCED)) {
if($exportShoppingCartLabelsFormat) {
$isSpecificFormat = true;
[$specificFormatWidth, $specificFormatHeight] = $this->getSpecificFormatDetails($exportShoppingCartLabelsFormat);
}
else {
throw new ErrorException("Aucun format d'étiquette n'est défini dans les paramètres.");
}
}

$ordersArray = $this->orderRepository->findOrdersByDistribution($distribution);
$ordersArray = $this->filterOrdersExcludedUsersAndPointSales($ordersArray);

$content = \Yii::$app->getView()->render('@backend/views/distribution/shopping-cart-labels.php', [
'distribution' => $distribution,
'ordersArray' => $ordersArray,
'isSpecificFormat' => $isSpecificFormat,
'shoppingCartLabelsPerColumn' => $this->producerSolver->getConfig('export_shopping_cart_labels_number_per_column') ?: 8
]);

$pdf = new Pdf([
if($isSpecificFormat) {
$pdf = $this->getPdf($distribution, $content, true, $specificFormatWidth, $specificFormatHeight);
}
else {
$pdf = $this->getPdf($distribution, $content, false);
$pdf->getApi()->SetColumns(4);
$pdf->getApi()->keepColumns = true;
}

return $pdf->render();
}

public function getPdf(Distribution $distribution, string $content, bool $isSpecificFormat, float $specificFormatWidth = 0, float $specificFormatHeight = 0)
{
return new Pdf([
'mode' => Pdf::MODE_UTF8,
'format' => Pdf::FORMAT_A4,
'orientation' => Pdf::ORIENT_PORTRAIT,
@@ -40,17 +87,8 @@ class DistributionShoppingCartLabelsPdfGenerator extends AbstractGenerator imple
'@app/web/pdf/Etiquettes-' . $distribution->date . '-' . $this->getProducerContextId() . '.pdf'
),
'content' => $content,
'cssInline' => $this->getCss(),
'methods' => [
'SetHeader' => ['Étiquettes du ' . date('d/m/Y', strtotime($distribution->date))],
'SetFooter' => ['{PAGENO}'],
],
'cssInline' => $this->getCss($isSpecificFormat, $specificFormatWidth, $specificFormatHeight),
]);

$pdf->getApi()->SetColumns(4);
$pdf->getApi()->keepColumns = true;

return $pdf->render();
}

public function filterOrdersExcludedUsersAndPointSales(array $ordersArray)
@@ -77,9 +115,9 @@ class DistributionShoppingCartLabelsPdfGenerator extends AbstractGenerator imple
return $order->pointSale && $order->pointSale->exclude_export_shopping_cart_labels;
}

public function getCss(): string
public function getCss(bool $isSpecificFormat = false, float $specificFormatWith = 0, float $specificFormatHeight = 0): string
{
return '
$css = '
@page {
margin: 0px 0px 0px 0px !important;
padding: 0px 0px 0px 0px !important;
@@ -90,21 +128,6 @@ class DistributionShoppingCartLabelsPdfGenerator extends AbstractGenerator imple
font-size: 13px;
}
.clr {
clear: both;
}
.shopping-cart-label {
-webkit-column-break-inside:avoid;
column-break-inside: avoid;
-webkit-page-break-inside:avoid;
page-break-inside: avoid;
}
.shopping-cart-label .inner {
padding: 8px;
}
.shopping-cart-label .username {
font-weight: bold;
font-size: 13px;
@@ -119,7 +142,75 @@ class DistributionShoppingCartLabelsPdfGenerator extends AbstractGenerator imple
.shopping-cart-label .products {
font-size: 10px;
}';

if($isSpecificFormat) {

$paddingShoppingCartLabel = 3;
$specificFormatWith = $specificFormatWith - 2 * $paddingShoppingCartLabel;
$specificFormatHeight = $specificFormatHeight - 2 * $paddingShoppingCartLabel;

$css .= '
.shopping-cart-label {
box-sizing: border-box;
padding: '.$paddingShoppingCartLabel.'mm;
width: '.$specificFormatWith.'mm;
height: '.$specificFormatHeight.'mm;
display: block;
float: left;
}';
}
else {
$css .= '
.shopping-cart-label {
-webkit-column-break-inside:avoid;
column-break-inside: avoid;
-webkit-page-break-inside:avoid;
page-break-inside: avoid;
}
';
.shopping-cart-label .inner {
padding: 8px;
}
';
}

return $css;
}

public function getShoppingCartLabelAsHtml(Order $order): string
{
return '<div class="shopping-cart-label">
<div class="inner">
<div class="username">
'.$this->orderSolver->getOrderUsername($order).'
</div>
<div class="point-sale">
'.date('d/m', strtotime($order->distribution->date)).' &bull;
'.$order->pointSale->name.'
</div>
<div class="products">
'.$this->orderRepository->getCartSummary($order).'
</div>
</div>
</div>';
}

public function getSpecificFormatDetails(string $name): array
{
$specificFormatDetailsArray = $this->getSpecificFormatDetailsArray();
if(!isset($specificFormatDetailsArray[$name])) {
throw new ErrorException("Format d'étiquette inconnu");
}
return $specificFormatDetailsArray[$name];
}

public function populateDropdownSpecificFormats(): array
{
$specificFormatsArray = [null => '--'];
foreach($this->getSpecificFormatDetailsArray() as $key => $specificFormat) {
$specificFormatsArray[$key] = $specificFormat[0].'x'.$specificFormat[1].' mm';
}
return $specificFormatsArray;
}
}

+ 7
- 1
common/logic/Document/Document/Service/DocumentManager.php 파일 보기

@@ -52,7 +52,13 @@ class DocumentManager extends AbstractManager
]);

$contentFooter = '<div id="footer">';
$contentFooter .= '<div class="infos-bottom">' . Html::encode($producer->document_infos_bottom) . '</div>';
if ($this->producerSolver->getConfig('document_image_bottom')) {
$urlDocumentImageBottom = \Yii::$app->urlManagerProducer->getHostInfo() . '/' . \Yii::$app->urlManagerProducer->baseUrl . '/uploads/' . $producer->document_image_bottom;
$contentFooter .= '<div class="image"><img src="'.$urlDocumentImageBottom.'" style="max-height:80px;" /></div>';
}
if ($this->producerSolver->getConfig('document_infos_bottom')) {
$contentFooter .= '<div class="infos-bottom">'.nl2br($producer->document_infos_bottom).'</div>';
}
$contentFooter .= '</div>';

$marginBottom = 10;

common/logic/Feature/Feature/Model/Feature.php → common/logic/Feature/Feature/Feature.php 파일 보기

@@ -36,17 +36,19 @@
* termes.
*/

namespace common\logic\Feature\Feature\Model;
namespace common\logic\Feature\Feature;

use common\components\ActiveRecordCommon;
use common\logic\Feature\FeatureProducer\Model\FeatureProducer;
use common\logic\Feature\FeatureProducer\FeatureProducer;
use yii\db\ActiveQuery;
use yii\db\Schema;

class Feature extends ActiveRecordCommon
{
const ALIAS_CONTACT = 'contact';
const ALIAS_PRODUCT_PRICE_IMPORT = 'product_price_import';
const ALIAS_ONLINE_PAYMENT = 'online_payment';
const ALIAS_EXPORT_SHOPPING_CART_LABELS_ADVANCED = 'export_shopping_cart_labels_advanced';
const ALIAS_SETTINGS = 'settings';

/**
* @inheritdoc
@@ -64,6 +66,7 @@ class Feature extends ActiveRecordCommon
return [
[['alias', 'name'], 'required'],
[['status', 'is_paid_feature', 'only_for_selected_producers'], 'boolean'],
[['position'], 'integer'],
[['price'], 'double'],
[['alias', 'name', 'description'], 'string'],
];
@@ -82,7 +85,8 @@ class Feature extends ActiveRecordCommon
'status' => 'Statut',
'is_paid_feature' => "Fonctionnalité payante",
'price' => 'Prix',
'only_for_selected_producers' => 'Uniquement pour les producteurs sélectionnés'
'only_for_selected_producers' => 'Uniquement pour les producteurs sélectionnés',
'position' => 'Position'
];
}


common/logic/Feature/Feature/Service/FeatureBuilder.php → common/logic/Feature/Feature/FeatureBuilder.php 파일 보기

@@ -1,10 +1,8 @@
<?php

namespace common\logic\Feature\Feature\Service;
namespace common\logic\Feature\Feature;

use common\logic\AbstractBuilder;
use common\logic\Feature\Feature\Model\Feature;
use common\logic\Feature\Feature\Repository\FeatureRepository;

class FeatureBuilder extends AbstractBuilder
{
@@ -38,7 +36,7 @@ class FeatureBuilder extends AbstractBuilder
public function createFeature(
string $alias,
string $name,
string $description,
string $description = '',
bool $status = true,
bool $isPaidFeature = false,
float $price = null

+ 48
- 0
common/logic/Feature/Feature/FeatureChecker.php 파일 보기

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

namespace common\logic\Feature\Feature;

use common\logic\AbstractChecker;
use common\logic\Feature\FeatureProducer\FeatureProducerRepository;
use yii\base\ErrorException;

class FeatureChecker extends AbstractChecker
{
protected FeatureRepository $featureRepository;
protected FeatureProducerRepository $featureProducerRepository;

public function loadDependencies(): void
{
$this->featureRepository = $this->loadService(FeatureRepository::class);
$this->featureProducerRepository = $this->loadService(FeatureProducerRepository::class);
}

public function isEnabled(string $aliasFeature): bool
{
$feature = $this->featureRepository->findOneFeatureByAlias($aliasFeature);
if(!$feature) {
throw new ErrorException("Fonctionnalité avec l'alias '".$aliasFeature."' non trouvée");
}

if(!$feature->status) {
return false;
}

$featureProducer = $this->featureProducerRepository->findOneFeatureProducer($feature);
if(!$featureProducer || is_null($featureProducer->status)) {
if($feature->is_paid_feature || $feature->only_for_selected_producers) {
return false;
}

return $feature->status;
}
else {
return $featureProducer->status;
}
}

public function isDisabled(string $aliasFeature): bool
{
return !$this->isEnabled($aliasFeature);
}
}

+ 24
- 0
common/logic/Feature/Feature/FeatureDefinition.php 파일 보기

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

namespace common\logic\Feature\Feature;

use common\logic\AbstractDefinition;

class FeatureDefinition extends AbstractDefinition
{
public function getEntityFqcn(): string
{
return Feature::class;
}

public function getFeaturesBase(): array
{
return [
Feature::ALIAS_CONTACT => 'Formulaire de contact',
Feature::ALIAS_PRODUCT_PRICE_IMPORT => 'Produits : import prix',
Feature::ALIAS_ONLINE_PAYMENT => 'Paiement en ligne',
Feature::ALIAS_EXPORT_SHOPPING_CART_LABELS_ADVANCED => "Génération d'étiquettes avec un format spécifique",
Feature::ALIAS_SETTINGS => 'Système de paramètres'
];
}
}

common/logic/Feature/Feature/Service/FeatureImporter.php → common/logic/Feature/Feature/FeatureImporter.php 파일 보기

@@ -1,6 +1,6 @@
<?php

namespace common\logic\Feature\Feature\Service;
namespace common\logic\Feature\Feature;

use common\logic\AbstractManager;

@@ -19,13 +19,10 @@ class FeatureImporter extends AbstractManager
{
$featuresBaseArray = $this->featureDefinition->getFeaturesBase();

foreach($featuresBaseArray as $alias => $featureBase) {
foreach($featuresBaseArray as $alias => $featureBaseName) {
$this->featureBuilder->createFeature(
$alias,
$featureBase['name'],
'',
$featureBase['status'],
$featureBase['is_paid_feature']
$featureBaseName
);
}
}

+ 43
- 0
common/logic/Feature/Feature/FeatureManager.php 파일 보기

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

namespace common\logic\Feature\Feature;

use common\logic\AbstractManager;
use common\logic\Feature\FeatureProducer\FeatureProducerBuilder;

class FeatureManager extends AbstractManager
{
protected FeatureBuilder $featureBuilder;
protected FeatureProducerBuilder $featureProducerBuilder;

public function loadDependencies(): void
{
$this->featureBuilder = $this->loadService(FeatureBuilder::class);
$this->featureProducerBuilder = $this->loadService(FeatureProducerBuilder::class);
}

public function enableFeature(Feature $feature): void
{
$this->featureBuilder->updateStatus($feature, true);
}

public function disableFeature(Feature $feature): void
{
$this->featureBuilder->updateStatus($feature, false);
}

public function enableFeatureForProducer(Feature $feature)
{
$this->featureProducerBuilder->updateStatusByFeature($feature, true);
}

public function disableFeatureForProducer(Feature $feature)
{
$this->featureProducerBuilder->updateStatusByFeature($feature, false);
}

public function defaultFeatureForProducer(Feature $feature)
{
$this->featureProducerBuilder->updateStatusByFeature($feature, null);
}
}

common/logic/Feature/Feature/Module/FeatureModule.php → common/logic/Feature/Feature/FeatureModule.php 파일 보기

@@ -1,13 +1,8 @@
<?php

namespace common\logic\Feature\Feature\Module;
namespace common\logic\Feature\Feature;

use common\logic\AbstractModule;
use common\logic\Feature\Feature\Repository\FeatureRepository;
use common\logic\Feature\Feature\Service\FeatureBuilder;
use common\logic\Feature\Feature\Service\FeatureDefinition;
use common\logic\Feature\Feature\Service\FeatureImporter;
use common\logic\Feature\Feature\Service\FeatureManager;

class FeatureModule extends AbstractModule
{
@@ -16,6 +11,7 @@ class FeatureModule extends AbstractModule
return [
FeatureDefinition::class,
FeatureRepository::class,
FeatureChecker::class,
FeatureBuilder::class,
FeatureImporter::class,
FeatureManager::class
@@ -32,6 +28,11 @@ class FeatureModule extends AbstractModule
return FeatureRepository::getInstance();
}

public function getChecker(): FeatureChecker
{
return FeatureChecker::getInstance();
}

public function getBuilder(): FeatureBuilder
{
return FeatureBuilder::getInstance();

common/logic/Feature/Feature/Repository/FeatureRepository.php → common/logic/Feature/Feature/FeatureRepository.php 파일 보기

@@ -1,9 +1,8 @@
<?php

namespace common\logic\Feature\Feature\Repository;
namespace common\logic\Feature\Feature;

use common\logic\AbstractRepository;
use common\logic\Feature\Feature\Model\Feature;

class FeatureRepository extends AbstractRepository
{
@@ -19,7 +18,7 @@ class FeatureRepository extends AbstractRepository
return [
self::WITH => ['featureProducers'],
self::JOIN_WITH => [],
self::ORDER_BY => '',
self::ORDER_BY => 'position ASC',
self::ATTRIBUTE_ID_PRODUCER => ''
];
}
@@ -37,4 +36,11 @@ class FeatureRepository extends AbstractRepository
->filterByAlias($alias)
->findOne();
}

public function findPaidFeatures(): array
{
return $this->createQuery()
->filterIsPaidFeature()
->find();
}
}

common/logic/Feature/Feature/Repository/FeatureRepositoryQuery.php → common/logic/Feature/Feature/FeatureRepositoryQuery.php 파일 보기

@@ -1,9 +1,8 @@
<?php

namespace common\logic\Feature\Feature\Repository;
namespace common\logic\Feature\Feature;

use common\logic\AbstractRepositoryQuery;
use common\logic\Feature\Feature\Service\FeatureDefinition;

class FeatureRepositoryQuery extends AbstractRepositoryQuery
{
@@ -19,4 +18,12 @@ class FeatureRepositoryQuery extends AbstractRepositoryQuery
$this->andWhere(['alias' => $alias]);
return $this;
}

public function filterIsPaidFeature(): self
{
$this->andWhere(['is_paid_feature' => true]);
return $this;
}


}

+ 0
- 31
common/logic/Feature/Feature/Service/FeatureDefinition.php 파일 보기

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

namespace common\logic\Feature\Feature\Service;

use common\logic\AbstractDefinition;
use common\logic\Feature\Feature\Model\Feature;

class FeatureDefinition extends AbstractDefinition
{
public function getEntityFqcn(): string
{
return Feature::class;
}

public function getFeaturesBase(): array
{
return [
Feature::ALIAS_CONTACT => $this->buildFeatureArray('Formulaire de contact', true, false),
Feature::ALIAS_PRODUCT_PRICE_IMPORT => $this->buildFeatureArray('Produits : import prix', true, false),
];
}

public function buildFeatureArray(string $name, bool $status, bool $isPaidFeature): array
{
return [
'name' => $name,
'status' => $status,
'is_paid_feature' => $isPaidFeature
];
}
}

+ 0
- 80
common/logic/Feature/Feature/Service/FeatureManager.php 파일 보기

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

namespace common\logic\Feature\Feature\Service;

use common\logic\AbstractManager;
use common\logic\Feature\Feature\Model\Feature;
use common\logic\Feature\Feature\Repository\FeatureRepository;
use common\logic\Feature\FeatureProducer\Repository\FeatureProducerRepository;
use common\logic\Feature\FeatureProducer\Service\FeatureProducerBuilder;
use yii\base\ErrorException;

class FeatureManager extends AbstractManager
{
protected FeatureRepository $featureRepository;
protected FeatureProducerRepository $featureProducerRepository;
protected FeatureBuilder $featureBuilder;
protected FeatureProducerBuilder $featureProducerBuilder;

public function loadDependencies(): void
{
$this->featureRepository = $this->loadService(FeatureRepository::class);
$this->featureProducerRepository = $this->loadService(FeatureProducerRepository::class);
$this->featureBuilder = $this->loadService(FeatureBuilder::class);
$this->featureProducerBuilder = $this->loadService(FeatureProducerBuilder::class);
}

public function isEnabled(string $aliasFeature): bool
{
$feature = $this->featureRepository->findOneFeatureByAlias($aliasFeature);
if(!$feature) {
throw new ErrorException("Fonctionnalité avec l'alias '".$aliasFeature."' non trouvée");
}

if(!$feature->status) {
return false;
}

$featureProducer = $this->featureProducerRepository->findOneFeatureProducer($feature);
if(!$featureProducer || is_null($featureProducer->status)) {
if($feature->is_paid_feature || $feature->only_for_selected_producers) {
return false;
}

return $feature->status;
}
else {
return $featureProducer->status;
}
}

public function isDisabled(string $aliasFeature): bool
{
return !$this->isEnabled($aliasFeature);
}

public function enable(Feature $feature): void
{
$this->featureBuilder->updateStatus($feature, true);
}

public function disable(Feature $feature): void
{
$this->featureBuilder->updateStatus($feature, false);
}

public function enableForProducer(Feature $feature)
{
$this->featureProducerBuilder->updateStatusByFeature($feature, true);
}

public function disableForProducer(Feature $feature)
{
$this->featureProducerBuilder->updateStatusByFeature($feature, false);
}

public function defaultForProducer(Feature $feature)
{
$this->featureProducerBuilder->updateStatusByFeature($feature, null);
}
}

common/logic/Feature/FeatureProducer/Model/FeatureProducer.php → common/logic/Feature/FeatureProducer/FeatureProducer.php 파일 보기

@@ -36,10 +36,10 @@
* termes.
*/

namespace common\logic\Feature\FeatureProducer\Model;
namespace common\logic\Feature\FeatureProducer;

use common\components\ActiveRecordCommon;
use common\logic\Feature\Feature\Model\Feature;
use common\logic\Feature\Feature\Feature;
use common\logic\Producer\Producer\Model\Producer;
use yii\db\ActiveQuery;


common/logic/Feature/FeatureProducer/Service/FeatureProducerBuilder.php → common/logic/Feature/FeatureProducer/FeatureProducerBuilder.php 파일 보기

@@ -1,12 +1,9 @@
<?php

namespace common\logic\Feature\FeatureProducer\Service;
namespace common\logic\Feature\FeatureProducer;

use common\logic\AbstractBuilder;
use common\logic\Feature\Feature\Model\Feature;
use common\logic\Feature\FeatureProducer\Model\FeatureProducer;
use common\logic\Feature\FeatureProducer\Repository\FeatureProducerRepository;
use common\logic\Producer\Producer\Model\Producer;
use common\logic\Feature\Feature\Feature;

class FeatureProducerBuilder extends AbstractBuilder
{

+ 13
- 0
common/logic/Feature/FeatureProducer/FeatureProducerDefinition.php 파일 보기

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

namespace common\logic\Feature\FeatureProducer;

use common\logic\AbstractDefinition;

class FeatureProducerDefinition extends AbstractDefinition
{
public function getEntityFqcn(): string
{
return FeatureProducer::class;
}
}

common/logic/Feature/FeatureProducer/Module/FeatureProducerModule.php → common/logic/Feature/FeatureProducer/FeatureProducerModule.php 파일 보기

@@ -1,11 +1,8 @@
<?php

namespace common\logic\Feature\FeatureProducer\Module;
namespace common\logic\Feature\FeatureProducer;

use common\logic\AbstractModule;
use common\logic\Feature\FeatureProducer\Repository\FeatureProducerRepository;
use common\logic\Feature\FeatureProducer\Service\FeatureProducerBuilder;
use common\logic\Feature\FeatureProducer\Service\FeatureProducerDefinition;

class FeatureProducerModule extends AbstractModule
{
@@ -14,7 +11,7 @@ class FeatureProducerModule extends AbstractModule
return [
FeatureProducerDefinition::class,
FeatureProducerRepository::class,
FeatureProducerBuilder::class
FeatureProducerBuilder::class,
];
}


common/logic/Feature/FeatureProducer/Repository/FeatureProducerRepository.php → common/logic/Feature/FeatureProducer/FeatureProducerRepository.php 파일 보기

@@ -1,10 +1,9 @@
<?php

namespace common\logic\Feature\FeatureProducer\Repository;
namespace common\logic\Feature\FeatureProducer;

use common\logic\AbstractRepository;
use common\logic\Feature\Feature\Model\Feature;
use common\logic\Producer\Producer\Model\Producer;
use common\logic\Feature\Feature\Feature;

class FeatureProducerRepository extends AbstractRepository
{

common/logic/Feature/FeatureProducer/Repository/FeatureProducerRepositoryQuery.php → common/logic/Feature/FeatureProducer/FeatureProducerRepositoryQuery.php 파일 보기

@@ -1,10 +1,9 @@
<?php

namespace common\logic\Feature\FeatureProducer\Repository;
namespace common\logic\Feature\FeatureProducer;

use common\logic\AbstractRepositoryQuery;
use common\logic\Feature\Feature\Model\Feature;
use common\logic\Feature\FeatureProducer\Service\FeatureProducerDefinition;
use common\logic\Feature\Feature\Feature;

class FeatureProducerRepositoryQuery extends AbstractRepositoryQuery
{
@@ -17,7 +16,7 @@ class FeatureProducerRepositoryQuery extends AbstractRepositoryQuery

public function filterByFeature(Feature $feature): self
{
$this->andWhere(['id_feature' => $feature]);
$this->andWhere(['id_feature' => $feature->id]);
return $this;
}
}

+ 0
- 25
common/logic/Feature/FeatureProducer/Service/FeatureProducerDefinition.php 파일 보기

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

namespace common\logic\Feature\FeatureProducer\Service;

use common\logic\AbstractDefinition;
use common\logic\Feature\FeatureProducer\Model\FeatureProducer;
use common\logic\Feature\FeatureProducer\Repository\FeatureProducerRepository;

class FeatureProducerDefinition extends AbstractDefinition
{
public function getEntityFqcn(): string
{
return FeatureProducer::class;
}

public function getDefinition(): FeatureProducerDefinition
{
return FeatureProducerDefinition::getInstance();
}

public function getRepository(): FeatureProducerRepository
{
return FeatureProducerRepository::getInstance();
}
}

+ 4
- 2
common/logic/Order/Order/Service/TillerManager.php 파일 보기

@@ -21,8 +21,10 @@ class TillerManager extends AbstractManager
public function loadDependencies(): void
{
$this->producerSolver = $this->loadService(ProducerSolver::class);
$this->tillerActivated = $this->producerSolver->getConfig('tiller');
$this->tillerClient = $this->getClient();
if($this->producerSolver->getProducerContext(false)) {
$this->tillerActivated = $this->producerSolver->getConfig('tiller');
$this->tillerClient = $this->getClient();
}
$this->orderSolver = $this->loadService(OrderSolver::class);
$this->orderBuilder = $this->loadService(OrderBuilder::class);
$this->productOrderSolver = $this->loadService(ProductOrderSolver::class);

+ 3
- 2
common/logic/PointSale/PointSale/Model/PointSale.php 파일 보기

@@ -86,7 +86,7 @@ class PointSale extends ActiveRecordCommon
[['id_producer', 'id_user', 'maximum_number_orders', 'status'], 'integer'],
['id_producer', 'required'],
[['users', 'users_comment', 'code'], 'safe'],
[['product_price_percent'], 'double'],
[['product_price_percent', 'minimum_order_amount'], 'double'],
];
}

@@ -129,7 +129,8 @@ class PointSale extends ActiveRecordCommon
'button_generate_delivery_note_point_sale' => 'Activer le bouton de génération de bon de livraison par point de vente',
'button_generate_delivery_note_each_user' => 'Activer le bouton de génération de bon de livraison par client',
'exclude_export_shopping_cart_labels' => "Exclure de l'export d'étiquettes",
'is_home_delivery' => "Livraison à domicile"
'is_home_delivery' => "Livraison à domicile",
'minimum_order_amount' => "Montant minimum de commande (€)"
];
}


+ 14
- 4
common/logic/Producer/Producer/Model/Producer.php 파일 보기

@@ -116,6 +116,11 @@ class Producer extends ActiveRecordCommon
*/
public $photoFile;

/**
* @var UploadedFile
*/
public $document_image_bottomFile;

/**
* @inheritdoc
*/
@@ -138,7 +143,7 @@ class Producer extends ActiveRecordCommon
return $model->tiller == true;
}
],
[['logoFile', 'photoFile'], 'file', 'extensions' => 'png, jpg, jpeg', 'mimeTypes' => 'image/png, image/jpeg'],
[['logoFile', 'photoFile', 'document_image_bottomFile'], 'file', 'extensions' => 'png, jpg, jpeg', 'mimeTypes' => 'image/png, image/jpeg'],
[
[
'order_delay',
@@ -241,6 +246,8 @@ class Producer extends ActiveRecordCommon
'option_testimony',
'contact_email',
'admin_comment',
'export_shopping_cart_labels_format',
'document_image_bottom',
],
'string'
],
@@ -290,6 +297,7 @@ class Producer extends ActiveRecordCommon
'siret',
'logo',
'photo',
'document_image_bottom',
'postcode',
'city',
'code',
@@ -339,8 +347,8 @@ class Producer extends ActiveRecordCommon
'id' => 'ID',
'name' => 'Nom',
'siret' => 'Siret',
'logo' => 'Logo',
'photo' => 'Photo',
'logoFile' => 'Logo',
'photoFile' => 'Photo',
'description' => 'Description',
'postcode' => 'Code postal',
'city' => 'Ville',
@@ -398,6 +406,7 @@ class Producer extends ActiveRecordCommon
'document_delivery_note_first_reference' => 'Première référence des bons de livraison',
'document_infos_top' => 'Informations affichées en haut des documents',
'document_infos_bottom' => 'Informations affichées en bas des documents',
'document_image_bottomFile' => "Image affichée en bas des documents",
'document_infos_quotation' => 'Informations affichées en bas des devis',
'document_infos_invoice' => 'Informations affichées en bas des factures',
'document_infos_delivery_note' => 'Informations affichées en bas des bons de livraison',
@@ -455,9 +464,10 @@ class Producer extends ActiveRecordCommon
'option_invoice_only_based_on_delivery_notes' => 'Facturer uniquement sur la base des bons de livraison',
'option_document_width_logo' => 'Largeur du logo dans les documents',
'export_shopping_cart_labels_number_per_column' => "Étiquettes (PDF) : nombre d'étiquettes par colonne",
'export_shopping_cart_labels_format' => 'Étiquettes (PDF) : format',
'option_document_display_price_unit_reference' => "Afficher les prix au kilogramme",
'id_user_group_default' => "Groupe utilisateur par défaut attribué à l'inscription",
'option_check_by_default_prevent_user_credit' => "Par défaut, prévenir l'utilisateur quand on crédite son compte"
'option_check_by_default_prevent_user_credit' => "Par défaut, prévenir l'utilisateur quand on crédite son compte",
];
}


+ 9
- 4
common/logic/ProducerContextTrait.php 파일 보기

@@ -7,18 +7,18 @@ use yii\base\ErrorException;

trait ProducerContextTrait
{
protected ?Producer $producerContext = null;
protected ?Producer $producerContext;

public function setProducerContext(Producer $producer): self
public function setProducerContext(Producer $producer = null): self
{
$this->producerContext = $producer;

return $this;
}

public function getProducerContext(): Producer
public function getProducerContext(bool $throwExceptionIfNull = true): ?Producer
{
if(is_null($this->producerContext)) {
if($throwExceptionIfNull && is_null($this->producerContext)) {
throw new ErrorException("Le contexte producteur n'est pas défini.");
}

@@ -29,4 +29,9 @@ trait ProducerContextTrait
{
return $this->getProducerContext()->id;
}

public function isOutOfProducerContext(): bool
{
return !$this->producerContext;
}
}

+ 39
- 0
common/logic/Setting/AdminSettingBag.php 파일 보기

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

namespace common\logic\Setting;

use common\logic\AbstractManager;
use common\logic\Setting\SettingBuilder;
use common\logic\Setting\SettingRepository;

class AdminSettingBag extends AbstractManager
{
protected SettingRepository $settingRepository;
protected SettingBuilder $settingBuilder;

public function loadDependencies(): void
{
$this->settingRepository = $this->loadService(SettingRepository::class);
$this->settingBuilder = $this->loadService(SettingBuilder::class);
}

public function get(string $name)
{
$setting = $this->settingRepository->findOneAdminSettingByName($name);

if($setting) {
return $setting->getValue();
}

return null;
}

public function set(string $name, $value)
{
$setting = $this->settingBuilder->createSetting($name);

if($setting) {
$this->settingBuilder->updateValue($setting, $value);
}
}
}

+ 108
- 0
common/logic/Setting/Setting.php 파일 보기

@@ -0,0 +1,108 @@
<?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\logic\Setting;

use common\components\ActiveRecordCommon;
use common\logic\Producer\Producer\Model\Producer;

class Setting extends ActiveRecordCommon
{
/**
* @inheritdoc
*/
public static function tableName()
{
return 'setting';
}

/**
* @inheritdoc
*/
public function rules()
{
return [
[['name'], 'required'],
[['id_producer'], 'integer'],
[['id_producer'], 'exist', 'skipOnError' => true, 'targetClass' => Producer::class, 'targetAttribute' => ['id_producer' => 'id']],
[['name', 'string', 'text'], 'string'],
[['name','string'], 'string', 'max' => 255],
[['date'], 'date', 'format' => 'php:Y-m-d'],
[['integer'], 'integer'],
[['float', 'double'], 'number'],
[['boolean'], 'boolean'],
[['name'], 'unique'],
];
}

/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'id_producer' => 'Producteur',
'name' => 'Nom',
];
}

public function getValue()
{
$settingDetail = $this->getSettingDetail();
$type = $settingDetail->getType();
return $this->$type;
}

public function getSettingDetail()
{
return SettingDefinition::getInstance()->getSettingDetailByName($this->name);
}

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

public function populateProducer(Producer $producer = null): void
{
if($producer) {
$this->populateFieldObject('id_producer', 'producer', $producer);
}
}
}

+ 84
- 0
common/logic/Setting/SettingBuilder.php 파일 보기

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

namespace common\logic\Setting;

use common\logic\AbstractBuilder;
use common\logic\Producer\Producer\Model\Producer;
use yii\base\ErrorException;

class SettingBuilder extends AbstractBuilder
{
protected SettingDefinition $settingDefinition;
protected SettingRepository $settingRepository;

public function loadDependencies(): void
{
$this->settingDefinition = $this->loadService(SettingDefinition::class);
$this->settingRepository = $this->loadService(SettingRepository::class);
}

public function instanciateSetting(
string $name,
Producer $producer = null
): Setting
{
$setting = new Setting();
$setting->name = $name;
$setting->populateProducer($producer);
$this->initDefaultValue($setting);

return $setting;
}

public function createSetting(
string $name,
Producer $producer = null
): Setting
{
if($producer) {
$setting = $this->settingRepository->findOneProducerSettingByName($name);
}
else {
$setting = $this->settingRepository->findOneAdminSettingByName($name);
}

if(!$setting) {
$setting = $this->instanciateSetting($name);
$this->create($setting);
}

return $setting;
}

public function initValue(Setting $setting, $value)
{
$settingDetail = $this->settingDefinition->getSettingDetailByName($setting->name);
$type = $settingDetail->getType();
if(in_array($type, ['string', 'text', 'date', 'integer', 'float', 'double', 'boolean'])) {
$setting->$type = $value;
}
else {
throw new ErrorException("Le type de donnée du SettingDetail n'est pas reconnu.");
}
}

public function initDefaultValue(Setting $setting)
{
$settingDetail = $this->settingDefinition->getSettingDetailByName($setting->name);
if($settingDetail->getDefaultValue()) {
$this->initValue($setting, $settingDetail->getDefaultValue());
}
}

public function updateValue(Setting $setting, $value): void
{
$this->initValue($setting, $value);
$this->update($setting);
}

public function updateDefaultValueByName(Setting $setting)
{
$this->initDefaultValue($setting);
$this->update($setting);
}
}

+ 47
- 0
common/logic/Setting/SettingDefinition.php 파일 보기

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

namespace common\logic\Setting;

use common\logic\AbstractDefinition;
use common\logic\Setting\SettingDetails\Admin\AdminSettingDefinition;
use common\logic\Setting\SettingDetails\Producer\ProducerSettingDefinition;

class SettingDefinition extends AbstractDefinition
{
public function getEntityFqcn(): string
{
return Setting::class;
}

public function getSettingDetailByName(string $name)
{
$adminSettingDefinition = AdminSettingDefinition::getInstance();
$producerSettingDefinition = ProducerSettingDefinition::getInstance();

$settingDetail = $this->findSettingDetailInArray($name, $adminSettingDefinition->getSettingDetails());
if(!$settingDetail) {
$this->findSettingDetailInArray($name, $producerSettingDefinition->getSettingDetails());
}

if(!$settingDetail) {
throw new \Exception('SettingDetail non trouvé');
}

return $settingDetail;
}

public function findSettingDetailInArray(string $name, array $settingDetailsArray)
{
foreach($settingDetailsArray as $section => $subsectionsArray) {
foreach($subsectionsArray as $subsection => $settingDetailsArray) {
foreach($settingDetailsArray as $settingDetail) {
if($name == $settingDetail->getName()) {
return $settingDetail;
}
}
}
}

return null;
}
}

+ 170
- 0
common/logic/Setting/SettingDetails/AbstractSettingDetail.php 파일 보기

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

namespace common\logic\Setting\SettingDetails;

class AbstractSettingDetail
{
public string $name;
public string $label;
public string $type;
public string $section;
public ?string $formType = null;
public array $options = [];
public $defaultValue = null;
public ?string $subSection = null;
public ?string $helpMessage = null;

public function setName(string $name): self
{
$this->name = $name;
return $this;
}

public function getName(): string
{
return $this->name;
}

public function setLabel(string $label): self
{
$this->label = $label;
return $this;
}

public function getLabel(): string
{
return $this->label;
}

public function setSection(string $section): self
{
$this->section = $section;
return $this;
}

public function getSection(): string
{
return $this->section;
}

public function setSubSection(string $subSection): self
{
$this->subSection = $subSection;
return $this;
}

public function getSubSection(): ?string
{
return $this->subSection;
}

public function setHelpMessage(string $helpMessage): self
{
$this->helpMessage = $helpMessage;
return $this;
}

public function getHelpMessage(): ?string
{
return $this->helpMessage;
}

public function getFormType(): ?string
{
return $this->formType;
}

public function setFormTypeInput(): self
{
$this->formType = 'input';
return $this;
}

public function setFormTypeTextarea(): self
{
$this->formType = 'textarea';
return $this;
}

public function setFormTypeCheckbox(): self
{
$this->formType = 'checkbox';
return $this;
}

public function setFormTypeToggle(): self
{
$this->formType = 'toggle';
return $this;
}

public function setFormTypeSelect(array $options): self
{
$this->formType = 'select';
$this->options = $options;
return $this;
}

public function getOptions(): array
{
return $this->options;
}

public function setDefaultValue($defaultValue): self
{
$this->defaultValue = $defaultValue;
return $this;
}

public function getDefaultValue()
{
return $this->defaultValue;
}

public function setTypeString(): self
{
$this->type = 'string';
return $this;
}

public function setTypeText(): self
{
$this->type = 'text';
return $this;
}

public function setTypeBoolean(): self
{
$this->type = 'boolean';
return $this;
}

public function setTypeDate(): self
{
$this->type = 'date';
return $this;
}

public function setTypeInteger(): self
{
$this->type = 'integer';
return $this;
}

public function setTypeFloat(): self
{
$this->type = 'float';
return $this;
}

public function setTypeDouble(): self
{
$this->type = 'double';
return $this;
}

public function getType(): string
{
return $this->type;
}
}

+ 67
- 0
common/logic/Setting/SettingDetails/Admin/AdminSettingDefinition.php 파일 보기

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

namespace common\logic\Setting\SettingDetails\Admin;

use common\logic\Setting\SettingDefinition;
use common\logic\Setting\SettingDetails\Admin\General\AdministratorEmailAdminSetting;
use common\logic\Setting\SettingDetails\Admin\General\AdministratorPhoneNumberAdminSetting;

class AdminSettingDefinition extends SettingDefinition
{
const SECTION_GENERAL = 'general';
const SUBSECTION_GENERAL = 'general.main';

public function getSettingDetails(): array
{
return [
self::SECTION_GENERAL => [
self::SUBSECTION_GENERAL => [
new AdministratorEmailAdminSetting,
new AdministratorPhoneNumberAdminSetting,
]
],
];
}

public function getSectionLabels(): array
{
return [
self::SECTION_GENERAL => 'General',
self::SUBSECTION_GENERAL => 'General',
];
}

public function getSectionsArray(): array
{
$sectionsArray = [];

foreach($this->getSettingDetails() as $sectionName => $subsectionsArray) {
$sectionsArray[] = [
'name' => $sectionName,
'nameDisplay' => $this->getSectionLabelBySectionName($sectionName),
];
}

return $sectionsArray;
}

public function getSettingDetailsFlat(): array
{
$settingDetailsFlatArray = [];

foreach($this->getSettingDetails() as $sectionsArray) {
foreach($sectionsArray as $subsectionsArray) {
foreach($subsectionsArray as $settingDetail) {
$settingDetailsFlatArray[] = $settingDetail;
}
}
}

return $settingDetailsFlatArray;
}

public function getSectionLabelBySectionName(string $sectionName): string
{
return $this->getSectionLabels()[$sectionName];
}
}

+ 17
- 0
common/logic/Setting/SettingDetails/Admin/General/AdministratorEmailAdminSetting.php 파일 보기

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

namespace common\logic\Setting\SettingDetails\Admin\General;

use common\logic\Setting\SettingDetails\AbstractSettingDetail;

class AdministratorEmailAdminSetting extends AbstractSettingDetail
{
public function __construct()
{
$this
->setName('administratorEmail')
->setLabel("Email de l'administrateur")
->setTypeString()
->setFormTypeInput();
}
}

+ 17
- 0
common/logic/Setting/SettingDetails/Admin/General/AdministratorPhoneNumberAdminSetting.php 파일 보기

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

namespace common\logic\Setting\SettingDetails\Admin\General;

use common\logic\Setting\SettingDetails\AbstractSettingDetail;

class AdministratorPhoneNumberAdminSetting extends AbstractSettingDetail
{
public function __construct()
{
$this
->setName('administratorPhoneNumber')
->setLabel("Numéro de téléphone de l'administrateur")
->setTypeString()
->setFormTypeInput();
}
}

+ 30
- 0
common/logic/Setting/SettingDetails/Producer/ProducerSettingDefinition.php 파일 보기

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

namespace common\logic\Setting\SettingDetails\Producer;

use common\logic\Setting\SettingDefinition;

class ProducerSettingDefinition extends SettingDefinition
{
const SECTION_GENERAL = 'general';
const SUBSECTION_GENERAL = 'general.main';

public function getSettings(): array
{
return [
self::SECTION_GENERAL => [
self::SUBSECTION_GENERAL => [
// ...
]
]
];
}

public function getSectionLabels(): array
{
return [
self::SECTION_GENERAL => 'General',
self::SUBSECTION_GENERAL => 'General',
];
}
}

+ 37
- 0
common/logic/Setting/SettingImporter.php 파일 보기

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

namespace common\logic\Setting;

use common\logic\AbstractManager;
use common\logic\Setting\SettingDetails\Admin\AdminSettingDefinition;

class SettingImporter extends AbstractManager
{
protected AdminSettingDefinition $adminSettingDefinition;
protected SettingBuilder $settingBuilder;

public function loadDependencies(): void
{
$this->adminSettingDefinition = $this->loadService(AdminSettingDefinition::class);
$this->settingBuilder = $this->loadService(SettingBuilder::class);
}

public function importFromDefinitions()
{
$this->importFromAdminSettingDefinition();
$this->importFromProducerSettingDefinition();
}

public function importFromAdminSettingDefinition(): void
{
\Yii::$app->logic->setProducerContext(null);
foreach ($this->adminSettingDefinition->getSettingDetailsFlat() as $settingDetail) {
$this->settingBuilder->createSetting($settingDetail->getName());
}
}

public function importFromProducerSettingDefinition(): void
{
// /!\ bien définir le contexte producteur
}
}

+ 53
- 0
common/logic/Setting/SettingModule.php 파일 보기

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

namespace common\logic\Setting;

use common\logic\AbstractModule;
use common\logic\Setting\SettingDetails\Admin\AdminSettingDefinition;
use common\logic\Setting\SettingDetails\Producer\ProducerSettingDefinition;

class SettingModule extends AbstractModule
{
public function getServices(): array
{
return [
SettingDefinition::class,
AdminSettingDefinition::class,
ProducerSettingDefinition::class,
SettingRepository::class,
SettingBuilder::class,
SettingImporter::class,
AdminSettingBag::class
];
}

public function getDefinition(): SettingDefinition
{
return SettingDefinition::getInstance();
}

public function getAdminSettingDefinition(): AdminSettingDefinition
{
return AdminSettingDefinition::getInstance();
}

public function getProducerSettingDefinition(): ProducerSettingDefinition
{
return ProducerSettingDefinition::getInstance();
}

public function getRepository(): SettingRepository
{
return SettingRepository::getInstance();
}

public function getImporter(): SettingImporter
{
return SettingImporter::getInstance();
}

public function getAdminSettingBag(): AdminSettingBag
{
return AdminSettingBag::getInstance();
}
}

+ 50
- 0
common/logic/Setting/SettingRepository.php 파일 보기

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

namespace common\logic\Setting;

use common\logic\AbstractRepository;

class SettingRepository extends AbstractRepository
{
protected SettingRepositoryQuery $query;

public function loadDependencies(): void
{
$this->loadQuery(SettingRepositoryQuery::class);
}
public function getDefaultOptionsSearch(): array
{
return [
self::WITH => ['producer'],
self::JOIN_WITH => [],
self::ORDER_BY => '',
self::ATTRIBUTE_ID_PRODUCER => 'setting.id_producer'
];
}

public function findOneSettingByName(string $name)
{
if($this->isOutOfProducerContext()) {
return $this->findOneAdminSettingByName($name);
}
else {
return $this->findOneProducerSettingByName($name);
}
}

public function findOneAdminSettingByName(string $name)
{
return $this->createQuery()
->filterProducerIsNull()
->filterByName($name)
->findOne();
}

public function findOneProducerSettingByName(string $name)
{
return $this->createDefaultQuery()
->filterByName($name)
->findOne();
}
}

+ 27
- 0
common/logic/Setting/SettingRepositoryQuery.php 파일 보기

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

namespace common\logic\Setting;

use common\logic\AbstractRepositoryQuery;

class SettingRepositoryQuery extends AbstractRepositoryQuery
{
protected SettingDefinition $definition;

public function loadDependencies(): void
{
$this->loadDefinition(SettingDefinition::class);
}

public function filterProducerIsNull(): self
{
$this->andWhere(['id_producer' => null]);
return $this;
}

public function filterByName(string $name): self
{
$this->andWhere(['name' => $name]);
return $this;
}
}

+ 2
- 0
common/mail/orderConfirm-html.php 파일 보기

@@ -60,6 +60,8 @@ $orderModule = OrderModule::getInstance();
<?php endif; ?>
<?php if(strlen($pointSale->locality) > 0): ?> situé à <?= Html::encode($pointSale->locality) ?><?php endif ?>.</p>

<p>Retrouvez à tout moment votre commande dans l'espace de votre producteur via l'onglet <a href="<?= Yii::$app->urlManager->createAbsoluteUrl('order/history') ?>">Mes commandes</a>.</p>

<?php $payment_infos = $producerModule->getConfig('option_payment_info') ; ?>
<?php if($payment_infos && strlen($payment_infos) > 0): ?>
<p><strong>Informations de paiement :</strong><br />

+ 1
- 1
common/mail/paymentErrorProducer-text.php 파일 보기

@@ -45,7 +45,7 @@ $userModule = UserModule::getInstance();

Bonjour,

Le client <?= $userModule->getUsername() ?> vient de passer une commande pour le <?= date('d/m/Y',strtotime($distribution->date)) ?> mais le paiement par carte bancaire n'a pas abouti.
Le client <?= $userModule->getUsername($user) ?> vient de passer une commande pour le <?= date('d/m/Y',strtotime($distribution->date)) ?> mais le paiement par carte bancaire n'a pas abouti.
Il vient de recevoir un message pour régulariser le paiement par virement bancaire.

Sa commande a été enregistrée avec l'état "non payé".

+ 32
- 0
common/versions/23.11.A.php 파일 보기

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

require_once dirname(__FILE__).'/_macros.php';

version(
'13/11/2023',
[
[
"[Administration] Utilisateurs > liste : possibilité de filtrer par groupe,",
"[Administration] Utilisateurs > création : possibilité de choisir d'envoyer ou non l'email de bienvenue",
"[Administration] Utilisateurs > import : possibilité de choisir d'envoyer ou non l'email de bienvenue",
"[Administration] Documents : logo et nom/adresse du producteur sur la même ligne dans l'entête",
"[Administration] Documents : possibilité d'afficher le prix au kg dans la colonne 'Prix unitaire' des PDF",
"[Administration] Utilisateurs > formulaire crédit : possibilité de choisir si le champs 'Prévenir l'utilisateur' est coché par défaut ou non",
"[Boutique] Inscription : possibilité d'ajouter automatiquement un utilisateur à un groupe au moment de son inscription",
],
[
"[Administration] Tableau de bord : correctif lien export distribution cassé (404)",
]
],
[
[
"[Technique] Feature flag : première version"
],
[
"[Boutique] Contact : correctif spam et sender from",
]
],
$userCurrent
);

?>

+ 31
- 0
common/versions/23.11.B.php 파일 보기

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

require_once dirname(__FILE__).'/_macros.php';

version(
'20/11/2023',
[
[
"[Administration] Export : première version étiquettes avancées (formats spécifiques)",
"[Administration] Documents PDF : gestion images pied de page",
"[Administration et boutique] Points de vente : minimum de commande",
"[Boutique] Commander : envoi d'un email de confirmation lors d'une modification de commande",
"[Boutique] Produits : gestion de l'affichage en grand des images",
"[Boutique] Commander : suppression étape 'Confirmation'",
],
[
"[Administration] Communiquer > envoyer un email : correctif point de vente supprimé visible"
]
],
[
[
"[Global] Système de feature flag",
"[Technique] Paramètres : nouveau système de settings"
],
[
]
],
$userCurrent
);

?>

+ 386
- 0
common/web/js/simple-lightbox/simpleLightbox.css 파일 보기

@@ -0,0 +1,386 @@
.slbOverlay, .slbWrapOuter, .slbWrap {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
}

.slbOverlay {
overflow: hidden;
z-index: 2000;
background-color: #000;
opacity: 0.7;
-webkit-animation: slbOverlay 0.5s;
-moz-animation: slbOverlay 0.5s;
animation: slbOverlay 0.5s;
}

.slbWrapOuter {
overflow-x: hidden;
overflow-y: auto;
z-index: 2010;
}

.slbWrap {
position: absolute;
text-align: center;
}

.slbWrap:before {
content: "";
display: inline-block;
height: 100%;
vertical-align: middle;
}

.slbContentOuter {
position: relative;
display: inline-block;
vertical-align: middle;
margin: 0px auto;
padding: 0 1em;
box-sizing: border-box;
z-index: 2020;
text-align: left;
max-width: 100%;
}

.slbContentEl .slbContentOuter {
padding: 5em 1em;
}

.slbContent {
position: relative;
}

.slbContentEl .slbContent {
-webkit-animation: slbEnter 0.3s;
-moz-animation: slbEnter 0.3s;
animation: slbEnter 0.3s;
background-color: #fff;
box-shadow: 0 0.2em 1em rgba(0, 0, 0, 0.4);
}

.slbImageWrap {
-webkit-animation: slbEnter 0.3s;
-moz-animation: slbEnter 0.3s;
animation: slbEnter 0.3s;
position: relative;
}

.slbImageWrap:after {
content: "";
position: absolute;
left: 0;
right: 0;
top: 5em;
bottom: 5em;
display: block;
z-index: -1;
box-shadow: 0 0.2em 1em rgba(0, 0, 0, 0.6);
background-color: #FFF;
}

.slbDirectionNext .slbImageWrap {
-webkit-animation: slbEnterNext 0.4s;
-moz-animation: slbEnterNext 0.4s;
animation: slbEnterNext 0.4s;
}

.slbDirectionPrev .slbImageWrap {
-webkit-animation: slbEnterPrev 0.4s;
-moz-animation: slbEnterPrev 0.4s;
animation: slbEnterPrev 0.4s;
}

.slbImage {
width: auto;
max-width: 100%;
height: auto;
display: block;
line-height: 0;
box-sizing: border-box;
padding: 5em 0;
margin: 0 auto;
}

.slbCaption {
display: inline-block;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-wrap: normal;
font-size: 1.4em;
position: absolute;
left: 0;
right: 0;
bottom: 0;
padding: 0.71429em 0;
color: #fff;
color: rgba(255, 255, 255, 0.7);
text-align: center;
}

.slbCloseBtn, .slbArrow {
margin: 0;
padding: 0;
border: 0;
cursor: pointer;
background: none;
}

.slbCloseBtn::-moz-focus-inner, .slbArrow::-moz-focus-inner {
padding: 0;
border: 0;
}

.slbCloseBtn:hover, .slbArrow:hover {
opacity: 0.5;
}

.slbCloseBtn:active, .slbArrow:active {
opacity: 0.8;
}

.slbCloseBtn {
-webkit-animation: slbEnter 0.3s;
-moz-animation: slbEnter 0.3s;
animation: slbEnter 0.3s;
font-size: 3em;
width: 1.66667em;
height: 1.66667em;
line-height: 1.66667em;
position: absolute;
right: -0.33333em;
top: 0;
color: #fff;
color: rgba(255, 255, 255, 0.7);
text-align: center;
}

.slbLoading .slbCloseBtn {
display: none;
}

.slbLoadingText {
font-size: 1.4em;
color: #fff;
color: rgba(255, 255, 255, 0.9);
}

.slbArrows {
position: fixed;
top: 50%;
left: 0;
right: 0;
}

.slbLoading .slbArrows {
display: none;
}

.slbArrow {
position: absolute;
top: 50%;
margin-top: -5em;
width: 5em;
height: 10em;
opacity: 0.7;
text-indent: -999em;
overflow: hidden;
}

.slbArrow:before {
content: "";
position: absolute;
top: 50%;
left: 50%;
margin: -0.8em 0 0 -0.8em;
border: 0.8em solid transparent;
}

.slbArrow.next {
right: 0;
}

.slbArrow.next:before {
border-left-color: #fff;
}

.slbArrow.prev {
left: 0;
}

.slbArrow.prev:before {
border-right-color: #fff;
}

.slbIframeCont {
width: 80em;
height: 0;
overflow: hidden;
padding-top: 56.25%;
margin: 5em 0;
}

.slbIframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
box-shadow: 0 0.2em 1em rgba(0, 0, 0, 0.6);
background: #000;
}

@-webkit-keyframes slbOverlay {
from {
opacity: 0;
}
to {
opacity: 0.7;
}
}

@-moz-keyframes slbOverlay {
from {
opacity: 0;
}
to {
opacity: 0.7;
}
}

@keyframes slbOverlay {
from {
opacity: 0;
}
to {
opacity: 0.7;
}
}

@-webkit-keyframes slbEnter {
from {
opacity: 0;
-webkit-transform: translate3d(0, -1em, 0);
}
to {
opacity: 1;
-webkit-transform: translate3d(0, 0, 0);
}
}

@-moz-keyframes slbEnter {
from {
opacity: 0;
-moz-transform: translate3d(0, -1em, 0);
}
to {
opacity: 1;
-moz-transform: translate3d(0, 0, 0);
}
}

@keyframes slbEnter {
from {
opacity: 0;
-webkit-transform: translate3d(0, -1em, 0);
-moz-transform: translate3d(0, -1em, 0);
-ms-transform: translate3d(0, -1em, 0);
-o-transform: translate3d(0, -1em, 0);
transform: translate3d(0, -1em, 0);
}
to {
opacity: 1;
-webkit-transform: translate3d(0, 0, 0);
-moz-transform: translate3d(0, 0, 0);
-ms-transform: translate3d(0, 0, 0);
-o-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
}

@-webkit-keyframes slbEnterNext {
from {
opacity: 0;
-webkit-transform: translate3d(4em, 0, 0);
}
to {
opacity: 1;
-webkit-transform: translate3d(0, 0, 0);
}
}

@-moz-keyframes slbEnterNext {
from {
opacity: 0;
-moz-transform: translate3d(4em, 0, 0);
}
to {
opacity: 1;
-moz-transform: translate3d(0, 0, 0);
}
}

@keyframes slbEnterNext {
from {
opacity: 0;
-webkit-transform: translate3d(4em, 0, 0);
-moz-transform: translate3d(4em, 0, 0);
-ms-transform: translate3d(4em, 0, 0);
-o-transform: translate3d(4em, 0, 0);
transform: translate3d(4em, 0, 0);
}
to {
opacity: 1;
-webkit-transform: translate3d(0, 0, 0);
-moz-transform: translate3d(0, 0, 0);
-ms-transform: translate3d(0, 0, 0);
-o-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
}

@-webkit-keyframes slbEnterPrev {
from {
opacity: 0;
-webkit-transform: translate3d(-4em, 0, 0);
}
to {
opacity: 1;
-webkit-transform: translate3d(0, 0, 0);
}
}

@-moz-keyframes slbEnterPrev {
from {
opacity: 0;
-moz-transform: translate3d(-4em, 0, 0);
}
to {
opacity: 1;
-moz-transform: translate3d(0, 0, 0);
}
}

@keyframes slbEnterPrev {
from {
opacity: 0;
-webkit-transform: translate3d(-4em, 0, 0);
-moz-transform: translate3d(-4em, 0, 0);
-ms-transform: translate3d(-4em, 0, 0);
-o-transform: translate3d(-4em, 0, 0);
transform: translate3d(-4em, 0, 0);
}
to {
opacity: 1;
-webkit-transform: translate3d(0, 0, 0);
-moz-transform: translate3d(0, 0, 0);
-ms-transform: translate3d(0, 0, 0);
-o-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
}

+ 532
- 0
common/web/js/simple-lightbox/simpleLightbox.js 파일 보기

@@ -0,0 +1,532 @@
(function(root, factory) {

if (typeof define === 'function' && define.amd) {
define([], factory);
} else if (typeof module === 'object' && module.exports) {
module.exports = factory();
} else {
root.SimpleLightbox = factory();
}

}(this, function() {

function assign(target) {

for (var i = 1; i < arguments.length; i++) {

var obj = arguments[i];

if (obj) {
for (var key in obj) {
obj.hasOwnProperty(key) && (target[key] = obj[key]);
}
}

}

return target;

}

function addClass(element, className) {

if (element && className) {
element.className += ' ' + className;
}

}

function removeClass(element, className) {

if (element && className) {
element.className = element.className.replace(
new RegExp('(\\s|^)' + className + '(\\s|$)'), ' '
).trim();
}

}

function parseHtml(html) {

var div = document.createElement('div');
div.innerHTML = html.trim();

return div.childNodes[0];

}

function matches(el, selector) {

return (el.matches || el.matchesSelector || el.msMatchesSelector).call(el, selector);

}

function getWindowHeight() {

return 'innerHeight' in window
? window.innerHeight
: document.documentElement.offsetHeight;

}

function SimpleLightbox(options) {

this.init.apply(this, arguments);

}

SimpleLightbox.defaults = {

// add custom classes to lightbox elements
elementClass: '',
elementLoadingClass: 'slbLoading',
htmlClass: 'slbActive',
closeBtnClass: '',
nextBtnClass: '',
prevBtnClass: '',
loadingTextClass: '',

// customize / localize controls captions
closeBtnCaption: 'Close',
nextBtnCaption: 'Next',
prevBtnCaption: 'Previous',
loadingCaption: 'Loading...',

bindToItems: true, // set click event handler to trigger lightbox on provided $items
closeOnOverlayClick: true,
closeOnEscapeKey: true,
nextOnImageClick: true,
showCaptions: true,

captionAttribute: 'title', // choose data source for library to glean image caption from
urlAttribute: 'href', // where to expect large image

startAt: 0, // start gallery at custom index
loadingTimeout: 100, // time after loading element will appear

appendTarget: 'body', // append elsewhere if needed

beforeSetContent: null, // convenient hooks for extending library behavoiur
beforeClose: null,
afterClose: null,
beforeDestroy: null,
afterDestroy: null,

videoRegex: new RegExp(/youtube.com|vimeo.com/) // regex which tests load url for iframe content

};

assign(SimpleLightbox.prototype, {

init: function(options) {

options = this.options = assign({}, SimpleLightbox.defaults, options);

var self = this;
var elements;

if (options.$items) {
elements = options.$items.get();
}

if (options.elements) {
elements = [].slice.call(
typeof options.elements === 'string'
? document.querySelectorAll(options.elements)
: options.elements
);
}

this.eventRegistry = {lightbox: [], thumbnails: []};
this.items = [];
this.captions = [];

if (elements) {

elements.forEach(function(element, index) {

self.items.push(element.getAttribute(options.urlAttribute));
self.captions.push(element.getAttribute(options.captionAttribute));

if (options.bindToItems) {

self.addEvent(element, 'click', function(e) {

e.preventDefault();
self.showPosition(index);

}, 'thumbnails');

}

});

}

if (options.items) {
this.items = options.items;
}

if (options.captions) {
this.captions = options.captions;
}

},

addEvent: function(element, eventName, callback, scope) {

this.eventRegistry[scope || 'lightbox'].push({
element: element,
eventName: eventName,
callback: callback
});

element.addEventListener(eventName, callback);

return this;

},

removeEvents: function(scope) {

this.eventRegistry[scope].forEach(function(item) {
item.element.removeEventListener(item.eventName, item.callback);
});

this.eventRegistry[scope] = [];

return this;

},

next: function() {

return this.showPosition(this.currentPosition + 1);

},

prev: function() {

return this.showPosition(this.currentPosition - 1);

},

normalizePosition: function(position) {

if (position >= this.items.length) {
position = 0;
} else if (position < 0) {
position = this.items.length - 1;
}

return position;

},

showPosition: function(position) {

var newPosition = this.normalizePosition(position);

if (typeof this.currentPosition !== 'undefined') {
this.direction = newPosition > this.currentPosition ? 'next' : 'prev';
}

this.currentPosition = newPosition;

return this.setupLightboxHtml()
.prepareItem(this.currentPosition, this.setContent)
.show();

},

loading: function(on) {

var self = this;
var options = this.options;

if (on) {

this.loadingTimeout = setTimeout(function() {

addClass(self.$el, options.elementLoadingClass);

self.$content.innerHTML =
'<p class="slbLoadingText ' + options.loadingTextClass + '">' +
options.loadingCaption +
'</p>';
self.show();

}, options.loadingTimeout);

} else {

removeClass(this.$el, options.elementLoadingClass);
clearTimeout(this.loadingTimeout);

}

},

prepareItem: function(position, callback) {

var self = this;
var url = this.items[position];

this.loading(true);

if (this.options.videoRegex.test(url)) {

callback.call(self, parseHtml(
'<div class="slbIframeCont"><iframe class="slbIframe" frameborder="0" allowfullscreen src="' + url + '"></iframe></div>')
);

} else {

var $imageCont = parseHtml(
'<div class="slbImageWrap"><img class="slbImage" src="' + url + '" /></div>'
);

this.$currentImage = $imageCont.querySelector('.slbImage');

if (this.options.showCaptions && this.captions[position]) {
$imageCont.appendChild(parseHtml(
'<div class="slbCaption">' + this.captions[position] + '</div>')
);
}

this.loadImage(url, function() {

self.setImageDimensions();

callback.call(self, $imageCont);

self.loadImage(self.items[self.normalizePosition(self.currentPosition + 1)]);

});

}

return this;

},

loadImage: function(url, callback) {

if (!this.options.videoRegex.test(url)) {

var image = new Image();
callback && (image.onload = callback);
image.src = url;

}

},

setupLightboxHtml: function() {

var o = this.options;

if (!this.$el) {

this.$el = parseHtml(
'<div class="slbElement ' + o.elementClass + '">' +
'<div class="slbOverlay"></div>' +
'<div class="slbWrapOuter">' +
'<div class="slbWrap">' +
'<div class="slbContentOuter">' +
'<div class="slbContent"></div>' +
'<button type="button" title="' + o.closeBtnCaption + '" class="slbCloseBtn ' + o.closeBtnClass + '">×</button>' +
(this.items.length > 1
? '<div class="slbArrows">' +
'<button type="button" title="' + o.prevBtnCaption + '" class="prev slbArrow' + o.prevBtnClass + '">' + o.prevBtnCaption + '</button>' +
'<button type="button" title="' + o.nextBtnCaption + '" class="next slbArrow' + o.nextBtnClass + '">' + o.nextBtnCaption + '</button>' +
'</div>'
: ''
) +
'</div>' +
'</div>' +
'</div>' +
'</div>'
);

this.$content = this.$el.querySelector('.slbContent');

}

this.$content.innerHTML = '';

return this;

},

show: function() {

if (!this.modalInDom) {

document.querySelector(this.options.appendTarget).appendChild(this.$el);
addClass(document.documentElement, this.options.htmlClass);
this.setupLightboxEvents();
this.modalInDom = true;

}

return this;

},

setContent: function(content) {

var $content = typeof content === 'string'
? parseHtml(content)
: content
;

this.loading(false);

this.setupLightboxHtml();

removeClass(this.$content, 'slbDirectionNext');
removeClass(this.$content, 'slbDirectionPrev');

if (this.direction) {
addClass(this.$content, this.direction === 'next'
? 'slbDirectionNext'
: 'slbDirectionPrev'
);
}

if (this.options.beforeSetContent) {
this.options.beforeSetContent($content, this);
}

this.$content.appendChild($content);

return this;

},

setImageDimensions: function() {

if (this.$currentImage) {
this.$currentImage.style.maxHeight = getWindowHeight() + 'px';
}

},

setupLightboxEvents: function() {

var self = this;

if (this.eventRegistry.lightbox.length) {
return this;
}

this.addEvent(this.$el, 'click', function(e) {

var $target = e.target;

if (matches($target, '.slbCloseBtn') || (self.options.closeOnOverlayClick && matches($target, '.slbWrap'))) {

self.close();

} else if (matches($target, '.slbArrow')) {

matches($target, '.next') ? self.next() : self.prev();

} else if (self.options.nextOnImageClick && self.items.length > 1 && matches($target, '.slbImage')) {

self.next();

}

}).addEvent(document, 'keyup', function(e) {

self.options.closeOnEscapeKey && e.keyCode === 27 && self.close();

if (self.items.length > 1) {
(e.keyCode === 39 || e.keyCode === 68) && self.next();
(e.keyCode === 37 || e.keyCode === 65) && self.prev();
}

}).addEvent(window, 'resize', function() {

self.setImageDimensions();

});

return this;

},

close: function() {

if (this.modalInDom) {

this.runHook('beforeClose');
this.removeEvents('lightbox');
this.$el && this.$el.parentNode.removeChild(this.$el);
removeClass(document.documentElement, this.options.htmlClass);
this.modalInDom = false;
this.runHook('afterClose');

}

this.direction = undefined;
this.currentPosition = this.options.startAt;

},

destroy: function() {

this.close();
this.runHook('beforeDestroy');
this.removeEvents('thumbnails');
this.runHook('afterDestroy');

},

runHook: function(name) {

this.options[name] && this.options[name](this);

}

});

SimpleLightbox.open = function(options) {

var instance = new SimpleLightbox(options);

return options.content
? instance.setContent(options.content).show()
: instance.showPosition(instance.options.startAt);

};

SimpleLightbox.registerAsJqueryPlugin = function($) {

$.fn.simpleLightbox = function(options) {

var lightboxInstance;
var $items = this;

return this.each(function() {
if (!$.data(this, 'simpleLightbox')) {
lightboxInstance = lightboxInstance || new SimpleLightbox($.extend({}, options, {$items: $items}));
$.data(this, 'simpleLightbox', lightboxInstance);
}
});

};

$.SimpleLightbox = SimpleLightbox;

};

if (typeof window !== 'undefined' && window.jQuery) {
SimpleLightbox.registerAsJqueryPlugin(window.jQuery);
}

return SimpleLightbox;

}));

+ 1
- 0
common/web/js/simple-lightbox/simpleLightbox.min.css
파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
파일 보기


+ 1
- 0
common/web/js/simple-lightbox/simpleLightbox.min.js
파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
파일 보기


+ 2
- 1
composer.json 파일 보기

@@ -37,7 +37,8 @@
"weluse/yii2-mailjet": "^0.2.0",
"ext-json": "*",
"ext-curl": "*",
"yiisoft/yii2-twig": "^2.4"
"yiisoft/yii2-twig": "^2.4",
"letyii/yii2-tinymce": "dev-master"
},
"require-dev": {
"yiisoft/yii2-codeception": "*",

+ 104
- 2
composer.lock 파일 보기

@@ -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": "a8cbf9ba6bfa2f0b47fba2344732ac95",
"content-hash": "95e88fd70427210dc0d1edd79d17cce1",
"packages": [
{
"name": "2amigos/yii2-chartjs-widget",
@@ -1190,6 +1190,48 @@
],
"time": "2022-09-19T18:31:07+00:00"
},
{
"name": "letyii/yii2-tinymce",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/letyii/yii2-tinymce.git",
"reference": "861873f30d9e16f76239ae827f2848879f66f78f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/letyii/yii2-tinymce/zipball/861873f30d9e16f76239ae827f2848879f66f78f",
"reference": "861873f30d9e16f76239ae827f2848879f66f78f",
"shasum": ""
},
"require": {
"php": ">=5.4",
"tinymce/tinymce": ">=4",
"yiisoft/yii2": "*"
},
"default-branch": true,
"type": "library",
"autoload": {
"psr-4": {
"letyii\\tinymce\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "TinyMce for yii2 textarea",
"homepage": "https://github.com/letyii/yii2-tinymce",
"keywords": [
"tinymce",
"yii2"
],
"support": {
"issues": "https://github.com/letyii/yii2-tinymce/issues",
"source": "https://github.com/letyii/yii2-tinymce/tree/master"
},
"time": "2015-07-08T08:11:14+00:00"
},
{
"name": "linslin/yii2-curl",
"version": "1.5.0",
@@ -2546,6 +2588,65 @@
],
"time": "2022-11-03T14:55:06+00:00"
},
{
"name": "tinymce/tinymce",
"version": "6.7.2",
"source": {
"type": "git",
"url": "https://github.com/tinymce/tinymce-dist.git",
"reference": "a4c139bde17a4e8b2e84d225020cd5acc24a728a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tinymce/tinymce-dist/zipball/a4c139bde17a4e8b2e84d225020cd5acc24a728a",
"reference": "a4c139bde17a4e8b2e84d225020cd5acc24a728a",
"shasum": ""
},
"type": "component",
"extra": {
"component": {
"scripts": [
"tinymce.js",
"plugins/*/plugin.js",
"themes/*/theme.js",
"models/*/model.js",
"icons/*/icons.js"
],
"files": [
"tinymce.min.js",
"plugins/*/plugin.min.js",
"themes/*/theme.min.js",
"models/*/model.min.js",
"skins/**",
"icons/*/icons.min.js"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT-only"
],
"description": "Web based JavaScript HTML WYSIWYG editor control.",
"homepage": "https://www.tiny.cloud/",
"keywords": [
"contenteditable",
"editing",
"html",
"javascript",
"rich editor",
"rich text",
"rich text editor",
"richtext",
"rte",
"text",
"tinymce",
"wysiwyg"
],
"support": {
"source": "https://github.com/tinymce/tinymce-dist/tree/6.7.2"
},
"time": "2023-10-25T06:42:18+00:00"
},
{
"name": "twig/twig",
"version": "v3.7.1",
@@ -6669,7 +6770,8 @@
"stability-flags": {
"kartik-v/yii2-mpdf": 20,
"c006/yii2-paypal-ipn": 20,
"yurkinx/yii2-image": 20
"yurkinx/yii2-image": 20,
"letyii/yii2-tinymce": 20
},
"prefer-stable": false,
"prefer-lowest": false,

+ 2
- 1
console/commands/ImportFeaturesController.php 파일 보기

@@ -2,11 +2,12 @@

namespace console\commands;

use common\logic\Feature\Feature\Module\FeatureModule;
use common\logic\Feature\Feature\FeatureModule;
use yii\console\Controller;

class ImportFeaturesController extends Controller
{
// ./yii import-features/index
public function actionIndex()
{
$featureModule = FeatureModule::getInstance();

+ 18
- 0
console/commands/SettingController.php 파일 보기

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

namespace console\commands;

use common\logic\Setting\SettingModule;
use yii\console\Controller;

class SettingController extends Controller
{
// ./yii setting/import
public function actionImport()
{
$settingModule = SettingModule::getInstance();
$settingModule->getImporter()->importFromDefinitions();
}
}

?>

+ 25
- 0
console/migrations/m231113_073008_add_column_feature_position.php 파일 보기

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

use yii\db\Migration;

/**
* Class m231113_073008_add_column_feature_position
*/
class m231113_073008_add_column_feature_position extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->addColumn('feature', 'position', $this->integer()->notNull()->defaultValue(0));
}

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

+ 26
- 0
console/migrations/m231113_084553_add_column_point_sale_minimum_order_amount.php 파일 보기

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

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

/**
* Class m231113_084553_add_column_point_sale_minimum_order_amount
*/
class m231113_084553_add_column_point_sale_minimum_order_amount extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->addColumn('point_sale', 'minimum_order_amount', Schema::TYPE_FLOAT);
}

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

+ 26
- 0
console/migrations/m231113_131131_add_column_producer_export_shopping_cart_labels_format.php 파일 보기

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

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

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

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

+ 37
- 0
console/migrations/m231114_085352_create_table_setting.php 파일 보기

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

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

/**
* Class m231114_085352_create_table_setting
*/
class m231114_085352_create_table_setting extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->createTable('setting', [
'id' => 'pk',
'name' => Schema::TYPE_STRING.' NOT NULL',
'id_producer' => Schema::TYPE_INTEGER,
'string' => Schema::TYPE_STRING,
'text' => Schema::TYPE_TEXT,
'date' => Schema::TYPE_DATE,
'integer' => Schema::TYPE_INTEGER,
'float' => Schema::TYPE_FLOAT,
'double' => Schema::TYPE_DOUBLE,
'boolean' => Schema::TYPE_BOOLEAN,
]);
}

/**
* {@inheritdoc}
*/
public function safeDown()
{
$this->dropTable('setting');
}
}

+ 26
- 0
console/migrations/m231115_083420_add_column_producer_document_image_bottom.php 파일 보기

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

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

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

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

+ 4
- 1
frontend/controllers/SiteController.php 파일 보기

@@ -138,9 +138,12 @@ class SiteController extends FrontendController

public function actionService()
{
$paidFeaturesArray = $this->getFeatureModule()->getRepository()->findPaidFeatures();

return $this->render('service', [
'producerDemoAccount' => $this->getProducerModule()->findOneProducerDemoAccount(),
'dataProviderPrices' => $this->getDataProviderPrices()
'dataProviderPrices' => $this->getDataProviderPrices(),
'paidFeaturesArray' => $paidFeaturesArray
]);
}


+ 7
- 6
frontend/views/site/_prices_producer.php 파일 보기

@@ -36,7 +36,9 @@
* termes.
*/

use common\helpers\Price;
use yii\grid\GridView;
use yii\helpers\Html;

?>

@@ -89,16 +91,15 @@ GridView::widget([
</tr>
</thead>
<tbody>
<?php foreach($paidFeaturesArray as $paidFeature): ?>
<tr>
<td>
<strong>Paiement en ligne</strong><br>
<p>Le paiement en ligne permet à vos clients d'alimenter leur crédit (compte prépayé en ligne) par carte bancaire.<br>
Le logiciel fonctionne avec la plateforme <a href="https://stripe.com/fr">Stripe</a> pour accepter les paiements.<br>
<a href="https://stripe.com/fr/pricing">Voir les tarifs Stripe</a>
</p>
<div><strong><?= Html::encode($paidFeature->name) ?></strong></div>
<div><?= $paidFeature->description ?></div>
</td>
<td>120 €</td>
<td><?= Price::format($paidFeature->price, 0) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>


+ 4
- 1
frontend/views/site/service.php 파일 보기

@@ -140,7 +140,10 @@ $this->setIcon('console');
</h2>
</div>
<div class="panel-body">
<?= $this->render('_prices_producer', ['dataProviderPrices' => $dataProviderPrices]); ?>
<?= $this->render('_prices_producer', [
'dataProviderPrices' => $dataProviderPrices,
'paidFeaturesArray' => $paidFeaturesArray
]); ?>
</div>
</div>


+ 131
- 125
producer/controllers/CreditController.php 파일 보기

@@ -40,6 +40,7 @@ namespace producer\controllers;

use common\helpers\GlobalParam;
use common\helpers\MeanPayment;
use common\logic\Feature\Feature\Feature;
use common\logic\Payment\Model\Payment;
use producer\models\CreditForm;
use yii\filters\VerbFilter;
@@ -108,12 +109,14 @@ class CreditController extends ProducerBaseController

public function actionAdd()
{
$featureChecker = $this->getFeatureModule()->getChecker();
$producer = $this->getProducerCurrent();
if (\Yii::$app->user->isGuest) {
return $this->redirect($this->getUrlManagerFrontend()->createAbsoluteUrl(['site/producer', 'id' => $producer->id]));
}

if ($producer->online_payment || $producer->option_stripe_mode_test) {
if ($featureChecker->isEnabled(Feature::ALIAS_ONLINE_PAYMENT)
&& ($producer->online_payment || $producer->option_stripe_mode_test)) {

$creditForm = new CreditForm;

@@ -132,7 +135,7 @@ class CreditController extends ProducerBaseController
'product_data' => [
'name' => 'Alimentation crédit',
],
'unit_amount' => (float) $creditForm->amount * 100,
'unit_amount' => (float)$creditForm->amount * 100,
],
'quantity' => 1,
]
@@ -180,153 +183,156 @@ class CreditController extends ProducerBaseController
$paymentManager = $this->getPaymentModule();
$producerModule = $this->getProducerModule();
$userModule = $this->getUserModule();
$producer = $this->getProducerCurrent();
$contactProducer = $producerModule->getMainContact($producer);

$payload = @file_get_contents('php://input');
$sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];

try {
$event = \Stripe\Webhook::constructEvent(
$payload,
$sig_header,
$producerModule->getPrivateKeyEndpointStripe($producer)
);
} catch (\UnexpectedValueException $e) {
// Invalid payload
http_response_code(400);
exit();
} catch (\Stripe\Exception\SignatureVerificationException $e) {
// Invalid signature
http_response_code(400);
exit();
}
$featureChecker = $this->getFeatureModule()->getChecker();

$paymentIntent = $event->data->object;
$paymentIntentMetadata = $paymentIntent->metadata;
$amount = $paymentIntent->amount / 100;
$idUser = $paymentIntentMetadata->user_id;
if ($featureChecker->isEnabled(Feature::ALIAS_ONLINE_PAYMENT)) {
$producer = $this->getProducerCurrent();
$contactProducer = $producerModule->getMainContact($producer);

if($idUser) {
$user = $userModule->findOneUserById($idUser);
$payload = @file_get_contents('php://input');
$sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];

if (isset($paymentIntentMetadata->order_id)) {
$order = $this->getOrderModule()->findOneOrderById($paymentIntentMetadata->order_id);
$orderModule->initOrder($order);
$pointSale = $this->getPointSaleModule()->findOnePointSaleById($order->id_point_sale);
$distribution = $this-> getDistributionModule()->findOneDistributionById($order->id_distribution);
try {
$event = \Stripe\Webhook::constructEvent(
$payload,
$sig_header,
$producerModule->getPrivateKeyEndpointStripe($producer)
);
} catch (\UnexpectedValueException $e) {
// Invalid payload
http_response_code(400);
exit();
} catch (\Stripe\Exception\SignatureVerificationException $e) {
// Invalid signature
http_response_code(400);
exit();
}

// Handle the event
switch ($event->type) {
case 'charge.succeeded':
$paymentIntent = $event->data->object;
$paymentIntentMetadata = $paymentIntent->metadata;
$amount = $paymentIntent->amount / 100;
$idUser = $paymentIntentMetadata->user_id;

$paymentExist = Payment::searchOne([
'id_user' => $idUser,
'amount' => $amount,
], [
'conditions' => [
'date > DATE_SUB(NOW(), INTERVAL 1 MINUTE)'
]
]);
if ($idUser) {
$user = $userModule->findOneUserById($idUser);

if (!$paymentExist) {
if (isset($paymentIntentMetadata->order_id)) {
$order = $this->getOrderModule()->findOneOrderById($paymentIntentMetadata->order_id);
$orderModule->initOrder($order);
$pointSale = $this->getPointSaleModule()->findOnePointSaleById($order->id_point_sale);
$distribution = $this->getDistributionModule()->findOneDistributionById($order->id_distribution);
}

$paymentManager->creditUser($user, $amount, MeanPayment::CREDIT_CARD, $user);
// Handle the event
switch ($event->type) {
case 'charge.succeeded':

if (isset($order) && $order) {
$paymentExist = Payment::searchOne([
'id_user' => $idUser,
'amount' => $amount,
], [
'conditions' => [
'date > DATE_SUB(NOW(), INTERVAL 1 MINUTE)'
]
]);

if (!$paymentExist) {

$paymentManager->creditUser($user, $amount, MeanPayment::CREDIT_CARD, $user);

if (isset($order) && $order) {

$paymentManager->payOrder($order, MeanPayment::CREDIT_CARD, $user, true);


// client : envoi d'un email de confirmation de paiement
/*\Yii::$app->mailerService->sendFromProducer(
'Confirmation de commande',
'paymentOrderConfirm',
[
'amount' => $amount,
'user' => $user,
'producer' => $producer,
],
$user->email,
$producer
);*/

// producteur : mail de confirmation
\Yii::$app->mailerService->sendFromSite(
'Confirmation de commande',
'orderConfirmProducer',
[
'order' => $order,
'pointSale' => $pointSale,
'distribution' => $distribution,
'user' => $user,
'producer' => $producer
],
$contactProducer->email
);
} else {
$userProducer = $this->getUserProducerModule()->findOneUserProducer($user);

\Yii::$app->mailerService->sendFromProducer(
'Alimentation de votre crédit',
'creditConfirm',
[
'user' => $user,
'userProducer' => $userProducer,
'producer' => $producer,
'amount' => $amount,
],
$user->email,
$producer
);
}
}

$paymentManager->payOrder($order, MeanPayment::CREDIT_CARD, $user, true);
break;

case 'charge.failed':

// client : envoi d'un email de confirmation de paiement
/*\Yii::$app->mailerService->sendFromProducer(
'Confirmation de commande',
'paymentOrderConfirm',
[
'amount' => $amount,
'user' => $user,
'producer' => $producer,
],
$user->email,
$producer
);*/
// client
\Yii::$app->mailerService->sendFromProducer(
'Erreur de paiement',
'paymentError',
[
'amount' => $amount,
'user' => $user,
'producer' => $producer,
],
$user->email,
$producer
);

// producteur : mail de confirmation
// producteur
if (isset($order) && $order) {
\Yii::$app->mailerService->sendFromSite(
'Confirmation de commande',
'orderConfirmProducer',
[
'order' => $order,
'pointSale' => $pointSale,
'distribution' => $distribution,
'user' => $user,
'producer' => $producer
],
$contactProducer->email
);
} else {
$userProducer = $this->getUserProducerModule()->findOneUserProducer($user);

\Yii::$app->mailerService->sendFromProducer(
'Alimentation de votre crédit',
'creditConfirm',
'Erreur de paiement',
'paymentErrorProducer',
[
'amount' => $amount,
'user' => $user,
'userProducer' => $userProducer,
'producer' => $producer,
'amount' => $amount,
'order' => $order,
'distribution' => $distribution
],
$user->email,
$producer
$contactProducer->email,
);
}
}

break;

case 'charge.failed':

// client
\Yii::$app->mailerService->sendFromProducer(
'Erreur de paiement',
'paymentError',
[
'amount' => $amount,
'user' => $user,
'producer' => $producer,
],
$user->email,
$producer
);

// producteur
if (isset($order) && $order) {
\Yii::$app->mailerService->sendFromSite(
'Erreur de paiement',
'paymentErrorProducer',
[
'amount' => $amount,
'user' => $user,
'producer' => $producer,
'order' => $order,
'distribution' => $distribution
],
$contactProducer->email,
);
}
break;

break;
// handle other event types
default:
echo 'Received unknown event type ' . $event->type;
}

// handle other event types
default:
echo 'Received unknown event type ' . $event->type;
http_response_code(200);
} else {
http_response_code(200);
}

http_response_code(200);
}
else {
http_response_code(200);
}

die();

+ 33
- 35
producer/controllers/OrderController.php 파일 보기

@@ -50,7 +50,6 @@ use common\logic\Order\ProductOrder\Model\ProductOrder;
use common\logic\PointSale\PointSale\Model\PointSale;
use common\logic\Producer\Producer\Model\Producer;
use common\logic\Product\Product\Model\Product;
use common\logic\User\CreditHistory\Model\CreditHistory;
use common\logic\User\User\Model\User;
use DateTime;
use yii\base\UserException;
@@ -476,44 +475,42 @@ class OrderController extends ProducerBaseController
/*
* Envoi email de confirmation
*/
if ($isNewOrder) {

$emailSubject = 'Confirmation de commande';
$emailContentParams = [
'order' => $order,
'pointSale' => $pointSale,
'distribution' => $distribution,
'user' => $user,
'producer' => $producer
];

// au client
if ($producerModule->getConfig('option_email_confirm')) {
\Yii::$app->mailerService->sendFromProducer(
$emailSubject,
'orderConfirm',
$emailContentParams,
$user->email,
$producer
);
$emailSubject = 'Confirmation de commande';
$emailContentParams = [
'order' => $order,
'pointSale' => $pointSale,
'distribution' => $distribution,
'user' => $user,
'producer' => $producer
];

}
// au client
if ($producerModule->getConfig('option_email_confirm')) {
\Yii::$app->mailerService->sendFromProducer(
$emailSubject,
'orderConfirm',
$emailContentParams,
$user->email,
$producer
);

// au producteur
$contactProducer = $producerModule->getMainContact($producer);
if ($producerModule->getConfig('option_email_confirm_producer') && $contactProducer && strlen(
$contactProducer->email
)) {

\Yii::$app->mailerService->sendFromSite(
$emailSubject,
'orderConfirmProducer',
$emailContentParams,
$contactProducer->email
);
}
}

// au producteur
$contactProducer = $producerModule->getMainContact($producer);
if ($producerModule->getConfig('option_email_confirm_producer') && $contactProducer && strlen(
$contactProducer->email
)) {

\Yii::$app->mailerService->sendFromSite(
$emailSubject,
'orderConfirmProducer',
$emailContentParams,
$contactProducer->email
);
}


$order = $orderModule->findOneOrderById($order->id);
$orderModule->initOrder($order);
$orderModule->updateOrderTillerSynchronization($order);
@@ -935,6 +932,7 @@ class OrderController extends ProducerBaseController
$product['photo'] = '';
}
else {
$product['photo_big'] = Image::getThumbnailBig($product['photo']);
$product['photo'] = Image::getThumbnailSmall($product['photo']);
}


+ 2
- 2
producer/controllers/SiteController.php 파일 보기

@@ -40,7 +40,7 @@ namespace producer\controllers;

use common\forms\ContactForm;
use common\helpers\GlobalParam;
use common\logic\Feature\Feature\Model\Feature;
use common\logic\Feature\Feature\Feature;
use common\logic\Product\Product\Model\Product;
use yii\data\ActiveDataProvider;
use yii\helpers\Html;
@@ -180,7 +180,7 @@ class SiteController extends ProducerBaseController
public function actionContact()
{
$featureModule = $this->getFeatureModule();
if($featureModule->getManager()->isDisabled(Feature::ALIAS_CONTACT)) {
if($featureModule->getChecker()->isDisabled(Feature::ALIAS_CONTACT)) {
return $this->redirect(['site/index']);
}


+ 4
- 1
producer/views/credit/history.php 파일 보기

@@ -36,15 +36,18 @@
* termes.
*/

use common\logic\Feature\Feature\Feature;
use common\logic\Feature\Feature\FeatureModule;
use common\logic\Payment\Module\PaymentModule;
use yii\grid\GridView;

$paymentManager = PaymentModule::getInstance();
$featureChecker = FeatureModule::getInstance()->getChecker();
$producer = $this->context->getProducerCurrent();
$this->setTitle('Crédit : <span id="credit-user">' . number_format($creditUser, 2) . ' €</span>');
$this->setPageTitle('Crédit');

if ($this->context->getProducerCurrent()->online_payment) {
if ($featureChecker->isEnabled(Feature::ALIAS_ONLINE_PAYMENT) && $this->context->getProducerCurrent()->online_payment) {
$this->addButton(
[
'label' => '<span class="glyphicon glyphicon-credit-card"></span> Créditer mon compte',

+ 3
- 3
producer/views/layouts/main.php 파일 보기

@@ -36,8 +36,8 @@
* termes.
*/

use common\logic\Feature\Feature\Model\Feature;
use common\logic\Feature\Feature\Module\FeatureModule;
use common\logic\Feature\Feature\Feature;
use common\logic\Feature\Feature\FeatureModule;
use common\logic\Order\Order\Model\Order;
use common\logic\Producer\Producer\Module\ProducerModule;
use common\logic\User\User\Module\UserModule;
@@ -181,7 +181,7 @@ if (!Yii::$app->user->isGuest) {
'label' => '<span class="glyphicon glyphicon-envelope"></span> Contact',
'url' => $this->getUrlManagerProducer()->createUrl(['site/contact']),
'active' => $this->getControllerAction() == 'site/contact',
'visible' => $featureModule->getManager()->isEnabled(Feature::ALIAS_CONTACT)
'visible' => $featureModule->getChecker()->isEnabled(Feature::ALIAS_CONTACT)
],
],
]);

+ 123
- 124
producer/views/order/order.php 파일 보기

@@ -112,7 +112,7 @@ $this->setTitle('Commander');
></step-date>
<?php endif; ?>

<li id="step-products" :class="'col-md-3 '+((step == 'products') ? 'active ' : '')">
<li id="step-products" :class="'col-md-4 '+((step == 'products') ? 'active ' : '')">
<button @click="changeStep('products')"
:class="'btn '+ (step == 'products' ? 'btn-primary' : 'btn-default')"
:disabled="step == 'date' || step == 'point-sale'">
@@ -123,14 +123,14 @@ $this->setTitle('Commander');
{{ countProductOrdered() }} produit{{ (countProductOrdered() > 1) ? 's' : '' }}
</div>
</li>
<li id="step-payment" :class="'col-md-3 '+((step == 'payment') ? 'active' : '')">
<!--<li id="step-payment" :class="'col-md-3 '+((step == 'payment') ? 'active' : '')">
<button @click="changeStep('payment')"
:class="'btn '+ (step == 'payment' ? 'btn-primary' : 'btn-default')"
:disabled="step == 'date' || step == 'point-sale' || step == 'products'">
<span class="button-content"><span
class="glyphicon glyphicon-ok"></span> Confirmation</span>
</button>
</li>
</li>-->
</ul>
<div class="clr"></div>
</div>
@@ -240,6 +240,9 @@ $this->setTitle('Commander');
</div>
<div class="comment" v-if="pointSale.infos && pointSale.infos.length > 0"
v-html="pointSale.infos"></div>
<div class="minimum-order-amount" v-if="pointSale.minimum_order_amount">
Montant minimum de commande : {{ formatPrice(pointSale.minimum_order_amount) }}
</div>
</td>
<td class="locality">{{ pointSale.locality }}</td>
<td class="actions">
@@ -325,8 +328,9 @@ $this->setTitle('Commander');
<tr v-for="product in products"
v-if="product.id_product_category == category.id && product.productDistribution && product.productDistribution[0] && product.productDistribution[0].active == 1">
<td class="photo">
<img v-if="product.photo.length" class="photo-product"
:src="product.photo"/>
<a class="product-photo" :href="product.photo_big" :title="product.name">
<img v-if="product.photo.length" class="photo-product" :src="product.photo"/>
</a>
</td>
<td class="name product-name-description-block">
<span class="name">{{ product.name }}</span>
@@ -425,137 +429,132 @@ $this->setTitle('Commander');
</tr>
</tbody>
</table>
<div class="block-actions">
<button class="btn btn-primary" @click="changeStep('payment')">Valider</button>
</div>
</div>
<div class="alert alert-warning" v-else>
Aucun produit disponible
</div>
</div>
</div>
</transition>
<transition name="slide">
<div id="content-step-payment" v-if="step == 'payment'">
<div>
<div class="delivery">
<div class="delivery-home" v-if="pointSaleActive.is_home_delivery">
<label for="deliver-address">Adresse de livraison</label>
<textarea id="deliver-address" v-model="deliveryAddress" class="form-control"
required="required"></textarea>
</div>
</div>

<div class="comment">
<label for="order-comment">Commentaire</label>
<textarea id="order-comment" v-model="comment" class="form-control"></textarea>
</div>
<div id="content-step-payment">
<div>
<div class="delivery">
<div class="delivery-home" v-if="pointSaleActive.is_home_delivery">
<label for="deliver-address">Adresse de livraison</label>
<textarea id="deliver-address" v-model="deliveryAddress" class="form-control"
required="required"></textarea>
</div>
</div>

<template
v-if="producer.credit == 1 && pointSaleActive.credit == 1 && (pointSaleActive.credit_functioning == 'mandatory' || (pointSaleActive.credit_functioning == 'user' && user.credit_active)) && !checkCreditLimit(order) ">
<div class="alert alert-danger">
Vous devez
<template v-if="producer.online_payment == 1"><a
href="<?= \Yii::$app->urlManager->createUrl(['credit/add']) ?>">recharger
votre crédit</a></template>
<template v-else>recharger votre crédit</template>
auprès de votre producteur ou supprimer des produits.</span>
Votre producteur n'autorise pas un crédit inférieur
à <strong>{{ formatPrice(producer.credit_limit) }}</strong>.
</div>
<div class="block-actions">
<a class="btn btn-primary"
href="<?= \Yii::$app->urlManager->createUrl(['site/index']) ?>">Retour à
l'accueil</a>
</div>
</template>
<template v-else>
<div class="credit">
<div v-if="user && producer.credit == 1 && pointSaleActive.credit == 1 && (pointSaleActive.credit_functioning != 'user' || (pointSaleActive.credit_functioning == 'user' && user.credit_active))">
<input type="checkbox" id="use-credit" v-model="useCredit" disabled="disabled"
v-if="pointSaleActive.credit_functioning == 'mandatory' || (pointSaleActive.credit_functioning == 'user' && user.credit_active)"/>
<input type="checkbox" id="use-credit" v-model="useCredit" v-else/> <label
for="use-credit">Utiliser mon Crédit ({{ formatPrice(user.credit)
}})</label>
<div class="comment">
<label for="order-comment">Commentaire</label>
<textarea id="order-comment" v-model="comment" class="form-control"></textarea>
</div>

<template
v-if="producer.credit == 1 && pointSaleActive.credit == 1 && (pointSaleActive.credit_functioning == 'mandatory' || (pointSaleActive.credit_functioning == 'user' && user.credit_active)) && !checkCreditLimit(order) ">
<div class="alert alert-danger">
Vous devez
<template v-if="producer.online_payment == 1"><a
href="<?= \Yii::$app->urlManager->createUrl(['credit/add']) ?>">recharger
votre crédit</a></template>
<template v-else>recharger votre crédit</template>
auprès de votre producteur ou supprimer des produits.</span>
Votre producteur n'autorise pas un crédit inférieur
à <strong>{{ formatPrice(producer.credit_limit) }}</strong>.
</div>
<div class="block-actions">
<a class="btn btn-primary"
href="<?= \Yii::$app->urlManager->createUrl(['site/index']) ?>">Retour à
l'accueil</a>
</div>
</template>
<template v-else>
<div class="credit">
<div v-if="user && producer.credit == 1 && pointSaleActive.credit == 1 && (pointSaleActive.credit_functioning != 'user' || (pointSaleActive.credit_functioning == 'user' && user.credit_active))">
<input type="checkbox" id="use-credit" v-model="useCredit" disabled="disabled"
v-if="pointSaleActive.credit_functioning == 'mandatory' || (pointSaleActive.credit_functioning == 'user' && user.credit_active)"/>
<input type="checkbox" id="use-credit" v-model="useCredit" v-else/> <label
for="use-credit">Utiliser mon Crédit ({{ formatPrice(user.credit)
}})</label>

<div class="info" v-if="useCredit">
<template v-if="order == null || order.amount_paid == 0">
<span v-if="checkCreditLimit(order)">{{ priceTotal(true) }} seront débités</span>
<span v-else>
<div class="info" v-if="useCredit">
<template v-if="order == null || order.amount_paid == 0">
<span v-if="checkCreditLimit(order)">{{ priceTotal(true) }} seront débités</span>
<span v-else>
{{ formatPrice(user.credit) }} seront débités. (Limite de crédit à {{ formatPrice(producer.credit_limit) }})<br/>
Restera {{ formatPrice(priceTotal() - user.credit) }} à régler.
</span>
</template>
<template
v-else-if="order != null && order.amount_paid > 0 && order.amount_paid < priceTotal()">
<span v-if="checkCreditLimit(order)">{{ formatPrice(priceTotal() - order.amount_paid) }} seront débités</span>
<span v-else>
</template>
<template
v-else-if="order != null && order.amount_paid > 0 && order.amount_paid < priceTotal()">
<span v-if="checkCreditLimit(order)">{{ formatPrice(priceTotal() - order.amount_paid) }} seront débités</span>
<span v-else>
{{ formatPrice(user.credit) }} seront débités. (Limite de crédit à {{ formatPrice(producer.credit_limit) }})<br/>
Restera {{ formatPrice(priceTotal() - order.amount_paid - user.credit) }} à régler.
</span>
</template>
<template v-else-if="order != null && order.amount_paid > priceTotal()">
<span>{{ formatPrice(order.amount_paid - priceTotal()) }} seront remboursés</span>
</template>
</div>
</div>
<div v-else>
<span class="glyphicon glyphicon-chevron-right"></span>
<?php if ($producerModule->isOnlinePaymentActiveAndTypeOrder($producer)): ?>
La commande est à payer en ligne lors de l'étape suivante.
<?php elseif ($producer->option_payment_info && strlen($producer->option_payment_info) > 0): ?>
Confirmez votre commande et retrouvez les informations liées au paiement sur la page suivante.
<?php else: ?>
La commande sera à régler sur place.
<?php endif; ?>
</template>
<template v-else-if="order != null && order.amount_paid > priceTotal()">
<span>{{ formatPrice(order.amount_paid - priceTotal()) }} seront remboursés</span>
</template>
</div>
</div>
<div v-else>
<span class="glyphicon glyphicon-chevron-right"></span>
<?php if ($producerModule->isOnlinePaymentActiveAndTypeOrder($producer)): ?>
La commande est à payer en ligne lors de l'étape suivante.
<?php elseif ($producer->option_payment_info && strlen($producer->option_payment_info) > 0): ?>
Confirmez votre commande et retrouvez les informations liées au paiement sur la page suivante.
<?php else: ?>
La commande sera à régler sur place.
<?php endif; ?>
</div>
</div>
<div id="signup-guest" v-if="!user && producer.option_allow_order_guest">
<h3>Informations personnelles</h3>
<form action="#">
<div class="form-group field-signupguest-email required">
<label class="control-label" for="signupguest-email">Email</label>
<input type="email" id="signupguest-email" class="form-control"
name="SignupForm[email]">
<p class="help-block help-block-error"></p>
</div>
<div class="form-group field-signupguest-firstname required">
<label class="control-label" for="signupguest-firstname">Prénom</label>
<input type="text" id="signupguest-firstname" class="form-control"
name="SignupForm[firstname]">
<p class="help-block help-block-error"></p>
</div>
<div class="form-group field-signupguest-lastname required">
<label class="control-label" for="signupguest-lastname">Nom</label>
<input type="text" id="signupguest-lastname" class="form-control"
name="SignupForm[lastname]">
<p class="help-block help-block-error"></p>
</div>
<div class="form-group field-signupguest-phone required">
<label class="control-label" for="signupguest-phone">Téléphone</label>
<input type="text" id="signupguest-phone" class="form-control"
name="SignupForm[phone]">
<p class="help-block help-block-error"></p>
</div>
</form>
</div>
<div class="block-actions">
<button class="btn btn-primary" disabled="disabled" v-if="disableConfirmButton || !oneProductOrdered()">Je
confirme ma commande
</button>
<button class="btn btn-primary" v-else @click="confirmClick">Je confirme ma
commande
</button>
</div>
</template>
</div>
</div>
<div id="signup-guest" v-if="!user && producer.option_allow_order_guest">
<h3>Informations personnelles</h3>
<form action="#">
<div class="form-group field-signupguest-email required">
<label class="control-label" for="signupguest-email">Email</label>
<input type="email" id="signupguest-email" class="form-control"
name="SignupForm[email]">
<p class="help-block help-block-error"></p>
</div>
<!--<div class="form-group field-signupguest-password required">
<label class="control-label" for="signupguest-password">Mot de passe</label>
<input type="password" id="signupguest-password" class="form-control" name="SignupForm[password]">
<p class="help-block help-block-error"></p>
</div>-->
<div class="form-group field-signupguest-firstname required">
<label class="control-label" for="signupguest-firstname">Prénom</label>
<input type="text" id="signupguest-firstname" class="form-control"
name="SignupForm[firstname]">
<p class="help-block help-block-error"></p>
</div>
<div class="form-group field-signupguest-lastname required">
<label class="control-label" for="signupguest-lastname">Nom</label>
<input type="text" id="signupguest-lastname" class="form-control"
name="SignupForm[lastname]">
<p class="help-block help-block-error"></p>
</div>
<div class="form-group field-signupguest-phone required">
<label class="control-label" for="signupguest-phone">Téléphone</label>
<input type="text" id="signupguest-phone" class="form-control"
name="SignupForm[phone]">
<p class="help-block help-block-error"></p>
</div>
</form>
</div>
<div class="block-actions">
<button class="btn btn-primary" disabled="disabled" v-if="disableConfirmButton">Je
confirme ma commande
</button>
<button class="btn btn-primary" v-else @click="confirmClick">Je confirme ma
commande
</button>
</div>
</template>

</div>
<div class="alert alert-warning" v-else>
Aucun produit disponible
</div>
</div>
</div>
</transition>
<transition name="slide">

</transition>
</div>
</div>

@@ -647,7 +646,7 @@ $this->setTitle('Commander');
</div>

<script type="text/x-template" id="template-step-date">
<li id="step-date" :class="'col-md-3'+((step == 'date') ? ' active' : '')+(first ? ' first' : '')">
<li id="step-date" :class="'col-md-4'+((step == 'date') ? ' active' : '')+(first ? ' first' : '')">
<button @click="changeStep('date')" :class="'btn '+ (step == 'date' ? 'btn-primary' : 'btn-default')"
:disabled="producer && producer.option_order_entry_point == 'point-sale' && !pointSaleActive">
<span class="button-content"><span class="glyphicon glyphicon-time"></span> Date</span></span>
@@ -659,7 +658,7 @@ $this->setTitle('Commander');
</script>

<script type="text/x-template" id="template-step-point-sale">
<li id="step-point-sale" :class="'col-md-3'+((step == 'point-sale') ? ' active ' : '')+(first ? ' first' : '')">
<li id="step-point-sale" :class="'col-md-4'+((step == 'point-sale') ? ' active ' : '')+(first ? ' first' : '')">
<button @click="changeStep('point-sale')"
:class="'btn '+ (step == 'point-sale' ? 'btn-primary' : 'btn-default')"
:disabled="producer && (producer.option_order_entry_point == 'date' && step == 'date')">

+ 1
- 1
producer/views/site/index.php 파일 보기

@@ -157,7 +157,7 @@ $this->setPageTitle(Html::encode($producer->type . ' à ' . $producer->city));
'contentOptions' => ['class' => 'photo'],
'value' => function ($model) {
if (strlen($model->photo)) {
return '<img class="photo-product" src="'. Image::getThumbnailSmall($model->photo).'" />';
return '<a class="product-photo" href="'.Image::getThumbnailBig($model->photo).'" title="'.Html::encode($model->name).'"><img class="photo-product" src="'. Image::getThumbnailSmall($model->photo).'" />';
}
return '';
}

+ 36
- 34
producer/web/css/screen.css 파일 보기

@@ -1347,7 +1347,7 @@ termes.
padding-right: 0px;
}
/* line 72, ../sass/order/_order.scss */
.order-order #main #app-order-order #steps ul li#step-payment .btn::after, .order-order #main #app-order-order #steps ul li.first .btn::before {
.order-order #main #app-order-order #steps ul li#step-products .btn::after, .order-order #main #app-order-order #steps ul li.first .btn::before {
display: none;
}
/* line 77, ../sass/order/_order.scss */
@@ -1471,26 +1471,28 @@ termes.
font-weight: bold;
}
/* line 222, ../sass/order/_order.scss */
.order-order #main #app-order-order table#points-sale td.name .comment {
.order-order #main #app-order-order table#points-sale td.name .comment,
.order-order #main #app-order-order table#points-sale td.name .minimum-order-amount {
color: gray;
}
/* line 225, ../sass/order/_order.scss */
.order-order #main #app-order-order table#points-sale td.name .comment a {
/* line 226, ../sass/order/_order.scss */
.order-order #main #app-order-order table#points-sale td.name .comment a,
.order-order #main #app-order-order table#points-sale td.name .minimum-order-amount a {
color: #F39C12;
}
/* line 231, ../sass/order/_order.scss */
/* line 232, ../sass/order/_order.scss */
.order-order #main #app-order-order table#points-sale td.actions {
width: 150px;
}
/* line 233, ../sass/order/_order.scss */
/* line 234, ../sass/order/_order.scss */
.order-order #main #app-order-order table#points-sale td.actions button {
width: 100%;
}
/* line 239, ../sass/order/_order.scss */
/* line 240, ../sass/order/_order.scss */
.order-order #main #app-order-order table#points-sale tr.selected td {
background-color: white;
}
/* line 247, ../sass/order/_order.scss */
/* line 248, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products td.category-name {
font-family: "highvoltageregular";
font-size: 22px;
@@ -1498,12 +1500,12 @@ termes.
text-transform: uppercase;
padding-top: 13px;
}
/* line 254, ../sass/order/_order.scss */
/* line 255, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products td.category-name .glyphicon-triangle-bottom,
.order-order #main #app-order-order table#products td.category-name .glyphicon-triangle-right {
font-size: 15px;
}
/* line 259, ../sass/order/_order.scss */
/* line 260, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products td.category-name span.label {
font-family: "Arial";
font-weight: normal;
@@ -1511,17 +1513,17 @@ termes.
text-transform: none;
margin-left: 15px;
}
/* line 268, ../sass/order/_order.scss */
/* line 269, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products td.category-name:hover {
cursor: pointer;
background-color: #F39C12;
color: white;
}
/* line 276, ../sass/order/_order.scss */
/* line 277, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products td.photo img {
width: 100px;
}
/* line 286, ../sass/order/_order.scss */
/* line 287, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products .price-unit .decreasing-prices {
margin-top: 10px;
font-size: 10px;
@@ -1529,34 +1531,34 @@ termes.
padding-bottom: 2px;
margin-bottom: 0px;
}
/* line 294, ../sass/order/_order.scss */
/* line 295, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products .price-unit .decreasing-prices ul li {
margin-bottom: 5px;
}
/* line 296, ../sass/order/_order.scss */
/* line 297, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products .price-unit .decreasing-prices ul li strong {
font-weight: bold;
}
/* line 304, ../sass/order/_order.scss */
/* line 305, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products .price-unit, .order-order #main #app-order-order table#products .price-total {
width: 135px;
text-align: center;
}
/* line 308, ../sass/order/_order.scss */
/* line 309, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products .price-unit .unit, .order-order #main #app-order-order table#products .price-total .unit {
color: gray;
font-size: 13px;
}
/* line 313, ../sass/order/_order.scss */
/* line 314, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products .td-quantity {
width: 175px;
}
/* line 315, ../sass/order/_order.scss */
/* line 316, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products .td-quantity input.quantity {
text-align: center;
border-right: 0px none;
}
/* line 319, ../sass/order/_order.scss */
/* line 320, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products .td-quantity .input-group-addon {
padding: 5px;
padding-left: 0px;
@@ -1564,69 +1566,69 @@ termes.
border-left: 0px none;
border-right: 0px none;
}
/* line 330, ../sass/order/_order.scss */
/* line 331, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products tr.total .summary h3 {
margin-top: 0px;
font-family: "capsuularegular";
text-transform: none;
margin-bottom: 5px;
}
/* line 337, ../sass/order/_order.scss */
/* line 338, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products tr.total .summary ul {
margin-bottom: 15px;
padding-left: 20px;
font-size: 23px;
}
/* line 344, ../sass/order/_order.scss */
/* line 345, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products tr.total .summary ul li .quantity {
font-size: 18px;
}
/* line 348, ../sass/order/_order.scss */
/* line 349, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products tr.total .summary ul li .name {
font-family: "capsuularegular";
font-size: 24px;
}
/* line 352, ../sass/order/_order.scss */
/* line 353, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products tr.total .summary ul li .other {
font-family: "capsuularegular";
font-size: 18px;
}
/* line 360, ../sass/order/_order.scss */
/* line 361, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products tr.total .price-total {
font-size: 23px;
}
/* line 368, ../sass/order/_order.scss */
/* line 369, ../sass/order/_order.scss */
.order-order #main #app-order-order #content-step-payment .delivery {
margin-bottom: 20px;
}
/* line 371, ../sass/order/_order.scss */
/* line 372, ../sass/order/_order.scss */
.order-order #main #app-order-order #content-step-payment .delivery .delivery-home {
margin-bottom: 20px;
}
/* line 380, ../sass/order/_order.scss */
/* line 381, ../sass/order/_order.scss */
.order-order #main #app-order-order #content-step-payment .comment {
margin-bottom: 20px;
}
/* line 385, ../sass/order/_order.scss */
/* line 386, ../sass/order/_order.scss */
.order-order #main #app-order-order #content-step-payment .credit .info {
margin-left: 20px;
color: gray;
}
/* line 392, ../sass/order/_order.scss */
/* line 393, ../sass/order/_order.scss */
.order-order #main #app-order-order #specific-delays {
margin-top: 15px;
}
/* line 400, ../sass/order/_order.scss */
/* line 401, ../sass/order/_order.scss */
.order-order #main #app-order-order #infos {
margin-top: 30px;
}
/* line 402, ../sass/order/_order.scss */
/* line 403, ../sass/order/_order.scss */
.order-order #main #app-order-order #infos .panel-body {
padding-top: 0px;
white-space: pre-line;
}

/* line 412, ../sass/order/_order.scss */
/* line 413, ../sass/order/_order.scss */
#main #content .panel h3 {
font-family: "highvoltageregular";
margin: 0px;

+ 5
- 3
producer/web/js/producer.js 파일 보기

@@ -85,6 +85,8 @@ function opendistrib_products() {
$(this).hide();
$(this).parent().find('.content').show();
});

$('a.product-photo').simpleLightbox();
}

function opendistrib_datepicker() {
@@ -101,11 +103,11 @@ function opendistrib_fix_width_sidebar() {
}

function opendistrib_scroll(id) {
if ($("#" + id).size())
if ($("#" + id).size()) {
$('html,body').animate({
scrollTop: $("#" + id).offset().top
},
1000);
}, 500);
}
}

function opendistrib_base_url(with_slug) {

+ 11
- 1
producer/web/js/vuejs/order-order.js 파일 보기

@@ -509,6 +509,7 @@ var app = new Vue({
return thePriceWithTax;
}
},

confirmClick: function() {

var app = this ;
@@ -516,7 +517,16 @@ var app = new Vue({
// delivery
if(app.pointSaleActive.is_home_delivery && !app.deliveryAddress) {
this.errors = [] ;
this.errors.push('Veuillez saisir une adresse de livraison') ;
this.errors.push('Veuillez saisir une adresse de livraison.') ;
opendistrib_scroll('page-title');
return false ;
}

// montant minimum de commande
if(app.pointSaleActive.minimum_order_amount > 0 && app.priceTotal() < app.pointSaleActive.minimum_order_amount) {
this.errors = [] ;
this.errors.push('Le montant minimum de commande est de '+app.formatPrice(app.pointSaleActive.minimum_order_amount)+' pour ce point de vente.') ;
opendistrib_scroll('page-title');
return false ;
}


+ 3
- 2
producer/web/sass/order/_order.scss 파일 보기

@@ -69,7 +69,7 @@
padding-right: 0px ;
}
&#step-payment .btn::after,
&#step-products .btn::after,
&.first .btn::before {
display: none ;
}
@@ -219,7 +219,8 @@
font-weight: bold;
}

.comment {
.comment,
.minimum-order-amount {
color: gray;

a {

Loading…
취소
저장