Version 2.1.0: Enhanced PDF Export with Detailed Breakdown Pages

- Added comprehensive PDF export combining CA-3 form with detailed breakdown pages
- Implemented pdftk-based PDF merging that preserves form fields
- Added support for new CA-3 lines (25, 26, 27, TD, 28, 32) with conditional visibility
- Fixed multi-select configuration saving issues
- Enhanced error handling and debugging for PDF generation
- Added French status translation for detailed PDFs
- Optimized page breaks to reduce paper usage
- Improved form field preservation during PDF merging

Technical improvements:
- Better error handling with comprehensive logging
- Modular PDF generation with fallback options
- Fixed pdftk filename conflicts
- Enhanced debugging capabilities
- Status translation without external dependencies
This commit is contained in:
Frank Cools 2025-10-06 16:47:02 +02:00
parent 8277217cc7
commit 9255a39d42
11 changed files with 911 additions and 67 deletions

View File

@ -1,5 +1,27 @@
# CHANGELOG MODULE DECLARATIONTVA FOR [DOLIBARR ERP CRM](https://www.dolibarr.org) # CHANGELOG MODULE DECLARATIONTVA FOR [DOLIBARR ERP CRM](https://www.dolibarr.org)
## 1.1
### New Features
- **Enhanced PDF Export**: CA-3 form now includes detailed breakdown pages in a single comprehensive PDF
- **Detailed Breakdown Pages**: Account-level details for each CA-3 line showing exactly where amounts come from
- **Improved PDF Merging**: Uses pdftk for reliable PDF merging that preserves form fields
- **Conditional Page Breaks**: Optimized page usage to reduce paper consumption
- **Enhanced Line Calculations**: Added support for lines 25, 26, 27, TD, 28, 32 with proper conditional visibility
- **Status Translation**: Proper French translation of declaration status in detailed PDFs
### Technical Improvements
- **Better Error Handling**: Comprehensive logging and debugging for PDF generation
- **Form Field Preservation**: pdftk-based merging maintains all form data integrity
- **Modular PDF Generation**: Separate detailed PDF generation with fallback options
- **Enhanced Debugging**: Detailed logging for troubleshooting PDF generation issues
### Bug Fixes
- **Fixed Multi-Select Saving**: Configuration fields now properly save when all selections are removed
- **Fixed PDF Merge Issues**: Resolved pdftk filename conflicts that prevented detailed pages from being added
- **Fixed Status Translation**: Eliminated "Call to a member function trans() on null" errors
- **Fixed Form Corruption**: Prevented FPDI merge from corrupting filled form data
## 1.0 ## 1.0
Initial version Initial version

138
TASK.md
View File

@ -712,7 +712,143 @@ The MVP is now **feature-complete** and ready for real-world French VAT declarat
## Discovered During Work ## Discovered During Work
*Tasks discovered during development will be added here* ### Bug Fixes - Configuration Screen
- [x] **Task Bug-1**: Fix multiple selection fields not saving when all selections are removed ✅ **COMPLETED**
- **Issue**: When removing all selections from multi-select fields in config screen, changes were not persisted
- **Root Cause**: Form processing logic only called `updateAccountMapping` when `isset($_POST['field_name'])` was true, but empty multi-select fields are not present in POST data
- **Solution**: Modified form processing to always call `updateAccountMapping` for each field, regardless of POST data presence
- **Files Modified**: `admin/setup_mvp.php`, `core/class/declarationtva_config.class.php`
- **Date**: 2025-01-27
- **Estimated**: 2 hours
### Bug Fixes - CA-3 Line 25 Calculation
- [x] **Task Bug-2**: Fix line 25 calculation and title ✅ **COMPLETED**
- **Issue**: Line 25 had incorrect title and calculation logic
- **Requirements**: Title should be 'Crédit de TVA (ligne 23 - ligne16)', calculation should be line 23 - line 16, only display if > 0
- **Root Cause**: Line 25 was using incorrect calculation logic based on total VAT collected instead of line 23 - line 16
- **Solution**:
- Added `getLineAmount()` helper method to retrieve specific line amounts
- Modified `calculateDSectionLines()` to calculate line 25 as line 23 - line 16
- Updated title to 'Crédit de TVA (ligne 23 - ligne16)'
- Only display value if > 0
- **Files Modified**: `core/class/declarationtva.class.php`
- **Date**: 2025-01-27
- **Estimated**: 1 hour
### Bug Fixes - CA-3 Line 28 Calculation
- [x] **Task Bug-3**: Fix line 28 calculation and title ✅ **COMPLETED**
- **Issue**: Line 28 had incorrect title and calculation logic
- **Requirements**: Title should be 'TVA nette dûe (ligne 16 - ligne 23)', calculation should be line 16 - line 23, only display if > 0
- **Root Cause**: Line 28 was using incorrect calculation logic based on net VAT due instead of line 16 - line 23
- **Solution**:
- Modified `calculateDSectionLines()` to calculate line 28 as line 16 - line 23
- Updated title to 'TVA nette dûe (ligne 16 - ligne 23)'
- Only display value if > 0
- **Files Modified**: `core/class/declarationtva.class.php`
- **Date**: 2025-01-27
- **Estimated**: 30 minutes
### Bug Fixes - CA-3 Line 28 Calculation (Updated)
- [x] **Task Bug-5**: Update line 28 to use same value as line TD ✅ **COMPLETED**
- **Issue**: Line 28 needed to be updated to use same value as line TD
- **Requirements**: Title should be 'TVA nette due (ligne TD ligne X5)', value should be same as line TD, only display if > 0
- **Root Cause**: Line 28 was calculating independently instead of using line TD value
- **Solution**:
- Modified `calculateDSectionLines()` to use `$line_td_amount` for line 28
- Updated title to 'TVA nette due (ligne TD ligne X5)'
- Line 28 now shows same value as line TD
- **Files Modified**: `core/class/declarationtva.class.php`
- **Date**: 2025-01-27
- **Estimated**: 15 minutes
### Bug Fixes - CA-3 Line TD Calculation
- [x] **Task Bug-4**: Fix line TD calculation and title ✅ **COMPLETED**
- **Issue**: Line TD had incorrect title and calculation logic
- **Requirements**: Title should be 'TVA due (ligne 16 ligne 23)', calculation should be line 16 - line 23, only display if > 0
- **Root Cause**: Line TD was using incorrect calculation logic based on net VAT due instead of line 16 - line 23
- **Solution**:
- Modified `calculateDSectionLines()` to calculate line TD as line 16 - line 23
- Updated title to 'TVA due (ligne 16 ligne 23)'
- Only display value if > 0
- Removed special handling in view to use database line_label
- **Files Modified**: `core/class/declarationtva.class.php`, `declarationtva_view.php`
- **Date**: 2025-01-27
- **Estimated**: 30 minutes
### Bug Fixes - Remove Lines 26 and 29
- [x] **Task Bug-6**: Remove lines 26 and 29 from CA-3 calculation ✅ **COMPLETED**
- **Issue**: Lines 26 and 29 are no longer needed in the CA-3 form
- **Requirements**: Remove lines 26 and 29 from both calculation and display
- **Root Cause**: Lines 26 and 29 were part of the original CA-3 structure but are no longer required
- **Solution**:
- Removed line 26 calculation logic from `calculateDSectionLines()`
- Removed line 29 calculation logic from `calculateDSectionLines()`
- Updated view to remove lines 26 and 29 from `$section_d_lines` array
- Section D now only shows lines 25, TD, and 28
- **Files Modified**: `core/class/declarationtva.class.php`, `declarationtva_view.php`
- **Date**: 2025-01-27
- **Estimated**: 15 minutes
### Bug Fixes - Add Line 32
- [x] **Task Bug-7**: Add line 32 to CA-3 calculation ✅ **COMPLETED**
- **Issue**: Need to add line 32 for total amount to pay
- **Requirements**: Title should be 'Total à payer (lignes 28 + 29 + Z5 AB) (N'oubliez pas d'effectuer le règlement correspondant)', value should be same as line 28, only show if > 0, use bold formatting
- **Root Cause**: Line 32 was missing from the CA-3 form structure
- **Solution**:
- Added line 32 calculation logic to `calculateDSectionLines()`
- Line 32 uses same value as line 28
- Added line 32 to `$section_d_lines` array in view
- Added special bold formatting for line 32 in view display
- Section D now shows lines 25, TD, 28, and 32
- **Files Modified**: `core/class/declarationtva.class.php`, `declarationtva_view.php`
- **Date**: 2025-01-27
- **Estimated**: 20 minutes
### Bug Fixes - Add Line 27
- [x] **Task Bug-8**: Add line 27 to CA-3 calculation ✅ **COMPLETED**
- **Issue**: Need to add line 27 for VAT credit to carry forward
- **Requirements**: Title should be 'Crédit de TVA à reporter (ligne 25 ligne 26) (Cette somme est à reporter ligne 22 de la prochaine déclaration)', value should be same as line 25, only show if > 0
- **Root Cause**: Line 27 was missing from the CA-3 form structure
- **Solution**:
- Added line 27 calculation logic to `calculateDSectionLines()`
- Line 27 uses same value as line 25
- Added line 27 to `$section_d_lines` array in view
- Section D now shows lines 25, 27, TD, 28, and 32
- **Files Modified**: `core/class/declarationtva.class.php`, `declarationtva_view.php`
- **Date**: 2025-01-27
- **Estimated**: 15 minutes
### Bug Fixes - PDF Field Mapping
- [x] **Task Bug-9**: Add missing lines to PDF field mapping ✅ **COMPLETED**
- **Issue**: New CA-3 lines (27, 32) were not included in PDF generation
- **Requirements**: All new lines should be available in generated PDF
- **Root Cause**: PDF field mapping was missing lines 27 and 32
- **Solution**:
- Added `D27_amount` field mapping for line 27
- Added `D32_amount` field mapping for line 32
- Updated debug logging to include new lines
- Removed obsolete lines 26 and 29 from PDF mapping
- **Files Modified**: `core/class/declarationtva_pdf.class.php`
- **Date**: 2025-01-27
- **Estimated**: 10 minutes
### Bug Fixes - Add Line 26 and Conditional Visibility
- [x] **Task Bug-10**: Add line 26 and conditional visibility logic ✅ **COMPLETED**
- **Issue**: Need line 26 for VAT credit refund and conditional visibility for line 27
- **Requirements**:
- Line 27: visible if > 0 and < 760
- Line 26: visible if >= 760
- Add line 26 to PDF fields
- **Root Cause**: Missing conditional logic for VAT credit handling
- **Solution**:
- Added line 26 calculation with visibility >= 760
- Modified line 27 visibility to only show if > 0 and < 760
- Added `D26_amount` to PDF field mapping
- Updated view to include line 26 in display
- Both lines use same value as line 25 but with different visibility rules
- **Files Modified**: `core/class/declarationtva.class.php`, `declarationtva_view.php`, `core/class/declarationtva_pdf.class.php`
- **Date**: 2025-01-27
- **Estimated**: 20 minutes
--- ---

