diff --git a/copanier/__init__.py b/copanier/__init__.py index 84faa14..53a99a2 100644 --- a/copanier/__init__.py +++ b/copanier/__init__.py @@ -181,15 +181,19 @@ async def sesame(request, response): async def send_sesame(request, response): email = request.form.get("email") token = utils.create_token(email) - emails.send_from_template( - env, - "access_granted", - email, - f"Sésame {config.SITE_NAME}", - hostname=request.host, - token=token.decode(), - ) - response.message(f"Un sésame vous a été envoyé à l'adresse '{email}'") + try: + emails.send_from_template( + env, + "access_granted", + email, + f"Sésame {config.SITE_NAME}", + hostname=request.host, + token=token.decode(), + ) + except RuntimeError: + response.message("Oops, impossible d'envoyer le courriel…", status="error") + else: + response.message(f"Un sésame vous a été envoyé à l'adresse '{email}'") response.redirect = "/" @@ -533,6 +537,12 @@ async def export_products(request, response, id): response.xlsx(reports.products(delivery)) +@app.route("/livraison/archive/{id}/exporter/produits", methods=["GET"]) +async def export_archived_products(request, response, id): + delivery = Delivery.load(f"archive/{id}") + response.xlsx(reports.products(delivery)) + + @app.route("/livraison/{id}/edit", methods=["GET"]) @staff_only async def edit_delivery(request, response, id): @@ -617,7 +627,6 @@ async def place_order(request, response, id): delivery.persist() if user and orderer.id == user.id: - # Only send email if order has been placed by the user itself. # Send the emails to everyone in the group. groups = request["groups"].groups if orderer.group_id in groups.keys(): @@ -643,9 +652,13 @@ async def place_order(request, response, id): response.redirect = f"/livraison/{delivery.id}" else: order = delivery.orders.get(orderer.id) or Order() + force_adjustment = "adjust" in request.query and user and user.is_staff response.html( "place_order.html", - {"delivery": delivery, "person": orderer, "order": order}, + delivery=delivery, + person=orderer, + order=order, + force_adjustment=force_adjustment, ) @@ -657,10 +670,14 @@ async def send_order(request, response, id): if not order: response.message(f"Aucune commande pour «{email}»", status="warning") else: - emails.send_order( - request, env, person=Person(email=email), delivery=delivery, order=order - ) - response.message(f"Résumé de commande envoyé à «{email}»") + try: + emails.send_order( + request, env, person=Person(email=email), delivery=delivery, order=order + ) + except RuntimeError: + response.message("Oops, impossible d'envoyer le courriel…", status="error") + else: + response.message(f"Résumé de commande envoyé à «{email}»") response.redirect = f"/livraison/{delivery.id}" diff --git a/copanier/models.py b/copanier/models.py index bdd9aa6..57818d5 100644 --- a/copanier/models.py +++ b/copanier/models.py @@ -245,6 +245,7 @@ class Order(Base): return round( sum(p.quantity * _get_price(ref) for ref, p in self.products.items()), 2 ) + return round(total, 2) @property def has_adjustments(self): @@ -352,7 +353,9 @@ class Delivery(PersistedBase): @classmethod def incoming(cls): - return [d for d in cls.all() if d.is_foreseen] + return sorted( + [d for d in cls.all() if d.is_foreseen], key=lambda d: d.order_before + ) @classmethod def former(cls): @@ -425,3 +428,8 @@ class Delivery(PersistedBase): def get_referents(self): return [producer.referent for producer in self.producers.values()] + + def total_for(self, person): + if person.email not in self.orders: + return 0 + return self.orders[person.email].total(self.products) diff --git a/copanier/static/app.css b/copanier/static/app.css index 998bbcf..6402485 100644 --- a/copanier/static/app.css +++ b/copanier/static/app.css @@ -1,23 +1,14 @@ :root { --primary-color: #0062b7; - --primary-color-light: #e6f0fa; - --secondary-color: #e10055; + --link-color: #00d1b2; --text-color: #414664; - --border-color: #e6e6eb; - --primary-background-color: #fff; - --secondary-background-color: #fafafb; - --disease: #7846af; - --disease-light: #f5ebfa; - --country: #0f8796; - --country-light: #e6f5f5; - --group: #d03800; - --group-light: #fff5eb; - --keyword: #8c5a2d; - --keyword-light: #f5f0eb; - --kind: #cd0073; - --kind-light: #faf0f5; - --ern: #32009b; - --ern-light: #ebebf5; + --light-text-color: #fafafb; + --background-color: #fafafb; + --flag-color: #A87CA0; + --warning-color: #FFA631; + --danger-color: #d9534f; + --neutral-color: #4D8FAC; + --success-color: #0f8796; } @@ -103,7 +94,7 @@ body { font-size: .8rem; font-family: 'Work Sans', sans-serif; text-rendering: optimizeLegibility; - background-color: var(--secondary-background-color); + background-color: var(--background-color); padding: 0; margin: 0; } @@ -113,7 +104,6 @@ h3, h4, h5, legend { - /*margin: 0;*/ color: #444; line-height: 1; font-weight: 300; @@ -125,7 +115,7 @@ h3 { font-size: 1.4rem } h4 { font-size: 1.1rem } a { - color: #00d1b2; + color: var(--link-color); cursor: pointer; text-decoration: none; -webkit-transition: none 86ms ease-out; @@ -165,6 +155,7 @@ main { padding: 1rem; } +.flag, button, a.button, input[type=submit] { @@ -190,13 +181,14 @@ input[type=submit] { cursor: pointer; } input[type=submit], -input[type=submit] + a.button { +input[type=submit] + a.button, +a.button + a.button { margin-top: 5px; } input[type=submit]:hover, .button:hover { - color: #fff; + color: var(--light-text-color); background-color: var(--primary-color); } @@ -204,29 +196,40 @@ input[type=submit]:hover, button.primary, a.button.primary, input[type=submit].primary { - color: #fff; + color: var(--background-color); background: var(--primary-color); } button.primary:hover, a.button.primary:hover, input[type=submit].primary:hover { - background-color: #fff; + background-color: var(--background-color); color: var(--primary-color); } button.danger, a.button.danger, input[type=submit].danger { - color: #d9534f; - border-color: #d9534f; + color: var(--danger-color); + border-color: var(--danger-color); } button.danger:hover, a.button.danger:hover, input[type=submit].danger:hover { - background-color: #d9534f; - color: #fff; + background-color: var(--danger-color); + color: var(--light-text-color); +} +.flag { + border-color: var(--flag-color); + color: var(--light-text-color); + background-color: var(--flag-color); + cursor: help; +} +.flag.warning { + background-color: var(--warning-color); + border-color: var(--warning-color); + font-weight: 600; } @@ -247,10 +250,10 @@ textarea { position: relative; height: 2rem; padding: .4rem .8rem; - color: #50596c; + color: var(--text-color); font-size: .8rem; line-height: 1rem; - background-color: #fff; + background-color: var(--background-color); border: .05rem solid #bbc; box-sizing: border-box; } @@ -351,10 +354,10 @@ tr:nth-child(even) { background-color: #ddd; } thead tr { - background-color: #3498db; + background-color: var(--neutral-color); } thead tr * { - color: #f1f1f1; + color: var(--light-text-color); } thead th + th { border-left: 1px solid white; @@ -398,11 +401,11 @@ article.delivery th.person { } td.missing, th.missing { - background-color: #db7734; + background-color: var(--warning-color); } .missing a { - color: #f1f1f1; - border-color: #f1f1f1; + color: var(--light-text-color); + border-color: var(--light-text-color); height: 1rem; } hr { @@ -436,13 +439,13 @@ hr { vertical-align: middle; } .notification.success { - background-color: #0f8796; + background-color: var(--success-color); } .notification.error { - background-color: #e10055; + background-color: var(--danger-color); } .notification.warning { - background-color: #f9b42d; + background-color: var(--warning-color); } .notification.info { background-color: #aed1b175; @@ -453,14 +456,14 @@ hr { font-size: 2rem; } .not-paid { - background-color: #db7734; + background-color: var(--warning-color); } .toggle { display: none; } .toggle-label { cursor: pointer; - color: #00d1b2; + color: var(--link-color); } .toggle-container { display: none; diff --git a/copanier/templates/delivery.html b/copanier/templates/delivery.html index 6e5484c..23da823 100644 --- a/copanier/templates/delivery.html +++ b/copanier/templates/delivery.html @@ -42,6 +42,9 @@ {% endif %} {% if request.user and request.user.is_staff %} +
  • +  Désarchiver +
  • Liste des produits
  • diff --git a/copanier/templates/edit_delivery.html b/copanier/templates/edit_delivery.html index a1478f8..0911ce5 100644 --- a/copanier/templates/edit_delivery.html +++ b/copanier/templates/edit_delivery.html @@ -48,7 +48,7 @@