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

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

revue de la compta : introduction du contrôle de la direction des comptes

File size: 65.8 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]', 'C'=>$GLOBALS['association_metas']['classe_produits'], 'D'=>$GLOBALS['association_metas']['classe_charges']);
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 que la direction d'operation est bonnne
427 * Cela est precise par les regles C (pour credit) et D (pour debit) du plan comptable
428 * et dans tous les cas multi passe toujours...
429 */
430function comptabilite_verifier_code($code, $sens='multi', $classe='', $plan='') {
431    $regles = comptabilite_liste_planregles($plan);
432    $ruleC = $regles['C']; unset($regles['C']);
433    $ruleD = $regles['D']; unset($regles['D']);
434    if ( !preg_match('/^'. implode('', $regles) .'\w*$/', $code) ) // champ de longueur insuffisante ou ne commencant pas de facon adequate
435        return _T('compta:erreur_plan_code_format', array('nombre'=>count($regles),) );
436    if (sql_countsel('spip_asso_plan', "code='$code'")>1) // occurences multiples d'une meme reference
437        return _T('compta:erreur_plan_code_doublon');
438    if ( $classe!==FALSE AND $classe!=='' AND $code[0]!=$classe ) // discordance avec la classe
439        return _T('compta:erreur_code_classe', array('nombre'=>$classe,) );
440    if ( $ruleC AND preg_match('/^('.$ruleC.')\w*$/', $code) AND $sens=='debit' ) // comptes uniquement créditeurs...
441        return _T('compta:erreur_code_type', array('code'=>$code, 'interdit'=>_T('compta:item_direction_debit'),) );
442    if ( $ruleD AND preg_match('/^('.$ruleD.')\w*$/', $code) AND $sens=='credit' ) // comptes uniquement débiteurs...
443        return _T('compta:erreur_code_type', array('code'=>$code, 'interdit'=>_T('compta:item_direction_credit'),) );
444     return '';
445}
446
447/**
448 * Validation de la table du plan comptable utilise
449 * - on doit avoir un certain nombre de classes differentes
450 * - chaque reference doit etre unique
451 * - les references et classes doivent respecter le referentiel choisi
452 *
453 * @param int $nbr
454 *   Nonbre de classes requises par le plugin
455 * @note:ex
456 *   association_valider_plan_comptable()
457 */
458function comptabilite_verifier_plan($nbr=2, $plan='', $lang='') {
459    $classes = array(); // initialiser la liste des classes
460    $codes = array(); // initialiser la liste des references
461    $sql = sql_select('code, classe', 'spip_asso_plan'); // recupere la reference et la classe de tous les comptes du plan comptable
462    while ($r = sql_fetch($sql)) { // verification de chaque reference
463        $classes[$r['classe']] = $r['classe']; // on comptes les classes differentes
464        if( array_key_exists($r['code'], $codes) ) // on a deux fois le meme code...
465            return _T('compta:erreur_plan_code_doublon', array('code'=>$r['code'],) ); // ...on arrete sur cette erreur...
466        else { // c'est la 1ere occurence
467            $codes[$r['code']] = comptabilite_verifier_code($r['code'], $r['type_op'], $r['classe'], $lang); // verifier qu'il est bien forme
468            if ($codes[$r['code']]) // mauvais format...
469                return $codes[$r['code']]; // ...on arrete sur cette erreur...
470        }
471    }
472    if ( count($classes)<$nbr ) // on doit avoir au moins $nbr classes differentes
473        return _T('compta:erreur_plan_nombre_classes', array('nombre'=>$nbr,) ); // ...on arrete sur cette erreur...
474    foreach($classes as $classe) // verifier chaque classe
475        if ( $erreur = comptabilite_verifier_classe($classe, $plan, $lang) )
476            return $erreur; // ...renvoyer la premiere erreur rencontree...
477    return '';
478}
479
480/** @} */
481
482
483/*****************************************
484 * @defgroup filtre_selecteur_compta_
485 * Selecteurs dHTML propres a la compta
486 *
487** @{ */
488
489/**
490 * Selecteur de classe du plan comptable
491 *
492 * @param string $classe
493 *   Classe comptable selectionnee
494 * @param string $name
495 *   Nom du selecteur de classe dans le formulaire
496 * @param string $ref
497 *   Nom du selecteur de code dans le formulaire
498 * @return string $res
499 *   Liste deroulante des classes comptables disponibles
500 * @note:ex
501 * balise_SELECTEUR_CLASSE_COMPTABLE_dyn($classe)
502 */
503function filtre_selecteur_compta_classe($classe, $name='classe', $ref='code') {
504    if (@!$GLOBALS['meta']['html5'] AND $ref) { // JavaScript sur le onChange
505        $js = ' var OptCurrVal = document.getElementById(\'selecteur_'.$name.'\').value; ';
506        $js .= ' var OptGroupElt = document.getElementById(\'PlanComptableClasse\'+OptCurrVal); '; // mettre le selecteur de code directement au debut de la classe selectionnée
507        $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)
508    }
509    $res = "<select name='$name' id='selecteur_$name'". ($js?" onclick='$js'":'') .">\n";
510    $res .= '<option value="">'. _T('compta:item_no_classe') ."</option>\n";
511    $lc = ($GLOBALS['association_metas']['plan_comptable']?comptabilite_liste_plancodes():array(1,2,3,4,5,6,7,8,9,0));
512    foreach ($lc as $code) {
513        if (strlen($code)==1) { // il s'agit d'une classe
514            $res .= '<option value="'.$code.'"';
515            $res .= ($code==$classe) ? ' selected="selected"' : '';
516            $res .= '>'.$code.' - '. comptabilite_reference_intitule($code) ;
517            $res .= "</option>\n";
518        }
519    }
520    return "$res</select>\n";
521}
522
523/**
524 * Selecteur de reference du plan comptable
525 *
526 * @param string $code
527 *   Reference comptable selectionnee
528 * @param string $name
529 *   Nom du champ de reference comptable dans le formulaire
530 * @param string $ref
531 *   Nom du champ d'intitule comptable dans le formulaire
532 * @return string $res
533 *   Liste deroulante des references comptables disponibles
534 * @note:ex
535 * balise_SELECTEUR_CODE_COMPTABLE_dyn($code)
536 */
537function filtre_selecteur_compta_code($code, $name='code', $ref='intitule') {
538    if (@$GLOBALS['meta']['html5']) { // the sexy way
539        $res = "<datalist id='selecteur_$name'>\n"; // ce sera une liste d'auto-completion
540        $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
541        $res .= "<select name='$name'>\n"; // fall-back
542        $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
543    } else { // the causual way
544#       $js = 'var CurrentOption=document.getElementById(\'selecteur_'.$name.'\'); document.getElementById(\''.$name.'\').value=CurrentOption.value; '; // recopier le code dans le champ prevu
545        $js = 'var currentVal=String(this.options[this.selectedIndex].text).split(\'-\'); document.getElementById(\''.$name.'\').value=currentVal[0]; '; // recopier le code dans le champ prevu
546        if ($ref)
547#           $js .= ' document.getElementById(\''.$ref.'\').value=CurrentOption.options[CurrentOption.selectedIndex].text; '; // recopier l'intitule dans le champ prevu
548            $js .= ' document.getElementById(\''.$ref.'\').value=currentVal[1]; '; // recopier l'intitule dans le champ prevu
549        $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)
550        $res .= '<option value="">'. _T('compta:item_no_code') ."</option>\n"; // pas de valeur : pour ne rien choisir.
551    }
552    $optgroup = FALSE;
553    foreach (comptabilite_liste_plancodes() as $rc) {
554        if (@!$GLOBALS['meta']['html5'] AND strlen($rc)==1) { // il s'agit d'une classe
555            if ($optgroup) //
556                $lst .= "</optgroup>\n";
557            $res .= '<optgroup id="PlanComptableClasse'.$rc.'" label="'.$rc.' - '.  comptabilite_reference_intitule($rc) ."\">\n";
558            $optgroup = TRUE;
559        } else { // il s'agit d'une reference
560            $res .= '<option value="'.$rc.'"';
561            $res .= ($code==$rc) ? ' selected="selected"' : '';
562            $res .= '>'.$rc.' - '. comptabilite_reference_intitule($rc) ;
563            $res .= "</option>\n";
564        }
565    }
566    if ($optgroup)
567        $res .= "</optgroup>\n";
568    return "$res</select>\n". (@$GLOBALS['meta']['html5']?'</datalist>\n':'');
569}
570
571/**
572 * Selecteur de destinations
573 *
574 * @param array $destinations
575 *   Tableau de id_destination=>montant deja selectionnees (vide pour un ajout)
576 * @param int $defaut
577 *   Permet de selectionner une destination par defaut (par id_destination)
578 *   quand $destinations est vide
579 * @return string $res
580 *   Code HTML+JS correspondant au selecteur de destinations
581 * @note
582 *   Associaspip : selon la configuration, on ne peut associer qu'une destination unique ou ventiler sur plusieurs destinations
583 * @note:ex
584 *   association_editeur_destinations($destinations, $defaut)
585 */
586function filtre_selecteur_compta_destinations($destinations=array(), $defaut='') {
587        $options = array();
588        $sql = sql_select('id_destination, intitule', 'spip_asso_destination', '', '', 'intitule');
589        while ( $r = sql_fetch($sql) ) // Constuire les balises OPTIONs d'un SELECT ; mais il faudrait arranger ca si une seule
590                $options[$r['id_destination']] = '<option value="'.$r['id_destination'].'">'.$r['intitule'].'</option>';
591        if ( !count($options) OR !$GLOBALS['association_metas']['destinations'] ) // aucune destination definie ! ou usage desactive !
592            return '';
593        $idIndex = 1;
594        if (intval($GLOBALS['association_metas']['destinations'])>1) { // destinations multiples : on insere ...
595            $script = '<script type="text/javascript" src="'
596            . find_in_path('javascript/jquery.destinations_form.js')
597                . '"></script>'; // ...le JS qui permet de les gere
598            $addDestinationButton = "\n<button class='destButton' type='button' onclick='addFormField(); return FALSE;'>+</button>"; // ...le bouton pour ajouter une destination
599        } else // destination unique
600            $script = $addDestinationButton = '';
601        if ( count($destinations) ) { // si on a une liste de destinations (on edite une operation)
602          $options = join("\n", $options) ;
603          $res = '';
604          foreach ($destinations as $destId => $destMontant) { // restitution des listes de selection HTML
605                $res .= '<div id="row'.$idIndex.'" class="choix"><ul>'
606                . '<li>'
607                . '<select name="id_dest['.$idIndex.']" id="id_dest_'.$idIndex.'" >'
608                . preg_replace("/(value='".$destId."')/", '$1 selected="selected"', $options)
609                . '</select></li>';
610                if (($GLOBALS['association_metas']['destinations'])>1) { // destinations multiples
611                    $res .= '<li><input name="montant_dest['.$idIndex.']" value="'
612                        . association_formater_nombre($destMontant)
613                        . '" type="text" id="montant_dest_'.$idIndex.'" class="number decimal price" />'
614                        . '<button class="destButton" type="button" onclick="addFormField(); return false;">+</button>';
615                    if ($idIndex>1) // bouton de suppression de l'affectation courante
616                        $res .= '<button class="destButton" type="button" onclick="removeFormField(\'#row'.$idIndex.'\'); return false;">-</button>';
617                }
618                $res .= '</li></ul></div>';
619                $idIndex++;
620          }
621        } else { // pas de destination deja definies pour cette operation
622            if ( $defaut ) // un choix par defaut
623              $options[$defaut] = str_replace('<option ', '<option selected="selected" ', $options[$defaut]);
624            $n = " name='id_dest[1]' id='id_dest_1'";
625            if ( count($options)==1 ) // on a une seule destination possible, pas de selecteur
626              $res = "<input$n readonly='readonly' value='$id' /> $texte";
627            else // plusieurs destinations possibles
628              $res = "<ul>\n<li>"
629                . "<select$n>" . join("\n", $options) . '</select>'
630                . "\n</li><li><input name='montant_dest[1]' id='montant_dest_1'/>"
631                . $addDestinationButton.'</li></ul>';
632        }
633    return $script
634      . '<div id="divTxtDestination" class="formulaire_edition_destinations">'
635      . '<label>'
636      . _T('asso:destination')
637      . '</label>'
638      . $res
639      . ((intval($GLOBALS['association_metas']['destinations'])>1)? '' :
640        ('<input type="hidden" id="idNextDestination" value="'.($idIndex+1).'" />'))
641      . '</div>';
642}
643
644/**
645 * Selecteur de plan comptable
646 *
647 * @param string $pcg
648 *   ID du plan comptable selectionne
649 * @param string $nom
650 *   Nom du selecteur dans le formulaire
651 * @return string $res
652 *   Liste deroulante des plans comptables disponibles :
653 * ce sont de fichiers de langue "lang/pcg2*.php"
654 */
655function filtre_selecteur_compta_plan($pcg, $nom='plan_comptable') {
656    $liste_plans = array_keys(find_all_in_path('lang/', 'pcg2', FALSE) ); // '\\bpcg2.*\\b'
657    foreach ($liste_plans as $pos=>$plan) {
658        $lang = strpos($plan, '_', 3); // l'indicateur de langue commence au premier underscore
659        $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"
660    }
661    $desc_table = charger_fonction('trouver_table', 'base');
662    $res = "<select name='$nom' id='selecteur_$nom'>\n";
663    $res .= '<option value="">'. _T('ecrire:item_non') ."</option>\n";
664    foreach (array_unique($liste_plans) as $id) {
665        $res .= '<option value="'.$id.'"'.
666        ($id==$pcg?' selected="selected"':'') .'>';
667        $pays = $desc_table('pays') ? sql_getfetsel('nom', 'spip_pays', 'code='.sql_quote($id) ) : '';
668        $res .= $pays ? extraire_multi($pays, $GLOBALS['spip_lang']) : strtoupper($id) ;
669        $res .= "</option>\n";
670    }
671    return "$res</select>\n";
672}
673
674/** @} */
675
676
677/*****************************************
678 * @defgroup comptabilite_ _
679 * Divers
680 *
681** @{ */
682
683
684/**
685 * On recupere les soldes des differents comptes de la classe specifiee pour la periode specifiee
686 * Ceci permet d'etablir la balance des comptes de la classe :
687 * http://fr.wikipedia.org/wiki/Balance_comptable
688 *
689 * @param int $classe
690 *   Classe dont on veut recuperer les soldes des differents comptes
691 * @param int $periode
692 *   ID exercice ou annee (selon configuration)
693 * @param int $destination
694 *   ID destination
695 * @param float $direction
696 *   Le signe de ce parametre indique le type de compte (et donc le sens de calcul du solde)
697 *   positif : comptes de credit (solde=recettes-depenses)
698 *   negatif : comptes de debit (solde=depenses-recettes)
699 * @return ressource $query
700 *   Resultat de la requete donnant les soldes de chaque compte de la classe indiquee
701 * @note
702 *   d'apres http://www.lacompta.ch/MITIC/theorie.php?ID=26 c'est le solde qui est recherche, et il corresponde bien a :
703 *  recettes-depenses=recettes pour les classes 6
704 *  depenses-recettes=depenses pour les classes 7
705 */
706function association_calcul_soldes_comptes_classe($classe, $periode=0, $destination=0, $direction='-1') {
707    $c_group = (($classe==$GLOBALS['association_metas']['classe_banques'])?'journal':'imputation');
708    $valeurs = (($direction)
709        ?
710        ( ($direction<0)
711            ?'SUM('.(($destination)?'a_d':'a_c').'.depense-'.(($destination)?'a_d':'a_c').'.recette) AS valeurs'
712            : 'SUM('.(($destination)?'a_d':'a_c').'.recette-'.(($destination)?'a_d':'a_c').'.depense) AS valeurs'
713        )
714        :
715        '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' );
716    $c_having = ($direction) ? 'valeurs>0' : ''; // on ne retiendra que les totaux non nuls...
717    if ( sql_countsel('spip_asso_plan','active=1') ) { // existence de comptes actifs
718        $p_join = " RIGHT JOIN spip_asso_plan AS a_p ON a_c.$c_group=a_p.code";
719        $p_select = ', a_p.code, a_p.intitule, a_p.classe';
720        $p_order = 'a_p.code'; // imputation ou journal
721#       $p_where = 'a_p.classe='.sql_quote($classe);
722        $p_having = 'a_p.classe='.sql_quote($classe); // ok : on agrege par code (indirectement) associe a une classe unique selectionnee ...
723    } else { // pas de comptes actifs ?!?
724        $p_join = $p_select = $p_where = $p_having = '';
725        $p_order = $c_group; // imputation ou journal
726    }
727    if ( $periode ) { // restriction sur une periode donnee
728        if ($GLOBALS['association_metas']['exercices']) { // exercice budgetaire personnalise
729            $exercice = sql_fetsel('date_debut, date_fin', 'spip_asso_exercices', "id_exercice=".intval($periode));
730            $c_where = "a_c.date_operation>='$exercice[date_debut]' AND a_c.date_operation<='$exercice[date_fin]' ";
731        } else { // exercice budgetaire par annee civile
732            $c_where = "DATE_FORMAT(a_c.date_operation, '%Y')=".intval($periode);
733        }
734#    } elseif ( $classe==$GLOBALS['association_metas']['classe_banques'] ) { // encaisse
735#       $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() ';
736    } else { // tout depuis le debut ?!?
737        $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 ?
738    }
739    $query = sql_select(
740        "$c_group, $valeurs ". ($destination ? ', a_d.id_destination' : '') .$p_select, // select
741        '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
742        ($destination ? "a_d.id_destination=$destination AND " : '') . ($p_where?"$p_where AND ":'')  .$c_where, // where
743        $c_group, // group by
744        $p_order, // order by
745        '', // limit
746        $c_having. (($c_having && $p_having)?' AND ':'') .$p_having // having
747    );
748    return $query;
749}
750
751/**
752 * On affiche les totaux (recettes et depenses) des differents comptes de la classe specifiee pour une periode donnee
753 *
754 * @param array $classes
755 *   Liste des classes dont on veut afficher les soldes des differents comptes
756 * @param string $prefixe
757 *   Prefixe a applique aux termes qualifiant la direction pour former le titre du tableau
758 * @param float $direction
759 *   Le signe de ce parametre indique le type de compte (et donc le sens de calcul du solde)
760 *   positif : comptes de credit (solde=recettes-depenses)
761 *   negatif : comptes de debit (solde=depenses-recettes)
762 * @param int $periode
763 *   ID exercice ou annee (selon configuration)
764 * @param int $destination
765 *   ID destination
766 * @return void
767 *   HTML-Table listant les differents soldes ordonnes par classes puis par numeros de compte
768 */
769function association_liste_totaux_comptes_classes($classes, $prefixe='', $direction='-1', $periode=0, $destination=0) {
770    if( !is_array($classes) ) { // a priori une chaine ou un entier d'une unique classe
771        $liste_classes = array( $classes ) ; // transformer en tableau (puisqu'on va operer sur des tableaux);
772    } else { // c'est un tableau de plusieurs classes
773        $liste_classes = $classes;
774    }
775    $titre = $prefixe.'_'. ( ($direction) ? (($direction<0)?'depenses':'recettes') : 'soldes' );
776    echo "<table width='100%' class='asso_tablo' id='asso_tablo_$titre'>\n";
777    echo "\n<tr>";
778    echo '<th scope="col" style="width:10px">&nbsp;</th>';
779    echo '<th scope="col" style="width:30px">&nbsp;</th>';
780    echo '<th scope="col">'. _T("asso:$titre") .'</th>';
781    if ($direction) { // mode liste comptable : charge, produit, actifs, passifs
782        echo '<th scope="col" style="width:80px">&nbsp;</th>';
783    } else { // mode liste standard : contributions volontaires et autres
784        echo '<th scope="col" style="width:80px">'. _T("asso:$prefixe".'_recettes') .'</th>';
785        echo '<th scope="col" style="width:80px">'. _T("asso:$prefixe".'_depenses') .'</th>';
786        // echo '<th scope="col" width="80">'. _T("asso:$prefixe".'_solde') .'</th>';
787    }
788    echo "</tr>\n";
789    $total_valeurs = $total_recettes = $total_depenses = 0;
790    $chapitre = '';
791    $i = 0;
792    foreach ( $liste_classes as $rang => $classe ) {
793        $query = association_calcul_soldes_comptes_classe($classe, $periode, $destination, $direction );
794        while ($data = sql_fetch($query)) {
795            echo '<tr>';
796            $new_chapitre = substr($data['code'], 0, 2);
797            if ($chapitre!=$new_chapitre) {
798                echo '<td class="text">'. $new_chapitre . '</td>';
799                echo '<td colspan="3" class="text">'. comptabilite_reference_intitule($new_chapitre) .'</td>';
800                $chapitre = $new_chapitre;
801                echo "</tr>\n<tr>";
802            }
803#           if ( floatval($data['valeurs']) || floatval($data['recettes']) || floatval($data['depenses']) ) { // non-zero...
804                echo "<td>&nbsp;</td>";
805                echo '<td class="text">'. $data['code'] .'</td>';
806                echo '<td class="text">'. $data['intitule'] .'</td>';
807                if ($direction) { // mode liste comptable
808                    echo '<td class="decimal">'. association_formater_nombre($data['valeurs']) .'</td>';
809                    $total_valeurs += $data['valeurs'];
810                } else { // mode liste standard
811                    echo '<td class="decimal">'. association_formater_nombre($data['recettes']) .'</td>';
812                    $total_recettes += $data['recettes'];
813                    echo '<td class="decimal">'. association_formater_nombre($data['depenses']) .'</td>';
814                    $total_depenses += $data['depenses'];
815                    //echo '<td class="decimal">'. association_formater_nombre($data['soldes']) .'</td>';
816                    $total_valeurs += $data['soldes'];
817                }
818                echo "</tr>\n";
819#           }
820        }
821    }
822    echo "\n<tr class='row_first'>";
823    echo '<th colspan="2">&nbsp;</th>';
824    echo '<th scope="row" class="text solde">'. _T("asso:$prefixe".'_total') .'</th>';
825    if ($direction) { // mode liste comptable
826        echo '<th class="solde decimal">'. association_formater_nombre($total_valeurs) . '</th>';
827    } else { // mode liste standard
828        echo '<th class="entree decimal">'. association_formater_nombre($total_recettes) . '</th>';
829        echo '<th class="sortie decimal">'. association_formater_nombre($total_depenses) . '</th>';
830        // echo '<th class="solde decimal">'. association_formater_nombre($total_valeurs) . '</th>';
831    }
832    echo "</tr>\n</table>\n";
833    return $total_valeurs;
834}
835
836/**
837 * On affiche la difference entre les recettes et les depenses (passees en parametre) pour les classes d'un exercice
838 * @param float $recettes
839 *   Total des recettes
840 * @param float $depenses
841 *   Total des depenses
842 * @return void
843 *   Table-HTML presentant le solde comptable (deficit ou benefice)
844 */
845function association_liste_resultat_net($recettes, $depenses) {
846    echo "<table width='100%' class='asso_tablo' id='asso_tablo_bilan_solde'>\n";
847    echo "<tr>";
848    echo '<th style="width: 10px">&nbsp;</th>';
849    echo '<th style="width: 30px">&nbsp;</th>';
850    echo '<th scope="row">'. _T('asso:cpte_resultat_titre_resultat') .'</th>';
851    echo '<th style="width: 80px">&nbsp;</th>';
852    echo "</tr>";
853    echo "\n<tr>";
854    echo '<th colspan="2">&nbsp;</th>';
855    $res = $recettes-$depenses;
856    echo '<th class="solde text">'. (($res<0) ? _T('asso:cpte_resultat_perte') : _T('asso:cpte_resultat_benefice')) .'</th>';
857    echo '<th class="solde decimal">'. association_formater_nombre(abs($res)) .'</th>';
858    echo "</tr></table>";
859}
860
861function export_compte($ids, $mode, $icone = true)
862{
863        // exports connus (a completer au besoin)
864        foreach(array('csv','ctx','dbk','json','tex','tsv','xml','yaml') as $t){
865                        $args = $ids['id_periode'] . "-$mode-"
866                        . $ids['type_periode']
867                        .($ids['destination']? ('-' . $ids['destination']) :'');
868
869                $s = ($t == 'tex') ? 'latex' : $t;
870                $script = "export_soldescomptes_$s";
871                include_spip('inc/actions');
872                $url = generer_action_auteur($script, $args);
873                $t = strtoupper($t);
874                if ($icone)
875                    echo association_navigation_raccourci1($t, 'export-24.png', $url);
876                else
877                    echo "<a href='$url'>$t</a> ";
878        }
879}
880
881
882// Brique commune aux classes d'exportation des etats comptables
883class ExportComptes_TXT {
884
885    var $periode; // id_exercice || annee
886    var $destination; // id_destination
887    var $type; // type d'export : bilan|resultat
888    var $classes; // liste des classes a exporter
889    var $titre; // intitule de l'exercice
890    var $out; // contenu du fichier
891
892    /**
893     * Constructeur (fonction d'initialisatio de la classe)
894     *
895     * @param array|string $var
896     *   Tableau des parametres (les cles sont : id_periode, id_destination, titre_periode, classes, titre)
897     *   Ce tableau peut etre serialise et c'est la chaine de caracteres resultante qui est passee
898     *   Enfin, quand il n'y a rien, on recupere les differents elements dans l'environnement
899     * @return $this->
900     *   Les proprietes de la classe sont initialisees
901     */
902    function __construct($var='') {
903        if ( !$var ) // non transmis
904            $tableau = association_passeparam_compta(); // recuperer dans l'environnement (parametres d'URL)
905        elseif ( is_string($var) ) // transmis comme lien serialise
906            $tableau = unserialize(rawurldecode($var));
907        elseif ( is_array($var) ) // transmis comme tableau PHP
908            $tableau = $var;
909        else
910            $tableau = array($var=>0);
911        $this->periode = intval($tableau['id_periode']);
912        $this->destination = intval($tableau['destination']);
913        $this->type = $tableau['type'];
914        $this->titre = ($tableau['titre_periode']);
915        if ( count($tableau['classes']) ) { // on a la liste des classes qui est fournie
916            $this->classes = $tableau['classes'];
917        } else { // on sait retrouver la liste des tables en se basant sur le type d'exportation
918            switch ($tableau['type']) {
919                case 'bilan' :
920                    $query = sql_select(
921                        'classe', // select
922                        'spip_asso_plan', // from
923                        sql_in('classe', array($GLOBALS['association_metas']['classe_charges'],$GLOBALS['association_metas']['classe_produits'],$GLOBALS['association_metas']['classe_contributions_volontaires']), 'NOT'), // where  not in
924                        'classe', // group by
925                        'classe' // order by
926                    );
927                    while ($data = sql_fetch($query)) {
928                        $this->classes[$data['classe']] = 0;
929                    }
930                    break;
931                case 'resultat' :
932                    $this->classes = array($GLOBALS['association_metas']['classe_charges']=>'-1', $GLOBALS['association_metas']['classe_produits']=>'+1', $GLOBALS['association_metas']['classe_contributions_volontaires']=>0);
933                    break;
934            }
935        }
936        $this->out = '';
937    }
938
939    /**
940     * Export texte de type tableau (lignes*colonnes) simple : CSV,CTX,HTML*SPIP,INI*,TSV,etc.
941     *
942     * 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"
943     *
944     * @param string $champsSeparateur
945     *   Caractere separant deux champs/colonnes.
946     *   (par exemple : la virgule)
947     * @param string $lignesSeparateur
948     *   Caractere separant deux lignes/enregistrements.
949     *   (par exemple : le saut de ligne)
950     * @param array $echappements
951     *   Tableaux des remplacemens simples a effectuer : "des ceci"=>"par cela"
952     *   Il faut, en effet, souvent proteger la presence de caracteres speciaux
953     *   qui sont utilises comme parametres ici.
954     * @param string $champDebut
955     *   Caracter place au debut de chaque champ/colonne
956     * @param string $champFin
957     *   Caracter place a la fin de chaque champ/enregistrement
958     * @param bool $entete
959     *   Indique si en plus des donnees il faut rajouter (vrai --par defaut) ou pas (faux) une ligne de titre au debut
960     * @param bool $multi
961     *   Indique si on recupere directement le solde (faux --par defaut) ou si on recupere separement les totaux des recettes et des depenses
962     * @return string $this->out
963     *   Contenu de l'export
964     */
965    function exportLignesUniques($champsSeparateur, $lignesSeparateur, $echappements=array(), $champDebut='', $champFin='', $entete=TRUE, $multi=FALSE) {
966        if ($entete) {
967            $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), utf8_decode(html_entity_decode(_T('asso:entete_code')))) .$champFin.$champsSeparateur;
968            $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), utf8_decode(html_entity_decode(_T('asso:entete_intitule')))) .$champFin.$champsSeparateur;
969            if (!$multi) {
970                $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), utf8_decode(html_entity_decode(_T('asso:entete_montant')))) .$champFin.$lignesSeparateur;
971            } else {
972                $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), utf8_decode(html_entity_decode(_T('asso:entete_recette')))) .$champFin.$champsSeparateur;
973                $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), utf8_decode(html_entity_decode(_T('asso:entete_depense')))) .$champFin.$lignesSeparateur;
974            }
975        }
976        foreach ($this->classes as $laClasse=>$laDirection) {
977            $query = association_calcul_soldes_comptes_classe($laClasse, $this->periode, $this->destination, $multi?0:$laDirection);
978            $chapitre = '';
979            $i = 0;
980            while ($data = sql_fetch($query)) {
981                $new_chapitre = substr($data['code'], 0, 2);
982                if ($chapitre!=$new_chapitre) {
983                    $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), $new_chapitre) .$champFin.$champsSeparateur;
984                    $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), comptabilite_reference_intitule($new_chapitre) ) .$champFin.$champsSeparateur;
985                    $this->out .= $champsSeparateur.' '.$champsSeparateur;
986                    $this->out .= $lignesSeparateur;
987                    $chapitre = $new_chapitre;
988                }
989                $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), $data['code']) .$champFin.$champsSeparateur;
990                $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), $data['intitule']) .$champFin.$champsSeparateur;
991                if (!$multi) {
992                    $this->out .= $champDebut. ($laDirection?$data['valeurs']:$data['recettes']-$data['depenses']) .$champFin.$lignesSeparateur;
993                } else {
994                    $this->out .= $champDebut.$data['recettes'].$champFin.$champsSeparateur;
995                    $this->out .= $champDebut.$data['depenses'].$champFin.$lignesSeparateur;
996                }
997            }
998        }
999    }
1000
1001    /**
1002     * Export texte de type s-expression / properties-list / balisage (conteneurs*conteneurs*donnees) simple : JSON, XML (utilisable avec ASN.1), YAML, etc.
1003     *
1004     * de par la simplicite recherchee il n'y a pas de types ou d'attributs : BSON, Bencode, JSON, pList, XML, etc.
1005     *
1006     * @param array $balises
1007     *   Tableau des balises d'ouverture (...1) et de fermeture (...0) a appliquer.
1008     *   Elles sont indexees par des cles (...N) convenues ainsi :
1009     * - titre : pour l'intitule de la synthese exportee
1010     * - nom : pour le nom de l'association
1011     * - exercice : pour l'intitule de l'exercice
1012     * - categorie : pour ?
1013     * - chapitre : pour ?
1014     * - libelle : pour ?
1015     * - code : pour la reference comptable d'un compte
1016     * - intitule : pour l'intitule renseigne pour un compte
1017     * - credit : pour la somme des recettes d'un compte
1018     * - debit : pour la somme des depenses d'un compte
1019     * - montant : pour le sode d'un compte
1020     * @param array $echappements
1021     *   Tableaux des remplacemens simples a effectuer : "des ceci"=>"par cela"
1022     *   Il faut, en effet, souvent proteger la presence de caracteres speciaux
1023     *   qui sont utilises comme parametres ici.
1024     * @param string $champDebut
1025     *   Caracter place au debut de chaque champ/colonne
1026     * @param string $champFin
1027     *   Caracter place a la fin de chaque champ/enregistrement
1028     * @param string $ident
1029     *   Caractere d'indentation des blocs
1030     * @param bool $entetePerso
1031     *   Indique si en plus des donnees il faut rajouter (vrai --par defaut) ou pas (faux) une ligne de titre au debut
1032     * @param bool $multi
1033     *   Indique si on recupere directement le solde (faux --par defaut) ou si on recupere separement les totaux des recettes et des depenses
1034     * @return string $this->out
1035     *   Contenu de l'export
1036     */
1037    function exportLignesMultiples($balises, $echappements=array(), $champDebut='', $champFin='', $indent="\t", $entetesPerso='', $multi=FALSE) {
1038        $this->out .= "$balises[compteresultat1]\n";
1039        if (!$entetesPerso) {
1040            $this->out .= "$indent$balises[entete1]\n";
1041            $this->out .= "$indent$indent$balises[titre1] $champDebut". utf8_decode(html_entity_decode(_T('asso:cpte_resultat_titre_general'))) ."$champFin $balises[titre0]\n";
1042            $this->out .= "$indent$indent$balises[nom1] $champDebut". $GLOBALS['association_metas']['nom'] ."$champFin $balises[nom0]\n";
1043            $this->out .= "$indent$indent$balises[exercice1] $champDebut". $this->titre ."$champFin $balises[exercice0]\n";
1044            $this->out .= "$indent$balises[entete0]\n";
1045        }
1046        foreach ($this->classes as $laClasse=>$laDirection) {
1047            $baliseClasse = $nomClasse.'1';
1048            $this->out .= "$indent$balises[$baliseClasse]\n";
1049            $query = association_calcul_soldes_comptes_classe($laClasse, $this->periode, $this->destination, $laDirection);
1050            $chapitre = '';
1051            $i = 0;
1052            while ($data = sql_fetch($query)) {
1053                if ( !$laDirection ) {
1054                    $valeurs = ($data['depenses']>0)?$data['depenses']:$data['recettes'];
1055                } else {
1056                    $valeurs = $data['valeurs'];
1057                }
1058                $new_chapitre = substr($data['code'], 0, 2);
1059                if ($chapitre!=$new_chapitre) {
1060                    if ($chapitre!='') {
1061                        $this->out .= "$indent$indent$balises[chapitre0]\n";
1062                    }
1063                    $this->out .= "$indent$indent$balises[chapitre1]\n";
1064                    $this->out .= "$indent$indent$indent$balises[code1] $champDebut". str_replace(array_keys($echappements), array_values($echappements), $new_chapitre) ."$champFin $balises[code0]\n";;
1065                    $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";
1066                    $chapitre = $new_chapitre;
1067                }
1068                $this->out .= "$indent$indent$indent$balises[categorie1]\n";
1069                $this->out .= "$indent$indent$indent$indent$balises[code1] $champDebut". str_replace(array_keys($echappements), array_values($echappements), $data['code']) ."$champFin $balises[code0]\n";
1070                $this->out .= "$indent$indent$indent$indent$balises[intitule1] $champDebut". str_replace(array_keys($echappements), array_values($echappements), $data['intitule']) ."$champFin $balises[intitule0]\n";
1071                if ( !$multi ) {
1072                    $this->out .= "$indent$indent$indent$indent$balises[montant1] $champDebut".$valeurs."$champFin $balises[montant0]\n";
1073                } else {
1074                    $this->out .= "$indent$indent$indent$indent$balises[credit1] $champDebut".$data['recettes']."$champFin $balises[credit0]\n";
1075                    $this->out .= "$indent$indent$indent$indent$balises[debit1] $champDebut".$data['depenses']."$champFin $balises[debit0]\n";
1076                }
1077                $this->out .= "$indent$indent$indent$balises[categorie0]\n";
1078            }
1079            if ($chapitre!='') {
1080                $this->out .= "$indent$indent$balises[chapitre0]\n";
1081            }
1082            $baliseClasse = $nomClasse.'0';
1083            $this->out .= "$indent$balises[$baliseClasse]\n";
1084        }
1085        $this->out .= "$balises[compteresultat0]\n";
1086    }
1087
1088    /**
1089     * Fichier texte final a afficher/telecharger
1090     *
1091     * @param string $ext
1092     *   Extension a donner au fichier
1093     * @param string $subtype
1094     *   Sous-type a inclure dans le nom du fichier
1095     *   Par defaut, c'est le type d'export (bilon ou resultat).
1096     * @return
1097     */
1098    function leFichier($ext, $subtype='') {
1099        $fichier = 'compte_'. ($subtype?$subtype:$this->type) .'_'.$this->periode.'_'.$this->destination.".$ext";
1100
1101        header('Content-type: application/'.$ext);
1102        header('Content-Disposition: attachment; filename="'.$fichier.'"');
1103        echo  $this->out;
1104    }
1105
1106}
1107
1108if (test_plugin_actif('FPDF')) {
1109
1110    define('FPDF_FONTPATH', 'font/');
1111    include_spip('fpdf');
1112    include_spip('inc/charsets');
1113
1114class ExportComptes_PDF extends FPDF {
1115
1116    // variables de parametres de mise en page
1117    var $icone_h = 20;
1118    var $icone_v = 20;
1119
1120    // variables de mise en page calculees
1121    var $largeur_utile = 190; // largeur sans les marges droites et gauches
1122    var $cell_padding = 2; // espacement entre les bords des cellules et leur contenu
1123
1124    // position du curseur
1125    var $xx = 0; // abscisse 1ere boite
1126    var $yy = 0; // ordonnee 1ere boite
1127
1128    // variables de fonctionnement passees en parametre
1129    var $periode; // id_exercice ou annee
1130    var $destination; // id_destination
1131    var $titre; // intitule de l'exercice
1132
1133    /**
1134     * Initialisations
1135     * @param array $ids
1136     *   Tableau des parametres (les cles sont : id_periode, id_destination, titre_periode, classes, titre)
1137     *   Quand il n'y a rien, on recupere les differents elements dans l'environnement
1138     * @return $this->
1139     *   Les proprietes de la classe sont initialisees
1140     */
1141    function init($ids='') {
1142        if ( !$ids ) // tableau de parametres non transmis
1143            $ids = association_passeparam_compta(); // recuperer dans l'environnemet (parametres d'URL)
1144        // passer les parametres transmis aux variables de la classe
1145        $this->periode = $ids['id_periode'];
1146        $this->destination = $ids['destination'];
1147        $this->titre = $ids['titre_periode'];
1148        // calculer les dimensions de mise en page
1149        $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);
1150        $this->cell_padding = ($GLOBALS['association_metas']['fpdf_marginc']?$GLOBALS['association_metas']['fpdf_marginc']:2);
1151        // initialiser les variables de mise en page
1152        $this->xx = ($GLOBALS['association_metas']['fpdf_marginl']?$GLOBALS['association_metas']['fpdf_marginl']:10); // marge gauche
1153        $this->yy = ($GLOBALS['association_metas']['fpdf_margint']?$GLOBALS['association_metas']['fpdf_margint']:10); // marge haute
1154        // meta pour le fichier PDF
1155        $this->SetAuthor('Marcel BOLLA');
1156        $this->SetCreator('Associaspip & Fpdf');
1157        $this->SetTitle('Module Comptabilite');
1158        $this->SetSubject('Etats comptables');
1159        // typo par defaut
1160        $this->underline = '';
1161        $this->FontStyle = '';
1162        $this->FontSizePy = 12;
1163        $this->FontFamily = ($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial');
1164        // engager la page
1165        // http://fpdf.org/en/doc/addpage.htm
1166        $this->AddPage($GLOBALS['association_metas']['fpdf_orientation'],
1167                       $GLOBALS['association_metas']['fpdf_format']
1168                       ? $GLOBALS['association_metas']['fpdf_format']
1169                       : array(($GLOBALS['association_metas']['fpdf_widht']
1170                                ? $GLOBALS['association_metas']['fpdf_widht']
1171                                : 210),
1172                               ($GLOBALS['association_metas']['fpdf_height']
1173                                ? $GLOBALS['association_metas']['fpdf_height']
1174                                :297) ) );
1175    }
1176
1177    /**
1178     * Pied de pages :
1179     * redefinition de FPDF::Footer() qui est automatiquement appele par FPDF::AddPage() et FPDF::Close() !
1180     *
1181     * @note
1182     *   http://www.id.uzh.ch/cl/zinfo/fpdf/doc/footer.htm
1183     *   Adapter la marge basse (et la hauteur utile) des pages en consequence
1184     */
1185    function Footer() {
1186        // Positionnement a 2 fois la marge du bas
1187        $this->SetY(-2*($GLOBALS['association_metas']['fpdf_margint']?$GLOBALS['association_metas']['fpdf_margint']:10));
1188        // typo
1189        $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), 'I', 8); // police: italique 8px
1190        $this->SetTextColor(128); // Couleur du texte : gris-50.2% (fond blanc)
1191        // Date et Numéro de page
1192        include_spip('inc/filtres');
1193        $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');
1194    }
1195
1196    /**
1197     * Haut de pages :
1198     * redefinition de FPDF qui est directement appele par FPDF::AddPage()
1199     * @note
1200     *   http://www.id.uzh.ch/cl/zinfo/fpdf/doc/header.htm
1201     *   Adapter la marge haute (et la hauteur utile) des pages en consequence
1202    */
1203    function Header() {
1204        // nop
1205    }
1206
1207    /**
1208     * Cartouche au debut de la 1ere page
1209     *
1210     * @param string $titre
1211     *   Nom de l'export : place au dessous le nom de l'association et au dessus de l'intitule de l'exercice
1212     * @return void
1213     *   Le contenu du PDF
1214     * @note
1215     *   Contrairement au Header ceci fait partir du contenu/flux et n'est pas repete sur toutes les pages, et peut accepter des parametres
1216     */
1217    function association_cartouche_pdf($titre='') {
1218        // Les coordonnees courantes
1219        $xc = $this->xx+$this->cell_padding;
1220        $yc = $this->yy+$this->cell_padding;
1221        $this->SetDrawColor(128); // La couleur du trace : gris 50.2% (sur fond blanc)
1222        // Le logo du site
1223#       $chercher_logo = charger_fonction('chercher_logo', 'inc');
1224#       $logo = $chercher_logo(0, 'id_site');
1225        $logo = find_in_path('IMG/siteon0.jpg'); // Probleme FPDF et images non JPEG :-/ http://forum.virtuemart.net/index.php?topic=75616.0
1226        if ($logo) {
1227            include_spip('/inc/filtres_images_mini');
1228            $this->Image(extraire_attribut(image_reduire($logo, $this->icone_h, $this->icone_v), 'src'), $xc, $yc, $this->icone_h);
1229        }
1230        // typo
1231        $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), 'B', 22); // police : gras 22px
1232        $this->SetFillColor(235); // Couleur du cadre, du fond du cadre : gris-92,2%
1233        $this->SetTextColor(0); // Couleur du texte : noir
1234        $largeur_pour_titre = $this->largeur_utile-$this->icone_h-3*$this->cell_padding;
1235        // Titre centre
1236        $xc += $this->cell_padding+($logo?$this->icone_h:0);
1237        $this->SetXY($xc, $yc);
1238        $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);
1239        $yc += 12;
1240        $this->Ln($this->cell_padding); // Saut de ligne
1241        $yc += $this->cell_padding;
1242        // typo
1243        $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), '', 12); // police : normal 12px
1244        $this->SetFillColor(235); // Couleur de remplissage : gris-92.2%
1245        // Sous titre Nom de l'association
1246        $this->SetXY($xc, $yc);
1247        $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);
1248        $yc += 6;
1249        $this->Ln($this->cell_padding/2); // Demi saut de ligne
1250        $yc += $this->cell_padding/2;
1251        // typo
1252        $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), '', 12); // police : normal 12px
1253        $this->SetFillColor(235); // Couleur de fond : gris-92.2%
1254        //Sous titre Intitule de l'exercice
1255        $this->SetXY($xc, $yc);
1256        $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);
1257        $yc += 6;
1258        $this->Ln($this->cell_padding); // Saut de ligne
1259        $yc += $this->cell_padding;
1260        $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
1261        $this->yy = $yc; // on sauve la position du curseur dans la page
1262    }
1263
1264    // Fichier final envoye
1265    function File($titre='etat_comptes') {
1266        $this->Output($titre.'_'.$this->periode.'_'.$this->destination.'.pdf', 'I');
1267    }
1268
1269    // on affiche les totaux (recettes et depenses) d'un exercice des differents comptes de la classe specifiee
1270    function association_liste_totaux_comptes_classes($classes, $prefixe='', $direction='-1', $periode=0, $destination=0) {
1271        if( !is_array($classes) ) { // a priori une chaine ou un entier d'une unique classe
1272            $liste_classes = array( $classes ) ; // transformer en tableau (puisqu'on va operer sur des tableaux);
1273        } else { // c'est un tableau de plusieurs classes
1274            $liste_classes = $classes;
1275        }
1276        // Les coordonnees courantes
1277        $xc = $this->xx+$this->cell_padding;
1278        $y_orig = $this->yy+$this->cell_padding;
1279        $yc = $y_orig+$this->cell_padding;
1280        // typo
1281        $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), 'B', 14); // police: gras 14px
1282        $this->SetFillColor(235); // Couleursdu fond du cadre de titre : gris-92.2%
1283        $this->SetTextColor(0); // Couleurs du texte du cadre de titre
1284        // Titre centre
1285        $titre = $prefixe.'_'. ( ($direction) ? (($direction<0)?'depenses':'recettes') : 'soldes' );
1286        $this->SetXY($xc, $yc);
1287        $this->Cell($this->largeur_utile, 10, html_entity_decode(_T("asso:$titre")), 0, 0, 'C');
1288        $yc += 10;
1289        $this->Ln($this->cell_padding); // Saut de ligne
1290        $yc += $this->cell_padding;
1291        // initialisation du calcul+affichage des comptes
1292        $total_valeurs = $total_recettes = $total_depenses = 0;
1293        $chapitre = '';
1294        $i = 0;
1295        foreach ( $liste_classes as $rang => $classe ) { // calcul+affichage par classe
1296            $query = association_calcul_soldes_comptes_classe($classe, $this->periode, $this->destination, $direction );
1297            $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), '', 12); // police : normal 12px
1298            while ($data = sql_fetch($query)) {
1299                $this->SetXY($xc, $yc); // positionne le curseur
1300                $new_chapitre = substr($data['code'], 0, 2);
1301                if ($chapitre!=$new_chapitre) { // debut de categorie
1302                    $this->SetFillColor(225); // Couleur de fond de la ligne : gris-92.2%
1303                    $this->Cell(20, 6, utf8_decode($new_chapitre), 0, 0, 'L', TRUE);
1304                    $this->Cell(($this->largeur_utile)-(2*$this->cell_padding+20), 6, utf8_decode(comptabilite_reference_intitule($new_chapitre)), 0, 0, 'L', TRUE);
1305                    $chapitre = $new_chapitre;
1306                    $this->Ln(); // Saut de ligne
1307                    $yc += 6;
1308                }
1309                $this->SetFillColor(245); // Couleur de fond du total : gris-96.1%
1310                $this->SetXY($xc, $yc); // positionne le curseur
1311#               if ( floatval($data['valeurs']) || floatval($data['recettes']) || floatval($data['depenses']) ) { // non-zero...
1312                    $this->Cell(20, 6, utf8_decode($data['code']), 0, 0, 'R', TRUE);
1313                    $this->Cell(($this->largeur_utile)-(2*$this->cell_padding+50), 6, utf8_decode($data['intitule']), 0, 0, 'L', TRUE);
1314                    $this->Cell(30, 6, association_formater_nombre($data['valeurs']), 0, 0, 'R', TRUE);
1315                    if ($direction) { // mode liste comptable
1316                        $this->Cell(30, 6, association_formater_nombre($data['valeurs']), 0, 0, 'R', TRUE);
1317                        $total_valeurs += $data['valeurs'];
1318                    } else { // mode liste standard
1319                        $this->Cell(30, 6, association_formater_nombre($data['depenses']>0?$data['depenses']:$data['recettes']), 0, 0, 'R', TRUE);
1320                        $total_recettes += $data['recettes'];
1321                        $total_depenses += $data['depenses'];
1322                        $total_valeurs += $data['soldes'];
1323                    }
1324                    $this->Ln(); // Saut de ligne
1325                    $yc += 6;
1326#               }
1327            }
1328        }
1329        $this->SetXY($xc, $yc); // positionne le curseur
1330        $this->SetFillColor(215); // Couleur de fond : 84.3%
1331        if ($direction) { // mode liste comptable : charge, produit, actifs, passifs
1332            $this->Cell(($this->largeur_utile)-(2*$this->cell_padding+30), 6, html_entity_decode(_T("asso:$prefixe".'_total')), 1, 0, 'R', TRUE);
1333            $this->Cell(30, 6, association_formater_nombre($total_valeurs), 1, 0, 'R', TRUE);
1334        } else { // mode liste standard : contributions volontaires et autres
1335            $this->Cell(($this->largeur_utile)/2-(2*$this->cell_padding+30), 6, html_entity_decode(_T("asso:$prefixe".'_total_depenses')), 1, 0, 'R', TRUE);
1336            $this->Cell(30, 6, association_formater_nombre($total_depenses), 1, 0, 'R', TRUE);
1337            $xc += ( $this->largeur_utile)/2;
1338            $this->SetXY($xc, $yc); // positionne le curseur sur l'autre demi page
1339            $this->Cell(($this->largeur_utile)/2-(2*$this->cell_padding+30), 6, html_entity_decode(_T("asso:$prefixe".'_total_recettes')), 1, 0, 'R', TRUE);
1340            $this->Cell(30, 6, association_formater_nombre($total_recettes), 1, 0, 'R', TRUE);
1341        }
1342        $yc += 6;
1343        $this->Ln($this->cell_padding); // Saut de ligne
1344        $yc += $this->cell_padding;
1345        $this->Rect($this->xx, $y_orig, $this->largeur_utile, $yc-$y_orig); // Rectangle tout autour
1346        $this->yy = $yc; // on sauve la position du curseur dans la page
1347        return $total_valeurs;
1348    }
1349
1350    // on affiche le resultat comptable net : benefice ou deficit
1351    function association_liste_resultat_net($lesRecettes, $lesDepenses) {
1352        // Les coordonnees courantes
1353        $xc = $this->xx+$this->cell_padding;
1354        $y_orig = $this->yy+$this->cell_padding;
1355        $yc = $y_orig+$this->cell_padding;
1356        // typo
1357        $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), 'B', 14); // police : gras 14px
1358        $this->SetFillColor(235); // Couleur du fond : gris-92.2%
1359        $this->SetTextColor(0); // Couleur du texte : noir
1360        // Titre centre
1361        $this->SetXY($xc, $yc);
1362        $this->Cell($this->largeur_utile, 10, html_entity_decode(_T('asso:cpte_resultat_titre_resultat')), 0, 0, 'C');
1363        $yc += 10;
1364        $this->Ln($this->cell_padding); // Saut de ligne
1365        $yc += $this->cell_padding;
1366        $this->SetFillColor(215); // Couleur de fond : gris-84.3%
1367        $leSolde = $lesRecettes-$lesDepenses;
1368        $this->SetXY($xc, $yc);
1369        $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);
1370        $this->Cell(30, 6, association_formater_nombre($leSolde), 1, 0, 'R', TRUE);
1371        $yc += 6;
1372        $this->Ln($this->cell_padding); // Saut de ligne
1373        $yc += $this->cell_padding;
1374        $this->Rect($this->xx, $y_orig, $this->largeur_utile, $yc-$y_orig); // Rectangle tout autour
1375        $this->yy = $yc; // on sauve la position du curseur dans la page
1376    }
1377
1378} // fin classe
1379
1380} // fin if
1381
1382?>
Note: See TracBrowser for help on using the repository browser.