Browse Source

Merge branch 'develop'

master
Guillaume Bourgeois 6 months ago
parent
commit
4b1a5de1ee
74 changed files with 1838 additions and 196 deletions
  1. +1
    -1
      backend/controllers/InvoiceController.php
  2. +1
    -0
      backend/controllers/ProducerInvoiceController.php
  3. +42
    -0
      backend/controllers/SponsorshipController.php
  4. +3
    -7
      backend/controllers/TaxRateAdminController.php
  5. +39
    -0
      backend/controllers/UserAdminController.php
  6. +91
    -5
      backend/controllers/UserController.php
  7. +29
    -12
      backend/views/dashboard-admin/index.php
  8. +23
    -0
      backend/views/delivery-note/index.php
  9. +1
    -1
      backend/views/distribution/index.php
  10. +1
    -1
      backend/views/document/download.php
  11. +19
    -8
      backend/views/invoice/index.php
  12. +13
    -0
      backend/views/layouts/left.php
  13. +68
    -33
      backend/views/producer-admin/_form.php
  14. +9
    -0
      backend/views/producer-admin/index.php
  15. +10
    -0
      backend/views/producer-invoice/index.php
  16. +129
    -0
      backend/views/sponsorship/index.php
  17. +2
    -1
      backend/views/subscription/index.php
  18. +124
    -0
      backend/views/user-admin/email_deliverability.php
  19. +11
    -1
      backend/views/user-admin/index.php
  20. +18
    -2
      backend/views/user/_form.php
  21. +6
    -0
      backend/views/user/_menu_navigation.php
  22. +15
    -1
      backend/views/user/index.php
  23. +124
    -0
      backend/views/user/messages.php
  24. +9
    -0
      backend/views/user/view.php
  25. +9
    -0
      backend/web/js/backend.js
  26. +1
    -1
      backend/web/js/vuejs/distribution-index.js
  27. +1
    -0
      common/components/BusinessLogic.php
  28. +6
    -0
      common/components/BusinessLogicTrait.php
  29. +1
    -1
      common/config/params.php
  30. +30
    -0
      common/versions/24.6.C.php
  31. +26
    -0
      console/migrations/m240611_091617_add_column_user_problem_receiving_emails.php
  32. +37
    -0
      console/migrations/m240611_125313_create_table_user_message.php
  33. +26
    -0
      console/migrations/m240612_062929_add_column_user_note_email.php
  34. +41
    -0
      console/migrations/m240613_070747_add_columns_producer_sponsorship.php
  35. +29
    -9
      domain/Config/TaxRate/TaxRate.php
  36. +0
    -9
      domain/Config/TaxRate/TaxRateBuilder.php
  37. +24
    -0
      domain/Config/TaxRate/TaxRateManager.php
  38. +26
    -1
      domain/Document/DeliveryNote/DeliveryNoteSearch.php
  39. +55
    -7
      domain/Document/Invoice/InvoiceSearch.php
  40. +2
    -0
      domain/Feature/Feature/Feature.php
  41. +2
    -0
      domain/Feature/Feature/FeatureDefinition.php
  42. +1
    -1
      domain/Feature/Feature/FeatureRepository.php
  43. +56
    -2
      domain/Producer/Producer/Producer.php
  44. +8
    -0
      domain/Producer/Producer/ProducerBuilder.php
  45. +14
    -0
      domain/Producer/Producer/ProducerRepository.php
  46. +13
    -0
      domain/Producer/Producer/ProducerRepositoryQuery.php
  47. +30
    -0
      domain/Producer/Producer/ProducerSolver.php
  48. +18
    -3
      domain/User/User/User.php
  49. +6
    -1
      domain/User/User/UserManager.php
  50. +12
    -0
      domain/User/User/UserRepository.php
  51. +6
    -0
      domain/User/User/UserRepositoryQuery.php
  52. +10
    -1
      domain/User/User/UserSearch.php
  53. +113
    -0
      domain/User/UserMessage/UserMessage.php
  54. +20
    -0
      domain/User/UserMessage/UserMessageBuilder.php
  55. +13
    -0
      domain/User/UserMessage/UserMessageDefinition.php
  56. +35
    -0
      domain/User/UserMessage/UserMessageManager.php
  57. +38
    -0
      domain/User/UserMessage/UserMessageModule.php
  58. +61
    -0
      domain/User/UserMessage/UserMessageRepository.php
  59. +28
    -0
      domain/User/UserMessage/UserMessageRepositoryQuery.php
  60. +30
    -3
      frontend/controllers/SiteController.php
  61. +11
    -0
      frontend/forms/SignupForm.php
  62. +13
    -0
      frontend/views/site/_signup_sponsorship_message.php
  63. +5
    -0
      frontend/views/site/signup.php
  64. +10
    -5
      frontend/views/site/signup_producer.php
  65. +13
    -0
      producer/controllers/NewsletterController.php
  66. +1
    -1
      producer/controllers/OrderController.php
  67. +10
    -0
      producer/controllers/SiteController.php
  68. +24
    -0
      producer/views/layouts/main.php
  69. +33
    -18
      producer/views/newsletter/index.php
  70. +0
    -1
      producer/views/subscription/_form.php
  71. +74
    -55
      producer/web/css/screen.css
  72. +2
    -1
      producer/web/js/vuejs/order-order.js
  73. +22
    -1
      producer/web/sass/_responsive.scss
  74. +4
    -2
      producer/web/sass/order/_order.scss

+ 1
- 1
backend/controllers/InvoiceController.php View File

@@ -60,7 +60,7 @@ class InvoiceController extends DocumentController
public function actionAjaxDeleteDeliveryNote($idInvoice, $idDeliveryNote)
{
$orderModule = $this->getOrderModule();
$invoiceModule = $this-> getInvoiceModule();
$invoiceModule = $this-> getInvoiceModule();
$deliveryNoteModule = $this->getDeliveryNoteModule();
$invoice = $invoiceModule->findOneInvoiceById($idInvoice);
$deliveryNote = $deliveryNoteModule->findOneDeliveryNoteById($idDeliveryNote);

+ 1
- 0
backend/controllers/ProducerInvoiceController.php View File

@@ -31,6 +31,7 @@ class ProducerInvoiceController extends BackendController
public function actionIndex()
{
return $this->render('index', [
'producer' => $this->getProducerCurrent(),
'invoicesArray' => $this->getProducerModule()
->getDolibarrUtils()
->getDolibarrProducerInvoices($this->getProducerCurrent())

+ 42
- 0
backend/controllers/SponsorshipController.php View File

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

namespace backend\controllers;

use backend\controllers\BackendController;
use domain\Feature\Feature\Feature;
use yii\filters\AccessControl;

class SponsorshipController extends BackendController
{
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::class,
'rules' => [
[
'allow' => true,
'roles' => ['@'],
'matchCallback' => function ($rule, $action) {
return
$this->getFeatureModule()->getChecker()->isEnabled(Feature::ALIAS_SPONSORSHIP)
&& $this->getUserModule()
->getAuthorizationChecker()
->isGrantedAsProducer($this->getUserCurrent());
}
],
],
],
];
}

public function actionIndex()
{
$producerCurrent = $this->getProducerCurrent();

return $this->render('index', [
'producer' => $producerCurrent,
'producersGodsonsArray' => $this->getProducerModule()->getRepository()->findProducersSponsorshipGodsons($producerCurrent)
]);
}
}

+ 3
- 7
backend/controllers/TaxRateAdminController.php View File

@@ -88,7 +88,7 @@ class TaxRateAdminController extends BackendController

public function actionCreate()
{
$model = $this-> getTaxRateModule()->instanciateTaxRate();
$model = $this->getTaxRateModule()->getBuilder()->instanciateTaxRate();

if ($model->load(\Yii::$app->request->post()) && $model->save()) {
$this->setFlash('success', 'Taxe créée.');
@@ -116,19 +116,15 @@ class TaxRateAdminController extends BackendController

public function actionDelete(int $id)
{
$taxRateModule = $this-> getTaxRateModule();
$taxRate = $this->findModel($id);
$taxRateModule->delete($taxRate);

$taxRate->delete();
$this->setFlash('success', 'Taxe supprimé');

return $this->redirect(['tax-rate-admin/index']);
}

protected function findModel($id)
{
$taxRateModule = $this-> getTaxRateModule();
if (($taxRate = $taxRateModule->findOneTaxRateById($id)) !== null) {
if (($taxRate = $this->getTaxRateModule()->getRepository()->findOneTaxRateById($id)) !== null) {
return $taxRate;
} else {
throw new NotFoundHttpException('The requested page does not exist.');

+ 39
- 0
backend/controllers/UserAdminController.php View File

@@ -41,6 +41,7 @@ namespace backend\controllers;
use domain\User\User\UserSearch;
use Yii;
use yii\filters\AccessControl;
use yii\web\NotFoundHttpException;

class UserAdminController extends BackendController
{
@@ -81,6 +82,35 @@ class UserAdminController extends BackendController
]);
}

public function actionEmailDeliverability()
{
$searchModel = new UserSearch();
$searchModel->problem_receiving_emails = true;
$dataProvider = $searchModel->search([
'UserSearch' => isset(\Yii::$app->request->queryParams['UserSearch']) ?
Yii::$app->request->queryParams['UserSearch'] : []
]);

return $this->render('email_deliverability', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider
]);
}

public function actionReportProblemReceivingEmails(int $id)
{
$user = $this->findModel($id);

if($this->getUserModule()->getManager()->reportProblemReceivingEmails($user)) {
$this->setFlash('success', "L'utilisateur <strong>".$this->getUserModule()->getSolver()->getUsername($user)."</strong> a bien été signalé comme ayant des problèmes dans la réception de vos emails. L'administrateur débloquera la situation dès que possible et préviendra l'utilisateur.");
}
else {
$this->setFlash('error', "Une erreur est survenue.");
}

return $this->redirect('index');
}

public function actionRedirectView(int $idUserProducer)
{
$userCurrent = $this->getUserCurrent();
@@ -96,4 +126,13 @@ class UserAdminController extends BackendController
return $this->redirectReferer();
}
}

protected function findModel($id)
{
if (($user = $this->getUserModule()->findOneUserById($id)) !== null) {
return $user;
} else {
throw new NotFoundHttpException("Utilisateur introuvable");
}
}
}

+ 91
- 5
backend/controllers/UserController.php View File

@@ -69,11 +69,16 @@ class UserController extends BackendController
'allow' => true,
'roles' => ['@'],
'matchCallback' => function ($rule, $action) {
return $this->getUserModule()
->getAuthorizationChecker()
->isGrantedAsProducer($this->getUserCurrent());
$userCurrent = $this->getUserCurrent();
$authorizationChecker = $this->getUserModule()->getAuthorizationChecker();
if(in_array($action->id, ['messages', 'message-delete'])) {
return $authorizationChecker->isGrantedAsAdministrator($userCurrent);
}
else {
return $authorizationChecker->isGrantedAsProducer($userCurrent);
}
}
]
],
],
],
];
@@ -246,7 +251,7 @@ class UserController extends BackendController
}

$this->setFlash('success', 'Utilisateur <strong>' . Html::encode($userModule->getUsername($model)) . '</strong> modifié.');
return $this->redirect(['view', 'id' => $model->id]);
return $this->redirect(['update', 'id' => $model->id]);
}
} else {
throw new UserException("Vous ne pouvez pas modifier cet utilisateur.");
@@ -258,6 +263,87 @@ class UserController extends BackendController
]));
}

public function actionMessages(int $id, int $idUserMessage = null)
{
$userMessageModule = $this->getUserMessageModule();
$user = $this->findModel($id);
$isUpdate = false;

if($idUserMessage) {
$isUpdate = true;
$userMessageModel = $this->findUserMessage($idUserMessage);
}
else {
$userMessageModel = $userMessageModule->getBuilder()->instanciateUserMessage(
$user,
$this->getUserCurrent()
);
}

if($userMessageModel->load(\Yii::$app->request->post()) && $userMessageModel->validate()) {
if($isUpdate) {
$userMessageModel->save();
$this->setFlash('success', "Le message a bien été modifié.");
}
else {
$userMessageModule->getManager()->createUserMessage(
$user,
$userMessageModel->getMessage(),
$this->getUserCurrent()
);
$this->setFlash('success', "Le message a bien été envoyé à l'utilisateur.");
}

return $this->redirect(['messages', 'id' => $id]);
}

return $this->render('messages', [
'user' => $user,
'userMessageModel' => $userMessageModel,
'userMessagesDataProvider' => $userMessageModule->getRepository()
->queryUserMessagesByUser($user)
->getDataProvider(20)
]);
}

public function actionMessageDelete(int $idUser, int $idUserMessage)
{
$userMessageModule = $this->getUserMessageModule();
$userMessage = $this->findUserMessage($idUserMessage);
if($userMessageModule->getManager()->deleteUserMessage($userMessage)) {
$this->setFlash('success', "Le message a bien été supprimé.");
}
else {
$this->setFlash('error', "Une erreur est survenue pendant la suppression du message.");
}
return $this->redirect(['messages', 'id' => $idUser]);
}

