mirror of
https://github.com/almet/copanier.git
synced 2025-04-28 19:42:37 +02:00
Handle multiple producers per delivery
This commit is contained in:
parent
04d5fca259
commit
6abe77de05
9 changed files with 80 additions and 16 deletions
|
@ -37,7 +37,7 @@ def send_order(request, env, person, delivery, order):
|
||||||
env,
|
env,
|
||||||
"order_summary",
|
"order_summary",
|
||||||
person.email,
|
person.email,
|
||||||
f"{config.SITE_NAME} : résumé de la commande {delivery.producer}",
|
f"{config.SITE_NAME} : résumé de la commande {delivery.name}",
|
||||||
order=order,
|
order=order,
|
||||||
delivery=delivery,
|
delivery=delivery,
|
||||||
request=request
|
request=request
|
||||||
|
|
|
@ -24,7 +24,10 @@ def products_from_xlsx(delivery, data):
|
||||||
delivery.products = []
|
delivery.products = []
|
||||||
for row in rows[1:]:
|
for row in rows[1:]:
|
||||||
raw = {k: v for k, v in dict(zip(headers, row)).items() if v}
|
raw = {k: v for k, v in dict(zip(headers, row)).items() if v}
|
||||||
delivery.products.append(Product(**raw))
|
try:
|
||||||
|
delivery.products.append(Product(**raw))
|
||||||
|
except TypeError as e:
|
||||||
|
raise ValueError(f"Erreur durant l'importation de {raw['ref']}")
|
||||||
delivery.persist()
|
delivery.persist()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -96,6 +96,7 @@ class Product(Base):
|
||||||
url: str = ""
|
url: str = ""
|
||||||
img: str = ""
|
img: str = ""
|
||||||
packing: int = None
|
packing: int = None
|
||||||
|
producer: str = ""
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
out = self.name
|
out = self.name
|
||||||
|
@ -133,9 +134,13 @@ class Order(Base):
|
||||||
yield from self.products.items()
|
yield from self.products.items()
|
||||||
|
|
||||||
def total(self, products):
|
def total(self, products):
|
||||||
|
def _get_price(ref):
|
||||||
|
product = products.get(ref)
|
||||||
|
return product.price if product else 0
|
||||||
|
|
||||||
products = {p.ref: p for p in products}
|
products = {p.ref: p for p in products}
|
||||||
return round(
|
return round(
|
||||||
sum(p.quantity * products[ref].price for ref, p in self.products.items()), 2
|
sum(p.quantity * _get_price(ref) for ref, p in self.products.items()), 2
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -153,7 +158,7 @@ class Delivery(Base):
|
||||||
ADJUSTMENT = 2
|
ADJUSTMENT = 2
|
||||||
ARCHIVED = 3
|
ARCHIVED = 3
|
||||||
|
|
||||||
producer: str
|
name: str
|
||||||
from_date: datetime_field
|
from_date: datetime_field
|
||||||
to_date: datetime_field
|
to_date: datetime_field
|
||||||
order_before: datetime_field
|
order_before: datetime_field
|
||||||
|
@ -183,6 +188,10 @@ class Delivery(Base):
|
||||||
def total(self):
|
def total(self):
|
||||||
return round(sum(o.total(self.products) for o in self.orders.values()), 2)
|
return round(sum(o.total(self.products) for o in self.orders.values()), 2)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def has_multiple_producers(self):
|
||||||
|
return len(set([p.producer for p in self.products])) > 1
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_open(self):
|
def is_open(self):
|
||||||
return datetime.now().date() <= self.order_before.date()
|
return datetime.now().date() <= self.order_before.date()
|
||||||
|
@ -288,3 +297,12 @@ class Delivery(Base):
|
||||||
|
|
||||||
def has_order(self, person):
|
def has_order(self, person):
|
||||||
return person.email in self.orders
|
return person.email in self.orders
|
||||||
|
|
||||||
|
def get_products_by(self, producer):
|
||||||
|
return [p for p in self.products if p.producer == producer]
|
||||||
|
|
||||||
|
def total_for_producer(self, producer, person=None):
|
||||||
|
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)
|
||||||
|
|
|
@ -41,10 +41,10 @@ def full(delivery):
|
||||||
wb = Workbook()
|
wb = Workbook()
|
||||||
ws = wb.active
|
ws = wb.active
|
||||||
ws.title = f"{delivery.producer} {delivery.from_date.date()}"
|
ws.title = f"{delivery.producer} {delivery.from_date.date()}"
|
||||||
headers = ["ref", "produit", "prix"] + [e for e in delivery.orders] + ["total"]
|
headers = ["ref", "produit", "prix", "producer"] + [e for e in delivery.orders] + ["total"]
|
||||||
ws.append(headers)
|
ws.append(headers)
|
||||||
for product in delivery.products:
|
for product in delivery.products:
|
||||||
row = [product.ref, str(product), product.price]
|
row = [product.ref, str(product), product.price, product.producer]
|
||||||
for order in delivery.orders.values():
|
for order in delivery.orders.values():
|
||||||
wanted = order.products.get(product.ref)
|
wanted = order.products.get(product.ref)
|
||||||
row.append(wanted.quantity if wanted else 0)
|
row.append(wanted.quantity if wanted else 0)
|
||||||
|
|
|
@ -350,6 +350,7 @@ td.total,
|
||||||
th.total {
|
th.total {
|
||||||
background-color: #bbb;
|
background-color: #bbb;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr:nth-child(even) {
|
tr:nth-child(even) {
|
||||||
background-color: #ddd;
|
background-color: #ddd;
|
||||||
}
|
}
|
||||||
|
@ -362,6 +363,15 @@ thead tr * {
|
||||||
thead th + th {
|
thead th + th {
|
||||||
border-left: 1px solid white;
|
border-left: 1px solid white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tr .producer {
|
||||||
|
border-bottom: 1px solid #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.subtotal {
|
||||||
|
background-color: #bbb;
|
||||||
|
}
|
||||||
|
|
||||||
article.order {
|
article.order {
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
<table class="delivery">
|
<table class="delivery">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
{% if delivery.has_multiple_producers %}
|
||||||
|
<th class="producer">Producteur</th>
|
||||||
|
{% endif %}
|
||||||
<th class="product">Produit</th>
|
<th class="product">Produit</th>
|
||||||
<th class="price">Prix</th>
|
<th class="price">Prix</th>
|
||||||
{% if delivery.has_packing %}
|
{% if delivery.has_packing %}
|
||||||
|
@ -27,6 +30,9 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for product in delivery.products %}
|
{% for product in delivery.products %}
|
||||||
<tr>
|
<tr>
|
||||||
|
{% if delivery.has_multiple_producers and (loop.first or loop.previtem.producer != product.producer) %}
|
||||||
|
<th class="producer" rowspan="{{ delivery.get_products_by(product.producer) | length }}">{{ product.producer }}</th>
|
||||||
|
{% endif %}
|
||||||
<th class="product">{{ product }}</th>
|
<th class="product">{{ product }}</th>
|
||||||
<td>{{ product.price | round(2) }} €</td>
|
<td>{{ product.price | round(2) }} €</td>
|
||||||
{% if delivery.has_packing %}
|
{% if delivery.has_packing %}
|
||||||
|
@ -35,22 +41,39 @@
|
||||||
<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>
|
||||||
{% 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 %}
|
||||||
</tr>
|
</tr>
|
||||||
|
{% if delivery.has_multiple_producers and (loop.last or loop.nextitem.producer != product.producer) %}
|
||||||
|
<tr class="subtotal">
|
||||||
|
<td>—</td>
|
||||||
|
<th>Sous Total {{ product.producer }}</th>
|
||||||
|
<td>—</td>
|
||||||
|
{% if delivery.has_packing %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
<th>{{ delivery.total_for_producer(product.producer) }} €</th>
|
||||||
|
{% for email in delivery.orders.keys() %}
|
||||||
|
<th>{{ delivery.total_for_producer(product.producer, email) }} €</th>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<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_multiple_producers %}
|
||||||
|
<td>—</td>
|
||||||
|
{% endif %}
|
||||||
{% if delivery.has_packing %}
|
{% if delivery.has_packing %}
|
||||||
<td>—</td>
|
<td>—</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<th class="total">{{ delivery.total }} €</th>
|
<th class="total">{{ delivery.total }} €</th>
|
||||||
{% for email, order in delivery.orders.items() %}
|
{% for email, order in delivery.orders.items() %}
|
||||||
<td>{{ order.total(delivery.products) }} €</td>
|
<td class="total">{{ order.total(delivery.products) }} €</td>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -8,11 +8,11 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<label>
|
<label>
|
||||||
<p>Producteur</p>
|
<p>Nom</p>
|
||||||
<input type="text" name="producer" value="{{ delivery.producer or '' }}" required>
|
<input type="text" name="name" value="{{ delivery.name or '' }}">
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<p>Description des produits</p>
|
<h5>Description des produits</h5>
|
||||||
<input type="text" name="description" value="{{ delivery.description or '' }}">
|
<input type="text" name="description" value="{{ delivery.description or '' }}">
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
<dd>Prix d'une unité, en euros.</dd>
|
<dd>Prix d'une unité, en euros.</dd>
|
||||||
<dt>unit</dt>
|
<dt>unit</dt>
|
||||||
<dd>Conditionnement d'une unité: 1kg, 33cl…</dd>
|
<dd>Conditionnement d'une unité: 1kg, 33cl…</dd>
|
||||||
|
<dt>producer</dt>
|
||||||
|
<dd>Le nom du producteur</dd>
|
||||||
<dt>description</dt>
|
<dt>description</dt>
|
||||||
<dd>Plus de détails sur le produit.</dd>
|
<dd>Plus de détails sur le produit.</dd>
|
||||||
<dt>packing</dt>
|
<dt>packing</dt>
|
||||||
|
|
|
@ -2,38 +2,46 @@
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<article class="order">
|
<article class="order">
|
||||||
<h3><a href="/livraison/{{ delivery.id }}">{{ delivery.producer }}</a> — Commande de «{{ person.email }}»</h3>
|
<h3>{% if delivery.producer %}<a href="/livraison/{{ delivery.id }}">{{ delivery.producer }}</a> — {% endif %}Commande de « {{ person.email }} »</h3>
|
||||||
{% include "includes/delivery_head.html" %}
|
{% include "includes/delivery_head.html" %}
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<table class="order">
|
<table class="order">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
{% if delivery.has_multiple_producers %}
|
||||||
|
<th class="producer">Producteur</th>
|
||||||
|
{% endif %}
|
||||||
<th class="product">Produit</th>
|
<th class="product">Produit</th>
|
||||||
<th class="price">Prix</th>
|
<th class="price">Prix</th>
|
||||||
{% if delivery.has_packing %}
|
{% if delivery.has_packing %}
|
||||||
<th class="packing">Conditionnement</th>
|
<th class="packing">Conditionnement</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<th class="amount">Commande</th>
|
<th class="amount">Commande</th>
|
||||||
{% if delivery.status == delivery.ADJUSTMENT or order.has_adjustments %}<th class="amount">Ajustement +/−</th>{% endif %}
|
{% if delivery.status == delivery.ADJUSTMENT or order.has_adjustments %}
|
||||||
|
<th class="amount">Ajustement +/−</th>
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for product in delivery.products %}
|
{% for product in delivery.products %}
|
||||||
<tr>
|
<tr>
|
||||||
|
{% if delivery.has_multiple_producers and (loop.first or loop.previtem.producer != product.producer) %}
|
||||||
|
<th class="producer" rowspan="{{ delivery.get_products_by(product.producer) | length }}">{{ product.producer }}</th>
|
||||||
|
{% endif %}
|
||||||
<th class="product">{{ product }}
|
<th class="product">{{ product }}
|
||||||
{% if product.description or product.img %}
|
{% if product.description or product.img %}
|
||||||
{% with unique_id=loop.index %}
|
{% with unique_id=loop.index %}
|
||||||
{% include "includes/modal_product.html" %}
|
{% include "includes/modal_product.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% endif %}</p>
|
{% endif %}
|
||||||
</th>
|
</th>
|
||||||
<td>{{ product.price | round(2) }} €</td>
|
<td>{{ product.price | round(2) }} €</td>
|
||||||
{% if delivery.has_packing %}
|
{% if delivery.has_packing %}
|
||||||
<td{% if delivery.status == delivery.ADJUSTMENT and delivery.product_missing(product) %} class="missing" title="Les commandes individuelles ne correspondent pas aux conditionnements"{% endif %}>{{ product.packing or "—" }}{% if delivery.status == delivery.ADJUSTMENT and delivery.product_missing(product) %} (−{{ delivery.product_missing(product) }}){% endif %}</td>
|
<td {% if delivery.status == delivery.ADJUSTMENT and delivery.product_missing(product) %} class="missing" title="Les commandes individuelles ne correspondent pas aux conditionnements"{% endif %}>{{ product.packing or "—" }}{% if delivery.status == delivery.ADJUSTMENT and delivery.product_missing(product) %} (−{{ delivery.product_missing(product) }}){% endif %}</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td class="with-input"><input {% if delivery.status != delivery.OPEN %}type="text" readonly{% else%}type="number"{% endif%} min=0 name="wanted:{{ product.ref }}" value="{{ order[product].wanted }}"></td>
|
<td class="with-input"><input {% if delivery.status != delivery.OPEN %}type="text" readonly{% else%}type="number"{% endif%} min=0 name="wanted:{{ product.ref }}" value="{{ order[product].wanted }}"></td>
|
||||||
{% if delivery.status == delivery.ADJUSTMENT or order.has_adjustments %}
|
{% if delivery.status == delivery.ADJUSTMENT or order.has_adjustments %}
|
||||||
<td class="with-input"><input type="number" name="adjustment:{{ product.ref }}" value="{{ order[product].adjustment }}" {% if not delivery.product_missing(product) %}readonly{% endif %}></td>
|
<td class="with-input"><input type="number" name="adjustment:{{ product.ref }}" value="{{ order[product].adjustment }}" {% if not delivery.product_missing(product) %}readonly{% endif %}></td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
Loading…
Reference in a new issue