Thank you, black.

This commit is contained in:
Alexis M 2019-10-02 20:49:18 +02:00
parent 698d2ef87f
commit c0e30927f7
5 changed files with 214 additions and 174 deletions

View file

@ -114,12 +114,9 @@ async def auth_required(request, response):
request["groups"] = groups request["groups"] = groups
group = groups.get_user_group(email) group = groups.get_user_group(email)
user_info = {'email': email} user_info = {"email": email}
if group: if group:
user_info.update(dict( user_info.update(dict(group_id=group.id, group_name=group.name))
group_id=group.id,
group_name=group.name)
)
user = Person(**user_info) user = Person(**user_info)
request["user"] = user request["user"] = user
session.user.set(user) session.user.set(user)
@ -192,14 +189,15 @@ async def logout(request, response):
@app.route("/", methods=["GET"]) @app.route("/", methods=["GET"])
async def home(request, response): async def home(request, response):
if not request['user'].group_id: if not request["user"].group_id:
response.redirect = "/groupes" response.redirect = "/groupes"
return return
response.html( response.html(
"home.html", "home.html",
incoming=Delivery.incoming(), incoming=Delivery.incoming(),
former=Delivery.former(), former=Delivery.former(),
archives=list(Delivery.all(is_archived=True))) archives=list(Delivery.all(is_archived=True)),
)
@app.route("/groupes", methods=["GET"]) @app.route("/groupes", methods=["GET"])
@ -211,8 +209,8 @@ async def handle_groups(request, response):
async def join_group(request, response, id): async def join_group(request, response, id):
user = session.user.get(None) user = session.user.get(None)
group = request["groups"].add_user(user.email, id) group = request["groups"].add_user(user.email, id)
request['groups'].persist() request["groups"].persist()
redirect = '/' if not request['user'].group_id else '/groupes' redirect = "/" if not request["user"].group_id else "/groupes"
response.message(f"Vous avez bien rejoint le groupe '{group.name}'") response.message(f"Vous avez bien rejoint le groupe '{group.name}'")
response.redirect = redirect response.redirect = redirect
@ -224,16 +222,15 @@ async def create_group(request, response):
if request.method == "POST": if request.method == "POST":
form = request.form form = request.form
members = [] members = []
if form.get('members'): if form.get("members"):
members = [m.strip() for m in form.get('members').split(',')] members = [m.strip() for m in form.get("members").split(",")]
if not request['user'].group_id and request['user'].email not in members: if not request["user"].group_id and request["user"].email not in members:
members.append(request['user'].email) members.append(request["user"].email)
group = Group.create( group = Group.create(
id=slugify(form.get('name')), id=slugify(form.get("name")), name=form.get("name"), members=members
name=form.get('name'), )
members=members)
request["groups"].add_group(group) request["groups"].add_group(group)
request["groups"].persist() request["groups"].persist()
response.message(f"Le groupe {group.name} à bien été créé") response.message(f"Le groupe {group.name} à bien été créé")
@ -248,10 +245,10 @@ async def edit_group(request, response, id):
if request.method == "POST": if request.method == "POST":
form = request.form form = request.form
members = [] members = []
if form.get('members'): if form.get("members"):
members = [m.strip() for m in form.get('members').split(',')] members = [m.strip() for m in form.get("members").split(",")]
group.members = members group.members = members
group.name = form.get('name') group.name = form.get("name")
request["groups"].groups[id] = group request["groups"].groups[id] = group
request["groups"].persist() request["groups"].persist()
response.redirect = "/groupes" response.redirect = "/groupes"
@ -344,68 +341,77 @@ async def import_products(request, response, id):
@app.route("/livraison/{delivery_id}/producteurices") @app.route("/livraison/{delivery_id}/producteurices")
async def list_producers(request, response, delivery_id): async def list_producers(request, response, delivery_id):
delivery = Delivery.load(delivery_id) delivery = Delivery.load(delivery_id)
response.html("list_products.html", { response.html(
'edit_mode': True, "list_products.html",
'delivery': delivery, {
'referent': request.query.get('referent', None), "edit_mode": True,
}) "delivery": delivery,
"referent": request.query.get("referent", None),
},
)
@app.route("/livraison/{delivery_id}/{producer_id}/éditer", methods=["GET", "POST"]) @app.route("/livraison/{delivery_id}/{producer_id}/éditer", methods=["GET", "POST"])
async def edit_producer(request, response, delivery_id, producer_id): async def edit_producer(request, response, delivery_id, producer_id):
delivery = Delivery.load(delivery_id) delivery = Delivery.load(delivery_id)
producer = delivery.producers.get(producer_id) producer = delivery.producers.get(producer_id)
if request.method == 'POST': if request.method == "POST":
form = request.form form = request.form
producer.referent = form.get('referent') producer.referent = form.get("referent")
producer.tel_referent = form.get('tel_referent') producer.tel_referent = form.get("tel_referent")
producer.description = form.get('description') producer.description = form.get("description")
producer.contact = form.get('contact') producer.contact = form.get("contact")
delivery.producers[producer_id] = producer delivery.producers[producer_id] = producer
delivery.persist() delivery.persist()
response.html("edit_producer.html", { response.html(
'delivery': delivery, "edit_producer.html",
'producer': producer, {
'products': delivery.get_products_by(producer.id) "delivery": delivery,
}) "producer": producer,
"products": delivery.get_products_by(producer.id),
},
)
@app.route("/livraison/{delivery_id}/{producer_id}/{product_ref}/éditer", methods=["GET", "POST"]) @app.route(
"/livraison/{delivery_id}/{producer_id}/{product_ref}/éditer",
methods=["GET", "POST"],
)
async def edit_product(request, response, delivery_id, producer_id, product_ref): async def edit_product(request, response, delivery_id, producer_id, product_ref):
delivery = Delivery.load(delivery_id) delivery = Delivery.load(delivery_id)
product = delivery.get_product(product_ref) product = delivery.get_product(product_ref)
if request.method == 'POST': if request.method == "POST":
form = request.form form = request.form
product.name = form.get('name') product.name = form.get("name")
product.price = form.float('price') product.price = form.float("price")
product.unit = form.get('unit') product.unit = form.get("unit")
product.description = form.get('description') product.description = form.get("description")
product.url = form.get('url') product.url = form.get("url")
if form.get('packing'): if form.get("packing"):
product.packing = form.int('packing') product.packing = form.int("packing")
else: else:
product.packing = None product.packing = None
if 'rupture' in form: if "rupture" in form:
product.rupture = form.get('rupture') product.rupture = form.get("rupture")
else: else:
product.rupture = None product.rupture = None
delivery.persist() delivery.persist()
response.message('Le produit à bien été modifié') response.message("Le produit à bien été modifié")
response.redirect = f'/livraison/{delivery_id}/{producer_id}/éditer' response.redirect = f"/livraison/{delivery_id}/{producer_id}/éditer"
response.html("edit_product.html", { response.html("edit_product.html", {"delivery": delivery, "product": product})
'delivery': delivery,
'product': product
})
@app.route("/livraison/{delivery_id}/{producer_id}/ajouter-produit", methods=["GET", "POST"])
@app.route(
"/livraison/{delivery_id}/{producer_id}/ajouter-produit", methods=["GET", "POST"]
)
async def create_product(request, response, delivery_id, producer_id): async def create_product(request, response, delivery_id, producer_id):
delivery = Delivery.load(delivery_id) delivery = Delivery.load(delivery_id)
product = Product(name="", ref="", price=0) product = Product(name="", ref="", price=0)
if request.method == 'POST': if request.method == "POST":
product.producer = producer_id product.producer = producer_id
form = request.form form = request.form
product.update_from_form(form) product.update_from_form(form)
@ -413,62 +419,74 @@ async def create_product(request, response, delivery_id, producer_id):
delivery.products.append(product) delivery.products.append(product)
delivery.persist() delivery.persist()
response.message('Le produit à bien été créé') response.message("Le produit à bien été créé")
response.redirect = f'/livraison/{delivery_id}/producteurice/{producer_id}/éditer' response.redirect = (
f"/livraison/{delivery_id}/producteurice/{producer_id}/éditer"
)
response.html("edit_product.html", { response.html(
'delivery': delivery, "edit_product.html",
'producer_id': producer_id, {"delivery": delivery, "producer_id": producer_id, "product": product},
'product': product, )
})
@app.route("/livraison/{id}/gérer", methods=['GET'])
@app.route("/livraison/{id}/gérer", methods=["GET"])
async def manage_delivery(request, response, id): async def manage_delivery(request, response, id):
delivery = Delivery.load(id) delivery = Delivery.load(id)
response.html("manage_delivery.html",{ response.html(
'delivery': delivery, "manage_delivery.html",
'referents': [p.referent for p in delivery.producers.values()] {
}) "delivery": delivery,
"referents": [p.referent for p in delivery.producers.values()],
},
)
@app.route("/livraison/{id}/envoi-email-referentes", methods=['GET', 'POST'])
@app.route("/livraison/{id}/envoi-email-referentes", methods=["GET", "POST"])
async def send_referent_emails(request, response, id): async def send_referent_emails(request, response, id):
delivery = Delivery.load(id) delivery = Delivery.load(id)
date = delivery.to_date.strftime("%Y-%m-%d") date = delivery.to_date.strftime("%Y-%m-%d")
if request.method == 'POST': if request.method == "POST":
email_body = request.form.get('email_body') email_body = request.form.get("email_body")
email_subject = request.form.get('email_subject') email_subject = request.form.get("email_subject")
for referent in delivery.get_referents(): for referent in delivery.get_referents():
producers = delivery.get_producers_for_referent(referent) producers = delivery.get_producers_for_referent(referent)
summary = reports.summary(delivery, producers) summary = reports.summary(delivery, producers)
emails.send(referent, email_subject, email_body, copy=delivery.contact, attachments=[ emails.send(
(f"{config.SITE_NAME}-{date}-{referent}.xlsx", summary) referent,
]) email_subject,
email_body,
copy=delivery.contact,
attachments=[(f"{config.SITE_NAME}-{date}-{referent}.xlsx", summary)],
)
response.message("Le mail à bien été envoyé") response.message("Le mail à bien été envoyé")
response.redirect = f"/livraison/{id}/gérer" response.redirect = f"/livraison/{id}/gérer"
response.html("prepare_referent_email.html", { response.html("prepare_referent_email.html", {"delivery": delivery})
'delivery': delivery
})
@app.route("/livraison/{id}/bon-de-commande-referent⋅e", methods=['GET']) @app.route("/livraison/{id}/bon-de-commande-referent⋅e", methods=["GET"])
async def download_referent_summary(request, response, id): async def download_referent_summary(request, response, id):
delivery = Delivery.load(id) delivery = Delivery.load(id)
date = delivery.to_date.strftime("%Y-%m-%d") date = delivery.to_date.strftime("%Y-%m-%d")
if not request['user'].is_referent(delivery): if not request["user"].is_referent(delivery):
return return
referent = request['user'].email referent = request["user"].email
producers = delivery.get_producers_for_referent(referent) producers = delivery.get_producers_for_referent(referent)
summary = reports.summary(delivery, producers) summary = reports.summary(delivery, producers)
response.xlsx(summary, filename=f"{config.SITE_NAME}-{date}-{referent}.xlsx") response.xlsx(summary, filename=f"{config.SITE_NAME}-{date}-{referent}.xlsx")
@app.route("/livraison/{id}/product⋅eur⋅rice/{producer}/bon-de-commande", methods=["GET"]) @app.route(
"/livraison/{id}/product⋅eur⋅rice/{producer}/bon-de-commande", methods=["GET"]
)
async def download_producer_report(request, response, id, producer): async def download_producer_report(request, response, id, producer):
delivery = Delivery.load(id) delivery = Delivery.load(id)
summary = reports.summary(delivery, [producer, ]) summary = reports.summary(delivery, [producer])
date = delivery.to_date.strftime("%Y-%m-%d") date = delivery.to_date.strftime("%Y-%m-%d")
response.xlsx(summary, filename=f"{config.SITE_NAME}-{date}-{producer}-bon-de-commande.xlsx") response.xlsx(
summary, filename=f"{config.SITE_NAME}-{date}-{producer}-bon-de-commande.xlsx"
)
@app.route("/livraison/{id}/exporter", methods=["GET"]) @app.route("/livraison/{id}/exporter", methods=["GET"])
@ -526,7 +544,7 @@ async def place_order(request, response, id):
if request.method == "POST": if request.method == "POST":
# When the delivery is closed, only staff can access. # When the delivery is closed, only staff can access.
if delivery.status == delivery.CLOSED and not (user and user.is_staff) : if delivery.status == delivery.CLOSED and not (user and user.is_staff):
response.message("La livraison est fermée", "error") response.message("La livraison est fermée", "error")
response.redirect = delivery_url response.redirect = delivery_url
return return
@ -563,17 +581,27 @@ async def place_order(request, response, id):
if user and orderer.id == user.id: if user and orderer.id == user.id:
# Only send email if order has been placed by the user itself. # Only send email if order has been placed by the user itself.
# Send the emails to everyone in the group. # Send the emails to everyone in the group.
groups = request['groups'].groups groups = request["groups"].groups
if orderer.group_id in groups.keys(): if orderer.group_id in groups.keys():
for email in groups[orderer.group_id].members: for email in groups[orderer.group_id].members:
emails.send_order( emails.send_order(
request, env, person=Person(email=email), delivery=delivery, order=order request,
env,
person=Person(email=email),
delivery=delivery,
order=order,
) )
else: else:
emails.send_order( emails.send_order(
request, env, person=Person(email=orderer.email), delivery=delivery, order=order request,
env,
person=Person(email=orderer.email),
delivery=delivery,
order=order,
)
response.message(
f"La commande pour « {orderer.name} » a bien été prise en compte!"
) )
response.message(f"La commande pour « {orderer.name} » a bien été prise en compte!")
response.redirect = f"/livraison/{delivery.id}" response.redirect = f"/livraison/{delivery.id}"
else: else:
order = delivery.orders.get(orderer.id) or Order() order = delivery.orders.get(orderer.id) or Order()
@ -634,7 +662,7 @@ async def import_multiple_commands(request, response, id):
current_ref = None current_ref = None
for row in reader: for row in reader:
for label, value in row.items(): for label, value in row.items():
if label == 'ref': if label == "ref":
current_ref = value current_ref = value
else: else:
wanted = int(value or 0) wanted = int(value or 0)
@ -652,14 +680,20 @@ async def import_multiple_commands(request, response, id):
async def xls_report(request, response, id): async def xls_report(request, response, id):
delivery = Delivery.load(id) delivery = Delivery.load(id)
date = delivery.to_date.strftime("%Y-%m-%d") date = delivery.to_date.strftime("%Y-%m-%d")
response.xlsx(reports.summary(delivery), filename=f"{config.SITE_NAME}-{date}-bon-de-commande.xlsx") response.xlsx(
reports.summary(delivery),
filename=f"{config.SITE_NAME}-{date}-bon-de-commande.xlsx",
)
@app.route("/livraison/{id}/rapport-complet.xlsx", methods=["GET"]) @app.route("/livraison/{id}/rapport-complet.xlsx", methods=["GET"])
async def xls_full_report(request, response, id): async def xls_full_report(request, response, id):
delivery = Delivery.load(id) delivery = Delivery.load(id)
date = delivery.to_date.strftime("%Y-%m-%d") date = delivery.to_date.strftime("%Y-%m-%d")
response.xlsx(reports.full(delivery), filename=f"{config.SITE_NAME}-{date}-rapport-complet.xlsx") response.xlsx(
reports.full(delivery),
filename=f"{config.SITE_NAME}-{date}-rapport-complet.xlsx",
)
@app.route("/livraison/{id}/ajuster/{ref}", methods=["GET", "POST"]) @app.route("/livraison/{id}/ajuster/{ref}", methods=["GET", "POST"])
@ -691,7 +725,7 @@ async def adjust_product(request, response, id, ref):
@staff_only @staff_only
async def delivery_balance(request, response, id): async def delivery_balance(request, response, id):
delivery = Delivery.load(id) delivery = Delivery.load(id)
groups = request['groups'] groups = request["groups"]
delivery_url = f"/livraison/{delivery.id}" delivery_url = f"/livraison/{delivery.id}"
balance = [] balance = []
@ -700,7 +734,7 @@ async def delivery_balance(request, response, id):
for producer in delivery.producers.values(): for producer in delivery.producers.values():
group = groups.get_user_group(producer.referent) group = groups.get_user_group(producer.referent)
if hasattr(group, 'id'): if hasattr(group, "id"):
group_id = group.id group_id = group.id
else: else:
group_id = group group_id = group
@ -717,13 +751,16 @@ async def delivery_balance(request, response, id):
for debiter, amount, crediter in results: for debiter, amount, crediter in results:
results_dict[debiter][crediter] = amount results_dict[debiter][crediter] = amount
response.html("delivery_balance.html", { response.html(
"delivery_balance.html",
{
"delivery": delivery, "delivery": delivery,
"debiters": debiters, "debiters": debiters,
"crediters": crediters, "crediters": crediters,
"results": results_dict, "results": results_dict,
"groups": groups.groups, "groups": groups.groups,
}) },
)
@app.route("/livraison/{id}/solde.xlsx", methods=["GET"]) @app.route("/livraison/{id}/solde.xlsx", methods=["GET"])