public function findUserMessage(int $idUserMessage)
{
$userMessage = $this->getUserMessageModule()->getRepository()->findOneUserMessageById($idUserMessage);

if(!$userMessage) {
throw new NotFoundHttpException("Le message utilisateur n'a pas été trouvé.");
}

return $userMessage;
}

public function actionReportProblemReceivingEmails(int $id)
{
$user = $this->findModel($id);

if($this->getUserModule()->getManager()->reportProblemReceivingEmails($user)) {
$this->setFlash('success', "L'utilisateur <strong>".$this->getUserModule()->getSolver()->getUsername($user)."</strong> a bien été signalé comme ayant des problèmes dans la réception de vos emails. L'administrateur débloquera la situation dès que possible et préviendra l'utilisateur.");
}
else {
$this->setFlash('error', "Une erreur est survenue.");
}

return $this->redirect('index');
}

public function initForm($model)
{
$userPointSaleModule = $this->getUserPointSaleModule();

+ 29
- 12
backend/views/dashboard-admin/index.php View File

@@ -10,18 +10,13 @@ $this->setMetaRefresh(true);
?>

<div class="dashboard-admin-index">
<div class="row">
<div class="col-lg-6 col-xs-6">
<?= AdminLTE::smallBox(
$countUsersOnline,
'En ligne',
$countUsersOnline ? 'green' : 'blue',
'wifi',
Yii::$app->urlManager->createUrl('online-admin/index')
) ?>

<div id="producers-online" class="box box-solid">
<div class="box-header with-border">
<i class="fa fa-bookmark"></i>
<h3 class="box-title">Producteurs en ligne</h3>
</div>
<div class="col-lg-6 col-xs-6" id="producers-online">
<!--<h3>Producteurs en ligne :</h3>-->
<div class="box-body">
<?php if($usersWithStatusProducerOnlineArray && count($usersWithStatusProducerOnlineArray) > 0): ?>
<?php foreach ($usersWithStatusProducerOnlineArray as $userWithStatusProducerOnline): ?>
<?php $producer = $userWithStatusProducerOnline->producer; ?>
@@ -33,10 +28,32 @@ $this->setMetaRefresh(true);
<?php endforeach; ?>
<div class="clr"></div>
<?php else: ?>
<div class="alert alert-info">Aucun producteur en ligne actuellement.</div>
<p><em>Aucun producteur en ligne actuellement.</em></p>
<?php endif; ?>
</div>
</div>

<div class="row">
<div class="col-lg-6 col-xs-6">
<?= AdminLTE::smallBox(
$countUsersOnline,
'Utilisateurs en ligne',
$countUsersOnline ? 'green' : 'blue',
'wifi',
Yii::$app->urlManager->createUrl('online-admin/index')
) ?>
</div>
<div class="col-lg-6 col-xs-6">
<?php $countUsersWithProblemReceivingEmails = $this->getUserModule()->getRepository()->countUsersWithProblemReceivingEmails(); ?>
<?= AdminLTE::smallBox(
$countUsersWithProblemReceivingEmails,
'Problèmes réception emails',
$countUsersWithProblemReceivingEmails ? 'red' : 'green',
'send',
Yii::$app->urlManager->createUrl('user-admin/email-deliverability')
) ?>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-xs-6">
<?= AdminLTE::smallBox(

+ 23
- 0
backend/views/delivery-note/index.php View File

@@ -117,9 +117,32 @@ $this->addButton(['label' => 'Nouveau bon de livraison <span class="glyphicon gl
return $deliveryNoteModule->getAmountWithTax($deliveryNote, Order::INVOICE_AMOUNT_TOTAL, true);
}
],
[
'attribute' => 'with_invoice',
'header' => 'Facture',
'filter' => [
0 => 'Non',
1 => 'Oui',
],
'format' => 'raw',
'headerOptions' => ['class' => 'column-hide-on-mobile'],
'filterOptions' => ['class' => 'column-hide-on-mobile'],
'contentOptions' => ['class' => 'column-hide-on-mobile'],
'value' => function ($deliveryNote) use ($deliveryNoteModule) {
$invoice = $deliveryNoteModule->getSolver()->getInvoice($deliveryNote);
if($invoice) {
return Html::a($invoice->reference, ['invoice/update', 'id' => $invoice->id], ['class' => 'btn btn-xs btn-default']);
}
return '';
}
],
[
'attribute' => 'is_sent',
'header' => 'Envoyé',
'filter' => [
0 => 'Non',
1 => 'Oui',
],
'format' => 'raw',
'headerOptions' => ['class' => 'column-hide-on-mobile'],
'filterOptions' => ['class' => 'column-hide-on-mobile'],

+ 1
- 1
backend/views/distribution/index.php View File

@@ -91,7 +91,7 @@ $this->setPageTitle('Distributions') ;
<h4>
Distribution du <strong>{{ dateFormat }}</strong>
<a v-if="distribution.active" class="btn btn-default" :href="distribution.url_order" @click="copyLinkOrder($event, distribution.url_order)"><span class="glyphicon glyphicon-link"></span></a>
<span v-if="distribution.is_leave_period" class="label label-info">
<span v-if="distribution.is_leave_period" class="label label-default">
<span class="glyphicon glyphicon-info-sign"></span>
Congés
</span>

+ 1
- 1
backend/views/document/download.php View File

@@ -173,7 +173,7 @@ $documentPriceDecimals = (int) $producerModule->getConfig('option_document_price
</tr>

<?php
$taxRateArray = $this-> getTaxRateModule()->findTaxRatesAsArray();
$taxRateArray = $this-> getTaxRateModule()->getRepository()->findTaxRatesAsArray();
foreach ($documentModule->getTotalVatArray($document, $typeAmount) as $idTaxRate => $totalVat): ?>
<tr>
<td class="align-right" colspan="4">

+ 19
- 8
backend/views/invoice/index.php View File

@@ -52,7 +52,6 @@ $this->addButton(['label' => 'Nouvelle facture <span class="glyphicon glyphicon-
?>

<div class="invoice-index">

<?php if (Invoice::searchCount()): ?>
<?= GridView::widget([
'filterModel' => $searchModel,
@@ -82,7 +81,7 @@ $this->addButton(['label' => 'Nouvelle facture <span class="glyphicon glyphicon-
'name',
[
'attribute' => 'username',
'header' => 'Utilisateur',
'label' => 'Utilisateur',
'headerOptions' => ['class' => 'column-hide-on-mobile'],
'filterOptions' => ['class' => 'column-hide-on-mobile'],
'contentOptions' => ['class' => 'column-hide-on-mobile'],
@@ -93,6 +92,13 @@ $this->addButton(['label' => 'Nouvelle facture <span class="glyphicon glyphicon-
[
'attribute' => 'date',
'header' => 'Date',
'filter' => \yii\jui\DatePicker::widget([
'language' => 'fr',
'dateFormat' => 'dd/MM/yyyy',
'model' => $searchModel,
'attribute' => 'date',
'options' => ['class' => 'form-control']
]),
'headerOptions' => ['class' => 'column-hide-on-mobile'],
'filterOptions' => ['class' => 'column-hide-on-mobile'],
'contentOptions' => ['class' => 'column-hide-on-mobile'],
@@ -105,28 +111,34 @@ $this->addButton(['label' => 'Nouvelle facture <span class="glyphicon glyphicon-
'header' => 'Montant',
'format' => 'raw',
'value' => function ($invoice) use ( $invoiceModule) {
$amountWithTax = $invoiceModule->getAmountWithTax($invoice, Order::INVOICE_AMOUNT_TOTAL);
return $invoiceModule->getAmountWithTax($invoice, Order::INVOICE_AMOUNT_TOTAL, true);
}
],
[
'header' => 'Payée',
'attribute' => 'paid',
'label' => 'Payée',
'filter' => [
0 => 'Non',
1 => 'Oui',
],
'format' => 'raw',
'headerOptions' => ['class' => 'column-hide-on-mobile'],
'filterOptions' => ['class' => 'column-hide-on-mobile'],
'contentOptions' => ['class' => 'column-hide-on-mobile'],
'value' => function ($invoice) use ( $invoiceModule) {
$amountWithTax = $invoiceModule->getAmountWithTax($invoice, Order::INVOICE_AMOUNT_TOTAL);
if($amountWithTax && $invoiceModule->isInvoicePaid($invoice)) {
if($invoiceModule->isInvoicePaid($invoice)) {
return '<span class="label label-success">Oui</span>';
}

return '<span class="label label-default">Non</span>';
}
],
[
'attribute' => 'is_sent',
'header' => 'Envoyée',
'filter' => [
0 => 'Non',
1 => 'Oui',
],
'format' => 'raw',
'headerOptions' => ['class' => 'column-hide-on-mobile'],
'filterOptions' => ['class' => 'column-hide-on-mobile'],
@@ -166,7 +178,6 @@ $this->addButton(['label' => 'Nouvelle facture <span class="glyphicon glyphicon-
'title' => 'Export CSV Evoliz', 'class' => 'btn btn-default'
]);
}

return '';
},
'update' => function ($url, $invoice) {

+ 13
- 0
backend/views/layouts/left.php View File

@@ -145,6 +145,12 @@ $isUserCurrentGrantedAsProducer = $userModule->getAuthorizationChecker()->isGran
'url' => ['/user-admin/index'],
'visible' => $isUserCurrentGrantedAsAdministrator
],
[
'label' => 'Deliverabilité emails',
'icon' => 'paper-plane',
'url' => ['/user-admin/email-deliverability'],
'visible' => $isUserCurrentGrantedAsAdministrator
],
[
'label' => 'Statistiques',
'icon' => 'line-chart',
@@ -273,6 +279,13 @@ $isUserCurrentGrantedAsProducer = $userModule->getAuthorizationChecker()->isGran
'template' => '<a href="{url}">{icon} {label}' . $countProducerInvoicesUnpaidLabel . '</a>'
],
['label' => 'Tarifs & modules', 'icon' => 'euro', 'url' => ['/producer/billing'], 'visible' => $isUserCurrentGrantedAsProducer],
[
'label' => 'Parrainage',
'icon' => 'users',
'url' => ['/sponsorship/index'],
'visible' => $isUserCurrentGrantedAsProducer && $featureChecker->isEnabled(Feature::ALIAS_SPONSORSHIP),
'active' => Yii::$app->controller->id == 'sponsorship'
],
[
'label' => 'Développement',
'icon' => 'code',

+ 68
- 33
backend/views/producer-admin/_form.php View File

@@ -44,40 +44,75 @@ use yii\widgets\ActiveForm;

<?php $form = ActiveForm::begin(); ?>

<h3>Général</h3>
<?= $form->field($model, 'slug') ?>
<?= $form->field($model, 'name') ?>
<?= $form->field($model, 'type')->textInput(['placeholder' => 'Boulangerie, brasserie, ferme ...']); ?>
<?= $form->field($model, 'postcode') ?>
<?= $form->field($model, 'city') ?>
<?= $form->field($model, 'contact_email') ?>
<?= $form->field($model, 'latitude') ?>
<?= $form->field($model, 'longitude') ?>
<?= $form->field($model, 'code')->label('Code d\'accès') ?>
<?= $form->field($model, 'admin_comment')->textarea(['rows' => 7]) ?>
<?= $form->field($model, 'is_new')->checkbox() ?>
<div class="row">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
<i class="fa fa-th-list"></i>
Général
</h3>
</div>
<div class="panel-body">
<?= $form->field($model, 'slug') ?>
<?= $form->field($model, 'name') ?>
<?= $form->field($model, 'type')->textInput(['placeholder' => 'Boulangerie, brasserie, ferme ...']); ?>
<?= $form->field($model, 'postcode') ?>
<?= $form->field($model, 'city') ?>
<?= $form->field($model, 'contact_email') ?>
<?= $form->field($model, 'latitude') ?>
<?= $form->field($model, 'longitude') ?>
<?= $form->field($model, 'code')->label('Code d\'accès') ?>
<?= $form->field($model, 'admin_comment')->textarea(['rows' => 7]) ?>
<?= $form->field($model, 'is_new')->checkbox() ?>
</div>
</div>
</div>
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
<i class="fa fa-euro"></i>
Facturation
</h3>
</div>
<div class="panel-body">
<?= $form->field($model, 'option_billing_type')
->dropDownList(Producer::getBillingTypePopulateDropdown()); ?>
<?= $form->field($model, 'option_billing_frequency')
->dropDownList(Producer::getBillingFrequencyPopulateDropdown()); ?>
<?= $form->field($model, 'option_billing_reduction')
->dropDownList([
0 => 'Non',
1 => 'Oui'
]); ?>
<?= $form->field($model, 'option_billing_reduction_percentage') ?>
<?= $form->field($model, 'option_billing_permanent_transfer')
->dropDownList([
0 => 'Non',
1 => 'Oui'
]); ?>
<?= $form->field($model, 'option_billing_permanent_transfer_amount') ?>
<?= $form->field($model, 'dolibarr_socid') ?>
<?= $form->field($model, 'dolibarr_product_id') ?>
</div>
</div>

<h3>Facturation</h3>
<?= $form->field($model, 'option_billing_type')
->dropDownList(Producer::getBillingTypePopulateDropdown()); ?>
<?= $form->field($model, 'option_billing_frequency')
->dropDownList(Producer::getBillingFrequencyPopulateDropdown()); ?>
<?= $form->field($model, 'option_billing_reduction')
->dropDownList([
0 => 'Non',
1 => 'Oui'
]); ?>
<?= $form->field($model, 'option_billing_reduction_percentage') ?>
<?= $form->field($model, 'option_billing_permanent_transfer')
->dropDownList([
0 => 'Non',
1 => 'Oui'
]); ?>
<?= $form->field($model, 'option_billing_permanent_transfer_amount') ?>
<?= $form->field($model, 'dolibarr_socid') ?>
<?= $form->field($model, 'dolibarr_product_id') ?>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
<i class="fa fa-users"></i>
Parrainage
</h3>
</div>
<div class="panel-body">
<?= $form->field($model, 'sponsorship_sponsor_reward')->checkbox() ?>
</div>
</div>
</div>
</div>

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

+ 9
- 0
backend/views/producer-admin/index.php View File

@@ -122,11 +122,13 @@ $this->addButton(['label' => 'Nouveau producteur <span class="glyphicon glyphico
$summaryQuaterly = $producerModule->getSummaryAmountsToBeBilled($producer, '3 derniers mois', 3);
$isBillingFrequencyBiannual = $producerModule->isBillingFrequencyBiannual($producer);
$summaryBiannual = $producerModule->getSummaryAmountsToBeBilled($producer, '6 derniers mois', 6);
$isBilled = false;

if(($isBillingFrequencyMonthly && $summaryMonthly)
|| ($isBillingFrequencyQuaterly && $summaryQuaterly)
|| ($isBillingFrequencyBiannual && $summaryBiannual)) {

$isBilled = true;
$str .= '<li>';
if ($isBillingFrequencyMonthly && $summaryMonthly) {
$str .= $summaryMonthly;
@@ -155,6 +157,13 @@ $this->addButton(['label' => 'Nouveau producteur <span class="glyphicon glyphico

$str .= '</ul>';

if($isBilled && $producerModule->getSolver()->isFreeBillingPeriodSponsorship($producer)) {
$str .= "<br /><p><span class=\"glyphicon glyphicon-warning-sign\"></span> <strong>Pas de facturation, offre de parrainage jusqu'au ".$producerModule->getSolver()->getDateEndFreeBillingPeriodSponsorship($producer)->format('d/m/Y')."</strong></p>";
if(!$producer->getSponsorshipSponsorReward()) {
$str .= '<p><i class="fa fa-gift"></i> Créer un avoir de 30€ pour le parrain <strong>'.Html::encode($producer->getSponsoredBy()->getName()).'</strong> sur Dolibarr et valider la récompense parrain.</p>';
}
}

return $str;
}
],

+ 10
- 0
backend/views/producer-invoice/index.php View File

@@ -42,6 +42,8 @@ use yii\helpers\Html;
$this->setTitle('Mes factures') ;
$this->addBreadcrumb($this->getTitle()) ;

$producerModule = $this->getProducerModule();

?>

<?php if($invoicesArray && count($invoicesArray)): ?>
@@ -81,4 +83,12 @@ $this->addBreadcrumb($this->getTitle()) ;
<div class="callout callout-info">
<span class="glyphicon glyphicon-info-sign"></span> Vous n'avez encore aucune facture.
</div>

<?php if($producerModule->getSolver()->isFreeBillingPeriodSponsorship($producer)): ?>
<div class="callout callout-info">
<i class="fa fa-gift"></i>
Et comme vous avez été parrainé au moment de votre inscription, vous n'aurez pas de facture <em>Souke</em>
avant le <?= $producerModule->getSolver()->getDateEndFreeBillingPeriodSponsorship($producer)->format('d/m/Y'); ?>.
</div>
<?php endif; ?>
<?php endif; ?>

+ 129
- 0
backend/views/sponsorship/index.php View File

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

/**
Copyright La boîte à pain (2018)

contact@souke.fr

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\helpers\Price;
use yii\helpers\Html;

$this->setTitle('Parrainage') ;
$this->addBreadcrumb($this->getTitle()) ;

$producerModule = $this->getProducerModule();
$sponsorshipLink = $producerModule->getSolver()->getSponsorshipLink($producer);

?>

<?php if($producer->getSponsoredBy()): ?>
<div class="callout callout-info">
<i class="fa fa-users"></i>
Vous avez été parrainé par le producteur <strong><?= Html::encode($producer->getSponsoredBy()->getName()) ?></strong>.
<?php if($producerModule->getSolver()->isFreeBillingPeriodSponsorship($producer)): ?>
<br /><i class="fa fa-gift"></i>
Vous bénéficiez donc de 3 mois offerts et n'aurez pas de facture <em>Souke</em> avant le <?= $producerModule->getSolver()->getDateEndFreeBillingPeriodSponsorship($producer)->format('d/m/Y'); ?>.
<?php endif; ?>
</div>
<?php endif; ?>

<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
<span class="glyphicon glyphicon-link"></span>
Votre lien de partage
</h3>
</div>
<div class="panel-body">
<strong><?= $sponsorshipLink ?></strong>
<a class="btn btn-xs btn-default" id="sponsorship-link-copy" href="<?= $sponsorshipLink ?>" title="Copier le lien dans le presse-papier">
<span class="glyphicon glyphicon-copy"></span>
</a>
</div>
</div>

<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
<i class="fa fa-info"></i>
Comment ça marche ?
</h3>
</div>
<div class="panel-body">
<p>
Partagez votre lien de parrainage avec les producteurs que vous souhaitez parrainer. Quand un de ces
producteurs s'inscrit en suivant ce lien, il devient votre filleul et obtenez tous les deux une récompense :
</p>
<ul>
<li>
Votre filleul démarre sur le logiciel avec <strong>3 mois offerts</strong>.
</li>
<li>
Vous obtenez <strong>30€ de réduction</strong> sur vos prochaines factures dès lors que votre filleul
utilise le logiciel en production.
</li>
</ul>
</div>
</div>

<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
<i class="fa fa-users"></i>
Vos filleuls
</h3>
</div>
<div class="panel-body">
<?php if($producersGodsonsArray && count($producersGodsonsArray)): ?>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Producteur</th>
<th>Date</th>
</tr>
</thead>
<tbody>
<?php foreach($producersGodsonsArray as $producerGodson): ?>
<tr>
<td><?= Html::encode($producerGodson->getName()); ?></td>
<td><?= date('d/m/Y', strtotime($producerGodson->date_creation)); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<p>Vous n'avez encore parrainé aucun producteur.</p>
<?php endif; ?>
</div>
</div>

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

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

use common\helpers\GlobalParam;
use domain\_\StatusInterface;
use domain\PointSale\PointSale\PointSale;
use domain\Product\Product\Product;
use domain\Product\Product\ProductModule;
@@ -110,7 +111,7 @@ $subscriptionsArray = Subscription::searchAll() ;
'attribute' => 'id_point_sale',
'label' => 'Point de vente',
'format' => 'raw',
'filter' => ArrayHelper::map(PointSale::find()->where(['id_producer' => GlobalParam::getCurrentProducerId()])->asArray()->all(), 'id', 'name'),
'filter' => ArrayHelper::map(PointSale::find()->where(['id_producer' => GlobalParam::getCurrentProducerId(), 'status' => StatusInterface::STATUS_ONLINE])->orderBy('is_bread_box ASC, name ASC')->asArray()->all(), 'id', 'name'),
'value' => function($model) {
if($model->pointSale) {
return Html::encode($model->pointSale->name) ;

+ 124
- 0
backend/views/user-admin/email_deliverability.php View File

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

/**
* Copyright Souke (2018)
*
* contact@souke.fr
*
* 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\helpers\GlobalParam;
use common\helpers\Price;
use domain\Order\Order\Order;
use domain\Producer\Producer\ProducerModule;
use domain\User\User\User;
use domain\User\User\UserModule;
use domain\User\UserProducer\UserProducer;
use domain\User\UserProducer\UserProducerModule;
use yii\grid\GridView;
use yii\helpers\Html;
use domain\User\UserGroup\UserGroupModule;

$userModule = UserModule::getInstance();
$producerModule = ProducerModule::getInstance();
$userCurrent = GlobalParam::getCurrentUser();
$userGroupModule = UserGroupModule::getInstance();
$userProducerModule = UserProducerModule::getInstance();

$this->setTitle('Déliverabilité des emails');
$this->addBreadcrumb($this->getTitle());

?>

<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'summary' =>
'Affichage de <b>{begin, number}-{end, number}</b> sur <b>{totalCount, number}</b> {totalCount, plural, one{élément} other{éléments}}.',
'columns' => [
[
'attribute' => 'username',
'label' => 'Nom',
'value' => function ($user) use ($userModule) {
return $userModule->getUsername($user);
}
],
[
'attribute' => 'type',
'label' => 'Type',
'headerOptions' => ['class' => 'column-hide-on-mobile'],
'filterOptions' => ['class' => 'column-hide-on-mobile'],
'contentOptions' => ['class' => 'column-hide-on-mobile'],
'filter' => $userModule->getTypeChoicesArray(),
'value' => function ($user) use ($userModule) {
return $userModule->getTypeLabel($user['type']);
}
],
[
'attribute' => 'contacts',
'header' => 'Contacts',
'format' => 'raw',
'headerOptions' => ['class' => 'column-hide-on-mobile'],
'filterOptions' => ['class' => 'column-hide-on-mobile'],
'contentOptions' => ['class' => 'column-hide-on-mobile'],
'value' => function ($user) {
$html = '';
if (strlen($user['phone'])) {
$html .= $user['phone'];
}
if (strlen($user['phone']) && strlen($user['email'])) {
$html .= '<br />';
}
if (strlen($user['email'])) {
$html .= $user['email'];
}
return $html;
}
],
[
'attribute' => 'producers',
'label' => 'Producteurs',
'format' => 'raw',
'contentOptions' => ['class' => 'column-hide-on-mobile align-center'],
'value' => function ($user) use ($userProducerModule) {
$html = '';
$userProducersArray = $userProducerModule->getRepository()->findUserProducersByUser($user);

foreach($userProducersArray as $userProducer) {
$html .= '<a href="'.Yii::$app->urlManager->createUrl(['user-admin/redirect-view', 'idUserProducer' => $userProducer->id]).'" class="btn btn-default"><span class="glyphicon glyphicon-eye-open"></span> '.$userProducer->producer->name.'</a> ';
}

return $html;
}
]
],
]); ?>

+ 11
- 1
backend/views/user-admin/index.php View File

@@ -122,7 +122,7 @@ $this->addBreadcrumb($this->getTitle());
],
[
'class' => 'yii\grid\ActionColumn',
'template' => '{switch} ',
'template' => '{problemReceivingEmails} {switch}',
'headerOptions' => ['class' => 'column-actions'],
'contentOptions' => ['class' => 'column-actions'],
'buttons' => [
@@ -142,6 +142,16 @@ $this->addBreadcrumb($this->getTitle());
'class' => 'btn btn-default'
]);
},
'problemReceivingEmails' => function($url, $model) use ($userModule, $userCurrent) {
if(!$model->getProblemReceivingEmails()) {
return Html::a('<i class="fa fa-life-ring"></i>',
Yii::$app->urlManager->createUrl(['user-admin/report-problem-receiving-emails', 'id' => $model->id]),
[
'title' => 'Signaler comme ayant des problèmes de réception des emails',
'class' => 'btn btn-default',
]);
}
},
'switch' => function($url, $model) use ($userModule, $userCurrent) {
if($userModule->getAuthorizationChecker()->isGrantedAsAdministrator($userCurrent)) {
return Html::a('<i class="fa fa-fw fa-user-secret"></i>',

+ 18
- 2
backend/views/user/_form.php View File

@@ -56,7 +56,7 @@ $distributionModule = DistributionModule::getInstance();
<div class="user-form" id="app-user-form">

<?php $form = ActiveForm::begin([
'enableClientValidation' => false
'enableClientValidation' => false,
]); ?>

<div class="col-md-8 col-no-padding-left">
@@ -97,7 +97,7 @@ $distributionModule = DistributionModule::getInstance();
</h3>
</div>
<div class="panel-body">
<?= $form->field($model, 'password_new')->passwordInput() ?>
<?= $form->field($model, 'password_new')->passwordInput(['autocomplete' => 'new-password']) ?>
</div>
</div>
<?php endif; ?>
@@ -184,6 +184,20 @@ $distributionModule = DistributionModule::getInstance();
]
); ?>
<?php endif; ?>
<?= $form->field($model, 'problem_receiving_emails')->widget(Toggle::class,
[
'options' => [
'data-id' => $model->id,
'data-on' => 'Oui',
'data-off' => 'Non',
'data-onstyle' => 'danger',
'data-offstyle' => 'default',
],
]
); ?>
<?php if($userModule->getAuthorizationChecker()->isGrantedAsAdministrator($userCurrent)): ?>
<?= $form->field($model, 'note_emails')->textarea(['rows' => 7]); ?>
<?php endif; ?>
</div>
</div>

@@ -201,6 +215,8 @@ $distributionModule = DistributionModule::getInstance();
'data-id' => $model->id,
'data-on' => 'Oui',
'data-off' => 'Non',
'data-onstyle' => 'danger',
'data-offstyle' => 'default',
],
]
)->hint('Si activé, le nom du client sera mis en avant dans la liste des commandes'); ?>

+ 6
- 0
backend/views/user/_menu_navigation.php View File

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

use common\helpers\GlobalParam;
use domain\Order\Order\OrderModule;
use domain\User\User\User;
use domain\User\User\UserModule;
@@ -8,8 +9,10 @@ use common\helpers\Price;
$userModule = UserModule::getInstance();
$orderModule = OrderModule::getInstance();

$userCurrent = GlobalParam::getCurrentUser();
$credit = $userModule->getRepository()->getCredit($user);
$countOrders = $orderModule->getRepository()->countOrdersByUser($user);
$countUserMessages = $this->getUserMessageModule()->getRepository()->countUserMessagesByUser($user);

?>

@@ -18,6 +21,9 @@ $countOrders = $orderModule->getRepository()->countOrdersByUser($user);
<?= menu_navigation_item('update', 'Profil', $action, $user) ?>
<?= menu_navigation_item('credit', 'Cagnotte <span class="label label-default badge">'.Price::format($credit).'</span>', $action, $user) ?>
<?= menu_navigation_item('orders', 'Commandes <span class="label label-default badge">'.$countOrders.'</span>', $action, $user) ?>
<?php if($userModule->getAuthorizationChecker()->isGrantedAsAdministrator($userCurrent)): ?>
<?= menu_navigation_item('messages', 'Messages <span class="label label-default badge">'.$countUserMessages.'</span>', $action, $user) ?>
<?php endif; ?>
</ul>



+ 15
- 1
backend/views/user/index.php View File

@@ -115,6 +115,9 @@ $this->render('_menu_filter', [
}
if (strlen($user['email'])) {
$html .= $user['email'];
if($user['problem_receiving_emails']) {
$html .= ' <span class="fa fa-life-ring"></span>';
}
}
return $html;
}
@@ -199,10 +202,11 @@ $this->render('_menu_filter', [
],
[
'class' => 'yii\grid\ActionColumn',
'template' => '{view} {update}
'template' => '{view} {update}
<div class="wrapper-button-dropdown">
<button type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-default dropdown-toggle"><span class="caret"></span></button>
<ul class="dropdown-menu">
<li>{problemReceivingEmails}</li>
<li>{delete}</li>
<li>{switch}</li>
</ul></div>',
@@ -225,6 +229,16 @@ $this->render('_menu_filter', [
'class' => 'btn btn-default'
]);
},
'problemReceivingEmails' => function($url, $model) use ($userModule, $userCurrent) {
if(!$model->getProblemReceivingEmails()) {
return Html::a('<i class="fa fa-life-ring"></i> Réception emails',
Yii::$app->urlManager->createUrl(['user/report-problem-receiving-emails', 'id' => $model->id]),
[
'title' => 'Signaler comme ayant des problèmes de réception des emails',
'class' => '',
]);
}
},
'delete' => function ($url, $model) {
return Html::a('<span class="glyphicon glyphicon-minus"></span> Enlever',
Yii::$app->urlManager->createUrl(array_merge(['user/delete', 'id' => $model->id], Yii::$app->getRequest()->getQueryParams())),

+ 124
- 0
backend/views/user/messages.php View File

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

/**
* Copyright Souke (2018)
*
* contact@souke.fr
*
* 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 domain\User\User\UserModule;
use yii\grid\GridView;
use yii\helpers\Html;
use yii\widgets\ActiveForm;

$userModule = $this->getUserModule();
$orderModule = $this->getOrderModule();
$userModule = $this->getUserModule();
$username = Html::encode($userModule->getSolver()->getUsername($user));
$this->setTitle($username.' (#'.$user->id.')') ;
$this->addBreadcrumb(['label' => 'Utilisateurs', 'url' => ['index']]) ;
$this->addBreadcrumb(['label' => $username]) ;
$this->addBreadcrumb('Messages') ;

?>

<?= $this->render('_menu_navigation', [
'action' => 'messages',
'user' => $user
]); ?>

<div class="user-messages tab-content">

<div class="alert alert-dark">
<i class="bi bi-info-circle"></i>
Les messages envoyés ici sont affichés à l'utilisateur directement sur la boutique.
</div>

<?php $form = ActiveForm::begin([
'enableClientValidation' => false
]); ?>
<?= $form->field($userMessageModel, 'message')->textarea(['rows' => 4]) ?>
<?= Html::submitButton($userMessageModel->isNewRecord ? 'Envoyer' : 'Modifier', ['class' => 'btn btn-primary', 'name' => 'save']) ?>
<?php ActiveForm::end(); ?>
<br />

<?= GridView::widget([
'dataProvider' => $userMessagesDataProvider,
'columns' => [
[
'label' => 'Message',
'format' => 'raw',
'value' => function ($userMessage) {
return nl2br(Html::encode($userMessage->getMessage())).'<br /><br /><em>'.$userMessage->getCreatedAt()->format('\L\e d/m/Y à H:i').'</em>';
}
],
[
'label' => 'Lu',
'format' => 'raw',
'value' => function ($userMessage) {
if($userMessage->getReadAt()) {
//return '<span class="label label-success" title="'.$userMessage->getReadAt()->format('\L\e d/m/Y à H:i').'">Oui</span>';
return '<span class="label label-success">Oui</span><br />'.$userMessage->getReadAt()->format('\L\e d/m/Y à H:i');
}
else {
return '<span class="label label-default">Non</span>';
}
}
],
[
'class' => 'yii\grid\ActionColumn',
'template' => '{update} {delete}',
'headerOptions' => ['class' => 'column-actions'],
'contentOptions' => ['class' => 'column-actions'],
'buttons' => [
'update' => function ($url, $userMessage) {
return Html::a('<span class="glyphicon glyphicon-pencil"></span>',
Yii::$app->urlManager->createUrl(['user/messages', 'id' => $userMessage->getUser()->id, 'idUserMessage' => $userMessage->id]),
[
'title' => 'Modifier',
'class' => 'btn btn-default'
]);
},
'delete' => function ($url, $userMessage) {
return Html::a('<span class="glyphicon glyphicon-trash"></span>',
Yii::$app->urlManager->createUrl(['user/message-delete', 'idUser' => $userMessage->getUser()->getId(), 'idUserMessage' => $userMessage->getId()]),
[
'title' => 'Supprimer',
'class' => 'btn btn-default'
]);
},
],
],
],
]); ?>
</div>

+ 9
- 0
backend/views/user/view.php View File

@@ -139,6 +139,15 @@ $this->addBreadcrumb('Récapitulatif') ;
</span>
</li>
<?php endif; ?>
<li class="list-group-item">
<strong>Problème réception emails</strong>
<span class="pull-right">
<?php $hasProblemReceivingEmails = $model->problem_receiving_emails; ?>
<span class="label label-<?= $hasProblemReceivingEmails ? 'danger' : 'default' ?>">
<?= $hasProblemReceivingEmails ? 'Oui' : 'Non' ?>
</span>
</span>
</li>
<?php if($model->userPointSale): ?>
<li class="list-group-item">
<strong>Points de vente</strong>

+ 9
- 0
backend/web/js/backend.js View File

@@ -59,8 +59,17 @@ $(document).ready(function () {
opendistrib_check_all_checkboxes();
opendistrib_dashboard_admin_statistics();
opendistrib_tinymce_responsive();
opendistrib_sponsorship();
});

function opendistrib_sponsorship() {
$('#sponsorship-link-copy').click(function() {
navigator.clipboard.writeText($(this).attr('href'));
appAlerts.alert('success', 'Lien de parrainage copié');
return false;
});
}

function saveData (data, fileName) {
var a = document.createElement("a");
document.body.appendChild(a);

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

@@ -252,7 +252,7 @@ if($(selector).length) {
key: 'leave_period',
dates: leave_period_dates,
highlight: {
color: 'blue',
color: 'gray',
fillMode: 'solid'
}
});

+ 1
- 0
common/components/BusinessLogic.php View File

@@ -26,6 +26,7 @@ class BusinessLogic
$this->getUserProducerModule(),
$this->getUserPointSaleModule(),
$this->getUserModule(),
$this->getUserMessageModule(),
$this->getPointSaleDistributionModule(),
$this->getProductDistributionModule(),
$this->getProductCategoryModule(),

+ 6
- 0
common/components/BusinessLogicTrait.php View File

@@ -36,6 +36,7 @@ use domain\Ticket\TicketMessage\TicketMessageModule;
use domain\Ticket\TicketUser\TicketUserModule;
use domain\User\User\UserModule;
use domain\User\UserGroup\UserGroupModule;
use domain\User\UserMessage\UserMessageModule;
use domain\User\UserProducer\UserProducerModule;
use domain\User\UserUserGroup\UserUserGroupModule;

@@ -181,6 +182,11 @@ trait BusinessLogicTrait
return UserModule::getInstance();
}

public function getUserMessageModule(): UserMessageModule
{
return UserMessageModule::getInstance();
}

public function getUserGroupModule(): UserGroupModule
{
return UserGroupModule::getInstance();

+ 1
- 1
common/config/params.php View File

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

return [
'version' => '24.6.B',
'version' => '24.6.C',
'maintenanceMode' => false,
'siteName' => 'Souke',
'tinyMcePlugins' => 'preview searchreplace autolink autosave save directionality code visualblocks visualchars fullscreen image link lists wordcount help',

+ 30
- 0
common/versions/24.6.C.php View File

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

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

version(
'17/06/2024',
[
[
"Points de vente partagés : possibilité de distributions en commun avec d'autres producteurs (Points de vente > Partages)",
"[Boutique & administration] Gestion des utilisateurs qui ne reçoivent pas les emails :
possibilité d'informer facilement le développeur via la boutique (onglet 'Infolettre') pour les utilisateurs
et la section 'Utilisateurs' de l'administration pour les producteurs",
],
[
"[Administration] Factures & bons de livraisons > Liste : amélioration des filtres de recherche",
"[Administration] Abonnements > liste : ajustement de l'ordre des points de vente dans le champs de recherche",
"[Boutique & administration] Congés : couleur en gris dans les calendrier",
"[Boutique] Abonnements > formulaire création/modification : amélioration responsive tableau produit",
]
],
[
[
],
[
]
],
$userCurrent
);

?>

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

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

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

/**
* Class m240611_091617_add_column_user_problem_receiving_emails
*/
class m240611_091617_add_column_user_problem_receiving_emails extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->addColumn('user', 'problem_receiving_emails', Schema::TYPE_BOOLEAN);
}

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

+ 37
- 0
console/migrations/m240611_125313_create_table_user_message.php View File

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

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

/**
* Class m240611_125313_create_table_user_message
*/
class m240611_125313_create_table_user_message extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->createTable('user_message', [
'id' => 'pk',
'id_user' => Schema::TYPE_INTEGER,
'message' => Schema::TYPE_TEXT,
'created_at' => Schema::TYPE_DATETIME,
'created_by' => Schema::TYPE_INTEGER,
'read_at' => Schema::TYPE_DATETIME,
]);
$this->addForeignKey('fk_user_message_id_user', 'user_message', 'id_user', 'user', 'id');
$this->addForeignKey('fk_user_message_created_by', 'user_message', 'created_by', 'user', 'id');
}

/**
* {@inheritdoc}
*/
public function safeDown()
{
$this->dropTable('user_message');
$this->dropForeignKey('fk_user_message_id_user', 'user_message');
$this->dropForeignKey('fk_user_message_created_by', 'user_message');
}
}

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

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

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

/**
* Class m240612_062929_add_column_user_note_email
*/
class m240612_062929_add_column_user_note_email extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->addColumn('user', 'note_emails', Schema::TYPE_TEXT);
}

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

+ 41
- 0
console/migrations/m240613_070747_add_columns_producer_sponsorship.php View File

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

use common\helpers\Password;
use domain\Producer\Producer\ProducerBuilder;
use domain\Producer\Producer\ProducerRepository;
use yii\db\Migration;
use yii\db\Schema;

/**
* Class m240613_070747_add_columns_producer_sponsorship
*/
class m240613_070747_add_columns_producer_sponsorship extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->addColumn('producer', 'sponsorship_code', Schema::TYPE_STRING);
$this->addColumn('producer', 'sponsored_by', Schema::TYPE_INTEGER);
$this->addColumn('producer', 'sponsorship_sponsor_reward', Schema::TYPE_BOOLEAN);
$this->addColumn('producer', 'sponsorship_godson_reward', Schema::TYPE_BOOLEAN);

$producersArray = ProducerRepository::getInstance()->findAll();
foreach($producersArray as $producer) {
ProducerBuilder::getInstance()->initSponsorshipCode($producer);
$producer->save();
}
}

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

+ 29
- 9
domain/Config/TaxRate/TaxRate.php View File

@@ -6,17 +6,11 @@ use common\components\ActiveRecordCommon;

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

/**
* @inheritdoc
*/
public function rules()
{
return [
@@ -25,9 +19,6 @@ class TaxRate extends ActiveRecordCommon
];
}

/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
@@ -36,4 +27,33 @@ class TaxRate extends ActiveRecordCommon
'value' => 'Valeur (0.2 pour 20%)',
];
}

/* Getters / Setters */

public function getId(): ?int
{
return $this->id;
}

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

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

public function getValue(): ?float
{
return $this->value;
}

public function setValue(?float $value): self
{
$this->value = $value;
return $this;
}
}

+ 0
- 9
domain/Config/TaxRate/TaxRateBuilder.php View File

@@ -12,13 +12,4 @@ class TaxRateBuilder extends AbstractBuilder

return $taxRate;
}

public function createTaxRate(): TaxRate
{
$taxRate = $this->instanciateTaxRate();

$this->saveCreate($taxRate);

return $taxRate;
}
}

+ 24
- 0
domain/Config/TaxRate/TaxRateManager.php View File

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

namespace domain\Config\TaxRate;

use domain\_\AbstractManager;

class TaxRateManager extends AbstractManager
{
protected TaxRateBuilder $taxRateBuilder;

public function loadDependencies(): void
{
$this->taxRateBuilder = $this->loadService(TaxRateBuilder::class);
}

public function createTaxRate(string $name, float $value): TaxRate
{
$taxRate = $this->taxRateBuilder->instanciateTaxRate();
$taxRate->setName($name);
$taxRate->setValue($value);
$taxRate->save();
return $taxRate;
}
}

+ 26
- 1
domain/Document/DeliveryNote/DeliveryNoteSearch.php View File

@@ -43,6 +43,7 @@ use yii\data\ActiveDataProvider;

class DeliveryNoteSearch extends DeliveryNote
{
public $with_invoice;
public $id_point_sale;
public $date_distribution;

@@ -50,6 +51,7 @@ class DeliveryNoteSearch extends DeliveryNote
{
return [
[[], 'safe'],
[['is_sent', 'with_invoice'], 'boolean'],
[['comment', 'address', 'status', 'date_distribution'], 'string'],
[['id_user', 'id_point_sale'], 'integer'],
[['name', 'reference'], 'string', 'max' => 255],
@@ -58,7 +60,8 @@ class DeliveryNoteSearch extends DeliveryNote

public function search($params)
{
$deliveryNoteRepository = DeliveryNoteRepository::getInstance();
$deliveryNoteModule = DeliveryNoteModule::getInstance();
$deliveryNoteRepository = $deliveryNoteModule->getRepository();
$optionsSearch = $deliveryNoteRepository->getDefaultOptionsSearch();

if(isset($params['DeliveryNoteSearch']['id_point_sale'])) {
@@ -93,10 +96,32 @@ class DeliveryNoteSearch extends DeliveryNote
$query->andWhere(['order.id_point_sale' => $this->id_point_sale]);
}

if(!is_null($this->is_sent) && is_numeric($this->is_sent)) {
if($this->is_sent) {
$query->andWhere(['delivery_note.is_sent' => 1]);
}
else {
$query->andWhere('delivery_note.is_sent IS NULL OR delivery_note.is_sent = 0');
}
}

if ($this->date_distribution && strlen($this->date_distribution)) {
$query->andFilterWhere(['like', 'distribution.date', date('Y-m-d', strtotime(str_replace('/', '-', $this->date_distribution)))]);
}

// filtre facture (oui / non)
$models = $dataProvider->getModels();
foreach($models as $index => $deliveryNote) {
if(!is_null($this->with_invoice) && is_numeric($this->with_invoice)) {
$invoice = $deliveryNoteModule->getSolver()->getInvoice($deliveryNote);
if(($this->with_invoice && !$invoice) || (!$this->with_invoice && $invoice)) {
unset($models[$index]);
}
}
}
$dataProvider->setModels($models);


return $dataProvider;
}
}

+ 55
- 7
domain/Document/Invoice/InvoiceSearch.php View File

@@ -40,35 +40,57 @@ namespace domain\Document\Invoice;

use common\helpers\GlobalParam;
use yii\data\ActiveDataProvider;
use yii\data\Sort;

class InvoiceSearch extends Invoice
{
var $paid = null;
var $username;

public function rules()
{
return [
[[], 'safe'],
[['comment', 'address', 'status', 'username'], 'string'],
[['name', 'reference'], 'string', 'max' => 255],
[['paid'], 'safe'],
[['is_sent'], 'boolean'],
[['comment', 'address', 'status', 'username', 'date'], 'string'],
[['name', 'reference', 'username'], 'string', 'max' => 255],
];
}

public function search($params)
{
$invoiceRepository = InvoiceRepository::getInstance();
$invoiceModule = InvoiceModule::getInstance();
$invoiceRepository = $invoiceModule->getRepository();
$optionsSearch = $invoiceRepository->getDefaultOptionsSearch();

$sort = new Sort([
'attributes' => [
'status',
'reference',
'name',
'date',
'username' => [
'asc' => ['user_invoice.lastname' => SORT_ASC, 'user_invoice.name' => SORT_ASC],
'desc' => ['user_invoice.lastname' => SORT_DESC, 'user_invoice.name' => SORT_DESC],
]
],
'defaultOrder' => [
'status' => SORT_ASC,
'reference' => SORT_DESC
]
]);

$query = Invoice::find()
->with($optionsSearch['with'])
->joinWith($optionsSearch['join_with'])
->where(['invoice.id_producer' => GlobalParam::getCurrentProducerId()])
->orderBy('invoice.status ASC, invoice.reference DESC')
//->orderBy('invoice.status ASC, invoice.reference DESC')
->orderBy($sort->orders)
->groupBy('invoice.id');

$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => ['attributes' => ['name', 'reference', 'date']],
'sort' => $sort,
'pagination' => [
'pageSize' => 20,
],
@@ -82,7 +104,6 @@ class InvoiceSearch extends Invoice
$query->andFilterWhere(['like', 'invoice.name', $this->name]);
$query->andFilterWhere(['like', 'invoice.reference', $this->reference]);
$query->andFilterWhere(['like', 'invoice.status', $this->status]);

$query->andFilterWhere([
'or',
['like', 'user_invoice.lastname', $this->username],
@@ -90,6 +111,33 @@ class InvoiceSearch extends Invoice
['like', 'user_invoice.name_legal_person', $this->username],
]);

if ($this->date && strlen($this->date)) {
$query->andFilterWhere(['like', 'invoice.date', date('Y-m-d', strtotime(str_replace('/', '-', $this->date)))]);
}

// filtre envoyé
if(!is_null($this->is_sent) && is_numeric($this->is_sent)) {
if($this->is_sent) {
$query->andWhere(['invoice.is_sent' => 1]);
}
else {
$query->andWhere('invoice.is_sent IS NULL OR invoice.is_sent = 0');
}
}

// filter payé / non payé
// @TODO : comprendre pourquoi la pagination ne suit pas
$models = $dataProvider->getModels();
foreach($models as $index => $invoice) {
if(!is_null($this->paid) && is_numeric($this->paid)) {
$isInvoicePaid = $invoiceModule->getSolver()->isInvoicePaid($invoice);
if(($this->paid && !$isInvoicePaid) || (!$this->paid && $isInvoicePaid)) {
unset($models[$index]);
}
}
}
$dataProvider->setModels($models);

return $dataProvider;
}
}

+ 2
- 0
domain/Feature/Feature/Feature.php View File

@@ -51,6 +51,8 @@ class Feature extends ActiveRecordCommon
const ALIAS_SETTINGS = 'settings';
const ALIAS_SHOP_SUPPORT = 'shop_support';
const ALIAS_SHARED_POINT_SALE = 'shared_point_sale';
const ALIAS_SUMUP_SYNCHRONIZATION = 'sumup_synchronization';
const ALIAS_SPONSORSHIP = 'sponsorship';

/**
* @inheritdoc

+ 2
- 0
domain/Feature/Feature/FeatureDefinition.php View File

@@ -21,6 +21,8 @@ class FeatureDefinition extends AbstractDefinition
Feature::ALIAS_SETTINGS => 'Système de paramètres',
Feature::ALIAS_SHOP_SUPPORT => 'Support boutique',
Feature::ALIAS_SHARED_POINT_SALE => 'Points de vente partagés',
Feature::ALIAS_SUMUP_SYNCHRONIZATION => "Synchronisation de commandes avec Sumup / Tiller",
Feature::ALIAS_SPONSORSHIP => "Parrainage producteurs"
];
}
}

+ 1
- 1
domain/Feature/Feature/FeatureRepository.php View File

@@ -39,7 +39,7 @@ class FeatureRepository extends AbstractRepository

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

+ 56
- 2
domain/Producer/Producer/Producer.php View File

@@ -173,7 +173,8 @@ class Producer extends ActiveRecordCommon
'option_weeks_distributions_activated_in_advance',
'option_document_width_logo',
'export_shopping_cart_labels_number_per_column',
'id_user_group_default'
'id_user_group_default',
'sponsored_by'
],
'integer'
],
@@ -261,6 +262,7 @@ class Producer extends ActiveRecordCommon
'option_leave_period_message_title',
'option_leave_period_message',
'option_credit_description',
'sponsorship_code'
],
'string'
],
@@ -304,7 +306,9 @@ class Producer extends ActiveRecordCommon
'delivery_note_automatic_validation',
'is_new',
'agree_contact_about_software_development',
'option_leave_period_message_display'
'option_leave_period_message_display',
'sponsorship_sponsor_reward',
'sponsorship_godson_reward',
],
'boolean'
],
@@ -542,6 +546,7 @@ class Producer extends ActiveRecordCommon
'option_leave_period_message_title' => 'Titre du message des congés',
'option_leave_period_message' => 'Message des congés',
'option_credit_description' => "Description Cagnotte",
'sponsorship_sponsor_reward' => 'Récompense parrain',
];
}

@@ -550,6 +555,50 @@ class Producer extends ActiveRecordCommon
return $this->name;
}

public function getSponsorshipCode(): string
{
return $this->sponsorship_code;
}

public function setSponsorshipCode(string $sponsorshipCode): self
{
$this->sponsorship_code = $sponsorshipCode;
return $this;
}

public function getSponsoredBy(): ?Producer
{
return $this->sponsoredByRelation;
}

public function setSponsoredBy(Producer $producer): self
{
$this->populateFieldObject('sponsored_by', 'sponsoredByRelation', $producer);
return $this;
}

public function getSponsorshipSponsorReward(): ?bool
{
return $this->sponsorship_sponsor_reward;
}

public function setSponsorshipSponsorReward(bool $sponsorshipSponsorReward = null): self
{
$this->sponsorship_sponsor_reward = $sponsorshipSponsorReward;
return $this;
}

public function getSponsorshipGodsonReward(): ?bool
{
return $this->sponsorship_godson_reward;
}

public function setSponsorshipGodsonReward(bool $sponsorshipGodsonReward = null): self
{
$this->sponsorship_godson_reward = $sponsorshipGodsonReward;
return $this;
}

/*
* Relations
*/
@@ -590,6 +639,11 @@ class Producer extends ActiveRecordCommon
$this->populateFieldObject('id_user_group_default', 'userGroupDefault', $userGroup);
}

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

// ---

public static function getBillingTypePopulateDropdown()

+ 8
- 0
domain/Producer/Producer/ProducerBuilder.php View File

@@ -30,8 +30,10 @@ class ProducerBuilder extends AbstractBuilder
public function instanciateProducer(): Producer
{
$producer = new Producer();

$producer->order_deadline = Producer::ORDER_DEADLINE_DEFAULT;
$producer->order_delay = Producer::ORDER_DELAY_DEFAULT;
$this->initSponsorshipCode($producer);

return $producer;
}
@@ -161,4 +163,10 @@ class ProducerBuilder extends AbstractBuilder
$producer->save();
}
}

public function initSponsorshipCode(Producer $producer): self
{
$producer->setSponsorshipCode(Password::generate());
return $this;
}
}

+ 14
- 0
domain/Producer/Producer/ProducerRepository.php View File

@@ -445,4 +445,18 @@ class ProducerRepository extends AbstractRepository

return 0;
}

public function findOneProducerBySponsorshipCode(string $sponsorshipCode): ?Producer
{
return $this->createQuery()
->filterBySponsorshipCode($sponsorshipCode)
->findOne();
}

public function findProducersSponsorshipGodsons(Producer $producer): array
{
return $this->createQuery()
->filterBySponsoredBy($producer)
->find();
}
}

+ 13
- 0
domain/Producer/Producer/ProducerRepositoryQuery.php View File

@@ -42,4 +42,17 @@ class ProducerRepositoryQuery extends AbstractRepositoryQuery
$this->andWhere('producer.option_time_saved > 0');
return $this;
}

public function filterBySponsorshipCode(string $sponsorshipCode): self
{
$this->andWhere('producer.sponsorship_code LIKE :sponsorship_code')
->addParams(['sponsorship_code' => $sponsorshipCode]);
return $this;
}

public function filterBySponsoredBy(Producer $producer): self
{
$this->andWhere(['producer.sponsored_by' => $producer->id]);
return $this;
}
}

+ 30
- 0
domain/Producer/Producer/ProducerSolver.php View File

@@ -307,4 +307,34 @@ class ProducerSolver extends AbstractService implements SolverInterface

return false;
}

public function getSponsorshipLink(Producer $producer): string
{
return \Yii::$app->urlManagerFrontend->createAbsoluteUrl(['site/signup', 'from' => $producer->getSponsorshipCode()]);
}

public function getDateEndFreeBillingPeriodSponsorship(Producer $producer): ?\DateTime
{
if($producer->getSponsoredBy()) {
$dateStart = new \DateTime($producer->date_creation);
return $dateStart
->modify('+4 months')
->modify('first day of this month');
}

return null;
}

public function isFreeBillingPeriodSponsorship(Producer $producer): bool
{
if($producer->getSponsoredBy()) {
$dateNow = new \DateTime();
$dateEndFreeBillingPeriodSponsorship = $this->getDateEndFreeBillingPeriodSponsorship($producer);
if($dateNow < $dateEndFreeBillingPeriodSponsorship) {
return true;
}
}

return false;
}
}

+ 18
- 3
domain/User/User/User.php View File

@@ -108,8 +108,8 @@ class User extends ActiveRecordCommon implements IdentityInterface
[['no_mail', 'mail_distribution_monday', 'mail_distribution_tuesday', 'mail_distribution_wednesday',
'mail_distribution_thursday', 'mail_distribution_friday', 'mail_distribution_saturday',
'mail_distribution_sunday', 'is_main_contact', 'newsletter', 'exclude_export_shopping_cart_labels',
'send_mail_welcome', 'trust_alert', 'newsletter_souke'], 'boolean'],
[['lastname', 'name', 'phone', 'address', 'type', 'name_legal_person', 'evoliz_code', 'trust_alert_comment'], 'string'],
'send_mail_welcome', 'trust_alert', 'newsletter_souke', 'problem_receiving_emails'], 'boolean'],
[['lastname', 'name', 'phone', 'address', 'type', 'name_legal_person', 'evoliz_code', 'trust_alert_comment', 'note_emails'], 'string'],
['lastname', 'verifyOneName', 'skipOnError' => false, 'skipOnEmpty' => false],
[['email', 'email_sending_invoicing_documents'], 'email', 'message' => 'Cette adresse email n\'est pas valide'],
['email', 'verifyEmail'],
@@ -157,7 +157,9 @@ class User extends ActiveRecordCommon implements IdentityInterface
'email_sending_invoicing_documents' => 'Email facturation',
'trust_alert' => 'Alerte confiance',
'trust_alert_comment' => 'Commentaire',
'newsletter_souke' => "S'abonner à l'infolettre de Souke"
'newsletter_souke' => "S'abonner à l'infolettre de Souke",
'problem_receiving_emails' => "Rencontre des problèmes pour recevoir les emails",
'note_emails' => "Note emails"
];
}

