Implement declaration validation with confirmation dialog

Features:
- Add validation confirmation dialog (non-JavaScript popup)
- Remove recalculate button after validation
- Generate and save detailed PDF to Dolibarr documents
- Add document icons in declaration list
- Add status icons (checkmark for validated, edit for draft)
- Create documents linking table
- Add validation language strings (FR/EN)

Technical:
- Enhanced validateDeclaration() method with user tracking
- saveValidatedPDF() method for document storage
- hasValidatedDocument() method for icon display
- Custom confirmation dialog with CSS styling
- Database migration for documents table
- Status-based UI changes
This commit is contained in:
Frank Cools 2025-10-06 17:03:47 +02:00
parent 91ebf73866
commit f07f6a7c28
6 changed files with 256 additions and 19 deletions

View File

@ -764,21 +764,6 @@ class DeclarationTVA
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
@ -1005,4 +990,118 @@ class DeclarationTVA
'account_count' => count($details)
);
}
/**
* Validate a 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',
validated_date = NOW(),
validated_by = " . $this->db->escape($GLOBALS['user']->id) . "
WHERE rowid = " . (int)$declaration_id . "
AND entity = " . $this->entity;
$result = $this->db->query($sql);
if ($result) {
return true;
} else {
$this->error = $this->db->lasterror();
return false;
}
}
/**
* Save validated PDF to Dolibarr documents
*
* @param int $declaration_id Declaration ID
* @param string $pdf_path Path to the PDF file
* @return bool Success
*/
public function saveValidatedPDF($declaration_id, $pdf_path)
{
global $conf, $user;
// Create documents directory if it doesn't exist
$doc_dir = DOL_DATA_ROOT . '/declarationtva/validated/';
if (!is_dir($doc_dir)) {
dol_mkdir($doc_dir);
}
// Generate filename
$filename = 'CA3_Validated_' . $this->declaration_number . '_' . date('Y-m-d') . '.pdf';
$dest_path = $doc_dir . $filename;
// Copy PDF to documents directory
if (copy($pdf_path, $dest_path)) {
// Add document record to Dolibarr
require_once DOL_DOCUMENT_ROOT . '/core/class/ecmfiles.class.php';
$ecmfile = new EcmFiles($this->db);
$ecmfile->filepath = 'declarationtva/validated/';
$ecmfile->filename = $filename;
$ecmfile->label = 'CA-3 Validated Declaration - ' . $this->declaration_number;
$ecmfile->description = 'Validated CA-3 declaration with detailed breakdown';
$ecmfile->entity = $this->entity;
$ecmfile->fk_user_author = $user->id;
$ecmfile->fk_user_modif = $user->id;
$ecmfile->datec = dol_now();
$ecmfile->tms = dol_now();
$ecmfile->mimetype = 'application/pdf';
$ecmfile->filesize = filesize($dest_path);
$ecmfile->checksum = md5_file($dest_path);
$result = $ecmfile->create($user);
if ($result > 0) {
// Link document to declaration
$this->linkDocumentToDeclaration($declaration_id, $ecmfile->id);
return true;
}
}
return false;
}
/**
* Link document to declaration
*
* @param int $declaration_id Declaration ID
* @param int $document_id Document ID
* @return bool Success
*/
private function linkDocumentToDeclaration($declaration_id, $document_id)
{
$sql = "INSERT INTO " . MAIN_DB_PREFIX . "declarationtva_documents
(declaration_id, document_id, created_date)
VALUES (" . (int)$declaration_id . ", " . (int)$document_id . ", NOW())";
return $this->db->query($sql);
}
/**
* Check if declaration has a validated document
*
* @param int $declaration_id Declaration ID
* @return bool Has document
*/
public function hasValidatedDocument($declaration_id)
{
$sql = "SELECT COUNT(*) as count
FROM " . MAIN_DB_PREFIX . "declarationtva_documents dd
INNER JOIN " . MAIN_DB_PREFIX . "ecm_files ef ON dd.document_id = ef.rowid
WHERE dd.declaration_id = " . (int)$declaration_id . "
AND dd.entity = " . $this->entity;
$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;
}
}

View File