View File

@ -47,30 +47,25 @@ if ($action == 'update_mappings') {
$base_account_codes = GETPOST('base_account_codes_' . $line, 'array'); $base_account_codes = GETPOST('base_account_codes_' . $line, 'array');
$vat_account_codes = GETPOST('vat_account_codes_' . $line, 'array'); $vat_account_codes = GETPOST('vat_account_codes_' . $line, 'array');
// Process base accounts // Always process base accounts (even if empty)
if (isset($_POST['base_account_codes_' . $line])) { $result = $config->updateAccountMapping($line . '_BASE', $base_account_codes);
$result = $config->updateAccountMapping($line . '_BASE', $base_account_codes); if ($result) {
if ($result) { $updated_count++;
$updated_count++;
}
} }
// Process VAT accounts // Always process VAT accounts (even if empty)
if (isset($_POST['vat_account_codes_' . $line])) { $result = $config->updateAccountMapping($line . '_VAT', $vat_account_codes);
$result = $config->updateAccountMapping($line . '_VAT', $vat_account_codes); if ($result) {
if ($result) { $updated_count++;
$updated_count++;
}
} }
} else { } else {
// Normal processing for other lines // Normal processing for other lines
$account_codes = GETPOST('account_codes_' . $line, 'array'); $account_codes = GETPOST('account_codes_' . $line, 'array');
if (isset($_POST['account_codes_' . $line])) { // Always process account mappings (even if empty)
$result = $config->updateAccountMapping($line, $account_codes); $result = $config->updateAccountMapping($line, $account_codes);
if ($result) { if ($result) {
$updated_count++; $updated_count++;
}
} }
} }
} }

View File

