diff --git a/data/schema.sql b/data/schema.sql index 59af3f8..43b7d84 100644 --- a/data/schema.sql +++ b/data/schema.sql @@ -10,9 +10,9 @@ CREATE TABLE IF NOT EXISTS plugin_facturation_factures ( archivee INTEGER DEFAULT 0, -- bool moyen_paiement TEXT NOT NULL, contenu TEXT NOT NULL, - total REAL DEFAULT 0, + total REAL DEFAULT 0 - FOREIGN KEY(moyen_paiement) REFERENCES compta_moyens_paiement(code) + -- FOREIGN KEY(moyen_paiement) REFERENCES compta_moyens_paiement(code) ); CREATE TABLE IF NOT EXISTS plugin_facturation_clients ( @@ -27,6 +27,22 @@ CREATE TABLE IF NOT EXISTS plugin_facturation_clients ( email TEXT ); + +CREATE TABLE IF NOT EXISTS plugin_facturation_paiement +-- Moyens de paiement +( + code TEXT NOT NULL PRIMARY KEY, + nom TEXT NOT NULL +); + +--INSERT INTO compta_moyens_paiement (code, nom) VALUES ('AU', 'Autre'); +INSERT OR IGNORE INTO plugin_facturation_paiement (code, nom) VALUES ('CB', 'Carte bleue'); +INSERT OR IGNORE INTO plugin_facturation_paiement (code, nom) VALUES ('CH', 'Chèque'); +INSERT OR IGNORE INTO plugin_facturation_paiement (code, nom) VALUES ('ES', 'Espèces'); +INSERT OR IGNORE INTO plugin_facturation_paiement (code, nom) VALUES ('PR', 'Prélèvement'); +INSERT OR IGNORE INTO plugin_facturation_paiement (code, nom) VALUES ('TI', 'TIP'); +INSERT OR IGNORE INTO plugin_facturation_paiement (code, nom) VALUES ('VI', 'Virement'); + -- CREATE TABLE IF NOT EXISTS plugin_facturation_produits ( -- id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, -- designation TEXT, diff --git a/data/schema_remove.sql b/data/schema_remove.sql index 87f1d60..2a3e2dc 100644 --- a/data/schema_remove.sql +++ b/data/schema_remove.sql @@ -1,3 +1,4 @@ DROP TABLE `plugin_facturation_factures`; DROP TABLE `plugin_facturation_clients`; +DROP TABLE `plugin_facturation_paiement`; -- DROP TABLE `plugin_facturation_produits`; \ No newline at end of file diff --git a/garradin_plugin.ini b/garradin_plugin.ini index 3eac1d7..08b577b 100644 --- a/garradin_plugin.ini +++ b/garradin_plugin.ini @@ -2,7 +2,7 @@ nom="Facturation" description="Permet d'éditer des factures, devis et reçus à ses membres ainsi qu'à une base de clients supplémentaire." auteur="zou" url="https://gitlab.com/ramoloss/garradin-plugin-facturation/" -version="0.3.0" +version="0.4.0" menu=1 config=1 min_version="0.9.2" \ No newline at end of file diff --git a/lib/Client.php b/lib/Client.php index d3ca58d..1bd92b6 100644 --- a/lib/Client.php +++ b/lib/Client.php @@ -45,7 +45,7 @@ class Client if($key == 'ville') { - $data[$key] = mb_strtoupper($data[$key]); + $data[$key] = strtoupper($data[$key]); } elseif ($key == 'code_postal') { diff --git a/lib/Facture.php b/lib/Facture.php index 4f3068f..1745828 100644 --- a/lib/Facture.php +++ b/lib/Facture.php @@ -2,8 +2,10 @@ namespace Garradin\Plugin\Facturation; +use DateTime; use Garradin\DB; use Garradin\UserException; +use Garradin\Services\Services_User; class Facture { @@ -21,12 +23,28 @@ class Facture 'total' ]; - public $type = [ - 0 => 'devis', - 1 => 'facture', - 2 => 'cerfa', - 3 => 'cotis', - ]; + public $types = [ + 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() { @@ -51,11 +69,10 @@ class Facture throw new UserException("La valeur de $k est vide"); } - switch($k) { case 'type_facture': - if (!array_key_exists($datas[$k], $this->type)) { + if (!array_key_exists($datas[$k], $this->types)) { throw new UserException("$k est de type non-attendue ($data)."); } if ($datas[$k] < 2) { @@ -87,24 +104,19 @@ class Facture } break; case 'date_emission': - if (!strtotime($datas[$k])) { - throw new UserException("La date d'émission est non-attendue ($data)."); - } + $datas[$k] = \DateTime::createFromFormat('!d/m/Y', $data)->format('Y-m-d'); break; case 'date_echeance': - if (!strtotime($datas[$k])) { - throw new UserException("La date d'émission est non-attendue ($data)."); - } - if (isset($datas['date_emission']) && (strtotime($datas[$k]) < strtotime($datas['date_emission']))) { + $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': - $cats = new \Garradin\Compta\Categories; - if (!array_key_exists($datas[$k], $cats->listMoyensPaiement())) { + if (!array_key_exists($datas[$k], $this->listMoyensPaiement())) { throw new UserException("Le moyen de paiement ne correspond pas à la liste interne ($data)."); } - unset($cats); break; case 'contenu': if ($fac) @@ -176,9 +188,7 @@ class Facture { $db = DB::getInstance(); - $r = $db->first('SELECT *, strftime(\'%s\', date_emission) AS date_emission, - strftime(\'%s\', date_echeance) AS date_echeance - FROM plugin_facturation_factures WHERE id = ? LIMIT 1;', (int)$id); + $r = $db->first('SELECT * FROM plugin_facturation_factures WHERE id = ? LIMIT 1;', (int)$id); if(!$r) { @@ -190,6 +200,12 @@ class Facture $r->contenu = json_decode($r->contenu, true); } + $r->date_emission = \DateTime::createFromFormat('!Y-m-d', $r->date_emission); + if ($r->date_echeance) + { + $r->date_echeance= \DateTime::createFromFormat('!Y-m-d', $r->date_echeance); + } + return $r; } @@ -276,26 +292,47 @@ class Facture // ** Pour type reçu ** - public $recu_fields = ['id', 'intitule', 'montant', 'date', 'expiration']; + public $recu_fields = ['id', 'label', 'amount', 'date', 'expiry', 'paid', 'paid_amount']; - public function getCotis($membre_id = 1) + public function getCotis(int $user_id, int $su_id = null) { - // C un peu overkill nn? - // Copié/modifié de Membres\Cotisations::listSubscriptionsForMember($id) - $db = DB::getInstance(); - return $db->get('SELECT cm.id, c.intitule, strftime(\'%s\', c.debut) AS debut, strftime(\'%s\', c.fin) AS fin, c.montant, strftime(\'%s\', cm.date) AS date, - CASE WHEN c.duree IS NOT NULL THEN date(cm.date, \'+\'||c.duree||\' days\') >= date() - WHEN c.fin IS NOT NULL THEN (cm.id IS NOT NULL AND cm.date <= c.fin AND cm.date >= c.debut) - WHEN cm.id IS NOT NULL THEN 1 ELSE 0 END AS a_jour, - strftime(\'%s\', CASE WHEN c.duree IS NOT NULL THEN date(cm.date, \'+\'||c.duree||\' days\') - WHEN c.fin IS NOT NULL THEN c.fin ELSE 1 END ) AS expiration, - (julianday(date()) - julianday(CASE WHEN c.duree IS NOT NULL THEN date(cm.date, \'+\'||c.duree||\' days\') - WHEN c.fin IS NOT NULL THEN c.fin END)) AS nb_jours - FROM cotisations_membres AS cm - INNER JOIN cotisations AS c ON c.id = cm.id_cotisation - WHERE cm.id_membre = ? - AND ((c.fin IS NOT NULL AND cm.date <= c.fin AND cm.date >= c.debut) OR c.fin IS NULL) - GROUP BY cm.id_cotisation - ORDER BY cm.date DESC;', (int)$membre_id); + $where = 'WHERE su.id_user = ?'; + if (null !== $su_id) + { + $where .= ' AND su.id = '.$su_id; + } + + $sql = 'SELECT su.id, s.label, su.date, MAX(su.expiry_date) as expiry, sf.label as fee, sf.amount as amount, su.paid, SUM(tl.debit) as paid_amount + FROM services_users su + INNER JOIN services s ON s.id = su.id_service + 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.' + GROUP BY su.id + ORDER BY su.date;'; + + return DB::getInstance()->get($sql, $user_id); } + + public function listMoyensPaiement($assoc = false) + { + $db = DB::getInstance(); + + $query = 'SELECT code, nom FROM plugin_facturation_paiement ORDER BY nom COLLATE NOCASE;'; + + if ($assoc) { + return $db->getAssoc($query); + } + else { + return $db->getGrouped($query); + } + } + + public function getMoyenPaiement($code) + { + $db = DB::getInstance(); + return $db->firstColumn('SELECT nom FROM plugin_facturation_paiement WHERE code = ?;', $code); + } + } diff --git a/templates/_js.tpl b/templates/_js.tpl index fa2d0a1..6c2da5c 100644 --- a/templates/_js.tpl +++ b/templates/_js.tpl @@ -15,12 +15,26 @@ function plus(){ var newdiv = document.createElement('tr'); newdiv.innerHTML = document.getElementById('Line1').innerHTML; + newdiv.querySelector('.fact_rm_line button').onclick = function(){ + this.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode); + updateSum(); + }; document.getElementById('Lines').appendChild(newdiv); } plus(); + updateSum(); $('#ajouter_ligne').onclick = plus; + a = document.querySelectorAll('[name="remove_line"]'); + l = a.length; + for(i = 0; i < l; i++) { + a[i].onclick = function(){ + this.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode); + updateSum(); + }; + } + function changeTypeSaisie(type) { g.toggle(['.type_client', '.type_membre'], false); @@ -39,5 +53,40 @@ } } ()); + + + // Hide type specific parts of the form + function hideAllTypes() { + g.toggle('[data-types]', false); + } + + // Toggle parts of the form when a type is selected + function selectType(v) { + hideAllTypes(); + g.toggle('[data-types~=t' + v + ']', true); + g.toggle('[data-types=all-but-advanced]', v != 0); + // Disable required form elements, or the form won't be able to be submitted + $('[data-types=all-but-advanced] input[required]').forEach((e) => { + e.disabled = v == 'advanced' ? true : false; + }); + + } + + var radios = $('fieldset input[type=radio][name=type]'); + + radios.forEach((e) => { + e.onchange = () => { + document.querySelectorAll('fieldset').forEach((e, k) => { + if (k == 0 || e.dataset.types) return; + g.toggle(e, true); + g.toggle('p.submit', true); + }); + console.log(e.value); + selectType(e.value); + }; + }); + + hideAllTypes(); {/literal} + selectType({$radio.type}); \ No newline at end of file diff --git a/templates/_list_actions.tpl b/templates/_list_actions.tpl index 8a16600..c99c888 100644 --- a/templates/_list_actions.tpl +++ b/templates/_list_actions.tpl @@ -1,13 +1,13 @@ - {if $session->canAccess('membres', Membres::DROIT_ADMIN)}{/if} - - Pour les membres cochés : + + + + PAS FONCTIONNEL - Pour les client·es coché·es : {csrf_field key="membres_action"}