Initial commit
This commit is contained in:
commit
8bc69e9b15
|
@ -0,0 +1,5 @@
|
|||
.venv/
|
||||
dist/
|
||||
build/
|
||||
__pycache__/
|
||||
.envrc
|
|
@ -0,0 +1,4 @@
|
|||
include quittance/templates/quittance.html
|
||||
include quittance/templates/quittance.css
|
||||
include quittance/templates/*.ttf
|
||||
include quittance/templates/*.otf
|
|
@ -0,0 +1,35 @@
|
|||
# Quittance
|
||||
|
||||
Génère des quittances de loyers, au format PDF.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
pip install quittance
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
Il faut d’abord configurer quelques informations (addresses, noms…),
|
||||
le plus simple est d’exécuter :
|
||||
|
||||
quittance config
|
||||
|
||||
qui vous génèrera un fichier de configuration et vous proposera de
|
||||
l’éditer.
|
||||
|
||||
Il est possible d’éditer la configuration en ligne de commande :
|
||||
|
||||
quittance config --bailleur-city Lyon --bailleur-country France
|
||||
|
||||
C’est pratique pour de petites corrections, mais pour tout configurer
|
||||
préférez éditer le fichier `toml`.
|
||||
|
||||
|
||||
## Utilisation
|
||||
|
||||
Pour générer une nouvelle quittance, exécutez :
|
||||
|
||||
quittance
|
||||
|
||||
Une nouvelle quittance sera générée au format PDF.
|
|
@ -0,0 +1,36 @@
|
|||
[build-system]
|
||||
requires = ["setuptools", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "quittance"
|
||||
description = "Génère des quittances de loyers."
|
||||
readme = "README.md"
|
||||
license = {text = "MIT License"}
|
||||
authors = [
|
||||
{name = "Julien Palard", email = "julien@palard.fr"},
|
||||
]
|
||||
requires-python = ">= 3.7"
|
||||
dependencies = [
|
||||
"weasyprint",
|
||||
"tomlkit",
|
||||
"jinja2",
|
||||
]
|
||||
dynamic = ["version"]
|
||||
|
||||
[project.urls]
|
||||
homepage = "https://git.afpy.org/mdk/quittance"
|
||||
|
||||
[project.scripts]
|
||||
quittance = "quittance.__main__:main"
|
||||
|
||||
[tool.setuptools]
|
||||
include-package-data = true
|
||||
|
||||
[tool.setuptools.packages]
|
||||
find = {}
|
||||
|
||||
[tool.setuptools.dynamic.version]
|
||||
attr = "quittance.__version__"
|
||||
|
||||
[tool.black]
|
|
@ -0,0 +1 @@
|
|||
__version__ = "0.1"
|
|
@ -0,0 +1,111 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from pathlib import Path
|
||||
from datetime import date, timedelta
|
||||
import sys
|
||||
import json
|
||||
import locale
|
||||
from argparse import ArgumentParser
|
||||
|
||||
from weasyprint import HTML
|
||||
from jinja2 import Environment, PackageLoader, select_autoescape
|
||||
import tomlkit as toml
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = ArgumentParser(description="Génère une quittance de loyer")
|
||||
subparsers = parser.add_subparsers(required=False)
|
||||
|
||||
parser_conf = subparsers.add_parser(
|
||||
"config",
|
||||
help="Quittance configuration. Run without argument to see the current configuration.",
|
||||
)
|
||||
parser_conf.add_argument("--bailleur-name", metavar="'Ano Nymous'")
|
||||
parser_conf.add_argument("--bailleur-address", metavar="'10 route de la corniche'")
|
||||
parser_conf.add_argument("--bailleur-code-postal", metavar="69000")
|
||||
parser_conf.add_argument("--bailleur-city", metavar="'Lyon'")
|
||||
parser_conf.add_argument("--bailleur-country", metavar="'France'")
|
||||
parser_conf.add_argument("--locataire-name", metavar="'Ano Nymous'")
|
||||
parser_conf.add_argument("--locataire-address", metavar="'10 route de la corniche'")
|
||||
parser_conf.add_argument("--locataire-code-postal", metavar="'69000'")
|
||||
parser_conf.add_argument("--locataire-city", metavar="'Lyon'")
|
||||
parser_conf.add_argument("--locataire-country", metavar="'France'")
|
||||
parser_conf.add_argument("--dernier-numero", type=int, metavar="123")
|
||||
parser_conf.add_argument("--loyer-hors-charges", type=int, metavar="123")
|
||||
parser_conf.add_argument("--charges", type=int, metavar="123")
|
||||
parser_conf.set_defaults(func=main_config)
|
||||
|
||||
parser.set_defaults(func=main_new)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
func = args.func
|
||||
del args.func
|
||||
return func(args)
|
||||
|
||||
|
||||
def load_config():
|
||||
try:
|
||||
with open(Path.home() / ".config" / "quittance.toml", "r") as f:
|
||||
config = toml.load(f)
|
||||
except FileNotFoundError:
|
||||
config = {}
|
||||
return config
|
||||
|
||||
|
||||
def save_config(config):
|
||||
with open(Path.home() / ".config" / "quittance.toml", "w") as f:
|
||||
toml.dump(config, f)
|
||||
|
||||
|
||||
def main_config(args):
|
||||
keys = set(vars(args).keys())
|
||||
args = {key: value for key, value in vars(args).items() if value is not None}
|
||||
config = load_config()
|
||||
config.update(args)
|
||||
if "dernier_numero" not in config:
|
||||
config["dernier_numero"] = 0
|
||||
for missing_key in sorted(keys - set(config)):
|
||||
config[missing_key] = ""
|
||||
save_config(config)
|
||||
print(
|
||||
f"You can edit the configuration file: {Path.home() / '.config' / 'quittance.toml'}"
|
||||
)
|
||||
print(toml.dumps(config))
|
||||
|
||||
|
||||
def get_prev_month():
|
||||
today = date.today()
|
||||
first_of_month = today.replace(day=1)
|
||||
last_day_of_last_month = first_of_month - timedelta(days=1)
|
||||
first_day_of_last_month = last_day_of_last_month.replace(day=1)
|
||||
return f"du {first_day_of_last_month:%A %d %B %Y} au {last_day_of_last_month:%A %d %B %Y}"
|
||||
|
||||
|
||||
def main_new(args):
|
||||
locale.setlocale(locale.LC_ALL, "fr_FR.UTF-8")
|
||||
env = Environment(loader=PackageLoader("quittance"), autoescape=select_autoescape())
|
||||
template = env.get_template("quittance.html")
|
||||
config = load_config()
|
||||
|
||||
env = config.copy()
|
||||
|
||||
env["date"] = date.today().strftime("%A %d %B %Y")
|
||||
env["periode"] = get_prev_month()
|
||||
|
||||
config["dernier_numero"] += 1
|
||||
env["quittance_no"] = config["dernier_numero"]
|
||||
save_config(config)
|
||||
|
||||
output = f"{date.today():%Y-%m-%d} quittance-{env['quittance_no']}.pdf"
|
||||
HTML(
|
||||
string=template.render(**env), base_url=str(Path(template.filename).parent)
|
||||
).write_pdf(output)
|
||||
print("Nouvelle quittance :", output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Binary file not shown.
|
@ -0,0 +1,82 @@
|
|||
@font-face {
|
||||
font-family: Pacifico;
|
||||
src: url(pacifico.ttf);
|
||||
}
|
||||
@font-face {
|
||||
font-family: Source Sans Pro;
|
||||
font-weight: 400;
|
||||
src: url(sourcesanspro-regular.otf);
|
||||
}
|
||||
@font-face {
|
||||
font-family: Source Sans Pro;
|
||||
font-weight: 700;
|
||||
src: url(sourcesanspro-bold.otf);
|
||||
}
|
||||
|
||||
@page {
|
||||
font-family: Pacifico;
|
||||
margin: 3cm;
|
||||
}
|
||||
|
||||
html {
|
||||
color: #14213d;
|
||||
font-family: Source Sans Pro;
|
||||
font-size: 11pt;
|
||||
line-height: 1.6;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #1ee494;
|
||||
font-family: Pacifico;
|
||||
font-size: 40pt;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
aside {
|
||||
display: flex;
|
||||
margin: 2em 0 4em;
|
||||
}
|
||||
aside address {
|
||||
font-style: normal;
|
||||
white-space: pre-line;
|
||||
}
|
||||
aside address#from {
|
||||
color: #a9a;
|
||||
flex: 1;
|
||||
}
|
||||
aside address#to {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
dl#informations {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
text-align: right;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
dt, dd {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
}
|
||||
dt {
|
||||
color: #a9a;
|
||||
}
|
||||
dt::before {
|
||||
content: '';
|
||||
display: block;
|
||||
}
|
||||
dt::after {
|
||||
content: ' : ';
|
||||
}
|
||||
|
||||
b#recu {
|
||||
font-size: 16pt;
|
||||
}
|
||||
|
||||
p#legal {
|
||||
font-size: 8pt;
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link href="quittance.css" media="print" rel="stylesheet">
|
||||
<title>Quittance de loyer</title>
|
||||
<meta name="description" content="Quittance de loyer">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Quittance de loyer</h1>
|
||||
|
||||
<aside>
|
||||
<address id="from">
|
||||
<b>Bailleur</b>
|
||||
{{ bailleur_name }}
|
||||
{{ bailleur_address }}
|
||||
{{ bailleur_code_postal }} {{ bailleur_city }}
|
||||
{{ bailleur_country }}
|
||||
</address>
|
||||
|
||||
<address id="to">
|
||||
<b>Locataire Destinataire</b>
|
||||
{{ locataire_name }}
|
||||
{{ locataire_address }}
|
||||
{{ locataire_code_postal}} {{ locataire_city }}
|
||||
{{ locataire_country }}
|
||||
</address>
|
||||
</aside>
|
||||
|
||||
<dl id="informations">
|
||||
<dt>Quittance №</dt>
|
||||
<dd>{{ quittance_no }}</dd>
|
||||
</dl>
|
||||
|
||||
<dl id="payment">
|
||||
<dt><b id="recu">Reçu</b> de</dt>
|
||||
<dd>{{ locataire_name }}</dd>
|
||||
<dt>La somme de</dt>
|
||||
<dd>{{ loyer_hors_charges + charges }} €</dd>
|
||||
<dt>Au titre du paiement du loyer et des charges du logement sis</dt>
|
||||
<dd>{{ locataire_address }} {{locataire_city }}</dd>
|
||||
<dt>Pour la période de location</dt>
|
||||
<dd>{{ periode }}</dd>
|
||||
<dt>Date du règlement</dt>
|
||||
<dd>{{ date }}</dd>
|
||||
</dl>
|
||||
|
||||
<br/>
|
||||
<h3>Détail de la quittance de loyer</h3>
|
||||
|
||||
<dl>
|
||||
<dt>Loyer hors charge</dt><dd>{{ loyer_hors_charges}} €</dd>
|
||||
<dt>Charges / Provisions de charges</dt><dd>{{ charges }} €</dd>
|
||||
<dt>Régularisation annuelle</dt><dd>ø</dd>
|
||||
<dt>Total</dt><dd>{{ loyer_hors_charges + charges}} €</dd>
|
||||
</dl>
|
||||
<br/>
|
||||
<dl id="where">
|
||||
<dt>Fait à</dt>
|
||||
<dd>{{ bailleur_city }}</dd>
|
||||
<dt>Le</dt>
|
||||
<dd>{{ date }}</dd>
|
||||
<dt>Signature du bailleur</dt>
|
||||
<dd>{{ bailleur_name }}</dd>
|
||||
</dl>
|
||||
|
||||
<p id="legal">
|
||||
Le paiement de la présente n'emporte pas présomption de paiement des termes antérieurs. Cette quittance ou ce reçu annule tous les reçus qui auraient pu être donnés pour acompte versé sur le présent terme. En cas de congé précédemment donné, cette quittance ou ce reçu représenterait l'indemnité d'occupation et ne saurait être considéré comme un titre d'occupation. Sous réserve d'encaissement.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue