Parcourir la source

[Administration] Documents > Factures : génération sur base des commandes #1218

feature/souke
Guillaume Bourgeois il y a 1 an
Parent
révision
64154a4dc4
14 fichiers modifiés avec 492 ajouts et 422 suppressions
  1. +31
    -89
      backend/controllers/DocumentController.php
  2. +26
    -18
      backend/controllers/InvoiceController.php
  3. +321
    -294
      backend/views/document/_form.php
  4. +16
    -15
      backend/web/js/vuejs/document-form.js
  5. +1
    -1
      common/logic/Document/DeliveryNote/Model/DeliveryNote.php
  6. +1
    -1
      common/logic/Document/Document/Model/Document.php
  7. +6
    -0
      common/logic/Document/Document/Service/DocumentBuilder.php
  8. +5
    -2
      common/logic/Document/Document/Service/DocumentUtils.php
  9. +1
    -0
      common/logic/Document/Invoice/Model/Invoice.php
  10. +2
    -1
      common/logic/Order/Order/Model/Order.php
  11. +3
    -1
      common/logic/Order/Order/Repository/OrderRepository.php
  12. +14
    -0
      common/logic/Order/Order/Repository/OrderRepositoryQuery.php
  13. +37
    -0
      common/logic/Order/Order/Service/OrderBuilder.php
  14. +28
    -0
      console/migrations/m230912_070221_add_column_order_ignore_when_invoicing.php

+ 31
- 89
backend/controllers/DocumentController.php Voir le fichier

@@ -38,6 +38,7 @@

namespace backend\controllers;

use common\helpers\Ajax;
use common\helpers\CSV;
use common\helpers\GlobalParam;
use common\helpers\Price;
@@ -113,6 +114,7 @@ class DocumentController extends BackendController

