From 6007a2aa3a8910867cd5e03dcb7dbf724008c327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20M=C3=83taireau?= Date: Sun, 5 Apr 2020 22:02:02 +0200 Subject: [PATCH] first working version for shipping fees --- TODO | 3 +- copanier/emails.py | 3 +- copanier/models.py | 55 ++++++++++++++++--- copanier/templates/delivery/balance.html | 19 +++++-- copanier/templates/emails/order_summary.txt | 2 +- .../templates/includes/delivery_table.html | 17 +++++- .../templates/includes/order_summary.html | 2 +- .../templates/products/edit_producer.html | 9 +++ .../templates/products/shipping_fees.html | 18 ++++++ copanier/views/delivery.py | 7 ++- copanier/views/products.py | 28 ++++++++++ 11 files changed, 145 insertions(+), 18 deletions(-) create mode 100644 copanier/templates/products/shipping_fees.html diff --git a/TODO b/TODO index 2ef347a..8d74ed8 100644 --- a/TODO +++ b/TODO @@ -10,10 +10,11 @@ x Ajouter un moyen d'ajouter un⋅e producteurice x Ajouter un moyen de changer le nom de la personne référente pour un producteur / productrice x Faciliter la duplication de distribution x Si un produit est en rupture de stock, alors il n'est pas compté dans les totaux -x Permettre la supression des produits x Permettre la suppression de producteurs x Ajouter une info « prix mis à jour » pour les référent⋅e⋅s +Permettre la supression des produits (terminer) + Gérer le souci d'URL pour l'édition d'Apiluly Rendre le formulaire d'édition producteur plus compact diff --git a/copanier/emails.py b/copanier/emails.py index b0f6811..1aac560 100644 --- a/copanier/emails.py +++ b/copanier/emails.py @@ -37,7 +37,7 @@ def send_from_template(env, template, to, subject, **params): send(to, subject, body=txt, html=html) -def send_order(request, env, person, delivery, order): +def send_order(request, env, person, delivery, order, group_id): send_from_template( env, "order_summary", @@ -47,4 +47,5 @@ def send_order(request, env, person, delivery, order): order=order, delivery=delivery, request=request, + group_id=group_id, ) diff --git a/copanier/models.py b/copanier/models.py index 6a2837c..19fc9e5 100644 --- a/copanier/models.py +++ b/copanier/models.py @@ -240,16 +240,25 @@ class Order(Base): def __iter__(self): yield from self.products.items() - def total(self, products): + def total(self, products, delivery, email=None, include_shipping=True): def _get_price(ref): product = products.get(ref) return product.price if product and not product.rupture else 0 + producers = set([p.producer for p in products]) products = {p.ref: p for p in products} - return round( - sum(p.quantity * _get_price(ref) for ref, p in self.products.items()), 2 + + total_products = sum( + p.quantity * _get_price(ref) for ref, p in self.products.items() ) + total_shipping = 0 + if include_shipping: + for producer in producers: + total_shipping = total_shipping + delivery.shipping_for(email, producer) + + return round(total_products + total_shipping, 2) + @property def has_adjustments(self): return any(choice.adjustment for email, choice in self) @@ -277,6 +286,7 @@ class Delivery(PersistedBase): products: List[Product] = field(default_factory=list) producers: Dict[str, Producer] = field(default_factory=dict) orders: Dict[str, Order] = field(default_factory=dict) + shipping: Dict[str, price_field] = field(default_factory=dict) def __post_init__(self): self.id = None # Not a field because we don't want to persist it. @@ -307,7 +317,7 @@ class Delivery(PersistedBase): @property def total(self): - return round(sum(o.total(self.products) for o in self.orders.values()), 2) + return round(sum(o.total(self.products, self) for o in self.orders.values()), 2) @property def is_open(self): @@ -431,11 +441,20 @@ class Delivery(PersistedBase): self.products.remove(product) return product - def total_for_producer(self, producer, person=None): + def total_for_producer(self, producer, person=None, include_shipping=True): 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) + return self.orders.get(person).total( + producer_products, self, person, include_shipping + ) + return round( + sum( + o.total(producer_products, self, person, include_shipping=False) + for o in self.orders.values() + ) + + self.shipping.get(producer, 0), + 2, + ) def get_producers_for_referent(self, referent): return { @@ -450,4 +469,24 @@ class Delivery(PersistedBase): def total_for(self, person): if person.email not in self.orders: return 0 - return self.orders[person.email].total(self.products) + return self.orders[person.email].total(self.products, self) + + def shipping_for(self, person, producer): + producer_shipping = self.shipping.get(producer) + if not producer_shipping: + return 0 + + if not person: + return producer_shipping + + producer_total = ( + self.total_for_producer(producer, include_shipping=False) + - producer_shipping + ) + person_amount = self.total_for_producer( + producer, person=person, include_shipping=False + ) + + percentage_person = person_amount / producer_total + shipping = percentage_person * producer_shipping + return shipping diff --git a/copanier/templates/delivery/balance.html b/copanier/templates/delivery/balance.html index db9486c..69d8e1b 100644 --- a/copanier/templates/delivery/balance.html +++ b/copanier/templates/delivery/balance.html @@ -1,8 +1,16 @@ {% extends "base.html" %} -{% block title %}

  Répartition des paiements Télécharger

{% endblock %} {% block body %} -

{{ delivery.name }} du {{ delivery.to_date | date }}.

-

Les personnes indiquées avec un * à côté de leur nom sont celles qui ont payé cette commande pour leur groupe.

+
+

  {{ delivery.name }} : Répartition des paiements

+
+ +
+
+ @@ -13,11 +21,14 @@ {% for crediter in crediters %} {% set due_amount = results[debiter[0]][crediter[0]] | round(2) %} - + {% endfor %} {% endfor %}
{% if debiter[0] in debiters_groups %} {{ debiters_groups[debiter[0]].name }}{% else %}{{ debiter[0] }}{% endif %} ({{ debiter[1] | round(2) }}){% if due_amount != 0.00 %}{{due_amount}}{% endif %}
+

  Mais, comment ça marche ? La répartition des chèques se fait automatiquement, en soustrayant ce que les personnes doivent (au nom de leur coloc / groupe) à ce qui leur est du (dans le cas où elles sont référentes pour certains produits).

+

Les personnes indiquées avec un * à côté de leur nom sont celles qui ont payé cette commande pour leur groupe.

+ {% endblock body %} diff --git a/copanier/templates/emails/order_summary.txt b/copanier/templates/emails/order_summary.txt index 1c1638e..4a8cf1a 100644 --- a/copanier/templates/emails/order_summary.txt +++ b/copanier/templates/emails/order_summary.txt @@ -7,7 +7,7 @@ Produit | Prix unitaire | Quantité {% for product in delivery.products %}{% if order[product].quantity %}{{ product.name }} | {{ product.price }} € | {{ order[product].quantity }} x {{ product.unit }} {% endif %}{% endfor %} -Total: {{ order.total(delivery.products) if order else 0 }} € +Total: {{ order.total(delivery.products, delivery, group_id) if order else 0 }} € Distribution: {{ delivery.where }}, le {{ delivery.from_date|date }} de {{ delivery.from_date|time }} à {{ delivery.to_date|time }} diff --git a/copanier/templates/includes/delivery_table.html b/copanier/templates/includes/delivery_table.html index 099bc06..1a89e8a 100644 --- a/copanier/templates/includes/delivery_table.html +++ b/copanier/templates/includes/delivery_table.html @@ -61,6 +61,21 @@ {% endif %} {% endfor %} + {% if delivery.shipping.get(producer) %} + + Frais de livraison + — + {% if delivery.has_packing %} + — + {% endif %} + {{ delivery.shipping[producer] }} € + {% if not list_only %} + {% for email, order in delivery.orders.items() %} + {{ delivery.shipping_for(email, producer)|round(2) }} € + {% endfor %} + {% endif %} + + {% endif %} Total— {% if delivery.has_packing %} @@ -70,7 +85,7 @@ {{ delivery.total_for_producer(producer) }} € {% if not list_only %} {% for email, order in delivery.orders.items() %} - {{ order.total(delivery.get_products_by(producer)) }} € + {{ order.total(delivery.get_products_by(producer), delivery, email) }} € {% endfor %} {% endif %} diff --git a/copanier/templates/includes/order_summary.html b/copanier/templates/includes/order_summary.html index bc2d3fc..d7554a7 100644 --- a/copanier/templates/includes/order_summary.html +++ b/copanier/templates/includes/order_summary.html @@ -9,4 +9,4 @@ {% endif %} {% endfor %} -

Total: {{ order.total(delivery.products) if order else 0 }} €

+

Total: {{ order.total(delivery.products, delivery, group_id) if order else 0 }} €

diff --git a/copanier/templates/products/edit_producer.html b/copanier/templates/products/edit_producer.html index 8c4d31f..c516d33 100644 --- a/copanier/templates/products/edit_producer.html +++ b/copanier/templates/products/edit_producer.html @@ -13,6 +13,9 @@
  • Supprimer
  • +
  • + Gérer des frais de livraison +
  • {% if producer.needs_price_update(delivery) %}
  • Marquer les prix comme OK @@ -61,6 +64,12 @@ +{% if delivery.shipping.get(producer.id) %} +

    Frais de livraison

    +Des frais de livraison de {{ delivery.shipping[producer.id] }}€ ont été enregistrés pour ce producteur. +Modifier +{% endif %} + {% if products %}

    Produits Ajouter un produit diff --git a/copanier/templates/products/shipping_fees.html b/copanier/templates/products/shipping_fees.html new file mode 100644 index 0000000..a139ceb --- /dev/null +++ b/copanier/templates/products/shipping_fees.html @@ -0,0 +1,18 @@ +{% extends "base.html" %} +{% block toplink %}↶ Retourner aux produits{% endblock %} + +{% block body %} +
    +

    Frais de port pour « {{ producer.name }} »

    +
    +
    + +
    + +
    +
    + +{% endblock body %} diff --git a/copanier/views/delivery.py b/copanier/views/delivery.py index b8a70d8..4b68714 100644 --- a/copanier/views/delivery.py +++ b/copanier/views/delivery.py @@ -244,6 +244,7 @@ async def place_order(request, response, id): person=Person(email=email), delivery=delivery, order=order, + group_id=orderer.group_id, ) else: emails.send_order( @@ -252,6 +253,7 @@ async def place_order(request, response, id): person=Person(email=orderer.email), delivery=delivery, order=order, + group_id=orderer.email, ) response.message( f"La commande pour « {orderer.name} » a bien été prise en compte, " @@ -326,7 +328,9 @@ async def delivery_balance(request, response, id): balance = [] for group_id, order in delivery.orders.items(): - balance.append((group_id, order.total(delivery.products) * -1)) + balance.append( + (group_id, order.total(delivery.products, delivery, group_id) * -1) + ) producer_groups = {} @@ -347,6 +351,7 @@ async def delivery_balance(request, response, id): group_id = producer.referent_name amount = delivery.total_for_producer(producer.id) + print(producer.id, amount) if amount: balance.append((group_id, amount)) diff --git a/copanier/views/products.py b/copanier/views/products.py index b3ec0e5..a5d1606 100644 --- a/copanier/views/products.py +++ b/copanier/views/products.py @@ -228,6 +228,34 @@ async def create_product(request, response, delivery_id, producer_id): ) +@app.route( + "/distribution/{delivery_id}/{producer_id}/frais-de-livraison", + methods=["GET", "POST"], +) +async def set_shipping_price(request, response, delivery_id, producer_id): + delivery = Delivery.load(delivery_id) + producer = delivery.producers.get(producer_id) + + if request.method == "POST": + form = request.form + shipping = form.float("shipping") + + delivery.shipping[producer_id] = shipping + delivery.persist() + response.message("Les frais de livraison ont bien été enregistrés, merci !") + response.redirect = f"/distribution/{delivery_id}/produits" + return + + response.html( + "products/shipping_fees.html", + { + "delivery": delivery, + "producer": producer, + "shipping": delivery.shipping.get(producer_id, ""), + }, + ) + + @app.route("/distribution/{id}/copier", methods=["GET"]) async def copy_products(request, response, id): deliveries = Delivery.all()