Browse Source

[Administration] Dolibarr : génération automatique des factures producteurs #1182

feature/souke
Guillaume Bourgeois 1 year ago
parent
commit
9fb4aa0545
19 changed files with 277 additions and 8 deletions
  1. +16
    -1
      backend/controllers/ProducerAdminController.php
  2. +3
    -0
      backend/views/producer-admin/_form.php
  3. +11
    -1
      backend/views/producer-admin/index.php
  4. +1
    -0
      backend/views/producer-price-range-admin/create.php
  5. +3
    -2
      backend/views/producer-price-range-admin/update.php
  6. +62
    -0
      common/components/AbstractApi.php
  7. +51
    -0
      common/components/DolibarrApi.php
  8. +7
    -0
      common/config/main.php
  9. +8
    -0
      common/logic/AbstractUtils.php
  10. +5
    -1
      common/logic/Producer/Producer/Model/Producer.php
  11. +6
    -0
      common/logic/Producer/Producer/Repository/ProducerRepository.php
  12. +52
    -0
      common/logic/Producer/Producer/Service/DolibarrProducerUtils.php
  13. +3
    -1
      common/logic/Producer/Producer/Wrapper/ProducerContainer.php
  14. +2
    -0
      common/logic/Producer/Producer/Wrapper/ProducerManager.php
  15. +2
    -0
      common/logic/Producer/ProducerPriceRange/Model/ProducerPriceRange.php
  16. +12
    -0
      common/logic/Producer/ProducerPriceRange/Repository/ProducerPriceRangeRepository.php
  17. +1
    -1
      common/logic/UtilsInterface.php
  18. +2
    -1
      composer.json
  19. +30
    -0
      console/migrations/m230905_091112_add_columns_dolibarr.php

+ 16
- 1
backend/controllers/ProducerAdminController.php View File

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


