diff --git a/CHANGELOG b/CHANGELOG
index e4e5dae..635627c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,15 +1,3 @@
-0.12
-- Ajout Mollie à la table moyens de paiement
-0.11
-- Changement mention finale pour devis
-0.9
-- Ajout possibilité choisir champs identité et adresse membre
-0.8.8
-- correction typo
-0.8.7
-- correction typo
-0.8.6
-- Correction erreur si pas de prix saisi
0.8.5
- Ajout numéro SIREN/SIRET pour les clients
- Ajout pagination liste factures
diff --git a/README.md b/README.md
index 8685449..9f161d4 100644
--- a/README.md
+++ b/README.md
@@ -1,50 +1,56 @@
# Plugin Facturation pour Paheko (ex Garradin)
-Plugin de facturation pour le logiciel de gestion d'association Paheko
-( https://paheko.eu/ - https://fossil.kd2.org/paheko ).
-
+Plugin de facturation pour le logiciel de gestion d'association Paheko ( https://paheko.eu/ - https://fossil.kd2.org/paheko ).
Source :
-- version compatible paheko 1.3.x : https://git.roflcopter.fr/lesanges/facturation
-- version historique non compatible paheko 1.3.x : https://gitlab.com/noizette/garradin-plugin-facturation
+- version historique : https://gitlab.com/noizette/garradin-plugin-facturation
+- version compatible paheko 1.3.x : https://git.roflcopter.fr/lesanges/paheko-plugin-facturation
-## Installation :
-Vous pouvez télécharger l'archive .tar.gz depuis la page des
-[releases](https://git.roflcopter.fr/lesanges/paheko-plugin-facturation/releases),
-supprimer le numéro de version du nom de l'archive puis la placer dans
-le dossier plugins de Paheko.
+## Installation:
+Vous pouvez télécharger l'archive .tar.gz depuis la page des [releases](https://git.roflcopter.fr/lesanges/paheko-plugin-facturation/releases), supprimer le numéro de version du nom de l'archive puis la placer dans le dossier plugins de Paheko.
+
+### Anciennes versions (<0.6)
+
+Normalement, les plugins de Paheko doivent seulement être laissé sous forme d'archive .tar.gz dans le dossier plugins, or pour la génération des PDF, la librairie mPDF a besoin d'écrire des fichiers temporaires.
+
+Il faut pour cela faire :
+
+ tar xvf paheko-plugin-facturation-v0.5.0.tar.gz
+ mv paheko-plugin-facturation-v0.5.0 facturation
+ rm paheko-plugin-facturation-v0.5.0.tar.gz
+ chown -R www-data:www-data facturation/
+ chmod -R g+w facturation/
+
+
+Supprimer l'archive permet à Paheko de ne pas la lire à la place du dossier.
+
+*Note : www-data correspond dans la plupart des cas à l'utlisateur d'Apache, si vous utilisez un autre serveur web, il faudra probablement adapter.*
+
+## Migration vers Garradin 1.0 (obsolète)
+
+Lorsque vous tentez de mettre à jour une installation de Garradin avec le plugin facturation vers Garradin 1.0, l'upgrade est bloquée par le plugin.
+
+Pour remédier à cela et parvenir à faire la mise à jour, il faut dans un premier temps installer la version 0.4 du plugin sur votre installation Garradin 0.9.8, se rendre sur la page principale du plugin (menu > Facturation), vous pouvez ensuite mettre à jour Garradin vers la 1.0.
## Fonctionnalités :
- Créer et gérer une base de client·es
- Créer et modifier des factures et devis adressés aux membres de l'association ou des client·es ajouté·es
-- (obsolète) Créer des reçus fiscaux pour des dons et génération du CERFA correspondant
-- (obsolète) Créer des reçus sur des cotisations
-- Génération des documents (facture et devis) en PDF
+- Créer des reçus fiscaux pour des dons et génération du cerfa correspondant
+- Créer des reçus sur des cotisations
+- Génération des documents (facture et devis) en PDF grâce à la librairie mPDF
- Liste les documents associés sur la fiche d'un·e client·e
-- Permet de définir le statut du document à « réglé »
+- Permet de définir le statut du document sur reglée
- **Configuration** :
- Possibilité d'ajouter un numéro RNA et SIRET de l'association si elle en possède (apparait alors sur les documents)
- - Possibilité de choisir certains champs à faire figurer sur la facture (adresse, code postal, ville)
- - Modification du pied de page des documents (notamment pour y inscrire des mentions légales)
+ - Modification du pied de page des documents (notament pour y inscrire des mentions légales)
- Vérifier le code postal : si coché, lors d'ajout ou de modification de client, le plugin vérifiera que le code postal entré est bien formaté (par rapport aux codes postaux français seulement)
- - Noms de client·es uniques : si coché, lors d'ajout ou de modification de client·e, le nom du/de la client·e ne pourra pas être le même que celui d'un·e client·e déjà existant
- - (obsolète) Informations relatives au CERFA pour les reçus fiscaux
- - (obsolète) Image qui sert de signature sur le CERFA
+ - Noms de client·es uniques : si coché, lors d'ajout ou de modicifation de client·e, le nom du/de la client·e ne pourra pas être le même que celui d'un·e client·e déjà existant
+ - Informations relatives au cerfa pour les reçus fiscaux
+ - Image qui set de signature sur le cerfa
-Note : pour le moment, les actions sur la liste des clients à cocher
-ne fonctionnent pas. Pour supprimer un client, le faire depuis sa
-fiche.
+Note : pour le moment, les actions sur la liste des clients à cocher ne fonctionnent pas. Pour supprimer un client, le faire depuis sa fiche.
-## Futur de ce plugin :
-Un nouveau plugin est en cours de développement par BohwaZ, donc il
-n'est pas pertinent d'ajouter de nouvelles fonctionnalités à celui-ci.
-
-Par contre, si des bugs sont signalés sur la liste
-hebergement@paheko.cloud ou aide@paheko.cloud, je
-(lesanges@zaclys.net) peux tenter de les corriger, à condition que ça
-n'impacte pas trop la structure du plugin.
-
-## Futur improbable (obsolète) :
-- Ajout des champs Référence, Prix unitaire, Quantité sur les documents
+## Futur :
+- Ajout des champs Référence, Prix unitaire, Quantité sur les documents
- Actions sur liste de client·es (exporter, supprimer)
- Afficher/filtrer les documents par statuts réglé/archivé
- Changer statut depuis la liste des documents
@@ -53,12 +59,15 @@ n'impacte pas trop la structure du plugin.
- Gestion TVA ?
- Un devis ne devrait pas pouvoir être réglé
- Quid si un·e membre de l'asso est supprimé·e alors que des documents lui sont adressés ?
+
+## Futur improbable :
- Opérations de paiements dans la compta liés à une facture
- Gestion de produits
-Le plugin nécessite l'extension PHP mbstring.
-## (???) Inclus les bibliothèques suivantes :
+Le plugin nécessite l'extension PHP mbstring.
+
+## Inclus les bibliothèques suivantes :
- Composer :
https://getcomposer.org/
diff --git a/admin/_facture_common.php b/admin/_facture_common.php
index 8b93c95..cedacfa 100644
--- a/admin/_facture_common.php
+++ b/admin/_facture_common.php
@@ -4,28 +4,6 @@ namespace Paheko;
require_once __DIR__ . '/_inc.php';
-function toArray($array, $cle, $sep=",")
-{
- $result = array();
- foreach ($array as $elem)
- {
- $ro = new \ReflectionObject($elem);
- $proprietes = $ro->getProperties();
- $ligne = "";
- foreach ($proprietes as $p)
- {
- if ($p->getName() == $cle) {
- $key = $p->getValue($elem);
- }
- else {
- $ligne .= $sep . $p->getValue($elem);
- }
- }
- $result[$key] = substr($ligne, strlen($sep));
- }
- return $result;
-}
-
if (!isset($target) || !in_array( $target, ['new', 'edit'])) {
throw new Exception('blabla illegal call'); // Fix: exception type?
} else {
@@ -56,147 +34,127 @@ $tpl->assign('formes_don', array('1' => 'Acte authentique',
'3' => 'Don manuel',
'4' => 'Autres'));
$tpl->assign('natures_don', array('1' => 'Numéraire',
- '2' => 'Chèque',
- '3' => 'Virement, CB; ...'));
+ '2' => 'Chèque',
+ '3' => 'Virement, CB; ...'));
$tpl->assign('textes_don', $facture->listTextesCerfa());
if ( !$target ) {
f(['id' => 'required|numeric']);
$id = (int) qg('id');
-
+
if (!$f = $facture->get($id))
{
throw new UserException("Ce document n'existe pas.");
}
}
-if ($f->archivee)
-{
- throw new UserException("Ce document est archivé, vous n'avez pas le droit de le modifier");
-}
+
// Traitement
$data=[];
$form->runIf(f('save') && !$form->hasErrors(),
function () use ($client, &$data, $form)
- {
- try
- {
- if ( count(f('designation')) !== count(f('prix')) )
- {
- throw new UserException('Nombre de désignations et de prix reçus différent.');
- }
+ {
+ try
+ {
+ if ( count(f('designation')) !== count(f('prix')) )
+ {
+ throw new UserException('Nombre de désignations et de prix reçus différent.');
+ }
- $data = [
- 'numero' => f('numero_facture'),
- 'date_emission' => f('date_emission'),
- 'date_echeance' => f('date_echeance'),
- 'reglee' => f('reglee') == 1?1:0,
- 'archivee' => f('archivee') == 1?1:0,
- 'moyen_paiement' => f('moyen_paiement'),
- 'nom_contact' => f('nom_contact'),
- 'toto' => 0
- ];
- $data['type_facture'] = f('type');
- if (in_array(f('type'), [DEVIS, FACT]))
- {
- foreach(f('designation') as $k=>$value)
- {
- if (empty($value) && f('prix')[$k] != null) {
- throw new UserException("Il manque la désignation de la ligne " . $k+1 . " !!");
- }
- elseif ($value != '' && f('prix')[$k] == null) {
- throw new UserException('Il manque le prix sur la ligne '. $k+1 . ' !!');
- } elseif (empty($value) && f('prix')[$k] == null) {
- continue;
- }
+ $data = [
+ 'numero' => f('numero_facture'),
+ 'date_emission' => f('date_emission'),
+ 'date_echeance' => f('date_echeance'),
+ 'reglee' => f('reglee') == 1?1:0,
+ 'archivee' => f('archivee') == 1?1:0,
+ 'moyen_paiement' => f('moyen_paiement'),
+ 'toto' => 0
+ ];
+ $data['type_facture'] = f('type');
+ if (in_array(f('type'), [DEVIS, FACT]))
+ {
+ foreach(f('designation') as $k=>$value)
+ {
+ $data['contenu'][$k]['designation'] = $value;
+ $data['contenu'][$k]['prix'] = Utils::moneyToInteger(f('prix')[$k]);
+ $data['toto'] += Utils::moneyToInteger(f('prix')[$k]);
+ }
+ $data['total'] = $data['toto'];
+ unset($data['toto']);
+ }
+ elseif ( f('type') == CERFA )
+ {
+ $data['moyen_paiement'] = f('moyen_paiement_cerfa');
+ $data['contenu'] = [
+ 'forme' => f('forme_don'),
+ 'nature' => f('nature_don'),
+ 'texte' => f('texte_don')];
+ $data['total'] = Utils::moneyToInteger(f('total'));
+ unset($data['toto']);
+ }
+ if (f('base_receveur') == 'client')
+ {
+ $data['receveur_membre'] = 0;
+ $data['receveur_id'] = f('client');
+ }
+ elseif (f('base_receveur') == 'membre')
+ {
+ $data['receveur_membre'] = 1;
+ $data['receveur_id'] = f('membre');
+ }
+ else
+ {
+ throw new UserException('Vous devez indiquer si le receveur est un client ou un membre');
+ }
- $data['contenu'][$k]['designation'] = $value;
- $data['contenu'][$k]['prix'] = Utils::moneyToInteger(f('prix')[$k]);
- $data['toto'] += Utils::moneyToInteger(f('prix')[$k]);
- }
- $data['total'] = $data['toto'];
- unset($data['toto']);
- if (! isset($data['contenu'])) {
- throw new UserException("Aucune désignation ni aucun prix saisi !!");
- }
- if (f('type') == FACT) {
- $data['numero_commande'] = f('numero_commande');
- $data['reference_acheteur'] = f('reference_acheteur');
- }
- }
- elseif ( f('type') == CERFA )
- {
- $data['moyen_paiement'] = f('moyen_paiement_cerfa');
- $data['contenu'] = [
- 'forme' => f('forme_don'),
- 'nature' => f('nature_don'),
- 'texte' => f('texte_don')];
- $data['total'] = Utils::moneyToInteger(f('total'));
- unset($data['toto']);
- }
- if (f('base_receveur') == 'client')
- {
- $data['receveur_membre'] = 0;
- $data['receveur_id'] = f('client');
- }
- elseif (f('base_receveur') == 'membre')
- {
- $data['receveur_membre'] = 1;
- $data['receveur_id'] = f('membre');
- }
- else
- {
- throw new UserException('Vous devez indiquer si le receveur est un client ou un membre');
- }
-
- }
- catch(UserException $e)
- {
- $form->addError($e->getMessage());
- }
-
- }, $csrf_key);
+ }
+ catch(UserException $e)
+ {
+ $form->addError($e->getMessage());
+ }
+
+ }, $csrf_key);
$form->runIf(f('select_cotis') && !$form->hasErrors(),
function () use ($step)
- {
- $step = true;
- }, 'add_cotis_1');
+ {
+ $step = true;
+ }, 'add_cotis_1');
$form->runIf(f('add_cotis') && !$form->hasErrors(),
function () use ($radio, $fields, $facture, $form)
- {
- $radio['type'] = f('cotisation');
- try
- {
- $num = (int) str_replace('cotis_', '', $radio['type']);
- foreach($fields as $field)
- {
- $cotis[$field] = f($field.'_'.$num);
- }
+ {
+ $radio['type'] = f('cotisation');
+ try
+ {
+ $num = (int) str_replace('cotis_', '', $radio['type']);
+ foreach($fields as $field)
+ {
+ $cotis[$field] = f($field.'_'.$num);
+ }
- $r = $facture->getCotis(f('membre_cotis'), $cotis['id']);
- $r = $r[0];
+ $r = $facture->getCotis(f('membre_cotis'), $cotis['id']);
+ $r = $r[0];
- $data = [
- 'type_facture' => COTIS,
- 'numero' => f('numero_facture'),
- 'receveur_membre' => 1,
- 'receveur_id' => f('membre_cotis'),
- 'date_emission' => f('date_emission'),
- 'moyen_paiement' => 'AU',
- 'total' => $r->paid_amount ?? $r->amount,
- 'contenu' => ['id' => $cotis['id'],
- 'intitule' => $cotis['label'],
- 'souscription' => $cotis['date'],
- 'expiration' => $cotis['expiry'] ]
- ];
+ $data = [
+ 'type_facture' => COTIS,
+ 'numero' => f('numero_facture'),
+ 'receveur_membre' => 1,
+ 'receveur_id' => f('membre_cotis'),
+ 'date_emission' => f('date_emission'),
+ 'moyen_paiement' => 'AU',
+ 'total' => $r->paid_amount ?? $r->amount,
+ 'contenu' => ['id' => $cotis['id'],
+ 'intitule' => $cotis['label'],
+ 'souscription' => $cotis['date'],
+ 'expiration' => $cotis['expiry'] ]
+ ];
- }
- catch (UserException $e)
- {
- $form->addError($e->getMessage());
- }
- }, 'add_cotis_2');
+ }
+ catch (UserException $e)
+ {
+ $form->addError($e->getMessage());
+ }
+ }, 'add_cotis_2');
if (! $form->hasErrors())
{
@@ -230,6 +188,7 @@ if (! $form->hasErrors())
}
// Affichage
+
if ($target)
{
$doc = null;
@@ -265,28 +224,24 @@ if ($target)
elseif (isset($doc['type'])) {
$radio['type'] = $doc['type'];
} // ... ou par défaut
- else
+ else
{
$radio['type'] = FACT;
}
}
-else
+else
{
$doc['moyen_paiement'] = $f->moyen_paiement;
$doc['type'] = $f->type_facture;
$doc['numero_facture'] = $f->numero;
$doc['reglee'] = $f->reglee;
- $doc['archivee'] = $f->archivee;
$doc['base_receveur'] = $f->receveur_membre?'membre':'client';
$doc['client'] = $f->receveur_id;
$doc['membre'] = $f->receveur_id;
$doc['contenu'] = $f->contenu;
-
+
$doc['date_emission'] = f('date_emission') ?: $f->date_emission;
$doc['date_echeance'] = f('date_echeance')?: $f->date_echeance; // Smarty m'a saoulé pour utiliser form_field|date_fr:---
- $doc['nom_contact'] = $f->nom_contact;
- $doc['numero_commande'] = $f->numero_commande;
- $doc['reference_acheteur'] = $f->reference_acheteur;
/* modif DD -- CERFA -------------------------------------- */
if ( $f->type_facture == CERFA ) {
$doc['total'] = $f->total;
@@ -294,10 +249,9 @@ else
$doc['nature_don'] = $f->contenu['nature'];
$doc['texte_don'] = $f->contenu['texte'];
}
-
+
$radio['type'] = f('type')??$doc['type'];
}
-
$tpl->assign('types_details', $facture->types);
$tpl->assign('client_id', f('client') ?: -1);
@@ -334,10 +288,8 @@ if (in_array($radio['type'], [DEVIS, FACT]))
}
}
else {
- /*
- $designations = ['Exemple'];
- $prix = [250];
- */
+ $designations = ['Exemple'];
+ $prix = [250];
}
}
@@ -345,10 +297,10 @@ $date = new \DateTime;
$date->setTimestamp(time());
$tpl->assign('date', $date->format('d/m/Y'));
+
$tpl->assign(compact('liste', 'radio', 'step', 'designations', 'prix', 'from_user', 'identite', 'csrf_key', 'doc'));
-$tpl->assign('users', toArray($db->get('SELECT id, '.$identite.' FROM users WHERE id_category != -2 NOT IN (SELECT id FROM users_categories WHERE hidden = 1) ORDER BY ' .$identite. ';'), 'id', " "));
+$tpl->assign('users', $db->getAssoc('SELECT id, '.$identite.' FROM users WHERE id_category != -2 NOT IN (SELECT id FROM users_categories WHERE hidden = 1) ORDER BY ' .$identite. ';'));
$tpl->assign('clients', $db->getAssoc('SELECT id, nom FROM plugin_facturation_clients;'));
-$tpl->assign('contacts', $db->getAssoc('SELECT id, nom_contact FROM plugin_facturation_clients;'));
$tpl->assign('require_number', $require_number);
$tpl->assign('number_pattern', PATTERNS_LIST[$plugin->getConfig('pattern')]);
diff --git a/admin/_inc.php b/admin/_inc.php
index f11fc22..582e462 100644
--- a/admin/_inc.php
+++ b/admin/_inc.php
@@ -11,7 +11,7 @@ define('CERFA', 2);
define('COTIS', 3);
const PATTERNS_LIST = [
- '' => 'Aucun, le numéro sera à spécifier manuellement pour chaque document',
+ null => 'Aucun, le numéro sera à spécifier manuellement pour chaque document',
'%{type}-%{year}-%{ynumber}' => 'Type-Année-Numéro du document par année ("FACT-2021-42")',
'%{year}-%{type}-%04{ynumber}' => 'Année-Type-Numéro du document par année ("2021-DEVIS-0042")',
'%{t}-%{year}-%{ynumber}' => 'Type court-Année-Numéro du document par année ("F-2021-42")',
@@ -48,11 +48,11 @@ $tpl->register_function('money_fac', function (array $params)
if (!isset($user)) {
$user = false;
- }
+ }
if (!isset($name))
{
- $name = 'prix[]';
+ $name = 'prix[]';
}
if (null !== $current_value && !$user) {
@@ -62,7 +62,7 @@ $tpl->register_function('money_fac', function (array $params)
if (null !== $current_value) {
$current_value = htmlspecialchars($current_value, ENT_QUOTES, 'UTF-8');
}
-
+
$currency = Config::getInstance()->get('monnaie');
return sprintf('
';
echo str_replace("\n", ' ', $v['designation']);
echo ' | ';
@@ -202,17 +170,12 @@ EOF;
$echeance
$reglee
Moyen de paiement : $moyen_paiement
+
$footer
-
EOF;
- if ($f->type_facture == DEVIS) {
- echo <<Bon pour accord, date et signature
-EOF;
- }
$content = ob_get_clean();
@@ -221,7 +184,7 @@ EOF;
{
$lieu = $plugin->getConfig('ville_asso');
$intitule = $f->contenu['intitule'];
-
+
$souscription = date('d/m/Y', strtotime($f->contenu['souscription']));
if($f->contenu['expiration'] == '1970-01-01')
@@ -257,7 +220,7 @@ EOF;
}
//-- Layout du document
-
+
ob_start();
echo <<
@@ -270,7 +233,7 @@ EOF;
size: A4 portrait;
margin: 0;
}
-
+
body {
padding: 4mm;
font-family: Helvetica, Arial, sans;
@@ -304,13 +267,6 @@ EOF;
width: 40%;
vertical-align: top;
}
- .adressage td#logo {
- width: 20%;
- vertical-align: top;
- }
- .adressage img#logo {
- height : 3cm;
- }
.contenuTexte {
padding: 0 6mm;
@@ -362,7 +318,6 @@ EOF;
- | $logo |
$asso |
$receveur |
@@ -393,7 +348,7 @@ elseif ($f->type_facture == CERFA)
$t['objet1'] = $plugin->getConfig('objet_1');
$t['objet2'] = $plugin->getConfig('objet_2');
- $t['nom'] = $nom_client;
+ $t['nom'] = $c->nom;
$t['adresse'] = $c->adresse;
$t['cp'] = $c->code_postal;
$t['ville'] = $c->ville;
@@ -408,7 +363,7 @@ elseif ($f->type_facture == CERFA)
$t['forme'] = $f->contenu['forme'];
$t['nature'] = $f->contenu['nature'];
$t['texte'] = $libelles[$f->contenu['texte']];
-
+
$t['art200'] = $t['art238'] = $t['art885'] = '';
if($plugin->getConfig('droit_art200')){
$t['art200'] = 'X';
@@ -474,7 +429,7 @@ elseif ($f->type_facture == CERFA)
margin: 0;
padding: 0;
}
-
+
body {
font-family: Helvetica, Arial, sans;
font-size: 10pt;
@@ -496,11 +451,11 @@ elseif ($f->type_facture == CERFA)
background-size: cover;
background-position: -5mm -4.8mm;
}
-
+
#p1 {
background-image: url('{$url}p/facturation/cerfa-1.png');
}
-
+
#p2 {
background-image: url('{$url}p/facturation/cerfa-2.png');
position: relative;
@@ -564,7 +519,7 @@ if(qg('d') !== null)
{
$filename = 'Print';
if (preg_match('!(.*)!U', $html, $match)) {
- $filename = str_replace(" ", "_", trim($match[1]));
+ $filename = trim($match[1]);
}
header('Content-type: application/pdf');
diff --git a/data/schema.sql b/data/schema.sql
index 923b4eb..0cc7a67 100644
--- a/data/schema.sql
+++ b/data/schema.sql
@@ -10,10 +10,7 @@ CREATE TABLE IF NOT EXISTS plugin_facturation_factures (
archivee INTEGER DEFAULT 0, -- bool
moyen_paiement TEXT NOT NULL,
contenu TEXT NOT NULL,
- total INTEGER DEFAULT 0,
- nom_contact TEXT,
- numero_commande TEXT,
- reference_acheteur TEXT
+ total INTEGER DEFAULT 0
-- FOREIGN KEY(moyen_paiement) REFERENCES compta_moyens_paiement(code)
);
@@ -27,9 +24,7 @@ CREATE TABLE IF NOT EXISTS plugin_facturation_clients (
siret TEXT,
date_creation TEXT NOT NULL DEFAULT CURRENT_DATE CHECK (date(date_creation) IS NOT NULL AND date(date_creation) = date_creation), -- Date d\'inscription
telephone TEXT,
- email TEXT,
- nom_contact TEXT,
- note TEXT
+ email TEXT
);
@@ -48,7 +43,6 @@ INSERT OR IGNORE INTO plugin_facturation_paiement (code, nom) VALUES ('PR', 'Pr
INSERT OR IGNORE INTO plugin_facturation_paiement (code, nom) VALUES ('TI', 'TIP');
INSERT OR IGNORE INTO plugin_facturation_paiement (code, nom) VALUES ('VI', 'Virement');
INSERT OR IGNORE INTO plugin_facturation_paiement (code, nom) VALUES ('HA', 'HelloAsso');
-INSERT OR IGNORE INTO plugin_facturation_paiement (code, nom) VALUES ('MO', 'Mollie');
INSERT OR IGNORE INTO plugin_facturation_paiement (code, nom) VALUES ('AU', 'Autre');
-- Modif DD -- ajout de la table des textes associés aux CERFA
diff --git a/lib/Client.php b/lib/Client.php
index 66e7d75..66aabf3 100644
--- a/lib/Client.php
+++ b/lib/Client.php
@@ -17,9 +17,7 @@ class Client
'ville',
'siret',
'telephone',
- 'email',
- 'nom_contact',
- 'note'
+ 'email'
];
private $config = [
@@ -42,7 +40,7 @@ class Client
{
$data[$key] = trim($data[$key]);
- if($data[$key] == '' && ! in_array($key, ['siret', 'telephone', 'email', 'nom_contact', 'note']))
+ if($data[$key] == '' && ($key != 'siret' && $key != 'telephone' && $key != 'email'))
{
throw new UserException('Le champs '.$key.' doit être renseigné.');
}
@@ -138,12 +136,6 @@ class Client
'email' => [
'label' => 'E-Mail',
],
- 'nom_contact' => [
- 'label' => 'Contact',
- ],
- 'note' => [
- 'label' => 'Note',
- ],
'nb_documents' => [
'label' => 'Nombre de documents',
'select' => '(SELECT COUNT(*) FROM plugin_facturation_factures WHERE receveur_id = c.id)',
diff --git a/lib/Facture.php b/lib/Facture.php
index e6f766e..0776728 100644
--- a/lib/Facture.php
+++ b/lib/Facture.php
@@ -30,24 +30,31 @@ class Facture
'archivee',
'moyen_paiement',
'contenu',
- 'total',
- 'nom_contact',
- 'numero_commande',
- 'reference_acheteur'
+ 'total'
];
public $types = [
- DEVIS => [
- 'id' => DEVIS,
- 'accounts' => [],
- 'label' => 'Devis',
- 'help' => ''],
- FACT => [
- 'id' => FACT,
- 'accounts' => [],
- 'label' => 'Facture',
- 'help' => ''],
- ];
+ DEVIS => [
+ 'id' => DEVIS,
+ 'accounts' => [],
+ 'label' => 'Devis',
+ 'help' => ''],
+ FACT => [
+ 'id' => FACT,
+ 'accounts' => [],
+ 'label' => 'Facture',
+ 'help' => ''],
+ CERFA => [
+ 'id' => CERFA,
+ 'accounts' => [],
+ 'label' => 'Reçu fiscal',
+ 'help' => 'Reçu fiscal pour un don (membre ou client)'],
+ COTIS => [
+ 'id' => COTIS,
+ 'accounts' => [],
+ 'label' => 'Reçu de cotisation',
+ 'help' => 'Reçu pour une cotisation payée par un·e membre'],
+ ];
public function __construct()
{
@@ -68,119 +75,116 @@ class Facture
if(!is_array($data) && null !== $data){
$datas[$k] = trim($data);
}
- if ($datas[$k] === '' && ! in_array($k, ['numero', 'nom_contact', 'numero_commande', 'reference_acheteur']))
+ if ($datas[$k] === '' && $k != 'numero')
{
throw new UserException("La valeur de $k est vide");
}
-
+
switch($k)
{
case 'type_facture':
- if (!array_key_exists($datas[$k], $this->types)) {
- throw new UserException("$k est de type non-attendue ($data).");
- }
- if ($datas[$k] < 2) {
- $fac = true;
- $cerfa = false;
- $recu = false;
- }
- elseif ($datas[$k] == 2) {
- $fac = false;
- $cerfa = true;
- $recu = false;
- }
- elseif ($datas[$k] == 3) {
- $fac = false;
- $cerfa = false;
- $recu = true;
- }
- break;
+ if (!array_key_exists($datas[$k], $this->types)) {
+ throw new UserException("$k est de type non-attendue ($data).");
+ }
+ if ($datas[$k] < 2) {
+ $fac = true;
+ $cerfa = false;
+ $recu = false;
+ }
+ elseif ($datas[$k] == 2) {
+ $fac = false;
+ $cerfa = true;
+ $recu = false;
+ }
+ elseif ($datas[$k] == 3) {
+ $fac = false;
+ $cerfa = false;
+ $recu = true;
+ }
+ break;
case 'receveur_membre':
case 'reglee':
case 'archivee':
- if ($datas[$k] != 1 && $datas[$k] != 0) {
- throw new UserException("$k est de valeur non-attendue ($data).");
- }
- break;
+ if ($datas[$k] != 1 && $datas[$k] != 0) {
+ throw new UserException("$k est de valeur non-attendue ($data).");
+ }
+ break;
case 'receveur_id':
- if (!is_numeric($datas[$k]) || $datas[$k] < 0) {
- throw new UserException("L'id du receveur est non-attendu ($data).");
- }
- break;
+ if (!is_numeric($datas[$k]) || $datas[$k] < 0) {
+ throw new UserException("L'id du receveur est non-attendu ($data).");
+ }
+ break;
case 'date_emission':
- $datas[$k] = \DateTime::createFromFormat('!d/m/Y', $data)->format('Y-m-d');
- break;
+ $datas[$k] = \DateTime::createFromFormat('!d/m/Y', $data)->format('Y-m-d');
+ break;
case 'date_echeance':
- $datas[$k] = \DateTime::createFromFormat('!d/m/Y', $data)->format('Y-m-d');
- if (DateTime::createFromFormat('!Y-m-d', $datas[$k])->format('U') < DateTime::createFromFormat('!Y-m-d', $datas['date_emission'])->format('U'))
- {
- throw new UserException("La date d'échéance est antérieure à la date d'émission ($data).");
- }
- break;
+ $datas[$k] = \DateTime::createFromFormat('!d/m/Y', $data)->format('Y-m-d');
+ if (DateTime::createFromFormat('!Y-m-d', $datas[$k])->format('U') < DateTime::createFromFormat('!Y-m-d', $datas['date_emission'])->format('U'))
+ {
+ throw new UserException("La date d'échéance est antérieure à la date d'émission ($data).");
+ }
+ break;
case 'moyen_paiement':
- if (!array_key_exists($datas[$k], $this->listMoyensPaiement())) {
- throw new UserException("Le moyen de paiement ne correspond pas à la liste interne ($data).");
- }
- break;
+ if (!array_key_exists($datas[$k], $this->listMoyensPaiement())) {
+ throw new UserException("Le moyen de paiement ne correspond pas à la liste interne ($data).");
+ }
+ break;
case 'contenu':
- if ($fac)
- {
- if (!is_array($datas[$k]) || empty($datas[$k])) {
- throw new UserException("Le contenu du document est vide ($data).");
- }
- $total = 0;
- foreach($datas[$k] as $g => $r)
+ if ($fac)
{
- if (empty($r['designation']) && empty($r['prix']))
- {
- unset($datas[$k][$g]);
- unset($datas[$k]['prix']);
- continue;
+ if (!is_array($datas[$k]) || empty($datas[$k])) {
+ throw new UserException("Le contenu du document est vide ($data).");
}
- elseif (! is_numeric($r['prix']) && empty($r['prix']))
+ $total = 0;
+ foreach($datas[$k] as $g => $r)
{
- $datas[$k]['prix'] = 0;
- }
- elseif (empty($r['designation'])) {
- throw new UserException("Une au moins des désignations est absente.");
+ if (empty($r['designation']) && empty($r['prix']))
+ {
+ unset($datas[$k][$g]);
+ unset($datas[$k]['prix']);
+ continue;
+ }
+ elseif (empty($r['prix']))
+ {
+ $datas[$k]['prix'] = 0;
+ }
+
+ if (!is_int($r['prix']))
+ {
+ throw new UserException('Un (ou plus) des prix n\'est pas un entier.');
+ }
+
+ $total += $r['prix'];
}
- if (!is_int($r['prix']))
+ if($fac && !$total)
{
- throw new UserException('Un (ou plus) des prix n\'est pas un entier.');
+ throw new UserException("Toutes les désignations/prix sont vides.");
}
-
- $total += $r['prix'];
}
-
- if ($fac && count($datas['contenu']) == 0)
+ elseif ($cerfa)
{
- throw new UserException("Toutes les désignations/prix sont vides.");
- }
- }
- elseif ($cerfa)
- {
- }
- elseif ($recu)
- {
- // $fields = ['id', 'intitule', 'date', 'expiration'];
- // foreach ($datas[$k]as $)
- }
- $datas[$k] = json_encode($datas[$k]);
- break;
+ }
+ elseif ($recu)
+ {
+ // $fields = ['id', 'intitule', 'date', 'expiration'];
+ // foreach ($datas[$k]as $)
+ }
+ $datas[$k] = json_encode($datas[$k]);
+ break;
case 'total':
- if ($cerfa && $datas[$k] < 1) {
- throw new UserException('Le total ne peut être inférieur à 1€ pour les reçus (bug encore non résolu).');
- }
- if ($fac && !isset($datas['contenu'])) {
- throw new UserException("Pas de contenu fourni pour vérifier le total.");
- }
- if ($fac && $total != $datas[$k])
- {
- throw new UserException("Les totaux sont différents ($total != $datas[$k].");
- }
- break;
+ if ($cerfa && $datas[$k] < 1) {
+ throw new UserException('Le total ne peut être inférieur à 1€ pour les reçus (bug encore non résolu).');
+ }
+ if ($fac && !isset($datas['contenu'])) {
+ throw new UserException("Pas de contenu fourni pour vérifier le total.");
+ }
+ if ($fac && $total != $datas[$k])
+ {
+ throw new UserException("Les totaux sont différents ($total != $datas[$k].");
+ }
+ break;
}
}
}
@@ -283,7 +287,7 @@ class Facture
$r = $db->first('SELECT * FROM plugin_facturation_factures WHERE id = ? LIMIT 1;', (int)$id);
if(!$r)
- {
+ {
throw new UserException("Pas de document retournée avec cet id.");
}
@@ -318,17 +322,9 @@ class Facture
return $r;
}
- public function list($all = true): DynamicList
+ public function list(): DynamicList
{
$id_field = \Paheko\Users\DynamicFields::getNameFieldsSQL('u');
- $plugin_name = preg_replace('/^.*\/(\w+)\/$/', '${1}', \Paheko\PLUGIN_ADMIN_URL);
- $plugin = \Paheko\Plugins::get($plugin_name);
-
- // adresse et ville peuvent être redéfinies dans la configuration du plugin
- $adresse_client = $plugin->getConfig('adresse_client');
- if ($adresse_client == null) { $adresse = 'u.adresse'; } else { $adresse = 'u.' . $adresse_client; }
- $ville_client = $plugin->getConfig('ville_client');
- if ($ville_client == null) { $ville = 'u.ville'; } else { $ville = 'u.' . $ville_client; }
$columns = [
// Sélectionner cette colonne, mais ne pas la mettre dans la liste des colonnes
@@ -343,9 +339,6 @@ class Facture
'receveur_id' => [
],
// Créer une colonne virtuelle
- 'select' => [
- 'label' => '',
- ],
'type' => [
'label' => 'Type',
'select' => null,
@@ -356,18 +349,15 @@ class Facture
],
'receveur' => [
'label' => 'Receveur',
- // l'identité du membre peut être redéfinie dans la configuration des membres
- 'select' => sprintf('CASE WHEN receveur_membre THEN CASE %s WHEN "" THEN "** ABSENT **" ELSE %s END ELSE c.nom END', $id_field, $id_field),
+ 'select' => sprintf('CASE WHEN receveur_membre THEN %s ELSE c.nom END', $id_field),
],
'receveur_adresse' => [
- // l'adresse peut être redéfinie dans la configuration du plugin
'label' => 'Adresse',
- 'select' => sprintf('CASE WHEN receveur_membre THEN %s ELSE c.adresse END', $adresse),
+ 'select' => 'CASE WHEN receveur_membre THEN u.adresse ELSE c.adresse END',
],
'receveur_ville' => [
- // la ville peut être redéfinie dans la configuration du plugin
'label' => 'Ville',
- 'select' => sprintf('CASE WHEN receveur_membre THEN %s ELSE c.ville END', $ville),
+ 'select' => 'CASE WHEN receveur_membre THEN u.ville ELSE c.ville END',
],
'date_emission' => [
'label' => 'Émission',
@@ -379,6 +369,9 @@ class Facture
'reglee' => [
'label' => 'Réglée',
],
+ 'archivee' => [
+ 'label' => 'Archivée',
+ ],
'moyen_paiement' => [
'label' => 'Moyen de paiement',
'select' => 'mp.nom',
@@ -396,13 +389,9 @@ class Facture
LEFT JOIN users AS u ON f.receveur_membre = 1 AND u.id = f.receveur_id
LEFT JOIN plugin_facturation_clients AS c ON f.receveur_membre = 0 AND c.id = f.receveur_id';
- if ($all) {
- $where = "f.archivee != TRUE";
- } else {
- $where = "f.archivee == TRUE";
- }
- $list = new DynamicList($columns, $tables, $where);
+ $list = new DynamicList($columns, $tables);
$list->orderBy('date_emission', true);
+ $list->setCount('COUNT(f.id)');
$currency = Config::getInstance()->monnaie;
@@ -410,14 +399,15 @@ class Facture
// Remplir la colonne virtuelle
$row->type = self::TYPES_NAMES[$row->type_facture] ?? null;
$row->reglee = $row->reglee ? 'Réglée' : 'Non';
+ $row->archivee = $row->archivee ? 'Archivée' : 'Non';
// Remplir le contenu
$content = json_decode((string)$row->contenu);
if ($row->type_facture == COTIS && isset($content->intitule, $content->souscription)) {
$row->contenu = sprintf("Cotisation %s\nSouscrite le %s",
- $content->intitule,
- Utils::date_fr($content->souscription, 'd/m/Y')
+ $content->intitule,
+ Utils::date_fr($content->souscription, 'd/m/Y')
);
}
elseif ($row->type_facture != CERFA) {
@@ -502,7 +492,7 @@ class Facture
public $recu_fields = ['id', 'label', 'amount', 'date', 'expiry', 'paid', 'paid_amount'];
- public function getCotis(int $user_id, ?int $su_id = null)
+ public function getCotis(int $user_id, int $su_id = null)
{
$where = 'WHERE su.id_user = ?';
if (null !== $su_id)
@@ -516,7 +506,7 @@ class Facture
LEFT JOIN services_fees sf ON sf.id = su.id_fee
LEFT JOIN acc_transactions_users tu ON tu.id_service_user = su.id
LEFT JOIN acc_transactions_lines tl ON tl.id_transaction = tu.id_transaction
- '.$where.'
+ '.$where.'
GROUP BY su.id
ORDER BY su.date;';
@@ -536,41 +526,26 @@ class Facture
return $db->getGrouped($query);
}
}
-
+
/* modif DD -- lecture et retour des textes de CERFA -- */
public function listTextesCerfa($menu = true)
{
$db = DB::getInstance();
-
+
$sel = ($menu) ? 'id, menu' : 'id, texte';
$query = 'SELECT '.$sel.' FROM "plugin_facturation_txt_cerfa" WHERE 1 ORDER BY id ;';
return $db->getAssoc($query);
}
-
+
public function getMoyenPaiement($code)
{
$db = DB::getInstance();
return $db->firstColumn('SELECT nom FROM plugin_facturation_paiement WHERE code = ?;', $code);
}
-
+
public function delete($id)
{
return DB::getInstance()->delete('plugin_facturation_factures', 'id = '. (int)$id);
}
-
- public function archiver($id, $archive)
- {
- $f = $this->get($id);
- $f->archivee = $archive;
- $f->date_emission = $f->date_emission->format('d/m/Y');
- $f->date_echeance = $f->date_echeance->format('d/m/Y');
- $id = $f->id;
- unset($f->id);
- $datas = (array)$f;
- $this->_checkFields($datas);
- $datas["id"] = $id;
- $db = DB::getInstance();
- return $db->update('plugin_facturation_factures', $datas, $db->where('id', (int)$id));
- }
}
diff --git a/plugin.ini b/plugin.ini
index acd3b80..3bf1573 100644
--- a/plugin.ini
+++ b/plugin.ini
@@ -1,8 +1,8 @@
name="Facturation"
-description="Permet d'éditer des factures et devis à ses membres ainsi qu'à une base de clients supplémentaire."
-author="zou ; adapté par Jean-Christophe Engel"
-url="https://gitea.zaclys.com/lesanges/facturation"
-version="0.17"
+description="Permet d'éditer des factures, devis et reçus à ses membres ainsi qu'à une base de clients supplémentaire."
+author="zou ; adapté par jce"
+url="https://git.roflcopter.fr/lesanges/paheko-plugin-facturation"
+version="0.8.5"
menu=true
restrict_section="accounting"
restrict_level="read"
diff --git a/templates/_form.tpl b/templates/_form.tpl
index 4ab076e..3e6029d 100644
--- a/templates/_form.tpl
+++ b/templates/_form.tpl
@@ -3,7 +3,7 @@
{$list->getHTMLPagination()|raw}
-
- {if $archive}
- {csrf_field key="desarchiver_factures"}
- {button type="submit" name="desarchiver" label="Désarchiver les factures sélectionnées" shape="right"}
- {else}
- {csrf_field key="archiver_factures"}
- {button type="submit" name="archiver" label="Archiver les factures sélectionnées" shape="right"}
- {/if}
-
- Export de la liste :
- {linkbutton href="?export=csv" label="Export CSV" shape="download"}
- {linkbutton href="?export=ods" label="Export tableur" shape="download"}
-
-
+
+ Export de la liste :
+ {linkbutton href="?export=csv" label="Export CSV" shape="download"}
+ {linkbutton href="?export=ods" label="Export tableur" shape="download"}
+
{else}
Aucun document, vous pouvez commencer par {link href="facture_ajouter.php" label="créer un nouveau document"}.
{/if}
-
+
{include file="_foot.tpl"}
diff --git a/upgrade.php b/upgrade.php
index f47ac27..5df22ea 100644
--- a/upgrade.php
+++ b/upgrade.php
@@ -6,13 +6,13 @@ use Paheko\Entities\Files\File;
$db = DB::getInstance();
$old_version = $plugin->oldVersion();
-error_log("upgrade:: à partir de la version = " . $old_version);
+error_log("upgrade::version = " . $old_version);
// 0.2.0 - Stock le contenu en json plutôt qu'en serialized
if (version_compare($old_version, '0.2.0', '<'))
{
$r = (array) DB::getInstance()->get('SELECT * FROM plugin_facturation_factures');
-
+
foreach ($r as $e) {
$e->contenu =json_encode(unserialize((string) $e->contenu));
$db->update('plugin_facturation_factures', $e, $db->where('id', (int)$e->id));
@@ -33,7 +33,7 @@ if (version_compare($old_version, '0.3.0', '<'))
$db->exec('DROP TABLE `plugin_facturation_config`;');
}
-// 0.4.0 -
+// 0.4.0 -
if (version_compare($old_version, '0.4.0', '<'))
{
$db->exec(<<listAll();
foreach($factures as $k=>$f)
@@ -119,13 +119,13 @@ EOT
foreach($f->contenu as $line => $content)
{
// Petit bug qui peut arriver avec des contenus mal enregistrés en db
- if (is_int($content))
- {
- continue;
- }
+ if (is_int($content))
+ {
+ continue;
+ }
$contenu[] = ['designation' => $content['designation'],
- 'prix' => (int) ($content['prix'] * 100) ];
+ 'prix' => (int) ($content['prix'] * 100) ];
}
$f->contenu = $contenu;
@@ -143,7 +143,7 @@ EOT
}
}
-// 0.6.2 -
+// 0.6.2 -
if (version_compare($old_version, '0.6.2', '<'))
{
define('DEVIS', 0);
@@ -168,14 +168,14 @@ if (version_compare($old_version, '0.6.2', '<'))
INSERT OR IGNORE INTO plugin_facturation_txt_cerfa
("id","menu","texte") VALUES ('0','Aucun','');
INSERT OR IGNORE INTO plugin_facturation_txt_cerfa
- ("id","menu","texte")
+ ("id","menu","texte")
VALUES ('1','HelloAsso','Don via HelloAsso');
INSERT OR IGNORE INTO plugin_facturation_txt_cerfa
- ("id","menu","texte")
+ ("id","menu","texte")
VALUES ('2','Frais de déplacement',
'Renonciation aux remboursements de frais de déplacement');
INSERT OR IGNORE INTO plugin_facturation_txt_cerfa
- ("id","menu","texte")
+ ("id","menu","texte")
VALUES ('3','Don en nature','Don en nature');
EOT
);
@@ -211,6 +211,7 @@ if (version_compare($old_version, '0.8.1', '<'))
}
// 0.8.5 Ajout champs SIREN/SIRET à la table clients
+
if (version_compare($old_version, '0.8.5', '<'))
{
$db->exec(<<exec(<<exec(<<iterate($sql) as $client)
- {
- $db->insert('plugin_facturation_clients_tmp', $client);
- }
- // remplacer l'ancienne table par la nouvelle
- $db->exec(<<exec(<<iterate($sql) as $facture)
- {
- $db->insert('plugin_facturation_factures_tmp', $facture);
- }
- // remplacer l'ancienne table par la nouvelle
- $db->exec(<< |