View file

@ -13,18 +13,19 @@ def send(to, subject, body, html=None, copy=None, attachments=None):
if not attachments: if not attachments:
attachments = [] attachments = []
msg = MIMEMultipart('alternative') msg = MIMEMultipart("alternative")
msg["Subject"] = subject msg["Subject"] = subject
msg["From"] = config.FROM_EMAIL msg["From"] = config.FROM_EMAIL
msg["To"] = to msg["To"] = to
msg["Bcc"] = copy if copy else config.FROM_EMAIL msg["Bcc"] = copy if copy else config.FROM_EMAIL
for file_name, attachment in attachments: for file_name, attachment in attachments:
part = MIMEBase('application','application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=utf-8') part = MIMEBase(
"application",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=utf-8",
)
part.set_payload(attachment) part.set_payload(attachment)
part.add_header('Content-Disposition', part.add_header("Content-Disposition", "attachment", filename=file_name)
'attachment',
filename=file_name)
encoders.encode_base64(part) encoders.encode_base64(part)
msg.attach(part) msg.attach(part)
@ -33,7 +34,7 @@ def send(to, subject, body, html=None, copy=None, attachments=None):
msg.attach(MIMEText(html, "html")) msg.attach(MIMEText(html, "html"))
if not config.SEND_EMAILS: if not config.SEND_EMAILS:
body = body.replace('https', 'http') body = body.replace("https", "http")
return print("Sending email", str(body)) return print("Sending email", str(body))
try: try:
server = smtplib.SMTP_SSL(config.SMTP_HOST) server = smtplib.SMTP_SSL(config.SMTP_HOST)
@ -61,5 +62,5 @@ def send_order(request, env, person, delivery, order):
display_prices=True, display_prices=True,
order=order, order=order,
delivery=delivery, delivery=delivery,
request=request request=request,
) )

