mirror of
https://github.com/almet/copanier.git
synced 2025-04-28 11:32:38 +02:00
353 lines
12 KiB
Python
353 lines
12 KiB
Python
from collections import defaultdict
|
|
from functools import partial
|
|
from roll import HttpError
|
|
|
|
from debts.solver import order_balance, check_balance, reduce_balance
|
|
|
|
from .core import app, session, env
|
|
from ..models import Delivery, Person, Order, ProductOrder, Groups, SavedConfiguration
|
|
from .. import utils, reports, emails, config
|
|
|
|
|
|
@app.listen("startup")
|
|
async def on_startup():
|
|
Delivery.init_fs()
|
|
|
|
|
|
@app.route("/", methods=["GET"])
|
|
async def home(request, response):
|
|
if not Delivery.is_defined() and not Groups.is_defined():
|
|
response.redirect = app.url_for("onboarding")
|
|
return
|
|
if not request["user"].group_id:
|
|
response.redirect = app.url_for("groups")
|
|
return
|
|
response.html(
|
|
"delivery/list_deliveries.html",
|
|
incoming=Delivery.incoming(),
|
|
former=Delivery.former(),
|
|
)
|
|
|
|
|
|
@app.route("/distribution", methods=["GET"])
|
|
async def new_delivery(request, response):
|
|
response.html("delivery/edit_delivery.html", delivery={})
|
|
|
|
|
|
@app.route("/distribution", methods=["POST"])
|
|
async def create_delivery(request, response):
|
|
form = request.form
|
|
data = {}
|
|
data["from_date"] = f"{form.get('date')} {form.get('from_time')}"
|
|
data["to_date"] = f"{form.get('date')} {form.get('to_time')}"
|
|
for name in Delivery.__dataclass_fields__.keys():
|
|
if name in form:
|
|
data[name] = form.get(name)
|
|
delivery = Delivery(**data)
|
|
delivery.persist()
|
|
response.message("La distribution a bien été créée!")
|
|
response.redirect = f"/distribution/{delivery.id}"
|
|
|
|
|
|
@app.route("/distribution/{id}/{producer}/bon-de-commande.pdf", methods=["GET"])
|
|
async def pdf_for_producer(request, response, id, producer):
|
|
delivery = Delivery.load(id)
|
|
response.pdf(
|
|
"list_products.html",
|
|
{"list_only": True, "delivery": delivery, "producers": [producer]},
|
|
filename=utils.prefix(f"bon-de-commande-{producer}.pdf", delivery),
|
|
)
|
|
|
|
|
|
@app.route("/distribution/{id}/gérer", methods=["GET"])
|
|
async def show_delivery_toolbox(request, response, id):
|
|
delivery = Delivery.load(id)
|
|
response.html(
|
|
"delivery/show_toolbox.html",
|
|
{
|
|
"delivery": delivery,
|
|
"referents": [p.referent for p in delivery.producers.values()],
|
|
},
|
|
)
|
|
|
|
|
|
@app.route("/distribution/{id}/envoi-email-referentes", methods=["GET", "POST"])
|
|
async def send_referent_emails(request, response, id):
|
|
delivery = Delivery.load(id)
|
|
if request.method == "POST":
|
|
email_body = request.form.get("email_body")
|
|
email_subject = request.form.get("email_subject")
|
|
sent_mails = 0
|
|
for referent in delivery.get_referents():
|
|
producers = delivery.get_producers_for_referent(referent)
|
|
attachments = []
|
|
for producer in producers:
|
|
if delivery.producers[producer].has_active_products(delivery):
|
|
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(
|
|
referent,
|
|
email_subject,
|
|
email_body,
|
|
copy=delivery.contact,
|
|
attachments=attachments,
|
|
)
|
|
response.message(f"Un mail à été envoyé aux {sent_mails} référent⋅e⋅s")
|
|
response.redirect = f"/distribution/{id}/gérer"
|
|
|
|
response.html("delivery/prepare_referent_email.html", {"delivery": delivery})
|
|
|
|
|
|
@app.route("/distribution/{id}/exporter", methods=["GET"])
|
|
async def export_products(request, response, id):
|
|
delivery = Delivery.load(id)
|
|
response.xlsx(reports.products(delivery))
|
|
|
|
|
|
@app.route("/distribution/{id}/edit", methods=["GET"])
|
|
async def edit_delivery(request, response, id):
|
|
delivery = Delivery.load(id)
|
|
response.html("delivery/edit_delivery.html", {"delivery": delivery})
|
|
|
|
|
|
@app.route("/distribution/{id}/edit", methods=["POST"])
|
|
async def post_delivery(request, response, id):
|
|
delivery = Delivery.load(id)
|
|
form = request.form
|
|
delivery.from_date = f"{form.get('date')} {form.get('from_time')}"
|
|
delivery.to_date = f"{form.get('date')} {form.get('to_time')}"
|
|
for name in Delivery.__dataclass_fields__.keys():
|
|
if name in form:
|
|
setattr(delivery, name, form.get(name))
|
|
delivery.persist()
|
|
response.message("La distribution a bien été mise à jour!")
|
|
response.redirect = f"/distribution/{delivery.id}"
|
|
|
|
|
|
@app.route("/distribution/{id}", methods=["GET"])
|
|
async def show_delivery(request, response, id):
|
|
delivery = Delivery.load(id)
|
|
response.html("delivery/show_delivery.html", {"delivery": delivery})
|
|
|
|
|
|
@app.route("/distribution/{id}/commander", methods=["POST", "GET"])
|
|
async def place_order(request, response, id):
|
|
delivery = Delivery.load(id)
|
|
# email = request.query.get("email", None)
|
|
user = session.user.get(None)
|
|
orderer = request.query.get("orderer", None)
|
|
if orderer:
|
|
orderer = Person(email=orderer, group_id=orderer)
|
|
|
|
delivery_url = f"/distribution/{delivery.id}"
|
|
if not orderer and user:
|
|
orderer = user
|
|
|
|
if not orderer:
|
|
response.message("Impossible de comprendre pour qui passer commande…", "error")
|
|
response.redirect = delivery_url
|
|
return
|
|
|
|
if request.method == "POST":
|
|
# When the delivery is closed, only staff can access.
|
|
if delivery.status == delivery.CLOSED and not (user and user.is_staff):
|
|
response.message("La distribution est fermée", "error")
|
|
response.redirect = delivery_url
|
|
return
|
|
|
|
form = request.form
|
|
order = Order(phone_number=form.get("phone_number", ""))
|
|
for product in delivery.products:
|
|
try:
|
|
wanted = form.int(f"wanted:{product.ref}", 0)
|
|
except HttpError:
|
|
continue
|
|
try:
|
|
adjustment = form.int(f"adjustment:{product.ref}", 0)
|
|
except HttpError:
|
|
adjustment = 0
|
|
if wanted or adjustment:
|
|
order.products[product.ref] = ProductOrder(
|
|
wanted=wanted, adjustment=adjustment
|
|
)
|
|
|
|
if not delivery.orders:
|
|
delivery.orders = {}
|
|
|
|
if not order.products:
|
|
if orderer.id in delivery.orders:
|
|
del delivery.orders[orderer.id]
|
|
delivery.persist()
|
|
response.message("La commande est vide.", status="warning")
|
|
response.redirect = delivery_url
|
|
return
|
|
delivery.orders[orderer.id] = order
|
|
delivery.persist()
|
|
|
|
if user and orderer.id == user.id:
|
|
# Send the emails to everyone in the group.
|
|
groups = request["groups"].groups
|
|
if orderer.group_id in groups.keys():
|
|
for email in groups[orderer.group_id].members:
|
|
emails.send_order(
|
|
request,
|
|
env,
|
|
person=Person(email=email),
|
|
delivery=delivery,
|
|
order=order,
|
|
group_id=orderer.group_id,
|
|
url_for=app.url_for,
|
|
)
|
|
else:
|
|
emails.send_order(
|
|
request,
|
|
env,
|
|
person=Person(email=orderer.email),
|
|
delivery=delivery,
|
|
order=order,
|
|
group_id=orderer.email,
|
|
url_for=app.url_for,
|
|
)
|
|
response.message(
|
|
f"La commande pour « {orderer.name} » a bien été prise en compte, "
|
|
"on a envoyé un récap par email 😘"
|
|
)
|
|
response.redirect = f"/distribution/{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(
|
|
"delivery/place_order.html",
|
|
delivery=delivery,
|
|
person=orderer,
|
|
order=order,
|
|
force_adjustment=force_adjustment,
|
|
)
|
|
|
|
|
|
@app.route("/distribution/{id}/résumé-de-commandes", methods=["GET"])
|
|
async def show_orders_summary(request, response, id):
|
|
delivery = Delivery.load(id)
|
|
response.pdf(
|
|
"delivery/show_orders_summary.html",
|
|
{"delivery": delivery},
|
|
css="order-summary.css",
|
|
filename=utils.prefix("résumé-de-commandes.pdf", delivery),
|
|
)
|
|
|
|
@app.route("/distribution/{id}/résumé-de-commandes.html", methods=["GET"])
|
|
async def show_orders_summary(request, response, id):
|
|
delivery = Delivery.load(id)
|
|
response.html(
|
|
"delivery/show_orders_summary.html",
|
|
delivery=delivery,
|
|
display_prices=True
|
|
)
|
|
|
|
@app.route("/distribution/{id}/rapport-complet.xlsx", methods=["GET"])
|
|
async def generate_report(request, response, id):
|
|
delivery = Delivery.load(id)
|
|
date = delivery.to_date.strftime("%Y-%m-%d")
|
|
response.xlsx(
|
|
reports.full(delivery),
|
|
filename=f"{config.SITE_NAME}-{date}-rapport-complet.xlsx",
|
|
)
|
|
|
|
|
|
@app.route("/distribution/{id}/ajuster/{ref}", methods=["GET", "POST"])
|
|
async def adjust_product(request, response, id, ref):
|
|
delivery = Delivery.load(id)
|
|
delivery_url = f"/distribution/{delivery.id}"
|
|
product = None
|
|
for product in delivery.products:
|
|
if product.ref == ref:
|
|
break
|
|
else:
|
|
response.message(f"Référence inconnue: {ref}")
|
|
response.redirect = delivery_url
|
|
return
|
|
if request.method == "POST":
|
|
form = request.form
|
|
for email, order in delivery.orders.items():
|
|
choice = order[product]
|
|
choice.adjustment = form.int(email, 0)
|
|
order[product] = choice
|
|
delivery.persist()
|
|
response.message(f"Le produit «{product.ref}» a bien été ajusté!")
|
|
response.redirect = delivery_url
|
|
else:
|
|
response.html(
|
|
"delivery/adjust_product.html", {"delivery": delivery, "product": product}
|
|
)
|
|
|
|
|
|
@app.route("/distribution/{id}/paiements", methods=["GET"])
|
|
async def compute_payments(request, response, id):
|
|
delivery = Delivery.load(id)
|
|
groups = request["groups"]
|
|
|
|
balance = []
|
|
for group_id, order in delivery.orders.items():
|
|
balance.append(
|
|
(group_id, order.total(delivery.products, delivery, group_id) * -1)
|
|
)
|
|
|
|
producer_groups = {}
|
|
|
|
for producer in delivery.producers.values():
|
|
group = groups.get_user_group(producer.referent)
|
|
# When a group contains multiple producer contacts,
|
|
# the first one is elected to receive the money,
|
|
# and all the other ones are separated in the table.
|
|
group_id = None
|
|
if hasattr(group, "id"):
|
|
if (
|
|
group.id not in producer_groups
|
|
or producer_groups[group.id] == producer.referent_name
|
|
):
|
|
producer_groups[group.id] = producer.referent_name
|
|
group_id = group.id
|
|
if not group_id:
|
|
group_id = producer.referent_name
|
|
|
|
amount = delivery.total_for_producer(producer.id)
|
|
print(producer.id, amount)
|
|
if amount:
|
|
balance.append((group_id, amount))
|
|
|
|
debiters, crediters = order_balance(balance)
|
|
results = reduce_balance(debiters[:], crediters[:])
|
|
|
|
results_dict = defaultdict(partial(defaultdict, float))
|
|
|
|
for debiter, amount, crediter in results:
|
|
results_dict[debiter][crediter] = amount
|
|
|
|
template_name = "delivery/compute_balance.html"
|
|
template_args = {
|
|
"delivery": delivery,
|
|
"debiters": debiters,
|
|
"crediters": crediters,
|
|
"results": results_dict,
|
|
"debiters_groups": groups.groups,
|
|
"crediters_groups": producer_groups,
|
|
}
|
|
|
|
response.html(template_name, template_args)
|