@@ -0,0 +1,53 @@ | |||
<?php | |||
namespace backend\controllers; | |||
use yii\filters\AccessControl; | |||
class ProducerInvoiceController extends BackendController | |||
{ | |||
public function behaviors() | |||
{ | |||
return [ | |||
'access' => [ | |||
'class' => AccessControl::class, | |||
'rules' => [ | |||
[ | |||
'allow' => true, | |||
'roles' => ['@'], | |||
'matchCallback' => function ($rule, $action) { | |||
return | |||
$this->getParameterBag()->get('dolibarrApiKey') | |||
&& $this->getUserModule() | |||
->getAuthorizationChecker() | |||
->isGrantedAsProducer($this->getUserCurrent()); | |||
} | |||
], | |||
], | |||
], | |||
]; | |||
} | |||
public function actionIndex() | |||
{ | |||
return $this->render('index', [ | |||
'invoicesArray' => $this->getProducerModule() | |||
->getDolibarrUtils() | |||
->getDolibarrProducerInvoices($this->getProducerCurrent()) | |||
]); | |||
} | |||
public function actionDownload(int $idDolibarrInvoice) | |||
{ | |||
$documentDownload = \Yii::$app->dolibarrApi->downloadInvoice($idDolibarrInvoice); | |||
if($documentDownload) { | |||
return \Yii::$app->response->sendContentAsFile(base64_decode($documentDownload['content']), $documentDownload['filename'], [ | |||
'mimeType' => $documentDownload['content-type'] | |||
]); | |||
} | |||
else { | |||
$this->addFlash('error', 'Facture introuvable'); | |||
return $this->redirectReferer(); | |||
} | |||
} | |||
} |
@@ -256,7 +256,7 @@ class SubscriptionController extends BackendController | |||
$subscriptionModule = $this->getSubscriptionModule(); | |||
$distributionModule = $this-> getDistributionModule(); | |||
$subscription = $subscriptionModule->findOneSubscriptionById($idSubscription); | |||
$matchedDistributionsArray = $distributionModule->findDistributionsIncomingMatchWithSubscrtiption($subscription); | |||
$matchedDistributionsArray = $distributionModule->findDistributionsIncomingMatchWithSubscrtiption($subscription, true); | |||
if ($generate) { | |||
if ($update) { |
@@ -401,13 +401,6 @@ $this->setPageTitle('Distributions') ; | |||
<order-state-payment :order="order" :producer="producer"></order-state-payment> | |||
</a> | |||
<span class="glyphicon glyphicon-time" title="Débit automatique du crédit la veille de la distribution" v-if="order.amount != 0 && order.isCreditAutoPayment && (order.amount_paid == 0 || order.amount_paid < order.amount)"></span> | |||
<div v-if="order.amount_paid > 0 && order.amount_paid < order.amount"> | |||
<span class="glyphicon glyphicon-alert"></span> Reste à payer | |||
</div> | |||
<div v-if="order.amount_paid > order.amount"> | |||
<span class="glyphicon glyphicon-alert"></span> Surplus à rembourser | |||
</div> | |||
</template> | |||
</td> | |||
<td class="column-credit" v-if="!idActivePointSale || (pointSaleActive && pointSaleActive.credit == 1)"> |
@@ -8,7 +8,7 @@ $i = 0; | |||
foreach($ordersArray as $key => $order) { | |||
$index ++; | |||
echo $distributionShoppingCartLabelsPdfGenerator->getShoppingCartLabelAsHtml($order); | |||
echo $distributionShoppingCartLabelsPdfGenerator->getShoppingCartLabelAsHtml($order, $index); | |||
if($index == $shoppingCartLabelsPerColumn) { | |||
$index = 0; |
@@ -9,7 +9,8 @@ $userModule = $this->getUserModule(); | |||
$documentModule = $this->getDocumentModule(); | |||
$orderModule = $this->getOrderModule(); | |||
$displayPrices = Yii::$app->controller->getClass() != 'DeliveryNote' || (Yii::$app->controller->getClass() == 'DeliveryNote' && $producerModule->getConfig('document_display_prices_delivery_note')); | |||
$isDocumentDeliveryNote = $documentModule->getSolver()->isDocumentDeliveryNote($document); | |||
$displayPrices = !$isDocumentDeliveryNote || ($isDocumentDeliveryNote && $producerModule->getConfig('document_display_prices_delivery_note')); | |||
$displayProductDescription = $producerModule->getConfig('document_display_product_description'); | |||
$documentPriceDecimals = (int) $producerModule->getConfig('option_document_price_decimals'); | |||
@@ -152,6 +152,13 @@ $isUserCurrentGrantedAsProducer = $userModule->getAuthorizationChecker()->isGran | |||
['label' => 'Paramètres', 'icon' => 'cog', 'url' => ['/producer/update'], 'visible' => $isUserCurrentGrantedAsProducer], | |||
['label' => 'Accès', 'icon' => 'lock', 'url' => ['/access/index'], 'visible' => $isUserCurrentGrantedAsProducer], | |||
['label' => "Opendistrib", 'options' => ['class' => 'header'], 'visible' => $isUserCurrentGrantedAsProducer], | |||
[ | |||
'label' => 'Mes factures', | |||
'icon' => 'clone', | |||
'url' => ['/producer-invoice/index'], | |||
'visible' => $isUserCurrentGrantedAsProducer && Yii::$app->parameterBag->get('dolibarrApiKey'), | |||
'active' => Yii::$app->controller->id == 'producer-invoice', | |||
], | |||
[ | |||
'label' => 'Développement', | |||
'icon' => 'code', |
@@ -0,0 +1,82 @@ | |||
<?php | |||
/** | |||
Copyright La boîte à pain (2018) | |||
contact@opendistrib.net | |||
Ce logiciel est un programme informatique servant à aider les producteurs | |||
à distribuer leur production en circuits courts. | |||
Ce logiciel est régi par la licence CeCILL soumise au droit français et | |||
respectant les principes de diffusion des logiciels libres. Vous pouvez | |||
utiliser, modifier et/ou redistribuer ce programme sous les conditions | |||
de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA | |||
sur le site "http://www.cecill.info". | |||
En contrepartie de l'accessibilité au code source et des droits de copie, | |||
de modification et de redistribution accordés par cette licence, il n'est | |||
offert aux utilisateurs qu'une garantie limitée. Pour les mêmes raisons, | |||
seule une responsabilité restreinte pèse sur l'auteur du programme, le | |||
titulaire des droits patrimoniaux et les concédants successifs. | |||
A cet égard l'attention de l'utilisateur est attirée sur les risques | |||
associés au chargement, à l'utilisation, à la modification et/ou au | |||
développement et à la reproduction du logiciel par l'utilisateur étant | |||
donné sa spécificité de logiciel libre, qui peut le rendre complexe à | |||
manipuler et qui le réserve donc à des développeurs et des professionnels | |||
avertis possédant des connaissances informatiques approfondies. Les | |||
utilisateurs sont donc invités à charger et tester l'adéquation du | |||
logiciel à leurs besoins dans des conditions permettant d'assurer la | |||
sécurité de leurs systèmes et ou de leurs données et, plus généralement, | |||
à l'utiliser et l'exploiter dans les mêmes conditions de sécurité. | |||
Le fait que vous puissiez accéder à cet en-tête signifie que vous avez | |||
pris connaissance de la licence CeCILL, et que vous en avez accepté les | |||
termes. | |||
*/ | |||
use common\helpers\Price; | |||
use yii\helpers\Html; | |||
$this->setTitle('Mes factures') ; | |||
$this->addBreadcrumb($this->getTitle()) ; | |||
?> | |||
<?php if($invoicesArray && count($invoicesArray)): ?> | |||
<div class="callout callout-info"> | |||
<span class="glyphicon glyphicon-info-sign"></span> Les factures et les réglements sont saisis en début de mois. | |||
</div> | |||
<table class="table table-striped table-bordered"> | |||
<thead> | |||
<tr> | |||
<th>Date</th> | |||
<th>Référence</th> | |||
<th>Montant</th> | |||
<th>Statut</th> | |||
<th>Téléchargement</th> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
<?php foreach($invoicesArray as $invoice): ?> | |||
<tr> | |||
<td><?= date('d/m/Y', $invoice['date']); ?></td> | |||
<td><?= Html::encode($invoice['ref']); ?></td> | |||
<td><?= Price::format($invoice['total_ttc']); ?></td> | |||
<td><?= ($invoice['remaintopay'] > 0) ? '<span class="label label-warning">Impayée</span>' : '<span class="label label-success">Payée</span>'; ?></td> | |||
<td> | |||
<a class="btn btn-default" href="<?= $this->getUrlManagerBackend()->createUrl(['producer-invoice/download', 'idDolibarrInvoice' => $invoice['id']]); ?>"> | |||
<span class="glyphicon glyphicon-download-alt"></span> Télécharger | |||
</a> | |||
</td> | |||
</tr> | |||
<?php endforeach; ?> | |||
</tbody> | |||
</table> | |||
<?php else: ?> | |||
<div class="callout callout-info"> | |||
<span class="glyphicon glyphicon-info-sign"></span> Vous n'avez encore aucune facture. | |||
</div> | |||
<?php endif; ?> |
@@ -425,10 +425,8 @@ $this->addBreadcrumb($this->getTitle()); | |||
<?= $form->field($model, 'document_invoice_first_reference'); ?> | |||
<?= $form->field($model, 'document_delivery_note_prefix')->hint($hintKeywordsPrefix);; ?> | |||
<?= $form->field($model, 'document_delivery_note_first_reference'); ?> | |||
<?= $form->field($model, 'option_invoice_only_based_on_delivery_notes')->dropDownList([ | |||
0 => 'Non', | |||
1 => 'Oui' | |||
]); ?> | |||
<?= $form->field($model, 'delivery_note_automatic_validation')->dropDownList(Dropdown::noYesChoices()); ?> | |||
<?= $form->field($model, 'option_invoice_only_based_on_delivery_notes')->dropDownList(Dropdown::noYesChoices()); ?> | |||
<?= $form->field($model, 'option_document_width_logo') | |||
->dropDownList(Dropdown::numberChoices(50, 250, true, 'px', 50)); ?> | |||
<?= $form->field($model, 'document_display_orders_invoice')->dropDownList(Dropdown::noYesChoices()); ?> |
@@ -2,14 +2,44 @@ | |||
namespace common\components; | |||
use Psr\Http\Message\ResponseInterface; | |||
class DolibarrApi extends AbstractApi | |||
{ | |||
const RESOURCE_INVOICES = 'invoices'; | |||
const RESOURCE_PRODUCTS = 'products'; | |||
const RESOURCE_DOCUMENTS = 'documents'; | |||
public function getInvoicesByThirParty(int $idThirdParty) | |||
{ | |||
return $this->get(self::RESOURCE_INVOICES, [ | |||
'sortfield' => 't.rowid', | |||
'sortorder' => 'DESC', | |||
'thirdparty_ids' => $idThirdParty, | |||
'limit' => 24 | |||
]); | |||
} | |||
public function getInvoice(int $idInvoice) | |||
{ | |||
return $this->get(self::RESOURCE_INVOICES.'/'.$idInvoice, [ | |||
'contact_list' => 1 | |||
]); | |||
} | |||
public function downloadInvoice(string $idInvoice) | |||
{ | |||
$invoice = $this->getInvoice($idInvoice); | |||
if($invoice && isset($invoice['last_main_doc'])) { | |||
$originalFilename = str_replace('facture/', '', $invoice['last_main_doc']); | |||
return $this->get(self::RESOURCE_DOCUMENTS.'/download', [ | |||
'modulepart' => 'facture', | |||
'original_file' => $originalFilename | |||
]); | |||
} | |||
return null; | |||
} | |||
public function createInvoice(int $idUser) | |||
{ | |||
return $this->post(self::RESOURCE_INVOICES, [ |
@@ -198,7 +198,9 @@ return [ | |||
], | |||
DeliveryNote::class => [ | |||
// Order : assignation du bon de livraison aux commandes | |||
common\logic\Order\Order\Event\DeliveryNoteObserver::class | |||
common\logic\Order\Order\Event\DeliveryNoteObserver::class, | |||
// DeliveryNote : validation automatique des bons de livraison | |||
common\logic\Document\DeliveryNote\Event\DeliveryNoteObserver::class | |||
], | |||
Ticket::class => [ | |||
// User : envoi email nouveau ticket à l'administrateur |
@@ -37,7 +37,7 @@ | |||
*/ | |||
return [ | |||
'version' => '23.11.C', | |||
'version' => '23.12.A', | |||
'maintenanceMode' => false, | |||
'siteName' => 'Opendistrib', | |||
'adminEmail' => 'contact@opendistrib.net', |
@@ -40,6 +40,7 @@ namespace common\controllers; | |||
use common\components\BusinessLogic; | |||
use common\components\BusinessLogicTrait; | |||
use common\components\ParameterBag; | |||
use common\logic\User\User\Model\User; | |||
use yii; | |||
use yii\web\Response; | |||
@@ -111,6 +112,11 @@ class CommonController extends \yii\web\Controller | |||
{ | |||
return $this->redirect(Yii::$app->request->referrer ?: Yii::$app->homeUrl); | |||
} | |||
public function getParameterBag(): ParameterBag | |||
{ | |||
return Yii::$app->parameterBag; | |||
} | |||
} | |||
?> |
@@ -115,6 +115,10 @@ class DistributionReportPdfGenerator extends AbstractGenerator implements Distri | |||
table tr td { | |||
font-size: 13px ; | |||
} | |||
.payment-detail-remaining-surplus { | |||
font-size: 10px; | |||
} | |||
', | |||
'methods' => [ | |||
'SetHeader' => ['Commandes du ' . date('d/m/Y', strtotime($distribution->date))], | |||
@@ -289,7 +293,7 @@ class DistributionReportPdfGenerator extends AbstractGenerator implements Distri | |||
public function columnOrderAmount(Order $order): string | |||
{ | |||
$html = '<td><strong>'.number_format($order->amount_with_tax, 2) . ' €</strong>'; | |||
$html = '<td class="td-order-amount"><strong>'.number_format($order->amount_with_tax, 2) . ' €</strong>'; | |||
$paymentLabelPaid = $this->orderRepository->getPaymentLabelPaid($order); | |||
if($paymentLabelPaid && strlen($paymentLabelPaid)) { |
@@ -28,7 +28,7 @@ class DistributionReportTotalProductCsvGenerator extends AbstractGenerator imple | |||
public function generate(Distribution $distribution, bool $save = false) | |||
{ | |||
$datas = []; | |||
$productsArray = $this->productRepository->findProductsByDistribution($distribution); | |||
$productsArray = $this->productRepository->findProductsByDistribution($distribution, true,'product.order ASC'); | |||
$ordersArray = $this->orderRepository->findOrdersByDistribution($distribution); | |||
foreach($productsArray as $product) { |
@@ -115,7 +115,7 @@ class DistributionShoppingCartLabelsPdfGenerator extends AbstractGenerator imple | |||
return $order->pointSale && $order->pointSale->exclude_export_shopping_cart_labels; | |||
} | |||
public function getCss(bool $isSpecificFormat = false, float $specificFormatWith = 0, float $specificFormatHeight = 0): string | |||
public function getCss(bool $isSpecificFormat = false, float $specificFormatWidth = 0, float $specificFormatHeight = 0): string | |||
{ | |||
$css = ' | |||
@page { | |||
@@ -130,7 +130,7 @@ class DistributionShoppingCartLabelsPdfGenerator extends AbstractGenerator imple | |||
.shopping-cart-label .username { | |||
font-weight: bold; | |||
font-size: 13px; | |||
font-size: 16px; | |||
} | |||
.shopping-cart-label .point-sale { | |||
@@ -145,16 +145,19 @@ class DistributionShoppingCartLabelsPdfGenerator extends AbstractGenerator imple | |||
}'; | |||
if($isSpecificFormat) { | |||
$paddingShoppingCartLabel = 3; | |||
$specificFormatWith = $specificFormatWith - 2 * $paddingShoppingCartLabel; | |||
$specificFormatHeight = $specificFormatHeight - 2 * $paddingShoppingCartLabel; | |||
$paddingWidthShoppingCartLabel = 8; | |||
$paddingHeightShoppingCartLabel = 5; | |||
$specificFormatWidth = $specificFormatWidth - 2 * $paddingWidthShoppingCartLabel; | |||
$specificFormatHeight = $specificFormatHeight - 2 * $paddingHeightShoppingCartLabel + 1; | |||
$css .= ' | |||
.shopping-cart-label { | |||
box-sizing: border-box; | |||
padding: '.$paddingShoppingCartLabel.'mm; | |||
width: '.$specificFormatWith.'mm; | |||
padding-top: '.$paddingHeightShoppingCartLabel.'mm; | |||
padding-bottom: '.$paddingHeightShoppingCartLabel.'mm; | |||
padding-left: '.$paddingWidthShoppingCartLabel.'mm; | |||
padding-right: '.$paddingWidthShoppingCartLabel.'mm; | |||
width: '.$specificFormatWidth.'mm; | |||
height: '.$specificFormatHeight.'mm; | |||
display: block; | |||
float: left; | |||
@@ -178,9 +181,9 @@ class DistributionShoppingCartLabelsPdfGenerator extends AbstractGenerator imple | |||
return $css; | |||
} | |||
public function getShoppingCartLabelAsHtml(Order $order): string | |||
public function getShoppingCartLabelAsHtml(Order $order, int $index): string | |||
{ | |||
return '<div class="shopping-cart-label"> | |||
return '<div class="shopping-cart-label shopping-cart-label-'.$index.'"> | |||
<div class="inner"> | |||
<div class="username"> | |||
'.$this->orderSolver->getOrderUsername($order).' |
@@ -0,0 +1,30 @@ | |||
<?php | |||
namespace common\logic\Document\DeliveryNote\Event; | |||
use common\logic\Document\DeliveryNote\Event\DeliveryNoteCreateEvent; | |||
use common\logic\Document\DeliveryNote\Model\DeliveryNote; | |||
use common\logic\Document\Document\Module\DocumentModule; | |||
use common\logic\Order\Order\Module\OrderModule; | |||
use common\logic\Producer\Producer\Module\ProducerModule; | |||
use justcoded\yii2\eventlistener\observers\Observer; | |||
class DeliveryNoteObserver extends Observer | |||
{ | |||
public function events() | |||
{ | |||
return [ | |||
DeliveryNote::EVENT_CREATE => 'onDeliveryNoteCreate' | |||
]; | |||
} | |||
public function onDeliveryNoteCreate(DeliveryNoteCreateEvent $event) | |||
{ | |||
$producerModule = ProducerModule::getInstance(); | |||
$documentModule = DocumentModule::getInstance(); | |||
if($producerModule->getSolver()->getConfig('delivery_note_automatic_validation')) { | |||
$documentModule->getManager()->validateDocument($event->deliveryNote); | |||
} | |||
} | |||
} |
@@ -6,6 +6,7 @@ use common\logic\Distribution\Distribution\Model\Distribution; | |||
use common\logic\Document\DeliveryNote\Event\DeliveryNoteCreateEvent; | |||
use common\logic\Document\DeliveryNote\Model\DeliveryNote; | |||
use common\logic\Document\DeliveryNote\Repository\DeliveryNoteRepository; | |||
use common\logic\Document\Document\Model\Document; | |||
use common\logic\Document\Document\Service\DocumentBuilder; | |||
use common\logic\Order\Order\Model\Order; | |||
use common\logic\Order\Order\Service\OrderSolver; | |||
@@ -34,6 +35,7 @@ class DeliveryNoteBuilder extends DocumentBuilder | |||
{ | |||
$deliveryNote = new DeliveryNote(); | |||
$deliveryNote->status = Document::STATUS_DRAFT; | |||
$this->initDocumentProducer($deliveryNote); | |||
$this->initTaxCalculationMethod($deliveryNote); | |||
@@ -31,6 +31,7 @@ class DocumentManager extends AbstractManager | |||
if ($status == Document::STATUS_VALID) { | |||
$this->documentReferenceGenerator->generateReference($document); | |||
$this->generatePdf($document, Pdf::DEST_FILE); | |||
} | |||
$this->documentBuilder->update($document); |
@@ -4,6 +4,7 @@ namespace common\logic\Order\Order\Repository; | |||
use common\helpers\GlobalParam; | |||
use common\helpers\MeanPayment; | |||
use common\helpers\Price; | |||
use common\logic\AbstractRepository; | |||
use common\logic\Distribution\Distribution\Model\Distribution; | |||
use common\logic\Distribution\Distribution\Repository\DistributionRepository; | |||
@@ -581,7 +582,13 @@ class OrderRepository extends AbstractRepository | |||
$titleLabel = 'Paiement partiel '.$amountPaid; | |||
} | |||
return '<span class="label label-'.$classLabel.'" title="'.$titleLabel.'">'.$label.'</span>'; | |||
$labelHtml = '<span class="label label-'.$classLabel.'" title="'.$titleLabel.'">'.$label.'</span>'; | |||
if($amountPaid) { | |||
$labelHtml .= $this->orderSolver->getPaymentLabelAmountRemainingSurplus($order); | |||
} | |||
return $labelHtml; | |||
} | |||
public function getPaymentLabelPaid(Order $order): string | |||
@@ -615,13 +622,7 @@ class OrderRepository extends AbstractRepository | |||
} | |||
} | |||
$orderPaymentStatus = $this->orderSolver->getPaymentStatus($order); | |||
if($orderPaymentStatus == Order::PAYMENT_SURPLUS) { | |||
$label .= ' (surplus)'; | |||
} | |||
elseif($orderPaymentStatus == Order::PAYMENT_UNPAID) { | |||
$label .= ' (partiel)'; | |||
} | |||
$label .= $this->orderSolver->getPaymentLabelAmountRemainingSurplus($order); | |||
} | |||
} | |||
@@ -300,6 +300,30 @@ class OrderSolver extends AbstractService implements SolverInterface | |||
return $html; | |||
} | |||
public function getPaymentLabelAmountRemainingSurplus(Order $order): string | |||
{ | |||
$amountPaid = $this->getOrderAmountPaid($order); | |||
if($amountPaid) { | |||
$amountRemaining = $this->getOrderAmountWithTax($order, Order::AMOUNT_REMAINING); | |||
if($amountRemaining > 0) { | |||
return $this->getHtmlPaymentLabelAmountRemainingSurplus('Reste <strong>'.Price::format($amountRemaining).'</strong> à payer'); | |||
} | |||
$amountSurplus = $this->getOrderAmountWithTax($order, Order::AMOUNT_SURPLUS); | |||
if($amountSurplus > 0) { | |||
return $this->getHtmlPaymentLabelAmountRemainingSurplus('Surplus : <strong>'.Price::format($amountSurplus).'</strong> à rembourser'); | |||
} | |||
} | |||
return ''; | |||
} | |||
private function getHtmlPaymentLabelAmountRemainingSurplus(string $text): string | |||
{ | |||
return '<br><span class="payment-detail-remaining-surplus">'.$text.'</span>'; | |||
} | |||
/** | |||
* Retourne l'origine de la commande (client, automatique ou admin) sous forme texte ou HTML. | |||
*/ |
@@ -98,6 +98,14 @@ class PointSaleRepository extends AbstractRepository | |||
->find(); | |||
} | |||
public function findPointSalesByUserAccess(User $user) | |||
{ | |||
return $this->createDefaultQuery() | |||
->filterIsOnline() | |||
->filterByUserAccess($user) | |||
->find(); | |||
} | |||
public function queryPointSalesPublic(Producer $producer) | |||
{ | |||
return $this->createDefaultQuery() |
@@ -6,6 +6,7 @@ use common\logic\AbstractRepositoryQuery; | |||
use common\logic\PointSale\PointSale\Model\PointSale; | |||
use common\logic\PointSale\PointSale\Service\PointSaleDefinition; | |||
use common\logic\StatusInterface; | |||
use common\logic\User\User\Model\User; | |||
use yii\db\ActiveQuery; | |||
class PointSaleRepositoryQuery extends AbstractRepositoryQuery | |||
@@ -44,4 +45,12 @@ class PointSaleRepositoryQuery extends AbstractRepositoryQuery | |||
return $this; | |||
} | |||
public function filterByUserAccess(User $user): self | |||
{ | |||
$this->andWhere('status = 1 AND (restricted_access = 0 OR (restricted_access = 1 AND (SELECT COUNT(*) FROM user_point_sale WHERE point_sale.id = user_point_sale.id_point_sale AND user_point_sale.id_user = :id_user) > 0))'); | |||
$this->addParams([':id_user' => $user->id]); | |||
return $this; | |||
} | |||
} |
@@ -287,7 +287,8 @@ class Producer extends ActiveRecordCommon | |||
'option_export_display_column_delivery_note', | |||
'option_invoice_only_based_on_delivery_notes', | |||
'option_document_display_price_unit_reference', | |||
'option_check_by_default_prevent_user_credit' | |||
'option_check_by_default_prevent_user_credit', | |||
'delivery_note_automatic_validation' | |||
], | |||
'boolean' | |||
], | |||
@@ -468,6 +469,7 @@ class Producer extends ActiveRecordCommon | |||
'option_document_display_price_unit_reference' => "Afficher les prix au kilogramme", | |||
'id_user_group_default' => "Groupe utilisateur par défaut attribué à l'inscription", | |||
'option_check_by_default_prevent_user_credit' => "Par défaut, prévenir l'utilisateur quand on crédite son compte", | |||
'delivery_note_automatic_validation' => 'Validation automatique des bons de livraison' | |||
]; | |||
} | |||
@@ -22,6 +22,16 @@ class DolibarrProducerUtils extends AbstractManager | |||
$this->producerRepository = $this->loadService(ProducerRepository::class); | |||
} | |||
public function getDolibarrProducerInvoices(Producer $producer): array | |||
{ | |||
$invoicesArray = []; | |||
if($producer->dolibarr_socid) { | |||
$invoicesArray = $this->dolibarrApi->getInvoicesByThirParty($producer->dolibarr_socid); | |||
} | |||
return $invoicesArray; | |||
} | |||
public function generateDolibarrProducerInvoice(Producer $producer) | |||
{ | |||
$idProduct = $this->getDolibarrProductId($producer); |
@@ -66,8 +66,12 @@ class ProductRepository extends AbstractRepository | |||
return $this->createDefaultQuery()->count(); | |||
} | |||
public function queryProductsByDistribution(Distribution $distribution, bool $filterStatus = true) | |||
public function queryProductsByDistribution(Distribution $distribution, bool $filterStatus = true, string $orderBy = null) | |||
{ | |||
if(!$orderBy) { | |||
$orderBy = 'product_distribution.active DESC, product.order ASC'; | |||
} | |||
return $this->createDefaultQuery($filterStatus) | |||
->joinWith([ | |||
'productDistribution' => function ($query) use ($distribution) { | |||
@@ -76,15 +80,15 @@ class ProductRepository extends AbstractRepository | |||
); | |||
} | |||
]) | |||
->orderBy('product_distribution.active DESC, product.order ASC'); | |||
->orderBy($orderBy); | |||
} | |||
/** | |||
* Retourne les produits d'une production donnée. | |||
*/ | |||
public function findProductsByDistribution(Distribution $distribution, bool $filterStatus = true) | |||
public function findProductsByDistribution(Distribution $distribution, bool $filterStatus = true, string $orderBy = null) | |||
{ | |||
$productArray = $this->queryProductsByDistribution($distribution, $filterStatus)->find(); | |||
$productArray = $this->queryProductsByDistribution($distribution, $filterStatus, $orderBy)->find(); | |||
return $this->buildProductsArrayById($productArray); | |||
} | |||
@@ -8,15 +8,15 @@ version( | |||
[ | |||
], | |||
[ | |||
"[Admin] Distribution > exports : ouverture dans un nouvel onglet", | |||
"[Administration] Distribution > exports : ouverture dans un nouvel onglet", | |||
] | |||
], | |||
[ | |||
[ | |||
], | |||
[ | |||
"[Admin] Tarifs : erreur vue modules payants", | |||
"[Admin] Produits : import prix cassé" | |||
"[Administration] Tarifs : erreur vue modules payants", | |||
"[Administration] Produits : import prix cassé" | |||
] | |||
], | |||
$userCurrent |
@@ -0,0 +1,28 @@ | |||
<?php | |||
require_once dirname(__FILE__).'/_macros.php'; | |||
version( | |||
'04/12/2023', | |||
[ | |||
[ | |||
"[Administration] Nouvel onglet 'Mes factures' : possibilité de retrouver ses factures Opendistrib", | |||
"[Administration] Distributions > Génération des bons de livraison : validation automatique (Paramètres > Facturation > Validation automatique des bons de livraison)", | |||
"[Administration] Export étiquettes PDF avec format spécifique (70x42mm)", | |||
], | |||
[ | |||
"[Administration] Export commandes PDF : affichage du montant restant/surplus à régler", | |||
"[Administration] Abonnements : correctif regénération commandes distributions à venir", | |||
"[Boutique] Abonnements : correctif problème points de vente à accès restreint affichés" | |||
] | |||
], | |||
[ | |||
[ | |||
], | |||
[ | |||
] | |||
], | |||
$userCurrent | |||
); | |||
?> |
@@ -211,3 +211,14 @@ termes. | |||
#main #content .site-error .alert .btn { | |||
text-decoration: none; | |||
} | |||
/* Paiement */ | |||
/* line 144, ../sass/_common.scss */ | |||
.payment-detail-remaining-surplus { | |||
font-size: 13px; | |||
color: gray; | |||
} | |||
/* line 148, ../sass/_common.scss */ | |||
.payment-detail-remaining-surplus strong { | |||
font-weight: bold; | |||
} |
@@ -138,4 +138,14 @@ | |||
.actions { | |||
//text-align: center; | |||
} | |||
} | |||
/* Paiement */ | |||
.payment-detail-remaining-surplus { | |||
font-size: 13px; | |||
color: gray; | |||
strong { | |||
font-weight: bold; | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
<?php | |||
use yii\db\Migration; | |||
use yii\db\Schema; | |||
/** | |||
* Class m231130_082147_add_column_producer_delivery_note_automatic_validation | |||
*/ | |||
class m231130_082147_add_column_producer_delivery_note_automatic_validation extends Migration | |||
{ | |||
/** | |||
* {@inheritdoc} | |||
*/ | |||
public function safeUp() | |||
{ | |||
$this->addColumn('producer', 'delivery_note_automatic_validation', Schema::TYPE_BOOLEAN); | |||
} | |||
/** | |||
* {@inheritdoc} | |||
*/ | |||
public function safeDown() | |||
{ | |||
$this->dropColumn('producer', 'delivery_note_automatic_validation'); | |||
} | |||
} |
@@ -226,7 +226,6 @@ class CreditController extends ProducerBaseController | |||
// Handle the event | |||
switch ($event->type) { | |||
case 'charge.succeeded': | |||
$paymentExist = Payment::searchOne([ | |||
'id_user' => $idUser, | |||
'amount' => $amount, | |||
@@ -238,13 +237,9 @@ class CreditController extends ProducerBaseController | |||
if (!$paymentExist) { | |||
$paymentManager->creditUser($user, $amount, MeanPayment::CREDIT_CARD, $user); | |||
if (isset($order) && $order) { | |||
$paymentManager->payOrder($order, MeanPayment::CREDIT_CARD, $user, true); | |||
// client : envoi d'un email de confirmation de paiement | |||
/*\Yii::$app->mailerService->sendFromProducer( | |||
'Confirmation de commande', | |||
@@ -272,8 +267,9 @@ class CreditController extends ProducerBaseController | |||
$contactProducer->email | |||
); | |||
} else { | |||
$userProducer = $this->getUserProducerModule()->findOneUserProducer($user); | |||
$paymentManager->creditUser($user, $amount, MeanPayment::CREDIT_CARD, $user); | |||
$userProducer = $this->getUserProducerModule()->findOneUserProducer($user); | |||
\Yii::$app->mailerService->sendFromProducer( | |||
'Alimentation de votre crédit', | |||
'creditConfirm', |
@@ -245,7 +245,7 @@ class SubscriptionController extends ProducerBaseController | |||
$params = []; | |||
$productModule = $this->getProductModule(); | |||
$subscriptionModule = $this->getSubscriptionModule(); | |||
$user = GlobalParam::getCurrentUser(); | |||
$user = $this->getUserCurrent(); | |||
$userProducer = $this->getUserProducerModule()->findOneUserProducer($this->getUserCurrent()); | |||
$pointSale = null; | |||
@@ -304,7 +304,7 @@ class SubscriptionController extends ProducerBaseController | |||
$params['products'] = $productsArray; | |||
$pointsSaleArray = $this->getPointSaleModule()->findPointSales(); | |||
$pointsSaleArray = $this->getPointSaleModule()->findPointSalesByUserAccess($user); | |||
foreach ($pointsSaleArray as &$pointSale) { | |||
$pointSale = array_merge($pointSale->getAttributes(), [ | |||
'userPointSale' => ($pointSale->userPointSale ? $pointSale->userPointSale[0] : '') |
@@ -323,10 +323,9 @@ $this->setTitle('Commander'); | |||
v-if="countSelectedProductsByCategory(category) > 1">s</template></span> | |||
</td> | |||
</tr> | |||
<template | |||
v-if="(categoryCurrent && categoryCurrent.id == category.id) || category.id == null"> | |||
<template v-if="(categoryCurrent && categoryCurrent.id == category.id) || category.id == null"> | |||
<tr v-for="product in products" | |||
v-if="product.id_product_category == category.id && product.productDistribution && product.productDistribution[0] && product.productDistribution[0].active == 1"> | |||
v-if="product.id_product_category == category.id && isProductAvailable(product)"> | |||
<td class="photo"> | |||
<a class="product-photo" :href="product.photo_big" :title="product.name"> | |||
<img v-if="product.photo.length" class="photo-product" :src="product.photo"/> |
@@ -249,11 +249,12 @@ var app = new Vue({ | |||
if(response.data.categories) { | |||
app.categories = response.data.categories ; | |||
if(app.countProductsByCategory(response.data.categories[0])) { | |||
app.setCategoryCurrent(response.data.categories[0], true) ; | |||
} | |||
else { | |||
app.setCategoryCurrent(response.data.categories[1], true) ; | |||
for(keyCategory in response.data.categories) { | |||
var category = response.data.categories[keyCategory]; | |||
if(category.id && app.countProductsByCategory(category)) { | |||
app.setCategoryCurrent(category, true) ; | |||
break; | |||
} | |||
} | |||
} | |||
@@ -599,10 +600,13 @@ var app = new Vue({ | |||
} | |||
return this.producer.credit_limit == null || (this.producer.credit_limit != null && (this.user.credit - total >= this.producer.credit_limit)) ; | |||
}, | |||
isProductAvailable: function(product) { | |||
return product.productDistribution && product.productDistribution[0] && product.productDistribution[0].active == 1; | |||
}, | |||
countProductsByCategory: function(category) { | |||
var count = 0 ; | |||
for(var i = 0 ; i < this.products.length ; i++) { | |||
if(this.products[i].id_product_category == category.id) { | |||
if(this.products[i].id_product_category == category.id && this.isProductAvailable(this.products[i])) { | |||
count ++ ; | |||
} | |||
} |