public function actionDolibarr(int $id)
{
$producerManager = $this->getProducerManager();
$producer = $this->findProducer($id);
if($producer->dolibarr_socid) {
$producerManager->generateDolibarrProducerInvoice($producer);
$this->setFlash('success', "Facture générée sur Dolibarr pour le producteur <strong>".$producer->name."</strong>");
}
else {
$this->setFlash('error', "Dolibarr : l'id user du producteur doit être défini");
}

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

public function actionUserTransfer($fromProducerId, $toProducerId, $withOrders = 1) public function actionUserTransfer($fromProducerId, $toProducerId, $withOrders = 1)
{ {
$producerManager = $this->getProducerManager(); $producerManager = $this->getProducerManager();
if (($model = $producerManager->findOneProducerById($id)) !== null) { if (($model = $producerManager->findOneProducerById($id)) !== null) {
return $model; return $model;
} else { } else {
throw new NotFoundHttpException('The requested page does not exist.');
throw new NotFoundHttpException('Producteur introuvable.');
} }
} }



+ 3
- 0
backend/views/producer-admin/_form.php View File

1 => 'Oui' 1 => 'Oui'
]); ?> ]); ?>
<?= $form->field($model, 'option_billing_permanent_transfer_amount') ?> <?= $form->field($model, 'option_billing_permanent_transfer_amount') ?>
<?= $form->field($model, 'dolibarr_socid') ?>
<?= $form->field($model, 'dolibarr_product_id') ?>

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

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

], ],
[ [
'class' => 'yii\grid\ActionColumn', 'class' => 'yii\grid\ActionColumn',
'template' => '{update} {billing} {alwaysdata}',
'template' => '{update} {dolibarr} {billing} {alwaysdata}',
'headerOptions' => ['class' => 'column-actions'], 'headerOptions' => ['class' => 'column-actions'],
'contentOptions' => ['class' => 'column-actions'], 'contentOptions' => ['class' => 'column-actions'],
'buttons' => [ 'buttons' => [
] ]
); );
}, },
'dolibarr' => function ($url, $model) {
return Html::a(
'<span class="glyphicon glyphicon-paste"></span>',
$url,
[
'title' => 'Générer la facture sur Dolibarr',
'class' => 'btn btn-default'
]
);
},
'billing' => function ($url, $model) { 'billing' => function ($url, $model) {
return Html::a( return Html::a(
'<span class="glyphicon glyphicon-euro"></span>', '<span class="glyphicon glyphicon-euro"></span>',

+ 1
- 0
backend/views/producer-price-range-admin/create.php View File

<?= $form->field($model, 'range_begin') ?> <?= $form->field($model, 'range_begin') ?>
<?= $form->field($model, 'range_end') ?> <?= $form->field($model, 'range_end') ?>
<?= $form->field($model, 'price') ?> <?= $form->field($model, 'price') ?>
<?= $form->field($model, 'dolibarr_product_id') ?>
<div class="form-group"> <div class="form-group">
<?= Html::submitButton('Ajouter', ['class' => 'btn btn-success']) ?> <?= Html::submitButton('Ajouter', ['class' => 'btn btn-success']) ?>
</div> </div>

+ 3
- 2
backend/views/producer-price-range-admin/update.php View File

use yii\helpers\Html; use yii\helpers\Html;
use yii\widgets\ActiveForm; use yii\widgets\ActiveForm;


$this->setTitle('Éditer une taxe') ;
$this->addBreadcrumb(['label' => 'taxe', 'url' => ['index']]) ;
$this->setTitle('Éditer une tranche de prix') ;
$this->addBreadcrumb(['label' => 'tranche de prix', 'url' => ['index']]) ;
$this->addBreadcrumb('Éditer') ; $this->addBreadcrumb('Éditer') ;


?> ?>
<?= $form->field($model, 'range_begin') ?> <?= $form->field($model, 'range_begin') ?>
<?= $form->field($model, 'range_end') ?> <?= $form->field($model, 'range_end') ?>
<?= $form->field($model, 'price') ?> <?= $form->field($model, 'price') ?>
<?= $form->field($model, 'dolibarr_product_id') ?>
<div class="form-group"> <div class="form-group">
<?= Html::submitButton('Ajouter', ['class' => 'btn btn-success']) ?> <?= Html::submitButton('Ajouter', ['class' => 'btn btn-success']) ?>
</div> </div>

+ 62
- 0
common/components/AbstractApi.php View File

<?php

namespace common\components;

use GuzzleHttp\Client;
use linslin\yii2\curl\Curl;
use Psr\Http\Message\ResponseInterface;

abstract class AbstractApi
{
public string $url;
public array $auth;

public function __construct(string $url, array $auth)
{
$this->url = $url;
$this->auth = $auth;
}

protected function get(string $resource, array $data = [], bool $raw = false)
{
$client = $this->getClient();
$client->setGetParams($data);
return $client->get($this->buildUrl($resource), $raw);
}

protected function post(string $resource, array $data = [], bool $raw = true)
{
$client = $this->getClient();
$client->setPostParams($data);
return $client->post($this->buildUrl($resource), $raw);
}

protected function put(string $resource, array $data = [], bool $raw = true)
{
$client = $this->getClient();
$client->setPostParams($data);
return $client->put($this->buildUrl($resource), $raw);
}

private function getClient()
{
$curl = new Curl();
$curl->setHeader($this->auth[0], $this->auth[1]);
return $curl;
}

public function buildUrl(string $resource): string
{
return $this->url.$resource;
}

protected function getUrl(): string
{
return $this->url;
}

private function getAuth(): array
{
return $this->auth;
}
}

+ 51
- 0
common/components/DolibarrApi.php View File

<?php

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 createInvoice(int $idUser)
{
return $this->post(self::RESOURCE_INVOICES, [
'socid' => $idUser
]);
}

public function addInvoiceLine(int $idInvoice, int $idProduct)
{
$productArray = $this->getProduct($idProduct);

return $this->post(self::RESOURCE_INVOICES . '/' . $idInvoice . '/lines', [
'fk_product' => $idProduct,
'subprice' => $productArray['price'],
'qty' => 1,
'desc' => $productArray['description']
]);
}

public function validateInvoice(int $idInvoice)
{
return $this->post(self::RESOURCE_INVOICES . '/' . $idInvoice . '/validate', [], false);
}

public function generateInvoicePdf(string $reference)
{
return $this->put(self::RESOURCE_DOCUMENTS . '/builddoc', [
'modulepart' => 'invoice',
'doctemplate' => 'crabe',
'langcode' => 'fr_FR',
'original_file' => $reference.'/'.$reference.'.pdf'
]);
}

public function getProduct(int $idProduct)
{
return $this->get(self::RESOURCE_PRODUCTS . '/' . $idProduct);
}
}

+ 7
- 0
common/config/main.php View File

*/ */


use common\components\BusinessLogic; use common\components\BusinessLogic;
use common\components\DolibarrApi;
use common\logic\Distribution\Distribution\Model\Distribution; use common\logic\Distribution\Distribution\Model\Distribution;
use common\logic\Document\DeliveryNote\Model\DeliveryNote; use common\logic\Document\DeliveryNote\Model\DeliveryNote;
use common\logic\Ticket\Ticket\Model\Ticket; use common\logic\Ticket\Ticket\Model\Ticket;
'enableStrictParsing' => false, 'enableStrictParsing' => false,
'rules' => [], 'rules' => [],
], ],
'dolibarrApi' => function() {
return new DolibarrApi(
Yii::$app->parameterBag->get('dolibarrApiUrl'),
['DOLAPIKEY', \Yii::$app->parameterBag->get('dolibarrApiKey')]
);
},
'logic' => function () { 'logic' => function () {
return new BusinessLogic(); return new BusinessLogic();
}, },

+ 8
- 0
common/logic/AbstractUtils.php View File

<?php

namespace common\logic;

abstract class AbstractUtils extends AbstractService implements UtilsInterface
{

}

+ 5
- 1
common/logic/Producer/Producer/Model/Producer.php View File

'option_online_payment_minimum_amount', 'option_online_payment_minimum_amount',
'option_document_price_decimals', 'option_document_price_decimals',
'option_billing_reduction_percentage', 'option_billing_reduction_percentage',
'dolibarr_socid',
'dolibarr_product_id'
], ],
'integer' 'integer'
], ],
'option_testimony' => 'Témoignage', 'option_testimony' => 'Témoignage',
'option_time_saved' => 'Temps gagné / semaine', 'option_time_saved' => 'Temps gagné / semaine',
'contact_email' => 'Email de contact', 'contact_email' => 'Email de contact',
'admin_comment' => 'Commentaire'
'admin_comment' => 'Commentaire',
'dolibarr_socid' => 'Dolibarr : id user',
'dolibarr_product_id' => 'Dolibarr : id produit'
]; ];
} }



