DeclarationTVA/admin/setup_mvp.php
Frank Cools 0d27113489 🔄 Add Gitea-Based Template Auto-Update System
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!
2025-10-03 00:17:59 +02:00

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();
?>