feat generate recap devis

This commit is contained in:
Tolotsoa 2025-09-02 20:49:17 +03:00
parent 90d9b5a52c
commit a278022748
16 changed files with 5938 additions and 4247 deletions

View File

@ -1,4 +1,5 @@
<?php
return [
'routes' => [
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
@ -72,6 +73,7 @@ return [
['name' => 'page#exportDevisToFacture', 'url' => '/exportDevisToFacture', 'verb' => 'POST'],
['name' => 'page#exportFactureToPdf', 'url' => '/facture/exportFactureToPdf', 'verb' => 'POST'],
['name' => 'page#exportFactureByClientAndMonthYearToPdf', 'url' => '/facture/exportFactureByClientAndMonthYearToPdf', 'verb' => 'POST'],
['name' => 'page#exportDevisRecap', 'url' => '/devis/exportDevisRecap', 'verb' => 'POST'],
['name' => 'page#getProduits', 'url' => '/getProduits', 'verb' => 'PROPFIND'],
['name' => 'page#getProduitsById', 'url' => '/getProduitsById', 'verb' => 'POST'],
@ -104,7 +106,7 @@ return [
['name' => 'page#getProduits', 'url' => '/getProduits', 'verb' => 'PROPFIND'],
['name' => 'page#getProduitsById', 'url' => '/getProduitsById', 'verb' => 'POST'],
['name' => 'page#insertProduit', 'url' => '/produit/insert', 'verb' => 'POST'],
// bibliotheque
['name' => 'page#getBibliotheques', 'url' => '/getBibliotheques', 'verb' => 'PROPFIND'],
['name' => 'page#insertBibliotheque', 'url' => '/bibliotheque/insert', 'verb' => 'POST'],
@ -121,7 +123,7 @@ return [
['name' => 'page#savePDF', 'url' => '/savePDF', 'verb' => 'POST'],
['name' => 'page#saveDocumentRecap', 'url' => '/saveDocumentRecap', 'verb' => 'POST'],
['name' => 'page#saveNewPDF', 'url' => '/saveNewPDF', 'verb' => 'POST'],
['name' => 'admin#backup', 'url' => '/backup', 'verb' => 'GET'],

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,173 @@
<?php
namespace OCA\Gestion\Service\Devis;
use OCA\Gestion\Db\Bdd;
use OCA\Gestion\Constants\MultipleFactureTypeConstant;
/**
* Classe responsable du traitement et de la préparation des données de devis
*/
class DevisDataProcessor
{
/** @var Bdd */
private $gestionBdd;
public function __construct(Bdd $gestionBdd)
{
$this->gestionBdd = $gestionBdd;
}
public function prepareDevisData($devisData, $currentConfig, $filter, $idNextCloud)
{
$data_devis = [];
foreach ($devisData as $devis) {
$devis_temp = $this->createDevisStructure($devis, $currentConfig);
$devis_temp = $this->calculateDevisAmounts($devis_temp, $devis, $filter, $idNextCloud);
$data_devis[] = $devis_temp;
}
return $data_devis;
}
public function createDevisStructure($devis, $currentConfig)
{
return array(
'devis_id' => $devis['devis_id'],
'devis_full_number' => $devis['devis_full_number'],
'calendar_uuid' => $devis['calendar_uuid'],
'num_commande' => '',
'devis_date' => $devis['devis_date'],
'lieu_nom' => $devis['lieu_nom'],
'lieu_adresse' => $devis['lieu_adresse'],
'defunt_nom' => $devis['defunt_nom'],
'defunt_sexe' => $devis['defunt_sexe'],
'client_nom' => $devis['client_nom'],
'client_entreprise' => $devis['client_entreprise'],
'client_adresse' => $devis['client_adresse'],
'group_name' => $devis['group_name'],
'group_address' => $devis['group_address'],
'group_postal_code' => $devis['group_postal_code'],
'group_city' => $devis['group_city'],
'group_email' => $devis['group_email'],
'group_siret_number' => $devis['group_siret_number'],
'montant_htc' => 0,
'tva' => $currentConfig->tva_default,
'montant_tva' => 0,
'montant_ttc' => 0
);
}
public function calculateDevisAmounts($devis_temp, $devis, $filter, $idNextCloud)
{
$produits = json_decode($this->gestionBdd->getListProduit($devis['devis_id'], $idNextCloud));
foreach ($produits as $produit) {
$htPrice = $this->getProductPrice($produit, $devis, $filter);
$devis_temp['montant_htc'] += $htPrice * $produit->quantite;
}
$devis_temp['montant_tva'] = ($devis_temp['montant_htc'] * $devis_temp['tva']) / 100;
$devis_temp['montant_ttc'] = $devis_temp['montant_tva'] + $devis_temp['montant_htc'];
return $devis_temp;
}
public function getProductPrice($produit, $devis, $filter)
{
$htPrice = $produit->prix_unitaire;
if ($devis['group_name'] != null) {
$price = $this->gestionBdd->getProductPriceByClientGroupId($filter, $produit->id);
if ($price != null) {
$htPrice = $price;
}
}
return $htPrice;
}
public function getClientInfoForDevis($devis, $filterType)
{
if ($filterType === 'group' && !empty($devis['group_name'])) {
return [
'name' => 'Groupe ' . $devis['group_name'],
'address' => $devis['group_address'],
'city' => $devis['group_postal_code'] . ' ' . $devis['group_city'],
'siret' => $devis['group_siret_number'],
'email' => $devis['group_email']
];
} else {
$clientAddresses = \OCA\Gestion\Helpers\FileExportHelpers::GetAddressAndCityFromAddress($devis['client_adresse']);
return [
'name' => $devis['client_nom'],
'address' => $clientAddresses['address'],
'city' => $clientAddresses['city'],
'siret' => '',
'email' => ''
];
}
}
public function generateCsvContent($devisData)
{
$headers = [
'Numéro Devis',
'Date',
'Client Nom',
'Client Entreprise',
'Défunt',
'Lieu',
'Thanatopracteur',
'Commentaire',
'UUID Calendrier'
];
$csvContent = $this->arrayToCsv($headers);
foreach ($devisData as $devis) {
$row = [
$devis['devis_full_number'] ?? '',
$devis['devis_date'] ?? '',
$devis['client_nom'] ?? '',
$devis['client_entreprise'] ?? '',
$devis['defunt_nom'] ?? '',
$devis['lieu_nom'] ?? '',
trim(($devis['thanato_nom'] ?? '') . ' ' . ($devis['thanato_prenom'] ?? '')),
$devis['devis_comment'] ?? '',
$devis['calendar_uuid'] ?? ''
];
$csvContent .= $this->arrayToCsv($row);
}
return $csvContent;
}
public function getClientNameForFolder($firstDevis, $filterType)
{
if ($filterType === MultipleFactureTypeConstant::GROUP_FILTER_TYPE && !empty($firstDevis["group_name"])) {
return $firstDevis["group_name"];
}
return $firstDevis["client_nom"] ?? 'CLIENT_INCONNU';
}
public function getRecapFilename($month, $year)
{
$monthStr = str_pad($month, 2, '0', STR_PAD_LEFT);
return $year . $monthStr;
}
private function arrayToCsv($array)
{
$output = fopen('php://temp', 'r+');
fputcsv($output, $array, ';');
rewind($output);
$csvLine = fgets($output);
fclose($output);
return $csvLine;
}
}

View File

@ -0,0 +1,144 @@
<?php
namespace OCA\Gestion\Service\Devis;
use OCP\Files\IRootFolder;
use OCP\Files\NotPermittedException;
class DevisPdfGenerator
{
/** @var IRootFolder */
private $rootFolder;
/** @var DevisPdfLayoutManager */
private $layoutManager;
/** @var DevisPdfTableRenderer */
private $tableRenderer;
public const DEFAULT_NEXTCLOUD_ADMIN = 'admin';
public function __construct(IRootFolder $rootFolder)
{
$this->rootFolder = $rootFolder;
$this->layoutManager = new DevisPdfLayoutManager($rootFolder);
$this->tableRenderer = new DevisPdfTableRenderer();
}
public function generatePdfDocument($storage, $currentConfig, $data_devis, $clientInfo, $month, $year, $montant)
{
$configurationAddresses = \OCA\Gestion\Helpers\FileExportHelpers::GetAddressAndCityFromAddress($currentConfig->adresse);
$configurationAddress = $configurationAddresses["address"];
$configurationAddressCity = $configurationAddresses["city"];
$pdf = $this->createPdfInstance();
$folderPath = $this->createDestinationFolder($storage, $currentConfig, $data_devis[0], $month, $year);
// Créer le contexte une seule fois
$context = new PageContext($pdf, $currentConfig, $configurationAddress, $configurationAddressCity, $data_devis, $clientInfo, $year, $montant);
$this->generatePdfPages($context);
return $this->savePdfFile($storage, $pdf, $folderPath, $clientInfo, $month, $year);
}
public function createPdfInstance()
{
$pdf = new \FPDF();
$pdf->AddFont('Arial', '', 'Comic Sans MS.php');
$pdf->AddFont('Arial', 'B', 'comic-sans-bold.php');
return $pdf;
}
public function createDestinationFolder($storage, $currentConfig, $firstDevis, $month, $year)
{
$date_temp = date("t-m-Y", strtotime($firstDevis['devis_date']));
$formatter = new \IntlDateFormatter('fr_FR', \IntlDateFormatter::LONG, \IntlDateFormatter::NONE);
$date_formated = $formatter->format(\DateTime::createFromFormat('d-m-Y', $date_temp));
$folderPath = html_entity_decode($currentConfig->path) . '/DOCUMENTS RECAPITULATIFS/' . $year . '/' .
str_pad($month, 2, '0', STR_PAD_LEFT) . ' ' .
strtoupper(\OCA\Gestion\Helpers\FileExportHelpers::ConvertSpecialChar(explode(' ', $date_formated)[1])) . '/';
try {
$storage->newFolder($folderPath);
} catch(NotPermittedException $e) {
// Le dossier existe déjà
}
return $folderPath;
}
private function generatePdfPages(PageContext $context)
{
$pagination = $this->calculatePagination(count($context->dataDevis));
$totals = ['ht' => 0, 'tva' => 0, 'ttc' => 0];
for ($num_page = 1; $num_page <= $pagination['nb_pages']; $num_page++) {
$this->generateSinglePage($context, $num_page, $pagination, $totals);
}
}
private function calculatePagination($totalItems)
{
return [
'nb_pages' => ceil($totalItems / 26),
'items_per_page' => 26,
'current_index' => 0
];
}
// VOICI LA FONCTION CORRIGÉE - 4 paramètres au lieu de 10
private function generateSinglePage(PageContext $context, $num_page, &$pagination, &$totals)
{
$context->pdf->AddPage();
$context->pdf->SetAutoPagebreak(false);
$context->pdf->SetMargins(0, 0, 10);
$this->layoutManager->addPageHeader($context->pdf, $context->currentConfig, $context->configurationAddress, $context->configurationAddressCity);
$this->layoutManager->addPageNumber($context->pdf, $num_page, $pagination['nb_pages']);
$this->layoutManager->addClientInfoSection($context->pdf, $context->clientInfo, $context->dataDevis[0]);
$this->layoutManager->addDocumentTitle($context->pdf, $context->dataDevis[0], $context->year);
$itemsToProcess = min($pagination['items_per_page'], count($context->dataDevis) - $pagination['current_index']);
$config = [
'pagination' => $pagination,
'itemsToProcess' => $itemsToProcess,
'numPage' => $num_page,
'nbPage' => $pagination['nb_pages'],
'totals' => &$totals,
'sansMontant' => $context->montant
];
$this->tableRenderer->createDevisTable(
$context->pdf,
$context->dataDevis,
$config
);
$this->layoutManager->addLegalFooter($context->pdf, $context->currentConfig);
$pagination['current_index'] += $itemsToProcess;
}
private function savePdfFile($storage, $pdf, $folderPath, $clientInfo, $month, $year)
{
$filename = $this->generateDevisRecapFilename($clientInfo, $month, $year);
$fullPdfPath = $folderPath . $filename . '.pdf';
$storage->newFile($fullPdfPath);
$pdfContent = $pdf->Output('', 'S');
$file_pdf = $storage->get($fullPdfPath);
$file_pdf->putContent($pdfContent);
return $fullPdfPath;
}
private function generateDevisRecapFilename($clientInfo, $month, $year)
{
$monthName = strtoupper(\OCA\Gestion\Helpers\FileExportHelpers::ConvertSpecialChar(date('F', mktime(0, 0, 0, $month, 10))));
$clientName = strtoupper(\OCA\Gestion\Helpers\FileExportHelpers::ConvertSpecialChar($clientInfo['name']));
return $clientName . '_RECAP_DEVIS_' . $monthName . '_' . $year;
}
}

View File

@ -0,0 +1,155 @@
<?php
namespace OCA\Gestion\Service\Devis;
use OCP\Files\IRootFolder;
class DevisPdfLayoutManager
{
/** @var IRootFolder */
private $rootFolder;
private $defaultImagePath = "/var/www/html/data/admin/files/.gestion/";
public const DEFAULT_NEXTCLOUD_ADMIN = 'admin';
public function __construct(IRootFolder $rootFolder)
{
$this->rootFolder = $rootFolder;
}
public function addPageHeader($pdf, $config, $address, $city)
{
if ($this->doesLogoExist()) {
$pdf->Image($this->defaultImagePath."logo.png", 10, 10, 75, 25);
}
$this->addCompanyInfo($pdf, $config, $address, $city);
}
public function addPageNumber($pdf, $currentPage, $totalPages)
{
if ($totalPages > 1) {
$pdf->SetXY(120, 5);
$pdf->SetFont("Arial", "B", 9);
$pdf->Cell(160, 8, $currentPage . '/' . $totalPages, 0, 0, 'C');
}
}
public function addClientInfoSection($pdf, $clientInfo, $firstDevis)
{
$date_temp = date("t-m-Y", strtotime($firstDevis['devis_date']));
$formatter = new \IntlDateFormatter('fr_FR', \IntlDateFormatter::LONG, \IntlDateFormatter::NONE);
$date_formated = $formatter->format(\DateTime::createFromFormat('d-m-Y', $date_temp));
$this->addClientInfo($pdf, $clientInfo, $date_formated);
}
public function addDocumentTitle($pdf, $firstDevis, $year)
{
$date_temp = date("t-m-Y", strtotime($firstDevis['devis_date']));
$formatter = new \IntlDateFormatter('fr_FR', \IntlDateFormatter::LONG, \IntlDateFormatter::NONE);
$date_formated = $formatter->format(\DateTime::createFromFormat('d-m-Y', $date_temp));
$pdf->SetFont("Arial", "B", 14);
$pdf->SetXY(10, 90);
$pdf->Cell(
0,
10,
"RECAPITULATIF DEVIS " .
strtoupper(\OCA\Gestion\Helpers\FileExportHelpers::ConvertSpecialChar(explode(' ', $date_formated)[1])) . " " . $year,
0,
0,
'C'
);
}
public function addLegalFooter($pdf, $config)
{
$y0 = 260;
$pageWidth = $pdf->GetPageWidth();
$pdf->SetFont('Arial', '', 6);
$pdf->SetXY(1, $y0 + 4);
$pdf->Cell($pageWidth, 5, mb_convert_encoding(html_entity_decode($config->legal_one), 'ISO-8859-1', 'UTF-8'), 0, 0, 'C');
$pdf->SetXY(1, $y0 + 8);
$pdf->Cell($pageWidth, 5, mb_convert_encoding(html_entity_decode($config->legal_two), 'ISO-8859-1', 'UTF-8'), 0, 0, 'C');
$pdf->SetXY(1, $y0 + 12);
$pdf->Cell($pageWidth, 5, mb_convert_encoding(html_entity_decode($config->telephone), 'ISO-8859-1', 'UTF-8'), 0, 0, 'C');
}
private function addCompanyInfo($pdf, $config, $address, $city)
{
$companyInfoXAxis = 10;
$companyInfoYAxis = 40;
$pdf->SetFont('Arial', '', 11);
$pdf->SetXY($companyInfoXAxis, $companyInfoYAxis);
$pdf->Cell(0, 7, \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport($config->entreprise));
$companyInfoYAxis += 7;
$pdf->SetXY($companyInfoXAxis, $companyInfoYAxis);
$pdf->Cell(0, 7, \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport($address));
$companyInfoYAxis += 7;
$pdf->SetXY($companyInfoXAxis, $companyInfoYAxis);
$pdf->Cell(0, 7, \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport($city));
$companyInfoYAxis += 7;
$pdf->SetXY($companyInfoXAxis, $companyInfoYAxis);
$pdf->Cell(0, 7, mb_convert_encoding(html_entity_decode('Tél : '), 'ISO-8859-1', 'UTF-8') . \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport($config->telephone));
$companyInfoYAxis += 7;
$pdf->SetXY($companyInfoXAxis, $companyInfoYAxis);
$pdf->Cell(0, 7, 'Mail : ' . $config->mail);
}
private function addClientInfo($pdf, $clientInfo, $dateFormatted)
{
$clientInfoXAxis = 125;
$clientInfoYAxis = 40;
$pdf->SetFont('Arial', '', 11);
$pdf->SetXY($clientInfoXAxis, $clientInfoYAxis);
$pdf->Cell(0, 7, mb_convert_encoding($clientInfo['name'], 'ISO-8859-1', 'UTF-8'));
$clientInfoYAxis += 7;
$pdf->SetXY($clientInfoXAxis, $clientInfoYAxis);
$pdf->SetMargins(0, 0, 10);
$pdf->MultiCell(0, 7, trim(\OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport($clientInfo['address'])));
$pdf->SetMargins(0, 0, 0);
$clientInfoYAxis += 14;
$pdf->SetXY($clientInfoXAxis, $clientInfoYAxis);
$pdf->Cell(0, 7, trim(mb_convert_encoding(html_entity_decode($clientInfo['city']), 'ISO-8859-1', 'UTF-8')));
if (!empty($clientInfo['siret'])) {
$clientInfoYAxis += 7;
$pdf->SetXY($clientInfoXAxis, $clientInfoYAxis);
$pdf->Cell(0, 7, 'Siret: ' . \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport($clientInfo['siret']));
}
if (!empty($clientInfo['email'])) {
$clientInfoYAxis += 7;
$pdf->SetXY($clientInfoXAxis, $clientInfoYAxis);
$pdf->Cell(0, 7, 'Email: ' . mb_convert_encoding(html_entity_decode($clientInfo['email']), 'ISO-8859-1', 'UTF-8'));
}
$clientInfoYAxis += 7;
$pdf->SetXY($clientInfoXAxis, $clientInfoYAxis);
$pdf->Cell(0, 7, "le " . mb_convert_encoding($dateFormatted, 'ISO-8859-1', 'UTF-8'));
}
private function doesLogoExist()
{
$storage = $this->rootFolder->getUserFolder(self::DEFAULT_NEXTCLOUD_ADMIN);
try {
if (isset($storage)) {
$storage->get('/.gestion/logo.png');
return true;
}
return false;
} catch (\OCP\Files\NotFoundException $e) {
return false;
}
}
}

View File

@ -0,0 +1,176 @@
<?php
namespace OCA\Gestion\Service\Devis;
class DevisPdfTableRenderer
{
public function createDevisTable($pdf, $dataDevis, $config)
{
// Extraction des paramètres du tableau de configuration
$pagination = $config['pagination'];
$itemsToProcess = $config['itemsToProcess'];
$numPage = $config['numPage'];
$nbPage = $config['nbPage'];
$totals = &$config['totals']; // Référence pour modification
$sansMontant = $config['sansMontant'] ?? false; // false par défaut = afficher les montants
$this->drawTableStructure($pdf, $numPage, $nbPage, $sansMontant);
$this->addTableHeaders($pdf, $sansMontant);
$this->populateTableData($pdf, $dataDevis, $pagination['current_index'], $itemsToProcess, $totals, $sansMontant);
// Afficher les totaux seulement sur la dernière page ET si les montants sont affichés
if ($numPage == $nbPage && !$sansMontant) {
$this->addTableTotals($pdf, $totals);
}
}
private function drawTableStructure($pdf, $numPage, $nbPage, $sansMontant)
{
$pdf->SetLineWidth(0.2);
$pdf->Rect(5, 105, 200, 130, "D");
$pdf->Line(5, 115, 205, 115);
// Si c'est la dernière page ET qu'on affiche les montants, ligne de total à 225
$endY = ($numPage == $nbPage && !$sansMontant) ? 225 : 235;
if (!$sansMontant) {
// AVEC montants : toutes les colonnes
$verticalLines = [25, 45, 70, 90, 125, 155, 175, 190];
} else {
// SANS montants : seulement jusqu'à Défunt
$verticalLines = [35, 70, 105, 125, 155];
}
foreach ($verticalLines as $x) {
$pdf->Line($x, 105, $x, $endY);
}
}
private function addTableHeaders($pdf, $sansMontant)
{
$pdf->SetFont('Arial', 'B', 8);
if (!$sansMontant) {
// AVEC montants
$headers = [
[5, 20, mb_convert_encoding(html_entity_decode("N° Devis"), 'ISO-8859-1', 'UTF-8')],
[25, 20, mb_convert_encoding(html_entity_decode("N° Dossier"), 'ISO-8859-1', 'UTF-8')],
[45, 25, mb_convert_encoding(html_entity_decode("N° Commande"), 'ISO-8859-1', 'UTF-8')],
[70, 20, "Date"],
[90, 35, "Lieu du soin"],
[125, 30, \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport("Défunt")],
[155, 20, "H.T."],
[175, 15, "TVA 20%"],
[190, 15, "T.T.C"]
];
} else {
// SANS montants - colonnes plus larges
$headers = [
[5, 30, mb_convert_encoding(html_entity_decode("N° Devis"), 'ISO-8859-1', 'UTF-8')],
[35, 35, mb_convert_encoding(html_entity_decode("N° Dossier"), 'ISO-8859-1', 'UTF-8')],
[70, 35, mb_convert_encoding(html_entity_decode("N° Commande"), 'ISO-8859-1', 'UTF-8')],
[105, 20, "Date"],
[125, 30, "Lieu du soin"],
[155, 50, \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport("Défunt")]
];
}
foreach ($headers as $header) {
$pdf->SetXY($header[0], 106);
$pdf->Cell($header[1], 8, $header[2], 0, 0, 'C');
}
}
private function populateTableData($pdf, $dataDevis, $startIndex, $itemsToProcess, &$totals, $sansMontant)
{
$formatterDate = new \IntlDateFormatter('fr_FR', \IntlDateFormatter::SHORT, \IntlDateFormatter::NONE);
$formatterDate->setPattern('dd-MMM');
$yDevis = 115;
for ($i = $startIndex; $i < $startIndex + $itemsToProcess && $i < count($dataDevis); $i++) {
$devis = $dataDevis[$i];
$dateSoin = new \DateTime($devis['devis_date']);
$this->addTableRow($pdf, $devis, $formatterDate, $dateSoin, $yDevis, $sansMontant);
// Calculer les totaux seulement si on affiche les montants
if (!$sansMontant) {
$totals['ht'] += $devis['montant_htc'];
$totals['tva'] += $devis['montant_tva'];
$totals['ttc'] += $devis['montant_ttc'];
}
$yDevis += 5;
}
}
private function addTableRow($pdf, $devis, $formatterDate, $dateSoin, $yDevis, $sansMontant)
{
$pdf->SetFont('Arial', '', 7);
if (!$sansMontant) {
// AVEC montants
$rowData = [
[6, 18, $devis['devis_full_number']],
[26, 18, $devis['calendar_uuid']],
[46, 23, $devis['num_commande']],
[71, 18, mb_convert_encoding($formatterDate->format($dateSoin), 'ISO-8859-1', 'UTF-8')],
[91, 33, \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport($devis['lieu_nom'])],
[126, 28, \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport($devis['defunt_nom'])],
[156, 18, number_format($devis['montant_htc'], 2, '.', '') . chr(128), 'C'],
[176, 13, number_format($devis['montant_tva'], 2, '.', '') . chr(128), 'C'],
[191, 13, number_format($devis['montant_ttc'], 2, '.', '') . chr(128), 'C']
];
} else {
// SANS montants - colonnes plus larges
$rowData = [
[6, 28, $devis['devis_full_number']],
[36, 33, $devis['calendar_uuid']],
[71, 33, $devis['num_commande']],
[106, 18, mb_convert_encoding($formatterDate->format($dateSoin), 'ISO-8859-1', 'UTF-8')],
[126, 28, \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport($devis['lieu_nom'])],
[156, 48, \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport($devis['defunt_nom'])]
];
}
foreach ($rowData as $data) {
$pdf->SetXY($data[0], $yDevis);
$align = isset($data[3]) ? $data[3] : '';
$pdf->Cell($data[1], 5, $data[2], 0, 0, $align);
}
}
private function addTableTotals($pdf, $totals)
{
// Cette méthode n'est appelée que si on affiche les montants (!$sansMontant)
$pdf->Line(5, 225, 205, 225);
$pdf->SetFont('Arial', 'B', 8);
$pdf->SetXY(5, 225);
$pdf->Cell(150, 8, 'TOTAL', 0, 0, 'C');
$pdf->SetXY(156, 225);
$pdf->Cell(18, 8, number_format($totals['ht'], 2, '.', '') . chr(128), 0, 0, 'C');
$pdf->SetXY(176, 225);
$pdf->Cell(13, 8, number_format($totals['tva'], 2, '.', '') . chr(128), 0, 0, 'C');
$pdf->SetXY(191, 225);
$pdf->Cell(13, 8, number_format($totals['ttc'], 2, '.', '') . chr(128), 0, 0, 'C');
$pdf->SetXY(170, 240);
$pdf->Cell(20, 8, 'TOTAL TTC', 0, 0, 'C');
$pdf->SetXY(190, 240);
$pdf->Cell(15, 8, number_format($totals['ttc'], 2, '.', '') . chr(128), 0, 0, 'C');
$lines = [
[170, 240, 170, 248],
[190, 240, 190, 248],
[205, 240, 205, 248],
[170, 240, 205, 240],
[170, 248, 205, 248]
];
foreach ($lines as $line) {
$pdf->Line($line[0], $line[1], $line[2], $line[3]);
}
}
}

View File

@ -0,0 +1,89 @@
<?php
namespace OCA\Gestion\Service\Devis;
use OCA\Gestion\Db\Bdd;
use OCP\Files\IRootFolder;
use OCA\Gestion\Constants\MultipleFactureTypeConstant;
/**
* Service principal pour la génération des récapitulatifs de devis
*/
class DevisRecapService
{
/** @var Bdd */
private $gestionBdd;
/** @var IRootFolder */
private $rootFolder;
/** @var DevisDataProcessor */
private $dataProcessor;
/** @var DevisPdfGenerator */
private $pdfGenerator;
public const DEFAULT_NEXTCLOUD_ADMIN = 'admin';
public function __construct(
Bdd $gestionBdd,
IRootFolder $rootFolder,
) {
$this->gestionBdd = $gestionBdd;
$this->rootFolder = $rootFolder;
$this->dataProcessor = new DevisDataProcessor($gestionBdd);
$this->pdfGenerator = new DevisPdfGenerator($rootFolder);
}
public function generateDevisRecap($filter, $month, $year, $idNextCloud, $filterType, $montant)
{
$storage = $this->rootFolder->getUserFolder($idNextCloud);
$configs = json_decode($this->gestionBdd->getConfiguration(self::DEFAULT_NEXTCLOUD_ADMIN));
$currentConfig = $configs[0];
$devisData = $this->getDevisData($filter, $month, $year, $currentConfig, $filterType);
if (empty($devisData)) {
return null;
}
$processedData = $this->dataProcessor->prepareDevisData($devisData, $currentConfig, $filter, $idNextCloud);
$clientInfo = $this->dataProcessor->getClientInfoForDevis($processedData[0], $filterType);
return $this->pdfGenerator->generatePdfDocument(
$storage,
$currentConfig,
$processedData,
$clientInfo,
$month,
$year,
$montant
);
}
private function getDevisData($filter, $month, $year, $currentConfig, $filterType)
{
$isFilterByClient = $filterType === MultipleFactureTypeConstant::CLIENT_FILTER_TYPE;
if ($isFilterByClient) {
return $this->gestionBdd->getDevisPdfDataByClientAndMonthYear($filter, $month, $year, $currentConfig);
} else {
return $this->gestionBdd->getDevisPdfDataByClientGroupFacturationAndMonthYear($filter, $month, $year, $currentConfig);
}
}
// Méthodes utilitaires conservées pour la compatibilité CSV
public function generateCsvContent($devisData)
{
return $this->dataProcessor->generateCsvContent($devisData);
}
public function getClientNameForFolder($firstDevis, $filterType)
{
return $this->dataProcessor->getClientNameForFolder($firstDevis, $filterType);
}
public function getRecapFilename($month, $year)
{
return $this->dataProcessor->getRecapFilename($month, $year);
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace OCA\Gestion\Service\Devis;
use OCP\Files\IRootFolder;
/**
* Objet simple pour regrouper les paramètres de contexte
*/
class PageContext
{
public $pdf;
public $currentConfig;
public $configurationAddress;
public $configurationAddressCity;
public $dataDevis;
public $clientInfo;
public $year;
public $montant;
public function __construct($pdf, $currentConfig, $configurationAddress, $configurationAddressCity, $dataDevis, $clientInfo, $year, $montant)
{
$this->pdf = $pdf;
$this->currentConfig = $currentConfig;
$this->configurationAddress = $configurationAddress;
$this->configurationAddressCity = $configurationAddressCity;
$this->dataDevis = $dataDevis;
$this->clientInfo = $clientInfo;
$this->year = $year;
$this->montant = $montant;
}
}

View File

@ -41,77 +41,79 @@ use OCA\Gestion\Service\InvoiceRecap\InvoiceRecapService;
use OCP\DB\Exception;
use OCP\Files\IRootFolder;
class InvoicePdfService {
/** @var Bdd */
private $gestionBdd;
class InvoicePdfService
{
/** @var Bdd */
private $gestionBdd;
/** @var IRootFolder */
private $rootFolder;
private $rootFolder;
/** @var InvoiceRecapService */
private $invoiceRecapService;
private $invoiceRecapService;
private const DEFAULT_NEXTCLOUD_ADMIN = "admin";
public function __construct(
Bdd $gestionBdd,
public function __construct(
Bdd $gestionBdd,
IRootFolder $rootFolder,
InvoiceRecapService $invoiceRecapService) {
InvoiceRecapService $invoiceRecapService
) {
$this->gestionBdd = $gestionBdd;
$this->rootFolder = $rootFolder;
$this->invoiceRecapService = $invoiceRecapService;
}
}
private function getLogo(){
private function getLogo()
{
$storage = $this->rootFolder->getUserFolder(self::DEFAULT_NEXTCLOUD_ADMIN);
try{
try {
if(isset($storage)){
$file = $storage->get('/.gestion/logo.png');
}else{
return "nothing";
}
} catch(\OCP\Files\NotFoundException $e) {
$file = $storage->get('/.gestion/logo.jpeg');
}
}
catch(\OCP\Files\NotFoundException $e) {
return "nothing";
}
try {
try {
if(isset($storage)) {
$file = $storage->get('/.gestion/logo.png');
} else {
return "nothing";
}
} catch(\OCP\Files\NotFoundException $e) {
$file = $storage->get('/.gestion/logo.jpeg');
}
} catch(\OCP\Files\NotFoundException $e) {
return "nothing";
}
return base64_encode($file->getContent());
}
return base64_encode($file->getContent());
}
private function generateFactureSinglePdfByFactureId($factureId,$idNextCloud){
private function generateFactureSinglePdfByFactureId($factureId, $idNextCloud)
{
$storage = $this->rootFolder->getUserFolder($idNextCloud);
$configs = json_decode($this->gestionBdd->getConfiguration(self::DEFAULT_NEXTCLOUD_ADMIN));
$currentConfig = $configs[0];
$logo = $this->getLogo();
$invoicePdfData = $this->gestionBdd->getInvoicePdfData($factureId,$currentConfig);
if($invoicePdfData == null){
return null;
}
$currentConfig = $configs[0];
$logo = $this->getLogo();
$invoicePdfData = $this->gestionBdd->getInvoicePdfData($factureId, $currentConfig);
if($invoicePdfData == null) {
return null;
}
$clean_folder = html_entity_decode(string: $currentConfig->path).'/';
$factureFolders = $this->getFacturesFolder($invoicePdfData,$clean_folder);
$factureFolders = $this->getFacturesFolder($invoicePdfData, $clean_folder);
$pdf = new InvoicePdfHandler();
// $pdf->AddFont('ComicSans','','Comic Sans MS.php');
// $pdf->AddFont('ComicSans','B','comic-sans-bold.php');
$pdf->InvoicePdfFactory($invoicePdfData,$logo);
$pdf->InvoicePdfFactory($invoicePdfData, $logo);
$pdf->SetFactureContent();
$pdfContent = $pdf->Output('','S');
$pdfContent = $pdf->Output('', 'S');
$pdfFilename = $pdf->GetInvoiceFilename();
$prefixPdf = "FACTURE";
if($invoicePdfData['is_negative']){
if($invoicePdfData['is_negative']) {
$prefixPdf = "AVOIR";
}
$pdfFilename = $prefixPdf."_".$pdfFilename;
$filenames = [];
foreach($factureFolders as $folder){
foreach($factureFolders as $folder) {
try {
$storage->newFolder($folder);
}
catch(\OCP\Files\NotPermittedException $e) {
} catch(\OCP\Files\NotPermittedException $e) {
}
$ff_pdf = $folder.$pdfFilename.'.pdf';
$ff_pdf = $folder.$pdfFilename.'.pdf';
$storage->newFile($ff_pdf);
$file_pdf = $storage->get($ff_pdf);
$file_pdf->putContent($pdfContent);
@ -124,18 +126,19 @@ class InvoicePdfService {
];
}
public function generateFacturePdfByFactureId($factureId,$idNextCloud){
public function generateFacturePdfByFactureId($factureId, $idNextCloud)
{
$factureType = $this->gestionBdd->getFactureTypeByFactureId($factureId);
if($factureType == FactureTypeConstant::TYPE_SINGLE){
return $this->generateFactureSinglePdfByFactureId($factureId,$idNextCloud);
}
else{
return $this->generateFactureGroupPdfByFactureId($factureId,$idNextCloud);
if($factureType == FactureTypeConstant::TYPE_SINGLE) {
return $this->generateFactureSinglePdfByFactureId($factureId, $idNextCloud);
} else {
return $this->generateFactureGroupPdfByFactureId($factureId, $idNextCloud);
}
}
private function getGroupFactureFolder(array $factureData,$racinePath){
$clientRacineFolder = $racinePath.'CLIENTS/'.mb_strtoupper($factureData["group_name"],'UTF-8').'/';
private function getGroupFactureFolder(array $factureData, $racinePath)
{
$clientRacineFolder = $racinePath.'CLIENTS/'.mb_strtoupper($factureData["group_name"], 'UTF-8').'/';
$factureDate = $factureData['date_paiement'];
$factureDatetime = new DateTime($factureDate);
$factureDateYear = $factureDatetime->format('Y');
@ -145,10 +148,11 @@ class InvoicePdfService {
$factureByYearFolder
];
}
private function getFacturesFolder(array $factureData,$racinePath){
$clientRacineFolder = $racinePath.'CLIENTS/'.mb_strtoupper($factureData["client_nom"],'UTF-8').'/';
$defuntsFolder = $clientRacineFolder.'DEFUNTS/'.mb_strtoupper($factureData['defunt_nom'],'UTF-8').'/'.'FACTURES'.'/';
private function getFacturesFolder(array $factureData, $racinePath)
{
$clientRacineFolder = $racinePath.'CLIENTS/'.mb_strtoupper($factureData["client_nom"], 'UTF-8').'/';
$defuntsFolder = $clientRacineFolder.'DEFUNTS/'.mb_strtoupper($factureData['defunt_nom'], 'UTF-8').'/'.'FACTURES'.'/';
$devisDate = $factureData['devis_date'];
$devisDatetime = new DateTime($devisDate);
$devisDateYear = $devisDatetime->format('Y');
@ -160,48 +164,48 @@ class InvoicePdfService {
];
}
private function generateFactureGroupPdfByFactureId($factureId,$idNextCloud){
private function generateFactureGroupPdfByFactureId($factureId, $idNextCloud)
{
$storage = $this->rootFolder->getUserFolder($idNextCloud);
$configs = json_decode($this->gestionBdd->getConfiguration(self::DEFAULT_NEXTCLOUD_ADMIN));
$currentConfig = $configs[0];
$logo = $this->getLogo();
$invoicePdfData = $this->gestionBdd->getInvoiceGroupPdfData($factureId,$currentConfig);
if($invoicePdfData == null){
return "";
}
$currentConfig = $configs[0];
$logo = $this->getLogo();
$invoicePdfData = $this->gestionBdd->getInvoiceGroupPdfData($factureId, $currentConfig);
if($invoicePdfData == null) {
return "";
}
$templateType = $invoicePdfData['template_type_key'];
$clean_folder = html_entity_decode(string: $currentConfig->path).'/';
$factureFolders = $this->getGroupFactureFolder($invoicePdfData,$clean_folder);
//For testing
$factureFolders = $this->getGroupFactureFolder($invoicePdfData, $clean_folder);
//For testing
// $templateType = ClientTemplateTypeConstant::OGF;
switch ($templateType) {
case ClientTemplateTypeConstant::FUNECAP:
$pdf = new InvoiceFunecapPdfHandler();
break;
case ClientTemplateTypeConstant::OGF:
$pdf = new InvoiceOgfPdfHandler();
break;
default:
$pdf = new InvoiceGroupPdfHandler();
break;
}
// $pdf->AddFont('ComicSans','','Comic Sans MS.php');
// $pdf->AddFont('ComicSans','B','comic-sans-bold.php');
$pdf->InvoicePdfFactory($invoicePdfData,$logo);
$pdf->InvoicePdfFactory($invoicePdfData, $logo);
$pdf->SetFactureContent();
$pdfContent = $pdf->Output('','S');
$pdfContent = $pdf->Output('', 'S');
$pdfFilename = $pdf->GetInvoiceFilename();
$filenames = [];
foreach($factureFolders as $folder){
foreach($factureFolders as $folder) {
try {
$storage->newFolder($folder);
}
catch(\OCP\Files\NotPermittedException $e) {
} catch(\OCP\Files\NotPermittedException $e) {
}
$ff_pdf = $folder.$pdfFilename.'.pdf';
$ff_pdf = $folder.$pdfFilename.'.pdf';
$storage->newFile($ff_pdf);
$file_pdf = $storage->get($ff_pdf);
$file_pdf->putContent($pdfContent);
@ -214,58 +218,61 @@ class InvoicePdfService {
];
}
public function generateFacturePdfByFactureIds(array $factureIds,$idNextCloud){
foreach( $factureIds as $factureId ){
$this->generateFacturePdfByFactureId($factureId,$idNextCloud);
public function generateFacturePdfByFactureIds(array $factureIds, $idNextCloud)
{
foreach($factureIds as $factureId) {
$this->generateFacturePdfByFactureId($factureId, $idNextCloud);
}
}
public function generateMultipleInvoicePdfByClientAndMonthYear($filter,$month,$year,$idNextCloud,$filterType){
public function generateMultipleInvoicePdfByClientAndMonthYear($filter, $month, $year, $idNextCloud, $filterType)
{
$storage = $this->rootFolder->getUserFolder($idNextCloud);
$configs = json_decode($this->gestionBdd->getConfiguration(self::DEFAULT_NEXTCLOUD_ADMIN));
$currentConfig = $configs[0];
$currentConfig = $configs[0];
$logo = $this->getLogo();
$invoiceData = $this->gestionBdd->getInvoicePdfDataByClientAndMonthYear($filter,$month,$year,$currentConfig,$filterType);
if(empty($invoiceData)){
return null;
}
$invoiceData = $this->gestionBdd->getInvoicePdfDataByClientAndMonthYear($filter, $month, $year, $currentConfig, $filterType);
if(empty($invoiceData)) {
return null;
}
$pdf = new InvoicePdfHandler();
// $pdf->AddFont('ComicSans','','Comic Sans MS.php');
// $pdf->AddFont('ComicSans','B','comic-sans-bold.php');
$pdf->MutlipleInvoicePdfFactory($invoiceData,$logo);
$pdf->MutlipleInvoicePdfFactory($invoiceData, $logo);
$pdf->SetMultipleFactureContent();
$racinePath = html_entity_decode(string: $currentConfig->path).'/';
$clientNameInFolder = $invoiceData[0]["client_nom"];
if($invoiceData[0]['facture_type'] == MultipleFactureTypeConstant::GROUP_FILTER_TYPE){
if($invoiceData[0]["group_name"] != null && $invoiceData[0]["group_name"] != ""){
if($invoiceData[0]['facture_type'] == MultipleFactureTypeConstant::GROUP_FILTER_TYPE) {
if($invoiceData[0]["group_name"] != null && $invoiceData[0]["group_name"] != "") {
$clientNameInFolder = $invoiceData[0]["group_name"];
}
}
$clientRacineFolder = $racinePath.'CLIENTS/'.mb_strtoupper($clientNameInFolder,'UTF-8').'/';
$filename = "FACTURE".'_'.$pdf->GetMultipleInvoiceFilename($month,$year);
$clientRacineFolder = $racinePath.'CLIENTS/'.mb_strtoupper($clientNameInFolder, 'UTF-8').'/';
$filename = "FACTURE".'_'.$pdf->GetMultipleInvoiceFilename($month, $year);
$filenamePath = $clientRacineFolder.$filename.'.pdf';
$pdfContent = $pdf->Output('','S');
$pdfContent = $pdf->Output('', 'S');
try {
$storage->newFolder($clientRacineFolder);
}
catch(\OCP\Files\NotPermittedException $e) {
}
} catch(\OCP\Files\NotPermittedException $e) {
}
$storage->newFile($filenamePath);
$file_pdf = $storage->get($filenamePath);
$file_pdf->putContent($pdfContent);
return $filenamePath;
}
public function generateInvoiceRecap($filter,$filterType,$date,$idNextCloud){
$this->invoiceRecapService->generateInvoiceRecap($filter,$filterType,$date,$idNextCloud);
public function generateInvoiceRecap($filter, $filterType, $date, $idNextCloud)
{
$this->invoiceRecapService->generateInvoiceRecap($filter, $filterType, $date, $idNextCloud);
}
public function exportGroupOfDevisIntoFacture($clientId,$clientType,$month,$year,$facturationDate,$idNextcloud = BddConstant::DEFAULT_ADMIN_ID_NEXTCLOUD){
try{
public function exportGroupOfDevisIntoFacture($clientId, $clientType, $month, $year, $facturationDate, $idNextcloud = BddConstant::DEFAULT_ADMIN_ID_NEXTCLOUD)
{
try {
$datetime = new Datetime();
$month = $month ?? $datetime->format('m');
$year = $year ?? $datetime->format('Y');
$factureId = null;
$fkClientId = null;
$fkClientGroupFacturationId = null;
@ -274,22 +281,22 @@ class InvoicePdfService {
DevisMentionConstant::MENTION
];
// Recuperer les devis non facturés du client avant la date de facturation du mois
//Si il a devis qui n est pas encore facturés
//Si il a devis qui n est pas encore facturés
//Cree un facture, atttaché l ID du facture au devis et generer le pdf
if($clientType == MultipleFactureTypeConstant::CLIENT_FILTER_TYPE){
$devisIds = $this->gestionBdd->getDevisIdsByClientIdAndDate($clientId,$facturationDate,$devisMentionFiltersToBeInvoiced);
if($clientType == MultipleFactureTypeConstant::CLIENT_FILTER_TYPE) {
$devisIds = $this->gestionBdd->getDevisIdsByClientIdAndDate($clientId, $facturationDate, $devisMentionFiltersToBeInvoiced);
$fkClientId = $clientId;
$factureId = $this->gestionBdd->getFactureIdByClientIdAndDate($clientId,$facturationDate);
$factureId = $this->gestionBdd->getFactureIdByClientIdAndDate($clientId, $facturationDate);
}else{
$devisIds = $this->gestionBdd->getDevisIdsByClientGroupFacturationIdAnDate($clientId , $facturationDate , $devisMentionFiltersToBeInvoiced );
} else {
$devisIds = $this->gestionBdd->getDevisIdsByClientGroupFacturationIdAnDate($clientId, $facturationDate, $devisMentionFiltersToBeInvoiced);
$fkClientGroupFacturationId = $clientId;
$factureId = $this->gestionBdd->getFactureIdByClientGroupFacturationIdAndDate($clientId,$facturationDate);
$factureId = $this->gestionBdd->getFactureIdByClientGroupFacturationIdAndDate($clientId, $facturationDate);
}
// if($clientType == MultipleFactureTypeConstant::CLIENT_FILTER_TYPE){
// $devisIds = $this->gestionBdd->getDevisIdsByClientIdAndMonthYear($clientId,$month,$year,$devisMentionFiltersToBeInvoiced);
// $devisIds = $this->gestionBdd->getDevisIdsByClientIdAndMonthYear($clientId,$month,$year,$devisMentionFiltersToBeInvoiced);
// $fkClientId = $clientId;
// }
// else{
@ -307,11 +314,11 @@ class InvoicePdfService {
// $fkClientId,
// $fkClientGroupFacturationId);
// }
if (!empty($devisIds)) {
//Get facture by date and client
$clientIsAlreadyFacturedForThisDate = $factureId != null && $factureId != 0;
if (!$clientIsAlreadyFacturedForThisDate) {
$clientIsAlreadyFacturedForThisDate = $factureId != null && $factureId != 0;
if (!$clientIsAlreadyFacturedForThisDate) {
$factureId = $this->gestionBdd->createFactureAndReturnFactureId(
$facturationDate,
FactureTypeConstant::TYPE_GROUP,
@ -320,16 +327,15 @@ class InvoicePdfService {
$fkClientId,
$fkClientGroupFacturationId
);
}
$this->gestionBdd->invoiceListOfDevisIds($devisIds , $factureId);
$factureGeneratedResponse = $this->generateFactureGroupPdfByFactureId($factureId,$idNextcloud);
}
$this->gestionBdd->invoiceListOfDevisIds($devisIds, $factureId);
$factureGeneratedResponse = $this->generateFactureGroupPdfByFactureId($factureId, $idNextcloud);
return $factureGeneratedResponse["filenames"];
}
return null;
}
catch(Exception){
} catch(Exception) {
return null;
}
}
}

View File

@ -5,7 +5,7 @@ import "../css/mycss.css";
import { globalConfiguration } from "./modules/mainFunction.mjs";
import "./listener/main_listener";
import "./listener/devisListener";
import { exportClientDevisByMonthAndYearToPdf } from "./modules/ajaxRequest.mjs";
import { exportClientDevisByMonthAndYearToPdf, exportClientDevisRecap } from "./modules/ajaxRequest.mjs";
import 'select2/dist/css/select2.css';
import 'select2';
import '../css/mycss.css';

View File

@ -148,7 +148,6 @@ document.onchange = function(event) {
};
$('body').on('click', '#showGroupDevisFacturationModal', function () {
console.log("sdsfs");
$('#groupDevisFacturationModal').show();
});
@ -156,6 +155,14 @@ $('body').on('click', '#closeGroupDevisModal', function () {
$('#groupDevisFacturationModal').hide();
});
$('body').on('click', '#showDevisRecapModal', function () {
$('#devisRecapMontant').show();
});
$('body').on('click', '#closeDevisRecapModal', function () {
$('#devisRecapMontant').hide();
});
$('body').on('click', '#invoiceGroupQuote', function () {
var dateValue = document.getElementById("facturationDate").value;
@ -202,3 +209,95 @@ $('body').on('click', '#invoiceGroupQuote', function () {
hideLoader();
});
});
$('body').on('click', '#devisRecapAction', function () {
var valMontant = document.getElementById("sansMontant").checked;
const urlParams = new URLSearchParams(window.location.search);
const filter = urlParams.get('cli');
const year = urlParams.get('annee');
const month = urlParams.get('mois');
const filterType = urlParams.get('filterType');
var devisPayload = {
clientId: filter,
month: month,
year: year,
clientType: filterType,
montant: valMontant
};
showLoader();
$.ajax({
url: baseUrl + '/devis/exportDevisRecap',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(devisPayload)
}).done(function (response) {
if (response != null && response.trim() !== "") {
// Le retour est une string directe, pas un array JSON
var filename = response.replace(/\\/g, '/'); // Corriger les backslashes Windows
showSuccess('Sauvegardé dans ' + filename);
// Fermeture de la modal avec gestion d'erreur intégrée
var modalId = 'devisRecapMontant';
// Essayer Bootstrap 5 d'abord
if (typeof bootstrap !== 'undefined' && bootstrap.Modal) {
var modalElement = document.getElementById(modalId);
if (modalElement) {
var modalInstance = bootstrap.Modal.getInstance(modalElement);
if (modalInstance) {
modalInstance.hide();
} else {
// Si pas d'instance, créer et fermer
modalInstance = new bootstrap.Modal(modalElement);
modalInstance.hide();
}
}
}
// Essayer Bootstrap 4/3 avec jQuery
else if (typeof $ !== 'undefined' && $.fn.modal) {
try {
$('#' + modalId).modal('hide');
} catch (e) {
console.warn('Erreur lors de la fermeture de la modal avec jQuery:', e);
// Fallback manuel
var modalElement = document.getElementById(modalId);
if (modalElement) {
modalElement.classList.remove('show');
modalElement.style.display = 'none';
}
}
}
// Fallback : fermeture manuelle pure JavaScript
else {
var modalElement = document.getElementById(modalId);
if (modalElement) {
// Retirer les classes Bootstrap
modalElement.classList.remove('show');
modalElement.style.display = 'none';
modalElement.setAttribute('aria-hidden', 'true');
// Retirer le backdrop si présent
var backdrop = document.querySelector('.modal-backdrop');
if (backdrop) {
backdrop.remove();
}
// Retirer la classe modal-open du body
document.body.classList.remove('modal-open');
document.body.style.removeProperty('padding-right');
document.body.style.removeProperty('overflow');
}
}
} else {
showError(t('gestion', "Les données pour sauvegarde sont vides"));
}
}).fail(function (response, code) {
showError(t('gestion', "Erreur dans la génération du récapitulatif devis"));
}).always(function () {
hideLoader();
});
});

View File

@ -858,6 +858,7 @@ export function exportClientDevisByMonthAndYearToPdf(clientId,year,month,filterT
});
};
/**
* Set bijoux photo
* @param {*} bijouxId

View File

@ -3,6 +3,7 @@
<form method="get" class="d-flex flex-row align-items-center">
<select name="cli" id="clientselector">
<?php
$showRecapButton = false;
foreach ($_['clients'] as $key => $client) {
?>
<option <?php
@ -17,8 +18,9 @@
?>
</select>&nbsp;&nbsp;
<select name="annee" id="yearselector">
<option value="-1" <?php if ((int) $_GET['annee'] == -1)
echo 'selected' ?>>Toutes les années</option>
<option value="-1" <?php if ((int) $_GET['annee'] == -1) {
echo 'selected';
} ?>>Toutes les années</option>
<?php
$currentYear = date('Y');
for ($year = $currentYear; $year >= $currentYear - 10; $year--) {
@ -27,73 +29,89 @@
?>
</select>&nbsp;&nbsp;
<select name="mois" id="monthselector">
<option value="0" <?php if ((int) $_GET['mois'] == 0)
echo 'selected' ?>>Tous les mois</option>
<option value="1" <?php if ((int) $_GET['mois'] == 1)
echo 'selected' ?>>Janvier</option>
<option value="2" <?php if ((int) $_GET['mois'] == 2)
echo 'selected' ?>>Fevrier</option>
<option value="3" <?php if ((int) $_GET['mois'] == 3)
echo 'selected' ?>>Mars</option>
<option value="4" <?php if ((int) $_GET['mois'] == 4)
echo 'selected' ?>>Avril</option>
<option value="5" <?php if ((int) $_GET['mois'] == 5)
echo 'selected' ?>>Mai</option>
<option value="6" <?php if ((int) $_GET['mois'] == 6)
echo 'selected' ?>>Juin</option>
<option value="7" <?php if ((int) $_GET['mois'] == 7)
echo 'selected' ?>>Juillet</option>
<option value="8" <?php if ((int) $_GET['mois'] == 8)
echo 'selected' ?>>Août</option>
<option value="9" <?php if ((int) $_GET['mois'] == 9)
echo 'selected' ?>>Septembre</option>
<option value="10" <?php if ((int) $_GET['mois'] == 10)
echo 'selected' ?>>Octobre</option>
<option value="11" <?php if ((int) $_GET['mois'] == 11)
echo 'selected' ?>>Novembre</option>
<option value="12" <?php if ((int) $_GET['mois'] == 12)
echo 'selected' ?>>Decembre</option>
<option value="0" <?php if ((int) $_GET['mois'] == 0) {
echo 'selected';
} ?>>Tous les mois</option>
<option value="1" <?php if ((int) $_GET['mois'] == 1) {
echo 'selected';
} ?>>Janvier</option>
<option value="2" <?php if ((int) $_GET['mois'] == 2) {
echo 'selected';
} ?>>Fevrier</option>
<option value="3" <?php if ((int) $_GET['mois'] == 3) {
echo 'selected';
} ?>>Mars</option>
<option value="4" <?php if ((int) $_GET['mois'] == 4) {
echo 'selected';
} ?>>Avril</option>
<option value="5" <?php if ((int) $_GET['mois'] == 5) {
echo 'selected';
} ?>>Mai</option>
<option value="6" <?php if ((int) $_GET['mois'] == 6) {
echo 'selected';
} ?>>Juin</option>
<option value="7" <?php if ((int) $_GET['mois'] == 7) {
echo 'selected';
} ?>>Juillet</option>
<option value="8" <?php if ((int) $_GET['mois'] == 8) {
echo 'selected';
} ?>>Août</option>
<option value="9" <?php if ((int) $_GET['mois'] == 9) {
echo 'selected';
} ?>>Septembre</option>
<option value="10" <?php if ((int) $_GET['mois'] == 10) {
echo 'selected';
} ?>>Octobre</option>
<option value="11" <?php if ((int) $_GET['mois'] == 11) {
echo 'selected';
} ?>>Novembre</option>
<option value="12" <?php if ((int) $_GET['mois'] == 12) {
echo 'selected';
} ?>>Decembre</option>
</select>&nbsp;&nbsp;
<input type="hidden" name="filterType" id="filterType"
value="<?php echo ($_GET['filterType'] ?? 'group'); ?>">
value="<?php echo($_GET['filterType'] ?? 'group'); ?>">
<input type="submit" value="Filtrer" />
</form>
<div class="d-flex flex-row">
<?php
$clients = $_['clients'];
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
$devis = array_filter($_['devis'], function ($currentDevis) {
if ($currentDevis->cid) {
$datesplit = explode("-", $currentDevis->date);
$year = (int) $datesplit[0];
$month = (int) $datesplit[1];
$checkClient = false;
$filterType = "group";
if (array_key_exists('filterType', $_GET) && $_GET['filterType'] == 'client') {
$filterType = "client";
}
$clientIsNotSelected = strcmp($_GET['cli'], '') == 0;
if ($clientIsNotSelected) {
if ($filterType == "group") {
$checkClient = $_['clients'][0]->fk_client_group_facturation_id == $currentDevis->cid;
} else {
$checkClient = $_['clients'][0]->id == $currentDevis->cid;
}
} else {
if ($filterType == "group") {
$checkClient = $currentDevis->fk_client_group_facturation_id == $_GET['cli'];
} else {
$checkClient = $currentDevis->cid == $_GET['cli'];
}
}
$checkYear = ((int) ($_GET['annee']) == -1) ? (true) : ($year == ((int) $_GET['annee']));
$checkMounth = (((int) $_GET['mois']) == 0) ? (true) : ($month == ((int) $_GET['mois']));
return $checkClient && $checkYear && $checkMounth;
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
if(intval($_GET['mois']) != 0 && intval($_GET['annee']) != 0) {
$showRecapButton = true;
}
return false;
});
if (strcmp($_GET['cli'], '') != 0 && sizeof($devis) > 0) {
?>
$devis = array_filter($_['devis'], function ($currentDevis) {
if ($currentDevis->cid) {
$datesplit = explode("-", $currentDevis->date);
$year = (int) $datesplit[0];
$month = (int) $datesplit[1];
$checkClient = false;
$filterType = "group";
if (array_key_exists('filterType', $_GET) && $_GET['filterType'] == 'client') {
$filterType = "client";
}
$clientIsNotSelected = strcmp($_GET['cli'], '') == 0;
if ($clientIsNotSelected) {
if ($filterType == "group") {
$checkClient = $_['clients'][0]->fk_client_group_facturation_id == $currentDevis->cid;
} else {
$checkClient = $_['clients'][0]->id == $currentDevis->cid;
}
} else {
if ($filterType == "group") {
$checkClient = $currentDevis->fk_client_group_facturation_id == $_GET['cli'];
} else {
$checkClient = $currentDevis->cid == $_GET['cli'];
}
}
$checkYear = ((int) ($_GET['annee']) == -1) ? (true) : ($year == ((int) $_GET['annee']));
$checkMounth = (((int) $_GET['mois']) == 0) ? (true) : ($month == ((int) $_GET['mois']));
return $checkClient && $checkYear && $checkMounth;
}
return false;
});
if (strcmp($_GET['cli'], '') != 0 && sizeof($devis) > 0) {
?>
<button class="btn btn-secondary" type="button"
id="exportMultipleDevisToPdf"><?php p($l->t('Save in Nextcloud')); ?></button>
<button class="btn btn-secondary" type="button" id="showGroupDevisFacturationModal" data-toggle="modal"
@ -101,22 +119,28 @@
Facturer
</button>
<?php
}
}
if (strcmp($_GET['cli'], '') != 0 && sizeof($devis) > 0) {
?>
<?php if($showRecapButton) {?><button class="btn btn-secondary" type="button" id="showDevisRecapModal" data-toggle="modal"
data-target="#devisRecapMontant">Generer le document recapitulatif</button><?php }
}
?>
}
?>
</div>
</div>
<hr>
<div id="gestion-canvas" class="canvas_div_pdf">
<?php
if ($_SERVER['REQUEST_METHOD'] == 'GET' && strcmp($_GET['cli'], '') != 0) {
if (sizeof($devis) == 0)
if (sizeof($devis) == 0) {
echo "Aucun devis trouvé.";
}
}
;
;
foreach ($devis as $key => $currentDevis) {
?>
foreach ($devis as $key => $currentDevis) {
?>
<div class="bootstrap-iso d-flex flex-column justify-content-between">
<div class="d-flex flex-column w-100">
<h2 class="mt-3 mb-3 text-center"> <?php p($l->t('Quote')); ?>
@ -143,12 +167,12 @@
</div>
<div class="col-2 h-100 m-0" style="min-height:250px;">
<?php
if (isset($_['logo']) && $_['logo'] !== "nothing") {
echo "<center><a><img alt='" . $l->t('Company logo') . "' class=\"img-fluid\" src=\"data:image/png;base64, " . $_['logo'] . "\"/></a></center>";
} else {
echo "<span style='font-size:12px' id='Company-logo' data-html2canvas-ignore><b><center>" . $l->t('You can add your company logo here.') . "</center></b><br/><i>" . $l->t('To add a logo, drop the logo.png file in ".gestion" folder at the root of your Nextcloud Files app. Remember to set "Show hidden files".') . "</i><br/><br/><center>" . $l->t('This message will not appear on generated PDF.') . "</center></span>";
}
?>
if (isset($_['logo']) && $_['logo'] !== "nothing") {
echo "<center><a><img alt='" . $l->t('Company logo') . "' class=\"img-fluid\" src=\"data:image/png;base64, " . $_['logo'] . "\"/></a></center>";
} else {
echo "<span style='font-size:12px' id='Company-logo' data-html2canvas-ignore><b><center>" . $l->t('You can add your company logo here.') . "</center></b><br/><i>" . $l->t('To add a logo, drop the logo.png file in ".gestion" folder at the root of your Nextcloud Files app. Remember to set "Show hidden files".') . "</i><br/><br/><center>" . $l->t('This message will not appear on generated PDF.') . "</center></span>";
}
?>
</div>
<div class="col-5 h-100 m-0" style="min-height:250px;">
<h5 class="p-3 m-0 text-dark text-center border border-2 border-dark"><?php p($l->t('TO')); ?>
@ -197,7 +221,7 @@
style="display:inline"
data-table="devis" data-column="order_number"
data-id="<?php echo $currentDevis->id;?>">
<?php echo ($currentDevis->order_number == "" ) ? "-" : $currentDevis->order_number ; ?>
<?php echo ($currentDevis->order_number == "") ? "-" : $currentDevis->order_number ; ?>
</div>
</div>
<hr />
@ -209,7 +233,7 @@
style="display:inline"
data-table="devis" data-column="case_number"
data-id="<?php echo $currentDevis->id;?>">
<?php echo ($currentDevis->case_number == "" ) ? "-" : $currentDevis->case_number ; ?>
<?php echo ($currentDevis->case_number == "") ? "-" : $currentDevis->case_number ; ?>
</div>
</div>
<hr />
@ -253,16 +277,16 @@
</thead>
<tbody>
<?php
$totalhtc = 0;
$tva = json_decode($_['configuration'])[0]->tva_default;
$totalttc = 0;
$totalprice = 0;
foreach ($currentDevis->dproduits as $key => $produit) {
$totalhtc = $totalhtc + ($produit->quantite * $produit->prix_unitaire);
}
$totalttc = ($totalhtc * $tva) / 100;
$totalprice = $totalhtc + $totalttc;
?>
$totalhtc = 0;
$tva = json_decode($_['configuration'])[0]->tva_default;
$totalttc = 0;
$totalprice = 0;
foreach ($currentDevis->dproduits as $key => $produit) {
$totalhtc = $totalhtc + ($produit->quantite * $produit->prix_unitaire);
}
$totalttc = ($totalhtc * $tva) / 100;
$totalprice = $totalhtc + $totalttc;
?>
<tr>
<td>&euro;<?php echo number_format($totalhtc, 2) ?></td>
<td><?php echo $tva ?> &percnt;</td>
@ -288,8 +312,29 @@
<hr data-html2canvas-ignore>
<hr data-html2canvas-ignore>
<?php
}
?>
}
?>
</div>
<div class="modal" id="devisRecapMontant" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Souhaitez-vous le récapitulatif avec ou sans montant ?</h5>
</div>
<div class="modal-body">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="sansMontant" value="">
<label class="form-check-label" for="sansMontant">
Sans Montant
</label>
</div>
</div>
<div class="modal-footer">
<button id="closeDevisRecapModal" type="button" class="btn btn-secondary">Annuler</button>
<button id="devisRecapAction" type="button" class="btn btn-primary">Générer</button>
</div>
</div>
</div>
</div>
<div class="modal" id="groupDevisFacturationModal" tabindex="-1">
<div class="modal-dialog">