@@ -247,6 +249,19 @@ class User extends ActiveRecordCommon implements IdentityInterface
}
}

/* Getters / Setters */

public function getProblemReceivingEmails(): ?bool
{
return $this->problem_receiving_emails;
}

public function setProblemReceivingEmails(bool $problemReceivingEmails = null): self
{
$this->problem_receiving_emails = $problemReceivingEmails;
return $this;
}

/*
* Relations
*/

+ 6
- 1
domain/User/User/UserManager.php View File

@@ -19,7 +19,6 @@ class UserManager extends AbstractManager
{
$password = $this->userBuilder->generatePassword($user);
$this->userNotifier->sendMailWelcome($user, $password);

}

public function newPassword(User $user)
@@ -27,4 +26,10 @@ class UserManager extends AbstractManager
$password = $this->userBuilder->generatePassword($user);
$this->userNotifier->sendMailNewPassword($user, $password);
}

public function reportProblemReceivingEmails(User $user): bool
{
$user->setProblemReceivingEmails(true);
return $this->userBuilder->update($user);
}
}

+ 12
- 0
domain/User/User/UserRepository.php View File

@@ -312,4 +312,16 @@ class UserRepository extends AbstractRepository

return $userSystem;
}

public function findUsersWithProblemReceivingEmails()
{
return $this->createQuery()
->filterHasProblemReceivingEmails()
->find();
}

