NEW FEATURES: ✅ PDF Generation Class (DeclarationTVA_PDF) ✅ Template Management System (Official + Custom) ✅ PDF Export Button in Declaration View ✅ Template Upload/Reset in Configuration ✅ Official CA-3 Template Support (10963*30) TECHNICAL IMPLEMENTATION: - core/class/declarationtva_pdf.class.php: Complete PDF generation system - templates/declarationtva/: Template storage directory - Template management in admin/setup_mvp.php - PDF export action in declarationtva_view.php - Bilingual support (French/English) TEMPLATE SYSTEM: - Built-in official template (10963*30) - Custom template upload capability - Template validation and version management - Fallback to official template if custom not available PDF FEATURES: - Professional CA-3 layout with all sections - Company information and declaration details - Complete CA-3 line data with amounts - Totals and calculations display - Downloadable PDF files This adds professional PDF export functionality to the CA-3 declaration system!
494 lines
18 KiB
PHP
494 lines
18 KiB
PHP
<?php
|
|
/**
|
|
* DeclarationTVA View Declaration
|
|
* French CA-3 VAT Declaration Module for Dolibarr
|
|
* MVP Version - Phase 1
|
|
*/
|
|
|
|
// Load Dolibarr environment
|
|
if (file_exists('../main.inc.php')) {
|
|
$res = @include '../main.inc.php';
|
|
} elseif (file_exists('../../main.inc.php')) {
|
|
$res = @include '../../main.inc.php';
|
|
} else {
|
|
$res = 0;
|
|
}
|
|
|
|
if (!$res) {
|
|
die("Include of main fails");
|
|
}
|
|
|
|
// Load module classes
|
|
require_once DOL_DOCUMENT_ROOT . '/custom/declarationtva/core/class/declarationtva.class.php';
|
|
require_once DOL_DOCUMENT_ROOT . '/custom/declarationtva/core/class/declarationtva_config.class.php';
|
|
require_once DOL_DOCUMENT_ROOT . '/custom/declarationtva/core/class/declarationtva_period.class.php';
|
|
|
|
// Access control
|
|
if (!$user->hasRight("declarationtva", "declarationtva", "read")) {
|
|
accessforbidden();
|
|
}
|
|
|
|
// Load language files
|
|
$langs->load("declarationtva@declarationtva");
|
|
|
|
// Get declaration ID
|
|
$id = GETPOST('id', 'int');
|
|
if (empty($id)) {
|
|
accessforbidden();
|
|
}
|
|
|
|
// Initialize objects
|
|
$declarationtva = new DeclarationTVA($db, $conf->entity);
|
|
$config = new DeclarationTVA_Config($db, $conf->entity);
|
|
$period = new DeclarationTVA_Period($db, $conf->entity);
|
|
|
|
// Handle actions
|
|
$action = GETPOST('action', 'alpha');
|
|
$token = GETPOST('token', 'alpha');
|
|
|
|
if ($action == 'recalculate' && $token) {
|
|
if ($declarationtva->recalculateCA3Amounts($id)) {
|
|
setEventMessages($langs->trans("DeclarationRecalculated"), null, 'mesgs');
|
|
} else {
|
|
setEventMessages($langs->trans("ErrorRecalculatingDeclaration"), null, 'errors');
|
|
}
|
|
}
|
|
|
|
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');
|
|
header("Location: declarationtvaindex.php");
|
|
exit;
|
|
}
|
|
|
|
// Use declaration's own dates
|
|
$start_date = $declarationtva->start_date;
|
|
$end_date = $declarationtva->end_date;
|
|
|
|
// Page title
|
|
$title = $langs->trans("ViewDeclaration") . ' - ' . $declarationtva->declaration_number;
|
|
llxHeader('', $title);
|
|
|
|
// Print page header
|
|
print load_fiche_titre($title, '', 'title_accountancy');
|
|
|
|
// Print declaration details
|
|
print '<div class="fiche">';
|
|
print '<div class="titre">' . $langs->trans("DeclarationDetails") . '</div>';
|
|
|
|
print '<table class="noborder centpercent">';
|
|
|
|
print '<tr>';
|
|
print '<td class="fieldrequired">' . $langs->trans("DeclarationNumber") . '</td>';
|
|
print '<td>' . $declarationtva->declaration_number . '</td>';
|
|
print '</tr>';
|
|
|
|
print '<tr>';
|
|
print '<td>' . $langs->trans("DeclarationName") . '</td>';
|
|
print '<td>' . $declarationtva->declaration_name . '</td>';
|
|
print '</tr>';
|
|
|
|
print '<tr>';
|
|
print '<td>' . $langs->trans("Period") . '</td>';
|
|
print '<td>' . dol_print_date($start_date, 'day') . ' - ' . dol_print_date($end_date, 'day') . '</td>';
|
|
print '</tr>';
|
|
|
|
print '<tr>';
|
|
print '<td>' . $langs->trans("Status") . '</td>';
|
|
print '<td>' . $langs->trans("Status" . ucfirst($declarationtva->status)) . '</td>';
|
|
print '</tr>';
|
|
|
|
print '<tr>';
|
|
print '<td>' . $langs->trans("CreatedDate") . '</td>';
|
|
print '<td>' . dol_print_date($declarationtva->created_date, 'dayhour') . '</td>';
|
|
print '</tr>';
|
|
|
|
|
|
print '</table>';
|
|
print '</div>';
|
|
|
|
// Print CA-3 amounts (placeholder for now)
|
|
print '<div class="fiche">';
|
|
print '<div class="titre">' . $langs->trans("CA3Amounts") . '</div>';
|
|
|
|
print '<table class="noborder centpercent">';
|
|
|
|
// Get actual CA-3 lines from database
|
|
$ca3_lines = $declarationtva->getCA3Lines($id);
|
|
|
|
// Get CA-3 line definitions for proper descriptions
|
|
$ca3_definitions = $config->getCA3LineDefinitions();
|
|
|
|
// Helper function to format amounts with original values in brackets
|
|
function formatAmountWithOriginal($amount, $line_label, $amount_type = 'vat') {
|
|
// If amount is zero, show empty field
|
|
if ($amount == 0) {
|
|
return '';
|
|
}
|
|
|
|
// Parse original amounts from line_label if they exist
|
|
if (strpos($line_label, '|ORIGINAL_') !== false) {
|
|
$parts = explode('|', $line_label);
|
|
$original_info = $parts[1] ?? '';
|
|
|
|
$pattern = $amount_type == 'base' ? '/ORIGINAL_BASE:([0-9.]+)/' : '/ORIGINAL_VAT:([0-9.]+)/';
|
|
if (preg_match($pattern, $original_info, $matches)) {
|
|
$original_amount = floatval($matches[1]);
|
|
if ($original_amount != $amount) {
|
|
return number_format($amount, 0) . ' (' . number_format($original_amount, 2) . ')';
|
|
}
|
|
}
|
|
}
|
|
return number_format($amount, 0);
|
|
}
|
|
|
|
// Helper function to format simple amounts (no original values)
|
|
function formatAmount($amount) {
|
|
// If amount is zero, show empty field
|
|
if ($amount == 0) {
|
|
return '';
|
|
}
|
|
return number_format($amount, 0);
|
|
}
|
|
|
|
// Create a lookup array for quick access
|
|
$ca3_data = array();
|
|
foreach ($ca3_lines as $line) {
|
|
$ca3_data[$line['ca3_line']] = $line;
|
|
}
|
|
|
|
// Section A: Opérations imposables
|
|
print '<tr class="liste_titre">';
|
|
print '<td colspan="4"><strong>A. ' . $langs->trans("CA3SectionA") . '</strong></td>';
|
|
print '</tr>';
|
|
|
|
print '<tr class="liste_titre">';
|
|
print '<th>' . $langs->trans("CA3Line") . '</th>';
|
|
print '<th colspan="2">' . $langs->trans("Description") . '</th>';
|
|
print '<th class="right">' . $langs->trans("Amount") . '</th>';
|
|
print '</tr>';
|
|
|
|
$section_a_lines = array('A1', 'A2', 'A3', 'A4', 'A5');
|
|
foreach ($section_a_lines as $line) {
|
|
$data = isset($ca3_data[$line]) ? $ca3_data[$line] : array('line_label' => '', 'vat_amount' => 0);
|
|
$description = isset($ca3_definitions[$line]) ? $ca3_definitions[$line]['label'] : $data['line_label'];
|
|
print '<tr>';
|
|
print '<td><a href="#" onclick="toggleDetails(\'' . $line . '\'); return false;" class="butAction">' . $line . '</a></td>';
|
|
print '<td colspan="2">' . $description . '</td>';
|
|
print '<td class="right">' . formatAmountWithOriginal($data['vat_amount'], $data['line_label']) . '</td>';
|
|
print '</tr>';
|
|
|
|
// Add dropdown row for account details
|
|
print '<tr id="details_' . $line . '" style="display: none;">';
|
|
print '<td colspan="4">';
|
|
print '<div class="account-details">';
|
|
print '<div class="account-details-header">' . $langs->trans("AccountBreakdown") . ' - ' . $line . '</div>';
|
|
print '<div class="account-details-content" id="content_' . $line . '">';
|
|
print '<div class="loading">' . $langs->trans("Loading") . '...</div>';
|
|
print '</div>';
|
|
print '</div>';
|
|
print '</td>';
|
|
print '</tr>';
|
|
}
|
|
|
|
// Section B: TVA due
|
|
print '<tr class="liste_titre">';
|
|
print '<td colspan="4"><strong>B. ' . $langs->trans("CA3SectionB") . '</strong></td>';
|
|
print '</tr>';
|
|
|
|
// Special header for lines 08, 09, 9B with base and VAT columns
|
|
print '<tr class="liste_titre">';
|
|
print '<td colspan="4"><strong>' . $langs->trans("BaseHTAndVATByRate") . '</strong></td>';
|
|
print '</tr>';
|
|
|
|
print '<tr class="liste_titre">';
|
|
print '<th>' . $langs->trans("CA3Line") . '</th>';
|
|
print '<th>' . $langs->trans("Description") . '</th>';
|
|
print '<th class="right">' . $langs->trans("BaseAmount") . '</th>';
|
|
print '<th class="right">' . $langs->trans("VATAmount") . '</th>';
|
|
print '</tr>';
|
|
|
|
$base_vat_lines = array('08', '09', '9B');
|
|
foreach ($base_vat_lines as $line) {
|
|
$data = isset($ca3_data[$line]) ? $ca3_data[$line] : array('line_label' => '', 'base_amount' => 0, 'vat_amount' => 0);
|
|
$description = isset($ca3_definitions[$line]) ? $ca3_definitions[$line]['label'] : $data['line_label'];
|
|
print '<tr>';
|
|
print '<td><a href="#" onclick="toggleDetails(\'' . $line . '\'); return false;" class="butAction">' . $line . '</a></td>';
|
|
print '<td>' . $description . '</td>';
|
|
print '<td class="right">' . formatAmountWithOriginal($data['base_amount'], $data['line_label'], 'base') . '</td>';
|
|
print '<td class="right">' . formatAmountWithOriginal($data['vat_amount'], $data['line_label']) . '</td>';
|
|
print '</tr>';
|
|
|
|
// Add dropdown row for account details
|
|
print '<tr id="details_' . $line . '" style="display: none;">';
|
|
print '<td colspan="4">';
|
|
print '<div class="account-details">';
|
|
print '<div class="account-details-header">' . $langs->trans("AccountBreakdown") . ' - ' . $line . '</div>';
|
|
print '<div class="account-details-content" id="content_' . $line . '">';
|
|
print '<div class="loading">' . $langs->trans("Loading") . '...</div>';
|
|
print '</div>';
|
|
print '</div>';
|
|
print '</td>';
|
|
print '</tr>';
|
|
}
|
|
|
|
// Line 16: Subtotal (calculated automatically)
|
|
$data = isset($ca3_data['16']) ? $ca3_data['16'] : array('line_label' => '', 'vat_amount' => 0);
|
|
print '<tr class="pair" style="background-color: #ffe6e6 !important;">';
|
|
print '<td style="background-color: #ffe6e6 !important;"><strong>16</strong></td>';
|
|
print '<td colspan="2" style="background-color: #ffe6e6 !important;"><strong>Sous-total TVA due (08 + 09 + 9B)</strong></td>';
|
|
print '<td class="right" style="background-color: #ffe6e6 !important;"><strong>' . formatAmount($data['vat_amount']) . '</strong></td>';
|
|
print '</tr>';
|
|
|
|
// Reset to normal layout for line 17
|
|
print '<tr class="liste_titre">';
|
|
print '<th>' . $langs->trans("CA3Line") . '</th>';
|
|
print '<th colspan="2">' . $langs->trans("Description") . '</th>';
|
|
print '<th class="right">' . $langs->trans("Amount") . '</th>';
|
|
print '</tr>';
|
|
|
|
$data = isset($ca3_data['17']) ? $ca3_data['17'] : array('line_label' => '', 'vat_amount' => 0);
|
|
$description = isset($ca3_definitions['17']) ? $ca3_definitions['17']['label'] : $data['line_label'];
|
|
print '<tr>';
|
|
print '<td><a href="#" onclick="toggleDetails(\'17\'); return false;" class="butAction">17</a></td>';
|
|
print '<td colspan="2">' . $description . '</td>';
|
|
print '<td class="right">' . formatAmountWithOriginal($data['vat_amount'], $data['line_label']) . '</td>';
|
|
print '</tr>';
|
|
|
|
// Add dropdown row for account details
|
|
print '<tr id="details_17" style="display: none;">';
|
|
print '<td colspan="4">';
|
|
print '<div class="account-details">';
|
|
print '<div class="account-details-header">' . $langs->trans("AccountBreakdown") . ' - 17</div>';
|
|
print '<div class="account-details-content" id="content_17">';
|
|
print '<div class="loading">' . $langs->trans("Loading") . '...</div>';
|
|
print '</div>';
|
|
print '</div>';
|
|
print '</td>';
|
|
print '</tr>';
|
|
|
|
// Section C: TVA déductible
|
|
print '<tr class="liste_titre">';
|
|
print '<td colspan="4"><strong>C. ' . $langs->trans("CA3SectionC") . '</strong></td>';
|
|
print '</tr>';
|
|
|
|
print '<tr class="liste_titre">';
|
|
print '<th>' . $langs->trans("CA3Line") . '</th>';
|
|
print '<th colspan="2">' . $langs->trans("Description") . '</th>';
|
|
print '<th class="right">' . $langs->trans("Amount") . '</th>';
|
|
print '</tr>';
|
|
|
|
$section_c_lines = array('20', '21', '22');
|
|
foreach ($section_c_lines as $line) {
|
|
$data = isset($ca3_data[$line]) ? $ca3_data[$line] : array('line_label' => '', 'vat_amount' => 0);
|
|
$description = isset($ca3_definitions[$line]) ? $ca3_definitions[$line]['label'] : $data['line_label'];
|
|
print '<tr>';
|
|
print '<td><a href="#" onclick="toggleDetails(\'' . $line . '\'); return false;" class="butAction">' . $line . '</a></td>';
|
|
print '<td colspan="2">' . $description . '</td>';
|
|
print '<td class="right">' . formatAmountWithOriginal($data['vat_amount'], $data['line_label']) . '</td>';
|
|
print '</tr>';
|
|
|
|
// Add dropdown row for account details
|
|
print '<tr id="details_' . $line . '" style="display: none;">';
|
|
print '<td colspan="4">';
|
|
print '<div class="account-details">';
|
|
print '<div class="account-details-header">' . $langs->trans("AccountBreakdown") . ' - ' . $line . '</div>';
|
|
print '<div class="account-details-content" id="content_' . $line . '">';
|
|
print '<div class="loading">' . $langs->trans("Loading") . '...</div>';
|
|
print '</div>';
|
|
print '</div>';
|
|
print '</td>';
|
|
print '</tr>';
|
|
}
|
|
|
|
// Line 23: Subtotal (calculated automatically)
|
|
$data = isset($ca3_data['23']) ? $ca3_data['23'] : array('line_label' => '', 'vat_amount' => 0);
|
|
print '<tr class="pair" style="background-color: #ffe6e6 !important;">';
|
|
print '<td style="background-color: #ffe6e6 !important;"><strong>23</strong></td>';
|
|
print '<td colspan="2" style="background-color: #ffe6e6 !important;"><strong>Sous-total TVA déductible (20 + 21 + 22)</strong></td>';
|
|
print '<td class="right" style="background-color: #ffe6e6 !important;"><strong>' . formatAmount($data['vat_amount']) . '</strong></td>';
|
|
print '</tr>';
|
|
|
|
// Section D: Résultat
|
|
print '<tr class="liste_titre">';
|
|
print '<td colspan="4"><strong>D. ' . $langs->trans("CA3SectionD") . '</strong></td>';
|
|
print '</tr>';
|
|
|
|
print '<tr class="liste_titre">';
|
|
print '<th>' . $langs->trans("CA3Line") . '</th>';
|
|
print '<th colspan="2">' . $langs->trans("Description") . '</th>';
|
|
print '<th class="right">' . $langs->trans("Amount") . '</th>';
|
|
print '</tr>';
|
|
|
|
$section_d_lines = array('25', '26', 'TD', '28', '29');
|
|
foreach ($section_d_lines as $line) {
|
|
$data = isset($ca3_data[$line]) ? $ca3_data[$line] : array('line_label' => '', 'vat_amount' => 0);
|
|
|
|
// Special handling for line TD
|
|
if ($line == 'TD') {
|
|
$description = 'TVA due (montant à payer)';
|
|
} else {
|
|
$description = isset($ca3_definitions[$line]) ? $ca3_definitions[$line]['label'] : $data['line_label'];
|
|
}
|
|
|
|
print '<tr style="background-color: #ffe6e6 !important;">';
|
|
print '<td style="background-color: #ffe6e6 !important;"><strong>' . $line . '</strong></td>';
|
|
print '<td colspan="2" style="background-color: #ffe6e6 !important;">' . $description . '</td>';
|
|
print '<td class="right" style="background-color: #ffe6e6 !important;">' . formatAmount($data['vat_amount']) . '</td>';
|
|
print '</tr>';
|
|
}
|
|
|
|
// Show message if no data
|
|
if (empty($ca3_lines)) {
|
|
print '<tr>';
|
|
print '<td colspan="3" class="center">' . $langs->trans("NoCA3Data") . '</td>';
|
|
print '</tr>';
|
|
}
|
|
|
|
print '</table>';
|
|
print '</div>';
|
|
|
|
// Print actions
|
|
print '<div class="fiche">';
|
|
print '<div class="titre">' . $langs->trans("Actions") . '</div>';
|
|
|
|
print '<div class="center">';
|
|
|
|
// Recalculate button (always available)
|
|
print '<a href="' . $_SERVER['PHP_SELF'] . '?id=' . $id . '&action=recalculate&token=' . newToken() . '" class="butAction">' . $langs->trans("Recalculate") . '</a> ';
|
|
|
|
// PDF Export button (always available)
|
|
print '<a href="' . $_SERVER['PHP_SELF'] . '?id=' . $id . '&action=export_pdf" class="butAction">' . $langs->trans("ExportPDF") . '</a> ';
|
|
|
|
if ($declarationtva->status == 'draft') {
|
|
print '<a href="' . $_SERVER['PHP_SELF'] . '?id=' . $id . '&action=validate" class="butAction">' . $langs->trans("Validate") . '</a> ';
|
|
} elseif ($declarationtva->status == 'validated') {
|
|
print '<a href="' . $_SERVER['PHP_SELF'] . '?id=' . $id . '&action=submit" class="butAction">' . $langs->trans("Submit") . '</a> ';
|
|
}
|
|
|
|
print '<a href="declarationtvaindex.php" class="butAction">' . $langs->trans("BackToList") . '</a>';
|
|
|
|
print '</div>';
|
|
print '</div>';
|
|
|
|
// Add CSS for dropdown styling
|
|
print '<style>
|
|
.account-details {
|
|
background-color: #f8f9fa;
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 4px;
|
|
margin: 10px 0;
|
|
padding: 15px;
|
|
}
|
|
|
|
.account-details-header {
|
|
font-weight: bold;
|
|
color: #495057;
|
|
margin-bottom: 10px;
|
|
padding-bottom: 5px;
|
|
border-bottom: 1px solid #dee2e6;
|
|
}
|
|
|
|
.account-details-content {
|
|
font-size: 0.9em;
|
|
}
|
|
|
|
.account-details table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.account-details table th,
|
|
.account-details table td {
|
|
padding: 8px;
|
|
text-align: left;
|
|
border: 1px solid #dee2e6;
|
|
}
|
|
|
|
.account-details table th {
|
|
background-color: #e9ecef;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.account-details table tr:nth-child(even) {
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
.loading {
|
|
text-align: center;
|
|
color: #6c757d;
|
|
font-style: italic;
|
|
}
|
|
</style>';
|
|
|
|
// Add JavaScript for dropdown functionality
|
|
print '<script>
|
|
var loadedDetails = {};
|
|
|
|
function toggleDetails(line) {
|
|
var detailsRow = document.getElementById("details_" + line);
|
|
var contentDiv = document.getElementById("content_" + line);
|
|
|
|
if (detailsRow.style.display === "none") {
|
|
// Show the dropdown
|
|
detailsRow.style.display = "";
|
|
|
|
// Load content if not already loaded
|
|
if (!loadedDetails[line]) {
|
|
loadLineDetails(line);
|
|
}
|
|
} else {
|
|
// Hide the dropdown
|
|
detailsRow.style.display = "none";
|
|
}
|
|
}
|
|
|
|
function loadLineDetails(line) {
|
|
var contentDiv = document.getElementById("content_" + line);
|
|
var declarationId = ' . $declarationtva->rowid . ';
|
|
|
|
// Show loading
|
|
contentDiv.innerHTML = "<div class=\"loading\">' . $langs->trans("Loading") . '...</div>";
|
|
|
|
// Make AJAX request to get line details
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.open("GET", "declarationtva_line_details_ajax.php?declaration_id=" + declarationId + "&ca3_line=" + line, true);
|
|
xhr.onreadystatechange = function() {
|
|
if (xhr.readyState === 4 && xhr.status === 200) {
|
|
contentDiv.innerHTML = xhr.responseText;
|
|
loadedDetails[line] = true;
|
|
} else if (xhr.readyState === 4) {
|
|
contentDiv.innerHTML = "<div class=\"error\">' . $langs->trans("ErrorLoadingDetails") . '</div>";
|
|
}
|
|
};
|
|
xhr.send();
|
|
}
|
|
</script>';
|
|
|
|
// Print footer
|
|
llxFooter();
|
|
?>
|