diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..e69de29 diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..ad1b3a4 --- /dev/null +++ b/LICENCE @@ -0,0 +1,14 @@ +Plugin Facturation pour Garradin +Copyright (C) 2019 zou + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation version 3 of the License + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see \ No newline at end of file diff --git a/README.md b/README.md index 0136279..8bcd025 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,30 @@ -# Plugin reçus fiscaux pour Paheko +# Plugin reçus fiscaux pour Garradin -Plugin de reçus fiscaux pour le logiciel de gestion d'association [Paheko](https://paheko.cloud). +Plugin de reçus fiscaux pour le logiciel de gestion d'association Garradin ( https://garradin.eu/ - https://fossil.kd2.org/garradin ). +Source : https:// -## Installation +## Installation: +Vous pouvez télécharger l'archive .tar.gz depuis la page des [releases](https://), et la placer directement dans le dossier plugins de Garradin. -Télécharger la [version la plus récente](https://git.roflcopter.fr/lesanges/recusfiscaux/releases) au format tar.gz, supprimer le numéro de version du nom de l'archive et la copier dans le répertoire data/plugins de Paheko - -## Fonctionnalités -- Créer des reçus fiscaux pour les dons des membres - - reçu par activité et tarif : un, plusieurs ou tous - - reçu par personne : une, plusieurs ou tous +## Fonctionnalités : +- Créer des reçus fiscaux pour des dons et génération du cerfa correspondant + - reçu par type d'activité : 1, n ou tous + - reçu par tarif d'activité : 1, n ou tous + - reçu par personne : 1, n ou tous + - reçu par versement : 1, n ou tous - distinguer les différents taux de réduction - - génération des reçus au format PDF avec un générateur PDF externe - ou impression depuis le navigateur +- Créer des reçus sur des cotisations +- **Configuration** : + - Possibilité d'ajouter un numéro RNA et SIRET de l'association si elle en possède (apparait alors sur les documents) + - Modification du pied de page des documents (notament pour y inscrire des mentions légales) + - Informations relatives au cerfa pour les reçus fiscaux + - Image qui set de signature sur le cerfa -## Configuration -- association - - Objet (but) de l'association - - articles du CGI concernés par la réduction fiscale - - taux de réduction applicables -- responsable - - nom - - fonction - - signature (image) -- autres - - ville (précède la date sur le formulaire) - - paramétrage du numéro de reçu (préfixe quelconque, année fiscale, numéro de membre ou séquentiel) - - possibilité d'imprimer l'adresse de courriel - - choix et ordre des champs pour le nom et prénom (le libellé doit contenir le terme 'nom', casse indifférente) +Le plugin nécessite l'extension PHP mbstring. + +## Inclus les bibliothèques suivantes : + +- Composer : + https://getcomposer.org/ + Copyright (c) Nils Adermann, Jordi Boggiano, + Licence: MIT diff --git a/admin/action.php b/admin/action.php deleted file mode 100644 index f3aa4dc..0000000 --- a/admin/action.php +++ /dev/null @@ -1,219 +0,0 @@ -position - $b->position; -}); -$champsNom = array(); -foreach ($confNoms as $nom => $champ) -{ - if ($champ->position != 0) { $champsNom[] = $nom; } -} - -// membres donateurs -$_SESSION['membresDonateurs'] = Utils::getDonateurs($_SESSION['annee_recu'], - $champsNom); - -// comparaison de lignes de versements -// comparer 2 lignes selon le nom -function comparerNoms($ligne1, $ligne2) -{ - return - $_SESSION['membresDonateurs'][$ligne1->idUser]->rang - - - $_SESSION['membresDonateurs'][$ligne2->idUser]->rang; -} - -// comparer 2 activités par leur libellé -function comparerActivites($ligne1, $ligne2) -{ - return strcoll( - $_SESSION['lesActivites'][$_SESSION['lesTarifs'][$ligne1->idTarif]->idActivite]->label, - $_SESSION['lesActivites'][$_SESSION['lesTarifs'][$ligne2->idTarif]->idActivite]->label); -} - -// comparer 2 lignes selon la date -function comparerDate($ligne1, $ligne2) -{ - return - strtotime($ligne1->date) - strtotime($ligne2->date); -} - -// comparer 2 lignes selon un champ numérique entier -function comparerChamp($ligne1, $ligne2, $champ) -{ - return $ligne1->$champ - $ligne2->$champ; -} - -// ------------------------------------------------------------------------ -// fonctions pour l'affichage -// ------------------------------------------------------------------------ - -// afficher les informations d'une activité et d'un tarif -$tpl->register_function('afficher_debut_tarif', function ($params) -{ - $versement = $params['versement']; - $idTarif = $versement->idTarif; - - $out = sprintf(' -
- -
- ', - $idTarif, "total_general"); - if ($idTarif == 0) { - // versement sur un compte non rattaché à une activité - $out .= sprintf(' -
-
'; - return $out; -}); - -// Afficher les informations d'une personne -$tpl->register_function('afficher_debut_personne', function ($params) -{ - $idUser = $params['user']; - $idVersement = $params['idVersement']; - - $personne = $_SESSION['membresDonateurs'][$idUser]; - $out = sprintf(' -
- -
- - -
-
-
', - $idVersement, - $personne->nomPrenom, - "total_general" - ); - return $out; -}); - -// afficher infos compte -$tpl->register_function('afficher_debut_compte', function ($params) -{ - $idCompte = $params['idCompte']; - $out = sprintf(' -
-

Compte N° %1$s : %2$s

', - $_SESSION['comptes'][$idCompte]->codeCompte, - $_SESSION['comptes'][$idCompte]->nomCompte); - return $out; -}); - -// afficher un versement -$tpl->register_function('afficher_versement', function ($params) -{ - $versement = $params['versement']; - $idVersement = $params['idVersement']; - $rang = $params['rang']; - $pair = $params['pair']; - - $out = '
' : 'impair">'; - $out .= sprintf(' - - -
', - $idVersement, - $rang, - number_format( - $versement->versement/100, - 2, - ",", - " " - ), - date_format(date_create($versement->date),"d/m/Y"), - "total_general" - ); - return $out; -}); - -$tpl->register_function('fin_compte', function () -{ - $out = ' -
'; - return $out; -}); - -$tpl->register_function('fin_personne', function () -{ - $out = ' -
-
'; - return $out; -}); - -$tpl->register_function('fin_tarif', function ($params) -{ - $out = ' -
'; - return $out; -}); - -// ------------------------------------------------------------------------ -// aiguillage -// ------------------------------------------------------------------------ -unset($_SESSION['comptesSelectionnes']); -unset($_SESSION['tauxSelectionnes']); -unset($_SESSION['lesVersements']); - -if ($_GET['action'] == 'personne') { - require('versements_personnes.php'); -} else if ($_GET['action'] == 'activite') { - require('versements_activites.php'); -} diff --git a/admin/choix_annee.php b/admin/choix_annee.php deleted file mode 100644 index 4a95bdb..0000000 --- a/admin/choix_annee.php +++ /dev/null @@ -1,24 +0,0 @@ -runIf('change', function () { - $_SESSION['annee_recu'] = f('annee_recu'); -}, $csrf_key, PLUGIN_ROOT . '/admin/index.php'); - -$tpl->assign(compact('anneesFiscales', 'csrf_key')); - -$tpl->assign('annee_recu', $_SESSION['annee_recu']); - -$tpl->display(PLUGIN_ROOT . '/templates/choix_annee.tpl'); diff --git a/admin/config.php b/admin/config.php deleted file mode 100644 index a5d7c51..0000000 --- a/admin/config.php +++ /dev/null @@ -1,123 +0,0 @@ -requireAccess($session::SECTION_CONFIG, $session::ACCESS_ADMIN); - -// récupérer les champs des noms -$champsNom = Utils::getChampsNom($config, $plugin); - -$csrf_key = 'recusfiscaux_config'; - -$form->runIf('save', function () use ($plugin, $champsNom) { - // Objet de l'asso - $plugin->setConfigProperty('objet_asso', trim(f('objet_asso'))); - - // Articles du CGI - $confArticles = $plugin->getConfig('articlesCGI'); - // effacer l'ancienne configuration - for ($i = 0; $i < count($confArticles); ++$i) { - $confArticles[$i]->valeur = false; - } - // et copier la nouvelle - $art_sel = f('articlesCGI') ?: []; - foreach ($art_sel as $article) { - $confArticles[$article]->valeur = true; - } - $plugin->setConfigProperty('articlesCGI', $confArticles); - - // Taux de réduction - $confTaux = $plugin->getConfig('reduction'); - // effacer l'ancienne configuration - for ($i = 0; $i < count($confTaux); ++$i) { - $confTaux[$i]->valeur = false; - } - // et copier la nouvelle - $taux_sel = f('tauxReduction') ?: []; - foreach ($taux_sel as $taux) { - $confTaux[$taux]->valeur = true; - } - $plugin->setConfigProperty("reduction", $confTaux); - - // Informations au sujet du responsable - $plugin->setConfigProperty('nom_responsable', trim(f('nom_responsable') ?: '') ?: null); - $plugin->setConfigProperty('fonction_responsable', trim(f('fonction_responsable') ?: '') ?: null); - $plugin->setConfigProperty('ville_asso', trim(f('ville_asso') ?: '') ?: null); - - // signature - if (isset($_SESSION['sig_file']) && count($_SESSION['sig_file']) > 0) { - // supprimer la signature précédente, si besoin - if ( - null !== $plugin->getConfig('signature') - && - $plugin->getConfig('signature') != $_SESSION['sig_file'][0]->path - ) { - $sig_file = \Paheko\Files\Files::get($plugin->getConfig('signature')); - if (null !== $sig_file) { - $sig_file->delete(); - } - } - // puis installer la nouvelle - $plugin->setConfigProperty('signature', $_SESSION['sig_file'][0]->path); - } - - // Numérotation des reçus - $configNum = $plugin->getConfig('numerotation'); - $formNum = clone $configNum; - if ($configNum->prefixe != trim(f('prefixe'))) { - $formNum->prefixe = trim(f('prefixe')); - } - $formNum->annee = f('annee'); - $formNum->membre = f('membre'); - $formNum->sequentiel = f('sequentiel'); - $formNum->valeur_init = f('valeur_init'); - $plugin->setConfigProperty('numerotation', $formNum); - - // Impression des adresses de courriel - $plugin->setConfigProperty('imprimerCourriel', trim(f('imprimerCourriel') ?: '') ?: null); - - // champs pour le nom et prénom - foreach ($champsNom as $nom => $champ) { - $champ->position = 0; - } - $noms_sel = f('champsNom') ?: []; - $i = -count($noms_sel); - foreach ($noms_sel as $nom) { - $champsNom[$nom]->position = $i++; - } - $plugin->setConfigProperty('champsNom', $champsNom); - - // enregistrer la nouvelle config - $plugin->save(); -}, $csrf_key, PLUGIN_ADMIN_URL . 'config.php?ok'); - - -// test fonctions fichiers : voir files.sor -// $fichiers = Files::list('config'); -// error_log("fichiers config = " . print_r($fichiers, true)); -// $fichiers = Files::list('ext/recusfiscaux'); -// error_log("fichiers ext/recusfiscaux = " . print_r($fichiers, true)); -$sig_file = Files::get('ext/recusfiscaux/default_signature.png'); -// error_log("sig_file = " . print_r($sig_file, true)); - -//error_log("config.php::config=" . print_r($plugin->getConfig(), true)); - - -// trier les champs de nom pour l'affichage -uasort($champsNom, function ($a, $b) { - return $a->position - $b->position; -}); - -$path = qg('path') ?: File::CONTEXT_CONFIG; -$tpl->assign('default_signature', '/' . 'ext/recusfiscaux/default_signature.png'); -// $tpl->assign('default_signature', \Paheko\WWW_URL . "plugin/recusfiscaux/default_signature.png"); -$tpl->assign('plugin_config', $plugin->getConfig()); -$tpl->assign('plugin_css', ['style.css']); -$tpl->assign('numerotation', $plugin->getConfig('numerotation')); -$tpl->assign(compact('csrf_key', 'path', 'champsNom')); -$tpl->display(PLUGIN_ROOT . '/templates/config.tpl'); diff --git a/admin/generer_recus.php b/admin/generer_recus.php deleted file mode 100644 index 6fa83bf..0000000 --- a/admin/generer_recus.php +++ /dev/null @@ -1,473 +0,0 @@ -fileURL('signature')) ? - $config->fileURL('signature') : - ((null !== $plugin->getConfig('signature')) ? - \KD2\HTTP::getScheme() . '://' . \KD2\HTTP::getHost() . WWW_URI . $plugin->getConfig('signature') : - ""); - -// logo -$config = Config::getInstance(); -$logo_asso = - (null !== $config->fileURL('logo')) ? - $config->fileURL('logo') : - ""; - -// articles du CGI -$articlesCGI = array(); -foreach ($plugin->getConfig('articlesCGI') as $article) { - if ($article->valeur == 1) { - $articlesCGI[] = $article->titre; - } -} -$nbArticles = count($articlesCGI); -if ($nbArticles == 1) { - $texteArticles = 'à l’article ' . $articlesCGI[0]; -} elseif ($nbArticles > 1) { - $texteArticles = 'aux articles '; - for ($i = 0; $i < $nbArticles; ++$i) { - $texteArticles .= $articlesCGI[$i]; - if ($i < $nbArticles - 2) { - $texteArticles .= ", "; - } else if ($i == $nbArticles - 2) { - $texteArticles .= " et "; - } - } -} - -// libellés pour les taux de réduction -$libelles_taux = Utils::getLignesReduction($plugin->getConfig('reduction')); - -// numérotation des reçus -$configNum = $plugin->getConfig('numerotation'); - -// filtrer les versements sélectionnés -$lesLignes = f('selected'); -$versementsSelectionnes = array(); -foreach ($lesLignes as $ligne) { - $versementsSelectionnes[] = $_SESSION['lesVersements'][$ligne]; -} - -// cumuler les versements -if ($_GET['type'] == 'personne') { - $totalPersonnes = cumulerVersementsPersonne($versementsSelectionnes); -} elseif ($_GET['type'] == 'activite') { - $totalPersonnes = cumulerVersementsTarif($versementsSelectionnes); -} - -// générer les reçus -if ($_GET['format'] == 'pdf') { - genererRecusPDF($totalPersonnes, - $signature, - $logo_asso, - $texteArticles, - $plugin, - $configNum, - $libelles_taux - ); -} else if ($_GET['format'] == 'print') { - generererRecusHTML($tpl, - $totalPersonnes, - $signature, - $logo_asso, - $texteArticles, - $plugin, - $configNum, - $libelles_taux - ); -} else { - // Erreur : format inconnu ; ne devrait pas se produire -} - -function genererRecusPDF($totalPersonnes, - $signature, - $logo_asso, - $texteArticles, - $plugin, - $configNum, - $libelles_taux -) -{ - // - $fichierHTML = sprintf('%s/print-%s.html', CACHE_ROOT, md5(random_bytes(16))); - // - $listeFichiersPDF = array(); - $fmt = new \NumberFormatter('fr_FR', \NumberFormatter::SPELLOUT); - $prefixeNum = getNumPrefixe($configNum); - $numero_sequentiel = getNumSequentiel($configNum); - foreach ($totalPersonnes as $idPersonne => $personne) { - $tpl = new UserTemplate(null); - /* $tpl->setSource(PLUGIN_ROOT . '/templates/recu.skel'); */ - $tpl->setSourcePath(PLUGIN_ROOT . '/templates/recu.skel'); - - $tpl->assignArray(compact('signature', 'logo_asso', 'texteArticles')); - $tpl->assign('objet_asso', $plugin->getConfig('objet_asso')); - $tpl->assign('nom_responsable', $plugin->getConfig('nom_responsable')); - $tpl->assign('fonction_responsable', $plugin->getConfig('fonction_responsable')); - $tpl->assign('ville_asso', $plugin->getConfig('ville_asso')); - $tpl->assign('nom', $personne->nomPrenom); - $tpl->assign('adresse', $personne->adresse); - $tpl->assign('code_postal', $personne->codePostal); - $tpl->assign('ville', $personne->ville); - $tpl->assign('date', date("j/m/Y")); - - // numéro de reçu - $tpl->assign('numero', - faireNumeroRecu($prefixeNum, - $configNum->membre, - $personne->numero, - $numero_sequentiel)); - - // adresse de courriel - if ($plugin->getConfig('imprimerCourriel')) { - $courriel = $personne->courriel; - } else { - $courriel = ""; - } - $tpl->assign('courriel', $courriel); - - // les versements - $tpl->registerSection( - 'versements', - function () use ($personne, $libelles_taux, $fmt) { - foreach ($personne->versements as $taux => $versement) { - $ligne['montant'] = $versement->montant; - $ligne['euros'] = $fmt->format((int)($versement->montant / 100)); - if ($versement->montant % 100 != 0) { - $ligne['cents'] = $fmt->format($versement->montant % 100); - } else { - $ligne['cents'] = ""; - } - $ligne['libelle'] = $libelles_taux[$taux]; - $ligne['dateMin'] = date("d/m/Y", $versement->dateMin); - $ligne['dateMax'] = date("d/m/Y", $versement->dateMax); - yield $ligne; - } - } - ); - - // mentions complémentaires - $complements = mentionsComplémentaires(); - - $tpl->registerSection( - 'informations', - function () use ($complements) { - foreach ($complements as $elem) { - yield (array) $elem; - } - } - ); - - // - // récupérer les reçus au format html - $recuHTML = $tpl->fetch(); - // enregistrer dans le fichier - file_put_contents($fichierHTML, $recuHTML, FILE_APPEND); - // - - // fabriquer le fichier PDF - genererPDF($tpl->fetch(), $personne->nomPrenom, $listeFichiersPDF); - } - - // afficher dans un dialog - //marche pas - // printf(' - // - // - // - // - // ', $fichierHTML, "90%", "90%"); - - // affiche une page vide - // $link = ""; - // echo $link; - - // faire une archive zip - $fichierZip = Utils::makeArchive( - $listeFichiersPDF, - $_SESSION['annee_recu'], - PLUGIN_ROOT . "/zip" - ); - - //supprimer les fichiers pdf - foreach ($listeFichiersPDF as $f) { - \Paheko\Utils::safe_unlink($f); - } -} // genererRecusPDF - -function generererRecusHTML($tpl, - $totalPersonnes, - $signature, - $logo_asso, - $texteArticles, - $plugin, - $configNum, - $libelles_taux -) -{ - $tpl->register_function('afficher_numero_recu', function($params) - { - $prefixeNum = $params['prefixe']; - $membre = $params['membre']; - $numero_personne = $params['numero_personne']; - $numero_sequentiel = $params['numero_sequentiel']; - $numero = faireNumeroRecu($prefixeNum, - $membre, - $numero_personne, - $numero_sequentiel); - $out = sprintf(' -

Reçu %s

', - $numero - ); - return $out; - }); - - $tpl->assign(compact( - 'totalPersonnes', - 'logo_asso', - 'signature', - 'libelles_taux', - 'texteArticles' - )); - $tpl->assign('prefixeNum', getNumPrefixe($configNum)); - $tpl->assign('membre', $configNum->membre); - $tpl->assign('numero_sequentiel', getNumSequentiel($configNum)); - $tpl->assign('org_name', Config::getInstance()->get('org_name')); - $tpl->assign('org_address', Config::getInstance()->get('org_address')); - $tpl->assign('objet_asso', $plugin->getConfig('objet_asso')); - $tpl->assign('courriel', $plugin->getConfig('imprimerCourriel')); - $tpl->assign('complements', mentionsComplémentaires()); - $tpl->assign('ville_asso', $plugin->getConfig('ville_asso')); - $tpl->assign('date', date("j/m/Y")); - $tpl->assign('nom_responsable', $plugin->getConfig('nom_responsable')); - $tpl->assign('fonction_responsable', $plugin->getConfig('fonction_responsable')); - - $tpl->assign('plugin_css', ['previs_recu.css', 'imprimer_recu.css']); - $tpl->display(PLUGIN_ROOT . '/templates/recu_html.tpl'); -} // generererRecusHTML - -/** - * Cumuler les versements de chaque personne - * @param tableau des versements triés par idUser, date - * @return tableau des versements cumulés : id => Personne - */ -function cumulerVersementsPersonne($versements) -{ - $totalPersonnes = array(); - $idPersonneCourant = -1; - $dateMin = PHP_INT_MAX; - $dateMax = -1; - $totalVersements = 0; - foreach ($versements as $ligne) { - if ($ligne->idUser != $idPersonneCourant) { - // changement de personne - if ($idPersonneCourant != -1) { - $totalPersonnes[$idPersonneCourant]->ajouterVersement( - $_SESSION['taux_reduction'], - $totalVersements, - $dateMin, - $dateMax - ); - } - $dateMin = strtotime($ligne->date); - $dateMax = strtotime($ligne->date); - $idPersonneCourant = $ligne->idUser; - $totalVersements = $ligne->versement; - // créer les infos de la personne, sauf si elle est déjà présente - if (!array_key_exists($idPersonneCourant, $totalPersonnes)) { - $totalPersonnes[$idPersonneCourant] = $_SESSION['membresDonateurs'][$ligne->idUser]->clone(); - } - } else { - // même personne : cumuler versements et mettre à jour les dates - $totalVersements += $ligne->versement; - if (strtotime($ligne->date) < $dateMin) { - $dateMin = strtotime($ligne->date); - } - if (strtotime($ligne->date) > $dateMax) { - $dateMax = strtotime($ligne->date); - } - } - } - // et le dernier - $totalPersonnes[$idPersonneCourant]->ajouterVersement( - $_SESSION['taux_reduction'], - $totalVersements, - $dateMin, - $dateMax - ); - return $totalPersonnes; -} // cumulerVersementsPersonne - -/** - * Cumuler les versements de chaque personne par tarif - * @param tableau des versements triés par idTarif, idUser, date - * @return tableau des versements cumulés : id => Personne - */ -function cumulerVersementsTarif($versements) -{ - $totalPersonnes = array(); - $idTarifCourant = -1; - $idPersonneCourant = -1; - $idCompteCourant = -1; - $dateMin = PHP_INT_MAX; - $dateMax = -1; - $totalVersements = 0; - foreach ($versements as $ligne) { - if ( - $ligne->idTarif != $idTarifCourant || - $ligne->idUser != $idPersonneCourant || - $ligne->idCompte != $idCompteCourant - ) { - if ($idTarifCourant != -1) { - // changement de tarif, de personne ou de compte - $tarifCompte = ($idTarifCourant == 0) ? - $idCompteCourant : - $idTarifCourant . "_" . $idCompteCourant; - $totalPersonnes[$idPersonneCourant]->ajouterVersement( - $_SESSION['tauxSelectionnes'][$tarifCompte], - $totalVersements, - $dateMin, - $dateMax - ); - } - $dateMin = strtotime($ligne->date); - $dateMax = strtotime($ligne->date); - $idTarifCourant = $ligne->idTarif; - $idPersonneCourant = $ligne->idUser; - $idCompteCourant = $ligne->idCompte; - $totalVersements = $ligne->versement; - // créer les infos de la personne, sauf si elle est déjà présente - if (!array_key_exists($idPersonneCourant, $totalPersonnes)) { - $totalPersonnes[$idPersonneCourant] = $_SESSION['membresDonateurs'][$ligne->idUser]->clone(); - } - } else { - // même personne : cumuler versements et mettre à jour les dates - $totalVersements += $ligne->versement; - if (strtotime($ligne->date) < $dateMin) { - $dateMin = strtotime($ligne->date); - } - if (strtotime($ligne->date) > $dateMax) { - $dateMax = strtotime($ligne->date); - } - } - } - // et le dernier - $tarifCompte = ($idTarifCourant == 0) ? - $idCompteCourant : - $idTarifCourant . "_" . $idCompteCourant; - $totalPersonnes[$idPersonneCourant]->ajouterVersement( - $_SESSION['tauxSelectionnes'][$tarifCompte], - $totalVersements, - $dateMin, - $dateMax - ); - return $totalPersonnes; -} // cumulerVersementsTarif - -/** - * génère un fichier PDF à partir d'un document html - * ajoute son nom à la liste de fichiers - */ -function genererPDF($docHTML, $nomPersonne, &$listeFichiersPDF) -{ - // fabriquer le fichier PDF - $nomPDF = \Paheko\Utils::filePDF($docHTML); - // changer le nom du fichier - $nom = str_replace(' ', '_', $nomPersonne); - $nom = str_replace("'", "", $nom); - $nomFichier = sprintf( - '%s/recu_%s_%s.pdf', - dirname($nomPDF), - $_SESSION['annee_recu'], - $nom - ); - rename($nomPDF, $nomFichier); - // ajouter le nom du fichier à la liste pour mettre dans une archive - $listeFichiersPDF[] = $nomFichier; -} // genererPDF - -function faireNumeroRecu($prefixeNum, $membre, $numero, &$numero_sequentiel) -{ - if (isset($membre) && $membre) { - if ($prefixeNum != "") { - $prefixeNum .= "-"; - } - $prefixeNum .= $numero; - } - if ($numero_sequentiel) { - if ($prefixeNum != "") { - $prefixeNum .= "-"; - } - $prefixeNum .= $numero_sequentiel; - ++$numero_sequentiel; - } - return $prefixeNum; -} - -/** - * renvoyer le préfixe du numéro de reçu - */ -function getNumPrefixe($configNum) -{ - $prefixeNum = ""; - if (isset($configNum->prefixe) && $configNum->prefixe != "") { - $prefixeNum = $configNum->prefixe; - } - if (isset($configNum->annee) && $configNum->annee) { - if ($prefixeNum != "") { - $prefixeNum .= "-"; - } - $prefixeNum .= $_SESSION['annee_recu']; - } - return $prefixeNum; -} - -/** - * renvoyer le premier numéro de la numérotation séquentielle - * renvoie false si pas de numérotation séquentielle - */ -function getNumSequentiel($configNum) -{ - if (isset($configNum->sequentiel) && $configNum->sequentiel) { - if (isset($configNum->valeur_init) && $configNum->valeur_init != "") { - $numero_sequentiel = $configNum->valeur_init; - } else { - $numero_sequentiel = 1; - } - } - return isset($numero_sequentiel) ? $numero_sequentiel : false; -} - -function mentionsComplémentaires() -{ - $donnees = array( - 'Nature du don : ' => "Numéraire", - 'Mode de versement : ' => "chèque et/ou virement" - ); - $complements = array(); - foreach ($donnees as $titre => $libelle) { - $elem = new \stdClass(); - $elem->titre = $titre; - $elem->libelle = $libelle; - $complements[] = $elem; - } - return $complements; -} diff --git a/admin/imprimer_recu.css b/admin/imprimer_recu.css deleted file mode 100644 index 31778da..0000000 --- a/admin/imprimer_recu.css +++ /dev/null @@ -1,51 +0,0 @@ -/* - * impression - */ -@page -{ - size: A4 portrait; -} - -header.header { - display: none; -} - -body { - background: #fff; - padding: 0; - margin: 0; -} - -.noprint { - display: none; -} - -nav.tabs -{ - display: none; -} - -main { - margin: 0; -} - -div.previs_recu -{ - font-family: Serif; - font-size: 11pt; - background-color: white; - break-after: always; -} - -/* supprimer saut de page après dernier */ -div.previs_recu:last-of-type -{ - font-family: Serif; - font-size: 11pt; - background-color: white; - break-after: avoid; -} - -#__profiler { - display: none; -} diff --git a/admin/index.php b/admin/index.php deleted file mode 100644 index 3b1e76f..0000000 --- a/admin/index.php +++ /dev/null @@ -1,75 +0,0 @@ -needUpgrade()) { - $plugin->upgrade(); -} - -// Année fiscale par défaut -if (! isset($_SESSION['annee_recu']) || $_SESSION['annee_recu'] == "") -{ - $_SESSION['annee_recu'] = date("Y") - 1; -} - -// nombre de taux de réduction activés -$nbTaux = 0; -foreach ($plugin->getConfig('reduction') as $taux) -{ - if ($taux->valeur) { ++$nbTaux; } -} - -// idem avec les champs nom/prénom -$nbChamps = 0; -$champsNom = Utils::getChampsNom($config, $plugin); -if (null !== $champsNom) -{ - foreach ($champsNom as $nom => $champ) - { - if ($champ->position != 0) { ++$nbChamps; } - } -} - -// comptes sur lesquels des versements de membres ont été faits -// pendant l'année fiscale choisie -$_SESSION['comptes'] = Utils::getComptes($_SESSION['annee_recu'], 'like', '7%'); - -// liste des activités, tarifs et comptes associés -$activitesTarifsComptes = Utils::getTarifsComptes($_SESSION['annee_recu'], 'like', '7%'); -$_SESSION['lesTarifs'] = Utils::getTarifs(); -$_SESSION['lesActivites'] = Utils::getActivites(); - -// liste des comptes associés à aucune activité -$comptesSansActivite = array(); -foreach ($_SESSION['comptes'] as $id => $elem) -{ - $trouve = false; - foreach ($activitesTarifsComptes as $elem) - { - if ($id == $elem->idCompte) { $trouve = true ; break; } - } - if (! $trouve) { $comptesSansActivite[] = $id; } -} - -// préparation de l'affichage -$tpl->assign('annee_recu', $_SESSION['annee_recu']); -$tpl->assign('lesComptes', $_SESSION['comptes']); -$tpl->assign('lesTarifs', $_SESSION['lesTarifs']); -$tpl->assign('lesActivites', $_SESSION['lesActivites']); -$tpl->assign('activitesTarifsComptes', $activitesTarifsComptes); -$tpl->assign('comptesSansActivite', $comptesSansActivite); -$tpl->assign('nbComptesSansActivite',count($comptesSansActivite)); -$tpl->assign('nbTarifs', count($activitesTarifsComptes)); -$tpl->assign('nbComptes', count($_SESSION['comptes'])); -$tpl->assign('plugin_config', $plugin->getConfig()); -$tpl->assign('nbTaux', $nbTaux); -$tpl->assign('nbChamps', $nbChamps); -$tpl->assign('plugin_css', ['style.css']); -$tpl->assign('plugin_url', \Paheko\Utils::plugin_url()); - -// envoyer au template -$tpl->display(PLUGIN_ROOT . '/templates/index.tpl'); diff --git a/admin/previs_recu.css b/admin/previs_recu.css deleted file mode 100644 index 3605d4b..0000000 --- a/admin/previs_recu.css +++ /dev/null @@ -1,103 +0,0 @@ -/* - * prévisualisation reçu au format HTML - */ - -div.previs_recu -{ - width : 18.5cm; -} - -#logo -{ - max-height : 4cm; -} - -#titre -{ - margin : 0 2cm 0 2cm; - text-align : center; - font-size : 14pt; - font-weight: bold; -} - -#articles -{ - margin-bottom: 0.5cm; - text-align : center; -} - -#numRecu -{ - text-align : right; - margin-right: 1em; -} - -#versements -{ - border-top: 1px solid rgb(0, 0, 128); - border-bottom: 1px solid rgb(0, 0, 128); - padding-top: 1em; - margin-bottom: 1em; -} - -.cartouche -{ - padding-bottom: 1em; -} - -.rubrique -{ - background-color : rgb(200, 200, 250); - padding : 0 0 0 1mm; - margin-bottom: 1em; -} - -.titre, .important -{ - font-weight:bold; -} -.libelle -{ - font-weight: normal; -} - -#ville -{ - margin-bottom: 0; -} - -#signature -{ - display: block; - max-width : 7cm; - max-height : 4cm; - margin: 0 auto; - padding-bottom : 2mm; -} - -#versements > ul -{ - list-style: inside; - margin-left: 2em; -} - -#date_versements -{ - margin-left: 1.5em; -} - -p.complements -{ - margin-top : 1em; -} - -span.titre, span.libelle -{ - display : inline; -} - -/* Ne pas imprimer le bandeau des boutons du profiler */ -#__profiler -{ - display: none; -} diff --git a/admin/script.js b/admin/script.js deleted file mode 100644 index 8f68766..0000000 --- a/admin/script.js +++ /dev/null @@ -1,292 +0,0 @@ -"use strict"; - -/** - * renvoyer la valeur numérique d'un montant formaté en € - * @param texte qui représente nu nombre - */ -function getNumber(texte) { - return Number(texte.replace(/[^0-9,]/g, '').replace(/,/, '.')); -} - -/** - * afficher un montant au format monétaire - * @param montant à afficher - * @param idElem : élément où faire l'affichage - */ -function displayNumber(montant, idElem) { - idElem.innerHTML = - montant.toLocaleString('fr-FR', { - style: 'currency', currency: 'EUR', - minimumFractionDigits: 2 - }); -} - -// ------------------------------------------------------------------------ -// actions sur la liste des versements -// ------------------------------------------------------------------------ - -/** - * Fonction appelée quand on (dé)coche la case globale - * (dé)sélectionner toutes les cases de toutes les activités - * @param {HTMLInputElement} idCaseGlobale id de la case globale - * @param {HTMLSpanElement} idTotalGeneral id du total général - */ -function cocherDecocherTout(idCaseGlobale, idTotalGeneral) { - // itérer sur la liste des éléments détails : 1 par couple - let lesDetails = document.querySelectorAll("details.activite"); - for (let i = 0; i < lesDetails.length; ++i) { - let idCase = lesDetails[i].querySelector("input[type=checkbox]"); - idCase.checked = idCaseGlobale.checked; - cocherDecocherTarif(idCase, idTotalGeneral); - } - // changer le message - changerMessage(idCaseGlobale.nextElementSibling, idCaseGlobale); -} - -/** - * Fonction appelée quand on (dé)coche la case d'activité - * (dé)sélectionner toutes les cases de cette activité - * @param {HTMLInputElement} idCaseGlobale id de la case d'activité - * @param {HTMLSpanElement} idTotalGeneral id du total général - */ -function cocherDecocherTarif(idCaseGlobale, idTotalGeneral) { - let lesPersonnes = idCaseGlobale.closest("details").querySelectorAll("div.personne"); - cocherDecocherLesPersonnes(idCaseGlobale, lesPersonnes, idTotalGeneral); -} - -/** - * idem dans le cas des versements des personnes - * @param {HTMLInputElement} idCaseGlobale id case à cocher d'une personne - */ -function cocherDecocherToutesLesPersonnes(idCaseGlobale, idTotalGeneral) { - let lesPersonnes = document.querySelectorAll("summary.personne"); - cocherDecocherLesPersonnes(idCaseGlobale, lesPersonnes, idTotalGeneral); - changerMessage(idCaseGlobale.nextElementSibling, idCaseGlobale); -} - -/** - * @param {HTMLInputElement} idCaseGlobale - * @param {NodeListOf} lesPersonnes - * @param {HTMLSpanElement} idTotalGeneral id du total général -*/ -function cocherDecocherLesPersonnes(idCaseGlobale, lesPersonnes, idTotalGeneral) { - for (let j = 0; j < lesPersonnes.length; ++j) { - // trouver l'élément total de la personne - let idTotal = lesPersonnes[j].querySelector("span.total"); - // puis la case à cocher - let idCase = lesPersonnes[j].querySelector("input[type=checkbox]"); - idCase.checked = idCaseGlobale.checked; - // puis traiter toutes les cases de la personne - cocherDecocherPersonne(idCase, idTotal, idTotalGeneral); - } -} - -/** - * Fonction appelée quand on (dé)coche la case d'une personne - * - (dé)sélectionner toutes les cases à cocher - * - faire le total des cases cochées et l'afficher - * @param {HTMLInputElement} idCase id de la case qui a été cochée - * @param {HTMLSpanElement} idTotal id de l'élément où afficher le total de la personne - * @param {HTMLSpanElement} idTotalGeneral id de l'élément où afficher le total général - */ -function cocherDecocherPersonne(idCase, idTotal, idTotalGeneral) { - let conteneur = idCase.closest("details").querySelector("div.versements"); - let listeCases = conteneur.querySelectorAll("input[type=checkbox]"); - - for (let i = 0; i < listeCases.length; ++i) { - if (listeCases[i].checked != idCase.checked) { - listeCases[i].checked = idCase.checked; - cocherDecocherVersement(listeCases[i], idTotal, idTotalGeneral); - } - } -} - -/** - * Fonction appelée quand on (dé)coche la case d'un versement - * Mettre à jour le total des cases cochées et le total global et les afficher - * - * @param {HTMLInputElement} idCase id de la case qui a été (dé)cochée - * @param {HTMLSpanElement} idTotal id du total de la personne - * @param {HTMLSpanElement} idTotalGeneral id du total général - */ - -function cocherDecocherVersement(idCase, idTotal, idTotalGeneral) { - let div = idCase.closest("div"); - let idmontant = div.querySelector("span.montant"); - let montant = getNumber(idmontant.textContent); - let totalPersonne = getNumber(idTotal.textContent); - let totalGeneral = getNumber(idTotalGeneral.textContent); - if (idCase.checked) { - totalPersonne += montant; - totalGeneral += montant; - } else { - totalPersonne -= montant; - totalGeneral -= montant; - } - displayNumber(totalPersonne, idTotal); - displayNumber(totalGeneral, idTotalGeneral); -} - -/** - * changer le message en fonction de l'état coché de la case - * @param {Element} message - * @param {HTMLInputElement} idCase - */ -function changerMessage(message, idCase) { - if (idCase.checked) { - message.innerHTML = "Cliquer pour dé-cocher toutes les lignes"; - } else { - message.innerHTML = "Cliquer pour cocher toutes les lignes"; - } -} - -/** - * afficher/masquer les détails - * @param {string} idElem bouton de masquage/affichage - * @param {string} classe des détails à afficher/masquer - * @param {string} texte du bouton - */ -function montrerMasquerDetails(idElem, classe, texte) { - let lesDetails = document.querySelectorAll(classe); - if (lesDetails.length > 0) { - let leBouton = document.getElementById(idElem); - if (leBouton.textContent.includes('Replier')) { - // masquer - lesDetails.forEach((e) => { - e.removeAttribute('open'); - }); - leBouton.textContent = "Déplier " + texte; - leBouton.setAttribute('data-icon', '↓'); - } - else { - // montrer - lesDetails.forEach((e) => { - e.setAttribute('open', 'open'); - }); - leBouton.textContent = "Replier " + texte; - leBouton.setAttribute('data-icon', '↑'); - } - } -} - -/** - * fonction appelée lors de la demande de génération des reçus - * vérifier qu'au moins un versement a été sélectionné - * @return vrai si au moins un choix a été fait - * @param {HTMLFormElement} formulaire - */ -function verifierChoix(formulaire) { - return verifierCases(formulaire, 'checkbox', "au moins un versement"); -} - -// ------------------------------------------------------------------------ -// actions sur la page d'accueil -// ------------------------------------------------------------------------ - -/** - * positionner l'action déclenchée par l'envoi du formulaire - * afficher et masquer des portions de formulaire selon l'action - * @param {HTMLFormElement} formulaire - * @param {string} action après envoi du formulaire - * @param {any} idElem id de l'élément à afficher - * @param {any} nomClasse classe des éléments à masquer (sauf idElem) - */ -function choixMethodeGeneration(formulaire, action, idElem, nomClasse) { - formulaire.setAttribute('action', 'action.php?action=' + action); - for (let elem of formulaire.querySelectorAll(nomClasse)) { - if (elem.id == idElem) { - elem.classList.remove('hidden'); - } - else { - elem.classList.add('hidden'); - } - } -} - -/** - * vérifier - * - qu'au moins une activité/tarif est sélectionnée - * - qu'un radio de chaque activité/tarif sélectionné a été sélectionné :) - * @param conteneur des cases à vérifier - */ -function verifierActivitésTaux(conteneur) { - let nbChoix = 0; - // parcourir les cases à cocher - for (let idCase of conteneur.querySelectorAll("input[type=checkbox]")) { - if (idCase.checked) { - ++nbChoix; - // vérifier qu'un radio de la même ligne est sélectionné - let ligneCorrecte = false; - // trouver la ligne englobante - let ligne = idCase.closest("li"); - for (let idRadio of ligne.querySelectorAll('input[type=radio]')) { - if (idRadio.checked) { ligneCorrecte = true; break; } - } - if (!ligneCorrecte) { - alert("Erreur : il faut sélectionner un taux de réduction dans chaque ligne cochée"); - return false; - } - } - } - if (nbChoix == 0) { - alert("Erreur : il faut sélectionner au moins une ligne"); - } - return nbChoix != 0; -} - -/** - * vérifier qu'un taux a été sélectionné dans le conteneur paramètre - */ -function verifierTaux(conteneur) { - return verifierCases(conteneur, 'radio', "un taux de réduction"); -} - -// ------------------------------------------------------------------------ -// actions sur la config -// ------------------------------------------------------------------------ - -/** - * vérifier les données saisies dans le formulaire de configuration - */ -function verifierConfig(divArticles, divTauxReduc) { - // articles - if (!verifierCases(divArticles, "checkbox", "au moins un article")) { return false; } - - // taux de réduction - if (!verifierCases(divTauxReduc, "checkbox", "au moins un taux de réduction")) { return false; } - - // Nom, fonction, signature - - - // alert("Erreur : il faut sélectionner au moins un versement"); - return true; -} - -/** - * Vérifier qu'au moins une case est cochée dans le conteneur - * @param conteneur - * @param type de case à vérifier (radio, checkbox) - * @param message à afficher si erreur - */ -function verifierCases(conteneur, type, message) { - let selecteur = "input[type=" + type + "]"; - let listeCheck = conteneur.querySelectorAll(selecteur); - for (let elem of listeCheck) { - if (elem.checked) { return true; } - } - alert("Erreur : il faut sélectionner " + message); - return false; -} - -/** - * petite bidouille pour utiliser ma feuille de style pour imprimer les reçus - * à la place de la feuille de style de paheko - * @param {*} document - */ -function changerStyle(document) { - let styles = document.querySelectorAll('link[rel="stylesheet"]'); - for (let sheet of styles) { - if (sheet.href.includes('print.css')) { sheet.media = "tv"; } - if (sheet.href.includes('imprimer_recu.css')) { sheet.media = 'print'; } - } -} diff --git a/admin/style.css b/admin/style.css deleted file mode 100644 index 23b3976..0000000 --- a/admin/style.css +++ /dev/null @@ -1,178 +0,0 @@ -/* - * liste des versements - */ - -label.strong { - font-weight : bold; -} - -div.pair { - background-color: rgba(var(--gSecondColor), 0.15); -} - -fieldset.versements -{ - margin-bottom : 0; - margin-right : 0.5em; - -webkit-border-radius:8px; - border-radius:8px; -} - -div span { - padding-left : 0.5em; - padding-right : 0.5em; -} - -td.montant { - text-align : right; -} - -span.montant { - width : 5em; - text-align : right; -} - -span.total -{ - font-weight : bold; -} - -summary.activite -{ - margin-bottom : 0.5em; -} - -summary.personne -{ - margin-bottom : 0.5em; - padding-top : 0; - padding-bottom : 0; -} - -div.activite -{ - background-color: rgba(var(--gSecondColor), 0.3); -} - -div.personne -{ - font-weight : normal; - background-color: rgba(var(--gSecondColor), 0.25); -} - -h3.activite -{ - display : inline; -} - -p.activite -{ - margin-left : 2.5em; -} - -input.check_global -{ - margin : 0.2em 0.5em; -} - -div.versements -{ - margin-left : 4em; - display: flex; - flex-wrap: wrap; -} - -/* - * page d'accueil -*/ - -div.explications ul -{ - list-style : initial; -} - -dl#menu -{ - min-width : 40em; - width : 50%; -} - -/* - * configuration - */ - -#signature -{ - padding : 1em 0.5em 0 0.5em; - max-width: 300px; - max-height: 120px; -} - -div.actions -{ - display : inline; -} - -a.icn-btn { - font-family: "paheko", sans-serif; - font-size : 1.2em; -} - -dl.config -{ - padding-bottom : 1ex; - padding-right : 1em; -} - -div#articles_cgi, div#config_nom_fonction, div#numero_recus -{ - display: flex; -} - -div.article -{ - margin-right : 3em; -} -/* -div#config_nom_fonction -{ - display: flex; -} - -div#numero_recus -{ - display:flex; -} -*/ -div.champnom -{ - display : flex; - margin-top : 0.25rem; -} -div.infos -{ - border : 1px solid rgba(var(--gMainColor)); - border-radius : 0.25rem; - padding : 0.4rem; - margin-left : 1em; - width : 20em; -} -ul#liste_activites dd -{ - display: inline-block; -} - -ul.reduction span.radio-btn -{ - margin-left : 2em; - border-spacing : 0.1em; -} -input#f_prefixe -{ - width : 8em; - min-width : 8em; -} -input#f_valeur_init -{ - max-width: 4em; -} diff --git a/admin/upload.php b/admin/upload.php deleted file mode 100644 index fca8872..0000000 --- a/admin/upload.php +++ /dev/null @@ -1,24 +0,0 @@ -runIf('upload', function () use ($parent, $session) { - $_SESSION['sig_file'] = \Paheko\Files\Files::uploadMultiple($parent, 'file', $session); -}, $csrf_key, PLUGIN_ROOT . '/admin/config.php'); - -$tpl->assign(compact('parent', 'csrf_key')); - -$tpl->display(PLUGIN_ROOT . '/templates/upload.tpl'); diff --git a/admin/versements_activites.php b/admin/versements_activites.php deleted file mode 100644 index 56114bf..0000000 --- a/admin/versements_activites.php +++ /dev/null @@ -1,98 +0,0 @@ - - strpos($elem, '_') !== false ? substr($elem, 0, strpos($elem, '_')) : "", - $tarifsSelectionnes); -$lesComptes = array_map(fn($elem) : string => - strpos($elem, '_') !== false ? substr($elem, 1 + strpos($elem, '_')) : "", - $tarifsSelectionnes); - -# versements des tarifs sélectionnées et de leur compte associé -if (count($lesTarifs) != 0) -{ - $_SESSION['lesVersements'] = - Utils::getVersementsTarifsComptes( - $_SESSION['annee_recu'], - $lesTarifs, - $lesComptes, - $champsNom); - // error_log("lesVersements=" . print_r($_SESSION['lesVersements'], true)); -} - -// ajouter les versements sans tarif (tri par nom, compte, date) -if (isset($_SESSION['comptesSelectionnes'])) -{ - $versementsSansTarif = Utils::getVersementsComptes($_SESSION['annee_recu'], - $_SESSION['comptesSelectionnes'], - $champsNom); - foreach ($versementsSansTarif as $versement) - { - $_SESSION['lesVersements'][] = $versement; - } -} -//error_log("lesVersements=" . print_r($_SESSION['lesVersements'], true)); - -// préparation de l'affichage -$tpl->assign('lesVersements', $_SESSION['lesVersements']); -$tpl->assign('annee_recu', $_SESSION['annee_recu']); -$tpl->assign('plugin_css', ['style.css']); - -// envoyer au template -$tpl->display(PLUGIN_ROOT . '/templates/versements_activites.tpl'); - diff --git a/admin/versements_personnes.php b/admin/versements_personnes.php deleted file mode 100644 index 8db95e8..0000000 --- a/admin/versements_personnes.php +++ /dev/null @@ -1,34 +0,0 @@ -assign('lesVersements', $_SESSION['lesVersements']); -$tpl->assign('annee_recu', $_SESSION['annee_recu']); -$tpl->assign('plugin_css', ['style.css']); - -// envoyer au template -$tpl->assign('plugin_config', $plugin->getConfig()); -$tpl->display(PLUGIN_ROOT . '/templates/versements_personnes.tpl'); diff --git a/config.json b/config.json index 622936c..59b6ce7 100644 --- a/config.json +++ b/config.json @@ -1,38 +1,28 @@ { "articlesCGI" : [ { - "titre" : "200", - "valeur" : false + "titre" : "Article 200", + "valeur" : 0 }, { - "titre" : "238 bis", - "valeur" : false + "titre" : "Article 228 bis", + "valeur" : 0 }, { - "titre" : "978", - "valeur" : false + "titre" : "Article 978", + "valeur" : 0 } ], "reduction" : [ { "taux" : "normal", "ligne" : "UF", - "remarque" : "", - "valeur" : false + "remarque" : "" }, { "taux" : "majoré", "ligne" : "UD", - "remarque" : "aide aux personnes en difficulté", - "valeur" : false + "remarque" : "aide aux personnes en difficulté" } - ], - "numerotation" : { - "prefixe" : "", - "annee" : false, - "membre" : false, - "sequentiel" : false, - "valeur_init": 1 - }, - "imprimerCourriel" : false + ] } diff --git a/data/default_signature.png b/data/default_signature.png index 4ea7ecf..38aa69f 100644 Binary files a/data/default_signature.png and b/data/default_signature.png differ diff --git a/garradin_plugin.ini b/garradin_plugin.ini new file mode 100644 index 0000000..1660303 --- /dev/null +++ b/garradin_plugin.ini @@ -0,0 +1,8 @@ +nom="Reçus fiscaux" +description="Génération de reçus fiscaux pour les dons des membres" +auteur="jce" +url="https://git.roflcopter.fr/lesanges/recus-fiscaux-garradin" +version="0.1" +menu=1 +config=1 +min_version="1.1" diff --git a/install.php b/install.php index c9b26e3..3405396 100644 --- a/install.php +++ b/install.php @@ -1,23 +1,20 @@ get('name'); -const SIGNATURE_DEFAUT = 'default_signature.png'; -const CONFIG_INIT = 'config.json'; +//$db->import(dirname(__FILE__) . "/data/schema.sql"); -// configuration initiale -$config_init = json_decode(file_get_contents(Plugins::getPath($nom_plugin) . '/' . CONFIG_INIT), - true); +/* +$plugin->setConfig('footer', "[EXEMPLE]\n". + "Association exonérée des impôts commerciaux\n". + "En cas de retard de paiement, indemnité forfaitaire légale pour frais de recouvrement : 40,00 €\n". + "[Coordonnées bancaires]\n". + "Association enregistrée en préfecture de XXX au numéro YYY" + ); +$plugin->setConfig('validate_cp', true); -// enregistrer dans la config du plugin -foreach ($config_init as $cle => $valeur) { - $plugin->setConfigProperty($cle, $valeur); -} -$plugin->save(); - -// « signature » par défaut à remplacer (voir l'onglet de configuration) -$path = __DIR__ . '/data/' . SIGNATURE_DEFAUT; -$default_signature_file = Files::createFromPath('ext/' . $nom_plugin . '/' . SIGNATURE_DEFAUT, - $path); +$path = __DIR__.'/data/default_signature.png'; +$png = (new File)->createAndStore('skel/plugin/recusDons','signature.png', $path, null); +*/ diff --git a/lib/Activite.php b/lib/Activite.php new file mode 100644 index 0000000..d59c950 --- /dev/null +++ b/lib/Activite.php @@ -0,0 +1,33 @@ +id = $id; + $this->label = $label; + $this->description = $description; + } + + /* + * @return instance de Activite initialisée avec l'objet o + */ + public static function copier($o) + { + return new Activite( + $o->id, + $o->label, + $o->description); + } +} diff --git a/lib/Personne.php b/lib/Personne.php index 1f639af..118aca1 100644 --- a/lib/Personne.php +++ b/lib/Personne.php @@ -1,6 +1,6 @@ id = $id; - $this->numero = $numero; - $this->courriel = $courriel; - $this->rang = $rang; $this->nomPrenom = $nomPrenom; $this->adresse = $adresse; $this->codePostal = $codePostal; $this->ville = $ville; - $this->versements = array(); // clé = tarif, valeur = Versement + $this->courriel = $courriel; + $this->versements = array(); } /** @@ -46,37 +40,33 @@ class Personne { return new Personne( $this->id, - $this->numero, - $this->courriel, - $this->rang, $this->nomPrenom, $this->adresse, $this->codePostal, - $this->ville); + $this->ville, + $this->courriel); } /** * ajouter un versement - * @param $tauxReduction + * @param $idActivite + * @param $idTarif * @param $montant - * @param $dateMin - * @param $dateMax + * @param $tauxReduction */ public function ajouterVersement( - $tauxReduction, + $idActivite, + $idTarif, $montant, - $dateMin, - $dateMax + $tauxReduction ) { - if (array_key_exists($tauxReduction, $this->versements)) - { - $this->versements[$tauxReduction]->ajouter($montant, $dateMin, $dateMax); - } - else - { - $this->versements[$tauxReduction] = new Versement($montant, $dateMin, $dateMax); - } + $this->versements[] = + new Versement( + $idActivite, + $idTarif, + $montant, + $tauxReduction + ); } - } diff --git a/lib/RecusHTML.php b/lib/RecusHTML.php new file mode 100644 index 0000000..b5db027 --- /dev/null +++ b/lib/RecusHTML.php @@ -0,0 +1,151 @@ +nomAsso = $nomAsso; + $this->adresseAsso = $adresseAsso; + $this->objetAsso = $objetAsso; + $this->signature = $signature; + $this->html = $this->entete(); + } + + function get() + { + //echo $this->html; + return $this->html; + } + + // imprimer le reçu + function imprimer_recu($annee_recu, + $numero, + $nom, + $lesMontants, + $adresse, + $code_postal, + $ville) + { + ob_start(); +echo << +

Reçu numéro {$annee_recu}/{$numero}

+ + +
+

Bénéficiaire des versements

+

Association « {$this->nomAsso} »

+

{$this->adresseAsso}

+

Objet : {$this->objetAsso}

+
+ +
+

Donateur

+

{$nom}

+

{$adresse}

+

{$code_postal} {$ville}

+
+ +
+

Le bénéficiaire reconnaît avoir reçu au titre des dons et versements ouvrant droit à réduction d'impôt :

+
    + +FDD; + + foreach ($lesMontants as $taux => $montant) + { + $this->imprimer_montant("la somme de ", + $montant, + Utils::getLigneReduction($taux)); + } + echo "
\n"; + + $this->imprimer_description("Date des versements :", + "année {$annee_recu}"); +echo <<Le bénéficiaire certifie sur l’honneur que les dons et versements qu’il reçoit ouvrent droit à la réduction d'impôt prévue à l’article 200 du CGI

+ +FDD; + $this->imprimer_description("Forme du don : ", + "Autre"); + $this->imprimer_description("Nature du don : ", + "Numéraire"); + $this->imprimer_description("Mode de versement : ", + "chèque et/ou virement"); + echo "
\n"; + + // cartouche final + $date = date("j/m/Y"); +echo << +

Rennes le {$date}

+ +

Président

+ + + +FDD; + $this->html .= ob_get_clean(); + } + + // imprimer un libellé précédé de son titre en gras + function imprimer_description($titre, $libelle) + { + echo <<{$titre} {$libelle}

+ +FDD; + } + + // imprimer le montant de la réduction et un libellé + function imprimer_montant($texte, $montant, $libelle = "") + { + $valeur = number_format($montant, 2, ',', ''); + echo "
  • {$texte} {$valeur} euros"; + if ($libelle != "") { + echo " : {$libelle}"; + } + echo "
  • \n"; + } + + protected function entete() + { + $styleSheet = \Garradin\PLUGIN_ROOT . "/lib/pdf.css"; + ob_start(); +echo << + + + + + + +
    +
    + Cerfa +
    +
    + N° 11580*4 +
    +

    Reçu au titre des dons à certains organismes d'intérêt général

    +

    Article 200, 238 bis et 885-0 V bis A du code général des impôts

    + +FDD; + return ob_get_clean(); + } +} diff --git a/lib/RecusPDF.php b/lib/RecusPDF.php new file mode 100644 index 0000000..9891cc2 --- /dev/null +++ b/lib/RecusPDF.php @@ -0,0 +1,196 @@ +AddFont($family, + '', + $family.$style.".ttf", + true); + // bold + $this->AddFont($family, + 'B', + $family.$style."-Bold.ttf", + true); + $this->nomAsso = $nomAsso; + $this->adresseAsso = $adresseAsso; + $this->logoCerfa = $logo; + $this->signature = $signature; + } + + // Header + function Header() + { + parent::Header(); + // Logo + $this->Image($this->logoCerfa, 10, 6, 30); + + // document title + $this->SetTextColor(0, 0, 0); + $this->SetFont('DejaVu','B',12); + $titre = "Reçu au titre des dons à certains organismes d'intérêt général"; + $this->SetXY(50, 10); + // Titre + $this->MultiCell(100, + 6, + $titre, + 0, + 'C'); + + // numéro de Cerfa + $cerfa = "N° 11580*3"; + $this->SetFont('DejaVu', 'B', 10); + $this->SetXY(10, 25); + $this->Cell(100, 0, $cerfa); + + // Articles + $this->SetFont('DejaVu', '', 9); + $this->SetXY(50, 25); + $this->Cell(100, 0, 'Article 200, 238 bis et 885-0 V bis A du code général des impôts'); + } + + // imprimer les informations du bénéficiaire + function imprimer_beneficiaire($nom, $adresse) + { + $this->titre_rubrique("Bénéficiaire des versements"); + $this->SetFont('DejaVu', 'B', 11); + $this->Cell(0, 6, 'Association « ' . $nom . ' »', 'LR', 1); + $this->Cell(0, 6, str_replace(array("\r\n", "\n", "\r"), " ", $adresse), 'LR', 1); + $this->imprimer_description("Objet : ", + "célébrer le culte protestant évangélique et pourvoir aux frais et besoins de ce culte."); + $this->Cell(0, 6, "", 'LRB', 1); + } + + // imprimer un libellé précédé de son titre en gras + function imprimer_description($titre, $libelle) + { + $this->SetFont('DejaVu', 'B', 11); + $this->Cell($this->GetStringWidth($titre), 6, $titre, 'L', 0); + $this->SetFont('DejaVu', '', 11); + $this->Cell(0, 6, $libelle, 'R', 1); + } + + // imprimer le montant de la réduction et un libellé + function imprimer_montant($texte, $montant, $libelle = "") + { + $this->SetFont('DejaVu'); + $this->Cell($this->GetStringWidth($texte), + 6, + $texte, + 'L', + 0); + $this->SetFont('DejaVu','B'); + $valeur = number_format($montant, 2, "," , "") . " euros"; + $this->Cell($this->GetStringWidth($valeur), + 6, + $valeur, + '', + 0); + $this->SetFont('DejaVu'); + if ($libelle != "") + { + $this->Cell(0, + 6, + " : " . $libelle, + 'R', + 1); + } + else + { + $this->Cell(0, 6, "", 'R', 1); + } + } + + function titre_rubrique($texte) + { + $this->SetFont('DejaVu','B',12); + $largeur_texte = $this->GetStringWidth($texte); + $this->setX(10); + $this->SetFillColor(0, 255, 255); + $this->Cell(0, 6, $texte, 'LTR', 1, 'C', true); + $this->Cell(0, 6, "", 'LR', 1); + } + + // imprimer le reçu + function imprimer_recu($annee_recu, + $numero, + $nom, + $lesMontants, + $adresse, + $code_postal, + $ville) + { + + $this->AddPage(); + // Numéro de reçu + $this->SetFont('DejaVu', 'B', 11); + $this->MultiCell(0, 20, 'Reçu numéro ' . $annee_recu . '/' . $numero); + + // bénéficiaire + $this->imprimer_beneficiaire($this->nomAsso, $this->adresseAsso); + + // donateur + $this->Ln(10); + $this->titre_rubrique("Donateur"); + $this->SetFont('DejaVu', 'B', 11); + $this->Cell(0, 6, $nom, 'LR', 1); + $this->Cell(0, 6, $adresse, 'LR', 1); + $this->Cell(0, 6, $code_postal . " " . $ville, 'LR', 1); + $this->Cell(0, 6, "", 'LRB', 1); + + // Montant et autres informations + $this->Ln(10); + $this->SetFont('DejaVu', '', 11); + $this->Cell(0, + 6, + "Le bénéficiaire reconnaît avoir reçu au titre des dons et versements ouvrant droit à réduction d'impôt :", + 'LTR', + 1); + foreach ($lesMontants as $taux => $montant) + { + $this->imprimer_montant(" - la somme de ", $montant, Utils::getLigneReduction($taux)); + } + $this->Cell(0, 3, "", 'LR', 1); + $this->imprimer_description('Date des versements : ', + 'année ' . $annee_recu); + $this->Cell(0, 3, "", 'LR', 1); + $this->MultiCell(0, 6, + "Le bénéficiaire certifie sur l’honneur que les dons et versements qu’il reçoit ouvrent droit à la réduction +d'impôt prévue à l’article 200 du CGI", + 'LR'); + $this->Cell(0, 3, "", 'LR', 1); + $this->imprimer_description("Forme du don : ", "Autre"); + $this->Cell(0, 3, "", 'LR', 1); + $this->imprimer_description("Nature du don : ", "Numéraire"); + $this->Cell(0, 3, "", 'LR', 1); + $this->imprimer_description("Mode de versement : ", "chèque et/ou virement"); + $this->Cell(0, 0, "", 'LRB', 1); + + // cartouche final + $this->Ln(10); + $this->Cell(0, 6, "", 'LRT', 1); + $this->Cell(0, 6, "Rennes le " . date("j/m/Y"), 'LR', 1, 'R'); + $this->Cell(0, 36, "", 'LR', 1); + $this->Cell(0, 0, "", 'LBR', 1); + $this->SetXY(100, 220); + $this->Image($this->signature, null, null, 50); + } +} // class RecusPDF diff --git a/lib/Tarif.php b/lib/Tarif.php new file mode 100644 index 0000000..997b79b --- /dev/null +++ b/lib/Tarif.php @@ -0,0 +1,41 @@ +id = $id; + $this->idActivite = $idActivite; + $this->label = $label; + $this->description = $description; + $this->montant = $montant; + } + + /* + * @return instance de Tarif initialisée avec l'objet o + */ + public static function copier($o) + { + return new Tarif( + $o->id, + $o->idActivite, + $o->label, + $o->description, + $o->montant); + } +} diff --git a/lib/Utils.php b/lib/Utils.php index 343616f..8e7c9a8 100644 --- a/lib/Utils.php +++ b/lib/Utils.php @@ -1,465 +1,239 @@ getGrouped($sql); - } + /** + * @return tarifs demandés + * @param array $tarifs + */ + public static function getTarifs($tarifs) + { + $db = DB::getInstance(); + $sql = sprintf( + 'SELECT id, id_service as idActivite, label, description, amount as montant + FROM services_fees + WHERE services_fees.%s', + $db->where('id', $tarifs)); + return $db->get($sql); + } - /** - * @return informations sur les activités - */ - public static function getActivites() - { - $db = DB::getInstance(); - $sql = sprintf( - 'SELECT - services.id, - services.label, - services.description - FROM services'); - return $db->getGrouped($sql); - } + /** + * @return activités correspondant aux tarifs demandés + * @param array $tarifs + */ + public static function getActivites($tarifs) + { + $db = DB::getInstance(); + $sql = sprintf( + 'SELECT services.id, services.label, services.description + FROM services + LEFT JOIN services_fees ON services_fees.id_service = services.id + WHERE services_fees.%s + GROUP BY services.id', + $db->where('id', $tarifs)); + return $db->get($sql); + } - /** - * @return comptes sur lesquels des versements de membres ont été faits - * @param string $annee - * @param $op : opérateur de combinaison des comptes - * @param array $comptes - */ - public static function getComptes($annee, $op, $comptes) - { - $db = DB::getInstance(); - $sql = sprintf( - 'SELECT - acc_accounts.id, - acc_years.label, - acc_accounts.code as codeCompte, - acc_accounts.label as nomCompte - FROM acc_transactions_users - INNER JOIN users - ON acc_transactions_users.id_user = users.id - INNER JOIN acc_transactions - ON acc_transactions_users.id_transaction = acc_transactions.id - INNER JOIN acc_transactions_lines - ON acc_transactions_lines.id_transaction = acc_transactions.id - INNER JOIN acc_accounts - ON acc_transactions_lines.id_account = acc_accounts.id - INNER JOIN acc_years - ON acc_transactions.id_year = acc_years.id - WHERE - (strftime("%%Y", acc_transactions.date) = "%d" - AND - acc_accounts.%s - ) - GROUP by acc_accounts.id - ORDER by acc_accounts.code', - $annee, - $db->where('code', $op, $comptes) - ); - return $db->getGrouped($sql); - } + /** + * @return versements correspondants à l'année et aux tarifs donnés + * @param $annee + * @param array $tarifs + */ + public static function getVersementsTarifs($annee, $tarifs) + { + $db = DB::getInstance(); + $sql = sprintf( + 'SELECT + services_fees.id as idTarif, + membres.id as idUser, + acc_transactions_lines.credit as versement, + acc_transactions.date + FROM acc_transactions_users + INNER JOIN membres on acc_transactions_users.id_user = membres.id + INNER JOIN acc_transactions on acc_transactions_users.id_transaction = acc_transactions.id + INNER JOIN services_users on acc_transactions_users.id_service_user = services_users.id + INNER JOIN services_fees on services_users.id_fee = services_fees.id + INNER JOIN acc_transactions_lines on acc_transactions_lines.id_transaction = acc_transactions.id + WHERE + (strftime(%s, acc_transactions.date) = "%d" + AND + services_fees.%s + AND + acc_transactions_lines.credit > 0) + ORDER by services_fees.id, membres.nom, acc_transactions.date', + '"%Y"', + $annee, + $db->where('id', $tarifs)); + return $db->get($sql); + } - /** - * @return tarifs des activités et comptes ayant des versements de - * membres dans l'année - * @param string $annee - * @param $op : opérateur de combinaison des comptes - * @param array $comptes - */ - public static function getTarifsComptes($annee, $op, $comptes) - { - $db = DB::getInstance(); - $sql = sprintf( - ' - SELECT - services_users.id_fee as idTarif, - acc_accounts.id as idCompte, - acc_accounts.code as codeCompte - FROM acc_transactions_users - INNER JOIN acc_transactions - ON acc_transactions_users.id_transaction = acc_transactions.id - INNER JOIN services_users - ON acc_transactions_users.id_service_user = services_users.id - INNER JOIN services_fees - ON services_users.id_fee = services_fees.id - INNER JOIN acc_transactions_lines - ON acc_transactions_lines.id_transaction = acc_transactions.id - INNER JOIN acc_accounts - ON acc_transactions_lines.id_account = acc_accounts.id - WHERE - (strftime("%%Y", acc_transactions.date) = "%d" - AND - acc_accounts.%s - ) - GROUP BY services_fees.id, acc_accounts.code - ORDER BY acc_accounts.code - ', - $annee, - $db->where('code', $op, $comptes) - ); - return $db->get($sql); - } + /** + * Versements totaux par personne pour une année donnée + * @param année + */ + public static function getVersementsTotaux($annee) + { + $sql = + "SELECT + membres.id as idUser, + sum(acc_transactions_lines.credit) AS versement + FROM + acc_transactions_users, + membres, + acc_transactions + INNER JOIN acc_transactions_lines + ON acc_transactions_lines.id_transaction = acc_transactions.id + WHERE ( + strftime('%Y', acc_transactions.date) = ? + AND + acc_transactions_lines.credit > 0 + AND + acc_transactions_users.id_transaction = acc_transactions.id + AND + acc_transactions_users.id_user = membres.id + ) + GROUP by acc_transactions_users.id_user + ORDER by membres.nom COLLATE U_NOCASE; + "; + return DB::getInstance()->get($sql, $annee); + } - /** - * @return versements correspondants à l'année donnée - * @param $annee - * @param array $champsNom : liste non vide des champs de nom/prénom - */ - public static function getVersementsPersonnes($annee, $op, $comptes, $champsNom) - { - $db = DB::getInstance(); - $tri = Utils::combinerTri($champsNom); - $sql = sprintf( - 'SELECT - users.id as idUser, - acc_accounts.id as idCompte, - acc_accounts.code as codeCompte, - acc_transactions_lines.credit as versement, - acc_transactions.date - FROM acc_transactions_users - INNER JOIN users - ON acc_transactions_users.id_user = users.id - INNER JOIN acc_transactions - ON acc_transactions_users.id_transaction = acc_transactions.id - INNER JOIN acc_transactions_lines - ON acc_transactions_lines.id_transaction = acc_transactions.id - INNER JOIN acc_accounts - ON acc_transactions_lines.id_account = acc_accounts.id - WHERE - (strftime("%%Y", acc_transactions.date) = "%d" - AND - acc_accounts.%s - ) - GROUP BY acc_transactions.id, acc_accounts.id - ORDER by %s, acc_accounts.code, acc_transactions.date', - $annee, - $db->where('code', $op, $comptes), - $tri - ); - return $db->get($sql); - } + /** + * @return personnes ayant versé des dons pour une année donnée + * @param $annee + */ + public static function getDonateurs($annee) + { + $sql = + "SELECT + membres.id as idUser, + membres.nom as nom, + membres.adresse as adresse, + membres.code_postal as codePostal, + membres.ville as ville + FROM + acc_transactions_users, + membres, + acc_transactions + INNER JOIN acc_transactions_lines + ON acc_transactions_lines.id_transaction = acc_transactions.id + WHERE ( + strftime('%Y', acc_transactions.date) = ? + AND + acc_transactions_lines.credit > 0 + AND + acc_transactions_users.id_transaction = acc_transactions.id + AND + acc_transactions_users.id_user = membres.id + ) + GROUP by membres.id + ORDER by membres.nom COLLATE U_NOCASE; + "; + return DB::getInstance()->get($sql, $annee); + } - /** - * @return versements correspondants à : - * @param $annee : année fiscale - * @param array $tarifs : tarifs sélectionnés - * @param array $comptes : comptes associés aux tarifs - * @param array $champsNom : liste non vide des champs de nom/prénom - * @remarks tri par tarif, nom, compte, date - */ - public static function getVersementsTarifsComptes($annee, - $tarifs, - $comptes, - $champsNom) - { - $db = DB::getInstance(); - $tri = Utils::combinerTri($champsNom); - $condition = Utils::combinerTarifsComptes($tarifs, $comptes); - $sql = sprintf( - 'SELECT - services_fees.id as idTarif, - acc_accounts.id as idCompte, - acc_accounts.code as codeCompte, - users.id as idUser, - acc_transactions_lines.credit as versement, - acc_transactions.date - FROM acc_transactions_users - INNER JOIN users - ON acc_transactions_users.id_user = users.id - INNER JOIN acc_transactions - ON acc_transactions_users.id_transaction = acc_transactions.id - INNER JOIN services_users - ON acc_transactions_users.id_service_user = services_users.id - INNER JOIN services_fees - ON services_users.id_fee = services_fees.id - INNER JOIN acc_transactions_lines - ON acc_transactions_lines.id_transaction = acc_transactions.id - INNER JOIN acc_accounts - ON acc_transactions_lines.id_account = acc_accounts.id - WHERE - (strftime("%%Y", acc_transactions.date) = "%d" - AND - %s - ) - GROUP BY acc_transactions.id, acc_accounts.id - ORDER by %s, acc_accounts.code, acc_transactions.date', - $annee, - $condition, - $tri - ); - // error_log("\ngetVersementsTarifsComptes : sql=" . $sql); - return $db->get($sql); - } + public static function getLignesReduction($lesTaux) + { + foreach ($lesTaux as $elem) + { + $ligne = "taux " . $elem->taux . ", ligne " . $elem->ligne; + if ($elem->remarque != "") { + $ligne .= ", " . $elem->remarque; + } + $lignes[$elem->taux] = $ligne; + } + return $lignes; + } + public static function getLigneReduction($taux) + { + return $_SESSION['ligneReduction'][$taux]; + } - /** - * @return versements correspondants à : - * @param $annee année fiscale - * @param $comptesIsoles comptes NON associés à un tarif - * @param array $champsNom : liste non vide des champs de nom/prénom - * @remarks tri par nom, compte, date - */ - public static function getVersementsComptes($annee, - $comptesIsoles, - $champsNom) - { - $db = DB::getInstance(); - $tri = Utils::combinerTri($champsNom); - $sql = sprintf( - ' - SELECT - 0 as idTarif, - acc_accounts.id as idCompte, - acc_accounts.code as codeCompte, - users.id as idUser, - acc_transactions_lines.credit as versement, - acc_transactions.date - FROM acc_transactions_users - INNER JOIN users - ON acc_transactions_users.id_user = users.id - INNER JOIN acc_transactions - ON acc_transactions_users.id_transaction = acc_transactions.id - INNER JOIN acc_transactions_lines - ON acc_transactions_lines.id_transaction = acc_transactions.id - INNER JOIN acc_accounts - ON acc_transactions_lines.id_account = acc_accounts.id - WHERE - (strftime("%%Y", acc_transactions.date) = "%d" - AND - acc_accounts.%s - ) - GROUP BY acc_transactions.id, acc_accounts.id - ORDER by %s, acc_accounts.code, acc_transactions.date - ', - $annee, - $db->where('id', 'in', $comptesIsoles), - $tri - ); - return $db->get($sql); - } + /** + * @return liste de toutes les activités, tarifs et comptes associés + */ + public static function getActivitesTarifsEtComptes() + { + return DB::getInstance()->get( + "SELECT + services.id as idActivite, + services.label as titreActivite, + services.description as descActivite, + services_fees.id as idTarif, + services_fees.label as titreTarif, + services_fees.description as descTarif, + acc_accounts.code as numeroCpt, + acc_accounts.label as nomCpt + FROM services + LEFT JOIN services_fees ON services_fees.id_service = services.id + LEFT JOIN acc_accounts ON services_fees.id_account = acc_accounts.id + ORDER BY services.label" + ); + } - /** - * @return personnes ayant versé des dons pour une année donnée - * @param $annee - * @param array $champsNom : champs qui définissent le nom et le prénom d'une personne - */ - public static function getDonateurs($annee, $champsNom) : array - { - // concaténer les champs nom/prénoms pour la sélection - $nom = Utils::combinerChamps($champsNom); - // et pour le tri - $tri = Utils::combinerTri($champsNom); - $sql = sprintf( - 'SELECT - users.id as idUser, - users.numero, - users.email, - row_number() over(order by %s) as rang, - %s as nom, - users.adresse as adresse, - users.code_postal as codePostal, - users.ville as ville - FROM - acc_transactions_users, - users, - acc_transactions - INNER JOIN acc_transactions_lines - ON acc_transactions_lines.id_transaction = acc_transactions.id - WHERE ( - strftime("%%Y", acc_transactions.date) = "%d" - AND - acc_transactions_users.id_transaction = acc_transactions.id - AND - acc_transactions_users.id_user = users.id - ) - GROUP by users.id - ORDER by %1$s COLLATE U_NOCASE - ', - $tri, - $nom, - $annee - ); - $donateurs = array(); - foreach (DB::getInstance()->iterate($sql) as $personne) - { - $donateurs[$personne->idUser] = new Personne($personne->idUser, - $personne->numero, - $personne->email, - $personne->rang, - $personne->nom, - $personne->adresse, - $personne->codePostal, - $personne->ville); - } - return $donateurs; - } + /** + * @return nom de l'association + */ + public static function getNomAsso() { + return DB::getInstance()->first( + "SELECT value + FROM config + WHERE key = 'nom_asso'" + )->value; + } - /** - * combiner les champs avec un opérateur - * @param array $champs : liste (non vide) de champs - * @return chaîne combinée - */ - private static function combinerChamps($champs) - { - $op = ' || " " || '; - $result = 'ifnull(users.' . $champs[0] . ', "")'; - for ($i = 1; $i < count($champs); ++$i) - { - $result .= $op . 'ifnull(users.' . $champs[$i] . ', "")'; - } - return 'trim(' . $result . ')'; - } + /** + * @return adresse de l'association + */ + public static function getAdresseAsso() { + return DB::getInstance()->first( + "SELECT value + FROM config + WHERE key = 'adresse_asso'" + )->value; + } - /** - * combiner les clés de tri - * @param clés de tri - * @return chaîne combinée - */ - private static function combinerTri(array $champs) : string - { - $tri = 'users.' . $champs[0]; - for ($i = 1; $i < count($champs); ++$i) - { - $tri .= ', users.' . $champs[$i]; - } - return $tri; - } + /** + * @return liste des années fiscales + */ + public static function getAnneesFiscales() { + $rows = DB::getInstance()->get( + "SELECT strftime('%Y', start_date) as annee + FROM acc_years + ORDER by start_date DESC" + ); + $anneesFiscales = array(); + foreach ($rows as $row) { + $anneesFiscales[] = $row->annee; + } + return $anneesFiscales; + } - /** - * combiner chaque tarif avec le numéro de compte associé - */ - private static function combinerTarifsComptes($tarifs, $comptes) - { - $condition = '('; - $lesCond = array_map(fn($e1, $e2) : string => - "(services_fees.id = '$e1' AND acc_accounts.id = '$e2')", - $tarifs, $comptes); - $nb = 0; - foreach ($lesCond as $cond) - { - if ($nb > 0) { $condition .= ' OR '; } - $condition .= $cond; - ++$nb; - } - $condition .= ')'; - return $condition; - } - - /** - * @return liste des années fiscales - */ - public static function getAnneesFiscales() : array - { - $rows = DB::getInstance()->get( - "SELECT strftime('%Y', start_date) as annee - FROM acc_years - UNION - SELECT strftime('%Y', end_date) as annee - FROM acc_years - ORDER by annee DESC" - ); - $anneesFiscales = array(); - foreach ($rows as $row) { - $anneesFiscales[] = $row->annee; - } - return $anneesFiscales; - } - - public static function getLignesReduction($lesTaux) - { - foreach ($lesTaux as $elem) - { - $lignes[$elem->taux] = $elem->remarque; - } - return $lignes; - } - - /** - * récupérer dans la config du plugin les champs des membres - * utilisés pour le nom et le prénom ; ajouter/supprimer les - * modifications par rapport à la config paheko - * @return array tableau des champs : clé = nom, valeur = { titre, position } - */ - public static function getChampsNom($config, $plugin) : array - { - // récupérer dans la config du plugin les champs mémorisés - // pour le nom et le prénom (le tableau est vide si pas mémorisé) - $champsNom = (array) $plugin->getConfig('champsNom'); - - // récupérer dans la config Paheko les champs des membres - // utilisés pour le nom et le prénom - $champsPaheko = DynamicFields::getInstance()->listAssocNames(); - - foreach ($champsPaheko as $name => $title) - { - if (stristr($title, 'nom')) - { - // retenir les champs dont le titre contient le terme 'nom' - // est-il présent dans la config du plugin ? - if (! array_key_exists($name, $champsNom)) - { - // absent => l'ajouter - $champ = new \stdClass(); - $champ->titre = $title; - $champ->position = 0; - $champsNom[$name] = $champ; - } - } - } - // opération symétrique : un champ mémorisé dans la config du - // plugin a-t-il disparu de la config paheko ? - foreach ($champsNom as $nom => $champ) - { - if (! array_key_exists($nom, $champsPaheko)) - { - // absent => le supprimer - unset($champsNom[$nom]); - } - } - // mettre à jour la config du plugin - $plugin->setConfigProperty('champsNom', $champsNom); - return $champsNom; - } - - /** - * enregistrer les fichiers dans une archive zip - * @param $fileList : liste des fichiers à archiver - * @param $year : pour générer le nom de l'archive - * @param $archiveDir : ne sert plus - */ - static function makeArchive( - $fileList, - $year, - $archiveDir = null) - { - $zipFilename = "recus_dons" . $year . ".zip"; - header('Content-type: application/zip'); - header(sprintf('Content-Disposition: attachment; filename="%s"', $zipFilename)); - $zip = new ZipWriter('php://output'); - $zip->setCompression(0); - foreach ($fileList as $fileName) - { - $zip->add(basename($fileName), null, $fileName); - } - $zip->close(); - } // makeArchive + /** + * enregistrer les fichiers dans une archive zip + * @param $fileList : liste des fichiers à archiver + * @param $year : pour générer le nom de l'archive + * @param $archiveDir : ne sert plus + */ + static function makeArchive( + $fileList, + $year, + $archiveDir = null) + { + $zipFilename = "recus_dons" . $year . ".zip"; + header('Content-type: application/zip'); + header(sprintf('Content-Disposition: attachment; filename="%s"', $zipFilename)); + $zip = new ZipWriter('php://output'); + $zip->setCompression(0); + foreach ($fileList as $fileName) + { + $zip->add(basename($fileName), null, $fileName); + } + $zip->close(); + } // makeArchive } diff --git a/lib/Versement.php b/lib/Versement.php index 35fa9ec..3ac3f07 100644 --- a/lib/Versement.php +++ b/lib/Versement.php @@ -1,40 +1,24 @@ idActivite = $idActivite; + $this->idTarif = $idTarif; $this->montant = $montant; - $this->dateMin = $dateMin; - $this->dateMax = $dateMax; + $this->tauxReduction = $tauxReduction; } - - /** - * ajouter un versement en fixant les dates min et max - * @param $montant - * @param $dateMin - * @param $dateMax - */ - public function ajouter($montant, $dateMin, $dateMax) - { - $this->montant += $montant; - if ($dateMin < $this->dateMin) - { - $this->dateMin = $dateMin; - } - if ($dateMax > $this->dateMax) - { - $this->dateMax = $dateMax; - } - } } diff --git a/lib/pdf.css b/lib/pdf.css new file mode 100644 index 0000000..a3a92ed --- /dev/null +++ b/lib/pdf.css @@ -0,0 +1,121 @@ +/* organisation spatiale */ +body +{ + width : 21cm; + padding : 1cm 1cm; + display: grid; + grid-template-areas: + 'entete' + 'beneficiaire' + 'donateur' + 'versements' + 'signature'; +} + +#entete +{ + grid-area: entete; + width: 100%; +} + +#logoCerfa +{ + line-height: 40px; + width: 100px; + background-color: rgb(0, 0, 128); + border-radius : 50%; + text-align : center; + margin : 0.5em; +} + +.centre +{ + display : inline-block; + vertical-align : middle; + line-height: 20px; /* moitié de la hauteur du logo */ + color : white; + font-weight: bold; + font-size : 14pt; +} + +#numCerfa +{ + width: 100px; /* largeur du logo */ + text-align: center; +} + +#titre +{ + margin : 0 4cm 0 4cm; + text-align : center; + font-size : 140%; + font-weight: bold; +} + +#articles +{ + margin : 0 4cm 0 4cm; /* idem titre */ +} + +#numRecu +{ + text-align : right; +/* display : inline;*/ +} + +#beneficiaire +{ + grid-area: beneficiaire; + width: 100%; +} + +#donateur +{ + grid-area: donateur; + width: 100%; +} + +#versements +{ + grid-area: versements; + width: 100%; +} + +#final +{ + grid-area: signature; + width: 100%; +} + +.rubrique +{ + background-color : rgb(200, 200, 250); + padding : 0.5em; +} + +.cartouche +{ + margin : 0.5em auto; + padding : 0 0.5em; + border : 1px solid rgb(0, 0, 128); + border-radius : 8px; +} + +.titre, .important +{ + font-weight:bold; +} + +#signature +{ + display: block; + width : 7cm; + margin: 0 auto; + padding-bottom : 0.5em; +} + +#fonction +{ + text-align : center; + padding-bottom : 2em; +} diff --git a/plugin.ini b/plugin.ini deleted file mode 100644 index 61216fc..0000000 --- a/plugin.ini +++ /dev/null @@ -1,8 +0,0 @@ -name="Reçus fiscaux" -description="Génération de reçus fiscaux pour les dons des membres" -author="Jean-Christophe Engel" -url="https://acloud8.zaclys.com/index.php/s/n9daWAND24T2W3e" -version="0.10" -menu=1 -config=1 -min_version="1.3" diff --git a/templates/_nav.tpl b/templates/_nav.tpl index 4f41861..3c501aa 100644 --- a/templates/_nav.tpl +++ b/templates/_nav.tpl @@ -1,12 +1,12 @@ -{include file="_head.tpl" title="%s"|args:$plugin.label current="plugin_%s"|args:$plugin.name} +{include file="admin/_head.tpl" title="%s"|args:$plugin.nom current="plugin_%s"|args:$plugin.id}
    - {* Tous les versements *} - + - {* Activités, tarifs et comptes *} - + {literal} + // activer/désactiver les radios des activités/tarifs + for (var laCase of document.querySelectorAll("input[type=checkbox]")) { + laCase.addEventListener('change', (evt) => { + var idCase = evt.target; + // chercher la ligne englobante () + var ligne = idCase.closest("tr"); + // itérer sur les radio de cette ligne + var lesRadios = ligne.querySelectorAll('input[type=radio]'); + for (var idRadio of lesRadios) { + if (idCase.checked) { + idRadio.disabled = ''; + } else { + idRadio.disabled = 'disabled'; + } + } + }); + } + {/literal} -{include file="_foot.tpl"} +{include file="admin/_foot.tpl"} \ No newline at end of file diff --git a/templates/recu.skel b/templates/recu.skel deleted file mode 100644 index 8691222..0000000 --- a/templates/recu.skel +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - - - -
    - -

    Reçu au titre des dons à certains organismes d'intérêt général

    -

    Articles 200, 238 bis et 978 du code général des impôts

    -
    -

    Reçu {{$numero}}

    -
    -
    - -
    -

    Bénéficiaire des versements

    -

    Association « {{$config.org_name}} »
    - {{$config.org_address}}
    - Objet : {{$objet_asso}} -

    -
    - -
    -

    Donateur

    -

    - {{$nom}}
    - {{$adresse}}
    - {{$code_postal}} {{$ville}} - {{if $courriel != ""}} -
    courriel : {{$courriel}} - {{/if}} -

    -
    - -
    -

    Le bénéficiaire reconnaît avoir reçu au titre des dons et versements ouvrant droit à réduction d'impôt :

    -
      - {{#versements}} -
    • - la somme de ***{{$montant|raw|money}}*** euros - {{if $cents != ""}} - ({{$euros}} euros et {{$cents}} cents) - {{else}} - ({{$euros}} euros) - {{/if}} - {{if $libelle != ""}} - ({{$libelle}}) - {{/if}} -
      date des versements : - {{if $dateMin == $dateMax}} - le {{$dateMin}} - {{else}} - du {{$dateMin}} au {{$dateMax}} - {{/if}} -
    • - {{/versements}} -
    - - {{#informations}} -

    {{$titre}} {{$libelle}}

    - {{/informations}} - -

    Le bénéficiaire certifie sur l’honneur que les dons et versements qu’il reçoit ouvrent droit à la réduction d'impôt prévue {{$texteArticles}} du code général des impôts.

    -
    - -
    -

    {{$ville_asso}} le {{$date}} - -

    -
    - {{$nom_responsable}}
    - {{$fonction_responsable}} -
    -
    - - diff --git a/templates/recu_html.tpl b/templates/recu_html.tpl deleted file mode 100644 index 2bed92f..0000000 --- a/templates/recu_html.tpl +++ /dev/null @@ -1,136 +0,0 @@ -{include file="_head.tpl" title="%s"|args:$plugin.label current="plugin_%s"|args:$plugin.id} - - - -{* Itération sur les personnes *} -{foreach from=$totalPersonnes key="idPersonne" item="personne"} -
    - -
    - -

    Reçu au titre des dons à certains organismes d'intérêt général

    -

    Articles 200, 238 bis et 978 du code général des impôts

    -
    - {if $numero_sequentiel} - {afficher_numero_recu prefixe=$prefixeNum membre=$membre numero_personne=$personne->numero numero_sequentiel=$numero_courant} - - {else} - {afficher_numero_recu prefixe=$prefixeNum membre=$membre numero_personne=$personne->numero numero_sequentiel=$numero_sequentiel} - {/if} -
    -
    - -
    -

    Bénéficiaire des versements

    -

    Association « {$org_name} »
    - {$org_address}
    - Objet : {$objet_asso} -

    -
    - -
    -

    Donateur

    -

    - {$personne.nomPrenom}
    - {$personne.adresse}
    - {$personne.codePostal} {$personne.ville} - {if $courriel && $personne.courriel != ""} -
    courriel : {$personne.courriel} - {/if} -

    -
    - -
    -

    Le bénéficiaire reconnaît avoir reçu au titre des dons et versements ouvrant droit à réduction d'impôt :

    -
      - {foreach from=$personne.versements key="taux" item="versement"} -
    • - la somme de ***{$versement.montant|raw|money}*** euros - format((int)($versement->montant / 100)); - if ($versement->montant % 100 != 0) { - $cents = $fmt->format($versement->montant % 100); - } else { - $cents = ""; - } - ?> - {if $cents != ""} - ({$euros} euros et {$cents} cents) - {else} - ({$euros} euros) - {/if} - - {if $libelle != ""} - ({$libelle}) - {/if} -
      date des versements : - dateMin); - $dmax = date("d/m/Y", $versement->dateMax); - ?> - {if $versement.dateMin == $versement.dateMax} - le {$dmin} - {else} - du {$dmin} au {$dmax} - {/if} -{* - Erreur : dates décalées d'un jour (en arrière) - {if $versement.dateMin == $versement.dateMax} - le {$versement.dateMin|date_format:"%d/%m/%Y"} - {else} - du {$versement.dateMin|date_format:"%d/%m/%Y"} au {$versement.dateMax|date_format:"%d/%m/%Y"} - {/if} -*} - -
    • - {/foreach} -
    - - {foreach from=$complements item="elem"} -

    {$elem.titre} {$elem.libelle}

    - {/foreach} - -

    Le bénéficiaire certifie sur l’honneur que les dons et versements qu’il reçoit ouvrent droit à la réduction d'impôt prévue {$texteArticles} du code général des impôts.

    -
    - -
    -

    {$ville_asso} le {$date} - -

    -
    - {$nom_responsable}
    - {$fonction_responsable} -
    -
    -
    - -{/foreach} {* Itération sur les personnes *} - -{* scripts divers *} - - -{* - * remplacer la feuille de style d'impression de paheko par la mienne - * puis déclencher l'impression -*} -{literal} - -{/literal} - - -{include file="_foot.tpl"} diff --git a/templates/upload.tpl b/templates/upload.tpl deleted file mode 100644 index 70f4cff..0000000 --- a/templates/upload.tpl +++ /dev/null @@ -1,18 +0,0 @@ -{include file="_head.tpl" title="Envoi de fichier"} - -{form_errors} - -
    -
    - Téléverser des fichiers -
    - {input type="file" name="file[]" multiple=true label="Fichiers à envoyer" data-enhanced=1} -
    -

    - {csrf_field key=$csrf_key} - {button type="submit" name="upload" label="Envoyer" shape="upload" class="main"} -

    -
    -
    - -{include file="_foot.tpl"} diff --git a/templates/versements_activites.tpl b/templates/versements_activites.tpl index 3775378..83dc026 100644 --- a/templates/versements_activites.tpl +++ b/templates/versements_activites.tpl @@ -1,104 +1,88 @@ -{include file="%s/templates/_nav.tpl"|args:$plugin_root current_nav="activite"} +{include file="%s/templates/_nav.tpl"|args:$plugin_root current_nav="versements"} -

    Année {$annee_recu} : versements par activité et tarif

    +

    Liste des versements par activité et tarif

    -
    -
    -
    - - 0,00 € -
    -
    - - -
    -
    - - -
    -
    - {button type="submit" label="Télécharger les reçus au format PDF" shape="download" - form="versements_activites" - formaction="generer_recus.php?type=activite&format=pdf" - onclick="return verifierChoix(this.form)"} - {button type="submit" target="_dialog" label="Imprimer les reçus" shape="print" - form="versements_activites" - formaction="generer_recus.php?type=activite&format=print" - onclick="return verifierChoix(this.form)"} -
    -
    -
    +
    + + +
    -
    +{* + + +
    +*} - {* Itération sur les versements *} - {foreach from=$lesVersements key="rang" item="versement"} - {if $rang == 0} - {* premier versement *} - idTarif; - $personneCourante = $versement->idUser; - $compteCourant = $versement->idCompte; - $codeCompte = $versement->codeCompte; - ?> - {afficher_debut_tarif versement=$versement} - {afficher_debut_personne user=$personneCourante idVersement="%s_%s"|args:$tarifCourant,$personneCourante} - {afficher_debut_compte idCompte=$compteCourant} - {elseif $versement.idTarif != $tarifCourant} - {* changement de tarif *} - {fin_compte} - {fin_personne} - {fin_tarif} - idTarif; - $personneCourante = $versement->idUser; - $compteCourant = $versement->idCompte; - $codeCompte = $versement->codeCompte; - ?> - {afficher_debut_tarif versement=$versement} - {afficher_debut_personne user=$personneCourante idVersement="%s_%s"|args:$tarifCourant,$personneCourante} - {afficher_debut_compte idCompte=$compteCourant} - {elseif $versement.idUser != $personneCourante} - {* changement de personne *} - {fin_compte} - {fin_personne} - idUser; - $compteCourant = $versement->idCompte; - $codeCompte = $versement->codeCompte; - ?> - {afficher_debut_personne user=$personneCourante idVersement="%s_%s"|args:$tarifCourant,$personneCourante} - {afficher_debut_compte idCompte=$compteCourant} - {elseif $versement.codeCompte != $codeCompte} - {fin_compte} - {* changement de compte *} - idCompte; - $codeCompte = $versement->codeCompte; - ?> - {afficher_debut_compte idCompte=$compteCourant} - {else} - {* même personne, même compte *} - {/if} - {afficher_versement versement=$versement idVersement="%s_%s"|args:$tarifCourant,$personneCourante rang=$rang pair=$pair} - - {/foreach} {* Itération sur les versements *} - {fin_compte} - {fin_personne} - {fin_tarif} +
    + +
    + + +
    + + {* Itération sur les versements *} + {foreach from=$lesVersements key="i" item="versement"} + {if $i == 0} + {* premier versement *} + idTarif; + $personneCourante = $versement->idUser; + ?> + {afficher_debut_tarif versement=$versement} + {afficher_debut_personne versement=$versement} + {afficher_versement versement=$versement rang=$i} + {else} + {* autre versement *} + {if $versement.idTarif != $tarifCourant} + {* changement de tarif *} + {* fin versements d'une personne *} + + idTarif; + $personneCourante = $versement->idUser; + ?> + {afficher_debut_tarif versement=$versement} + {afficher_debut_personne versement=$versement} + {afficher_versement versement=$versement rang=$i} + {elseif $versement.idUser != $personneCourante} + {* changement de personne *} + + idUser; + ?> + {afficher_debut_personne versement=$versement} + {afficher_versement versement=$versement rang=$i} + {else} + {* même personne *} + {afficher_versement versement=$versement rang=$i} + {/if} + {/if} + {/foreach} {* Itération sur les versements *} + + + +
    -{* scripts divers *} +{* scripts pour cases à cocher *} +{literal} + +{/literal} + -{include file="_foot.tpl"} +{include file="admin/_foot.tpl"} \ No newline at end of file diff --git a/templates/versements_personnes.tpl b/templates/versements_personnes.tpl index e5ce3c2..4005c8b 100644 --- a/templates/versements_personnes.tpl +++ b/templates/versements_personnes.tpl @@ -1,86 +1,51 @@ {include file="%s/templates/_nav.tpl"|args:$plugin_root current_nav="personne"} -

    Année {$annee_recu} : versements par personne

    +

    Liste des versements totaux par personne

    -
    -
    +
    -
    - - 0,00 € -
    -
    - - -
    -
    - -
    -
    - {button type="submit" label="Télécharger les reçus au format PDF" shape="download" - form="versements_personnes" - formaction="generer_recus.php?type=personne&format=pdf" - onclick="return verifierChoix(this.form)"} - {button type="submit" label="Imprimer les reçus" shape="print" - form="versements_personnes" - formaction="generer_recus.php?type=personne&format=print" - onclick="return verifierChoix(this.form)"} -
    -
    -
    + {* Itération sur les personnes *} + + + + + + + + + + + + + + {foreach from=$lesVersementsTotaux key=rang item="versement"} + + + + + + + + + + {/foreach} + +
    + + + IdNom PrénomMontantAdresseCode postalVille
    + {input + type="checkbox" + name="selected[]" + value=$rang} + {$versement.idUser}{$lesDonateurs[$versement.idUser]->nomPrenom}{$versement.versement|raw|money}{$lesDonateurs[$versement.idUser]->adresse}{$lesDonateurs[$versement.idUser]->codePostal}{$lesDonateurs[$versement.idUser]->ville}
    - - - {* Itération sur les personnes *} - {foreach from=$lesVersements key="rang" item="versement"} - - {if $rang == 0} - {* 1ère personne *} - idUser; - $compteCourant = $versement->idCompte; - $codeCompte = $versement->codeCompte; - ?> - {afficher_debut_personne user=$personneCourante idVersement=$personneCourante} - {afficher_debut_compte idCompte=$compteCourant} - {elseif $versement.idUser != $personneCourante} - {* changement de personne *} - {fin_compte} - {fin_personne} - idUser; - $compteCourant = $versement->idCompte; - $codeCompte = $versement->codeCompte; - ?> - {afficher_debut_personne user=$personneCourante idVersement=$personneCourante} - {afficher_debut_compte idCompte=$compteCourant} - {elseif $versement.codeCompte != $codeCompte} - {fin_compte} - {* changement de compte *} - idCompte; - $codeCompte = $versement->codeCompte; - ?> - {afficher_debut_compte idCompte=$compteCourant} - {else} - {* même personne, même compte *} - {/if} - {afficher_versement versement=$versement idVersement=$personneCourante rang=$rang pair=$pair} - - {/foreach} {* Itération sur les personnes *} - {fin_compte} - {fin_personne} + -{* scripts divers *} +{* scripts pour cases à cocher *} -{include file="_foot.tpl"} +{include file="admin/_foot.tpl"} \ No newline at end of file diff --git a/uninstall.php b/uninstall.php deleted file mode 100644 index 4246dbe..0000000 --- a/uninstall.php +++ /dev/null @@ -1,20 +0,0 @@ -delete(); -} - -// signature réelle -$signature = $plugin->getConfig('signature'); -if (null !== $signature) { - $sig_file = \Paheko\Files\Files::get($signature); - if (null !== $sig_file) { - $sig_file->delete(); - } -} -unset($_SESSION['sig_file']); diff --git a/upgrade.php b/upgrade.php deleted file mode 100644 index 699d018..0000000 --- a/upgrade.php +++ /dev/null @@ -1,19 +0,0 @@ -getInfos('version'); - -if (version_compare($old_version, '0.9', '<')) -{ - $configNum = new \stdClass(); - $configNum->prefixe = ""; - $configNum->annee = false; - $configNum->membre = false; - $configNum->sequentiel = false; - $configNum->valeur_init = 1; - $plugin->setConfigProperty('numerotation', $configNum); - $plugin->setConfigProperty('imprimerCourriel', false); -} diff --git a/www/admin/action.php b/www/admin/action.php new file mode 100644 index 0000000..4fef81e --- /dev/null +++ b/www/admin/action.php @@ -0,0 +1,9 @@ +requireAccess($session::SECTION_CONFIG, $session::ACCESS_ADMIN); +$art_sel=f('articlesCGI') ? : []; + +if (f('save') && $form->check('recusfiscaux_config')) +{ + try { + $plugin->setConfig('objet_asso', trim(f('objet_asso'))); + $confArticles = $plugin->getConfig('articlesCGI'); + // effacer l'ancienne configuration + for ($i = 0; $i < count($confArticles); ++$i) { + $confArticles[$i]->valeur = 0; + } + // et copier la nouvelle + foreach ($art_sel as $article) { + $confArticles[$article]->valeur = 1; + } + $plugin->setConfig("articlesCGI", $confArticles); + \Garradin\Utils::redirect(PLUGIN_URL . 'config.php?ok'); + } + catch (UserException $e) + { + $form->addError($e->getMessage()); + } +} + +$tpl->assign('ok', qg('ok') !== null); +$tpl->assign('plugin_config', $plugin->getConfig()); +$tpl->assign('plugin_css', ['style.css']); + +$tpl->display(PLUGIN_ROOT . '/templates/config.tpl'); diff --git a/www/admin/generer_activites.php b/www/admin/generer_activites.php new file mode 100644 index 0000000..a6278d6 --- /dev/null +++ b/www/admin/generer_activites.php @@ -0,0 +1,122 @@ + $personne) +{ + // générer un fichier par reçu + $html = new RecusHTML( + $nomAsso, + $adresseAsso, + $plugin->getConfig('objet_asso'), + $signature + ); + // extraire les montants des versements + $lesMontants = array(); + foreach ($personne->versements as $versement) + { + if (array_key_exists($versement->tauxReduction, $lesMontants)) { + $lesMontants[$versement->tauxReduction] += $versement->montant; + } + else { + $lesMontants[$versement->tauxReduction] = $versement->montant; + } + } + $html->imprimer_recu( + $_SESSION['annee_recu'], + $personne->id, + $personne->nomPrenom, + $lesMontants, + $personne->adresse, + $personne->codePostal, + $personne->ville + ); + // fabriquer le fichier PDF + $nomPDF = \Garradin\Utils::filePDF($html->get()); + // changer le nom du fichier + $nom = str_replace(' ', '_', $personne->nomPrenom); + $nom = str_replace("'", "", $nom); + $nomFichier = "recu_" . $_SESSION['annee_recu'] . "_" . $nom . ".pdf"; + rename($nomPDF, $nomFichier); + // ajouter le nom du fichier à la liste pour mettre dans une archive + $listeFichiers[] = $nomFichier; +} + +// faire une archive zip +$fichierZip = Utils::makeArchive( + $listeFichiers, + $_SESSION['annee_recu'], + PLUGIN_ROOT . "/zip" +); + +/** + * Cumuler les versements de chaque personne par tarif + * @param tableau des versements triés par idTarif, idUser, date + * @return tableau des versements cumulés : id => Personne + */ +function cumulerVersements($versements) +{ + $totalPersonnes = array(); + $idTarif_courant = -1; + $idPersonne_courant = -1; + $totalVersements = 0; + foreach ($versements as $ligne) + { + if ( + $ligne->idTarif != $idTarif_courant || + $ligne->idUser != $idPersonne_courant + ) + { + if ($idTarif_courant != -1) { + $totalPersonnes[$idPersonne_courant]->ajouterVersement( + $_SESSION['lesTarifs'][$idTarif_courant]->idActivite, + $idTarif_courant, + $totalVersements/100, + $_SESSION['tauxSelectionnes'][$idTarif_courant] + ); + } + $idTarif_courant = $ligne->idTarif; + $idPersonne_courant = $ligne->idUser; + $totalVersements = $ligne->versement; + // créer les infos de la personne, sauf si elle est déjà présente + if (!array_key_exists($idPersonne_courant, $totalPersonnes)) + { + $totalPersonnes["$idPersonne_courant"] = $_SESSION['membresDonateurs'][$ligne->idUser]->clone(); + } + } else { + // cumuler versements + $totalVersements += $ligne->versement; + } + } + // et le dernier + $totalPersonnes[$idPersonne_courant]->ajouterVersement( + $_SESSION['lesTarifs'][$idTarif_courant]->idActivite, + $idTarif_courant, + $totalVersements/100, + $_SESSION['tauxSelectionnes'][$idTarif_courant] + ); + + return $totalPersonnes; +} diff --git a/www/admin/generer_personnes.php b/www/admin/generer_personnes.php new file mode 100644 index 0000000..15de5f6 --- /dev/null +++ b/www/admin/generer_personnes.php @@ -0,0 +1,64 @@ +getConfig('objet_asso'), + $signature + ); + + // extraire les montants des versements + $lesMontants[$_SESSION['taux_reduction']] = $ligne->versement/100; + $personne = $_SESSION['membresDonateurs'][$ligne->idUser]; + $html->imprimer_recu( + $_SESSION['annee_recu'], + $personne->id, + $personne->nomPrenom, + $lesMontants, + $personne->adresse, + $personne->codePostal, + $personne->ville + ); + // fabriquer le fichier PDF + $nomPDF = \Garradin\Utils::filePDF($html->get()); + // changer le nom du fichier + $nom = str_replace(' ', '_', $personne->nomPrenom); + $nom = str_replace("'", "", $nom); + $nomFichier = "recu_" . $_SESSION['annee_recu'] . "_" . $nom . ".pdf"; + rename($nomPDF, $nomFichier); + // ajouter le nom du fichier à la liste pour mettre dans une archive + $listeFichiers[] = $nomFichier; +} + +// faire une archive zip +$fichierZip = Utils::makeArchive( + $listeFichiers, + $_SESSION['annee_recu'], + PLUGIN_ROOT . "/zip" +); + diff --git a/www/admin/index.php b/www/admin/index.php new file mode 100644 index 0000000..ff7ca3d --- /dev/null +++ b/www/admin/index.php @@ -0,0 +1,28 @@ +getConfig('reduction')); + +// liste des activités, cotisations et comptes associés +$activitesTarifsComptes = Utils::getActivitesTarifsEtComptes(); + +// préparation de l'affichage +$tpl->assign('anneesFiscales', $anneesFiscales); +$tpl->assign('anneeCourante', $anneeCourante); +$tpl->assign('activitesTarifsComptes', $activitesTarifsComptes); +$tpl->assign('plugin_config', $plugin->getConfig()); +$tpl->assign('plugin_css', ['style.css']); + +// envoyer au template +$tpl->display(PLUGIN_ROOT . '/templates/index.tpl'); diff --git a/www/admin/script.js b/www/admin/script.js new file mode 100644 index 0000000..04b177e --- /dev/null +++ b/www/admin/script.js @@ -0,0 +1,180 @@ +/** + * Fonction appelée quand on (dé)coche la case de sélection globale + * (dé)sélectionner toutes les cases à cocher de toutes les activités + * @param id de la case globale + */ +function cocherDecocherTout(idCaseGlobale) +{ + // chercher le formulaire englobant + var formulaire = idCaseGlobale.closest("form"); + // itérer sur la liste des éléments détails : 1 par couple + var lesDetails = formulaire.querySelectorAll("details"); + for (var i = 0; i < lesDetails.length; ++i) { + // itérer sur les personnes + var lesH3 = lesDetails[i].querySelectorAll("h3.personne"); + for (var j = 0; j < lesH3.length; ++j) { + // trouver l'élément total de la personne + var idTotal = lesH3[j].querySelector("span"); + // puis la case à cocher + var fieldset = lesH3[j].nextElementSibling; + var idCase = fieldset.querySelector("input"); + idCase.checked = idCaseGlobale.checked; + // puis traiter toutes les cases de la personne + cocherDecocherPersonne(idCase, idTotal); + } + } + // changer le message + var message = idCaseGlobale.nextElementSibling; + if (idCase.checked) { + message.innerHTML = "Cliquer pour dé-cocher toutes les lignes"; + } else { + message.innerHTML = "Cliquer pour cocher toutes les lignes"; + } +} + +/** + * Fonction appelée quand on (dé)coche la case d'une personne + * - (dé)sélectionner toutes les cases à cocher + * - faire le total des cases cochées et l'afficher + * + * @param id de la case qui a été cochée + * @param id de l'élément où afficher le total + */ +function cocherDecocherPersonne(idCase, idTotal) +{ + // chercher le fieldset englobant + var fieldset = idCase.closest("fieldset"); + var listeCases = fieldset.querySelectorAll("input[type=checkbox]"); + for (var i = 1; i < listeCases.length; ++i) + { + listeCases[i].checked = idCase.checked; + } + // changer le message + var message = idCase.nextElementSibling; + if (idCase.checked) { + message.innerHTML = "Cliquer pour dé-cocher toutes les lignes"; + } else { + message.innerHTML = "Cliquer pour cocher toutes les lignes"; + } + // calculer et afficher le total + var listeMontants = fieldset.querySelectorAll("span.montant"); + calculerTotal(listeCases, listeMontants, idTotal); +} + +/** + * Fonction appelée quand on (dé)coche la case d'un versement + * - (dé)sélectionner cette case (?) + * - faire le total des cases cochées et l'afficher + * + * @param id de la case qui a été cochée + * @param id de l'élément où afficher le total + */ +function cocherDecocherVersement(idCase, idTotal) +{ + var fieldset = idCase.closest("fieldset"); + var listeCases = fieldset.querySelectorAll("input[type=checkbox]"); + var listeMontants = fieldset.querySelectorAll("span.montant"); + calculerTotal(listeCases, listeMontants, idTotal); +} + +/** + * Faire le total des cases cochées et l'afficher + * @param listes des cases + * @param listes des montants associés + * @param id de l'élément où afficher le total +*/ +function calculerTotal(listeCases, listeMontants, idTotal) +{ + var total = 0; + for (var i = 1; i < listeCases.length; ++i) + { + if (listeCases[i].checked) { + total += parseFloat(listeMontants[i-1].textContent.replace(/\s/g, "")); + } + } + // "afficher" le total + idTotal.innerHTML = + total.toLocaleString('fr-FR', {style: 'currency', currency: 'EUR', + minimumFractionDigits: 2}); +} + +/** + * fonction appelée lors de la validation du formulaire + * @return vrai si au moins un choix a été fait + * @param : formulaire +*/ +function verifierChoix(formulaire) +{ + var listeCheck = formulaire.getElementsByTagName("input"); + var ok = false; + for (var i = 1; i < listeCheck.length; ++i) + { + if (listeCheck[i].checked) + { + ok = true; + break; + } + } + if (! ok) + { + alert("Erreur : il faut sélectionner au moins un versement"); + } + return ok; +} + +/** + * fonction appelée pour afficher et masquer des portions de formulaire +*/ +function afficherMasquer(formulaire, nomClasse1, nomClasse2) +{ + for (var elem of formulaire.querySelectorAll(nomClasse1)) { + elem.classList.remove('hidden'); + } + for (var elem of formulaire.querySelectorAll(nomClasse2)) { + elem.classList.add('hidden'); + } +} + +// vérifier +// - qu'au moins une activité/tarif est sélectionnée +// - qu'un radio de chaque activité/tarif sélectionné a été sélectionné :) +function verifierCases(idElem) +{ + var div = document.getElementById(idElem); + var nbChoix = 0; + // parcourir les cases à cocher + for (var idCase of div.querySelectorAll("input[type=checkbox]")) + { + if (idCase.checked) { + ++nbChoix; + // vérifier qu'un radio de la même ligne est sélectionné + var ligneCorrecte = false; + // trouver la ligne englobante + var ligne = idCase.closest("tr"); + for (var idRadio of ligne.querySelectorAll('input[type=radio]')) + { + if (idRadio.checked) { ligneCorrecte = true; break; } + } + if (! ligneCorrecte) { + alert("Erreur : il faut sélectionner un taux de réduction dans chaque ligne cochée"); + return false; + } + } + } + if (nbChoix == 0) { + alert("Erreur : il faut sélectionner au moins une activité/tarif"); + } + return nbChoix != 0; +} + +// vérifier qu'un radio a été sélectionné dans la div paramètre +function verifierRadio(idElem) +{ + var div = document.getElementById(idElem); + for (var idRadio of div.querySelectorAll('input[type=radio]')) + { + if (idRadio.checked) { return true; } + } + alert("Erreur : il faut sélectionner un taux de réduction"); + return false; +} diff --git a/www/admin/style.css b/www/admin/style.css new file mode 100644 index 0000000..605b741 --- /dev/null +++ b/www/admin/style.css @@ -0,0 +1,32 @@ +/* liste des versements */ +div.pair { + padding : 0.1em; + background: rgba(var(--gSecondColor), 0.2); +} +div.impair { + padding : 0.1em; +} +fieldset { + border:1px solid brown; + -webkit-border-radius:8px; + border-radius:8px; +} +fieldset label { + font-weight:bold; +} +div span { + padding-left : 0.5em; + padding-right : 0.5em; +} +td.montant { + text-align : right; +} +summary.activite { + background: rgba(var(--gMainColor), 0.25); +} +h3.personne { + background: rgba(var(--gSecondColor), 0.35); +} +input[type="text"] { + width: 50em; +} diff --git a/www/admin/versements_activites.php b/www/admin/versements_activites.php new file mode 100644 index 0000000..4f263c1 --- /dev/null +++ b/www/admin/versements_activites.php @@ -0,0 +1,148 @@ +id] = Tarif::copier($ot); +} +$_SESSION['lesTarifs'] = $lesTarifs; + +// activités correspondants aux tarifs sélectionnés +$lesActivites = array(); +foreach (Utils::getActivites($tarifsSelectionnes) as $activite) { + $lesActivites[$activite->id] = Activite::copier($activite); +} +$_SESSION['lesActivites'] = $lesActivites; + +// versements correspondants aux tarifs sélectionnés +$_SESSION['lesVersements'] = Utils::getVersementsTarifs($_SESSION['annee_recu'], $tarifsSelectionnes); + +// membres donateurs +$membresDonateurs = array(); +$versementsMembres = Utils::getDonateurs($_SESSION['annee_recu']); +foreach ($versementsMembres as $versement) { + $membresDonateurs[$versement->idUser] = new Personne($versement->idUser, + $versement->nom, + $versement->adresse, + $versement->codePostal, + $versement->ville); +} +$_SESSION['membresDonateurs'] = $membresDonateurs; + +// ------------------------------------------------------------------------ +// fonctions pour l'affichage + +// afficher les informations d'une activité et d'un tarif +$tpl->register_function('afficher_debut_tarif', function ($params) +{ + $versement = $params['versement']; + $idTarif = $versement->idTarif; + $tarif = $_SESSION['lesTarifs'][$idTarif]; + $idActivite = $tarif->idActivite; + $activite = $_SESSION['lesActivites'][$idActivite]; + + $out = '
    + '; + $out .= sprintf(' +

    Activité « %s »

    ', $activite->label); + if (!empty($activite->description)) { + $out .= sprintf(' +

    %s

    ', $activite->description); + } + $out .= sprintf(' +

    tarif « %s »', $tarif->label); + if ($tarif->montant > 0) { + $out .= sprintf(' montant : %.2f €', $tarif->montant/100); + } else { + $out .= ' montant : libre'; + } + $out .= '

    +
    '; + return $out; +}); + +// afficher les informations d'une personne +$tpl->register_function('afficher_debut_personne', function ($params) +{ + $versement = $params['versement']; + $idUser = $versement->idUser; + $personne = $_SESSION['membresDonateurs'][$idUser]; + $idVersement = $versement->idTarif . "_" . $versement->idUser; + $out = sprintf('

    Versements de %s : 0,00 €

    ', + $personne->nomPrenom, + $idVersement); + $out .= sprintf(' +
    ', + $idVersement); + $out .= sprintf(' + ', + $idVersement, + $idVersement); + $out .= sprintf(' + ', + $idVersement); + $out .= '
    +
    '; + return $out; +}); + +// afficher un versement +$tpl->register_function('afficher_versement', function ($params) +{ + $versement = $params['versement']; + $rang = $params['rang']; + $idVersement = $versement->idTarif . "_" . $versement->idUser; + $out = '
    ' : 'impair">'; + $out .= sprintf(' + + + %.2f + %s +
    ', + $idVersement, $idVersement, + $rang, $rang, + $idVersement, $rang, $idVersement, $idVersement, $rang, + $versement->versement/100, + date_format(date_create($versement->date),"d/m/Y")); + return $out; +}); +// ------------------------------------------------------------------------ + +// préparation de l'affichage +$tpl->assign('lesActivites', $lesActivites); +$tpl->assign('lesTarifs', $lesTarifs); +$tpl->assign('lesVersements', $_SESSION['lesVersements']); +$tpl->assign('plugin_css', ['style.css']); + +// envoyer au template +$tpl->display(PLUGIN_ROOT . '/templates/versements_activites.tpl'); diff --git a/www/admin/versements_personnes.php b/www/admin/versements_personnes.php new file mode 100644 index 0000000..496a76f --- /dev/null +++ b/www/admin/versements_personnes.php @@ -0,0 +1,36 @@ +idUser] = new Personne($versement->idUser, + $versement->nom, + $versement->adresse, + $versement->codePostal, + $versement->ville); +} +$_SESSION['membresDonateurs'] = $membresDonateurs; + +// préparation de l'affichage +$tpl->assign('lesVersementsTotaux', $_SESSION['lesVersementsTotaux']); +$tpl->assign('lesDonateurs', $membresDonateurs); +$tpl->assign('plugin_css', ['style.css']); + +// envoyer au template +$tpl->display(PLUGIN_ROOT . '/templates/versements_personnes.tpl');