public function countUsersWithProblemReceivingEmails(): int
{
return count($this->findUsersWithProblemReceivingEmails());
}
}

+ 6
- 0
domain/User/User/UserRepositoryQuery.php View File

@@ -61,4 +61,10 @@ class UserRepositoryQuery extends AbstractRepositoryQuery
->addParams(['date' => $date->format('Y-m-d H:i:s')]);
return $this;
}

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

+ 10
- 1
domain/User/User/UserSearch.php View File

@@ -54,7 +54,9 @@ class UserSearch extends User
public function rules()
{
return [
[['no_mail', 'mail_distribution_monday', 'mail_distribution_tuesday', 'mail_distribution_wednesday', 'mail_distribution_thursday', 'mail_distribution_friday', 'mail_distribution_saturday', 'mail_distribution_sunday'], 'boolean'],
[['no_mail', 'mail_distribution_monday', 'mail_distribution_tuesday', 'mail_distribution_wednesday',
'mail_distribution_thursday', 'mail_distribution_friday', 'mail_distribution_saturday',
'mail_distribution_sunday', 'problem_receiving_emails'], 'boolean'],
[['lastname', 'name', 'phone', 'address', 'type', 'newsletter', 'contacts'], 'string'],
[['id_point_sale', 'inactive', 'subscribers', 'id_user_user_group'], 'integer'],
[['date_last_connection', 'id_point_sale', 'username'], 'safe'],
@@ -91,6 +93,7 @@ class UserSearch extends User
. '`user`.date_last_connection, '
. '`user`.name_legal_person, '
. '`user`.type, '
. '`user`.problem_receiving_emails, '
. '(SELECT COUNT(*) FROM `order` WHERE `user`.id = `order`.id_user) AS count_orders');

if($producer) {
@@ -181,6 +184,12 @@ class UserSearch extends User
]);
}

