<?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(); | |||||
} | |||||
} | |||||
} |
$subscriptionModule = $this->getSubscriptionModule(); | $subscriptionModule = $this->getSubscriptionModule(); | ||||
$distributionModule = $this-> getDistributionModule(); | $distributionModule = $this-> getDistributionModule(); | ||||
$subscription = $subscriptionModule->findOneSubscriptionById($idSubscription); | $subscription = $subscriptionModule->findOneSubscriptionById($idSubscription); | ||||
$matchedDistributionsArray = $distributionModule->findDistributionsIncomingMatchWithSubscrtiption($subscription); | |||||
$matchedDistributionsArray = $distributionModule->findDistributionsIncomingMatchWithSubscrtiption($subscription, true); | |||||
if ($generate) { | if ($generate) { | ||||
if ($update) { | if ($update) { |
<order-state-payment :order="order" :producer="producer"></order-state-payment> | <order-state-payment :order="order" :producer="producer"></order-state-payment> | ||||
</a> | </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> | <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> | </template> | ||||
</td> | </td> | ||||
<td class="column-credit" v-if="!idActivePointSale || (pointSaleActive && pointSaleActive.credit == 1)"> | <td class="column-credit" v-if="!idActivePointSale || (pointSaleActive && pointSaleActive.credit == 1)"> |
foreach($ordersArray as $key => $order) { | foreach($ordersArray as $key => $order) { | ||||
$index ++; | $index ++; | ||||
echo $distributionShoppingCartLabelsPdfGenerator->getShoppingCartLabelAsHtml($order); | |||||
echo $distributionShoppingCartLabelsPdfGenerator->getShoppingCartLabelAsHtml($order, $index); | |||||
if($index == $shoppingCartLabelsPerColumn) { | if($index == $shoppingCartLabelsPerColumn) { | ||||
$index = 0; | $index = 0; |
$documentModule = $this->getDocumentModule(); | $documentModule = $this->getDocumentModule(); | ||||
$orderModule = $this->getOrderModule(); | $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'); | $displayProductDescription = $producerModule->getConfig('document_display_product_description'); | ||||
$documentPriceDecimals = (int) $producerModule->getConfig('option_document_price_decimals'); | $documentPriceDecimals = (int) $producerModule->getConfig('option_document_price_decimals'); | ||||
['label' => 'Paramètres', 'icon' => 'cog', 'url' => ['/producer/update'], 'visible' => $isUserCurrentGrantedAsProducer], | ['label' => 'Paramètres', 'icon' => 'cog', 'url' => ['/producer/update'], 'visible' => $isUserCurrentGrantedAsProducer], | ||||
['label' => 'Accès', 'icon' => 'lock', 'url' => ['/access/index'], 'visible' => $isUserCurrentGrantedAsProducer], | ['label' => 'Accès', 'icon' => 'lock', 'url' => ['/access/index'], 'visible' => $isUserCurrentGrantedAsProducer], | ||||
['label' => "Opendistrib", 'options' => ['class' => 'header'], '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', | 'label' => 'Développement', | ||||
'icon' => 'code', | 'icon' => 'code', |
<?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; ?> |
<?= $form->field($model, 'document_invoice_first_reference'); ?> | <?= $form->field($model, 'document_invoice_first_reference'); ?> | ||||
<?= $form->field($model, 'document_delivery_note_prefix')->hint($hintKeywordsPrefix);; ?> | <?= $form->field($model, 'document_delivery_note_prefix')->hint($hintKeywordsPrefix);; ?> | ||||
<?= $form->field($model, 'document_delivery_note_first_reference'); ?> | <?= $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') | <?= $form->field($model, 'option_document_width_logo') | ||||
->dropDownList(Dropdown::numberChoices(50, 250, true, 'px', 50)); ?> | ->dropDownList(Dropdown::numberChoices(50, 250, true, 'px', 50)); ?> | ||||
<?= $form->field($model, 'document_display_orders_invoice')->dropDownList(Dropdown::noYesChoices()); ?> | <?= $form->field($model, 'document_display_orders_invoice')->dropDownList(Dropdown::noYesChoices()); ?> |
namespace common\components; | namespace common\components; | ||||
use Psr\Http\Message\ResponseInterface; | |||||
class DolibarrApi extends AbstractApi | class DolibarrApi extends AbstractApi | ||||
{ | { | ||||
const RESOURCE_INVOICES = 'invoices'; | const RESOURCE_INVOICES = 'invoices'; | ||||
const RESOURCE_PRODUCTS = 'products'; | const RESOURCE_PRODUCTS = 'products'; | ||||
const RESOURCE_DOCUMENTS = 'documents'; | 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) | public function createInvoice(int $idUser) | ||||
{ | { | ||||
return $this->post(self::RESOURCE_INVOICES, [ | return $this->post(self::RESOURCE_INVOICES, [ |
], | ], | ||||
DeliveryNote::class => [ | DeliveryNote::class => [ | ||||
// Order : assignation du bon de livraison aux commandes | // 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 => [ | Ticket::class => [ | ||||
// User : envoi email nouveau ticket à l'administrateur | // User : envoi email nouveau ticket à l'administrateur |
*/ | */ | ||||
return [ | return [ | ||||
'version' => '23.11.C', | |||||
'version' => '23.12.A', | |||||
'maintenanceMode' => false, | 'maintenanceMode' => false, | ||||
'siteName' => 'Opendistrib', | 'siteName' => 'Opendistrib', | ||||
'adminEmail' => 'contact@opendistrib.net', | 'adminEmail' => 'contact@opendistrib.net', |
use common\components\BusinessLogic; | use common\components\BusinessLogic; | ||||
use common\components\BusinessLogicTrait; | use common\components\BusinessLogicTrait; | ||||
use common\components\ParameterBag; | |||||
use common\logic\User\User\Model\User; | use common\logic\User\User\Model\User; | ||||
use yii; | use yii; | ||||
use yii\web\Response; | use yii\web\Response; | ||||
{ | { | ||||
return $this->redirect(Yii::$app->request->referrer ?: Yii::$app->homeUrl); | return $this->redirect(Yii::$app->request->referrer ?: Yii::$app->homeUrl); | ||||
} | } | ||||
public function getParameterBag(): ParameterBag | |||||
{ | |||||
return Yii::$app->parameterBag; | |||||
} | |||||
} | } | ||||
?> | ?> |
table tr td { | table tr td { | ||||
font-size: 13px ; | font-size: 13px ; | ||||
} | } | ||||
.payment-detail-remaining-surplus { | |||||
font-size: 10px; | |||||
} | |||||
', | ', | ||||
'methods' => [ | 'methods' => [ | ||||
'SetHeader' => ['Commandes du ' . date('d/m/Y', strtotime($distribution->date))], | 'SetHeader' => ['Commandes du ' . date('d/m/Y', strtotime($distribution->date))], | ||||
public function columnOrderAmount(Order $order): string | 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); | $paymentLabelPaid = $this->orderRepository->getPaymentLabelPaid($order); | ||||
if($paymentLabelPaid && strlen($paymentLabelPaid)) { | if($paymentLabelPaid && strlen($paymentLabelPaid)) { |
public function generate(Distribution $distribution, bool $save = false) | public function generate(Distribution $distribution, bool $save = false) | ||||
{ | { | ||||
$datas = []; | $datas = []; | ||||
$productsArray = $this->productRepository->findProductsByDistribution($distribution); | |||||
$productsArray = $this->productRepository->findProductsByDistribution($distribution, true,'product.order ASC'); | |||||
$ordersArray = $this->orderRepository->findOrdersByDistribution($distribution); | $ordersArray = $this->orderRepository->findOrdersByDistribution($distribution); | ||||
foreach($productsArray as $product) { | foreach($productsArray as $product) { |
return $order->pointSale && $order->pointSale->exclude_export_shopping_cart_labels; | 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 = ' | $css = ' | ||||
@page { | @page { | ||||
.shopping-cart-label .username { | .shopping-cart-label .username { | ||||
font-weight: bold; | font-weight: bold; | ||||
font-size: 13px; | |||||
font-size: 16px; | |||||
} | } | ||||
.shopping-cart-label .point-sale { | .shopping-cart-label .point-sale { | ||||
}'; | }'; | ||||
if($isSpecificFormat) { | 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 .= ' | $css .= ' | ||||
.shopping-cart-label { | .shopping-cart-label { | ||||
box-sizing: border-box; | 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; | height: '.$specificFormatHeight.'mm; | ||||
display: block; | display: block; | ||||
float: left; | float: left; | ||||
return $css; | 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="inner"> | ||||
<div class="username"> | <div class="username"> | ||||
'.$this->orderSolver->getOrderUsername($order).' | '.$this->orderSolver->getOrderUsername($order).' |
<?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); | |||||
} | |||||
} | |||||
} |
use common\logic\Document\DeliveryNote\Event\DeliveryNoteCreateEvent; | use common\logic\Document\DeliveryNote\Event\DeliveryNoteCreateEvent; | ||||
use common\logic\Document\DeliveryNote\Model\DeliveryNote; | use common\logic\Document\DeliveryNote\Model\DeliveryNote; | ||||
use common\logic\Document\DeliveryNote\Repository\DeliveryNoteRepository; | use common\logic\Document\DeliveryNote\Repository\DeliveryNoteRepository; | ||||
use common\logic\Document\Document\Model\Document; | |||||
use common\logic\Document\Document\Service\DocumentBuilder; | use common\logic\Document\Document\Service\DocumentBuilder; | ||||
use common\logic\Order\Order\Model\Order; | use common\logic\Order\Order\Model\Order; | ||||
use common\logic\Order\Order\Service\OrderSolver; | use common\logic\Order\Order\Service\OrderSolver; | ||||
{ | { | ||||
$deliveryNote = new DeliveryNote(); | $deliveryNote = new DeliveryNote(); | ||||
$deliveryNote->status = Document::STATUS_DRAFT; | |||||
$this->initDocumentProducer($deliveryNote); | $this->initDocumentProducer($deliveryNote); | ||||
$this->initTaxCalculationMethod($deliveryNote); | $this->initTaxCalculationMethod($deliveryNote); | ||||
if ($status == Document::STATUS_VALID) { | if ($status == Document::STATUS_VALID) { | ||||
$this->documentReferenceGenerator->generateReference($document); | $this->documentReferenceGenerator->generateReference($document); | ||||
$this->generatePdf($document, Pdf::DEST_FILE); | |||||
} | } | ||||
$this->documentBuilder->update($document); | $this->documentBuilder->update($document); |
use common\helpers\GlobalParam; | use common\helpers\GlobalParam; | ||||
use common\helpers\MeanPayment; | use common\helpers\MeanPayment; | ||||
use common\helpers\Price; | |||||
use common\logic\AbstractRepository; | use common\logic\AbstractRepository; | ||||
use common\logic\Distribution\Distribution\Model\Distribution; | use common\logic\Distribution\Distribution\Model\Distribution; | ||||
use common\logic\Distribution\Distribution\Repository\DistributionRepository; | use common\logic\Distribution\Distribution\Repository\DistributionRepository; | ||||
$titleLabel = 'Paiement partiel '.$amountPaid; | $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 | public function getPaymentLabelPaid(Order $order): string | ||||
} | } | ||||
} | } | ||||
$orderPaymentStatus = $this->orderSolver->getPaymentStatus($order); | |||||
if($orderPaymentStatus == Order::PAYMENT_SURPLUS) { | |||||
$label .= ' (surplus)'; | |||||
} | |||||
elseif($orderPaymentStatus == Order::PAYMENT_UNPAID) { | |||||
$label .= ' (partiel)'; | |||||
} | |||||
$label .= $this->orderSolver->getPaymentLabelAmountRemainingSurplus($order); | |||||
} | } | ||||
} | } | ||||
return $html; | 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. | * Retourne l'origine de la commande (client, automatique ou admin) sous forme texte ou HTML. | ||||
*/ | */ |
->find(); | ->find(); | ||||
} | } | ||||
public function findPointSalesByUserAccess(User $user) | |||||
{ | |||||
return $this->createDefaultQuery() | |||||
->filterIsOnline() | |||||
->filterByUserAccess($user) | |||||
->find(); | |||||
} | |||||
public function queryPointSalesPublic(Producer $producer) | public function queryPointSalesPublic(Producer $producer) | ||||
{ | { | ||||
return $this->createDefaultQuery() | return $this->createDefaultQuery() |
use common\logic\PointSale\PointSale\Model\PointSale; | use common\logic\PointSale\PointSale\Model\PointSale; | ||||
use common\logic\PointSale\PointSale\Service\PointSaleDefinition; | use common\logic\PointSale\PointSale\Service\PointSaleDefinition; | ||||
use common\logic\StatusInterface; | use common\logic\StatusInterface; | ||||
use common\logic\User\User\Model\User; | |||||
use yii\db\ActiveQuery; | use yii\db\ActiveQuery; | ||||
class PointSaleRepositoryQuery extends AbstractRepositoryQuery | class PointSaleRepositoryQuery extends AbstractRepositoryQuery | ||||
return $this; | 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; | |||||
} | |||||
} | } |
'option_export_display_column_delivery_note', | 'option_export_display_column_delivery_note', | ||||
'option_invoice_only_based_on_delivery_notes', | 'option_invoice_only_based_on_delivery_notes', | ||||
'option_document_display_price_unit_reference', | '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' | 'boolean' | ||||
], | ], | ||||
'option_document_display_price_unit_reference' => "Afficher les prix au kilogramme", | 'option_document_display_price_unit_reference' => "Afficher les prix au kilogramme", | ||||
'id_user_group_default' => "Groupe utilisateur par défaut attribué à l'inscription", | 'id_user_group_default' => "Groupe utilisateur par défaut attribué à l'inscription", | ||||
'option_check_by_default_prevent_user_credit' => "Par défaut, prévenir l'utilisateur quand on crédite son compte", | 'option_check_by_default_prevent_user_credit' => "Par défaut, prévenir l'utilisateur quand on crédite son compte", | ||||
'delivery_note_automatic_validation' => 'Validation automatique des bons de livraison' | |||||
]; | ]; | ||||
} | } | ||||
$this->producerRepository = $this->loadService(ProducerRepository::class); | $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) | public function generateDolibarrProducerInvoice(Producer $producer) | ||||
{ | { | ||||
$idProduct = $this->getDolibarrProductId($producer); | $idProduct = $this->getDolibarrProductId($producer); |
return $this->createDefaultQuery()->count(); | 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) | return $this->createDefaultQuery($filterStatus) | ||||
->joinWith([ | ->joinWith([ | ||||
'productDistribution' => function ($query) use ($distribution) { | 'productDistribution' => function ($query) use ($distribution) { | ||||
); | ); | ||||
} | } | ||||
]) | ]) | ||||
->orderBy('product_distribution.active DESC, product.order ASC'); | |||||
->orderBy($orderBy); | |||||
} | } | ||||
/** | /** | ||||
* Retourne les produits d'une production donnée. | * 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); | return $this->buildProductsArrayById($productArray); | ||||
} | } | ||||
[ | [ | ||||
], | ], | ||||
[ | [ | ||||
"[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 | $userCurrent |
<?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 | |||||
); | |||||
?> |
#main #content .site-error .alert .btn { | #main #content .site-error .alert .btn { | ||||
text-decoration: none; | 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; | |||||
} |
.actions { | .actions { | ||||
//text-align: center; | //text-align: center; | ||||
} | } | ||||
} | |||||
/* Paiement */ | |||||
.payment-detail-remaining-surplus { | |||||
font-size: 13px; | |||||
color: gray; | |||||
strong { | |||||
font-weight: bold; | |||||
} | |||||
} | } |
<?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'); | |||||
} | |||||
} |
// Handle the event | // Handle the event | ||||
switch ($event->type) { | switch ($event->type) { | ||||
case 'charge.succeeded': | case 'charge.succeeded': | ||||
$paymentExist = Payment::searchOne([ | $paymentExist = Payment::searchOne([ | ||||
'id_user' => $idUser, | 'id_user' => $idUser, | ||||
'amount' => $amount, | 'amount' => $amount, | ||||
if (!$paymentExist) { | if (!$paymentExist) { | ||||
$paymentManager->creditUser($user, $amount, MeanPayment::CREDIT_CARD, $user); | |||||
if (isset($order) && $order) { | if (isset($order) && $order) { | ||||
$paymentManager->payOrder($order, MeanPayment::CREDIT_CARD, $user, true); | $paymentManager->payOrder($order, MeanPayment::CREDIT_CARD, $user, true); | ||||
// client : envoi d'un email de confirmation de paiement | // client : envoi d'un email de confirmation de paiement | ||||
/*\Yii::$app->mailerService->sendFromProducer( | /*\Yii::$app->mailerService->sendFromProducer( | ||||
'Confirmation de commande', | 'Confirmation de commande', | ||||
$contactProducer->email | $contactProducer->email | ||||
); | ); | ||||
} else { | } else { | ||||
$userProducer = $this->getUserProducerModule()->findOneUserProducer($user); | |||||
$paymentManager->creditUser($user, $amount, MeanPayment::CREDIT_CARD, $user); | |||||
$userProducer = $this->getUserProducerModule()->findOneUserProducer($user); | |||||
\Yii::$app->mailerService->sendFromProducer( | \Yii::$app->mailerService->sendFromProducer( | ||||
'Alimentation de votre crédit', | 'Alimentation de votre crédit', | ||||
'creditConfirm', | 'creditConfirm', |
$params = []; | $params = []; | ||||
$productModule = $this->getProductModule(); | $productModule = $this->getProductModule(); | ||||
$subscriptionModule = $this->getSubscriptionModule(); | $subscriptionModule = $this->getSubscriptionModule(); | ||||
$user = GlobalParam::getCurrentUser(); | |||||
$user = $this->getUserCurrent(); | |||||
$userProducer = $this->getUserProducerModule()->findOneUserProducer($this->getUserCurrent()); | $userProducer = $this->getUserProducerModule()->findOneUserProducer($this->getUserCurrent()); | ||||
$pointSale = null; | $pointSale = null; | ||||
$params['products'] = $productsArray; | $params['products'] = $productsArray; | ||||
$pointsSaleArray = $this->getPointSaleModule()->findPointSales(); | |||||
$pointsSaleArray = $this->getPointSaleModule()->findPointSalesByUserAccess($user); | |||||
foreach ($pointsSaleArray as &$pointSale) { | foreach ($pointsSaleArray as &$pointSale) { | ||||
$pointSale = array_merge($pointSale->getAttributes(), [ | $pointSale = array_merge($pointSale->getAttributes(), [ | ||||
'userPointSale' => ($pointSale->userPointSale ? $pointSale->userPointSale[0] : '') | 'userPointSale' => ($pointSale->userPointSale ? $pointSale->userPointSale[0] : '') |
v-if="countSelectedProductsByCategory(category) > 1">s</template></span> | v-if="countSelectedProductsByCategory(category) > 1">s</template></span> | ||||
</td> | </td> | ||||
</tr> | </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" | <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"> | <td class="photo"> | ||||
<a class="product-photo" :href="product.photo_big" :title="product.name"> | <a class="product-photo" :href="product.photo_big" :title="product.name"> | ||||
<img v-if="product.photo.length" class="photo-product" :src="product.photo"/> | <img v-if="product.photo.length" class="photo-product" :src="product.photo"/> |
if(response.data.categories) { | if(response.data.categories) { | ||||
app.categories = 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; | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
return this.producer.credit_limit == null || (this.producer.credit_limit != null && (this.user.credit - total >= this.producer.credit_limit)) ; | 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) { | countProductsByCategory: function(category) { | ||||
var count = 0 ; | var count = 0 ; | ||||
for(var i = 0 ; i < this.products.length ; i++) { | 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 ++ ; | count ++ ; | ||||
} | } | ||||
} | } |