View file

@ -47,9 +47,13 @@ def products_and_producers_from_xlsx(delivery, data):
raise ValueError("Le fichier doit comporter deux onglets.") raise ValueError("Le fichier doit comporter deux onglets.")
# First, get the products data from the first tab. # First, get the products data from the first tab.
products_sheet = data.get_sheet_by_name(sheet_names[0]) products_sheet = data.get_sheet_by_name(sheet_names[0])
delivery.products = items_from_xlsx(list(products_sheet.values), [], Product, PRODUCT_FIELDS, append_list) delivery.products = items_from_xlsx(
list(products_sheet.values), [], Product, PRODUCT_FIELDS, append_list
)
# Then import producers info # Then import producers info
producers_sheet = data.get_sheet_by_name(sheet_names[1]) producers_sheet = data.get_sheet_by_name(sheet_names[1])
delivery.producers = items_from_xlsx(list(producers_sheet.values), {}, Producer, PRODUCER_FIELDS, append_dict) delivery.producers = items_from_xlsx(
list(producers_sheet.values), {}, Producer, PRODUCER_FIELDS, append_dict
)
delivery.persist() delivery.persist()

View file

@ -74,15 +74,14 @@ class Base:
def dump(self): def dump(self):
return yaml.dump(asdict(self), allow_unicode=True) return yaml.dump(asdict(self), allow_unicode=True)
@dataclass @dataclass
class PersistedBase(Base): class PersistedBase(Base):
@classmethod @classmethod
def get_root(cls): def get_root(cls):
return Path(config.DATA_ROOT) / cls.__root__ return Path(config.DATA_ROOT) / cls.__root__
@dataclass @dataclass
class Person(Base): class Person(Base):
email: str email: str
@ -127,7 +126,7 @@ class Groups(PersistedBase):
data = yaml.safe_load(path.read_text()) data = yaml.safe_load(path.read_text())
data = {k: v for k, v in data.items() if k in cls.__dataclass_fields__} data = {k: v for k, v in data.items() if k in cls.__dataclass_fields__}
else: else:
data = {'groups': {}} data = {"groups": {}}
groups = cls(**data) groups = cls(**data)
groups.path = path groups.path = path
return groups return groups
@ -160,6 +159,7 @@ class Groups(PersistedBase):
def init_fs(cls): def init_fs(cls):
cls.get_root().mkdir(parents=True, exist_ok=True) cls.get_root().mkdir(parents=True, exist_ok=True)
@dataclass @dataclass
class Producer(Base): class Producer(Base):
id: str id: str
@ -189,15 +189,15 @@ class Product(Base):
return out return out
def update_from_form(self, form): def update_from_form(self, form):
self.name = form.get('name') self.name = form.get("name")
self.price = form.float('price') self.price = form.float("price")
self.unit = form.get('unit') self.unit = form.get("unit")
self.description = form.get('description') self.description = form.get("description")
self.url = form.get('url') self.url = form.get("url")
if form.get('packing'): if form.get("packing"):
self.packing = form.int('packing') self.packing = form.int("packing")
if 'rupture' in form: if "rupture" in form:
self.rupture = form.get('rupture') self.rupture = form.get("rupture")
else: else:
self.rupture = None self.rupture = None
return self return self