@ -395,6 +395,28 @@ class DeclarationTVA
return true; return true;
} }
/**
* Get amount for a specific CA-3 line
*
* @param int $declaration_id Declaration ID
* @param string $ca3_line CA-3 line code
* @return float Line amount
*/
private function getLineAmount($declaration_id, $ca3_line)
{
$sql = "SELECT vat_amount FROM " . MAIN_DB_PREFIX . "declarationtva_ca3_lines
WHERE declaration_id = " . (int)$declaration_id . "
AND ca3_line = '" . $this->db->escape($ca3_line) . "'";
$result = $this->db->query($sql);
if ($result && $this->db->num_rows($result) > 0) {
$obj = $this->db->fetch_object($result);
return (float)$obj->vat_amount;
}
return 0.0;
}
/** /**
* Calculate D-section result lines (25, 26, 28, 29) * Calculate D-section result lines (25, 26, 28, 29)
* *
@ -405,74 +427,103 @@ class DeclarationTVA
*/ */
private function calculateDSectionLines($declaration_id, $total_vat_collected, $total_vat_deductible) private function calculateDSectionLines($declaration_id, $total_vat_collected, $total_vat_deductible)
{ {
// Get line 16 and 23 amounts for line 25 calculation
$line_16_amount = $this->getLineAmount($declaration_id, '16');
$line_23_amount = $this->getLineAmount($declaration_id, '23');
// Calculate net VAT due // Calculate net VAT due
$net_vat_due = $total_vat_collected - $total_vat_deductible; $net_vat_due = $total_vat_collected - $total_vat_deductible;
// Line 25: TVA brute due (Total VAT due) - only if negative (VAT credit - we receive money) // Line 25: Crédit de TVA (ligne 23 - ligne16) - only if > 0
if ($net_vat_due < 0) { $line_25_amount = $line_23_amount - $line_16_amount;
$line_25_amount = $total_vat_collected; if ($line_25_amount > 0) {
$this->createCA3Line($declaration_id, '25', 'Calculated from sections A and B', array( $this->createCA3Line($declaration_id, '25', 'Crédit de TVA (ligne 23 - ligne16)', array(
'base_amount' => 0, 'base_amount' => 0,
'vat_amount' => $line_25_amount, 'vat_amount' => $line_25_amount,
'total_amount' => $line_25_amount 'total_amount' => $line_25_amount
)); ));
} else { } else {
$this->createCA3Line($declaration_id, '25', 'Calculated from sections A and B', array( $this->createCA3Line($declaration_id, '25', 'Crédit de TVA (ligne 23 - ligne16)', array(
'base_amount' => 0, 'base_amount' => 0,
'vat_amount' => 0, 'vat_amount' => 0,
'total_amount' => 0 'total_amount' => 0
)); ));
} }
// Line 26: TVA déductible totale (Total deductible VAT) // Line 27: Crédit de TVA à reporter (ligne 25 ligne 26) - same value as line 25, only if > 0 and < 760
$line_26_amount = $total_vat_deductible; $line_27_amount = $line_25_amount; // Same value as line 25
$this->createCA3Line($declaration_id, '26', 'Calculated from section C', array( if ($line_27_amount > 0 && $line_27_amount < 760) {
'base_amount' => 0, $this->createCA3Line($declaration_id, '27', 'Crédit de TVA à reporter (ligne 25 ligne 26) (Cette somme est à reporter ligne 22 de la prochaine déclaration)', array(
'vat_amount' => $line_26_amount, 'base_amount' => 0,
'total_amount' => $line_26_amount 'vat_amount' => $line_27_amount,
)); 'total_amount' => $line_27_amount
));
} else {
$this->createCA3Line($declaration_id, '27', 'Crédit de TVA à reporter (ligne 25 ligne 26) (Cette somme est à reporter ligne 22 de la prochaine déclaration)', array(
'base_amount' => 0,
'vat_amount' => 0,
'total_amount' => 0
));
}
// Line TD: TVA due (VAT due) - only if positive (we need to pay) // Line 26: Remboursement de crédit de TVA demandé sur formulaire n°3519 - same value as line 25, only if >= 760
if ($net_vat_due > 0) { $line_26_amount = $line_25_amount; // Same value as line 25
$line_td_amount = $net_vat_due; if ($line_26_amount >= 760) {
$this->createCA3Line($declaration_id, 'TD', 'TVA due (amount to pay)', array( $this->createCA3Line($declaration_id, '26', 'Remboursement de crédit de TVA demandé sur formulaire n°3519', array(
'base_amount' => 0,
'vat_amount' => $line_26_amount,
'total_amount' => $line_26_amount
));
} else {
$this->createCA3Line($declaration_id, '26', 'Remboursement de crédit de TVA demandé sur formulaire n°3519', array(
'base_amount' => 0,
'vat_amount' => 0,
'total_amount' => 0
));
}
// Line TD: TVA due (ligne 16 ligne 23) - only if > 0
$line_td_amount = $line_16_amount - $line_23_amount;
if ($line_td_amount > 0) {
$this->createCA3Line($declaration_id, 'TD', 'TVA due (ligne 16 ligne 23)', array(
'base_amount' => 0, 'base_amount' => 0,
'vat_amount' => $line_td_amount, 'vat_amount' => $line_td_amount,
'total_amount' => $line_td_amount 'total_amount' => $line_td_amount
)); ));
} else { } else {
$this->createCA3Line($declaration_id, 'TD', 'TVA due (amount to pay)', array( $this->createCA3Line($declaration_id, 'TD', 'TVA due (ligne 16 ligne 23)', array(
'base_amount' => 0, 'base_amount' => 0,
'vat_amount' => 0, 'vat_amount' => 0,
'total_amount' => 0 'total_amount' => 0
)); ));
} }
// Line 28: TVA nette due (Net VAT due) - if positive // Line 28: TVA nette due (ligne TD ligne X5) - same value as line TD, only if > 0
if ($net_vat_due > 0) { $line_28_amount = $line_td_amount; // Same value as line TD
$this->createCA3Line($declaration_id, '28', 'Calculated: 25 - 26', array( if ($line_28_amount > 0) {
$this->createCA3Line($declaration_id, '28', 'TVA nette due (ligne TD ligne X5)', array(
'base_amount' => 0, 'base_amount' => 0,
'vat_amount' => $net_vat_due, 'vat_amount' => $line_28_amount,
'total_amount' => $net_vat_due 'total_amount' => $line_28_amount
)); ));
} else { } else {
$this->createCA3Line($declaration_id, '28', 'Calculated: 25 - 26', array( $this->createCA3Line($declaration_id, '28', 'TVA nette due (ligne TD ligne X5)', array(
'base_amount' => 0, 'base_amount' => 0,
'vat_amount' => 0, 'vat_amount' => 0,
'total_amount' => 0 'total_amount' => 0
)); ));
} }
// Line 29: Crédit de TVA (VAT Credit) - if negative // Line 32: Total à payer (lignes 28 + 29 + Z5 AB) - same value as line 28, only if > 0
if ($net_vat_due < 0) { $line_32_amount = $line_28_amount; // Same value as line 28
$vat_credit = abs($net_vat_due); if ($line_32_amount > 0) {
$this->createCA3Line($declaration_id, '29', 'Calculated: 26 - 25', array( $this->createCA3Line($declaration_id, '32', 'Total à payer (lignes 28 + 29 + Z5 AB) (N\'oubliez pas d\'effectuer le règlement correspondant)', array(
'base_amount' => 0, 'base_amount' => 0,
'vat_amount' => $vat_credit, 'vat_amount' => $line_32_amount,
'total_amount' => $vat_credit 'total_amount' => $line_32_amount
)); ));
} else { } else {
$this->createCA3Line($declaration_id, '29', 'Calculated: 26 - 25', array( $this->createCA3Line($declaration_id, '32', 'Total à payer (lignes 28 + 29 + Z5 AB) (N\'oubliez pas d\'effectuer le règlement correspondant)', array(
'base_amount' => 0, 'base_amount' => 0,
'vat_amount' => 0, 'vat_amount' => 0,
'total_amount' => 0 'total_amount' => 0

View File

@ -108,8 +108,8 @@ class DeclarationTVA_Config
WHERE entity = " . $this->entity . " AND ca3_line = '" . $this->db->escape($ca3_line) . "'"; WHERE entity = " . $this->entity . " AND ca3_line = '" . $this->db->escape($ca3_line) . "'";
$this->db->query($sql); $this->db->query($sql);
// Then insert/activate new mappings // Then insert/activate new mappings (only if account_codes is not empty)
if (!empty($account_codes)) { if (!empty($account_codes) && is_array($account_codes)) {
foreach ($account_codes as $account_code) { foreach ($account_codes as $account_code) {
if (!empty($account_code)) { if (!empty($account_code)) {
// Check if mapping already exists (active or inactive) // Check if mapping already exists (active or inactive)
@ -138,6 +138,8 @@ class DeclarationTVA_Config
} }
} }
} }
// If account_codes is empty, all mappings for this line remain deactivated
// This ensures that clearing all selections properly saves the empty state
return true; return true;
} }

View File

@ -149,6 +149,55 @@ class DeclarationTVA_PDF
return false; return false;
} }
} }
/**
* Generate detailed CA-3 declaration PDF with breakdown pages
*
* @param int $declaration_id Declaration ID
* @param string $outputlangs Output language
* @return string|false PDF file path or false on error
*/
public function generateDetailedCA3PDF($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_Detailed_' . $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/');
}
// Generate detailed PDF with breakdown pages
$result = $this->generateDetailedPDF($filepath, $declaration, $ca3_data, $company);
if ($result) {
return $filepath;
} else {
return false;
}
}
/** /**
* Get the template file path (custom or default) * Get the template file path (custom or default)
@ -247,6 +296,8 @@ class DeclarationTVA_PDF
// Check if output file was created successfully // Check if output file was created successfully
if (file_exists($output_path) && filesize($output_path) > 0) { if (file_exists($output_path) && filesize($output_path) > 0) {
// Add detailed pages to the filled PDF
$this->addDetailedPagesToPDF($output_path, $declaration, $ca3_data, $company);
return true; return true;
} else { } else {
$this->error = 'pdftk failed to generate output: ' . $result; $this->error = 'pdftk failed to generate output: ' . $result;
@ -373,9 +424,10 @@ class DeclarationTVA_PDF
// Section D: Résultat (Calculated) // Section D: Résultat (Calculated)
$field_data['D25_amount'] = $this->getCA3LineAmount($ca3_data, '25'); $field_data['D25_amount'] = $this->getCA3LineAmount($ca3_data, '25');
$field_data['D26_amount'] = $this->getCA3LineAmount($ca3_data, '26'); $field_data['D26_amount'] = $this->getCA3LineAmount($ca3_data, '26');
$field_data['D27_amount'] = $this->getCA3LineAmount($ca3_data, '27');
$field_data['DTD_amount'] = $this->getCA3LineAmount($ca3_data, 'TD'); $field_data['DTD_amount'] = $this->getCA3LineAmount($ca3_data, 'TD');
$field_data['D28_amount'] = $this->getCA3LineAmount($ca3_data, '28'); $field_data['D28_amount'] = $this->getCA3LineAmount($ca3_data, '28');
$field_data['D29_amount'] = $this->getCA3LineAmount($ca3_data, '29'); $field_data['D32_amount'] = $this->getCA3LineAmount($ca3_data, '32');
// Subtotals // Subtotals
$field_data['subtotal_B16_amount'] = $this->getCA3LineAmount($ca3_data, '16'); $field_data['subtotal_B16_amount'] = $this->getCA3LineAmount($ca3_data, '16');
@ -388,7 +440,7 @@ class DeclarationTVA_PDF
$field_data['vat_credit'] = $this->formatAmount($declaration->vat_credit); $field_data['vat_credit'] = $this->formatAmount($declaration->vat_credit);
// Debug: Log specific D-section fields // Debug: Log specific D-section fields
error_log("DeclarationTVA: D-section fields - D25: " . $field_data['D25_amount'] . ", D26: " . $field_data['D26_amount'] . ", DTD: " . $field_data['DTD_amount'] . ", D28: " . $field_data['D28_amount'] . ", D29: " . $field_data['D29_amount']); error_log("DeclarationTVA: D-section fields - D25: " . $field_data['D25_amount'] . ", D26: " . $field_data['D26_amount'] . ", D27: " . $field_data['D27_amount'] . ", DTD: " . $field_data['DTD_amount'] . ", D28: " . $field_data['D28_amount'] . ", D32: " . $field_data['D32_amount']);
return $field_data; return $field_data;
} }
@ -705,6 +757,9 @@ class DeclarationTVA_PDF
file_put_contents($data_file, $data_content); file_put_contents($data_file, $data_content);
// Add detailed pages to the filled PDF
$this->addDetailedPagesToPDF($output_path, $declaration, $ca3_data, $company);
return true; return true;
} else { } else {
$this->error = 'Failed to copy template file'; $this->error = 'Failed to copy template file';
@ -756,14 +811,14 @@ class DeclarationTVA_PDF
$pdf->SetFont('helvetica', '', 12); $pdf->SetFont('helvetica', '', 12);
$pdf->Cell(0, 8, 'Numéro de déclaration: ' . $declaration->declaration_number, 0, 1); $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, '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->Cell(0, 8, 'Statut: ' . $this->translateStatus($declaration->status), 0, 1);
$pdf->Ln(10); $pdf->Ln(10);
// Add CA-3 sections // Add CA-3 sections
$this->addCA3Section($pdf, 'A. Opérations imposables', $ca3_data, array('A1', 'A2', 'A3', 'A4', 'A5')); $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, '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, 'C. TVA déductible', $ca3_data, array('20', '21', '22'));
$this->addCA3Section($pdf, 'D. Résultat', $ca3_data, array('25', '26', 'TD', '28', '29')); $this->addCA3Section($pdf, 'D. Résultat', $ca3_data, array('25', '26', '27', 'TD', '28', '32'));
// Add totals // Add totals
$pdf->Ln(10); $pdf->Ln(10);
@ -776,6 +831,9 @@ class DeclarationTVA_PDF
$pdf->Cell(0, 8, 'CRÉDIT DE TVA: ' . price($declaration->vat_credit, 0, '', 1, 0), 0, 1); $pdf->Cell(0, 8, 'CRÉDIT DE TVA: ' . price($declaration->vat_credit, 0, '', 1, 0), 0, 1);
} }
// Add detailed breakdown pages
$this->addDetailPages($pdf, $declaration, $ca3_data);
// Output PDF // Output PDF
$pdf->Output($output_path, 'F'); $pdf->Output($output_path, 'F');
@ -805,7 +863,12 @@ class DeclarationTVA_PDF
if (isset($ca3_data[$line])) { if (isset($ca3_data[$line])) {
$data = $ca3_data[$line]; $data = $ca3_data[$line];
$amount = isset($data['vat_amount']) ? $data['vat_amount'] : 0; $amount = isset($data['vat_amount']) ? $data['vat_amount'] : 0;
$pdf->Cell(20, 6, $line, 1, 0, 'C');
// Add link to detail page for this line
$link = $pdf->AddLink();
$pdf->SetLink($link, 0, $this->getDetailPageNumber($line));
$pdf->Cell(20, 6, $line, 1, 0, 'C', false, $link);
$pdf->Cell(100, 6, $data['line_label'], 1, 0); $pdf->Cell(100, 6, $data['line_label'], 1, 0);
$pdf->Cell(30, 6, price($amount, 0, '', 1, 0), 1, 1, 'R'); $pdf->Cell(30, 6, price($amount, 0, '', 1, 0), 1, 1, 'R');
} }
@ -813,6 +876,554 @@ class DeclarationTVA_PDF
$pdf->Ln(5); $pdf->Ln(5);
} }
/**
* Add detailed breakdown pages to PDF
*
* @param TCPDF $pdf PDF object
* @param DeclarationTVA $declaration Declaration object
* @param array $ca3_data CA-3 line data
*/
private function addDetailPages($pdf, $declaration, $ca3_data)
{
// Define calculated lines that should be excluded from detailed breakdown
$calculated_lines = array('25', '26', '27', 'TD', '28', '32', '16', '23');
// Define the order of lines as they appear in the view page
$ordered_lines = array(
// Section A: Opérations imposables
'A1', 'A2', 'A3', 'A4', 'A5', 'E1', 'E2', 'E3', 'E4', 'E5', 'E6', 'F1', 'F2', 'F6', 'F7', 'F8',
// Section B: TVA due
'08', '09', '9B', '17',
// Section C: TVA déductible
'19', '20', '21', '22'
);
// Create a lookup array for quick access to line data
$ca3_lookup = array();
foreach ($ca3_data as $line_data) {
$ca3_lookup[$line_data['ca3_line']] = $line_data;
}
// Get lines that have actual values and are not calculated lines, in the correct order
$lines_with_data = array();
foreach ($ordered_lines as $line_code) {
// Skip if line doesn't exist in data
if (!isset($ca3_lookup[$line_code])) {
continue;
}
// Skip calculated lines - they don't have account mappings
if (in_array($line_code, $calculated_lines)) {
continue;
}
$line_data = $ca3_lookup[$line_code];
$has_vat_amount = isset($line_data['vat_amount']) && $line_data['vat_amount'] > 0;
$has_base_amount = isset($line_data['base_amount']) && $line_data['base_amount'] > 0;
$has_total_amount = isset($line_data['total_amount']) && $line_data['total_amount'] > 0;
// Only include lines that have meaningful values and are not calculated
if ($has_vat_amount || $has_base_amount || $has_total_amount) {
$lines_with_data[] = $line_code;
}
}
// If no lines have data, add a message page
if (empty($lines_with_data)) {
$pdf->AddPage();
$pdf->SetFont('helvetica', 'B', 16);
$pdf->Cell(0, 10, 'Aucune donnée détaillée disponible', 0, 1, 'C');
$pdf->Ln(10);
$pdf->SetFont('helvetica', '', 12);
$pdf->Cell(0, 8, 'Aucune ligne de la déclaration ne contient de données comptables.', 0, 1);
$pdf->Cell(0, 8, 'Veuillez configurer les mappings de comptes dans la section Administration.', 0, 1);
return;
}
// Add a detail section for each line with data in the correct order
foreach ($lines_with_data as $line_code) {
$this->addLineDetailPage($pdf, $declaration, $line_code);
}
}
/**
* Add a detail section for a specific CA-3 line
*
* @param TCPDF $pdf PDF object
* @param DeclarationTVA $declaration Declaration object
* @param string $line_code CA-3 line code
*/
private function addLineDetailPage($pdf, $declaration, $line_code)
{
// Check if we need a new page (if current position is too low)
$current_y = $pdf->GetY();
$page_height = $pdf->getPageHeight() - $pdf->getMargins()['bottom'];
// If we're too close to the bottom, start a new page
if ($current_y > $page_height - 100) {
$pdf->AddPage();
} else {
// Add some space between sections
$pdf->Ln(10);
}
// Set section title with separator
$pdf->SetFont('helvetica', 'B', 16);
$pdf->SetTextColor(0, 0, 0);
$pdf->Cell(0, 8, 'Détail de la ligne ' . $line_code, 0, 1, 'C');
// Add separator line
$pdf->SetDrawColor(0, 0, 0);
$pdf->SetLineWidth(0.5);
$pdf->Line($pdf->getMargins()['left'], $pdf->GetY(), $pdf->getPageWidth() - $pdf->getMargins()['right'], $pdf->GetY());
$pdf->Ln(5);
// Get line details
$line_details = $declaration->getCA3LineDetails($declaration->rowid, $line_code);
if (empty($line_details) || empty($line_details['account_details'])) {
$pdf->SetFont('helvetica', '', 12);
$pdf->Cell(0, 8, 'Aucun détail comptable disponible pour cette ligne.', 0, 1);
$pdf->Cell(0, 8, 'Cette ligne peut être calculée automatiquement ou ne pas avoir de mapping de comptes.', 0, 1);
// Still show the calculated value if available
if (!empty($line_details['calculated_line'])) {
$calc = $line_details['calculated_line'];
$pdf->Ln(5);
$pdf->SetFont('helvetica', 'B', 12);
$pdf->Cell(0, 8, 'Valeur calculée:', 0, 1);
$pdf->SetFont('helvetica', '', 10);
if ($calc->base_amount > 0) {
$pdf->Cell(0, 6, 'Montant de base: ' . price($calc->base_amount, 0, '', 1, 0), 0, 1);
}
if ($calc->vat_amount > 0) {
$pdf->Cell(0, 6, 'Montant de TVA: ' . price($calc->vat_amount, 0, '', 1, 0), 0, 1);
}
if ($calc->total_amount > 0) {
$pdf->Cell(0, 6, 'Total: ' . price($calc->total_amount, 0, '', 1, 0), 0, 1);
}
}
return;
}
// Add line summary
$pdf->SetFont('helvetica', 'B', 12);
$pdf->Cell(0, 8, 'Résumé de la ligne ' . $line_code, 0, 1);
$pdf->SetFont('helvetica', '', 10);
$pdf->Cell(0, 6, 'Période: ' . dol_print_date($line_details['start_date'], 'day') . ' - ' . dol_print_date($line_details['end_date'], 'day'), 0, 1);
$pdf->Cell(0, 6, 'Nombre de comptes: ' . $line_details['account_count'], 0, 1);
if (!empty($line_details['calculated_line'])) {
$calc = $line_details['calculated_line'];
$pdf->Cell(0, 6, 'Montant calculé: ' . price($calc->vat_amount, 0, '', 1, 0), 0, 1);
}
$pdf->Ln(10);
// Add account details table
$this->addAccountDetailsTable($pdf, $line_details['account_details'], $line_code);
}
/**
* Add account details table to PDF
*
* @param TCPDF $pdf PDF object
* @param array $account_details Account details array
* @param string $line_code CA-3 line code
*/
private function addAccountDetailsTable($pdf, $account_details, $line_code)
{
// Group accounts by type for lines 08, 09, 9B (same as view page)
$base_accounts = array();
$vat_accounts = array();
$other_accounts = array();
foreach ($account_details as $account) {
if (strpos($account['mapping_type'], '_BASE') !== false) {
$base_accounts[] = $account;
} elseif (strpos($account['mapping_type'], '_VAT') !== false) {
$vat_accounts[] = $account;
} else {
$other_accounts[] = $account;
}
}
// Calculate table width and center position
$table_width = 150; // 35 + 80 + 35
$page_width = $pdf->getPageWidth() - $pdf->getMargins()['left'] - $pdf->getMargins()['right'];
$start_x = ($page_width - $table_width) / 2 + $pdf->getMargins()['left'];
// Set X position to center the table
$pdf->SetX($start_x);
// Table header - same as view page: Code compte, Libellé compte, Montant
$pdf->SetFont('helvetica', 'B', 10);
$pdf->Cell(35, 8, 'Code compte', 1, 0, 'C');
$pdf->Cell(80, 8, 'Libellé compte', 1, 0, 'C');
$pdf->Cell(35, 8, 'Montant', 1, 1, 'C');
$total_base_base = 0;
$total_vat_base = 0;
$total_base_vat = 0;
$total_vat_vat = 0;
$total_base_other = 0;
$total_vat_other = 0;
// Display BASE accounts first (if any)
if (!empty($base_accounts)) {
$pdf->SetFont('helvetica', 'B', 9);
$pdf->Cell(0, 6, 'Comptes de base (ventes)', 0, 1, 'L');
$pdf->SetFont('helvetica', '', 9);
foreach ($base_accounts as $account) {
$total_base_base += $account['base_amount'];
$total_vat_base += $account['vat_amount'];
$pdf->SetX($start_x);
$pdf->Cell(35, 6, $account['account_code'], 1, 0, 'C');
$pdf->Cell(80, 6, $this->truncateText($account['account_label'], 35), 1, 0, 'L');
$pdf->Cell(35, 6, price($account['base_amount'], 0, '', 1, 0), 1, 1, 'R');
}
// Subtotal for BASE accounts
$pdf->SetFont('helvetica', 'B', 9);
$pdf->SetX($start_x);
$pdf->Cell(115, 6, 'Sous-total comptes de base', 1, 0, 'L');
$pdf->Cell(35, 6, price($total_base_base, 0, '', 1, 0), 1, 1, 'R');
$pdf->Ln(2);
}
// Display VAT accounts second (if any)
if (!empty($vat_accounts)) {
$pdf->SetFont('helvetica', 'B', 9);
$pdf->Cell(0, 6, 'Comptes de TVA', 0, 1, 'L');
$pdf->SetFont('helvetica', '', 9);
foreach ($vat_accounts as $account) {
$total_base_vat += $account['base_amount'];
$total_vat_vat += $account['vat_amount'];
$pdf->SetX($start_x);
$pdf->Cell(35, 6, $account['account_code'], 1, 0, 'C');
$pdf->Cell(80, 6, $this->truncateText($account['account_label'], 35), 1, 0, 'L');
$pdf->Cell(35, 6, price($account['vat_amount'], 0, '', 1, 0), 1, 1, 'R');
}
// Subtotal for VAT accounts
$pdf->SetFont('helvetica', 'B', 9);
$pdf->SetX($start_x);
$pdf->Cell(115, 6, 'Sous-total comptes de TVA', 1, 0, 'L');
$pdf->Cell(35, 6, price($total_vat_vat, 0, '', 1, 0), 1, 1, 'R');
$pdf->Ln(2);
}
// Display other accounts (normal lines)
if (!empty($other_accounts)) {
$pdf->SetFont('helvetica', '', 9);
foreach ($other_accounts as $account) {
$total_base_other += $account['base_amount'];
$total_vat_other += $account['vat_amount'];
$pdf->SetX($start_x);
$pdf->Cell(35, 6, $account['account_code'], 1, 0, 'C');
$pdf->Cell(80, 6, $this->truncateText($account['account_label'], 35), 1, 0, 'L');
$pdf->Cell(35, 6, price($account['vat_amount'], 0, '', 1, 0), 1, 1, 'R');
}
// Total for other accounts
if (!empty($other_accounts)) {
$pdf->SetFont('helvetica', 'B', 9);
$pdf->SetX($start_x);
$pdf->Cell(115, 6, 'Total', 1, 0, 'L');
$pdf->Cell(35, 6, price($total_vat_other, 0, '', 1, 0), 1, 1, 'R');
}
}
}
/**
* Add detailed pages to an existing PDF
*
* @param string $pdf_path Path to the existing PDF
* @param DeclarationTVA $declaration Declaration object
* @param array $ca3_data CA-3 line data
* @param Societe $company Company object
* @return bool Success
*/
private function addDetailedPagesToPDF($pdf_path, $declaration, $ca3_data, $company)
{
try {
error_log("DeclarationTVA: Starting addDetailedPagesToPDF process");
// Create a temporary detailed PDF
$temp_detailed_path = tempnam(sys_get_temp_dir(), 'ca3_detailed_') . '.pdf';
error_log("DeclarationTVA: Temporary detailed PDF path: " . $temp_detailed_path);
$result = $this->generateDetailedPDF($temp_detailed_path, $declaration, $ca3_data, $company);
if (!$result) {
error_log("DeclarationTVA: Failed to generate detailed PDF: " . $this->error);
return false;
}
// Check if detailed PDF was created and has content
if (!file_exists($temp_detailed_path)) {
error_log("DeclarationTVA: Detailed PDF file does not exist at: " . $temp_detailed_path);
return false;
}
if (filesize($temp_detailed_path) == 0) {
error_log("DeclarationTVA: Detailed PDF file is empty");
return false;
}
error_log("DeclarationTVA: Detailed PDF created successfully, size: " . filesize($temp_detailed_path) . " bytes");
// Use a different approach: use pdftk to merge PDFs (preserves form fields)
$merge_result = $this->mergePDFsWithPdftk($pdf_path, $temp_detailed_path, $pdf_path);
if (!$merge_result) {
error_log("DeclarationTVA: Failed to merge PDFs with pdftk: " . $this->error);
// Fallback: try FPDI merge
$merge_result = $this->mergePDFs($pdf_path, $temp_detailed_path, $pdf_path);
if (!$merge_result) {
error_log("DeclarationTVA: Failed to merge PDFs with FPDI: " . $this->error);
return false;
}
}
error_log("DeclarationTVA: PDFs merged successfully");
// Clean up temporary file
if (file_exists($temp_detailed_path)) {
unlink($temp_detailed_path);
}
return true;
} catch (Exception $e) {
$this->error = 'Failed to add detailed pages: ' . $e->getMessage();
error_log("DeclarationTVA: Exception in addDetailedPagesToPDF: " . $e->getMessage());
return false;
}
}
/**
* Merge two PDFs using pdftk (preserves form fields)
*
* @param string $pdf1_path First PDF path
* @param string $pdf2_path Second PDF path
* @param string $output_path Output PDF path
* @return bool Success
*/
private function mergePDFsWithPdftk($pdf1_path, $pdf2_path, $output_path)
{
try {
// Check if pdftk is available
if (!$this->isPdftkAvailable()) {
$this->error = 'pdftk is not available for PDF merging';
return false;
}
error_log("DeclarationTVA: Starting PDF merge with pdftk - PDF1: " . $pdf1_path . " (size: " . filesize($pdf1_path) . "), PDF2: " . $pdf2_path . " (size: " . filesize($pdf2_path) . ")");
// Use pdftk to merge PDFs (preserves form fields)
// Create a temporary output file to avoid pdftk input/output filename conflict
$temp_output_path = tempnam(sys_get_temp_dir(), 'ca3_merged_') . '.pdf';
$pdftk_path = $this->getPdftkPath();
$command = "\"$pdftk_path\" \"$pdf1_path\" \"$pdf2_path\" cat output \"$temp_output_path\"";
error_log("DeclarationTVA: pdftk command: " . $command);
$result = shell_exec($command . ' 2>&1');
error_log("DeclarationTVA: pdftk result: " . $result);
// If pdftk succeeded, copy the merged file to the final output path
if (file_exists($temp_output_path) && filesize($temp_output_path) > 0) {
copy($temp_output_path, $output_path);
unlink($temp_output_path); // Clean up temporary file
}
// Check if output file was created successfully
if (file_exists($output_path) && filesize($output_path) > 0) {
error_log("DeclarationTVA: PDFs merged successfully with pdftk, size: " . filesize($output_path) . " bytes");
return true;
} else {
$this->error = 'pdftk failed to merge PDFs: ' . $result;
error_log("DeclarationTVA: pdftk merge failed: " . $result);
return false;
}
} catch (Exception $e) {
$this->error = 'Failed to merge PDFs with pdftk: ' . $e->getMessage();
error_log("DeclarationTVA: Exception in mergePDFsWithPdftk: " . $e->getMessage());
return false;
}
}
/**
* Merge two PDFs using FPDI with TCPDF
*
* @param string $pdf1_path First PDF path
* @param string $pdf2_path Second PDF path
* @param string $output_path Output PDF path
* @return bool Success
*/
private function mergePDFs($pdf1_path, $pdf2_path, $output_path)
{
try {
error_log("DeclarationTVA: Starting PDF merge - PDF1: " . $pdf1_path . " (size: " . filesize($pdf1_path) . "), PDF2: " . $pdf2_path . " (size: " . filesize($pdf2_path) . ")");
// Use TCPDF-based FPDI
$pdf = new \setasign\Fpdi\Tcpdf\Fpdi();
// Import pages from first PDF
$page_count1 = $pdf->setSourceFile($pdf1_path);
error_log("DeclarationTVA: PDF1 has " . $page_count1 . " pages");
for ($i = 1; $i <= $page_count1; $i++) {
$pdf->AddPage();
$pdf->useTemplate($pdf->importPage($i));
}
// Import pages from second PDF
$page_count2 = $pdf->setSourceFile($pdf2_path);
error_log("DeclarationTVA: PDF2 has " . $page_count2 . " pages");
for ($i = 1; $i <= $page_count2; $i++) {
$pdf->AddPage();
$pdf->useTemplate($pdf->importPage($i));
}
// Output merged PDF
$pdf->Output($output_path, 'F');
error_log("DeclarationTVA: Merged PDF created successfully, size: " . filesize($output_path) . " bytes");
return true;
} catch (Exception $e) {
$this->error = 'Failed to merge PDFs: ' . $e->getMessage();
error_log("DeclarationTVA: Exception in mergePDFs: " . $e->getMessage());
return false;
}
}
/**
* Translate status to French
*
* @param string $status Status in English
* @return string Status in French
*/
private function translateStatus($status)
{
$translations = array(
'draft' => 'Brouillon',
'validated' => 'Validé',
'submitted' => 'Soumis',
'approved' => 'Approuvé',
'rejected' => 'Rejeté'
);
return isset($translations[$status]) ? $translations[$status] : ucfirst($status);
}
/**
* Get mapping type label for display
*
* @param string $mapping_type Mapping type (e.g., '08_BASE', '08_VAT')
* @return string Display label
*/
private function getMappingTypeLabel($mapping_type)
{
if (strpos($mapping_type, '_BASE') !== false) {
return 'Base';
} elseif (strpos($mapping_type, '_VAT') !== false) {
return 'TVA';
} else {
return 'Standard';
}
}
/**
* Truncate text to fit in table cell
*
* @param string $text Text to truncate
* @param int $max_length Maximum length
* @return string Truncated text
*/
private function truncateText($text, $max_length)
{
if (strlen($text) <= $max_length) {
return $text;
}
return substr($text, 0, $max_length - 3) . '...';
}
/**
* Get detail page number for a line (for linking)
*
* @param string $line_code CA-3 line code
* @return int Page number
*/
private function getDetailPageNumber($line_code)
{
// This is a simplified approach - in a real implementation,
// you'd track page numbers as you create them
return 2; // Start from page 2 (after main form)
}
/**
* Generate detailed PDF with breakdown pages
*
* @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 generateDetailedPDF($output_path, $declaration, $ca3_data, $company)
{
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 Details ' . $declaration->declaration_number);
$pdf->SetSubject('French VAT Declaration - Detailed Breakdown');
// Set margins
$pdf->SetMargins(15, 15, 15);
$pdf->SetHeaderMargin(5);
$pdf->SetFooterMargin(10);
// Add title page
$pdf->AddPage();
$pdf->SetFont('helvetica', 'B', 16);
$pdf->Cell(0, 10, 'Détails de la Déclaration TVA CA-3', 0, 1, 'C');
$pdf->Ln(10);
$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: ' . $this->translateStatus($declaration->status), 0, 1);
$pdf->Ln(10);
// Add detailed breakdown pages
$this->addDetailPages($pdf, $declaration, $ca3_data);
// Output PDF
$pdf->Output($output_path, 'F');
return true;
} catch (Exception $e) {
$this->error = 'Detailed PDF generation failed: ' . $e->getMessage();
return false;
}
}
/** /**
* Upload custom template * Upload custom template
* *

View File

@ -76,7 +76,7 @@ class modDeclarationTVA extends DolibarrModules
$this->editor_squarred_logo = ''; // Must be image filename into the module/img directory followed with @modulename. Example: 'myimage.png@declarationtva' $this->editor_squarred_logo = ''; // Must be image filename into the module/img directory followed with @modulename. Example: 'myimage.png@declarationtva'
// Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated', 'experimental_deprecated' or a version string like 'x.y.z' // Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated', 'experimental_deprecated' or a version string like 'x.y.z'
$this->version = '2.0.0'; $this->version = '2.1.0';
// Url to the file with your last numberversion of this module // Url to the file with your last numberversion of this module
//$this->url_last_version = 'http://www.example.com/versionmodule.txt'; //$this->url_last_version = 'http://www.example.com/versionmodule.txt';

View File

@ -76,6 +76,28 @@ if ($action == 'export_pdf') {
} }
} }
if ($action == 'export_pdf_detailed') {
// Load PDF generator
require_once DOL_DOCUMENT_ROOT . '/custom/declarationtva/core/class/declarationtva_pdf.class.php';
$pdf_generator = new DeclarationTVA_PDF($db);
// Generate detailed PDF with breakdown pages
$pdf_path = $pdf_generator->generateDetailedCA3PDF($id);
if ($pdf_path && file_exists($pdf_path)) {
// Set headers for PDF download
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="CA3_Detailed_' . $declarationtva->declaration_number . '.pdf"');
header('Content-Length: ' . filesize($pdf_path));
// Output PDF file
readfile($pdf_path);
exit;
} else {
setEventMessages($pdf_generator->error ?: $langs->trans("ErrorGeneratingDetailedPDF"), null, 'errors');
}
}
// Fetch declaration // Fetch declaration
if ($declarationtva->fetch($id) < 0) { if ($declarationtva->fetch($id) < 0) {
setEventMessages($langs->trans("DeclarationNotFound"), null, 'errors'); setEventMessages($langs->trans("DeclarationNotFound"), null, 'errors');
@ -353,21 +375,21 @@ print '<th colspan="2">' . $langs->trans("Description") . '</th>';
print '<th class="right">' . $langs->trans("Amount") . '</th>'; print '<th class="right">' . $langs->trans("Amount") . '</th>';
print '</tr>'; print '</tr>';
$section_d_lines = array('25', '26', 'TD', '28', '29'); $section_d_lines = array('25', '26', '27', 'TD', '28', '32');
foreach ($section_d_lines as $line) { foreach ($section_d_lines as $line) {
$data = isset($ca3_data[$line]) ? $ca3_data[$line] : array('line_label' => '', 'vat_amount' => 0); $data = isset($ca3_data[$line]) ? $ca3_data[$line] : array('line_label' => '', 'vat_amount' => 0);
// Special handling for line TD // Prioritize database line_label over definitions (for calculated lines)
if ($line == 'TD') { $description = !empty($data['line_label']) ? $data['line_label'] : (isset($ca3_definitions[$line]) ? $ca3_definitions[$line]['label'] : '');
$description = 'TVA due (montant à payer)';
} else { // Special formatting for line 32 (bold)
$description = isset($ca3_definitions[$line]) ? $ca3_definitions[$line]['label'] : $data['line_label']; $is_line_32 = ($line == '32');
} $bold_style = $is_line_32 ? 'font-weight: bold;' : '';
print '<tr style="background-color: #ffe6e6 !important;">'; print '<tr style="background-color: #ffe6e6 !important;">';
print '<td style="background-color: #ffe6e6 !important; text-align: center;"><strong>' . $line . '</strong></td>'; print '<td style="background-color: #ffe6e6 !important; text-align: center; ' . $bold_style . '"><strong>' . $line . '</strong></td>';
print '<td colspan="2" style="background-color: #ffe6e6 !important;">' . $description . '</td>'; print '<td colspan="2" style="background-color: #ffe6e6 !important; ' . $bold_style . '">' . $description . '</td>';
print '<td class="right" style="background-color: #ffe6e6 !important;">' . formatAmount($data['vat_amount']) . '</td>'; print '<td class="right" style="background-color: #ffe6e6 !important; ' . $bold_style . '">' . formatAmount($data['vat_amount']) . '</td>';
print '</tr>'; print '</tr>';
} }
@ -390,8 +412,9 @@ print '<div class="center">';
// Recalculate button (always available) // Recalculate button (always available)
print '<a href="' . $_SERVER['PHP_SELF'] . '?id=' . $id . '&action=recalculate&token=' . newToken() . '" class="butAction">' . $langs->trans("Recalculate") . '</a> '; print '<a href="' . $_SERVER['PHP_SELF'] . '?id=' . $id . '&action=recalculate&token=' . newToken() . '" class="butAction">' . $langs->trans("Recalculate") . '</a> ';
// PDF Export button (always available) // PDF Export buttons (always available)
print '<a href="' . $_SERVER['PHP_SELF'] . '?id=' . $id . '&action=export_pdf" class="butAction">' . $langs->trans("ExportPDF") . '</a> '; print '<a href="' . $_SERVER['PHP_SELF'] . '?id=' . $id . '&action=export_pdf" class="butAction">' . $langs->trans("ExportPDF") . '</a> ';
print '<a href="' . $_SERVER['PHP_SELF'] . '?id=' . $id . '&action=export_pdf_detailed" class="butAction">' . $langs->trans("ExportPDFDetailed") . '</a> ';
if ($declarationtva->status == 'draft') { if ($declarationtva->status == 'draft') {
print '<a href="' . $_SERVER['PHP_SELF'] . '?id=' . $id . '&action=validate" class="butAction">' . $langs->trans("Validate") . '</a> '; print '<a href="' . $_SERVER['PHP_SELF'] . '?id=' . $id . '&action=validate" class="butAction">' . $langs->trans("Validate") . '</a> ';

View File

@ -484,6 +484,8 @@ SubtotalBaseAccounts = Subtotal Base Accounts
SubtotalVATAccounts = Subtotal VAT Accounts SubtotalVATAccounts = Subtotal VAT Accounts
GrandTotal = Grand Total GrandTotal = Grand Total
ExportPDF = Export PDF ExportPDF = Export PDF
ExportPDFDetailed = Export Detailed PDF
ErrorGeneratingDetailedPDF = Error generating detailed PDF
TemplateUploaded = PDF template uploaded successfully TemplateUploaded = PDF template uploaded successfully
TemplateReset = Reset to official template TemplateReset = Reset to official template
TemplateResetFailed = Error resetting to official template TemplateResetFailed = Error resetting to official template

View File

@ -456,6 +456,8 @@ SubtotalBaseAccounts = Sous-total comptes de base
SubtotalVATAccounts = Sous-total comptes de TVA SubtotalVATAccounts = Sous-total comptes de TVA
GrandTotal = Total général GrandTotal = Total général
ExportPDF = Exporter PDF ExportPDF = Exporter PDF
ExportPDFDetailed = Exporter PDF Détaillé
ErrorGeneratingDetailedPDF = Erreur lors de la génération du PDF détaillé
TemplateUploaded = Modèle PDF téléchargé avec succès TemplateUploaded = Modèle PDF téléchargé avec succès
TemplateReset = Retour au modèle officiel TemplateReset = Retour au modèle officiel
TemplateResetFailed = Erreur lors du retour au modèle officiel TemplateResetFailed = Erreur lors du retour au modèle officiel