if(isset($this->problem_receiving_emails)) {
$query->andWhere([
'user.problem_receiving_emails' => (bool) $this->problem_receiving_emails
]);
}

return $dataProvider;
}
}

+ 113
- 0
domain/User/UserMessage/UserMessage.php View File

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

namespace domain\User\UserMessage;

use common\components\ActiveRecordCommon;
use domain\User\User\User;

class UserMessage extends ActiveRecordCommon
{
public static function tableName()
{
return 'user_message';
}

public function rules()
{
return [
[['id_user', 'message', 'created_at', 'created_by'], 'required'],
[['message'], 'string'],
[['id_user', 'created_by'], 'integer'],
[['created_at', 'read_at'], 'safe']
];
}

public function attributeLabels()
{
return [
'id_user' => 'Utilisateur',
'message' => 'Message',
'created_at' => 'Date de création',
'created_by' => 'Créé par',
'read_at', 'Date de lecture'
];
}

/* Getters / Setters */

public function getId(): ?int
{
return $this->id;
}

public function getUser(): User
{
return $this->userRelation;
}

public function setUser(User $user): self
{
$this->populateFieldObject('id_user', 'userRelation', $user);
return $this;
}

public function getMessage(): string
{
return $this->message;
}

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

public function getCreatedAt(): \DateTime
{
return new \DateTime($this->created_at);
}

public function setCreatedAt(\DateTime $createdAt): self
{
$this->created_at = $createdAt->format('Y-m-d H:i:s');
return $this;
}

public function getCreatedBy(): User
{
return $this->createdByRelation;
}

public function setCreatedBy(User $createdBy): self
{
$this->populateFieldObject('created_by', 'createdByRelation', $createdBy);
return $this;
}

public function getReadAt(): ?\DateTime
{
if($this->read_at) {
return new \DateTime($this->read_at);
}

return null;
}

public function setReadAt(\DateTime $readAt): self
{
$this->read_at = $readAt->format('Y-m-d H:i:s');
return $this;
}

/* Relations */

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

public function getCreatedByRelation()
{
return $this->hasOne(User::class, ['id' => 'created_by']);
}
}

