diff --git a/admin/setup_mvp.php b/admin/setup_mvp.php index 52484c8..c65f655 100644 --- a/admin/setup_mvp.php +++ b/admin/setup_mvp.php @@ -259,9 +259,91 @@ if (empty($mappings_by_line)) { } } +// Template Management Section +print '
'; +print '
'; + +// Load PDF class +require_once DOL_DOCUMENT_ROOT . '/custom/declarationtva/core/class/declarationtva_pdf.class.php'; +$pdf_generator = new DeclarationTVA_PDF($db); + +// Handle template upload +if ($action == 'upload_template') { + $uploaded_file = $_FILES['template_file']; + if ($pdf_generator->uploadCustomTemplate($uploaded_file)) { + setEventMessages($langs->trans("TemplateUploaded"), null, 'mesgs'); + } else { + setEventMessages($pdf_generator->error, null, 'errors'); + } +} + +// Handle template reset +if ($action == 'reset_template') { + if ($pdf_generator->resetToDefaultTemplate()) { + setEventMessages($langs->trans("TemplateReset"), null, 'mesgs'); + } else { + setEventMessages($langs->trans("TemplateResetFailed"), null, 'errors'); + } +} + +// Get template information +$template_info = $pdf_generator->getTemplateInfo(); + +print '
'; +print ''; +print ''; + +print ''; +print ''; +print ''; +print ''; + +print ''; +print ''; +print ''; +print ''; + +print ''; +print ''; +print ''; +print ''; + +print ''; +print ''; +print ''; +print ''; + +print ''; +print ''; +print ''; + +if ($template_info['custom_template']) { + print ''; + print ''; + print ''; +} + +print '
Gestion des modèles PDF CA-3
Modèle actuel'; +if ($template_info['custom_template']) { + print 'Modèle personnalisé'; +} else { + print 'Modèle officiel'; +} +print '
Version officielle' . $template_info['official_number'] . '
Nouveau modèle'; +print ''; +print '
Format PDF uniquement, taille max 10MB'; +print '
'; +print ''; +print '
'; + print 'Revenir au modèle officiel'; + print '
'; +print '
'; + +print '
'; +print '
'; + print ''; // Print footer llxFooter(); -?> ?> \ No newline at end of file diff --git a/core/class/declarationtva_pdf.class.php b/core/class/declarationtva_pdf.class.php new file mode 100644 index 0000000..61be6f8 --- /dev/null +++ b/core/class/declarationtva_pdf.class.php @@ -0,0 +1,334 @@ +. + */ + +/** + * \file core/class/declarationtva_pdf.class.php + * \ingroup declarationtva + * \brief PDF generation for CA-3 declarations + */ + +require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; + +/** + * Class to generate CA-3 declaration PDF + */ +class DeclarationTVA_PDF +{ + /** + * @var DoliDB Database handler + */ + public $db; + + /** + * @var string Error code (or message) + */ + public $error = ''; + + /** + * @var string[] Several error codes (or messages) + */ + public $errors = array(); + + /** + * @var int Entity + */ + public $entity; + + /** + * @var string Template path + */ + public $template_path; + + /** + * @var string Template version + */ + public $template_version = '30'; + + /** + * @var string Template document number + */ + public $template_document = '10963'; + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + public function __construct($db) + { + $this->db = $db; + $this->entity = (int) $conf->entity; + $this->template_path = DOL_DOCUMENT_ROOT.'/custom/declarationtva/templates/declarationtva/'; + } + + /** + * Generate CA-3 declaration PDF + * + * @param int $declaration_id Declaration ID + * @param string $outputlangs Output language + * @return string|false PDF file path or false on error + */ + public function generateCA3PDF($declaration_id, $outputlangs = '') + { + global $conf, $langs, $user; + + // Load declaration data + $declaration = new DeclarationTVA($this->db); + $result = $declaration->fetch($declaration_id); + if ($result <= 0) { + $this->error = 'Declaration not found'; + return false; + } + + // Get CA-3 line data + $ca3_data = $declaration->getCA3Lines($declaration_id); + if (empty($ca3_data)) { + $this->error = 'No CA-3 data found'; + return false; + } + + // Get company information + $company = new Societe($this->db); + $company->fetch($conf->entity); + + // Generate PDF filename + $filename = 'CA3_' . $declaration->declaration_number . '_' . date('Y-m-d') . '.pdf'; + $filepath = DOL_DATA_ROOT . '/declarationtva/' . $filename; + + // Ensure directory exists + if (!is_dir(DOL_DATA_ROOT . '/declarationtva/')) { + dol_mkdir(DOL_DATA_ROOT . '/declarationtva/'); + } + + // Check if we have a custom template + $template_file = $this->getTemplatePath(); + if (!$template_file) { + $this->error = 'CA-3 template not found'; + return false; + } + + // Generate PDF using FPDI or similar library + $result = $this->fillPDFTemplate($template_file, $filepath, $declaration, $ca3_data, $company); + + if ($result) { + return $filepath; + } else { + return false; + } + } + + /** + * Get the template file path (custom or default) + * + * @return string|false Template file path or false if not found + */ + private function getTemplatePath() + { + // Check for custom template first + $custom_template = $this->template_path . 'ca3_custom_template.pdf'; + if (file_exists($custom_template)) { + return $custom_template; + } + + // Fall back to default template + $default_template = $this->template_path . 'ca3_official_template.pdf'; + if (file_exists($default_template)) { + return $default_template; + } + + return false; + } + + /** + * Fill PDF template with declaration data + * + * @param string $template_path Template file path + * @param string $output_path Output file path + * @param DeclarationTVA $declaration Declaration object + * @param array $ca3_data CA-3 line data + * @param Societe $company Company object + * @return bool Success + */ + private function fillPDFTemplate($template_path, $output_path, $declaration, $ca3_data, $company) + { + // This is a placeholder - we'll need to implement actual PDF filling + // For now, we'll create a simple PDF with the data + + try { + // Create a new PDF document + $pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false); + + // Set document information + $pdf->SetCreator('DeclarationTVA Module'); + $pdf->SetAuthor($company->name); + $pdf->SetTitle('CA-3 Declaration ' . $declaration->declaration_number); + $pdf->SetSubject('French VAT Declaration'); + + // Set margins + $pdf->SetMargins(15, 15, 15); + $pdf->SetHeaderMargin(5); + $pdf->SetFooterMargin(10); + + // Add a page + $pdf->AddPage(); + + // Add title + $pdf->SetFont('helvetica', 'B', 16); + $pdf->Cell(0, 10, 'Déclaration TVA CA-3', 0, 1, 'C'); + $pdf->Ln(10); + + // Add declaration information + $pdf->SetFont('helvetica', '', 12); + $pdf->Cell(0, 8, 'Numéro de déclaration: ' . $declaration->declaration_number, 0, 1); + $pdf->Cell(0, 8, 'Période: ' . dol_print_date($declaration->start_date, 'day') . ' - ' . dol_print_date($declaration->end_date, 'day'), 0, 1); + $pdf->Cell(0, 8, 'Statut: ' . $declaration->status, 0, 1); + $pdf->Ln(10); + + // Add CA-3 sections + $this->addCA3Section($pdf, 'A. Opérations imposables', $ca3_data, array('A1', 'A2', 'A3', 'A4', 'A5')); + $this->addCA3Section($pdf, 'B. TVA due', $ca3_data, array('08', '09', '9B', '17')); + $this->addCA3Section($pdf, 'C. TVA déductible', $ca3_data, array('20', '21', '22')); + $this->addCA3Section($pdf, 'D. Résultat', $ca3_data, array('25', '26', 'TD', '28', '29')); + + // Add totals + $pdf->Ln(10); + $pdf->SetFont('helvetica', 'B', 12); + $pdf->Cell(0, 8, 'TOTAL TVA COLLECTÉE: ' . price($declaration->total_vat_collected, 0, '', 1, 0), 0, 1); + $pdf->Cell(0, 8, 'TOTAL TVA DÉDUCTIBLE: ' . price($declaration->total_vat_deductible, 0, '', 1, 0), 0, 1); + $pdf->Cell(0, 8, 'TVA NETTE DUE: ' . price($declaration->net_vat_due, 0, '', 1, 0), 0, 1); + + if ($declaration->vat_credit > 0) { + $pdf->Cell(0, 8, 'CRÉDIT DE TVA: ' . price($declaration->vat_credit, 0, '', 1, 0), 0, 1); + } + + // Output PDF + $pdf->Output($output_path, 'F'); + + return true; + + } catch (Exception $e) { + $this->error = 'PDF generation failed: ' . $e->getMessage(); + return false; + } + } + + /** + * Add CA-3 section to PDF + * + * @param TCPDF $pdf PDF object + * @param string $section_title Section title + * @param array $ca3_data CA-3 data + * @param array $lines Lines to include + */ + private function addCA3Section($pdf, $section_title, $ca3_data, $lines) + { + $pdf->SetFont('helvetica', 'B', 12); + $pdf->Cell(0, 8, $section_title, 0, 1); + + $pdf->SetFont('helvetica', '', 10); + foreach ($lines as $line) { + if (isset($ca3_data[$line])) { + $data = $ca3_data[$line]; + $amount = isset($data['vat_amount']) ? $data['vat_amount'] : 0; + $pdf->Cell(20, 6, $line, 1, 0, 'C'); + $pdf->Cell(100, 6, $data['line_label'], 1, 0); + $pdf->Cell(30, 6, price($amount, 0, '', 1, 0), 1, 1, 'R'); + } + } + $pdf->Ln(5); + } + + /** + * Upload custom template + * + * @param array $file Uploaded file array + * @return bool Success + */ + public function uploadCustomTemplate($file) + { + if (!isset($file['tmp_name']) || !is_uploaded_file($file['tmp_name'])) { + $this->error = 'No file uploaded'; + return false; + } + + // Validate file type + $file_info = pathinfo($file['name']); + if (strtolower($file_info['extension']) !== 'pdf') { + $this->error = 'Only PDF files are allowed'; + return false; + } + + // Validate file size (max 10MB) + if ($file['size'] > 10 * 1024 * 1024) { + $this->error = 'File too large (max 10MB)'; + return false; + } + + // Move uploaded file + $target_path = $this->template_path . 'ca3_custom_template.pdf'; + if (!is_dir($this->template_path)) { + dol_mkdir($this->template_path); + } + + if (move_uploaded_file($file['tmp_name'], $target_path)) { + return true; + } else { + $this->error = 'Failed to save template'; + return false; + } + } + + /** + * Get template information + * + * @return array Template information + */ + public function getTemplateInfo() + { + $info = array( + 'version' => $this->template_version, + 'document' => $this->template_document, + 'official_number' => $this->template_document . '*' . $this->template_version, + 'custom_template' => false, + 'template_path' => '' + ); + + $custom_template = $this->template_path . 'ca3_custom_template.pdf'; + if (file_exists($custom_template)) { + $info['custom_template'] = true; + $info['template_path'] = $custom_template; + } + + return $info; + } + + /** + * Reset to default template + * + * @return bool Success + */ + public function resetToDefaultTemplate() + { + $custom_template = $this->template_path . 'ca3_custom_template.pdf'; + if (file_exists($custom_template)) { + return unlink($custom_template); + } + return true; + } +} diff --git a/declarationtva_view.php b/declarationtva_view.php index 281563f..9f638d2 100644 --- a/declarationtva_view.php +++ b/declarationtva_view.php @@ -54,6 +54,28 @@ if ($action == 'recalculate' && $token) { } } +if ($action == 'export_pdf') { + // Load PDF generator + require_once DOL_DOCUMENT_ROOT . '/custom/declarationtva/core/class/declarationtva_pdf.class.php'; + $pdf_generator = new DeclarationTVA_PDF($db); + + // Generate PDF + $pdf_path = $pdf_generator->generateCA3PDF($id); + + if ($pdf_path && file_exists($pdf_path)) { + // Set headers for PDF download + header('Content-Type: application/pdf'); + header('Content-Disposition: attachment; filename="CA3_' . $declarationtva->declaration_number . '.pdf"'); + header('Content-Length: ' . filesize($pdf_path)); + + // Output PDF + readfile($pdf_path); + exit; + } else { + setEventMessages($pdf_generator->error ?: $langs->trans("ErrorGeneratingPDF"), null, 'errors'); + } +} + // Fetch declaration if ($declarationtva->fetch($id) < 0) { setEventMessages($langs->trans("DeclarationNotFound"), null, 'errors'); @@ -357,6 +379,9 @@ print '
'; // Recalculate button (always available) print '' . $langs->trans("Recalculate") . ' '; +// PDF Export button (always available) +print '' . $langs->trans("ExportPDF") . ' '; + if ($declarationtva->status == 'draft') { print '' . $langs->trans("Validate") . ' '; } elseif ($declarationtva->status == 'validated') { diff --git a/langs/en_US/declarationtva.lang b/langs/en_US/declarationtva.lang index 6cec5eb..60d045e 100644 --- a/langs/en_US/declarationtva.lang +++ b/langs/en_US/declarationtva.lang @@ -466,3 +466,8 @@ VATAccounts = VAT Accounts SubtotalBaseAccounts = Subtotal Base Accounts SubtotalVATAccounts = Subtotal VAT Accounts GrandTotal = Grand Total +ExportPDF = Export PDF +TemplateUploaded = PDF template uploaded successfully +TemplateReset = Reset to official template +TemplateResetFailed = Error resetting to official template +ErrorGeneratingPDF = Error generating PDF diff --git a/langs/fr_FR/declarationtva.lang b/langs/fr_FR/declarationtva.lang index e451294..67c594d 100644 --- a/langs/fr_FR/declarationtva.lang +++ b/langs/fr_FR/declarationtva.lang @@ -455,3 +455,8 @@ VATAccounts = Comptes de TVA SubtotalBaseAccounts = Sous-total comptes de base SubtotalVATAccounts = Sous-total comptes de TVA GrandTotal = Total général +ExportPDF = Exporter PDF +TemplateUploaded = Modèle PDF téléchargé avec succès +TemplateReset = Retour au modèle officiel +TemplateResetFailed = Erreur lors du retour au modèle officiel +ErrorGeneratingPDF = Erreur lors de la génération du PDF diff --git a/templates/declarationtva/README.md b/templates/declarationtva/README.md new file mode 100644 index 0000000..d017e04 --- /dev/null +++ b/templates/declarationtva/README.md @@ -0,0 +1,26 @@ +# CA-3 Template Directory + +This directory contains the CA-3 PDF templates for the DeclarationTVA module. + +## Template Files + +- `ca3_official_template.pdf` - Official CA-3 template (version 30, document 10963) +- `ca3_custom_template.pdf` - Custom template uploaded by user (if any) + +## Template Information + +- **Official Number**: 10963*30 +- **Version**: 30 +- **Document**: 10963 +- **Source**: French Tax Authorities + +## Template Management + +The module will: +1. Use the custom template if available (`ca3_custom_template.pdf`) +2. Fall back to the official template (`ca3_official_template.pdf`) +3. Allow users to upload updated templates via the configuration page + +## File Permissions + +Ensure the web server has write permissions to this directory for template uploads.