Initial commit

This commit is contained in:
Julien Palard 2023-04-02 23:32:30 +02:00
commit 8bc69e9b15
Signed by: mdk
GPG Key ID: 0EFC1AC1006886F8
11 changed files with 345 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.venv/
dist/
build/
__pycache__/
.envrc

4
MANIFEST.in Normal file
View File

@ -0,0 +1,4 @@
include quittance/templates/quittance.html
include quittance/templates/quittance.css
include quittance/templates/*.ttf
include quittance/templates/*.otf

35
README.md Normal file
View File

@ -0,0 +1,35 @@
# Quittance
Génère des quittances de loyers, au format PDF.
## Installation
pip install quittance
## Configuration
Il faut dabord configurer quelques informations (addresses, noms…),
le plus simple est dexé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
Cest 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.

36
pyproject.toml Normal file
View File

@ -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]

1
quittance/__init__.py Normal file
View File

@ -0,0 +1 @@
__version__ = "0.1"

111
quittance/__main__.py Normal file
View File

@ -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.

View File

@ -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;
}

View File

@ -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.