+ 20
- 0
domain/User/UserMessage/UserMessageBuilder.php View File

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

namespace domain\User\UserMessage;

use domain\_\AbstractBuilder;
use domain\User\User\User;

class UserMessageBuilder extends AbstractBuilder
{
public function instanciateUserMessage(User $user, User $createdBy): UserMessage
{
$userMessage = new UserMessage();

$userMessage->setUser($user);
$userMessage->setCreatedAt(new \DateTime());
$userMessage->setCreatedBy($createdBy);

return $userMessage;
}
}

+ 13
- 0
domain/User/UserMessage/UserMessageDefinition.php View File

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

namespace domain\User\UserMessage;

use domain\_\AbstractDefinition;

class UserMessageDefinition extends AbstractDefinition
{
public function getEntityFqcn(): string
{
return UserMessage::class;
}
}

+ 35
- 0
domain/User/UserMessage/UserMessageManager.php View File

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

namespace domain\User\UserMessage;

use domain\_\AbstractManager;
use domain\User\User\User;

class UserMessageManager extends AbstractManager
{
protected UserMessageBuilder $userMessageBuilder;

public function loadDependencies(): void
{
$this->userMessageBuilder = $this->loadService(UserMessageBuilder::class);
}

public function createUserMessage(User $user, string $message, User $createdBy): UserMessage
{
$userMessage = $this->userMessageBuilder->instanciateUserMessage($user, $createdBy);
$userMessage->setMessage($message);
$userMessage->save();
return $userMessage;
}

public function deleteUserMessage(UserMessage $userMessage): bool
{
return $userMessage->delete();
}

public function readUserMessage(UserMessage $userMessage): bool
{
$userMessage->setReadAt(new \DateTime());
return $userMessage->save();
}
}

+ 38
- 0
domain/User/UserMessage/UserMessageModule.php View File

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

namespace domain\User\UserMessage;

use domain\_\AbstractModule;

class UserMessageModule extends AbstractModule
{
public function getServices(): array
{
return [
UserMessageDefinition::class,
UserMessageBuilder::class,
UserMessageRepository::class,
UserMessageManager::class
];
}

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

public function getBuilder(): UserMessageBuilder
{
return UserMessageBuilder::getInstance();
}

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

public function getManager(): UserMessageManager
{
return UserMessageManager::getInstance();
}
}

+ 61
- 0
domain/User/UserMessage/UserMessageRepository.php View File

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

namespace domain\User\UserMessage;

use domain\_\AbstractRepository;
use domain\User\User\User;

class UserMessageRepository extends AbstractRepository
{
protected UserMessageRepositoryQuery $query;

public function loadDependencies(): void
{
$this->loadQuery(UserMessageRepositoryQuery::class);
}

public function getDefaultOptionsSearch(): array
{
return [
self::WITH => [],
self::JOIN_WITH => [],
self::ORDER_BY => 'created_at DESC',
self::ATTRIBUTE_ID_PRODUCER => ''
];
}

public function findOneUserMessageById(int $id): ?UserMessage
{
return $this->createQuery()
->filterById($id)
->findOne();
}

public function queryUserMessagesByUser(User $user)
{
return $this->createDefaultQuery()
->filterByUser($user);
}

public function findUserMessagesByUser(User $user): array
{
return $this->queryUserMessagesByUser($user)->find();
}

public function countUserMessagesByUser(User $user): int
{
return count($this->findUserMessagesByUser($user));
}

public function findUserMessagesUnreadByUser(User $user)
{
return $this->queryUserMessagesByUser($user)
->filterIsUnread()
->find();
}

public function countUserMessagesUnreadByUser(User $user): int
{
return count($this->findUserMessagesUnreadByUser($user));
}
}

+ 28
- 0
domain/User/UserMessage/UserMessageRepositoryQuery.php View File

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

namespace domain\User\UserMessage;

use domain\_\AbstractRepositoryQuery;
use domain\User\User\User;

class UserMessageRepositoryQuery extends AbstractRepositoryQuery
{
protected UserMessageDefinition $definition;

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

public function filterByUser(User $user): self
{
$this->andWhere(['user_message.id_user' => $user->id]);
return $this;
}

public function filterIsUnread(): self
{
$this->andWhere('user_message.read_at IS NULL');
return $this;
}
}

+ 30
- 3
frontend/controllers/SiteController.php View File

