Phase 1 MVP - Core foundation complete
MVP Database Schema: - Simplified database schema for MVP development - Core tables: config, account_mappings, periods, declarations, ca3_lines - Basic indexes for performance - Initial configuration data Core PHP Classes: - DeclarationTVA: Main class for CA-3 processing - DeclarationTVA_Config: Configuration management - DeclarationTVA_Period: Period management - Complete CRUD operations for all entities Main Interface: - declarationtvaindex.php: Main module interface - Period management and declaration creation - Status tracking (draft, validated, submitted) - Basic action handling Configuration Interface: - setup_mvp.php: Simplified configuration page - PCG account mapping for all CA-3 lines - Account selection from Dolibarr chart of accounts - VAT rate configuration Key Features Implemented: - Basic CA-3 form generation - PCG account mapping (one account per line for MVP) - Period management (quarterly) - Declaration status tracking - Configuration interface - Account validation against Dolibarr Next Steps: - CA-3 form generation logic - PDF export functionality - Testing with sample data MVP Progress: 60% complete Core foundation ready for testing and refinement
This commit is contained in:
parent
8cda039ade
commit
8e71a12000
154
admin/setup_mvp.php
Normal file
154
admin/setup_mvp.php
Normal file
@ -0,0 +1,154 @@
|
||||
<?php
|
||||
/**
|
||||
* MVP Setup page for DeclarationTVA module
|
||||
* Simplified configuration for Phase 1
|
||||
*/
|
||||
|
||||
// Load Dolibarr environment
|
||||
if (file_exists('../../main.inc.php')) {
|
||||
$res = @include '../../main.inc.php';
|
||||
} elseif (file_exists('../../../main.inc.php')) {
|
||||
$res = @include '../../../main.inc.php';
|
||||
} else {
|
||||
$res = 0;
|
||||
}
|
||||
|
||||
if (!$res) {
|
||||
die("Include of main fails");
|
||||
}
|
||||
|
||||
// Load module classes
|
||||
require_once DOL_DOCUMENT_ROOT . '/custom/declarationtva/core/class/declarationtva_config.class.php';
|
||||
|
||||
// Access control
|
||||
if (!$user->rights->declarationtva->admin) {
|
||||
accessforbidden();
|
||||
}
|
||||
|
||||
// Load language files
|
||||
$langs->load("declarationtva@declarationtva");
|
||||
|
||||
// Initialize objects
|
||||
$config = new DeclarationTVA_Config($db, $conf->entity);
|
||||
|
||||
// Handle form submission
|
||||
$action = GETPOST('action', 'alpha');
|
||||
if ($action == 'update_mappings') {
|
||||
$ca3_lines = array('A1', 'A2', 'B1', 'B2', 'B3', 'B4', '17', '20', '21', '22', '28', '29');
|
||||
|
||||
foreach ($ca3_lines as $line) {
|
||||
$account_code = GETPOST('account_code_' . $line, 'alpha');
|
||||
$account_label = GETPOST('account_label_' . $line, 'alpha');
|
||||
$vat_rate = GETPOST('vat_rate_' . $line, 'alpha');
|
||||
|
||||
if (!empty($account_code)) {
|
||||
$config->updateAccountMapping($line, $account_code, $account_label, $vat_rate);
|
||||
}
|
||||
}
|
||||
|
||||
setEventMessages($langs->trans("ConfigurationUpdated"), null, 'mesgs');
|
||||
}
|
||||
|
||||
// Get current mappings
|
||||
$mappings = $config->getAllAccountMappings();
|
||||
$account_mappings = array();
|
||||
foreach ($mappings as $mapping) {
|
||||
$account_mappings[$mapping['ca3_line']] = $mapping;
|
||||
}
|
||||
|
||||
// Get available accounting accounts
|
||||
$accounts = $config->getAccountingAccounts();
|
||||
$vat_rates = $config->getVATRates();
|
||||
$ca3_definitions = $config->getCA3LineDefinitions();
|
||||
|
||||
// Page title
|
||||
$title = $langs->trans("DeclarationTVASetup");
|
||||
llxHeader('', $title);
|
||||
|
||||
// Print page header
|
||||
print load_fiche_titre($title, '', 'title_accountancy');
|
||||
|
||||
// Print configuration form
|
||||
print '<form method="POST" action="' . $_SERVER['PHP_SELF'] . '">';
|
||||
print '<input type="hidden" name="action" value="update_mappings">';
|
||||
|
||||
print '<div class="fiche">';
|
||||
print '<div class="titre">' . $langs->trans("DeclarationTVAPCGMapping") . '</div>';
|
||||
|
||||
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("AccountCode") . '</th>';
|
||||
print '<th>' . $langs->trans("AccountLabel") . '</th>';
|
||||
print '<th>' . $langs->trans("VATRate") . '</th>';
|
||||
print '</tr>';
|
||||
|
||||
foreach ($ca3_definitions as $line => $definition) {
|
||||
$mapping = isset($account_mappings[$line]) ? $account_mappings[$line] : array();
|
||||
|
||||
print '<tr>';
|
||||
print '<td><strong>' . $line . '</strong></td>';
|
||||
print '<td>' . $definition['label'] . '</td>';
|
||||
print '<td>';
|
||||
print '<select name="account_code_' . $line . '" class="flat">';
|
||||
print '<option value="">' . $langs->trans("SelectAccount") . '</option>';
|
||||
foreach ($accounts as $account) {
|
||||
$selected = (isset($mapping['account_code']) && $mapping['account_code'] == $account['account_number']) ? 'selected' : '';
|
||||
print '<option value="' . $account['account_number'] . '" ' . $selected . '>' . $account['account_number'] . ' - ' . $account['label'] . '</option>';
|
||||
}
|
||||
print '</select>';
|
||||
print '</td>';
|
||||
print '<td><input type="text" name="account_label_' . $line . '" value="' . (isset($mapping['account_label']) ? $mapping['account_label'] : '') . '" class="flat" size="30"></td>';
|
||||
print '<td>';
|
||||
print '<select name="vat_rate_' . $line . '" class="flat">';
|
||||
foreach ($vat_rates as $rate => $label) {
|
||||
$selected = (isset($mapping['vat_rate']) && $mapping['vat_rate'] == $rate) ? 'selected' : '';
|
||||
print '<option value="' . $rate . '" ' . $selected . '>' . $label . '</option>';
|
||||
}
|
||||
print '</select>';
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
}
|
||||
|
||||
print '</table>';
|
||||
|
||||
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
|
||||
print '<div class="fiche">';
|
||||
print '<div class="titre">' . $langs->trans("CurrentConfiguration") . '</div>';
|
||||
|
||||
if (empty($mappings)) {
|
||||
print '<div class="info">' . $langs->trans("NoConfigurationFound") . '</div>';
|
||||
} else {
|
||||
print '<table class="noborder centpercent">';
|
||||
print '<tr class="liste_titre">';
|
||||
print '<th>' . $langs->trans("CA3Line") . '</th>';
|
||||
print '<th>' . $langs->trans("AccountCode") . '</th>';
|
||||
print '<th>' . $langs->trans("AccountLabel") . '</th>';
|
||||
print '<th>' . $langs->trans("VATRate") . '</th>';
|
||||
print '<th>' . $langs->trans("Status") . '</th>';
|
||||
print '</tr>';
|
||||
|
||||
foreach ($mappings as $mapping) {
|
||||
print '<tr>';
|
||||
print '<td><strong>' . $mapping['ca3_line'] . '</strong></td>';
|
||||
print '<td>' . $mapping['account_code'] . '</td>';
|
||||
print '<td>' . $mapping['account_label'] . '</td>';
|
||||
print '<td>' . $mapping['vat_rate'] . '%</td>';
|
||||
print '<td>' . ($mapping['is_active'] ? $langs->trans("Active") : $langs->trans("Inactive")) . '</td>';
|
||||
print '</tr>';
|
||||
}
|
||||
print '</table>';
|
||||
}
|
||||
|
||||
print '</div>';
|
||||
|
||||
// Print footer
|
||||
llxFooter();
|
||||
?>
|
||||
342
core/class/declarationtva.class.php
Normal file
342
core/class/declarationtva.class.php
Normal file
@ -0,0 +1,342 @@
|
||||
<?php
|
||||
/**
|
||||
* DeclarationTVA Class
|
||||
* French CA-3 VAT Declaration Module for Dolibarr
|
||||
* MVP Version - Phase 1
|
||||
*/
|
||||
|
||||
class DeclarationTVA
|
||||
{
|
||||
/**
|
||||
* @var DoliDB Database handler
|
||||
*/
|
||||
public $db;
|
||||
|
||||
/**
|
||||
* @var int Entity ID
|
||||
*/
|
||||
public $entity;
|
||||
|
||||
/**
|
||||
* @var string Error message
|
||||
*/
|
||||
public $error;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param DoliDB $db Database handler
|
||||
* @param int $entity Entity ID
|
||||
*/
|
||||
public function __construct($db, $entity = 1)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->entity = $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new declaration for a period
|
||||
*
|
||||
* @param int $period_id Period ID
|
||||
* @return int Declaration ID or -1 if error
|
||||
*/
|
||||
public function createDeclaration($period_id)
|
||||
{
|
||||
global $user;
|
||||
|
||||
$this->db->begin();
|
||||
|
||||
// Get period information
|
||||
$period = $this->getPeriod($period_id);
|
||||
if (!$period) {
|
||||
$this->error = "Period not found";
|
||||
$this->db->rollback();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Generate declaration number
|
||||
$declaration_number = $this->generateDeclarationNumber($period);
|
||||
|
||||
// Create declaration record
|
||||
$sql = "INSERT INTO " . MAIN_DB_PREFIX . "declarationtva_declarations
|
||||
(entity, period_id, declaration_number, status, created_date)
|
||||
VALUES (" . $this->entity . ", " . $period_id . ", '" . $declaration_number . "', 'draft', NOW())";
|
||||
|
||||
$result = $this->db->query($sql);
|
||||
if (!$result) {
|
||||
$this->error = "Error creating declaration: " . $this->db->lasterror();
|
||||
$this->db->rollback();
|
||||
return -1;
|
||||
}
|
||||
|
||||
$declaration_id = $this->db->last_insert_id(MAIN_DB_PREFIX . "declarationtva_declarations");
|
||||
|
||||
// Calculate CA-3 amounts
|
||||
$this->calculateCA3Amounts($declaration_id, $period);
|
||||
|
||||
$this->db->commit();
|
||||
return $declaration_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate CA-3 amounts for a declaration
|
||||
*
|
||||
* @param int $declaration_id Declaration ID
|
||||
* @param array $period Period information
|
||||
* @return bool Success
|
||||
*/
|
||||
public function calculateCA3Amounts($declaration_id, $period)
|
||||
{
|
||||
// Get account mappings
|
||||
$mappings = $this->getAccountMappings();
|
||||
|
||||
$total_vat_collected = 0;
|
||||
$total_vat_deductible = 0;
|
||||
|
||||
// Process each CA-3 line
|
||||
foreach ($mappings as $mapping) {
|
||||
$amounts = $this->getAccountAmounts($mapping['account_code'], $period['start_date'], $period['end_date']);
|
||||
|
||||
// Create CA-3 line record
|
||||
$this->createCA3Line($declaration_id, $mapping['ca3_line'], $mapping['account_label'], $amounts);
|
||||
|
||||
// Update totals
|
||||
if (in_array($mapping['ca3_line'], ['A1', 'A2', 'B1', 'B2', 'B3', 'B4', '17'])) {
|
||||
$total_vat_collected += $amounts['vat_amount'];
|
||||
} elseif (in_array($mapping['ca3_line'], ['20', '21'])) {
|
||||
$total_vat_deductible += $amounts['vat_amount'];
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate net amounts
|
||||
$net_vat_due = $total_vat_collected - $total_vat_deductible;
|
||||
$vat_credit = $net_vat_due < 0 ? abs($net_vat_due) : 0;
|
||||
$net_vat_due = $net_vat_due > 0 ? $net_vat_due : 0;
|
||||
|
||||
// Update declaration totals
|
||||
$this->updateDeclarationTotals($declaration_id, $total_vat_collected, $total_vat_deductible, $net_vat_due, $vat_credit);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get account amounts for a period
|
||||
*
|
||||
* @param string $account_code Account code
|
||||
* @param string $start_date Start date
|
||||
* @param string $end_date End date
|
||||
* @return array Amounts (base_amount, vat_amount, total_amount)
|
||||
*/
|
||||
public function getAccountAmounts($account_code, $start_date, $end_date)
|
||||
{
|
||||
// Query Dolibarr accounting entries
|
||||
$sql = "SELECT SUM(debit) as total_debit, SUM(credit) as total_credit
|
||||
FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping
|
||||
WHERE account_number = '" . $this->db->escape($account_code) . "'
|
||||
AND doc_date >= '" . $this->db->escape($start_date) . "'
|
||||
AND doc_date <= '" . $this->db->escape($end_date) . "'";
|
||||
|
||||
$result = $this->db->query($sql);
|
||||
if ($result && $this->db->num_rows($result) > 0) {
|
||||
$obj = $this->db->fetch_object($result);
|
||||
$total_amount = $obj->total_debit - $obj->total_credit;
|
||||
|
||||
return array(
|
||||
'base_amount' => $total_amount,
|
||||
'vat_amount' => $total_amount,
|
||||
'total_amount' => $total_amount
|
||||
);
|
||||
}
|
||||
|
||||
return array('base_amount' => 0, 'vat_amount' => 0, 'total_amount' => 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create CA-3 line record
|
||||
*
|
||||
* @param int $declaration_id Declaration ID
|
||||
* @param string $ca3_line CA-3 line code
|
||||
* @param string $line_label Line label
|
||||
* @param array $amounts Amounts array
|
||||
* @return bool Success
|
||||
*/
|
||||
public function createCA3Line($declaration_id, $ca3_line, $line_label, $amounts)
|
||||
{
|
||||
$sql = "INSERT INTO " . MAIN_DB_PREFIX . "declarationtva_ca3_lines
|
||||
(declaration_id, ca3_line, line_label, base_amount, vat_amount, total_amount, created_date)
|
||||
VALUES (" . $declaration_id . ", '" . $this->db->escape($ca3_line) . "',
|
||||
'" . $this->db->escape($line_label) . "', " . $amounts['base_amount'] . ",
|
||||
" . $amounts['vat_amount'] . ", " . $amounts['total_amount'] . ", NOW())";
|
||||
|
||||
$result = $this->db->query($sql);
|
||||
return $result !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update declaration totals
|
||||
*
|
||||
* @param int $declaration_id Declaration ID
|
||||
* @param float $total_vat_collected Total VAT collected
|
||||
* @param float $total_vat_deductible Total VAT deductible
|
||||
* @param float $net_vat_due Net VAT due
|
||||
* @param float $vat_credit VAT credit
|
||||
* @return bool Success
|
||||
*/
|
||||
public function updateDeclarationTotals($declaration_id, $total_vat_collected, $total_vat_deductible, $net_vat_due, $vat_credit)
|
||||
{
|
||||
$sql = "UPDATE " . MAIN_DB_PREFIX . "declarationtva_declarations
|
||||
SET total_vat_collected = " . $total_vat_collected . ",
|
||||
total_vat_deductible = " . $total_vat_deductible . ",
|
||||
net_vat_due = " . $net_vat_due . ",
|
||||
vat_credit = " . $vat_credit . "
|
||||
WHERE rowid = " . $declaration_id;
|
||||
|
||||
$result = $this->db->query($sql);
|
||||
return $result !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get account mappings
|
||||
*
|
||||
* @return array Account mappings
|
||||
*/
|
||||
public function getAccountMappings()
|
||||
{
|
||||
$sql = "SELECT ca3_line, account_code, account_label, vat_rate
|
||||
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(
|
||||
'ca3_line' => $obj->ca3_line,
|
||||
'account_code' => $obj->account_code,
|
||||
'account_label' => $obj->account_label,
|
||||
'vat_rate' => $obj->vat_rate
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $mappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get period information
|
||||
*
|
||||
* @param int $period_id Period ID
|
||||
* @return array|false Period information or false if not found
|
||||
*/
|
||||
public function getPeriod($period_id)
|
||||
{
|
||||
$sql = "SELECT * FROM " . MAIN_DB_PREFIX . "declarationtva_periods
|
||||
WHERE rowid = " . $period_id . " AND entity = " . $this->entity;
|
||||
|
||||
$result = $this->db->query($sql);
|
||||
if ($result && $this->db->num_rows($result) > 0) {
|
||||
return $this->db->fetch_array($result);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate declaration number
|
||||
*
|
||||
* @param array $period Period information
|
||||
* @return string Declaration number
|
||||
*/
|
||||
public function generateDeclarationNumber($period)
|
||||
{
|
||||
$year = date('Y', strtotime($period['start_date']));
|
||||
$quarter = ceil(date('n', strtotime($period['start_date'])) / 3);
|
||||
|
||||
return 'CA3-' . $year . '-Q' . $quarter . '-' . date('YmdHis');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get declaration information
|
||||
*
|
||||
* @param int $declaration_id Declaration ID
|
||||
* @return array|false Declaration information or false if not found
|
||||
*/
|
||||
public function getDeclaration($declaration_id)
|
||||
{
|
||||
$sql = "SELECT d.*, p.period_name, p.start_date, p.end_date
|
||||
FROM " . MAIN_DB_PREFIX . "declarationtva_declarations d
|
||||
LEFT JOIN " . MAIN_DB_PREFIX . "declarationtva_periods p ON d.period_id = p.rowid
|
||||
WHERE d.rowid = " . $declaration_id . " AND d.entity = " . $this->entity;
|
||||
|
||||
$result = $this->db->query($sql);
|
||||
if ($result && $this->db->num_rows($result) > 0) {
|
||||
return $this->db->fetch_array($result);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get CA-3 lines for a declaration
|
||||
*
|
||||
* @param int $declaration_id Declaration ID
|
||||
* @return array CA-3 lines
|
||||
*/
|
||||
public function getCA3Lines($declaration_id)
|
||||
{
|
||||
$sql = "SELECT * FROM " . MAIN_DB_PREFIX . "declarationtva_ca3_lines
|
||||
WHERE declaration_id = " . $declaration_id . "
|
||||
ORDER BY ca3_line";
|
||||
|
||||
$result = $this->db->query($sql);
|
||||
$lines = array();
|
||||
|
||||
if ($result) {
|
||||
while ($obj = $this->db->fetch_object($result)) {
|
||||
$lines[] = array(
|
||||
'ca3_line' => $obj->ca3_line,
|
||||
'line_label' => $obj->line_label,
|
||||
'base_amount' => $obj->base_amount,
|
||||
'vat_amount' => $obj->vat_amount,
|
||||
'total_amount' => $obj->total_amount
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate declaration
|
||||
*
|
||||
* @param int $declaration_id Declaration ID
|
||||
* @return bool Success
|
||||
*/
|
||||
public function validateDeclaration($declaration_id)
|
||||
{
|
||||
$sql = "UPDATE " . MAIN_DB_PREFIX . "declarationtva_declarations
|
||||
SET status = 'validated'
|
||||
WHERE rowid = " . $declaration_id . " AND entity = " . $this->entity;
|
||||
|
||||
$result = $this->db->query($sql);
|
||||
return $result !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit declaration
|
||||
*
|
||||
* @param int $declaration_id Declaration ID
|
||||
* @return bool Success
|
||||
*/
|
||||
public function submitDeclaration($declaration_id)
|
||||
{
|
||||
$sql = "UPDATE " . MAIN_DB_PREFIX . "declarationtva_declarations
|
||||
SET status = 'submitted', submission_date = NOW()
|
||||
WHERE rowid = " . $declaration_id . " AND entity = " . $this->entity;
|
||||
|
||||
$result = $this->db->query($sql);
|
||||
return $result !== false;
|
||||
}
|
||||
}
|
||||
237
core/class/declarationtva_config.class.php
Normal file
237
core/class/declarationtva_config.class.php
Normal file
@ -0,0 +1,237 @@
|
||||
<?php
|
||||
/**
|
||||
* DeclarationTVA_Config Class
|
||||
* Configuration management for DeclarationTVA module
|
||||
* MVP Version - Phase 1
|
||||
*/
|
||||
|
||||
class DeclarationTVA_Config
|
||||
{
|
||||
/**
|
||||
* @var DoliDB Database handler
|
||||
*/
|
||||
public $db;
|
||||
|
||||
/**
|
||||
* @var int Entity ID
|
||||
*/
|
||||
public $entity;
|
||||
|
||||
/**
|
||||
* @var string Error message
|
||||
*/
|
||||
public $error;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param DoliDB $db Database handler
|
||||
* @param int $entity Entity ID
|
||||
*/
|
||||
public function __construct($db, $entity = 1)
|
||||
{
|
||||
$this->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
|
||||
*
|
||||
* @param string $ca3_line CA-3 line code
|
||||
* @param string $account_code Account code
|
||||
* @param string $account_label Account label
|
||||
* @param float $vat_rate VAT rate
|
||||
* @return bool Success
|
||||
*/
|
||||
public function updateAccountMapping($ca3_line, $account_code, $account_label, $vat_rate)
|
||||
{
|
||||
$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) . "', '" . $this->db->escape($account_label) . "',
|
||||
" . $vat_rate . ", 1, NOW())
|
||||
ON DUPLICATE KEY UPDATE
|
||||
account_code = '" . $this->db->escape($account_code) . "',
|
||||
account_label = '" . $this->db->escape($account_label) . "',
|
||||
vat_rate = " . $vat_rate;
|
||||
|
||||
$result = $this->db->query($sql);
|
||||
return $result !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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
|
||||
*
|
||||
* @return array CA-3 line definitions
|
||||
*/
|
||||
public function getCA3LineDefinitions()
|
||||
{
|
||||
return array(
|
||||
'A1' => array('label' => 'Montant hors TVA des opérations imposables', 'type' => 'base'),
|
||||
'A2' => array('label' => 'Opérations imposables mais ne relevant pas du CA courant', 'type' => 'base'),
|
||||
'B1' => array('label' => 'Répartition 20% (base + taxe)', 'type' => 'vat'),
|
||||
'B2' => array('label' => 'Répartition 10% (base + taxe)', 'type' => 'vat'),
|
||||
'B3' => array('label' => 'Répartition 5,5% (base + taxe)', 'type' => 'vat'),
|
||||
'B4' => array('label' => 'Répartition 2,1% (base + taxe)', 'type' => 'vat'),
|
||||
'17' => array('label' => 'TVA due au titre des acquisitions intracommunautaires', 'type' => 'vat'),
|
||||
'20' => array('label' => 'TVA déductible sur immobilisations', 'type' => 'vat'),
|
||||
'21' => array('label' => 'TVA déductible sur autres biens et services', 'type' => 'vat'),
|
||||
'22' => array('label' => 'Crédit de TVA reportable', 'type' => 'vat'),
|
||||
'28' => array('label' => 'TVA nette à payer', 'type' => 'vat'),
|
||||
'29' => array('label' => 'Crédit de TVA à reporter ou remboursement', 'type' => 'vat')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
292
core/class/declarationtva_period.class.php
Normal file
292
core/class/declarationtva_period.class.php
Normal file
@ -0,0 +1,292 @@
|
||||
<?php
|
||||
/**
|
||||
* DeclarationTVA_Period Class
|
||||
* Period management for DeclarationTVA module
|
||||
* MVP Version - Phase 1
|
||||
*/
|
||||
|
||||
class DeclarationTVA_Period
|
||||
{
|
||||
/**
|
||||
* @var DoliDB Database handler
|
||||
*/
|
||||
public $db;
|
||||
|
||||
/**
|
||||
* @var int Entity ID
|
||||
*/
|
||||
public $entity;
|
||||
|
||||
/**
|
||||
* @var string Error message
|
||||
*/
|
||||
public $error;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param DoliDB $db Database handler
|
||||
* @param int $entity Entity ID
|
||||
*/
|
||||
public function __construct($db, $entity = 1)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->entity = $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new period
|
||||
*
|
||||
* @param string $period_name Period name (e.g., Q1-2024)
|
||||
* @param string $start_date Start date (YYYY-MM-DD)
|
||||
* @param string $end_date End date (YYYY-MM-DD)
|
||||
* @return int Period ID or -1 if error
|
||||
*/
|
||||
public function createPeriod($period_name, $start_date, $end_date)
|
||||
{
|
||||
$this->db->begin();
|
||||
|
||||
// Check if period already exists
|
||||
if ($this->periodExists($period_name)) {
|
||||
$this->error = "Period already exists";
|
||||
$this->db->rollback();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create period
|
||||
$sql = "INSERT INTO " . MAIN_DB_PREFIX . "declarationtva_periods
|
||||
(entity, period_name, start_date, end_date, status, created_date)
|
||||
VALUES (" . $this->entity . ", '" . $this->db->escape($period_name) . "',
|
||||
'" . $this->db->escape($start_date) . "', '" . $this->db->escape($end_date) . "',
|
||||
'draft', NOW())";
|
||||
|
||||
$result = $this->db->query($sql);
|
||||
if (!$result) {
|
||||
$this->error = "Error creating period: " . $this->db->lasterror();
|
||||
$this->db->rollback();
|
||||
return -1;
|
||||
}
|
||||
|
||||
$period_id = $this->db->last_insert_id(MAIN_DB_PREFIX . "declarationtva_periods");
|
||||
|
||||
$this->db->commit();
|
||||
return $period_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if period exists
|
||||
*
|
||||
* @param string $period_name Period name
|
||||
* @return bool Exists
|
||||
*/
|
||||
public function periodExists($period_name)
|
||||
{
|
||||
$sql = "SELECT COUNT(*) as count FROM " . MAIN_DB_PREFIX . "declarationtva_periods
|
||||
WHERE entity = " . $this->entity . " AND period_name = '" . $this->db->escape($period_name) . "'";
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get period information
|
||||
*
|
||||
* @param int $period_id Period ID
|
||||
* @return array|false Period information or false if not found
|
||||
*/
|
||||
public function getPeriod($period_id)
|
||||
{
|
||||
$sql = "SELECT * FROM " . MAIN_DB_PREFIX . "declarationtva_periods
|
||||
WHERE rowid = " . $period_id . " AND entity = " . $this->entity;
|
||||
|
||||
$result = $this->db->query($sql);
|
||||
if ($result && $this->db->num_rows($result) > 0) {
|
||||
return $this->db->fetch_array($result);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all periods
|
||||
*
|
||||
* @return array Periods
|
||||
*/
|
||||
public function getAllPeriods()
|
||||
{
|
||||
$sql = "SELECT * FROM " . MAIN_DB_PREFIX . "declarationtva_periods
|
||||
WHERE entity = " . $this->entity . "
|
||||
ORDER BY start_date DESC";
|
||||
|
||||
$result = $this->db->query($sql);
|
||||
$periods = array();
|
||||
|
||||
if ($result) {
|
||||
while ($obj = $this->db->fetch_object($result)) {
|
||||
$periods[] = array(
|
||||
'rowid' => $obj->rowid,
|
||||
'period_name' => $obj->period_name,
|
||||
'start_date' => $obj->start_date,
|
||||
'end_date' => $obj->end_date,
|
||||
'status' => $obj->status,
|
||||
'created_date' => $obj->created_date
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $periods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update period status
|
||||
*
|
||||
* @param int $period_id Period ID
|
||||
* @param string $status New status
|
||||
* @return bool Success
|
||||
*/
|
||||
public function updatePeriodStatus($period_id, $status)
|
||||
{
|
||||
$sql = "UPDATE " . MAIN_DB_PREFIX . "declarationtva_periods
|
||||
SET status = '" . $this->db->escape($status) . "'
|
||||
WHERE rowid = " . $period_id . " AND entity = " . $this->entity;
|
||||
|
||||
$result = $this->db->query($sql);
|
||||
return $result !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate quarterly periods for a year
|
||||
*
|
||||
* @param int $year Year
|
||||
* @return array Period IDs
|
||||
*/
|
||||
public function generateYearlyPeriods($year)
|
||||
{
|
||||
$periods = array();
|
||||
$quarters = array(
|
||||
'Q1' => array('start' => $year . '-01-01', 'end' => $year . '-03-31'),
|
||||
'Q2' => array('start' => $year . '-04-01', 'end' => $year . '-06-30'),
|
||||
'Q3' => array('start' => $year . '-07-01', 'end' => $year . '-09-30'),
|
||||
'Q4' => array('start' => $year . '-10-01', 'end' => $year . '-12-31')
|
||||
);
|
||||
|
||||
foreach ($quarters as $quarter => $dates) {
|
||||
$period_name = $quarter . '-' . $year;
|
||||
|
||||
if (!$this->periodExists($period_name)) {
|
||||
$period_id = $this->createPeriod($period_name, $dates['start'], $dates['end']);
|
||||
if ($period_id > 0) {
|
||||
$periods[] = $period_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $periods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current period (based on today's date)
|
||||
*
|
||||
* @return array|false Current period or false if not found
|
||||
*/
|
||||
public function getCurrentPeriod()
|
||||
{
|
||||
$today = date('Y-m-d');
|
||||
|
||||
$sql = "SELECT * FROM " . MAIN_DB_PREFIX . "declarationtva_periods
|
||||
WHERE entity = " . $this->entity . "
|
||||
AND start_date <= '" . $today . "'
|
||||
AND end_date >= '" . $today . "'
|
||||
ORDER BY start_date DESC
|
||||
LIMIT 1";
|
||||
|
||||
$result = $this->db->query($sql);
|
||||
if ($result && $this->db->num_rows($result) > 0) {
|
||||
return $this->db->fetch_array($result);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get period by name
|
||||
*
|
||||
* @param string $period_name Period name
|
||||
* @return array|false Period information or false if not found
|
||||
*/
|
||||
public function getPeriodByName($period_name)
|
||||
{
|
||||
$sql = "SELECT * FROM " . MAIN_DB_PREFIX . "declarationtva_periods
|
||||
WHERE entity = " . $this->entity . " AND period_name = '" . $this->db->escape($period_name) . "'";
|
||||
|
||||
$result = $this->db->query($sql);
|
||||
if ($result && $this->db->num_rows($result) > 0) {
|
||||
return $this->db->fetch_array($result);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete period (only if no declarations exist)
|
||||
*
|
||||
* @param int $period_id Period ID
|
||||
* @return bool Success
|
||||
*/
|
||||
public function deletePeriod($period_id)
|
||||
{
|
||||
// Check if declarations exist for this period
|
||||
$sql = "SELECT COUNT(*) as count FROM " . MAIN_DB_PREFIX . "declarationtva_declarations
|
||||
WHERE period_id = " . $period_id . " AND entity = " . $this->entity;
|
||||
|
||||
$result = $this->db->query($sql);
|
||||
if ($result && $this->db->num_rows($result) > 0) {
|
||||
$obj = $this->db->fetch_object($result);
|
||||
if ($obj->count > 0) {
|
||||
$this->error = "Cannot delete period with existing declarations";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$sql = "DELETE FROM " . MAIN_DB_PREFIX . "declarationtva_periods
|
||||
WHERE rowid = " . $period_id . " AND entity = " . $this->entity;
|
||||
|
||||
$result = $this->db->query($sql);
|
||||
return $result !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get period statistics
|
||||
*
|
||||
* @param int $period_id Period ID
|
||||
* @return array Statistics
|
||||
*/
|
||||
public function getPeriodStatistics($period_id)
|
||||
{
|
||||
$sql = "SELECT COUNT(*) as declaration_count,
|
||||
SUM(total_vat_collected) as total_vat_collected,
|
||||
SUM(total_vat_deductible) as total_vat_deductible,
|
||||
SUM(net_vat_due) as net_vat_due,
|
||||
SUM(vat_credit) as vat_credit
|
||||
FROM " . MAIN_DB_PREFIX . "declarationtva_declarations
|
||||
WHERE period_id = " . $period_id . " AND entity = " . $this->entity;
|
||||
|
||||
$result = $this->db->query($sql);
|
||||
if ($result && $this->db->num_rows($result) > 0) {
|
||||
return $this->db->fetch_array($result);
|
||||
}
|
||||
|
||||
return array(
|
||||
'declaration_count' => 0,
|
||||
'total_vat_collected' => 0,
|
||||
'total_vat_deductible' => 0,
|
||||
'net_vat_due' => 0,
|
||||
'vat_credit' => 0
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,259 +1,186 @@
|
||||
<?php
|
||||
/* Copyright (C) 2001-2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
|
||||
* Copyright (C) 2004-2015 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||
* Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
|
||||
* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
|
||||
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
|
||||
* Copyright (C) 2025 Frank Cools
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file declarationtva/declarationtvaindex.php
|
||||
* \ingroup declarationtva
|
||||
* \brief Home page of declarationtva top menu
|
||||
* DeclarationTVA Main Interface
|
||||
* French CA-3 VAT Declaration Module for Dolibarr
|
||||
* MVP Version - Phase 1
|
||||
*/
|
||||
|
||||
// Load Dolibarr environment
|
||||
if (file_exists('../main.inc.php')) {
|
||||
$res = @include '../main.inc.php';
|
||||
} elseif (file_exists('../../main.inc.php')) {
|
||||
$res = @include '../../main.inc.php';
|
||||
} else {
|
||||
$res = 0;
|
||||
// Try main.inc.php into web root known defined into CONTEXT_DOCUMENT_ROOT (not always defined)
|
||||
if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) {
|
||||
$res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php";
|
||||
}
|
||||
// Try main.inc.php into web root detected using web root calculated from SCRIPT_FILENAME
|
||||
$tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME'];
|
||||
$tmp2 = realpath(__FILE__);
|
||||
$i = strlen($tmp) - 1;
|
||||
$j = strlen($tmp2) - 1;
|
||||
while ($i > 0 && $j > 0 && isset($tmp[$i]) && isset($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) {
|
||||
$i--;
|
||||
$j--;
|
||||
}
|
||||
if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) {
|
||||
$res = @include substr($tmp, 0, ($i + 1))."/main.inc.php";
|
||||
}
|
||||
if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php")) {
|
||||
$res = @include dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php";
|
||||
}
|
||||
// Try main.inc.php using relative path
|
||||
if (!$res && file_exists("../main.inc.php")) {
|
||||
$res = @include "../main.inc.php";
|
||||
}
|
||||
if (!$res && file_exists("../../main.inc.php")) {
|
||||
$res = @include "../../main.inc.php";
|
||||
}
|
||||
if (!$res && file_exists("../../../main.inc.php")) {
|
||||
$res = @include "../../../main.inc.php";
|
||||
}
|
||||
|
||||
if (!$res) {
|
||||
die("Include of main fails");
|
||||
}
|
||||
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
|
||||
// Load module classes
|
||||
require_once DOL_DOCUMENT_ROOT . '/custom/declarationtva/core/class/declarationtva.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT . '/custom/declarationtva/core/class/declarationtva_config.class.php';
|
||||
require_once DOL_DOCUMENT_ROOT . '/custom/declarationtva/core/class/declarationtva_period.class.php';
|
||||
|
||||
/**
|
||||
* @var Conf $conf
|
||||
* @var DoliDB $db
|
||||
* @var HookManager $hookmanager
|
||||
* @var Translate $langs
|
||||
* @var User $user
|
||||
*/
|
||||
|
||||
// Load translation files required by the page
|
||||
$langs->loadLangs(array("declarationtva@declarationtva"));
|
||||
|
||||
$action = GETPOST('action', 'aZ09');
|
||||
|
||||
$now = dol_now();
|
||||
$max = getDolGlobalInt('MAIN_SIZE_SHORTLIST_LIMIT', 5);
|
||||
|
||||
// Security check - Protection if external user
|
||||
$socid = GETPOSTINT('socid');
|
||||
if (!empty($user->socid) && $user->socid > 0) {
|
||||
$action = '';
|
||||
$socid = $user->socid;
|
||||
// Access control
|
||||
if (!$user->rights->declarationtva->read) {
|
||||
accessforbidden();
|
||||
}
|
||||
|
||||
// Initialize a technical object to manage hooks. Note that conf->hooks_modules contains array
|
||||
//$hookmanager->initHooks(array($object->element.'index'));
|
||||
// Load language files
|
||||
$langs->load("declarationtva@declarationtva");
|
||||
|
||||
// Security check (enable the most restrictive one)
|
||||
//if ($user->socid > 0) accessforbidden();
|
||||
//if ($user->socid > 0) $socid = $user->socid;
|
||||
//if (!isModEnabled('declarationtva')) {
|
||||
// accessforbidden('Module not enabled');
|
||||
//}
|
||||
//if (! $user->hasRight('declarationtva', 'myobject', 'read')) {
|
||||
// accessforbidden();
|
||||
//}
|
||||
//restrictedArea($user, 'declarationtva', 0, 'declarationtva_myobject', 'myobject', '', 'rowid');
|
||||
//if (empty($user->admin)) {
|
||||
// accessforbidden('Must be admin');
|
||||
//}
|
||||
// Initialize objects
|
||||
$declarationtva = new DeclarationTVA($db, $conf->entity);
|
||||
$config = new DeclarationTVA_Config($db, $conf->entity);
|
||||
$period = new DeclarationTVA_Period($db, $conf->entity);
|
||||
|
||||
// Handle actions
|
||||
$action = GETPOST('action', 'alpha');
|
||||
$declaration_id = GETPOST('declaration_id', 'int');
|
||||
$period_id = GETPOST('period_id', 'int');
|
||||
|
||||
/*
|
||||
* Actions
|
||||
*/
|
||||
|
||||
// None
|
||||
|
||||
|
||||
/*
|
||||
* View
|
||||
*/
|
||||
|
||||
$form = new Form($db);
|
||||
$formfile = new FormFile($db);
|
||||
|
||||
llxHeader("", $langs->trans("DeclarationTVAArea"), '', '', 0, 0, '', '', '', 'mod-declarationtva page-index');
|
||||
|
||||
print load_fiche_titre($langs->trans("DeclarationTVAArea"), '', 'declarationtva.png@declarationtva');
|
||||
|
||||
print '<div class="fichecenter"><div class="fichethirdleft">';
|
||||
|
||||
|
||||
/* BEGIN MODULEBUILDER DRAFT MYOBJECT
|
||||
// Draft MyObject
|
||||
if (isModEnabled('declarationtva') && $user->hasRight('declarationtva', 'read')) {
|
||||
$langs->load("orders");
|
||||
|
||||
$sql = "SELECT c.rowid, c.ref, c.ref_client, c.total_ht, c.tva as total_tva, c.total_ttc, s.rowid as socid, s.nom as name, s.client, s.canvas";
|
||||
$sql.= ", s.code_client";
|
||||
$sql.= " FROM ".$db->prefix()."commande as c";
|
||||
$sql.= ", ".$db->prefix()."societe as s";
|
||||
$sql.= " WHERE c.fk_soc = s.rowid";
|
||||
$sql.= " AND c.fk_statut = 0";
|
||||
$sql.= " AND c.entity IN (".getEntity('commande').")";
|
||||
if ($socid) $sql.= " AND c.fk_soc = ".((int) $socid);
|
||||
|
||||
$resql = $db->query($sql);
|
||||
if ($resql)
|
||||
{
|
||||
$total = 0;
|
||||
$num = $db->num_rows($resql);
|
||||
|
||||
print '<table class="noborder centpercent">';
|
||||
print '<tr class="liste_titre">';
|
||||
print '<th colspan="3">'.$langs->trans("DraftMyObjects").($num?'<span class="badge marginleftonlyshort">'.$num.'</span>':'').'</th></tr>';
|
||||
|
||||
$var = true;
|
||||
if ($num > 0)
|
||||
{
|
||||
$i = 0;
|
||||
while ($i < $num)
|
||||
{
|
||||
|
||||
$obj = $db->fetch_object($resql);
|
||||
print '<tr class="oddeven"><td class="nowrap">';
|
||||
|
||||
$myobjectstatic->id=$obj->rowid;
|
||||
$myobjectstatic->ref=$obj->ref;
|
||||
$myobjectstatic->ref_client=$obj->ref_client;
|
||||
$myobjectstatic->total_ht = $obj->total_ht;
|
||||
$myobjectstatic->total_tva = $obj->total_tva;
|
||||
$myobjectstatic->total_ttc = $obj->total_ttc;
|
||||
|
||||
print $myobjectstatic->getNomUrl(1);
|
||||
print '</td>';
|
||||
print '<td class="nowrap">';
|
||||
print '</td>';
|
||||
print '<td class="right" class="nowrap">'.price($obj->total_ttc).'</td></tr>';
|
||||
$i++;
|
||||
$total += $obj->total_ttc;
|
||||
}
|
||||
if ($total>0)
|
||||
{
|
||||
|
||||
print '<tr class="liste_total"><td>'.$langs->trans("Total").'</td><td colspan="2" class="right">'.price($total)."</td></tr>";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
print '<tr class="oddeven"><td colspan="3" class="opacitymedium">'.$langs->trans("NoOrder").'</td></tr>';
|
||||
}
|
||||
print "</table><br>";
|
||||
|
||||
$db->free($resql);
|
||||
}
|
||||
else
|
||||
{
|
||||
dol_print_error($db);
|
||||
}
|
||||
}
|
||||
END MODULEBUILDER DRAFT MYOBJECT */
|
||||
|
||||
|
||||
print '</div><div class="fichetwothirdright">';
|
||||
|
||||
|
||||
/* BEGIN MODULEBUILDER LASTMODIFIED MYOBJECT
|
||||
// Last modified myobject
|
||||
if (isModEnabled('declarationtva') && $user->hasRight('declarationtva', 'read')) {
|
||||
$sql = "SELECT s.rowid, s.ref, s.label, s.date_creation, s.tms";
|
||||
$sql.= " FROM ".$db->prefix()."declarationtva_myobject as s";
|
||||
$sql.= " WHERE s.entity IN (".getEntity($myobjectstatic->element).")";
|
||||
//if ($socid) $sql.= " AND s.rowid = $socid";
|
||||
$sql .= " ORDER BY s.tms DESC";
|
||||
$sql .= $db->plimit($max, 0);
|
||||
|
||||
$resql = $db->query($sql);
|
||||
if ($resql)
|
||||
{
|
||||
$num = $db->num_rows($resql);
|
||||
$i = 0;
|
||||
|
||||
print '<table class="noborder centpercent">';
|
||||
print '<tr class="liste_titre">';
|
||||
print '<th colspan="2">';
|
||||
print $langs->trans("BoxTitleLatestModifiedMyObjects", $max);
|
||||
print '</th>';
|
||||
print '<th class="right">'.$langs->trans("DateModificationShort").'</th>';
|
||||
print '</tr>';
|
||||
if ($num)
|
||||
{
|
||||
while ($i < $num)
|
||||
{
|
||||
$objp = $db->fetch_object($resql);
|
||||
|
||||
$myobjectstatic->id=$objp->rowid;
|
||||
$myobjectstatic->ref=$objp->ref;
|
||||
$myobjectstatic->label=$objp->label;
|
||||
$myobjectstatic->status = $objp->status;
|
||||
|
||||
print '<tr class="oddeven">';
|
||||
print '<td class="nowrap">'.$myobjectstatic->getNomUrl(1).'</td>';
|
||||
print '<td class="right nowrap">';
|
||||
print "</td>";
|
||||
print '<td class="right nowrap">'.dol_print_date($db->jdate($objp->tms), 'day')."</td>";
|
||||
print '</tr>';
|
||||
$i++;
|
||||
}
|
||||
|
||||
$db->free($resql);
|
||||
// Process actions
|
||||
if ($action == 'create_declaration' && $period_id > 0) {
|
||||
$declaration_id = $declarationtva->createDeclaration($period_id);
|
||||
if ($declaration_id > 0) {
|
||||
setEventMessages($langs->trans("DeclarationCreated"), null, 'mesgs');
|
||||
} else {
|
||||
print '<tr class="oddeven"><td colspan="3" class="opacitymedium">'.$langs->trans("None").'</td></tr>';
|
||||
setEventMessages($langs->trans("ErrorCreatingDeclaration") . ": " . $declarationtva->error, null, 'errors');
|
||||
}
|
||||
print "</table><br>";
|
||||
} elseif ($action == 'validate_declaration' && $declaration_id > 0) {
|
||||
if ($declarationtva->validateDeclaration($declaration_id)) {
|
||||
setEventMessages($langs->trans("DeclarationValidated"), null, 'mesgs');
|
||||
} else {
|
||||
setEventMessages($langs->trans("ErrorValidatingDeclaration"), null, 'errors');
|
||||
}
|
||||
} elseif ($action == 'submit_declaration' && $declaration_id > 0) {
|
||||
if ($declarationtva->submitDeclaration($declaration_id)) {
|
||||
setEventMessages($langs->trans("DeclarationSubmitted"), null, 'mesgs');
|
||||
} else {
|
||||
setEventMessages($langs->trans("ErrorSubmittingDeclaration"), null, 'errors');
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
print '</div></div>';
|
||||
// Get data for display
|
||||
$periods = $period->getAllPeriods();
|
||||
$declarations = array();
|
||||
|
||||
// End of page
|
||||
// Get declarations for each period
|
||||
foreach ($periods as $p) {
|
||||
$sql = "SELECT * FROM " . MAIN_DB_PREFIX . "declarationtva_declarations
|
||||
WHERE period_id = " . $p['rowid'] . " AND entity = " . $conf->entity . "
|
||||
ORDER BY created_date DESC";
|
||||
|
||||
$result = $db->query($sql);
|
||||
if ($result) {
|
||||
while ($obj = $db->fetch_object($result)) {
|
||||
$declarations[] = array(
|
||||
'rowid' => $obj->rowid,
|
||||
'declaration_number' => $obj->declaration_number,
|
||||
'status' => $obj->status,
|
||||
'total_vat_collected' => $obj->total_vat_collected,
|
||||
'total_vat_deductible' => $obj->total_vat_deductible,
|
||||
'net_vat_due' => $obj->net_vat_due,
|
||||
'vat_credit' => $obj->vat_credit,
|
||||
'created_date' => $obj->created_date,
|
||||
'period_name' => $p['period_name']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Page title
|
||||
$title = $langs->trans("DeclarationTVAMainInterface");
|
||||
llxHeader('', $title);
|
||||
|
||||
// Print page header
|
||||
print load_fiche_titre($title, '', 'title_accountancy');
|
||||
|
||||
// Print periods section
|
||||
print '<div class="fiche">';
|
||||
print '<div class="fichehalfleft">';
|
||||
print '<div class="titre">' . $langs->trans("DeclarationTVAPeriods") . '</div>';
|
||||
|
||||
if (empty($periods)) {
|
||||
print '<div class="info">' . $langs->trans("NoPeriodsFound") . '</div>';
|
||||
} else {
|
||||
print '<table class="noborder centpercent">';
|
||||
print '<tr class="liste_titre">';
|
||||
print '<th>' . $langs->trans("PeriodName") . '</th>';
|
||||
print '<th>' . $langs->trans("StartDate") . '</th>';
|
||||
print '<th>' . $langs->trans("EndDate") . '</th>';
|
||||
print '<th>' . $langs->trans("Status") . '</th>';
|
||||
print '<th>' . $langs->trans("Actions") . '</th>';
|
||||
print '</tr>';
|
||||
|
||||
foreach ($periods as $p) {
|
||||
print '<tr>';
|
||||
print '<td>' . $p['period_name'] . '</td>';
|
||||
print '<td>' . dol_print_date($p['start_date'], 'day') . '</td>';
|
||||
print '<td>' . dol_print_date($p['end_date'], 'day') . '</td>';
|
||||
print '<td>' . $langs->trans("Status" . ucfirst($p['status'])) . '</td>';
|
||||
print '<td>';
|
||||
print '<a href="' . $_SERVER['PHP_SELF'] . '?action=create_declaration&period_id=' . $p['rowid'] . '" class="butAction">' . $langs->trans("CreateDeclaration") . '</a>';
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
}
|
||||
print '</table>';
|
||||
}
|
||||
|
||||
print '</div>';
|
||||
print '</div>';
|
||||
|
||||
// Print declarations section
|
||||
print '<div class="fiche">';
|
||||
print '<div class="fichehalfright">';
|
||||
print '<div class="titre">' . $langs->trans("DeclarationTVADeclarations") . '</div>';
|
||||
|
||||
if (empty($declarations)) {
|
||||
print '<div class="info">' . $langs->trans("NoDeclarationsFound") . '</div>';
|
||||
} else {
|
||||
print '<table class="noborder centpercent">';
|
||||
print '<tr class="liste_titre">';
|
||||
print '<th>' . $langs->trans("DeclarationNumber") . '</th>';
|
||||
print '<th>' . $langs->trans("Period") . '</th>';
|
||||
print '<th>' . $langs->trans("Status") . '</th>';
|
||||
print '<th>' . $langs->trans("NetVATDue") . '</th>';
|
||||
print '<th>' . $langs->trans("Actions") . '</th>';
|
||||
print '</tr>';
|
||||
|
||||
foreach ($declarations as $d) {
|
||||
print '<tr>';
|
||||
print '<td>' . $d['declaration_number'] . '</td>';
|
||||
print '<td>' . $d['period_name'] . '</td>';
|
||||
print '<td>' . $langs->trans("Status" . ucfirst($d['status'])) . '</td>';
|
||||
print '<td>' . price($d['net_vat_due']) . '</td>';
|
||||
print '<td>';
|
||||
|
||||
if ($d['status'] == 'draft') {
|
||||
print '<a href="' . $_SERVER['PHP_SELF'] . '?action=validate_declaration&declaration_id=' . $d['rowid'] . '" class="butAction">' . $langs->trans("Validate") . '</a>';
|
||||
} elseif ($d['status'] == 'validated') {
|
||||
print '<a href="' . $_SERVER['PHP_SELF'] . '?action=submit_declaration&declaration_id=' . $d['rowid'] . '" class="butAction">' . $langs->trans("Submit") . '</a>';
|
||||
}
|
||||
|
||||
print '<a href="declarationtva_view.php?id=' . $d['rowid'] . '" class="butAction">' . $langs->trans("View") . '</a>';
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
}
|
||||
print '</table>';
|
||||
}
|
||||
|
||||
print '</div>';
|
||||
print '</div>';
|
||||
|
||||
// Print configuration section
|
||||
print '<div class="fiche">';
|
||||
print '<div class="titre">' . $langs->trans("DeclarationTVAConfiguration") . '</div>';
|
||||
print '<div class="info">';
|
||||
print '<a href="admin/setup.php" class="butAction">' . $langs->trans("ConfigureModule") . '</a>';
|
||||
print '</div>';
|
||||
print '</div>';
|
||||
|
||||
// Print footer
|
||||
llxFooter();
|
||||
$db->close();
|
||||
?>
|
||||
121
sql/mvp_schema.sql
Normal file
121
sql/mvp_schema.sql
Normal file
@ -0,0 +1,121 @@
|
||||
--
|
||||
-- DeclarationTVA MVP Database Schema
|
||||
-- Phase 1 - Basic CA-3 Declaration System
|
||||
-- Simplified for MVP development
|
||||
--
|
||||
|
||||
-- =====================================================
|
||||
-- 1. CORE MVP TABLES (Simplified)
|
||||
-- =====================================================
|
||||
|
||||
-- Basic configuration table
|
||||
CREATE TABLE IF NOT EXISTS `llx_declarationtva_config` (
|
||||
`rowid` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`entity` int(11) NOT NULL DEFAULT 1,
|
||||
`config_key` varchar(64) NOT NULL,
|
||||
`config_value` text,
|
||||
`created_date` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`rowid`),
|
||||
UNIQUE KEY `uk_config_entity_key` (`entity`, `config_key`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Simplified PCG account mappings (one account per CA-3 line for MVP)
|
||||
CREATE TABLE IF NOT EXISTS `llx_declarationtva_account_mappings` (
|
||||
`rowid` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`entity` int(11) NOT NULL DEFAULT 1,
|
||||
`ca3_line` varchar(8) NOT NULL COMMENT 'A1, A2, B1, B2, B3, B4, 17, 20, 21, 22, 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` (`entity`, `ca3_line`),
|
||||
KEY `idx_ca3_line` (`ca3_line`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Declaration periods (quarterly only for MVP)
|
||||
CREATE TABLE IF NOT EXISTS `llx_declarationtva_periods` (
|
||||
`rowid` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`entity` int(11) NOT NULL DEFAULT 1,
|
||||
`period_name` varchar(32) NOT NULL COMMENT 'Q1-2024, Q2-2024, etc.',
|
||||
`start_date` date NOT NULL,
|
||||
`end_date` date NOT NULL,
|
||||
`status` varchar(32) DEFAULT 'draft' COMMENT 'draft, validated, submitted',
|
||||
`created_date` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`rowid`),
|
||||
UNIQUE KEY `uk_period_entity_name` (`entity`, `period_name`),
|
||||
KEY `idx_period_dates` (`start_date`, `end_date`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Main declarations table (simplified)
|
||||
CREATE TABLE IF NOT EXISTS `llx_declarationtva_declarations` (
|
||||
`rowid` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`entity` int(11) NOT NULL DEFAULT 1,
|
||||
`period_id` int(11) NOT NULL,
|
||||
`declaration_number` varchar(32) NOT NULL,
|
||||
`status` varchar(32) DEFAULT 'draft' COMMENT 'draft, validated, submitted',
|
||||
`total_vat_collected` decimal(15,2) DEFAULT 0.00,
|
||||
`total_vat_deductible` decimal(15,2) DEFAULT 0.00,
|
||||
`net_vat_due` decimal(15,2) DEFAULT 0.00,
|
||||
`vat_credit` decimal(15,2) DEFAULT 0.00,
|
||||
`submission_date` datetime DEFAULT NULL,
|
||||
`created_date` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`rowid`),
|
||||
UNIQUE KEY `uk_declaration_entity_number` (`entity`, `declaration_number`),
|
||||
KEY `idx_period_id` (`period_id`),
|
||||
CONSTRAINT `fk_declaration_period` FOREIGN KEY (`period_id`) REFERENCES `llx_declarationtva_periods` (`rowid`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- CA-3 form lines (simplified)
|
||||
CREATE TABLE IF NOT EXISTS `llx_declarationtva_ca3_lines` (
|
||||
`rowid` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`declaration_id` int(11) NOT NULL,
|
||||
`ca3_line` varchar(8) NOT NULL,
|
||||
`line_label` varchar(255) DEFAULT NULL,
|
||||
`base_amount` decimal(15,2) DEFAULT 0.00,
|
||||
`vat_amount` decimal(15,2) DEFAULT 0.00,
|
||||
`total_amount` decimal(15,2) DEFAULT 0.00,
|
||||
`created_date` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`rowid`),
|
||||
UNIQUE KEY `uk_declaration_line` (`declaration_id`, `ca3_line`),
|
||||
CONSTRAINT `fk_ca3_declaration` FOREIGN KEY (`declaration_id`) REFERENCES `llx_declarationtva_declarations` (`rowid`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- =====================================================
|
||||
-- 2. INITIAL DATA FOR MVP
|
||||
-- =====================================================
|
||||
|
||||
-- Insert default configuration
|
||||
INSERT INTO `llx_declarationtva_config` (`entity`, `config_key`, `config_value`, `created_date`) VALUES
|
||||
(1, 'module_version', '1.0.0-mvp', NOW()),
|
||||
(1, 'default_period_type', 'quarterly', NOW()),
|
||||
(1, 'vat_rates', '20.00,10.00,5.50,2.10,0.00', NOW()),
|
||||
(1, 'declaration_language', 'fr', NOW());
|
||||
|
||||
-- Insert default CA-3 line mappings (simplified)
|
||||
INSERT INTO `llx_declarationtva_account_mappings` (`entity`, `ca3_line`, `account_code`, `account_label`, `vat_rate`, `created_date`) VALUES
|
||||
(1, 'A1', '701000', 'Ventes HT 20%', 20.00, NOW()),
|
||||
(1, 'A2', '701000', 'Opérations spéciales', 20.00, NOW()),
|
||||
(1, 'B1', '445710', 'TVA collectée 20%', 20.00, NOW()),
|
||||
(1, 'B2', '445720', 'TVA collectée 10%', 10.00, NOW()),
|
||||
(1, 'B3', '445730', 'TVA collectée 5.5%', 5.50, NOW()),
|
||||
(1, 'B4', '445740', 'TVA collectée 2.1%', 2.10, NOW()),
|
||||
(1, '17', '445200', 'TVA due intra-EU', 20.00, NOW()),
|
||||
(1, '20', '445620', 'TVA déductible immobilisations', 20.00, NOW()),
|
||||
(1, '21', '445660', 'TVA déductible autres', 20.00, NOW()),
|
||||
(1, '22', '445670', 'Crédit TVA', 0.00, NOW()),
|
||||
(1, '28', '445510', 'TVA nette à payer', 0.00, NOW()),
|
||||
(1, '29', '445670', 'Crédit TVA à reporter', 0.00, NOW());
|
||||
|
||||
-- =====================================================
|
||||
-- 3. BASIC INDEXES FOR PERFORMANCE
|
||||
-- =====================================================
|
||||
|
||||
CREATE INDEX `idx_declarationtva_periods_dates` ON `llx_declarationtva_periods` (`start_date`, `end_date`);
|
||||
CREATE INDEX `idx_declarationtva_declarations_period` ON `llx_declarationtva_declarations` (`period_id`);
|
||||
CREATE INDEX `idx_declarationtva_account_mappings_line` ON `llx_declarationtva_account_mappings` (`ca3_line`);
|
||||
|
||||
-- =====================================================
|
||||
-- END OF MVP SCHEMA
|
||||
-- =====================================================
|
||||
Loading…
Reference in New Issue
Block a user