+ 6
- 0
common/logic/Producer/Producer/Repository/ProducerRepository.php View File

]; ];
} }


public function getTurnoverLastMonth(Producer $producer, bool $format = false)
{
$period = date('Y-m', strtotime('-1 month'));
return $this->getTurnover($producer, $period, $format);
}

/** /**
* Retourne le CA du producteur pour un mois donné * Retourne le CA du producteur pour un mois donné
*/ */

+ 52
- 0
common/logic/Producer/Producer/Service/DolibarrProducerUtils.php View File

<?php

namespace common\logic\Producer\Producer\Service;

use common\components\DolibarrApi;
use common\logic\AbstractUtils;
use common\logic\Producer\Producer\Model\Producer;
use common\logic\Producer\Producer\Repository\ProducerRepository;
use common\logic\Producer\ProducerPriceRange\Repository\ProducerPriceRangeRepository;
use yii\base\ErrorException;

class DolibarrProducerUtils extends AbstractUtils
{
protected DolibarrApi $dolibarrApi;
protected ProducerPriceRangeRepository $producerPriceRangeRepository;
protected ProducerRepository $producerRepository;

public function loadDependencies(): void
{
$this->dolibarrApi = \Yii::$app->dolibarrApi;
$this->producerPriceRangeRepository = $this->loadService(ProducerPriceRangeRepository::class);
$this->producerRepository = $this->loadService(ProducerRepository::class);
}

public function generateDolibarrProducerInvoice(Producer $producer)
{
$idProduct = $this->getDolibarrProductId($producer);
if(!$idProduct) {
throw new ErrorException("Dolibarr : pas d'id product trouvé pour le producteur ".$producer->name);
}

$idInvoice = $this->dolibarrApi->createInvoice($producer->dolibarr_socid);
$this->dolibarrApi->addInvoiceLine($idInvoice, $idProduct);
$responseValidate = $this->dolibarrApi->validateInvoice($idInvoice);
$this->dolibarrApi->generateInvoicePdf($responseValidate['ref']);
}

private function getDolibarrProductId(Producer $producer)
{
if($producer->dolibarr_product_id) {
$idProduct = $producer->dolibarr_product_id;
}
else {
$producerPriceRange = $this->producerPriceRangeRepository->getProducerPriceRangeByTurnover($this->producerRepository->getTurnoverLastMonth($producer));
if($producerPriceRange && $producerPriceRange->dolibarr_product_id) {
$idProduct = $producerPriceRange->dolibarr_product_id;
}
}

return $idProduct;
}
}

+ 3
- 1
common/logic/Producer/Producer/Wrapper/ProducerContainer.php View File



