mirror of
https://github.com/almet/copanier.git
synced 2025-04-28 19:42:37 +02:00
Allow to define ProductOrder.ajustment when product needs adjustment
This commit is contained in:
parent
165427bf8f
commit
425222a0a2
16 changed files with 219 additions and 48 deletions
|
@ -80,7 +80,7 @@ async def auth_required(request, response):
|
|||
# Should be handler Roll side?
|
||||
# In dev mode, we serve the static, but we don't have yet a way to mark static
|
||||
# route as unprotected.
|
||||
if request.path.startswith('/static/'):
|
||||
if request.path.startswith("/static/"):
|
||||
return
|
||||
if request.route.payload and not request.route.payload.get("unprotected"):
|
||||
token = request.cookies.get("token")
|
||||
|
@ -198,7 +198,7 @@ async def import_products(request, response, id):
|
|||
response.message(err, status="error")
|
||||
response.redirect = path
|
||||
return
|
||||
elif data.filename.endswith('.xlsx'):
|
||||
elif data.filename.endswith(".xlsx"):
|
||||
try:
|
||||
imports.products_from_xlsx(delivery, data)
|
||||
except ValueError as err:
|
||||
|
@ -258,15 +258,25 @@ async def place_order(request, response, id):
|
|||
response.redirect = delivery_url
|
||||
return
|
||||
if request.method == "POST":
|
||||
if not (user and user.is_staff) and delivery.status == delivery.CLOSED:
|
||||
response.message("La livraison est fermée", "error")
|
||||
response.redirect = delivery_url
|
||||
return
|
||||
form = request.form
|
||||
order = Order(paid=form.bool("paid", False))
|
||||
for product in delivery.products:
|
||||
try:
|
||||
quantity = form.int(product.ref, 0)
|
||||
wanted = form.int(f"wanted:{product.ref}", 0)
|
||||
except HttpError:
|
||||
continue
|
||||
if quantity:
|
||||
order.products[product.ref] = ProductOrder(wanted=quantity)
|
||||
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:
|
||||
|
|
|
@ -33,7 +33,6 @@ def price_field(value):
|
|||
|
||||
@dataclass
|
||||
class Base:
|
||||
|
||||
@classmethod
|
||||
def create(cls, data=None, **kwargs):
|
||||
if isinstance(data, Base):
|
||||
|
@ -109,7 +108,11 @@ class Product(Base):
|
|||
@dataclass
|
||||
class ProductOrder(Base):
|
||||
wanted: int
|
||||
ordered: int = 0
|
||||
adjustment: int = 0
|
||||
|
||||
@property
|
||||
def quantity(self):
|
||||
return self.wanted + self.adjustment
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -117,14 +120,15 @@ class Order(Base):
|
|||
products: Dict[str, ProductOrder] = field(default_factory=dict)
|
||||
paid: bool = False
|
||||
|
||||
def get_quantity(self, product):
|
||||
choice = self.products.get(product.ref)
|
||||
return choice.wanted if choice else 0
|
||||
def __getitem__(self, ref):
|
||||
if isinstance(ref, Product):
|
||||
ref = ref.ref
|
||||
return self.products.get(ref, ProductOrder(wanted=0))
|
||||
|
||||
def total(self, products):
|
||||
products = {p.ref: p for p in products}
|
||||
return round(
|
||||
sum(p.wanted * products[ref].price for ref, p in self.products.items()), 2
|
||||
sum(p.quantity * products[ref].price for ref, p in self.products.items()), 2
|
||||
)
|
||||
|
||||
|
||||
|
@ -133,6 +137,9 @@ class Delivery(Base):
|
|||
|
||||
__root__ = "delivery"
|
||||
__lock__ = threading.Lock()
|
||||
CLOSED = 0
|
||||
OPEN = 1
|
||||
ADJUSTMENT = 2
|
||||
|
||||
producer: str
|
||||
from_date: datetime_field
|
||||
|
@ -145,6 +152,14 @@ class Delivery(Base):
|
|||
orders: Dict[str, Order] = field(default_factory=dict)
|
||||
id: str = field(default_factory=lambda *a, **k: uuid.uuid4().hex)
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
if self.is_open:
|
||||
return self.OPEN
|
||||
if self.needs_adjustment:
|
||||
return self.ADJUSTMENT
|
||||
return self.CLOSED
|
||||
|
||||
@property
|
||||
def total(self):
|
||||
return round(sum(o.total(self.products) for o in self.orders.values()), 2)
|
||||
|
@ -165,6 +180,10 @@ class Delivery(Base):
|
|||
def has_packing(self):
|
||||
return any(p.packing for p in self.products)
|
||||
|
||||
@property
|
||||
def needs_adjustment(self):
|
||||
return self.has_packing and any(self.product_missing(p) for p in self.products)
|
||||
|
||||
@classmethod
|
||||
def init_fs(cls):
|
||||
cls.get_root().mkdir(parents=True, exist_ok=True)
|
||||
|
@ -202,7 +221,7 @@ class Delivery(Base):
|
|||
total = 0
|
||||
for order in self.orders.values():
|
||||
if product.ref in order.products:
|
||||
total += order.products[product.ref].wanted
|
||||
total += order.products[product.ref].quantity
|
||||
return total
|
||||
|
||||
def product_missing(self, product):
|
||||
|
@ -211,3 +230,6 @@ class Delivery(Base):
|
|||
wanted = self.product_wanted(product)
|
||||
orphan = wanted % product.packing
|
||||
return product.packing - orphan if orphan else 0
|
||||
|
||||
def has_order(self, person):
|
||||
return person.email in self.orders
|
||||
|
|
|
@ -10,7 +10,15 @@ def summary(delivery):
|
|||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = f"{delivery.producer} {delivery.from_date.date()}"
|
||||
ws.append(["ref", "produit", "prix", "unités", "total"])
|
||||
headers = [
|
||||
"ref",
|
||||
"produit",
|
||||
"prix unitaire",
|
||||
"quantité commandée",
|
||||
"unité",
|
||||
"total",
|
||||
]
|
||||
ws.append(headers)
|
||||
for product in delivery.products:
|
||||
wanted = delivery.product_wanted(product)
|
||||
ws.append(
|
||||
|
@ -19,10 +27,11 @@ def summary(delivery):
|
|||
product.label,
|
||||
product.price,
|
||||
wanted,
|
||||
product.unit,
|
||||
round(product.price * wanted, 2),
|
||||
]
|
||||
)
|
||||
ws.append(["", "", "", "Total", delivery.total])
|
||||
ws.append(["", "", "", "", "Total", delivery.total])
|
||||
return save_virtual_workbook(wb)
|
||||
|
||||
|
||||
|
@ -36,12 +45,14 @@ def full(delivery):
|
|||
row = [product.ref, product.label, product.price]
|
||||
for order in delivery.orders.values():
|
||||
wanted = order.products.get(product.ref)
|
||||
row.append(wanted.wanted if wanted else 0)
|
||||
row.append(wanted.quantity if wanted else 0)
|
||||
row.append(delivery.product_wanted(product))
|
||||
ws.append(row)
|
||||
footer = ["Total", "", ""] + [
|
||||
o.total(delivery.products) for o in delivery.orders.values()
|
||||
] + [delivery.total]
|
||||
footer = (
|
||||
["Total", "", ""]
|
||||
+ [o.total(delivery.products) for o in delivery.orders.values()]
|
||||
+ [delivery.total]
|
||||
)
|
||||
ws.append(footer)
|
||||
return save_virtual_workbook(wb)
|
||||
|
||||
|
|
|
@ -236,7 +236,6 @@ textarea {
|
|||
line-height: 1rem;
|
||||
background-color: #fff;
|
||||
border: .05rem solid #bbc;
|
||||
border-radius: .1rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
@ -276,6 +275,9 @@ input:focus,
|
|||
select:focus {
|
||||
box-shadow: 0 0 .1rem var(--primary-color);
|
||||
}
|
||||
input[readonly] {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
|
@ -355,6 +357,7 @@ td.with-input {
|
|||
}
|
||||
td.with-input input {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
article.delivery {
|
||||
width: 100%;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block body %}
|
||||
<h3>{{ delivery.producer }} {% if delivery.is_open %}<a class="button" href="/livraison/{{ delivery.id }}/commander">Gérer ma commande</a>{% endif %}</h3>
|
||||
<h3>{{ delivery.producer }} {% include "includes/order_button.html" %}</h3>
|
||||
{% include "includes/delivery_head.html" %}
|
||||
<article class="delivery">
|
||||
<table class="delivery">
|
||||
|
@ -11,7 +11,7 @@
|
|||
<th class="product">Produit</th>
|
||||
<th class="price">Prix</th>
|
||||
{% if delivery.has_packing %}
|
||||
<th class="packing">Lot</th>
|
||||
<th class="packing">Conditionnement</th>
|
||||
{% endif %}
|
||||
<th class="amount">Total</th>
|
||||
{% for email, order in delivery.orders.items() %}
|
||||
|
@ -32,13 +32,9 @@
|
|||
{% if delivery.has_packing %}
|
||||
<td class="packing">{{ product.packing or '—'}}</td>
|
||||
{% endif %}
|
||||
<th{% if delivery.product_missing(product) %} class="missing" title="Les commandes individuelles ne correspondent pas aux lots"{% endif %}>{{ delivery.product_wanted(product) }}{% if delivery.product_missing(product) %} ({{ delivery.product_missing(product) }}){% endif %}</th>
|
||||
<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) }}{% if delivery.status == delivery.ADJUSTMENT and delivery.product_missing(product) %} (−{{ delivery.product_missing(product) }}){% endif %}</th>
|
||||
{% for email, order in delivery.orders.items() %}
|
||||
{% if product.ref in order.products %}
|
||||
<td>{{ order.products[product.ref].wanted }}</td>
|
||||
{% else %}
|
||||
<td>—</td>
|
||||
{% endif %}
|
||||
<td>{{ order[product.ref].quantity or "—" }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
@ -57,7 +53,7 @@
|
|||
<hr>
|
||||
<ul class="toolbox">
|
||||
<li>
|
||||
<a href="/livraison/{{ delivery.id }}/rapport.xlsx"><i class="icon-magnifying-glass"></i> Rapport résumé</a>
|
||||
<a href="/livraison/{{ delivery.id }}/rapport.xlsx"><i class="icon-ribbon"></i> Bon de commande</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/livraison/{{ delivery.id }}/rapport-complet.xlsx"><i class="icon-grid"></i> Rapport complet</a>
|
||||
|
|
|
@ -8,27 +8,27 @@
|
|||
{% endif %}
|
||||
<form method="post">
|
||||
<label>
|
||||
<h5>Producteur</h5>
|
||||
<p>Producteur</p>
|
||||
<input type="text" name="producer" value="{{ delivery.producer or '' }}" required>
|
||||
</label>
|
||||
<label>
|
||||
<h5>Description des produits</h5>
|
||||
<p>Description des produits</p>
|
||||
<input type="text" name="description" value="{{ delivery.description or '' }}" required>
|
||||
</label>
|
||||
<label>
|
||||
<h5>Lieu</h5>
|
||||
<p>Lieu</p>
|
||||
<input type="text" name="where" value="{{ delivery.where or '' }}" required>
|
||||
</label>
|
||||
<label>
|
||||
<h5>Date de livraison</h5>
|
||||
<p>Date de livraison</p>
|
||||
<input type="date" name="date" value="{{ delivery.from_date.date() if delivery.from_date else '' }}" required> de <input type="time" name="from_time" value="{{ delivery.from_date.time() if delivery.from_date else '' }}" required> à <input type="time" name="to_time" value="{{ delivery.to_date.time() if delivery.to_date else '' }}" required>
|
||||
</label>
|
||||
<label>
|
||||
<h5>Date de limite de commande</h5>
|
||||
<p>Date de limite de commande</p>
|
||||
<input type="date" name="order_before" value="{{ delivery.order_before.date() if delivery.order_before else '' }}" required>
|
||||
</label>
|
||||
<label>
|
||||
<h5>Instructions particulières</h5>
|
||||
<p>Instructions particulières</p>
|
||||
<input type="text" name="instructions" value="{{ delivery.instructions or '' }}">
|
||||
</label>
|
||||
<div>
|
||||
|
|
|
@ -5,8 +5,8 @@ Voici le résumé de votre commande «{{ delivery.producer }}»
|
|||
Produit | Prix unitaire | Quantité
|
||||
|
||||
{% for product in delivery.products %}
|
||||
{% if order.get_quantity(product) %}
|
||||
{{ product.name }} | {{ product.price }} € | {{ order.get_quantity(product) }}
|
||||
{% if order[product].quantity %}
|
||||
{{ product.name }} | {{ product.price }} € | {{ order[product].quantity }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
<li><i class="icon-basket"></i> <strong>Produits</strong> {{ delivery.description }}</li>
|
||||
<li><i class="icon-streetsign"></i> <strong>Lieu</strong> {{ delivery.where }}</li>
|
||||
<li><i class="icon-clock"></i> <strong>Date de livraison</strong> <time datetime="{{ delivery.from_date }}">{{ delivery.from_date|date }} de {{ delivery.from_date|time }} à {{ delivery.to_date|time }}</time></li>
|
||||
<li><i class="icon-hourglass"></i> {% if delivery.is_open %}<strong>Date limite de commande</strong> <time datetime="{{ delivery.order_before.date() }}">{{ delivery.order_before|date }}</time>{% else %}<strong>Fermée</strong>{% endif %}</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 }}</time>{% elif delivery.status == delivery.ADJUSTMENT %}<strong>Ajustement en cours</strong>{% else %}<strong>Fermée</strong>{% endif %}</li>
|
||||
{% if delivery.instructions %}<li><i class="icon-lightbulb"></i> <strong>À savoir</strong> {{ delivery.instructions }}</li>{% endif %}
|
||||
</ul>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<ul class="delivery">
|
||||
{% for delivery in deliveries %}
|
||||
<li>
|
||||
<h3><a href="/livraison/{{ delivery.id }}"><i class="icon-hotairballoon"></i> {{ delivery.producer }}</a> {% if delivery.is_open %}<a class="button" href="/livraison/{{ delivery.id }}/commander">{% if request.user.email in delivery.orders %}Gérer ma commande{% else %}Commander{% endif %}</a>{% endif %}</h3>
|
||||
<h3><a href="/livraison/{{ delivery.id }}"><i class="icon-hotairballoon"></i> {{ delivery.producer }}</a> {% include "includes/order_button.html" %}</h3>
|
||||
{% include "includes/delivery_head.html" %}
|
||||
</li>
|
||||
<hr>
|
||||
|
|
9
copanier/templates/includes/order_button.html
Normal file
9
copanier/templates/includes/order_button.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
{% if delivery.status != delivery.CLOSED %}
|
||||
<a class="button" href="/livraison/{{ delivery.id }}/commander">
|
||||
{% if delivery.status == delivery.ADJUSTMENT %}
|
||||
Ajuster ma commande
|
||||
{% elif delivery.status == delivery.OPEN %}
|
||||
{% if request.user.email in delivery.orders %}Gérer ma commande{% else %}Commander{% endif %}
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endif %}
|
|
@ -1,10 +1,10 @@
|
|||
<table class="order">
|
||||
<tr><th class="product">Produit</th><th class="price">Prix unitaire</th><th class="amount">Quantité</th></tr>
|
||||
{% for product in delivery.products %}
|
||||
{% if order.get_quantity(product) %}
|
||||
{% if order[product].quantity %}
|
||||
<tr>
|
||||
<th class="product" style="text-align: left;">{{ product.label }}</th>
|
||||
<td>{{ product.price }} €</td><td>{{ order.get_quantity(product) }}</td>
|
||||
<td>{{ product.price }} €</td><td>{{ order[product].quantity }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
|
|
@ -6,7 +6,14 @@
|
|||
{% include "includes/delivery_head.html" %}
|
||||
<form method="post">
|
||||
<table class="order">
|
||||
<tr><th class="product">Produit</th><th class="price">Prix</th><th class="packing">Lot</th><th class="amount">Quantité</th></tr>
|
||||
<tr>
|
||||
<th class="product">Produit</th>
|
||||
<th class="price">Prix</th>
|
||||
{% if delivery.has_packing %}
|
||||
<th class="packing">Conditionnement</th>
|
||||
{% endif %}
|
||||
<th class="amount">Quantité</th>
|
||||
{% if delivery.status == delivery.ADJUSTMENT %}<th class="amount">Ajustement</th>{% endif %}</tr>
|
||||
{% for product in delivery.products %}
|
||||
<tr>
|
||||
<th class="product">{{ product.label }}
|
||||
|
@ -17,8 +24,14 @@
|
|||
{% endif %}</p>
|
||||
</th>
|
||||
<td>{{ product.price }} €</td>
|
||||
<td{% if delivery.product_missing(product) %} class="missing" title="Les commandes individuelles ne correspondent pas aux lots"{% endif %}>{{ product.packing or "—" }}{% if delivery.product_missing(product) %} (manque {{ delivery.product_missing(product) }}){% endif %}</td>
|
||||
<td class="with-input"><input type="number" name="{{ product.ref }}" value="{{ order.get_quantity(product) }}"></td></tr>
|
||||
{% 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>
|
||||
{% 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>
|
||||
{% if delivery.status == delivery.ADJUSTMENT %}
|
||||
<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 %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<p>Total: {{ order.total(delivery.products) if order else 0 }} €</p>
|
||||
|
|
14
setup.cfg
14
setup.cfg
|
@ -7,13 +7,25 @@ packages = find:
|
|||
include_package_data = True
|
||||
install_requires =
|
||||
Jinja2==2.10
|
||||
minicli==0.4.4
|
||||
openpyxl==2.6.1
|
||||
PyJWT==1.7.1
|
||||
PyYAML==5.1
|
||||
roll==0.10.1
|
||||
ujson==1.35
|
||||
|
||||
[options.extras_require]
|
||||
dev =
|
||||
hupper==1.6.1
|
||||
minicli==0.4.4
|
||||
usine==0.2.2
|
||||
test =
|
||||
pyquery==1.4.0
|
||||
pytest==4.3.1
|
||||
pytest-asyncio==0.10.0
|
||||
prod =
|
||||
gunicorn==19.9.0
|
||||
uvloop==0.12.2
|
||||
|
||||
|
||||
[options.entry_points]
|
||||
console_scripts =
|
||||
|
|
|
@ -36,6 +36,13 @@ def test_delivery_is_open_when_order_before_is_in_the_future(delivery):
|
|||
assert delivery.is_open
|
||||
|
||||
|
||||
def test_delivery_status(delivery):
|
||||
delivery.order_before = now() + timedelta(hours=1)
|
||||
assert delivery.status == delivery.OPEN
|
||||
delivery.order_before = now() - timedelta(days=1)
|
||||
assert delivery.status == delivery.CLOSED
|
||||
|
||||
|
||||
def test_can_create_product():
|
||||
product = Product(name="Lait 1.5L", ref="123", price=1.5)
|
||||
assert product.ref == "123"
|
||||
|
@ -103,3 +110,13 @@ def test_person_is_staff_if_no_staff_in_config(monkeypatch):
|
|||
monkeypatch.setattr(config, 'STAFF', [])
|
||||
person = Person(email="foo@bar.fr")
|
||||
assert person.is_staff
|
||||
|
||||
|
||||
def test_productorder_quantity():
|
||||
choice = ProductOrder(wanted=3)
|
||||
assert choice.wanted == 3
|
||||
assert choice.quantity == 3
|
||||
choice = ProductOrder(wanted=3, adjustment=2)
|
||||
assert choice.quantity == 5
|
||||
choice = ProductOrder(wanted=3, adjustment=-1)
|
||||
assert choice.quantity == 2
|
||||
|
|
26
tests/test_reports.py
Normal file
26
tests/test_reports.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
from io import BytesIO
|
||||
|
||||
from openpyxl import load_workbook
|
||||
|
||||
from copanier import reports
|
||||
from copanier.models import Order, Product, ProductOrder
|
||||
|
||||
|
||||
def test_export_products(client, delivery):
|
||||
delivery.products[0].packing = 6
|
||||
delivery.products.append(
|
||||
Product(ref="456", name="yaourt", price="3.5", packing=4, unit="pot 125ml")
|
||||
)
|
||||
delivery.products.append(Product(ref="789", name="fromage", price="9.2"))
|
||||
delivery.orders["foo@bar.org"] = Order(
|
||||
products={"123": ProductOrder(wanted=1), "456": ProductOrder(wanted=4)}
|
||||
)
|
||||
delivery.persist()
|
||||
wb = load_workbook(filename=BytesIO(reports.summary(delivery)))
|
||||
assert list(wb.active.values) == [
|
||||
("ref", "produit", "prix unitaire", "quantité commandée", "unité", "total"),
|
||||
("123", "Lait", 1.5, 1, None, 1.5),
|
||||
("456", "yaourt (pot 125ml)", 3.5, 4, "pot 125ml", 14),
|
||||
("789", "fromage", 9.2, 0, None, 0),
|
||||
(None, None, None, None, "Total", 15.5),
|
||||
]
|
|
@ -1,9 +1,11 @@
|
|||
from datetime import datetime, timedelta
|
||||
from io import BytesIO
|
||||
|
||||
import pytest
|
||||
from openpyxl import load_workbook
|
||||
from pyquery import PyQuery as pq
|
||||
|
||||
from copanier.models import Delivery, Order, ProductOrder
|
||||
from copanier.models import Delivery, Order, ProductOrder, Product
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
@ -52,7 +54,7 @@ async def test_create_delivery(client):
|
|||
|
||||
async def test_place_order_with_session(client, delivery):
|
||||
delivery.persist()
|
||||
body = {"123": "3"}
|
||||
body = {"wanted:123": "3"}
|
||||
resp = await client.post(f"/livraison/{delivery.id}/commander", body=body)
|
||||
assert resp.status == 302
|
||||
delivery = Delivery.load(id=delivery.id)
|
||||
|
@ -79,7 +81,7 @@ async def test_place_empty_order_should_delete_previous(client, delivery):
|
|||
|
||||
async def test_place_order_with_empty_string(client, delivery):
|
||||
delivery.persist()
|
||||
body = {"123": ""} # User deleted the field value.
|
||||
body = {"wanted:123": ""} # User deleted the field value.
|
||||
resp = await client.post(f"/livraison/{delivery.id}/commander", body=body)
|
||||
assert resp.status == 302
|
||||
delivery = Delivery.load(id=delivery.id)
|
||||
|
@ -88,7 +90,7 @@ async def test_place_order_with_empty_string(client, delivery):
|
|||
|
||||
async def test_change_paid_status_when_placing_order(client, delivery):
|
||||
delivery.persist()
|
||||
body = {"123": "3", "paid": 1}
|
||||
body = {"wanted:123": "3", "paid": 1}
|
||||
resp = await client.post(f"/livraison/{delivery.id}/commander", body=body)
|
||||
assert resp.status == 302
|
||||
delivery = Delivery.load(id=delivery.id)
|
||||
|
@ -96,6 +98,56 @@ async def test_change_paid_status_when_placing_order(client, delivery):
|
|||
assert delivery.orders["foo@bar.org"].paid is True
|
||||
|
||||
|
||||
async def test_get_place_order_with_closed_subscription(client, delivery):
|
||||
delivery.order_before = datetime.now() - timedelta(days=1)
|
||||
delivery.orders["foo@bar.org"] = Order(products={"123": ProductOrder(wanted=1)})
|
||||
delivery.persist()
|
||||
assert delivery.status == delivery.CLOSED
|
||||
resp = await client.get(f"/livraison/{delivery.id}/commander")
|
||||
doc = pq(resp.body)
|
||||
assert doc('[name="wanted:123"]').attr("readonly")
|
||||
assert not doc('[name="adjustment:123"]')
|
||||
|
||||
|
||||
async def test_get_place_order_with_adjustment_status(client, delivery):
|
||||
resp = await client.get(f"/livraison/{delivery.id}/commander")
|
||||
doc = pq(resp.body)
|
||||
assert not doc('[name="wanted:123"]').attr("readonly")
|
||||
assert not doc('[name="adjustment:123"]')
|
||||
delivery.order_before = datetime.now() - timedelta(days=1)
|
||||
delivery.products[0].packing = 6
|
||||
delivery.products.append(Product(ref="456", name="yaourt", price="3.5", packing=4))
|
||||
delivery.products.append(Product(ref="789", name="fromage", price="9.2"))
|
||||
delivery.orders["foo@bar.org"] = Order(
|
||||
products={"123": ProductOrder(wanted=1), "456": ProductOrder(wanted=4)}
|
||||
)
|
||||
delivery.persist()
|
||||
assert delivery.status == delivery.ADJUSTMENT
|
||||
resp = await client.get(f"/livraison/{delivery.id}/commander")
|
||||
doc = pq(resp.body)
|
||||
assert doc('[name="wanted:123"]').attr("readonly")
|
||||
assert doc('[name="adjustment:123"]')
|
||||
assert not doc('[name="adjustment:123"]').attr("readonly")
|
||||
assert doc('[name="wanted:456"]').attr("readonly")
|
||||
assert doc('[name="adjustment:456"]')
|
||||
# Already adjusted.
|
||||
assert doc('[name="adjustment:456"]').attr("readonly")
|
||||
assert doc('[name="adjustment:789"]')
|
||||
# Needs no adjustment.
|
||||
assert doc('[name="adjustment:789"]').attr("readonly")
|
||||
|
||||
|
||||
async def test_cannot_place_order_on_closed_delivery(client, delivery, monkeypatch):
|
||||
monkeypatch.setattr("copanier.config.STAFF", ["someone@else.org"])
|
||||
delivery.order_before = datetime.now() - timedelta(days=1)
|
||||
delivery.persist()
|
||||
body = {"wanted:123": "3"}
|
||||
resp = await client.post(f"/livraison/{delivery.id}/commander", body=body)
|
||||
assert resp.status == 302
|
||||
delivery = Delivery.load(id=delivery.id)
|
||||
assert not delivery.orders
|
||||
|
||||
|
||||
async def test_export_products(client, delivery):
|
||||
delivery.persist()
|
||||
resp = await client.get(f"/livraison/{delivery.id}/exporter/produits")
|
||||
|
|
Loading…
Reference in a new issue