. */ /** * \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'; /** * @var string Gitea repository URL for templates */ public $gitea_repo_url = 'https://git.covago.com/frank/DeclarationTVA'; /** * @var string Manifest file URL */ public $manifest_url = 'https://git.covago.com/frank/DeclarationTVA/raw/branch/main/templates/manifest.json'; /** * 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; } /** * Check for template updates from Gitea * * @return array Update information */ public function checkTemplateUpdates() { $update_info = array( 'update_available' => false, 'current_version' => $this->template_version, 'latest_version' => $this->template_version, 'download_url' => '', 'release_date' => '', 'error' => '' ); try { // Fetch manifest from Gitea $manifest_content = file_get_contents($this->manifest_url); if ($manifest_content === false) { $update_info['error'] = 'Failed to fetch manifest from Gitea'; return $update_info; } $manifest = json_decode($manifest_content, true); if (!$manifest || !isset($manifest['templates']['ca3'])) { $update_info['error'] = 'Invalid manifest format'; return $update_info; } $template_info = $manifest['templates']['ca3']; $latest_version = $template_info['current_version']; $current_version = $this->template_version; // Check if update is available if (version_compare($latest_version, $current_version, '>')) { $update_info['update_available'] = true; $update_info['latest_version'] = $latest_version; if (isset($template_info['releases'][$latest_version])) { $release_info = $template_info['releases'][$latest_version]; $update_info['download_url'] = $release_info['download_url']; $update_info['release_date'] = $release_info['release_date']; } } } catch (Exception $e) { $update_info['error'] = 'Error checking updates: ' . $e->getMessage(); } return $update_info; } /** * Download and install template update * * @param string $version Version to download * @param string $download_url Download URL * @return bool Success */ public function downloadTemplateUpdate($version, $download_url) { try { // Ensure template directory exists if (!is_dir($this->template_path)) { dol_mkdir($this->template_path); } // Download template $template_content = file_get_contents($download_url); if ($template_content === false) { $this->error = 'Failed to download template from Gitea'; return false; } // Save as official template $template_file = $this->template_path . 'ca3_official_template.pdf'; if (file_put_contents($template_file, $template_content) === false) { $this->error = 'Failed to save downloaded template'; return false; } // Update version info $this->template_version = $version; return true; } catch (Exception $e) { $this->error = 'Error downloading template: ' . $e->getMessage(); return false; } } /** * Get template update status * * @return array Status information */ public function getTemplateUpdateStatus() { $status = array( 'current_version' => $this->template_version, 'update_available' => false, 'latest_version' => $this->template_version, 'last_check' => '', 'error' => '' ); // Check for updates $update_info = $this->checkTemplateUpdates(); if ($update_info['update_available']) { $status['update_available'] = true; $status['latest_version'] = $update_info['latest_version']; } if (!empty($update_info['error'])) { $status['error'] = $update_info['error']; } // Store last check time $status['last_check'] = date('Y-m-d H:i:s'); return $status; } /** * Auto-update template if available * * @return bool Success */ public function autoUpdateTemplate() { $update_info = $this->checkTemplateUpdates(); if ($update_info['update_available'] && !empty($update_info['download_url'])) { return $this->downloadTemplateUpdate( $update_info['latest_version'], $update_info['download_url'] ); } return true; // No update needed } }