Compare commits
No commits in common. "3374781ec95824462cf6ba4b85ef512dc7424944" and "717fc5320f16fc15bb16778113ac63ac7915e968" have entirely different histories.
3374781ec9
...
717fc5320f
20 changed files with 541 additions and 951 deletions
87
CHANGELOG
Normal file
87
CHANGELOG
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
0.8.4
|
||||||
|
- Correction bug si identité définie par plusieurs champs
|
||||||
|
- Correction bug oubli choix receveur
|
||||||
|
- Mise-à-jour Changelog
|
||||||
|
|
||||||
|
0.8.3 :
|
||||||
|
- Mise-à-jour README
|
||||||
|
|
||||||
|
0.8.2 :
|
||||||
|
- Tri des membres en ordre alphabétique
|
||||||
|
- Élimination de plusieurs bugs
|
||||||
|
|
||||||
|
0.8.1 :
|
||||||
|
- reprise du code par jce (lesanges@zaclys.net ; https://git.roflcopter.fr/lesanges/paheko-plugin-facturation)
|
||||||
|
- Achèvement migration vers Paheko 1.3
|
||||||
|
|
||||||
|
0.7.4 :
|
||||||
|
- Corrections de dépréciations PHP, le plugin être compatible PHP8.2
|
||||||
|
- Quelques problèmes réglés de formulaires invalidables
|
||||||
|
- Nettoyage du code et factorization des facture_{ajouter,modifier}.php
|
||||||
|
|
||||||
|
0.7.3 :
|
||||||
|
- Règle le problème de formulaire bloqué que je croyais avoir résolu la dernière fois :)
|
||||||
|
|
||||||
|
0.7.2 :
|
||||||
|
- Petite correction du moyen de paiement qui n'était pas retenu à cause d'un doublon dans le formulaire.
|
||||||
|
|
||||||
|
0.7.1 :
|
||||||
|
- Ajout d'une case dans la config pour choisir si les factures sont TTC ou HT
|
||||||
|
- Le formulaire de document était cassé à cause d'un champs obligatoire invisible
|
||||||
|
|
||||||
|
0.7.0 :
|
||||||
|
- Modernisation des formulaires, de la fiche client
|
||||||
|
- Liste dynamiques des documents et des clients + export CSV/ODS
|
||||||
|
- Numérotation automatique des documents
|
||||||
|
- Ajout du bouton Dupliquer sur les documents
|
||||||
|
- Divers fixes
|
||||||
|
- Changements de la gestion des CERFA :
|
||||||
|
- organisation du champ `contenu` pour gérer les paramètres de CERFA sous la forme {"forme":"3","nature":"1","texte":"1"}
|
||||||
|
- ajout d'une table `plugin_facturation_txt_cerfa` pour stocker les textes explicatifs additionnels
|
||||||
|
- formulaires adaptés aux CERFA pour permettre le choix de la forme, de la nature, du mode de versement et du texte explicatif éventuel
|
||||||
|
|
||||||
|
0.6.0 :
|
||||||
|
- Génération des PDF par Garradin, on se débarasse de mPDF et l'installation du plugin devient standard
|
||||||
|
- Fix #32 (prix total affiché incorrect), en stockant les prix sous formes d'entiers à la manière de Garradin
|
||||||
|
- Possibilité de supprimer un document
|
||||||
|
- Fix #34, problème sélection membre
|
||||||
|
|
||||||
|
0.5.0 :
|
||||||
|
Mise à jour rapide du plugin pour Garradin 1.1.
|
||||||
|
La gestion de la signature dans les reçus fiscaux a été désactivés pour l'instant (changements de méthodes avec Garradin 1.1, flemme de m'y pencher pour le moment)
|
||||||
|
|
||||||
|
0.4.1 :
|
||||||
|
Release mineure qui corrige un problème qui apparaît, selon la configuration de PHP (?), lors de la migration depuis une version précédente.
|
||||||
|
|
||||||
|
0.4.0 :
|
||||||
|
Cette version a pour principal but d'être compatible avec Garradin 1.0 et de ne pas empêcher la mise à jour d'une ancienne installation comprenant une ancienne version du plugin.
|
||||||
|
Le formulaire de création/édition de document a été modifié pour simplifier l'expérience utilisateur : tout les types de documents se font depuis le même formulaire, suivi des évolutions graphiques/ergonomiques de Garradin.
|
||||||
|
|
||||||
|
0.3.0 :
|
||||||
|
- Ajouts des reçus fiscaux et des reçus sur des cotisations :
|
||||||
|
- Génération du cerfa pour reçu fiscaux, paramètres disponibles dans la config
|
||||||
|
- Ajout d'une page d'aide
|
||||||
|
- L'identité des destinataires membres est maintenant définie selon la configuration de Garradin, et non plus de manière statique sur la colonne "nom"
|
||||||
|
- Suppression de la classe Facturation\Config pour utiliser celle que propose Garradin
|
||||||
|
- Ajout du champs de numéro RNA
|
||||||
|
|
||||||
|
|
||||||
|
0.2.0 :
|
||||||
|
- Stockage des désignations des documents changé de serialize à json_decode
|
||||||
|
- Petites améliorations sur les formulaires
|
||||||
156
CHANGELOG.md
156
CHANGELOG.md
|
|
@ -1,156 +0,0 @@
|
||||||
# Changelog
|
|
||||||
|
|
||||||
## v0.25 - 2026-05-15 - @Jason
|
|
||||||
### Added
|
|
||||||
- Options d'affichage du rendu facture
|
|
||||||
|
|
||||||
## v0.19 - 2026-05-15 - @Jason
|
|
||||||
### Fixed
|
|
||||||
- Réparation des schémas partiellement migrés
|
|
||||||
|
|
||||||
## v0.18 - 2026-05-15 - @Jason
|
|
||||||
### Changed
|
|
||||||
- améliorations proposées par Jason (jojodavo@gmail.com)
|
|
||||||
### Added
|
|
||||||
- Personnalisation du rendu des factures
|
|
||||||
|
|
||||||
## v0.17 - 2026-02-26 - @jce
|
|
||||||
### Added
|
|
||||||
- archivage factures
|
|
||||||
|
|
||||||
## v0.15 - 2025-03-25 - @jce
|
|
||||||
### Added
|
|
||||||
- champ contact à la fiche client
|
|
||||||
|
|
||||||
## v0.13 - 2025-02-26 - @jce
|
|
||||||
### Changed
|
|
||||||
- Amélioration contrôles saisie facture
|
|
||||||
|
|
||||||
## v0.12 - 2025-01-04 - @jce
|
|
||||||
### Added
|
|
||||||
- Mollie à la table moyens de paiement
|
|
||||||
|
|
||||||
## v0.11 - 2024-10-19 - @jce
|
|
||||||
### Changed
|
|
||||||
- mention finale pour devis
|
|
||||||
|
|
||||||
## v0.9 - 2024-08-30 - @jce
|
|
||||||
### Added
|
|
||||||
- possibilité choisir champs identité et adresse membre
|
|
||||||
|
|
||||||
## v0.8.8 - 2024-08-13 - @jce
|
|
||||||
### Added
|
|
||||||
- impression logo avec option de configuration
|
|
||||||
### Changed
|
|
||||||
- Adaptation Readme à la situation actuelle
|
|
||||||
|
|
||||||
## v0.8.6 - 2024-03-29 - @jce
|
|
||||||
### Fixed
|
|
||||||
- Erreur si pas de prix saisi
|
|
||||||
|
|
||||||
## v0.8.5 - 2024-01-12 - @jce
|
|
||||||
### Added
|
|
||||||
- numéro SIREN/SIRET pour les clients
|
|
||||||
- pagination liste factures
|
|
||||||
|
|
||||||
## v0.8.4 - 2024-01-11 - @jce
|
|
||||||
### Fixed
|
|
||||||
- Bug si identité définie par plusieurs champs
|
|
||||||
- Bug oubli choix receveur
|
|
||||||
|
|
||||||
## v0.8.2 - 2024-01-10 - @jce
|
|
||||||
### Added
|
|
||||||
- Tri des membres en ordre alphabétique
|
|
||||||
### Fixed
|
|
||||||
- Élimination de plusieurs bugs
|
|
||||||
|
|
||||||
## v0.8.1 - 2023-11-03 - @jce
|
|
||||||
### Changed
|
|
||||||
- reprise du code par jce (lesanges@zaclys.net ; https://gitea.zaclys.com/lesanges/facturation)
|
|
||||||
- Achèvement migration vers Paheko 1.3
|
|
||||||
|
|
||||||
## v0.7.4 - 2023-04-16 - @noizette
|
|
||||||
### Fixed
|
|
||||||
- Corrections de dépréciations PHP, le plugin être compatible PHP8.2
|
|
||||||
### Changed
|
|
||||||
- Quelques problèmes réglés de formulaires invalidables
|
|
||||||
- Nettoyage du code et factorization des facture_{ajouter,modifier}.php
|
|
||||||
|
|
||||||
## v0.7.3 - 2022-03-03 - @noizette
|
|
||||||
### Fixed
|
|
||||||
- Règle le problème de formulaire bloqué que je croyais avoir résolu la dernière fois :)
|
|
||||||
|
|
||||||
## v0.7.2 - 2022-01-31 - @noizette
|
|
||||||
### Fixed
|
|
||||||
- Petite correction du moyen de paiement qui n'était pas retenu à cause d'un doublon dans le formulaire.
|
|
||||||
|
|
||||||
## v0.7.1 - 2022-01-10 - @noizette
|
|
||||||
### Added
|
|
||||||
- ajout d'une case dans la config pour choisir si les factures sont TTC ou HT
|
|
||||||
### Fixed
|
|
||||||
- Le formulaire de document était cassé à cause d'un champs obligatoire invisible
|
|
||||||
|
|
||||||
## v0.7.0 - @noizette
|
|
||||||
### Changed
|
|
||||||
- Modernisation des formulaires, de la fiche client
|
|
||||||
- Liste dynamiques des documents et des clients + export CSV/ODS
|
|
||||||
- Numérotation automatique des documents
|
|
||||||
- Divers fixes
|
|
||||||
- Changements de la gestion des CERFA :
|
|
||||||
- organisation du champ `contenu` pour gérer les paramètres de CERFA sous la forme {"forme":"3","nature":"1","texte":"1"}
|
|
||||||
- ajout d'une table `plugin_facturation_txt_cerfa` pour stocker les textes explicatifs additionnels
|
|
||||||
- formulaires adaptés aux CERFA pour permettre le choix de la forme, de la nature, du mode de versement et du texte explicatif éventuel
|
|
||||||
### Added
|
|
||||||
- Ajout du bouton Dupliquer sur les documents
|
|
||||||
|
|
||||||
## v0.6.0 - @noizette
|
|
||||||
### Changed
|
|
||||||
- Génération des PDF par Garradin, on se débarasse de mPDF et l'installation du plugin devient standard
|
|
||||||
- Possibilité de supprimer un document
|
|
||||||
### Fixed
|
|
||||||
- Fix #32 (prix total affiché incorrect), en stockant les prix sous formes d'entiers à la manière de Garradin
|
|
||||||
- Fix #34, problème sélection membre
|
|
||||||
|
|
||||||
## v0.5.0 - @noizette
|
|
||||||
### Added
|
|
||||||
Mise à jour rapide du plugin pour Garradin 1.1.
|
|
||||||
### Changed
|
|
||||||
- La gestion de la signature dans les reçus fiscaux a été désactivés
|
|
||||||
pour l'instant (changements de méthodes avec Garradin 1.1, flemme de
|
|
||||||
m'y pencher pour le moment)
|
|
||||||
|
|
||||||
## v0.4.1 - @noizette
|
|
||||||
### Fixed
|
|
||||||
- Release mineure qui corrige un problème qui apparaît, selon la
|
|
||||||
configuration de PHP (?), lors de la migration depuis une version
|
|
||||||
précédente.
|
|
||||||
|
|
||||||
## v0.4.0 - @noizette
|
|
||||||
### Changed
|
|
||||||
- Cette version a pour principal but d'être compatible avec Garradin
|
|
||||||
1.0 et de ne pas empêcher la mise à jour d'une ancienne installation
|
|
||||||
comprenant une ancienne version du plugin.
|
|
||||||
- Le formulaire de création/édition de document a été modifié pour
|
|
||||||
simplifier l'expérience utilisateur : tout les types de documents se
|
|
||||||
font depuis le même formulaire, suivi des évolutions
|
|
||||||
graphiques/ergonomiques de Garradin.
|
|
||||||
|
|
||||||
## v0.3.0 - @noizette
|
|
||||||
### Added
|
|
||||||
- Ajouts des reçus fiscaux et des reçus sur des cotisations :
|
|
||||||
- Génération du cerfa pour reçu fiscaux, paramètres disponibles dans la config
|
|
||||||
- Ajout d'une page d'aide
|
|
||||||
- Ajout du champs de numéro RNA
|
|
||||||
### Changed
|
|
||||||
- L'identité des destinataires membres est maintenant définie selon la
|
|
||||||
configuration de Garradin, et non plus de manière statique sur la
|
|
||||||
colonne "nom"
|
|
||||||
### Removed
|
|
||||||
- Suppression de la classe Facturation\Config pour utiliser celle que
|
|
||||||
propose Garradin
|
|
||||||
|
|
||||||
## v0.2.0 - @noizette
|
|
||||||
### Added
|
|
||||||
- Stockage des désignations des documents changé de serialize à json_decode
|
|
||||||
### Changed
|
|
||||||
- Petites améliorations sur les formulaires
|
|
||||||
|
|
@ -4,8 +4,30 @@ namespace Paheko;
|
||||||
|
|
||||||
require_once __DIR__ . '/_inc.php';
|
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'])) {
|
if (!isset($target) || !in_array( $target, ['new', 'edit'])) {
|
||||||
throw new \LogicException('Appel interne invalide du formulaire de document.');
|
throw new Exception('blabla illegal call'); // Fix: exception type?
|
||||||
} else {
|
} else {
|
||||||
$target = ($target === 'new' ? true:false);
|
$target = ($target === 'new' ? true:false);
|
||||||
}
|
}
|
||||||
|
|
@ -13,6 +35,7 @@ if (!isset($target) || !in_array( $target, ['new', 'edit'])) {
|
||||||
$session->requireAccess($session::SECTION_ACCOUNTING, $session::ACCESS_WRITE);
|
$session->requireAccess($session::SECTION_ACCOUNTING, $session::ACCESS_WRITE);
|
||||||
|
|
||||||
use Paheko\DB;
|
use Paheko\DB;
|
||||||
|
use stdClass;
|
||||||
use const \Paheko\Plugin\Facturation\PATTERNS_LIST;
|
use const \Paheko\Plugin\Facturation\PATTERNS_LIST;
|
||||||
|
|
||||||
$db = DB::getInstance();
|
$db = DB::getInstance();
|
||||||
|
|
@ -46,18 +69,21 @@ if ( !$target ) {
|
||||||
throw new UserException("Ce document n'existe pas.");
|
throw new UserException("Ce document n'existe pas.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$target && $f->archivee)
|
if (! $target && $f->archivee)
|
||||||
{
|
{
|
||||||
throw new UserException("Ce document est archivé, vous n'avez pas le droit de le modifier");
|
throw new UserException("Ce document est archivé, vous n'avez pas le droit de le modifier");
|
||||||
}
|
}
|
||||||
// Traitement
|
// Traitement
|
||||||
$data=[];
|
$data=[];
|
||||||
$form->runIf(f('save') && !$form->hasErrors(),
|
$form->runIf(f('save') && !$form->hasErrors(),
|
||||||
function () use (&$data, $form)
|
function () use ($client, &$data, $form)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$type = (int) f('type');
|
if ( count(f('designation')) !== count(f('prix')) )
|
||||||
|
{
|
||||||
|
throw new UserException('Nombre de désignations et de prix reçus différent.');
|
||||||
|
}
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'numero' => f('numero_facture'),
|
'numero' => f('numero_facture'),
|
||||||
|
|
@ -67,51 +93,37 @@ $form->runIf(f('save') && !$form->hasErrors(),
|
||||||
'archivee' => f('archivee') == 1?1:0,
|
'archivee' => f('archivee') == 1?1:0,
|
||||||
'moyen_paiement' => f('moyen_paiement'),
|
'moyen_paiement' => f('moyen_paiement'),
|
||||||
'nom_contact' => f('nom_contact'),
|
'nom_contact' => f('nom_contact'),
|
||||||
'numero_commande' => f('numero_commande') ?: null,
|
'toto' => 0
|
||||||
'reference_acheteur' => f('reference_acheteur') ?: null,
|
|
||||||
];
|
];
|
||||||
|
$data['type_facture'] = f('type');
|
||||||
$data['type_facture'] = $type;
|
if (in_array(f('type'), [DEVIS, FACT]))
|
||||||
|
|
||||||
if (in_array($type, [DEVIS, FACT], true))
|
|
||||||
{
|
{
|
||||||
$designations = (array) f('designation');
|
foreach(f('designation') as $k=>$value)
|
||||||
$prix = (array) f('prix');
|
|
||||||
|
|
||||||
if (count($designations) !== count($prix))
|
|
||||||
{
|
|
||||||
throw new UserException('Nombre de désignations et de prix reçus différent.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$total = 0;
|
|
||||||
|
|
||||||
foreach($designations as $k=>$value)
|
|
||||||
{
|
{
|
||||||
$prix_ligne = $prix[$k] ?? null;
|
if (empty($value) && f('prix')[$k] != null) {
|
||||||
|
throw new UserException("Il manque la désignation de la ligne " . $k+1 . " !!");
|
||||||
if (empty($value) && $prix_ligne !== null && $prix_ligne !== '') {
|
|
||||||
throw new UserException("Il manque la désignation de la ligne " . ($k + 1) . " !!");
|
|
||||||
}
|
}
|
||||||
elseif ($value != '' && ($prix_ligne === null || $prix_ligne === '')) {
|
elseif ($value != '' && f('prix')[$k] == null) {
|
||||||
throw new UserException('Il manque le prix sur la ligne '. ($k + 1) . ' !!');
|
throw new UserException('Il manque le prix sur la ligne '. $k+1 . ' !!');
|
||||||
}
|
} elseif (empty($value) && f('prix')[$k] == null) {
|
||||||
elseif (empty($value) && ($prix_ligne === null || $prix_ligne === '')) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$prix_ligne = Utils::moneyToInteger($prix_ligne);
|
|
||||||
$data['contenu'][$k]['designation'] = $value;
|
$data['contenu'][$k]['designation'] = $value;
|
||||||
$data['contenu'][$k]['prix'] = $prix_ligne;
|
$data['contenu'][$k]['prix'] = Utils::moneyToInteger(f('prix')[$k]);
|
||||||
$total += $prix_ligne;
|
$data['toto'] += Utils::moneyToInteger(f('prix')[$k]);
|
||||||
}
|
}
|
||||||
|
$data['total'] = $data['toto'];
|
||||||
$data['total'] = $total;
|
unset($data['toto']);
|
||||||
|
|
||||||
if (! isset($data['contenu'])) {
|
if (! isset($data['contenu'])) {
|
||||||
throw new UserException("Aucune désignation ni aucun prix saisi !!");
|
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 ($type === CERFA)
|
elseif ( f('type') == CERFA )
|
||||||
{
|
{
|
||||||
$data['moyen_paiement'] = f('moyen_paiement_cerfa');
|
$data['moyen_paiement'] = f('moyen_paiement_cerfa');
|
||||||
$data['contenu'] = [
|
$data['contenu'] = [
|
||||||
|
|
@ -119,12 +131,8 @@ $form->runIf(f('save') && !$form->hasErrors(),
|
||||||
'nature' => f('nature_don'),
|
'nature' => f('nature_don'),
|
||||||
'texte' => f('texte_don')];
|
'texte' => f('texte_don')];
|
||||||
$data['total'] = Utils::moneyToInteger(f('total'));
|
$data['total'] = Utils::moneyToInteger(f('total'));
|
||||||
|
unset($data['toto']);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new UserException('Ce type de document doit être créé avec son formulaire dédié.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (f('base_receveur') == 'client')
|
if (f('base_receveur') == 'client')
|
||||||
{
|
{
|
||||||
$data['receveur_membre'] = 0;
|
$data['receveur_membre'] = 0;
|
||||||
|
|
@ -149,20 +157,18 @@ $form->runIf(f('save') && !$form->hasErrors(),
|
||||||
}, $csrf_key);
|
}, $csrf_key);
|
||||||
|
|
||||||
$form->runIf(f('select_cotis') && !$form->hasErrors(),
|
$form->runIf(f('select_cotis') && !$form->hasErrors(),
|
||||||
function () use (&$step)
|
function () use ($step)
|
||||||
{
|
{
|
||||||
$step = true;
|
$step = true;
|
||||||
}, 'add_cotis_1');
|
}, 'add_cotis_1');
|
||||||
|
|
||||||
$form->runIf(f('add_cotis') && !$form->hasErrors(),
|
$form->runIf(f('add_cotis') && !$form->hasErrors(),
|
||||||
function () use (&$data, &$radio, $fields, $facture, $form)
|
function () use ($radio, $fields, $facture, $form)
|
||||||
{
|
{
|
||||||
$radio['type'] = f('cotisation');
|
$radio['type'] = f('cotisation');
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$num = (int) str_replace('cotis_', '', $radio['type']);
|
$num = (int) str_replace('cotis_', '', $radio['type']);
|
||||||
$cotis = [];
|
|
||||||
|
|
||||||
foreach($fields as $field)
|
foreach($fields as $field)
|
||||||
{
|
{
|
||||||
$cotis[$field] = f($field.'_'.$num);
|
$cotis[$field] = f($field.'_'.$num);
|
||||||
|
|
@ -177,12 +183,8 @@ $form->runIf(f('add_cotis') && !$form->hasErrors(),
|
||||||
'receveur_membre' => 1,
|
'receveur_membre' => 1,
|
||||||
'receveur_id' => f('membre_cotis'),
|
'receveur_id' => f('membre_cotis'),
|
||||||
'date_emission' => f('date_emission'),
|
'date_emission' => f('date_emission'),
|
||||||
'date_echeance' => f('date_echeance') ?: f('date_emission'),
|
|
||||||
'reglee' => 1,
|
|
||||||
'archivee' => 0,
|
|
||||||
'moyen_paiement' => 'AU',
|
'moyen_paiement' => 'AU',
|
||||||
'total' => $r->paid_amount ?? $r->amount,
|
'total' => $r->paid_amount ?? $r->amount,
|
||||||
'nom_contact' => null,
|
|
||||||
'contenu' => ['id' => $cotis['id'],
|
'contenu' => ['id' => $cotis['id'],
|
||||||
'intitule' => $cotis['label'],
|
'intitule' => $cotis['label'],
|
||||||
'souscription' => $cotis['date'],
|
'souscription' => $cotis['date'],
|
||||||
|
|
@ -262,7 +264,7 @@ if ($target)
|
||||||
} // ... s'il est défini dans le document copié
|
} // ... s'il est défini dans le document copié
|
||||||
elseif (isset($doc['type'])) {
|
elseif (isset($doc['type'])) {
|
||||||
$radio['type'] = $doc['type'];
|
$radio['type'] = $doc['type'];
|
||||||
} // ... ou par défaut
|
} // ... ou par défaut
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$radio['type'] = FACT;
|
$radio['type'] = FACT;
|
||||||
|
|
@ -282,9 +284,9 @@ else
|
||||||
|
|
||||||
$doc['date_emission'] = f('date_emission') ?: $f->date_emission;
|
$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['date_echeance'] = f('date_echeance')?: $f->date_echeance; // Smarty m'a saoulé pour utiliser form_field|date_fr:---
|
||||||
$doc['nom_contact'] = $f->nom_contact ?? null;
|
$doc['nom_contact'] = $f->nom_contact;
|
||||||
$doc['numero_commande'] = $f->numero_commande ?? null;
|
$doc['numero_commande'] = $f->numero_commande;
|
||||||
$doc['reference_acheteur'] = $f->reference_acheteur ?? null;
|
$doc['reference_acheteur'] = $f->reference_acheteur;
|
||||||
/* modif DD -- CERFA -------------------------------------- */
|
/* modif DD -- CERFA -------------------------------------- */
|
||||||
if ( $f->type_facture == CERFA ) {
|
if ( $f->type_facture == CERFA ) {
|
||||||
$doc['total'] = $f->total;
|
$doc['total'] = $f->total;
|
||||||
|
|
@ -344,11 +346,11 @@ $date->setTimestamp(time());
|
||||||
$tpl->assign('date', $date->format('d/m/Y'));
|
$tpl->assign('date', $date->format('d/m/Y'));
|
||||||
|
|
||||||
$tpl->assign(compact('liste', 'radio', 'step', 'designations', 'prix', 'from_user', 'identite', 'csrf_key', 'doc'));
|
$tpl->assign(compact('liste', 'radio', 'step', 'designations', 'prix', 'from_user', 'identite', 'csrf_key', 'doc'));
|
||||||
$tpl->assign('users', iterator_to_array(Users\Users::iterateAssocByCategory()));
|
$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('clients', $db->getAssoc('SELECT id, nom FROM plugin_facturation_clients;'));
|
$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('contacts', $db->getAssoc('SELECT id, nom_contact FROM plugin_facturation_clients;'));
|
||||||
$tpl->assign('require_number', $require_number);
|
$tpl->assign('require_number', $require_number);
|
||||||
$tpl->assign('number_pattern', PATTERNS_LIST[$plugin->getConfig('pattern')] ?? PATTERNS_LIST['']);
|
$tpl->assign('number_pattern', PATTERNS_LIST[$plugin->getConfig('pattern')]);
|
||||||
|
|
||||||
if ($target) {
|
if ($target) {
|
||||||
$tpl->display(PLUGIN_ROOT . '/templates/facture_ajouter.tpl');
|
$tpl->display(PLUGIN_ROOT . '/templates/facture_ajouter.tpl');
|
||||||
|
|
|
||||||
|
|
@ -22,42 +22,6 @@ const PATTERNS_LIST = [
|
||||||
'%06{id}' => 'Numéro unique du document sur 6 chiffres ("000042")',
|
'%06{id}' => 'Numéro unique du document sur 6 chiffres ("000042")',
|
||||||
];
|
];
|
||||||
|
|
||||||
function ensureSchemaColumns(): void
|
|
||||||
{
|
|
||||||
$db = \Paheko\DB::getInstance();
|
|
||||||
|
|
||||||
$tables = [
|
|
||||||
'plugin_facturation_clients' => [
|
|
||||||
'siret' => 'siret TEXT',
|
|
||||||
'telephone' => 'telephone TEXT',
|
|
||||||
'email' => 'email TEXT',
|
|
||||||
'nom_contact' => 'nom_contact TEXT',
|
|
||||||
'note' => 'note TEXT',
|
|
||||||
],
|
|
||||||
'plugin_facturation_factures' => [
|
|
||||||
'nom_contact' => 'nom_contact TEXT',
|
|
||||||
'numero_commande' => 'numero_commande TEXT',
|
|
||||||
'reference_acheteur' => 'reference_acheteur TEXT',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($tables as $table => $columns) {
|
|
||||||
$schema = $db->getTableSchema($table);
|
|
||||||
|
|
||||||
if (!$schema) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($columns as $column => $definition) {
|
|
||||||
if (!isset($schema['columns'][$column])) {
|
|
||||||
$db->exec(sprintf('ALTER TABLE %s ADD COLUMN %s;', $db->quoteIdentifier($table), $definition));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ensureSchemaColumns();
|
|
||||||
|
|
||||||
$client = new Client;
|
$client = new Client;
|
||||||
$facture = new Facture;
|
$facture = new Facture;
|
||||||
|
|
||||||
|
|
@ -99,7 +63,7 @@ $tpl->register_function('money_fac', function (array $params)
|
||||||
$current_value = htmlspecialchars($current_value, ENT_QUOTES, 'UTF-8');
|
$current_value = htmlspecialchars($current_value, ENT_QUOTES, 'UTF-8');
|
||||||
}
|
}
|
||||||
|
|
||||||
$currency = Config::getInstance()->get('currency');
|
$currency = Config::getInstance()->get('monnaie');
|
||||||
return sprintf('<td><nobr><input type="text" pattern="[0-9]*([.,][0-9]{1,2})?" inputmode="decimal" size="8" class="money" style="width: 60%%" onchange="updateSum();" name="%s" value="%s" /><b>%s</b></nobr></td>', $name, $current_value, $currency);
|
return sprintf('<td><nobr><input type="text" pattern="[0-9]*([.,][0-9]{1,2})?" inputmode="decimal" size="8" class="money" style="width: 60%%" onchange="updateSum();" name="%s" value="%s" /><b>%s</b></nobr></td>', $name, $current_value, $currency);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ $id = (int) qg('id');
|
||||||
|
|
||||||
$c = $client->get($id);
|
$c = $client->get($id);
|
||||||
|
|
||||||
if (!$c)
|
if (!$client)
|
||||||
{
|
{
|
||||||
throw new UserException("Ce client n'existe pas.");
|
throw new UserException("Ce client n'existe pas.");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,17 +11,6 @@ $champsPaheko = DynamicFields::getInstance()->listAssocNames();
|
||||||
$champsPaheko = array('' => '- Choisir un champ -') + $champsPaheko;
|
$champsPaheko = array('' => '- Choisir un champ -') + $champsPaheko;
|
||||||
|
|
||||||
$form->runIf('save', function () use ($plugin) {
|
$form->runIf('save', function () use ($plugin) {
|
||||||
$invoice_theme = f('invoice_theme') ?: 'modern';
|
|
||||||
$invoice_accent_color = trim(f('invoice_accent_color') ?: '#4656A5');
|
|
||||||
|
|
||||||
if (!in_array($invoice_theme, ['modern', 'classic', 'minimal'], true)) {
|
|
||||||
throw new UserException('Le style de facture sélectionné est invalide.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!preg_match('/^#[0-9A-Fa-f]{6}$/', $invoice_accent_color)) {
|
|
||||||
throw new UserException('La couleur d\'accent doit être au format hexadécimal, par exemple #4656A5.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$plugin->setConfigProperty('rna_asso', trim(f('rna_asso')));
|
$plugin->setConfigProperty('rna_asso', trim(f('rna_asso')));
|
||||||
$plugin->setConfigProperty('siret_asso', trim(f('siret_asso')));
|
$plugin->setConfigProperty('siret_asso', trim(f('siret_asso')));
|
||||||
$plugin->setConfigProperty('ttc', (bool) f('ttc'));
|
$plugin->setConfigProperty('ttc', (bool) f('ttc'));
|
||||||
|
|
@ -33,14 +22,6 @@ $form->runIf('save', function () use ($plugin) {
|
||||||
|
|
||||||
$plugin->setConfigProperty('logo', (bool)f('logo'));
|
$plugin->setConfigProperty('logo', (bool)f('logo'));
|
||||||
$plugin->setConfigProperty('footer', f('footer'));
|
$plugin->setConfigProperty('footer', f('footer'));
|
||||||
$plugin->setConfigProperty('invoice_theme', $invoice_theme);
|
|
||||||
$plugin->setConfigProperty('invoice_accent_color', strtoupper($invoice_accent_color));
|
|
||||||
$plugin->setConfigProperty('invoice_note', trim(f('invoice_note') ?: ''));
|
|
||||||
$plugin->setConfigProperty('payment_terms', trim(f('payment_terms') ?: ''));
|
|
||||||
$plugin->setConfigProperty('invoice_show_status', (bool) f('invoice_show_status'));
|
|
||||||
$plugin->setConfigProperty('invoice_show_address_borders', (bool) f('invoice_show_address_borders'));
|
|
||||||
$plugin->setConfigProperty('invoice_show_email', (bool) f('invoice_show_email'));
|
|
||||||
$plugin->setConfigProperty('invoice_show_web', (bool) f('invoice_show_web'));
|
|
||||||
|
|
||||||
$plugin->setConfigProperty('adresse_client', f('adresse_client'));
|
$plugin->setConfigProperty('adresse_client', f('adresse_client'));
|
||||||
$plugin->setConfigProperty('code_postal_client', f('code_postal_client'));
|
$plugin->setConfigProperty('code_postal_client', f('code_postal_client'));
|
||||||
|
|
@ -55,21 +36,8 @@ $form->runIf('save', function () use ($plugin) {
|
||||||
|
|
||||||
|
|
||||||
$tpl->assign('ok', qg('ok') !== null);
|
$tpl->assign('ok', qg('ok') !== null);
|
||||||
$conf = $plugin->getConfig();
|
$tpl->assign('conf', $plugin->getConfig());
|
||||||
|
|
||||||
foreach (['invoice_show_status', 'invoice_show_address_borders', 'invoice_show_email', 'invoice_show_web'] as $key) {
|
|
||||||
if (!isset($conf->$key)) {
|
|
||||||
$conf->$key = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$tpl->assign('conf', $conf);
|
|
||||||
$tpl->assign('patterns', \Paheko\Plugin\Facturation\PATTERNS_LIST);
|
$tpl->assign('patterns', \Paheko\Plugin\Facturation\PATTERNS_LIST);
|
||||||
$tpl->assign('champsPaheko', $champsPaheko);
|
$tpl->assign('champsPaheko', $champsPaheko);
|
||||||
$tpl->assign('invoice_themes', [
|
|
||||||
'modern' => 'Moderne',
|
|
||||||
'classic' => 'Classique',
|
|
||||||
'minimal' => 'Minimal',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$tpl->display(PLUGIN_ROOT . '/templates/config.tpl');
|
$tpl->display(PLUGIN_ROOT . '/templates/config.tpl');
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,11 @@ $id = (int) qg('id');
|
||||||
|
|
||||||
$f = $facture->get($id);
|
$f = $facture->get($id);
|
||||||
|
|
||||||
if (!$f)
|
if (!$client)
|
||||||
{
|
{
|
||||||
throw new UserException("Ce document n'existe pas.");
|
throw new UserException("Ce document n'existe pas.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$csrf_key = 'delete_doc_' . $f->id;
|
|
||||||
|
|
||||||
$form->runIf(f('delete') && !$form->hasErrors(),
|
$form->runIf(f('delete') && !$form->hasErrors(),
|
||||||
function () use ($facture, $f, $form)
|
function () use ($facture, $f, $form)
|
||||||
{
|
{
|
||||||
|
|
@ -30,8 +28,7 @@ $form->runIf(f('delete') && !$form->hasErrors(),
|
||||||
{
|
{
|
||||||
$form->addError($e->getMessage());
|
$form->addError($e->getMessage());
|
||||||
}
|
}
|
||||||
}, $csrf_key);
|
});
|
||||||
|
|
||||||
$tpl->assign('doc', $f);
|
$tpl->assign('doc', $f);
|
||||||
$tpl->assign(compact('csrf_key'));
|
|
||||||
$tpl->display(PLUGIN_ROOT . '/templates/facture_supprimer.tpl');
|
$tpl->display(PLUGIN_ROOT . '/templates/facture_supprimer.tpl');
|
||||||
|
|
|
||||||
699
admin/pdf.php
699
admin/pdf.php
|
|
@ -4,27 +4,11 @@ namespace Paheko;
|
||||||
|
|
||||||
require_once __DIR__ . '/_inc.php';
|
require_once __DIR__ . '/_inc.php';
|
||||||
$session->requireAccess($session::SECTION_ACCOUNTING, $session::ACCESS_READ);
|
$session->requireAccess($session::SECTION_ACCOUNTING, $session::ACCESS_READ);
|
||||||
|
$users = new Users\Users;
|
||||||
|
|
||||||
f(['id' => 'required|numeric']);
|
f(['id' => 'required|numeric']);
|
||||||
$id = (int) qg('id');
|
$id = (int) qg('id');
|
||||||
$sign_tag = UserTemplate\Functions::signature();
|
$sign_tag = UserTemplate\Functions::signature();
|
||||||
$h = static function ($value): string {
|
|
||||||
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
|
|
||||||
};
|
|
||||||
$br = static function ($value) use ($h): string {
|
|
||||||
return nl2br($h($value));
|
|
||||||
};
|
|
||||||
$format_identifier = static function ($value) use ($h): string {
|
|
||||||
$value = preg_replace('/\s+/', '', (string) $value);
|
|
||||||
return $value !== '' ? $h(implode(' ', str_split($value, 3))) : '';
|
|
||||||
};
|
|
||||||
$detail_line = static function (string $label, $value) use ($h, $br): string {
|
|
||||||
if ($value === null || trim((string) $value) === '') {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return sprintf('<div><span>%s</span>%s</div>', $h($label), $br($value));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Vérification que le document existe
|
// Vérification que le document existe
|
||||||
if (!$f = $facture->get($id))
|
if (!$f = $facture->get($id))
|
||||||
|
|
@ -34,42 +18,47 @@ if (!$f = $facture->get($id))
|
||||||
|
|
||||||
$moyen_paiement = $facture->getMoyenPaiement($f->moyen_paiement);
|
$moyen_paiement = $facture->getMoyenPaiement($f->moyen_paiement);
|
||||||
|
|
||||||
// Récupération infos membre/client dans un objet normalisé pour le rendu
|
// Récupération infos membre
|
||||||
if ($f->receveur_membre)
|
try
|
||||||
{
|
{
|
||||||
$user = Users\Users::get($f->receveur_id);
|
if ($f->receveur_membre)
|
||||||
|
{
|
||||||
|
$c = $users->get($f->receveur_id);
|
||||||
|
// l'identité du membre peut être redéfinie dans la configuration des membres
|
||||||
|
$name_fields = \Paheko\Users\DynamicFields::getNameFields();
|
||||||
|
array_walk($name_fields, function(&$elem) use ($c) {
|
||||||
|
$elem = $c->$elem;
|
||||||
|
});
|
||||||
|
$nom_client = implode(" ", $name_fields);
|
||||||
|
if (preg_match('/^ +$/', $nom_client)) {
|
||||||
|
$nom_client = "** ABSENT **";
|
||||||
|
}
|
||||||
|
|
||||||
if (!$user) {
|
// adresse, code postal et ville peuvent être redéfini(e)s dans la configuration du plugin
|
||||||
throw new UserException("Ce membre n'existe plus.");
|
$adresse_client = $plugin->getConfig('adresse_client');
|
||||||
|
if ($adresse_client != null && $c->$adresse_client != null) { $c->adresse = $c->$adresse_client; }
|
||||||
|
$code_postal_client = $plugin->getConfig('code_postal_client');
|
||||||
|
if ($code_postal_client != null && $c->$code_postal_client != null) { $c->code_postal = $c->$code_postal_client; }
|
||||||
|
$ville_client = $plugin->getConfig('ville_client');
|
||||||
|
if ($ville_client != null && $c->$ville_client != null) { $c->ville = $c->$ville_client; }
|
||||||
|
|
||||||
|
foreach(['ville','code_postal','adresse'] as $v)
|
||||||
|
{
|
||||||
|
if($c->$v == '')
|
||||||
|
{
|
||||||
|
$c->$v = '[À RENSEIGNER DANS LA FICHE MEMBRE]';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$c = $client->get($f->receveur_id);
|
||||||
|
$nom_client = $c->nom;
|
||||||
}
|
}
|
||||||
|
|
||||||
$nom_client = Users\DynamicFields::getNameFromArray($user) ?: '** ABSENT **';
|
|
||||||
$user_fields = Users\DynamicFields::getInstance()->listAssocNames();
|
|
||||||
$get_user_field = static function (?string $configured, string $fallback) use ($user, $user_fields) {
|
|
||||||
$field = $configured ?: $fallback;
|
|
||||||
return array_key_exists($field, $user_fields) ? ($user->$field ?? null) : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
$c = (object) [
|
|
||||||
'nom' => $nom_client,
|
|
||||||
'adresse' => $get_user_field($plugin->getConfig('adresse_client'), 'adresse') ?: '[À RENSEIGNER DANS LA FICHE MEMBRE]',
|
|
||||||
'code_postal' => $get_user_field($plugin->getConfig('code_postal_client'), 'code_postal') ?: '[À RENSEIGNER]',
|
|
||||||
'ville' => $get_user_field($plugin->getConfig('ville_client'), 'ville') ?: '[À RENSEIGNER]',
|
|
||||||
'siret' => null,
|
|
||||||
'email' => Users\DynamicFields::getFirstEmailField() ? ($user->{Users\DynamicFields::getFirstEmailField()} ?? null) : null,
|
|
||||||
'telephone' => null,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
else
|
catch(UserException $e)
|
||||||
{
|
{
|
||||||
$client_data = $client->get($f->receveur_id);
|
$form->addError($e);
|
||||||
|
|
||||||
if (!$client_data) {
|
|
||||||
throw new UserException("Ce client n'existe plus.");
|
|
||||||
}
|
|
||||||
|
|
||||||
$nom_client = $client_data->nom;
|
|
||||||
$c = $client_data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Formatage dates
|
// Formatage dates
|
||||||
|
|
@ -85,417 +74,307 @@ if (isset($f->date_echeance))
|
||||||
// Génération factures, devis et cotisation
|
// Génération factures, devis et cotisation
|
||||||
if ($f->type_facture != CERFA)
|
if ($f->type_facture != CERFA)
|
||||||
{
|
{
|
||||||
$labels = [
|
switch ($f->type_facture)
|
||||||
FACT => ['Facture', 'Émise le', 'Adressée à', 'Échéance de paiement'],
|
{
|
||||||
DEVIS => ['Devis', 'Émis le', 'Adressé à', 'Échéance du devis'],
|
case FACT:
|
||||||
COTIS => ['Reçu de cotisation', 'Émis le', 'Adressé à', 'Date du reçu'],
|
$doc = 'Facture n° '. $f->numero;
|
||||||
];
|
$txtemis = $doc . " - Émise le " . $emission;
|
||||||
[$document_label, $emission_label, $recipient_label, $due_label] = $labels[$f->type_facture] ?? $labels[FACT];
|
$txtdest = "Adressée à :";
|
||||||
$doc = sprintf('%s n° %s', $document_label, $f->numero);
|
break;
|
||||||
$theme = $plugin->getConfig('invoice_theme') ?: 'modern';
|
case DEVIS:
|
||||||
$theme = in_array($theme, ['modern', 'classic', 'minimal'], true) ? $theme : 'modern';
|
$doc = 'Devis n° '. $f->numero;
|
||||||
$accent = $plugin->getConfig('invoice_accent_color') ?: '#4656A5';
|
$txtemis = $doc . " - Émis le " . $emission;
|
||||||
$accent = preg_match('/^#[0-9A-Fa-f]{6}$/', $accent) ? $accent : '#4656A5';
|
$txtdest = "Adressé à :";
|
||||||
$show_address_borders = $plugin->getConfig('invoice_show_address_borders') !== false;
|
break;
|
||||||
$show_email = $plugin->getConfig('invoice_show_email') !== false;
|
case COTIS:
|
||||||
$show_web = $plugin->getConfig('invoice_show_web') !== false;
|
$doc = 'Reçu de cotisation n° '. $f->numero;
|
||||||
$address_border_class = $show_address_borders ? 'invoice-address-borders' : 'invoice-no-address-borders';
|
$txtemis = $doc . " - Émis le " . $emission;
|
||||||
$currency = $config->get('currency') ?: '€';
|
$txtdest = "Adressé à :";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// utiliser l'adresse configurée dans le plugin sinon celle de l'asso sinon rien !
|
||||||
|
if ($plugin->getConfig('rue_asso') != null &&
|
||||||
|
$plugin->getConfig('cp_asso') != null &&
|
||||||
|
$plugin->getConfig('ville_asso') != null)
|
||||||
|
{
|
||||||
|
$adresse =
|
||||||
|
(($plugin->getConfig('numero_rue_asso') != null) ? $plugin->getConfig('numero_rue_asso') . " " : "") .
|
||||||
|
$plugin->getConfig('rue_asso') . "<br>" .
|
||||||
|
$plugin->getConfig('cp_asso') . " " .
|
||||||
|
$plugin->getConfig('ville_asso');
|
||||||
|
}
|
||||||
|
else if ($config->get('org_address') != null)
|
||||||
|
{
|
||||||
|
$adresse = str_replace("\n", '<br>', $config->get('org_address'));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$adresse = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
$logo='';
|
||||||
|
if ($plugin->getConfig('logo')) {
|
||||||
|
$logo = '<img id="logo" src="' . $config->fileURL('logo') . '" />';
|
||||||
|
}
|
||||||
|
$asso =
|
||||||
|
// 'Émis par :<br><br>'.
|
||||||
|
'<b>'.$config->get('org_name')."</b><br>".
|
||||||
|
$adresse ."<br>".
|
||||||
|
(($t = $plugin->getConfig('rna_asso'))?"RNA : $t<br>":'').
|
||||||
|
(($t = $plugin->getConfig('siret_asso'))?"SIRET : " . implode(' ', str_split($t, 3)) . "<br>":'').
|
||||||
|
(($t = $config->get('email_asso'))?"Email : $t<br>":'').
|
||||||
|
(($t = $config->get('site_asso'))?"Site web : $t<br>":'');
|
||||||
|
|
||||||
|
$receveur =
|
||||||
|
$txtdest.'<br>'.
|
||||||
|
'<b>'.$nom_client.'</b><br>'.
|
||||||
|
(($t = $f->nom_contact)?"Contact : $t<br>":'').
|
||||||
|
$c->adresse."<br>".
|
||||||
|
$c->code_postal.' '.$c->ville."<br>".
|
||||||
|
(($t = $c->siret)?"SIREN/SIRET : " . implode(' ', str_split($t, 3)) . "<br>":'').
|
||||||
|
(($t = $c->email)?"Email : $t<br>":'').
|
||||||
|
(($t = $c->telephone)?"Tel : $t<br>":'');
|
||||||
|
if ($f->type_facture == FACT) {
|
||||||
|
$receveur .=
|
||||||
|
(($t = $f->numero_commande)?"Commande N° : $t<br>":'').
|
||||||
|
(($t = $f->reference_acheteur)?"Référence : $t<br>":'');
|
||||||
|
}
|
||||||
$total = Utils::money_format($f->total, ',', ' ');
|
$total = Utils::money_format($f->total, ',', ' ');
|
||||||
$ttc = $plugin->getConfig('ttc') ? 'TTC' : 'HT';
|
|
||||||
|
|
||||||
if ($plugin->getConfig('rue_asso') && $plugin->getConfig('cp_asso') && $plugin->getConfig('ville_asso')) {
|
// Devis et facture
|
||||||
$adresse = trim(($plugin->getConfig('numero_rue_asso') ? $plugin->getConfig('numero_rue_asso') . ' ' : '') . $plugin->getConfig('rue_asso'))
|
if ($f->type_facture != COTIS)
|
||||||
. "\n" . $plugin->getConfig('cp_asso') . ' ' . $plugin->getConfig('ville_asso');
|
{
|
||||||
}
|
$echeance = ($f->type_facture?'Échéance de paiement':'Échéance du devis')." : ".$echeance;
|
||||||
else {
|
if ($f->type_facture == FACT) {
|
||||||
$adresse = $config->get('org_address_public') ?: $config->get('org_address') ?: '';
|
$reglee = !$f->reglee?'Cette facture est en attente de règlement.':'Cette facture a été réglée.';
|
||||||
}
|
} else {
|
||||||
|
$reglee = "";
|
||||||
|
}
|
||||||
|
$footer = str_replace("\n", '<br>', $plugin->getConfig('footer') ?? '[Pied de page à configurer]');
|
||||||
|
$ttc = $plugin->getConfig('ttc') ? 'TTC':'HT';
|
||||||
|
|
||||||
$logo = '';
|
// Génération du contenu de la facture
|
||||||
if ($plugin->getConfig('logo') && ($logo_url = $config->fileURL('logo'))) {
|
ob_start();
|
||||||
$logo = sprintf('<img class="logo" src="%s" alt="" />', $h($logo_url));
|
echo <<<EOF
|
||||||
}
|
<div class="h2">
|
||||||
|
<span>Contenu</span> - $doc
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
|
||||||
$asso_details = '<strong>' . $h($config->get('org_name')) . '</strong>';
|
<table class="contenu">
|
||||||
$asso_details .= $adresse ? '<p>' . $br($adresse) . '</p>' : '';
|
<thead>
|
||||||
$asso_details .= ($t = $plugin->getConfig('rna_asso')) ? $detail_line('RNA', $t) : '';
|
<tr>
|
||||||
$asso_details .= ($t = $plugin->getConfig('siret_asso')) ? '<div><span>SIRET</span>' . $format_identifier($t) . '</div>' : '';
|
<th>
|
||||||
$asso_details .= $show_email && ($t = $config->get('org_email')) ? $detail_line('Email', $t) : '';
|
Désignations
|
||||||
$asso_details .= ($t = $config->get('org_phone')) ? $detail_line('Tel.', $t) : '';
|
</th>
|
||||||
$asso_details .= $show_web && ($t = $config->get('org_web')) ? $detail_line('Web', $t) : '';
|
<th>
|
||||||
$brand_summary = '<div class="brand-summary">' . $logo . '<strong>' . $h($config->get('org_name')) . '</strong></div>';
|
Prix
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
EOF;
|
||||||
|
|
||||||
$receveur = '<strong>' . $h($nom_client) . '</strong>';
|
$i = 1;
|
||||||
$receveur .= ($t = ($f->nom_contact ?? null)) ? $detail_line('Contact', $t) : '';
|
foreach($f->contenu as $k=>$v)
|
||||||
$receveur .= '<p>' . $br($c->adresse ?? '') . '<br>' . $h(trim(($c->code_postal ?? '') . ' ' . ($c->ville ?? ''))) . '</p>';
|
{
|
||||||
$receveur .= ($t = $c->siret ?? null) ? '<div><span>SIREN/SIRET</span>' . $format_identifier($t) . '</div>' : '';
|
echo '<tr><td>';
|
||||||
$receveur .= $show_email && ($t = $c->email ?? null) ? $detail_line('Email', $t) : '';
|
echo str_replace("\n", '<br>', $v['designation']);
|
||||||
$receveur .= ($t = $c->telephone ?? null) ? $detail_line('Tel.', $t) : '';
|
echo '</td><td>';
|
||||||
$receveur .= $f->type_facture == FACT && ($t = ($f->numero_commande ?? null)) ? $detail_line('Commande', $t) : '';
|
echo Utils::money_format($v['prix'], ',', ' ') .' €';
|
||||||
$receveur .= $f->type_facture == FACT && ($t = ($f->reference_acheteur ?? null)) ? $detail_line('Référence', $t) : '';
|
echo '</td></tr>';
|
||||||
|
$i++;
|
||||||
if ($f->type_facture != COTIS) {
|
|
||||||
$rows = '';
|
|
||||||
|
|
||||||
foreach($f->contenu as $v) {
|
|
||||||
$rows .= sprintf(
|
|
||||||
'<tr><td class="designation">%s</td><td class="amount">%s %s</td></tr>',
|
|
||||||
$br($v['designation']),
|
|
||||||
$h(Utils::money_format($v['prix'], ',', ' ')),
|
|
||||||
$h($currency)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$payment_terms = trim((string) ($plugin->getConfig('payment_terms') ?: ''));
|
echo <<<EOF
|
||||||
$invoice_note = trim((string) ($plugin->getConfig('invoice_note') ?: ''));
|
</tbody>
|
||||||
$status = '';
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td><b>Total</b><br>Net à payer</td>
|
||||||
|
<td><b>$total €</b><br>({$ttc})</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
<footer>
|
||||||
|
<div class="h2"><span>Détails</span></div>
|
||||||
|
<hr>
|
||||||
|
|
||||||
if ($plugin->getConfig('invoice_show_status') && $f->type_facture == FACT) {
|
$echeance <br>
|
||||||
$status_class = $f->reglee ? 'paid' : 'pending';
|
$reglee
|
||||||
$status_label = $f->reglee ? 'Facture réglée' : 'En attente de règlement';
|
Moyen de paiement : $moyen_paiement
|
||||||
$status = sprintf('<p class="status %s">%s</p>', $status_class, $h($status_label));
|
<p>
|
||||||
|
$footer
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
EOF;
|
||||||
|
if ($f->type_facture == DEVIS) {
|
||||||
|
echo <<<EOF
|
||||||
|
<p><b>Bon pour accord, date et signature<b></p>
|
||||||
|
EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
$approval = $f->type_facture == DEVIS ? '<section class="approval"><strong>Bon pour accord</strong><span>Date, nom et signature</span></section>' : '';
|
$content = ob_get_clean();
|
||||||
$note = $invoice_note !== '' ? '<p class="invoice-note">' . $br($invoice_note) . '</p>' : '';
|
|
||||||
$terms = $payment_terms !== '' ? $detail_line('Conditions', $payment_terms) : '';
|
|
||||||
$footer = $br($plugin->getConfig('footer') ?? '[Pied de page à configurer]');
|
|
||||||
$echeance_html = $detail_line($due_label, $echeance ?? '');
|
|
||||||
$moyen_html = $detail_line('Moyen de paiement', $moyen_paiement);
|
|
||||||
|
|
||||||
$content = <<<HTML
|
|
||||||
<section class="items">
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Désignation</th>
|
|
||||||
<th class="amount">Montant</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
$rows
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</section>
|
|
||||||
<section class="summary">
|
|
||||||
<div class="details">
|
|
||||||
$echeance_html
|
|
||||||
$terms
|
|
||||||
$moyen_html
|
|
||||||
$status
|
|
||||||
</div>
|
|
||||||
<div class="total">
|
|
||||||
<span>Total $ttc</span>
|
|
||||||
<strong>$total {$h($currency)}</strong>
|
|
||||||
<em>Net à payer</em>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
$note
|
|
||||||
$approval
|
|
||||||
<footer>$footer</footer>
|
|
||||||
HTML;
|
|
||||||
}
|
}
|
||||||
else {
|
else // Reçu de cotisation
|
||||||
$lieu = $plugin->getConfig('ville_asso') ?: '';
|
{
|
||||||
|
$lieu = $plugin->getConfig('ville_asso');
|
||||||
$intitule = $f->contenu['intitule'];
|
$intitule = $f->contenu['intitule'];
|
||||||
$souscription = date('d/m/Y', strtotime($f->contenu['souscription']));
|
|
||||||
$expiration = $f->contenu['expiration'] == '1970-01-01'
|
|
||||||
? "le jour même, s'agissant d'une cotisation ponctuelle"
|
|
||||||
: 'le ' . date('d/m/Y', strtotime($f->contenu['expiration']));
|
|
||||||
$footer = $br($plugin->getConfig('footer') ?? '');
|
|
||||||
|
|
||||||
$content = <<<HTML
|
$souscription = date('d/m/Y', strtotime($f->contenu['souscription']));
|
||||||
<section class="letter">
|
|
||||||
<p>À {$h($lieu)}, le {$h($emission)},</p>
|
if($f->contenu['expiration'] == '1970-01-01')
|
||||||
|
{
|
||||||
|
$expiration = "jour même, s'agissant d'une cotisation ponctuelle.";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$expiration = date('d/m/Y', strtotime($f->contenu['expiration']));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Génération du contenu du reçu de cotisation
|
||||||
|
$content = <<<EOF
|
||||||
|
<div class="h2">
|
||||||
|
<span>Reçu de votre cotisation</span> - $doc
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="contenuTexte">
|
||||||
|
<p>À $lieu, le $emission,</p>
|
||||||
<p>Bonjour,</p>
|
<p>Bonjour,</p>
|
||||||
<p>Nous accusons réception de votre cotisation « {$h($intitule)} » reçue le {$h($emission)} et nous vous en remercions.</p>
|
|
||||||
<p>Nous reconnaissons que vous avez acquitté la somme de <strong>{$h($total)} {$h($currency)}</strong>. Votre adhésion sera donc effective à compter du {$h($souscription)} jusqu'à {$h($expiration)}.</p>
|
<p>Nous accusons réception de votre cotisation « $intitule » reçue le $emission et nous vous en remercions.</p>
|
||||||
<p>Nous vous prions de recevoir nos meilleures salutations.</p>
|
<p>Nous reconnaissons que vous avez acquitté la somme de {$total} €.<br>111
|
||||||
<p><strong>Pour {$h($config->get('org_name'))}</strong></p>
|
Votre adhésion sera donc effective à compter du $souscription jusqu’au $expiration.</p>
|
||||||
<p class="legal">Nous vous rappelons que la cotisation n'est pas soumise à la TVA et qu'elle ne donne pas lieu à la délivrance d'une facture. Elle n'ouvre pas droit au bénéfice des dispositions des articles 200, 238 bis et 885-0 V bis A du code général des impôts.</p>
|
<br>
|
||||||
</section>
|
|
||||||
<footer>$footer</footer>
|
<p>Nous vous prions de recevoir, chère adhérente, cher adhérent, nos meilleures salutations,</p>
|
||||||
HTML;
|
<br>
|
||||||
|
<p>-représentant·e de l'asso-</p>
|
||||||
|
<br>
|
||||||
|
<p><i>Nous vous rappelons que la cotisation n’est pas soumise à la TVA et qu’elle ne donne pas lieu à la délivrance d’une facture. Elle n’ouvre pas droit au bénéfice des dispositions des articles 200, 238 bis et 885-0 V bis A du code général des impôts.</i></p>
|
||||||
|
</div>
|
||||||
|
EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
$html_title = $h($doc . '_' . $emission);
|
//-- Layout du document
|
||||||
$doc_html = $h($doc);
|
|
||||||
$txtemis = $h($emission_label . ' ' . $emission);
|
|
||||||
$txtdest = $h($recipient_label);
|
|
||||||
|
|
||||||
$html = <<<HTML
|
ob_start();
|
||||||
|
echo <<<EOF
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html class="invoice-theme-$theme $address_border_class">
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>$html_title</title>
|
<title>{$doc}_{$emission}</title>
|
||||||
<style>
|
<style>
|
||||||
@page { size: A4 portrait; margin: 14mm; }
|
@page {
|
||||||
* { box-sizing: border-box; }
|
size: A4 portrait;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
padding: 4mm;
|
||||||
font-family: Helvetica, Arial, sans-serif;
|
font-family: Helvetica, Arial, sans;
|
||||||
font-size: 10pt;
|
font-size: 10pt;
|
||||||
line-height: 1.45;
|
background: white;
|
||||||
color: #20242A;
|
|
||||||
background: #fff;
|
|
||||||
}
|
}
|
||||||
.invoice {
|
|
||||||
display: block;
|
.titre {
|
||||||
padding-bottom: 28mm;
|
|
||||||
page-break-after: avoid;
|
|
||||||
break-after: avoid-page;
|
|
||||||
}
|
|
||||||
.hero {
|
|
||||||
display: table;
|
|
||||||
width: 100%;
|
|
||||||
padding-bottom: 10mm;
|
|
||||||
border-bottom: 2px solid $accent;
|
|
||||||
}
|
|
||||||
.brand, .document-title {
|
|
||||||
display: table-cell;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
.brand { width: 46%; }
|
|
||||||
.document-title { width: 54%; }
|
|
||||||
.brand-summary {
|
|
||||||
display: inline-block;
|
|
||||||
max-width: 72mm;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
font-size: 8pt;
|
||||||
|
margin-bottom: 6mm;
|
||||||
|
margin-top: 0mm;
|
||||||
}
|
}
|
||||||
.logo {
|
.h2 {
|
||||||
display: block;
|
margin: 20px 20px 0px 20px;
|
||||||
max-width: 34mm;
|
|
||||||
max-height: 24mm;
|
|
||||||
margin: 0 auto 4mm;
|
|
||||||
}
|
}
|
||||||
.brand-summary strong {
|
.h2 span {
|
||||||
display: block;
|
font-weight: bold;
|
||||||
font-size: 10pt;
|
font-size: 14pt;
|
||||||
line-height: 1.3;
|
|
||||||
}
|
}
|
||||||
.document-title {
|
hr {
|
||||||
text-align: right;
|
margin: 5px 0px 15px 0px;
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid;
|
||||||
}
|
}
|
||||||
.document-title h1 {
|
.adressage {
|
||||||
margin: 0 0 2mm;
|
font-size: 11pt;
|
||||||
color: $accent;
|
margin: auto;
|
||||||
font-size: 19pt;
|
|
||||||
font-weight: 700;
|
|
||||||
line-height: 1.15;
|
|
||||||
letter-spacing: 0;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
.document-title p {
|
.adressage td {
|
||||||
margin: 0;
|
width: 40%;
|
||||||
color: #566070;
|
|
||||||
}
|
|
||||||
.addresses {
|
|
||||||
display: table;
|
|
||||||
width: 100%;
|
|
||||||
margin: 10mm 0 8mm;
|
|
||||||
}
|
|
||||||
.card {
|
|
||||||
display: table-cell;
|
|
||||||
width: 50%;
|
|
||||||
padding: 6mm;
|
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
border: 1px solid #D9DEE8;
|
|
||||||
}
|
}
|
||||||
.card + .card { border-left: 0; }
|
.adressage td#logo {
|
||||||
.card h2 {
|
width: 20%;
|
||||||
margin: 0 0 4mm;
|
vertical-align: top;
|
||||||
color: $accent;
|
|
||||||
font-size: 9pt;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0;
|
|
||||||
}
|
}
|
||||||
.card strong {
|
.adressage img#logo {
|
||||||
display: block;
|
height : 3cm;
|
||||||
margin-bottom: 2mm;
|
|
||||||
font-size: 12pt;
|
|
||||||
}
|
}
|
||||||
.card p { margin: 2mm 0; }
|
|
||||||
.card div {
|
.contenuTexte {
|
||||||
margin-top: 1.5mm;
|
padding: 0 6mm;
|
||||||
}
|
}
|
||||||
.card span {
|
|
||||||
display: inline-block;
|
.contenu {
|
||||||
min-width: 27mm;
|
|
||||||
margin-right: 3mm;
|
|
||||||
color: #687386;
|
|
||||||
}
|
|
||||||
.invoice-no-address-borders .card {
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
.invoice-no-address-borders .card + .card {
|
|
||||||
padding-left: 10mm;
|
|
||||||
}
|
|
||||||
.items table {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contenu td, .contenu th {
|
||||||
|
vertical-align: top;
|
||||||
|
padding: 8px 10px;
|
||||||
|
margin : 0px;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
.items th {
|
|
||||||
padding: 4mm 3mm;
|
.contenu tr th, .contenu tr td {
|
||||||
color: #fff;
|
text-align: right;
|
||||||
background: $accent;
|
width: 16%;
|
||||||
|
}
|
||||||
|
.contenu tr td:nth-child(1), .contenu tr th:nth-child(1) {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-weight: 700;
|
width: 84%;
|
||||||
}
|
}
|
||||||
.items td {
|
|
||||||
padding: 4mm 3mm;
|
.contenu thead tr, .contenu tfoot tr {
|
||||||
border-bottom: 1px solid #E5E8EF;
|
background-color: #CCE;
|
||||||
vertical-align: top;
|
border: 10px solid black;
|
||||||
}
|
}
|
||||||
.items tr:nth-child(even) td {
|
.contenu tbody tr:nth-child(even) {
|
||||||
background: #F7F8FB;
|
background-color: #DDF !important;
|
||||||
}
|
}
|
||||||
.amount {
|
.contenu tfoot {
|
||||||
width: 34mm;
|
display: table-row-group;
|
||||||
text-align: right;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.summary {
|
|
||||||
display: table;
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 8mm;
|
|
||||||
}
|
|
||||||
.details, .total {
|
|
||||||
display: table-cell;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
.details {
|
|
||||||
width: 64%;
|
|
||||||
color: #3B4350;
|
|
||||||
}
|
|
||||||
.details div {
|
|
||||||
margin-bottom: 2mm;
|
|
||||||
}
|
|
||||||
.details span {
|
|
||||||
display: inline-block;
|
|
||||||
min-width: 42mm;
|
|
||||||
margin-right: 4mm;
|
|
||||||
color: #687386;
|
|
||||||
}
|
|
||||||
.total {
|
|
||||||
width: 36%;
|
|
||||||
padding: 4mm 5mm;
|
|
||||||
color: #fff;
|
|
||||||
background: $accent;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
.total span, .total em {
|
|
||||||
display: block;
|
|
||||||
font-style: normal;
|
|
||||||
font-size: 9.5pt;
|
|
||||||
}
|
|
||||||
.total strong {
|
|
||||||
display: block;
|
|
||||||
margin: 1mm 0;
|
|
||||||
font-size: 17pt;
|
|
||||||
line-height: 1.15;
|
|
||||||
}
|
|
||||||
.status {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 2mm 0 0;
|
|
||||||
padding: 1.5mm 3mm;
|
|
||||||
border: 1px solid #D9DEE8;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
.status.paid { color: #1F6B3A; }
|
|
||||||
.status.pending { color: #9A4D00; }
|
|
||||||
.invoice-note, .approval, .letter {
|
|
||||||
margin-top: 8mm;
|
|
||||||
}
|
|
||||||
.approval {
|
|
||||||
min-height: 25mm;
|
|
||||||
padding-top: 4mm;
|
|
||||||
border-top: 1px solid #D9DEE8;
|
|
||||||
}
|
|
||||||
.approval strong, .approval span {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.legal {
|
|
||||||
margin-top: 8mm;
|
|
||||||
color: #566070;
|
|
||||||
font-size: 9pt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
position: fixed;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
margin: 14mm 0;
|
||||||
margin: 0;
|
width: inherit;
|
||||||
padding-top: 6mm;
|
font-size: 9pt;
|
||||||
color: #566070;
|
|
||||||
font-size: 8.5pt;
|
|
||||||
text-align: center;
|
|
||||||
border-top: 1px solid #D9DEE8;
|
|
||||||
}
|
|
||||||
.invoice-theme-classic .hero {
|
|
||||||
border-bottom-width: 1px;
|
|
||||||
}
|
|
||||||
.invoice-theme-classic .items th,
|
|
||||||
.invoice-theme-classic .total {
|
|
||||||
background: #2F343D;
|
|
||||||
}
|
|
||||||
.invoice-theme-minimal .hero,
|
|
||||||
.invoice-theme-minimal .card,
|
|
||||||
.invoice-theme-minimal footer {
|
|
||||||
border-color: #E6E8ED;
|
|
||||||
}
|
|
||||||
.invoice-theme-minimal .items th,
|
|
||||||
.invoice-theme-minimal .total {
|
|
||||||
color: #20242A;
|
|
||||||
background: #F0F2F6;
|
|
||||||
}
|
|
||||||
@media screen {
|
|
||||||
body {
|
|
||||||
padding: 14mm;
|
|
||||||
}
|
|
||||||
footer {
|
|
||||||
right: 14mm;
|
|
||||||
bottom: 14mm;
|
|
||||||
left: 14mm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media print {
|
|
||||||
footer {
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main class="invoice">
|
<p class="titre">
|
||||||
<header class="hero">
|
$txtemis
|
||||||
<section class="brand">
|
</p>
|
||||||
$brand_summary
|
|
||||||
</section>
|
<table class="adressage">
|
||||||
<section class="document-title">
|
<tr>
|
||||||
<h1>$doc_html</h1>
|
<td id="logo">$logo</td>
|
||||||
<p>$txtemis</p>
|
<td>$asso</td>
|
||||||
</section>
|
<td>$receveur</td>
|
||||||
</header>
|
</tr>
|
||||||
<section class="addresses">
|
</table>
|
||||||
<div class="card">
|
<br>
|
||||||
<h2>Émetteur</h2>
|
$content
|
||||||
$asso_details
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<h2>$txtdest</h2>
|
|
||||||
$receveur
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
$content
|
|
||||||
</main>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
HTML;
|
EOF;
|
||||||
|
|
||||||
|
$html = ob_get_clean();
|
||||||
|
|
||||||
} // Génération du CERFA
|
} // Génération du CERFA
|
||||||
elseif ($f->type_facture == CERFA)
|
elseif ($f->type_facture == CERFA)
|
||||||
{
|
{
|
||||||
|
|
@ -515,9 +394,9 @@ elseif ($f->type_facture == CERFA)
|
||||||
$t['objet2'] = $plugin->getConfig('objet_2');
|
$t['objet2'] = $plugin->getConfig('objet_2');
|
||||||
|
|
||||||
$t['nom'] = $nom_client;
|
$t['nom'] = $nom_client;
|
||||||
$t['adresse'] = $c->adresse ?? '';
|
$t['adresse'] = $c->adresse;
|
||||||
$t['cp'] = $c->code_postal ?? '';
|
$t['cp'] = $c->code_postal;
|
||||||
$t['ville'] = $c->ville ?? '';
|
$t['ville'] = $c->ville;
|
||||||
$t['total'] = '***'.Utils::money_format($f->total).'***';
|
$t['total'] = '***'.Utils::money_format($f->total).'***';
|
||||||
$t['total_lettre'] = numfmt_create('fr_FR', \NumberFormatter::SPELLOUT)->format($f->total/100). ' euros';
|
$t['total_lettre'] = numfmt_create('fr_FR', \NumberFormatter::SPELLOUT)->format($f->total/100). ' euros';
|
||||||
|
|
||||||
|
|
|
||||||
10
config.json
10
config.json
|
|
@ -1,14 +1,6 @@
|
||||||
{
|
{
|
||||||
"footer": "[EXEMPLE]\nAssociation exonérée des impôts commerciaux\nEn cas de retard de paiement, indemnité forfaitaire légale pour frais de recouvrement : 40,00 €\n[Coordonnées bancaires]\nAssociation enregistrée en préfecture de XXX au numéro YYY",
|
"footer": "[EXEMPLE]\nAssociation exonérée des impôts commerciaux\nEn cas de retard de paiement, indemnité forfaitaire légale pour frais de recouvrement : 40,00 €\n[Coordonnées bancaires]\nAssociation enregistrée en préfecture de XXX au numéro YYY",
|
||||||
"pattern": "%{type}-%{year}-%{ynumber}",
|
"pattern": "%{type}-%{year}-%{ynumber}",
|
||||||
"invoice_theme": "modern",
|
|
||||||
"invoice_accent_color": "#4656A5",
|
|
||||||
"invoice_note": "",
|
|
||||||
"payment_terms": "Paiement à réception",
|
|
||||||
"invoice_show_status": true,
|
|
||||||
"invoice_show_address_borders": true,
|
|
||||||
"invoice_show_email": true,
|
|
||||||
"invoice_show_web": true,
|
|
||||||
"ttc": false,
|
"ttc": false,
|
||||||
"validate_cp": true
|
"validate_cp": true
|
||||||
}
|
}
|
||||||
|
|
@ -3,13 +3,13 @@ CREATE TABLE IF NOT EXISTS plugin_facturation_factures (
|
||||||
type_facture INTEGER NOT NULL DEFAULT 0,
|
type_facture INTEGER NOT NULL DEFAULT 0,
|
||||||
numero TEXT NOT NULL UNIQUE,
|
numero TEXT NOT NULL UNIQUE,
|
||||||
receveur_membre INTEGER NOT NULL, -- bool
|
receveur_membre INTEGER NOT NULL, -- bool
|
||||||
receveur_id INTEGER NOT NULL,
|
receveur_id INTEGER NOT NULL,
|
||||||
date_emission TEXT NOT NULL, -- CHECK (date(date_emission) IS NOT NULL AND date(date_emission) = date_emission),
|
date_emission TEXT NOT NULL, -- CHECK (date(date_emission) IS NOT NULL AND date(date_emission) = date_emission),
|
||||||
date_echeance TEXT NOT NULL, -- CHECK (date(date_echeance) IS NOT NULL AND date(date_echeance) = date_echeance),
|
date_echeance TEXT NOT NULL, -- CHECK (date(date_echeance) IS NOT NULL AND date(date_echeance) = date_echeance),
|
||||||
reglee INTEGER DEFAULT 0, -- bool
|
reglee INTEGER DEFAULT 0, -- bool
|
||||||
archivee INTEGER DEFAULT 0, -- bool
|
archivee INTEGER DEFAULT 0, -- bool
|
||||||
moyen_paiement TEXT NOT NULL,
|
moyen_paiement TEXT NOT NULL,
|
||||||
contenu TEXT NOT NULL,
|
contenu TEXT NOT NULL,
|
||||||
total INTEGER DEFAULT 0,
|
total INTEGER DEFAULT 0,
|
||||||
nom_contact TEXT,
|
nom_contact TEXT,
|
||||||
numero_commande TEXT,
|
numero_commande TEXT,
|
||||||
|
|
@ -20,10 +20,10 @@ CREATE TABLE IF NOT EXISTS plugin_facturation_factures (
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS plugin_facturation_clients (
|
CREATE TABLE IF NOT EXISTS plugin_facturation_clients (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
nom TEXT NOT NULL,
|
nom TEXT NOT NULL,
|
||||||
adresse TEXT NOT NULL,
|
adresse TEXT NOT NULL,
|
||||||
code_postal TEXT NOT NULL,
|
code_postal TEXT NOT NULL,
|
||||||
ville TEXT NOT NULL,
|
ville TEXT NOT NULL,
|
||||||
siret TEXT,
|
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
|
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,
|
telephone TEXT,
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Paheko;
|
namespace Paheko;
|
||||||
|
use Paheko\Entities\Files\File;
|
||||||
|
use Paheko\Plugin\Facturation\Facture;
|
||||||
|
|
||||||
$db = DB::getInstance();
|
$db = DB::getInstance();
|
||||||
|
|
||||||
$db->import(dirname(__FILE__) . "/data/schema.sql");
|
$db->import(dirname(__FILE__) . "/data/schema.sql");
|
||||||
|
|
||||||
$default_config = json_decode(file_get_contents(__DIR__ . '/config.json'));
|
|
||||||
|
|
||||||
if ($default_config instanceof \stdClass) {
|
|
||||||
$plugin->setConfig($default_config);
|
|
||||||
$plugin->save();
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ namespace Paheko\Plugin\Facturation;
|
||||||
|
|
||||||
use Paheko\DB;
|
use Paheko\DB;
|
||||||
use Paheko\DynamicList;
|
use Paheko\DynamicList;
|
||||||
use Paheko\Plugins;
|
use Paheko\Entities\Plugin;
|
||||||
use Paheko\UserException;
|
use Paheko\UserException;
|
||||||
use Paheko\Utils;
|
use Paheko\Utils;
|
||||||
|
|
||||||
|
|
@ -29,12 +29,9 @@ class Client
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$plugin = Plugins::get('facturation');
|
$plugin = new Plugin('facturation');
|
||||||
|
$this->config['unique_client_name'] = $plugin->getConfig('unique_client_name') ?: false;
|
||||||
if ($plugin) {
|
$this->config['validate_cp'] = $plugin->getConfig('validate_cp') ?: false;
|
||||||
$this->config['unique_client_name'] = (bool) $plugin->getConfig('unique_client_name');
|
|
||||||
$this->config['validate_cp'] = (bool) ($plugin->getConfig('validate_cp') ?? true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function _checkFields(&$data)
|
public function _checkFields(&$data)
|
||||||
|
|
@ -105,20 +102,8 @@ class Client
|
||||||
{
|
{
|
||||||
$db = DB::getInstance();
|
$db = DB::getInstance();
|
||||||
|
|
||||||
$client = $db->first('SELECT *, strftime(\'%s\', date_creation) AS date_creation
|
return $db->first('SELECT *, strftime(\'%s\', date_creation) AS date_creation
|
||||||
FROM plugin_facturation_clients WHERE id = ? LIMIT 1;', (int)$id);
|
FROM plugin_facturation_clients WHERE id = ? LIMIT 1;', (int)$id);
|
||||||
|
|
||||||
if (!$client) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (['siret', 'telephone', 'email', 'nom_contact', 'note'] as $key) {
|
|
||||||
if (!property_exists($client, $key)) {
|
|
||||||
$client->$key = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $client;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function listAll()
|
public function listAll()
|
||||||
|
|
|
||||||
111
lib/Facture.php
111
lib/Facture.php
|
|
@ -6,10 +6,9 @@ use DateTime;
|
||||||
use Paheko\Config;
|
use Paheko\Config;
|
||||||
use Paheko\DB;
|
use Paheko\DB;
|
||||||
use Paheko\DynamicList;
|
use Paheko\DynamicList;
|
||||||
use Paheko\Plugins;
|
|
||||||
use Paheko\UserException;
|
use Paheko\UserException;
|
||||||
use Paheko\Users\DynamicFields;
|
|
||||||
use Paheko\Utils;
|
use Paheko\Utils;
|
||||||
|
use Paheko\Services\Services_User;
|
||||||
|
|
||||||
class Facture
|
class Facture
|
||||||
{
|
{
|
||||||
|
|
@ -48,52 +47,17 @@ class Facture
|
||||||
'accounts' => [],
|
'accounts' => [],
|
||||||
'label' => 'Facture',
|
'label' => 'Facture',
|
||||||
'help' => ''],
|
'help' => ''],
|
||||||
CERFA => [
|
|
||||||
'id' => CERFA,
|
|
||||||
'accounts' => [],
|
|
||||||
'label' => 'Reçu fiscal',
|
|
||||||
'help' => 'Fonction historique conservée pour les anciens reçus. Le module Paheko "Reçus fiscaux" est recommandé pour les nouveaux usages.'],
|
|
||||||
COTIS => [
|
|
||||||
'id' => COTIS,
|
|
||||||
'accounts' => [],
|
|
||||||
'label' => 'Reçu de cotisation',
|
|
||||||
'help' => 'Fonction historique conservée pour les anciens reçus. Le module Paheko "Reçu de paiement" est recommandé pour les nouveaux usages.'],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
private function normalizeDate($value, string $field): string
|
|
||||||
{
|
|
||||||
if ($value instanceof \DateTimeInterface) {
|
|
||||||
return $value->format('Y-m-d');
|
|
||||||
}
|
|
||||||
|
|
||||||
$value = trim((string) $value);
|
|
||||||
|
|
||||||
foreach (['!Y-m-d', '!d/m/Y'] as $format) {
|
|
||||||
$date = DateTime::createFromFormat($format, $value);
|
|
||||||
$errors = DateTime::getLastErrors();
|
|
||||||
|
|
||||||
if ($date && (!$errors || (!$errors['warning_count'] && !$errors['error_count']))) {
|
|
||||||
return $date->format('Y-m-d');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new UserException(sprintf('La date "%s" est invalide pour le champ %s.', $value, $field));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix : est dépendant de l'ordre des données dans l'array
|
// Fix : est dépendant de l'ordre des données dans l'array
|
||||||
// et implique que toutes les données soient présentes (pas possible de faire un update partiel)
|
// et implique que toutes les données soient présentes (pas possible de faire un update partiel)
|
||||||
public function _checkFields(&$datas)
|
public function _checkFields(&$datas)
|
||||||
{
|
{
|
||||||
$type_facture = isset($datas['type_facture']) ? (int) $datas['type_facture'] : null;
|
|
||||||
$fac = in_array($type_facture, [DEVIS, FACT], true);
|
|
||||||
$cerfa = $type_facture === CERFA;
|
|
||||||
$recu = $type_facture === COTIS;
|
|
||||||
$total = 0;
|
|
||||||
|
|
||||||
foreach($datas as $k=>$data)
|
foreach($datas as $k=>$data)
|
||||||
{
|
{
|
||||||
if (!in_array($k, $this->keys))
|
if (!in_array($k, $this->keys))
|
||||||
|
|
@ -112,11 +76,24 @@ class Facture
|
||||||
switch($k)
|
switch($k)
|
||||||
{
|
{
|
||||||
case 'type_facture':
|
case 'type_facture':
|
||||||
$datas[$k] = (int) $datas[$k];
|
if (!array_key_exists($datas[$k], $this->types)) {
|
||||||
|
|
||||||
if (!array_key_exists($datas[$k], self::TYPES_NAMES)) {
|
|
||||||
throw new UserException("$k est de type non-attendue ($data).");
|
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;
|
break;
|
||||||
case 'receveur_membre':
|
case 'receveur_membre':
|
||||||
case 'reglee':
|
case 'reglee':
|
||||||
|
|
@ -131,11 +108,11 @@ class Facture
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'date_emission':
|
case 'date_emission':
|
||||||
$datas[$k] = $this->normalizeDate($data, $k);
|
$datas[$k] = \DateTime::createFromFormat('!d/m/Y', $data)->format('Y-m-d');
|
||||||
break;
|
break;
|
||||||
case 'date_echeance':
|
case 'date_echeance':
|
||||||
$datas[$k] = $this->normalizeDate($data, $k);
|
$datas[$k] = \DateTime::createFromFormat('!d/m/Y', $data)->format('Y-m-d');
|
||||||
if (isset($datas['date_emission']) && DateTime::createFromFormat('!Y-m-d', $datas[$k])->format('U') < DateTime::createFromFormat('!Y-m-d', $datas['date_emission'])->format('U'))
|
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).");
|
throw new UserException("La date d'échéance est antérieure à la date d'émission ($data).");
|
||||||
}
|
}
|
||||||
|
|
@ -157,12 +134,12 @@ class Facture
|
||||||
if (empty($r['designation']) && empty($r['prix']))
|
if (empty($r['designation']) && empty($r['prix']))
|
||||||
{
|
{
|
||||||
unset($datas[$k][$g]);
|
unset($datas[$k][$g]);
|
||||||
|
unset($datas[$k]['prix']);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
elseif (!isset($r['prix']) || $r['prix'] === '')
|
elseif (! is_numeric($r['prix']) && empty($r['prix']))
|
||||||
{
|
{
|
||||||
$datas[$k][$g]['prix'] = 0;
|
$datas[$k]['prix'] = 0;
|
||||||
$r['prix'] = 0;
|
|
||||||
}
|
}
|
||||||
elseif (empty($r['designation'])) {
|
elseif (empty($r['designation'])) {
|
||||||
throw new UserException("Une au moins des désignations est absente.");
|
throw new UserException("Une au moins des désignations est absente.");
|
||||||
|
|
@ -274,7 +251,7 @@ class Facture
|
||||||
$type = ctype_digit($v) ? 'd' : 's';
|
$type = ctype_digit($v) ? 'd' : 's';
|
||||||
return sprintf('%' . $match[1] . $type, $v);
|
return sprintf('%' . $match[1] . $type, $v);
|
||||||
}, $selpattern);
|
}, $selpattern);
|
||||||
$modele = '/^' . preg_quote($prefixe, '/') . '\d+$/';
|
$modele = '/^' . $prefixe . '\d+$/';
|
||||||
$numeros_filtres = array_filter($numeros, function($elem) use ($modele) {
|
$numeros_filtres = array_filter($numeros, function($elem) use ($modele) {
|
||||||
return preg_match($modele, $elem);
|
return preg_match($modele, $elem);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
@ -315,12 +292,6 @@ class Facture
|
||||||
$r->contenu = json_decode($r->contenu, true);
|
$r->contenu = json_decode($r->contenu, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (['nom_contact', 'numero_commande', 'reference_acheteur'] as $key) {
|
|
||||||
if (!property_exists($r, $key)) {
|
|
||||||
$r->$key = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$r->date_emission = \DateTime::createFromFormat('!Y-m-d', $r->date_emission);
|
$r->date_emission = \DateTime::createFromFormat('!Y-m-d', $r->date_emission);
|
||||||
if ($r->date_echeance)
|
if ($r->date_echeance)
|
||||||
{
|
{
|
||||||
|
|
@ -349,19 +320,15 @@ class Facture
|
||||||
|
|
||||||
public function list($all = true): DynamicList
|
public function list($all = true): DynamicList
|
||||||
{
|
{
|
||||||
$db = DB::getInstance();
|
$id_field = \Paheko\Users\DynamicFields::getNameFieldsSQL('u');
|
||||||
$id_field = DynamicFields::getNameFieldsSQL('u');
|
$plugin_name = preg_replace('/^.*\/(\w+)\/$/', '${1}', \Paheko\PLUGIN_ADMIN_URL);
|
||||||
$plugin = Plugins::get('facturation');
|
$plugin = \Paheko\Plugins::get($plugin_name);
|
||||||
$user_fields = DynamicFields::getInstance()->listAssocNames();
|
|
||||||
|
|
||||||
// adresse et ville peuvent être redéfinies dans la configuration du plugin
|
// adresse et ville peuvent être redéfinies dans la configuration du plugin
|
||||||
$adresse_client = $plugin ? $plugin->getConfig('adresse_client') : null;
|
$adresse_client = $plugin->getConfig('adresse_client');
|
||||||
$adresse_client = $adresse_client ?: 'adresse';
|
if ($adresse_client == null) { $adresse = 'u.adresse'; } else { $adresse = 'u.' . $adresse_client; }
|
||||||
$adresse = array_key_exists($adresse_client, $user_fields) ? 'u.' . $db->quoteIdentifier($adresse_client) : 'NULL';
|
$ville_client = $plugin->getConfig('ville_client');
|
||||||
|
if ($ville_client == null) { $ville = 'u.ville'; } else { $ville = 'u.' . $ville_client; }
|
||||||
$ville_client = $plugin ? $plugin->getConfig('ville_client') : null;
|
|
||||||
$ville_client = $ville_client ?: 'ville';
|
|
||||||
$ville = array_key_exists($ville_client, $user_fields) ? 'u.' . $db->quoteIdentifier($ville_client) : 'NULL';
|
|
||||||
|
|
||||||
$columns = [
|
$columns = [
|
||||||
// Sélectionner cette colonne, mais ne pas la mettre dans la liste des colonnes
|
// Sélectionner cette colonne, mais ne pas la mettre dans la liste des colonnes
|
||||||
|
|
@ -390,7 +357,7 @@ class Facture
|
||||||
'receveur' => [
|
'receveur' => [
|
||||||
'label' => 'Receveur',
|
'label' => 'Receveur',
|
||||||
// l'identité du membre peut être redéfinie dans la configuration des membres
|
// l'identité du membre peut être redéfinie dans la configuration des membres
|
||||||
'select' => sprintf('CASE WHEN receveur_membre THEN COALESCE(NULLIF(%s, \'\'), \'** ABSENT **\') ELSE c.nom END', $id_field),
|
'select' => sprintf('CASE WHEN receveur_membre THEN CASE %s WHEN "" THEN "** ABSENT **" ELSE %s END ELSE c.nom END', $id_field, $id_field),
|
||||||
],
|
],
|
||||||
'receveur_adresse' => [
|
'receveur_adresse' => [
|
||||||
// l'adresse peut être redéfinie dans la configuration du plugin
|
// l'adresse peut être redéfinie dans la configuration du plugin
|
||||||
|
|
@ -426,7 +393,7 @@ class Facture
|
||||||
|
|
||||||
$tables = 'plugin_facturation_factures AS f
|
$tables = 'plugin_facturation_factures AS f
|
||||||
INNER JOIN plugin_facturation_paiement AS mp ON mp.code = f.moyen_paiement
|
INNER JOIN plugin_facturation_paiement AS mp ON mp.code = f.moyen_paiement
|
||||||
LEFT JOIN users_view AS u ON f.receveur_membre = 1 AND u.id = f.receveur_id
|
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';
|
LEFT JOIN plugin_facturation_clients AS c ON f.receveur_membre = 0 AND c.id = f.receveur_id';
|
||||||
|
|
||||||
if ($all) {
|
if ($all) {
|
||||||
|
|
@ -437,7 +404,7 @@ class Facture
|
||||||
$list = new DynamicList($columns, $tables, $where);
|
$list = new DynamicList($columns, $tables, $where);
|
||||||
$list->orderBy('date_emission', true);
|
$list->orderBy('date_emission', true);
|
||||||
|
|
||||||
$currency = Config::getInstance()->currency;
|
$currency = Config::getInstance()->monnaie;
|
||||||
|
|
||||||
$list->setModifier(function ($row) use ($currency) {
|
$list->setModifier(function ($row) use ($currency) {
|
||||||
// Remplir la colonne virtuelle
|
// Remplir la colonne virtuelle
|
||||||
|
|
@ -493,10 +460,7 @@ class Facture
|
||||||
}
|
}
|
||||||
else // Si c'est un membre de l'asso
|
else // Si c'est un membre de l'asso
|
||||||
{
|
{
|
||||||
if (!\Paheko\Users\Users::exists((int) $id))
|
throw new UserException("Woopsie, g pô encore implémenté l'usage des users de l'asso comme clients");
|
||||||
{
|
|
||||||
throw new UserException("Ce membre n'existe pas.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$r = (array)DB::getInstance()->get('SELECT *, strftime(\'%s\', date_emission) AS date_emission,
|
$r = (array)DB::getInstance()->get('SELECT *, strftime(\'%s\', date_emission) AS date_emission,
|
||||||
|
|
@ -528,10 +492,7 @@ class Facture
|
||||||
}
|
}
|
||||||
else // Si c'est un membre de l'asso
|
else // Si c'est un membre de l'asso
|
||||||
{
|
{
|
||||||
if (!\Paheko\Users\Users::exists((int) $id))
|
throw new UserException("Woopsie, g pô encore implémenté l'usage des users de l'asso comme clients");
|
||||||
{
|
|
||||||
throw new UserException("Ce membre n'existe pas.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return DB::getInstance()->test('plugin_facturation_factures', 'receveur_membre = ? AND receveur_id = ?', $base, $id);
|
return DB::getInstance()->test('plugin_facturation_factures', 'receveur_membre = ? AND receveur_id = ?', $base, $id);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
name="Facturation"
|
name="Facturation"
|
||||||
description="Permet d'éditer des factures et devis à ses membres ainsi qu'à une base de clients supplémentaire."
|
description="Permet d'éditer des factures et devis à ses membres ainsi qu'à une base de clients supplémentaire."
|
||||||
author="Sur la base de Jean-Christophe Engel, amélioré par Jordan"
|
author="zou ; adapté par Jean-Christophe Engel"
|
||||||
url="https://gitea.zaclys.com/lesanges/facturation"
|
url="https://gitea.zaclys.com/lesanges/facturation"
|
||||||
version="0.27"
|
version="0.17.2"
|
||||||
menu=true
|
menu=true
|
||||||
restrict_section="accounting"
|
restrict_section="accounting"
|
||||||
restrict_level="read"
|
restrict_level="read"
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@
|
||||||
<dt><label>Document adressé à :</label></dt>
|
<dt><label>Document adressé à :</label></dt>
|
||||||
{if !empty($clients)}
|
{if !empty($clients)}
|
||||||
<dd>
|
<dd>
|
||||||
{input type="radio" name="base_receveur" value="membre" label="Un·e membre" default="membre" source=$doc}
|
{input type="radio" name="base_receveur" value="membre" label="Un·e membre" default=1 source=$doc}
|
||||||
{input type="radio" name="base_receveur" value="client" label="Un·e client·e" source=$doc}
|
{input type="radio" name="base_receveur" value="client" label="Un·e client·e" source=$doc}
|
||||||
</dd>
|
</dd>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
||||||
|
|
@ -45,15 +45,9 @@
|
||||||
function changeTypeSaisie(type)
|
function changeTypeSaisie(type)
|
||||||
{
|
{
|
||||||
g.toggle(['.type_client', '.type_membre'], false);
|
g.toggle(['.type_client', '.type_membre'], false);
|
||||||
document.querySelectorAll('.type_client input, .type_client select, .type_client textarea, .type_membre input, .type_membre select, .type_membre textarea').forEach((e) => {
|
|
||||||
e.disabled = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (type) {
|
if (type) {
|
||||||
g.toggle('.type_' + type, true);
|
g.toggle('.type_' + type, true);
|
||||||
document.querySelectorAll('.type_' + type + ' input, .type_' + type + ' select, .type_' + type + ' textarea').forEach((e) => {
|
|
||||||
e.disabled = false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,11 +76,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const selclient = document.querySelector('#f_client');
|
const selclient = document.querySelector('#f_client');
|
||||||
if (selclient) {
|
selclient.addEventListener("change", () => {
|
||||||
selclient.addEventListener("change", () => {
|
modifierContact(selclient, '#f_contact_list', '#f_nom_contact');
|
||||||
modifierContact(selclient, '#f_contact_list', '#f_nom_contact');
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} ());
|
} ());
|
||||||
|
|
||||||
|
|
@ -94,18 +86,12 @@
|
||||||
// Hide type specific parts of the form
|
// Hide type specific parts of the form
|
||||||
function hideAllTypes() {
|
function hideAllTypes() {
|
||||||
g.toggle('[data-types]', false);
|
g.toggle('[data-types]', false);
|
||||||
document.querySelectorAll('[data-types] input, [data-types] select, [data-types] textarea, [data-types] button').forEach((e) => {
|
|
||||||
e.disabled = true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle parts of the form when a type is selected
|
// Toggle parts of the form when a type is selected
|
||||||
function selectType(v) {
|
function selectType(v) {
|
||||||
hideAllTypes();
|
hideAllTypes();
|
||||||
g.toggle('[data-types~=t' + v + ']', true);
|
g.toggle('[data-types~=t' + v + ']', true);
|
||||||
document.querySelectorAll('[data-types~=t' + v + '] input, [data-types~=t' + v + '] select, [data-types~=t' + v + '] textarea, [data-types~=t' + v + '] button').forEach((e) => {
|
|
||||||
e.disabled = false;
|
|
||||||
});
|
|
||||||
g.toggle('[data-types=all-but-advanced]', v != 0);
|
g.toggle('[data-types=all-but-advanced]', v != 0);
|
||||||
// Disable required form elements, or the form won't be able to be submitted
|
// Disable required form elements, or the form won't be able to be submitted
|
||||||
$('[data-types=all-but-advanced] input[required]').forEach((e) => {
|
$('[data-types=all-but-advanced] input[required]').forEach((e) => {
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_ADMIN)}
|
{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_ADMIN)}
|
||||||
{linkbutton shape="delete" href="client_supprimer.php?id=%d"|args:$row.id label="Supprimer"}
|
{linkbutton shape="delete" href="client_supprimer.php?id=%d"|args:$row.id label="Supprimer"}
|
||||||
{/if}
|
{/if}
|
||||||
{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_WRITE)}
|
{if $session->canAccess($session::SECTION_USERS, $session::ACCESS_ADMIN)}
|
||||||
{linkbutton shape="edit" href="client_modifier.php?id=%d"|args:$row.id label="Modifier"}
|
{linkbutton shape="edit" href="client_modifier.php?id=%d"|args:$row.id label="Modifier"}
|
||||||
{/if}
|
{/if}
|
||||||
{linkbutton shape="user" href="client.php?id=%d"|args:$row.id label="Fiche client"}
|
{linkbutton shape="user" href="client.php?id=%d"|args:$row.id label="Fiche client"}
|
||||||
|
|
|
||||||
|
|
@ -38,27 +38,14 @@
|
||||||
{input type="checkbox" name="logo" value="1" source=$conf label="Imprimer le logo de l'association"}
|
{input type="checkbox" name="logo" value="1" source=$conf label="Imprimer le logo de l'association"}
|
||||||
{input type="textarea" class="full-width" rows="5" name="footer" source=$conf label="Pied de document — informations légales" required=true}
|
{input type="textarea" class="full-width" rows="5" name="footer" source=$conf label="Pied de document — informations légales" required=true}
|
||||||
</dl>
|
</dl>
|
||||||
<fieldset>
|
|
||||||
<legend>Présentation des factures</legend>
|
|
||||||
<dl>
|
|
||||||
{input type="select" name="invoice_theme" label="Style visuel" options=$invoice_themes source=$conf}
|
|
||||||
{input type="color" name="invoice_accent_color" label="Couleur d'accent" source=$conf default="#4656A5"}
|
|
||||||
{input type="text" name="payment_terms" label="Conditions de paiement" source=$conf}
|
|
||||||
{input type="textarea" class="full-width" rows="3" name="invoice_note" source=$conf label="Note affichée sous le tableau"}
|
|
||||||
{input type="checkbox" name="invoice_show_status" value="1" source=$conf label="Afficher le statut de règlement sur la facture"}
|
|
||||||
{input type="checkbox" name="invoice_show_address_borders" value="1" source=$conf label="Afficher les bordures des blocs émetteur et destinataire"}
|
|
||||||
{input type="checkbox" name="invoice_show_email" value="1" source=$conf label="Afficher les adresses email"}
|
|
||||||
{input type="checkbox" name="invoice_show_web" value="1" source=$conf label="Afficher le site web de l'association"}
|
|
||||||
</dl>
|
|
||||||
</fieldset>
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>
|
<legend>
|
||||||
Choisir les champs à faire figurer sur la facture ou le devis pour l'adresse d'un membre
|
Choisir les champs à faire figurer sur la facture ou le devis pour l'adresse d'un membre
|
||||||
</legend>
|
</legend>
|
||||||
<dl>
|
<dl>
|
||||||
{input type="select" name="adresse_client" label="Adresse" required=false options=$champsPaheko source=$conf}
|
{input type="select" name="adresse_client" label="Adresse" required=true options=$champsPaheko source=$conf}
|
||||||
{input type="select" name="code_postal_client" label="Code postal" required=false options=$champsPaheko source=$conf}
|
{input type="select" name="code_postal_client" label="Code postal" required=true options=$champsPaheko source=$conf}
|
||||||
{input type="select" name="ville_client" label="Ville" required=false options=$champsPaheko source=$conf}
|
{input type="select" name="ville_client" label="Ville" required=true options=$champsPaheko source=$conf}
|
||||||
</dl>
|
</dl>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
@ -73,6 +60,7 @@
|
||||||
F = Facture, D = Devis
|
F = Facture, D = Devis
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
<i>Pour personnaliser l'apparence de la facture, il faut pour l'instant se retrousser les manches et éditer soi-même le fichier admin/pdf.php du plugin ! </i>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<p class="submit">
|
<p class="submit">
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<p class="submit">
|
<p class="submit">
|
||||||
{csrf_field key=$csrf_key}
|
{csrf_field key="delete_doc_"|cat:$doc.id}
|
||||||
<input type="submit" name="delete" value="Supprimer →" />
|
<input type="submit" name="delete" value="Supprimer →" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
|
||||||
134
upgrade.php
134
upgrade.php
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Paheko;
|
namespace Paheko;
|
||||||
use Paheko\Plugin\Facturation\Facture;
|
use Paheko\Plugin\Facturation\Facture;
|
||||||
|
use Paheko\Entities\Files\File;
|
||||||
|
|
||||||
$db = DB::getInstance();
|
$db = DB::getInstance();
|
||||||
$old_version = $plugin->oldVersion();
|
$old_version = $plugin->oldVersion();
|
||||||
|
|
@ -24,9 +25,11 @@ if (version_compare($old_version, '0.3.0', '<'))
|
||||||
$conf = $db->getAssoc('SELECT cle, valeur FROM plugin_facturation_config ORDER BY cle;');
|
$conf = $db->getAssoc('SELECT cle, valeur FROM plugin_facturation_config ORDER BY cle;');
|
||||||
foreach($conf as $k=>$v)
|
foreach($conf as $k=>$v)
|
||||||
{
|
{
|
||||||
$plugin->setConfigProperty($k, $v);
|
if(!$plugin->setConfig($k, $v))
|
||||||
|
{
|
||||||
|
throw new UserException('Erreur dans la conversion de la configuration pour la clé : '.$k);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$plugin->save();
|
|
||||||
$db->exec('DROP TABLE `plugin_facturation_config`;');
|
$db->exec('DROP TABLE `plugin_facturation_config`;');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,13 +59,13 @@ if (version_compare($old_version, '0.4.0', '<'))
|
||||||
type_facture INTEGER NOT NULL DEFAULT 0,
|
type_facture INTEGER NOT NULL DEFAULT 0,
|
||||||
numero TEXT NOT NULL UNIQUE,
|
numero TEXT NOT NULL UNIQUE,
|
||||||
receveur_membre INTEGER NOT NULL, -- bool
|
receveur_membre INTEGER NOT NULL, -- bool
|
||||||
receveur_id INTEGER NOT NULL,
|
receveur_id INTEGER NOT NULL,
|
||||||
date_emission TEXT NOT NULL, -- CHECK (date(date_emission) IS NOT NULL AND date(date_emission) = date_emission),
|
date_emission TEXT NOT NULL, -- CHECK (date(date_emission) IS NOT NULL AND date(date_emission) = date_emission),
|
||||||
date_echeance TEXT NOT NULL, -- CHECK (date(date_echeance) IS NOT NULL AND date(date_echeance) = date_echeance),
|
date_echeance TEXT NOT NULL, -- CHECK (date(date_echeance) IS NOT NULL AND date(date_echeance) = date_echeance),
|
||||||
reglee INTEGER DEFAULT 0, -- bool
|
reglee INTEGER DEFAULT 0, -- bool
|
||||||
archivee INTEGER DEFAULT 0, -- bool
|
archivee INTEGER DEFAULT 0, -- bool
|
||||||
moyen_paiement TEXT NOT NULL,
|
moyen_paiement TEXT NOT NULL,
|
||||||
contenu TEXT NOT NULL,
|
contenu TEXT NOT NULL,
|
||||||
total REAL DEFAULT 0
|
total REAL DEFAULT 0
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -78,13 +81,13 @@ EOT
|
||||||
// 0.6.0 -
|
// 0.6.0 -
|
||||||
if (version_compare($old_version, '0.6.0', '<'))
|
if (version_compare($old_version, '0.6.0', '<'))
|
||||||
{
|
{
|
||||||
defined('DEVIS') || define('DEVIS', 0);
|
define('DEVIS', 0);
|
||||||
defined('FACT') || define('FACT', 1);
|
define('FACT', 1);
|
||||||
defined('CERFA') || define('CERFA', 2);
|
define('CERFA', 2);
|
||||||
defined('COTIS') || define('COTIS', 3);
|
define('COTIS', 3);
|
||||||
$facture = new Facture;
|
$facture = new Facture;
|
||||||
$r = $db->first('SELECT id, total FROM plugin_facturation_factures;');
|
$r = $db->first('SELECT id, total FROM plugin_facturation_factures;');
|
||||||
if ($r && strpos((string) $r->total, '.') !== false)
|
if (strpos($r->total,'.'))
|
||||||
{
|
{
|
||||||
// SQL -> total integer
|
// SQL -> total integer
|
||||||
$db->exec(<<<EOT
|
$db->exec(<<<EOT
|
||||||
|
|
@ -94,13 +97,13 @@ if (version_compare($old_version, '0.6.0', '<'))
|
||||||
type_facture INTEGER NOT NULL DEFAULT 0,
|
type_facture INTEGER NOT NULL DEFAULT 0,
|
||||||
numero TEXT NOT NULL UNIQUE,
|
numero TEXT NOT NULL UNIQUE,
|
||||||
receveur_membre INTEGER NOT NULL, -- bool
|
receveur_membre INTEGER NOT NULL, -- bool
|
||||||
receveur_id INTEGER NOT NULL,
|
receveur_id INTEGER NOT NULL,
|
||||||
date_emission TEXT NOT NULL, -- CHECK (date(date_emission) IS NOT NULL AND date(date_emission) = date_emission),
|
date_emission TEXT NOT NULL, -- CHECK (date(date_emission) IS NOT NULL AND date(date_emission) = date_emission),
|
||||||
date_echeance TEXT NOT NULL, -- CHECK (date(date_echeance) IS NOT NULL AND date(date_echeance) = date_echeance),
|
date_echeance TEXT NOT NULL, -- CHECK (date(date_echeance) IS NOT NULL AND date(date_echeance) = date_echeance),
|
||||||
reglee INTEGER DEFAULT 0, -- bool
|
reglee INTEGER DEFAULT 0, -- bool
|
||||||
archivee INTEGER DEFAULT 0, -- bool
|
archivee INTEGER DEFAULT 0, -- bool
|
||||||
moyen_paiement TEXT NOT NULL,
|
moyen_paiement TEXT NOT NULL,
|
||||||
contenu TEXT NOT NULL,
|
contenu TEXT NOT NULL,
|
||||||
total INTEGER DEFAULT 0
|
total INTEGER DEFAULT 0
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -113,8 +116,6 @@ EOT
|
||||||
$factures = $facture->listAll();
|
$factures = $facture->listAll();
|
||||||
foreach($factures as $k=>$f)
|
foreach($factures as $k=>$f)
|
||||||
{
|
{
|
||||||
$contenu = [];
|
|
||||||
|
|
||||||
foreach($f->contenu as $line => $content)
|
foreach($f->contenu as $line => $content)
|
||||||
{
|
{
|
||||||
// Petit bug qui peut arriver avec des contenus mal enregistrés en db
|
// Petit bug qui peut arriver avec des contenus mal enregistrés en db
|
||||||
|
|
@ -136,16 +137,19 @@ EOT
|
||||||
$facture->edit($f->id, $data);
|
$facture->edit($f->id, $data);
|
||||||
unset($contenu);
|
unset($contenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$path = __DIR__.'/data/default_sign.png';
|
||||||
|
$png = (new File)->createAndStore('skel/plugin/facturation','sign.png', $path, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0.6.2 -
|
// 0.6.2 -
|
||||||
if (version_compare($old_version, '0.6.2', '<'))
|
if (version_compare($old_version, '0.6.2', '<'))
|
||||||
{
|
{
|
||||||
defined('DEVIS') || define('DEVIS', 0);
|
define('DEVIS', 0);
|
||||||
defined('FACT') || define('FACT', 1);
|
define('FACT', 1);
|
||||||
defined('CERFA') || define('CERFA', 2);
|
define('CERFA', 2);
|
||||||
defined('COTIS') || define('COTIS', 3);
|
define('COTIS', 3);
|
||||||
$facture = new Facture;
|
$facture = new Facture;
|
||||||
$db->exec(<<<EOT
|
$db->exec(<<<EOT
|
||||||
INSERT OR IGNORE INTO plugin_facturation_paiement
|
INSERT OR IGNORE INTO plugin_facturation_paiement
|
||||||
|
|
@ -197,8 +201,7 @@ EOT
|
||||||
// 0.7.1 - Ajout clé config TTC/HT
|
// 0.7.1 - Ajout clé config TTC/HT
|
||||||
if (version_compare($old_version, '0.7.1', '<'))
|
if (version_compare($old_version, '0.7.1', '<'))
|
||||||
{
|
{
|
||||||
$plugin->setConfigProperty('ttc', false);
|
$plugin->setConfig('ttc', false);
|
||||||
$plugin->save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0.8.1 - Signal menu item
|
// 0.8.1 - Signal menu item
|
||||||
|
|
@ -214,10 +217,10 @@ if (version_compare($old_version, '0.8.5', '<'))
|
||||||
CREATE TABLE IF NOT EXISTS plugin_facturation_clients_tmp
|
CREATE TABLE IF NOT EXISTS plugin_facturation_clients_tmp
|
||||||
(
|
(
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
nom TEXT NOT NULL,
|
nom TEXT NOT NULL,
|
||||||
adresse TEXT NOT NULL,
|
adresse TEXT NOT NULL,
|
||||||
code_postal TEXT NOT NULL,
|
code_postal TEXT NOT NULL,
|
||||||
ville TEXT NOT NULL,
|
ville TEXT NOT NULL,
|
||||||
siret TEXT,
|
siret TEXT,
|
||||||
date_creation TEXT NOT NULL DEFAULT CURRENT_DATE CHECK (date(date_creation) IS NOT NULL AND date(date_creation) = date_creation),
|
date_creation TEXT NOT NULL DEFAULT CURRENT_DATE CHECK (date(date_creation) IS NOT NULL AND date(date_creation) = date_creation),
|
||||||
telephone TEXT,
|
telephone TEXT,
|
||||||
|
|
@ -258,10 +261,10 @@ if (version_compare($old_version, '0.15', '<'))
|
||||||
CREATE TABLE IF NOT EXISTS plugin_facturation_clients_tmp
|
CREATE TABLE IF NOT EXISTS plugin_facturation_clients_tmp
|
||||||
(
|
(
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
nom TEXT NOT NULL,
|
nom TEXT NOT NULL,
|
||||||
adresse TEXT NOT NULL,
|
adresse TEXT NOT NULL,
|
||||||
code_postal TEXT NOT NULL,
|
code_postal TEXT NOT NULL,
|
||||||
ville TEXT NOT NULL,
|
ville TEXT NOT NULL,
|
||||||
siret TEXT,
|
siret TEXT,
|
||||||
date_creation TEXT NOT NULL DEFAULT CURRENT_DATE CHECK (date(date_creation) IS NOT NULL AND date(date_creation) = date_creation),
|
date_creation TEXT NOT NULL DEFAULT CURRENT_DATE CHECK (date(date_creation) IS NOT NULL AND date(date_creation) = date_creation),
|
||||||
telephone TEXT,
|
telephone TEXT,
|
||||||
|
|
@ -290,13 +293,13 @@ EOT
|
||||||
type_facture INTEGER NOT NULL DEFAULT 0,
|
type_facture INTEGER NOT NULL DEFAULT 0,
|
||||||
numero TEXT NOT NULL UNIQUE,
|
numero TEXT NOT NULL UNIQUE,
|
||||||
receveur_membre INTEGER NOT NULL, -- bool
|
receveur_membre INTEGER NOT NULL, -- bool
|
||||||
receveur_id INTEGER NOT NULL,
|
receveur_id INTEGER NOT NULL,
|
||||||
date_emission TEXT NOT NULL, -- CHECK (date(date_emission) IS NOT NULL AND date(date_emission) = date_emission),
|
date_emission TEXT NOT NULL, -- CHECK (date(date_emission) IS NOT NULL AND date(date_emission) = date_emission),
|
||||||
date_echeance TEXT NOT NULL, -- CHECK (date(date_echeance) IS NOT NULL AND date(date_echeance) = date_echeance),
|
date_echeance TEXT NOT NULL, -- CHECK (date(date_echeance) IS NOT NULL AND date(date_echeance) = date_echeance),
|
||||||
reglee INTEGER DEFAULT 0, -- bool
|
reglee INTEGER DEFAULT 0, -- bool
|
||||||
archivee INTEGER DEFAULT 0, -- bool
|
archivee INTEGER DEFAULT 0, -- bool
|
||||||
moyen_paiement TEXT NOT NULL,
|
moyen_paiement TEXT NOT NULL,
|
||||||
contenu TEXT NOT NULL,
|
contenu TEXT NOT NULL,
|
||||||
total INTEGER DEFAULT 0,
|
total INTEGER DEFAULT 0,
|
||||||
nom_contact TEXT,
|
nom_contact TEXT,
|
||||||
numero_commande TEXT,
|
numero_commande TEXT,
|
||||||
|
|
@ -319,64 +322,3 @@ EOT
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0.18 - Personnalisation du rendu des factures
|
|
||||||
if (version_compare($old_version, '0.18', '<'))
|
|
||||||
{
|
|
||||||
$defaults = json_decode(file_get_contents(__DIR__ . '/config.json'), true);
|
|
||||||
|
|
||||||
foreach (['invoice_theme', 'invoice_accent_color', 'invoice_note', 'payment_terms', 'invoice_show_status'] as $key) {
|
|
||||||
if ($plugin->getConfig($key) === null && array_key_exists($key, $defaults)) {
|
|
||||||
$plugin->setConfigProperty($key, $defaults[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$plugin->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 0.19 - Réparation des schémas partiellement migrés
|
|
||||||
if (version_compare($old_version, '0.19', '<'))
|
|
||||||
{
|
|
||||||
$tables = [
|
|
||||||
'plugin_facturation_clients' => [
|
|
||||||
'siret' => 'siret TEXT',
|
|
||||||
'telephone' => 'telephone TEXT',
|
|
||||||
'email' => 'email TEXT',
|
|
||||||
'nom_contact' => 'nom_contact TEXT',
|
|
||||||
'note' => 'note TEXT',
|
|
||||||
],
|
|
||||||
'plugin_facturation_factures' => [
|
|
||||||
'nom_contact' => 'nom_contact TEXT',
|
|
||||||
'numero_commande' => 'numero_commande TEXT',
|
|
||||||
'reference_acheteur' => 'reference_acheteur TEXT',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($tables as $table => $columns) {
|
|
||||||
$schema = $db->getTableSchema($table);
|
|
||||||
|
|
||||||
if (!$schema) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($columns as $column => $definition) {
|
|
||||||
if (!isset($schema['columns'][$column])) {
|
|
||||||
$db->exec(sprintf('ALTER TABLE %s ADD COLUMN %s;', $db->quoteIdentifier($table), $definition));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 0.25 - Options d'affichage du rendu facture
|
|
||||||
if (version_compare($old_version, '0.25', '<'))
|
|
||||||
{
|
|
||||||
$defaults = json_decode(file_get_contents(__DIR__ . '/config.json'), true);
|
|
||||||
|
|
||||||
foreach (['invoice_show_address_borders', 'invoice_show_email', 'invoice_show_web'] as $key) {
|
|
||||||
if ($plugin->getConfig($key) === null && array_key_exists($key, $defaults)) {
|
|
||||||
$plugin->setConfigProperty($key, $defaults[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$plugin->save();
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue