Skip to content

Commit 5adb5bc

Browse files
authored
Merge pull request #15 from aparcar/get_contracts
add get_contracts function and overview for cli
2 parents 39f1b2d + 313ec50 commit 5adb5bc

File tree

7 files changed

+213
-269
lines changed

7 files changed

+213
-269
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,10 @@
11
*.egg-info/*
22
__pycache__
3+
assets/
4+
bin/
5+
contracts/
6+
invoices/
7+
lib*
8+
pyvenv.cfg
9+
settings.yaml
10+
share/

rechnung/cli.py

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from .settings import get_settings_from_cwd, copy_assets, create_required_settings_file
66
from .invoice import create_invoices, render_invoices, send_invoices
7-
from .contract import create_contracts, render_contracts, send_contract
7+
from .contract import create_contracts, render_contracts, send_contract, get_contracts
88

99
cwd = os.getcwd()
1010

@@ -32,55 +32,76 @@ def init():
3232

3333

3434
@cli1.command()
35-
@click.argument("start_date")
36-
@click.argument("end_date")
37-
@click.argument("n_months", type=int)
38-
@click.argument("year")
39-
@click.argument("suffix")
40-
def create(start_date, end_date, n_months, year, suffix):
35+
@click.argument("year", type=int)
36+
@click.argument("month", type=int)
37+
def create(year, month):
4138
"""
4239
Mass create invoices.
4340
"""
4441
print("Creating invoices...")
45-
create_invoices(cwd, start_date, end_date, n_months, year, suffix)
42+
settings = get_settings_from_cwd(cwd)
43+
create_invoices(settings, year, month)
4644

4745

4846
@cli1.command()
49-
def contracts():
47+
def print_contracts():
5048
"""
51-
Mass create contracts.
49+
Print an overview of all contracs
5250
"""
53-
print("Not yet implemented")
51+
settings = get_settings_from_cwd(cwd)
52+
for cid, data in get_contracts(settings).items():
53+
slug = data.get("email", "unknown")
54+
total_monthly = sum(map(lambda i: i["price"], data["items"]))
55+
print(f"{cid}: {slug} {data['start']} {total_monthly}€")
56+
57+
58+
@cli1.command()
59+
def print_stats():
60+
"""
61+
Print stats about the contracts
62+
"""
63+
settings = get_settings_from_cwd(cwd)
64+
contracts = get_contracts(settings).values()
65+
print(f"{len(contracts)} contracts in total")
66+
67+
total_monthly = sum(
68+
map(lambda x: x[0]["price"], list(map(lambda i: i["items"], contracts)))
69+
)
70+
print(f"{total_monthly:.2f}€ per month")
5471

5572

5673
@cli1.command()
5774
def render():
5875
"""
59-
Render all unrendered invoices.
76+
Render all unrendered invoices and contracs
6077
"""
6178
print("Rendering invoices and contracts...")
62-
render_invoices(cwd)
63-
render_contracts(cwd)
79+
settings = get_settings_from_cwd(cwd)
80+
render_invoices(settings)
81+
render_contracts(settings)
6482

6583

6684
@cli1.command()
67-
@click.argument("year_suffix")
68-
def send(year_suffix):
85+
@click.argument("year", type=int)
86+
@click.argument("month", type=int)
87+
def send(year, month):
6988
"""
7089
Send invoices by email.
7190
"""
72-
print("Sending invoices *.{}".format(year_suffix))
73-
send_invoices(cwd, year_suffix)
91+
print(f"Sending invoices for {year}.{month:02}")
92+
settings = get_settings_from_cwd(cwd)
93+
send_invoices(settings, year, month)
7494

7595

7696
@cli1.command()
77-
@click.argument("cid")
97+
@click.argument("cid", type=int)
7898
def send_contract_mail(cid):
7999
"""
80100
Send contract by email.
81101
"""
82-
print("Sending contract for customer {}".format(cid))
83-
send_contract(cwd, cid)
102+
print(f"Sending contract {cid}")
103+
settings = get_settings_from_cwd(cwd)
104+
send_contract(settings, cid)
84105

85106

86107
cli = click.CommandCollection(sources=[cli1])

rechnung/contract.py

Lines changed: 71 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@
33
import os
44
import os.path
55
import yaml
6+
from collections import OrderedDict
67

78
from pathlib import Path
8-
from .invoice import get_positions, get_customers
9-
from .settings import get_settings_from_cwd
109
from .helpers import (
1110
generate_pdf,
1211
get_pdf,
@@ -17,21 +16,36 @@
1716
)
1817

1918

20-
def generate_contract(customer, positions):
19+
def get_contracts(settings, year=None, month=None, inactive=False):
20+
contracts = OrderedDict()
21+
for filename in settings.contracts_dir.glob("*.yaml"):
22+
with open(Path(settings.contracts_dir / filename), "r") as contract_file:
23+
contract = yaml.safe_load(contract_file)
2124

25+
if year and month:
26+
if contract["start"] < datetime.date(year, month, 1):
27+
contracts[contract["cid"]] = contract
28+
else:
29+
print(f"Ignoring {contract['cid']} with start {contract['start']}")
30+
else:
31+
contracts[contract["cid"]] = contract
32+
33+
return {k: contracts[k] for k in sorted(contracts)}
34+
35+
36+
def create_contract(customer, positions):
2237
contract_data = customer
2338
contract_data["product"] = positions[0]
24-
contract_data["product"]["price"] = round(positions[0]["price"] * 1.19, 2)
25-
26-
if "email" not in customer.keys():
27-
contract_data["email"] = None
28-
else:
29-
contract_data["email"] = customer["email"]
39+
contract_data["product"]["price"] = round(
40+
positions[0]["price"] * 1.0 + settings.vat, 2
41+
)
42+
contract_data["email"] = customer["email"]
3043

3144
return contract_data
3245

3346

34-
def render_pdf_contracts(directory, template, settings):
47+
def render_contracts(settings):
48+
template = get_template(settings.contract_template_file)
3549
logo_path = settings.assets_dir / "logo.png"
3650

3751
for contract_filename in Path(settings.contracts_dir).glob("*.yaml"):
@@ -43,43 +57,44 @@ def render_pdf_contracts(directory, template, settings):
4357
print("Rendering contract pdf for {}".format(contract_data["cid"]))
4458
contract_data["logo_path"] = logo_path
4559

46-
for element in ["price", "initial_cost"]:
47-
contract_data["product"][element] = locale.format_string(
48-
"%.2f", contract_data["product"][element]
49-
)
60+
for item in contract_data["items"]:
61+
for element in ["price", "initial_cost"]:
62+
item[element] = locale.format_string("%.2f", item.get(element, 0))
5063

5164
if contract_data["start"]:
5265
try:
53-
contract_data["start"] = datetime.datetime.strptime(
54-
contract_data["start"], "%Y-%m-%d"
55-
).strftime("%-d. %B %Y")
66+
contract_data["start"] = contract_data["start"].strftime(
67+
"%-d. %B %Y"
68+
)
5669
except ValueError:
5770
pass
5871

72+
print(contract_data)
5973
contract_html = template.render(contract=contract_data)
6074

6175
generate_pdf(
62-
contract_html, settings.contract_css_file, contract_pdf_filename
76+
contract_html, settings.contract_css_asset_file, contract_pdf_filename
6377
)
6478

6579

66-
def save_contract_yaml(contracts_dir, contract_data):
67-
outfilename = os.path.join(contracts_dir, "{}.yaml".format(contract_data["cid"]))
80+
def save_contract_yaml(settings, contract_data):
81+
outfilename = settings.contracts_dir / contract_data["cid"] + ".yaml"
6882
try:
6983
with open(outfilename, "x") as outfile:
7084
outfile.write(yaml.dump(contract_data, default_flow_style=False))
7185
except FileExistsError:
7286
print("Contract {} already exists.".format(outfilename))
7387

7488

75-
def create_yaml_contracts(contracts_dir, customers, positions):
89+
def create_yaml_contracts(settings, customers, positions):
7690
for cid in customers.keys():
77-
print("Creating contract yaml for {}".format(cid))
78-
contract_data = generate_contract(customers[cid], positions[cid])
79-
save_contract_yaml(contracts_dir, contract_data)
91+
print(f"Creating contract yaml for {cid}")
92+
contract_data = create_contract(customers[cid], positions[cid])
93+
save_contract_yaml(settings, contract_data)
8094

8195

82-
def send_contract_mail(settings, mail_template, cid):
96+
def send_contract(settings, cid):
97+
mail_template = get_template(settings.contract_mail_template_file)
8398
contract_pdf_path = Path(settings.contracts_dir) / f"{cid}.pdf"
8499
contract_yaml_filename = Path(settings.contracts_dir) / f"{cid}.yaml"
85100

@@ -90,31 +105,41 @@ def send_contract_mail(settings, mail_template, cid):
90105
contract_data = yaml.safe_load(yaml_file)
91106

92107
if contract_data["email"] is None:
93-
print("No email given")
108+
print("No email given for contract {cid}")
109+
quit()
94110

95-
contract_pdf_filename = "Dein_Westnetz_Vertrag_{}.pdf".format(cid)
111+
contract_pdf_filename = f"{settings.company} {contract_yaml_filename.stem}.pdf"
96112
contract_mail_text = mail_template.render()
97113
contract_pdf = get_pdf(contract_pdf_path)
98114

99-
product_pdf_file = "{}.pdf".format(contract_data["product"]["description"])
100-
product_pdf_path = Path(settings.assets_dir) / product_pdf_file
101-
product_pdf = get_pdf(product_pdf_path)
102-
103-
policy_pdf_file = settings.policy_attachment_asset_file
104-
policy_pdf_path = Path(settings.assets_dir) / policy_pdf_file
105-
policy_pdf = get_pdf(policy_pdf_path)
106-
107-
pdf_documents = [contract_pdf, product_pdf, policy_pdf]
108-
pdf_filenames = [
109-
contract_pdf_filename,
110-
product_pdf_file,
111-
"Widerrufsbelehrung.pdf",
112-
]
113-
114-
contract_receiver = contract_data["email"]
115+
pdf_documents = [contract_pdf]
116+
pdf_filenames = [contract_pdf_filename]
117+
118+
for item in contract_data["items"]:
119+
item_pdf_file = f"{item['description']}.pdf"
120+
if not item_pdf_file in pdf_filenames:
121+
item_pdf_path = Path(settings.assets_dir / item_pdf_file)
122+
if item_pdf_path.is_file():
123+
item_pdf = get_pdf(item_pdf_path)
124+
pdf_documents.append(item_pdf)
125+
pdf_filenames.append(item_pdf_file)
126+
else:
127+
print(f"Item file {item_pdf_file} not found")
128+
129+
if settings.policy_attachment_asset_file:
130+
policy_pdf_file = settings.policy_attachment_asset_file
131+
policy_pdf_path = settings.assets_dir / policy_pdf_file
132+
if policy_pdf_path.is_file():
133+
policy_pdf = get_pdf(policy_pdf_path)
134+
pdf_documents.append(policy_pdf)
135+
pdf_filenames.append(policy_pdf_file)
136+
else:
137+
print(
138+
f"Policy file {settings.policy_attachment_asset_file.name} not found"
139+
)
115140

116141
contract_email = generate_email_with_pdf_attachments(
117-
contract_receiver,
142+
contract_data["email"],
118143
settings.sender,
119144
settings.contract_mail_subject,
120145
contract_mail_text,
@@ -133,20 +158,6 @@ def send_contract_mail(settings, mail_template, cid):
133158
)
134159

135160

136-
def create_contracts(directory):
137-
settings = get_settings_from_cwd(directory)
138-
customers = get_customers(settings.customers_dir)
161+
def create_contracts(settings):
139162
positions = get_positions(settings.positions_dir)
140-
create_yaml_contracts(settings.contracts_dir, customers, positions)
141-
142-
143-
def render_contracts(directory):
144-
settings = get_settings_from_cwd(directory)
145-
template = get_template(settings.contract_template_file)
146-
render_pdf_contracts(directory, template, settings)
147-
148-
149-
def send_contract(directory, cid):
150-
settings = get_settings_from_cwd(directory)
151-
mail_template = get_template(settings.contract_mail_template_file)
152-
send_contract_mail(settings, mail_template, cid)
163+
create_yaml_contracts(settings)

0 commit comments

Comments
 (0)