Aucun produit n'est encore défini pour cette livraison.
{% if request.user and request.user.is_staff %} -diff --git a/copanier/__init__.py b/copanier/__init__.py index b39f108..eaddbaa 100644 --- a/copanier/__init__.py +++ b/copanier/__init__.py @@ -341,7 +341,114 @@ async def import_products(request, response, id): response.redirect = f"/livraison/{delivery.id}" -@app.route("/livraison/{id}/exporter/produits", methods=["GET"]) +@app.route("/livraison/{delivery_id}/producteurices") +async def list_producers(request, response, delivery_id): + delivery = Delivery.load(delivery_id) + response.html("list_products.html", { + 'list_only': True, + 'edit_mode': True, + 'delivery': delivery, + 'referent': request.query.get('referent', None), + }) + + +@app.route("/livraison/{delivery_id}/{producer_id}/éditer", methods=["GET", "POST"]) +async def edit_producer(request, response, delivery_id, producer_id): + delivery = Delivery.load(delivery_id) + producer = delivery.producers.get(producer_id) + if request.method == 'POST': + form = request.form + producer.referent = form.get('referent') + producer.tel_referent = form.get('tel_referent') + producer.description = form.get('description') + producer.contact = form.get('contact') + delivery.producers[producer_id] = producer + delivery.persist() + + response.html("edit_producer.html", { + 'delivery': delivery, + 'producer': producer, + 'products': delivery.get_products_by(producer.id) + }) + + +@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): + delivery = Delivery.load(delivery_id) + product = delivery.get_product(product_ref) + + if request.method == 'POST': + form = request.form + product.name = form.get('name') + product.price = form.float('price') + product.unit = form.get('unit') + product.description = form.get('description') + product.url = form.get('url') + if form.get('packing'): + product.packing = form.int('packing') + if 'rupture' in form: + product.rupture = form.get('rupture') + else: + product.rupture = None + delivery.persist() + response.message('Le produit à bien été édité') + response.redirect = f'/livraison/{delivery_id}/producteurice/{producer_id}/éditer' + + response.html("edit_product.html", { + 'delivery': delivery, + 'product': product + }) + +@app.route("/livraison/{delivery_id}/{producer_id}/ajouter-produit", methods=["GET", "POST"]) +async def create_product(request, response, delivery_id, producer_id): + delivery = Delivery.load(delivery_id) + product = Product(name="", ref="", price=0) + + if request.method == 'POST': + product.producer = producer_id + form = request.form + product.update_from_form(form) + product.ref = slugify(f"{producer_id}-{product.name}") + + delivery.products.append(product) + delivery.persist() + response.message('Le produit à bien été créé') + response.redirect = f'/livraison/{delivery_id}/producteurice/{producer_id}/éditer' + + response.html("edit_product.html", { + 'delivery': delivery, + 'producer_id': producer_id, + 'product': product, + }) + +@app.route("/livraison/{id}/gérer", methods=['GET']) +async def manage_delivery(request, response, id): + delivery = Delivery.load(id) + response.html("manage_delivery.html",{ + 'delivery': delivery + }) + +@app.route("/livraison/{id}/envoi-email-referentes", methods=['GET', 'POST']) +async def send_referent_emails(request, response, id): + delivery = Delivery.load(id) + date = delivery.to_date.strftime("%Y-%m-%d") + if request.method == 'POST': + email_body = request.form.get('email_body') + email_subject = request.form.get('email_subject') + for referent in delivery.get_referents(): + producers = delivery.get_producers_for_referent(referent) + summary = reports.summary(delivery, producers) + emails.send(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.redirect = f"/livraison/{id}/gérer" + + response.html("prepare_referent_email.html", { + 'delivery': delivery + }) + +@app.route("/livraison/{id}/exporter", methods=["GET"]) async def export_products(request, response, id): delivery = Delivery.load(id) response.xlsx(reports.products(delivery)) @@ -570,7 +677,10 @@ async def delivery_balance(request, response, id): for producer in delivery.producers.values(): group = groups.get_user_group(producer.referent) - group_id = group.id if group else producer.referent + if hasattr(group, 'id'): + group_id = group.id + else: + group_id = group amount = delivery.total_for_producer(producer.id) if amount: balance.append((group_id, amount)) diff --git a/copanier/emails.py b/copanier/emails.py index 10af47e..7c6be23 100644 --- a/copanier/emails.py +++ b/copanier/emails.py @@ -1,18 +1,32 @@ import smtplib from email.message import EmailMessage +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from email.mime.base import MIMEBase +from email.mime.base import MIMEBase +from email import encoders from . import config -def send(to, subject, body, html=None): - msg = EmailMessage() - msg.set_content(body) +def send(to, subject, body, html=None, copy=None, attachments=None): + msg = MIMEMultipart() + msg.attach(MIMEText(body, "plain")) + if html: + msg.attach(MIMEText(html, "html")) msg["Subject"] = subject msg["From"] = config.FROM_EMAIL msg["To"] = to - msg["Bcc"] = config.FROM_EMAIL - if html: - msg.add_alternative(html, subtype="html") + msg["Bcc"] = copy if copy else config.FROM_EMAIL + + for file_name, attachment in attachments: + part = MIMEBase('application','application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=utf-8') + part.set_payload(attachment) + part.add_header('Content-Disposition', + 'attachment', + filename=file_name) + encoders.encode_base64(part) + msg.attach(part) if not config.SEND_EMAILS: return print("Sending email", str(msg)) try: diff --git a/copanier/models.py b/copanier/models.py index 91ed5ef..a5fa0ed 100644 --- a/copanier/models.py +++ b/copanier/models.py @@ -152,7 +152,7 @@ class Groups(PersistedBase): for group in self.groups.values(): if email in group.members: return group - return None + return email @classmethod def init_fs(cls): @@ -164,7 +164,7 @@ class Producer(Base): referent: str = "" tel_referent: str = "" contact: str = "" - location: str = "" + description: str = "" @dataclass @@ -185,6 +185,20 @@ class Product(Base): # if self.unit: # out += f" ({self.unit})" return out + + def update_from_form(self, form): + self.name = form.get('name') + self.price = form.float('price') + self.unit = form.get('unit') + self.description = form.get('description') + self.url = form.get('url') + if form.get('packing'): + self.packing = form.int('packing') + if 'rupture' in form: + self.rupture = form.get('rupture') + else: + self.rupture = None + return self @dataclass @@ -387,9 +401,24 @@ class Delivery(PersistedBase): def get_products_by(self, producer): return [p for p in self.products if p.producer == producer] + + def get_product(self, ref): + products = [p for p in self.products if p.ref == ref] + if products: + return products[0] def total_for_producer(self, producer, person=None): producer_products = [p for p in self.products if p.producer == producer] if person: return self.orders.get(person).total(producer_products) return round(sum(o.total(producer_products) for o in self.orders.values()), 2) + + def get_producers_for_referent(self, referent): + return { + id: producer + for id, producer in self.producers.items() + if producer.referent == referent + } + + def get_referents(self): + return [producer.referent for producer in self.producers.values()] \ No newline at end of file diff --git a/copanier/reports.py b/copanier/reports.py index ba79669..2efd61a 100644 --- a/copanier/reports.py +++ b/copanier/reports.py @@ -35,11 +35,12 @@ def summary_for_products(wb, title, delivery, total=None, products=None): ws.append(["", "", "", "", "Total", total]) - -def summary(delivery): +def summary(delivery, producers=None): wb = Workbook() wb.remove(wb.active) - for producer in delivery.producers: + if not producers: + producers = delivery.producers + for producer in producers: summary_for_products( wb, producer, diff --git a/copanier/static/app.css b/copanier/static/app.css index 17fc706..8b8d024 100644 --- a/copanier/static/app.css +++ b/copanier/static/app.css @@ -450,6 +450,11 @@ hr { .notification.warning { background-color: #f9b42d; } +.notification.info { + background-color: #aed1b175; + color: #000; +} + .notification i { font-size: 2rem; } @@ -515,6 +520,7 @@ ul.delivery-head li { list-style: none; padding: 0; margin: 0; + font-size: 0.8em; } ul.delivery-head li i{ float: left; diff --git a/copanier/templates/delivery.html b/copanier/templates/delivery.html index dcfb7cb..30f3b3b 100644 --- a/copanier/templates/delivery.html +++ b/copanier/templates/delivery.html @@ -3,13 +3,18 @@ {% block body %}
Aucun produit n'est encore défini pour cette livraison.
{% if request.user and request.user.is_staff %} -Produit | +Prix | +Unité | +Description | +Packaging | +Rupture ? | ++ |
---|---|---|---|---|---|---|
{{ product.name }} | +{{ product.price }}€ | +{{ product.unit }} | +{{ product.description }} | +{% if product.packing %}{{ product.packing }}{% endif %} | +{% if product.rupture %}RUPTURE !!{% endif %} | +éditer | +
Produit | +Prix | +Unité | +Description | +Packaging | +Rupture ? | ++ |
---|---|---|---|---|---|---|
{{ product.name }} | +{{ product.price }}€ | +{{ product.unit }} | +{{ product.description }} | +{% if product.packing %}{{ product.packing }}{% endif %} | +{% if product.rupture %}RUPTURE !!{% endif %} | +éditer | +
Conditionnement | {% endif %} + {% if edit_mode %}Éditer | {% endif %} + {% if not list_only %}Total | {% for orderer, order in delivery.orders.items() %}@@ -19,16 +26,18 @@ {% endif %} | {% endfor %} + {% endif %}|||
---|---|---|---|---|---|---|
{{ product }} {% if product.rupture %}(RUPTURE !){% endif %} + | {% if edit_mode %}{% endif %}{{ product }}{% if edit_mode %}{% endif %}{% if product.rupture %} {{ product.rupture }}{% endif %} | {{ product.price | round(2) }} € | {% if delivery.has_packing %}{% if product.packing %}{{ product.packing }} x {% endif %} {{ product.unit }} | {% endif %} + {% if not list_only %}{{ delivery.product_wanted(product) }} {% if delivery.status == delivery.ADJUSTMENT and delivery.product_missing(product) %} (−{{ delivery.product_missing(product) }}) @@ -38,8 +47,11 @@ {% for email, order in delivery.orders.items() %} | {{ order[product.ref].quantity or "—" }} | {% endfor %} + {% endif %} + {% if edit_mode %}modifier | {% endif %}
Total | — | {% if delivery.has_packing %} @@ -50,6 +62,7 @@{{ order.total(delivery.get_products_by(producer)) }} € | {% endfor %}
Format pris en charge: xlsx
diff --git a/copanier/templates/list_products.html b/copanier/templates/list_products.html new file mode 100644 index 0000000..a3014b8 --- /dev/null +++ b/copanier/templates/list_products.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} + +{% block body %} +Voici la liste des product⋅eurs⋅rices dont {{ referent }} est référent⋅e. voir tous les produits
{% endif %} +Le mail ci dessous (vous pouvez le modifier !) va être envoyé aux référent⋅e⋅s, avec en pièce jointe le tableau comportant les commandes pour les product⋅eurs⋅rices.
+ + +{% endblock %} \ No newline at end of file diff --git a/copanier/templates/signing_sheet.html b/copanier/templates/signing_sheet.html index 9ef43f9..8280f71 100644 --- a/copanier/templates/signing_sheet.html +++ b/copanier/templates/signing_sheet.html @@ -9,7 +9,7 @@