Online edition + manage delivery + send emails.

This commit is contained in:
Alexis M 2019-09-30 00:31:19 +02:00
parent ff79712fb5
commit f96ca0a192
16 changed files with 398 additions and 23 deletions

View file

@ -341,7 +341,114 @@ async def import_products(request, response, id):
response.redirect = f"/livraison/{delivery.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): async def export_products(request, response, id):
delivery = Delivery.load(id) delivery = Delivery.load(id)
response.xlsx(reports.products(delivery)) response.xlsx(reports.products(delivery))
@ -570,7 +677,10 @@ 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)
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) amount = delivery.total_for_producer(producer.id)
if amount: if amount:
balance.append((group_id, amount)) balance.append((group_id, amount))

View file

@ -1,18 +1,32 @@
import smtplib import smtplib
from email.message import EmailMessage 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 from . import config
def send(to, subject, body, html=None): def send(to, subject, body, html=None, copy=None, attachments=None):
msg = EmailMessage() msg = MIMEMultipart()
msg.set_content(body) msg.attach(MIMEText(body, "plain"))
if html:
msg.attach(MIMEText(html, "html"))
msg["Subject"] = subject msg["Subject"] = subject
msg["From"] = config.FROM_EMAIL msg["From"] = config.FROM_EMAIL
msg["To"] = to msg["To"] = to
msg["Bcc"] = config.FROM_EMAIL msg["Bcc"] = copy if copy else config.FROM_EMAIL
if html:
msg.add_alternative(html, subtype="html") 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: if not config.SEND_EMAILS:
return print("Sending email", str(msg)) return print("Sending email", str(msg))
try: try:

View file

@ -152,7 +152,7 @@ class Groups(PersistedBase):
for group in self.groups.values(): for group in self.groups.values():
if email in group.members: if email in group.members:
return group return group
return None return email
@classmethod @classmethod
def init_fs(cls): def init_fs(cls):
@ -164,7 +164,7 @@ class Producer(Base):
referent: str = "" referent: str = ""
tel_referent: str = "" tel_referent: str = ""
contact: str = "" contact: str = ""
location: str = "" description: str = ""
@dataclass @dataclass
@ -186,6 +186,20 @@ class Product(Base):
# out += f" ({self.unit})" # out += f" ({self.unit})"
return out 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 @dataclass
class ProductOrder(Base): class ProductOrder(Base):
@ -388,8 +402,23 @@ class Delivery(PersistedBase):
def get_products_by(self, producer): def get_products_by(self, producer):
return [p for p in self.products if p.producer == 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): def total_for_producer(self, producer, person=None):
producer_products = [p for p in self.products if p.producer == producer] producer_products = [p for p in self.products if p.producer == producer]
if person: if person:
return self.orders.get(person).total(producer_products) return self.orders.get(person).total(producer_products)
return round(sum(o.total(producer_products) for o in self.orders.values()), 2) 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()]

View file

