mirror of
https://github.com/almet/copanier.git
synced 2025-04-28 19:42:37 +02:00
Merge branch 'master' into crac
This commit is contained in:
commit
d54324c2c1
12 changed files with 131 additions and 61 deletions
|
@ -181,6 +181,7 @@ async def sesame(request, response):
|
||||||
async def send_sesame(request, response):
|
async def send_sesame(request, response):
|
||||||
email = request.form.get("email")
|
email = request.form.get("email")
|
||||||
token = utils.create_token(email)
|
token = utils.create_token(email)
|
||||||
|
try:
|
||||||
emails.send_from_template(
|
emails.send_from_template(
|
||||||
env,
|
env,
|
||||||
"access_granted",
|
"access_granted",
|
||||||
|
@ -189,6 +190,9 @@ async def send_sesame(request, response):
|
||||||
hostname=request.host,
|
hostname=request.host,
|
||||||
token=token.decode(),
|
token=token.decode(),
|
||||||
)
|
)
|
||||||
|
except RuntimeError:
|
||||||
|
response.message("Oops, impossible d'envoyer le courriel…", status="error")
|
||||||
|
else:
|
||||||
response.message(f"Un sésame vous a été envoyé à l'adresse '{email}'")
|
response.message(f"Un sésame vous a été envoyé à l'adresse '{email}'")
|
||||||
response.redirect = "/"
|
response.redirect = "/"
|
||||||
|
|
||||||
|
@ -533,6 +537,12 @@ async def export_products(request, response, id):
|
||||||
response.xlsx(reports.products(delivery))
|
response.xlsx(reports.products(delivery))
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/livraison/archive/{id}/exporter/produits", methods=["GET"])
|
||||||
|
async def export_archived_products(request, response, id):
|
||||||
|
delivery = Delivery.load(f"archive/{id}")
|
||||||
|
response.xlsx(reports.products(delivery))
|
||||||
|
|
||||||
|
|
||||||
@app.route("/livraison/{id}/edit", methods=["GET"])
|
@app.route("/livraison/{id}/edit", methods=["GET"])
|
||||||
@staff_only
|
@staff_only
|
||||||
async def edit_delivery(request, response, id):
|
async def edit_delivery(request, response, id):
|
||||||
|
@ -617,7 +627,6 @@ async def place_order(request, response, id):
|
||||||
delivery.persist()
|
delivery.persist()
|
||||||
|
|
||||||
if user and orderer.id == user.id:
|
if user and orderer.id == user.id:
|
||||||
# Only send email if order has been placed by the user itself.
|
|
||||||
# Send the emails to everyone in the group.
|
# Send the emails to everyone in the group.
|
||||||
groups = request["groups"].groups
|
groups = request["groups"].groups
|
||||||
if orderer.group_id in groups.keys():
|
if orderer.group_id in groups.keys():
|
||||||
|
@ -643,9 +652,13 @@ async def place_order(request, response, id):
|
||||||
response.redirect = f"/livraison/{delivery.id}"
|
response.redirect = f"/livraison/{delivery.id}"
|
||||||
else:
|
else:
|
||||||
order = delivery.orders.get(orderer.id) or Order()
|
order = delivery.orders.get(orderer.id) or Order()
|
||||||
|
force_adjustment = "adjust" in request.query and user and user.is_staff
|
||||||
response.html(
|
response.html(
|
||||||
"place_order.html",
|
"place_order.html",
|
||||||
{"delivery": delivery, "person": orderer, "order": order},
|
delivery=delivery,
|
||||||
|
person=orderer,
|
||||||
|
order=order,
|
||||||
|
force_adjustment=force_adjustment,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -657,9 +670,13 @@ async def send_order(request, response, id):
|
||||||
if not order:
|
if not order:
|
||||||
response.message(f"Aucune commande pour «{email}»", status="warning")
|
response.message(f"Aucune commande pour «{email}»", status="warning")
|
||||||
else:
|
else:
|
||||||
|
try:
|
||||||
emails.send_order(
|
emails.send_order(
|
||||||
request, env, person=Person(email=email), delivery=delivery, order=order
|
request, env, person=Person(email=email), delivery=delivery, order=order
|
||||||
)
|
)
|
||||||
|
except RuntimeError:
|
||||||
|
response.message("Oops, impossible d'envoyer le courriel…", status="error")
|
||||||
|
else:
|
||||||
response.message(f"Résumé de commande envoyé à «{email}»")
|
response.message(f"Résumé de commande envoyé à «{email}»")
|
||||||
response.redirect = f"/livraison/{delivery.id}"
|
response.redirect = f"/livraison/{delivery.id}"
|
||||||
|
|
||||||
|
|
|
@ -245,6 +245,7 @@ class Order(Base):
|
||||||
return round(
|
return round(
|
||||||
sum(p.quantity * _get_price(ref) for ref, p in self.products.items()), 2
|
sum(p.quantity * _get_price(ref) for ref, p in self.products.items()), 2
|
||||||
)
|
)
|
||||||
|
return round(total, 2)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def has_adjustments(self):
|
def has_adjustments(self):
|
||||||
|
@ -352,7 +353,9 @@ class Delivery(PersistedBase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def incoming(cls):
|
def incoming(cls):
|
||||||
return [d for d in cls.all() if d.is_foreseen]
|
return sorted(
|
||||||
|
[d for d in cls.all() if d.is_foreseen], key=lambda d: d.order_before
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def former(cls):
|
def former(cls):
|
||||||
|
@ -425,3 +428,8 @@ class Delivery(PersistedBase):
|
||||||
|
|
||||||
def get_referents(self):
|
def get_referents(self):
|
||||||
return [producer.referent for producer in self.producers.values()]
|
return [producer.referent for producer in self.producers.values()]
|
||||||
|
|
||||||
|
def total_for(self, person):
|
||||||
|
if person.email not in self.orders:
|
||||||
|
return 0
|
||||||
|
return self.orders[person.email].total(self.products)
|
||||||
|
|
|
@ -1,23 +1,14 @@
|
||||||
:root {
|
:root {
|
||||||
--primary-color: #0062b7;
|
--primary-color: #0062b7;
|
||||||
--primary-color-light: #e6f0fa;
|
--link-color: #00d1b2;
|
||||||
--secondary-color: #e10055;
|
|
||||||
--text-color: #414664;
|
--text-color: #414664;
|
||||||
--border-color: #e6e6eb;
|
--light-text-color: #fafafb;
|
||||||
--primary-background-color: #fff;
|
--background-color: #fafafb;
|
||||||
--secondary-background-color: #fafafb;
|
--flag-color: #A87CA0;
|
||||||
--disease: #7846af;
|
--warning-color: #FFA631;
|
||||||
--disease-light: #f5ebfa;
|
--danger-color: #d9534f;
|
||||||
--country: #0f8796;
|
--neutral-color: #4D8FAC;
|
||||||
--country-light: #e6f5f5;
|
--success-color: #0f8796;
|
||||||
--group: #d03800;
|
|
||||||
--group-light: #fff5eb;
|
|
||||||
--keyword: #8c5a2d;
|
|
||||||
--keyword-light: #f5f0eb;
|
|
||||||
--kind: #cd0073;
|
|
||||||
--kind-light: #faf0f5;
|
|
||||||
--ern: #32009b;
|
|
||||||
--ern-light: #ebebf5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -103,7 +94,7 @@ body {
|
||||||
font-size: .8rem;
|
font-size: .8rem;
|
||||||
font-family: 'Work Sans', sans-serif;
|
font-family: 'Work Sans', sans-serif;
|
||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
background-color: var(--secondary-background-color);
|
background-color: var(--background-color);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
@ -113,7 +104,6 @@ h3,
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
legend {
|
legend {
|
||||||
/*margin: 0;*/
|
|
||||||
color: #444;
|
color: #444;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
|
@ -125,7 +115,7 @@ h3 { font-size: 1.4rem }
|
||||||
h4 { font-size: 1.1rem }
|
h4 { font-size: 1.1rem }
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: #00d1b2;
|
color: var(--link-color);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
-webkit-transition: none 86ms ease-out;
|
-webkit-transition: none 86ms ease-out;
|
||||||
|
@ -165,6 +155,7 @@ main {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flag,
|
||||||
button,
|
button,
|
||||||
a.button,
|
a.button,
|
||||||
input[type=submit] {
|
input[type=submit] {
|
||||||
|
@ -190,13 +181,14 @@ input[type=submit] {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
input[type=submit],
|
input[type=submit],
|
||||||
input[type=submit] + a.button {
|
input[type=submit] + a.button,
|
||||||
|
a.button + a.button {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=submit]:hover,
|
input[type=submit]:hover,
|
||||||
.button:hover {
|
.button:hover {
|
||||||
color: #fff;
|
color: var(--light-text-color);
|
||||||
background-color: var(--primary-color);
|
background-color: var(--primary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,29 +196,40 @@ input[type=submit]:hover,
|
||||||
button.primary,
|
button.primary,
|
||||||
a.button.primary,
|
a.button.primary,
|
||||||
input[type=submit].primary {
|
input[type=submit].primary {
|
||||||
color: #fff;
|
color: var(--background-color);
|
||||||
background: var(--primary-color);
|
background: var(--primary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
button.primary:hover,
|
button.primary:hover,
|
||||||
a.button.primary:hover,
|
a.button.primary:hover,
|
||||||
input[type=submit].primary:hover {
|
input[type=submit].primary:hover {
|
||||||
background-color: #fff;
|
background-color: var(--background-color);
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
button.danger,
|
button.danger,
|
||||||
a.button.danger,
|
a.button.danger,
|
||||||
input[type=submit].danger {
|
input[type=submit].danger {
|
||||||
color: #d9534f;
|
color: var(--danger-color);
|
||||||
border-color: #d9534f;
|
border-color: var(--danger-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
button.danger:hover,
|
button.danger:hover,
|
||||||
a.button.danger:hover,
|
a.button.danger:hover,
|
||||||
input[type=submit].danger:hover {
|
input[type=submit].danger:hover {
|
||||||
background-color: #d9534f;
|
background-color: var(--danger-color);
|
||||||
color: #fff;
|
color: var(--light-text-color);
|
||||||
|
}
|
||||||
|
.flag {
|
||||||
|
border-color: var(--flag-color);
|
||||||
|
color: var(--light-text-color);
|
||||||
|
background-color: var(--flag-color);
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
.flag.warning {
|
||||||
|
background-color: var(--warning-color);
|
||||||
|
border-color: var(--warning-color);
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -247,10 +250,10 @@ textarea {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 2rem;
|
height: 2rem;
|
||||||
padding: .4rem .8rem;
|
padding: .4rem .8rem;
|
||||||
color: #50596c;
|
color: var(--text-color);
|
||||||
font-size: .8rem;
|
font-size: .8rem;
|
||||||
line-height: 1rem;
|
line-height: 1rem;
|
||||||
background-color: #fff;
|
background-color: var(--background-color);
|
||||||
border: .05rem solid #bbc;
|
border: .05rem solid #bbc;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
@ -351,10 +354,10 @@ tr:nth-child(even) {
|
||||||
background-color: #ddd;
|
background-color: #ddd;
|
||||||
}
|
}
|
||||||
thead tr {
|
thead tr {
|
||||||
background-color: #3498db;
|
background-color: var(--neutral-color);
|
||||||
}
|
}
|
||||||
thead tr * {
|
thead tr * {
|
||||||
color: #f1f1f1;
|
color: var(--light-text-color);
|
||||||
}
|
}
|
||||||
thead th + th {
|
thead th + th {
|
||||||
border-left: 1px solid white;
|
border-left: 1px solid white;
|
||||||
|
@ -398,11 +401,11 @@ article.delivery th.person {
|
||||||
}
|
}
|
||||||
td.missing,
|
td.missing,
|
||||||
th.missing {
|
th.missing {
|
||||||
background-color: #db7734;
|
background-color: var(--warning-color);
|
||||||
}
|
}
|
||||||
.missing a {
|
.missing a {
|
||||||
color: #f1f1f1;
|
color: var(--light-text-color);
|
||||||
border-color: #f1f1f1;
|
border-color: var(--light-text-color);
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
}
|
}
|
||||||
hr {
|
hr {
|
||||||
|
@ -436,13 +439,13 @@ hr {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
.notification.success {
|
.notification.success {
|
||||||
background-color: #0f8796;
|
background-color: var(--success-color);
|
||||||
}
|
}
|
||||||
.notification.error {
|
.notification.error {
|
||||||
background-color: #e10055;
|
background-color: var(--danger-color);
|
||||||
}
|
}
|
||||||
.notification.warning {
|
.notification.warning {
|
||||||
background-color: #f9b42d;
|
background-color: var(--warning-color);
|
||||||
}
|
}
|
||||||
.notification.info {
|
.notification.info {
|
||||||
background-color: #aed1b175;
|
background-color: #aed1b175;
|
||||||
|
@ -453,14 +456,14 @@ hr {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
.not-paid {
|
.not-paid {
|
||||||
background-color: #db7734;
|
background-color: var(--warning-color);
|
||||||
}
|
}
|
||||||
.toggle {
|
.toggle {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.toggle-label {
|
.toggle-label {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: #00d1b2;
|
color: var(--link-color);
|
||||||
}
|
}
|
||||||
.toggle-container {
|
.toggle-container {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
@ -42,6 +42,9 @@
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if request.user and request.user.is_staff %}
|
{% if request.user and request.user.is_staff %}
|
||||||
|
<li>
|
||||||
|
<a href="/livraison/{{ delivery.id }}/désarchiver" class="button danger"><i class="icon-hazardous"></i> Désarchiver</a>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/livraison/{{ delivery.id }}/exporter"><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>
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
<ul class="toolbox">
|
<ul class="toolbox">
|
||||||
{% if delivery.status == delivery.CLOSED %}
|
{% if delivery.status == delivery.CLOSED %}
|
||||||
<li>
|
<li>
|
||||||
<a href="/livraison/{{ delivery.id }}/archiver" class="button danger"><i class="icon-hazardous"></i> Archiver</a>
|
<a href="/livraison/{{ delivery.id }}/archiver" class="button danger"><i class="icon-layers"></i> Archiver</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li>
|
<li>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<p>Hey ho!</p>
|
<p>Hey ho!</p>
|
||||||
|
|
||||||
<p>Voici le sésame, <a href="https://{{ hostname }}/sésame/{{ token }}">clique ici</a> pour t'authentifier.</p>
|
<p>Voici le sésame, clique dessus pour t'authentifier, ou copie-colle-le dans ton navigateur:</p>
|
||||||
|
|
||||||
|
<a href="https://{{ hostname }}/sésame/{{ token }}">https://{{ hostname }}/sésame/{{ token }}</a>
|
||||||
|
|
||||||
<p>{{ config.EMAIL_SIGNATURE }}</p>
|
<p>{{ config.EMAIL_SIGNATURE }}</p>
|
||||||
|
|
|
@ -7,3 +7,9 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if request.user.email in delivery.orders %}
|
||||||
|
<span class="flag" title="Mon solde"><i class="icon-wallet"></i> {{ delivery.total_for(request.user) }} €</span>
|
||||||
|
{% if delivery.status == delivery.CLOSED and delivery.is_passed and not delivery.orders[request.user.email].paid %}
|
||||||
|
<span class="flag warning" title="Ma commande n'est pas marquée comme soldée"><i class="icon-caution"></i> Commande à solder</span>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
{% for product in delivery.products %}
|
{% for product in delivery.products %}
|
||||||
{% if order[product].quantity %}
|
{% if order[product].quantity %}
|
||||||
<tr>
|
<tr>
|
||||||
|
<td>{{ product.ref }}</td>
|
||||||
<th class="product" style="text-align: left;">{{ product }}</th>
|
<th class="product" style="text-align: left;">{{ product }}</th>
|
||||||
{% if display_prices %}<td>{{ product.price | round(2) }} €</td>{% endif %}<td>{{ order[product].quantity }} x {{ product.unit }}</td>
|
{% if display_prices %}<td>{{ product.price | round(2) }} €</td>{% endif %}<td>{{ order[product].quantity }} x {{ product.unit }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<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 %}
|
{% if delivery.status == delivery.ADJUSTMENT or order.has_adjustments or force_adjustment %}
|
||||||
<th class="amount">Ajustement +/−</th>
|
<th class="amount">Ajustement +/−</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -36,8 +36,8 @@
|
||||||
{% 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) %} (manque {{ 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) %} (manque {{ delivery.product_missing(product) }}){% endif %}</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if delivery.status == delivery.ADJUSTMENT or order.has_adjustments %}
|
{% if delivery.status == delivery.ADJUSTMENT or order.has_adjustments or force_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 }}" min="{{ order[product].wanted * -1 }}" {% if not (delivery.product_missing(product) or force_adjustment) %}readonly{% endif %}></td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td class="with-input"><input {% if not request.user.is_referent(delivery) and delivery.status != delivery.OPEN or product.rupture %}type="text" readonly{% else%}type="number"{% endif%} min=0 name="wanted:{{ product.ref }}" value="{{ order[product].wanted }}"> x {{ product.unit }}</td>
|
<td class="with-input"><input {% if not request.user.is_referent(delivery) and delivery.status != delivery.OPEN or product.rupture %}type="text" readonly{% else%}type="number"{% endif%} min=0 name="wanted:{{ product.ref }}" value="{{ order[product].wanted }}"> x {{ product.unit }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -54,6 +54,9 @@
|
||||||
<input type="submit" value="Enregistrer la commande" class="primary">
|
<input type="submit" value="Enregistrer la commande" class="primary">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="button" href="/livraison/{{ delivery.id }}/courriel?email={{ person.email }}">Envoyer par courriel</a>
|
<a class="button" href="/livraison/{{ delivery.id }}/courriel?email={{ person.email }}">Envoyer par courriel</a>
|
||||||
|
{% if request.user.is_staff and delivery.status == delivery.CLOSED %}
|
||||||
|
<a class="button danger" href="/livraison/{{ delivery.id }}/commander?email={{ person.email }}&adjust">Ajuster</a>
|
||||||
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
</article>
|
</article>
|
||||||
{% endblock body %}
|
{% endblock body %}
|
||||||
|
|
|
@ -184,6 +184,7 @@ def upload_env():
|
||||||
"COPANIER_SEND_EMAILS": "1",
|
"COPANIER_SEND_EMAILS": "1",
|
||||||
"COPANIER_SMTP_PASSWORD": None,
|
"COPANIER_SMTP_PASSWORD": None,
|
||||||
"COPANIER_SMTP_LOGIN": None,
|
"COPANIER_SMTP_LOGIN": None,
|
||||||
|
"COPANIER_SMTP_HOST": None,
|
||||||
"COPANIER_STAFF": None,
|
"COPANIER_STAFF": None,
|
||||||
}
|
}
|
||||||
content = ""
|
content = ""
|
||||||
|
|
|
@ -105,6 +105,16 @@ def test_order_has_adjustments():
|
||||||
assert order.has_adjustments
|
assert order.has_adjustments
|
||||||
|
|
||||||
|
|
||||||
|
def test_order_total(delivery):
|
||||||
|
delivery.products = [Product(name="Lait", ref="123", price=1.5)]
|
||||||
|
order = Order()
|
||||||
|
assert order.total(delivery.products) == 0
|
||||||
|
order.products["123"] = ProductOrder(wanted=2)
|
||||||
|
assert order.total(delivery.products) == 3
|
||||||
|
order.products["unknown"] = ProductOrder(wanted=2)
|
||||||
|
assert order.total(delivery.products) == 3
|
||||||
|
|
||||||
|
|
||||||
def test_can_persist_delivery(delivery):
|
def test_can_persist_delivery(delivery):
|
||||||
with pytest.raises(AssertionError):
|
with pytest.raises(AssertionError):
|
||||||
delivery.path
|
delivery.path
|
||||||
|
|
|
@ -131,6 +131,7 @@ async def test_get_place_order_with_adjustment_status(client, delivery):
|
||||||
assert doc('[name="wanted:123"]').attr("readonly")
|
assert doc('[name="wanted:123"]').attr("readonly")
|
||||||
assert doc('[name="adjustment:123"]')
|
assert doc('[name="adjustment:123"]')
|
||||||
assert not doc('[name="adjustment:123"]').attr("readonly")
|
assert not doc('[name="adjustment:123"]').attr("readonly")
|
||||||
|
assert doc('[name="adjustment:123"]').attr("min") == "-1"
|
||||||
assert doc('[name="wanted:456"]').attr("readonly")
|
assert doc('[name="wanted:456"]').attr("readonly")
|
||||||
assert doc('[name="adjustment:456"]')
|
assert doc('[name="adjustment:456"]')
|
||||||
# Already adjusted.
|
# Already adjusted.
|
||||||
|
@ -154,6 +155,21 @@ async def test_get_place_order_with_closed_delivery_but_adjustments(client, deli
|
||||||
assert doc('[name="adjustment:123"]')
|
assert doc('[name="adjustment:123"]')
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_place_order_with_closed_delivery_but_force(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") is not None
|
||||||
|
assert not doc('[name="adjustment:123"]')
|
||||||
|
resp = await client.get(f"/livraison/{delivery.id}/commander?adjust")
|
||||||
|
doc = pq(resp.body)
|
||||||
|
assert doc('[name="wanted:123"]').attr("readonly") is not None
|
||||||
|
assert doc('[name="adjustment:123"]')
|
||||||
|
|
||||||
|
|
||||||
async def test_cannot_place_order_on_closed_delivery(client, delivery, monkeypatch):
|
async def test_cannot_place_order_on_closed_delivery(client, delivery, monkeypatch):
|
||||||
monkeypatch.setattr("copanier.config.STAFF", ["someone@else.org"])
|
monkeypatch.setattr("copanier.config.STAFF", ["someone@else.org"])
|
||||||
delivery.order_before = datetime.now() - timedelta(days=1)
|
delivery.order_before = datetime.now() - timedelta(days=1)
|
||||||
|
|
Loading…
Reference in a new issue