@@ -0,0 +1,137 @@ | |||
<?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. | |||
*/ | |||
namespace backend\controllers; | |||
use backend\controllers\BackendController; | |||
use domain\Communication\AutomaticEmail\AutomaticEmail; | |||
use domain\Feature\Feature\Feature; | |||
use domain\Product\Accessory\Accessory; | |||
use yii\base\Response; | |||
use yii\db\StaleObjectException; | |||
use yii\filters\AccessControl; | |||
use yii\web\NotFoundHttpException; | |||
class AutomaticEmailController extends BackendController | |||
{ | |||
public function behaviors() | |||
{ | |||
return [ | |||
'access' => [ | |||
'class' => AccessControl::class, | |||
'rules' => [ | |||
[ | |||
'allow' => true, | |||
'roles' => ['@'], | |||
'matchCallback' => function ($rule, $action) { | |||
return $this->getUserModule()->getAuthorizationChecker() | |||
->isGrantedAsProducer($this->getUserCurrent()) | |||
&& $this->getFeatureModule()->getChecker() | |||
->isEnabled(Feature::ALIAS_AUTOMATIC_EMAIL); | |||
} | |||
] | |||
], | |||
], | |||
]; | |||
} | |||
public function actionIndex() | |||
{ | |||
return $this->render('index', [ | |||
'dataProvider' => $this->getAutomaticEmailModule()->getRepository() | |||
->queryAutomaticEmails()->getDataProvider(20), | |||
]); | |||
} | |||
public function actionCreate() | |||
{ | |||
$automaticEmailModule = $this->getAutomaticEmailModule(); | |||
$automaticEmailModel = $automaticEmailModule->getBuilder()->instanciateAutomaticEmail($this->getProducerCurrent()); | |||
if ($automaticEmailModel->load(\Yii::$app->request->post()) && $automaticEmailModel->validate()) { | |||
$automaticEmail = $automaticEmailModule->getManager()->createAutomaticEmail( | |||
$this->getProducerCurrent(), | |||
$automaticEmailModel->getDay(), | |||
$automaticEmailModel->getDelayBeforeDistribution(), | |||
$automaticEmailModel->getSubject(), | |||
$automaticEmailModel->getMessage(), | |||
$automaticEmailModel->getIntegrateProductList() | |||
); | |||
$this->setFlash('success', "Email automatique ajouté"); | |||
return $this->redirectAfterSave('automatic-email', $automaticEmail->getId()); | |||
} | |||
return $this->render('create', [ | |||
'automaticEmail' => $automaticEmailModel | |||
]); | |||
} | |||
public function actionUpdate(int $id) | |||
{ | |||
$automaticEmail = $this->findAutomaticEmail($id); | |||
if ($automaticEmail->load(\Yii::$app->request->post()) && $automaticEmail->validate() && $automaticEmail->save()) { | |||
$this->setFlash('success', "Email automatique modifié"); | |||
return $this->redirectAfterSave('automatic-email', $automaticEmail->getId()); | |||
} | |||
return $this->render('update', [ | |||
'automaticEmail' => $automaticEmail | |||
]); | |||
} | |||
public function actionDelete(int $id): Response | |||
{ | |||
$automaticEmail = $this->findAutomaticEmail($id); | |||
if($automaticEmail->delete()) { | |||
$this->setFlash('success', "Email automatique supprimé"); | |||
} | |||
return $this->redirect('index'); | |||
} | |||
protected function findAutomaticEmail($id): AutomaticEmail | |||
{ | |||
if (($automaticEmail = $this->getAutomaticEmailModule()->getRepository()->findOneAutomaticEmailById($id)) !== null) { | |||
return $automaticEmail; | |||
} else { | |||
throw new NotFoundHttpException("L'email automatique n'a pas été trouvé."); | |||
} | |||
} | |||
} |
@@ -40,8 +40,10 @@ namespace backend\controllers; | |||
use backend\models\MailForm; | |||
use common\helpers\GlobalParam; | |||
use kartik\mpdf\Pdf; | |||
use domain\Communication\Email\ContactListResolver; | |||
use domain\Communication\Email\EmailGenerator; | |||
use domain\PointSale\PointSale\PointSale; | |||
use kartik\mpdf\Pdf; | |||
use yii\filters\AccessControl; | |||
use yii\filters\VerbFilter; | |||
@@ -84,91 +86,56 @@ class CommunicateController extends BackendController | |||
$usersPointSaleHasOrder = 0, | |||
$idDistribution = 0) | |||
{ | |||
$producerCurrent = $this->getProducerCurrent(); | |||
$userModule = $this->getUserModule(); | |||
$distributionModule = $this->getDistributionModule(); | |||
$emailModule = $this->getEmailModule(); | |||
$mailForm = new MailForm(); | |||
// Sujet et message par défaut | |||
$mailForm->subject = $this->getProducerModule()->getSolver()->getConfig('option_communicate_email_default_subject'); | |||
$mailForm->message = $this->getProducerModule()->getSolver()->getConfig('option_communicate_email_default_message'); | |||
if ($idPointSale && !$usersPointSaleLink && !$usersPointSaleHasOrder) { | |||
$usersPointSaleLink = 1; | |||
} | |||
if ($idDistribution && !$usersPointSaleLink && !$usersPointSaleHasOrder) { | |||
$usersPointSaleLink = 1; | |||
} | |||
if($idDistribution) { | |||
$users = []; | |||
$distribution = $distributionModule->getRepository()->findOneDistributionById($idDistribution); | |||
if($distribution) { | |||
$mailForm->id_distribution = $idDistribution; | |||
foreach($distribution->pointSaleDistribution as $pointSaleDistribution) { | |||
if($pointSaleDistribution->delivery) { | |||
$usersPointSaleArray = $userModule->getRepository()->queryUsersBy([ | |||
'id_producer' => $producerCurrent->id, | |||
'id_point_sale' => $pointSaleDistribution->id_point_sale, | |||
'users_point_sale_link' => $usersPointSaleLink, | |||
'users_point_sale_has_order' => $usersPointSaleHasOrder, | |||
'newsletter' => true | |||
])->all(); | |||
foreach($usersPointSaleArray as $user) { | |||
$users[$user['id']] = $user; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
else { | |||
$users = $userModule->queryUsersBy([ | |||
'id_producer' => GlobalParam::getCurrentProducerId(), | |||
'id_point_sale' => $idPointSale, | |||
'users_point_sale_link' => $usersPointSaleLink, | |||
'users_point_sale_has_order' => $usersPointSaleHasOrder, | |||
'subscribers' => $sectionSubscribers, | |||
'inactive' => $sectionInactiveUsers, | |||
'newsletter' => true | |||
])->all(); | |||
} | |||
$usersArray = []; | |||
foreach ($users as $key => $user) { | |||
if (isset($user['email']) && strlen($user['email']) > 0) { | |||
if($this->getProducerCurrent()->isDemoAccount()) { | |||
$usersArray[] = \Yii::$app->faker->email(); | |||
} | |||
else { | |||
$usersArray[] = $user['email']; | |||
} | |||
} else { | |||
unset($users[$key]); | |||
} | |||
} | |||
$pointsSaleArray = PointSale::find()->where(['id_producer' => GlobalParam::getCurrentProducerId(), 'status' => 1])->all(); | |||
$pointSale = null; | |||
if ($idPointSale) { | |||
$pointSale = PointSale::findOne(['id' => $idPointSale]); | |||
$pointSale = $idPointSale ? $this->getPointSaleModule()->getRepository()->findOnePointSaleById($idPointSale) : null; | |||
$distribution = $idDistribution ? $this->getDistributionModule()->getRepository()->findOneDistributionById($idDistribution) : null; | |||
if($distribution) { | |||
$mailForm->id_distribution = $distribution->id; | |||
} | |||
$usersArray = $emailModule->getContactListResolver()->search( | |||
$this->getProducerCurrent(), | |||
$distribution, | |||
$pointSale, | |||
$usersPointSaleLink, | |||
$usersPointSaleHasOrder, | |||
$sectionSubscribers, | |||
$sectionInactiveUsers | |||
); | |||
if ($mailForm->load(\Yii::$app->request->post()) && $mailForm->validate()) { | |||
if($this->getProducerCurrent()->isDemoAccount()) { | |||
$this->setFlash('error', "Fonctionnalité désactivée sur le compte de démo."); | |||
} | |||
else { | |||
$mailForm->sendEmail($users); | |||
$distribution = $mailForm->id_distribution | |||
? $this->getDistributionModule()->getRepository()->findOneDistributionById($mailForm->id_distribution) | |||
: null; | |||
$email = $emailModule->getGenerator()->createEmail( | |||
$mailForm->subject, | |||
$mailForm->message, | |||
$mailForm->integrate_product_list, | |||
$this->getProducerCurrent(), | |||
$distribution | |||
); | |||
$emailModule->getBulkMailer()->sendEmail($email, $usersArray); | |||
$this->setFlash('success', 'Votre email a bien été envoyé.'); | |||
} | |||
return $this->redirect(['email', 'idPointSale' => $idPointSale]); | |||
} | |||
$incomingDistributionsArray = $distributionModule->findDistributionsIncoming(); | |||
$pointsSaleArray = PointSale::find()->where(['id_producer' => GlobalParam::getCurrentProducerId(), 'status' => 1])->all(); | |||
$incomingDistributionsArray = $this->getDistributionModule()->getRepository()->findDistributionsIncoming(); | |||
$incomingDistributionsDatesArray = ['0' => '--']; | |||
foreach ($incomingDistributionsArray as $distribution) { | |||
$incomingDistributionsDatesArray[$distribution->id] = strftime('%A %d %B %Y', strtotime($distribution->date)); |
@@ -227,8 +227,18 @@ class DistributionController extends BackendController | |||
} | |||
$jsonProduct['quantity_max'] = $orderModule->getResolver()->getProductQuantityMax($product, $distribution); | |||
$jsonProduct['quantity_remaining'] = $orderModule->getResolver()->getProductQuantityRemaining($product, $distribution); | |||
$jsonProduct['quantity_form'] = 0; | |||
$jsonProduct['accessories'] = []; | |||
foreach($product->getProductAccessories() as $productAccessory) { | |||
$jsonProduct['accessories'][] = [ | |||
'id_accessory' => $productAccessory->getAccessory()->getId(), | |||
'quantity' => $productAccessory->getQuantity(), | |||
]; | |||
} | |||
if ($product->taxRate) { | |||
$jsonProduct['taxRate'] = $product->taxRate->getAttributes(); | |||
} | |||
@@ -530,11 +540,14 @@ class DistributionController extends BackendController | |||
$idDistribution, | |||
$idUser = false, | |||
$idPointSale = false, | |||
$idOrder = false | |||
$idOrder = false, | |||
$productOrderFormArray = [] | |||
) | |||
{ | |||
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; | |||
$productOrderFormArray = json_decode($productOrderFormArray, true); | |||
$distributionModule = $this-> getDistributionModule(); | |||
$orderModule = $this->getOrderModule(); | |||
$userModule = $this->getUserModule(); | |||
@@ -565,7 +578,7 @@ class DistributionController extends BackendController | |||
if (isset($order->productOrder)) { | |||
foreach ($order->productOrder as $productOrder) { | |||
if ($productOrder->id_product == $product['id']) { | |||
if ($productOrder->id_product == $product->id) { | |||
if ($productOrder->invoice_price) { | |||
$invoicePrice = number_format($productOrder->invoice_price, 5); | |||
} else { | |||
@@ -576,16 +589,28 @@ class DistributionController extends BackendController | |||
} | |||
} | |||
$productOrderArray[$product['id']] = [ | |||
// Quantité définie dans le formulaire | |||
if(isset($productOrderFormArray[$product->id]['quantity'])) { | |||
$quantity = $productOrderFormArray[$product->id]['quantity'] / $productModule->getSolver()->getUnitCoefficient($product); | |||
} | |||
$productOrderArray[$product->id] = [ | |||
'quantity' => $quantity, | |||
'unit' => $product->unit, | |||
'prices' => $priceArray, | |||
'active' => $product->productDistribution[0]->active | |||
&& (!$pointSale || $productModule->isAvailableOnPointSale($product, $pointSale)), | |||
'invoice_price' => $invoicePrice | |||
'invoice_price' => $invoicePrice, | |||
]; | |||
} | |||
// construction de $orderOverride | |||
$orderOverride = $orderModule->getBuilder()->instanciateOrderFromProductOrdersArray($productOrderArray, $order); | |||
foreach($productOrderArray as $idProduct => $productOrder) { | |||
$product = $productModule->getRepository()->findOneProductById($idProduct); | |||
$productOrderArray[$idProduct]['quantity_remaining'] = $orderModule->getResolver()->getProductQuantityRemaining($product, $distribution, $orderOverride); | |||
} | |||
return $productOrderArray; | |||
} | |||
@@ -44,6 +44,7 @@ use common\helpers\MeanPayment; | |||
use common\helpers\Price; | |||
use domain\Order\Order\OrderRepositoryQuery; | |||
use domain\Order\OrderStatus\OrderStatus; | |||
use backend\forms\ReportPaymentsForm; | |||
use Yii; | |||
use yii\filters\AccessControl; | |||
@@ -198,85 +199,98 @@ class ReportController extends BackendController | |||
return $condition; | |||
} | |||
public function actionPayments(string $dateStart, string $dateEnd) | |||
public function actionPayments() | |||
{ | |||
$orderModule = $this->getOrderModule(); | |||
$ordersArray = $orderModule->getRepository() | |||
->findOrdersByPeriod( | |||
new \DateTime($dateStart), | |||
new \DateTime($dateEnd) | |||
); | |||
$datas = [ | |||
[ | |||
'Date', | |||
'Client', | |||
'Montant', | |||
'Facture', | |||
'Crédit', | |||
'Espèce', | |||
'Chèque', | |||
'Virement', | |||
'Carte bancaire', | |||
'Total' | |||
] | |||
]; | |||
$dateStart = $dateEnd = null; | |||
$reportPaymentsForm = new ReportPaymentsForm(); | |||
if($reportPaymentsForm->load(Yii::$app->request->post()) && $reportPaymentsForm->validate()) { | |||
$dateStart = date('Y-m-d', strtotime(str_replace('/', '-', $reportPaymentsForm->date_start))); | |||
$dateEnd = date('Y-m-d', strtotime(str_replace('/', '-', $reportPaymentsForm->date_end))); | |||
} | |||
if($dateStart && $dateEnd) { | |||
$orderModule = $this->getOrderModule(); | |||
$ordersArray = $orderModule->getRepository() | |||
->findOrdersByPeriod( | |||
new \DateTime($dateStart), | |||
new \DateTime($dateEnd) | |||
); | |||
$sumAmountTotalSpentByCredit = 0; | |||
$sumAmountTotalSpentByMoney = 0; | |||
$sumAmountTotalSpentByCheque = 0; | |||
$sumAmountTotalSpentByTransfer = 0; | |||
$sumAmountTotalSpentByCreditCard = 0; | |||
$datas = [ | |||
[ | |||
'Date', | |||
'Client', | |||
'Montant', | |||
'Facture', | |||
'Crédit', | |||
'Espèce', | |||
'Chèque', | |||
'Virement', | |||
'Carte bancaire', | |||
'Total' | |||
] | |||
]; | |||
foreach($ordersArray as $order) { | |||
$orderModule->getBuilder()->initOrder($order); | |||
$sumAmountTotalSpentByCredit = 0; | |||
$sumAmountTotalSpentByMoney = 0; | |||
$sumAmountTotalSpentByCheque = 0; | |||
$sumAmountTotalSpentByTransfer = 0; | |||
$sumAmountTotalSpentByCreditCard = 0; | |||
$hasInvoice = false; | |||
$referenceInvoice = 'Non'; | |||
if($order->invoice) { | |||
$hasInvoice = true; | |||
$referenceInvoice = $order->invoice->reference.""; | |||
} | |||
foreach($ordersArray as $order) { | |||
$orderModule->getBuilder()->initOrder($order); | |||
$hasInvoice = false; | |||
$referenceInvoice = 'Non'; | |||
if($order->invoice) { | |||
$hasInvoice = true; | |||
$referenceInvoice = $order->invoice->reference.""; | |||
} | |||
$amountTotalSpentByCredit = $orderModule->getSolver()->getAmountTotalSpentByMeanPayment($order, MeanPayment::CREDIT); | |||
$sumAmountTotalSpentByCredit += $amountTotalSpentByCredit; | |||
$amountTotalSpentByMoney = $orderModule->getSolver()->getAmountTotalSpentByMeanPayment($order, MeanPayment::MONEY); | |||
$sumAmountTotalSpentByMoney += $amountTotalSpentByMoney; | |||
$amountTotalSpentByCheque = $orderModule->getSolver()->getAmountTotalSpentByMeanPayment($order, MeanPayment::CHEQUE); | |||
$sumAmountTotalSpentByCheque += $amountTotalSpentByCheque; | |||
$amountTotalSpentByTransfer = $orderModule->getSolver()->getAmountTotalSpentByMeanPayment($order, MeanPayment::TRANSFER); | |||
$sumAmountTotalSpentByTransfer += $amountTotalSpentByTransfer; | |||
$amountTotalSpentByCreditCard = $orderModule->getSolver()->getAmountTotalSpentByMeanPayment($order, MeanPayment::CREDIT_CARD); | |||
$sumAmountTotalSpentByCreditCard += $amountTotalSpentByCreditCard; | |||
$amountTotalSpentByCredit = $orderModule->getSolver()->getAmountTotalSpentByMeanPayment($order, MeanPayment::CREDIT); | |||
$sumAmountTotalSpentByCredit += $amountTotalSpentByCredit; | |||
$amountTotalSpentByMoney = $orderModule->getSolver()->getAmountTotalSpentByMeanPayment($order, MeanPayment::MONEY); | |||
$sumAmountTotalSpentByMoney += $amountTotalSpentByMoney; | |||
$amountTotalSpentByCheque = $orderModule->getSolver()->getAmountTotalSpentByMeanPayment($order, MeanPayment::CHEQUE); | |||
$sumAmountTotalSpentByCheque += $amountTotalSpentByCheque; | |||
$amountTotalSpentByTransfer = $orderModule->getSolver()->getAmountTotalSpentByMeanPayment($order, MeanPayment::TRANSFER); | |||
$sumAmountTotalSpentByTransfer += $amountTotalSpentByTransfer; | |||
$amountTotalSpentByCreditCard = $orderModule->getSolver()->getAmountTotalSpentByMeanPayment($order, MeanPayment::CREDIT_CARD); | |||
$sumAmountTotalSpentByCreditCard += $amountTotalSpentByCreditCard; | |||
$datas[] = [ | |||
$order->distribution->date, | |||
$orderModule->getSolver()->getOrderUsername($order), | |||
CSV::formatNumber($orderModule->getSolver()->getOrderAmountWithTax($order)), | |||
$referenceInvoice, | |||
$hasInvoice ? '' : CSV::formatNumber($amountTotalSpentByCredit), | |||
$hasInvoice ? '' : CSV::formatNumber($amountTotalSpentByMoney), | |||
$hasInvoice ? '' : CSV::formatNumber($amountTotalSpentByCheque), | |||
$hasInvoice ? '' : CSV::formatNumber($amountTotalSpentByTransfer), | |||
$hasInvoice ? '' : CSV::formatNumber($amountTotalSpentByCreditCard) | |||
]; | |||
} | |||
$datas[] = [ | |||
$order->distribution->date, | |||
$orderModule->getSolver()->getOrderUsername($order), | |||
CSV::formatNumber($orderModule->getSolver()->getOrderAmountWithTax($order)), | |||
$referenceInvoice, | |||
$hasInvoice ? '' : CSV::formatNumber($amountTotalSpentByCredit), | |||
$hasInvoice ? '' : CSV::formatNumber($amountTotalSpentByMoney), | |||
$hasInvoice ? '' : CSV::formatNumber($amountTotalSpentByCheque), | |||
$hasInvoice ? '' : CSV::formatNumber($amountTotalSpentByTransfer), | |||
$hasInvoice ? '' : CSV::formatNumber($amountTotalSpentByCreditCard) | |||
'', | |||
'', | |||
'', | |||
'Totaux paiements', | |||
CSV::formatNumber($sumAmountTotalSpentByCredit), | |||
CSV::formatNumber($sumAmountTotalSpentByMoney), | |||
CSV::formatNumber($sumAmountTotalSpentByCheque), | |||
CSV::formatNumber($sumAmountTotalSpentByTransfer), | |||
CSV::formatNumber($sumAmountTotalSpentByCreditCard), | |||
CSV::formatNumber($sumAmountTotalSpentByCredit + $sumAmountTotalSpentByMoney + $sumAmountTotalSpentByCheque + | |||
$sumAmountTotalSpentByTransfer + $sumAmountTotalSpentByCreditCard) | |||
]; | |||
} | |||
$datas[] = [ | |||
'', | |||
'', | |||
'', | |||
'Totaux paiements', | |||
CSV::formatNumber($sumAmountTotalSpentByCredit), | |||
CSV::formatNumber($sumAmountTotalSpentByMoney), | |||
CSV::formatNumber($sumAmountTotalSpentByCheque), | |||
CSV::formatNumber($sumAmountTotalSpentByTransfer), | |||
CSV::formatNumber($sumAmountTotalSpentByCreditCard), | |||
CSV::formatNumber($sumAmountTotalSpentByCredit + $sumAmountTotalSpentByMoney + $sumAmountTotalSpentByCheque + | |||
$sumAmountTotalSpentByTransfer + $sumAmountTotalSpentByCreditCard) | |||
]; | |||
CSV::send('commandes.csv', $datas); | |||
} | |||
CSV::send('commandes.csv', $datas); | |||
return $this->render('payments', [ | |||
'reportPaymentsForm' => $reportPaymentsForm | |||
]); | |||
} | |||
} |
@@ -159,6 +159,7 @@ class UserController extends BackendController | |||
); | |||
$userCreate->points_sale = $model->points_sale; | |||
$userCreate->user_groups = $model->user_groups; | |||
$userCreate->exclusive_access_selected_points_sale = $model->exclusive_access_selected_points_sale; | |||
$this->processLinkPointSale($userCreate); | |||
$this->processLinkUserGroup($userCreate); | |||
@@ -220,6 +221,7 @@ class UserController extends BackendController | |||
$model->newsletter = $userBelongToProducer->newsletter; | |||
$model->trust_alert = $userBelongToProducer->trust_alert; | |||
$model->trust_alert_comment = $userBelongToProducer->trust_alert_comment; | |||
$model->exclusive_access_selected_points_sale = $userBelongToProducer->exclusive_access_selected_points_sale; | |||
if ($model->load(\Yii::$app->request->post()) && $model->save()) { | |||
@@ -420,6 +422,11 @@ class UserController extends BackendController | |||
} | |||
} | |||
} | |||
// Accès exclusif aux points de vente sélectionnés | |||
$userProducer = UserProducer::findOne(['id_user' => $modelUser->id, 'id_producer' => GlobalParam::getCurrentProducerId()]); | |||
$userProducer->setExclusiveAccessSelectedPointsSale($modelUser->exclusive_access_selected_points_sale); | |||
$userProducer->save(); | |||
} | |||
/** |
@@ -0,0 +1,35 @@ | |||
<?php | |||
namespace backend\forms; | |||
use yii\base\Model; | |||
/** | |||
* UploadForm is the model behind the upload form. | |||
*/ | |||
class ReportPaymentsForm extends Model | |||
{ | |||
var $date_start; | |||
var $date_end; | |||
/** | |||
* @return array the validation rules. | |||
*/ | |||
public function rules() | |||
{ | |||
return [ | |||
[['date_start', 'date_end'], 'required'], | |||
[['date_start', 'date_end'], 'safe'], | |||
]; | |||
} | |||
public function attributeLabels() | |||
{ | |||
return [ | |||
'date_start' => "Date de début", | |||
'date_end' => "Date de fin" | |||
]; | |||
} | |||
} | |||
?> |
@@ -39,20 +39,10 @@ termes. | |||
namespace backend\models; | |||
use common\helpers\GlobalParam; | |||
use common\helpers\Mailjet; | |||
use common\helpers\Price; | |||
use domain\Config\Unit\UnitDefinition; | |||
use domain\Distribution\Distribution\Distribution; | |||
use domain\Communication\Email\EmailGenerator; | |||
use domain\Distribution\Distribution\DistributionModule; | |||
use domain\Producer\Producer\ProducerModule; | |||
use domain\Product\Product\Product; | |||
use domain\Product\Product\ProductModule; | |||
use GuzzleHttp\Client; | |||
use GuzzleHttp\Psr7\Response; | |||
use Psr\Http\Message\ResponseInterface; | |||
use Yii; | |||
use yii\base\Model; | |||
use yii\helpers\Html; | |||
/** | |||
* ContactForm is the model behind the contact form. | |||
@@ -62,7 +52,7 @@ class MailForm extends Model | |||
public $id_distribution ; | |||
public $subject; | |||
public $message; | |||
public $integrate_product_list = false; | |||
public $integrate_product_list; | |||
/** | |||
* @inheritdoc | |||
@@ -88,274 +78,4 @@ class MailForm extends Model | |||
'integrate_product_list' => 'Intégrer la liste des produits au message' | |||
]; | |||
} | |||
public function sendEmail($contactsArray, $fromProducer = true) | |||
{ | |||
$productModule = ProductModule::getInstance(); | |||
$producerModule = ProducerModule::getInstance(); | |||
$distributionModule = DistributionModule::getInstance(); | |||
$messageAutoText = '' ; | |||
$messageAutoHtml = '' ; | |||
$messageAutoHtml .= ' <style type="text/css"> | |||
h1, h2, h3, h4, h5, h6 { | |||
padding: 0px; | |||
margin: 0px; | |||
margin-bottom: 10px; | |||
} | |||
p { | |||
margin: 0px; | |||
padding: 0px; | |||
margin-bottom: 5px; | |||
} | |||
</style>'; | |||
if($this->id_distribution) { | |||
$messageAutoText = ' | |||
' ; | |||
$messageAutoHtml .= '<br /><br />' ; | |||
$distribution = Distribution::searchOne(['id' => $this->id_distribution]) ; | |||
if($distribution) { | |||
$linkOrder = $distributionModule->getLinkOrder($distribution); | |||
$dateOrder = strftime('%A %d %B %Y', strtotime($distribution->date)) ; | |||
$messageAutoHtml .= '<a href="'.$linkOrder.'">Passer ma commande du '.$dateOrder.'</a>' ; | |||
$messageAutoText .= 'Suivez ce lien pour passer votre commande du '.$dateOrder.' : | |||
'.$linkOrder ; | |||
if($this->integrate_product_list) { | |||
$productsArray = Product::find() | |||
->where([ | |||
'id_producer' => GlobalParam::getCurrentProducerId(), | |||
]) | |||
->andWhere('status >= :status') | |||
->addParams(['status' => Product::STATUS_OFFLINE]) | |||
->innerJoinWith(['productDistribution' => function($query) use($distribution) { | |||
$query->andOnCondition([ | |||
'product_distribution.id_distribution' => $distribution->id, | |||
'product_distribution.active' => 1 | |||
]); | |||
}]) | |||
->orderBy('product.name ASC') | |||
->all(); | |||
if(count($productsArray) > 1) { | |||
$messageAutoHtml .= '<br /><br />Produits disponibles : <br /><ul>' ; | |||
$messageAutoText .= ' | |||
Produits disponibles : | |||
' ; | |||
foreach($productsArray as $product) { | |||
$productDescription = $product->name ; | |||
if(strlen($product->description)) { | |||
$productDescription .= ' / '.$product->description ; | |||
} | |||
if($product->price) { | |||
$productDescription .= ' / '.Price::format($productModule->getPriceWithTax($product)) ; | |||
$productDescription .= ' ('. $productModule->getSolver()->strUnit($product, UnitDefinition::WORDING_UNIT).')' ; | |||
} | |||
$messageAutoText .= '- '.$productDescription.' | |||
' ; | |||
$messageAutoHtml .= '<li>'.Html::encode($productDescription).'</li>' ; | |||
} | |||
$messageAutoHtml .= '</ul>' ; | |||
} | |||
} | |||
} | |||
} | |||
if($fromProducer) { | |||
$producer = GlobalParam::getCurrentProducer() ; | |||
$fromEmail = $producerModule->getProducerEmailPlatform($producer) ; | |||
$fromName = $producer->name ; | |||
$linkProducer = 'https://'.$producer->slug.'.souke.fr'; | |||
$linkUnsubscribe = Yii::$app->urlManagerProducer->createAbsoluteUrl(['newsletter/unsubscribe', 'slug_producer' => $producer->slug]); | |||
// Message inscription newsletter | |||
$messageAutoText .= " | |||
-- | |||
Boutique : ".$linkProducer." | |||
Me désinscrire : ".$linkUnsubscribe; | |||
$messageAutoHtml .= "<br /><br />--<br>"; | |||
$messageAutoHtml .= "Boutique : <a href=\"".$linkProducer."\">".$linkProducer."</a><br>"; | |||
$messageAutoHtml .= "Me désinscrire : <a href=\"".$linkUnsubscribe."\">".$linkUnsubscribe."</a>"; | |||
} | |||
else { | |||
$fromEmail = 'contact@souke.fr' ; | |||
$fromName = 'Souke' ; | |||
} | |||
$subject = $this->subject; | |||
$htmlContent = nl2br($this->message).$messageAutoHtml; | |||
$textContent = $this->message.$messageAutoText; | |||
Yii::$app->bulkMailer->sendEmails($contactsArray, $fromName, $fromEmail, $subject, $htmlContent, $textContent); | |||
} | |||
/* | |||
public function sendEmailMailjet($usersArray, $fromProducer = true) | |||
{ | |||
$productModule = ProductModule::getInstance(); | |||
$producerModule = ProducerModule::getInstance(); | |||
$distributionModule = DistributionModule::getInstance(); | |||
$mj = new \Mailjet\Client( | |||
Mailjet::getApiKey('public'), | |||
Mailjet::getApiKey('private'), | |||
true, | |||
['version' => 'v3.1'] | |||
); | |||
$messageAutoText = '' ; | |||
$messageAutoHtml = '' ; | |||
$messageAutoHtml .= ' <style type="text/css"> | |||
h1, h2, h3, h4, h5, h6 { | |||
padding: 0px; | |||
margin: 0px; | |||
margin-bottom: 10px; | |||
} | |||
p { | |||
margin: 0px; | |||
padding: 0px; | |||
margin-bottom: 5px; | |||
} | |||
</style>'; | |||
if($this->id_distribution) { | |||
$messageAutoText = ' | |||
' ; | |||
$messageAutoHtml .= '<br /><br />' ; | |||
$distribution = Distribution::searchOne(['id' => $this->id_distribution]) ; | |||
if($distribution) { | |||
$linkOrder = $distributionModule->getLinkOrder($distribution); | |||
$dateOrder = strftime('%A %d %B %Y', strtotime($distribution->date)) ; | |||
$messageAutoHtml .= '<a href="'.$linkOrder.'">Passer ma commande du '.$dateOrder.'</a>' ; | |||
$messageAutoText .= 'Suivez ce lien pour passer votre commande du '.$dateOrder.' : | |||
'.$linkOrder ; | |||
if($this->integrate_product_list) { | |||
$productsArray = Product::find() | |||
->where([ | |||
'id_producer' => GlobalParam::getCurrentProducerId(), | |||
]) | |||
->andWhere('status >= :status') | |||
->addParams(['status' => Product::STATUS_OFFLINE]) | |||
->innerJoinWith(['productDistribution' => function($query) use($distribution) { | |||
$query->andOnCondition([ | |||
'product_distribution.id_distribution' => $distribution->id, | |||
'product_distribution.active' => 1 | |||
]); | |||
}]) | |||
->orderBy('product.name ASC') | |||
->all(); | |||
if(count($productsArray) > 1) { | |||
$messageAutoHtml .= '<br /><br />Produits disponibles : <br /><ul>' ; | |||
$messageAutoText .= ' | |||
Produits disponibles : | |||
' ; | |||
foreach($productsArray as $product) { | |||
$productDescription = $product->name ; | |||
if(strlen($product->description)) { | |||
$productDescription .= ' / '.$product->description ; | |||
} | |||
if($product->price) { | |||
$productDescription .= ' / '.Price::format($productModule->getPriceWithTax($product)) ; | |||
$productDescription .= ' ('. $productModule->getSolver()->strUnit($product, UnitDefinition::WORDING_UNIT).')' ; | |||
} | |||
$messageAutoText .= '- '.$productDescription.' | |||
' ; | |||
$messageAutoHtml .= '<li>'.Html::encode($productDescription).'</li>' ; | |||
} | |||
$messageAutoHtml .= '</ul>' ; | |||
} | |||
} | |||
} | |||
} | |||
if($fromProducer) { | |||
$producer = GlobalParam::getCurrentProducer() ; | |||
$fromEmail = $producerModule->getProducerEmailPlatform($producer) ; | |||
$fromName = $producer->name ; | |||
$linkProducer = 'https://'.$producer->slug.'.souke.fr'; | |||
$linkUnsubscribe = Yii::$app->urlManagerProducer->createAbsoluteUrl(['newsletter/unsubscribe', 'slug_producer' => $producer->slug]); | |||
// Message inscription newsletter | |||
$messageAutoText .= " | |||
-- | |||
Boutique : ".$linkProducer." | |||
Me désinscrire : ".$linkUnsubscribe; | |||
$messageAutoHtml .= "<br /><br />--<br>"; | |||
$messageAutoHtml .= "Boutique : <a href=\"".$linkProducer."\">".$linkProducer."</a><br>"; | |||
$messageAutoHtml .= "Me désinscrire : <a href=\"".$linkUnsubscribe."\">".$linkUnsubscribe."</a>"; | |||
} | |||
else { | |||
$fromEmail = 'contact@souke.fr' ; | |||
$fromName = 'Souke' ; | |||
} | |||
$body = ['Messages' => []] ; | |||
foreach($usersArray as $user) { | |||
$body['Messages'][] = [ | |||
'From' => [ | |||
'Email' => $fromEmail, | |||
'Name' => $fromName | |||
], | |||
'To' => [ | |||
[ | |||
'Email' => $user['email'], | |||
'Name' => $user['name'].' '.$user['lastname'] | |||
] | |||
], | |||
'Subject' => $this->subject, | |||
'TextPart' => $this->message.$messageAutoText, | |||
'HTMLPart' => nl2br($this->message).$messageAutoHtml | |||
] ; | |||
if(count($body['Messages']) == 50) { | |||
$response = $mj->post(\Mailjet\Resources::$Email, ['body' => $body]); | |||
$body['Messages'] = [] ; | |||
} | |||
} | |||
if(count($body['Messages']) > 0) { | |||
$response = $mj->post(\Mailjet\Resources::$Email, ['body' => $body]); | |||
} | |||
$success = (isset($response) && $response) ? $response->success() : false ; | |||
if(!$success) { | |||
Yii::error($response->getBody(), 'Mailjet'); | |||
} | |||
return $response ; | |||
} | |||
*/ | |||
} |
@@ -0,0 +1,91 @@ | |||
<?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\components\Date; | |||
use common\helpers\Dropdown; | |||
use lo\widgets\Toggle; | |||
use yii\widgets\ActiveForm; | |||
?> | |||
<div class="automatic-email-form"> | |||
<?php $form = ActiveForm::begin(); ?> | |||
<div class="col-md-8"> | |||
<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($automaticEmail, 'status')->widget(Toggle::class, | |||
[ | |||
'options' => [ | |||
'data-id' => $automaticEmail->id, | |||
'data-on' => 'Activé', | |||
'data-off' => 'Désactivé' | |||
], | |||
]); | |||
?> | |||
<?= $form->field($automaticEmail, 'day')->dropDownList(Date::getDaysOfWeekArray()) ?> | |||
<?= $form->field($automaticEmail, 'delay_before_distribution')->dropDownList(Dropdown::numberChoices(1, 7, false, ' jour(s) avant')); ?> | |||
<?= $form->field($automaticEmail, 'subject')->textInput() ?> | |||
<?= $form->field($automaticEmail, 'message')->widget(letyii\tinymce\Tinymce::class, [ | |||
'configs' => [ | |||
'plugins' => Yii::$app->parameterBag->get('tinyMcePlugins'), | |||
] | |||
]); ?> | |||
<?= $form->field($automaticEmail, 'integrate_product_list')->widget(Toggle::class, | |||
[ | |||
'options' => [ | |||
'data-id' => $automaticEmail->id, | |||
'data-on' => 'Oui', | |||
'data-off' => 'Non', | |||
'data-offstyle' => 'default', | |||
], | |||
]); | |||
?> | |||
</div> | |||
</div> | |||
</div> | |||
<?= $this->render('@backend/views/_include/form_actions.php',[ | |||
'model' => $automaticEmail, | |||
]); ?> | |||
<?php ActiveForm::end(); ?> | |||
</div> |
@@ -36,31 +36,14 @@ pris connaissance de la licence CeCILL, et que vous en avez accepté les | |||
termes. | |||
*/ | |||
$this->setTitle('Modifier une commande') ; | |||
$this->setTitle("Ajouter un email automatique") ; | |||
$this->addBreadcrumb(['label' => "Emails automatiques", 'url' => ['index']]) ; | |||
$this->addBreadcrumb('Ajouter') ; | |||
?> | |||
<div class="order-update"> | |||
<?php if($orderNotfound): ?> | |||
<div class="alert alert-danger">Cette commande est introuvable</div><br /> | |||
<a class="btn btn-default" href="<?php echo \Yii::$app->urlManager->createUrl(['order/index']); ?>">Retour</a> | |||
<?php else: ?> | |||
<?= $this->render('_form', [ | |||
'model' => $model, | |||
'pointsSaleArray' => $pointsSaleArray, | |||
'distributionDaysArray' => $distributionDaysArray, | |||
'productsArray' => $productsArray, | |||
'selectedProducts' => $selectedProducts, | |||
'availableProducts' => $availableProducts, | |||
'distribution' => $distribution, | |||
'ordersArray' => $ordersArray, | |||
'producersArray' => $producersArray, | |||
'idProducer' => $idProducer, | |||
'producer' => $producer, | |||
'credit' => $credit | |||
]) ?> | |||
<?php endif; ?> | |||
<div class="automatic-email-create"> | |||
<?= $this->render('_form', [ | |||
'automaticEmail' => $automaticEmail, | |||
]) ?> | |||
</div> |
@@ -0,0 +1,102 @@ | |||
<?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\components\View; | |||
use domain\Communication\AutomaticEmail\AutomaticEmail; | |||
use yii\helpers\Html; | |||
use yii\grid\GridView; | |||
/** | |||
* @var View $this | |||
*/ | |||
$this->setTitle('Emails automatiques'); | |||
$this->addBreadcrumb($this->getTitle()); | |||
$this->addButton(['label' => 'Nouvel email automatique <span class="glyphicon glyphicon-plus"></span>', 'url' => 'automatic-email/create', 'class' => 'btn btn-primary']); | |||
?> | |||
<div class="accessory-index"> | |||
<?= GridView::widget([ | |||
'dataProvider' => $dataProvider, | |||
'columns' => [ | |||
[ | |||
'attribute' => 'day', | |||
'value' => function(AutomaticEmail $automaticEmail) { | |||
return $automaticEmail->getDayAsString(); | |||
} | |||
], | |||
[ | |||
'attribute' => 'delay_before_distribution', | |||
'value' => function(AutomaticEmail $automaticEmail) { | |||
return $automaticEmail->getDelayBeforeDistributionAsString(); | |||
} | |||
], | |||
'subject', | |||
[ | |||
'attribute' => 'message', | |||
'format' => 'raw', | |||
], | |||
[ | |||
'attribute' => 'status', | |||
'format' => 'raw', | |||
'value' => function(AutomaticEmail $automaticEmail) { | |||
return $automaticEmail->getStatusAsHtml(); | |||
} | |||
], | |||
[ | |||
'class' => 'yii\grid\ActionColumn', | |||
'template' => '{update} {delete}', | |||
'headerOptions' => ['class' => 'column-actions'], | |||
'contentOptions' => ['class' => 'column-actions'], | |||
'buttons' => [ | |||
'update' => function ($url, $model) { | |||
return Html::a('<span class="glyphicon glyphicon-pencil"></span>', $url, [ | |||
'title' => 'Modifier', 'class' => 'btn btn-default' | |||
]); | |||
}, | |||
'delete' => function ($url, $model) { | |||
return Html::a('<span class="glyphicon glyphicon-trash"></span>', $url, [ | |||
'title' => 'Supprimer', 'class' => 'btn btn-default' | |||
]); | |||
} | |||
], | |||
], | |||
], | |||
]); ?> | |||
</div> |
@@ -36,24 +36,17 @@ pris connaissance de la licence CeCILL, et que vous en avez accepté les | |||
termes. | |||
*/ | |||
$this->setTitle('Passer une commande') ; | |||
use yii\helpers\Html; | |||
$this->setTitle("Modifier un email automatique") ; | |||
$this->addBreadcrumb(['label' => "Emails automatiques", 'url' => ['index']]) ; | |||
$this->addBreadcrumb(['label' => Html::encode($automaticEmail->getSubject()), 'url' => ['update', 'id' => $automaticEmail->getId()]]) ; | |||
$this->addBreadcrumb('Modifier') ; | |||
?> | |||
<div class="order-create"> | |||
<div class="automatic-email-update"> | |||
<?= $this->render('_form', [ | |||
'model' => $model, | |||
'pointsSaleArray' => $pointsSaleArray, | |||
'distributionDaysArray' => $distributionDaysArray, | |||
'productsArray' => $productsArray, | |||
'selectedProducts' => $selectedProducts, | |||
'availableProducts' => $availableProducts, | |||
'distribution' => $distribution, | |||
'ordersArray' => $ordersArray, | |||
'producersArray' => $producersArray, | |||
'idProducer' => $idProducer, | |||
'producer' => $producer, | |||
'credit' => $credit | |||
'automaticEmail' => $automaticEmail, | |||
]) ?> | |||
</div> |
@@ -40,7 +40,7 @@ use yii\helpers\Html ; | |||
use yii\widgets\ActiveForm; | |||
$this->setTitle('Envoyer un email') ; | |||
$this->addBreadcrumb(['label' => 'Communiquer', 'url' => ['user/index']]) ; | |||
$this->addBreadcrumb(['label' => 'Communication', 'url' => ['user/index']]) ; | |||
$this->addBreadcrumb($this->getTitle()) ; | |||
?> |
@@ -100,7 +100,9 @@ $this->render('@backend/views/user/_menu_filter.php',[ | |||
<br /><br /> | |||
<?php endif; ?> | |||
<?= implode(', ', $usersArray); ?> | |||
<?= implode(', ', array_map(function($user) { | |||
return $user['email']; | |||
}, $usersArray)); ?> | |||
</div> | |||
</div> | |||
</div> |
@@ -38,8 +38,8 @@ termes. | |||
use yii\helpers\Html ; | |||
$this->setTitle('Communiquer par papier') ; | |||
$this->addBreadcrumb('Communiquer') ; | |||
$this->setTitle('Communication par papier') ; | |||
$this->addBreadcrumb('Communication') ; | |||
?> | |||
@@ -66,10 +66,22 @@ $this->setMetaRefresh(true); | |||
) ?> | |||
</div> | |||
<div class="col-lg-6 col-xs-6"> | |||
<?php | |||
$colorBoxTicket = 'blue'; | |||
if($countTicketsAdminOpen == 0) { | |||
$colorBoxTicket = 'green'; | |||
} | |||
elseif($countTicketsAdminUnread == 0) { | |||
$colorBoxTicket = 'blue'; | |||
} | |||
elseif($countTicketsAdminUnread > 0) { | |||
$colorBoxTicket = 'red'; | |||
} | |||
?> | |||
<?= AdminLTE::smallBox( | |||
$countTicketsAdminOpen, | |||
'Tickets', | |||
$countTicketsAdminUnread ? 'red' : 'blue', | |||
$colorBoxTicket, | |||
'comments', | |||
Yii::$app->urlManager->createUrl('support-admin/index') | |||
) ?> |
@@ -37,6 +37,8 @@ termes. | |||
*/ | |||
use domain\Distribution\Distribution\DistributionModule; | |||
use domain\Feature\Feature\Feature; | |||
use domain\Feature\Feature\FeatureModule; | |||
use domain\Order\Order\Order; | |||
use domain\Order\Order\OrderModule; | |||
use domain\Producer\Producer\ProducerModule; | |||
@@ -52,12 +54,13 @@ $subscriptionModule = SubscriptionModule::getInstance(); | |||
$producerModule = ProducerModule::getInstance(); | |||
$settingModule = SettingModule::getInstance(); | |||
$adminSettingBag = $settingModule->getAdminSettingBag(); | |||
$featureChecker = FeatureModule::getInstance()->getChecker(); | |||
$this->setTitle('Tableau de bord'); | |||
?> | |||
<div class="dashboard-index"> | |||
<div <?php if($adminSettingBag->get('forumFlarumUrl')): ?>class="col-md-8"<?php endif; ?>> | |||
<div <?php if($featureChecker->isEnabled(Feature::ALIAS_FORUM)): ?>class="col-md-8"<?php endif; ?>> | |||
<?php if(Yii::$app->request->get('error_products_points_sale')): ?> | |||
<div class="alert alert-warning"> | |||
Vous devez ajouter <?php if(!$productsCount): ?> des produits<?php endif; ?> | |||
@@ -142,7 +145,7 @@ $this->setTitle('Tableau de bord'); | |||
<?php endif; ?> | |||
</div> | |||
<?php if($adminSettingBag->get('forumFlarumUrl')): ?> | |||
<?php if($featureChecker->isEnabled(Feature::ALIAS_FORUM)): ?> | |||
<div class="col-md-4"> | |||
<div class="panel panel-default"> | |||
<div class="panel-heading"> | |||
@@ -151,13 +154,11 @@ $this->setTitle('Tableau de bord'); | |||
</h3> | |||
</div> | |||
<div class="panel-body"> | |||
<?php //echo print_r($forumDiscussionsArray); ?> | |||
<?php if($forumDiscussionsArray && count($forumDiscussionsArray['data'])): ?> | |||
<table class="table"> | |||
<?php $forumDiscussionsLimitedArray = array_slice($forumDiscussionsArray['data'], 0, min(3, count($forumDiscussionsArray['data']))); ?> | |||
<?php foreach($forumDiscussionsLimitedArray as $forumDiscussion): ?> | |||
<tr> | |||
<?php //echo print_r($forumDiscussion); ?> | |||
<td><?= $forumDiscussion['attributes']['title']; ?></td> | |||
<td><?= date('d/m à H:i', strtotime($forumDiscussion['attributes']['lastPostedAt'])); ?></td> | |||
</tr> | |||
@@ -167,7 +168,7 @@ $this->setTitle('Tableau de bord'); | |||
<p>Aucun sujet sur le forum.</p> | |||
<?php endif; ?> | |||
<p> | |||
<a href="<?= $settingModule->getAdminSettingBag()->get('forumFlarumUrl'); ?>" class="btn btn-default"> | |||
<a href="<?= $settingModule->getAdminSettingBag()->get('forumFlarumUrl'); ?>" class="btn btn-default" target="_blank"> | |||
<span class="fa fa-comments"></span> | |||
Consulter le forum | |||
</a> |
@@ -130,6 +130,7 @@ $this->setPageTitle('Distributions') ; | |||
<td>Actif</td> | |||
<td>Nom</td> | |||
<td class="quantity-ordered">Commandé</td> | |||
<td class="quantity-remaining">Reste</td> | |||
<td class="quantity-max">Maximum</td> | |||
</tr> | |||
</thead> | |||
@@ -143,17 +144,26 @@ $this->setPageTitle('Distributions') ; | |||
</td> | |||
<td>{{ product.name }}</td> | |||
<td class="quantity-ordered"> | |||
<span v-if="isProductMaximumQuantityExceeded(product)" class="glyphicon glyphicon-alert"></span> | |||
{{ product.quantity_ordered ? product.quantity_ordered + ' '+ ((product.unit == 'piece') ? ' p.' : ' '+(product.unit == 'g' || product.unit == 'kg') ? 'kg' : 'litre(s)') : '∅' }} | |||
</td> | |||
<td class="quantity-remaining"> | |||
<span class="infinite" v-if="(getProductQuantityRemainingGlobal(product) === null)">∞</span> | |||
<span class="negative" v-else-if="getProductQuantityRemainingGlobal(product) <= 0"> | |||
{{ getProductQuantityRemainingGlobal(product) }} {{ product.unit == 'piece' ? ' p.' : ' '+(product.unit == 'g' || product.unit == 'kg') ? 'kg' : 'litre(s)' }} | |||
<span class="glyphicon glyphicon-alert" v-if="getProductQuantityRemainingGlobal(product) < 0"></span> | |||
</span> | |||
<span class="has-quantity" v-else>{{ getProductQuantityRemainingGlobal(product) }} {{ product.unit == 'piece' ? ' p.' : ' '+(product.unit == 'g' || product.unit == 'kg') ? 'kg' : 'litre(s)' }}</span> | |||
</td> | |||
<td class="quantity-max"> | |||
<div class="input-group"> | |||
<input type="text" class="form-control quantity-max" placeholder="∞" :data-id-product="product.id" v-model="getProductDistribution(product).quantity_max" @keyup="productQuantityMaxChange" /> | |||
<span class="input-group-addon">{{ (product.unit == 'piece') ? 'p.' : ' '+((product.unit == 'g' || product.unit == 'kg') ? 'kg' : 'litre(s)') }}</span> | |||
<span class="input-group-addon">{{ labelUnitReference(product.unit) }}</span> | |||
</div> | |||
<div v-if=" false && getProductDistribution(product).quantity_max != product.quantity_max"> | |||
Limitation accessoires : <br /> | |||
{{ product.quantity_max }} | |||
<div class="limit-quantity-accessories" v-if="!showLoading && product.quantity_max && (getProductDistribution(product).quantity_max > product.quantity_max || !getProductDistribution(product).quantity_max)"> | |||
<span class="glyphicon glyphicon-warning-sign"></span> Limitation accessoires : | |||
<strong>{{ product.quantity_max }} {{ labelUnitReference(product.unit) }}</strong> | |||
</div> | |||
</td> | |||
</tr> | |||
@@ -272,7 +282,7 @@ $this->setPageTitle('Distributions') ; | |||
:units="units" | |||
@close="closeModalOrderForm(true)" | |||
@ordercreatedupdated="orderCreatedUpdated" | |||
@updateproductorderprices="updateProductOrderPrices" | |||
@updateproductorders="updateProductOrders" | |||
></order-form> | |||
<div id="wrapper-nav-points-sale"> | |||
@@ -535,7 +545,7 @@ $this->setPageTitle('Distributions') ; | |||
:units="units" | |||
@close="closeModalOrderForm(false)" | |||
@ordercreatedupdated="orderCreatedUpdated" | |||
@updateproductorderprices="updateProductOrderPrices" | |||
@updateproductorders="updateProductOrders" | |||
@updateinvoiceprices="updateInvoicePrices" | |||
></order-form> | |||
@@ -709,7 +719,7 @@ $this->setPageTitle('Distributions') ; | |||
<tbody> | |||
<tr v-for="product in products" v-if="product.status >= 0 || order.productOrder[product.id].quantity > 0" :class="(order.productOrder[product.id].quantity > 0) ? 'product-ordered' : ''"> | |||
<td> | |||
<span class="label label-success" v-if="loadingUpdateProductOrder || order.productOrder[product.id].active">Actif</span> | |||
<span class="label label-success" v-if="order.productOrder[product.id].active">Actif</span> | |||
<span class="label label-danger" v-else>Inactif</span> | |||
</td> | |||
<td> | |||
@@ -745,12 +755,14 @@ $this->setPageTitle('Distributions') ; | |||
</span> | |||
</div> | |||
</td> | |||
<td class="quantity-remaining infinite" v-if="(getProductQuantityRemaining(product) === null) || order.productOrder[product.id].unit != product.unit">∞</td> | |||
<td class="quantity-remaining negative" v-else-if="getProductQuantityRemaining(product) <= 0"> | |||
{{ getProductQuantityRemaining(product) }} {{ order.productOrder[product.id].unit == 'piece' ? ' p.' : ' '+(order.productOrder[product.id].unit == 'g' || order.productOrder[product.id].unit == 'kg') ? 'kg' : 'litre(s)' }} | |||
<span class="glyphicon glyphicon-alert" v-if="getProductQuantityRemaining(product) < 0"></span> | |||
<td class="quantity-remaining"> | |||
<span class="infinite" v-if="(getProductQuantityRemaining(order, product) === null) || order.productOrder[product.id].unit != product.unit">∞</span> | |||
<span class="negative" v-else-if="getProductQuantityRemaining(order, product) <= 0"> | |||
{{ getProductQuantityRemaining(order, product) }} {{ labelUnitReference(order.productOrder[product.id].unit) }} | |||
<span class="glyphicon glyphicon-alert" v-if="getProductQuantityRemaining(order, product) < 0"></span> | |||
</span> | |||
<span class="has-quantity" v-else>{{ getProductQuantityRemaining(order, product) }} {{ labelUnitReference(order.productOrder[product.id].unit) }}</span> | |||
</td> | |||
<td class="quantity-remaining has-quantity" v-else>{{ getProductQuantityRemaining(product) }} {{ order.productOrder[product.id].unit == 'piece' ? ' p.' : ' '+(order.productOrder[product.id].unit == 'g' || order.productOrder[product.id].unit == 'kg') ? 'kg' : 'litre(s)' }}</td> | |||
</tr> | |||
</tbody> | |||
</table> | |||
@@ -764,7 +776,7 @@ $this->setPageTitle('Distributions') ; | |||
<button class="modal-default-button btn btn-primary" @click="submitFormCreate" v-if="!order.id">Créer</button> | |||
<div class="right"> | |||
<button class="modal-default-button btn btn-info btn-update-prices" @click="updateProductOrderPrices(true)">Recharger les prix</button> | |||
<button class="modal-default-button btn btn-info btn-update-prices" @click="updateProductOrders(true)">Recharger les prix</button> | |||
<button v-if="order.id" class="modal-default-button btn btn-info btn-update-prices" @click="updateInvoicePrices(true)"> | |||
Réinitialiser les prix facturés | |||
</button> |
@@ -117,7 +117,7 @@ $this->addButton(['label' => 'Nouvelle facture <span class="glyphicon glyphicon- | |||
} | |||
], | |||
[ | |||
'attribute' => 'paid', | |||
'attribute' => 'is_paid', | |||
'label' => 'Payée', | |||
'filter' => [ | |||
0 => 'Non', | |||
@@ -127,7 +127,7 @@ $this->addButton(['label' => 'Nouvelle facture <span class="glyphicon glyphicon- | |||
'headerOptions' => ['class' => 'column-hide-on-mobile'], | |||
'filterOptions' => ['class' => 'column-hide-on-mobile'], | |||
'contentOptions' => ['class' => 'column-hide-on-mobile'], | |||
'value' => function ($invoice) use ( $invoiceModule) { | |||
'value' => function ($invoice) use ($invoiceModule) { | |||
if($invoiceModule->isInvoicePaid($invoice)) { | |||
return '<span class="label label-success">Oui</span>'; | |||
} |
@@ -44,6 +44,8 @@ use common\helpers\GlobalParam; | |||
$producerModule = $this->getProducerModule(); | |||
$adminSettingBag = $this->getSettingModule()->getAdminSettingBag(); | |||
$sharedPointSaleModule = $this->getSharedPointSaleModule(); | |||
$userModule = $this->getUserModule(); | |||
$userCurrent = GlobalParam::getCurrentUser(); | |||
$producer = GlobalParam::getCurrentProducer(); | |||
?> | |||
@@ -93,7 +95,10 @@ $producer = GlobalParam::getCurrentProducer(); | |||
]) ?> | |||
<?php $producer = GlobalParam::getCurrentProducer(); ?> | |||
<?php if($producer && !$producerModule->isUpToDateWithOpendistribVersion($producer) && $producer->option_display_message_new_opendistrib_version): ?> | |||
<?php if($producer | |||
&& !$producerModule->isUpToDateWithOpendistribVersion($producer) | |||
&& $producer->option_display_message_new_opendistrib_version | |||
&& !$userModule->getAuthorizationChecker()->isGrantedAsAdministrator($userCurrent)): ?> | |||
<div class="alert alert-success"> | |||
<p> | |||
<i class="icon fa fa-cogs"></i> |
@@ -40,8 +40,11 @@ use common\helpers\GlobalParam; | |||
use common\helpers\Image; | |||
use common\helpers\Price; | |||
use domain\Distribution\Distribution\Distribution; | |||
use domain\Feature\Feature\Feature; | |||
use domain\Feature\Feature\FeatureModule; | |||
use domain\Producer\Producer\Producer; | |||
use domain\Producer\Producer\ProducerModule; | |||
use domain\Setting\AdminSettingBag; | |||
use domain\User\User\UserModule; | |||
use common\helpers\Environment; | |||
use yii\helpers\Html; | |||
@@ -50,6 +53,8 @@ $userModule = UserModule::getInstance(); | |||
$producerModule = ProducerModule::getInstance(); | |||
$producer = GlobalParam::getCurrentProducer(); | |||
$userCurrent = GlobalParam::getCurrentUser(); | |||
$featureChecker = FeatureModule::getInstance()->getChecker(); | |||
$adminSettingBag = AdminSettingBag::getInstance(); | |||
?> | |||
@@ -309,7 +314,6 @@ $userCurrent = GlobalParam::getCurrentUser(); | |||
</a> | |||
</li> | |||
<?php if ($userModule->getAuthorizationChecker()->isGrantedAsProducer($userCurrent)): ?> | |||
<li> | |||
<a href="<?= Yii::$app->urlManagerProducer->createAbsoluteUrl(['site/index', 'slug_producer' => GlobalParam::getCurrentProducer()->slug]); ?>"> | |||
@@ -319,6 +323,15 @@ $userCurrent = GlobalParam::getCurrentUser(); | |||
</li> | |||
<?php endif; ?> | |||
<?php if($featureChecker->isEnabled(Feature::ALIAS_FORUM)): ?> | |||
<li> | |||
<a href="<?= $adminSettingBag->get('forumFlarumUrl') ?>" target="_blank"> | |||
<i class="bi bi-people"></i> | |||
<span class="hidden-xs hidden-sm">Forum</span> | |||
</a> | |||
</li> | |||
<?php endif; ?> | |||
<li class="dropdown user user-menu"> | |||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"> | |||
<i class="bi bi-person"></i> |
@@ -245,12 +245,13 @@ $isUserCurrentGrantedAsProducer = $userModule->getAuthorizationChecker()->isGran | |||
], | |||
['label' => 'Abonnements', 'icon' => 'repeat', 'url' => ['/subscription/index'], 'visible' => $isUserCurrentGrantedAsProducer, 'active' => Yii::$app->controller->id == 'subscription'], | |||
[ | |||
'label' => 'Communiquer', | |||
'label' => 'Communication', | |||
'icon' => 'bullhorn', | |||
'url' => ['/communicate/email'], | |||
'visible' => $isUserCurrentGrantedAsProducer, | |||
'items' => [ | |||
['label' => 'Email', 'icon' => 'paper-plane', 'url' => ['/communicate/email'], 'visible' => $isUserCurrentGrantedAsProducer], | |||
['label' => 'Email automatique', 'icon' => 'paper-plane-o', 'url' => ['/automatic-email/index'], 'visible' => $isUserCurrentGrantedAsProducer && $featureChecker->isEnabled(Feature::ALIAS_AUTOMATIC_EMAIL)], | |||
['label' => 'Papier', 'icon' => 'print', 'url' => ['/communicate/paper'], 'visible' => $isUserCurrentGrantedAsProducer], | |||
] | |||
], | |||
@@ -272,6 +273,7 @@ $isUserCurrentGrantedAsProducer = $userModule->getAuthorizationChecker()->isGran | |||
['label' => 'Chiffre d\'affaire', 'icon' => 'line-chart', 'url' => ['/stats/index'], 'visible' => $isUserCurrentGrantedAsProducer], | |||
['label' => 'Rapports', 'icon' => 'pencil-square-o', 'url' => ['/report/index'], 'visible' => $isUserCurrentGrantedAsProducer], | |||
['label' => 'Produits', 'icon' => 'table', 'url' => ['/stats/products'], 'visible' => $isUserCurrentGrantedAsProducer], | |||
['label' => 'Export paiements', 'icon' => 'euro', 'url' => ['/report/payments'], 'visible' => $isUserCurrentGrantedAsProducer], | |||
], | |||
], | |||
['label' => 'Paramètres', 'icon' => 'cog', 'url' => ['/producer/update'], 'visible' => $isUserCurrentGrantedAsProducer], |
@@ -159,9 +159,7 @@ $this->addButton(['label' => 'Nouveau producteur <span class="glyphicon glyphico | |||
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>'; | |||
} | |||
$str .= '<p><i class="fa fa-gift"></i> Créer un avoir pour le parrain <strong>'.Html::encode($producer->getSponsoredBy()->getName()).'</strong> sur Dolibarr.</p>'; | |||
} | |||
return $str; |
@@ -0,0 +1,70 @@ | |||
<?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 yii\helpers\Html; | |||
use yii\widgets\ActiveForm; | |||
$this->setTitle('Paiements') ; | |||
$this->addBreadcrumb('Paiements') ; | |||
?> | |||
<div class="row"> | |||
<div class="col-md-8"> | |||
<div class="panel panel-default"> | |||
<div class="panel-heading"> | |||
<h3 class="panel-title"> | |||
<i class="fa fa-download"></i> | |||
Export CSV des paiements | |||
</h3> | |||
</div> | |||
<div class="panel-body"> | |||
<?php $form = ActiveForm::begin(); ?> | |||
<?= $form->field($reportPaymentsForm, 'date_start')->textInput([ | |||
'class' => 'datepicker form-control' | |||
]) ?> | |||
<?= $form->field($reportPaymentsForm, 'date_end')->textInput([ | |||
'class' => 'datepicker form-control' | |||
]) ?> | |||
<?= Html::submitButton('Exporter', ['class' => 'btn btn-primary']) ?> | |||
<?php ActiveForm::end(); ?> | |||
</div> | |||
</div> | |||
</div> | |||
</div> |
@@ -90,8 +90,8 @@ $sponsorshipLink = $producerModule->getSolver()->getSponsorshipLink($producer); | |||
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. | |||
Pendant les 3 mois offerts de votre filleul, vous obtenez chaque début de mois <strong>une réduction</strong> | |||
équivalente à <a href="<?= Yii::$app->urlManager->createUrl(['producer/billing']) ?>">la tranche dans laquelle il se situe</a>. | |||
</li> | |||
</ul> | |||
</div> |
@@ -120,7 +120,7 @@ $supportOnline = $adminSettingBag->get('supportOnline'); | |||
<div class="info-box"> | |||
<span class="info-box-icon bg-yellow"><i class="fa fa-users"></i></span> | |||
<div class="info-box-content"> | |||
<span class="info-box-text"><br/><?= Html::a("Ouvrir une discussion sur le forum", $adminSettingBag->get('forumFlarumUrl'), ['class' => 'btn btn-sm btn-default', 'target' => '_blank']); ?></span> | |||
<span class="info-box-text"><br/><?= Html::a("Aller sur le forum", $adminSettingBag->get('forumFlarumUrl'), ['class' => 'btn btn-sm btn-default', 'target' => '_blank']); ?></span> | |||
</div> | |||
</div> | |||
</div> |
@@ -104,6 +104,17 @@ $this->addBreadcrumb($this->getTitle()); | |||
return $html; | |||
} | |||
], | |||
[ | |||
'attribute' => 'note_emails', | |||
'format' => 'raw', | |||
'value' => function($user) { | |||
if($user->note_emails) { | |||
return nl2br($user->note_emails); | |||
} | |||
return ''; | |||
} | |||
], | |||
[ | |||
'attribute' => 'producers', | |||
'label' => 'Profils', |
@@ -111,18 +111,35 @@ $distributionModule = DistributionModule::getInstance(); | |||
</h3> | |||
</div> | |||
<div class="panel-body"> | |||
<?= $form->field($model, 'points_sale')->checkboxlist( | |||
ArrayHelper::map($pointsSaleArray, 'id', function ($pointSale) use ($model) { | |||
$commentUserPointSale = isset($pointSale->userPointSale[0]) ? $pointSale->userPointSale[0]->comment : ''; | |||
$html = Html::encode($pointSale->name); | |||
if ($pointSale->restricted_access) { | |||
$html .= '<input type="text" placeholder="Commentaire" class="form-control" name="User[comment_point_sale_' . $pointSale->id . ']" value="' . (($model->id) ? Html::encode($commentUserPointSale) : '') . '" />'; | |||
} | |||
return $html; | |||
}), [ | |||
'encode' => false | |||
]); | |||
?> | |||
<div class="row"> | |||
<div class="col-md-8"> | |||
<?= $form->field($model, 'points_sale')->checkboxlist( | |||
ArrayHelper::map($pointsSaleArray, 'id', function ($pointSale) use ($model) { | |||
$commentUserPointSale = isset($pointSale->userPointSale[0]) ? $pointSale->userPointSale[0]->comment : ''; | |||
$html = Html::encode($pointSale->name); | |||
if ($pointSale->restricted_access) { | |||
$html .= '<input type="text" placeholder="Commentaire" class="form-control" name="User[comment_point_sale_' . $pointSale->id . ']" value="' . (($model->id) ? Html::encode($commentUserPointSale) : '') . '" />'; | |||
} | |||
return $html; | |||
}), [ | |||
'encode' => false | |||
]); | |||
?> | |||
</div> | |||
<div class="col-md-4"> | |||
<?= $form->field($model, 'exclusive_access_selected_points_sale')->widget(Toggle::class, | |||
[ | |||
'options' => [ | |||
'data-id' => $model->id, | |||
'data-on' => 'Oui', | |||
'data-off' => 'Non', | |||
'data-onstyle' => 'success', | |||
'data-offstyle' => 'default', | |||
], | |||
] | |||
); ?> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<?php endif; ?> |
@@ -114,7 +114,9 @@ $this->addBreadcrumb('Cagnotte') ; | |||
</h3> | |||
</div> | |||
<div class="panel-body"> | |||
<?php $form = ActiveForm::begin(); ?> | |||
<?php $form = ActiveForm::begin([ | |||
'enableClientValidation' => false | |||
]); ?> | |||
<div class="row"> | |||
<div class="col-md-6"> | |||
<?= $form->field($creditForm, 'type')->dropDownList([ |
@@ -2400,111 +2400,136 @@ termes. | |||
.distribution-index #calendar .vc-day.is-not-in-month .vc-highlights * { | |||
opacity: 1 !important; | |||
} | |||
/* line 97, ../sass/distribution/_index.scss */ | |||
/* line 96, ../sass/distribution/_index.scss */ | |||
.distribution-index table td.quantity-remaining { | |||
text-align: right; | |||
} | |||
/* line 99, ../sass/distribution/_index.scss */ | |||
.distribution-index table td.quantity-remaining .has-quantity, .distribution-index table td.quantity-remaining .infinite { | |||
color: #00A65A; | |||
} | |||
/* line 103, ../sass/distribution/_index.scss */ | |||
.distribution-index table td.quantity-remaining .negative { | |||
color: #DD4B39; | |||
} | |||
/* line 107, ../sass/distribution/_index.scss */ | |||
.distribution-index table td.quantity-remaining .infinite, .distribution-index table td.quantity-remaining .empty { | |||
font-size: 18px; | |||
} | |||
/* line 113, ../sass/distribution/_index.scss */ | |||
.distribution-index #products td.quantities { | |||
width: 100px; | |||
text-align: right; | |||
} | |||
/* line 102, ../sass/distribution/_index.scss */ | |||
/* line 118, ../sass/distribution/_index.scss */ | |||
.distribution-index #products input.quantity-max { | |||
width: 50px; | |||
text-align: center; | |||
display: inline; | |||
} | |||
/* line 110, ../sass/distribution/_index.scss */ | |||
/* line 126, ../sass/distribution/_index.scss */ | |||
.distribution-index #infos-top .col-md-4 { | |||
padding: 0px; | |||
} | |||
/* line 116, ../sass/distribution/_index.scss */ | |||
/* line 132, ../sass/distribution/_index.scss */ | |||
.distribution-index #infos-top .info-box { | |||
min-height: 96px; | |||
height: 96px; | |||
} | |||
/* line 120, ../sass/distribution/_index.scss */ | |||
/* line 136, ../sass/distribution/_index.scss */ | |||
.distribution-index #infos-top .info-box .info-box-icon { | |||
height: 96px; | |||
width: 50px; | |||
line-height: 96px; | |||
} | |||
/* line 125, ../sass/distribution/_index.scss */ | |||
/* line 141, ../sass/distribution/_index.scss */ | |||
.distribution-index #infos-top .info-box .info-box-icon i.fa { | |||
font-size: 30px; | |||
} | |||
/* line 130, ../sass/distribution/_index.scss */ | |||
/* line 146, ../sass/distribution/_index.scss */ | |||
.distribution-index #infos-top .info-box .info-box-content { | |||
margin-left: 55px; | |||
} | |||
/* line 133, ../sass/distribution/_index.scss */ | |||
/* line 149, ../sass/distribution/_index.scss */ | |||
.distribution-index #infos-top .info-box .info-box-content .info-box-text { | |||
font-size: 12px; | |||
} | |||
/* line 136, ../sass/distribution/_index.scss */ | |||
/* line 152, ../sass/distribution/_index.scss */ | |||
.distribution-index #infos-top .info-box .info-box-content .info-box-text .btn { | |||
font-size: 12px; | |||
text-transform: uppercase; | |||
} | |||
/* line 142, ../sass/distribution/_index.scss */ | |||
/* line 158, ../sass/distribution/_index.scss */ | |||
.distribution-index #infos-top .info-box .info-box-content .info-box-number { | |||
font-size: 14px; | |||
} | |||
/* line 149, ../sass/distribution/_index.scss */ | |||
/* line 165, ../sass/distribution/_index.scss */ | |||
.distribution-index #infos-top #info-box-distribution .btn-active-week { | |||
float: right; | |||
} | |||
/* line 155, ../sass/distribution/_index.scss */ | |||
/* line 171, ../sass/distribution/_index.scss */ | |||
.distribution-index #infos-top #summary-ca-weight .normal { | |||
font-weight: normal; | |||
} | |||
/* line 165, ../sass/distribution/_index.scss */ | |||
/* line 181, ../sass/distribution/_index.scss */ | |||
.distribution-index #modal-products table.table thead tr td { | |||
font-weight: bold; | |||
} | |||
/* line 171, ../sass/distribution/_index.scss */ | |||
/* line 187, ../sass/distribution/_index.scss */ | |||
.distribution-index #modal-products table.table td.quantity-ordered, | |||
.distribution-index #modal-products table.table td.quantity-max { | |||
text-align: center; | |||
} | |||
/* line 176, ../sass/distribution/_index.scss */ | |||
/* line 192, ../sass/distribution/_index.scss */ | |||
.distribution-index #modal-products table.table td.quantity-ordered { | |||
width: 50px; | |||
} | |||
/* line 180, ../sass/distribution/_index.scss */ | |||
/* line 196, ../sass/distribution/_index.scss */ | |||
.distribution-index #modal-products table.table td.quantity-max { | |||
width: 120px; | |||
} | |||
/* line 183, ../sass/distribution/_index.scss */ | |||
/* line 199, ../sass/distribution/_index.scss */ | |||
.distribution-index #modal-products table.table td.quantity-max input { | |||
text-align: center; | |||
min-width: 50px; | |||
} | |||
/* line 191, ../sass/distribution/_index.scss */ | |||
/* line 204, ../sass/distribution/_index.scss */ | |||
.distribution-index #modal-products table.table td.quantity-max .limit-quantity-accessories { | |||
margin-top: 7px; | |||
font-size: 12px; | |||
} | |||
/* line 208, ../sass/distribution/_index.scss */ | |||
.distribution-index #modal-products table.table td.quantity-max .limit-quantity-accessories .quantity { | |||
font-weight: bold; | |||
} | |||
/* line 216, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders { | |||
position: relative; | |||
} | |||
/* line 196, ../sass/distribution/_index.scss */ | |||
/* line 221, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders .panel-heading .buttons .btn { | |||
position: relative; | |||
top: -19px; | |||
float: right; | |||
margin-left: 10px; | |||
} | |||
/* line 205, ../sass/distribution/_index.scss */ | |||
/* line 230, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders #wrapper-nav-points-sale { | |||
margin-bottom: 10px; | |||
} | |||
/* line 208, ../sass/distribution/_index.scss */ | |||
/* line 233, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders #wrapper-nav-points-sale ul#nav-points-sale { | |||
margin: 0px; | |||
padding: 0px; | |||
list-style-type: none; | |||
} | |||
/* line 213, ../sass/distribution/_index.scss */ | |||
/* line 238, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders #wrapper-nav-points-sale ul#nav-points-sale li { | |||
float: left; | |||
margin-right: 10px; | |||
margin-bottom: 10px; | |||
} | |||
/* line 219, ../sass/distribution/_index.scss */ | |||
/* line 244, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders #wrapper-nav-points-sale ul#nav-points-sale li a .label { | |||
background-color: white; | |||
border: solid 1px #e0e0e0; | |||
@@ -2512,7 +2537,7 @@ termes. | |||
-webkit-border-radius: 10px; | |||
border-radius: 10px; | |||
} | |||
/* line 230, ../sass/distribution/_index.scss */ | |||
/* line 255, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders #buttons-top-orders { | |||
background-color: #F5F5F5; | |||
padding: 10px 20px; | |||
@@ -2522,15 +2547,15 @@ termes. | |||
border-radius: 5px; | |||
margin-bottom: 20px; | |||
} | |||
/* line 241, ../sass/distribution/_index.scss */ | |||
/* line 266, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders #buttons-top-orders .right { | |||
float: right; | |||
} | |||
/* line 245, ../sass/distribution/_index.scss */ | |||
/* line 270, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders #buttons-top-orders .dropdown { | |||
display: inline-block; | |||
} | |||
/* line 250, ../sass/distribution/_index.scss */ | |||
/* line 275, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders .point-sale-totals { | |||
background-color: white; | |||
padding: 10px 20px; | |||
@@ -2540,30 +2565,30 @@ termes. | |||
border-radius: 5px; | |||
margin-bottom: 20px; | |||
} | |||
/* line 257, ../sass/distribution/_index.scss */ | |||
/* line 282, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders .point-sale-totals .title { | |||
color: gray; | |||
font-size: 13px; | |||
margin-right: 13px; | |||
text-transform: uppercase; | |||
} | |||
/* line 267, ../sass/distribution/_index.scss */ | |||
/* line 292, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.column-user { | |||
position: relative; | |||
} | |||
/* line 270, ../sass/distribution/_index.scss */ | |||
/* line 295, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.column-user:hover .shortcuts { | |||
display: block; | |||
} | |||
/* line 274, ../sass/distribution/_index.scss */ | |||
/* line 299, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.column-user a { | |||
color: #333; | |||
} | |||
/* line 277, ../sass/distribution/_index.scss */ | |||
/* line 302, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.column-user a:hover { | |||
text-decoration: underline; | |||
} | |||
/* line 282, ../sass/distribution/_index.scss */ | |||
/* line 307, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.column-user .shortcuts { | |||
display: none; | |||
float: right; | |||
@@ -2571,19 +2596,19 @@ termes. | |||
top: 1px; | |||
right: 1px; | |||
} | |||
/* line 290, ../sass/distribution/_index.scss */ | |||
/* line 315, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.column-user .user-trust-alert { | |||
color: red; | |||
} | |||
/* line 295, ../sass/distribution/_index.scss */ | |||
/* line 320, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.column-delivery-note { | |||
position: relative; | |||
} | |||
/* line 299, ../sass/distribution/_index.scss */ | |||
/* line 324, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.tiller { | |||
width: 60px; | |||
} | |||
/* line 302, ../sass/distribution/_index.scss */ | |||
/* line 327, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.tiller label { | |||
font-size: 12px; | |||
cursor: pointer; | |||
@@ -2591,88 +2616,88 @@ termes. | |||
top: -2px; | |||
font-weight: normal; | |||
} | |||
/* line 311, ../sass/distribution/_index.scss */ | |||
/* line 336, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.column-actions { | |||
position: relative; | |||
text-align: right; | |||
} | |||
/* line 315, ../sass/distribution/_index.scss */ | |||
/* line 340, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.column-actions .wrapper-button-dropdown { | |||
display: inline-block; | |||
} | |||
/* line 319, ../sass/distribution/_index.scss */ | |||
/* line 344, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.column-actions .dropdown-menu { | |||
left: -70px; | |||
width: 227px; | |||
} | |||
/* line 324, ../sass/distribution/_index.scss */ | |||
/* line 349, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.column-actions .modal-form-order, | |||
.distribution-index #orders table td.column-actions .modal-payment { | |||
text-align: left; | |||
} | |||
/* line 329, ../sass/distribution/_index.scss */ | |||
/* line 354, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.column-actions .add-subscription { | |||
position: relative; | |||
} | |||
/* line 332, ../sass/distribution/_index.scss */ | |||
/* line 357, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.column-actions .add-subscription .glyphicon-plus { | |||
position: absolute; | |||
top: 4px; | |||
right: 4px; | |||
font-size: 7px; | |||
} | |||
/* line 341, ../sass/distribution/_index.scss */ | |||
/* line 366, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.column-state-payment { | |||
width: 133px; | |||
} | |||
/* line 347, ../sass/distribution/_index.scss */ | |||
/* line 372, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.column-credit a.positive { | |||
color: green; | |||
} | |||
/* line 350, ../sass/distribution/_index.scss */ | |||
/* line 375, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.column-credit a.negative { | |||
color: red; | |||
} | |||
/* line 356, ../sass/distribution/_index.scss */ | |||
/* line 381, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table .state-payment-mobile { | |||
display: none; | |||
} | |||
/* line 360, ../sass/distribution/_index.scss */ | |||
/* line 385, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.column-payment { | |||
position: relative; | |||
} | |||
/* line 363, ../sass/distribution/_index.scss */ | |||
/* line 388, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table td.column-payment div.btn-group { | |||
width: 125px; | |||
} | |||
/* line 369, ../sass/distribution/_index.scss */ | |||
/* line 394, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table tr.view ul { | |||
list-style-type: none; | |||
margin-left: 0px; | |||
padding-left: 15px; | |||
} | |||
/* line 379, ../sass/distribution/_index.scss */ | |||
/* line 404, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table tr.view .comment { | |||
margin-top: 20px; | |||
} | |||
/* line 383, ../sass/distribution/_index.scss */ | |||
/* line 408, ../sass/distribution/_index.scss */ | |||
.distribution-index #orders table tr.view .delivery { | |||
margin-top: 20px; | |||
} | |||
/* line 392, ../sass/distribution/_index.scss */ | |||
/* line 417, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order .modal-container { | |||
width: 100%; | |||
padding: 0px; | |||
} | |||
/* line 396, ../sass/distribution/_index.scss */ | |||
/* line 421, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order .modal-container .modal-body { | |||
padding-right: 15px; | |||
} | |||
/* line 399, ../sass/distribution/_index.scss */ | |||
/* line 424, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order .modal-container .modal-body table { | |||
margin-bottom: 150px; | |||
} | |||
/* line 404, ../sass/distribution/_index.scss */ | |||
/* line 429, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order .modal-container .modal-footer { | |||
border-top-color: #f4f4f4; | |||
position: fixed; | |||
@@ -2684,64 +2709,64 @@ termes. | |||
text-align: center; | |||
border-top: solid 1px #e0e0e0; | |||
} | |||
/* line 416, ../sass/distribution/_index.scss */ | |||
/* line 441, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order .modal-container .modal-footer .actions-form button { | |||
float: none; | |||
} | |||
/* line 420, ../sass/distribution/_index.scss */ | |||
/* line 445, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order .modal-container .modal-footer .actions-form div.right { | |||
float: right; | |||
} | |||
/* line 427, ../sass/distribution/_index.scss */ | |||
/* line 452, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order .btn-credit { | |||
float: right; | |||
} | |||
/* line 433, ../sass/distribution/_index.scss */ | |||
/* line 458, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order table.table-products .product-ordered td { | |||
background-color: #e9e9e9; | |||
} | |||
/* line 437, ../sass/distribution/_index.scss */ | |||
/* line 462, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order table.table-products .product-ordered input.input-quantity { | |||
font-size: 16px; | |||
font-weight: bold; | |||
} | |||
/* line 443, ../sass/distribution/_index.scss */ | |||
/* line 468, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order table.table-products td.price { | |||
width: 150px; | |||
} | |||
/* line 446, ../sass/distribution/_index.scss */ | |||
/* line 471, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order table.table-products td.price input { | |||
text-align: center; | |||
} | |||
/* line 450, ../sass/distribution/_index.scss */ | |||
/* line 475, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order table.table-products td.price .input-group-addon { | |||
background-color: #eee; | |||
} | |||
/* line 454, ../sass/distribution/_index.scss */ | |||
/* line 479, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order table.table-products td.price .invoice-price { | |||
margin-top: 8px; | |||
} | |||
/* line 456, ../sass/distribution/_index.scss */ | |||
/* line 481, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order table.table-products td.price .invoice-price .label-invoice-price { | |||
font-size: 11px; | |||
font-weight: bold; | |||
color: gray; | |||
} | |||
/* line 464, ../sass/distribution/_index.scss */ | |||
/* line 489, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order table.table-products td.quantity { | |||
width: 165px; | |||
} | |||
/* line 467, ../sass/distribution/_index.scss */ | |||
/* line 492, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order table.table-products td.quantity input { | |||
text-align: center; | |||
color: black; | |||
} | |||
/* line 472, ../sass/distribution/_index.scss */ | |||
/* line 497, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order table.table-products td.quantity .form-control { | |||
border-right: 0px none; | |||
padding-right: 4px; | |||
} | |||
/* line 477, ../sass/distribution/_index.scss */ | |||
/* line 502, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order table.table-products td.quantity .input-group-addon { | |||
padding: 5px; | |||
padding-left: 0px; | |||
@@ -2749,35 +2774,19 @@ termes. | |||
border-left: 0px none; | |||
border-right: 0px none; | |||
} | |||
/* line 486, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order table.table-products td.quantity-remaining { | |||
text-align: right; | |||
} | |||
/* line 489, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order table.table-products td.quantity-remaining.quantity-remaining, .distribution-index .modal-form-order table.table-products td.quantity-remaining.infinite { | |||
color: #00A65A; | |||
} | |||
/* line 493, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order table.table-products td.quantity-remaining.negative { | |||
color: #DD4B39; | |||
} | |||
/* line 497, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order table.table-products td.quantity-remaining.infinite, .distribution-index .modal-form-order table.table-products td.quantity-remaining.empty { | |||
font-size: 18px; | |||
} | |||
/* line 504, ../sass/distribution/_index.scss */ | |||
/* line 513, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-form-order .actions-form button { | |||
margin-left: 15px; | |||
} | |||
/* line 512, ../sass/distribution/_index.scss */ | |||
/* line 521, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-payment .info-box .info-box-icon { | |||
width: 50px; | |||
} | |||
/* line 515, ../sass/distribution/_index.scss */ | |||
/* line 524, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-payment .info-box .info-box-icon i { | |||
font-size: 30px; | |||
} | |||
/* line 520, ../sass/distribution/_index.scss */ | |||
/* line 529, ../sass/distribution/_index.scss */ | |||
.distribution-index .modal-payment .info-box .info-box-content { | |||
margin-left: 50px; | |||
} | |||
@@ -2802,12 +2811,17 @@ termes. | |||
.user-update .panel-point-sales label.control-label { | |||
display: none; | |||
} | |||
/* line 18, ../sass/user/_form.scss */ | |||
/* line 16, ../sass/user/_form.scss */ | |||
.user-create .panel-point-sales .field-user-exclusive_access_selected_points_sale label.control-label, | |||
.user-update .panel-point-sales .field-user-exclusive_access_selected_points_sale label.control-label { | |||
display: block; | |||
} | |||
/* line 22, ../sass/user/_form.scss */ | |||
.user-create .panel-user-groups label.control-label, | |||
.user-update .panel-user-groups label.control-label { | |||
display: none; | |||
} | |||
/* line 25, ../sass/user/_form.scss */ | |||
/* line 29, ../sass/user/_form.scss */ | |||
.user-create #user-points_sale label, | |||
.user-create #user-user_groups label, | |||
.user-update #user-points_sale label, | |||
@@ -2819,7 +2833,7 @@ termes. | |||
padding: 5px 10px; | |||
border: solid 1px #e0e0e0; | |||
} | |||
/* line 33, ../sass/user/_form.scss */ | |||
/* line 37, ../sass/user/_form.scss */ | |||
.user-create #user-points_sale label .form-control, | |||
.user-create #user-user_groups label .form-control, | |||
.user-update #user-points_sale label .form-control, | |||
@@ -3321,6 +3335,11 @@ termes. | |||
margin-top: 0px; | |||
} | |||
/* line 4, ../sass/automatic-email/_form.scss */ | |||
.automatic-email-form form label { | |||
display: block; | |||
} | |||
/** | |||
Copyright Souke (2018) | |||
@@ -37,7 +37,6 @@ | |||
$(document).ready(function () { | |||
opendistrib_datepicker(); | |||
opendistrib_popover(); | |||
opendistrib_commandeauto(); | |||
opendistrib_points_vente_acces(); | |||
opendistrib_tooltip(); | |||
opendistrib_ordre_produits(); | |||
@@ -54,6 +53,7 @@ $(document).ready(function () { | |||
opendistrib_gridview_pagesize(); | |||
opendistrib_producers_admin(); | |||
opendistrib_user_form(); | |||
opendistrib_user_credit(); | |||
opendistrib_features_index(); | |||
opendistrib_point_sale_form(); | |||
opendistrib_check_all_checkboxes(); | |||
@@ -62,6 +62,18 @@ $(document).ready(function () { | |||
opendistrib_sponsorship(); | |||
}); | |||
function label_unit_reference(unit) { | |||
if(unit == 'piece') { | |||
return 'p.'; | |||
} | |||
else if(unit == 'g' || unit == 'kg') { | |||
return 'kg'; | |||
} | |||
else { | |||
return 'litre(s)'; | |||
} | |||
} | |||
function opendistrib_sponsorship() { | |||
$('#sponsorship-link-copy').click(function() { | |||
navigator.clipboard.writeText($(this).attr('href')); | |||
@@ -173,6 +185,12 @@ function opendistrib_user_form_event($fieldUserEmail, $fieldSendMailWelcome) { | |||
} | |||
} | |||
function opendistrib_user_credit() { | |||
if($('body.user-credit').length) { | |||
$('#creditform-amount').focus(); | |||
} | |||
} | |||
function opendistrib_producers_admin() { | |||
$('.producer-admin-index .btn-alwaysdata, .producer-admin-index .btn-dolibarr').click(function () { | |||
var $button = $(this); | |||
@@ -479,10 +497,6 @@ function opendistrib_points_vente_acces_event() { | |||
} | |||
} | |||
function opendistrib_commandeauto() { | |||
$('#subscriptionform-date_begin, #subscriptionform-date_end').datepicker(); | |||
} | |||
function opendistrib_sortable_list(element_selector, button_selector, route_ajax) { | |||
var fixHelper = function (e, ui) { | |||
ui.children().each(function () { | |||
@@ -567,6 +581,7 @@ function opendistrib_features_index() { | |||
} | |||
function opendistrib_datepicker() { | |||
$('#subscriptionform-date_begin, #subscriptionform-date_end').datepicker(); | |||
$('input.datepicker').datepicker({dateFormat: 'dd/mm/yy'}); | |||
} | |||
@@ -294,6 +294,21 @@ if($(selector).length) { | |||
} | |||
}); | |||
}, | |||
labelUnitReference: function(unit) { | |||
return label_unit_reference(unit); | |||
}, | |||
getProductQuantityRemainingGlobal: function(product) { | |||
var app = this; | |||
var productQuantityRemaining = product.quantity_remaining; | |||
// format | |||
if (productQuantityRemaining && product.unit != 'piece') { | |||
productQuantityRemaining = productQuantityRemaining.toFixed(2); | |||
} | |||
return productQuantityRemaining; | |||
}, | |||
initCountActiveProducts: function () { | |||
this.countActiveProducts = 0; | |||
for (var i = 0; i < this.products.length; i++) { | |||
@@ -334,6 +349,7 @@ if($(selector).length) { | |||
this.init(); | |||
}, | |||
productQuantityMaxChange: function (event) { | |||
var app = this; | |||
var quantityMax = event.currentTarget.value; | |||
axios.get("ajax-process-product-quantity-max", { | |||
params: { | |||
@@ -343,7 +359,7 @@ if($(selector).length) { | |||
} | |||
}) | |||
.then(function (response) { | |||
app.init(app.idActivePointSale); | |||
}); | |||
}, | |||
productActiveClick: function (event) { | |||
@@ -377,11 +393,8 @@ if($(selector).length) { | |||
return false; | |||
}, | |||
isProductMaximumQuantityExceeded: function (product) { | |||
return | |||
this.getProductDistribution(product) | |||
&& this.getProductDistribution(product).quantity_max | |||
&& product.quantity_ordered | |||
&& product.quantity_ordered > this.getProductDistribution(product).quantity_max; | |||
return product.quantity_remaining | |||
&& product.quantity_remaining < 0; | |||
}, | |||
pointSaleActiveClick: function (event) { | |||
var idPointSale = event.currentTarget.getAttribute('data-id-point-sale'); | |||
@@ -493,19 +506,19 @@ if($(selector).length) { | |||
}, | |||
updateOrderFromUrl: function () { | |||
this.initModalFormOrder(); | |||
this.updateProductOrderPrices(false); | |||
this.updateProductOrders(false); | |||
}, | |||
updateOrderClick: function (event) { | |||
var idOrder = event.currentTarget.getAttribute('data-id-order'); | |||
this.idOrderUpdate = idOrder; | |||
this.showModalFormOrderUpdate = true; | |||
this.initModalFormOrder(); | |||
this.updateProductOrderPrices(false); | |||
this.updateProductOrders(false); | |||
}, | |||
openModalFormOrderCreate: function () { | |||
this.showModalFormOrderCreate = true; | |||
this.initModalFormOrder(); | |||
this.updateProductOrderPrices(false); | |||
this.updateProductOrders(false); | |||
}, | |||
initModalFormOrder: function () { | |||
var app = this; | |||
@@ -829,8 +842,19 @@ if($(selector).length) { | |||
app.init(app.idActivePointSale); | |||
}); | |||
}, | |||
updateProductOrderPrices: function (updatePricesOnUpdateOrder) { | |||
getProductOrderArrayRequest: function (order) { | |||
var productOrderArrayRequest = {}; | |||
for (var key in order.productOrder) { | |||
productOrderArrayRequest[key] = { | |||
quantity: order.productOrder[key].quantity, | |||
unit: order.productOrder[key].unit, | |||
price: order.productOrder[key].price, | |||
invoice_price: order.productOrder[key].invoice_price | |||
}; | |||
} | |||
return JSON.stringify(productOrderArrayRequest); | |||
}, | |||
updateProductOrders: function (updatePricesOnUpdateOrder) { | |||
var app = this; | |||
app.loadingUpdateProductOrder = true; | |||
var order = null; | |||
@@ -853,7 +877,8 @@ if($(selector).length) { | |||
idDistribution: app.distribution.id, | |||
idUser: order.id_user, | |||
idPointSale: order.id_point_sale, | |||
idOrder: order.id | |||
idOrder: order.id, | |||
productOrderFormArray: app.getProductOrderArrayRequest(order) | |||
} | |||
}) | |||
.then(function (response) { | |||
@@ -861,6 +886,7 @@ if($(selector).length) { | |||
for (idProduct in response.data) { | |||
if (app.showModalFormOrderCreate) { | |||
Vue.set(app.orderCreate.productOrder[idProduct], 'quantity_remaining', response.data[idProduct].quantity_remaining); | |||
Vue.set(app.orderCreate.productOrder[idProduct], 'prices', response.data[idProduct].prices); | |||
Vue.set(app.orderCreate.productOrder[idProduct], 'active', response.data[idProduct].active); | |||
Vue.set(app.orderCreate.productOrder[idProduct], 'price', app.getBestProductPrice(app.orderCreate, idProduct, app.orderCreate.productOrder[idProduct].quantity, false)); | |||
@@ -870,6 +896,7 @@ if($(selector).length) { | |||
if (app.showModalFormOrderUpdate && app.idOrderUpdate) { | |||
for (keyOrderUpdate in app.ordersUpdate) { | |||
if (order.id == app.idOrderUpdate) { | |||
Vue.set(app.ordersUpdate[keyOrderUpdate].productOrder[idProduct], 'quantity_remaining', response.data[idProduct].quantity_remaining); | |||
Vue.set(app.ordersUpdate[keyOrderUpdate].productOrder[idProduct], 'prices', response.data[idProduct].prices); | |||
Vue.set(app.ordersUpdate[keyOrderUpdate].productOrder[idProduct], 'active', response.data[idProduct].active); | |||
Vue.set(app.ordersUpdate[keyOrderUpdate].productOrder[idProduct], 'invoice_price', response.data[idProduct].invoice_price); | |||
@@ -916,7 +943,7 @@ if($(selector).length) { | |||
} | |||
}) | |||
.then(function (response) { | |||
app.updateProductOrderPrices(false); | |||
app.updateProductOrders(false); | |||
appAlerts.alert('info', 'Prix facturés réinitialisés.'); | |||
}); | |||
} | |||
@@ -1033,6 +1060,9 @@ if($(selector).length) { | |||
} | |||
}, | |||
methods: { | |||
labelUnitReference: function(unit) { | |||
return label_unit_reference(unit); | |||
}, | |||
checkForm: function () { | |||
this.errors = []; | |||
@@ -1157,6 +1187,8 @@ if($(selector).length) { | |||
Vue.set(this.order.productOrder[id_product], 'price', app.getBestProductPrice(this.order, id_product, theQuantity, false)); | |||
Vue.set(this.order.productOrder[id_product], 'price_with_tax', app.getBestProductPrice(this.order, id_product, theQuantity, true)); | |||
} | |||
this.updateProductOrders(false); | |||
}, | |||
getProduct: function (idProduct) { | |||
for (var i = 0; i < this.products.length; i++) { | |||
@@ -1215,14 +1247,14 @@ if($(selector).length) { | |||
if (app.idActivePointSale == 0) { | |||
app.order.id_point_sale = response.data.id_favorite_point_sale; | |||
} | |||
app.updateProductOrderPrices(true); | |||
app.updateProductOrders(true); | |||
}); | |||
}, | |||
pointSaleChange: function (event) { | |||
this.updateProductOrderPrices(true); | |||
this.updateProductOrders(true); | |||
}, | |||
updateProductOrderPrices: function (updateProductOrderPrices) { | |||
this.$emit('updateproductorderprices', updateProductOrderPrices); | |||
updateProductOrders: function (updateProductOrders) { | |||
this.$emit('updateproductorders', updateProductOrders); | |||
}, | |||
updateInvoicePrices: function () { | |||
this.$emit('updateinvoiceprices'); | |||
@@ -1247,14 +1279,8 @@ if($(selector).length) { | |||
} | |||
return productQuantityOrder; | |||
}, | |||
getProductQuantityRemaining: function (product) { | |||
var productQuantityRemaining = null; | |||
var productQuantityMax = this.getProductQuantityMax(product); | |||
var productQuantityOrder = this.getProductQuantityOrder(product); | |||
if(productQuantityMax) { | |||
productQuantityRemaining = productQuantityMax - productQuantityOrder; | |||
} | |||
getProductQuantityRemaining: function (order, product) { | |||
var productQuantityRemaining = order.productOrder[product.id].quantity_remaining; | |||
// format | |||
if (productQuantityRemaining && product.unit != 'piece') { |
@@ -0,0 +1,8 @@ | |||
.automatic-email-form { | |||
form { | |||
label { | |||
display: block; | |||
} | |||
} | |||
} |
@@ -93,6 +93,22 @@ termes. | |||
} | |||
} | |||
table td.quantity-remaining { | |||
text-align: right; | |||
.has-quantity, .infinite { | |||
color: #00A65A; | |||
} | |||
.negative { | |||
color: #DD4B39; | |||
} | |||
.infinite, .empty { | |||
font-size: 18px; | |||
} | |||
} | |||
#products { | |||
td.quantities { | |||
width: 100px; | |||
@@ -184,6 +200,15 @@ termes. | |||
text-align: center; | |||
min-width: 50px; | |||
} | |||
.limit-quantity-accessories { | |||
margin-top: 7px; | |||
font-size: 12px; | |||
.quantity { | |||
font-weight: bold; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
@@ -482,22 +507,6 @@ termes. | |||
border-right: 0px none; | |||
} | |||
} | |||
td.quantity-remaining { | |||
text-align: right; | |||
&.quantity-remaining, &.infinite { | |||
color: #00A65A; | |||
} | |||
&.negative { | |||
color: #DD4B39; | |||
} | |||
&.infinite, &.empty { | |||
font-size: 18px; | |||
} | |||
} | |||
} | |||
.actions-form { |
@@ -1555,4 +1555,5 @@ a.btn, button.btn { | |||
@import "feature-admin/_index.scss"; | |||
@import "setting/_form.scss"; | |||
@import "dashboard-admin/_index.scss" ; | |||
@import "automatic-email/_form.scss" ; | |||
@import "_responsive.scss" ; |
@@ -12,6 +12,10 @@ | |||
label.control-label { | |||
display: none; | |||
} | |||
.field-user-exclusive_access_selected_points_sale label.control-label { | |||
display: block; | |||
} | |||
} | |||
.panel-user-groups { |
@@ -0,0 +1,35 @@ | |||
<?php | |||
namespace common\components; | |||
class ActiveDataProvider extends \yii\data\ActiveDataProvider | |||
{ | |||
public function filterByCallback($callback) | |||
{ | |||
$filtered_models = $filtered_keys = []; | |||
// la pagination ne fonctionne pas avec ce système de filtre : solution alternative sans pagination | |||
// @TODO : faire en sorte que la pagination fonctionne avec les filtres par callback | |||
$this->pagination = false; | |||
$have_been_filtered = false; | |||
$this->prepare(true); // force read of next page | |||
$non_filtered_models = $this->getModels(); | |||
foreach($non_filtered_models as $model) { | |||
$filterModel = $callback($model); | |||
if($filterModel) { | |||
$have_been_filtered = true; | |||
$this->setTotalCount($this->getTotalCount() - 1); | |||
} | |||
else { | |||
$filtered_models[] = $model; | |||
$filtered_keys[] = $model->id; | |||
} | |||
} | |||
if($have_been_filtered) { | |||
$this->setModels($filtered_models); | |||
$this->setKeys($filtered_keys); | |||
} | |||
} | |||
} |
@@ -2,27 +2,28 @@ | |||
namespace common\components\BulkMailer; | |||
use domain\Communication\Email\Email; | |||
use domain\User\User\UserSolver; | |||
use GuzzleHttp\Client; | |||
use Psr\Http\Message\ResponseInterface; | |||
class BulkMailerBrevo implements BulkMailerInterface | |||
{ | |||
public function sendEmails(array $contactsArray, string $fromName, string $fromEmail, string $subject, string $htmlContent, string $textContent = null) | |||
public function sendEmail(Email $email, array $usersArray) | |||
{ | |||
$userSolver = UserSolver::getInstance(); | |||
$client = new Client(); | |||
$data = [ | |||
'sender' => [ | |||
'name' => $fromName, | |||
'email' => $fromEmail | |||
'name' => $email->getFromName(), | |||
'email' => $email->getFromEmail() | |||
], | |||
'bcc' => [], | |||
'subject' => $subject, | |||
'htmlContent' => $htmlContent | |||
'subject' => $email->getSubject(), | |||
'htmlContent' => $email->getHtmlContent() | |||
] ; | |||
foreach($contactsArray as $user) { | |||
foreach($usersArray as $user) { | |||
$data['bcc'][] = [ | |||
'name' => $userSolver->getUsernameFromArray($user), | |||
'email' => $user['email'] |
@@ -2,10 +2,12 @@ | |||
namespace common\components\BulkMailer; | |||
use domain\Communication\Email\Email; | |||
interface BulkMailerInterface | |||
{ | |||
const MAILJET = 'mailjet'; | |||
const BREVO = 'brevo'; | |||
public function sendEmails(array $contactsArray, string $fromName, string $fromEmail, string $subject, string $htmlContent, string $textContent = null); | |||
public function sendEmail(Email $email, array $usersArray); | |||
} |
@@ -3,11 +3,12 @@ | |||
namespace common\components\BulkMailer; | |||
use common\helpers\Mailjet; | |||
use domain\Communication\Email\Email; | |||
use Mailjet\Client; | |||
class BulkMailerMailjet implements BulkMailerInterface | |||
{ | |||
public function sendEmails(array $contactsArray, string $fromName, string $fromEmail, string $subject, string $htmlContent, string $textContent = null) | |||
public function sendEmail(Email $email, array $usersArray) | |||
{ | |||
$client = new Client( | |||
Mailjet::getApiKey('public'), | |||
@@ -18,11 +19,11 @@ class BulkMailerMailjet implements BulkMailerInterface | |||
$data = ['Messages' => []] ; | |||
foreach($contactsArray as $user) { | |||
foreach($usersArray as $user) { | |||
$data['Messages'][] = [ | |||
'From' => [ | |||
'Email' => $fromEmail, | |||
'Name' => $fromName | |||
'Email' => $email->getFromEmail(), | |||
'Name' => $email->getFromName() | |||
], | |||
'To' => [ | |||
[ | |||
@@ -30,9 +31,9 @@ class BulkMailerMailjet implements BulkMailerInterface | |||
'Name' => $user['name'].' '.$user['lastname'] | |||
] | |||
], | |||
'Subject' => $subject, | |||
'HTMLPart' => $htmlContent, | |||
'TextPart' => $textContent | |||
'Subject' => $email->getSubject(), | |||
'HTMLPart' => $email->getHtmlContent(), | |||
'TextPart' => $email->getTextContent() | |||
] ; | |||
if(count($data['Messages']) == 50) { |
@@ -2,6 +2,7 @@ | |||
namespace common\components\BulkMailer; | |||
use domain\Communication\Email\Email; | |||
use domain\Setting\AdminSettingBag; | |||
use yii\base\ErrorException; | |||
@@ -22,8 +23,8 @@ class BulkMailerProxy implements BulkMailerInterface | |||
} | |||
} | |||
public function sendEmails(array $contactsArray, string $fromName, string $fromEmail, string $subject, string $htmlContent, string $textContent = null) | |||
public function sendEmail(Email $email, array $usersArray) | |||
{ | |||
$this->getBulkMailer()->sendEmails($contactsArray, $fromName, $fromEmail, $subject, $htmlContent, $textContent); | |||
$this->getBulkMailer()->sendEmail($email, $usersArray); | |||
} | |||
} |
@@ -26,6 +26,8 @@ class BusinessLogic | |||
$this->getUserProducerModule(), | |||
$this->getUserPointSaleModule(), | |||
$this->getUserModule(), | |||
$this->getEmailModule(), | |||
$this->getAutomaticEmailModule(), | |||
$this->getUserMessageModule(), | |||
$this->getPointSaleDistributionModule(), | |||
$this->getProductAccessoryModule(), |
@@ -2,6 +2,7 @@ | |||
namespace common\components; | |||
use domain\Communication\Email\EmailModule; | |||
use domain\Config\TaxRate\TaxRateModule; | |||
use domain\Config\Unit\UnitModule; | |||
use domain\Distribution\Distribution\DistributionModule; | |||
@@ -13,6 +14,7 @@ use domain\Document\Invoice\InvoiceModule; | |||
use domain\Document\Quotation\QuotationModule; | |||
use domain\Feature\Feature\FeatureModule; | |||
use domain\Feature\FeatureProducer\FeatureProducerModule; | |||
use domain\Communication\AutomaticEmail\AutomaticEmailModule; | |||
use domain\Opinion\OpinionModule; | |||
use domain\Order\Order\OrderModule; | |||
use domain\Order\OrderStatus\OrderStatusModule; | |||
@@ -44,6 +46,16 @@ use domain\User\UserUserGroup\UserUserGroupModule; | |||
trait BusinessLogicTrait | |||
{ | |||
public function getEmailModule(): EmailModule | |||
{ | |||
return EmailModule::getInstance(); | |||
} | |||
public function getAutomaticEmailModule(): AutomaticEmailModule | |||
{ | |||
return AutomaticEmailModule::getInstance(); | |||
} | |||
public function getProductAccessoryModule(): ProductAccessoryModule | |||
{ | |||
return ProductAccessoryModule::getInstance(); |
@@ -0,0 +1,25 @@ | |||
<?php | |||
namespace common\components; | |||
class Date | |||
{ | |||
public static function getDaysOfWeekArray(): array | |||
{ | |||
return [ | |||
1 => 'Lundi', | |||
2 => 'Mardi', | |||
3 => 'Mercredi', | |||
4 => 'Jeudi', | |||
5 => 'Vendredi', | |||
6 => 'Samedi', | |||
7 => 'Dimanche' | |||
]; | |||
} | |||
public static function getDayOfWeekStringByNumber(int $dayOfWeekNumber): string | |||
{ | |||
$daysOfWeekArray = self::getDaysOfWeekArray(); | |||
return $daysOfWeekArray[$dayOfWeekNumber]; | |||
} | |||
} |
@@ -67,7 +67,7 @@ class UrlManagerCommon extends UrlManager | |||
{ | |||
if ($this->_hostInfo === null) | |||
{ | |||
$secure = Yii::$app->getRequest()->getIsSecureConnection(); | |||
$secure = Yii::$app->getRequest()->isConsoleRequest ? false : Yii::$app->getRequest()->getIsSecureConnection(); | |||
$http = $secure ? 'https' : 'http'; | |||
if (isset($_SERVER['HTTP_HOST'])) { |
@@ -37,7 +37,7 @@ | |||
*/ | |||
return [ | |||
'version' => '24.6.D', | |||
'version' => '24.7.A', | |||
'maintenanceMode' => false, | |||
'siteName' => 'Souke', | |||
'tinyMcePlugins' => 'preview searchreplace autolink autosave save directionality code visualblocks visualchars fullscreen image link lists wordcount help', |
@@ -10,7 +10,7 @@ version( | |||
"[Administration] Utilisateur > Créer (en arrivant de la création d'une commande) : retour automatique à la création de la commande", | |||
], | |||
[ | |||
"[Administration] Tableau de bord : corectif affichage des commandes modifiées issues des abonnements" | |||
"[Administration] Tableau de bord : correctif affichage des commandes modifiées issues des abonnements" | |||
] | |||
], | |||
[ |
@@ -0,0 +1,28 @@ | |||
<?php | |||
require_once dirname(__FILE__).'/_macros.php'; | |||
version( | |||
'01/07/2024', | |||
[ | |||
[ | |||
"Gestion des accessoires produits (module payant) : possibilité de limiter les quantités de produits commandables en fonction des quantités d'accessoires disponibles (exemple : moules)", | |||
"Système de parrainage entre producteurs (voir dans 'Parrainage')", | |||
"[Administration] Statistiques > Export paiements : export CSV des paiements sur une période donnée", | |||
"Utilisateurs : accès exclusif à certains points de vente (Profil utilisateur > Points de vente : champs 'Accès exclusif aux points de vente sélectionnés')", | |||
], | |||
[ | |||
"[Administration] Utilisateurs > Cagnotte : curseur placé automatiquement dans le champs 'Montant'" | |||
] | |||
], | |||
[ | |||
[ | |||
], | |||
[ | |||
"[Administration] Déliverabilité emails : ajout colonne 'Note emails'" | |||
] | |||
], | |||
$userCurrent | |||
); | |||
?> |
@@ -0,0 +1,36 @@ | |||
<?php | |||
namespace console\commands; | |||
use domain\Communication\AutomaticEmail\AutomaticEmailModule; | |||
use domain\Communication\Email\EmailModule; | |||
use domain\Producer\Producer\ProducerModule; | |||
use yii\console\Controller; | |||
class AutomaticEmailController extends Controller | |||
{ | |||
// ./yii automatic-email/send | |||
public function actionSend() | |||
{ | |||
$automaticEmailModule = AutomaticEmailModule::getInstance(); | |||
$producerModule = ProducerModule::getInstance(); | |||
$emailModule = EmailModule::getInstance(); | |||
$producersArray = $producerModule->getRepository()->findProducers(); | |||
foreach($producersArray as $producer) { | |||
\Yii::$app->logic->setProducerContext($producer); | |||
$automaticEmailsArray = $automaticEmailModule->getRepository()->findAutomaticEmails(); | |||
foreach($automaticEmailsArray as $automaticEmail) { | |||
$distribution = $automaticEmailModule->getResolver()->getMatchedDistribution($automaticEmail); | |||
if($distribution) { | |||
$email = $automaticEmailModule->getManager()->createEmailFromAutomaticEmail($automaticEmail, $distribution); | |||
$usersArray = $emailModule->getContactListResolver()->search($producer, $distribution); | |||
$emailModule->getBulkMailer()->sendEmail($email, $usersArray); | |||
echo 'Email automatique "'.$automaticEmail->getSubject().'" envoyé à '.count($usersArray)." utilisateur(s)\n"; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
?> |
@@ -36,6 +36,8 @@ pris connaissance de la licence CeCILL, et que vous en avez accepté les | |||
termes. | |||
*/ | |||
setlocale(LC_TIME, 'fr_FR.utf8','fra'); | |||
$params = array_merge( | |||
require(__DIR__ . '/../../common/config/params.php'), | |||
require(__DIR__ . '/../../common/config/params-local.php'), |
@@ -0,0 +1,26 @@ | |||
<?php | |||
use yii\db\Migration; | |||
use yii\db\Schema; | |||
/** | |||
* Class m240626_133528_add_column_user_exclusive_access_selected_points_sale | |||
*/ | |||
class m240626_133528_add_column_user_exclusive_access_selected_points_sale extends Migration | |||
{ | |||
/** | |||
* {@inheritdoc} | |||
*/ | |||
public function safeUp() | |||
{ | |||
$this->addColumn('user_producer', 'exclusive_access_selected_points_sale', Schema::TYPE_BOOLEAN); | |||
} | |||
/** | |||
* {@inheritdoc} | |||
*/ | |||
public function safeDown() | |||
{ | |||
$this->dropColumn('user_producer', 'exclusive_access_selected_points_sale'); | |||
} | |||
} |
@@ -0,0 +1,37 @@ | |||
<?php | |||
use yii\db\Migration; | |||
use yii\db\Schema; | |||
/** | |||
* Class m240627_064315_create_table_automatic_email | |||
*/ | |||
class m240627_064315_create_table_automatic_email extends Migration | |||
{ | |||
/** | |||
* {@inheritdoc} | |||
*/ | |||
public function safeUp() | |||
{ | |||
$this->createTable('automatic_email', [ | |||
'id' => 'pk', | |||
'id_producer' => Schema::TYPE_INTEGER.' NOT NULL', | |||
'day' => Schema::TYPE_INTEGER.' NOT NULL', | |||
'delay_before_distribution' => Schema::TYPE_INTEGER.' NOT NULL', | |||
'subject' => Schema::TYPE_STRING.' NOT NULL', | |||
'message' => Schema::TYPE_TEXT.' NOT NULL', | |||
'integrate_product_list' => Schema::TYPE_BOOLEAN, | |||
'status' => Schema::TYPE_INTEGER.' NOT NULL', | |||
]); | |||
$this->addForeignKey('fk_automatic_email_id_producer', 'automatic_email', 'id_producer', 'producer', 'id'); | |||
} | |||
/** | |||
* {@inheritdoc} | |||
*/ | |||
public function safeDown() | |||
{ | |||
$this->dropTable('automatic_email'); | |||
} | |||
} |
@@ -0,0 +1,152 @@ | |||
<?php | |||
namespace domain\Communication\AutomaticEmail; | |||
use common\components\ActiveRecordCommon; | |||
use common\components\Date; | |||
use domain\Producer\Producer\Producer; | |||
use yii\db\ActiveQuery; | |||
class AutomaticEmail extends ActiveRecordCommon | |||
{ | |||
public static function tableName() | |||
{ | |||
return 'automatic_email'; | |||
} | |||
public function rules() | |||
{ | |||
return [ | |||
[['id_producer', 'day', 'subject', 'message', 'status'], 'required'], | |||
[['subject', 'message'], 'string'], | |||
[['id_producer', 'day', 'delay_before_distribution', 'status'], 'integer'], | |||
[['integrate_product_list'], 'boolean'] | |||
]; | |||
} | |||
public function attributeLabels() | |||
{ | |||
return [ | |||
'id_producer' => 'Producteur', | |||
'day' => 'Jour de distribution', | |||
'delay_before_distribution' => 'Envoi du message', | |||
'subject' => 'Sujet', | |||
'message' => 'Message', | |||
'integrate_product_list' => 'Intégrer la liste des produits au message', | |||
'status' => 'Statut' | |||
]; | |||
} | |||
/* Getters / Setters */ | |||
public function getId(): ?int | |||
{ | |||
return $this->id; | |||
} | |||
public function getProducer(): Producer | |||
{ | |||
return $this->producerRelation; | |||
} | |||
public function setProducer(Producer $producer): self | |||
{ | |||
$this->populateFieldObject('id_producer', 'producerRelation', $producer); | |||
return $this; | |||
} | |||
public function getDay(): int | |||
{ | |||
return $this->day; | |||
} | |||
public function setDay(int $day): self | |||
{ | |||
$this->day = $day; | |||
return $this; | |||
} | |||
public function getDelayBeforeDistribution(): int | |||
{ | |||
return $this->delay_before_distribution; | |||
} | |||
public function setDelayBeforeDistribution(int $delayBeforeDistribution): self | |||
{ | |||
$this->delay_before_distribution = $delayBeforeDistribution; | |||
return $this; | |||
} | |||
public function getSubject(): string | |||
{ | |||
return $this->subject; | |||
} | |||
public function setSubject(string $subject): self | |||
{ | |||
$this->subject = $subject; | |||
return $this; | |||
} | |||
public function getMessage(): string | |||
{ | |||
return $this->message; | |||
} | |||
public function setMessage(string $message): self | |||
{ | |||
$this->message = $message; | |||
return $this; | |||
} | |||
public function getIntegrateProductList(): ?bool | |||
{ | |||
return $this->integrate_product_list; | |||
} | |||
public function setIntegrateProductList(?bool $integrateProductList): self | |||
{ | |||
$this->integrate_product_list = $integrateProductList; | |||
return $this; | |||
} | |||
public function getStatus(): int | |||
{ | |||
return $this->status; | |||
} | |||
public function setStatus(int $status): self | |||
{ | |||
$this->status = $status; | |||
return $this; | |||
} | |||
/* Relations */ | |||
public function getProducerRelation(): ActiveQuery | |||
{ | |||
return $this->hasOne(Producer::class, ['id' => 'id_producer']); | |||
} | |||
/* Méthodes */ | |||
public function getDayAsString(): string | |||
{ | |||
return Date::getDayOfWeekStringByNumber($this->getDay()); | |||
} | |||
public function getDelayBeforeDistributionAsString(): string | |||
{ | |||
return $this->getDelayBeforeDistribution().' jour(s) avant'; | |||
} | |||
public function getStatusAsHtml(): string | |||
{ | |||
if($this->getStatus()) { | |||
return '<span class="label label-success">Activé</span>'; | |||
} | |||
else { | |||
return '<span class="label label-danger">Désactivé</span>'; | |||
} | |||
} | |||
} |
@@ -0,0 +1,19 @@ | |||
<?php | |||
namespace domain\Communication\AutomaticEmail; | |||
use domain\_\AbstractBuilder; | |||
use domain\_\StatusInterface; | |||
use domain\Producer\Producer\Producer; | |||
class AutomaticEmailBuilder extends AbstractBuilder | |||
{ | |||
public function instanciateAutomaticEmail(Producer $producer): AutomaticEmail | |||
{ | |||
$automaticEmail = new AutomaticEmail(); | |||
$automaticEmail->setProducer($producer); | |||
$automaticEmail->setDelayBeforeDistribution(2); | |||
$automaticEmail->setStatus(StatusInterface::STATUS_ONLINE); | |||
return $automaticEmail; | |||
} | |||
} |
@@ -0,0 +1,13 @@ | |||
<?php | |||
namespace domain\Communication\AutomaticEmail; | |||
use domain\_\AbstractDefinition; | |||
class AutomaticEmailDefinition extends AbstractDefinition | |||
{ | |||
public function getEntityFqcn(): string | |||
{ | |||
return AutomaticEmail::class; | |||
} | |||
} |
@@ -0,0 +1,54 @@ | |||
<?php | |||
namespace domain\Communication\AutomaticEmail; | |||
use domain\_\AbstractManager; | |||
use domain\Communication\Email\Email; | |||
use domain\Communication\Email\EmailGenerator; | |||
use domain\Distribution\Distribution\Distribution; | |||
use domain\Producer\Producer\Producer; | |||
class AutomaticEmailManager extends AbstractManager | |||
{ | |||
protected AutomaticEmailBuilder $automaticEmailBuilder; | |||
protected EmailGenerator $emailGenerator; | |||
public function loadDependencies(): void | |||
{ | |||
$this->automaticEmailBuilder = $this->loadService(AutomaticEmailBuilder::class); | |||
$this->emailGenerator = $this->loadService(EmailGenerator::class); | |||
} | |||
public function createAutomaticEmail( | |||
Producer $producer, | |||
int $day, | |||
int $delayBeforeDistribution, | |||
string $subject, | |||
string $message, | |||
bool $integrateProductList = null | |||
): AutomaticEmail | |||
{ | |||
$automaticEmail = $this->automaticEmailBuilder->instanciateAutomaticEmail($producer); | |||
$automaticEmail->setDay($day); | |||
$automaticEmail->setDelayBeforeDistribution($delayBeforeDistribution); | |||
$automaticEmail->setSubject($subject); | |||
$automaticEmail->setMessage($message); | |||
$automaticEmail->setIntegrateProductList($integrateProductList); | |||
$automaticEmail->save(); | |||
return $automaticEmail; | |||
} | |||
public function createEmailFromAutomaticEmail(AutomaticEmail $automaticEmail, Distribution $distribution): Email | |||
{ | |||
return $this->emailGenerator->createEmail( | |||
$automaticEmail->getSubject(), | |||
$automaticEmail->getMessage(), | |||
$automaticEmail->getIntegrateProductList(), | |||
$automaticEmail->getProducer(), | |||
$distribution | |||
); | |||
} | |||
} |
@@ -0,0 +1,44 @@ | |||
<?php | |||
namespace domain\Communication\AutomaticEmail; | |||
use domain\_\AbstractModule; | |||
class AutomaticEmailModule extends AbstractModule | |||
{ | |||
public function getServices(): array | |||
{ | |||
return [ | |||
AutomaticEmailDefinition::class, | |||
AutomaticEmailBuilder::class, | |||
AutomaticEmailRepository::class, | |||
AutomaticEmailResolver::class, | |||
AutomaticEmailManager::class | |||
]; | |||
} | |||
public function getDefinition(): AutomaticEmailDefinition | |||
{ | |||
return AutomaticEmailDefinition::getInstance(); | |||
} | |||
public function getBuilder(): AutomaticEmailBuilder | |||
{ | |||
return AutomaticEmailBuilder::getInstance(); | |||
} | |||
public function getRepository(): AutomaticEmailRepository | |||
{ | |||
return AutomaticEmailRepository::getInstance(); | |||
} | |||
public function getResolver(): AutomaticEmailResolver | |||
{ | |||
return AutomaticEmailResolver::getInstance(); | |||
} | |||
public function getManager(): AutomaticEmailManager | |||
{ | |||
return AutomaticEmailManager::getInstance(); | |||
} | |||
} |
@@ -0,0 +1,43 @@ | |||
<?php | |||
namespace domain\Communication\AutomaticEmail; | |||
use domain\_\AbstractRepository; | |||
use domain\_\RepositoryQueryInterface; | |||
class AutomaticEmailRepository extends AbstractRepository | |||
{ | |||
protected AutomaticEmailRepositoryQuery $query; | |||
public function loadDependencies(): void | |||
{ | |||
$this->loadQuery(AutomaticEmailRepositoryQuery::class); | |||
} | |||
public function getDefaultOptionsSearch(): array | |||
{ | |||
return [ | |||
self::WITH => [], | |||
self::JOIN_WITH => [], | |||
self::ORDER_BY => 'day ASC', | |||
self::ATTRIBUTE_ID_PRODUCER => 'automatic_email.id_producer' | |||
]; | |||
} | |||
public function findOneAutomaticEmailById(int $id): ?AutomaticEmail | |||
{ | |||
return $this->createDefaultQuery() | |||
->filterById($id) | |||
->findOne(); | |||
} | |||
public function queryAutomaticEmails(): RepositoryQueryInterface | |||
{ | |||
return $this->createDefaultQuery(); | |||
} | |||
public function findAutomaticEmails(): array | |||
{ | |||
return $this->queryAutomaticEmails()->find(); | |||
} | |||
} |
@@ -0,0 +1,15 @@ | |||
<?php | |||
namespace domain\Communication\AutomaticEmail; | |||
use domain\_\AbstractRepositoryQuery; | |||
class AutomaticEmailRepositoryQuery extends AbstractRepositoryQuery | |||
{ | |||
protected AutomaticEmailDefinition $definition; | |||
public function loadDependencies(): void | |||
{ | |||
$this->loadDefinition(AutomaticEmailDefinition::class); | |||
} | |||
} |
@@ -0,0 +1,36 @@ | |||
<?php | |||
namespace domain\Communication\AutomaticEmail; | |||
use domain\_\AbstractResolver; | |||
use domain\Distribution\Distribution\Distribution; | |||
use domain\Distribution\Distribution\DistributionRepository; | |||
use domain\Producer\Producer\ProducerSolver; | |||
class AutomaticEmailResolver extends AbstractResolver | |||
{ | |||
protected DistributionRepository $distributionRepository; | |||
protected ProducerSolver $producerSolver; | |||
public function loadDependencies(): void | |||
{ | |||
$this->distributionRepository = $this->loadService(DistributionRepository::class); | |||
$this->producerSolver = $this->loadService(ProducerSolver::class); | |||
} | |||
public function getMatchedDistribution(AutomaticEmail $automaticEmail): ?Distribution | |||
{ | |||
$date = (new \DateTime('+'.$automaticEmail->getDelayBeforeDistribution().' days')); | |||
$dateFormat = $date->format('Y-m-d'); | |||
$distribution = $this->distributionRepository->findOneDistribution($dateFormat); | |||
if($distribution | |||
&& $distribution->active | |||
&& $date->format('N') == $automaticEmail->getDay() | |||
&& !$this->producerSolver->isOnLeavePeriodByDistribution($distribution)) { | |||
return $distribution; | |||
} | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,81 @@ | |||
<?php | |||
namespace domain\Communication\Email; | |||
use common\helpers\GlobalParam; | |||
use domain\_\AbstractResolver; | |||
use domain\Distribution\Distribution\Distribution; | |||
use domain\PointSale\PointSale\PointSale; | |||
use domain\Producer\Producer\Producer; | |||
use domain\User\User\UserRepository; | |||
class ContactListResolver extends AbstractResolver | |||
{ | |||
protected UserRepository $userRepository; | |||
public function loadDependencies(): void | |||
{ | |||
$this->userRepository = $this->loadService(UserRepository::class); | |||
} | |||
public function search( | |||
Producer $producer, | |||
Distribution $distribution = null, | |||
PointSale $pointSale = null, | |||
bool $usersPointSaleLink = false, | |||
bool $usersPointSaleHasOrder = false, | |||
bool $isSubscriber = false, | |||
bool $isInactive = false | |||
): array | |||
{ | |||
if ($pointSale && !$usersPointSaleLink && !$usersPointSaleHasOrder) { | |||
$usersPointSaleLink = 1; | |||
} | |||
if ($distribution && !$usersPointSaleLink && !$usersPointSaleHasOrder) { | |||
$usersPointSaleLink = 1; | |||
} | |||
if($distribution) { | |||
$users = []; | |||
foreach($distribution->pointSaleDistribution as $pointSaleDistribution) { | |||
if($pointSaleDistribution->delivery) { | |||
$usersPointSaleArray = $this->userRepository->queryUsersBy([ | |||
'id_producer' => $producer->id, | |||
'id_point_sale' => $pointSaleDistribution->id_point_sale, | |||
'users_point_sale_link' => $usersPointSaleLink, | |||
'users_point_sale_has_order' => $usersPointSaleHasOrder, | |||
'newsletter' => true | |||
])->all(); | |||
foreach($usersPointSaleArray as $user) { | |||
$users[$user['id']] = $user; | |||
} | |||
} | |||
} | |||
} | |||
else { | |||
$users = $this->userRepository->queryUsersBy([ | |||
'id_producer' => $producer->id, | |||
'id_point_sale' => $pointSale ? $pointSale->id : null, | |||
'users_point_sale_link' => $usersPointSaleLink, | |||
'users_point_sale_has_order' => $usersPointSaleHasOrder, | |||
'subscribers' => $isSubscriber, | |||
'inactive' => $isInactive, | |||
'newsletter' => true | |||
])->all(); | |||
} | |||
$usersArray = []; | |||
foreach ($users as $user) { | |||
if (isset($user['email']) && strlen($user['email']) > 0) { | |||
if($producer->isDemoAccount()) { | |||
$user['email'] = \Yii::$app->faker->email(); | |||
} | |||
$usersArray[] = $user; | |||
} | |||
} | |||
return $usersArray; | |||
} | |||
} |
@@ -0,0 +1,67 @@ | |||
<?php | |||
namespace domain\Communication\Email; | |||
class Email | |||
{ | |||
protected string $fromName; | |||
protected string $fromEmail; | |||
protected string $subject; | |||
protected string $htmlContent; | |||
protected string $textContent; | |||
public function getFromName(): string | |||
{ | |||
return $this->fromName; | |||
} | |||
public function setFromName(string $fromName): self | |||
{ | |||
$this->fromName = $fromName; | |||
return $this; | |||
} | |||
public function getFromEmail(): string | |||
{ | |||
return $this->fromEmail; | |||
} | |||
public function setFromEmail(string $fromEmail): self | |||
{ | |||
$this->fromEmail = $fromEmail; | |||
return $this; | |||
} | |||
public function getSubject(): string | |||
{ | |||
return $this->subject; | |||
} | |||
public function setSubject(string $subject): self | |||
{ | |||
$this->subject = $subject; | |||
return $this; | |||
} | |||
public function getHtmlContent(): string | |||
{ | |||
return $this->htmlContent; | |||
} | |||
public function setHtmlContent(string $htmlContent): self | |||
{ | |||
$this->htmlContent = $htmlContent; | |||
return $this; | |||
} | |||
public function getTextContent(): string | |||
{ | |||
return $this->textContent; | |||
} | |||
public function setTextContent(string $textContent): self | |||
{ | |||
$this->textContent = $textContent; | |||
return $this; | |||
} | |||
} |
@@ -0,0 +1,27 @@ | |||
<?php | |||
namespace domain\Communication\Email; | |||
use domain\_\AbstractBuilder; | |||
class EmailBuilder extends AbstractBuilder | |||
{ | |||
public function instanciateEmail( | |||
string $fromName, | |||
string $fromEmail, | |||
string $subject, | |||
string $htmlContent, | |||
string $textContent | |||
{ | |||
$email = new Email(); | |||
$email->setFromName($fromName); | |||
$email->setFromEmail($fromEmail); | |||
$email->setSubject($subject); | |||
$email->setHtmlContent($htmlContent); | |||
$email->setTextContent($textContent); | |||
return $email; | |||
} | |||
} |
@@ -0,0 +1,145 @@ | |||
<?php | |||
namespace domain\Communication\Email; | |||
use common\helpers\Price; | |||
use domain\_\AbstractResolver; | |||
use domain\Config\Unit\UnitDefinition; | |||
use domain\Distribution\Distribution\Distribution; | |||
use domain\Distribution\Distribution\DistributionSolver; | |||
use domain\Producer\Producer\Producer; | |||
use domain\Producer\Producer\ProducerSolver; | |||
use domain\Product\Product\Product; | |||
use domain\Product\Product\ProductSolver; | |||
use yii\helpers\Html; | |||
class EmailGenerator extends AbstractResolver | |||
{ | |||
protected EmailBuilder $emailBuilder; | |||
protected ProductSolver $productSolver; | |||
protected DistributionSolver $distributionSolver; | |||
protected ProducerSolver $producerSolver; | |||
public function loadDependencies(): void | |||
{ | |||
$this->emailBuilder = $this->loadService(EmailBuilder::class); | |||
$this->productSolver = $this->loadService(ProductSolver::class); | |||
$this->distributionSolver = $this->loadService(DistributionSolver::class); | |||
$this->producerSolver = $this->loadService(ProducerSolver::class); | |||
} | |||
public function createEmail( | |||
string $subject, | |||
string $message, | |||
bool $integrateProductList, | |||
Producer $producer = null, | |||
Distribution $distribution = null | |||
{ | |||
$messageAutoText = '' ; | |||
$messageAutoHtml = '' ; | |||
$messageAutoHtml .= ' <style type="text/css"> | |||
h1, h2, h3, h4, h5, h6 { | |||
padding: 0px; | |||
margin: 0px; | |||
margin-bottom: 10px; | |||
} | |||
p { | |||
margin: 0px; | |||
padding: 0px; | |||
margin-bottom: 5px; | |||
} | |||
</style>'; | |||
if($distribution) { | |||
$messageAutoText = ' | |||
' ; | |||
$messageAutoHtml .= '<br /><br />' ; | |||
$linkOrder = $this->distributionSolver->getLinkOrder($distribution); | |||
$dateOrder = strftime('%A %d %B %Y', strtotime($distribution->date)) ; | |||
$messageAutoHtml .= '<a href="'.$linkOrder.'">Passer ma commande du '.$dateOrder.'</a>' ; | |||
$messageAutoText .= 'Suivez ce lien pour passer votre commande du '.$dateOrder.' : | |||
'.$linkOrder ; | |||
if($integrateProductList) { | |||
$productsArray = Product::find() | |||
->where([ | |||
'id_producer' => $producer->id, | |||
]) | |||
->andWhere('status >= :status') | |||
->addParams(['status' => Product::STATUS_OFFLINE]) | |||
->innerJoinWith(['productDistribution' => function($query) use($distribution) { | |||
$query->andOnCondition([ | |||
'product_distribution.id_distribution' => $distribution->id, | |||
'product_distribution.active' => 1 | |||
]); | |||
}]) | |||
->orderBy('product.name ASC') | |||
->all(); | |||
if(count($productsArray) > 0) { | |||
$messageAutoHtml .= '<br /><br />Produits disponibles : <br /><ul>' ; | |||
$messageAutoText .= ' | |||
Produits disponibles : | |||
' ; | |||
foreach($productsArray as $product) { | |||
$productDescription = $product->name ; | |||
if(strlen($product->description)) { | |||
$productDescription .= ' / '.$product->description ; | |||
} | |||
if($product->price) { | |||
$productDescription .= ' / '.Price::format($this->productSolver->getPriceWithTax($product)) ; | |||
$productDescription .= ' ('. $this->productSolver->strUnit($product, UnitDefinition::WORDING_UNIT).')' ; | |||
} | |||
$messageAutoText .= '- '.$productDescription.' | |||
' ; | |||
$messageAutoHtml .= '<li>'.Html::encode($productDescription).'</li>' ; | |||
} | |||
$messageAutoHtml .= '</ul>' ; | |||
} | |||
} | |||
} | |||
if($producer) { | |||
$fromEmail = $this->producerSolver->getProducerEmailPlatform($producer) ; | |||
$fromName = $producer->name ; | |||
$linkProducer = 'https://'.$producer->slug.'.souke.fr'; | |||
$linkUnsubscribe = \Yii::$app->urlManagerProducer->createAbsoluteUrl(['newsletter/unsubscribe', 'slug_producer' => $producer->slug]); | |||
// Message inscription newsletter | |||
$messageAutoText .= " | |||
-- | |||
Boutique : ".$linkProducer." | |||
Me désinscrire : ".$linkUnsubscribe; | |||
$messageAutoHtml .= "<br /><br />--<br>"; | |||
$messageAutoHtml .= "Boutique : <a href=\"".$linkProducer."\">".$linkProducer."</a><br>"; | |||
$messageAutoHtml .= "Me désinscrire : <a href=\"".$linkUnsubscribe."\">".$linkUnsubscribe."</a>"; | |||
} | |||
else { | |||
$fromEmail = 'contact@souke.fr' ; | |||
$fromName = 'Souke' ; | |||
} | |||
$htmlContent = $message.$messageAutoHtml; | |||
$textContent = strip_tags($message).$messageAutoText; | |||
return $this->emailBuilder->instanciateEmail( | |||
$fromName, | |||
$fromEmail, | |||
$subject, | |||
$htmlContent, | |||
$textContent | |||
); | |||
} | |||
} |
@@ -0,0 +1,43 @@ | |||
<?php | |||
namespace domain\Communication\Email; | |||
use common\components\BulkMailer\BulkMailerInterface; | |||
use domain\_\AbstractService; | |||
class EmailModule extends AbstractService | |||
{ | |||
public function getEntityFqcn(): string | |||
{ | |||
return ''; | |||
} | |||
public function getServices(): array | |||
{ | |||
return [ | |||
EmailBuilder::class, | |||
ContactListResolver::class, | |||
EmailGenerator::class, | |||
]; | |||
} | |||
public function getBuilder(): EmailBuilder | |||
{ | |||
return EmailBuilder::getInstance(); | |||
} | |||
public function getGenerator(): EmailGenerator | |||
{ | |||
return EmailGenerator::getInstance(); | |||
} | |||
public function getContactListResolver(): ContactListResolver | |||
{ | |||
return ContactListResolver::getInstance(); | |||
} | |||
public function getBulkMailer(): BulkMailerInterface | |||
{ | |||
return \Yii::$app->bulkMailer; | |||
} | |||
} |
@@ -38,8 +38,8 @@ | |||
namespace domain\Document\DeliveryNote; | |||
use common\components\ActiveDataProvider; | |||
use common\helpers\GlobalParam; | |||
use yii\data\ActiveDataProvider; | |||
class DeliveryNoteSearch extends DeliveryNote | |||
{ | |||
@@ -109,18 +109,21 @@ class DeliveryNoteSearch extends DeliveryNote | |||
$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]); | |||
// filtre avec ou sans facture | |||
$withInvoice = $this->with_invoice; | |||
if(is_numeric($withInvoice)) { | |||
$dataProvider->filterByCallback(function($model) use ($withInvoice) { | |||
$deliveryNoteModule = DeliveryNoteModule::getInstance(); | |||
$filterModel = false; | |||
if(is_numeric($withInvoice)) { | |||
$invoice = $deliveryNoteModule->getSolver()->getInvoice($model); | |||
if(($withInvoice && !$invoice) || (!$withInvoice && $invoice)) { | |||
$filterModel = true; | |||
} | |||
} | |||
} | |||
return $filterModel; | |||
}); | |||
} | |||
$dataProvider->setModels($models); | |||
return $dataProvider; | |||
} |
@@ -38,20 +38,20 @@ | |||
namespace domain\Document\Invoice; | |||
use common\components\ActiveDataProvider; | |||
use common\helpers\GlobalParam; | |||
use yii\data\ActiveDataProvider; | |||
use yii\data\Sort; | |||
class InvoiceSearch extends Invoice | |||
{ | |||
var $paid = null; | |||
var $is_paid; | |||
var $username; | |||
public function rules() | |||
{ | |||
return [ | |||
[['paid'], 'safe'], | |||
[['is_sent'], 'boolean'], | |||
[[], 'safe'], | |||
[['is_sent', 'is_paid'], 'boolean'], | |||
[['comment', 'address', 'status', 'username', 'date'], 'string'], | |||
[['name', 'reference', 'username'], 'string', 'max' => 255], | |||
]; | |||
@@ -84,7 +84,6 @@ class InvoiceSearch extends Invoice | |||
->with($optionsSearch['with']) | |||
->joinWith($optionsSearch['join_with']) | |||
->where(['invoice.id_producer' => GlobalParam::getCurrentProducerId()]) | |||
//->orderBy('invoice.status ASC, invoice.reference DESC') | |||
->orderBy($sort->orders) | |||
->groupBy('invoice.id'); | |||
@@ -116,7 +115,7 @@ class InvoiceSearch extends Invoice | |||
} | |||
// filtre envoyé | |||
if(!is_null($this->is_sent) && is_numeric($this->is_sent)) { | |||
if(is_numeric($this->is_sent)) { | |||
if($this->is_sent) { | |||
$query->andWhere(['invoice.is_sent' => 1]); | |||
} | |||
@@ -125,18 +124,21 @@ class InvoiceSearch extends Invoice | |||
} | |||
} | |||
// 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]); | |||
// filtre payé / non payé | |||
$isPaid = $this->is_paid; | |||
if(is_numeric($isPaid)) { | |||
$dataProvider->filterByCallback(function($model) use ($isPaid) { | |||
$filterModel = false; | |||
$invoiceModule = InvoiceModule::getInstance(); | |||
if(is_numeric($isPaid)) { | |||
$isInvoicePaid = $invoiceModule->getSolver()->isInvoicePaid($model); | |||
if(($isPaid && !$isInvoicePaid) || (!$isPaid && $isInvoicePaid)) { | |||
$filterModel = true; | |||
} | |||
} | |||
} | |||
return $filterModel; | |||
}); | |||
} | |||
$dataProvider->setModels($models); | |||
return $dataProvider; | |||
} |
@@ -55,6 +55,8 @@ class Feature extends ActiveRecordCommon | |||
const ALIAS_SPONSORSHIP = 'sponsorship'; | |||
const ALIAS_BRIDGE_EVOLIZ = 'bridge_evoliz'; | |||
const ALIAS_PRODUCT_ACCESSORY = 'product_accessory'; | |||
const ALIAS_FORUM = 'forum'; | |||
const ALIAS_AUTOMATIC_EMAIL = 'automatic_email'; | |||
/** | |||
* @inheritdoc |
@@ -25,6 +25,8 @@ class FeatureDefinition extends AbstractDefinition | |||
Feature::ALIAS_BRIDGE_SUMUP => "Pont vers SumUp", | |||
Feature::ALIAS_BRIDGE_EVOLIZ => "Pont vers Evoliz", | |||
Feature::ALIAS_PRODUCT_ACCESSORY => 'Accessoires produits', | |||
Feature::ALIAS_FORUM => 'Forum', | |||
Feature::ALIAS_AUTOMATIC_EMAIL => 'Emails automatiques' | |||
]; | |||
} | |||
} |
@@ -31,6 +31,8 @@ use domain\PointSale\UserPointSale\UserPointSaleRepository; | |||
use domain\Producer\Producer\Producer; | |||
use domain\Producer\Producer\ProducerRepository; | |||
use domain\Producer\Producer\ProducerSolver; | |||
use domain\Product\Product\Product; | |||
use domain\Product\Product\ProductRepository; | |||
use domain\Product\Product\ProductSolver; | |||
use domain\Subscription\Subscription\Subscription; | |||
use domain\Subscription\Subscription\SubscriptionBuilder; | |||
@@ -67,6 +69,7 @@ class OrderBuilder extends AbstractBuilder | |||
protected DocumentSolver $documentSolver; | |||
protected ProducerSolver $producerSolver; | |||
protected OrderStatusRepository $orderStatusRepository; | |||
protected ProductRepository $productRepository; | |||
public function loadDependencies(): void | |||
{ | |||
@@ -93,6 +96,7 @@ class OrderBuilder extends AbstractBuilder | |||
$this->documentSolver = $this->loadService(DocumentSolver::class); | |||
$this->producerSolver = $this->loadService(ProducerSolver::class); | |||
$this->orderStatusRepository = $this->loadService(OrderStatusRepository::class); | |||
$this->productRepository = $this->loadService(ProductRepository::class); | |||
} | |||
public function instanciateOrder(Distribution $distribution): Order | |||
@@ -118,6 +122,62 @@ class OrderBuilder extends AbstractBuilder | |||
return $order; | |||
} | |||
public function instanciateOrderFromProductOrdersArray(array $productOrdersArray, Order $order = null): Order | |||
{ | |||
if(!$order) { | |||
$order = new Order(); | |||
$this->initOrderStatus($order); | |||
} | |||
$productOrderObjectArray = []; | |||
foreach($productOrdersArray as $idProduct => $productOrder) { | |||
if($productOrder['quantity']) { | |||
$product = $this->productRepository->findOneProductById($idProduct); | |||
$productOrderObject = $this->productOrderBuilder->instanciateProductOrder( | |||
$order, | |||
$product, | |||
$productOrder['quantity'], | |||
0 | |||
); | |||
$productOrderObjectArray[] = $productOrderObject; | |||
} | |||
} | |||
$order->populateRelation('productOrder', $productOrderObjectArray); | |||
return $order; | |||
} | |||
public function addOrUpdateOrIgnoreOrderFromArray(Order $orderOverride, array $ordersArray, bool $ignoreOrderCurrent = false): array | |||
{ | |||
$override = false; | |||
foreach($ordersArray as $key => $order) { | |||
if($order->id == $orderOverride->id) { | |||
if($ignoreOrderCurrent) { | |||
unset($ordersArray[$key]); | |||
} | |||
else { | |||
$ordersArray[$key] = $orderOverride; | |||
$override = true; | |||
} | |||
} | |||
} | |||
if(!$override && !$ignoreOrderCurrent) { | |||
$ordersArray[] = $orderOverride; | |||
} | |||
return $ordersArray; | |||
} | |||
public function deleteProductOrderQuantity(Order $order, Product $product, float $quantity) | |||
{ | |||
foreach($order->productOrder as $productOrder) { | |||
if($productOrder->id_product == $product->id) { | |||
$productOrder->quantity -= $quantity; | |||
} | |||
} | |||
} | |||
public function initDateUpdate(Order $order) | |||
{ | |||
$order->date_update = date('Y-m-d H:i:s');; |
@@ -9,27 +9,51 @@ use domain\Feature\Feature\Feature; | |||
use domain\Feature\Feature\FeatureChecker; | |||
use domain\Product\Accessory\Accessory; | |||
use domain\Product\Product\Product; | |||
use yii\base\ErrorException; | |||
use domain\Product\Product\ProductSolver; | |||
class OrderResolver extends AbstractResolver | |||
{ | |||
protected ProductSolver $productSolver; | |||
protected OrderSolver $orderSolver; | |||
protected OrderRepository $orderRepository; | |||
protected ProductDistributionRepository $productDistributionRepository; | |||
protected FeatureChecker $featureChecker; | |||
protected OrderBuilder $orderBuilder; | |||
protected array $ordersArrayCachedByDistribution = []; | |||
public function loadDependencies(): void | |||
{ | |||
$this->productSolver = $this->loadService(ProductSolver::class); | |||
$this->orderSolver = $this->loadService(OrderSolver::class); | |||
$this->orderRepository = $this->loadService(OrderRepository::class); | |||
$this->productDistributionRepository = $this->loadService(ProductDistributionRepository::class); | |||
$this->featureChecker = $this->loadService(FeatureChecker::class); | |||
$this->orderBuilder = $this->loadService(OrderBuilder::class); | |||
} | |||
public function getProductQuantityByDistribution(Product $product, Distribution $distribution): float | |||
public function getProductQuantityByDistribution( | |||
Product $product, | |||
Distribution $distribution, | |||
bool $inNumberOfPieces = false, | |||
Order $orderCurrent = null | |||
): float | |||
{ | |||
$ordersArray = $this->orderRepository->findOrdersByDistribution($distribution); | |||
return $this->orderSolver->getProductQuantity($product, $ordersArray); | |||
if(!isset($this->ordersArrayCachedByDistribution[$distribution->id])) { | |||
$this->ordersArrayCachedByDistribution[$distribution->id] = $this->orderRepository->findOrdersByDistribution($distribution); | |||
} | |||
$ordersArray = $this->ordersArrayCachedByDistribution[$distribution->id]; | |||
if($orderCurrent) { | |||
$ordersArray = $this->orderBuilder->addOrUpdateOrIgnoreOrderFromArray($orderCurrent, $ordersArray); | |||
} | |||
$productQuantity = $this->orderSolver->getProductQuantity($product, $ordersArray); | |||
if($inNumberOfPieces) { | |||
$productQuantity = $this->productSolver->getNumberOfPieces($productQuantity, $product); | |||
} | |||
return $productQuantity; | |||
} | |||
public function getSmallestQuantityAccessory(Product $product): ?int | |||
@@ -48,7 +72,6 @@ class OrderResolver extends AbstractResolver | |||
public function getProductQuantityMax(Product $product, Distribution $distribution): ?float | |||
{ | |||
$productDistribution = $this->productDistributionRepository->findOneProductDistribution($distribution, $product); | |||
// @TODO : gérer via une exception | |||
if(!$productDistribution) { | |||
return null; | |||
} | |||
@@ -58,25 +81,52 @@ class OrderResolver extends AbstractResolver | |||
if($this->featureChecker->isEnabled(Feature::ALIAS_PRODUCT_ACCESSORY)) { | |||
$smallestQuantityAccessory = $this->getSmallestQuantityAccessory($product); | |||
if (!is_null($smallestQuantityAccessory)) { | |||
$quantityMax = is_null($quantityMax) ? $smallestQuantityAccessory : min($quantityMax, $smallestQuantityAccessory); | |||
$smallestQuantityAccessory = $this->productSolver->getWeightOrNumberOfPieces($smallestQuantityAccessory, $product); | |||
$quantityMax = is_null($quantityMax) ? $smallestQuantityAccessory | |||
: min($quantityMax, $smallestQuantityAccessory); | |||
} | |||
} | |||
return $quantityMax; | |||
} | |||
public function getQuantityOfAccessoryAvailableInDistribution(Accessory $accessory, Distribution $distribution): int | |||
public function getProductQuantityMaxOrderable(Product $product, Distribution $distribution, Order $orderCurrent = null): ?float | |||
{ | |||
$productQuantity = $this->orderSolver->getProductQuantity($product, $orderCurrent ? [$orderCurrent] : []); | |||
$productQuantityRemaining = $this->getProductQuantityRemaining($product, $distribution, $orderCurrent); | |||
if(is_null($productQuantityRemaining)) { | |||
return null; | |||
} | |||
return $productQuantity + $productQuantityRemaining; | |||
} | |||
public function getQuantityOfAccessoryAvailableInDistribution( | |||
Accessory $accessory, | |||
Distribution $distribution, | |||
Order $orderCurrent = null | |||
): int | |||
{ | |||
$quantityOfAccessoryUsed = 0; | |||
foreach($accessory->getProductAccessories() as $productAccessory) { | |||
$quantityOfAccessoryUsed += $this->getProductQuantityByDistribution($productAccessory->getProduct(), $distribution); | |||
$quantityOfAccessoryUsed += $this->getProductQuantityByDistribution( | |||
$productAccessory->getProduct(), | |||
$distribution, | |||
true, | |||
$orderCurrent | |||
); | |||
} | |||
return $accessory->getQuantity() - $quantityOfAccessoryUsed; | |||
} | |||
public function getSmallestQuantityAccessoryAvailable(Product $product, Distribution $distribution): ?int | |||
public function getSmallestQuantityAccessoryAvailable( | |||
Product $product, | |||
Distribution $distribution, | |||
Order $orderCurrent = null | |||
): ?int | |||
{ | |||
$smallestQuantity = null; | |||
@@ -84,7 +134,8 @@ class OrderResolver extends AbstractResolver | |||
foreach ($product->getProductAccessories() as $productAccessory) { | |||
$quantityAccessoryAvailableInDistribution = $this->getQuantityOfAccessoryAvailableInDistribution( | |||
$productAccessory->getAccessory(), | |||
$distribution | |||
$distribution, | |||
$orderCurrent | |||
); | |||
$smallestQuantity = is_null($smallestQuantity) ? $quantityAccessoryAvailableInDistribution | |||
: min($smallestQuantity, $quantityAccessoryAvailableInDistribution); | |||
@@ -94,21 +145,35 @@ class OrderResolver extends AbstractResolver | |||
return $smallestQuantity; | |||
} | |||
public function getProductQuantityRemaining(Product $product, Distribution $distribution): ?float | |||
public function getProductQuantityRemaining( | |||
Product $product, | |||
Distribution $distribution, | |||
Order $orderCurrent = null | |||
): ?float | |||
{ | |||
$productDistribution = $this->productDistributionRepository->findOneProductDistribution($distribution, $product); | |||
if(!$productDistribution) { | |||
return null; | |||
} | |||
$quantityOrder = $this->getProductQuantityByDistribution($product, $distribution); | |||
$quantityOrder = $this->getProductQuantityByDistribution( | |||
$product, | |||
$distribution, | |||
false, | |||
$orderCurrent | |||
); | |||
$quantityRemaining = is_null($productDistribution->quantity_max) ? null | |||
: ($productDistribution->quantity_max - $quantityOrder); | |||
// Limitation par nombre d'accessoires disponibles | |||
if($this->featureChecker->isEnabled(Feature::ALIAS_PRODUCT_ACCESSORY)) { | |||
$smallestQuantityAccessoryAvailable = $this->getSmallestQuantityAccessoryAvailable($product, $distribution); | |||
$smallestQuantityAccessoryAvailable = $this->getSmallestQuantityAccessoryAvailable( | |||
$product, | |||
$distribution, | |||
$orderCurrent | |||
); | |||
if (!is_null($smallestQuantityAccessoryAvailable)) { | |||
$smallestQuantityAccessoryAvailable = $this->productSolver->getWeightOrNumberOfPieces($smallestQuantityAccessoryAvailable, $product); | |||
$quantityRemaining = is_null($quantityRemaining) ? $smallestQuantityAccessoryAvailable | |||
: min($quantityRemaining, $smallestQuantityAccessoryAvailable); | |||
} |
@@ -3,6 +3,7 @@ | |||
namespace domain\Producer\Producer; | |||
use common\helpers\GlobalParam; | |||
use domain\Distribution\Distribution\Distribution; | |||
use domain\User\User\User; | |||
use domain\_\AbstractService; | |||
use domain\_\SolverInterface; | |||
@@ -276,6 +277,11 @@ class ProducerSolver extends AbstractService implements SolverInterface | |||
return $producer->option_leave_period_start && $producer->option_leave_period_end; | |||
} | |||
public function isOnLeavePeriodByDistribution(Distribution $distribution): bool | |||
{ | |||
return $this->isOnLeavePeriod($distribution->producer, \DateTime::createFromFormat('Y-m-d', $distribution->date)); | |||
} | |||
public function isOnLeavePeriod(Producer $producer, \DateTime $date = null): bool | |||
{ | |||
if(!$date) { |
@@ -20,7 +20,7 @@ class Accessory extends ActiveRecordCommon | |||
public function rules() | |||
{ | |||
return [ | |||
[['name', 'id_producer'], 'required'], | |||
[['name', 'id_producer', 'quantity'], 'required'], | |||
[['name'], 'string', 'max' => 255], | |||
[['quantity', 'id_producer'], 'integer'], | |||
[['selected_products_ids'], 'safe'], | |||
@@ -65,12 +65,12 @@ class Accessory extends ActiveRecordCommon | |||
return $this; | |||
} | |||
public function getQuantity(): ?int | |||
public function getQuantity(): int | |||
{ | |||
return $this->quantity; | |||
} | |||
public function setQuantity(?int $quantity): self | |||
public function setQuantity(int $quantity): self | |||
{ | |||
$this->quantity = $quantity; | |||
return $this; |
@@ -25,6 +25,25 @@ class ProductSolver extends AbstractService implements SolverInterface | |||
$this->pointSaleSolver = $this->loadService(PointSaleSolver::class); | |||
} | |||
public function getWeightOrNumberOfPieces(float $quantity, Product $product): float | |||
{ | |||
// Poids total | |||
if($product->unit != 'piece') { | |||
$quantity = ($quantity * $product->weight) / 1000; | |||
} | |||
return $quantity; | |||
} | |||
public function getNumberOfPieces(float $quantity, Product $product): int | |||
{ | |||
if($product->unit != 'piece') { | |||
$quantity = $quantity / ($product->weight / 1000); | |||
} | |||
return $quantity; | |||
} | |||
public function getUnitCoefficient(Product $product): int | |||
{ | |||
return $this->unitSolver->getUnitCoefficient($product->unit); |
@@ -80,6 +80,7 @@ class User extends ActiveRecordCommon implements IdentityInterface | |||
var $send_mail_welcome; | |||
var $trust_alert; | |||
var $trust_alert_comment; | |||
var $exclusive_access_selected_points_sale; | |||
/** | |||
* @inheritdoc | |||
@@ -108,7 +109,7 @@ 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', 'problem_receiving_emails'], 'boolean'], | |||
'send_mail_welcome', 'trust_alert', 'newsletter_souke', 'problem_receiving_emails', 'exclusive_access_selected_points_sale'], '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'], | |||
@@ -159,7 +160,8 @@ class User extends ActiveRecordCommon implements IdentityInterface | |||
'trust_alert_comment' => 'Commentaire', | |||
'newsletter_souke' => "S'abonner à l'infolettre de Souke", | |||
'problem_receiving_emails' => "Rencontre des problèmes pour recevoir les emails", | |||
'note_emails' => "Note emails" | |||
'note_emails' => "Note emails", | |||
'exclusive_access_selected_points_sale' => "Accès exclusif aux points de vente sélectionnés" | |||
]; | |||
} | |||
@@ -94,6 +94,7 @@ class UserSearch extends User | |||
. '`user`.name_legal_person, ' | |||
. '`user`.type, ' | |||
. '`user`.problem_receiving_emails, ' | |||
. '`user`.note_emails, ' | |||
. '(SELECT COUNT(*) FROM `order` WHERE `user`.id = `order`.id_user) AS count_orders'); | |||
if($producer) { |
@@ -63,7 +63,7 @@ class UserProducer extends ActiveRecordCommon | |||
return [ | |||
[['id_user', 'id_producer'], 'required'], | |||
[['id_user', 'id_producer', 'product_price_percent'], 'integer'], | |||
[['active', 'bookmark', 'credit_active', 'newsletter', 'trust_alert'], 'boolean'], | |||
[['active', 'bookmark', 'credit_active', 'newsletter', 'trust_alert', 'exclusive_access_selected_points_sale'], 'boolean'], | |||
[['credit', 'product_price_percent'], 'double'], | |||
[['trust_alert_comment'], 'string'] | |||
]; | |||
@@ -83,6 +83,7 @@ class UserProducer extends ActiveRecordCommon | |||
'product_price_percent' => 'Prix produits : pourcentage', | |||
'trust_alert' => 'Alert confiance', | |||
'trust_alert_comment' => 'Commentaire', | |||
'exclusive_access_selected_points_sale' => "Accès exclusif aux points de vente sélectionnés" | |||
]; | |||
} | |||
@@ -196,4 +197,15 @@ class UserProducer extends ActiveRecordCommon | |||
return $this; | |||
} | |||
public function getExclusiveAccessSelectedPointsSale(): ?bool | |||
{ | |||
return $this->exclusive_access_selected_points_sale; | |||
} | |||
public function setExclusiveAccessSelectedPointsSale(?bool $exclusiveAccessSelectedPointsSale): self | |||
{ | |||
$this->exclusive_access_selected_points_sale = $exclusiveAccessSelectedPointsSale; | |||
return $this; | |||
} | |||
} |
@@ -10,4 +10,23 @@ class UserProducerSolver extends AbstractSolver | |||
{ | |||
return $userProducer->credit < 0 || $userProducer->credit > 0; | |||
} | |||
public function filterPointsSaleByExclusiveAccess(UserProducer $userProducer, array $pointsSaleArray = []): array | |||
{ | |||
if(!$userProducer->getExclusiveAccessSelectedPointsSale()) { | |||
return $pointsSaleArray; | |||
} | |||
$filteredPointsSaleArray = []; | |||
foreach($pointsSaleArray as $pointSale) { | |||
$idPointSale = is_array($pointSale) ? $pointSale['id'] : $pointSale->id; | |||
foreach($userProducer->user->userPointSale as $userPointSale) { | |||
if($idPointSale == $userPointSale->pointSale->id) { | |||
$filteredPointsSaleArray[] = $pointSale; | |||
} | |||
} | |||
} | |||
return $filteredPointsSaleArray; | |||
} | |||
} |
@@ -7,18 +7,16 @@ $userCurrent = $this->getUserCurrent(); | |||
?> | |||
<?php if($producerManager->isProducerSignupOpen()): ?> | |||
<?php if(Yii::$app->user->isGuest || $userModule->getSolver()->isStatusUser($userCurrent)): ?> | |||
<a class="btn btn-primary" | |||
href="<?= Yii::$app->user->isGuest ? \Yii::$app->urlManagerFrontend->createUrl(['site/signup', 'type' => 'producer']) : \Yii::$app->urlManagerFrontend->createUrl(['site/signup-producer']); ?>"> | |||
<i class="bi bi-shop"></i> Je crée mon espace producteur | |||
</a><br /> | |||
<?php if($producerManager->getMaximumNumberProducers()): ?> | |||
<strong><?= ($producerSignupRemainingPlaces); ?></strong> | |||
place<?php if($producerSignupRemainingPlaces > 1): ?>s<?php endif; ?> restante<?php if($producerSignupRemainingPlaces > 1): ?>s<?php endif; ?> | |||
<?php endif; ?> | |||
<br /> | |||
<br /> | |||
<a class="btn btn-primary" | |||
href="<?= Yii::$app->user->isGuest ? \Yii::$app->urlManagerFrontend->createUrl(['site/signup', 'type' => 'producer']) : \Yii::$app->urlManagerFrontend->createUrl(['site/signup-producer']); ?>"> | |||
<i class="bi bi-shop"></i> Je crée mon espace producteur | |||
</a><br /> | |||
<?php if($producerManager->getMaximumNumberProducers()): ?> | |||
<strong><?= ($producerSignupRemainingPlaces); ?></strong> | |||
place<?php if($producerSignupRemainingPlaces > 1): ?>s<?php endif; ?> restante<?php if($producerSignupRemainingPlaces > 1): ?>s<?php endif; ?> | |||
<?php endif; ?> | |||
<br /> | |||
<br /> | |||
<?php else: ?> | |||
<div class="label label-warning label-producer-signup-closed"> | |||
La plateforme n'accueille pas de nouveaux producteurs pour le moment |
@@ -93,16 +93,16 @@ use yii\helpers\Html; | |||
<div class="panel panel-default"> | |||
<div class="panel-heading"> | |||
<h3 class="panel-title"> | |||
<h3 class="panel-title" id="modules"> | |||
<i class="bi bi-boxes"></i> | |||
Modules | |||
</h3> | |||
</div> | |||
<div class="panel-body"> | |||
<div class="alert alert-dark" role="alert"> | |||
Retrouvez ici les modules payants de Souke correspondant aux développements qui n'ont pas encore | |||
été totalement financés et aux fonctionnalités nécessitant une configuration spécifique. Contactez-moi | |||
pour demander l'activation d'un module. | |||
Retrouvez ici les modules payants de Souke correspondant à des fonctionnalités en lien avec des | |||
plateformes externes ou à des développements répondant à des besoins spécifiques. N'hésitez pas à | |||
<a href="<?= Yii::$app->urlManager->createUrl(['site/contact']) ?>">me contacter</a> pour en savoir plus ou pour demander l'activation d'un module. | |||
</div> | |||
<table class="table table-striped table-bordered"> | |||
<thead> |
@@ -44,11 +44,6 @@ $producerModule = $this->getProducerModule(); | |||
?> | |||
<div class="site-iamproducer"> | |||
<div class="panel panel-primary panel-padding-large"> | |||
<!--<div class="panel-heading"> | |||
<h2 class="panel-title"> | |||
Vous avez besoin d'un outil pour vous organiser au quotidien ? | |||
</h2> | |||
</div>--> | |||
<div class="panel-body"> | |||
<div class="hook"> | |||
Vous êtes producteur et vous souhaitez améliorer votre organisation au quotidien ?<br /> |
@@ -61,10 +61,10 @@ $this->setMeta('description', "Découvrez les fonctionnalités du logiciel, les | |||
</div> | |||
<div class="row"> | |||
<?= block_feature("calendar3", "Planification des jours de distributions"); ?> | |||
<?= block_feature("download", "Récapitulatif des commandes par jour de distribution (PDF et CSV), génération d'étiquettes (PDF)"); ?> | |||
<?= block_feature("download", "Récapitulatif des commandes par jour de distribution (PDF et CSV)<br />Génération d'étiquettes simple (PDF)<br />Génération d'étiquettes avec format spécifique (PDF) *"); ?> | |||
</div> | |||
<div class="row"> | |||
<?= block_feature("basket", "Gestion des produits, catégories et prix spécifiques"); ?> | |||
<?= block_feature("basket", "Gestion des produits, catégories et prix spécifiques<br />Gestion des accessoires produits *"); ?> | |||
<?php $featureSharedPointSaleDescription = ''; | |||
if($featureChecker->isEnabled(Feature::ALIAS_SHARED_POINT_SALE)): | |||
$featureSharedPointSaleDescription .= "<br />Partage de points de vente entre plusieurs producteurs pour des distributions communes"; | |||
@@ -77,7 +77,7 @@ $this->setMeta('description', "Découvrez les fonctionnalités du logiciel, les | |||
</div> | |||
<div class="row"> | |||
<?= block_feature("piggy-bank", "Système de cagnotte permettant la comptabilisation des paiements"); ?> | |||
<?= block_feature("credit-card", "Paiement en ligne possible via la plateforme <strong>Stripe</strong> pour que les clients puissent alimenter leur cagnotte de manière autonome"); ?> | |||
<?= block_feature("credit-card", "Paiement en ligne via la plateforme <strong>Stripe</strong> pour le paiement des commandes ou l'alimentation des cagnottes *"); ?> | |||
</div> | |||
<div class="row"> | |||
<?= block_feature("megaphone", "Communication facilitée avec les clients via l'envoi d'emails en masse"); ?> | |||
@@ -85,8 +85,12 @@ $this->setMeta('description', "Découvrez les fonctionnalités du logiciel, les | |||
</div> | |||
<div class="row"> | |||
<?= block_feature("graph-up", "Statistiques et rapports de vente"); ?> | |||
<?= block_feature("cloud-arrow-up", "Exports vers les logiciels <strong>Evoliz</strong> (comptabilité) et <strong>SumUp</strong> (caisse)"); ?> | |||
<?= block_feature("cloud-arrow-up", "Exports vers les logiciels <strong>Evoliz</strong> (comptabilité) et <strong>SumUp</strong> (caisse) *"); ?> | |||
</div> | |||
<p> | |||
* <a href="#modules">Modules payants</a> | |||
</p> | |||
</div> | |||
</div> | |||
@@ -170,10 +170,6 @@ class OrderController extends ProducerBaseController | |||
$idProducer = $producer->id; | |||
$order = new Order; | |||
if ($idProducer) { | |||
$this->_verifyProducerActive($idProducer); | |||
} | |||
if ($order->load($posts)) { | |||
$user = $this->getUserCurrent(); | |||
@@ -445,8 +441,9 @@ class OrderController extends ProducerBaseController | |||
) && isset($unitsArray[$product->id])) ? $unitsArray[$product->id] : $product->unit; | |||
$coefficient = Product::$unitsArray[$unit]['coefficient']; | |||
$quantity = ((float)$posts['products'][$product->id]) / $coefficient; | |||
if ($availableProducts[$product->id]['quantity_max'] && $quantity > $availableProducts[$product->id]['quantity_remaining']) { | |||
$quantity = $availableProducts[$product->id]['quantity_remaining']; | |||
$quantityRemaining = $orderModule->getResolver()->getProductQuantityRemaining($product, $order->distribution); | |||
if(!is_null($quantityRemaining) && $quantity > $quantityRemaining) { | |||
$quantity = $quantityRemaining; | |||
} | |||
$productOrder->quantity = $quantity; | |||
$productOrder->price = $productModule->getPrice($product, [ | |||
@@ -613,15 +610,16 @@ class OrderController extends ProducerBaseController | |||
return 0; | |||
} | |||
public function actionAjaxInfos(string $date = '', int $pointSaleId = 0) | |||
public function actionAjaxInfos(string $date = '', int $pointSaleId = 0, string $productsJson = null, bool $loadingProducts = false) | |||
{ | |||
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON; | |||
$productsArray = json_decode($productsJson, true); | |||
$user = GlobalParam::getCurrentUser(); | |||
$producer = $this->getProducerCurrent(); | |||
$pointSale = $this->getPointSaleModule()->findOnePointSaleById($pointSaleId); | |||
$order = $this->getOrderUser($date, $pointSale); | |||
return $this->buildJsonAjaxInfos($date, $producer, $pointSale, $user, $order); | |||
return $this->buildJsonAjaxInfos($date, $producer, $pointSale, $user, $order, $productsArray, $loadingProducts); | |||
} | |||
public function buildJsonAjaxInfos( | |||
@@ -629,7 +627,9 @@ class OrderController extends ProducerBaseController | |||
Producer $producer, | |||
PointSale $pointSale = null, | |||
User $user = null, | |||
Order $order = null | |||
Order $order = null, | |||
array $productsArray = null, | |||
bool $loadingProducts = false | |||
) | |||
{ | |||
$orderModule = $this->getOrderModule(); | |||
@@ -649,7 +649,7 @@ class OrderController extends ProducerBaseController | |||
$json['distribution'] = $distribution; | |||
$json['points_sale'] = $this->ajaxInfosPointsSale($producer, $distribution); | |||
$json['categories'] = $this->ajaxInfosProductCategories($producer); | |||
$json['products'] = $this->ajaxInfosProducts($producer, $distribution, $pointSale, $user, $order); | |||
$json['products'] = $this->ajaxInfosProducts($producer, $distribution, $pointSale, $user, $order, $productsArray); | |||
if ($order) { | |||
$json['order'] = array_merge($order->getAttributes(), [ | |||
@@ -817,6 +817,7 @@ class OrderController extends ProducerBaseController | |||
$orderModule = $this->getOrderModule(); | |||
$user = GlobalParam::getCurrentUser(); | |||
$userProducer = $this->getUserProducerModule()->getRepository()->findOneUserProducer($user); | |||
$pointsSaleArray = PointSale::find(); | |||
if ($distribution) { | |||
@@ -911,6 +912,12 @@ class OrderController extends ProducerBaseController | |||
} | |||
} | |||
// Filtre par accès exclusif aux points de vente | |||
$pointsSaleArray = $this->getUserProducerModule()->getSolver()->filterPointsSaleByExclusiveAccess( | |||
$userProducer, | |||
$pointsSaleArray | |||
); | |||
return $pointsSaleArray; | |||
} | |||
@@ -931,7 +938,8 @@ class OrderController extends ProducerBaseController | |||
Distribution $distribution, | |||
PointSale $pointSale = null, | |||
User $user = null, | |||
Order $order = null | |||
Order $order = null, | |||
array $productsFormArray = null | |||
) | |||
{ | |||
$unitModule = $this->getUnitModule(); | |||
@@ -960,6 +968,20 @@ class OrderController extends ProducerBaseController | |||
$productsArrayFilter = $productModule->filterProductsByPointSale($productsArray, $pointSale); | |||
$orderCurrent = $order; | |||
if(count($productsFormArray)) { | |||
$productOrdersArray = []; | |||
foreach($productsFormArray as $idProduct => $quantityProduct) { | |||
if($idProduct) { | |||
$productObject1 = $productModule->getRepository()->findOneProductById($idProduct); | |||
$productOrdersArray[$idProduct] = [ | |||
'quantity' => $quantityProduct / $productModule->getSolver()->getUnitCoefficient($productObject1) | |||
]; | |||
} | |||
} | |||
$orderCurrent = $orderModule->getBuilder()->instanciateOrderFromProductOrdersArray($productOrdersArray, $orderCurrent); | |||
} | |||
$indexProduct = 0; | |||
foreach ($productsArrayFilter as $key => &$product) { | |||
$productObject = $product; | |||
@@ -974,8 +996,6 @@ class OrderController extends ProducerBaseController | |||
] | |||
); | |||
$coefficient_unit = Product::$unitsArray[$product['unit']]['coefficient']; | |||
if (is_null($product['photo']) || strlen($product['photo']) == 0) { | |||
$product['photo'] = ''; | |||
} | |||
@@ -984,33 +1004,30 @@ class OrderController extends ProducerBaseController | |||
$product['photo'] = Image::getThumbnailSmall($product['photo']); | |||
} | |||
$product['quantity_max'] = (isset($product['productDistribution']) && isset($product['productDistribution'][0])) ? $product['productDistribution'][0]['quantity_max'] : null; | |||
$quantityOrder = $orderModule->getProductQuantity($productObject, $ordersArray); | |||
$product['quantity_ordered'] = $quantityOrder; | |||
$product['quantity_remaining'] = $product['quantity_max'] - $quantityOrder; | |||
$coefficientUnit = Product::$unitsArray[$product['unit']]['coefficient']; | |||
$product['coefficient_unit'] = $coefficientUnit; | |||
$product['quantity_max'] = $orderModule->getResolver()->getProductQuantityMaxOrderable($productObject, $distribution, $orderCurrent); | |||
$product['quantity_remaining'] = $orderModule->getResolver()->getProductQuantityRemaining($productObject, $distribution, $orderCurrent); | |||
$product['wording_unit'] = $unitModule->getSolver()->strUnit($product['unit'], UnitDefinition::WORDING_UNIT, true); | |||
$product['wording_unit_ref'] = $unitModule->getSolver()->strUnit($product['unit'], UnitDefinition::WORDING_SHORT, true); | |||
$quantityOrderUser = $orderModule->getSolver()->getProductQuantity($productObject, $orderCurrent ? [$orderCurrent] : [], true); | |||
$product['quantity_form'] = $quantityOrderUser * $coefficientUnit; | |||
if($product['quantity_remaining'] < 0 && $product['quantity_form']) { | |||
$product['quantity_form'] = $product['quantity_form'] + ($product['quantity_remaining'] * $coefficientUnit); | |||
$orderModule->getBuilder()->deleteProductOrderQuantity($orderCurrent, $productObject, abs($product['quantity_remaining'])); | |||
$product['quantity_remaining'] = $orderModule->getResolver()->getProductQuantityRemaining($productObject, $distribution, $orderCurrent); | |||
} | |||
$product['wording_unit'] = $unitModule->getSolver()->strUnit($product['unit'], 'wording_unit', true); | |||
if ($order) { | |||
$quantityOrderUser = $orderModule->getProductQuantity($productObject, [$order], true); | |||
$product['quantity_ordered'] = $quantityOrder; | |||
$product['quantity_remaining'] = $product['quantity_max'] - $quantityOrder + $quantityOrderUser; | |||
$product['quantity_form'] = $quantityOrderUser * $coefficient_unit; | |||
foreach ($order->productOrder as $productOrder) { | |||
if ($productOrder->id_product == $product['id']) { | |||
$product['wording_unit'] = $productModule->getSolver()->strUnit($productOrder->product, 'wording_unit', true); | |||
$product['step'] = $productOrder->step; | |||
} | |||
} | |||
} else { | |||
$product['quantity_form'] = 0; | |||
$product['wording_unit'] = $unitModule->getSolver()->strUnit($product['unit'], 'wording_unit', true); | |||
} | |||
$product['coefficient_unit'] = $coefficient_unit; | |||
if ($product['quantity_remaining'] < 0) { | |||
$product['quantity_remaining'] = 0; | |||
} | |||
$product['index'] = $indexProduct++; | |||
} | |||
@@ -1,341 +0,0 @@ | |||
<?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\OrderModule; | |||
use domain\Producer\Producer\Producer; | |||
use domain\Producer\Producer\ProducerModule; | |||
use yii\helpers\Html; | |||
use yii\widgets\ActiveForm; | |||
$producerModule = ProducerModule::getInstance(); | |||
$orderModule = OrderModule::getInstance(); | |||
?> | |||
<div class="order-form"> | |||
<?php | |||
$form = ActiveForm::begin([ | |||
'enableClientScript' => false | |||
]); | |||
?> | |||
<?php | |||
if(count($distributionDaysArray) <= 1) : | |||
?> | |||
<div class="alert alert-warning">Aucun jour de production n'a été programmé par le producteur.</div> | |||
<?php endif; ?> | |||
<?php if($idProducer && count($distributionDaysArray) > 1): ?> | |||
<div id="step-date" class="col-md-6"> | |||
<?= $form->field($model, 'id_distribution')->label('')->hiddenInput(); ?> | |||
<?php if (isset($model->id)): ?> | |||
<div class="date-order"><span><?php echo date('d/m/Y', strtotime($distribution->date)); ?></span></div> | |||
<?= Html::hiddenInput('id_order', $model->id,['id'=>'id-order']); ?> | |||
<?= Html::hiddenInput('paid_amount', $orderModule->getOrderAmount($model, Order::AMOUNT_PAID),['id'=>'paid-amount']); ?> | |||
<?php endif; ?> | |||
<div id="datepicker-distribution" <?php if (isset($model->id)): ?>style="display:none"<?php endif; ?>> | |||
</div> | |||
<?php if (!isset($model->id)): ?> | |||
<br /> | |||
<?php endif; ?> | |||
<div id="dates" style="display:none;"> | |||
<?php | |||
foreach ($distributionDaysArray as $idDistribution => $day) { | |||
if ($day != '--') { | |||
echo '<div><span class="date">' . $day . '</span><span class="id_distribution">' . $idDistribution. '</span></div>'; | |||
} | |||
} | |||
?> | |||
</div> | |||
<div class="clr"></div> | |||
<div id="orders-in-progress" style="display:none;"> | |||
<?php foreach ($ordersArray as $order): ?> | |||
<?php echo '<div class="order" data-iddistribution="' . $order->id_distribution. '" data-id="' . $order->id . '" data-href="' . \Yii::$app->urlManager->createUrl(['order/update', 'id' => $order->id, 'id_producer' => $order->distribution->id_producer]) . '"></div>'; ?> | |||
<?php endforeach; ?> | |||
</div> | |||
<div id="has-order-in-progress" style="display:none;" class="alert alert-danger">Vous avez déjà une commande en cours pour cette date. <a href="#">Cliquez ici</a> pour la modifier.</div> | |||
</div> | |||
<div class="col-md-6"> | |||
<?php if(strlen($producer->order_infos)): ?> | |||
<div id="order-infos"> | |||
<?= nl2br(Html::encode($producer->order_infos)) ?> | |||
</div> | |||
<?php endif; ?> | |||
</div> | |||
<div class="clr"></div> | |||
<div id="block-points-sale"> | |||
<h3 id="step-point-sale"><span><?= $producerModule->getPointSaleWording($producer); ?></span></h3> | |||
<?= | |||
$form->field($model, 'id_point_sale') | |||
->label('') | |||
->hiddenInput(); | |||
?> | |||
<input type="hidden" id="livraison" value="<?php if (!is_null($distribution) && $distribution->delivery): ?>1<?php else: ?>0<?php endif; ?>" /> | |||
<ul id="points-sale" class="blocks"> | |||
<?php | |||
foreach ($pointsSaleArray as $pointSale) { | |||
$comment = '' ; | |||
if(isset($pointSale->userPointSale) && is_array($pointSale->userPointSale) && count($pointSale->userPointSale)) | |||
{ | |||
foreach($pointSale->userPointSale as $userPointSale) | |||
{ | |||
if($userPointSale->id_user == GlobalParam::getCurrentUserId() && strlen($userPointSale->comment)) | |||
{ | |||
$comment = '<div class="comment"><span>'.Html::encode($userPointSale->comment).'</span></div>' ; | |||
} | |||
} | |||
} | |||
$htmlCode = '' ; | |||
$dataCode = '0' ; | |||
$code = '' ; | |||
if(strlen($pointSale->code)) | |||
{ | |||
if(!isset($model->id_point_sale) || $model->id_point_sale != $pointSale->id) | |||
{ | |||
$htmlCode .= '<span class="glyphicon glyphicon-lock"></span> ' ; | |||
$dataCode = '1' ; | |||
} | |||
else { | |||
$code = $pointSale->code ; | |||
} | |||
} | |||
echo '<li class="block point-sale point-sale-' . $pointSale->id . '" data-code="'.$dataCode.'" data-credit="'.(int) $pointSale->payment_method_credit.'"><div class="contenu">' . | |||
'<span style="display:none;" class="id">' . $pointSale->id . '</span>' . | |||
'<div class="name">' .$htmlCode. Html::encode($pointSale->name) . '</div>' . | |||
'<div class="address">à ' . Html::encode($pointSale->locality) . '</div>' . | |||
$comment . | |||
'<input type="hidden" name="code_point_sale_'.$pointSale->id.'" value="'.$code.'" />'. | |||
'</div></li>'; | |||
} | |||
?> | |||
</ul> | |||
<div class="clr"></div> | |||
<div id="step-infos-point-sale"> | |||
<?php | |||
foreach ($pointsSaleArray as $pointSale) { | |||
echo '<div class="alert alert-warning infos-point-sale infos-point-sale-'.$pointSale->id.'"><h4>Infos : <span>'.Html::encode($pointSale->name).'</span></h4>' . | |||
'<div class="jour jour-1">' . $pointSale->getStrInfos('monday') . '</div>' . | |||
'<div class="jour jour-2">' . $pointSale->getStrInfos('tuesday') . '</div>' . | |||
'<div class="jour jour-3">' . $pointSale->getStrInfos('wednesday') . '</div>' . | |||
'<div class="jour jour-4">' . $pointSale->getStrInfos('thursday') . '</div>' . | |||
'<div class="jour jour-5">' . $pointSale->getStrInfos('friday') . '</div>' . | |||
'<div class="jour jour-6">' . $pointSale->getStrInfos('saturday') . '</div>' . | |||
'<div class="jour jour-0">' . $pointSale->getStrInfos('sunday') . '</div>' . | |||
'</div>' ; | |||
} | |||
?> | |||
</div> | |||
<div class="clr"></div> | |||
</div> | |||
<div id="products"> | |||
<h3 id="step-products"><span>Produits</span></h3> | |||
<?php // erreur ?> | |||
<?php if (Yii::$app->session->getFlash('error')): ?> | |||
<div class="alert alert-danger"><div class="icon"></div><?= \Yii::$app->session->getFlash('error'); ?></div> | |||
<?php endif; ?> | |||
<div id=""> | |||
<div class="alert alert-warning unavailable">Produit indisponible pour ce point de vente</div> | |||
<table class="table table-bordered" id="table-products"> | |||
<thead> | |||
<tr> | |||
<th class="th-photo">Photo</th> | |||
<th class="product">Produit</th> | |||
<th class="price-unit">Prix unitaire</th> | |||
<th class="column-quantity">Quantité</th> | |||
<th class="total">Total</th> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
<?php foreach ($productsArray as $product): ?> | |||
<?php | |||
$quantity = 0; | |||
if (isset($selectedProducts[$product->id])) { | |||
$quantity = $selectedProducts[$product->id]; | |||
} | |||
?> | |||
<tr class="product-<?php echo $product->id; ?>" data-no-limit="<?php if(!$product->quantity_max): ?>1<?php else: ?>0<?php endif; ?>" data-quantity-max="<?= $quantity ?>" <?php if (count($availableProducts) && !$availableProducts[$product->id]['active']): ?>style="display:none;"<?php endif; ?>> | |||
<td class="td-photo"> | |||
<?php if (strlen($product->photo) && file_exists(dirname(__FILE__).'/../../web/uploads/' . $product->photo)): ?><a href="<?= \Yii::$app->urlManager->getBaseUrl() . '/uploads/' . $product->photo ?>" data-lightbox="product-<?php echo $product->id; ?>"><img class="photo img-rounded" src="<?= \Yii::$app->urlManager->getBaseUrl() . '/uploads/' . $product->photo ?>" alt="Photo <?= Html::encode($product->name); ?>" /></a><?php endif; ?> | |||
</td> | |||
<td class="produit"> | |||
<span class="name"><?= Html::encode($product->name); ?></span> - <span class="description"><?= Html::encode($product->getDescription()); ?></span><br /> | |||
<span class="recipe"><?= Html::encode($product->recipe); ?></span> | |||
</td> | |||
<td class="price-unit"> | |||
<span class="price"><?= Price::format($product->price); ?></span> € | |||
</td> | |||
<td class="column-quantity"> | |||
<div class="input-group" <?php if (isset($availableProducts[$product->id]) && $availableProducts[$product->id]['quantity_remaining'] == 0 && $quantity == 0): ?>style="display:none;"<?php endif; ?>> | |||
<span class="input-group-btn"> | |||
<button type="button" class="btn btn-default move-quantity minus">-</button> | |||
</span> | |||
<input type="text" value="<?php if (isset($selectedProducts[$product->id])): echo $selectedProducts[$product->id]; | |||
else: ?>0<?php endif; ?>" readonly name="Product[product_<?php echo $product->id; ?>]" class="quantity form-control"> | |||
<span class="input-group-btn"> | |||
<button type="button" class="btn btn-default move-quantity plus">+</button> | |||
</span> | |||
</div> | |||
<div class="quantity-remaining">Reste <span class="nb"><?php if (isset($availableProducts[$product->id])): echo $availableProducts[$product->id]['quantity_remaining'] + $quantity; | |||
endif; ?></span> <?php echo Html::encode(strtolower($product->name)); ?>(s) | |||
</div> | |||
<div class="unavailable">Épuisé</div> | |||
</td> | |||
<td class="total"><strong></strong></td> | |||
</tr> | |||
<?php endforeach; ?> | |||
</tbody> | |||
<tfoot> | |||
<tr> | |||
<td></td> | |||
<td></td> | |||
<td></td> | |||
<td id="total-order"><strong></strong></td> | |||
</tr> | |||
</tfoot> | |||
</table> | |||
</div> | |||
</div> | |||
<?php if($idProducer): ?> | |||
<?php | |||
$producer = Producer::findOne($idProducer); | |||
?> | |||
<div id="bar-fixed" class="<?php if($producer->credit): ?>credit<?php else: ?>no-credit<?php endif; ?>"> | |||
<div class="container"> | |||
<?php if (isset($model->id) && $orderModule->getSolver()->isOrderStatusValid($model)): ?> | |||
<a href="<?php echo \Yii::$app->urlManager->createUrl(['order/cancel', 'id' => $model->id]); ?>" class="btn btn-danger cancel-order">Annuler ma commande</a> | |||
<?php endif; ?> | |||
<span id="total-order-bottom"><span></span> €</span> | |||
<?= Html::submitButton('<span class="glyphicon glyphicon-comment"></span> Commentaire', ['class' => 'btn btn-default btn-comment', 'data-placement' => 'top', 'data-toggle' => 'tooltip', 'data-original-title' => 'Ajouter un commentaire']) ?> | |||
<?php | |||
if($producer->credit): | |||
$linkCredit = '<a class="info-credit" href="'.Yii::$app->urlManager->createUrl(['site/credit']) .'" data-toggle="tooltip" data-placement="bottom" title="En savoir plus sur le Crédit"><span class="glyphicon glyphicon-info-sign"></span></a>' ; ; | |||
?> | |||
<div id="checkbox-credit" > | |||
<?php if($credit || $orderModule->getAmount($order, Order::AMOUNT_PAID)): ?> | |||
<?= Html::checkbox('credit', true, ['label' => 'Utiliser mon Crédit <span class="the-credit" data-toggle="tooltip" data-placement="top" data-original-title="Vous avez actuellement '.number_format($credit,2).' € sur votre compte Crédit">'.number_format($credit,2).' €</span><br /><span class="info"></span>']) ?> | |||
<?= Html::hiddenInput('amount_credit', $credit, ['id' => 'amount-credit']) ?> | |||
<?= Html::hiddenInput('str_amount_credit', number_format($credit,2).' €', ['id' => 'str-amount-credit']) ?> | |||
<?php else: ?> | |||
<div id="info-credit-empty"> | |||
Votre compte Crédit est vide <?= $linkCredit ?> | |||
</div> | |||
<?php endif; ?> | |||
<div id="credit-disabled">Le Crédit est désactivé<br /> pour ce point de vente <?= $linkCredit ?></div> | |||
</div> | |||
<div class="clr"></div> | |||
<?php endif; ?> | |||
<?= $form->field($model, 'comment')->textarea(['rows' => 3, 'placeholder' => 'Un commentaire ?'])->label(''); ?> | |||
<div id="block-confirm-order"> | |||
<?php if($model->date_delete): $strButtonConfirmOrder = 'Réactiver ma commande' ; else: $strButtonConfirmOrder = 'Valider ma commande' ; endif; ?> | |||
<?= Html::submitButton('<span class="glyphicon glyphicon-ok"></span> '.$strButtonConfirmOrder, ['class' => 'btn btn-primary confirm-order']) ?> | |||
</div> | |||
<?php endif; ?> | |||
</div> | |||
</div> | |||
<?php | |||
// id_etablissement | |||
endif; ?> | |||
<?php ActiveForm::end(); ?> | |||
<!-- modal code point de vente --> | |||
<div class="modal fade" id="modal-code" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> | |||
<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">×</span></button> | |||
<h4 class="modal-title" id="myModalLabel">Code d'accès</h4> | |||
</div> | |||
<div class="modal-body"> | |||
<div class="alert alert-warning"> | |||
Ce point de vente nécessite un code d'accès. | |||
</div> | |||
<form action="index.php?r=order/validate-code-point-sale" method="post"> | |||
<input type="hidden" value="" name="idPointSale" id="id-point-sale" /> | |||
<div class="form-group field-code required"> | |||
<label class="control-label" for="code">Code d'accès :</label> | |||
<input type="password" class="form-control" id="code" name="code" /> | |||
<p class="help-block help-block-error" style="display:none;">Code incorrect</p> | |||
</div> | |||
<div class="form-group"> | |||
<button type="submit" class="btn btn-primary">Valider</button> | |||
</div> | |||
</form> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div><!-- commande-form --> |
@@ -354,7 +354,7 @@ $this->setMeta('description', $producerModule->getSeoGenerator()->generateMetaDe | |||
<span v-if="product.weight">({{ product.weight }} g)</span> | |||
</span> | |||
<div> | |||
<span v-if="product.quantity_max > 0 && ((product.quantity_form / product.coefficient_unit == product.quantity_remaining) || ((product.quantity_remaining * product.coefficient_unit) - product.quantity_form) < product.step)" | |||
<span v-if="product.quantity_max != null && (product.quantity_remaining <= 0 || product.quantity_remaining * product.coefficient_unit < product.step)" | |||
class="badge bg-danger">Épuisé</span> | |||
</div> | |||
<div class="description" v-if="product.description.length"> | |||
@@ -402,7 +402,7 @@ $this->setMeta('description', $producerModule->getSeoGenerator()->generateMetaDe | |||
<button class="btn btn-secondary btn-moins" | |||
type="button" | |||
@click="productQuantityClick(product, product.unit == 'piece' ? -1 : -parseFloat(product.step))" | |||
:disabled="product.quantity_form == 0"> | |||
:disabled="product.quantity_form == 0 || loadingProducts"> | |||
<i class="bi bi-dash-lg"></i></button> | |||
</span> | |||
<input type="text" v-model="product.quantity_form" | |||
@@ -412,7 +412,7 @@ $this->setMeta('description', $producerModule->getSeoGenerator()->generateMetaDe | |||
<button class="btn btn-secondary btn-plus" | |||
type="button" | |||
@click="productQuantityClick(product, product.unit == 'piece' ? 1 : parseFloat(product.step))" | |||
:disabled="product.quantity_form == product.quantity_remaining && product.quantity_max > 0"> | |||
:disabled="loadingProducts || (product.quantity_remaining != null && product.quantity_remaining <= 0) || (product.quantity_max != null && product.quantity_form >= product.quantity_max * product.coefficient_unit)"> | |||
<i class="bi bi-plus-lg"></i></button> | |||
</span> | |||
</div> |
@@ -5,8 +5,12 @@ var app = new Vue({ | |||
data() { | |||
return Object.assign({ | |||
order: null, | |||
timerAjaxInfosProducts: false, | |||
xhr: false, | |||
loading: false, | |||
loadingProducts: false, | |||
loadingInit: true, | |||
cancelTokenSource: null, | |||
step: null, | |||
producer: null, | |||
user: null, | |||
@@ -75,7 +79,7 @@ var app = new Vue({ | |||
}, | |||
mounted: function() { | |||
this.initDate(); | |||
this.init('first'); | |||
this.init('first', false); | |||
}, | |||
methods: { | |||
initDate: function() { | |||
@@ -126,208 +130,239 @@ var app = new Vue({ | |||
} | |||
} | |||
}, | |||
init: function(type, oldStep, step) { | |||
init: function(type, loadingProducts) { | |||
var app = this ; | |||
this.loading = true ; | |||
if(loadingProducts) { | |||
if (app.timerAjaxInfosProducts) { | |||
clearTimeout(app.timerAjaxInfosProducts); | |||
} | |||
app.timerAjaxInfosProducts = setTimeout(function(app) { | |||
app.loadingProducts = true ; | |||
if(app.xhr) { | |||
app.xhr.abort(); | |||
} | |||
app.xhr = $.get('ajax-infos', { | |||
date : app.getDate(), | |||
pointSaleId: app.pointSaleActiveId ? app.pointSaleActiveId : (app.pointSaleActive ? app.pointSaleActive.id : 0), | |||
productsJson: JSON.stringify(app.getProductsArray()), | |||
loadingProducts: loadingProducts | |||
}, function(response) { | |||
app.products = response.products; | |||
app.loadingProducts = false; | |||
}, 'json'); | |||
}.bind(this, app), 300); | |||
return; | |||
} | |||
else { | |||
this.loading = true ; | |||
} | |||
if(app.isChangeState('date', 'date', 'point-sale')) { | |||
app.pointSaleActive = null ; | |||
app.products = [] ; | |||
} | |||
axios.get("ajax-infos",{params: { | |||
date : this.getDate(), | |||
pointSaleId: this.pointSaleActiveId ? this.pointSaleActiveId : (this.pointSaleActive ? this.pointSaleActive.id : 0) | |||
//app.cancelTokenSource = axios.CancelToken.source(); | |||
axios.get("ajax-infos",{ | |||
//cancelToken: app.cancelTokenSource.token, | |||
params: { | |||
date : this.getDate(), | |||
pointSaleId: this.pointSaleActiveId ? this.pointSaleActiveId : (this.pointSaleActive ? this.pointSaleActive.id : 0), | |||
productsJson: this.getProductsArray(), | |||
loadingProducts: loadingProducts | |||
}}) | |||
.catch(function (thrown) { | |||
if (axios.isCancel(thrown)) { | |||
} | |||
}) | |||
.then(function(response) { | |||
app.calendar.attrs = []; | |||
app.calendar.availableDates = []; | |||
var distributions = response.data.distributions; | |||
app.distributions = distributions; | |||
if (distributions.length) { | |||
var arrayDate; | |||
var highlightStyle = { | |||
style: { | |||
background: 'white', | |||
border: 'solid 2px #198754' | |||
}, | |||
contentStyle: { | |||
color: '#198754' | |||
} | |||
}; | |||
for (var i = 0; i < distributions.length; i++) { | |||
app.calendar.attrs.push({ | |||
highlight: highlightStyle, | |||
dates: distributions[i].date | |||
}); | |||
if(response) { | |||
app.calendar.attrs = []; | |||
app.calendar.availableDates = []; | |||
var distributions = response.data.distributions; | |||
app.distributions = distributions; | |||
if (distributions.length) { | |||
var arrayDate; | |||
var highlightStyle = { | |||
style: { | |||
background: 'white', | |||
border: 'solid 2px #198754' | |||
}, | |||
contentStyle: { | |||
color: '#198754' | |||
} | |||
}; | |||
for (var i = 0; i < distributions.length; i++) { | |||
app.calendar.attrs.push({ | |||
highlight: highlightStyle, | |||
dates: distributions[i].date | |||
}); | |||
arrayDate = distributions[i].date.split('-'); | |||
app.calendar.availableDates.push({ | |||
highlight: highlightStyle, | |||
start: new Date(arrayDate[0], arrayDate[1] - 1, arrayDate[2]), | |||
end: new Date(arrayDate[0], arrayDate[1] - 1, arrayDate[2]) | |||
}); | |||
arrayDate = distributions[i].date.split('-'); | |||
app.calendar.availableDates.push({ | |||
highlight: highlightStyle, | |||
start: new Date(arrayDate[0], arrayDate[1] - 1, arrayDate[2]), | |||
end: new Date(arrayDate[0], arrayDate[1] - 1, arrayDate[2]) | |||
}); | |||
} | |||
} | |||
} | |||
if(response.data.leave_period) { | |||
leavePeriodStartDateArray = response.data.leave_period.start.split('-'); | |||
leavePeriodEndDateArray = response.data.leave_period.end.split('-'); | |||
if (response.data.leave_period) { | |||
leavePeriodStartDateArray = response.data.leave_period.start.split('-'); | |||
leavePeriodEndDateArray = response.data.leave_period.end.split('-'); | |||
app.calendar.attrs.push({ | |||
highlight: { | |||
style: { | |||
//background: '#E09F3E' | |||
background: 'gray' | |||
app.calendar.attrs.push({ | |||
highlight: { | |||
style: { | |||
//background: '#E09F3E' | |||
background: 'gray' | |||
}, | |||
contentStyle: { | |||
color: 'white' | |||
} | |||
}, | |||
contentStyle: { | |||
color: 'white' | |||
} | |||
}, | |||
dates: { | |||
start: new Date(leavePeriodStartDateArray[0], leavePeriodStartDateArray[1] - 1, leavePeriodStartDateArray[2]), | |||
end: new Date(leavePeriodEndDateArray[0], leavePeriodEndDateArray[1] - 1, leavePeriodEndDateArray[2]) | |||
}, | |||
popover: { | |||
label: 'En congé', | |||
hideIndicator: true, | |||
isInteractive: true | |||
}, | |||
}); | |||
} | |||
dates: { | |||
start: new Date(leavePeriodStartDateArray[0], leavePeriodStartDateArray[1] - 1, leavePeriodStartDateArray[2]), | |||
end: new Date(leavePeriodEndDateArray[0], leavePeriodEndDateArray[1] - 1, leavePeriodEndDateArray[2]) | |||
}, | |||
popover: { | |||
label: 'En congé', | |||
hideIndicator: true, | |||
isInteractive: true | |||
}, | |||
}); | |||
} | |||
if (response.data.distribution) { | |||
app.distribution = response.data.distribution; | |||
} | |||
if (response.data.distribution) { | |||
app.distribution = response.data.distribution; | |||
} | |||
var orders = []; | |||
if (response.data.orders) { | |||
orders = response.data.orders; | |||
} | |||
var orders = []; | |||
if (response.data.orders) { | |||
orders = response.data.orders; | |||
} | |||
if (orders.length) { | |||
for (var i = 0; i < orders.length; i++) { | |||
arrayDate = orders[i].date_distribution.split('-'); | |||
var dateOrder = new Date(arrayDate[0], arrayDate[1] - 1, arrayDate[2]); | |||
if (app.isAvailableDate(dateOrder)) { | |||
app.calendar.attrs.push({ | |||
highlight: { | |||
style: { | |||
background: '#198754' | |||
if (orders.length) { | |||
for (var i = 0; i < orders.length; i++) { | |||
arrayDate = orders[i].date_distribution.split('-'); | |||
var dateOrder = new Date(arrayDate[0], arrayDate[1] - 1, arrayDate[2]); | |||
if (app.isAvailableDate(dateOrder)) { | |||
app.calendar.attrs.push({ | |||
highlight: { | |||
style: { | |||
background: '#198754' | |||
}, | |||
contentStyle: { | |||
color: 'white' | |||
} | |||
}, | |||
contentStyle: { | |||
color: 'white' | |||
} | |||
}, | |||
popover: { | |||
label: orders[i].pointSale.name + ' (' + app.formatPrice(orders[i].amount_total)+')', | |||
hideIndicator: true, | |||
isInteractive: true | |||
}, | |||
dates: orders[i].date_distribution | |||
}); | |||
popover: { | |||
label: orders[i].pointSale.name + ' (' + app.formatPrice(orders[i].amount_total) + ')', | |||
hideIndicator: true, | |||
isInteractive: true | |||
}, | |||
dates: orders[i].date_distribution | |||
}); | |||
} | |||
} | |||
} | |||
} | |||
app.producer = response.data.producer; | |||
app.user = response.data.user; | |||
app.useCredit = response.data.producer.use_credit_checked_default; | |||
app.producer = response.data.producer; | |||
app.user = response.data.user; | |||
app.useCredit = response.data.producer.use_credit_checked_default; | |||
if (response.data.points_sale) { | |||
app.pointsSale = []; | |||
var orderPointSale = 0; | |||
for (var key in response.data.points_sale) { | |||
response.data.points_sale[key].order = orderPointSale++; | |||
app.pointsSale[response.data.points_sale[key].id] = response.data.points_sale[key]; | |||
if (response.data.points_sale) { | |||
app.pointsSale = []; | |||
var orderPointSale = 0; | |||
for (var key in response.data.points_sale) { | |||
response.data.points_sale[key].order = orderPointSale++; | |||
app.pointsSale[response.data.points_sale[key].id] = response.data.points_sale[key]; | |||
if(!app.pointsSaleCodes[response.data.points_sale[key].id]) { | |||
app.pointsSaleCodes[response.data.points_sale[key].id] = ''; | |||
Vue.set(app.pointsSaleCodes, response.data.points_sale[key].id, ''); | |||
if (!app.pointsSaleCodes[response.data.points_sale[key].id]) { | |||
app.pointsSaleCodes[response.data.points_sale[key].id] = ''; | |||
Vue.set(app.pointsSaleCodes, response.data.points_sale[key].id, ''); | |||
} | |||
} | |||
} | |||
} | |||
if(app.pointSaleActiveId) { | |||
app.pointSaleActive = app.getPointSale(app.pointSaleActiveId); | |||
} | |||
if(app.pointSaleActive) { | |||
if(app.producer.credit | |||
&& app.pointSaleActive.payment_method_credit | |||
&& (app.pointSaleActive.credit_functioning == 'mandatory' | |||
|| (app.pointSaleActive.credit_functioning == 'user' && app.user.credit_active) | |||
|| (app.pointSaleActive.credit_functioning == 'optional' && response.data.producer.use_credit_checked_default))) { | |||
app.paymentMethod = 'credit'; | |||
if (app.pointSaleActiveId) { | |||
app.pointSaleActive = app.getPointSale(app.pointSaleActiveId); | |||
} | |||
else if(app.pointSaleActive.payment_method_onsite) { | |||
app.paymentMethod = 'onsite'; | |||
if (app.pointSaleActive) { | |||
if (app.producer.credit | |||
&& app.pointSaleActive.payment_method_credit | |||
&& (app.pointSaleActive.credit_functioning == 'mandatory' | |||
|| (app.pointSaleActive.credit_functioning == 'user' && app.user.credit_active) | |||
|| (app.pointSaleActive.credit_functioning == 'optional' && response.data.producer.use_credit_checked_default))) { | |||
app.paymentMethod = 'credit'; | |||
} else if (app.pointSaleActive.payment_method_onsite) { | |||
app.paymentMethod = 'onsite'; | |||
} else if (app.pointSaleActive.payment_method_online) { | |||
app.paymentMethod = 'online'; | |||
} | |||
} | |||
else if(app.pointSaleActive.payment_method_online) { | |||
app.paymentMethod = 'online'; | |||
if (app.isChangeState('point-sale', 'point-sale', 'date')) { | |||
app.date = null; | |||
app.dateFormat = null; | |||
} | |||
} | |||
if(app.isChangeState('point-sale', 'point-sale', 'date')) { | |||
app.date = null ; | |||
app.dateFormat = null ; | |||
} | |||
// update order | |||
var updateOrder = false; | |||
if (app.isChangeState('date', 'point-sale', 'products') | |||
|| app.isChangeState('date', 'date', 'point-sale') | |||
|| app.isChangeState('point-sale', 'date', 'products') | |||
|| app.isChangeState('point-sale', 'point-sale', 'date')) { | |||
// update order | |||
var updateOrder = false ; | |||
if(app.isChangeState('date', 'point-sale', 'products') | |||
|| app.isChangeState('date', 'date', 'point-sale') | |||
|| app.isChangeState('point-sale', 'date', 'products') | |||
|| app.isChangeState('point-sale', 'point-sale', 'date')) { | |||
updateOrder = true; | |||
} | |||
updateOrder = true ; | |||
} | |||
if (updateOrder) { | |||
app.updateOrder(response); | |||
} | |||
if(updateOrder) { | |||
app.updateOrder(response); | |||
} | |||
if (type == 'first') { | |||
if (app.getDate() && app.pointSaleActive) { | |||
app.step = 'products'; | |||
if (response.data.products) { | |||
app.products = response.data.products; | |||
} | |||
if(type == 'first') { | |||
if(app.getDate() && app.pointSaleActive) { | |||
app.step = 'products' ; | |||
if(response.data.products) { | |||
app.products = response.data.products; | |||
app.updateOrder(response); | |||
} else if (app.producer.option_order_entry_point == 'point-sale') { | |||
app.step = 'point-sale'; | |||
} else if (app.getDate() && app.producer && app.producer.option_order_entry_point == 'date') { | |||
app.step = 'point-sale'; | |||
} else { | |||
app.step = 'date'; | |||
} | |||
app.updateOrder(response); | |||
} | |||
else if(app.producer.option_order_entry_point == 'point-sale') { | |||
app.step = 'point-sale' ; | |||
} | |||
else if(app.getDate() && app.producer && app.producer.option_order_entry_point == 'date') { | |||
app.step = 'point-sale' ; | |||
} | |||
else { | |||
app.step = 'date' ; | |||
} | |||
} | |||
if(response.data.categories) { | |||
app.categories = response.data.categories ; | |||
for(keyCategory in response.data.categories) { | |||
var category = response.data.categories[keyCategory]; | |||
if(category.id && app.countProductsByCategory(category)) { | |||
app.setCategoryCurrent(category, true) ; | |||
break; | |||
if (response.data.categories) { | |||
app.categories = response.data.categories; | |||
for (keyCategory in response.data.categories) { | |||
var category = response.data.categories[keyCategory]; | |||
if (category.id && app.countProductsByCategory(category)) { | |||
app.setCategoryCurrent(category, true); | |||
break; | |||
} | |||
} | |||
} | |||
} | |||
setTimeout(function() { | |||
app.responsive(); | |||
opendistrib_products(); | |||
}, 500); | |||
setTimeout(function () { | |||
app.responsive(); | |||
opendistrib_products(); | |||
}, 500); | |||
app.loading = false ; | |||
app.loadingInit = false ; | |||
app.loading = false; | |||
app.loadingProducts = false; | |||
app.loadingInit = false; | |||
} | |||
}); | |||
}, | |||
updateOrder: function(response) { | |||
@@ -412,7 +447,6 @@ var app = new Vue({ | |||
} | |||
}, | |||
changeStep: function(step) { | |||
this.errors = [] ; | |||
var oldStep = this.step ; | |||
@@ -423,7 +457,7 @@ var app = new Vue({ | |||
} | |||
if(!this.errors.length) { | |||
this.step = step ; | |||
this.init('basic', oldStep, step) ; | |||
this.init('basic', false) ; | |||
} | |||
}, | |||
dayClickList: function(event) { | |||
@@ -489,12 +523,15 @@ var app = new Vue({ | |||
this.nextStep() ; | |||
}, | |||
productQuantityClick: function(product, quantity) { | |||
if( this.products[product.index].quantity_form + quantity >= 0 && | |||
(this.products[product.index].quantity_form + quantity <= (this.products[product.index].quantity_remaining * this.products[product.index].coefficient_unit) || | |||
!this.products[product.index].quantity_max) | |||
if(this.products[product.index].quantity_form + quantity >= 0 | |||
&& (this.products[product.index].quantity_max == null | |||
|| this.products[product.index].quantity_form + quantity <= this.products[product.index].quantity_max * this.products[product.index].coefficient_unit) | |||
&& (this.products[product.index].quantity_remaining == null | |||
|| (quantity <= (this.products[product.index].quantity_remaining * this.products[product.index].coefficient_unit))) | |||
) { | |||
var theQuantity = parseFloat(this.products[product.index].quantity_form) + parseFloat(quantity); | |||
this.products[product.index].quantity_form = parseFloat(theQuantity.toFixed(2)) ; | |||
this.init('first', true); | |||
} | |||
}, | |||
oneProductOrdered: function() { | |||
@@ -652,14 +689,7 @@ var app = new Vue({ | |||
} | |||
// products | |||
var productsArray = {} ; | |||
for(var key in this.products) { | |||
if( this.products[key].quantity_form != null && | |||
this.products[key].quantity_form > 0) { | |||
productsArray[this.products[key].id] = this.products[key].quantity_form ; | |||
} | |||
} | |||
var productsArray = this.getProductsArray(); | |||
app.disableConfirmButton = true ; | |||
axios.post('ajax-process', { | |||
@@ -691,6 +721,16 @@ var app = new Vue({ | |||
} | |||
}); | |||
}, | |||
getProductsArray: function() { | |||
var productsArray = {} ; | |||
for(var key in this.products) { | |||
if( this.products[key].quantity_form != null && | |||
this.products[key].quantity_form > 0) { | |||
productsArray[this.products[key].id] = this.products[key].quantity_form ; | |||
} | |||
} | |||
return productsArray; | |||
}, | |||
checkProducts: function() { | |||
if(!this.oneProductOrdered()) { | |||
this.errors.push('Veuillez sélectionner au moins un produit.') ; |