mirror of
https://github.com/almet/copanier.git
synced 2025-04-28 19:42:37 +02:00
parent
9af506ba4b
commit
befb5fd1de
16 changed files with 249 additions and 115 deletions
|
@ -11,22 +11,48 @@ from roll.extensions import traceback, simple_server, static
|
||||||
from slugify import slugify
|
from slugify import slugify
|
||||||
|
|
||||||
from debts.solver import order_balance, check_balance, reduce_balance
|
from debts.solver import order_balance, check_balance, reduce_balance
|
||||||
|
from weasyprint import HTML
|
||||||
|
|
||||||
from . import config, reports, session, utils, emails, loggers, imports
|
from . import config, reports, session, utils, emails, loggers, imports
|
||||||
from .models import Delivery, Order, Person, Product, ProductOrder, Groups, Group
|
from .models import Delivery, Order, Person, Product, ProductOrder, Groups, Group
|
||||||
|
|
||||||
|
|
||||||
class Response(RollResponse):
|
class Response(RollResponse):
|
||||||
def html(self, template_name, *args, **kwargs):
|
def render_template(self, template_name, *args, **kwargs):
|
||||||
self.headers["Content-Type"] = "text/html; charset=utf-8"
|
|
||||||
context = app.context()
|
context = app.context()
|
||||||
context.update(kwargs)
|
context.update(kwargs)
|
||||||
context["request"] = self.request
|
context["request"] = self.request
|
||||||
|
context["config"] = config
|
||||||
|
context["request"] = self.request
|
||||||
if self.request.cookies.get("message"):
|
if self.request.cookies.get("message"):
|
||||||
context["message"] = json.loads(self.request.cookies["message"])
|
context["message"] = json.loads(self.request.cookies["message"])
|
||||||
self.cookies.set("message", "")
|
self.cookies.set("message", "")
|
||||||
context["config"] = config
|
return env.get_template(template_name).render(*args, **context)
|
||||||
self.body = env.get_template(template_name).render(*args, **context)
|
|
||||||
|
def html(self, template_name, *args, **kwargs):
|
||||||
|
self.headers["Content-Type"] = "text/html; charset=utf-8"
|
||||||
|
self.body = self.render_template(template_name, *args, **kwargs)
|
||||||
|
|
||||||
|
def render_pdf(self, template_name, *args, **kwargs):
|
||||||
|
html = self.render_template(template_name, *args, **kwargs)
|
||||||
|
|
||||||
|
static_folder = Path(__file__).parent / "static"
|
||||||
|
stylesheets = [
|
||||||
|
static_folder / "app.css",
|
||||||
|
static_folder / "icomoon.css",
|
||||||
|
static_folder / "page.css",
|
||||||
|
]
|
||||||
|
if "css" in kwargs:
|
||||||
|
stylesheets.append(static_folder / kwargs["css"])
|
||||||
|
|
||||||
|
return HTML(string=html).write_pdf(stylesheets=stylesheets)
|
||||||
|
|
||||||
|
def pdf(self, template_name, *args, **kwargs):
|
||||||
|
self.body = self.render_pdf(template_name, *args, **kwargs)
|
||||||
|
mimetype = "application/pdf"
|
||||||
|
filename = kwargs.get("filename", "file.pdf")
|
||||||
|
self.headers["Content-Disposition"] = f'attachment; filename="{filename}"'
|
||||||
|
self.headers["Content-Type"] = f"{mimetype}; charset=utf-8"
|
||||||
|
|
||||||
def xlsx(self, body, filename=f"{config.SITE_NAME}.xlsx"):
|
def xlsx(self, body, filename=f"{config.SITE_NAME}.xlsx"):
|
||||||
self.body = body
|
self.body = body
|
||||||
|
@ -228,9 +254,7 @@ async def create_group(request, response):
|
||||||
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()
|
||||||
|
@ -340,15 +364,31 @@ async def import_products(request, response, id):
|
||||||
|
|
||||||
|
|
||||||
@app.route("/livraison/{id}/producteurices")
|
@app.route("/livraison/{id}/producteurices")
|
||||||
|
@app.route("/livraison/{id}/producteurices.pdf")
|
||||||
async def list_producers(request, response, id):
|
async def list_producers(request, response, id):
|
||||||
delivery = Delivery.load(id)
|
delivery = Delivery.load(id)
|
||||||
response.html(
|
template_name = "list_products.html"
|
||||||
"list_products.html",
|
template_params = {
|
||||||
{
|
|
||||||
"edit_mode": True,
|
"edit_mode": True,
|
||||||
|
"list_only": True,
|
||||||
"delivery": delivery,
|
"delivery": delivery,
|
||||||
"referent": request.query.get("referent", None),
|
"referent": request.query.get("referent", None),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
if request.url.endswith(b".pdf"):
|
||||||
|
response.pdf(template_name, template_params, filename=utils.prefix("producteurices.pdf", delivery),)
|
||||||
|
else:
|
||||||
|
response.html(template_name, template_params)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/livraison/{id}/{producer}/bon-de-commande.pdf", methods=["GET"])
|
||||||
|
async def pdf_for_producer(request, response, id, producer):
|
||||||
|
delivery = Delivery.load(id)
|
||||||
|
date = delivery.to_date.strftime("%Y-%m-%d")
|
||||||
|
response.pdf(
|
||||||
|
"list_products.html",
|
||||||
|
{"list_only": True, "delivery": delivery, "producers": [producer]},
|
||||||
|
filename=utils.prefix(f"bon-de-commande-{producer}.pdf", delivery),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -432,10 +472,10 @@ async def create_product(request, response, delivery_id, producer_id):
|
||||||
|
|
||||||
|
|
||||||
@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 delivery_toolbox(request, response, id):
|
||||||
delivery = Delivery.load(id)
|
delivery = Delivery.load(id)
|
||||||
response.html(
|
response.html(
|
||||||
"manage_delivery.html",
|
"delivery_toolbox.html",
|
||||||
{
|
{
|
||||||
"delivery": delivery,
|
"delivery": delivery,
|
||||||
"referents": [p.referent for p in delivery.producers.values()],
|
"referents": [p.referent for p in delivery.producers.values()],
|
||||||
|
@ -446,56 +486,47 @@ async def manage_delivery(request, response, id):
|
||||||
@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")
|
|
||||||
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")
|
||||||
|
sent_mails = 0
|
||||||
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)
|
||||||
if any(
|
attachments = []
|
||||||
[delivery.producers[p].has_active_products(delivery) for p in producers]
|
for producer in producers:
|
||||||
):
|
if delivery.producers[producer].has_active_products(delivery):
|
||||||
summary = reports.summary(delivery, producers)
|
pdf_file = response.render_pdf(
|
||||||
|
"list_products.html",
|
||||||
|
{
|
||||||
|
"list_only": True,
|
||||||
|
"delivery": delivery,
|
||||||
|
"producers": [producer],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
attachments.append(
|
||||||
|
(
|
||||||
|
utils.prefix(f"{producer}.pdf", delivery),
|
||||||
|
pdf_file,
|
||||||
|
"application/pdf",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if attachments:
|
||||||
|
sent_mails = sent_mails + 1
|
||||||
emails.send(
|
emails.send(
|
||||||
referent,
|
referent,
|
||||||
email_subject,
|
email_subject,
|
||||||
email_body,
|
email_body,
|
||||||
copy=delivery.contact,
|
copy=delivery.contact,
|
||||||
attachments=[
|
attachments=attachments,
|
||||||
(f"{config.SITE_NAME}-{date}-{referent}.xlsx", summary)
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
response.message("Le mail à bien été envoyé")
|
response.message(f"Un mail à été envoyé aux {sent_mails} référent⋅e⋅s")
|
||||||
response.redirect = f"/livraison/{id}/gérer"
|
response.redirect = f"/livraison/{id}/gérer"
|
||||||
|
|
||||||
response.html("prepare_referent_email.html", {"delivery": delivery})
|
response.html("prepare_referent_email.html", {"delivery": delivery})
|
||||||
|
|
||||||
|
|
||||||
@app.route("/livraison/{id}/bon-de-commande-referent⋅e", methods=["GET"])
|
|
||||||
async def download_referent_summary(request, response, id):
|
|
||||||
delivery = Delivery.load(id)
|
|
||||||
date = delivery.to_date.strftime("%Y-%m-%d")
|
|
||||||
if not request["user"].is_referent(delivery):
|
|
||||||
return
|
|
||||||
referent = request["user"].email
|
|
||||||
producers = delivery.get_producers_for_referent(referent)
|
|
||||||
summary = reports.summary(delivery, producers)
|
|
||||||
response.xlsx(summary, filename=f"{config.SITE_NAME}-{date}-{referent}.xlsx")
|
|
||||||
|
|
||||||
|
|
||||||
@app.route(
|
|
||||||
"/livraison/{id}/product⋅eur⋅rice/{producer}/bon-de-commande",
|
|
||||||
methods=["GET"],
|
|
||||||
)
|
|
||||||
async def download_producer_report(request, response, id, producer):
|
|
||||||
delivery = Delivery.load(id)
|
|
||||||
summary = reports.summary(delivery, [producer])
|
|
||||||
date = delivery.to_date.strftime("%Y-%m-%d")
|
|
||||||
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"])
|
||||||
async def export_products(request, response, id):
|
async def export_products(request, response, id):
|
||||||
delivery = Delivery.load(id)
|
delivery = Delivery.load(id)
|
||||||
|
@ -636,7 +667,12 @@ async def send_order(request, response, id):
|
||||||
@app.route("/livraison/{id}/émargement", methods=["GET"])
|
@app.route("/livraison/{id}/émargement", methods=["GET"])
|
||||||
async def signing_sheet(request, response, id):
|
async def signing_sheet(request, response, id):
|
||||||
delivery = Delivery.load(id)
|
delivery = Delivery.load(id)
|
||||||
response.html("signing_sheet.html", {"delivery": delivery})
|
response.pdf(
|
||||||
|
"signing_sheet.html",
|
||||||
|
{"delivery": delivery},
|
||||||
|
css="signing-sheet.css",
|
||||||
|
filename=utils.prefix("commandes-par-groupe.pdf", delivery),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/livraison/{id}/importer/commande", methods=["POST"])
|
@app.route("/livraison/{id}/importer/commande", methods=["POST"])
|
||||||
|
@ -730,6 +766,7 @@ async def adjust_product(request, response, id, ref):
|
||||||
|
|
||||||
|
|
||||||
@app.route("/livraison/{id}/solde", methods=["GET"])
|
@app.route("/livraison/{id}/solde", methods=["GET"])
|
||||||
|
@app.route("/livraison/{id}/solde.pdf", methods=["GET"])
|
||||||
@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)
|
||||||
|
@ -770,17 +807,25 @@ 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(
|
template_name = "delivery_balance.html"
|
||||||
"delivery_balance.html",
|
template_args = {
|
||||||
{
|
"delivery": delivery,
|
||||||
"delivery": delivery,
|
"debiters": debiters,
|
||||||
"debiters": debiters,
|
"crediters": crediters,
|
||||||
"crediters": crediters,
|
"results": results_dict,
|
||||||
"results": results_dict,
|
"debiters_groups": groups.groups,
|
||||||
"debiters_groups": groups.groups,
|
"crediters_groups": producer_groups,
|
||||||
"crediters_groups": producer_groups,
|
}
|
||||||
},
|
|
||||||
)
|
if request.url.endswith(b".pdf"):
|
||||||
|
date = delivery.to_date.strftime("%Y-%m-%d")
|
||||||
|
response.pdf(
|
||||||
|
template_name,
|
||||||
|
template_args,
|
||||||
|
filename=utils.prefix("répartition-des-chèques.pdf", delivery),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
response.html(template_name, template_args)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/livraison/{id}/solde.xlsx", methods=["GET"])
|
@app.route("/livraison/{id}/solde.xlsx", methods=["GET"])
|
||||||
|
|
|
@ -11,13 +11,10 @@ def send(to, subject, body, html=None, copy=None, attachments=None):
|
||||||
text=body, html=html, subject=subject, mail_from=config.FROM_EMAIL
|
text=body, html=html, subject=subject, mail_from=config.FROM_EMAIL
|
||||||
)
|
)
|
||||||
|
|
||||||
for filename, attachment in attachments:
|
for filename, attachment, mime in attachments:
|
||||||
message.attach(
|
message.attach(filename=filename, data=attachment, mime=f"{mime} charset=utf-8")
|
||||||
filename=filename,
|
|
||||||
data=attachment,
|
|
||||||
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=utf-8",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
config.SEND_EMAILS = False
|
||||||
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))
|
||||||
|
|
|
@ -597,4 +597,57 @@ table.paiements td {
|
||||||
}
|
}
|
||||||
table.paiements tr:hover td {
|
table.paiements tr:hover td {
|
||||||
background: #f2f2f2;
|
background: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 600px;
|
||||||
|
margin: 100px auto;
|
||||||
|
}
|
||||||
|
.progressbar {
|
||||||
|
counter-reset: step;
|
||||||
|
}
|
||||||
|
.progressbar li {
|
||||||
|
list-style-type: none;
|
||||||
|
width: 20%;
|
||||||
|
float: left;
|
||||||
|
font-size: 12px;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #7d7d7d;
|
||||||
|
}
|
||||||
|
.progressbar li:before {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
content: counter(step);
|
||||||
|
counter-increment: step;
|
||||||
|
line-height: 25px;
|
||||||
|
border: 2px solid #7d7d7d;
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 auto 10px auto;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
.progressbar li:after {
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
background-color: #7d7d7d;
|
||||||
|
top: 15px;
|
||||||
|
left: -50%;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
.progressbar li:first-child:after {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
.progressbar li.active {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
.progressbar li.active:before {
|
||||||
|
border-color: #55b776;
|
||||||
|
}
|
||||||
|
.progressbar li.active + li:after {
|
||||||
|
background-color: #55b776;
|
||||||
}
|
}
|
28
copanier/static/page.css
Normal file
28
copanier/static/page.css
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
@page {
|
||||||
|
size: "A4";
|
||||||
|
margin: 0cm;
|
||||||
|
size: landscape;
|
||||||
|
}
|
||||||
|
|
||||||
|
#download {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
nav, .notification, .toolbox, .edit, .hide-on-print, .button{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.delivery-head {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
ul.delivery-head li {
|
||||||
|
margin-bottom: 1.5em !important;
|
||||||
|
}
|
||||||
|
table.delivery {
|
||||||
|
page-break-after: always;
|
||||||
|
}
|
||||||
|
.page-break {
|
||||||
|
page-break-after: always;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,3 @@
|
||||||
table, tr, td {
|
@page {
|
||||||
border: 0px;
|
size: portrait !important;
|
||||||
}
|
}
|
||||||
tr:nth-child(even) {background: #CCC}
|
|
||||||
tr:nth-child(odd) {background: #FFF}
|
|
|
@ -18,12 +18,11 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<section class="menu">
|
<section class="menu">
|
||||||
<h1><a href="/">{{ config.SITE_NAME }}</a> <small>{{ config.SITE_DESCRIPTION }}</small></h1>
|
<h1><a href="/">{{ config.SITE_NAME }}</a> <small>{{ config.SITE_DESCRIPTION }}</small></h1>
|
||||||
|
{% block title %}<h2>{% if delivery %}{{ delivery.name }}{% endif %}{% block actions %}{% endblock %}{% endblock %}</h2>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
{% if request.user %}
|
{% if request.user %}
|
||||||
{% if request.user.is_staff %}
|
<i class="icon-lock"></i> <span>{{ request["user"].email }} {% if request["user"].group_name %}({{ request["user"].group_name }}){% endif %} <a href="/déconnexion">déconnexion</a></span>
|
||||||
<a class="button" href="/livraison"><i class="icon-hotairballoon"></i> Nouvelle livraison</a>
|
|
||||||
{% endif %}
|
|
||||||
<i class="icon-lock"></i> <span>{{ request["user"].email }} {% if request["user"].group_name %}({{ request["user"].group_name }}){% endif %}</span>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</nav>
|
</nav>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -1,13 +1,23 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block actions %}
|
||||||
|
{% include "includes/order_button.html" %}
|
||||||
|
{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<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 %}
|
{% 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">Voir la boîte à outils</a></div>
|
<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">Voir la boîte à outils</a></div>
|
||||||
{% elif request['user'].email in delivery.get_referents() %}
|
{% 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 !). <a class="button" href="/livraison/{{ delivery.id }}/producteurices?referent={{request['user'].email}}">Gérer les produits dont vous vous occupez</a></div>
|
<div class="notification info"><i class="icon-lightbulb"></i> Vous êtes référent⋅e pour cette distribution (merci !). <a class="button" href="/livraison/{{ delivery.id }}/producteurices?referent={{request['user'].email}}">Gérer les produits dont vous vous occupez</a></div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<ul class="progressbar">
|
||||||
|
<li {% if delivery.status == delivery.OPEN %}class="active"{% endif %}>Mise à jour des prix</li>
|
||||||
|
<li {% if delivery.status == delivery.OPEN %}class="active"{% endif %}>Ouverture des commandes</li>
|
||||||
|
<li {% if delivery.status == delivery.ADJUSTMENT %}class="active"{% endif %}>Ajustements</li>
|
||||||
|
<li {% if delivery.can_generate_reports %}class="active"{% endif %}>Achats par les référent⋅e⋅s</li>
|
||||||
|
<li>Distribution</li>
|
||||||
|
</ul>
|
||||||
<article class="delivery">
|
<article class="delivery">
|
||||||
{% if delivery.has_products %}
|
{% if delivery.has_products %}
|
||||||
{% include "includes/delivery_table.html" %}
|
{% include "includes/delivery_table.html" %}
|
||||||
|
@ -22,7 +32,7 @@
|
||||||
<ul class="toolbox">
|
<ul class="toolbox">
|
||||||
{% if not delivery.is_archived and delivery.status == delivery.CLOSED %}
|
{% if not delivery.is_archived and delivery.status == delivery.CLOSED %}
|
||||||
<li>
|
<li>
|
||||||
<a href="/livraison/{{ delivery.id }}/bon-de-commande.xlsx"><i class="icon-ribbon"></i> Bon de commande</a>
|
<a href="/livraison/{{ delivery.iddelivery }}/bon-de-commande.xlsx"><i class="icon-ribbon"></i> Bon de commande</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/livraison/{{ delivery.id }}/rapport-complet.xlsx"><i class="icon-grid"></i> Rapport complet</a>
|
<a href="/livraison/{{ delivery.id }}/rapport-complet.xlsx"><i class="icon-grid"></i> Rapport complet</a>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
{% block title %}<h1><i class="icon-wallet"></i> Répartition des paiements <a id="download" class="button" href="/livraison/{{ delivery.id }}/solde.pdf">Télécharger</a></h1>{% endblock %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<h1><i class="icon-wallet"></i> <a href="/livraison/{{ delivery.id }}">{{ delivery.name }}</a><br />Répartition des paiements</h1>
|
<p class="info">{{ delivery.name }} du {{ delivery.to_date | date }}.</p>
|
||||||
<p class="info">Les personnes indiquées avec un <code>*</code> à côté de leur nom sont celles qui ont payé cette commande pour leur groupe.</p>
|
<p class="info">Les personnes indiquées avec un <code>*</code> à côté de leur nom sont celles qui ont payé cette commande pour leur groupe.</p>
|
||||||
<table class="paiements">
|
<table class="paiements">
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -16,13 +16,12 @@
|
||||||
<a class="button" href="/groupes"><i class="icon-globe"></i> Gérer les groupes / colocs</a>
|
<a class="button" href="/groupes"><i class="icon-globe"></i> Gérer les groupes / colocs</a>
|
||||||
|
|
||||||
<h3>Une fois les commandes passées (après le {{ delivery.order_before|date }})</h3>
|
<h3>Une fois les commandes passées (après le {{ delivery.order_before|date }})</h3>
|
||||||
<a class="button" href="/livraison/{{ delivery.id }}/bon-de-commande.xlsx"><i class="icon-download"></i> Télécharger le bon de commande global</a>
|
<a class="button" href="/livraison/{{ delivery.id }}/producteurices.pdf"><i class="icon-download"></i> Télécharger les bons de livraison</a>
|
||||||
<a class="button" href="/livraison/{{ delivery.id }}/rapport-complet.xlsx"><i class="icon-download"></i> Télécharger le récap (global) des commandes</a>
|
<a class="button" href="/livraison/{{ delivery.id }}/rapport-complet.xlsx"><i class="icon-download"></i> Télécharger le tableau des commandes</a>
|
||||||
<a class="button" href="/livraison/{{ delivery.id }}/envoi-email-referentes"><i class="icon-envelope"></i> Envoyer les infos de commande aux référent⋅e⋅s</a>
|
<a class="button" href="/livraison/{{ delivery.id }}/envoi-email-referentes"><i class="icon-envelope"></i> Envoyer les infos de commande aux référent⋅e⋅s</a>
|
||||||
|
|
||||||
<h3>Pour préparer la distribution (le {{ delivery.from_date|date }})</h3>
|
<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> Fiches de commandes par groupe</a>
|
<a class="button" href="/livraison/{{ delivery.id }}/émargement"><i class="icon-document"></i> Fiches de commandes par groupe</a>
|
||||||
<a class="button" href="/livraison/{{ delivery.id }}/rapport-complet.xlsx"><i class="icon-grid"></i> 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> Faire la répartition des paiements</a>
|
<a class="button" href="/livraison/{{ delivery.id }}/solde"><i class="icon-gears"></i> Faire la répartition des paiements</a>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -1,6 +1,5 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<h2>Livraisons à venir</h2>
|
|
||||||
{% with deliveries=incoming %}
|
{% with deliveries=incoming %}
|
||||||
{% include "includes/delivery_list.html" %}
|
{% include "includes/delivery_list.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
@ -15,6 +14,11 @@
|
||||||
<hr>
|
<hr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<ul class="toolbox">
|
<ul class="toolbox">
|
||||||
|
{% if request.user.is_staff %}
|
||||||
|
<li>
|
||||||
|
<a class="button" href="/livraison"><i class="icon-hotairballoon"></i> Nouvelle livraison</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
<li>
|
<li>
|
||||||
<a href="/groupes" class="button"><i class="icon-globe"></i> Gérer les groupes / colocs</a>
|
<a href="/groupes" class="button"><i class="icon-globe"></i> Gérer les groupes / colocs</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -4,11 +4,10 @@
|
||||||
{% if display_counts %}
|
{% if display_counts %}
|
||||||
<li><i class="icon-lightbulb"></i> <strong>{{ delivery.orders|length }}</strong> colocs</strong>, <strong>{{ delivery.products|length }}</strong> produits et <strong>{{ delivery.producers | length}}</strong> product⋅eurs⋅rices, <strong>{{ delivery.total }}€</strong></li>
|
<li><i class="icon-lightbulb"></i> <strong>{{ delivery.orders|length }}</strong> colocs</strong>, <strong>{{ delivery.products|length }}</strong> produits et <strong>{{ delivery.producers | length}}</strong> product⋅eurs⋅rices, <strong>{{ delivery.total }}€</strong></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li><i class="icon-strategy"></i> <strong>Référent⋅e</strong> <a href="mailto:{{ delivery.contact }}">{{ delivery.contact }}</a></li>
|
<li class="hide-on-print"><i class="icon-strategy"></i> <strong>Référent⋅e</strong> <a href="mailto:{{ delivery.contact }}">{{ delivery.contact }}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li><i class="icon-clock"></i> <strong>Date de livraison</strong> <time datetime="{{ delivery.from_date }}">{{ delivery.from_date|date|capitalize }} de {{ delivery.from_date|time }} à {{ delivery.to_date|time }}</time></li>
|
<li><i class="icon-clock"></i> <strong>Date de livraison</strong> <time datetime="{{ delivery.from_date }}">{{ delivery.from_date|date|capitalize }} de {{ delivery.from_date|time }} à {{ delivery.to_date|time }}</time></li>
|
||||||
<li><i class="icon-hourglass"></i> {% if delivery.status == delivery.OPEN %}<strong>Date limite de commande</strong> <time datetime="{{ delivery.order_before.date() }}">{{ delivery.order_before|date|capitalize }}</time>{% elif delivery.status == delivery.ADJUSTMENT %}<strong>Ajustement en cours</strong>{% elif delivery.status == delivery.CLOSED %}<strong>Fermée</strong>{% else %}<strong>Archivée</strong>{% endif %}</li>
|
<li class="hide-on-print"><i class="icon-hourglass"></i> {% if delivery.status == delivery.OPEN %}<strong>Date limite de commande</strong> <time datetime="{{ delivery.order_before.date() }}">{{ delivery.order_before|date|capitalize }}</time>{% elif delivery.status == delivery.ADJUSTMENT %}<strong>Ajustement en cours</strong>{% elif delivery.status == delivery.CLOSED %}<strong>Fermée</strong>{% else %}<strong>Archivée</strong>{% endif %}</li>
|
||||||
{% if delivery.instructions %}<li><i class="icon-lightbulb"></i> <strong>À savoir</strong> {{ delivery.instructions }}</li>{% endif %}
|
{% if delivery.instructions %}<li><i class="icon-lightbulb"></i> <strong>À savoir</strong> {{ delivery.instructions }}</li>{% endif %}
|
||||||
{% if delivery.infos_url %}<li><i class="icon-global"></i><strong>Plus d'infos</strong> <a href="{{ delivery.infos_url }}" title="{{ delivery.infos_url }}">{{ delivery.infos_url|truncate(20)}}</a></li>{% endif %}
|
{% if delivery.infos_url %}<li><i class="icon-global"></i><strong>Plus d'infos</strong> <a href="{{ delivery.infos_url }}" title="{{ delivery.infos_url }}">{{ delivery.infos_url|truncate(20)}}</a></li>{% endif %}
|
||||||
|
</ul>
|
||||||
</ul>
|
|
|
@ -1,19 +1,22 @@
|
||||||
{% if referent %}
|
{% if not producers and referent %}
|
||||||
{% set producers = delivery.get_producers_for_referent(referent) %}
|
{% set producers = delivery.get_producers_for_referent(referent) %}
|
||||||
{% else %}
|
{% elif not producers %}
|
||||||
{% set producers = delivery.producers %}
|
{% set producers = delivery.producers %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for producer in producers %}
|
{% for producer in producers %}
|
||||||
|
{% set producer_obj = delivery.producers[producer] %}
|
||||||
<h2>{{ producer }}
|
<h2>{{ producer }}
|
||||||
{% if edit_mode or request.user.is_staff or delivery.producers[producer].referent == request.user.email %}
|
{% if edit_mode or request.user.is_staff or producer_obj.referent == request.user.email %}
|
||||||
|
<span class="edit">
|
||||||
<a class="button" href="/livraison/{{ delivery.id }}/{{ producer }}/éditer"><i class="icon-ribbon"></i> Éditer</a>
|
<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>
|
<a class="button" href="/livraison/{{ delivery.id }}/{{ producer }}/ajouter-produit"><i class="icon-puzzle"></i> Ajouter un produit </a>
|
||||||
{% if delivery.can_generate_reports %}
|
{% if delivery.can_generate_reports %}
|
||||||
<a class="button" href="/livraison/{{ delivery.id }}/{{ producer }}/bon-de-commande"><i class="icon-grid"></i> Télécharger le bon de commande</a>
|
<a class="button" href="/livraison/{{ delivery.id }}/{{ producer }}/bon-de-commande.pdf"><i class="icon-grid"></i> Télécharger le bon de commande</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</h2>
|
</h2>
|
||||||
<h5>{% if delivery.producers[producer].description %}{{ delivery.producers[producer].description }}{% endif %}. Référent⋅e : {{ delivery.producers[producer].referent }} / {{ delivery.producers[producer].referent_tel }}</h5>
|
<h5>{% if producer_obj.description %}{{ producer_obj.description }}{% endif %}. Référent⋅e : <a href="mailto:{{ producer_obj.referent }}">{{ producer_obj.referent_name }}</a> / {{ producer_obj.referent_tel }}</h5>
|
||||||
<table class="delivery">
|
<table class="delivery">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -23,8 +26,8 @@
|
||||||
<th class="packing">Conditionnement</th>
|
<th class="packing">Conditionnement</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if edit_mode %}<th>Éditer</th>{% endif %}
|
{% if edit_mode %}<th>Éditer</th>{% endif %}
|
||||||
{% if not list_only %}
|
|
||||||
<th class="amount">Total</th>
|
<th class="amount">Total</th>
|
||||||
|
{% if not list_only %}
|
||||||
{% for orderer, order in delivery.orders.items() %}
|
{% for orderer, order in delivery.orders.items() %}
|
||||||
{% set orderer_name = request.groups.groups[orderer].name %}
|
{% set orderer_name = request.groups.groups[orderer].name %}
|
||||||
<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 %}">
|
||||||
|
@ -47,20 +50,19 @@
|
||||||
<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 edit_mode %}<td><a href="/livraison/{{ delivery.id }}/{{ product.producer }}/{{ product.ref }}/éditer">modifier</a></td>{% endif %}
|
{% if edit_mode %}<td><a href="/livraison/{{ delivery.id }}/{{ product.producer }}/{{ product.ref }}/éditer">modifier</a></td>{% 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) }})
|
||||||
{% if request.user.is_staff %}<a href="/livraison/{{ delivery.id }}/ajuster/{{ product.ref }}" class="button" title="ajuster le produit">ajuster</a>{% endif %}
|
{% if request.user.is_staff %}<a href="/livraison/{{ delivery.id }}/ajuster/{{ product.ref }}" class="button" title="ajuster le produit">ajuster</a>{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</th>
|
</th>
|
||||||
|
{% if not list_only %}
|
||||||
{% 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 %}
|
{% 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 %}
|
||||||
|
@ -70,11 +72,12 @@
|
||||||
<td>—</td>
|
<td>—</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<th class="total">{{ delivery.total_for_producer(producer) }} €</th>
|
<th class="total">{{ delivery.total_for_producer(producer) }} €</th>
|
||||||
{% for email, order in delivery.orders.items() %}
|
{% if not list_only %}
|
||||||
<td>{{ order.total(delivery.get_products_by(producer)) }} €</td>
|
{% for email, order in delivery.orders.items() %}
|
||||||
{% endfor %}
|
<td>{{ order.total(delivery.get_products_by(producer)) }} €</td>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% endfor %}
|
{% endfor %}
|
|
@ -1,8 +1,9 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<h3>{{ delivery.name }}</h3>
|
<p class="hide-on-print">
|
||||||
{% 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 %}
|
<a href="/livraison/{{ delivery.id }}/producteurices.pdf{% if referent %}?referent={{ referent }}{% endif %}">Télécharger la liste des commandes en PDF</a>
|
||||||
|
{% if referent %}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>{% endif %}
|
||||||
|
</p>
|
||||||
<article class="delivery">
|
<article class="delivery">
|
||||||
{% include "includes/delivery_table.html" %}
|
{% include "includes/delivery_table.html" %}
|
||||||
</article>
|
</article>
|
||||||
|
|
|
@ -12,7 +12,7 @@ Bonjour,
|
||||||
Et voilà, les commandes maintenant terminées, il est maintenant temps de passer à l'action !
|
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.
|
En pièce-jointe, les informations pour les producteurs⋅rices dont tu est référent⋅e.
|
||||||
|
|
||||||
Tu peux aussi retrouver le doc à cette URL : https://{{ request.host }}/livraison/{{ delivery.id }}/bon-de-commande-referent⋅e
|
Tu peux aussi retrouver le doc à cette URL : https://{{ request.host }}/livraison/{{ delivery.id }}/producteurices
|
||||||
|
|
||||||
Rendez-vous pour la distribution, le {{ delivery.from_date|date }} de {{ delivery.from_date|time }} à {{ delivery.to_date|time }} à {{ delivery.where }}.
|
Rendez-vous pour la distribution, le {{ delivery.from_date|date }} de {{ delivery.from_date|time }} à {{ delivery.to_date|time }} à {{ delivery.where }}.
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,8 @@
|
||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html>
|
{% block body %}
|
||||||
<head>
|
|
||||||
<title>{% if title %}{{ title }} - {% endif %}{{ config.SITE_NAME }}</title>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<link rel="stylesheet" type="text/css" href="/static/signing-sheet.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<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>{{ request.groups.groups[email].name }}</h3>
|
<h3>{{ request.groups.groups[email].name }}</h3>
|
||||||
{% include "includes/order_summary.html" %}
|
{% include "includes/order_summary.html" %}
|
||||||
<hr>
|
<hr class="page-break">
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</body>
|
{% endblock %}
|
|
@ -22,3 +22,8 @@ def read_token(token):
|
||||||
return jwt.decode(token, config.SECRET, algorithms=[config.JWT_ALGORITHM])
|
return jwt.decode(token, config.SECRET, algorithms=[config.JWT_ALGORITHM])
|
||||||
except (jwt.DecodeError, jwt.ExpiredSignatureError):
|
except (jwt.DecodeError, jwt.ExpiredSignatureError):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def prefix(string, delivery):
|
||||||
|
date = delivery.to_date.strftime("%Y-%m-%d")
|
||||||
|
return f"{config.SITE_NAME}-{date}-{string}"
|
||||||
|
|
Loading…
Reference in a new issue