requireAccess($session::SECTION_ACCOUNTING, $session::ACCESS_READ);
f(['id' => 'required|numeric']);
$id = (int) qg('id');
$sign_tag = UserTemplate\Functions::signature();
$h = static function ($value): string {
return htmlspecialchars_decode(
htmlentities($value, ENT_NOQUOTES, 'UTF-8', false),
ENT_NOQUOTES
);
};
$br = static function ($value) use ($h): string {
return nl2br($h($value));
};
$format_identifier = static function ($value) use ($h): string {
$value = preg_replace('/\s+/', '', (string) $value);
return $value !== '' ? $h(implode(' ', str_split($value, 3))) : '';
};
$detail_line = static function (string $label, $value) use ($h, $br): string {
if ($value === null || trim((string) $value) === '') {
return '';
}
return sprintf('
%s%s
', $h($label), $br($value));
};
// Vérification que le document existe
if (!$f = $facture->get($id))
{
throw new UserException("Ce document n'existe pas.");
}
$moyen_paiement = $facture->getMoyenPaiement($f->moyen_paiement);
// Récupération infos membre/client dans un objet normalisé pour le rendu
if ($f->receveur_membre)
{
$user = Users\Users::get($f->receveur_id);
if (!$user) {
throw new UserException("Ce membre n'existe plus.");
}
$nom_client = Users\DynamicFields::getNameFromArray($user) ?: '** ABSENT **';
$user_fields = Users\DynamicFields::getInstance()->listAssocNames();
$get_user_field = static function (?string $configured, string $fallback) use ($user, $user_fields) {
$field = $configured ?: $fallback;
return array_key_exists($field, $user_fields) ? ($user->$field ?? null) : null;
};
$c = (object) [
'nom' => $nom_client,
'adresse' => $get_user_field($plugin->getConfig('adresse_client'), 'adresse') ?: '[À RENSEIGNER DANS LA FICHE MEMBRE]',
'code_postal' => $get_user_field($plugin->getConfig('code_postal_client'), 'code_postal') ?: '[À RENSEIGNER]',
'ville' => $get_user_field($plugin->getConfig('ville_client'), 'ville') ?: '[À RENSEIGNER]',
'siret' => null,
'email' => Users\DynamicFields::getFirstEmailField() ? ($user->{Users\DynamicFields::getFirstEmailField()} ?? null) : null,
'telephone' => null,
];
}
else
{
$client_data = $client->get($f->receveur_id);
if (!$client_data) {
throw new UserException("Ce client n'existe plus.");
}
$nom_client = $client_data->nom;
$c = $client_data;
}
// Formatage dates
$emission = $f->date_emission->format('d/m/Y');
if (isset($f->date_echeance))
{
$echeance = $f->date_echeance->format('d/m/Y');
}
// -- Création du PDF
// Génération factures, devis et cotisation
if ($f->type_facture != CERFA)
{
$labels = [
FACT => ['Facture', 'Émise le', 'Adressée à', 'Échéance de paiement'],
DEVIS => ['Devis', 'Émis le', 'Adressé à', 'Échéance du devis'],
COTIS => ['Reçu de cotisation', 'Émis le', 'Adressé à', 'Date du reçu'],
];
[$document_label, $emission_label, $recipient_label, $due_label] = $labels[$f->type_facture] ?? $labels[FACT];
$doc = sprintf('%s n° %s', $document_label, $f->numero);
$theme = $plugin->getConfig('invoice_theme') ?: 'modern';
$theme = in_array($theme, ['modern', 'classic', 'minimal'], true) ? $theme : 'modern';
$accent = $plugin->getConfig('invoice_accent_color') ?: '#4656A5';
$accent = preg_match('/^#[0-9A-Fa-f]{6}$/', $accent) ? $accent : '#4656A5';
$show_address_borders = $plugin->getConfig('invoice_show_address_borders') !== false;
$show_email = $plugin->getConfig('invoice_show_email') !== false;
$show_web = $plugin->getConfig('invoice_show_web') !== false;
$address_border_class = $show_address_borders ? 'invoice-address-borders' : 'invoice-no-address-borders';
$currency = $config->get('currency') ?: '€';
$total = Utils::money_format($f->total, ',', ' ');
$ttc = $plugin->getConfig('ttc') ? 'TTC' : 'HT';
if ($plugin->getConfig('rue_asso') && $plugin->getConfig('cp_asso') && $plugin->getConfig('ville_asso')) {
$adresse = trim(($plugin->getConfig('numero_rue_asso') ? $plugin->getConfig('numero_rue_asso') . ' ' : '') . $plugin->getConfig('rue_asso'))
. "\n" . $plugin->getConfig('cp_asso') . ' ' . $plugin->getConfig('ville_asso');
}
else {
$adresse = $config->get('org_address_public') ?: $config->get('org_address') ?: '';
}
$logo = '';
if ($plugin->getConfig('logo') && ($logo_url = $config->fileURL('logo'))) {
$logo = sprintf('
', $h($logo_url));
}
$asso_details = '' . $h($config->get('org_name')) . '';
$asso_details .= $adresse ? '' . $br($adresse) . '
' : '';
$asso_details .= ($t = $plugin->getConfig('rna_asso')) ? $detail_line('RNA', $t) : '';
$asso_details .= ($t = $plugin->getConfig('siret_asso')) ? 'SIRET' . $format_identifier($t) . '
' : '';
$asso_details .= $show_email && ($t = $config->get('org_email')) ? $detail_line('Email', $t) : '';
$asso_details .= ($t = $config->get('org_phone')) ? $detail_line('Tel.', $t) : '';
$asso_details .= $show_web && ($t = $config->get('org_web')) ? $detail_line('Web', $t) : '';
$brand_summary = '' . $logo . '' . $h($config->get('org_name')) . '
';
$receveur = '' . $h($nom_client) . '';
$receveur .= ($t = ($f->nom_contact ?? null)) ? $detail_line('Contact', $t) : '';
$receveur .= '' . $br($c->adresse ?? '') . '
' . $h(trim(($c->code_postal ?? '') . ' ' . ($c->ville ?? ''))) . '
';
$receveur .= ($t = $c->siret ?? null) ? 'SIREN/SIRET' . $format_identifier($t) . '
' : '';
$receveur .= $show_email && ($t = $c->email ?? null) ? $detail_line('Email', $t) : '';
$receveur .= ($t = $c->telephone ?? null) ? $detail_line('Tel.', $t) : '';
$receveur .= $f->type_facture == FACT && ($t = ($f->numero_commande ?? null)) ? $detail_line('Commande', $t) : '';
$receveur .= $f->type_facture == FACT && ($t = ($f->reference_acheteur ?? null)) ? $detail_line('Référence', $t) : '';
if ($f->type_facture != COTIS) {
$rows = '';
foreach($f->contenu as $v) {
$rows .= sprintf(
'| %s | %s %s |
',
$br($v['designation']),
$h(Utils::money_format($v['prix'], ',', ' ')),
$h($currency)
);
}
$payment_terms = trim((string) ($plugin->getConfig('payment_terms') ?: ''));
$invoice_note = trim((string) ($plugin->getConfig('invoice_note') ?: ''));
$status = '';
if ($plugin->getConfig('invoice_show_status') && $f->type_facture == FACT) {
$status_class = $f->reglee ? 'paid' : 'pending';
$status_label = $f->reglee ? 'Facture réglée' : 'En attente de règlement';
$status = sprintf('%s
', $status_class, $h($status_label));
}
$approval = $f->type_facture == DEVIS ? 'Bon pour accordDate, nom et signature' : '';
$note = $invoice_note !== '' ? '' . $br($invoice_note) . '
' : '';
$terms = $payment_terms !== '' ? $detail_line('Conditions', $payment_terms) : '';
$footer = $br($plugin->getConfig('footer') ?? '[Pied de page à configurer]');
$echeance_html = $detail_line($due_label, $echeance ?? '');
$moyen_html = $detail_line('Moyen de paiement', $moyen_paiement);
$content = <<
| Désignation |
Montant |
$rows
$echeance_html
$terms
$moyen_html
$status
Total $ttc
$total {$h($currency)}
Net à payer
$note
$approval
HTML;
}
else {
$lieu = $plugin->getConfig('ville_asso') ?: '';
$intitule = $f->contenu['intitule'];
$souscription = date('d/m/Y', strtotime($f->contenu['souscription']));
$expiration = $f->contenu['expiration'] == '1970-01-01'
? "le jour même, s'agissant d'une cotisation ponctuelle"
: 'le ' . date('d/m/Y', strtotime($f->contenu['expiration']));
$footer = $br($plugin->getConfig('footer') ?? '');
$content = <<
À {$h($lieu)}, le {$h($emission)},
Bonjour,
Nous accusons réception de votre cotisation « {$h($intitule)} » reçue le {$h($emission)} et nous vous en remercions.
Nous reconnaissons que vous avez acquitté la somme de {$h($total)} {$h($currency)}. Votre adhésion sera donc effective à compter du {$h($souscription)} jusqu'à {$h($expiration)}.
Nous vous prions de recevoir nos meilleures salutations.
Pour {$h($config->get('org_name'))}
Nous vous rappelons que la cotisation n'est pas soumise à la TVA et qu'elle ne donne pas lieu à la délivrance d'une facture. Elle n'ouvre pas droit au bénéfice des dispositions des articles 200, 238 bis et 885-0 V bis A du code général des impôts.
HTML;
}
$html_title = $h($doc . '_' . $emission);
$doc_html = $h($doc);
$txtemis = $h($emission_label . ' ' . $emission);
$txtdest = $h($recipient_label);
$html = <<
$html_title
Émetteur
$asso_details
$txtdest
$receveur
$content
HTML;
} // Génération du CERFA
elseif ($f->type_facture == CERFA)
{
$doc = 'Reçu de don n°'. $f->numero;
$url = WWW_URL;
$libelles = $facture->listTextesCerfa(false);
$t['numero'] = $f->numero;
$t['org_name'] = $config->get('org_name');
$t['n_rue_asso'] = $plugin->getConfig('numero_rue_asso');
$t['rue_asso'] = $plugin->getConfig('rue_asso');
$t['cp_asso'] = $plugin->getConfig('cp_asso');
$t['ville_asso'] = $plugin->getConfig('ville_asso');
$t['objet0'] = $plugin->getConfig('objet_0');
$t['objet1'] = $plugin->getConfig('objet_1');
$t['objet2'] = $plugin->getConfig('objet_2');
$t['nom'] = $nom_client;
$t['adresse'] = $c->adresse ?? '';
$t['cp'] = $c->code_postal ?? '';
$t['ville'] = $c->ville ?? '';
$t['total'] = '***'.Utils::money_format($f->total).'***';
$t['total_lettre'] = numfmt_create('fr_FR', \NumberFormatter::SPELLOUT)->format($f->total/100). ' euros';
$t['d'] = ($f->date_emission->format('d'));
$t['m'] = ($f->date_emission->format('m'));
$t['Y'] = ($f->date_emission->format('Y'));
$t['forme'] = $f->contenu['forme'];
$t['nature'] = $f->contenu['nature'];
$t['texte'] = $libelles[$f->contenu['texte']];
$t['art200'] = $t['art238'] = $t['art885'] = '';
if($plugin->getConfig('droit_art200')){
$t['art200'] = 'X';
}
if($plugin->getConfig('droit_art238bis')){
$t['art238'] = 'X';
}
if($plugin->getConfig('droit_art885-0VbisA')){
$t['art885'] = 'X';
}
// forme du don
switch ($t['forme']){
case '1':
$t['frm'] = 'left: 15mm;';
break;
case '2':
$t['frm'] = 'left: 57.3mm;';
break;
case '3':
$t['frm'] = 'left: 115.2mm;';
break;
case '4':
$t['frm'] = 'left: 175.2mm;';
}
// nature du don
switch ($t['nature']){
case '1':
$t['nat'] = 'left: 15mm;';
break;
case '2':
$t['nat'] = 'left: 57.3mm;';
break;
case '3':
$t['nat'] = 'left: 115.2mm;';
}
// moyen de paiement
switch ($f->moyen_paiement){
case 'ES':
$t['pos'] = 'left: 15mm;';
break;
case 'CH':
$t['pos'] = 'left: 57.3mm;';
break;
default:
$t['pos'] = 'left: 115.2mm;';
}
$t['d2'] = ($f->date_echeance->format('d'));
$t['m2'] = ($f->date_echeance->format('m'));
$t['Y2'] = ($f->date_echeance->format('Y'));
ob_start();
echo <<
{$doc}_{$emission}
{$t['numero']}
{$t['org_name']}
{$t['n_rue_asso']}
{$t['rue_asso']}
{$t['cp_asso']}
{$t['ville_asso']}
{$t['objet0']}
{$t['objet1']}
{$t['objet2']}
X
{$t['nom']}
{$t['adresse']}
{$t['cp']}
{$t['ville']}
{$t['total']}
{$t['total_lettre']}
{$t['d']}
{$t['m']}
{$t['Y']}
{$t['art200']}
{$t['art238']}
{$t['art885']}
X
X
{$t['texte']}
X
{$t['d2']}
{$t['m2']}
{$t['Y2']}
{$sign_tag}
EOF;
$html = ob_get_clean();
} // End if cerfa
if(qg('d') !== null)
{
$filename = 'Print';
if (preg_match('!(.*)!U', $html, $match)) {
$filename = str_replace(" ", "_", trim($match[1]));
}
header('Content-type: application/pdf');
header(sprintf('Content-Disposition: attachment; filename="%s.pdf"', Utils::safeFileName($filename)));
Utils::streamPDF($html);
}
else
{
echo $html;
}