diff --git a/admin/setup_mvp.php b/admin/setup_mvp.php index d7c59ae..3421f0e 100644 --- a/admin/setup_mvp.php +++ b/admin/setup_mvp.php @@ -156,9 +156,6 @@ foreach ($lines_by_section as $section_code => $lines) { // Use Dolibarr's native multi-select with search functionality print $form->multiselectarray('account_codes_' . $line, $account_options, $selected_accounts, 0, 0, '', 0, '200px'); - - // Add helper text - print '
' . $langs->trans("MultiSelectHelp") . ''; print ''; print ''; } diff --git a/core/class/declarationtva_config.class.php b/core/class/declarationtva_config.class.php index e69de29..583ae01 100644 --- a/core/class/declarationtva_config.class.php +++ b/core/class/declarationtva_config.class.php @@ -0,0 +1,425 @@ +db = $db; + $this->entity = $entity; + } + + /** + * Get configuration value + * + * @param string $key Configuration key + * @param mixed $default Default value + * @return mixed Configuration value + */ + public function get($key, $default = null) + { + $sql = "SELECT config_value FROM " . MAIN_DB_PREFIX . "declarationtva_config + WHERE entity = " . $this->entity . " AND config_key = '" . $this->db->escape($key) . "'"; + + $result = $this->db->query($sql); + if ($result && $this->db->num_rows($result) > 0) { + $obj = $this->db->fetch_object($result); + return $obj->config_value; + } + + return $default; + } + + /** + * Set configuration value + * + * @param string $key Configuration key + * @param mixed $value Configuration value + * @return bool Success + */ + public function set($key, $value) + { + $sql = "INSERT INTO " . MAIN_DB_PREFIX . "declarationtva_config + (entity, config_key, config_value, created_date) + VALUES (" . $this->entity . ", '" . $this->db->escape($key) . "', + '" . $this->db->escape($value) . "', NOW()) + ON DUPLICATE KEY UPDATE config_value = '" . $this->db->escape($value) . "'"; + + $result = $this->db->query($sql); + return $result !== false; + } + + /** + * Get account mapping for a CA-3 line + * + * @param string $ca3_line CA-3 line code + * @return array|false Account mapping or false if not found + */ + public function getAccountMapping($ca3_line) + { + $sql = "SELECT * FROM " . MAIN_DB_PREFIX . "declarationtva_account_mappings + WHERE entity = " . $this->entity . " AND ca3_line = '" . $this->db->escape($ca3_line) . "'"; + + $result = $this->db->query($sql); + if ($result && $this->db->num_rows($result) > 0) { + return $this->db->fetch_array($result); + } + + return false; + } + + /** + * Update account mapping for multiple accounts + * + * @param string $ca3_line CA-3 line code + * @param array $account_codes Array of account codes + * @return bool Success + */ + public function updateAccountMapping($ca3_line, $account_codes) + { + // First, deactivate all existing mappings for this CA-3 line + $sql = "UPDATE " . MAIN_DB_PREFIX . "declarationtva_account_mappings + SET is_active = 0 + WHERE entity = " . $this->entity . " AND ca3_line = '" . $this->db->escape($ca3_line) . "'"; + $this->db->query($sql); + + // Then insert/activate new mappings + if (!empty($account_codes)) { + foreach ($account_codes as $account_code) { + if (!empty($account_code)) { + // Check if mapping already exists + $check_sql = "SELECT id FROM " . MAIN_DB_PREFIX . "declarationtva_account_mappings + WHERE entity = " . $this->entity . " + AND ca3_line = '" . $this->db->escape($ca3_line) . "' + AND account_code = '" . $this->db->escape($account_code) . "'"; + $check_result = $this->db->query($check_sql); + + if ($check_result && $this->db->num_rows($check_result) > 0) { + // Update existing mapping + $sql = "UPDATE " . MAIN_DB_PREFIX . "declarationtva_account_mappings + SET is_active = 1 + WHERE entity = " . $this->entity . " + AND ca3_line = '" . $this->db->escape($ca3_line) . "' + AND account_code = '" . $this->db->escape($account_code) . "'"; + $this->db->query($sql); + } else { + // Insert new mapping + $sql = "INSERT INTO " . MAIN_DB_PREFIX . "declarationtva_account_mappings + (entity, ca3_line, account_code, account_label, vat_rate, is_active, created_date) + VALUES (" . $this->entity . ", '" . $this->db->escape($ca3_line) . "', + '" . $this->db->escape($account_code) . "', '', 0, 1, NOW())"; + $this->db->query($sql); + } + } + } + } + + return true; + } + + /** + * Get all account mappings + * + * @return array Account mappings + */ + public function getAllAccountMappings() + { + $sql = "SELECT * FROM " . MAIN_DB_PREFIX . "declarationtva_account_mappings + WHERE entity = " . $this->entity . " AND is_active = 1 + ORDER BY ca3_line"; + + $result = $this->db->query($sql); + $mappings = array(); + + if ($result) { + while ($obj = $this->db->fetch_object($result)) { + $mappings[] = array( + 'rowid' => $obj->rowid, + 'ca3_line' => $obj->ca3_line, + 'account_code' => $obj->account_code, + 'account_label' => $obj->account_label, + 'vat_rate' => $obj->vat_rate, + 'is_active' => $obj->is_active + ); + } + } + + return $mappings; + } + + /** + * Get account mappings grouped by CA-3 line + * + * @return array Account mappings grouped by CA-3 line + */ + public function getAccountMappingsByLine() + { + $sql = "SELECT ca3_line, account_code FROM " . MAIN_DB_PREFIX . "declarationtva_account_mappings + WHERE entity = " . $this->entity . " AND is_active = 1 + ORDER BY ca3_line, account_code"; + + $result = $this->db->query($sql); + $mappings = array(); + + if ($result) { + while ($obj = $this->db->fetch_object($result)) { + if (!isset($mappings[$obj->ca3_line])) { + $mappings[$obj->ca3_line] = array(); + } + $mappings[$obj->ca3_line][] = $obj->account_code; + } + } + + return $mappings; + } + + /** + * Get available accounting accounts from Dolibarr + * + * @return array Accounting accounts + */ + public function getAccountingAccounts() + { + $sql = "SELECT account_number, label FROM " . MAIN_DB_PREFIX . "accounting_account + WHERE active = 1 + ORDER BY account_number"; + + $result = $this->db->query($sql); + $accounts = array(); + + if ($result) { + while ($obj = $this->db->fetch_object($result)) { + $accounts[] = array( + 'account_number' => $obj->account_number, + 'label' => $obj->label + ); + } + } + + return $accounts; + } + + /** + * Get CA-3 line definitions (Notice 4722 - Latest Official Structure) + * + * @return array CA-3 line definitions + */ + public function getCA3LineDefinitions() + { + return array( + // A. Opérations imposables (Taxable Operations) - Notice 4722 + 'A1' => array( + 'label' => 'Montant HT des opérations imposables normales (biens + services imposables en France à 20%, 10%, 5,5%, 2,1%)', + 'type' => 'base', + 'section' => 'A', + 'description' => 'HT amount of all taxable operations that form normal sales (goods + services taxable in France at 20%, 10%, 5,5%, 2,1%)', + 'pcg_accounts' => 'Sales: 7xxxx; VAT: 44571x / 44572x / 44573x / 44574x' + ), + 'A2' => array( + 'label' => 'Montant HT des opérations imposables spéciales ne relevant pas du CA courant (cessions d\'immobilisations, livraisons à soi-même)', + 'type' => 'base', + 'section' => 'A', + 'description' => 'HT amount of special taxable operations not part of ordinary turnover (e.g. cessions d\'immobilisations, livraisons à soi-même)', + 'pcg_accounts' => '775xxx (gains on disposal), 72xxx (production immobilisée), VAT: 44571x' + ), + 'A3' => array( + 'label' => 'Montant HT des services achetés à des prestataires non établis en France mais imposables en France (autoliquidation services étrangers)', + 'type' => 'base', + 'section' => 'A', + 'description' => 'HT amount of services purchased from providers not established in France but taxable in France (reverse charge for foreign services)', + 'pcg_accounts' => '6xxxx (services purchased), 4452xxx (TVA due autoliquidée)' + ), + 'A4' => array( + 'label' => 'Montant HT des importations imposables en France (hors UE), à l\'exclusion des produits pétroliers', + 'type' => 'base', + 'section' => 'A', + 'description' => 'HT amount of imports taxable in France (non-EU), excluding petroleum products', + 'pcg_accounts' => '6xxxx / 2xxxx purchases or assets; VAT: 4452xxx (import VAT)' + ), + 'A5' => array( + 'label' => 'Montant HT des opérations imposables à la sortie d\'un régime fiscal suspensif (régimes douaniers, zones franches, etc.)', + 'type' => 'base', + 'section' => 'A', + 'description' => 'HT amount of taxable operations at exit from a suspensive fiscal regime (customs regimes, free zones, etc.)', + 'pcg_accounts' => 'Specific 6xxxx/2xxxx depending on goods; VAT: 4452xxx' + ), + + // B. Décompte de la TVA due (VAT Due Calculation) - Notice 4722 + '08' => array( + 'label' => 'TVA due au taux de 20%', + 'type' => 'vat', + 'section' => 'B', + 'description' => 'VAT amounts due, calculated on A1/A2 bases at 20% rate', + 'pcg_accounts' => '44571x (TVA collectée à 20%)' + ), + '09' => array( + 'label' => 'TVA due au taux de 10%', + 'type' => 'vat', + 'section' => 'B', + 'description' => 'VAT amounts due, calculated on A1/A2 bases at 10% rate', + 'pcg_accounts' => '44572x (TVA collectée à 10%)' + ), + '9B' => array( + 'label' => 'TVA due aux taux réduits (5,5% et 2,1%)', + 'type' => 'vat', + 'section' => 'B', + 'description' => 'VAT amounts due, calculated on A1/A2 bases at reduced rates (5,5% and 2,1%)', + 'pcg_accounts' => '44573x (5,5%) / 44574x (2,1%) (TVA collectée aux taux réduits)' + ), + '17' => array( + 'label' => 'TVA due au titre des acquisitions intracommunautaires (autoliquidation)', + 'type' => 'vat', + 'section' => 'B', + 'description' => 'VAT due on intra-EU acquisitions (autoliquidation)', + 'pcg_accounts' => '4452xxx (TVA due intracommunautaire)' + ), + + // C. Décompte de la TVA déductible (Deductible VAT Calculation) - Notice 4722 + '20' => array( + 'label' => 'TVA déductible sur immobilisations', + 'type' => 'vat', + 'section' => 'C', + 'description' => 'Deductible VAT on capital goods (fixed assets)', + 'pcg_accounts' => '44562x (TVA déductible sur immobilisations)' + ), + '21' => array( + 'label' => 'TVA déductible sur autres biens et services', + 'type' => 'vat', + 'section' => 'C', + 'description' => 'Deductible VAT on other goods and services (operating expenses)', + 'pcg_accounts' => '44566x (TVA déductible sur autres biens et services)' + ), + '22' => array( + 'label' => 'TVA déductible sur importations', + 'type' => 'vat', + 'section' => 'C', + 'description' => 'Deductible VAT on imports', + 'pcg_accounts' => '44566x / 44562x (TVA déductible sur importations)' + ), + + // D. Résultat (Result) - Notice 4722 + '25' => array( + 'label' => 'TVA brute due (Total TVA due)', + 'type' => 'vat', + 'section' => 'D', + 'description' => 'Total VAT due (sum of all VAT due amounts)', + 'pcg_accounts' => 'Sum of 08 + 09 + 9B + 17' + ), + '26' => array( + 'label' => 'TVA déductible totale (Total deductible VAT)', + 'type' => 'vat', + 'section' => 'D', + 'description' => 'Total deductible VAT (sum of all deductible VAT amounts)', + 'pcg_accounts' => 'Sum of 20 + 21 + 22' + ), + '28' => array( + 'label' => 'TVA nette à payer', + 'type' => 'vat', + 'section' => 'D', + 'description' => 'Net VAT to pay (if total due > total deductible)', + 'pcg_accounts' => '445510 (État - TVA à décaisser)' + ), + '29' => array( + 'label' => 'Crédit de TVA à reporter ou remboursement', + 'type' => 'vat', + 'section' => 'D', + 'description' => 'VAT credit to carry forward or refund (if total deductible > total due)', + 'pcg_accounts' => '445670 (État - Crédit de TVA à reporter ou rembourser)' + ) + ); + } + + /** + * Get CA-3 section headers (Notice 4722) + * + * @return array Section headers + */ + public function getCA3SectionHeaders() + { + return array( + 'A' => array( + 'title' => 'A. Opérations imposables (Taxable Operations)', + 'description' => 'HT amounts of all taxable operations according to Notice 4722 (3310-CA3-SD)', + 'notice' => 'Notice 4722 - Summary Table CA3 (3310-CA3-SD)' + ), + 'B' => array( + 'title' => 'B. Décompte de la TVA due (VAT Due Calculation)', + 'description' => 'VAT amounts due, calculated on A1/A2 bases per applicable rate', + 'notice' => 'Notice 4722 - VAT Due Calculation' + ), + 'C' => array( + 'title' => 'C. Décompte de la TVA déductible (Deductible VAT Calculation)', + 'description' => 'VAT amounts that can be deducted from purchases and expenses', + 'notice' => 'Notice 4722 - Deductible VAT Calculation' + ), + 'D' => array( + 'title' => 'D. Résultat (Result)', + 'description' => 'Final calculation: net VAT to pay or credit to carry forward', + 'notice' => 'Notice 4722 - Final Result' + ) + ); + } + + /** + * Get supported VAT rates + * + * @return array VAT rates + */ + public function getVATRates() + { + return array( + '20.00' => '20%', + '10.00' => '10%', + '5.50' => '5.5%', + '2.10' => '2.1%', + '0.00' => '0%' + ); + } + + /** + * Validate account mapping + * + * @param string $ca3_line CA-3 line code + * @param string $account_code Account code + * @return bool Valid + */ + public function validateAccountMapping($ca3_line, $account_code) + { + // Check if account exists in Dolibarr + $sql = "SELECT COUNT(*) as count FROM " . MAIN_DB_PREFIX . "accounting_account + WHERE account_number = '" . $this->db->escape($account_code) . "' AND active = 1"; + + $result = $this->db->query($sql); + if ($result && $this->db->num_rows($result) > 0) { + $obj = $this->db->fetch_object($result); + return $obj->count > 0; + } + + return false; + } +} diff --git a/langs/en_US/declarationtva.lang b/langs/en_US/declarationtva.lang index 40f967e..05209a6 100644 --- a/langs/en_US/declarationtva.lang +++ b/langs/en_US/declarationtva.lang @@ -368,6 +368,5 @@ PCGAccounts = PCG Accounts AccountSelection = Account Selection SelectedAccounts = Selected Accounts AccountCount = Account Count -MultiSelectHelp = Hold Ctrl (or Cmd on Mac) to select multiple accounts ErrorCSRFToken = Security token error. Please try again. NoChangesDetected = No changes detected in the configuration. diff --git a/langs/fr_FR/declarationtva.lang b/langs/fr_FR/declarationtva.lang index 3318a9b..bf149e5 100644 --- a/langs/fr_FR/declarationtva.lang +++ b/langs/fr_FR/declarationtva.lang @@ -357,6 +357,5 @@ PCGAccounts = Comptes PCG AccountSelection = Sélection de comptes SelectedAccounts = Comptes sélectionnés AccountCount = Nombre de comptes -MultiSelectHelp = Maintenez Ctrl (ou Cmd sur Mac) pour sélectionner plusieurs comptes ErrorCSRFToken = Erreur de jeton de sécurité. Veuillez réessayer. NoChangesDetected = Aucun changement détecté dans la configuration.