dolibarr 23.0.4
html_cerfafr.modules.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2003 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2005-2006 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2012 Regis Houssin <regis.houssin@inodbox.com>
5 * Copyright (C) 2012 Marcos García <marcosgdf@gmail.com>
6 * Copyright (C) 2014-2020 Alexandre Spangaro <aspangaro@open-dsi.fr>
7 * Copyright (C) 2015 Benoit Bruchard <benoitb21@gmail.com>
8 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
9 * Copyright (C) 2025 Frédéric France <frederic.france@free.fr>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <https://www.gnu.org/licenses/>.
23 */
24
30require_once DOL_DOCUMENT_ROOT.'/core/modules/dons/modules_don.php';
31require_once DOL_DOCUMENT_ROOT.'/don/class/don.class.php';
32require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
33require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
34
35
40{
46 public function __construct($db)
47 {
48 global $conf, $langs;
49
50 $this->db = $db;
51 $this->name = "cerfafr";
52 $this->description = $langs->trans('DonationsReceiptModel').' - fr_FR - Cerfa 11580*04';
53
54 // Dimension page for size A4
55 $this->type = 'html';
56 }
57
58
64 public function isEnabled()
65 {
66 return true;
67 }
68
69
70 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
79 public function write_file($don, $outputlangs, $currency = '')
80 {
81 // phpcs:enable
82 global $user, $conf, $langs, $mysoc;
83
84 $now = dol_now();
85 $id = (!is_object($don) ? $don : '');
86
87 if (!is_object($outputlangs)) {
88 $outputlangs = $langs;
89 }
90
91 // Load traductions files required by page
92 $outputlangs->loadLangs(array("main", "dict", "companies", "bills", "products", "donations"));
93
94 $currency = !empty($currency) ? $currency : $conf->currency;
95
96 if (!empty($conf->don->dir_output)) {
97 // Definition of the object don (for upward compatibility)
98 if (!is_object($don)) {
99 $don = new Don($this->db);
100 $ret = $don->fetch($id);
101 $id = $don->id;
102 }
103
104 // Definition of $dir and $file
105 if (!empty($don->specimen)) {
106 $dir = $conf->don->dir_output;
107 $file = $dir."/SPECIMEN.html";
108 } else {
109 $donref = dol_sanitizeFileName($don->ref);
110 $dir = $conf->don->dir_output."/".$donref;
111 $file = $dir."/".$donref.".html";
112 }
113
114 if (!file_exists($dir)) {
115 if (dol_mkdir($dir) < 0) {
116 $this->error = $langs->trans("ErrorCanNotCreateDir", $dir);
117 return -1;
118 }
119 }
120
121 if (file_exists($dir)) {
122 $formclass = new Form($this->db);
123
124 // This is not the proper way to do it but $formclass->form_modes_reglement
125 // prints the translation instead of returning it
126 $formclass->load_cache_types_paiements();
127 // Don::fetch() stores the payment mode into mode_reglement_id. The modepaymentid property is only
128 // set by the create/update forms, so it is always empty when the document is built from a fetched
129 // donation and no payment mode checkbox was ever ticked on the receipt.
130 $modepaymentid = !empty($don->mode_reglement_id) ? $don->mode_reglement_id : $don->modepaymentid;
131 if ($modepaymentid) {
132 $paymentmode = !empty($formclass->cache_types_paiements[(int) $modepaymentid]['label']) ? $formclass->cache_types_paiements[$modepaymentid]['label'] : '';
133 } else {
134 $paymentmode = '';
135 }
136 $modepaymentcode = !empty($formclass->cache_types_paiements[(int) $modepaymentid]['code']) ? $formclass->cache_types_paiements[(int) $modepaymentid]['code'] : "";
137 if ($modepaymentcode == 'CHQ') {
138 $ModePaiement = '<td width="25%"><input type="checkbox"> Remise d\'espèces</td><td width="25%"><input type="checkbox" disabled="true" checked="checked"> Chèque</td><td width="50%"><input type="checkbox"> Virement, prélèvement, carte bancaire</td>';
139 } elseif ($modepaymentcode == 'LIQ') {
140 $ModePaiement = '<td width="25%"><input type="checkbox" checked="checked"> Remise d\'espèces</td><td width="25%"><input type="checkbox"> Chèque</td><td width="50%"><input type="checkbox"> Virement, prélèvement, carte bancaire</td>';
141 } elseif ($modepaymentcode == 'VIR' || $modepaymentcode == 'PRE' || $modepaymentcode == 'CB') {
142 $ModePaiement = '<td width="25%"><input type="checkbox"> Remise d\'espèces</td><td width="25%"><input type="checkbox"> Chèque</td><td width="50%"><input type="checkbox" checked="checked"> Virement, prélèvement, carte bancaire</td>';
143 } else {
144 $ModePaiement = '<td width="25%"><input type="checkbox"> Remise d\'espèces</td><td width="25%"><input type="checkbox"> Chèque</td><td width="50%"><input type="checkbox"> Virement, prélèvement, carte bancaire</td>';
145 }
146
147 /*
148 if (empty($don->societe))
149 {
150 $CodeDon = '<td width="33%"><input type="checkbox" disabled="true" checked="checked" > 200 du CGI</td><td width="33%"><input type="checkbox" disabled="true" > 238 bis du CGI</td><td width="33%"><input type="checkbox" disabled="true" > 978 du CGI</td>';
151 }
152 else
153 {
154 $CodeDon = '<td width="33%"><input type="checkbox" disabled="true" > 200 du CGI</td><td width="33%"><input type="checkbox" disabled="true" checked="checked" > 238 bis du CGI</td><td width="33%"><input type="checkbox" disabled="true" > 978 du CGI</td>';
155 }
156 */
157 // Donator identity.
158 // When option DONATION_USE_THIRDPARTIES is on, the donation form does not show the name/address
159 // inputs at all, so llx_don only holds fk_soc and every donator field stays empty. The receipt
160 // would then be issued without the mandatory identity of the donator, so fall back on the linked
161 // third party for each field left empty.
162 $donatorsociete = (string) $don->societe;
163 $donatorlastname = (string) $don->lastname;
164 $donatorfirstname = (string) $don->firstname;
165 $donatoraddress = (string) $don->address;
166 $donatorzip = (string) $don->zip;
167 $donatortown = (string) $don->town;
168
169 if (!empty($don->socid) && $don->socid > 0) {
170 $donatorthirdparty = new Societe($this->db);
171 if ($donatorthirdparty->fetch($don->socid) > 0) {
172 if (dol_strlen(trim($donatorsociete.$donatorlastname.$donatorfirstname)) == 0) {
173 // A third party holds a single name field, even for a private individual, so it goes
174 // to the "Nom" cell of the form and the "Prénoms" cell is left empty.
175 $donatorsociete = (string) $donatorthirdparty->name;
176 }
177 if (dol_strlen(trim($donatoraddress)) == 0) {
178 $donatoraddress = (string) $donatorthirdparty->address;
179 }
180 if (dol_strlen(trim($donatorzip)) == 0) {
181 $donatorzip = (string) $donatorthirdparty->zip;
182 }
183 if (dol_strlen(trim($donatortown)) == 0) {
184 $donatortown = (string) $donatorthirdparty->town;
185 }
186 } else {
187 dol_syslog("html_cerfafr::write_file Failed to load thirdparty ".$don->socid." linked to donation ".$don->id.", donator block will remain empty - ".$donatorthirdparty->error, LOG_ERR);
188 }
189 }
190
191 if (dol_strlen(trim($donatorsociete.$donatorlastname.$donatorfirstname)) == 0) {
192 dol_syslog("html_cerfafr::write_file No name found for the donator of donation ".$don->id.", the receipt will not be compliant", LOG_WARNING);
193 }
194
195 // Define contents
196 $donmodel = DOL_DOCUMENT_ROOT."/core/modules/dons/html_cerfafr.html";
197
198 $form = file_get_contents($donmodel);
199
200 $form = str_replace('__REF__', (string) $don->id, $form);
201 $form = str_replace('__DATE__', dol_print_date($don->date, 'day', false, $outputlangs), $form);
202 //$form = str_replace('__IP__',$user->ip,$form); // TODO $user->ip not exist
203 $form = str_replace('__AMOUNT__', price($don->amount), $form);
204 $form = str_replace('__AMOUNTLETTERS__', $this->amountToLetters($don->amount), $form);
205 $form = str_replace('__CURRENCY__', $outputlangs->transnoentitiesnoconv("Currency".$currency), $form);
206 $form = str_replace('__CURRENCYCODE__', $conf->currency, $form);
207 $form = str_replace('__MAIN_INFO_SOCIETE_NOM__', $mysoc->name, $form);
208 $form = str_replace('__MAIN_INFO_SOCIETE_ADDRESS__', (string) $mysoc->address, $form);
209 $form = str_replace('__MAIN_INFO_SOCIETE_ZIP__', (string) $mysoc->zip, $form);
210 $form = str_replace('__MAIN_INFO_SOCIETE_TOWN__', (string) $mysoc->town, $form);
211 $form = str_replace('__MAIN_INFO_SOCIETE_OBJECT__', $mysoc->socialobject, $form);
212
213 $form = str_replace('__DONATOR_FIRSTNAME__', dol_escape_htmltag($donatorfirstname), $form);
214 // The template concatenates __DONATOR_SOCIETE__ and __DONATOR_LASTNAME__ with no separator,
215 // so add a line break when both are filled in.
216 $donatorlastnameprefix = (dol_strlen(trim($donatorsociete)) > 0 && dol_strlen(trim($donatorlastname)) > 0) ? '<br>' : '';
217 $form = str_replace('__DONATOR_LASTNAME__', $donatorlastnameprefix.dol_escape_htmltag($donatorlastname), $form);
218 $form = str_replace('__DONATOR_SOCIETE__', dol_escape_htmltag($donatorsociete), $form);
219 $form = str_replace('__DONATOR_STATUT__', (string) $don->statut, $form);
220 $form = str_replace('__DONATOR_ADDRESS__', dol_nl2br(dol_escape_htmltag($donatoraddress, 0, 1)), $form);
221 $form = str_replace('__DONATOR_ZIP__', dol_escape_htmltag($donatorzip), $form);
222 $form = str_replace('__DONATOR_TOWN__', dol_escape_htmltag($donatortown), $form);
223
224 $form = str_replace('__PAYMENTMODE_LIB__ ', (string) $paymentmode, $form);
225 $form = str_replace('__NOW__', dol_print_date($now, 'day', false, $outputlangs), $form);
226 $form = str_replace('__DONATION_PAYMENT_MODE__', $ModePaiement, $form);
227 $form = str_replace('__DONATION_MESSAGE__', getDolGlobalString('DONATION_MESSAGE'), $form);
228
229 // Replace with a generic replacement feature '__(XXX)__' must become $outputlangs->trans('XXX')
230 $form = make_substitutions($form, array(), $outputlangs);
231
232 $frencharticle = '';
233 if (preg_match('/fr/i', $outputlangs->defaultlang)) {
234 $frencharticle = '<font size="+1">Article 200, 238 bis et 978 du code général des impôts (CGI)</font>';
235 }
236 $form = str_replace('__FrenchArticle__', $frencharticle, $form);
237
238 $frencheligibility = '';
239 if (preg_match('/fr/i', $outputlangs->defaultlang)) {
240 $frencheligibility = 'Le bénéficiaire certifie sur l\'honneur que les dons et versements qu\'il reçoit ouvrent droit à la réduction d\'impôt prévue à l\'article :';
241 }
242 $form = str_replace('__FrenchEligibility__', $frencheligibility, $form);
243
244 $art200 = '';
245 if ($mysoc->country_code == 'FR') {
246 if (getDolGlobalInt('DONATION_ART200') >= 1) {
247 $art200 = '<input type="checkbox" disabled="true" checked="checked" >200 du CGI';
248 } else {
249 $art200 = '<input type="checkbox" disabled="true">200 du CGI';
250 }
251 }
252 $form = str_replace('__ARTICLE200__', $art200, $form);
253
254 $art238 = '';
255 if ($mysoc->country_code == 'FR') {
256 if (getDolGlobalInt('DONATION_ART238') >= 1) {
257 $art238 = '<input type="checkbox" disabled="true" checked="checked" >238 bis du CGI';
258 } else {
259 $art238 = '<input type="checkbox" disabled="true">238 bis du CGI';
260 }
261 }
262 $form = str_replace('__ARTICLE238__', $art238, $form);
263
264 $art978 = '';
265 if ($mysoc->country_code == 'FR') {
266 if (getDolGlobalInt('DONATION_ART978') >= 1) {
267 $art978 = '<input type="checkbox" disabled="true" checked="checked" >978 du CGI';
268 } else {
269 $art978 = '<input type="checkbox" disabled="true">978 du CGI';
270 }
271 }
272 $form = str_replace('__ARTICLE978__', $art978, $form);
273
274 // Save file on disk
275 dol_syslog("html_cerfafr::write_file $file");
276 $handle = fopen($file, "w");
277 fwrite($handle, $form);
278 fclose($handle);
279 dolChmod($file);
280
281 $this->result = array('fullpath' => $file);
282
283 return 1;
284 } else {
285 $this->error = $langs->trans("ErrorCanNotCreateDir", $dir);
286 return 0;
287 }
288 } else {
289 $this->error = $langs->trans("ErrorConstantNotDefined", "DON_OUTPUTDIR");
290 return 0;
291 }
292 }
293
302 private function amountToLetters($montant, $devise1 = '', $devise2 = '')
303 {
304 $unite = array();
305 $dix = array();
306 $cent = array();
307 if (empty($devise1)) {
308 $dev1 = 'euros';
309 } else {
310 $dev1 = $devise1;
311 }
312 if (empty($devise2)) {
313 $dev2 = 'centimes';
314 } else {
315 $dev2 = $devise2;
316 }
317 $valeur_entiere = intval($montant);
318 $valeur_decimal = intval(round($montant - intval($montant), 2) * 100);
319 $dix_c = intval($valeur_decimal % 100 / 10);
320 $cent_c = intval($valeur_decimal % 1000 / 100);
321 $unite[1] = $valeur_entiere % 10;
322 $dix[1] = intval($valeur_entiere % 100 / 10);
323 $cent[1] = intval($valeur_entiere % 1000 / 100);
324 $unite[2] = intval($valeur_entiere % 10000 / 1000);
325 $dix[2] = intval($valeur_entiere % 100000 / 10000);
326 $cent[2] = intval($valeur_entiere % 1000000 / 100000);
327 $unite[3] = intval($valeur_entiere % 10000000 / 1000000);
328 $dix[3] = intval($valeur_entiere % 100000000 / 10000000);
329 $cent[3] = intval($valeur_entiere % 1000000000 / 100000000);
330 $chif = array('', 'un', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit', 'neuf', 'dix', 'onze', 'douze', 'treize', 'quatorze', 'quinze', 'seize', 'dix sept', 'dix huit', 'dix neuf');
331 $secon_c = '';
332 $trio_c = '';
333 $prim = array();
334 $secon = array();
335 $trio = array();
336 // @phpstan-ignore-next-line
337 '@phan-var string[] $prim
338 @phan-var string[] $secon
339 @phan-var string[] $trio
340 ';
341 for ($i = 1; $i <= 3; $i++) {
342 $prim[$i] = '';
343 $secon[$i] = '';
344 $trio[$i] = '';
345 if ($dix[$i] == 0) {
346 $secon[$i] = '';
347 $prim[$i] = $chif[$unite[$i]];
348 } elseif ($dix[$i] == 1) {
349 $secon[$i] = '';
350 $prim[$i] = $chif[($unite[$i] + 10)];
351 } elseif ($dix[$i] == 2) {
352 if ($unite[$i] == 1) {
353 $secon[$i] = 'vingt et';
354 $prim[$i] = $chif[$unite[$i]];
355 } else {
356 $secon[$i] = 'vingt';
357 $prim[$i] = $chif[$unite[$i]];
358 }
359 } elseif ($dix[$i] == 3) {
360 if ($unite[$i] == 1) {
361 $secon[$i] = 'trente et';
362 $prim[$i] = $chif[$unite[$i]];
363 } else {
364 $secon[$i] = 'trente';
365 $prim[$i] = $chif[$unite[$i]];
366 }
367 } elseif ($dix[$i] == 4) {
368 if ($unite[$i] == 1) {
369 $secon[$i] = 'quarante et';
370 $prim[$i] = $chif[$unite[$i]];
371 } else {
372 $secon[$i] = 'quarante';
373 $prim[$i] = $chif[$unite[$i]];
374 }
375 } elseif ($dix[$i] == 5) {
376 if ($unite[$i] == 1) {
377 $secon[$i] = 'cinquante et';
378 $prim[$i] = $chif[$unite[$i]];
379 } else {
380 $secon[$i] = 'cinquante';
381 $prim[$i] = $chif[$unite[$i]];
382 }
383 } elseif ($dix[$i] == 6) {
384 if ($unite[$i] == 1) {
385 $secon[$i] = 'soixante et';
386 $prim[$i] = $chif[$unite[$i]];
387 } else {
388 $secon[$i] = 'soixante';
389 $prim[$i] = $chif[$unite[$i]];
390 }
391 } elseif ($dix[$i] == 7) {
392 if ($unite[$i] == 1) {
393 $secon[$i] = 'soixante et';
394 $prim[$i] = $chif[$unite[$i] + 10];
395 } else {
396 $secon[$i] = 'soixante';
397 $prim[$i] = $chif[$unite[$i] + 10];
398 }
399 } elseif ($dix[$i] == 8) {
400 if ($unite[$i] == 1) {
401 $secon[$i] = 'quatre-vingts et';
402 $prim[$i] = $chif[$unite[$i]];
403 } else {
404 $secon[$i] = 'quatre-vingt';
405 $prim[$i] = $chif[$unite[$i]];
406 }
407 } elseif ($dix[$i] == 9) {
408 if ($unite[$i] == 1) {
409 $secon[$i] = 'quatre-vingts et';
410 $prim[$i] = $chif[$unite[$i] + 10];
411 } else {
412 $secon[$i] = 'quatre-vingts';
413 $prim[$i] = $chif[$unite[$i] + 10];
414 }
415 }
416 if ($cent[$i] == 1) {
417 $trio[$i] = 'cent';
418 } elseif ($cent[$i] != 0 || $cent[$i] != '') {
419 $trio[$i] = $chif[$cent[$i]].' cents';
420 }
421 }
422
423
424 $chif2 = array('', 'dix', 'vingt', 'trente', 'quarante', 'cinquante', 'soixante', 'soixante-dix', 'quatre-vingts', 'quatre-vingts dix');
425 $secon_c = $chif2[$dix_c];
426 if ($cent_c == 1) {
427 $trio_c = 'cent';
428 } elseif ($cent_c != 0 || $cent_c != '') {
429 $trio_c = $chif[$cent_c].' cents';
430 }
431
432 if (($cent[3] == 0 || $cent[3] == '') && ($dix[3] == 0 || $dix[3] == '') && ($unite[3] == 1)) {
433 $somme = $trio[3].' '.$secon[3].' '.$prim[3].' million ';
434 } elseif (($cent[3] != 0 && $cent[3] != '') || ($dix[3] != 0 && $dix[3] != '') || ($unite[3] != 0 && $unite[3] != '')) {
435 $somme = $trio[3].' '.$secon[3].' '.$prim[3].' millions ';
436 } else {
437 $somme = $trio[3].' '.$secon[3].' '.$prim[3];
438 }
439
440 if (($cent[2] == 0 || $cent[2] == '') && ($dix[2] == 0 || $dix[2] == '') && ($unite[2] == 1)) {
441 $somme .= ' mille ';
442 } elseif (($cent[2] != 0 && $cent[2] != '') || ($dix[2] != 0 && $dix[2] != '') || ($unite[2] != 0 && $unite[2] != '')) {
443 $somme .= $trio[2].' '.$secon[2].' '.$prim[2].' milles ';
444 } else {
445 $somme .= $trio[2].' '.$secon[2].' '.$prim[2];
446 }
447
448 $somme .= $trio[1].' '.$secon[1].' '.$prim[1];
449
450 $somme .= ' '.$dev1.' ';
451
452 if (($cent_c == '0' || $cent_c == '') && ($dix_c == '0' || $dix_c == '')) {
453 return $somme.' et z&eacute;ro '.$dev2;
454 } else {
455 return $somme.$trio_c.' '.$secon_c.' '.$dev2;
456 }
457 }
458}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
Class to manage donations.
Definition don.class.php:41
Class to manage generation of HTML components Only common components must be here.
Parent class of subscription templates.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to generate document for subscriptions.
write_file($don, $outputlangs, $currency='')
Write the object to document file to disk.
isEnabled()
Return if a module can be used or not.
__construct($db)
Constructor.
amountToLetters($montant, $devise1='', $devise2='')
numbers to letters
global $mysoc
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
dol_now($mode='gmt')
Return date for now.
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dolChmod($filepath, $newmask='')
Change mod of a file.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
make_substitutions($text, $substitutionarray, $outputlangs=null, $converttextinhtmlifnecessary=0)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newva...
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:125
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:128