NEW FEATURES: ✅ Gitea API Integration for Template Management ✅ Automatic Template Update Checking ✅ Version Control and Manifest System ✅ One-Click Template Updates in Configuration ✅ Professional Template Management Workflow TECHNICAL IMPLEMENTATION: - Enhanced DeclarationTVA_PDF class with auto-update methods - Template manifest system (templates/manifest.json) - Gitea API integration for template downloads - Update status display in configuration page - Version comparison and automatic updates TEMPLATE MANAGEMENT: - Current version: 30 (10963*30) - Gitea repository: https://git.covago.com/frank/DeclarationTVA - Manifest URL: templates/manifest.json - Auto-update on module activation - Fallback to built-in template MAINTENANCE WORKFLOW: 1. Tax authority updates form → Create fillable version (5 min) 2. Update manifest.json with new version (1 min) 3. Upload template to Gitea (2 min) 4. Users get automatic updates (0 min for maintainer) This creates a professional, self-hosted template management system with minimal maintenance overhead!
384 lines
14 KiB
PHP
384 lines
14 KiB
PHP
<?php
|
|
/**
|
|
* MVP Setup page for DeclarationTVA module
|
|
* Advanced multi-select PCG account mapping using Dolibarr native style
|
|
*/
|
|
|
|
// 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");
|
|
}
|
|
|
|
// Libraries
|
|
require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php";
|
|
require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
|
|
require_once DOL_DOCUMENT_ROOT . '/custom/declarationtva/core/class/declarationtva_config.class.php';
|
|
|
|
// Access control
|
|
if (!$user->hasRight("declarationtva", "declarationtva", "admin")) {
|
|
accessforbidden();
|
|
}
|
|
|
|
// Load language files
|
|
$langs->load("declarationtva@declarationtva");
|
|
|
|
// Initialize objects
|
|
$config = new DeclarationTVA_Config($db, $conf->entity);
|
|
$form = new Form($db);
|
|
|
|
// Handle form submission
|
|
$action = GETPOST('action', 'alpha');
|
|
if ($action == 'update_mappings') {
|
|
$ca3_definitions = $config->getCA3LineDefinitions();
|
|
$updated_count = 0;
|
|
|
|
|
|
foreach ($ca3_definitions as $line => $definition) {
|
|
// Special handling for lines 08, 09, 9B (need both base and VAT accounts)
|
|
if (in_array($line, array('08', '09', '9B'))) {
|
|
$base_account_codes = GETPOST('base_account_codes_' . $line, 'array');
|
|
$vat_account_codes = GETPOST('vat_account_codes_' . $line, 'array');
|
|
|
|
// Process base accounts
|
|
if (isset($_POST['base_account_codes_' . $line])) {
|
|
$result = $config->updateAccountMapping($line . '_BASE', $base_account_codes);
|
|
if ($result) {
|
|
$updated_count++;
|
|
}
|
|
}
|
|
|
|
// Process VAT accounts
|
|
if (isset($_POST['vat_account_codes_' . $line])) {
|
|
$result = $config->updateAccountMapping($line . '_VAT', $vat_account_codes);
|
|
if ($result) {
|
|
$updated_count++;
|
|
}
|
|
}
|
|
} else {
|
|
// Normal processing for other lines
|
|
$account_codes = GETPOST('account_codes_' . $line, 'array');
|
|
|
|
if (isset($_POST['account_codes_' . $line])) {
|
|
$result = $config->updateAccountMapping($line, $account_codes);
|
|
if ($result) {
|
|
$updated_count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($updated_count > 0) {
|
|
setEventMessages($langs->trans("ConfigurationUpdated"), null, 'mesgs');
|
|
} else {
|
|
setEventMessages($langs->trans("NoChangesDetected"), null, 'warnings');
|
|
}
|
|
}
|
|
|
|
// Get current mappings
|
|
$mappings_by_line = $config->getAccountMappingsByLine();
|
|
$accounts = $config->getAccountingAccounts();
|
|
$ca3_definitions = $config->getCA3LineDefinitions();
|
|
|
|
// Ensure table exists (create if missing)
|
|
$table_name = MAIN_DB_PREFIX . "declarationtva_account_mappings";
|
|
$check_table_sql = "SHOW TABLES LIKE '" . $table_name . "'";
|
|
$table_exists = $db->query($check_table_sql);
|
|
if (!$table_exists || $db->num_rows($table_exists) == 0) {
|
|
// Create the table if it doesn't exist
|
|
$create_table_sql = "CREATE TABLE IF NOT EXISTS `" . $table_name . "` (
|
|
`rowid` int(11) NOT NULL AUTO_INCREMENT,
|
|
`entity` int(11) NOT NULL DEFAULT 1,
|
|
`ca3_line` varchar(8) NOT NULL COMMENT 'A1, A2, A3, A4, A5, 08, 09, 9B, 17, 20, 21, 22, 25, 26, 28, 29',
|
|
`account_code` varchar(32) NOT NULL COMMENT 'PCG account code',
|
|
`account_label` varchar(255) DEFAULT NULL,
|
|
`vat_rate` decimal(5,2) DEFAULT NULL,
|
|
`is_active` tinyint(1) DEFAULT 1,
|
|
`created_date` datetime DEFAULT NULL,
|
|
PRIMARY KEY (`rowid`),
|
|
UNIQUE KEY `uk_mapping_entity_line_account` (`entity`, `ca3_line`, `account_code`),
|
|
KEY `idx_ca3_line` (`ca3_line`),
|
|
KEY `idx_account_code` (`account_code`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci";
|
|
|
|
$db->query($create_table_sql);
|
|
}
|
|
$section_headers = $config->getCA3SectionHeaders();
|
|
|
|
// Page title
|
|
$title = $langs->trans("DeclarationTVASetup");
|
|
llxHeader('', $title);
|
|
|
|
// Print page header
|
|
print load_fiche_titre($title, '', 'title_accountancy');
|
|
|
|
// Print notice information
|
|
print '<div class="info">';
|
|
print '<strong>Notice 4722 - Summary Table CA3 (3310-CA3-SD)</strong><br>';
|
|
print 'Configuration basée sur la structure officielle la plus récente du formulaire CA-3.';
|
|
print '</div><br>';
|
|
|
|
// Print configuration form
|
|
print '<form method="POST" action="' . $_SERVER['PHP_SELF'] . '">';
|
|
print '<input type="hidden" name="action" value="update_mappings">';
|
|
print '<input type="hidden" name="token" value="' . newToken() . '">';
|
|
|
|
print '<div class="fiche">';
|
|
print '<div class="titre">' . $langs->trans("DeclarationTVAPCGMapping") . '</div>';
|
|
|
|
// Group CA-3 lines by section
|
|
$lines_by_section = array();
|
|
foreach ($ca3_definitions as $line => $definition) {
|
|
$section = $definition['section'];
|
|
if (!isset($lines_by_section[$section])) {
|
|
$lines_by_section[$section] = array();
|
|
}
|
|
$lines_by_section[$section][$line] = $definition;
|
|
}
|
|
|
|
// Print each section
|
|
foreach ($lines_by_section as $section_code => $lines) {
|
|
$section_info = $section_headers[$section_code];
|
|
|
|
// Section header
|
|
print '<div class="titre">' . $section_info['title'] . '</div>';
|
|
print '<div class="info">' . $section_info['description'] . '</div>';
|
|
if (isset($section_info['notice'])) {
|
|
print '<div class="info"><strong>Référence:</strong> ' . $section_info['notice'] . '</div>';
|
|
}
|
|
|
|
// Skip D-section lines (25, 26, 28, 29) as they are calculated
|
|
if ($section_code == 'D') {
|
|
print '<div class="info"><strong>Note:</strong> Les lignes de la section D sont calculées automatiquement à partir des autres sections.</div>';
|
|
continue;
|
|
}
|
|
|
|
print '<table class="noborder centpercent">';
|
|
print '<tr class="liste_titre">';
|
|
print '<th>' . $langs->trans("CA3Line") . '</th>';
|
|
print '<th>' . $langs->trans("LineLabel") . '</th>';
|
|
print '<th>' . $langs->trans("Description") . '</th>';
|
|
print '<th>' . $langs->trans("PCGAccounts") . '</th>';
|
|
print '<th>' . $langs->trans("AccountSelection") . '</th>';
|
|
print '</tr>';
|
|
|
|
foreach ($lines as $line => $definition) {
|
|
print '<tr>';
|
|
print '<td><strong>' . $line . '</strong></td>';
|
|
print '<td>' . $definition['label'] . '</td>';
|
|
print '<td><small>' . $definition['description'] . '</small></td>';
|
|
print '<td><small>' . $definition['pcg_accounts'] . '</small></td>';
|
|
print '<td>';
|
|
|
|
// Create account options array for Dolibarr multi-select
|
|
$account_options = array();
|
|
foreach ($accounts as $account) {
|
|
$account_options[$account['account_number']] = $account['account_number'] . ' - ' . $account['label'];
|
|
}
|
|
|
|
// Special handling for lines 08, 09, 9B (need both base and VAT accounts)
|
|
if (in_array($line, array('08', '09', '9B'))) {
|
|
// Load separate mappings for base and VAT
|
|
$base_selected_accounts = isset($mappings_by_line[$line . '_BASE']) ? $mappings_by_line[$line . '_BASE'] : array();
|
|
$vat_selected_accounts = isset($mappings_by_line[$line . '_VAT']) ? $mappings_by_line[$line . '_VAT'] : array();
|
|
|
|
print '<div style="margin-bottom: 10px;">';
|
|
print '<strong>Comptes de base (ventes):</strong><br>';
|
|
print $form->multiselectarray('base_account_codes_' . $line, $account_options, $base_selected_accounts, 0, 0, '', 0, '200px');
|
|
print '</div>';
|
|
print '<div>';
|
|
print '<strong>Comptes de TVA:</strong><br>';
|
|
print $form->multiselectarray('vat_account_codes_' . $line, $account_options, $vat_selected_accounts, 0, 0, '', 0, '200px');
|
|
print '</div>';
|
|
} else {
|
|
// Normal single selection for other lines
|
|
$selected_accounts = isset($mappings_by_line[$line]) ? $mappings_by_line[$line] : array();
|
|
print $form->multiselectarray('account_codes_' . $line, $account_options, $selected_accounts, 0, 0, '', 0, '200px');
|
|
}
|
|
print '</td>';
|
|
print '</tr>';
|
|
}
|
|
|
|
print '</table>';
|
|
print '<br>';
|
|
}
|
|
|
|
print '<div class="titre">' . $langs->trans("Actions") . '</div>';
|
|
print '<input type="submit" class="button" value="' . $langs->trans("UpdateConfiguration") . '">';
|
|
print '</div>';
|
|
|
|
print '</form>';
|
|
|
|
// Print current configuration summary
|
|
print '<div class="fiche">';
|
|
print '<div class="titre">' . $langs->trans("CurrentConfiguration") . '</div>';
|
|
|
|
if (empty($mappings_by_line)) {
|
|
print '<div class="info">' . $langs->trans("NoConfigurationFound") . '</div>';
|
|
} else {
|
|
// Group by section for display
|
|
$mappings_by_section = array();
|
|
foreach ($mappings_by_line as $line => $account_codes) {
|
|
if (isset($ca3_definitions[$line])) {
|
|
$section = $ca3_definitions[$line]['section'];
|
|
if (!isset($mappings_by_section[$section])) {
|
|
$mappings_by_section[$section] = array();
|
|
}
|
|
$mappings_by_section[$section][$line] = $account_codes;
|
|
}
|
|
}
|
|
|
|
foreach ($mappings_by_section as $section_code => $section_mappings) {
|
|
$section_info = $section_headers[$section_code];
|
|
|
|
print '<div class="titre">' . $section_info['title'] . '</div>';
|
|
|
|
print '<table class="noborder centpercent">';
|
|
print '<tr class="liste_titre">';
|
|
print '<th>' . $langs->trans("CA3Line") . '</th>';
|
|
print '<th>' . $langs->trans("SelectedAccounts") . '</th>';
|
|
print '<th>' . $langs->trans("AccountCount") . '</th>';
|
|
print '</tr>';
|
|
|
|
foreach ($section_mappings as $line => $account_codes) {
|
|
print '<tr>';
|
|
print '<td><strong>' . $line . '</strong></td>';
|
|
print '<td>' . implode(', ', $account_codes) . '</td>';
|
|
print '<td>' . count($account_codes) . '</td>';
|
|
print '</tr>';
|
|
}
|
|
print '</table>';
|
|
print '<br>';
|
|
}
|
|
}
|
|
|
|
// Template Management Section
|
|
print '<div class="fichecenter">';
|
|
print '<div class="fichehalfleft">';
|
|
|
|
// 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');
|
|
}
|
|
}
|
|
|
|
// Handle template update
|
|
if ($action == 'update_template') {
|
|
if ($pdf_generator->autoUpdateTemplate()) {
|
|
setEventMessages($langs->trans("TemplateUpdated"), null, 'mesgs');
|
|
} else {
|
|
setEventMessages($pdf_generator->error ?: $langs->trans("TemplateUpdateFailed"), null, 'errors');
|
|
}
|
|
}
|
|
|
|
// Get template information
|
|
$template_info = $pdf_generator->getTemplateInfo();
|
|
|
|
// Get update status
|
|
$update_status = $pdf_generator->getTemplateUpdateStatus();
|
|
|
|
print '<form name="template_form" method="POST" enctype="multipart/form-data">';
|
|
print '<input type="hidden" name="token" value="' . newToken() . '">';
|
|
print '<input type="hidden" name="action" value="upload_template">';
|
|
|
|
print '<table class="noborder centpercent">';
|
|
print '<tr class="liste_titre">';
|
|
print '<td colspan="2"><strong>Gestion des modèles PDF CA-3</strong></td>';
|
|
print '</tr>';
|
|
|
|
print '<tr>';
|
|
print '<td><strong>Modèle actuel</strong></td>';
|
|
print '<td>';
|
|
if ($template_info['custom_template']) {
|
|
print '<span class="badge badge-status4">Modèle personnalisé</span>';
|
|
} else {
|
|
print '<span class="badge badge-status1">Modèle officiel</span>';
|
|
}
|
|
print '</td>';
|
|
print '</tr>';
|
|
|
|
print '<tr>';
|
|
print '<td><strong>Version actuelle</strong></td>';
|
|
print '<td>' . $template_info['official_number'] . '</td>';
|
|
print '</tr>';
|
|
|
|
// Update status
|
|
if ($update_status['update_available']) {
|
|
print '<tr class="warning">';
|
|
print '<td><strong>Mise à jour disponible</strong></td>';
|
|
print '<td>';
|
|
print '<span class="badge badge-status4">Version ' . $update_status['latest_version'] . ' disponible</span>';
|
|
print ' <a href="' . $_SERVER['PHP_SELF'] . '?action=update_template&token=' . newToken() . '" class="button button-save">Mettre à jour</a>';
|
|
print '</td>';
|
|
print '</tr>';
|
|
} else {
|
|
print '<tr>';
|
|
print '<td><strong>Statut</strong></td>';
|
|
print '<td><span class="badge badge-status1">À jour</span></td>';
|
|
print '</tr>';
|
|
}
|
|
|
|
if (!empty($update_status['error'])) {
|
|
print '<tr class="error">';
|
|
print '<td><strong>Erreur</strong></td>';
|
|
print '<td>' . $update_status['error'] . '</td>';
|
|
print '</tr>';
|
|
}
|
|
|
|
print '<tr>';
|
|
print '<td><strong>Nouveau modèle</strong></td>';
|
|
print '<td>';
|
|
print '<input type="file" name="template_file" accept=".pdf" required>';
|
|
print '<br><small>Format PDF uniquement, taille max 10MB</small>';
|
|
print '</td>';
|
|
print '</tr>';
|
|
|
|
print '<tr>';
|
|
print '<td colspan="2" class="center">';
|
|
print '<input type="submit" class="button" value="Télécharger le modèle">';
|
|
print '</td>';
|
|
print '</tr>';
|
|
|
|
if ($template_info['custom_template']) {
|
|
print '<tr>';
|
|
print '<td colspan="2" class="center">';
|
|
print '<a href="' . $_SERVER['PHP_SELF'] . '?action=reset_template&token=' . newToken() . '" class="button button-delete">Revenir au modèle officiel</a>';
|
|
print '</td>';
|
|
print '</tr>';
|
|
}
|
|
|
|
print '</table>';
|
|
print '</form>';
|
|
|
|
print '</div>';
|
|
print '</div>';
|
|
|
|
print '</div>';
|
|
|
|
// Print footer
|
|
llxFooter();
|
|
?>
|