@ -98,6 +98,25 @@ if ($action == 'export_pdf_detailed') {
}
}
if ($action == 'validate' && $token) {
// Validate the declaration
if ($declarationtva->validateDeclaration($id)) {
setEventMessages($langs->trans("DeclarationValidated"), null, 'mesgs');
// Generate and save detailed PDF to Dolibarr documents
require_once DOL_DOCUMENT_ROOT . '/custom/declarationtva/core/class/declarationtva_pdf.class.php';
$pdf_generator = new DeclarationTVA_PDF($db);
$pdf_path = $pdf_generator->generateDetailedCA3PDF($id);
if ($pdf_path && file_exists($pdf_path)) {
// Save PDF to Dolibarr documents
$declarationtva->saveValidatedPDF($id, $pdf_path);
}
} else {
setEventMessages($langs->trans("ErrorValidatingDeclaration"), null, 'errors');
}
}
// Fetch declaration
if ($declarationtva->fetch($id) < 0) {
setEventMessages($langs->trans("DeclarationNotFound"), null, 'errors');
@ -409,15 +428,18 @@ print '<div class="titre">' . $langs->trans("Actions") . '</div>';
print '<div class="center">';
// Recalculate button (always available)
print '<a href="' . $_SERVER['PHP_SELF'] . '?id=' . $id . '&action=recalculate&token=' . newToken() . '" class="butAction">' . $langs->trans("Recalculate") . '</a> ';
// Recalculate button (only for draft status)
if ($declarationtva->status == 'draft') {
print '<a href="' . $_SERVER['PHP_SELF'] . '?id=' . $id . '&action=recalculate&token=' . newToken() . '" class="butAction">' . $langs->trans("Recalculate") . '</a> ';
}
// 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_detailed" class="butAction">' . $langs->trans("ExportPDFDetailed") . '</a> ';
if ($declarationtva->status == 'draft') {
print '<a href="' . $_SERVER['PHP_SELF'] . '?id=' . $id . '&action=validate" class="butAction">' . $langs->trans("Validate") . '</a> ';
// Validation button with confirmation dialog
print '<a href="#" onclick="confirmValidation(' . $id . '); return false;" class="butAction">' . $langs->trans("Validate") . '</a> ';
} elseif ($declarationtva->status == 'validated') {
print '<a href="' . $_SERVER['PHP_SELF'] . '?id=' . $id . '&action=submit" class="butAction">' . $langs->trans("Submit") . '</a> ';
}
@ -520,6 +542,38 @@ function loadLineDetails(line) {
};
xhr.send();
}
function confirmValidation(declarationId) {
// Create confirmation dialog
var dialog = document.createElement("div");
dialog.style.cssText = "position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 10000; display: flex; align-items: center; justify-content: center;";
var dialogContent = document.createElement("div");
dialogContent.style.cssText = "background: white; padding: 20px; border-radius: 5px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); max-width: 500px; text-align: center;";
dialogContent.innerHTML = `
<h3>' . $langs->trans("ConfirmValidation") . '</h3>
<p>' . $langs->trans("ValidationConfirmationMessage") . '</p>
<div style="margin-top: 20px;">
<button onclick="proceedValidation(' . $id . '); this.parentElement.parentElement.parentElement.remove();"
style="background: #28a745; color: white; border: none; padding: 10px 20px; margin: 0 10px; border-radius: 3px; cursor: pointer;">
' . $langs->trans("YesValidate") . '
</button>
<button onclick="this.parentElement.parentElement.parentElement.remove();"
style="background: #6c757d; color: white; border: none; padding: 10px 20px; margin: 0 10px; border-radius: 3px; cursor: pointer;">
' . $langs->trans("Cancel") . '
</button>
</div>
`;
dialog.appendChild(dialogContent);
document.body.appendChild(dialog);
}
function proceedValidation(declarationId) {
// Redirect to validation action
window.location.href = "' . $_SERVER['PHP_SELF'] . '?id=" + declarationId + "&action=validate&token=' . newToken() . '";
}
</script>';
// Print footer

View File