@@ -80,7 +80,6 @@ class SiteController extends FrontendController
[
'actions' => ['signup-producer'],
'allow' => true,
'roles' => ['@'],
],
[
'actions' => ['logout'],
@@ -354,8 +353,12 @@ class SiteController extends FrontendController
*/
public function actionSignup(string $type = 'user')
{
$sponsorshipFromCode = Yii::$app->request->get('from');
$sponsorshipProducerFrom = $this->findProducerSponsorshipByCode($sponsorshipFromCode);

$model = new SignupForm();
$model->option_user_producer = $type;
$model->sponsored_by = $sponsorshipProducerFrom ? $sponsorshipProducerFrom->id : false;
$producerModule = $this->getProducerModule();

if ($model->load(Yii::$app->request->post())) {
@@ -382,11 +385,11 @@ class SiteController extends FrontendController
$dataProducers = $producersArray['data'];
$optionsProducers = $producersArray['options'];


$paidFeaturesArray = $this->getFeatureModule()->getRepository()->findPaidFeatures();

return $this->render('signup', [
'model' => $model,
'sponsorshipProducerFrom' => $sponsorshipProducerFrom,
'dataProducers' => $dataProducers,
'dataProviderPrices' => $this->getDataProviderPrices(),
'paidFeaturesArray' => $paidFeaturesArray,
@@ -396,12 +399,26 @@ class SiteController extends FrontendController

public function actionSignupProducer()
{
$userCurrent = $this->getUserCurrent();
$sponsorshipFromCode = Yii::$app->request->get('from');
$sponsorshipProducerFrom = $this->findProducerSponsorshipByCode($sponsorshipFromCode);
if(!$userCurrent) {
// Lien parrainage
if($sponsorshipFromCode && strlen($sponsorshipFromCode)) {
$this->redirect(['site/signup', 'from' => $sponsorshipFromCode]);
}
else {
$this->redirect(['site/signup']);
}
}

$model = new SignupForm();
$model->signup_producer_only = 'producer';
$model->option_user_producer = 'producer';
$model->sponsored_by = $sponsorshipProducerFrom ? $sponsorshipProducerFrom->id : false;

if ($model->load(Yii::$app->request->post())) {
$user = $model->signup($this->getUserCurrent());
$user = $model->signup($userCurrent);
if($user) {
$this->redirect(['site/signup-confirm']);
}
@@ -409,11 +426,21 @@ class SiteController extends FrontendController

return $this->render('signup_producer', [
'model' => $model,
'sponsorshipProducerFrom' => $sponsorshipProducerFrom,
'dataProviderPrices' => $this->getDataProviderPrices(),
'paidFeaturesArray' => $this->getFeatureModule()->getRepository()->findPaidFeatures()
]);
}

public function findProducerSponsorshipByCode($sponsorshipFromCode)
{
$sponsorshipProducerFrom = null;
if($sponsorshipFromCode && strlen($sponsorshipFromCode)) {
$sponsorshipProducerFrom = $this->getProducerModule()->getRepository()->findOneProducerBySponsorshipCode($sponsorshipFromCode);
}
return $sponsorshipProducerFrom;
}

public function actionSignupConfirm($idProducerRedirect = null)
{
$producerModule = $this->getProducerModule();

+ 11
- 0
frontend/forms/SignupForm.php View File

@@ -74,6 +74,7 @@ class SignupForm extends Model
public $is_test;
public $newsletter = false;
public $newsletter_souke = false;
public $sponsored_by = false;

/**
* @inheritdoc
@@ -176,6 +177,7 @@ class SignupForm extends Model
['id_producer', 'required', 'message' => 'Champs obligatoire', 'when' => function ($model) {
return $this->option_user_producer == 'user';
}],
['sponsored_by', 'safe'],
['code', 'required', 'message' => 'Champs obligatoire', 'when' => function ($model) {
$producer = Producer::findOne($this->id_producer);
if ($producer) {
@@ -297,6 +299,15 @@ class SignupForm extends Model
$producerModule->initProducer($producer);
$producer->save();

// Parrainage
if($this->sponsored_by) {
$sponsorshipProducerSponsor = $producerModule->getRepository()->findOneProducerById($this->sponsored_by);
if($sponsorshipProducerSponsor) {
$producer->sponsored_by = $this->sponsored_by;
$producer->save();
}
}

/*
* Envoi d'un email à l'administrateur pour le prévenir
* qu'un nouveau producteur s'est inscrit

+ 13
- 0
frontend/views/site/_signup_sponsorship_message.php View File

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

use yii\helpers\Html;

?>

<?php if($sponsorshipProducerFrom): ?>
<div class="alert alert-dark">
<i class="bi bi-people-fill"></i>
Le producteur <strong><?= Html::encode($sponsorshipProducerFrom->getName()) ?></strong> vous propose de vous parrainer.
En vous inscrivant, profitez de <strong>3 mois offerts</strong> d'utilisation du logiciel !
</div>
<?php endif; ?>

+ 5
- 0
frontend/views/site/signup.php View File

@@ -57,6 +57,11 @@ $this->params['breadcrumbs'][] = $this->title;
les identifiants indiqués sur la page de <?= Html::a('connexion', \Yii::$app->urlManager->createUrl(['site/login'])); ?> pour vous identifier.
</div>
<?php else: ?>

<?= $this->render('_signup_sponsorship_message.php', [
'sponsorshipProducerFrom' => $sponsorshipProducerFrom
]); ?>

<?php $form = ActiveForm::begin(['id' => 'form-signup', 'enableClientValidation'=> false]); ?>
<?= $form->field($model, 'email') ?>
<?= $form->field($model, 'password')->passwordInput() ?>

+ 10
- 5
frontend/views/site/signup_producer.php View File

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

<div class="row">
<div class="col-lg-5">

<?= $this->render('_signup_sponsorship_message.php', [
'sponsorshipProducerFrom' => $sponsorshipProducerFrom
]); ?>

<?php $form = ActiveForm::begin(['id' => 'form-signup','enableClientValidation'=> false]); ?>
<?= $form->field($model, 'name_producer') ?>
<?= $form->field($model, 'type')->textInput(['placeholder' => 'Boulangerie, brasserie, ferme ...']); ?>
@@ -62,7 +67,7 @@ $this->params['breadcrumbs'][] = $this->title;
'prompt' => '--',
])
->label('TVA à appliquer par défaut'); ?>
<?= $form->field($model, 'cgv')->checkbox()->label('J\'accepte les <button type="button" class="btn btn-xs btn-default btn-modal-cgv" data-toggle="modal" data-target="#modal-cgv">conditions générales de service</button> et les <button type="button" class="btn btn-xs btn-default btn-modal-prices" data-toggle="modal" data-target="#modal-prices">conditions tarifaires</button>.') ?>
<?= $form->field($model, 'cgv')->checkbox()->label('J\'accepte les <button type="button" class="btn btn-sm btn-secondary btn-modal-cgv" data-bs-toggle="modal" data-bs-target="#modal-cgv">conditions générales de service</button> et les <button type="button" class="btn btn-sm btn-secondary btn-modal-prices" data-bs-toggle="modal" data-bs-target="#modal-prices">conditions tarifaires</button>.') ?>

<?= $form->field($model, 'verifyCode')->widget(\yii\captcha\Captcha::className(), [
'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>',
@@ -82,14 +87,14 @@ $this->params['breadcrumbs'][] = $this->title;
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalLabel">Conditions générales de service</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<?= $this->render('_cgv_content.php'); ?>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Fermer</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
</div>
</div>
</div>
@@ -100,14 +105,14 @@ $this->params['breadcrumbs'][] = $this->title;
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalLabel">Tarifs</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<?= $this->render('../site/_prices_producer', ['dataProviderPrices' => $dataProviderPrices, 'paidFeaturesArray' => $paidFeaturesArray]); ?>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Fermer</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
</div>
</div>
</div>

+ 13
- 0
producer/controllers/NewsletterController.php View File

@@ -85,6 +85,19 @@ class NewsletterController extends ProducerBaseController
return $this->redirect('index');
}

public function actionReportProblemReceivingEmails()
{
if(!$this->getUserCurrent()) {
return $this->redirectProducerLoginFrontend('index');
}

if(!$this->getUserModule()->getManager()->reportProblemReceivingEmails($this->getUserCurrent())) {
$this->setFlash('error', "Une erreur est survenue.");
}

return $this->redirect('index');
}

public function redirectProducerLoginFrontend(string $actionNewsletter)
{
return $this->redirect($this->getUrlManagerFrontend()->createAbsoluteUrl(['site/producer', 'id' => $this->getProducerCurrent()->id, 'return_url' => \Yii::$app->urlManagerProducer->createAbsoluteUrl(['newsletter/'.$actionNewsletter, 'slug_producer' => $this->getProducerCurrent()->slug])]));

+ 1
- 1
producer/controllers/OrderController.php View File

@@ -719,7 +719,7 @@ class OrderController extends ProducerBaseController
$distributionsArrayFilterPointSale = [];
for ($i = 0; $i < count($distributionsArray); $i++) {
$distribution = $distributionsArray[$i];
if ($distributionModule->isPointSaleActive($distribution, $pointSaleCurrent)) {
if ($distribution && $distributionModule->isPointSaleActive($distribution, $pointSaleCurrent)) {
$countOrders = Order::searchCount([
'id_distribution' => $distribution->id,
'id_point_sale' => $pointSaleCurrent->id

+ 10
- 0
producer/controllers/SiteController.php View File

@@ -314,6 +314,16 @@ class SiteController extends ProducerBaseController
'producer' => $producer
]);
}

public function actionUserMessageRead(int $idUserMessage)
{
$userMessageModule = $this->getUserMessageModule();
$userMessage = $userMessageModule->getRepository()->findOneUserMessageById($idUserMessage);
if($userMessage) {
$userMessageModule->getManager()->readUserMessage($userMessage);
}
return $this->redirectReferer();
}
}

?>

+ 24
- 0
producer/views/layouts/main.php View File

@@ -60,6 +60,7 @@ $producerModule = ProducerModule::getInstance();
$featureModule = FeatureModule::getInstance();
$productModule = ProductModule::getInstance();
$pointSaleModule = PointSaleModule::getInstance();
$userMessageModule = $this->getUserMessageModule();
$producerUser = null;
if ($userModule->getAuthorizationChecker()->isGrantedAsProducer($userCurrent)) {
$producerUser = $producerModule->findOneProducerById($userCurrent->id_producer);
@@ -326,6 +327,29 @@ $mainColor = $producer->option_main_color ?: '#ee6f42' ;

<section id="content">

<?php
// Message utilisateur
$userMessagesArray = $userMessageModule->getRepository()->findUserMessagesUnreadByUser($userCurrent);
if($userMessagesArray && count($userMessagesArray)):
foreach($userMessagesArray as $userMessage): ?>
<div class="alert alert-info">
<h4 class="alert-heading">
<i class="bi bi-envelope"></i>
Nouveau message
</h4>
<p><?= nl2br(Html::encode($userMessage->getMessage())) ?></p>
<p>
<a class="alert-link" href="<?= Yii::$app->urlManager->createUrl(['site/'.($userModule->getAuthorizationChecker()->isGrantedAsAdministrator($userMessage->getCreatedBy()) ? 'support' : 'contact')]); ?>">
Répondre</a>
&bull;
<a class="alert-link" href="<?= Yii::$app->urlManager->createUrl(['site/user-message-read', 'idUserMessage' => $userMessage->getId()]); ?>">
Fermer
</a>
</p>
</div>
<?php endforeach;
endif; ?>

<?php
// Message congés
$leavePeriodMessageDisplay = $producerModule->getSolver()->getConfig('option_leave_period_message_display', $producer);

+ 33
- 18
producer/views/newsletter/index.php View File

@@ -66,24 +66,39 @@ else {
?>
<div class="newsletter-index">

<div class="alert alert-info">
<i class="bi bi-info-circle"></i>
<?php if($producer->option_newsletter_description): ?>
<?= nl2br(Html::encode($producer->option_newsletter_description)); ?>
<?php else: ?>
L'infolettre vous permet de recevoir les emails de prise de commande et les actualités de ce producteur.
<?php endif; ?>
</div>

<?php if($userModule->isUserSubscribedNewsletter($user)): ?>
<div class="alert alert-success">
<i class="bi bi-megaphone"></i>
Vous êtes inscrit à l'infolettre <strong><?= Html::encode($producer->name) ?></strong>.
<div class="row">
<div class="col-md-8">
<div class="alert alert-info">
<i class="bi bi-info-circle"></i>
<?php if($producer->option_newsletter_description): ?>
<?= nl2br(Html::encode($producer->option_newsletter_description)); ?>
<?php else: ?>
L'infolettre vous permet de recevoir les emails de prise de commande et les actualités de ce producteur.
<?php endif; ?>
</div>
<?php if($userModule->isUserSubscribedNewsletter($user)): ?>
<div class="alert alert-success">
<i class="bi bi-megaphone"></i>
Vous êtes inscrit à l'infolettre <strong><?= Html::encode($producer->name) ?></strong>.
</div>
<?php else: ?>
<div class="alert alert-danger">
<i class="bi bi-x-circle"></i>
Vous n'êtes pas inscrit à l'infolettre <strong><?= Html::encode($producer->name) ?></strong>.
</div>
<?php endif; ?>
</div>
<?php else: ?>
<div class="alert alert-danger">
<i class="bi bi-x-circle"></i>
Vous n'êtes pas inscrit à l'infolettre <strong><?= Html::encode($producer->name) ?></strong>.
<div class="col-md-4">
<div class="alert alert-dark">
<i class="bi bi-life-preserver"></i>
<?php if(!$user->getProblemReceivingEmails()): ?>
Vous rencontrez des problèmes pour recevoir les emails de ce producteur ?
<a href="<?= Yii::$app->urlManager->createUrl('newsletter/report-problem-receiving-emails') ?>">Cliquez ici</a> pour nous prévenir.
<?php else: ?>
Nous avons bien été prévenu de vos difficultés à recevoir les emails de ce producteur,
nous reviendrons vers vous dès que possible.
<?php endif; ?>
</div>
</div>
<?php endif; ?>
</div>
</div>

+ 0
- 1
producer/views/subscription/_form.php View File

@@ -233,7 +233,6 @@ $orderModule = OrderModule::getInstance();
<td class="price-unit">
{{ formatPrice(product.price_with_tax) }}<br/><span
class="unit">{{ product.wording_unit }}</span>

</td>
<td class="quantity">
<div class="input-group">

+ 74
- 55
producer/web/css/screen.css View File

@@ -1366,117 +1366,117 @@ termes.
}
/* line 152, ../sass/order/_order.scss */
.order-order #main #app-order-order #legend #leave-period-date-color {
background-color: #E09F3E;
border: solid 2px #E09F3E;
background-color: gray;
border: solid 2px gray;
}
/* line 158, ../sass/order/_order.scss */
/* line 160, ../sass/order/_order.scss */
.order-order #main #app-order-order #calendar {
margin-bottom: 15px;
}
/* line 160, ../sass/order/_order.scss */
/* line 162, ../sass/order/_order.scss */
.order-order #main #app-order-order #calendar .c-header .c-title-layout .c-title-popover .c-title-anchor .c-title[data-v-2083cb72] {
font-size: 2rem;
}
/* line 163, ../sass/order/_order.scss */
/* line 165, ../sass/order/_order.scss */
.order-order #main #app-order-order #calendar .c-day-background {
padding: 20px;
}
/* line 168, ../sass/order/_order.scss */
/* line 170, ../sass/order/_order.scss */
.order-order #main #app-order-order #calendar .c-day:hover .c-day-background {
background-color: #ee6f42 !important;
color: white !important;
}
/* line 174, ../sass/order/_order.scss */
/* line 176, ../sass/order/_order.scss */
.order-order #main #app-order-order #calendar .c-day-popover-content {
font-size: 1.3rem;
}
/* line 182, ../sass/order/_order.scss */
/* line 184, ../sass/order/_order.scss */
.order-order #main #app-order-order #credit-online-payment .panel .panel-heading .btn-default {
float: right;
position: relative;
top: -5px;
}
/* line 193, ../sass/order/_order.scss */
/* line 195, ../sass/order/_order.scss */
.order-order #main #app-order-order #content-step-date #distributions-list .card {
margin-bottom: 20px;
}
/* line 196, ../sass/order/_order.scss */
/* line 198, ../sass/order/_order.scss */
.order-order #main #app-order-order #content-step-date #distributions-list .card .btn-primary {
float: right;
}
/* line 200, ../sass/order/_order.scss */
/* line 202, ../sass/order/_order.scss */
.order-order #main #app-order-order #content-step-date #distributions-list .card .date {
font-size: 1.2rem;
line-height: 1.4rem;
text-transform: uppercase;
font-family: 'worksans_semibold';
}
/* line 207, ../sass/order/_order.scss */
/* line 209, ../sass/order/_order.scss */
.order-order #main #app-order-order #content-step-date #distributions-list .card .point-sales {
color: gray;
font-size: 14px;
margin-top: 8px;
}
/* line 216, ../sass/order/_order.scss */
/* line 218, ../sass/order/_order.scss */
.order-order #main #app-order-order .block-actions {
text-align: center;
margin-top: 40px;
}
/* line 223, ../sass/order/_order.scss */
/* line 225, ../sass/order/_order.scss */
.order-order #main #app-order-order table#points-sale td.name .the-name {
color: black;
font-size: 1.2em;
line-height: 1.4em;
font-family: 'worksans_semibold';
}
/* line 230, ../sass/order/_order.scss */
/* line 232, ../sass/order/_order.scss */
.order-order #main #app-order-order table#points-sale td.name .locality {
display: none;
}
/* line 234, ../sass/order/_order.scss */
/* line 236, ../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 .minimum-order-amount,
.order-order #main #app-order-order table#points-sale td.name .shared-point-sale-producers {
color: gray;
}
/* line 239, ../sass/order/_order.scss */
/* line 241, ../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,
.order-order #main #app-order-order table#points-sale td.name .shared-point-sale-producers a {
color: #ee6f42;
}
/* line 245, ../sass/order/_order.scss */
/* line 247, ../sass/order/_order.scss */
.order-order #main #app-order-order table#points-sale td.actions {
width: 150px;
}
/* line 247, ../sass/order/_order.scss */
/* line 249, ../sass/order/_order.scss */
.order-order #main #app-order-order table#points-sale td.actions button {
width: 100%;
}
/* line 259, ../sass/order/_order.scss */
/* line 261, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products {
margin-bottom: 40px;
border-top: solid 1px #b7ab9b;
border-left: solid 1px #b7ab9b;
border-right: solid 1px #b7ab9b;
}
/* line 265, ../sass/order/_order.scss */
/* line 267, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products thead {
display: none;
}
/* line 269, ../sass/order/_order.scss */
/* line 271, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products td {
border-bottom: solid 1px #b7ab9b;
border-left: 0px none;
border-right: 0px none;
}
/* line 277, ../sass/order/_order.scss */
/* line 279, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products tr.category-closed:hover td.category-name,
.order-order #main #app-order-order table#products tr.category-open td.category-name {
padding-left: 30px;
background-color: #f4efe8;
}
/* line 284, ../sass/order/_order.scss */
/* line 286, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products td.category-name {
transition: all 0.1s linear;
background-color: #f4efe8;
@@ -1488,12 +1488,12 @@ termes.
padding-top: 13px;
padding-left: 20px;
}
/* line 295, ../sass/order/_order.scss */
/* line 297, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products td.category-name .bi-caret-down-fill,
.order-order #main #app-order-order table#products td.category-name .bi-caret-right-fill {
font-size: 15px;
}
/* line 300, ../sass/order/_order.scss */
/* line 302, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products td.category-name span.badge {
font-family: "worksans_bold";
text-transform: uppercase;
@@ -1502,21 +1502,21 @@ termes.
position: relative;
top: -3px;
}
/* line 308, ../sass/order/_order.scss */
/* line 310, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products td.category-name span.badge.bg-primary {
float: right;
top: 2px;
}
/* line 314, ../sass/order/_order.scss */
/* line 316, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products td.category-name:hover {
cursor: pointer;
background-color: white;
}
/* line 321, ../sass/order/_order.scss */
/* line 323, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products td.photo img {
width: 75px;
}
/* line 331, ../sass/order/_order.scss */
/* line 333, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products .price-unit .decreasing-prices {
margin-top: 10px;
font-size: 10px;
@@ -1524,74 +1524,74 @@ termes.
padding-bottom: 2px;
margin-bottom: 0px;
}
/* line 339, ../sass/order/_order.scss */
/* line 341, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products .price-unit .decreasing-prices ul li {
margin-bottom: 5px;
}
/* line 341, ../sass/order/_order.scss */
/* line 343, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products .price-unit .decreasing-prices ul li strong {
font-weight: bold;
}
/* line 349, ../sass/order/_order.scss */
/* line 351, ../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 353, ../sass/order/_order.scss */
/* line 355, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products .price-unit .price-infos, .order-order #main #app-order-order table#products .price-total .price-infos {
color: gray;
font-size: 13px;
line-height: 15px;
}
/* line 360, ../sass/order/_order.scss */
/* line 362, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products tr.product-open td.price-total {
font-size: 1.1rem;
font-family: 'worksans_bold';
padding-top: 19px;
}
/* line 366, ../sass/order/_order.scss */
/* line 368, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products .td-quantity {
width: 175px;
}
/* line 369, ../sass/order/_order.scss */
/* line 371, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products .td-quantity input.quantity,
.order-order #main #app-order-order table#products .td-quantity .input-group-text {
background-color: white;
}
/* line 374, ../sass/order/_order.scss */
/* line 376, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products .td-quantity input.quantity {
text-align: center;
border: 0px none;
}
/* line 378, ../sass/order/_order.scss */
/* line 380, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products .td-quantity .input-group-text {
border: 0px none;
padding-right: 10px;
padding-left: 0px;
margin: 0px;
}
/* line 386, ../sass/order/_order.scss */
/* line 388, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products .td-quantity .input-group-btn button {
padding: 4px 5px 0px 5px;
}
/* line 388, ../sass/order/_order.scss */
/* line 390, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products .td-quantity .input-group-btn button .bi {
font-size: 1.5em;
font-weight: bold;
margin: 0px;
}
/* line 399, ../sass/order/_order.scss */
/* line 401, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products tr.total .summary {
padding: 25px;
}
/* line 402, ../sass/order/_order.scss */
/* line 404, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products tr.total .summary h3 {
font-family: 'worksans_bold';
margin-top: 0px;
text-transform: uppercase;
margin-bottom: 5px;
}
/* line 409, ../sass/order/_order.scss */
/* line 411, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products tr.total .summary ul {
margin-bottom: 15px;
padding-left: 20px;
@@ -1599,17 +1599,17 @@ termes.
line-height: 1.4rem;
list-style-type: disc;
}
/* line 417, ../sass/order/_order.scss */
/* line 419, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products tr.total .summary ul li .quantity {
font-weight: bold;
}
/* line 424, ../sass/order/_order.scss */
/* line 426, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products tr.total .price-total {
padding-top: 25px;
font-size: 1.5rem;
font-family: 'worksans_bold';
}
/* line 429, ../sass/order/_order.scss */
/* line 431, ../sass/order/_order.scss */
.order-order #main #app-order-order table#products tr.total .price-total span {
display: inline-block;
padding: 7px 15px;
@@ -1619,30 +1619,30 @@ termes.
color: white;
font-size: 1.2rem;
}
/* line 441, ../sass/order/_order.scss */
/* line 443, ../sass/order/_order.scss */
.order-order #main #app-order-order #payment-methods .infos {
margin-top: 10px;
color: gray;
}
/* line 448, ../sass/order/_order.scss */
/* line 450, ../sass/order/_order.scss */
.order-order #main #app-order-order #content-step-payment .delivery {
margin-bottom: 20px;
}
/* line 452, ../sass/order/_order.scss */
/* line 454, ../sass/order/_order.scss */
.order-order #main #app-order-order #content-step-payment .comment {
margin-bottom: 20px;
}
/* line 456, ../sass/order/_order.scss */
/* line 458, ../sass/order/_order.scss */
.order-order #main #app-order-order #content-step-payment #payment-methods {
margin-bottom: 20px;
}
/* line 461, ../sass/order/_order.scss */
/* line 463, ../sass/order/_order.scss */
.order-order #main #app-order-order #content-step-payment .credit .info {
margin-left: 20px;
color: gray;
}

