source: spip-zone/_plugins_/Associaspip/trunk/inc/association_comptabilite.php @ 73361

Last change on this file since 73361 was 73361, checked in by gildas.cotomale@…, 7 years ago

corrections dans r73343 : erreur systématique au contrôle de la classe comptable et non prise en compte du choix de plan comptable

File size: 66.1 KB
Line 
1<?php
2/***************************************************************************\
3 *  Comptaspip, extension de SPIP pour gestion comptable
4 *
5 * @copyright Copyright (c) 2007 Bernard Blazin & Francois de Montlivault
6 * @copyright Copyright (c) 2010 Emmanuel Saint-James & JeannotLapin
7 *
8 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
9\***************************************************************************/
10
11if (!defined('_ECRIRE_INC_VERSION'))
12        return;
13
14include_spip('base/association');
15
16
17/*****************************************
18 * @defgroup comptabilite_liste_
19 * Recuperation de tableaux PHP eventuellement vide
20 *
21** @{ */
22
23/**
24 * Recupere dans les tables la liste des destinations associees a une operation
25 *
26 * @param int $id_operation
27 *   id_compte de l'operation dans spip_asso_compte (et spip_asso_destination)
28 * @return array $destinations
29 *   Un tableau eventuellement vide de id_destination=>montant
30 * @note:ex
31 * association_liste_destinations_associees($id_operation);
32 */
33function comptabilite_liste_destinationsassociees($id_operation) {
34        $sql = sql_select('recette, depense, id_destination', 'spip_asso_destination_op', "id_compte=" . intval($id_operation));
35        $destinations = array();
36        while ( $r = sql_fetch($sql) ) {
37            $destinations[$r['id_destination']] = $r['recette'] + $r['depense']; // soit recette soit depense est egal a 0, on se contente les additionner
38        }
39        return $destinations;
40}
41
42/**
43 * Tableau des comptes d'une classe du plan comptable
44 *
45 * @param int $classe
46 *   Classe dont on veut recuprer les comptes
47 * @param int active
48 *   Ce parametre facultatif permet de se restreindre aux comptes actifs (1) ou inactifs (0)
49 * @return array $res
50 *   retourne un tableau $code=>$intitule trie par code
51 * @note:ex
52 * association_liste_plan_comptable($classe, $actives);
53 */
54function comptabilite_liste_comptesclasse($classe, $actives='') {
55    $res = array();
56    $sql = sql_select('code, intitule', 'spip_asso_plan', "classe='$classe'".($actives!=''?" AND active=$actives":''), '', 'code'); // recupere le code et l'intitule de tous les comptes de classe $val
57    while ( $r = sql_fetch($sql) )
58        $res[$r['code']] = $r['intitule'];
59    return $res;
60}
61
62/**
63 * Retourne toutes les references disponibles
64 *
65 * @param string $id
66 *   Identifiant du plan comptable qui nous interesse
67 * @return array $pc_liste
68 *   Liste de references comptables
69 */
70function comptabilite_liste_plancodes($id='') {
71    if (!$id)
72        $id = $GLOBALS['association']['plan_comptable'];
73    if ($id) {
74        $trads = array_keys(find_all_in_path('lang/', "pcg2$id", FALSE) ); // recuperer la liste des traductions existantes
75        include(find_in_path('lang/'.$trads[0])); // charger un des fichiers de langue
76        return array_keys($pc_liste); // on ne veut que les cles
77    } else { // $id===FALSE pour local...
78        $pc_liste = array(); // initialiser le tableau
79        $sql = sql_select('code, intitule', 'spip_asso_plan', '', '', 'code'); // recuperer les elements du tableau
80        while( $r = sql_fetch($sql) ) // remplir le tableau
81            $pc_liste[] = $r['code'];
82        return $pc_liste; // retourner le tableau
83    }
84}
85
86/**
87 * Retourne le tableau de nomenclature des comptes
88 *
89 * @param string $id
90 *   Identifiant du plan comptable qui nous interesse
91 * @return array $pc_norme
92 *   Liste de : classe de depart, classe d'arrivee, longueur minimale d'une reference comptable
93 */
94function comptabilite_liste_planregles($id='') {
95    if (!$id)
96        $id = $GLOBALS['association']['plan_comptable'];
97    if (!$id)
98        return array('[0-9]', '[0-9]');
99    $trads = array_keys(find_all_in_path('lang/', "pcg2$id", FALSE) );
100#    include_spip('lang/'. substr($trads[0], 0, -4)  ); // charger le premier fichier de langue SPIP
101    include(find_in_path('lang/'.$trads[0])); // charger le premier fichier de langue SPIP
102    return (array)$pc_norme; // retourner le tableau contenu dans le fichier
103}
104
105/** @} */
106
107
108/*****************************************
109 * @defgroup comptabilite_operation_
110 * Action sur la(s) operation(s) du grand journal
111 *
112** @{ */
113
114/**
115 * Ajouter une operation comptable ainsi que ses ventilations si necessaire
116 *
117 * @param string $date
118 *   Date de l'operation au format ISO
119 * @param float $recette
120 *   Montant encaisse
121 * @param float $depense
122 *   Montant decaisse
123 * @param string $justification
124 *   Libelle de l'operation
125 * @param string $imputation
126 *   Compte d'imputation (reference du plan comptable)
127 * @param string $journal
128 *   Compte financier impacte (reference du plan comptable)
129 * @param int $id_journal
130 *   ID de l'enregistrement associe dans le module  (chaque imputation etant gere par un seul module)
131 * @return int $id_operation
132 *   ID de l'operation dans spip_asso_comptes et spip_asso_destination_op
133 * @note:ex
134 * association_ajouter_operation_comptable($date, $recette, $depense, $justification, $imputation, $journal, $id_journal);
135 */
136function comptabilite_operation_ajouter($date, $recette, $depense, $justification, $imputation, $journal, $id_journal) {
137    $modifs = array(
138        'date_operation' => $date,
139        'imputation' => $imputation,
140        'recette' => $recette,
141        'depense' => $depense,
142        'journal' => $journal,
143        'id_journal' => $id_journal,
144        'justification' => $justification
145    );
146    $id_operation = sql_insertq('spip_asso_comptes', $modifs);
147    // on passe par modifier_contenu afin que l'enregistrement soit envoye aux plugins et que Champs Extras 2 la recupere
148    include_spip('inc/modifier');
149    modifier_contenu('asso_compte', $id_operation, '', $modifs);
150    if (!$imputation) { // On laisse passer ce qui est peut-etre une erreur, pour ceux qui ne definisse pas de plan comptable. Mais ce serait bien d'envoyer un message d'erreur au navigateur plutot que de le signaler seulement dans les log
151        spip_log("imputation manquante : id_compte=$id_compte, date=$date, recette=$recette, depense=$depense, journal=$journal, id_journal=$id_journal, justification=$justification",'associaspip');
152    }
153    if ($GLOBALS['association_metas']['destinations']) { // Si on doit gerer les destinations
154        comptabilite_operation_ventiler($id_operation, $recette, $depense);
155    }
156    return $id_operation;
157
158}
159
160/**
161 * Modifier une operation comptable ainsi que ses ventilations si necessaire
162 *
163 * @param string $date
164 *   Date de l'operation au format ISO
165 * @param float $recette
166 *   Montant encaisse
167 * @param float $depense
168 *   Montant decaisse
169 * @param string $justification
170 *   Libelle de l'operation
171 * @param string $imputation
172 *   Compte d'imputation (reference du plan comptable)
173 * @param string $journal
174 *   Compte financier impacte (reference du plan comptable)
175 * @param int $id_journal
176 *   ID de l'enregistrement associe dans le module  (chaque imputation etant gere par un seul module)
177 * @param int $id_operation
178 *   ID de l'operation dans spip_asso_comptes et spip_asso_destination_op
179 * @return string $err
180 *   Message d'erreur (vide en cas de succes)
181 * @note:ex
182 * association_modifier_operation_comptable($date, $recette, $depense, $justification, $imputation, $journal, $id_journal, $id_operation)
183 */
184function comptabilite_operation_modifier($date, $recette, $depense, $justification, $imputation, $journal, $id_journal, $id_operation) {
185    $err = '';
186    $id_operation = intval($id_operation);
187    if ( sql_countsel('spip_asso_comptes', "id_compte=$id_operation AND vu ") ) { // il ne faut pas modifier une operation verouillee !!!
188        spip_log("modification d'operation comptable : id_compte=$id_operation, date=$date, recette=$recette, depense=$depense, imputation=$imputation, journal=$journal, id_journal=$id_journal, justification=$justification",'associaspip');
189        return $err = _T('asso:operation_non_modifiable');
190    }
191    if ($GLOBALS['association_metas']['destinations']) { // Si on doit gerer les destinations
192        $err = comptabilite_operation_ventiler($id_operation, $recette, $depense);
193    }
194    $modifs = array(
195        'date_operation' => $date,
196        'imputation' => $imputation,
197        'recette' => $recette,
198        'depense' => $depense,
199        'journal' => $journal,
200        'justification' => $justification,
201    );
202    if ($id_journal) { // si id_journal est nul, ne pas le modifier afin de ne pas endommager l'entree dans la base en editant directement depuis le livre de comptes
203        $modifs['id_journal'] = $id_journal;
204    }
205    // on passe par modifier_contenu (et non sql_updateq) pour que la modification soit envoyee aux plugins et que Champs Extras 2 la recupere
206    include_spip('inc/modifier');
207    modifier_contenu('asso_compte', $id_operation, '', $modifs);
208    return $err;
209}
210
211/**
212 * Supprimer une operation dans spip_asso_comptes ainsi que si necessaire sa ventilation dans spip_asso_destination_op ;
213 * C'est la forme interne
214 *
215 * @param int $id_operation
216 *   ID de l'operation a supprimer
217 * @param bool $securite
218 *   Mettre a TRUE pour supprimer quand meme une operation verouillee
219 * @return int $annulation
220 *   ID de l'enregistrement d'ecriture inverse : indique donc une annulation
221 *   comptable quand different de 0, et une suppression pure et simple sinon
222 * @note:ex
223 *    association_supprimer_operation_comptable1($id_operation, $securite);
224 */
225function comptabilite_operation_supprimer($id_operation, $securite=FALSE) {
226    list($date, $recette, $depense, $imputation, $journal, $id_journal, $verrou) = sql_fetsel('date_operation, recette, depense, imputation, journal, id_journal, vu', 'spip_asso_comptes', "id_compte=$id_operation"); // recuperer les informations sur l'operation pour le fichier de log
227    if ( ($securite AND !$verrou) || !$securite ) { // operation non verouillee ou controle explicitement desactive...
228        $annulation = 0;
229        sql_delete('spip_asso_destination_op', "id_compte=$id_operation"); // on efface de la table destination_op toutes les entrees correspondant a cette operation  si on en trouve
230        spip_log("suppression de l'operation comptable $id_operation : date=$date, montant=$recette-$depense, imputation=$imputation, financier=$journal, id_journal=$id_journal, justification=...", 'associaspip'); // on logue quand meme
231        sql_delete('spip_asso_comptes', "id_compte=$id_operation"); // on efface enfin de la table comptes l'entree correspondant a cette operation
232    } else { // on ne supprime pas les ecritures validees/verouillees ; il faut annuler l'operation par une operation comptable inverse...
233        $annulation = sql_insertq('spip_asso_comptes', array(
234            'date_operation' => date('Y-m-d'),
235            'depense' => $recette,
236            'recette' => $depense,
237            'imputation' => _T('asso:compte_annulation_operation', array('numero'=>$id_compte,'date_operation'=>$date) ),
238            'imputation' => $imputation, // pas forcement vrai, mais on fait au plus simples...
239            'journal' => $journal, // pas forcement vrai, mais on fait au plus simples...
240            'id_journal' => -$id_journal, // on garde la trace par rapport au module ayant cree l'operation
241            'vu' => 1, // cette operation n'est pas moifiable non plus...
242        ) ); // on cree l'operation opposee a celle a annuler ; mais ce n'est pas une annulation correcte au regard des numeros de comptes (imputation/journal)...
243    }
244    return $annulation;
245}
246
247/**
248 * Editer des destinations comptables liees a une operation comptable
249 *
250 * @param int $id_compte
251 *   ID de l'operation comptable a ventiller
252 * @param float $recette
253 *   Montant total des recettes a ventiller
254 * @param float $depense
255 *   Montant total des depenses a ventiller
256 * @param array $repartion
257 *   Tableau des id_destination=>montant a ventiler.
258 * Quand vide, les ventilations sont recherchees dans $_POST['id_dest'] et $_POST['montant_dest']
259 * @return void
260 * @note:ex
261 * association_ajouter_destinations_comptables($id_compte, $recette, $depense);
262 */
263function comptabilite_operation_ventiler($id_compte, $recette=0, $depense=0, $repartion=array() ) {
264    sql_delete('spip_asso_destination_op', "id_compte=$id_compte"); // on efface de la table destination_op toutes les entrees correspondant a cette operation  si on en trouve
265    if ($recette>0) // soit une recette
266        $attribution_montant = 'recette';
267    else // soit une depense
268        $attribution_montant = 'depense';
269    if ( count($repartion) ) { // usage normal
270        $toutesDestinationsIds = array_keys($repartion);
271        $toutesDestinationsMontants = array_values($repartion);
272    } else { // donnees de formulaire Associaspip
273        $toutesDestinationsIds = association_recuperer_liste('id_dest', TRUE);
274        $toutesDestinationsMontants = association_recuperer_liste('montant_dest', TRUE);
275    }
276    if ( count($toutesDestinationsIds)>1 ) { // plusieurs destinations
277        foreach ($toutesDestinationsIds as $id => $id_destination) { // ventilation des montants. le tableau des montants a des cles indentique a celui des id
278            $id_dest_op = sql_insertq('spip_asso_destination_op', array(
279                'id_compte' => $id_compte,
280                'id_destination' => $id_destination,
281                $attribution_montant => association_recuperer_montant($toutesDestinationsMontants[$id], FALSE),
282            ));
283        }
284    } elseif ( count($toutesDestinationsIds)==1 ) { // une seule destination : le montant peut ne pas avoir ete precise, on entre directement le total recette+depense
285        $id_dest_op = sql_insertq('spip_asso_destination_op', array(
286            'id_compte' => $id_compte,
287            'id_destination' => $toutesDestinationsIds[1],
288            $attribution_montant => $depense+$recette
289        ));
290    }
291}
292
293/** @} */
294
295
296/*****************************************
297 * @defgroup comptabilite_reference_
298 * Retour de texte relatif a une reference comptable
299 *
300** @{ */
301
302/**
303 * Donner l'intitule d'une reference comptable.
304 *
305 * @param string $code
306 *   La reference comptable dont on veut l'intitule
307 * @param int $parent
308 *   Le nombre maximum de niveau dont il faut remonter dans la recherche d'un
309 * code parent lorsqu'on ne trouve pas le code exact demande.
310 * @return string $nom
311 *   L'intitule correspondant trouve
312 * @note:ex
313 * association_plan_comptable_complet($code,$parent);
314 */
315function comptabilite_reference_intitule($code, $parent=0) {
316    $nom = sql_getfetsel('intitule','spip_asso_plan','code='.sql_quote($code) ); // on tente de recuperer l'intitule defini...
317    if ($nom) // on a trouve ! alors...
318        return extraire_multi($nom, $GLOBALS['spip_lang']); // ...renvoyer la traduction
319    if ($GLOBALS['association_metas']['plan_comptable']) // sinon si on a un plan comptable selectionne
320        $nom = _T('pcg2'.$GLOBALS['association_metas']['plan_comptable'].':'.$code); // on tente de recuperer dans le plan choisi
321    if (str_replace('_', ' ',$code)!=$nom) // on a trouve alors...
322        return $nom; // ...renvoyer la traduction
323    $code = substr($code, 0, -1); // sinon on enleve le dernier caractere...
324    if (strlen($code) AND $parent) // ...et tant qu'il y a un caractere... et qu'on peut remonter...
325        return comptabilite_reference_intitule($code, $parent--); // ...on y retourne
326    else // mais quand on n'a pas de caractere a consommer...
327        return ''; // ...c'est la fin des haricots
328}
329
330/**
331 * Donne l'operation comptable associee a un enregistrement pour un module gerant une imputation comptable
332 *
333 * @param string $nom
334 *   Nom de la meta contenant le code d'imputation (cf. $var)
335 *   ou directement le code d'imputation gere par le module
336 * @param int $id
337 *   ID de l'enregistrement associe par-et-dans le module
338 * @param string $var
339 *   Nom de la table des metas :
340 * c'est l'entree du tableau $GLOBALS qui contient le nom associe a l'imputation
341 * @return int $id_op
342 *   ID de la premiere operation correspondante enregistree dans le grand livre
343 * @note:ex
344 * $association_imputation = charger_fonction('association_imputation', 'inc'); $id_operation = sql_getfetsel('id_compte', 'spip_asso_comptes', $association_imputation($nom, $id));
345 */
346function comptabilite_reference_operation($nom, $id=0, $var='association_metas') {
347    return sql_getfetsel('id_compte', 'spip_asso_comptes', 'imputation='. sql_quote($var?$GLOBALS[$var][$nom]:$nom) .'AND id_journal='. intval($id) );
348}
349
350/**
351 * Recupere le code du compte des virements internes
352 *
353 * @return string $res
354 *   C'est le code normalement defini dans la configuration du plugin.
355 *   S'il n'existe pas, on prend le premier compte 58x existant,
356 *   sinon on cree le compte 581 !
357 * @note:ex
358 * association_creer_compte_virement_interne();
359 */
360function comptabilite_reference_virements() {
361    if ($GLOBALS['association_metas']['pc_intravirements']) // un code de virement interne est deja defini !
362        return $GLOBALS['association_metas']['pc_intravirements'];
363    $res = comptabilite_liste_comptesclasse($GLOBALS['association_metas']['classe_banques']); // on recupere tous les comptes de la classe "financier" (classe 5)
364    foreach($res as $code => $libelle) { // existe-t-il le compte 58x ? (nota : c'est la compta francaise...)
365        if (substr($code,1,1)=='8') // il existe un code qui commence par 58...
366            return $code;
367    }
368    // j'ai rien trouve, je cree le compte 581
369    $code = $GLOBALS['association_metas']['classe_banques'].'81';
370    $id_plan = sql_insertq('spip_asso_plan', array(
371        'code' => $code,
372        'intitule' => _T('asso:virement_interne'),
373        'classe' => $GLOBALS['association_metas']['classe_banques'],
374        'type_op' => 'multi',
375        'solde_anterieur' => '0',
376        'date_anterieure' => date('Y-m-d'),
377        'commentaire' => _T('asso:compte_cree_automatiquement'),
378        'active' => '0',
379        'maj' => date('Y-m-d')
380    ));
381    if ($id_plan)
382        sql_insertq('spip_association_metas', array(
383            'nom' => 'pc_intravirements',
384            'valeur' => $code,
385        ));
386    return $code;
387}
388
389/** @} */
390
391
392/*****************************************
393 * @defgroup comptabilite_verifier_
394 * Validation du plan comptable...
395 *
396 * @param string $plan
397 *   Plan comptable par rapport auquel on valide
398 * @return string $err
399 *   Message d'erreur (vide si validation passee)
400 *
401** @{ */
402
403/**
404 * Verifie qu'une classe donnee est conforme au plan comptable choisi
405 *
406 * @param string $classe
407 *   Classe a verifier
408 */
409function comptabilite_verifier_classe($classe, $plan='') {
410    if ( strlen($classe)!=1 ) // champ vide ou ayant plus d'un caractere
411        return _T('compta:erreur_classe_longueur');
412    $regles = comptabilite_liste_planregles($plan);
413    if ( !preg_match('/^'.$regles[0].'$/', $classe) ) // champ hors plage
414        return _T('compta:erreur_classe_plage', array('intervalle'=>$regles[0],) );
415    return '';
416}
417
418/**
419 * Verifie qu'une reference donnee est conforme au plan comptable choisi
420 *
421 * @param string $code
422 *   Reference a verifier
423 * @param string $classe
424 *   Caractere de la classe si on souhaite s'assurer que c'est l'initial du code
425 * @param string $sens
426 *   credit|debit|multi : on s'assurera sommairement que le type de compte est bon
427 * Pour l'instant on ne prend en compte que 2 classes Associaspip mais y d'autres
428 * http://www.mesexercices.com/recherche_information/comptabilite-les-comptes-de-classe-6-charges-sont-normalement_6_38694.htm
429 * Par ailleurs, on ne peut pas fonctionner par classe caril y a des exceptions !
430 *  http://dz.viadeo.com/fr/questions/repondre/?questionId=0021aox4cgt6v70a
431 * Peut-etre faudrat-il rajouter des regles pour une meilleure validation ?
432 */
433function comptabilite_verifier_code($code, $classe='', $sens='multi', $plan='') {
434    $regles = comptabilite_liste_planregles($plan);
435    if ( !preg_match('/^'. implode('', $regles) .'\w*$/', $code) ) // champ de longueur insuffisante ou ne commencant pas de facon adequate
436        return _T('compta:erreur_plan_code_format', array('nombre'=>count($regles),) );
437#    elseif ( strlen($code)<count($regles) ) // champ de longueur insuffisante
438#       return _T('compta:erreur_code_longueur', array('nombre'=>count($regles),) );
439    if (sql_countsel('spip_asso_plan', "code='$code'")>1) // occurences multiples d'une meme reference
440        return _T('compta:erreur_plan_code_doublon');
441    if ( $classe!==FALSE AND $classe!=='' AND $code[0]!=$classe ) // discordance avec la classe
442        return _T('compta:erreur_code_classe', array('nombre'=>$classe,) );
443#    if ( $code[0]==$GLOBALS['association_metas']['classe_produits'] AND $sens=='debit' ) // ce sont tous des comptes essentiellement créditeurs...
444#       return _T('compta:erreur_classe_type', array('numero'=$code[0], 'interdit'=>_T('compta:item_direction_debit'),) );
445#    elseif ( $code[0]==$GLOBALS['association_metas']['classe_charges'] AND $sens=='credit' ) // ce sont tous des comptes essentiellement débiteurs...
446#       return _T('compta:erreur_classe_type', array('numero'=$code[0], 'interdit'=>_T('compta:item_direction_credit'),) );
447     return '';
448}
449
450/**
451 * Validation de la table du plan comptable utilise
452 * - on doit avoir un certain nombre de classes differentes
453 * - chaque reference doit etre unique
454 * - les references et classes doivent respecter le referentiel choisi
455 *
456 * @param int $nbr
457 *   Nonbre de classes requises par le plugin
458 * @note:ex
459 *   association_valider_plan_comptable()
460 */
461function comptabilite_verifier_plan($nbr=2, $plan='', $lang='') {
462    $classes = array(); // initialiser la liste des classes
463    $codes = array(); // initialiser la liste des references
464    $sql = sql_select('code, classe', 'spip_asso_plan'); // recupere la reference et la classe de tous les comptes du plan comptable
465    while ($r = sql_fetch($sql)) { // verification de chaque reference
466        $classes[$r['classe']] = $r['classe']; // on comptes les classes differentes
467        if( array_key_exists($r['code'], $codes) ) // on a deux fois le meme code...
468            return _T('compta:erreur_plan_code_doublon', array('code'=>$r['code'],) ); // ...on arrete sur cette erreur...
469        else { // c'est la 1ere occurence
470            $codes[$r['code']] = comptabilite_verifier_code($r['code'], $r['classe'], 0, $lang); // verifier qu'il est bien forme
471            if ($codes[$r['code']]) // mauvais format...
472                return $codes[$r['code']]; // ...on arrete sur cette erreur...
473        }
474    }
475    if ( count($classes)<$nbr ) // on doit avoir au moins $nbr classes differentes
476        return _T('compta:erreur_plan_nombre_classes', array('nombre'=>$nbr,) ); // ...on arrete sur cette erreur...
477    foreach($classes as $classe) // verifier chaque classe
478        if ( $erreur = comptabilite_verifier_classe($classe, $plan, $lang) )
479            return $erreur; // ...renvoyer la premiere erreur rencontree...
480    return '';
481}
482
483/** @} */
484
485
486/*****************************************
487 * @defgroup filtre_selecteur_compta_
488 * Selecteurs dHTML propres a la compta
489 *
490** @{ */
491
492/**
493 * Selecteur de classe du plan comptable
494 *
495 * @param string $classe
496 *   Classe comptable selectionnee
497 * @param string $name
498 *   Nom du selecteur de classe dans le formulaire
499 * @param string $ref
500 *   Nom du selecteur de code dans le formulaire
501 * @return string $res
502 *   Liste deroulante des classes comptables disponibles
503 * @note:ex
504 * balise_SELECTEUR_CLASSE_COMPTABLE_dyn($classe)
505 */
506function filtre_selecteur_compta_classe($classe, $name='classe', $ref='code') {
507    if (@!$GLOBALS['meta']['html5'] AND $ref) { // JavaScript sur le onChange
508        $js = ' var OptCurrVal = document.getElementById(\'selecteur_'.$name.'\').value; ';
509        $js .= ' var OptGroupElt = document.getElementById(\'PlanComptableClasse\'+OptCurrVal); '; // mettre le selecteur de code directement au debut de la classe selectionnée
510        $js .= ' if (OptGroupElt) { OptGroupElt.childNodes[0].selected=\'selected\'; document.getElementById(\'selecteur_'.$ref.'\').onchange(); } '; // appeler la fonction onChange du selecteur de code (pour repercuter la modification dans les champs lies)
511    }
512    $res = "<select name='$name' id='selecteur_$name'". ($js?" onclick='$js'":'') .">\n";
513    $res .= '<option value="">'. _T('compta:item_no_classe') ."</option>\n";
514    $lc = ($GLOBALS['association_metas']['plan_comptable']?comptabilite_liste_plancodes():array(1,2,3,4,5,6,7,8,9,0));
515    foreach ($lc as $code) {
516        if (strlen($code)==1) { // il s'agit d'une classe
517            $res .= '<option value="'.$code.'"';
518            $res .= ($code==$classe) ? ' selected="selected"' : '';
519            $res .= '>'.$code.' - '. comptabilite_reference_intitule($code) ;
520            $res .= "</option>\n";
521        }
522    }
523    return "$res</select>\n";
524}
525
526/**
527 * Selecteur de reference du plan comptable
528 *
529 * @param string $code
530 *   Reference comptable selectionnee
531 * @param string $name
532 *   Nom du champ de reference comptable dans le formulaire
533 * @param string $ref
534 *   Nom du champ d'intitule comptable dans le formulaire
535 * @return string $res
536 *   Liste deroulante des references comptables disponibles
537 * @note:ex
538 * balise_SELECTEUR_CODE_COMPTABLE_dyn($code)
539 */
540function filtre_selecteur_compta_code($code, $name='code', $ref='intitule') {
541    if (@$GLOBALS['meta']['html5']) { // the sexy way
542        $res = "<datalist id='selecteur_$name'>\n"; // ce sera une liste d'auto-completion
543        $res .= '<em class="explication">'. _T('compta:item_no_code') ."</em>\n"; // voir au sujet de l'option vide ci-apres : ceci n'est pas affiche quand la DataList est reconnue
544        $res .= "<select name='$name'>\n"; // fall-back
545        $res .= "<option value=''></option>\n"; // pas de valeur pour ne pas saisir une option indesiree, mais pas de texte pour ne pas parasiter la DataList
546    } else { // the causual way
547#       $js = 'var CurrentOption=document.getElementById(\'selecteur_'.$name.'\'); document.getElementById(\''.$name.'\').value=CurrentOption.value; '; // recopier le code dans le champ prevu
548        $js = 'var currentVal=String(this.options[this.selectedIndex].text).split(\'-\'); document.getElementById(\''.$name.'\').value=currentVal[0]; '; // recopier le code dans le champ prevu
549        if ($ref)
550#           $js .= ' document.getElementById(\''.$ref.'\').value=CurrentOption.options[CurrentOption.selectedIndex].text; '; // recopier l'intitule dans le champ prevu
551            $js .= ' document.getElementById(\''.$ref.'\').value=currentVal[1]; '; // recopier l'intitule dans le champ prevu
552        $res = "<select name='$name' id='selecteur_$name' onchange='$js'>\n"; // malgre le JS, le selecteur est homonyme pour permettre de prendre la selection sans remplir quand JS est desactive. faut par contre le placer avant le champ libre pour que la valeur qui y est saisie remplace celle-ci (en l'ecrasant)
553        $res .= '<option value="">'. _T('compta:item_no_code') ."</option>\n"; // pas de valeur : pour ne rien choisir.
554    }
555    $optgroup = FALSE;
556    foreach (comptabilite_liste_plancodes() as $rc) {
557        if (@!$GLOBALS['meta']['html5'] AND strlen($rc)==1) { // il s'agit d'une classe
558            if ($optgroup) //
559                $lst .= "</optgroup>\n";
560            $res .= '<optgroup id="PlanComptableClasse'.$rc.'" label="'.$rc.' - '.  comptabilite_reference_intitule($rc) ."\">\n";
561            $optgroup = TRUE;
562        } else { // il s'agit d'une reference
563            $res .= '<option value="'.$rc.'"';
564            $res .= ($code==$rc) ? ' selected="selected"' : '';
565            $res .= '>'.$rc.' - '. comptabilite_reference_intitule($rc) ;
566            $res .= "</option>\n";
567        }
568    }
569    if ($optgroup)
570        $res .= "</optgroup>\n";
571    return "$res</select>\n". (@$GLOBALS['meta']['html5']?'</datalist>\n':'');
572}
573
574/**
575 * Selecteur de destinations
576 *
577 * @param array $destinations
578 *   Tableau de id_destination=>montant deja selectionnees (vide pour un ajout)
579 * @param int $defaut
580 *   Permet de selectionner une destination par defaut (par id_destination)
581 *   quand $destinations est vide
582 * @return string $res
583 *   Code HTML+JS correspondant au selecteur de destinations
584 * @note
585 *   Associaspip : selon la configuration, on ne peut associer qu'une destination unique ou ventiler sur plusieurs destinations
586 * @note:ex
587 *   association_editeur_destinations($destinations, $defaut)
588 */
589function filtre_selecteur_compta_destinations($destinations=array(), $defaut='') {
590        $options = array();
591        $sql = sql_select('id_destination, intitule', 'spip_asso_destination', '', '', 'intitule');
592        while ( $r = sql_fetch($sql) ) // Constuire les balises OPTIONs d'un SELECT ; mais il faudrait arranger ca si une seule
593                $options[$r['id_destination']] = '<option value="'.$r['id_destination'].'">'.$r['intitule'].'</option>';
594        if ( !count($options) OR !$GLOBALS['association_metas']['destinations'] ) // aucune destination definie ! ou usage desactive !
595            return '';
596        $idIndex = 1;
597        if (intval($GLOBALS['association_metas']['destinations'])>1) { // destinations multiples : on insere ...
598            $script = '<script type="text/javascript" src="'
599            . find_in_path('javascript/jquery.destinations_form.js')
600                . '"></script>'; // ...le JS qui permet de les gere
601            $addDestinationButton = "\n<button class='destButton' type='button' onclick='addFormField(); return FALSE;'>+</button>"; // ...le bouton pour ajouter une destination
602        } else // destination unique
603            $script = $addDestinationButton = '';
604        if ( count($destinations) ) { // si on a une liste de destinations (on edite une operation)
605          $options = join("\n", $options) ;
606          $res = '';
607          foreach ($destinations as $destId => $destMontant) { // restitution des listes de selection HTML
608                $res .= '<div id="row'.$idIndex.'" class="choix"><ul>'
609                . '<li>'
610                . '<select name="id_dest['.$idIndex.']" id="id_dest_'.$idIndex.'" >'
611                . preg_replace("/(value='".$destId."')/", '$1 selected="selected"', $options)
612                . '</select></li>';
613                if (($GLOBALS['association_metas']['destinations'])>1) { // destinations multiples
614                    $res .= '<li><input name="montant_dest['.$idIndex.']" value="'
615                        . association_formater_nombre($destMontant)
616                        . '" type="text" id="montant_dest_'.$idIndex.'" class="number decimal price" />'
617                        . '<button class="destButton" type="button" onclick="addFormField(); return false;">+</button>';
618                    if ($idIndex>1) // bouton de suppression de l'affectation courante
619                        $res .= '<button class="destButton" type="button" onclick="removeFormField(\'#row'.$idIndex.'\'); return false;">-</button>';
620                }
621                $res .= '</li></ul></div>';
622                $idIndex++;
623          }
624        } else { // pas de destination deja definies pour cette operation
625            if ( $defaut ) // un choix par defaut
626              $options[$defaut] = str_replace('<option ', '<option selected="selected" ', $options[$defaut]);
627            $n = " name='id_dest[1]' id='id_dest_1'";
628            if ( count($options)==1 ) // on a une seule destination possible, pas de selecteur
629              $res = "<input$n readonly='readonly' value='$id' /> $texte";
630            else // plusieurs destinations possibles
631              $res = "<ul>\n<li>"
632                . "<select$n>" . join("\n", $options) . '</select>'
633                . "\n</li><li><input name='montant_dest[1]' id='montant_dest_1'/>"
634                . $addDestinationButton.'</li></ul>';
635        }
636    return $script
637      . '<div id="divTxtDestination" class="formulaire_edition_destinations">'
638      . '<label>'
639      . _T('asso:destination')
640      . '</label>'
641      . $res
642      . ((intval($GLOBALS['association_metas']['destinations'])>1)? '' :
643        ('<input type="hidden" id="idNextDestination" value="'.($idIndex+1).'" />'))
644      . '</div>';
645}
646
647/**
648 * Selecteur de plan comptable
649 *
650 * @param string $pcg
651 *   ID du plan comptable selectionne
652 * @param string $nom
653 *   Nom du selecteur dans le formulaire
654 * @return string $res
655 *   Liste deroulante des plans comptables disponibles :
656 * ce sont de fichiers de langue "lang/pcg2*.php"
657 */
658function filtre_selecteur_compta_plan($pcg, $nom='plan_comptable') {
659    $liste_plans = array_keys(find_all_in_path('lang/', 'pcg2', FALSE) ); // '\\bpcg2.*\\b'
660    foreach ($liste_plans as $pos=>$plan) {
661        $lang = strpos($plan, '_', 3); // l'indicateur de langue commence au premier underscore
662        $liste_plans[$pos] = substr($plan, 4, ($lang?$lang:strlen($plan))-4 ); // le tableau contient des noms de fichier comme "pcg2IdPlan_CodeLang.php" dont on ne veut garder ici que "IdPlan"
663    }
664    $desc_table = charger_fonction('trouver_table', 'base');
665    $res = "<select name='$nom' id='selecteur_$nom'>\n";
666    $res .= '<option value="">'. _T('ecrire:item_non') ."</option>\n";
667    foreach (array_unique($liste_plans) as $id) {
668        $res .= '<option value="'.$id.'"'.
669        ($id==$pcg?' selected="selected"':'') .'>';
670        $pays = $desc_table('pays') ? sql_getfetsel('nom', 'spip_pays', 'code='.sql_quote($id) ) : '';
671        $res .= $pays ? extraire_multi($pays, $GLOBALS['spip_lang']) : strtoupper($id) ;
672        $res .= "</option>\n";
673    }
674    return "$res</select>\n";
675}
676
677/** @} */
678
679
680/*****************************************
681 * @defgroup comptabilite_ _
682 * Divers
683 *
684** @{ */
685
686
687/**
688 * On recupere les soldes des differents comptes de la classe specifiee pour la periode specifiee
689 * Ceci permet d'etablir la balance des comptes de la classe :
690 * http://fr.wikipedia.org/wiki/Balance_comptable
691 *
692 * @param int $classe
693 *   Classe dont on veut recuperer les soldes des differents comptes
694 * @param int $periode
695 *   ID exercice ou annee (selon configuration)
696 * @param int $destination
697 *   ID destination
698 * @param float $direction
699 *   Le signe de ce parametre indique le type de compte (et donc le sens de calcul du solde)
700 *   positif : comptes de credit (solde=recettes-depenses)
701 *   negatif : comptes de debit (solde=depenses-recettes)
702 * @return ressource $query
703 *   Resultat de la requete donnant les soldes de chaque compte de la classe indiquee
704 * @note
705 *   d'apres http://www.lacompta.ch/MITIC/theorie.php?ID=26 c'est le solde qui est recherche, et il corresponde bien a :
706 *  recettes-depenses=recettes pour les classes 6
707 *  depenses-recettes=depenses pour les classes 7
708 */
709function association_calcul_soldes_comptes_classe($classe, $periode=0, $destination=0, $direction='-1') {
710    $c_group = (($classe==$GLOBALS['association_metas']['classe_banques'])?'journal':'imputation');
711    $valeurs = (($direction)
712        ?
713        ( ($direction<0)
714            ?'SUM('.(($destination)?'a_d':'a_c').'.depense-'.(($destination)?'a_d':'a_c').'.recette) AS valeurs'
715            : 'SUM('.(($destination)?'a_d':'a_c').'.recette-'.(($destination)?'a_d':'a_c').'.depense) AS valeurs'
716        )
717        :
718        'SUM('.(($destination)?'a_d':'a_c').'.recette) AS recettes, SUM('.(($destination)?'a_d':'a_c').'.depense) as depenses, SUM('.(($destination)?'a_d':'a_c').'.recette-'.(($destination)?'a_d':'a_c').'.depense) AS soldes' );
719    $c_having = ($direction) ? 'valeurs>0' : ''; // on ne retiendra que les totaux non nuls...
720    if ( sql_countsel('spip_asso_plan','active=1') ) { // existence de comptes actifs
721        $p_join = " RIGHT JOIN spip_asso_plan AS a_p ON a_c.$c_group=a_p.code";
722        $p_select = ', a_p.code, a_p.intitule, a_p.classe';
723        $p_order = 'a_p.code'; // imputation ou journal
724#       $p_where = 'a_p.classe='.sql_quote($classe);
725        $p_having = 'a_p.classe='.sql_quote($classe); // ok : on agrege par code (indirectement) associe a une classe unique selectionnee ...
726    } else { // pas de comptes actifs ?!?
727        $p_join = $p_select = $p_where = $p_having = '';
728        $p_order = $c_group; // imputation ou journal
729    }
730    if ( $periode ) { // restriction sur une periode donnee
731        if ($GLOBALS['association_metas']['exercices']) { // exercice budgetaire personnalise
732            $exercice = sql_fetsel('date_debut, date_fin', 'spip_asso_exercices', "id_exercice=".intval($periode));
733            $c_where = "a_c.date_operation>='$exercice[date_debut]' AND a_c.date_operation<='$exercice[date_fin]' ";
734        } else { // exercice budgetaire par annee civile
735            $c_where = "DATE_FORMAT(a_c.date_operation, '%Y')=".intval($periode);
736        }
737#    } elseif ( $classe==$GLOBALS['association_metas']['classe_banques'] ) { // encaisse
738#       $c_where = 'LEFT(a_c.imputation,1)<>'. sql_quote($GLOBALS['association_metas']['classe_contributions_volontaires']) .' AND a_c.date>=a_p.date_anterieure AND a_c.date<=NOW() ';
739    } else { // tout depuis le debut ?!?
740        $c_where = 'a_c.date_operation<=NOW()'; // il faut mettre un test valide car la chaine peut etre precedee de "AND "...  limiter alors a aujourd'hui ?
741    }
742    $query = sql_select(
743        "$c_group, $valeurs ". ($destination ? ', a_d.id_destination' : '') .$p_select, // select
744        'spip_asso_comptes AS a_c '. ($destination ? 'LEFT JOIN spip_asso_destination_op AS a_d ON a_d.id_compte=a_c.id_compte ' : '') .$p_join, // from
745        ($destination ? "a_d.id_destination=$destination AND " : '') . ($p_where?"$p_where AND ":'')  .$c_where, // where
746        $c_group, // group by
747        $p_order, // order by
748        '', // limit
749        $c_having. (($c_having && $p_having)?' AND ':'') .$p_having // having
750    );
751    return $query;
752}
753
754/**
755 * On affiche les totaux (recettes et depenses) des differents comptes de la classe specifiee pour une periode donnee
756 *
757 * @param array $classes
758 *   Liste des classes dont on veut afficher les soldes des differents comptes
759 * @param string $prefixe
760 *   Prefixe a applique aux termes qualifiant la direction pour former le titre du tableau
761 * @param float $direction
762 *   Le signe de ce parametre indique le type de compte (et donc le sens de calcul du solde)
763 *   positif : comptes de credit (solde=recettes-depenses)
764 *   negatif : comptes de debit (solde=depenses-recettes)
765 * @param int $periode
766 *   ID exercice ou annee (selon configuration)
767 * @param int $destination
768 *   ID destination
769 * @return void
770 *   HTML-Table listant les differents soldes ordonnes par classes puis par numeros de compte
771 */
772function association_liste_totaux_comptes_classes($classes, $prefixe='', $direction='-1', $periode=0, $destination=0) {
773    if( !is_array($classes) ) { // a priori une chaine ou un entier d'une unique classe
774        $liste_classes = array( $classes ) ; // transformer en tableau (puisqu'on va operer sur des tableaux);
775    } else { // c'est un tableau de plusieurs classes
776        $liste_classes = $classes;
777    }
778    $titre = $prefixe.'_'. ( ($direction) ? (($direction<0)?'depenses':'recettes') : 'soldes' );
779    echo "<table width='100%' class='asso_tablo' id='asso_tablo_$titre'>\n";
780    echo "\n<tr>";
781    echo '<th scope="col" style="width:10px">&nbsp;</th>';
782    echo '<th scope="col" style="width:30px">&nbsp;</th>';
783    echo '<th scope="col">'. _T("asso:$titre") .'</th>';
784    if ($direction) { // mode liste comptable : charge, produit, actifs, passifs
785        echo '<th scope="col" style="width:80px">&nbsp;</th>';
786    } else { // mode liste standard : contributions volontaires et autres
787        echo '<th scope="col" style="width:80px">'. _T("asso:$prefixe".'_recettes') .'</th>';
788        echo '<th scope="col" style="width:80px">'. _T("asso:$prefixe".'_depenses') .'</th>';
789        // echo '<th scope="col" width="80">'. _T("asso:$prefixe".'_solde') .'</th>';
790    }
791    echo "</tr>\n";
792    $total_valeurs = $total_recettes = $total_depenses = 0;
793    $chapitre = '';
794    $i = 0;
795    foreach ( $liste_classes as $rang => $classe ) {
796        $query = association_calcul_soldes_comptes_classe($classe, $periode, $destination, $direction );
797        while ($data = sql_fetch($query)) {
798            echo '<tr>';
799            $new_chapitre = substr($data['code'], 0, 2);
800            if ($chapitre!=$new_chapitre) {
801                echo '<td class="text">'. $new_chapitre . '</td>';
802                echo '<td colspan="3" class="text">'. comptabilite_reference_intitule($new_chapitre) .'</td>';
803                $chapitre = $new_chapitre;
804                echo "</tr>\n<tr>";
805            }
806#           if ( floatval($data['valeurs']) || floatval($data['recettes']) || floatval($data['depenses']) ) { // non-zero...
807                echo "<td>&nbsp;</td>";
808                echo '<td class="text">'. $data['code'] .'</td>';
809                echo '<td class="text">'. $data['intitule'] .'</td>';
810                if ($direction) { // mode liste comptable
811                    echo '<td class="decimal">'. association_formater_nombre($data['valeurs']) .'</td>';
812                    $total_valeurs += $data['valeurs'];
813                } else { // mode liste standard
814                    echo '<td class="decimal">'. association_formater_nombre($data['recettes']) .'</td>';
815                    $total_recettes += $data['recettes'];
816                    echo '<td class="decimal">'. association_formater_nombre($data['depenses']) .'</td>';
817                    $total_depenses += $data['depenses'];
818                    //echo '<td class="decimal">'. association_formater_nombre($data['soldes']) .'</td>';
819                    $total_valeurs += $data['soldes'];
820                }
821                echo "</tr>\n";
822#           }
823        }
824    }
825    echo "\n<tr class='row_first'>";
826    echo '<th colspan="2">&nbsp;</th>';
827    echo '<th scope="row" class="text solde">'. _T("asso:$prefixe".'_total') .'</th>';
828    if ($direction) { // mode liste comptable
829        echo '<th class="solde decimal">'. association_formater_nombre($total_valeurs) . '</th>';
830    } else { // mode liste standard
831        echo '<th class="entree decimal">'. association_formater_nombre($total_recettes) . '</th>';
832        echo '<th class="sortie decimal">'. association_formater_nombre($total_depenses) . '</th>';
833        // echo '<th class="solde decimal">'. association_formater_nombre($total_valeurs) . '</th>';
834    }
835    echo "</tr>\n</table>\n";
836    return $total_valeurs;
837}
838
839/**
840 * On affiche la difference entre les recettes et les depenses (passees en parametre) pour les classes d'un exercice
841 * @param float $recettes
842 *   Total des recettes
843 * @param float $depenses
844 *   Total des depenses
845 * @return void
846 *   Table-HTML presentant le solde comptable (deficit ou benefice)
847 */
848function association_liste_resultat_net($recettes, $depenses) {
849    echo "<table width='100%' class='asso_tablo' id='asso_tablo_bilan_solde'>\n";
850    echo "<tr>";
851    echo '<th style="width: 10px">&nbsp;</th>';
852    echo '<th style="width: 30px">&nbsp;</th>';
853    echo '<th scope="row">'. _T('asso:cpte_resultat_titre_resultat') .'</th>';
854    echo '<th style="width: 80px">&nbsp;</th>';
855    echo "</tr>";
856    echo "\n<tr>";
857    echo '<th colspan="2">&nbsp;</th>';
858    $res = $recettes-$depenses;
859    echo '<th class="solde text">'. (($res<0) ? _T('asso:cpte_resultat_perte') : _T('asso:cpte_resultat_benefice')) .'</th>';
860    echo '<th class="solde decimal">'. association_formater_nombre(abs($res)) .'</th>';
861    echo "</tr></table>";
862}
863
864function export_compte($ids, $mode, $icone = true)
865{
866        // exports connus (a completer au besoin)
867        foreach(array('csv','ctx','dbk','json','tex','tsv','xml','yaml') as $t){
868                        $args = $ids['id_periode'] . "-$mode-"
869                        . $ids['type_periode']
870                        .($ids['destination']? ('-' . $ids['destination']) :'');
871
872                $s = ($t == 'tex') ? 'latex' : $t;
873                $script = "export_soldescomptes_$s";
874                include_spip('inc/actions');
875                $url = generer_action_auteur($script, $args);
876                $t = strtoupper($t);
877                if ($icone)
878                    echo association_navigation_raccourci1($t, 'export-24.png', $url);
879                else
880                    echo "<a href='$url'>$t</a> ";
881        }
882}
883
884
885// Brique commune aux classes d'exportation des etats comptables
886class ExportComptes_TXT {
887
888    var $periode; // id_exercice || annee
889    var $destination; // id_destination
890    var $type; // type d'export : bilan|resultat
891    var $classes; // liste des classes a exporter
892    var $titre; // intitule de l'exercice
893    var $out; // contenu du fichier
894
895    /**
896     * Constructeur (fonction d'initialisatio de la classe)
897     *
898     * @param array|string $var
899     *   Tableau des parametres (les cles sont : id_periode, id_destination, titre_periode, classes, titre)
900     *   Ce tableau peut etre serialise et c'est la chaine de caracteres resultante qui est passee
901     *   Enfin, quand il n'y a rien, on recupere les differents elements dans l'environnement
902     * @return $this->
903     *   Les proprietes de la classe sont initialisees
904     */
905    function __construct($var='') {
906        if ( !$var ) // non transmis
907            $tableau = association_passeparam_compta(); // recuperer dans l'environnement (parametres d'URL)
908        elseif ( is_string($var) ) // transmis comme lien serialise
909            $tableau = unserialize(rawurldecode($var));
910        elseif ( is_array($var) ) // transmis comme tableau PHP
911            $tableau = $var;
912        else
913            $tableau = array($var=>0);
914        $this->periode = intval($tableau['id_periode']);
915        $this->destination = intval($tableau['destination']);
916        $this->type = $tableau['type'];
917        $this->titre = ($tableau['titre_periode']);
918        if ( count($tableau['classes']) ) { // on a la liste des classes qui est fournie
919            $this->classes = $tableau['classes'];
920        } else { // on sait retrouver la liste des tables en se basant sur le type d'exportation
921            switch ($tableau['type']) {
922                case 'bilan' :
923                    $query = sql_select(
924                        'classe', // select
925                        'spip_asso_plan', // from
926                        sql_in('classe', array($GLOBALS['association_metas']['classe_charges'],$GLOBALS['association_metas']['classe_produits'],$GLOBALS['association_metas']['classe_contributions_volontaires']), 'NOT'), // where  not in
927                        'classe', // group by
928                        'classe' // order by
929                    );
930                    while ($data = sql_fetch($query)) {
931                        $this->classes[$data['classe']] = 0;
932                    }
933                    break;
934                case 'resultat' :
935                    $this->classes = array($GLOBALS['association_metas']['classe_charges']=>'-1', $GLOBALS['association_metas']['classe_produits']=>'+1', $GLOBALS['association_metas']['classe_contributions_volontaires']=>0);
936                    break;
937            }
938        }
939        $this->out = '';
940    }
941
942    /**
943     * Export texte de type tableau (lignes*colonnes) simple : CSV,CTX,HTML*SPIP,INI*,TSV,etc.
944     *
945     * de par la simplicite recherchee il n'y a pas de types ou autres : CSV et CTX dans une certaine mesure pouvant distinguer "nombres", "chaines alphanumeriques" et "chaine binaires encodees"
946     *
947     * @param string $champsSeparateur
948     *   Caractere separant deux champs/colonnes.
949     *   (par exemple : la virgule)
950     * @param string $lignesSeparateur
951     *   Caractere separant deux lignes/enregistrements.
952     *   (par exemple : le saut de ligne)
953     * @param array $echappements
954     *   Tableaux des remplacemens simples a effectuer : "des ceci"=>"par cela"
955     *   Il faut, en effet, souvent proteger la presence de caracteres speciaux
956     *   qui sont utilises comme parametres ici.
957     * @param string $champDebut
958     *   Caracter place au debut de chaque champ/colonne
959     * @param string $champFin
960     *   Caracter place a la fin de chaque champ/enregistrement
961     * @param bool $entete
962     *   Indique si en plus des donnees il faut rajouter (vrai --par defaut) ou pas (faux) une ligne de titre au debut
963     * @param bool $multi
964     *   Indique si on recupere directement le solde (faux --par defaut) ou si on recupere separement les totaux des recettes et des depenses
965     * @return string $this->out
966     *   Contenu de l'export
967     */
968    function exportLignesUniques($champsSeparateur, $lignesSeparateur, $echappements=array(), $champDebut='', $champFin='', $entete=TRUE, $multi=FALSE) {
969        if ($entete) {
970            $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), utf8_decode(html_entity_decode(_T('asso:entete_code')))) .$champFin.$champsSeparateur;
971            $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), utf8_decode(html_entity_decode(_T('asso:entete_intitule')))) .$champFin.$champsSeparateur;
972            if (!$multi) {
973                $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), utf8_decode(html_entity_decode(_T('asso:entete_montant')))) .$champFin.$lignesSeparateur;
974            } else {
975                $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), utf8_decode(html_entity_decode(_T('asso:entete_recette')))) .$champFin.$champsSeparateur;
976                $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), utf8_decode(html_entity_decode(_T('asso:entete_depense')))) .$champFin.$lignesSeparateur;
977            }
978        }
979        foreach ($this->classes as $laClasse=>$laDirection) {
980            $query = association_calcul_soldes_comptes_classe($laClasse, $this->periode, $this->destination, $multi?0:$laDirection);
981            $chapitre = '';
982            $i = 0;
983            while ($data = sql_fetch($query)) {
984                $new_chapitre = substr($data['code'], 0, 2);
985                if ($chapitre!=$new_chapitre) {
986                    $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), $new_chapitre) .$champFin.$champsSeparateur;
987                    $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), comptabilite_reference_intitule($new_chapitre) ) .$champFin.$champsSeparateur;
988                    $this->out .= $champsSeparateur.' '.$champsSeparateur;
989                    $this->out .= $lignesSeparateur;
990                    $chapitre = $new_chapitre;
991                }
992                $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), $data['code']) .$champFin.$champsSeparateur;
993                $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), $data['intitule']) .$champFin.$champsSeparateur;
994                if (!$multi) {
995                    $this->out .= $champDebut. ($laDirection?$data['valeurs']:$data['recettes']-$data['depenses']) .$champFin.$lignesSeparateur;
996                } else {
997                    $this->out .= $champDebut.$data['recettes'].$champFin.$champsSeparateur;
998                    $this->out .= $champDebut.$data['depenses'].$champFin.$lignesSeparateur;
999                }
1000            }
1001        }
1002    }
1003
1004    /**
1005     * Export texte de type s-expression / properties-list / balisage (conteneurs*conteneurs*donnees) simple : JSON, XML (utilisable avec ASN.1), YAML, etc.
1006     *
1007     * de par la simplicite recherchee il n'y a pas de types ou d'attributs : BSON, Bencode, JSON, pList, XML, etc.
1008     *
1009     * @param array $balises
1010     *   Tableau des balises d'ouverture (...1) et de fermeture (...0) a appliquer.
1011     *   Elles sont indexees par des cles (...N) convenues ainsi :
1012     * - titre : pour l'intitule de la synthese exportee
1013     * - nom : pour le nom de l'association
1014     * - exercice : pour l'intitule de l'exercice
1015     * - categorie : pour ?
1016     * - chapitre : pour ?
1017     * - libelle : pour ?
1018     * - code : pour la reference comptable d'un compte
1019     * - intitule : pour l'intitule renseigne pour un compte
1020     * - credit : pour la somme des recettes d'un compte
1021     * - debit : pour la somme des depenses d'un compte
1022     * - montant : pour le sode d'un compte
1023     * @param array $echappements
1024     *   Tableaux des remplacemens simples a effectuer : "des ceci"=>"par cela"
1025     *   Il faut, en effet, souvent proteger la presence de caracteres speciaux
1026     *   qui sont utilises comme parametres ici.
1027     * @param string $champDebut
1028     *   Caracter place au debut de chaque champ/colonne
1029     * @param string $champFin
1030     *   Caracter place a la fin de chaque champ/enregistrement
1031     * @param string $ident
1032     *   Caractere d'indentation des blocs
1033     * @param bool $entetePerso
1034     *   Indique si en plus des donnees il faut rajouter (vrai --par defaut) ou pas (faux) une ligne de titre au debut
1035     * @param bool $multi
1036     *   Indique si on recupere directement le solde (faux --par defaut) ou si on recupere separement les totaux des recettes et des depenses
1037     * @return string $this->out
1038     *   Contenu de l'export
1039     */
1040    function exportLignesMultiples($balises, $echappements=array(), $champDebut='', $champFin='', $indent="\t", $entetesPerso='', $multi=FALSE) {
1041        $this->out .= "$balises[compteresultat1]\n";
1042        if (!$entetesPerso) {
1043            $this->out .= "$indent$balises[entete1]\n";
1044            $this->out .= "$indent$indent$balises[titre1] $champDebut". utf8_decode(html_entity_decode(_T('asso:cpte_resultat_titre_general'))) ."$champFin $balises[titre0]\n";
1045            $this->out .= "$indent$indent$balises[nom1] $champDebut". $GLOBALS['association_metas']['nom'] ."$champFin $balises[nom0]\n";
1046            $this->out .= "$indent$indent$balises[exercice1] $champDebut". $this->titre ."$champFin $balises[exercice0]\n";
1047            $this->out .= "$indent$balises[entete0]\n";
1048        }
1049        foreach ($this->classes as $laClasse=>$laDirection) {
1050            $baliseClasse = $nomClasse.'1';
1051            $this->out .= "$indent$balises[$baliseClasse]\n";
1052            $query = association_calcul_soldes_comptes_classe($laClasse, $this->periode, $this->destination, $laDirection);
1053            $chapitre = '';
1054            $i = 0;
1055            while ($data = sql_fetch($query)) {
1056                if ( !$laDirection ) {
1057                    $valeurs = ($data['depenses']>0)?$data['depenses']:$data['recettes'];
1058                } else {
1059                    $valeurs = $data['valeurs'];
1060                }
1061                $new_chapitre = substr($data['code'], 0, 2);
1062                if ($chapitre!=$new_chapitre) {
1063                    if ($chapitre!='') {
1064                        $this->out .= "$indent$indent$balises[chapitre0]\n";
1065                    }
1066                    $this->out .= "$indent$indent$balises[chapitre1]\n";
1067                    $this->out .= "$indent$indent$indent$balises[code1] $champDebut". str_replace(array_keys($echappements), array_values($echappements), $new_chapitre) ."$champFin $balises[code0]\n";;
1068                    $this->out .= "$indent$indent$indent$balises[libelle1] $champDebut". str_replace(array_keys($echappements), array_values($echappements), comptabilite_reference_intitule($new_chapitre) ) ."$champFin $balises[libelle0]\n";
1069                    $chapitre = $new_chapitre;
1070                }
1071                $this->out .= "$indent$indent$indent$balises[categorie1]\n";
1072                $this->out .= "$indent$indent$indent$indent$balises[code1] $champDebut". str_replace(array_keys($echappements), array_values($echappements), $data['code']) ."$champFin $balises[code0]\n";
1073                $this->out .= "$indent$indent$indent$indent$balises[intitule1] $champDebut". str_replace(array_keys($echappements), array_values($echappements), $data['intitule']) ."$champFin $balises[intitule0]\n";
1074                if ( !$multi ) {
1075                    $this->out .= "$indent$indent$indent$indent$balises[montant1] $champDebut".$valeurs."$champFin $balises[montant0]\n";
1076                } else {
1077                    $this->out .= "$indent$indent$indent$indent$balises[credit1] $champDebut".$data['recettes']."$champFin $balises[credit0]\n";
1078                    $this->out .= "$indent$indent$indent$indent$balises[debit1] $champDebut".$data['depenses']."$champFin $balises[debit0]\n";
1079                }
1080                $this->out .= "$indent$indent$indent$balises[categorie0]\n";
1081            }
1082            if ($chapitre!='') {
1083                $this->out .= "$indent$indent$balises[chapitre0]\n";
1084            }
1085            $baliseClasse = $nomClasse.'0';
1086            $this->out .= "$indent$balises[$baliseClasse]\n";
1087        }
1088        $this->out .= "$balises[compteresultat0]\n";
1089    }
1090
1091    /**
1092     * Fichier texte final a afficher/telecharger
1093     *
1094     * @param string $ext
1095     *   Extension a donner au fichier
1096     * @param string $subtype
1097     *   Sous-type a inclure dans le nom du fichier
1098     *   Par defaut, c'est le type d'export (bilon ou resultat).
1099     * @return
1100     */
1101    function leFichier($ext, $subtype='') {
1102        $fichier = 'compte_'. ($subtype?$subtype:$this->type) .'_'.$this->periode.'_'.$this->destination.".$ext";
1103
1104        header('Content-type: application/'.$ext);
1105        header('Content-Disposition: attachment; filename="'.$fichier.'"');
1106        echo  $this->out;
1107    }
1108
1109}
1110
1111if (test_plugin_actif('FPDF')) {
1112
1113    define('FPDF_FONTPATH', 'font/');
1114    include_spip('fpdf');
1115    include_spip('inc/charsets');
1116
1117class ExportComptes_PDF extends FPDF {
1118
1119    // variables de parametres de mise en page
1120    var $icone_h = 20;
1121    var $icone_v = 20;
1122
1123    // variables de mise en page calculees
1124    var $largeur_utile = 190; // largeur sans les marges droites et gauches
1125    var $cell_padding = 2; // espacement entre les bords des cellules et leur contenu
1126
1127    // position du curseur
1128    var $xx = 0; // abscisse 1ere boite
1129    var $yy = 0; // ordonnee 1ere boite
1130
1131    // variables de fonctionnement passees en parametre
1132    var $periode; // id_exercice ou annee
1133    var $destination; // id_destination
1134    var $titre; // intitule de l'exercice
1135
1136    /**
1137     * Initialisations
1138     * @param array $ids
1139     *   Tableau des parametres (les cles sont : id_periode, id_destination, titre_periode, classes, titre)
1140     *   Quand il n'y a rien, on recupere les differents elements dans l'environnement
1141     * @return $this->
1142     *   Les proprietes de la classe sont initialisees
1143     */
1144    function init($ids='') {
1145        if ( !$ids ) // tableau de parametres non transmis
1146            $ids = association_passeparam_compta(); // recuperer dans l'environnemet (parametres d'URL)
1147        // passer les parametres transmis aux variables de la classe
1148        $this->periode = $ids['id_periode'];
1149        $this->destination = $ids['destination'];
1150        $this->titre = $ids['titre_periode'];
1151        // calculer les dimensions de mise en page
1152        $this->largeur_utile = ($GLOBALS['association_metas']['fpdf_widht']?$GLOBALS['association_metas']['fpdf_widht']:210)-2*($GLOBALS['association_metas']['fpdf_marginl']?$GLOBALS['association_metas']['fpdf_marginl']:10);
1153        $this->cell_padding = ($GLOBALS['association_metas']['fpdf_marginc']?$GLOBALS['association_metas']['fpdf_marginc']:2);
1154        // initialiser les variables de mise en page
1155        $this->xx = ($GLOBALS['association_metas']['fpdf_marginl']?$GLOBALS['association_metas']['fpdf_marginl']:10); // marge gauche
1156        $this->yy = ($GLOBALS['association_metas']['fpdf_margint']?$GLOBALS['association_metas']['fpdf_margint']:10); // marge haute
1157        // meta pour le fichier PDF
1158        $this->SetAuthor('Marcel BOLLA');
1159        $this->SetCreator('Associaspip & Fpdf');
1160        $this->SetTitle('Module Comptabilite');
1161        $this->SetSubject('Etats comptables');
1162        // typo par defaut
1163        $this->underline = '';
1164        $this->FontStyle = '';
1165        $this->FontSizePy = 12;
1166        $this->FontFamily = ($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial');
1167        // engager la page
1168        // http://fpdf.org/en/doc/addpage.htm
1169        $this->AddPage($GLOBALS['association_metas']['fpdf_orientation'],
1170                       $GLOBALS['association_metas']['fpdf_format']
1171                       ? $GLOBALS['association_metas']['fpdf_format']
1172                       : array(($GLOBALS['association_metas']['fpdf_widht']
1173                                ? $GLOBALS['association_metas']['fpdf_widht']
1174                                : 210),
1175                               ($GLOBALS['association_metas']['fpdf_height']
1176                                ? $GLOBALS['association_metas']['fpdf_height']
1177                                :297) ) );
1178    }
1179
1180    /**
1181     * Pied de pages :
1182     * redefinition de FPDF::Footer() qui est automatiquement appele par FPDF::AddPage() et FPDF::Close() !
1183     *
1184     * @note
1185     *   http://www.id.uzh.ch/cl/zinfo/fpdf/doc/footer.htm
1186     *   Adapter la marge basse (et la hauteur utile) des pages en consequence
1187     */
1188    function Footer() {
1189        // Positionnement a 2 fois la marge du bas
1190        $this->SetY(-2*($GLOBALS['association_metas']['fpdf_margint']?$GLOBALS['association_metas']['fpdf_margint']:10));
1191        // typo
1192        $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), 'I', 8); // police: italique 8px
1193        $this->SetTextColor(128); // Couleur du texte : gris-50.2% (fond blanc)
1194        // Date et Numéro de page
1195        include_spip('inc/filtres');
1196        $this->Cell(0, 10, html_entity_decode(_T('asso:cpte_export_pied_notice') .' -- '. affdate(date('Y-m-d')) .' -- '. _T('asso:cpte_export_page', array('numero'=>$this->PageNo()) )), 0, 0, 'C');
1197    }
1198
1199    /**
1200     * Haut de pages :
1201     * redefinition de FPDF qui est directement appele par FPDF::AddPage()
1202     * @note
1203     *   http://www.id.uzh.ch/cl/zinfo/fpdf/doc/header.htm
1204     *   Adapter la marge haute (et la hauteur utile) des pages en consequence
1205    */
1206    function Header() {
1207        // nop
1208    }
1209
1210    /**
1211     * Cartouche au debut de la 1ere page
1212     *
1213     * @param string $titre
1214     *   Nom de l'export : place au dessous le nom de l'association et au dessus de l'intitule de l'exercice
1215     * @return void
1216     *   Le contenu du PDF
1217     * @note
1218     *   Contrairement au Header ceci fait partir du contenu/flux et n'est pas repete sur toutes les pages, et peut accepter des parametres
1219     */
1220    function association_cartouche_pdf($titre='') {
1221        // Les coordonnees courantes
1222        $xc = $this->xx+$this->cell_padding;
1223        $yc = $this->yy+$this->cell_padding;
1224        $this->SetDrawColor(128); // La couleur du trace : gris 50.2% (sur fond blanc)
1225        // Le logo du site
1226#       $chercher_logo = charger_fonction('chercher_logo', 'inc');
1227#       $logo = $chercher_logo(0, 'id_site');
1228        $logo = find_in_path('IMG/siteon0.jpg'); // Probleme FPDF et images non JPEG :-/ http://forum.virtuemart.net/index.php?topic=75616.0
1229        if ($logo) {
1230            include_spip('/inc/filtres_images_mini');
1231            $this->Image(extraire_attribut(image_reduire($logo, $this->icone_h, $this->icone_v), 'src'), $xc, $yc, $this->icone_h);
1232        }
1233        // typo
1234        $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), 'B', 22); // police : gras 22px
1235        $this->SetFillColor(235); // Couleur du cadre, du fond du cadre : gris-92,2%
1236        $this->SetTextColor(0); // Couleur du texte : noir
1237        $largeur_pour_titre = $this->largeur_utile-$this->icone_h-3*$this->cell_padding;
1238        // Titre centre
1239        $xc += $this->cell_padding+($logo?$this->icone_h:0);
1240        $this->SetXY($xc, $yc);
1241        $this->Cell($logo?($largeur_pour_titre):($largeur_pour_titre+$this->icone_h-$this->cell_padding), 12, html_entity_decode(_T("asso:$titre")), 0, 0, 'C', TRUE);
1242        $yc += 12;
1243        $this->Ln($this->cell_padding); // Saut de ligne
1244        $yc += $this->cell_padding;
1245        // typo
1246        $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), '', 12); // police : normal 12px
1247        $this->SetFillColor(235); // Couleur de remplissage : gris-92.2%
1248        // Sous titre Nom de l'association
1249        $this->SetXY($xc, $yc);
1250        $this->Cell($logo?$largeur_pour_titre:$largeur_pour_titre+$this->icone_h-$this->cell_padding, 6, utf8_decode(_T('asso:cpte_export_association', array('nom'=>$GLOBALS['association_metas']['nom']) )), 0, 0, 'C', TRUE);
1251        $yc += 6;
1252        $this->Ln($this->cell_padding/2); // Demi saut de ligne
1253        $yc += $this->cell_padding/2;
1254        // typo
1255        $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), '', 12); // police : normal 12px
1256        $this->SetFillColor(235); // Couleur de fond : gris-92.2%
1257        //Sous titre Intitule de l'exercice
1258        $this->SetXY($xc, $yc);
1259        $this->Cell($logo?$largeur_pour_titre:$largeur_pour_titre+$this->icone_h-$this->cell_padding, 6, utf8_decode(_T('asso:cpte_export_exercice', array('titre'=>$this->titre) )), 0, 0, 'C', TRUE);
1260        $yc += 6;
1261        $this->Ln($this->cell_padding); // Saut de ligne
1262        $yc += $this->cell_padding;
1263        $this->Rect($this->xx, $this->yy, $this->largeur_utile, $yc-($GLOBALS['association_metas']['fpdf_margint']?$GLOBALS['association_metas']['fpdf_margint']:10)); // Rectangle tout autour de l'entete
1264        $this->yy = $yc; // on sauve la position du curseur dans la page
1265    }
1266
1267    // Fichier final envoye
1268    function File($titre='etat_comptes') {
1269        $this->Output($titre.'_'.$this->periode.'_'.$this->destination.'.pdf', 'I');
1270    }
1271
1272    // on affiche les totaux (recettes et depenses) d'un exercice des differents comptes de la classe specifiee
1273    function association_liste_totaux_comptes_classes($classes, $prefixe='', $direction='-1', $periode=0, $destination=0) {
1274        if( !is_array($classes) ) { // a priori une chaine ou un entier d'une unique classe
1275            $liste_classes = array( $classes ) ; // transformer en tableau (puisqu'on va operer sur des tableaux);
1276        } else { // c'est un tableau de plusieurs classes
1277            $liste_classes = $classes;
1278        }
1279        // Les coordonnees courantes
1280        $xc = $this->xx+$this->cell_padding;
1281        $y_orig = $this->yy+$this->cell_padding;
1282        $yc = $y_orig+$this->cell_padding;
1283        // typo
1284        $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), 'B', 14); // police: gras 14px
1285        $this->SetFillColor(235); // Couleursdu fond du cadre de titre : gris-92.2%
1286        $this->SetTextColor(0); // Couleurs du texte du cadre de titre
1287        // Titre centre
1288        $titre = $prefixe.'_'. ( ($direction) ? (($direction<0)?'depenses':'recettes') : 'soldes' );
1289        $this->SetXY($xc, $yc);
1290        $this->Cell($this->largeur_utile, 10, html_entity_decode(_T("asso:$titre")), 0, 0, 'C');
1291        $yc += 10;
1292        $this->Ln($this->cell_padding); // Saut de ligne
1293        $yc += $this->cell_padding;
1294        // initialisation du calcul+affichage des comptes
1295        $total_valeurs = $total_recettes = $total_depenses = 0;
1296        $chapitre = '';
1297        $i = 0;
1298        foreach ( $liste_classes as $rang => $classe ) { // calcul+affichage par classe
1299            $query = association_calcul_soldes_comptes_classe($classe, $this->periode, $this->destination, $direction );
1300            $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), '', 12); // police : normal 12px
1301            while ($data = sql_fetch($query)) {
1302                $this->SetXY($xc, $yc); // positionne le curseur
1303                $new_chapitre = substr($data['code'], 0, 2);
1304                if ($chapitre!=$new_chapitre) { // debut de categorie
1305                    $this->SetFillColor(225); // Couleur de fond de la ligne : gris-92.2%
1306                    $this->Cell(20, 6, utf8_decode($new_chapitre), 0, 0, 'L', TRUE);
1307                    $this->Cell(($this->largeur_utile)-(2*$this->cell_padding+20), 6, utf8_decode(comptabilite_reference_intitule($new_chapitre)), 0, 0, 'L', TRUE);
1308                    $chapitre = $new_chapitre;
1309                    $this->Ln(); // Saut de ligne
1310                    $yc += 6;
1311                }
1312                $this->SetFillColor(245); // Couleur de fond du total : gris-96.1%
1313                $this->SetXY($xc, $yc); // positionne le curseur
1314#               if ( floatval($data['valeurs']) || floatval($data['recettes']) || floatval($data['depenses']) ) { // non-zero...
1315                    $this->Cell(20, 6, utf8_decode($data['code']), 0, 0, 'R', TRUE);
1316                    $this->Cell(($this->largeur_utile)-(2*$this->cell_padding+50), 6, utf8_decode($data['intitule']), 0, 0, 'L', TRUE);
1317                    $this->Cell(30, 6, association_formater_nombre($data['valeurs']), 0, 0, 'R', TRUE);
1318                    if ($direction) { // mode liste comptable
1319                        $this->Cell(30, 6, association_formater_nombre($data['valeurs']), 0, 0, 'R', TRUE);
1320                        $total_valeurs += $data['valeurs'];
1321                    } else { // mode liste standard
1322                        $this->Cell(30, 6, association_formater_nombre($data['depenses']>0?$data['depenses']:$data['recettes']), 0, 0, 'R', TRUE);
1323                        $total_recettes += $data['recettes'];
1324                        $total_depenses += $data['depenses'];
1325                        $total_valeurs += $data['soldes'];
1326                    }
1327                    $this->Ln(); // Saut de ligne
1328                    $yc += 6;
1329#               }
1330            }
1331        }
1332        $this->SetXY($xc, $yc); // positionne le curseur
1333        $this->SetFillColor(215); // Couleur de fond : 84.3%
1334        if ($direction) { // mode liste comptable : charge, produit, actifs, passifs
1335            $this->Cell(($this->largeur_utile)-(2*$this->cell_padding+30), 6, html_entity_decode(_T("asso:$prefixe".'_total')), 1, 0, 'R', TRUE);
1336            $this->Cell(30, 6, association_formater_nombre($total_valeurs), 1, 0, 'R', TRUE);
1337        } else { // mode liste standard : contributions volontaires et autres
1338            $this->Cell(($this->largeur_utile)/2-(2*$this->cell_padding+30), 6, html_entity_decode(_T("asso:$prefixe".'_total_depenses')), 1, 0, 'R', TRUE);
1339            $this->Cell(30, 6, association_formater_nombre($total_depenses), 1, 0, 'R', TRUE);
1340            $xc += ( $this->largeur_utile)/2;
1341            $this->SetXY($xc, $yc); // positionne le curseur sur l'autre demi page
1342            $this->Cell(($this->largeur_utile)/2-(2*$this->cell_padding+30), 6, html_entity_decode(_T("asso:$prefixe".'_total_recettes')), 1, 0, 'R', TRUE);
1343            $this->Cell(30, 6, association_formater_nombre($total_recettes), 1, 0, 'R', TRUE);
1344        }
1345        $yc += 6;
1346        $this->Ln($this->cell_padding); // Saut de ligne
1347        $yc += $this->cell_padding;
1348        $this->Rect($this->xx, $y_orig, $this->largeur_utile, $yc-$y_orig); // Rectangle tout autour
1349        $this->yy = $yc; // on sauve la position du curseur dans la page
1350        return $total_valeurs;
1351    }
1352
1353    // on affiche le resultat comptable net : benefice ou deficit
1354    function association_liste_resultat_net($lesRecettes, $lesDepenses) {
1355        // Les coordonnees courantes
1356        $xc = $this->xx+$this->cell_padding;
1357        $y_orig = $this->yy+$this->cell_padding;
1358        $yc = $y_orig+$this->cell_padding;
1359        // typo
1360        $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), 'B', 14); // police : gras 14px
1361        $this->SetFillColor(235); // Couleur du fond : gris-92.2%
1362        $this->SetTextColor(0); // Couleur du texte : noir
1363        // Titre centre
1364        $this->SetXY($xc, $yc);
1365        $this->Cell($this->largeur_utile, 10, html_entity_decode(_T('asso:cpte_resultat_titre_resultat')), 0, 0, 'C');
1366        $yc += 10;
1367        $this->Ln($this->cell_padding); // Saut de ligne
1368        $yc += $this->cell_padding;
1369        $this->SetFillColor(215); // Couleur de fond : gris-84.3%
1370        $leSolde = $lesRecettes-$lesDepenses;
1371        $this->SetXY($xc, $yc);
1372        $this->Cell(($this->largeur_utile)-(2*$this->cell_padding+30), 6, html_entity_decode(_T('asso:cpte_resultat_'.($leSolde<0?'perte':'benefice'))), 1, 0, 'R', TRUE);
1373        $this->Cell(30, 6, association_formater_nombre($leSolde), 1, 0, 'R', TRUE);
1374        $yc += 6;
1375        $this->Ln($this->cell_padding); // Saut de ligne
1376        $yc += $this->cell_padding;
1377        $this->Rect($this->xx, $y_orig, $this->largeur_utile, $yc-$y_orig); // Rectangle tout autour
1378        $this->yy = $yc; // on sauve la position du curseur dans la page
1379    }
1380
1381} // fin classe
1382
1383} // fin if
1384
1385?>
Note: See TracBrowser for help on using the repository browser.