source: spip-zone/_plugins_/Association/Associaspip/inc/association_comptabilite.php @ 66942

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

suite de la revue de la compta et corrections sur les commits du mois

File size: 55.8 KB
Line 
1<?php
2/***************************************************************************\
3 *  Associaspip, extension de SPIP pour gestion d'associations
4 *
5 * @copyright Copyright (c) 2007 Bernard Blazin & Francois de Montlivault
6 * @copyright Copyright (c) 2010 Emmanuel Saint-James
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 * @defgroup comptabilite_
18 * dedie au module (futur plugin) Comptabilite (ComptaSPIP)
19 *
20** @{ */
21
22/**
23 * Recupere dans les tables la liste des destinations associees a une operation
24 *
25 * @param int $id_operation
26 *   id_compte de l'operation dans spip_asso_compte (et spip_asso_destination)
27 * @return array $destinations
28 *   Un tableau de id_destination=>montant
29 *   ou une chaine vide
30 */
31function association_liste_destinations_associees($id_operation) {
32    if (!$id_compte)
33        return '';
34    if ($sql = sql_select(' spip_asso_destination_op.*, spip_asso_destination.intitule', 'spip_asso_destination_op RIGHT JOIN spip_asso_destination ON spip_asso_destination.id_destination=spip_asso_destination_op.id_destination', "id_compte=$id_operation", '', 'spip_asso_destination.intitule')) {
35        $destinations = array();
36        while ( $ventilations = sql_fetch($sql) ) {
37            $destination[$ventilations['id_destination']] = $destination_op['recette']+$destination_op['depense']; // soit recette soit depense est egal a 0, donc pour l'affichage du montant on se contente les additionner
38        }
39        if ( !count($destinations) )
40            $destinations = '';
41    } else {
42        $destinations = '';
43    }
44    return $destinations;
45}
46
47/**
48 * Selecteur de destinations dHTML
49 *
50 * @param string $destinations
51 *   Tableau de destinations deja selectionnees
52 *   Ou '' si on ajoute une operation
53 * @param bool $unique
54 *   Permet de specifier si on veut associer une destination unique (vrai),
55 *   par default on peut ventiler sur plusieurs destinations
56 * @param int $defaut
57 *   Permet de selectionner une destination par defaut (par id_destination)
58 *   quand $destinations est vide
59 * @return string $res
60 *   un <div> le code HTML/javascript correspondant au selecteur de destinations
61 */
62function association_editeur_destinations($destinations, $defaut='') {
63    $options_destinations = '';
64    $sql = sql_select('id_destination,intitule', 'spip_asso_destination', '', '', 'intitule');
65    while ($destination_info = sql_fetch($sql)) { // recupere la liste de toutes les destinations dans un code HTML <option value="destination_id">destination</option>
66        $options_destinations .= '<option value="'. $destination_info['id_destination'] .'">'.$destination_info['intitule'].'</option>';
67    }
68    $res = '';
69    if ($options_destinations) { // des destinations sont definies et on en a genere la liste HTML
70        $res = '<script type="text/javascript" src="'.find_in_path('javascript/jquery.destinations_form.js').'"></script>';
71        $res .= '<label for="destination">'
72            . _T('asso:destination') .'</label>'
73            . '<div id="divTxtDestination" class="formulaire_edition_destinations">';
74        $idIndex = 1;
75        if ( is_array($destinations) ) { // si on a une liste de destinations (on edite une operation)
76            foreach ($destinations as $destId => $destMontant) { // restitution des listes de selection HTML
77                $destination_selected = preg_replace('/(value="'.$destId.'")/', '$1 selected="selected"', $options_destinations);
78                $res .= '<div id="row'.$idIndex.'" class="choix"><ul>';
79                $res .= '<li class="editer_id_dest['.$idIndex.']">'
80                    . '<select name="id_dest['.$idIndex.']" id="id_dest['.$idIndex.']" >'
81                    . $destination_selected
82                    . '</select></li>';
83                if (!$GLOBALS['association_metas']['unique_dest']) { // destinations multiples
84                    $res .= '<li class="editer_montant_dest['.$idIndex.']"><input name="montant_dest['.$idIndex.']" value="'
85                        . association_formater_nombre($destMontant)
86                        . '" type="text" id="montant_dest['.$idIndex.']" class="number decimal price" /></li>'
87                        . '<button class="destButton" type="button" onClick="addFormField(); return FALSE;">+</button>';
88                    if ($idIndex>1) {
89                        $res .= '<button class="destButton" type="button" onClick="removeFormField(\'#row'.$idIndex.'\'); return FALSE;">-</button>';
90                    }
91                }
92                $res .= '<ul></div>';
93                $idIndex++;
94            }
95        } else { // pas de destination deja definies pour cette operation
96            if ($defaut!='') {
97                $options_destinations = preg_replace('/(value="'.$defaut.'")/', '$1 selected="selected"', $options_destinations);
98            }
99            $res .= '<div id="row1" class="choix"><ul><li class="editer_id_dest[1]"><select name="id_dest[1]" id="id_dest[1]" >'
100                . $options_destinations . '</select></li>';
101            if (!$GLOBALS['association_metas']['unique_dest']) { // destinations multiples
102                $res .= '<li class="editer_montant_dest[1]"><input name="montant_dest[1]" value="'
103                    .'" type="text" id="montant_dest[1]"/></li>'
104                    . '</ul><button class="destButton" type="button" onClick="addFormField(); return FALSE;">+</button>';
105            }
106            $res .= '</div>';
107        }
108        if (!$GLOBALS['association_metas']['unique_dest']) // destinations multiples
109            $res .= '<input type="hidden" id="idNextDestination" value="'.($idIndex+1).'">';
110        $res .= '</div>';
111    }
112    return $res;
113}
114
115/**
116 * Ajouter une operation comptable ainsi que ses ventilations si necessaire
117 *
118 * @param string $date
119 *   Date de l'operation au format ISO
120 * @param float $recette
121 *   Montant encaisse
122 * @param float $depense
123 *   Montant decaisse
124 * @param string $justification
125 *   Libelle de l'operation
126 * @param string $imputation
127 *   Compte d'imputation (reference du plan comptable)
128 * @param string $journal
129 *   Compte financier impacte (reference du plan comptable)
130 * @param int $id_journal
131 *   ID de l'enregistrement associe dans le module  (chaque imputation etant gere par un seul module)
132 * @return int $id_operation
133 *   ID de l'operation dans spip_asso_comptes et spip_asso_destination_op
134 */
135function association_ajouter_operation_comptable($date, $recette, $depense, $justification, $imputation, $journal, $id_journal) {
136    $modifs = array(
137        'date' => $date,
138        'imputation' => $imputation,
139        'recette' => $recette,
140        'depense' => $depense,
141        'journal' => $journal,
142        'id_journal' => $id_journal,
143        'justification' => $justification
144    );
145    $id_operation = sql_insertq('spip_asso_comptes', $modifs);
146    // on passe par modifier_contenu afin que l'enregistrement soit envoye aux plugins et que Champs Extras 2 la recupere
147    include_spip('inc/modifier');
148    modifier_contenu('asso_compte', $id_operation, '', $modifs);
149    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
150        spip_log("imputation manquante : id_compte=$id_compte, date=$date, recette=$recette, depense=$depense, journal=$journal, id_journal=$id_journal, justification=$justification",'associaspip');
151    }
152    if ($GLOBALS['association_metas']['destinations']) { // Si on doit gerer les destinations
153        association_ajouter_destinations_comptables($id_operation, $recette, $depense);
154    }
155    return $id_operation;
156
157}
158
159/** Modifier une operation comptable ainsi que ses ventilations si necessaire
160 *
161 * @param string $date
162 *   Date de l'operation au format ISO
163 * @param float $recette
164 *   Montant encaisse
165 * @param float $depense
166 *   Montant decaisse
167 * @param string $justification
168 *   Libelle de l'operation
169 * @param string $imputation
170 *   Compte d'imputation (reference du plan comptable)
171 * @param string $journal
172 *   Compte financier impacte (reference du plan comptable)
173 * @param int $id_journal
174 *   ID de l'enregistrement associe dans le module  (chaque imputation etant gere par un seul module)
175 * @param int $id_operation
176 *   ID de l'operation dans spip_asso_comptes et spip_asso_destination_op
177 * @return string $err
178 *   Message d'erreur (vide en cas de succes)
179 */
180function association_modifier_operation_comptable($date, $recette, $depense, $justification, $imputation, $journal, $id_journal, $id_operation) {
181    $err = '';
182    $id_operation = intval($id_operation);
183    if ( sql_countsel('spip_asso_comptes', "id_compte=$id_operation AND vu ") ) { // il ne faut pas modifier une operation verouillee !!!
184        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');
185        return $err = _T('asso:operation_non_modifiable');
186    }
187    if ($GLOBALS['association_metas']['destinations']) { // Si on doit gerer les destinations
188        $err = association_ajouter_destinations_comptables($id_operation, $recette, $depense);
189    }
190    $modifs = array(
191        'date' => $date,
192        'imputation' => $imputation,
193        'recette' => $recette,
194        'depense' => $depense,
195        'journal' => $journal,
196        'justification' => $justification,
197    );
198    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
199        $modifs['id_journal'] = $id_journal;
200    }
201    // on passe par modifier_contenu (et non sql_updateq) pour que la modification soit envoyee aux plugins et que Champs Extras 2 la recupere
202    include_spip('inc/modifier');
203    modifier_contenu('asso_compte', $id_operation, '', $modifs);
204    return $err;
205}
206
207/**
208 * Supprimer une operation dans spip_asso_comptes ainsi que si necessaire sa ventilation dans spip_asso_destination_op ;
209 * cas 1 : usage direct de id_compte
210 *
211 * @param int $id_operation
212 *   ID de l'operation a supprimer
213 * @param bool $securite
214 *   Mettre a TRUE pour supprimer quand meme une operation verouillee
215 * @return int $annulation
216 *   ID de l'enregistrement d'ecriture inverse : indique donc une annulation
217 *   comptable quand different de 0, et une suppression pure et simple sinon
218 */
219function association_supprimer_operation_comptable1($id_operation, $securite=FALSE) {
220    list($date, $recette, $depense, $imputation, $journal, $id_journal, $verrou) = sql_fetsel('date, 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
221    if ( ($securite AND !$verrou) || !$securite ) { // operation non verouillee ou controle explicitement desactive...
222        $annulation = 0;
223        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
224        spip_log("suppression d'operation comptable : id_compte=$id_operation, date=$date, recette=$recette, depense=$depense, imputation=$imputation, journal=$journal, id_journal=$id_journal, justification=...",'associaspip'); // on logue quand meme
225        sql_delete('spip_asso_comptes', "id_compte=$id_operation"); // on efface enfin de la table comptes l'entree correspondant a cette operation
226    } else { // on ne supprime pas les ecritures validees/verouillees ; il faut annuler l'operation par une operation comptable inverse...
227        $annulation = sql_insertq('spip_asso_comptes', array(
228            'date' => date('Y-m-d'),
229            'depense' => $recette,
230            'recette' => $depense,
231            'imputation' => _T('asso:compte_annulation_operation', array('numero'=>$id_compte,'date'=>$date) ),
232            'imputation' => $imputation, // pas forcement vrai, mais on fait au plus simples...
233            'journal' => $journal, // pas forcement vrai, mais on fait au plus simples...
234            'id_journal' => -$id_journal, // on garde la trace par rapport au module ayant cree l'operation
235            'vu' => 1, // cette operation n'est pas moifiable non plus...
236        ) ); // 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)...
237#       spip_log("annulation d'operation comptable : id_compte=$id_operation, date=$date, recette=$recette, depense=$depense, imputation=$imputation, journal=$journal, id_journal=$id_journal, justification=annule_par_op$annulation",'associaspip'); // on logue quand meme ?
238    }
239    return $annulation;
240}
241
242/**
243 * Supprimer une operation dans spip_asso_comptes ainsi que si necessaire sa ventilation dans spip_asso_destination_op ;
244 * cas 2 : usage par les modules du couple imputation&id_journal
245 *
246 * @param int $id_journal
247 *   ID de l'enregistrement associe dans le module  (chaque imputation etant gere par un seul module)
248 * @param string $pc_journal
249 *   Nom de la meta associe au module (renverra le code comptable gere uniquement par ce module)
250 * @return int $id_operation
251 *   ID de l'enregistrement supprime ou annule
252 *   (vaut donc 0 si aucun enregistrement touche)
253 */
254function association_supprimer_operation_comptable2($id_journal, $pc_journal) {
255    $association_imputation = charger_fonction('association_imputation', 'inc');
256    if ( $id_operation = sql_getfetsel('id_compte', 'spip_asso_comptes', $association_imputation($pc_journal, $id_journal)) )
257        association_supprimer_operation_comptable1($id_operation);
258    return $id_operation; // indique quelle operation a ete supprimee (0 si aucune --donc erreur dans les parametres ?)
259}
260
261/**
262 * Suppression en masse d'operations compatebles avec leur ventilations
263 *
264 * @param string $critere
265 *   Critere de selection SQL des operations a supprimer
266 * @retur void
267 * @warning
268 *   Cette fonction etant sans garde-four est a manipulur avec precaution !
269 *   Les suppression sont irreversibles...
270 */
271function association_supprimer_operations_comptables($critere) {
272    $where = sql_in_select('id_compte', 'id_compte', 'spip_asso_comptes', $critere); // on recupere la liste des a supprimer
273    sql_delete('spip_asso_destination_op', $where); // on efface les ventilations de ces operations  si on en trouve
274    $query_log = sql_select('id_compte, date, recette, depense, imputation, journal, id_journal', 'spip_asso_comptes', $where);
275    while ( list($id_compte, $date, $recette, $depense, $imputation, $journal, $id_journal) = fetch($query_log) ) { // on logue les enregistrements a supprimer du livre comptable
276        spip_log("suppression d'operation comptable : id_compte=$id_compte, date=$date, recette=$recette, depense=$depense, imputation=$imputation, journal=$journal, id_journal=$id_journal ",'associaspip');
277    }
278    sql_delete('spip_asso_comptes', $where); // on efface enfin de la table comptes les entrees correspondant a ces operations ($where ou $critere)
279}
280
281/**
282 * Fonction permettant d'ajouter/modifier les destinations comptables (presente dans $_POST) a une operation comptable
283 *
284 * @param int $id_compte
285 *   ID de l'operation comptable a ventiller
286 * @param float $recette
287 *   Montant total des recettes a ventiller
288 * @param float $depense
289 *   Montant total des depenses a ventiller
290 * @return void
291 */
292function association_ajouter_destinations_comptables($id_compte, $recette, $depense) {
293    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
294    if ($recette>0) {
295        $attribution_montant = 'recette';
296    } else {
297        $attribution_montant = 'depense';
298    }
299    $toutesDestinationsIds = association_recuperer_liste('id_dest');
300    $toutesDestinationsMontants = association_recuperer_liste('montant_dest');
301    if ( count($toutesDestinationsIds)>1 ) { // plusieurs destinations
302        foreach ($toutesDestinationsIds as $id => $id_destination) { // ventilation des montants. le tableau des montants a des cles indentique a celui des id
303            $id_dest_op = sql_insertq('spip_asso_destination_op', array(
304                'id_compte' => $id_compte,
305                'id_destination' => $id_destination,
306                $attribution_montant => association_recuperer_montant($toutesDestinationsMontants[$id], FALSE),
307            ));
308        }
309    } elseif ( count($toutesDestinationsIds)==1 ) { // une seule destination : le montant peut ne pas avoir ete precise, on entre directement le total recette+depense
310        $id_dest_op = sql_insertq('spip_asso_destination_op', array(
311            'id_compte' => $id_compte,
312            'id_destination' => $toutesDestinations[1],
313            $attribution_montant => $depense+$recette
314        ));
315    }
316}
317
318/**
319 * Prepare le critere sur une imputation comptable
320 *
321 * @param string $nom
322 *   Nom de la meta contenant le code d'imputation
323 * @param int $id
324 *   ID de l'enregistrement associe
325 * @param string $table
326 *   Nom ou alias de la table a interroger
327 * @return string $champ
328 *   sous-requete SQL de selection/restriction a une imputation comptable
329 */
330function inc_association_imputation_dist($nom, $id='', $table='') {
331    $champ = ($table ? ($table . '.') : '') . 'imputation';
332    return "$champ=". sql_quote($GLOBALS['association_metas'][$nom]) .($id?(" AND id_journal=".sql_quote($id)):'') ;
333}
334
335/**
336 * Valide le plan comptable :
337 *- on doit avoir au moins deux classes de comptes differentes
338 *- le code de chaque compte doit etre unique
339 *- le code du compte doit commencer par un chiffre egal a sa classe
340 *
341 * @return bool
342 *   TRUE si le plan comptable est valide
343 *   FALSE si le plan comptable est invalide
344 */
345function association_valider_plan_comptable() {
346    $classes = array();
347    $codes = array();
348    $query = sql_select('code, classe', 'spip_asso_plan'); // recupere le code et la classe de tous les comptes du plan comptabl
349    while ($data = sql_fetch($query)) {
350        $classe = $data['classe'];
351        $code = $data['code'];
352        $classes[$classe] = 0; // on comptes les classes differentes
353        if(array_key_exists($code, $codes)) {
354            return FALSE; // on a deux fois le meme code
355        } else {
356            $codes[$code] = 0;
357        }
358        if ((!preg_match("/^[0-9]{2}\w*$/", $code)) || ($code[0]!=$classe)) // on verifie que le code est bien de la forme chiffre-chiffre-caracteres alphanumeriques et que le premier digit correspond a la classe
359            return FALSE;
360    }
361    if (count($classes)<2)
362        return FALSE; // on doit avoir au moins deux classes differentes
363    return TRUE;
364}
365
366/**
367 * Tableau des comptes d'une classe du plan comptable
368 *
369 * @param int $val
370 *   Classe dont on veut recuprer les comptes
371 * @param int active
372 *   Ce parametre facultatif permet de se restreindre aux comptes actifs (1) ou inactifs (0)
373 * @return array $res
374 *   retourne un tableau $code=>$intitule trie par code
375 */
376function association_liste_plan_comptable($val, $actives='') {
377    $res = array();
378    $query = sql_select('code, intitule', 'spip_asso_plan', "classe='$val'".($actives!=''?" AND active=$actives":''), '', 'code'); // recupere le code et l'intitule de tous les comptes de classe $val
379    while ($data = sql_fetch($query)) {
380        $res[$data['code']] = $data['intitule'];
381    }
382    return $res;
383}
384
385/**
386 * Recupere le code du compte des virements internes
387 *
388 * @return string $res
389 *   C'est le code normalement defini dans la configuration du plugin.
390 *   S'il n'existe pas, on prend le premier compte 58x existant,
391 *   sinon on cree le compte 581 !
392 */
393function association_creer_compte_virement_interne() {
394    if ($GLOBALS['association_metas']['pc_intravirements']) // un code de virement interne est deja defini !
395        return $GLOBALS['association_metas']['pc_intravirements'];
396    $res = association_liste_plan_comptable($GLOBALS['association_metas']['classe_banques']); // on recupere tous les comptes de la classe "financier" (classe 5)
397    foreach($res as $code => $libelle) { // existe-t-il le compte 58x ? (nota : c'est la compta francaise...)
398        if (substr($code,1,1)=='8') // il existe un code qui commence par 58...
399            return $code;
400    }
401    // j'ai rien trouve, je cree le compte 581
402    $code = $GLOBALS['association_metas']['classe_banques'].'81';
403    $id_plan = sql_insertq('spip_asso_plan', array(
404        'code' => $code,
405        'intitule' => _T('asso:virement_interne'),
406        'classe' => $GLOBALS['association_metas']['classe_banques'],
407        'type_op' => 'multi',
408        'solde_anterieur' => '0',
409        'date_anterieure' => date('Y-m-d'),
410        'commentaire' => _T('asso:compte_cree_automatiquement'),
411        'active' => '0',
412        'maj' => date('Y-m-d')
413    ));
414    if ($id_plan)
415        sql_insertq('spip_association_metas', array(
416            'nom' => 'pc_intravirements',
417            'valeur' => $code,
418        ));
419    return $code;
420}
421
422/* on recupere les parametres de requete a passer aux fonctions */
423function association_passeparam_compta($classes=array()) {
424    $params = array(); // initialisation de la liste
425//    list($params['id_periode'], $params['sql_periode']) = association_passeparam_periode('operation', 'comptes', $id_compte);
426    $params['id_periode'] = association_passeparam_periode();
427    if ($GLOBALS['association_metas']['exercices']) {
428#       $params['exercice'] = association_passeparam_exercice();
429        $params['type_periode'] = 'exercice';
430        $exercice = sql_fetsel('date_debut, date_fin, intitule', 'spip_asso_exercices', "id_exercice=$paramas[id_periode]");
431        $params['debut_periode'] = $exercice['date_debut'];
432        $params['fin_periode'] = $exercice['date_fin'];
433        $params['titre_periode'] = $exercice['intitule'];
434    } else {
435#       $params['annee'] = association_passeparam_annee();
436        $params['type_periode'] = 'annee';
437        $params['debut_periode'] = "$params[id_periode]-01-01";
438        $params['fin_periode'] = "$params[id_periode]-12-31";
439        $params['titre_periode'] = $params['id_periode'];
440    }
441    $params['destination'] = association_recuperer_entier('destination');
442    $params['type'] = _request('type');
443    if ( !$classes ) { // pas en parametre, on prend dans la requete
444//      $params['classes'] = array_flip( explode(',', association_recuperer_liste('classes')) );
445        $keys = explode(',', association_recuperer_liste('classes'));
446        if ( count($keys) ) {
447            $vals = array_fill(0, count($keys) ,0);
448            $params['classes'] = array_combine($keys, $vals);
449        } else {
450            $params['classes'] = array();
451        }
452    } elseif ( is_array($classes) ) { // c'est a priori bon
453        $params['classes'] = $classes;
454    } else { // c'est un tableau de classe_comptable=>type_operations qui est requis !
455        $params['classes'] = $classes ? array( $classes=>0 ) : array() ;
456    }
457    $params['url'] = serialize($params); //!\ les cles numeriques peuvent poser probleme... <http://www.mail-archive.com/php-bugs@lists.php.net/msg100262.html> mais il semble qu'ici le souci vient de l'absence d'encodage lorsqu'on passe $var par URL...
458    return $params;
459}
460
461/**
462 * On recupere les soldes des differents comptes de la classe specifiee pour la periode specifiee
463 *
464 * @param int $classe
465 *   Classe dont on veut recuperer les soldes des differents comptes
466 * @param int $periode
467 *   ID exercice ou annee (selon configuration)
468 * @param int $destination
469 *   ID destination
470 * @param float $direction
471 *   Le signe de ce parametre indique le type de compte (et donc le sens de calcul du solde)
472 *   positif : comptes de credit (solde=recettes-depenses)
473 *   negatif : comptes de debit (solde=depenses-recettes)
474 * @return ressource $query
475 *   Resultat de la requete donnant les soldes de chaque compte de la classe indiquee
476 * @note
477 *   d'apres http://www.lacompta.ch/MITIC/theorie.php?ID=26 c'est le solde qui est recherche, et il corresponde bien a :
478 *  recettes-depenses=recettes pour les classes 6
479 *  depenses-recettes=depenses pour les classes 7
480 */
481function association_calcul_soldes_comptes_classe($classe, $periode=0, $destination=0, $direction='-1') {
482    $c_group = (($classe==$GLOBALS['association_metas']['classe_banques'])?'journal':'imputation');
483    $valeurs = (($direction)
484        ?
485        ( ($direction<0)
486            ?'SUM('.(($destination)?'a_d':'a_c').'.depense-'.(($destination)?'a_d':'a_c').'.recette) AS valeurs'
487            : 'SUM('.(($destination)?'a_d':'a_c').'.recette-'.(($destination)?'a_d':'a_c').'.depense) AS valeurs'
488        )
489        :
490        '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' );
491    $c_having = ($direction) ? 'valeurs>0' : ''; // on ne retiendra que les totaux non nuls...
492    if ( sql_countsel('spip_asso_plan','active=1') ) { // existence de comptes actifs
493        $p_join = " RIGHT JOIN spip_asso_plan AS a_p ON a_c.$c_group=a_p.code";
494        $p_select = ', a_p.code, a_p.intitule, a_p.classe';
495        $p_order = 'a_p.code'; // imputation ou journal
496#       $p_where = 'a_p.classe='.sql_quote($classe);
497        $p_having = 'a_p.classe='.sql_quote($classe); // ok : on agrege par code (indirectement) associe a une classe unique selectionnee ...
498    } else { // pas de comptes actifs ?!?
499        $p_join = $p_select = $p_where = $p_having = '';
500        $p_order = $c_group; // imputation ou journal
501    }
502    if ( $periode ) { // restriction sur une periode donnee
503        if ($GLOBALS['association_metas']['exercices']) { // exercice budgetaire personnalise
504            $exercice = sql_fetsel('date_debut, date_fin', 'spip_asso_exercices', "id_exercice=".intval($periode));
505            $c_where = "a_c.date_operation>='$exercice[date_debut]' AND a_c.date_operation<='$exercice[date_fin]' ";
506        } else { // exercice budgetaire par annee civile
507            $c_where = "DATE_FORMAT(a_c.date, '%Y')=".intval($periode);
508        }
509#    } elseif ( $classe==$GLOBALS['association_metas']['classe_banques'] ) { // encaisse
510#       $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() ';
511    } else { // tout depuis le debut ?!?
512        $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 ?
513    }
514    $query = sql_select(
515        "$c_group, $valeurs ". ($destination ? ', a_d.id_destination' : '') .$p_select, // select
516        '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
517        ($destination ? "a_d.id_destination=$destination AND " : '') . ($p_where?"$p_where AND ":'')  .$c_where, // where
518        $c_group, // group by
519        $p_order, // order by
520        '', // limit
521        $c_having. (($c_having && $p_having)?' AND ':'') .$p_having // having
522    );
523    return $query;
524}
525
526/**
527 * On affiche les totaux (recettes et depenses) des differents comptes de la classe specifiee pour une periode donnee
528 *
529 * @param array $classes
530 *   Liste des classes dont on veut afficher les soldes des differents comptes
531 * @param string $prefixe
532 *   Prefixe a applique aux termes qualifiant la direction pour former le titre du tableau
533 * @param float $direction
534 *   Le signe de ce parametre indique le type de compte (et donc le sens de calcul du solde)
535 *   positif : comptes de credit (solde=recettes-depenses)
536 *   negatif : comptes de debit (solde=depenses-recettes)
537 * @param int $periode
538 *   ID exercice ou annee (selon configuration)
539 * @param int $destination
540 *   ID destination
541 * @return void
542 *   HTML-Table listant les differents soldes ordonnes par classes puis par numeros de compte
543 */
544function association_liste_totaux_comptes_classes($classes, $prefixe='', $direction='-1', $periode=0, $destination=0) {
545    if( !is_array($classes) ) { // a priori une chaine ou un entier d'une unique classe
546        $liste_classes = array( $classes ) ; // transformer en tableau (puisqu'on va operer sur des tableaux);
547    } else { // c'est un tableau de plusieurs classes
548        $liste_classes = $classes;
549    }
550    $titre = $prefixe.'_'. ( ($direction) ? (($direction<0)?'depenses':'recettes') : 'soldes' );
551    echo "<table width='100%' class='asso_tablo' id='asso_tablo_$titre'>\n";
552    echo "<thead>\n<tr>";
553    echo '<th width="10">&nbsp;</td>';
554    echo '<th width="30">&nbsp;</td>';
555    echo '<th>'. _T("asso:$titre") .'</th>';
556    if ($direction) { // mode liste comptable : charge, produit, actifs, passifs
557        echo '<th width="80">&nbsp;</th>';
558    } else { // mode liste standard : contributions volontaires et autres
559        echo '<th width="80">'. _T("asso:$prefixe".'_recettes') .'</th>';
560        echo '<th width="80">'. _T("asso:$prefixe".'_depenses') .'</th>';
561        // echo '<th width="80">'. _T("asso:$prefixe".'_solde') .'</th>';
562    }
563    echo "</tr>\n</thead><tbody>";
564    $total_valeurs = $total_recettes = $total_depenses = 0;
565    $chapitre = '';
566    $i = 0;
567    foreach ( $liste_classes as $rang => $classe ) {
568        $query = association_calcul_soldes_comptes_classe($classe, $periode, $destination, $direction );
569        while ($data = sql_fetch($query)) {
570            echo '<tr>';
571            $new_chapitre = substr($data['code'], 0, 2);
572            if ($chapitre!=$new_chapitre) {
573                echo '<td class="text">'. $new_chapitre . '</td>';
574                echo '<td colspan="3" class="text">'. ($GLOBALS['association_metas']['plan_comptable_prerenseigne']?association_plan_comptable_complet($new_chapitre):sql_getfetsel('intitule','spip_asso_plan',"code='$new_chapitre'")) .'</td>';
575                $chapitre = $new_chapitre;
576                echo "</tr>\n<tr>";
577            }
578#           if ( floatval($data['valeurs']) || floatval($data['recettes']) || floatval($data['depenses']) ) { // non-zero...
579                echo "<td>&nbsp;</td>";
580                echo '<td class="text">'. $data['code'] .'</td>';
581                echo '<td class="text">'. $data['intitule'] .'</td>';
582                if ($direction) { // mode liste comptable
583                    echo '<td class="decimal">'. association_formater_nombre($data['valeurs']) .'</td>';
584                    $total_valeurs += $data['valeurs'];
585                } else { // mode liste standard
586                    echo '<td class="decimal">'. association_formater_nombre($data['recettes']) .'</td>';
587                    $total_recettes += $data['recettes'];
588                    echo '<td class="decimal">'. association_formater_nombre($data['depenses']) .'</td>';
589                    $total_depenses += $data['depenses'];
590                    //echo '<td class="decimal">'. association_formater_nombre($data['soldes']) .'</td>';
591                    $total_valeurs += $data['soldes'];
592                }
593                echo "</tr>\n";
594#           }
595        }
596    }
597    echo "</tbody><tfoot>\n<tr>";
598    echo '<th colspan="2">&nbsp;</th>';
599    echo '<th class="text">'. _T("asso:$prefixe".'_total') .'</th>';
600    if ($direction) { // mode liste comptable
601        echo '<th class="decimal">'. association_formater_nombre($total_valeurs) . '</th>';
602    } else { // mode liste standard
603        echo '<th class="decimal">'. association_formater_nombre($total_recettes) . '</th>';
604        echo '<th class="decimal">'. association_formater_nombre($total_depenses) . '</th>';
605        // echo '<th class="decimal">'. association_formater_nombre($total_valeurs) . '</th>';
606    }
607    echo "</tr>\n</tfoot>\n</table>\n";
608    return $total_valeurs;
609}
610
611/**
612 * On affiche la difference entre les recettes et les depenses (passees en parametre) pour les classes d'un exercice
613 * @param float $recettes
614 *   Total des recettes
615 * @param float $depenses
616 *   Total des depenses
617 * @return void
618 *   Table-HTML presentant le solde comptable (deficit ou benefice)
619 */
620function association_liste_resultat_net($recettes, $depenses) {
621    echo "<table width='100%' class='asso_tablo' id='asso_tablo_bilan_solde'>\n";
622    echo "<thead>\n<tr>";
623    echo '<th width="10">&nbsp;</td>';
624    echo '<th width="30">&nbsp;</td>';
625    echo '<th>'. _T('asso:cpte_resultat_titre_resultat') .'</th>';
626    echo '<th width="80">&nbsp;</th>';
627    echo "</tr>\n</thead>";
628    echo "<tfoot>\n<tr>";
629    echo '<th colspan="2">&nbsp;</th>';
630    $res = $recettes-$depenses;
631    echo '<th class="text">'. (($res<0) ? _T('asso:cpte_resultat_perte') : _T('asso:cpte_resultat_benefice')) .'</th>';
632    echo '<th class="decimal">'. association_formater_nombre(abs($res)) .'</th>';
633    echo "</tr></tfoot></table>";
634}
635
636// Brique commune aux classes d'exportation des etats comptables
637class ExportComptes_TXT {
638
639    var $periode; // id_exercice || annee
640    var $destination; // id_destination
641    var $type; // type d'export : bilan|resultat
642    var $classes; // liste des classes a exporter
643    var $titre; // intitule de l'exercice
644    var $out; // contenu du fichier
645
646    /**
647     * Constructeur (fonction d'initialisatio de la classe)
648     *
649     * @param array|string $var
650     *   Tableau des parametres (les cles sont : id_periode, id_destination, titre_periode, classes, titre)
651     *   Ce tableau peut etre serialise et c'est la chaine de caracteres resultante qui est passee
652     *   Enfin, quand il n'y a rien, on recupere les differents elements dans l'environnement
653     * @return $this->
654     *   Les proprietes de la classe sont initialisees
655     */
656    function __construct($var='') {
657        if ( !$var ) // non transmis
658            $tableau = association_passeparam_compta(); // recuperer dans l'environnement (parametres d'URL)
659        elseif ( is_string($var) ) // transmis comme lien serialise
660            $tableau = unserialize(rawurldecode($var));
661        elseif ( is_array($var) ) // transmis comme tableau PHP
662            $tableau = $var;
663        else
664            $tableau = array($var=>0);
665        $this->periode = intval($tableau['id_periode']);
666        $this->destination = intval($tableau['destination']);
667        $this->type = $tableau['type'];
668        $this->titre = ($tableau['titre_periode']);
669        if ( count($tableau['classes']) ) { // on a la liste des classes qui est fournie
670            $this->classes = $tableau['classes'];
671        } else { // on sait retrouver la liste des tables en se basant sur le type d'exportation
672            switch ($tableau['type']) {
673                case 'bilan' :
674                    $query = sql_select(
675                        'classe', // select
676                        'spip_asso_plan', // from
677                        sql_in('classe', array($GLOBALS['association_metas']['classe_charges'],$GLOBALS['association_metas']['classe_produits'],$GLOBALS['association_metas']['classe_contributions_volontaires']), 'NOT'), // where  not in
678                        'classe', // group by
679                        'classe' // order by
680                    );
681                    while ($data = sql_fetch($query)) {
682                        $this->classes[$data['classe']] = 0;
683                    }
684                    break;
685                case 'resultat' :
686                    $this->classes = array($GLOBALS['association_metas']['classe_charges']=>'-1', $GLOBALS['association_metas']['classe_produits']=>'+1', $GLOBALS['association_metas']['classe_contributions_volontaires']=>0);
687                    break;
688            }
689        }
690        $this->out = '';
691    }
692
693    /**
694     * Export texte de type tableau (lignes*colonnes) simple : CSV,CTX,HTML*SPIP,INI*,TSV,etc.
695     *
696     * 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"
697     *
698     * @param string $champsSeparateur
699     *   Caractere separant deux champs/colonnes.
700     *   (par exemple : la virgule)
701     * @param string $lignesSeparateur
702     *   Caractere separant deux lignes/enregistrements.
703     *   (par exemple : le saut de ligne)
704     * @param array $echappements
705     *   Tableaux des remplacemens simples a effectuer : "des ceci"=>"par cela"
706     *   Il faut, en effet, souvent proteger la presence de caracteres speciaux
707     *   qui sont utilises comme parametres ici.
708     * @param string $champDebut
709     *   Caracter place au debut de chaque champ/colonne
710     * @param string $champFin
711     *   Caracter place a la fin de chaque champ/enregistrement
712     * @param bool $entete
713     *   Indique si en plus des donnees il faut rajouter (vrai --par defaut) ou pas (faux) une ligne de titre au debut
714     * @param bool $multi
715     *   Indique si on recupere directement le solde (faux --par defaut) ou si on recupere separement les totaux des recettes et des depenses
716     * @return string $this->out
717     *   Contenu de l'export
718     */
719    function exportLignesUniques($champsSeparateur, $lignesSeparateur, $echappements=array(), $champDebut='', $champFin='', $entete=TRUE, $multi=FALSE) {
720        if ($entete) {
721            $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), utf8_decode(html_entity_decode(_T('asso:entete_code')))) .$champFin.$champsSeparateur;
722            $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), utf8_decode(html_entity_decode(_T('asso:entete_intitule')))) .$champFin.$champsSeparateur;
723            if (!$multi) {
724                $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), utf8_decode(html_entity_decode(_T('asso:entete_montant')))) .$champFin.$lignesSeparateur;
725            } else {
726                $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), utf8_decode(html_entity_decode(_T('asso:entete_recette')))) .$champFin.$champsSeparateur;
727                $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), utf8_decode(html_entity_decode(_T('asso:entete_depense')))) .$champFin.$lignesSeparateur;
728            }
729        }
730        foreach ($this->classes as $laClasse=>$laDirection) {
731            $query = association_calcul_soldes_comptes_classe($laClasse, $this->periode, $this->destination, $multi?0:$laDirection);
732            $chapitre = '';
733            $i = 0;
734            while ($data = sql_fetch($query)) {
735                $new_chapitre = substr($data['code'], 0, 2);
736                if ($chapitre!=$new_chapitre) {
737                    $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), $new_chapitre) .$champFin.$champsSeparateur;
738                    $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), ($GLOBALS['association_metas']['plan_comptable_prerenseigne']?association_plan_comptable_complet($new_chapitre):sql_getfetsel('intitule','spip_asso_plan',"code='$new_chapitre'"))) .$champFin.$champsSeparateur;
739                    $this->out .= $champsSeparateur.' '.$champsSeparateur;
740                    $this->out .= $lignesSeparateur;
741                    $chapitre = $new_chapitre;
742                }
743                $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), $data['code']) .$champFin.$champsSeparateur;
744                $this->out .= $champDebut. str_replace(array_keys($echappements), array_values($echappements), $data['intitule']) .$champFin.$champsSeparateur;
745                if (!$multi) {
746                    $this->out .= $champDebut. ($laDirection?$data['valeurs']:$data['recettes']-$data['depenses']) .$champFin.$lignesSeparateur;
747                } else {
748                    $this->out .= $champDebut.$data['recettes'].$champFin.$champsSeparateur;
749                    $this->out .= $champDebut.$data['depenses'].$champFin.$lignesSeparateur;
750                }
751            }
752        }
753    }
754
755    /**
756     * Export texte de type s-expression / properties-list / balisage (conteneurs*conteneurs*donnees) simple : JSON, XML (utilisable avec ASN.1), YAML, etc.
757     *
758     * de par la simplicite recherchee il n'y a pas de types ou d'attributs : BSON, Bencode, JSON, pList, XML, etc.
759     *
760     * @param array $balises
761     *   Tableau des balises d'ouverture (...1) et de fermeture (...0) a appliquer.
762     *   Elles sont indexees par des cles (...N) convenues ainsi :
763     * - titre : pour l'intitule de la synthese exportee
764     * - nom : pour le nom de l'association
765     * - exercice : pour l'intitule de l'exercice
766     * - categorie : pour ?
767     * - chapitre : pour ?
768     * - libelle : pour ?
769     * - code : pour la reference comptable d'un compte
770     * - intitule : pour l'intitule renseigne pour un compte
771     * - credit : pour la somme des recettes d'un compte
772     * - debit : pour la somme des depenses d'un compte
773     * - montant : pour le sode d'un compte
774     * @param array $echappements
775     *   Tableaux des remplacemens simples a effectuer : "des ceci"=>"par cela"
776     *   Il faut, en effet, souvent proteger la presence de caracteres speciaux
777     *   qui sont utilises comme parametres ici.
778     * @param string $champDebut
779     *   Caracter place au debut de chaque champ/colonne
780     * @param string $champFin
781     *   Caracter place a la fin de chaque champ/enregistrement
782     * @param string $ident
783     *   Caractere d'indentation des blocs
784     * @param bool $entetePerso
785     *   Indique si en plus des donnees il faut rajouter (vrai --par defaut) ou pas (faux) une ligne de titre au debut
786     * @param bool $multi
787     *   Indique si on recupere directement le solde (faux --par defaut) ou si on recupere separement les totaux des recettes et des depenses
788     * @return string $this->out
789     *   Contenu de l'export
790     */
791    function exportLignesMultiples($balises, $echappements=array(), $champDebut='', $champFin='', $indent="\t", $entetesPerso='', $multi=FALSE) {
792        $this->out .= "$balises[compteresultat1]\n";
793        if (!$entetesPerso) {
794            $this->out .= "$indent$balises[entete1]\n";
795            $this->out .= "$indent$indent$balises[titre1] $champDebut". utf8_decode(html_entity_decode(_T('asso:cpte_resultat_titre_general'))) ."$champFin $balises[titre0]\n";
796            $this->out .= "$indent$indent$balises[nom1] $champDebut". $GLOBALS['association_metas']['nom'] ."$champFin $balises[nom0]\n";
797            $this->out .= "$indent$indent$balises[exercice1] $champDebut". $this->titre ."$champFin $balises[exercice0]\n";
798            $this->out .= "$indent$balises[entete0]\n";
799        }
800        foreach ($this->classes as $laClasse=>$laDirection) {
801            $baliseClasse = $nomClasse.'1';
802            $this->out .= "$indent$balises[$baliseClasse]\n";
803            $query = association_calcul_soldes_comptes_classe($laClasse, $this->periode, $this->destination, $laDirection);
804            $chapitre = '';
805            $i = 0;
806            while ($data = sql_fetch($query)) {
807                if ( !$laDirection ) {
808                    $valeurs = ($data['depenses']>0)?$data['depenses']:$data['recettes'];
809                } else {
810                    $valeurs = $data['valeurs'];
811                }
812                $new_chapitre = substr($data['code'], 0, 2);
813                if ($chapitre!=$new_chapitre) {
814                    if ($chapitre!='') {
815                        $this->out .= "$indent$indent$balises[chapitre0]\n";
816                    }
817                    $this->out .= "$indent$indent$balises[chapitre1]\n";
818                    $this->out .= "$indent$indent$indent$balises[code1] $champDebut". str_replace(array_keys($echappements), array_values($echappements), $new_chapitre) ."$champFin $balises[code0]\n";;
819                    $this->out .= "$indent$indent$indent$balises[libelle1] $champDebut". str_replace(array_keys($echappements), array_values($echappements), ($GLOBALS['association_metas']['plan_comptable_prerenseigne']?association_plan_comptable_complet($new_chapitre):sql_getfetsel('intitule','spip_asso_plan',"code='$new_chapitre'"))) ."$champFin $balises[libelle0]\n";
820                    $chapitre = $new_chapitre;
821                }
822                $this->out .= "$indent$indent$indent$balises[categorie1]\n";
823                $this->out .= "$indent$indent$indent$indent$balises[code1] $champDebut". str_replace(array_keys($echappements), array_values($echappements), $data['code']) ."$champFin $balises[code0]\n";
824                $this->out .= "$indent$indent$indent$indent$balises[intitule1] $champDebut". str_replace(array_keys($echappements), array_values($echappements), $data['intitule']) ."$champFin $balises[intitule0]\n";
825                if ( !$multi ) {
826                    $this->out .= "$indent$indent$indent$indent$balises[montant1] $champDebut".$valeurs."$champFin $balises[montant0]\n";
827                } else {
828                    $this->out .= "$indent$indent$indent$indent$balises[credit1] $champDebut".$data['recettes']."$champFin $balises[credit0]\n";
829                    $this->out .= "$indent$indent$indent$indent$balises[debit1] $champDebut".$data['depenses']."$champFin $balises[debit0]\n";
830                }
831                $this->out .= "$indent$indent$indent$balises[categorie0]\n";
832            }
833            if ($chapitre!='') {
834                $this->out .= "$indent$indent$balises[chapitre0]\n";
835            }
836            $baliseClasse = $nomClasse.'0';
837            $this->out .= "$indent$balises[$baliseClasse]\n";
838        }
839        $this->out .= "$balises[compteresultat0]\n";
840    }
841
842    /**
843     * Fichier texte final a afficher/telecharger
844     *
845     * @param string $ext
846     *   Extension a donner au fichier
847     * @param string $subtype
848     *   Sous-type a inclure dans le nom du fichier
849     *   Par defaut, c'est le type d'export (bilon ou resultat).
850     * @return
851     */
852    function leFichier($ext, $subtype='') {
853        $fichier = _DIR_RACINE.'/'._NOM_TEMPORAIRES_ACCESSIBLES.'compte_'. ($subtype?$subtype:$this->type) .'_'.$this->periode.'_'.$this->destination.".$ext"; // on essaye de creer le fichier dans le cache local/ http://www.spip.net/fr_article4637.html
854        $f = fopen($fichier, 'w');
855        fputs($f, $this->out);
856                fclose($f);
857        header('Content-type: application/'.$ext);
858        header('Content-Disposition: attachment; filename="'.$fichier.'"');
859        readfile($fichier);
860    }
861
862}
863
864if (test_plugin_actif('FPDF')) {
865
866    define('FPDF_FONTPATH', 'font/');
867    include_spip('fpdf');
868    include_spip('inc/charsets');
869    include_spip('inc/association_plan_comptable');
870
871class ExportComptes_PDF extends FPDF {
872
873    // variables de parametres de mise en page
874    var $icone_h = 20;
875    var $icone_v = 20;
876    var $space_v = 2;
877    var $space_h = 2;
878
879    // variables de mise en page calculees
880    var $largeur_utile = 0; // largeur sans les marges droites et gauches
881    var $largeur_pour_titre = 0; // largeur utile sans icone
882
883    // position du curseur
884    var $xx = 0; // abscisse 1ere boite
885    var $yy = 0; // ordonnee 1ere boite
886
887    // variables de fonctionnement passees en parametre
888    var $periode; // id_exercice ou annee
889    var $destination; // id_destination
890    var $titre; // intitule de l'exercice
891
892    /**
893     * Initialisations
894     * @param array $ids
895     *   Tableau des parametres (les cles sont : id_periode, id_destination, titre_periode, classes, titre)
896     *   Quand il n'y a rien, on recupere les differents elements dans l'environnement
897     * @return $this->
898     *   Les proprietes de la classe sont initialisees
899     */
900    function init($ids='') {
901        if ( !$ids ) // tableau de parametres non transmis
902            $ids = association_passe_parametres_comptables(); // recuperer dans l'environnemet (parametres d'URL)
903        // passer les parametres transmis aux variables de la classe
904        $this->periode = $ids['id_periode'];
905        $this->destination = $ids['destination'];
906        $this->titre = $ids['titre_periode'];
907        // calculer les dimensions de mise en page
908        $this->largeur_utile = $GLOBALS['association_metas']['fpdf_widht']-2*$GLOBALS['association_metas']['fpdf_marginl'];
909        $this->largeur_pour_titre = $this->largeur_utile-$this->icone_h-3*$this->space_h;
910        // initialiser les variables de mise en page
911        $this->xx = $GLOBALS['association_metas']['fpdf_marginl'];
912        $this->yy = $GLOBALS['association_metas']['fpdf_margint'];
913        // meta pour le fichier PDF
914        $this->SetAuthor('Marcel BOLLA');
915        $this->SetCreator('Associaspip & Fpdf');
916        $this->SetTitle('Module Comptabilite');
917        $this->SetSubject('Etats comptables');
918        // typo par defaut
919        $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), '', 12);
920        // engager la page
921        // http://fpdf.org/en/doc/addpage.htm
922        $this->AddPage($GLOBALS['association_metas']['fpdf_orientation'], $GLOBALS['association_metas']['fpdf_format']?$GLOBALS['association_metas']['fpdf_format']:array($GLOBALS['association_metas']['fpdf_widht'], $GLOBALS['association_metas']['fpdf_height']) );
923    }
924
925    /**
926     * Pied de pages :
927     * redefinition de FPDF::Footer() qui est automatiquement appele par FPDF::AddPage() et FPDF::Close() !
928     *
929     * @note
930     *   http://www.id.uzh.ch/cl/zinfo/fpdf/doc/footer.htm
931     *   Adapter la marge basse (et la hauteur utile) des pages en consequence
932     */
933    function Footer() {
934        // Positionnement a 2 fois la marge du bas
935        $this->SetY(-2*$GLOBALS['association_metas']['fpdf_margint']);
936        // typo
937        $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), 'I', 8); // police: italique 8px
938        $this->SetTextColor(128); // Couleur du texte : gris-50.2% (fond blanc)
939        // Date et Numéro de page
940        $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');
941    }
942
943    /**
944     * Haut de pages :
945     * redefinition de FPDF qui est directement appele par FPDF::AddPage()
946     * @note
947     *   http://www.id.uzh.ch/cl/zinfo/fpdf/doc/header.htm
948     *   Adapter la marge haute (et la hauteur utile) des pages en consequence
949    */
950    function Header() {
951        // nop
952    }
953
954    /**
955     * Cartouche au debut de la 1ere page
956     *
957     * @param string $titre
958     *   Nom de l'export : place au dessous le nom de l'association et au dessus de l'intitule de l'exercice
959     * @return void
960     *   Le contenu du PDF
961     * @note
962     *   Contrairement au Header ceci fait partir du contenu/flux et n'est pas repete sur toutes les pages, et peut accepter des parametres
963     */
964    function association_cartouche_pdf($titre='') {
965        // Les coordonnees courantes
966        $xc = $this->xx+$this->space_h;
967        $yc = $this->yy+$this->space_v;
968        $this->SetDrawColor(128); // La couleur du trace : gris 50.2% (sur fond blanc)
969        // Le logo du site
970#       $chercher_logo = charger_fonction('chercher_logo', 'inc');
971#       $logo = $chercher_logo(0, 'id_site');
972        $logo = find_in_path('IMG/siteon0.jpg'); // Probleme FPDF et images non JPEG :-/ http://forum.virtuemart.net/index.php?topic=75616.0
973        if ($logo) {
974            include_spip('/inc/filtres_images_mini');
975            $this->Image(extraire_attribut(image_reduire($logo, $this->icone_h, $this->icone_v), 'src'), $xc, $yc, $this->icone_h);
976        }
977        // typo
978        $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), 'B', 22); // police : gras 22px
979        $this->SetFillColor(235); // Couleur du cadre, du fond du cadre : gris-92,2%
980        $this->SetTextColor(0); // Couleur du texte : noir
981        // Titre centre
982        $xc += $this->space_h+($logo?$this->icone_h:0);
983        $this->SetXY($xc, $yc);
984        $this->Cell($logo?($this->largeur_pour_titre):($this->largeur_pour_titre+$this->icone_h-$this->space_h), 12, html_entity_decode(_T("asso:$titre")), 0, 0, 'C', TRUE);
985        $yc += 12;
986        $this->Ln($this->space_v); // Saut de ligne
987        $yc += $this->space_v;
988        // typo
989        $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), '', 12); // police : normal 12px
990        $this->SetFillColor(235); // Couleur de remplissage : gris-92.2%
991        // Sous titre Nom de l'association
992        $this->SetXY($xc, $yc);
993        $this->Cell($logo?$this->largeur_pour_titre:$this->largeur_pour_titre+$this->icone_h-$this->space_h, 6, utf8_decode(_T('asso:cpte_export_association', array('nom'=>$GLOBALS['association_metas']['nom']) )), 0, 0, 'C', TRUE);
994        $yc += 6;
995        $this->Ln($this->space_v/2); // Saut de ligne
996        $yc += $this->space_v/2;
997        // typo
998        $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), '', 12); // police : normal 12px
999        $this->SetFillColor(235); // Couleur de fond : gris-92.2%
1000        //Sous titre Intitule de l'exercice
1001        $this->SetXY($xc, $yc);
1002        $this->Cell($logo?$this->largeur_pour_titre:$this->largeur_pour_titre+$this->icone_h-$this->space_h, 6, utf8_decode(_T('asso:cpte_export_exercice', array('titre'=>$this->titre) )), 0, 0, 'C', TRUE);
1003        $yc += 6;
1004        $this->Ln($this->space_v); // Saut de ligne
1005        $yc += $this->space_v;
1006        $this->Rect($this->xx, $this->yy, $this->largeur_utile, $yc-$GLOBALS['association_metas']['fpdf_margint']); // Rectangle tout autour de l'entete
1007        $this->yy = $yc; // on sauve la position du curseur dans la page
1008    }
1009
1010    // Fichier final envoye
1011    function File($titre='etat_comptes') {
1012        $this->Output($titre.'_'.$this->periode.'_'.$this->destination.'.pdf', 'I');
1013    }
1014
1015    // on affiche les totaux (recettes et depenses) d'un exercice des differents comptes de la classe specifiee
1016    function association_liste_totaux_comptes_classes($classes, $prefixe='', $direction='-1', $periode=0, $destination=0) {
1017        if( !is_array($classes) ) { // a priori une chaine ou un entier d'une unique classe
1018            $liste_classes = array( $classes ) ; // transformer en tableau (puisqu'on va operer sur des tableaux);
1019        } else { // c'est un tableau de plusieurs classes
1020            $liste_classes = $classes;
1021        }
1022        // Les coordonnees courantes
1023        $xc = $this->xx+$this->space_h;
1024        $y_orig = $this->yy+$this->space_v;
1025        $yc = $y_orig+$this->space_v;
1026        // typo
1027        $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), 'B', 14); // police: gras 14px
1028        $this->SetFillColor(235); // Couleursdu fond du cadre de titre : gris-92.2%
1029        $this->SetTextColor(0); // Couleurs du texte du cadre de titre
1030        // Titre centre
1031        $titre = $prefixe.'_'. ( ($direction) ? (($direction<0)?'depenses':'recettes') : 'soldes' );
1032        $this->SetXY($xc, $yc);
1033        $this->Cell($this->largeur_utile, 10, html_entity_decode(_T("asso:$titre")), 0, 0, 'C');
1034        $yc += 10;
1035        $this->Ln($this->space_v); // Saut de ligne
1036        $yc += $this->space_v;
1037        // initialisation du calcul+affichage des comptes
1038        $total_valeurs = $total_recettes = $total_depenses = 0;
1039        $chapitre = '';
1040        $i = 0;
1041        foreach ( $liste_classes as $rang => $classe ) { // calcul+affichage par classe
1042            $query = association_calcul_soldes_comptes_classe($classe, $this->periode, $this->destination, $direction );
1043            $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), '', 12); // police : normal 12px
1044            while ($data = sql_fetch($query)) {
1045                $this->SetXY($xc, $yc); // positionne le curseur
1046                $new_chapitre = substr($data['code'], 0, 2);
1047                if ($chapitre!=$new_chapitre) { // debut de categorie
1048                    $this->SetFillColor(225); // Couleur de fond de la ligne : gris-92.2%
1049                    $this->Cell(20, 6, utf8_decode($new_chapitre), 0, 0, 'L', TRUE);
1050                    $this->Cell(($this->largeur_utile)-(2*$this->space_h+20), 6, utf8_decode(($GLOBALS['association_metas']['plan_comptable_prerenseigne']?association_plan_comptable_complet($new_chapitre):sql_getfetsel('intitule','spip_asso_plan',"code='$new_chapitre'"))), 0, 0, 'L', TRUE);
1051                    $chapitre = $new_chapitre;
1052                    $this->Ln(); // Saut de ligne
1053                    $yc += 6;
1054                }
1055                $this->SetFillColor(245); // Couleur de fond du total : gris-96.1%
1056                $this->SetXY($xc, $yc); // positionne le curseur
1057#               if ( floatval($data['valeurs']) || floatval($data['recettes']) || floatval($data['depenses']) ) { // non-zero...
1058                    $this->Cell(20, 6, utf8_decode($data['code']), 0, 0, 'R', TRUE);
1059                    $this->Cell(($this->largeur_utile)-(2*$this->space_h+50), 6, utf8_decode($data['intitule']), 0, 0, 'L', TRUE);
1060                    $this->Cell(30, 6, association_formater_nombre($data['valeurs']), 0, 0, 'R', TRUE);
1061                    if ($direction) { // mode liste comptable
1062                        $this->Cell(30, 6, association_formater_nombre($data['valeurs']), 0, 0, 'R', TRUE);
1063                        $total_valeurs += $data['valeurs'];
1064                    } else { // mode liste standard
1065                        $this->Cell(30, 6, association_formater_nombre($data['depenses']>0?$data['depenses']:$data['recettes']), 0, 0, 'R', TRUE);
1066                        $total_recettes += $data['recettes'];
1067                        $total_depenses += $data['depenses'];
1068                        $total_valeurs += $data['soldes'];
1069                    }
1070                    $this->Ln(); // Saut de ligne
1071                    $yc += 6;
1072#               }
1073            }
1074        }
1075        $this->SetXY($xc, $yc); // positionne le curseur
1076        $this->SetFillColor(215); // Couleur de fond : 84.3%
1077        if ($direction) { // mode liste comptable : charge, produit, actifs, passifs
1078            $this->Cell(($this->largeur_utile)-(2*$this->space_h+30), 6, html_entity_decode(_T("asso:$prefixe".'_total')), 1, 0, 'R', TRUE);
1079            $this->Cell(30, 6, association_formater_nombre($total_valeurs), 1, 0, 'R', TRUE);
1080        } else { // mode liste standard : contributions volontaires et autres
1081            $this->Cell(($this->largeur_utile)/2-(2*$this->space_h+30), 6, html_entity_decode(_T("asso:$prefixe".'_total_depenses')), 1, 0, 'R', TRUE);
1082            $this->Cell(30, 6, association_formater_nombre($total_depenses), 1, 0, 'R', TRUE);
1083            $xc += ( $this->largeur_utile)/2;
1084            $this->SetXY($xc, $yc); // positionne le curseur sur l'autre demi page
1085            $this->Cell(($this->largeur_utile)/2-(2*$this->space_h+30), 6, html_entity_decode(_T("asso:$prefixe".'_total_recettes')), 1, 0, 'R', TRUE);
1086            $this->Cell(30, 6, association_formater_nombre($total_recettes), 1, 0, 'R', TRUE);
1087        }
1088        $yc += 6;
1089        $this->Ln($this->space_v); // Saut de ligne
1090        $yc += $this->space_v;
1091        $this->Rect($this->xx, $y_orig, $this->largeur_utile, $yc-$y_orig); // Rectangle tout autour
1092        $this->yy = $yc; // on sauve la position du curseur dans la page
1093        return $total_valeurs;
1094    }
1095
1096    // on affiche le resultat comptable net : benefice ou deficit
1097    function association_liste_resultat_net($lesRecettes, $lesDepenses) {
1098        // Les coordonnees courantes
1099        $xc = $this->xx+$this->space_h;
1100        $y_orig = $this->yy+$this->space_v;
1101        $yc = $y_orig+$this->space_v;
1102        // typo
1103        $this->SetFont(($GLOBALS['association_metas']['fpdf_font']?$GLOBALS['association_metas']['fpdf_font']:'Arial'), 'B', 14); // police : gras 14px
1104        $this->SetFillColor(235); // Couleur du fond : gris-92.2%
1105        $this->SetTextColor(0); // Couleur du texte : noir
1106        // Titre centre
1107        $this->SetXY($xc, $yc);
1108        $this->Cell($this->largeur_utile, 10, html_entity_decode(_T('asso:cpte_resultat_titre_resultat')), 0, 0, 'C');
1109        $yc += 10;
1110        $this->Ln($this->space_v); // Saut de ligne
1111        $yc += $this->space_v;
1112        $this->SetFillColor(215); // Couleur de fond : gris-84.3%
1113        $leSolde = $lesRecettes-$lesDepenses;
1114        $this->SetXY($xc, $yc);
1115        $this->Cell(($this->largeur_utile)-(2*$this->space_h+30), 6, html_entity_decode(_T('asso:cpte_resultat_'.($leSolde<0?'perte':'benefice'))), 1, 0, 'R', TRUE);
1116        $this->Cell(30, 6, association_formater_nombre($leSolde), 1, 0, 'R', TRUE);
1117        $yc += 6;
1118        $this->Ln($this->space_v); // Saut de ligne
1119        $yc += $this->space_v;
1120        $this->Rect($this->xx, $y_orig, $this->largeur_utile, $yc-$y_orig); // Rectangle tout autour
1121        $this->yy = $yc; // on sauve la position du curseur dans la page
1122    }
1123
1124} // fin classe
1125
1126} // fin if
1127
1128?>
Note: See TracBrowser for help on using the repository browser.