View file

@ -5,6 +5,7 @@ from openpyxl.writer.excel import save_virtual_workbook
from .models import Product, Producer from .models import Product, Producer
def summary_for_products(wb, title, delivery, total=None, products=None): def summary_for_products(wb, title, delivery, total=None, products=None):
if products == None: if products == None:
products = delivery.products products = delivery.products
@ -12,26 +13,23 @@ def summary_for_products(wb, title, delivery, total=None, products=None):
total = delivery.total total = delivery.total
ws = wb.create_sheet(title) ws = wb.create_sheet(title)
ws.append([ ws.append(
"ref", ["ref", "produit", "prix unitaire", "quantité commandée", "unité", "total"]
"produit", )
"prix unitaire",
"quantité commandée",
"unité",
"total",
])
for product in products: for product in products:
wanted = delivery.product_wanted(product) wanted = delivery.product_wanted(product)
if not wanted: if not wanted:
continue continue
ws.append([ ws.append(
[
product.ref, product.ref,
str(product), str(product),
product.price, product.price,
wanted, wanted,
product.unit, product.unit,
round(product.price * wanted, 2), round(product.price * wanted, 2),
]) ]
)
ws.append(["", "", "", "", "Total", total]) ws.append(["", "", "", "", "Total", total])
@ -46,7 +44,7 @@ def summary(delivery, producers=None):
producer, producer,
delivery, delivery,
total=delivery.total_for_producer(producer), total=delivery.total_for_producer(producer),
products=delivery.get_products_by(producer) products=delivery.get_products_by(producer),
) )
return save_virtual_workbook(wb) return save_virtual_workbook(wb)
@ -69,7 +67,7 @@ def full(delivery):
ws.append(row) ws.append(row)
footer = ( footer = (
["Total", "", ""] ["Total", "", ""]
+ [round(o.total(delivery.products),2) for o in delivery.orders.values()] + [round(o.total(delivery.products), 2) for o in delivery.orders.values()]
+ [round(delivery.total, 2)] + [round(delivery.total, 2)]
) )
footer.insert(1, "") footer.insert(1, "")