if ($model->save()) {
$this->processInvoiceViaDeliveryNotes($model);
$this->processInvoiceViaOrders($model);

$this->setFlash('success', $this->getFlashMessage('create', $model));
return $this->redirect(['/' . $this->getControllerUrl() . '/update', 'id' => $model->id]);
@@ -130,27 +132,33 @@ class DocumentController extends BackendController

public function processInvoiceViaDeliveryNotes($model)
{
$orderManager = $this->getOrderManager();
$documentManager = $this->getDocumentManager();
$deliveryNoteManager = $this->getDeliveryNoteManager();
if ($documentManager->getClass($model) == 'Invoice') {
if ($model->deliveryNotes && is_array($model->deliveryNotes) && count($model->deliveryNotes)) {
foreach ($model->deliveryNotes as $key => $idDeliveryNote) {
Order::updateAll([
'id_invoice' => $model->id
], [
'id_delivery_note' => $idDeliveryNote
]);
$deliveryNote = $deliveryNoteManager->findOneDeliveryNoteById($idDeliveryNote);
$orderManager->assignAllOrdersInvoiceByDeliveryNote($model, $deliveryNote);
}
}
}
}

public function processInvoiceViaOrders($model)
{
$orderManager = $this->getOrderManager();
$documentManager = $this->getDocumentManager();
if ($documentManager->getClass($model) == 'Invoice') {
if ($model->ordersOnCreate && is_array($model->ordersOnCreate) && count($model->ordersOnCreate)) {
foreach ($model->ordersOnCreate as $key => $idOrder) {
$order = $orderManager->findOneOrderById($idOrder);
$orderManager->updateOrderInvoice($order, $model);
}
}
}
}

/**
* Modifie un modèle Produit existant.
* Si la modification réussit, le navigateur est redirigé vers la page 'index'.
*
* @param integer $id
* @return mixed
*/
public function actionUpdate($id)
{
$model = $this->findModel($id);
@@ -173,7 +181,6 @@ class DocumentController extends BackendController
public function actionDelete($id)
{
$documentManager = $this->getDocumentManager();

$model = $this->findModel($id);

if ($documentManager->isStatusValid($model)) {
@@ -339,6 +346,7 @@ class DocumentController extends BackendController
{
$documentManager = $this->getDocumentManager();
$document = $this->findModel($id);

return $documentManager->downloadPdf($document);
}

@@ -356,10 +364,7 @@ class DocumentController extends BackendController
{
$documentManager = $this->getDocumentManager();
$document = $this->findModel($id);
if ($documentManager->send($document)) {
$document->is_sent = true;
$document->save();

if ($documentManager->sendDocument($document)) {
$this->setFlash('success', $this->getFlashMessage('send', $document));
} else {
$this->setFlash('danger', $this->getFlashMessage('send', $document));
@@ -408,19 +413,19 @@ class DocumentController extends BackendController
$deliveryNotesCreateArray = DeliveryNote::searchAll([
'id_user' => $user->id,
'status' => Document::STATUS_VALID,
'ignore_when_billing' => null
'ignore_when_invoicing' => null
], $options);
$deliveryNotesUpdateArray = DeliveryNote::searchAll([
'id_user' => $user->id,
'status' => Document::STATUS_VALID,
'order.id_invoice' => $idDocument,
'ignore_when_billing' => null
'ignore_when_invoicing' => null
], $options);
$json['delivery_note_create_array'] = $this->initDeliveryNoteArray('create', $deliveryNotesCreateArray);
$json['delivery_note_update_array'] = $this->initDeliveryNoteArray('update', $deliveryNotesUpdateArray);

$json['orders_create_array'] = $this->initOrdersArray($orderManager->findOrdersByUserNotInvoiced($user));
$json['orders_update_array'] = $this->initOrdersArray($orderManager->findOrdersByUserAndInvoice($user, $document));
$json['orders_update_array'] = $document ? $this->initOrdersArray($orderManager->findOrdersByUserAndInvoice($user, $document)) : [];
}

return $json;
@@ -496,10 +501,9 @@ class DocumentController extends BackendController
$document = $this->findModel($id);

if ($document) {
$documentManager->changeStatus($document,Document::STATUS_VALID);
$documentManager->saveUpdate($document);
$documentManager->validateDocument($document);

// génération PDF
// @TODO : gérer via un événement
$documentManager->generatePdf($document, Pdf::DEST_FILE);

$this->setFlash('success', $this->getFlashMessage('validate', $document));
@@ -517,40 +521,6 @@ class DocumentController extends BackendController
return $this->redirect([$this->getControllerUrl() . '/index']);
}

public function actionAjaxValidateDocument($idDocument, $classDocument)
{
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

$documentManager = $this->getDocumentManager();

if ($idDocument > 0 && $documentManager->isValidClass($classDocument)) {
$document = $classDocument::searchOne([
'id' => $idDocument
]);

if ($document) {
$documentManager->changeStatus($document,Document::STATUS_VALID);
$documentManager->saveUpdate($document);

return [
'return' => 'success',
'alert' => [
'type' => 'success',
'message' => 'Document validé'
]
];
}
}

return [
'return' => 'error',
'alert' => [
'type' => 'danger',
'message' => 'Une erreur est survenue lors de la validation du document.'
]
];
}

public function actionAjaxInit($idDocument, $classDocument)
{
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
@@ -639,8 +609,6 @@ class DocumentController extends BackendController

public function actionAjaxAddProduct($idDocument, $classDocument, $idProduct, $quantity, $price)
{
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

$documentManager = $this->getDocumentManager();
$productManager = $this->getProductManager();

@@ -677,30 +645,16 @@ class DocumentController extends BackendController
$productOrder->id_tax_rate = $product->taxRate->id;
$productOrder->save();

return [
'return' => 'success',
'alert' => [
'type' => 'success',
'message' => 'Produit ajouté'
]
];
return Ajax::responseSuccess('Produit ajouté');
}
}
}

return [
'return' => 'error',
'alert' => [
'type' => 'danger',
'message' => 'Une erreur est survenue lors de la suppression du produit.'
]
];
return Ajax::responseError('Une erreur est survenue lors de la suppression du produit.');
}

public function actionAjaxDeleteProductOrder($idProductOrder)
{
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

$productOrderManager = $this->getProductOrderManager();

$productOrder = $productOrderManager->findOneProductOrderById($idProductOrder);
@@ -708,22 +662,10 @@ class DocumentController extends BackendController
if ($productOrder) {
$productOrderManager->delete($productOrder);

return [
'return' => 'success',
'alert' => [
'type' => 'danger',
'message' => 'Produit supprimé'
]
];
return Ajax::responseSuccess('Produit supprimé');
}

return [
'return' => 'error',
'alert' => [
'type' => 'danger',
'message' => 'Une erreur est survenue lors de la suppression du produit.'
]
];
return Ajax::responseError('Une erreur est survenue lors de la suppression du produit.');
}

public function getClass()

+ 26
- 18
backend/controllers/InvoiceController.php Voir le fichier

@@ -60,16 +60,14 @@ class InvoiceController extends DocumentController

public function actionAjaxDeleteDeliveryNote($idInvoice, $idDeliveryNote)
{
$orderManager = $this->getOrderManager();
$invoiceManager = $this->getInvoiceManager();
$deliveryNoteManager = $this->getDeliveryNoteManager();
$invoice = $invoiceManager->findOneInvoiceById($idInvoice);
$deliveryNote = $deliveryNoteManager->findOneDeliveryNoteById($idDeliveryNote);

if ($invoice && $invoiceManager->isStatusDraft($invoice)) {
Order::updateAll([
'id_invoice' => null
], [
'id_delivery_note' => $idDeliveryNote
]);
if ($invoice && $invoiceManager->isStatusDraft($invoice) && $deliveryNote) {
$orderManager->unassignAllOrdersInvoiceByDeliveryNote($deliveryNote);
return Ajax::responseSuccess('Bon de livraison supprimé de la facture.');
} else {
return Ajax::responseError('Une erreur est survenue lors de la suppression du bon de livraison.');
@@ -78,6 +76,7 @@ class InvoiceController extends DocumentController

public function actionAjaxAddDeliveryNote($idInvoice, $idDeliveryNote)
{
$orderManager = $this->getOrderManager();
$invoiceManager = $this->getInvoiceManager();
$deliveryNoteManager = $this->getDeliveryNoteManager();

@@ -85,11 +84,7 @@ class InvoiceController extends DocumentController
$deliveryNote = $deliveryNoteManager->findOneDeliveryNoteById($idDeliveryNote);

if ($invoice && $invoiceManager->isStatusDraft($invoice) && $deliveryNote) {
Order::updateAll([
'id_invoice' => $idInvoice
], [
'id_delivery_note' => $idDeliveryNote
]);
$orderManager->assignAllOrdersInvoiceByDeliveryNote($invoice, $deliveryNote);
return Ajax::responseSuccess("Bon de livraison ajouté à la facture.");
} else {
return Ajax::responseError("Une erreur est survenue lors de l'ajout du bon de livraison.");
@@ -105,9 +100,7 @@ class InvoiceController extends DocumentController
$order = $orderManager->findOneOrderById($idOrder);

if ($invoice && $invoiceManager->isStatusDraft($invoice) && $order) {
$order->id_invoice = $idInvoice;
$order->update();

$orderManager->updateOrderInvoice($order, $invoice);
return Ajax::responseSuccess("Commande ajoutée à la facture.");
}
else {
@@ -124,13 +117,28 @@ class InvoiceController extends DocumentController
$order = $orderManager->findOneOrderById($idOrder);

if ($invoice && $invoiceManager->isStatusDraft($invoice)) {
$order->id_invoice = null;
$order->update();

$orderManager->updateOrderInvoice($order, null);
return Ajax::responseSuccess('Commande supprimée de la facture.');
}
else {
return Ajax::responseError('Une erreur est survenue lors de la suppression de la commande.');
}
}

public function actionAjaxIgnoreOrderWhenInvoicing($idInvoice, $idOrder)
{
$invoiceManager = $this->getInvoiceManager();
$orderManager = $this->getOrderManager();

$invoice = $invoiceManager->findOneInvoiceById($idInvoice);
$order = $orderManager->findOneOrderById($idOrder);

if ($invoice && $invoiceManager->isStatusDraft($invoice) && $order) {
$orderManager->updateOrderIgnoreWhenInvoicing($order, true);
return Ajax::responseSuccess("La commande sera maintenant ignorée au moment de la facturation.");
}
else {
return Ajax::responseError("Une erreur est survenue.");
}
}
}

+ 321
- 294
backend/views/document/_form.php Voir le fichier

@@ -38,7 +38,6 @@

use common\logic\Document\Document\Wrapper\DocumentManager;
use common\logic\Producer\Producer\Wrapper\ProducerManager;
use common\logic\User\User\Model\User;
use common\logic\User\User\Wrapper\UserManager;
use yii\helpers\Html;
use yii\widgets\ActiveForm;
@@ -96,25 +95,52 @@ $documentClass = $documentManager->getClass($model);

<?php if ($action == 'create' && $documentClass == 'Invoice'): ?>
<template v-if="idUser > 0">
<strong>Bons de livraison</strong>
<table v-if="deliveryNoteCreateArray && deliveryNoteCreateArray.length > 0" class="table table-bordered">
<thead>
<tr>
<th></th>
<th>Libellé</th>
<th v-if="taxRateProducer != 0">Montant (TTC)</th>
<th v-else>Montant</th>
</tr>
</thead>
<tbody>
<tr v-for="deliveryNote in deliveryNoteCreateArray">
<td><input type="checkbox" name="Invoice[deliveryNotes][]" :value="deliveryNote.id"/></td>
<td>{{ deliveryNote.name }}</td>
<td>{{ formatPrice(deliveryNote.total) }}</td>
</tr>
</tbody>
</table>
<div v-else class="alert alert-warning">Aucun bon de livraison pour cet utilisateur.</div>

<template v-if="deliveryNoteCreateArray && deliveryNoteCreateArray.length > 0">
<strong>Bons de livraison</strong>
<table v-if="deliveryNoteCreateArray && deliveryNoteCreateArray.length > 0"
class="table table-bordered">
<thead>
<tr>
<th></th>
<th>Libellé</th>
<th v-if="taxRateProducer != 0">Montant (TTC)</th>
<th v-else>Montant</th>
</tr>
</thead>
<tbody>
<tr v-for="deliveryNote in deliveryNoteCreateArray">
<td><input type="checkbox" name="Invoice[deliveryNotes][]"
:value="deliveryNote.id"/></td>
<td>{{ deliveryNote.name }}</td>
<td>{{ formatPrice(deliveryNote.total) }}</td>
</tr>
</tbody>
</table>
<div v-else class="alert alert-warning">Aucun bon de livraison pour cet utilisateur.</div>
</template>
<template v-else-if="ordersCreateArray && ordersCreateArray.length > 0">
<strong>Commandes</strong>
<table v-if="ordersCreateArray && ordersCreateArray.length > 0"
class="table table-bordered">
<thead>
<tr>
<th></th>
<th>Date</th>
<th v-if="taxRateProducer != 0">Montant (TTC)</th>
<th v-else>Montant</th>
</tr>
</thead>
<tbody>
<tr v-for="order in ordersCreateArray">
<td><input type="checkbox" name="Invoice[ordersOnCreate][]" :value="order.id"/></td>
<td>{{ order.name }}</td>
<td>{{ formatPrice(order.amount_with_tax) }}</td>
</tr>
</tbody>
</table>
<div v-else class="alert alert-warning">Aucune commande pour cet utilisateur.</div>
</template>
</template>
<?php endif; ?>

@@ -126,319 +152,320 @@ $documentClass = $documentManager->getClass($model);
</div>
</div>

<?php if ($action == 'update'): ?>
<?php if ($action == 'update'): ?>

<div class="col-md-6">
<div id="" class="info-box">
<span class="info-box-icon bg-green"><i class="fa fa-sticky-note-o"></i></span>
<div class="info-box-content">
<div class="col-md-6">
<div id="" class="info-box">
<span class="info-box-icon bg-green"><i class="fa fa-sticky-note-o"></i></span>
<div class="info-box-content">
<span class="info-box-text">
<?= $typeDocument ?>
<span v-html="document.html_label"></span>
<span v-if="document.is_sent" class="label label-success">Envoyé</span>
</span>
<span class="info-box-number">{{ document.reference }}</span>
<span class="info-box-text">Date</span>
<span class="info-box-number">{{ document.date }}</span>
</div>
<span class="info-box-number">{{ document.reference }}</span>
<span class="info-box-text">Date</span>
<span class="info-box-number">{{ document.date }}</span>
</div>
<div id="" class="info-box">
<span class="info-box-icon bg-yellow"><i class="fa fa-euro"></i></span>
<div class="info-box-content">
<span class="info-box-text">Total<span v-if="taxRateProducer != 0"> (TTC)</span></span>
<span class="info-box-number">{{ formatPrice(total_with_tax) }}</span>
<p v-if="invoiceUrl">
<a class="btn btn-sm btn-default" :href="invoiceUrl">
<span class="glyphicon glyphicon-eye-open"></span> Voir la facture
</a>
</p>
</div>
</div>
<div id="" class="info-box">
<span class="info-box-icon bg-yellow"><i class="fa fa-euro"></i></span>
<div class="info-box-content">
<span class="info-box-text">Total<span v-if="taxRateProducer != 0"> (TTC)</span></span>
<span class="info-box-number">{{ formatPrice(total_with_tax) }}</span>
<p v-if="invoiceUrl">
<a class="btn btn-sm btn-default" :href="invoiceUrl">
<span class="glyphicon glyphicon-eye-open"></span> Voir la facture
</a>
</p>
</div>
<div id="" class="info-box">
<span class="info-box-icon bg-yellow"><i class="fa fa-calendar"></i></span>
<div class="info-box-content">
<span class="info-box-text">Commandes</span>
<?php foreach($model->orders as $order): ?>
<?php if($order->distribution): ?>
<a class="btn btn-sm btn-default" href="<?= Yii::$app->urlManager->createUrl(['distribution/index', 'idOrderUpdate' => $order->id]); ?>">
<?= date('d/m/Y', strtotime($order->distribution->date)) ?>
</a>
<?php endif; ?>
<?php endforeach; ?>
</div>
</div>
<div id="" class="info-box">
<span class="info-box-icon bg-yellow"><i class="fa fa-calendar"></i></span>
<div class="info-box-content">
<span class="info-box-text">Commandes</span>
<?php foreach ($model->orders as $order): ?>
<?php if ($order->distribution): ?>
<a class="btn btn-sm btn-default"
href="<?= Yii::$app->urlManager->createUrl(['distribution/index', 'idOrderUpdate' => $order->id]); ?>">
<?= date('d/m/Y', strtotime($order->distribution->date)) ?>
</a>
<?php endif; ?>
<?php endforeach; ?>
</div>
<div id="" class="info-box">
<span class="info-box-icon bg-blue"><i class="fa fa-download"></i></span>
<div class="info-box-content">
<a href="<?= Yii::$app->urlManager->createUrl([Yii::$app->controller->getControllerUrl() . '/download', 'id' => $model->id]) ?>"
class="btn btn-sm btn-default"><span class="glyphicon glyphicon-download-alt"></span> Télécharger (PDF)</a>
</div>
<div id="" class="info-box">
<span class="info-box-icon bg-blue"><i class="fa fa-download"></i></span>
<div class="info-box-content">
<a href="<?= Yii::$app->urlManager->createUrl([Yii::$app->controller->getControllerUrl() . '/download', 'id' => $model->id]) ?>"
class="btn btn-sm btn-default"><span class="glyphicon glyphicon-download-alt"></span> Télécharger
(PDF)</a>

<?php if($documentManager->isStatusValid($model)): ?>
<a href="<?= Yii::$app->urlManager->createUrl([Yii::$app->controller->getControllerUrl() . '/regenerate', 'id' => $model->id]) ?>"
class="btn btn-sm btn-default"><span class="glyphicon glyphicon-repeat"></span> Regénérer (PDF)</a>
<?php endif; ?>
<?php if ($documentManager->isStatusValid($model)): ?>
<a href="<?= Yii::$app->urlManager->createUrl([Yii::$app->controller->getControllerUrl() . '/regenerate', 'id' => $model->id]) ?>"
class="btn btn-sm btn-default"><span class="glyphicon glyphicon-repeat"></span> Regénérer
(PDF)</a>
<?php endif; ?>

<?php if ($documentClass == 'Invoice' && $producerManager->getConfig('option_export_evoliz')): ?>
<a href="<?= Yii::$app->urlManager->createUrl([Yii::$app->controller->getControllerUrl() . '/export-csv-evoliz', 'id' => $model->id]) ?>"
class="btn btn-sm btn-default"><span class="glyphicon glyphicon-save-file"></span> Export Evoliz
(CSV)</a>
<?php endif; ?>
</div>
<?php if ($documentClass == 'Invoice' && $producerManager->getConfig('option_export_evoliz')): ?>
<a href="<?= Yii::$app->urlManager->createUrl([Yii::$app->controller->getControllerUrl() . '/export-csv-evoliz', 'id' => $model->id]) ?>"
class="btn btn-sm btn-default"><span class="glyphicon glyphicon-save-file"></span> Export Evoliz
(CSV)</a>
<?php endif; ?>
</div>
<div v-if="document.status == 'draft' || !document.is_sent" class="info-box">
<span class="info-box-icon bg-red"><i class="fa fa-flash"></i></span>
<div class="info-box-content">
</div>
<div v-if="document.status == 'draft' || !document.is_sent" class="info-box">
<span class="info-box-icon bg-red"><i class="fa fa-flash"></i></span>
<div class="info-box-content">

<a v-if="document.status == 'draft'" href="<?= Yii::$app->urlManager->createUrl([Yii::$app->controller->getControllerUrl() . '/validate', 'id' => $model->id, 'backUpdateForm' => 1]) ?>"
class="btn btn-sm btn-default"><span class="glyphicon glyphicon-ok"></span> Valider le document</a>
<a v-if="document.status == 'draft'"
href="<?= Yii::$app->urlManager->createUrl([Yii::$app->controller->getControllerUrl() . '/validate', 'id' => $model->id, 'backUpdateForm' => 1]) ?>"
class="btn btn-sm btn-default"><span class="glyphicon glyphicon-ok"></span> Valider le document</a>

<?php if (isset($model->user) && strlen($model->user->email) > 0): ?>
<a v-if="!document.is_sent" href="<?= Yii::$app->urlManager->createUrl([Yii::$app->controller->getControllerUrl() . '/send', 'id' => $model->id, 'backUpdateForm' => 1]) ?>"
class="btn btn-sm btn-default"><span class="glyphicon glyphicon-send"></span> Envoyer le
document</a>
<?php endif; ?>
</div>
<?php if (isset($model->user) && strlen($model->user->email) > 0): ?>
<a v-if="!document.is_sent"
href="<?= Yii::$app->urlManager->createUrl([Yii::$app->controller->getControllerUrl() . '/send', 'id' => $model->id, 'backUpdateForm' => 1]) ?>"
class="btn btn-sm btn-default"><span class="glyphicon glyphicon-send"></span> Envoyer le
document</a>
<?php endif; ?>
</div>
</div>
<div class="clr"></div>
</div>
<div class="clr"></div>

<?php if ($action == 'update' && $documentClass == 'Invoice'): ?>
<div class="row">
<div class="col-md-6" v-if="(deliveryNoteUpdateArray && deliveryNoteUpdateArray.length > 0) || (deliveryNoteCreateArray && deliveryNoteCreateArray.length > 0)">
<div class="panel panel-default">
<div class="panel-heading">
Bons de livraison
</div>
<div class="panel-body">
<table v-if="deliveryNoteUpdateArray && deliveryNoteUpdateArray.length > 0" class="table table-bordered">
<thead>
<tr>
<th>Libellé</th>
<th v-if="taxRateProducer != 0">Montant (TTC)</th>
<th v-else>Montant</th>
<th v-if="document.status == 'draft'"></th>
</tr>
</thead>
<tbody>
<tr v-for="deliveryNote in deliveryNoteUpdateArray">
<td><a :href="deliveryNote.url">{{ deliveryNote.name }}</a></td>
<td>{{ formatPrice(deliveryNote.total) }}</td>
<td v-if="document.status == 'draft'"><a class="btn btn-default" href="javascript:void(0);" @click="deleteDeliveryNoteFromInvoice" :data-id="deliveryNote.id"><span class="glyphicon glyphicon-trash"></span></a></td>
</tr>
</tbody>
</table>
<div v-else class="alert alert-warning">Aucun bon de livraison associé.</div>
<?php if ($action == 'update' && $documentClass == 'Invoice'): ?>
<div v-if="(deliveryNoteUpdateArray && deliveryNoteUpdateArray.length > 0) || (deliveryNoteCreateArray && deliveryNoteCreateArray.length > 0)">
<div class="panel panel-default">
<div class="panel-heading">
Bons de livraison
</div>
<div class="panel-body">
<table v-if="deliveryNoteUpdateArray && deliveryNoteUpdateArray.length > 0"
class="table table-bordered">
<thead>
<tr>
<th>Libellé</th>
<th v-if="taxRateProducer != 0">Montant (TTC)</th>
<th v-else>Montant</th>
<th v-if="document.status == 'draft'"></th>
</tr>
</thead>
<tbody>
<tr v-for="deliveryNote in deliveryNoteUpdateArray">
<td><a :href="deliveryNote.url">{{ deliveryNote.name }}</a></td>
<td>{{ formatPrice(deliveryNote.total) }}</td>
<td v-if="document.status == 'draft'"><a class="btn btn-default" href="javascript:void(0);"
@click="deleteDeliveryNoteFromInvoice"
:data-id="deliveryNote.id"><span
class="glyphicon glyphicon-trash"></span></a></td>
</tr>
</tbody>
</table>
<div v-else class="alert alert-warning">Aucun bon de livraison associé.</div>

<div v-if="document.status == 'draft'" id="delivery-note-add">
<div class="col-md-8">
<select class="form-control" v-model="deliveryNoteAddId">
<option value="0" selected="selected">--</option>
<option v-for="deliveryNote in deliveryNoteCreateArray" :value="deliveryNote.id">
{{ deliveryNote.name }}
</option>
</select>
</div>
<div class="col-md-4">
<button class="btn btn-primary" value="Ajouter" @click="submitDeliveryNoteAddToInvoice">Ajouter</button>
</div>
</div>
<div v-if="document.status == 'draft'" id="delivery-note-add">
<div class="col-md-8">
<select class="form-control" v-model="deliveryNoteAddId">
<option value="0" selected="selected">--</option>
<option v-for="deliveryNote in deliveryNoteCreateArray" :value="deliveryNote.id">
{{ deliveryNote.name }}
</option>
</select>
</div>
<div class="col-md-4">
<button class="btn btn-primary" value="Ajouter" @click="submitDeliveryNoteAddToInvoice">
Ajouter
</button>
</div>
</div>
</div>
<div class="col-md-6" v-else>
<div class="panel panel-default">
<div class="panel-heading">
Commandes
</div>
<div class="panel-body">
<table v-if="ordersUpdateArray && ordersUpdateArray.length > 0" class="table table-bordered">
<thead>
<tr>
<th>Date</th>
<th v-if="taxRateProducer != 0">Montant (TTC)</th>
<th v-else>Montant</th>
<th v-if="document.status == 'draft'"></th>
</tr>
</thead>
<tbody>
<tr v-for="order in ordersUpdateArray">
<td>{{ order.name }}</td>
<td>{{ formatPrice(order.amount_with_tax) }}</td>
<td v-if="document.status == 'draft'">
<a class="btn btn-default" href="javascript:void(0);" @click="deleteOrderFromInvoice" :data-id="order.id">
<span class="glyphicon glyphicon-trash"></span>
</a>
</td>
</tr>
</tbody>
</table>
<div v-else class="alert alert-warning">Aucune commande associée.</div>
</div>
</div>
<div v-else>
<div class="panel panel-default">
<div class="panel-heading">
Commandes
</div>
<div class="panel-body">
<table v-if="ordersUpdateArray && ordersUpdateArray.length > 0" class="table table-bordered">
<thead>
<tr>
<th>Date</th>
<th v-if="taxRateProducer != 0">Montant (TTC)</th>
<th v-else>Montant</th>
<th v-if="document.status == 'draft'"></th>
</tr>
</thead>
<tbody>
<tr v-for="order in ordersUpdateArray">
<td>{{ order.name }}</td>
<td>{{ formatPrice(order.amount_with_tax) }}</td>
<td v-if="document.status == 'draft'">
<a class="btn btn-default" href="javascript:void(0);" @click="deleteOrderFromInvoice"
:data-id="order.id">
<span class="glyphicon glyphicon-trash"></span>
</a>
</td>
</tr>
</tbody>
</table>
<div v-else class="alert alert-warning">Aucune commande associée.</div>

<div v-if="document.status == 'draft'" id="order-add">
<div class="col-md-8">
<select class="form-control" v-model="orderAddId">
<option value="0" selected="selected">--</option>
<option v-for="order in ordersCreateArray" :value="order.id">
{{ order.name }}
</option>
</select>
</div>
<div class="col-md-4">
<button class="btn btn-primary" value="Ajouter" @click="submitOrderAddToInvoice">Ajouter</button>
</div>
</div>
<div v-if="document.status == 'draft'" id="order-add">
<div class="col-md-8">
<select class="form-control" v-model="orderAddId">
<option value="0" selected="selected">--</option>
<option v-for="order in ordersCreateArray" :value="order.id">
{{ order.name }}
</option>
</select>
</div>
<div class="col-md-4">
<button class="btn btn-primary" value="Ajouter" @click="submitOrderAddToInvoice">Ajouter</button>
<button class="btn btn-danger" value="Ignorer" @click="submitOrderIgnoreWhenInvoicing">Ignorer</button>
</div>
</div>
</div>
</div>
<div class="clr"></div>
<?php endif; ?>
</div>
<div class="clr"></div>
<?php endif; ?>

<div class="">
<div class="panel panel-default" id="block-add-product">
<div class="panel-heading">
Ajouter un produit
</div>
<div class="panel-body">
<div class="col-md-6">
<strong>Produit</strong>
<select class="form-control" v-model="productAddId"
@change="changeProductAdd">
<option value="0" selected="selected">--</option>
<option v-for="product in productsArray" :value="product.id">
{{ product.name }}
</option>
</select>
<div class="panel panel-default" id="block-add-product">
<div class="panel-heading">
Ajouter un produit
</div>
<div class="panel-body">
<div class="col-md-6">
<strong>Produit</strong>
<select class="form-control" v-model="productAddId"
@change="changeProductAdd">
<option value="0" selected="selected">--</option>
<option v-for="product in productsArray" :value="product.id">
{{ product.name }}
</option>
</select>
</div>
<template v-if="productAddId > 0">
<div class="col-md-3">
<strong>Prix unitaire</strong>
<div class="input-group">
<input type="text" class="form-control input-price"
v-model="productAddPrice" @change="formatProductAddPrice"/>
<span class="input-group-addon"><span
class="glyphicon glyphicon-euro"></span> <span v-if="taxRateProducer != 0">HT</span></span>
</div>
<template v-if="productAddId > 0">
<div class="col-md-3">
<strong>Prix unitaire</strong>
<div class="input-group">
<input type="text" class="form-control input-price"
v-model="productAddPrice" @change="formatProductAddPrice"/>
<span class="input-group-addon"><span
class="glyphicon glyphicon-euro"></span> <span v-if="taxRateProducer != 0">HT</span></span>
</div>
</div>
<div class="col-md-3 total">
<strong>Quantité</strong>
<div class="input-group input-group-quantity">
</div>
<div class="col-md-3 total">
<strong>Quantité</strong>
<div class="input-group input-group-quantity">
<span class="input-group-btn">
<button class="btn btn-default" type="button"
@click="changeQuantityProductAdd(-1)">-</button>
</span>
<input type="text" class="form-control input-quantity"
v-model="productAddQuantity" @change="formatProductAddQuantity"/>
<span class="input-group-addon">{{ getProductById(productAddId).wording_unit }}</span>
<span class="input-group-btn">
<input type="text" class="form-control input-quantity"
v-model="productAddQuantity" @change="formatProductAddQuantity"/>
<span class="input-group-addon">{{ getProductById(productAddId).wording_unit }}</span>
<span class="input-group-btn">
<button class="btn btn-default"
type="button"
@click="changeQuantityProductAdd(1)">+</button>
</span>
</div>
<button class="btn btn-primary" value="Ajouter"
@click="submitProductAdd">Ajouter
</button>
<div class="clr"></div>
</div>
<!--<div class="col-md-3 total">
<strong>Total</strong>
<div class="input-group">
<input type="text" class="form-control input-price" readonly
:value="formatPrice(productAddPrice * productAddQuantity)"/>
<span class="input-group-addon"><span
class="glyphicon glyphicon-euro"></span> <span v-if="taxRateProducer != 0">HT</span></span>
</div>
</div>-->
</template>
</div>
<button class="btn btn-primary" value="Ajouter"
@click="submitProductAdd">Ajouter
</button>
<div class="clr"></div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
Produits
</div>
<div class="panel-body">
<div id="block-list-products">
<table class="table table-bordered" v-if="Object.keys(ordersArray).length > 0">
<thead>
<tr>
<th>Nom</th>
<th>Prix (unité)</th>
<th>Quantité</th>
<th v-if="taxRateProducer != 0">TVA</th>
<th v-if="taxRateProducer != 0">Total HT</th>
<th v-else>Total</th>
<th>Supprimer</th>
</tr>
</thead>
<tbody>
<template v-for="order in ordersArray">
<tr v-for="productOrder in order.productOrder">
<td class="col-md-4">
<div class="product-name">{{ getProductById(productOrder.id_product).name }}
</div>
<ul class="product-order-meta">
<li v-if="order.distribution_date">Commande : <a class="btn btn-sm btn-default" :href="productOrder.url_order">{{ order.distribution_date }}</a></li>
<li>Utilisateur : {{ order.username }}</li>
<li v-if="order.point_sale_name">Point de vente : {{ order.point_sale_name }}</li>
</ul>
</td>
<td class="col-md-2">
{{ formatPrice(getProductOrderPrice(productOrder)) }}
<template v-if="document.status == 'draft' && getProductOrderPrice(productOrder) != getBestProductPrice(productOrder.id_product, productOrder.quantity)">
<i class="fa fa-exclamation-triangle" title="Prix différent de celui défini au niveau du produit"></i>
</template>
</td>
<td class="col-md-2">{{ productOrder.quantity }}</td>
<td class="col-md-1" v-if="taxRateProducer != 0">
{{ getProductById(productOrder.id_product).tax_rate * 100 }} %
</td>
<td class="col-md-2">
{{ formatPrice(productOrder.quantity * getProductOrderPrice(productOrder)) }}
</td>
<td class="col-md-1">
<a class="btn btn-default" @click="deleteProductOrder(productOrder.id)">
<span class="glyphicon glyphicon-trash"></span>
</a>
</td>
</tr>
</template>
<template v-if="taxRateProducer != 0">
<tr>
<td colspan="4"><strong>Total HT</strong></td>
<td><strong>{{ formatPrice(total) }} HT</strong></td>
<td></td>
</tr>
<tr>
<td colspan="4"><strong>Montant TVA</strong></td>
<td><strong>{{ formatPrice(total_with_tax - total) }}</strong></td>
<td></td>
</tr>
<tr>
<td colspan="4"><strong>Total TTC</strong></td>
<td><strong>{{ formatPrice(total_with_tax) }} TTC</strong></td>
<td></td>
</tr>
</template>
<template v-else>
<tr>
<td colspan="3"><strong>Total</strong></td>
<td><strong>{{ formatPrice(total) }}</strong></td>
<td></td>
</tr>
</template>
</template>
<div class="clr"></div>
</div>
</div>

</tbody>
</table>
<div v-else class="alert alert-info">
Aucun produit.
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
Produits
</div>
<div class="panel-body">
<div id="block-list-products">
<table class="table table-bordered" v-if="Object.keys(ordersArray).length > 0">
<thead>
<tr>
<th>Nom</th>
<th>Prix (unité)</th>
<th>Quantité</th>
<th v-if="taxRateProducer != 0">TVA</th>
<th v-if="taxRateProducer != 0">Total HT</th>
<th v-else>Total</th>
<th>Supprimer</th>
</tr>
</thead>
<tbody>
<template v-for="order in ordersArray">
<tr v-for="productOrder in order.productOrder">
<td class="col-md-4">
<div class="product-name">{{ getProductById(productOrder.id_product).name }}
</div>
<ul class="product-order-meta">
<li v-if="order.distribution_date">Commande : <a class="btn btn-sm btn-default"
:href="productOrder.url_order">{{
order.distribution_date }}</a></li>
<li>Utilisateur : {{ order.username }}</li>
<li v-if="order.point_sale_name">Point de vente : {{ order.point_sale_name }}</li>
</ul>
</td>
<td class="col-md-2">
{{ formatPrice(getProductOrderPrice(productOrder)) }}
<template
v-if="document.status == 'draft' && getProductOrderPrice(productOrder) != getBestProductPrice(productOrder.id_product, productOrder.quantity)">
<i class="fa fa-exclamation-triangle"
title="Prix différent de celui défini au niveau du produit"></i>
</template>
</td>
<td class="col-md-2">{{ productOrder.quantity }}</td>
<td class="col-md-1" v-if="taxRateProducer != 0">
{{ getProductById(productOrder.id_product).tax_rate * 100 }} %
</td>
<td class="col-md-2">
{{ formatPrice(productOrder.quantity * getProductOrderPrice(productOrder)) }}
</td>
<td class="col-md-1">
<a class="btn btn-default" @click="deleteProductOrder(productOrder.id)">
<span class="glyphicon glyphicon-trash"></span>
</a>
</td>
</tr>
</template>
<template v-if="taxRateProducer != 0">
<tr>
<td colspan="4"><strong>Total HT</strong></td>
<td><strong>{{ formatPrice(total) }} HT</strong></td>
<td></td>
</tr>
<tr>
<td colspan="4"><strong>Montant TVA</strong></td>
<td><strong>{{ formatPrice(total_with_tax - total) }}</strong></td>
<td></td>
</tr>
<tr>
<td colspan="4"><strong>Total TTC</strong></td>
<td><strong>{{ formatPrice(total_with_tax) }} TTC</strong></td>
<td></td>
</tr>
</template>
<template v-else>
<tr>
<td colspan="3"><strong>Total</strong></td>
<td><strong>{{ formatPrice(total) }}</strong></td>
<td></td>
</tr>
</template>
</tbody>
</table>
<div v-else class="alert alert-info">
Aucun produit.
</div>
</div>
</div>
<?php endif; ?>


</div>
</div>
<?php endif; ?>

+ 16
- 15
backend/web/js/vuejs/document-form.js Voir le fichier

@@ -153,19 +153,6 @@ var app = new Vue({
}
});
},
validateDocument: function () {
var app = this;
axios.get(UrlManager.getBaseUrlAbsolute() + "document/ajax-validate-document", {
params: {
idDocument: app.getDocumentId(),
classDocument: app.getDocumentClass(),
}
})
.then(function (response) {
appAlerts.alertResponse(response);
app.init();
});
},
deleteDeliveryNoteFromInvoice: function(event) {
var app = this;
var idDeliveryNote = event.currentTarget.getAttribute('data-id');
@@ -200,12 +187,26 @@ var app = new Vue({
axios.get(UrlManager.getBaseUrlAbsolute() + "invoice/ajax-add-order", {
params: {
idInvoice: this.getDocumentId(),
idOrder: app.orderAddId,
idOrder: app.orderAddId
}
})
.then(function (response) {
appAlerts.alertResponse(response);
app.deliveryNoteAddId = 0;
app.orderAddId = 0;
app.init();
});
},
submitOrderIgnoreWhenInvoicing: function() {
var app = this;
axios.get(UrlManager.getBaseUrlAbsolute() + "invoice/ajax-ignore-order-when-invoicing", {
params: {
idInvoice: this.getDocumentId(),
idOrder: app.orderAddId
}
})
.then(function (response) {
appAlerts.alertResponse(response);
app.orderAddId = 0;
app.init();
});
},

+ 1
- 1
common/logic/Document/DeliveryNote/Model/DeliveryNote.php Voir le fichier

@@ -56,7 +56,7 @@ class DeliveryNote extends Document
{
$rules = parent::rules();

$rules[] = ['ignore_when_billing', 'boolean'];
$rules[] = ['ignore_when_invoicing', 'boolean'];

return $rules;
}

+ 1
- 1
common/logic/Document/Document/Model/Document.php Voir le fichier

@@ -70,7 +70,7 @@ class Document extends ActiveRecordCommon implements DocumentInterface
[['id_user', 'id_producer'], 'integer'],
[['is_sent'], 'boolean'],
[['name', 'reference', 'status'], 'string', 'max' => 255],
[['deliveryNotes'], 'safe']
[['deliveryNotes', 'ordersOnCreate'], 'safe']
];
}


+ 6
- 0
common/logic/Document/Document/Service/DocumentBuilder.php Voir le fichier

@@ -87,4 +87,10 @@ class DocumentBuilder extends AbstractBuilder
$document->tax_calculation_method = Document::TAX_CALCULATION_METHOD_DEFAULT;
}
}

public function updateDocumentIsSend(Document $document, bool $isSent)
{
$document->is_sent = $isSent;
$this->update($document);
}
}

+ 5
- 2
common/logic/Document/Document/Service/DocumentUtils.php Voir le fichier

@@ -100,10 +100,10 @@ class DocumentUtils extends AbstractService implements UtilsInterface
}
}

public function send(DocumentInterface $document): bool
public function sendDocument(DocumentInterface $document): bool
{
if (isset($document->user) && strlen($document->user->email) > 0) {
$producer = GlobalParam::getCurrentProducer();
$producer = $this->getProducerContext();

$subjectEmail = $this->documentSolver->getType($document);
if ($this->documentSolver->isStatusValid($document)) {
@@ -124,6 +124,9 @@ class DocumentUtils extends AbstractService implements UtilsInterface
$this->generatePdf($document, Pdf::DEST_FILE);
$email->attach($this->documentSolver->getFilenameComplete($document));

// @TODO : gérer via un événement
$this->documentBuilder->updateDocumentIsSend($document, true);

return $email->send();
}


+ 1
- 0
common/logic/Document/Invoice/Model/Invoice.php Voir le fichier

@@ -46,6 +46,7 @@ use common\logic\Document\Document\Model\Document;
class Invoice extends Document
{
public $deliveryNotes;
public $ordersOnCreate;

/**
* @inheritdoc

+ 2
- 1
common/logic/Order/Order/Model/Order.php Voir le fichier

@@ -108,7 +108,7 @@ class Order extends ActiveRecordCommon
],
'integer'
],
[['auto_payment', 'tiller_synchronization', 'delivery_home'], 'boolean'],
[['auto_payment', 'tiller_synchronization', 'delivery_home', 'ignore_when_invoicing'], 'boolean'],
[['status', 'reference', 'delivery_address', 'online_payment_url', 'tiller_external_id'], 'string'],
[['date', 'date_update', 'comment', 'comment_point_sale', 'mean_payment', 'tiller_external_id'], 'safe']
];
@@ -136,6 +136,7 @@ class Order extends ActiveRecordCommon
'delivery_address' => 'Adresse de livraison',
'online_payment_url' => 'URL de paiement',
'tiller_external_id' => 'Tiller : externalId',
'ignore_when_invoicing' => "Ignorer au moment de la facturation"
];
}


+ 3
- 1
common/logic/Order/Order/Repository/OrderRepository.php Voir le fichier

@@ -57,7 +57,7 @@ class OrderRepository extends AbstractRepository
'productOrder.product',
'creditHistory',
'creditHistory.userAction',
'pointSale'
'pointSale',
],
self::JOIN_WITH => [
'distribution',
@@ -132,6 +132,8 @@ class OrderRepository extends AbstractRepository
->filterIsNotInvoiced()
->filterIsPassed()
->filterIsValid()
->filterIsNotIgnoreWhenInvoicing()
->orderByDistributionDate('ASC')
->find();
}


+ 14
- 0
common/logic/Order/Order/Repository/OrderRepositoryQuery.php Voir le fichier

@@ -90,4 +90,18 @@ class OrderRepositoryQuery extends AbstractRepositoryQuery

return $this;
}

public function filterIsNotIgnoreWhenInvoicing(): self
{
$this->andWhere('order.ignore_when_invoicing IS NULL');

return $this;
}

public function orderByDistributionDate(string $order = 'ASC'): self
{
$this->orderBy(['distribution.date' => $order]);

return $this;
}
}

+ 37
- 0
common/logic/Order/Order/Service/OrderBuilder.php Voir le fichier

@@ -13,6 +13,7 @@ use common\logic\Distribution\Distribution\Service\DistributionSolver;
use common\logic\Document\DeliveryNote\Model\DeliveryNote;
use common\logic\Document\DeliveryNote\Service\DeliveryNoteBuilder;
use common\logic\Document\Document\Model\Document;
use common\logic\Document\Invoice\Model\Invoice;
use common\logic\Order\Order\Model\Order;
use common\logic\Order\Order\Repository\OrderRepository;
use common\logic\Order\ProductOrder\Model\ProductOrder;
@@ -653,4 +654,40 @@ class OrderBuilder extends AbstractBuilder
'id_delivery_note' => $deliveryNote->id
]);
}

public function unassignAllOrdersInvoiceByDeliveryNote(DeliveryNote $deliveryNote)
{
Order::updateAll([
'id_invoice' => null
], [
'id_delivery_note' => $deliveryNote->id
]);
}

public function assignAllOrdersInvoiceByDeliveryNote(Invoice $invoice, DeliveryNote $deliveryNote)
{
Order::updateAll([
'id_invoice' => $invoice->id
], [
'id_delivery_note' => $deliveryNote->id
]);
}

public function updateOrderIgnoreWhenInvoicing(Order $order, bool $ignoreWhenInvoicing)
{
$order->ignore_when_invoicing = $ignoreWhenInvoicing;
$this->update($order);
}

public function updateOrderInvoice(Order $order, Invoice $invoice = null)
{
if($invoice) {
$order->populateInvoice($invoice);
}
else {
$order->id_invoice = null;
}

$this->update($order);
}
}

+ 28
- 0
console/migrations/m230912_070221_add_column_order_ignore_when_invoicing.php Voir le fichier

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

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

/**
* Class m230912_070221_add_column_order_ignore_when_invoicing
*/
class m230912_070221_add_column_order_ignore_when_invoicing extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->addColumn('order', 'ignore_when_invoicing', Schema::TYPE_BOOLEAN);
$this->renameColumn('delivery_note', 'ignore_when_billing', 'ignore_when_invoicing');
}

/**
* {@inheritdoc}
*/
public function safeDown()
{
$this->dropColumn('order', 'ignore_when_invoicing');
$this->renameColumn('delivery_note', 'ignore_when_invoicing', 'ignore_when_billing');
}
}

Chargement…
Annuler
Enregistrer