/* line 474, ../sass/order/_order.scss */
/* line 476, ../sass/order/_order.scss */
#main #content .panel h3 {
font-family: "worksans_bold";
margin: 0px;
@@ -2216,13 +2216,32 @@ termes.
text-align: center;
}

/* line 291, ../sass/_responsive.scss */
/* line 292, ../sass/_responsive.scss */
.subscription-form #main #content .block.products .table td {
padding: 5px;
}
/* line 295, ../sass/_responsive.scss */
.subscription-form #main #content .block.products .table .recipe {
display: none;
}
/* line 298, ../sass/_responsive.scss */
.subscription-form #main #content .block.products .table .input-group-btn {
display: block;
width: 1000%;
}
/* line 301, ../sass/_responsive.scss */
.subscription-form #main #content .block.products .table .input-group-btn .btn {
display: block;
width: 100%;
}

/* line 312, ../sass/_responsive.scss */
#footer .content {
text-align: center;
}
}
@media screen and (max-width: 991px) {
/* line 303, ../sass/_responsive.scss */
/* line 324, ../sass/_responsive.scss */
#main {
padding-top: 0px;
}

+ 2
- 1
producer/web/js/vuejs/order-order.js View File

@@ -179,7 +179,8 @@ var app = new Vue({
app.calendar.attrs.push({
highlight: {
style: {
background: '#E09F3E'
//background: '#E09F3E'
background: 'gray'
},
contentStyle: {
color: 'white'

+ 22
- 1
producer/web/sass/_responsive.scss View File

@@ -284,7 +284,28 @@ termes.
}
}
}
}

.subscription-form {
#main #content .block.products {
.table {
td {
padding: 5px;
}
.recipe {
display: none;
}
.input-group-btn {
display: block;
width: 1000%;
.btn {
display: block;
width: 100%;
}
}
}

}
}
#footer {

+ 4
- 2
producer/web/sass/order/_order.scss View File

@@ -150,8 +150,10 @@
border: solid 2px #198754;
}
#leave-period-date-color {
background-color: #E09F3E;
border: solid 2px #E09F3E;
//background-color: #E09F3E;
//border: solid 2px #E09F3E;
background-color: gray;
border: solid 2px gray;
}
}

Loading…
Cancel
Save