use common\logic\AbstractContainer; use common\logic\AbstractContainer;
use common\logic\Producer\Producer\Repository\ProducerRepository; use common\logic\Producer\Producer\Repository\ProducerRepository;
use common\logic\Producer\Producer\Service\DolibarrProducerUtils;
use common\logic\Producer\Producer\Service\ProducerBuilder; use common\logic\Producer\Producer\Service\ProducerBuilder;
use common\logic\Producer\Producer\Service\ProducerDefinition; use common\logic\Producer\Producer\Service\ProducerDefinition;
use common\logic\Producer\Producer\Service\ProducerSolver; use common\logic\Producer\Producer\Service\ProducerSolver;
ProducerSolver::class, ProducerSolver::class,
ProducerRepository::class, ProducerRepository::class,
ProducerBuilder::class, ProducerBuilder::class,
ProducerUtils::class
ProducerUtils::class,
DolibarrProducerUtils::class,
]; ];
} }



+ 2
- 0
common/logic/Producer/Producer/Wrapper/ProducerManager.php View File



use common\logic\AbstractManager; use common\logic\AbstractManager;
use common\logic\Producer\Producer\Repository\ProducerRepository; use common\logic\Producer\Producer\Repository\ProducerRepository;
use common\logic\Producer\Producer\Service\DolibarrProducerUtils;
use common\logic\Producer\Producer\Service\ProducerBuilder; use common\logic\Producer\Producer\Service\ProducerBuilder;
use common\logic\Producer\Producer\Service\ProducerDefinition; use common\logic\Producer\Producer\Service\ProducerDefinition;
use common\logic\Producer\Producer\Service\ProducerSolver; use common\logic\Producer\Producer\Service\ProducerSolver;
* @mixin ProducerRepository * @mixin ProducerRepository
* @mixin ProducerBuilder * @mixin ProducerBuilder
* @mixin ProducerUtils * @mixin ProducerUtils
* @mixin DolibarrProducerUtils
*/ */
class ProducerManager extends AbstractManager class ProducerManager extends AbstractManager
{ {

+ 2
- 0
common/logic/Producer/ProducerPriceRange/Model/ProducerPriceRange.php View File

{ {
return [ return [
[['range_begin', 'range_end', 'price'], 'double'], [['range_begin', 'range_end', 'price'], 'double'],
[['dolibarr_product_id'], 'integer']
]; ];
} }


'range_begin' => 'Début', 'range_begin' => 'Début',
'range_end' => 'Fin', 'range_end' => 'Fin',
'price' => 'Tarif (HT)', 'price' => 'Tarif (HT)',
'dolibarr_product_id' => 'Dolibarr : id produit'
]; ];
} }
} }

+ 12
- 0
common/logic/Producer/ProducerPriceRange/Repository/ProducerPriceRangeRepository.php View File

return $this->queryProducerPriceRanges()->find(); return $this->queryProducerPriceRanges()->find();
} }


public function getProducerPriceRangeByTurnover(float $turnover = null): ?ProducerPriceRange
{
$producerPriceRangeArray = $this->findProducerPriceRanges();
foreach ($producerPriceRangeArray as $producerPriceRange) {
if ($turnover >= $producerPriceRange->range_begin && $turnover < $producerPriceRange->range_end) {
return $producerPriceRange;
}
}

return null;
}

public function getAmountToBeBilledByTurnover(float $turnover = null, $format = false) public function getAmountToBeBilledByTurnover(float $turnover = null, $format = false)
{ {
$amountToBeBilled = 0; $amountToBeBilled = 0;

+ 1
- 1
common/logic/UtilsInterface.php View File

interface UtilsInterface interface UtilsInterface
{ {


}
}

+ 2
- 1
composer.json View File

"justcoded/yii2-event-listener": "*", "justcoded/yii2-event-listener": "*",
"ext-pdo": "*", "ext-pdo": "*",
"weluse/yii2-mailjet": "^0.2.0", "weluse/yii2-mailjet": "^0.2.0",
"ext-json": "*"
"ext-json": "*",
"ext-curl": "*"
}, },
"require-dev": { "require-dev": {
"yiisoft/yii2-codeception": "*", "yiisoft/yii2-codeception": "*",

+ 30
- 0
console/migrations/m230905_091112_add_columns_dolibarr.php View File

<?php

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

/**
* Class m230905_091112_add_columns_dolibarr
*/
class m230905_091112_add_columns_dolibarr extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->addColumn('producer', 'dolibarr_socid', Schema::TYPE_INTEGER);
$this->addColumn('producer', 'dolibarr_product_id', Schema::TYPE_INTEGER);
$this->addColumn('producer_price_range', 'dolibarr_product_id', Schema::TYPE_INTEGER);
}

/**
* {@inheritdoc}
*/
public function safeDown()
{
$this->dropColumn('producer', 'dolibarr_socid');
$this->dropColumn('producer', 'dolibarr_product_id');
$this->dropColumn('producer_price_range', 'dolibarr_product_id');
}
}

Loading…
Cancel
Save