@ -123,6 +123,7 @@ if (empty($declarations)) {
print '<th class="center">' . $langs->trans("Period") . '</th>';
print '<th class="center">' . $langs->trans("Status") . '</th>';
print '<th class="center">' . $langs->trans("NetVATDue") . '</th>';
print '<th class="center">' . $langs->trans("Document") . '</th>';
print '<th class="center">' . $langs->trans("Actions") . '</th>';
print '</tr>';
@ -130,8 +131,33 @@ if (empty($declarations)) {
print '<tr>';
print '<td>' . $d['declaration_number'] . '</td>';
print '<td class="center">' . dol_print_date($d['start_date'], 'day') . ' - ' . dol_print_date($d['end_date'], 'day') . '</td>';
print '<td class="center">' . $langs->trans("Status" . ucfirst($d['status'])) . '</td>';
// Status with icon
$status_icon = '';
if ($d['status'] == 'validated') {
$status_icon = ' <i class="fa fa-check-circle text-success" title="' . $langs->trans("ValidatedDeclaration") . '"></i>';
} elseif ($d['status'] == 'draft') {
$status_icon = ' <i class="fa fa-edit text-warning" title="' . $langs->trans("DraftDeclaration") . '"></i>';
}
print '<td class="center">' . $langs->trans("Status" . ucfirst($d['status'])) . $status_icon . '</td>';
print '<td class="center">' . price($d['net_vat_due']) . '</td>';
// Document column
print '<td class="center">';
if ($d['status'] == 'validated') {
// Check if document exists
$has_document = $declarationtva->hasValidatedDocument($d['rowid']);
if ($has_document) {
print '<i class="fa fa-file-pdf-o text-success" title="' . $langs->trans("ValidatedPDFAvailable") . '"></i>';
} else {
print '<i class="fa fa-file-o text-muted" title="' . $langs->trans("NoDocument") . '"></i>';
}
} else {
print '<i class="fa fa-minus text-muted"></i>';
}
print '</td>';
print '<td class="center">';
if ($d['status'] == 'draft') {

View File

@ -492,3 +492,19 @@ TemplateResetFailed = Error resetting to official template
ErrorGeneratingPDF = Error generating PDF
TemplateUpdated = Template updated successfully
TemplateUpdateFailed = Error updating template
# Validation
Validate = Validate
ConfirmValidation = Confirm Validation
ValidationConfirmationMessage = Are you sure you want to validate this declaration? This action is irreversible and will automatically generate the detailed PDF.
YesValidate = Yes, validate
Cancel = Cancel
DeclarationValidated = Declaration validated successfully
ErrorValidatingDeclaration = Error validating declaration
# Document and status icons
Document = Document
ValidatedDeclaration = Validated Declaration
DraftDeclaration = Draft Declaration
ValidatedPDFAvailable = Validated PDF Available
NoDocument = No Document

View File

@ -464,3 +464,19 @@ TemplateResetFailed = Erreur lors du retour au modèle officiel
ErrorGeneratingPDF = Erreur lors de la génération du PDF
TemplateUpdated = Modèle mis à jour avec succès
TemplateUpdateFailed = Erreur lors de la mise à jour du modèle
# Validation
Validate = Valider
ConfirmValidation = Confirmer la validation
ValidationConfirmationMessage = Êtes-vous sûr de vouloir valider cette déclaration ? Cette action est irréversible et générera automatiquement le PDF détaillé.
YesValidate = Oui, valider
Cancel = Annuler
DeclarationValidated = Déclaration validée avec succès
ErrorValidatingDeclaration = Erreur lors de la validation de la déclaration
# Document and status icons
Document = Document
ValidatedDeclaration = Déclaration validée
DraftDeclaration = Déclaration brouillon
ValidatedPDFAvailable = PDF validé disponible
NoDocument = Aucun document

View File

@ -0,0 +1,26 @@
-- Migration: Add documents table for linking validated PDFs to declarations
-- Version: 2.1.0
-- Date: 2025-01-06
-- Create table for linking documents to declarations
CREATE TABLE IF NOT EXISTS `llx_declarationtva_documents` (
`rowid` int(11) NOT NULL AUTO_INCREMENT,
`declaration_id` int(11) NOT NULL,
`document_id` int(11) NOT NULL,
`created_date` datetime NOT NULL,
`entity` int(11) NOT NULL DEFAULT 1,
PRIMARY KEY (`rowid`),
KEY `idx_declaration_id` (`declaration_id`),
KEY `idx_document_id` (`document_id`),
KEY `idx_entity` (`entity`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Add validated_date and validated_by columns to declarations table if they don't exist
ALTER TABLE `llx_declarationtva_declarations`
ADD COLUMN IF NOT EXISTS `validated_date` datetime DEFAULT NULL,
ADD COLUMN IF NOT EXISTS `validated_by` int(11) DEFAULT NULL;
-- Add indexes for performance
ALTER TABLE `llx_declarationtva_declarations`
ADD INDEX IF NOT EXISTS `idx_validated_date` (`validated_date`),
ADD INDEX IF NOT EXISTS `idx_validated_by` (`validated_by`);