@ -35,11 +35,12 @@ def summary_for_products(wb, title, delivery, total=None, products=None):
ws.append(["", "", "", "", "Total", total]) ws.append(["", "", "", "", "Total", total])
def summary(delivery, producers=None):
def summary(delivery):
wb = Workbook() wb = Workbook()
wb.remove(wb.active) wb.remove(wb.active)
for producer in delivery.producers: if not producers:
producers = delivery.producers
for producer in producers:
summary_for_products( summary_for_products(
wb, wb,
producer, producer,

View file

@ -450,6 +450,11 @@ hr {
.notification.warning { .notification.warning {
background-color: #f9b42d; background-color: #f9b42d;
} }
.notification.info {
background-color: #aed1b175;
color: #000;
}
.notification i { .notification i {
font-size: 2rem; font-size: 2rem;
} }
@ -515,6 +520,7 @@ ul.delivery-head li {
list-style: none; list-style: none;
padding: 0; padding: 0;
margin: 0; margin: 0;
font-size: 0.8em;
} }
ul.delivery-head li i{ ul.delivery-head li i{
float: left; float: left;

View file

@ -3,13 +3,18 @@
{% block body %} {% block body %}
<h3>{{ delivery.name }} {% include "includes/order_button.html" %}</h3> <h3>{{ delivery.name }} {% include "includes/order_button.html" %}</h3>
{% include "includes/delivery_head.html" %} {% include "includes/delivery_head.html" %}
{% if request['user'].email == delivery.contact %}
<div class="notification info"><i class="icon-lightbulb"></i> Vous êtes la personne référente de cette distribution <a class="button" href="/livraison/{{ delivery.id }}/gérer">Gérer la distribution</a></div>
{% elif request['user'].email in delivery.get_referents() %}
<div class="notification info"><i class="icon-lightbulb"></i> Vous êtes référent⋅e pour cette distribution (merci !). Voici <a class="button" href="/livraison/{{ delivery.id }}/producteurices?referent={{request['user'].email}}">un petit lien pour aller voir les produits dont vous vous occupez !</a></div>
{% endif %}
<article class="delivery"> <article class="delivery">
{% if delivery.has_products %} {% if delivery.has_products %}
{% include "includes/delivery_table.html" %} {% include "includes/delivery_table.html" %}
{% else %} {% else %}
<p>Aucun produit n'est encore défini pour cette livraison.</p> <p>Aucun produit n'est encore défini pour cette livraison.</p>
{% if request.user and request.user.is_staff %} {% if request.user and request.user.is_staff %}
<div class="inline-text">Pour rajouter des produits, il faut d'abord <a href="/livraison/{{ delivery.id }}/exporter/produits"><i class="icon-layers"></i> télécharger le tableur avec les produits</a>, le modifier localement sur votre machine puis &nbsp;</div> <div class="inline-text">{% with unique_id="import-products" %}{% include "includes/modal_import_products.html" %}{% endwith %}.</div> <div class="inline-text">Pour rajouter des produits, il faut d'abord <a href="/livraison/{{ delivery.id }}/exporter"><i class="icon-layers"></i> télécharger le tableur avec les produits</a>, le modifier localement sur votre machine puis &nbsp;</div> <div class="inline-text">{% with unique_id="import-products" %}{% include "includes/modal_import_products.html" %}{% endwith %}.</div>
{% endif %} {% endif %}
{% endif %} {% endif %}
</article> </article>
@ -28,7 +33,7 @@
{% endif %} {% endif %}
{% if request.user and request.user.is_staff %} {% if request.user and request.user.is_staff %}
<li> <li>
<a href="/livraison/{{ delivery.id }}/exporter/produits"><i class="icon-layers"></i> Liste des produits</a> <a href="/livraison/{{ delivery.id }}/exporter"><i class="icon-layers"></i> Liste des produits</a>
</li> </li>
<li> <li>
<a href="/livraison/{{ delivery.id }}/edit"><i class="icon-adjustments"></i> Modifier la livraison</a> <a href="/livraison/{{ delivery.id }}/edit"><i class="icon-adjustments"></i> Modifier la livraison</a>

View file

@ -16,7 +16,7 @@
<input type="text" name="description" value="{{ delivery.description or '' }}"> <input type="text" name="description" value="{{ delivery.description or '' }}">
</label> </label>
<label> <label>
<p>Référent</p> <p>Référent⋅e</p>
<input type="email" name="contact" value="{{ delivery.contact or request.user.email }}" required> <input type="email" name="contact" value="{{ delivery.contact or request.user.email }}" required>
</label> </label>
<label> <label>
@ -56,6 +56,9 @@
{% include "includes/modal_import_products.html" %} {% include "includes/modal_import_products.html" %}
{% endwith %} {% endwith %}
</li> </li>
<li>
<a href="/livraison/{{ delivery.id }}/producteurices"><i class="icon-pencil"></i> Gérer les produits / product⋅eur⋅rice⋅s</a>
</li>
</ul> </ul>
{% endif %} {% endif %}
{% endblock body %} {% endblock body %}

View file

@ -0,0 +1,69 @@
{% extends "base.html" %}
{% block body %}
{% if producer.id %}
<h1>Modifier les informations pour « {{ producer.id }} »</h1>
{% else %}
<h1>Nouve⋅eau⋅lle product⋅eur⋅rice</h1>
{% endif %}
<form method="post">
{% if producer.id %}
<input type="hidden" name="id" value="{{ producer.id }}">
{% else %}
<label>
<p>Nom du / de la product⋅eur⋅rice</p>
<input type="text" name="id" value="{{ producer.id or '' }}">
</label>
{% endif %}
<label>
<h5>Description</h5>
<input type="text" name="description" value="{{ producer.description or '' }}">
</label>
<label>
<p>Email de la personne référente</p>
<input type="email" name="referent" value="{{ producer.referent or '' }}" required>
</label>
<label>
<p>Téléphone de la personne référente</p>
<input type="tel" name="tel_referent" value="{{ producer.tel_referent or '' }}" required>
</label>
<label>
<h5>Contact du⋅la product⋅eur⋅rice</h5>
<input type="text" name="contact" value="{{ producer.contact or '' }}">
</label>
<div>
<input type="submit" name="submit" value="Valider" class="primary">
</div>
</form>
{% if products %}
<h3>Produits <a class="button" href="/livraison/{{ delivery.id}}/{{ producer.id }}/ajouter-produit">Ajouter un produit</a></h3>
<table>
<thead>
<tr>
<th>Produit</th>
<th>Prix</th>
<th>Unité</th>
<th>Description</th>
<th>Packaging</th>
<th>Rupture ?</th>
<th></th>
</tr>
</thead>
{% for product in products %}
<tr>
<td><a href="/livraison/{{ delivery.id }}/{{ producer.id }}/{{ product.ref }}/éditer">{{ product.name }}</a></td>
<td>{{ product.price }}€</td>
<td>{{ product.unit }}</td>
<td>{{ product.description }}</td>
<td>{% if product.packing %}{{ product.packing }}{% endif %}</td>
<td>{% if product.rupture %}RUPTURE !!{% endif %}</td>
<td><a href="/livraison/{{ delivery.id }}/{{ producer.id }}/{{ product.ref }}/éditer">éditer</a></td>
</tr>
{% endfor %}
</table>
{% endif %}
<hr>
{% endblock body %}

View file

@ -0,0 +1,75 @@
{% extends "base.html" %}
{% block body %}
{% if product.ref %}
<h1>Modifier le produit « {{ product.name }} »</h1>
{% else %}
<h1>{{ producer_id }} : Nouveau produit</h1>
{% endif %}
<form method="post">
<label>
<p>Nom</p>
<input type="text" name="name" value="{{ product.name or '' }}" required>
</label>
<label>
<p>Prix (en €), pour une unité</p>
<input type="number" step="0.01" name="price" value="{{ product.price or '' }}" required>
</label>
<label>
<p>Unité de commande</p>
<input type="text" name="unit" placeholder="sachet de 200g" value="{{ product.unit or '' }}" required>
</label>
<label>
<h5>Description du produit</h5>
<input type="text" name="description" placeholder="Bière type American Pale Ale" value="{{ product.description or '' }}">
</label>
<label>
<h5>URL d'une image du produit</h5>
<input type="url" name="url" placeholder="https://example.com/image.png" value="{{ product.url or '' }}">
</label>
<label>
<h5>Conditionnement (par combien d'unités est vendu ce produit ?)</h5>
<h5><em>par exemple, entrez 6 si ce sont des cartons de 6 bouteilles</em></h5>
<input type="number" name="packing" placeholder="6" value="{{ product.packing or '' }}">
</label>
<br />
<label>
Produit actuellement en rupture ?
<input type="checkbox" name="rupture" value="RUPTURE" {% if product.rupture %}checked="true"{% endif %}>
</label>
<div>
<input type="submit" name="submit" value="Valider" class="primary">
</div>
</form>
{% if products %}
<h3>Produits</h3>
<table>
<thead>
<tr>
<th>Produit</th>
<th>Prix</th>
<th>Unité</th>
<th>Description</th>
<th>Packaging</th>
<th>Rupture ?</th>
<th></th>
</tr>
</thead>
{% for product in products %}
<tr>
<td><a href="/livraison/{{ delivery.id }}/produit/{{ product.ref }}/éditer">{{ product.name }}</a></td>
<td>{{ product.price }}€</td>
<td>{{ product.unit }}</td>
<td>{{ product.description }}</td>
<td>{% if product.packing %}{{ product.packing }}{% endif %}</td>
<td>{% if product.rupture %}RUPTURE !!{% endif %}</td>
<td><a href="/livraison/{{ delivery.id }}/produit/{{ product.ref }}/éditer">éditer</a></td>
</tr>
{% endfor %}
</table>
{% endif %}
<hr>
{% endblock body %}

View file

@ -1,6 +1,11 @@
{% for producer in delivery.producers %} {% if referent %}
<h2>{{ producer }}</h2> {% set producers = delivery.get_producers_for_referent(referent) %}
<h4>Référent⋅e : {{ delivery.producers[producer].referent }} / {{ delivery.producers[producer].tel_referent }}</h4> {% else %}
{% set producers = delivery.producers %}
{% endif %}
{% for producer in producers %}
<h2>{{ producer }} {% if edit_mode %}<a class="button" href="/livraison/{{ delivery.id }}/{{ producer }}/éditer"><i class="icon-ribbon"></i>Éditer</a> <a class="button" href="/livraison/{{ delivery.id }}/{{ producer }}/ajouter-produit"><i class="icon-puzzle"></i>Ajouter un produit </a>{% endif %}</h2>
<h5>{% if delivery.producers[producer].description %}{{ delivery.producers[producer].description }}{% endif %}. Référent⋅e : {{ delivery.producers[producer].referent }} / {{ delivery.producers[producer].tel_referent }}</h5>
<table class="delivery"> <table class="delivery">
<thead> <thead>
<tr> <tr>
@ -9,6 +14,8 @@
{% if delivery.has_packing %} {% if delivery.has_packing %}
<th class="packing">Conditionnement</th> <th class="packing">Conditionnement</th>
{% endif %} {% endif %}
{% if edit_mode %}<th>Éditer</th>{% endif %}
{% if not list_only %}
<th class="amount">Total</th> <th class="amount">Total</th>
{% for orderer, order in delivery.orders.items() %} {% for orderer, order in delivery.orders.items() %}
<th class="person{% if delivery.is_passed and not order.paid %} not-paid{% endif %}"> <th class="person{% if delivery.is_passed and not order.paid %} not-paid{% endif %}">
@ -19,16 +26,18 @@
{% endif %} {% endif %}
</th> </th>
{% endfor %} {% endfor %}
{% endif %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for product in delivery.get_products_by(producer) %} {% for product in delivery.get_products_by(producer) %}
<tr> <tr>
<th class="product {% if product.rupture %}rupture{% endif %}">{{ product }} {% if product.rupture %}(RUPTURE !){% endif %} <th class="product {% if product.rupture %}rupture{% endif %}">{% if edit_mode %}<a href="/livraison/{{ delivery.id }}/{{ product.producer }}/{{ product.ref }}/éditer">{% endif %}{{ product }}{% if edit_mode %}</a>{% endif %}{% if product.rupture %} {{ product.rupture }}{% endif %}
<td>{{ product.price | round(2) }} €</td> <td>{{ product.price | round(2) }} €</td>
{% if delivery.has_packing %} {% if delivery.has_packing %}
<td class="packing">{% if product.packing %}{{ product.packing }} x {% endif %} {{ product.unit }}</td> <td class="packing">{% if product.packing %}{{ product.packing }} x {% endif %} {{ product.unit }}</td>
{% endif %} {% endif %}
{% if not list_only %}
<th{% if delivery.status == delivery.ADJUSTMENT and delivery.product_missing(product) %} class="missing" title="Les commandes individuelles ne correspondent pas aux conditionnements"{% endif %}> <th{% if delivery.status == delivery.ADJUSTMENT and delivery.product_missing(product) %} class="missing" title="Les commandes individuelles ne correspondent pas aux conditionnements"{% endif %}>
{{ delivery.product_wanted(product) }} {{ delivery.product_wanted(product) }}
{% if delivery.status == delivery.ADJUSTMENT and delivery.product_missing(product) %} ({{ delivery.product_missing(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() %} {% for email, order in delivery.orders.items() %}
<td title="Commandé: {{ order[product.ref].wanted }} — Ajusté: {{ "%+d"|format(order[product.ref].adjustment) }}">{{ order[product.ref].quantity or "—" }}</td> <td title="Commandé: {{ order[product.ref].wanted }} — Ajusté: {{ "%+d"|format(order[product.ref].adjustment) }}">{{ order[product.ref].quantity or "—" }}</td>
{% endfor %} {% endfor %}
{% endif %}
{% if edit_mode %}<td><a href="/livraison/{{ delivery.id }}/{{ product.producer }}/{{ product.ref }}/éditer">modifier</a></td>{% endif %}
</tr> </tr>
{% endfor %} {% endfor %}
{% if not list_only %}
<tr> <tr>
<th class="total"><i class="icon-pricetags"></i> Total</th><td></td> <th class="total"><i class="icon-pricetags"></i> Total</th><td></td>
{% if delivery.has_packing %} {% if delivery.has_packing %}
@ -50,6 +62,7 @@
<td>{{ order.total(delivery.get_products_by(producer)) }} €</td> <td>{{ order.total(delivery.get_products_by(producer)) }} €</td>
{% endfor %} {% endfor %}
</tr> </tr>
{% endif %}
</tbody> </tbody>
</table> </table>
{% endfor %} {% endfor %}

View file

@ -1,6 +1,6 @@
{% extends "includes/modal.html" %} {% extends "includes/modal.html" %}
{% block modal_label %}<i class="icon-upload"></i> Importer les produits{% endblock modal_label %} {% block modal_label %}<i class="icon-upload"></i> Importer les produits depuis un tableur{% endblock modal_label %}
{% block modal_body %} {% block modal_body %}
<h3>Importer des produits</h3> <h3>Importer des produits</h3>
<p>Format pris en charge: xlsx</p> <p>Format pris en charge: xlsx</p>

View file

@ -0,0 +1,9 @@
{% extends "base.html" %}
{% block body %}
<h3>{{ delivery.name }}</h3>
{% if referent %}<p>Voici la liste des product⋅eurs⋅rices dont {{ referent }} est référent⋅e. <a class="button" href="/livraison/{{delivery.id}}/producteurices">voir tous les produits</a></p>{% endif %}
<article class="delivery">
{% include "includes/delivery_table.html" %}
</article>
{% endblock body %}

View file

@ -0,0 +1,20 @@
{% extends "base.html" %}
{% block body %}
<h1>Gérer « {{ delivery.name }} »</h1>
<h3>Avant et pendant la distribution</h3>
<a class="button" href="/livraison/{{ delivery.id }}/edit"><i class="icon-pencil"></i>&nbsp; Modifier la commande (dates, lieu, référent⋅e, etc)</a>
<a class="button" href="/livraison/{{ delivery.id }}/producteurices"><i class="icon-pencil"></i>&nbsp; Modifier les produits, les product⋅rices⋅eurs</a>
<a class="button" href="/groupes"><i class="icon-globe"></i>&nbsp; Gérer les groupes / colocs</a>
<h3>Une fois les commandes passées (après le {{ delivery.order_before|date }})</h3>
<a class="button" href="/livraison/{{ delivery.id }}/rapport-complet.xlsx"><i class="icon-download"></i>&nbsp; Télécharger le récap (global) des commandes</a>
<a class="button" href="/livraison/{{ delivery.id }}/envoi-email-referentes"><i class="icon-envelope"></i>&nbsp; Envoyer les infos de commande aux référent⋅e⋅s</a>
<h3>Pour préparer la distribution (le {{ delivery.from_date|date }})</h3>
<a class="button" href="/livraison/{{ delivery.id }}/émargement"><i class="icon-document"></i>&nbsp; Fiches de commandes par groupe</a>
<a class="button" href="/livraison/{{ delivery.id }}/rapport-complet.xlsx"><i class="icon-grid"></i>&nbsp; Télécharger le résumé général des commandes</a>
<a class="button" href="/livraison/{{ delivery.id }}/solde"><i class="icon-gears"></i>&nbsp; Faire la répartition des paiements</a>
{% endblock %}

View file

@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block body %}
<h1>Envoi d'un mail aux référent⋅e⋅s</h1>
<p>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.</p>
<form method="post">
<input type="text" name="email_subject" style="width:800px" value="{{ config.SITE_NAME }} - Les commandes pour vos producteurs⋅rices" /><br />
<textarea name="email_body" cols="80" rows="10">
Bonjour,
Et voilà, les commandes maintenant terminées, il est maintenant temps de passer à l'action !
En pièce-jointe, les informations pour les producteurs⋅rices dont tu est référent⋅e.
Rendez-vous pour la distribution, le {{ delivery.from_date|date }} de {{ delivery.from_date|time }} à {{ delivery.to_date|time }} à {{ delivery.where }}.
A bientôt !
</textarea><br />
<input type="submit" name="submit" value="Envoyer le mail aux référent⋅e⋅s (avec le tableur en PJ)" class="primary">
</form>
{% endblock %}

View file

@ -9,7 +9,7 @@
<body> <body>
<h2>{{ delivery.name }} {{ delivery.from_date.date() }} - liste d'émargement</h2> <h2>{{ delivery.name }} {{ delivery.from_date.date() }} - liste d'émargement</h2>
{% for email, order in delivery.orders.items() %} {% for email, order in delivery.orders.items() %}
<h3>{{ email }}</h3> <h3>{{ request.groups.groups[email].name }}</h3>
{% include "includes/order_summary.html" %} {% include "includes/order_summary.html" %}
<hr> <hr>
{% endfor %} {% endfor %}

View file

@ -239,7 +239,7 @@ async def test_post_delivery_balance(client, delivery):
async def test_export_products(client, delivery): async def test_export_products(client, delivery):
delivery.persist() delivery.persist()
resp = await client.get(f"/livraison/{delivery.id}/exporter/produits") resp = await client.get(f"/livraison/{delivery.id}/exporter")
wb = load_workbook(filename=BytesIO(resp.body)) wb = load_workbook(filename=BytesIO(resp.body))
assert list(wb.active.values) == [ assert list(wb.active.values) == [
("name", "ref", "price", "unit", "description", "url", "img", "packing", "producer"), ("name", "ref", "price", "unit", "description", "url", "img", "packing", "producer"),