fix generate pdf recap devis with more pages

This commit is contained in:
Tolotsoa 2025-09-03 14:50:46 +03:00
parent d9a63e9ac3
commit 9f0450a76c
5 changed files with 131 additions and 76 deletions

View File

@ -63,11 +63,24 @@ class DevisDataProcessor
{ {
$produits = json_decode($this->gestionBdd->getListProduit($devis['devis_id'], $idNextCloud)); $produits = json_decode($this->gestionBdd->getListProduit($devis['devis_id'], $idNextCloud));
// Variables pour gérer les articles comme dans les factures
$produitsReferenceArray = [];
foreach ($produits as $produit) { foreach ($produits as $produit) {
$htPrice = $this->getProductPrice($produit, $devis, $filter); $htPrice = $this->getProductPrice($produit, $devis, $filter);
$devis_temp['montant_htc'] += $htPrice * $produit->quantite; $devis_temp['montant_htc'] += $htPrice * $produit->quantite;
// Collecter les références comme dans les factures
if (isset($produit->reference) && !empty($produit->reference)) {
$produitsReferenceArray[] = $produit->reference;
}
} }
// Traitement identique aux factures
$produitsReferenceArray = array_unique($produitsReferenceArray);
$produitsReferenceAsString = implode("-", $produitsReferenceArray);
$devis_temp['article'] = !empty($produitsReferenceAsString) ? $produitsReferenceAsString : 'SOINS';
$devis_temp['montant_tva'] = ($devis_temp['montant_htc'] * $devis_temp['tva']) / 100; $devis_temp['montant_tva'] = ($devis_temp['montant_htc'] * $devis_temp['tva']) / 100;
$devis_temp['montant_ttc'] = $devis_temp['montant_tva'] + $devis_temp['montant_htc']; $devis_temp['montant_ttc'] = $devis_temp['montant_tva'] + $devis_temp['montant_htc'];

View File

@ -82,8 +82,8 @@ class DevisPdfGenerator
private function calculatePagination($totalItems) private function calculatePagination($totalItems)
{ {
return [ return [
'nb_pages' => ceil($totalItems / 26), 'nb_pages' => ceil($totalItems / 12), // RÉDUIRE à 12 par page
'items_per_page' => 26, 'items_per_page' => 12,
'current_index' => 0 'current_index' => 0
]; ];
} }

View File

@ -21,7 +21,7 @@ class DevisPdfLayoutManager
public function addPageHeader($pdf, $config, $address, $city) public function addPageHeader($pdf, $config, $address, $city)
{ {
if ($this->doesLogoExist()) { if ($this->doesLogoExist()) {
$pdf->Image($this->defaultImagePath."logo.png", 10, 10, 75, 25); $pdf->Image($this->defaultImagePath."logo.png", 10, 10, 25, 0);
} }
$this->addCompanyInfo($pdf, $config, $address, $city); $this->addCompanyInfo($pdf, $config, $address, $city);
} }

View File

@ -6,19 +6,23 @@ class DevisPdfTableRenderer
{ {
public function createDevisTable($pdf, $dataDevis, $config) public function createDevisTable($pdf, $dataDevis, $config)
{ {
// Extraction des paramètres du tableau de configuration
$pagination = $config['pagination']; $pagination = $config['pagination'];
$itemsToProcess = $config['itemsToProcess']; $itemsToProcess = $config['itemsToProcess'];
$numPage = $config['numPage']; $numPage = $config['numPage'];
$nbPage = $config['nbPage']; $nbPage = $config['nbPage'];
$totals = &$config['totals']; // Référence pour modification $totals = &$config['totals'];
$sansMontant = $config['sansMontant'] ?? false; // false par défaut = afficher les montants $sansMontant = $config['sansMontant'] ?? false;
// Système de pagination comme les factures
$maxItemsPerPage = 22; // Nombre maximum d'éléments par page
$startIndex = $pagination['current_index'];
$itemsThisPage = min($itemsToProcess, count($dataDevis) - $startIndex);
$this->drawTableStructure($pdf, $numPage, $nbPage, $sansMontant); $this->drawTableStructure($pdf, $numPage, $nbPage, $sansMontant);
$this->addTableHeaders($pdf, $sansMontant); $this->addTableHeaders($pdf, $sansMontant);
$this->populateTableData($pdf, $dataDevis, $pagination['current_index'], $itemsToProcess, $totals, $sansMontant); $this->populateTableData($pdf, $dataDevis, $startIndex, $itemsThisPage, $totals, $sansMontant);
// Afficher les totaux seulement sur la dernière page ET si les montants sont affichés // Totaux seulement sur la dernière page
if ($numPage == $nbPage && !$sansMontant) { if ($numPage == $nbPage && !$sansMontant) {
$this->addTableTotals($pdf, $totals); $this->addTableTotals($pdf, $totals);
} }
@ -30,15 +34,14 @@ class DevisPdfTableRenderer
$pdf->Rect(5, 105, 200, 130, "D"); $pdf->Rect(5, 105, 200, 130, "D");
$pdf->Line(5, 115, 205, 115); $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; $endY = ($numPage == $nbPage && !$sansMontant) ? 225 : 235;
if (!$sansMontant) { if (!$sansMontant) {
// AVEC montants : toutes les colonnes // AVEC montants : toutes les colonnes
$verticalLines = [25, 45, 70, 90, 125, 155, 175, 190]; $verticalLines = [25, 45, 65, 80, 100, 125, 145, 165, 185];
} else { } else {
// SANS montants : seulement jusqu'à Défunt // SANS montants : SUPPRIMER la colonne vide à droite
$verticalLines = [35, 70, 105, 125, 155]; $verticalLines = [35, 70, 105, 120, 135, 160]; // Seulement 6 lignes au lieu de 7
} }
foreach ($verticalLines as $x) { foreach ($verticalLines as $x) {
@ -48,36 +51,37 @@ class DevisPdfTableRenderer
private function addTableHeaders($pdf, $sansMontant) private function addTableHeaders($pdf, $sansMontant)
{ {
$pdf->SetFont('Arial', 'B', 8); $pdf->SetFont('Arial', 'B', 7);
if (!$sansMontant) { if (!$sansMontant) {
// AVEC montants
$headers = [ $headers = [
[5, 20, mb_convert_encoding(html_entity_decode("N° Devis"), 'ISO-8859-1', 'UTF-8')], [5, 20, "N° Devis"],
[25, 20, mb_convert_encoding(html_entity_decode("N° Dossier"), 'ISO-8859-1', 'UTF-8')], [25, 20, "N° Dossier"],
[45, 25, mb_convert_encoding(html_entity_decode("N° Commande"), 'ISO-8859-1', 'UTF-8')], [45, 20, "N° Commande"],
[70, 20, "Date"], [65, 15, "Date"],
[90, 35, "Lieu du soin"], [80, 20, "Article"],
[125, 30, \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport("Défunt")], [100, 25, "Lieu du soin"],
[155, 20, "H.T."], [125, 20, "Défunt"],
[175, 15, "TVA 20%"], [145, 20, "H.T."],
[190, 15, "T.T.C"] [165, 20, "TVA"],
[185, 20, "T.T.C"]
]; ];
} else { } else {
// SANS montants - colonnes plus larges // SANS montants : SUPPRIMER la colonne vide, agrandir la dernière colonne
$headers = [ $headers = [
[5, 30, mb_convert_encoding(html_entity_decode("N° Devis"), 'ISO-8859-1', 'UTF-8')], [5, 30, "N° Devis"],
[35, 35, mb_convert_encoding(html_entity_decode("N° Dossier"), 'ISO-8859-1', 'UTF-8')], [35, 35, "N° Dossier"],
[70, 35, mb_convert_encoding(html_entity_decode("N° Commande"), 'ISO-8859-1', 'UTF-8')], [70, 35, "N° Commande"],
[105, 20, "Date"], [105, 15, "Date"],
[125, 30, "Lieu du soin"], [120, 15, "Article"],
[155, 50, \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport("Défunt")] [135, 25, "Lieu du soin"],
[160, 45, "Défunt"] // Dernière colonne plus large
]; ];
} }
foreach ($headers as $header) { foreach ($headers as $header) {
$pdf->SetXY($header[0], 106); $pdf->SetXY($header[0] + 1, 106);
$pdf->Cell($header[1], 8, $header[2], 0, 0, 'C'); $pdf->Cell($header[1] - 2, 8, mb_convert_encoding($header[2], 'ISO-8859-1', 'UTF-8'), 0, 0, 'C');
} }
} }
@ -101,7 +105,7 @@ class DevisPdfTableRenderer
$totals['ttc'] += $devis['montant_ttc']; $totals['ttc'] += $devis['montant_ttc'];
} }
$yDevis += 5; $yDevis += 10;
} }
} }
@ -109,64 +113,101 @@ class DevisPdfTableRenderer
{ {
$pdf->SetFont('Arial', '', 7); $pdf->SetFont('Arial', '', 7);
if (!$sansMontant) { // Fonction généraliste pour gérer automatiquement le débordement
// AVEC montants $addSmartCell = function ($pdf, $x, $y, $width, $text, $align = 'L') use ($yDevis) {
$rowData = [ $textWidth = $pdf->GetStringWidth($text);
[6, 18, $devis['devis_full_number']], $maxWidth = $width - 2; // Marge de sécurité
[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'] ?? '')], // ← CORRECTION
[126, 28, \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport($devis['defunt_nom'] ?? '')], // ← CORRECTION
[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'] ?? '')], // ← CORRECTION
[156, 48, \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport($devis['defunt_nom'] ?? '')] // ← CORRECTION
];
}
foreach ($rowData as $data) { if ($textWidth > $maxWidth && strlen($text) > 10) {
$pdf->SetXY($data[0], $yDevis); // DÉBORDEMENT : utiliser MultiCell pour affichage complet
$align = isset($data[3]) ? $data[3] : ''; $pdf->SetXY($x, $y);
$pdf->Cell($data[1], 5, $data[2], 0, 0, $align); $pdf->MultiCell($width, 2.5, $text, 0, $align);
} else {
// PAS DE DÉBORDEMENT : utiliser Cell normal
$pdf->SetXY($x, $y);
$pdf->Cell($width, 5, $text, 0, 0, $align);
}
};
if (!$sansMontant) {
// N° Devis
$addSmartCell($pdf, 6, $yDevis, 18, $devis['devis_full_number']);
// N° Dossier - GESTION AUTOMATIQUE
$addSmartCell($pdf, 26, $yDevis, 18, $devis['calendar_uuid']);
// N° Commande - GESTION AUTOMATIQUE
$addSmartCell($pdf, 46, $yDevis, 18, $devis['num_commande']);
// Date
$addSmartCell($pdf, 66, $yDevis, 13, mb_convert_encoding($formatterDate->format($dateSoin), 'ISO-8859-1', 'UTF-8'));
// Article - GESTION AUTOMATIQUE
$articleText = \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport($devis['article'] ?? 'SOINS');
$addSmartCell($pdf, 81, $yDevis, 18, $articleText);
// Lieu du soin - GESTION AUTOMATIQUE
$lieuText = \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport($devis['lieu_nom'] ?? '');
$addSmartCell($pdf, 101, $yDevis, 23, $lieuText);
// Défunt - GESTION AUTOMATIQUE
$defuntText = \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport($devis['defunt_nom'] ?? '');
$addSmartCell($pdf, 126, $yDevis, 18, $defuntText);
// Montants - Toujours centrés
$addSmartCell($pdf, 146, $yDevis, 18, number_format($devis['montant_htc'], 2, '.', '') . chr(128), 'C');
$addSmartCell($pdf, 166, $yDevis, 18, number_format($devis['montant_tva'], 2, '.', '') . chr(128), 'C');
$addSmartCell($pdf, 186, $yDevis, 18, number_format($devis['montant_ttc'], 2, '.', '') . chr(128), 'C');
} else {
// Mode sans montants - POSITIONS AJUSTÉES
$addSmartCell($pdf, 6, $yDevis, 28, $devis['devis_full_number']);
$addSmartCell($pdf, 36, $yDevis, 33, $devis['calendar_uuid']);
$addSmartCell($pdf, 71, $yDevis, 33, $devis['num_commande']);
$addSmartCell($pdf, 106, $yDevis, 13, mb_convert_encoding($formatterDate->format($dateSoin), 'ISO-8859-1', 'UTF-8'));
$articleText = \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport($devis['article'] ?? 'SOINS');
$addSmartCell($pdf, 121, $yDevis, 13, $articleText);
$lieuText = \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport($devis['lieu_nom'] ?? '');
$addSmartCell($pdf, 136, $yDevis, 23, $lieuText);
// Défunt - DERNIÈRE COLONNE plus large (45 au lieu de 23)
$defuntText = \OCA\Gestion\Helpers\FileExportHelpers::FormatTextForExport($devis['defunt_nom'] ?? '');
$addSmartCell($pdf, 161, $yDevis, 44, $defuntText); // Plus large pour remplir l'espace
} }
} }
private function addTableTotals($pdf, $totals) 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->Line(5, 225, 205, 225);
$pdf->SetFont('Arial', 'B', 8); $pdf->SetFont('Arial', 'B', 8);
$pdf->SetXY(5, 225); $pdf->SetXY(5, 225);
$pdf->Cell(150, 8, 'TOTAL', 0, 0, 'C'); $pdf->Cell(130, 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); // POSITIONS CORRIGÉES
$pdf->SetXY(136, 225);
$pdf->Cell(18, 8, number_format($totals['ht'], 2, '.', '') . chr(128), 0, 0, 'C');
$pdf->SetXY(156, 225);
$pdf->Cell(18, 8, number_format($totals['tva'], 2, '.', '') . chr(128), 0, 0, 'C');
$pdf->SetXY(176, 225);
$pdf->Cell(14, 8, number_format($totals['ttc'], 2, '.', '') . chr(128), 0, 0, 'C');
// TOTAL TTC
$pdf->SetXY(155, 240);
$pdf->Cell(20, 8, 'TOTAL TTC', 0, 0, 'C'); $pdf->Cell(20, 8, 'TOTAL TTC', 0, 0, 'C');
$pdf->SetXY(190, 240); $pdf->SetXY(176, 240);
$pdf->Cell(15, 8, number_format($totals['ttc'], 2, '.', '') . chr(128), 0, 0, 'C'); $pdf->Cell(14, 8, number_format($totals['ttc'], 2, '.', '') . chr(128), 0, 0, 'C');
$lines = [ $lines = [
[170, 240, 170, 248], [155, 240, 155, 248],
[176, 240, 176, 248],
[190, 240, 190, 248], [190, 240, 190, 248],
[205, 240, 205, 248], [155, 240, 190, 240],
[170, 240, 205, 240], [155, 248, 190, 248]
[170, 248, 205, 248]
]; ];
foreach ($lines as $line) { foreach ($lines as $line) {

View File

@ -42,6 +42,7 @@ class DevisRecapService
$currentConfig = $configs[0]; $currentConfig = $configs[0];
$devisData = $this->getDevisData($filter, $month, $year, $currentConfig, $filterType); $devisData = $this->getDevisData($filter, $month, $year, $currentConfig, $filterType);
if (empty($devisData)) { if (empty($devisData)) {
return null; return null;
} }