mirror of
https://github.com/almet/copanier.git
synced 2025-04-28 11:32:38 +02:00
Update the demo mode
This commit is contained in:
parent
5e1e88545f
commit
ff262418c3
16 changed files with 789 additions and 711 deletions
|
@ -12,6 +12,10 @@ import yaml
|
|||
from . import config
|
||||
|
||||
|
||||
def demo_mode_enabled():
|
||||
return getattr(config, "DEMO_MODE", False)
|
||||
|
||||
|
||||
class DoesNotExist(ValueError):
|
||||
pass
|
||||
|
||||
|
@ -78,11 +82,10 @@ class Base:
|
|||
|
||||
@dataclass
|
||||
class PersistedBase(Base):
|
||||
|
||||
@classmethod
|
||||
def get_root(cls):
|
||||
root = Path(config.DATA_ROOT)
|
||||
if getattr(config, 'DEMO_MODE', False):
|
||||
if demo_mode_enabled():
|
||||
root = root / "demo"
|
||||
|
||||
return root / cls.__root__
|
||||
|
@ -111,6 +114,7 @@ class SavedConfiguration(PersistedBase):
|
|||
data = {}
|
||||
return cls(**data)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Person(Base):
|
||||
email: str
|
||||
|
@ -168,13 +172,12 @@ class Groups(PersistedBase):
|
|||
groups = cls.load()
|
||||
return len(groups.groups) > 0
|
||||
|
||||
|
||||
def persist(self):
|
||||
with self.__lock__:
|
||||
self.get_path().write_text(self.dump())
|
||||
|
||||
def add_group(self, group):
|
||||
assert group.id not in self.groups, "Un groupe avec ce nom existe déjà."
|
||||
assert group.id not in self.groups, "Un foyer avec ce nom existe déjà."
|
||||
self.groups[group.id] = group
|
||||
|
||||
def add_user(self, email, group_id):
|
||||
|
@ -294,7 +297,9 @@ class Order(Base):
|
|||
p.quantity * _get_price(ref) for ref, p in self.products.items()
|
||||
)
|
||||
|
||||
shipping = self.compute_shipping(delivery, producers, email) if include_shipping else 0
|
||||
shipping = (
|
||||
self.compute_shipping(delivery, producers, email) if include_shipping else 0
|
||||
)
|
||||
|
||||
return round(total_products + shipping, 2)
|
||||
|
||||
|
@ -360,9 +365,13 @@ class Delivery(PersistedBase):
|
|||
def products_need_price_update(self, products=None):
|
||||
products = products or self.products
|
||||
max_age = self.from_date.date() - timedelta(days=60)
|
||||
return any([product.last_update.date() < max_age
|
||||
for product in products
|
||||
if product.producer in self.producers])
|
||||
return any(
|
||||
[
|
||||
product.last_update.date() < max_age
|
||||
for product in products
|
||||
if product.producer in self.producers
|
||||
]
|
||||
)
|
||||
|
||||
@property
|
||||
def dates(self):
|
||||
|
@ -373,7 +382,7 @@ class Delivery(PersistedBase):
|
|||
"price_update_deadline": self.order_before - timedelta(weeks=2),
|
||||
"order_before": self.order_before,
|
||||
"adjustment_deadline": self.order_before + timedelta(days=4),
|
||||
"delivery_date": delivery_date
|
||||
"delivery_date": delivery_date,
|
||||
}
|
||||
|
||||
@property
|
||||
|
@ -392,11 +401,9 @@ class Delivery(PersistedBase):
|
|||
def is_waiting_products(self):
|
||||
return (
|
||||
datetime.now().date() >= self.order_before.date()
|
||||
and
|
||||
datetime.now().date() <= self.from_date.date()
|
||||
and datetime.now().date() <= self.from_date.date()
|
||||
)
|
||||
|
||||
|
||||
@property
|
||||
def is_foreseen(self):
|
||||
return datetime.now().date() <= self.from_date.date()
|
||||
|
@ -429,20 +436,20 @@ class Delivery(PersistedBase):
|
|||
|
||||
def _dedupe_products(raw_data):
|
||||
"""On some rare occasions, different products get
|
||||
the same identifier (ref).
|
||||
the same identifier (ref).
|
||||
|
||||
This function finds them and appends "-dedupe" to it.
|
||||
This is not ideal but fixes the problem before it causes more
|
||||
trouble (such as https://github.com/spiral-project/copanier/issues/136)
|
||||
This function finds them and appends "-dedupe" to it.
|
||||
This is not ideal but fixes the problem before it causes more
|
||||
trouble (such as https://github.com/spiral-project/copanier/issues/136)
|
||||
|
||||
This function returns True if dupes have been found.
|
||||
This function returns True if dupes have been found.
|
||||
"""
|
||||
if ('products' not in raw_data) or len(raw_data['products']) < 1:
|
||||
if ("products" not in raw_data) or len(raw_data["products"]) < 1:
|
||||
return False
|
||||
|
||||
products = raw_data['products']
|
||||
products = raw_data["products"]
|
||||
|
||||
counter = Counter([p['ref'] for p in products])
|
||||
counter = Counter([p["ref"] for p in products])
|
||||
most_common = counter.most_common(1)[0]
|
||||
number_of_dupes = most_common[1]
|
||||
|
||||
|
@ -454,24 +461,33 @@ class Delivery(PersistedBase):
|
|||
counter = 0
|
||||
new_products = []
|
||||
for product in products:
|
||||
ref = product['ref']
|
||||
ref = product["ref"]
|
||||
if ref == dupe_id:
|
||||
counter = counter + 1
|
||||
if counter == number_of_dupes: # Only change the last occurence.
|
||||
product['ref'] = f'{ref}-dedupe'
|
||||
if counter == number_of_dupes: # Only change the last occurence.
|
||||
product["ref"] = f"{ref}-dedupe"
|
||||
new_products.append(product)
|
||||
raw_data['products'] = new_products
|
||||
raw_data["products"] = new_products
|
||||
return True
|
||||
|
||||
data = yaml.safe_load(path.read_text())
|
||||
dupe_found = _dedupe_products(data)
|
||||
|
||||
# Tolerate extra fields (but we'll lose them if instance is persisted)
|
||||
data = {k: v for k, v in data.items() if k in cls.__dataclass_fields__}
|
||||
delivery = cls(**data)
|
||||
delivery.id = id
|
||||
|
||||
if demo_mode_enabled():
|
||||
delivery.from_date = datetime.now()
|
||||
delivery.to_date = datetime.now() + timedelta(days=10)
|
||||
delivery.order_before = datetime.now() + timedelta(days=5)
|
||||
delivery.validate_all_prices()
|
||||
delivery.persist()
|
||||
|
||||
if dupe_found:
|
||||
delivery.persist()
|
||||
|
||||
return delivery
|
||||
|
||||
@classmethod
|
||||
|
@ -591,3 +607,7 @@ class Delivery(PersistedBase):
|
|||
percentage_person = person_amount / producer_total
|
||||
shipping = percentage_person * producer_shipping
|
||||
return shipping
|
||||
|
||||
def validate_all_prices(self):
|
||||
for product in self.products:
|
||||
product.last_update = datetime.now()
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
{% if request.user and (request.user.is_staff or not config.HIDE_GROUPS_LINK) %}
|
||||
<li class="pure-menu-item">
|
||||
<a class="pure-menu-link" class="pure-menu-link" href="{{ url_for('groups') }}"><i
|
||||
class="icon-globe"></i> Gérer les groupes</a>
|
||||
class="icon-globe"></i> Gérer les foyers</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="pure-menu-item">
|
||||
|
|
|
@ -9,11 +9,13 @@
|
|||
<table class="paiements">
|
||||
<tr>
|
||||
<td></td>
|
||||
{% for crediter in crediters %}<td>{% if crediter[0] in crediters_groups %} {{ crediters_groups[crediter[0]] }}*{% else %}{{ crediter[0] }}{% endif %} (+{{ crediter[1] | round(2) }})</td>{% endfor %}
|
||||
{% for crediter in crediters %}<td>{% if crediter[0] in crediters_groups %} {{ crediters_groups[crediter[0]]
|
||||
}}*{% else %}{{ crediter[0] }}{% endif %} (+{{ crediter[1] | round(2) }})</td>{% endfor %}
|
||||
</tr>
|
||||
{% for debiter in debiters %}
|
||||
<tr>
|
||||
<td>{% if debiter[0] in debiters_groups %} {{ debiters_groups[debiter[0]].name }}{% else %}{{ debiter[0] }}{% endif %} ({{ debiter[1] | round(2) }})</td>
|
||||
<td>{% if debiter[0] in debiters_groups %} {{ debiters_groups[debiter[0]].name }}{% else %}{{ debiter[0] }}{%
|
||||
endif %} ({{ debiter[1] | round(2) }})</td>
|
||||
{% for crediter in crediters %}
|
||||
{% set due_amount = results[debiter[0]][crediter[0]] | round(2) %}
|
||||
|
||||
|
@ -23,7 +25,10 @@
|
|||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<p class="info"><i class="icon-lightbulb"></i> <strong>Mais, comment ça marche ?</strong> La répartition des chèques se fait automatiquement, en soustrayant ce que les personnes doivent (au nom de leur coloc / groupe) à ce qui leur est du (dans le cas où elles sont référentes pour certains produits).</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"><i class="icon-lightbulb"></i> <strong>Mais, comment ça marche ?</strong> La répartition des
|
||||
chèques se fait automatiquement, en soustrayant ce que les personnes doivent (au nom de leur foyer) à ce
|
||||
qui leur est du (dans le cas où elles sont référentes pour certains produits).</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 foyer.</p>
|
||||
|
||||
{% endblock body %}
|
|
@ -3,20 +3,26 @@
|
|||
{% block additional_menu %}
|
||||
<div class="pure-menu">
|
||||
<ul class="pure-menu-list">
|
||||
<li class="pure-menu-item"><a href="{{ url_for('list_products', id=delivery.id) }}/produits.pdf" class="pure-menu-link"><i class="icon-ribbon"></i> Bons de commande</a></li>
|
||||
<li class="pure-menu-item"><a href="{{ url_for('show_orders_summary', id=delivery.id) }}" class="pure-menu-link"><i class="icon-grid"></i> Commandes groupes</a></li>
|
||||
<li class="pure-menu-item"><a href="{{ url_for('compute_payments', id=delivery.id) }}" class="pure-menu-link"><i class="icon-wallet"></i> Paiements</a></li>
|
||||
<li class="pure-menu-item"><a href="{{ url_for('list_products', id=delivery.id) }}/produits.pdf"
|
||||
class="pure-menu-link"><i class="icon-ribbon"></i> Bons de commande</a></li>
|
||||
<li class="pure-menu-item"><a href="{{ url_for('show_orders_summary', id=delivery.id) }}"
|
||||
class="pure-menu-link"><i class="icon-grid"></i> Commandes foyers</a></li>
|
||||
<li class="pure-menu-item"><a href="{{ url_for('compute_payments', id=delivery.id) }}" class="pure-menu-link"><i
|
||||
class="icon-wallet"></i> Paiements</a></li>
|
||||
{% if request['user'].email == delivery.contact and delivery.status > delivery.EMPTY %}
|
||||
<li class="pure-menu-item">
|
||||
<a class="pure-menu-link" href="{{ url_for('show_delivery_toolbox', id=delivery.id) }}"><i class="icon-tools"></i> Boîte à outils</a>
|
||||
<a class="pure-menu-link" href="{{ url_for('show_delivery_toolbox', id=delivery.id) }}"><i
|
||||
class="icon-tools"></i> Boîte à outils</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if request.user and request.user.is_staff %}
|
||||
<li class="pure-menu-item">
|
||||
<a class="pure-menu-link" href="{{ url_for('edit_delivery', id=delivery.id) }}"><i class="icon-adjustments"></i> Modifier la distrib</a>
|
||||
<a class="pure-menu-link" href="{{ url_for('edit_delivery', id=delivery.id) }}"><i
|
||||
class="icon-adjustments"></i> Modifier la distrib</a>
|
||||
</li>
|
||||
<li class="pure-menu-item">
|
||||
<a class="pure-menu-link" href="{{ url_for('list_products', id=delivery.id) }}"><i class="icon-pricetags"></i> Éditer produits</a>
|
||||
<a class="pure-menu-link" href="{{ url_for('list_products', id=delivery.id) }}"><i
|
||||
class="icon-pricetags"></i> Éditer produits</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
@ -25,14 +31,16 @@
|
|||
{% block body %}
|
||||
|
||||
{% if delivery.status == delivery.CLOSED %}
|
||||
<div class="important-message">Une fois la distribution terminée, reviens ici pour <a class="button" href="{{ url_for('hand_over_delivery', id=delivery.id) }}">passer le relai !</a></div>
|
||||
<div class="important-message">Une fois la distribution terminée, reviens ici pour <a class="button"
|
||||
href="{{ url_for('hand_over_delivery', id=delivery.id) }}">passer le relai !</a></div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<div class="header">
|
||||
<h1>{{ delivery.name }}</h1>
|
||||
<h2>
|
||||
Distribution le <i class="icon-clock"></i> {{ delivery.from_date|date }}, {{ delivery.from_date|time }} - {{ delivery.to_date|time }}, à <i class="icon-streetsign"></i> {{ delivery.where }}.
|
||||
Distribution le <i class="icon-clock"></i> {{ delivery.from_date|date }}, {{ delivery.from_date|time }} - {{
|
||||
delivery.to_date|time }}, à <i class="icon-streetsign"></i> {{ delivery.where }}.
|
||||
</h2>
|
||||
|
||||
<h3>{% if delivery.products %}
|
||||
|
@ -45,13 +53,13 @@
|
|||
</div>
|
||||
|
||||
<article class="delivery">
|
||||
{% if request['user'].email == delivery.contact %}
|
||||
<div class="placeholder center">
|
||||
Hey, jettes un coup d'oeil à
|
||||
<a href="{{ url_for('show_delivery_toolbox', id=delivery.id) }}"> la boîte à outils</a> !
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if delivery.has_products %}
|
||||
{% if request['user'].email == delivery.contact %}
|
||||
<div class="placeholder center">
|
||||
Hey, jettes un coup d'oeil à
|
||||
<a href="{{ url_for('show_delivery_toolbox', id=delivery.id) }}"> la boîte à outils</a> !
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if delivery.has_products %}
|
||||
{% for (id, producer) in delivery.get_producers_for_referent(request.user.email).items() %}
|
||||
{% if producer.needs_price_update(delivery) %}
|
||||
{% set modal_body %}
|
||||
|
@ -60,25 +68,29 @@ Hey, jettes un coup d'oeil à
|
|||
Certains produits dont tu est référent⋅e ont besoin d'être mis à jour.<br /><br />
|
||||
Est-ce que tu veux t'en occuper maintenant ?<br />
|
||||
|
||||
<a class="button" href="{{ url_for('edit_producer', delivery_id=delivery.id, producer_id=producer.id) }}#products">Oui, mettre à jour les prix pour {{ producer.name }}</a>
|
||||
<a class="button"
|
||||
href="{{ url_for('edit_producer', delivery_id=delivery.id, producer_id=producer.id) }}#products">Oui, mettre à
|
||||
jour les prix pour {{ producer.name }}</a>
|
||||
{%- endset %}
|
||||
{{ macros.modal(id="update-price", body=modal_body, checked=True) }}
|
||||
{% break %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% include "includes/delivery_table.html" %}
|
||||
{% else %}
|
||||
<div class="placeholder">
|
||||
<h2>😔 Pour le moment, cette distribution est bien vide…</h2>
|
||||
{% if request.user and request.user.is_staff %}
|
||||
{% else %}
|
||||
<div class="placeholder">
|
||||
<h2>😔 Pour le moment, cette distribution est bien vide…</h2>
|
||||
{% if request.user and request.user.is_staff %}
|
||||
Occupons-nous donc de ça ! Deux options :
|
||||
<ol>
|
||||
<li><a href="{{ url_for('create_producer', delivery_id=delivery.id) }}">Ajouter les product⋅eurs⋅rices</a> à la main ;</li>
|
||||
<li>Ou bien <a href="{{ url_for('copy_products', id=delivery.id) }}">copier les produits d'une autre distribution</a>.</li>
|
||||
<li><a href="{{ url_for('create_producer', delivery_id=delivery.id) }}">Ajouter les product⋅eurs⋅rices</a> à
|
||||
la main ;</li>
|
||||
<li>Ou bien <a href="{{ url_for('copy_products', id=delivery.id) }}">copier les produits d'une autre
|
||||
distribution</a>.</li>
|
||||
</ol>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</article>
|
||||
<hr>
|
||||
<ul class="toolbox">
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block toplink %}<a href="{{ url_for('show_delivery', id=delivery.id) }}">↶ Retourner à la distribution</a>{% endblock %}
|
||||
{% block toplink %}<a href="{{ url_for('show_delivery', id=delivery.id) }}">↶ Retourner à la distribution</a>{% endblock
|
||||
%}
|
||||
|
||||
{% block body %}
|
||||
|
||||
|
@ -8,7 +9,9 @@
|
|||
<h1>{{ delivery.name }}</h1>
|
||||
</div>
|
||||
|
||||
<i class="icon-lightbulb"></i> <strong>{{ delivery.orders|length }}</strong> groupes, <strong>{{ delivery.products|length }}</strong> produits et <strong>{{ delivery.producers | length}}</strong> fournisseurs. <br /> Total de la commande : <strong>{{ delivery.total }}€</strong></li>
|
||||
<i class="icon-lightbulb"></i> <strong>{{ delivery.orders|length }}</strong> foyers, <strong>{{ delivery.products|length
|
||||
}}</strong> produits et <strong>{{ delivery.producers | length}}</strong> fournisseurs. <br /> Total de la commande
|
||||
: <strong>{{ delivery.total }}€</strong></li>
|
||||
|
||||
<h2>Rappel des dates</h2>
|
||||
{% include "includes/delivery_dates_table.html" %}
|
||||
|
@ -21,23 +24,31 @@
|
|||
|
||||
Avant et pendant la distribution :
|
||||
<ul>
|
||||
<li><a href="{{ url_for('groups', id=delivery.id) }}"><i class="icon-globe"></i> Gérer les groupes / colocs</a></li>
|
||||
<li><a href="{{ url_for('edit_delivery', id=delivery.id) }}"><i class="icon-pencil"></i> Modifier la commande (dates, lieu, référent⋅e, etc)</a></li>
|
||||
<li><a href="{{ url_for('list_products', id=delivery.id) }}"><i class="icon-pencil"></i> Gérer les produits</a></li>
|
||||
<li><a href="{{ url_for('groups', id=delivery.id) }}"><i class="icon-globe"></i> Gérer les foyers /
|
||||
colocs</a></li>
|
||||
<li><a href="{{ url_for('edit_delivery', id=delivery.id) }}"><i class="icon-pencil"></i> Modifier la commande
|
||||
(dates, lieu, référent⋅e, etc)</a></li>
|
||||
<li><a href="{{ url_for('list_products', id=delivery.id) }}"><i class="icon-pencil"></i> Gérer les
|
||||
produits</a></li>
|
||||
</ul>
|
||||
|
||||
Une fois les commandes passées :
|
||||
<ul>
|
||||
<li><a href="{{ url_for('list_products', id=delivery.id) }}/produits.pdf"><i class="icon-download"></i> Télécharger la liste des produits commandés</a></li>
|
||||
<li><a href="{{ url_for('generate_report', id=delivery.id) }}"><i class="icon-download"></i> Télécharger le tableau des commandes</a></li>
|
||||
<li><a href="{{ url_for('send_referent_emails', id=delivery.id) }}"><i class="icon-envelope"></i> Envoyer les infos de commande aux référent⋅e⋅s</a></li>
|
||||
<li><a href="{{ url_for('list_products', id=delivery.id) }}/produits.pdf"><i class="icon-download"></i>
|
||||
Télécharger la liste des produits commandés</a></li>
|
||||
<li><a href="{{ url_for('generate_report', id=delivery.id) }}"><i class="icon-download"></i> Télécharger le
|
||||
tableau des commandes</a></li>
|
||||
<li><a href="{{ url_for('send_referent_emails', id=delivery.id) }}"><i class="icon-envelope"></i> Envoyer les
|
||||
infos de commande aux référent⋅e⋅s</a></li>
|
||||
</ul>
|
||||
|
||||
Pour préparer la distribution :
|
||||
|
||||
<ul>
|
||||
<li><a href="{{ url_for('show_orders_summary', id=delivery.id) }}"><i class="icon-document"></i> Fiches de commandes par groupe</a></li>
|
||||
<li><a href="{{ url_for('compute_payments', id=delivery.id) }}"><i class="icon-gears"></i> Faire la répartition des paiements</a></li>
|
||||
<li><a href="{{ url_for('show_orders_summary', id=delivery.id) }}"><i class="icon-document"></i> Fiches de
|
||||
commandes par foyer</a></li>
|
||||
<li><a href="{{ url_for('compute_payments', id=delivery.id) }}"><i class="icon-gears"></i> Faire la
|
||||
répartition des paiements</a></li>
|
||||
</ul>
|
||||
|
||||
{% endblock %}
|
|
@ -3,11 +3,11 @@
|
|||
{% block body %}
|
||||
<div class="header">
|
||||
{% if group.id %}
|
||||
<h1>Modifier le groupe</h1>
|
||||
<h1>Modifier le foyer</h1>
|
||||
{% else %}
|
||||
<h1>Créer un nouveau groupe</h1>
|
||||
<h1>Créer un nouveau foyer</h1>
|
||||
{% endif %}
|
||||
<h4>Les groupes permettent de gérer les commandes pour d'autres personnes (colocs, familles, etc)</h4>
|
||||
<h4>Les foyers permettent de gérer les commandes pour d'autres personnes (colocs, familles, etc)</h4>
|
||||
</div>
|
||||
|
||||
<form method="post">
|
||||
|
@ -16,7 +16,7 @@
|
|||
<input type="text" name="name" value="{{ group.name or '' }}" required>
|
||||
</label>
|
||||
<label>
|
||||
<p>Membres du groupe (emails, séparés par des virgules)</p>
|
||||
<p>Membres du foyer (emails, séparés par des virgules)</p>
|
||||
<input type="text" name="members" value="{{ ', '.join(group.members) if group.members else '' }}">
|
||||
</label>
|
||||
<div>
|
||||
|
@ -27,7 +27,8 @@
|
|||
{% if group.id %}
|
||||
<ul class="toolbox">
|
||||
<li>
|
||||
<a href="{{ url_for('delete_group', id=group.id) }}" class="button danger"><i class="icon-hazardous"></i> Supprimer ce groupe</a>
|
||||
<a href="{{ url_for('delete_group', id=group.id) }}" class="button danger"><i
|
||||
class="icon-hazardous"></i> Supprimer ce foyer</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
|
|
@ -2,28 +2,31 @@
|
|||
|
||||
{% block body %}
|
||||
<div class="header">
|
||||
<h1>Groupes</h1>
|
||||
<h1>Foyers</h1>
|
||||
<div class="pure-menu pure-menu-horizontal">
|
||||
<ul class="pure-menu-list">
|
||||
<li class="pure-menu-item">
|
||||
<a class="pure-menu-link" href="{{ url_for('create_group') }}"><i class="icon-globe"></i> Créer un nouveau groupe</a>
|
||||
<a class="pure-menu-link" href="{{ url_for('create_group') }}"><i class="icon-globe"></i> Créer un
|
||||
nouveau foyer</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if not request['user'].group_id %}
|
||||
<p>Bienvenue ! Avant de pouvoir commander, peux-tu nous indiquer pour quelle coloc / famille tu vas passer commande ? </p>
|
||||
<p>Bienvenue ! Avant de pouvoir commander, peux-tu nous indiquer pour quel foyer tu vas passer commande ?
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if not groups.groups %}
|
||||
<p>On dirait que tu arrive parmis les premier⋅es ! <a class="button" href="{{ url_for('create_group') }}"><i class="icon-globe"></i> Créé un nouveau groupe.</a></p>
|
||||
<p>On dirait que tu arrive parmis les premier⋅es ! <a class="button" href="{{ url_for('create_group') }}"><i
|
||||
class="icon-globe"></i> Créé un nouveau foyer.</a></p>
|
||||
|
||||
{% else %}
|
||||
|
||||
<table id="groups" class="pure-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Groupe</th>
|
||||
<th>Foyer</th>
|
||||
<th>Membres</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
|
@ -39,13 +42,16 @@
|
|||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
<td>{% if group.id != request['user'].group_id %}<a class="pure-button" href="{{ url_for('join_group', id=group.id) }}">rejoindre</a>{% endif %} <a class="pure-button" href="{{ url_for('edit_group', id=group.id) }}">éditer</a></td>
|
||||
<td>{% if group.id != request['user'].group_id %}<a class="pure-button"
|
||||
href="{{ url_for('join_group', id=group.id) }}">rejoindre</a>{% endif %} <a class="pure-button"
|
||||
href="{{ url_for('edit_group', id=group.id) }}">éditer</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p>Tu ne fais partie d'aucun des groupes listés ici ? Tu peux <a class="button" href="{{ url_for('create_group') }}"><i class="icon-globe"></i> en créer un nouveau</a></p>
|
||||
<p>Tu ne fais partie d'aucun des foyers listés ici ? Tu peux <a class="button" href="{{ url_for('create_group') }}"><i
|
||||
class="icon-globe"></i> en créer un nouveau</a></p>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
|
@ -1,26 +1,31 @@
|
|||
<table class="pure-table fixed-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th><th>Dates</th><th>Coord</th><th>Référent⋅e⋅s</th>
|
||||
<th></th>
|
||||
<th>Dates</th>
|
||||
<th>Coord</th>
|
||||
<th>Référent⋅e⋅s</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Création de la distribution</th>
|
||||
<td>{{ delivery.dates.creation_date | date}}</td>
|
||||
<td>Rappeler aux référent⋅e⋅s produit de mettre leurs prix à jour, vérifier que tous les produits sont bien présentes, en ajouter si besoin</td>
|
||||
<td>Rappeler aux référent⋅e⋅s produit de mettre leurs prix à jour, vérifier que tous les produits sont bien
|
||||
présentes, en ajouter si besoin</td>
|
||||
<td>-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Mise à jour des prix</th>
|
||||
<td>Du {{ delivery.dates.price_update_start | date }} au {{ delivery.dates.price_update_deadline | date}}</td>
|
||||
<td>Du {{ delivery.dates.price_update_start | date }} au {{ delivery.dates.price_update_deadline | date}}
|
||||
</td>
|
||||
<td>—</td>
|
||||
<td>Les référent⋅e⋅s produit mettent les prix à jour.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Commandes</th>
|
||||
<td>Du {{ delivery.dates.price_update_deadline | date }} au {{ delivery.dates.order_before | date }}</td>
|
||||
<td>Envoyer le lien de commande aux groupes</td>
|
||||
<td>Envoyer le lien de commande aux foyers</td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -32,25 +37,29 @@
|
|||
<tr>
|
||||
<th>Récup des produits</th>
|
||||
<td>Du {{ delivery.dates.adjustment_deadline | date }} au {{ delivery.dates.delivery_date | date }}</td>
|
||||
<td><a href="{{ url_for('send_referent_emails', id=delivery.id) }}">Envoyer les infos de commande aux référent⋅e⋅s</a></td>
|
||||
<td><a href="{{ url_for('send_referent_emails', id=delivery.id) }}">Envoyer les infos de commande aux
|
||||
référent⋅e⋅s</a></td>
|
||||
<td>Transmettre les commandes, <strong>récupérer les produits</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Préparation de la distribution</th>
|
||||
<td>La veille du {{ delivery.dates.delivery_date | date }}</td>
|
||||
<td><a href="{{ url_for('show_orders_summary', id=delivery.id) }}">Imprimer les bons de commandes par groupe</a></td>
|
||||
<td><a href="{{ url_for('show_orders_summary', id=delivery.id) }}">Imprimer les bons de commandes par
|
||||
foyer</a></td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Distribution</th>
|
||||
<td>{{ delivery.dates.delivery_date | date }}</td>
|
||||
<td>Coordonner la distribution, <a href="{{ url_for('compute_payments', id=delivery.id) }}">faire la répartition des chèques</a></td>
|
||||
<td>Coordonner la distribution, <a href="{{ url_for('compute_payments', id=delivery.id) }}">faire la
|
||||
répartition des chèques</a></td>
|
||||
<td>Arriver 30mn avant le début de la distribution, répartir les produits par coloc</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Transmission</th>
|
||||
<td>Après la distribution</td>
|
||||
<td><a href="{{ url_for('hand_over_delivery', id=delivery.id) }}">Passer le relai à la nouvelle personne référente</a></a></td>
|
||||
<td><a href="{{ url_for('hand_over_delivery', id=delivery.id) }}">Passer le relai à la nouvelle personne
|
||||
référente</a></a></td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
{% if deliveries %}
|
||||
<ul class="delivery">
|
||||
{% for delivery in deliveries %}
|
||||
<li>
|
||||
<h3><a href="{{ url_for('show_delivery', id=delivery.id) }}"><i class="icon-hotairballoon"></i> {{ delivery.name }}</a> {% include "includes/order_button.html" %} <a class="button" href="{{ url_for('show_delivery', id=delivery.id) }}">Voir les commandes</a></h3>
|
||||
{% include "includes/delivery_head.html" %}
|
||||
</li>
|
||||
<hr>
|
||||
{% endfor %}
|
||||
{% for delivery in deliveries %}
|
||||
<li>
|
||||
<h3><a href="{{ url_for('show_delivery', id=delivery.id) }}"><i class="icon-hotairballoon"></i> {{ delivery.name
|
||||
}}</a> {% include "includes/order_button.html" %} <a class="button"
|
||||
href="{{ url_for('show_delivery', id=delivery.id) }}">Voir les commandes</a></h3>
|
||||
{% include "includes/delivery_head.html" %}
|
||||
</li>
|
||||
<hr>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p>Il n'y a aucune distribution à venir. Vous pouvez <a href="{{ url_for('new_delivery') }}">en lancer une nouvelle</a> !
|
||||
{% endif %}
|
||||
<p>🤔 Il n'y a aucune distribution à venir. Vous pouvez <a href="{{ url_for('new_delivery') }}">en lancer une
|
||||
nouvelle</a> !
|
||||
{% endif %}
|
|
@ -4,9 +4,13 @@
|
|||
<p>On dirait que vous venez de lancer ce logiciel pour la première fois, bienvenue ici 😀.</p>
|
||||
<p>Pour commencer, deux options :</p>
|
||||
<ul>
|
||||
<li><a href=" {{ url_for('activate_demo') }}">Activer le mode de démonstration</a>, cela va charger des données de démonstration pour que vous puissiez aller jeter un coup d'oeil au fonctionnement du logiciel ;</li>
|
||||
<li>Ou alors si vous avez envie de commencer à utiliser le logiciel, vous devez commencer par <a href="{{ url_for('groups') }}">rejoindre un groupe</a> et commencer une distribution.</p></li>
|
||||
<li><a class="button" href=" {{ url_for('activate_demo') }}">Activer le mode de démonstration</a> pour jeter un coup
|
||||
d'œil rapidement aux fonctionalités.</li>
|
||||
<li>Sinon, vous pouvez commencer par <a class="button" href="{{ url_for('groups') }}">rejoindre un foyer</a> et
|
||||
commencer une distribution.</p>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<p>Bon voyage 🙏 !</p>
|
||||
<p>La peinture est encore fraiche, alors si vous avez besoin d'un coup de main, n'hésitez pas à <a
|
||||
href="mailto:alexis@notmyidea.org">m'envoyer un mail</a> ! 🙏</p>
|
||||
{% endblock body %}
|
|
@ -20,7 +20,7 @@ async def join_group(request, response, id):
|
|||
request["groups"].persist()
|
||||
redirect = "/" if not request["user"].group_id else "/groupes"
|
||||
|
||||
response.message(f"Vous avez bien rejoint le groupe « {group.name} »")
|
||||
response.message(f"Vous avez bien rejoint le foyer « {group.name} »")
|
||||
response.redirect = redirect
|
||||
|
||||
|
||||
|
@ -41,14 +41,14 @@ async def create_group(request, response):
|
|||
)
|
||||
request["groups"].add_group(group)
|
||||
request["groups"].persist()
|
||||
response.message(f"Le groupe {group.name} à bien été créé")
|
||||
response.message(f"Le foyer {group.name} à bien été créé")
|
||||
response.redirect = "/"
|
||||
response.html("groups/edit_group.html", group=group)
|
||||
|
||||
|
||||
@app.route("/groupes/{id}/éditer", methods=["GET", "POST"])
|
||||
async def edit_group(request, response, id):
|
||||
assert id in request["groups"].groups, "Impossible de trouver le groupe"
|
||||
assert id in request["groups"].groups, "Impossible de trouver le foyer"
|
||||
group = request["groups"].groups[id]
|
||||
if request.method == "POST":
|
||||
form = request.form
|
||||
|
@ -65,8 +65,8 @@ async def edit_group(request, response, id):
|
|||
|
||||
@app.route("/groupes/{id}/supprimer", methods=["GET"])
|
||||
async def delete_group(request, response, id):
|
||||
assert id in request["groups"].groups, "Impossible de trouver le groupe"
|
||||
assert id in request["groups"].groups, "Impossible de trouver le foyer"
|
||||
deleted = request["groups"].groups.pop(id)
|
||||
request["groups"].persist()
|
||||
response.message(f"Le groupe {deleted.name} à bien été supprimé")
|
||||
response.message(f"Le foyer {deleted.name} à bien été supprimé")
|
||||
response.redirect = "/groupes"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from .core import app, session, env, url
|
||||
|
||||
from ..models import Groups, Person, SavedConfiguration
|
||||
from ..models import Groups, Person, SavedConfiguration, Delivery
|
||||
from .. import utils, emails, config
|
||||
|
||||
|
||||
|
@ -14,9 +14,9 @@ async def auth_required(request, response):
|
|||
|
||||
saved_config = SavedConfiguration.load()
|
||||
if saved_config.demo_mode_enabled:
|
||||
setattr(config, 'DEMO_MODE', True)
|
||||
setattr(config, "DEMO_MODE", True)
|
||||
else:
|
||||
setattr(config, 'DEMO_MODE', False)
|
||||
setattr(config, "DEMO_MODE", False)
|
||||
|
||||
if request.route.payload and not request.route.payload.get("unprotected"):
|
||||
token = request.cookies.get("token")
|
||||
|
@ -96,13 +96,16 @@ async def logout(request, response):
|
|||
async def onboarding(request, response):
|
||||
response.html("onboarding.html")
|
||||
|
||||
|
||||
@app.route("/premier-lancement/demo", methods=["GET"])
|
||||
async def activate_demo(request, response):
|
||||
saved_config = SavedConfiguration.load()
|
||||
saved_config.demo_mode_enabled = True
|
||||
|
||||
saved_config.persist()
|
||||
response.redirect = "/"
|
||||
|
||||
|
||||
@app.route("/premier-lancement/demo/désactiver", methods=["GET"])
|
||||
async def desactivate_demo(request, response):
|
||||
saved_config = SavedConfiguration.load()
|
||||
|
|
|
@ -134,9 +134,7 @@ async def validate_producer_prices(request, response, delivery_id, producer_id):
|
|||
@app.route("/produits/{delivery_id}/valider-prix", methods=["GET"])
|
||||
async def mark_all_prices_as_ok(request, response, delivery_id):
|
||||
delivery = Delivery.load(delivery_id)
|
||||
|
||||
for product in delivery.products:
|
||||
product.last_update = datetime.now()
|
||||
delivery.validate_all_prices()
|
||||
delivery.persist()
|
||||
|
||||
response.message(f"Les prix ont été marqués comme OK pour toute la distribution !")
|
||||
|
@ -156,8 +154,12 @@ async def create_product(request, response, delivery_id, producer_id):
|
|||
product.producer = producer_id
|
||||
form = request.form
|
||||
product.update_from_form(form)
|
||||
random_string = "".join(random.choices(string.ascii_lowercase + string.digits, k=8))
|
||||
product.ref = slugify(f"{producer_id}-{product.name}-{product.unit}-{random_string}")
|
||||
random_string = "".join(
|
||||
random.choices(string.ascii_lowercase + string.digits, k=8)
|
||||
)
|
||||
product.ref = slugify(
|
||||
f"{producer_id}-{product.name}-{product.unit}-{random_string}"
|
||||
)
|
||||
|
||||
delivery.products.append(product)
|
||||
delivery.persist()
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,64 +5,38 @@ groups:
|
|||
- gilki@tenhe.ls
|
||||
- mohu@zab.tj
|
||||
- nazhap@opolarti.ly
|
||||
- youpi@notmyidea.org
|
||||
name: Permuflard
|
||||
john:
|
||||
id: john
|
||||
members:
|
||||
- voige@sida.li
|
||||
name: John
|
||||
pre-du-fond:
|
||||
id: pre-du-fond
|
||||
members:
|
||||
- sakzace@cetbageg.ne
|
||||
- es@worru.cx
|
||||
name: Pré du fond
|
||||
chez-louise:
|
||||
id: chez-louise
|
||||
members:
|
||||
- suznala@iflavra.ch
|
||||
- sihvo@vo.gn
|
||||
name: Chez Louise
|
||||
les-filles-du-bout:
|
||||
id: les-filles-du-bout
|
||||
members:
|
||||
- tecuhmiz@ilmifhaf.edu
|
||||
- pi@hozep.sj
|
||||
name: Les filles du bout
|
||||
chaton:
|
||||
id: chaton
|
||||
members:
|
||||
- puet@helzet.ax
|
||||
- bi@noto.fm
|
||||
name: Châton
|
||||
chez-louise:
|
||||
id: chez-louise
|
||||
members:
|
||||
- suznala@iflavra.ch
|
||||
- sihvo@vo.gn
|
||||
name: Chez Louise
|
||||
chez-pascale:
|
||||
id: chez-pascale
|
||||
members:
|
||||
- gawumnud@izkep.bb
|
||||
name: Chez Pascale
|
||||
laxe:
|
||||
id: laxe
|
||||
john:
|
||||
id: john
|
||||
members:
|
||||
- couv@rujli.lr
|
||||
- casceci@ziceda.mv
|
||||
name: Laxe
|
||||
mouin:
|
||||
id: mouin
|
||||
- voige@sida.li
|
||||
name: John
|
||||
la-bas-au-loin:
|
||||
id: la-bas-au-loin
|
||||
members:
|
||||
- rop@uznofkoz.za
|
||||
- ga@ma.gb
|
||||
- fuatogi@bip.sb
|
||||
- te@itiorapa.gn
|
||||
name: Mouin
|
||||
le-chauffage:
|
||||
id: le-chauffage
|
||||
members:
|
||||
- jaigo@hevef.gl
|
||||
- du@nozcoze.lt
|
||||
- em@ca.fk
|
||||
- ifegomcic@pi.lt
|
||||
- zow@hanheh.tn
|
||||
name: Le chauffage
|
||||
- uwuvenfo@dunam.na
|
||||
- va@nowuk.th
|
||||
- ohu@vukuk.vu
|
||||
- ewmu@migo.hm
|
||||
name: La Bas au loin
|
||||
la-lointaine:
|
||||
id: la-lointaine
|
||||
members:
|
||||
|
@ -72,14 +46,6 @@ groups:
|
|||
- viw@iwfifbe.ua
|
||||
- enji@ladbaped.lt
|
||||
name: La lointaine
|
||||
la-bas-au-loin:
|
||||
id: la-bas-au-loin
|
||||
members:
|
||||
- uwuvenfo@dunam.na
|
||||
- va@nowuk.th
|
||||
- ohu@vukuk.vu
|
||||
- ewmu@migo.hm
|
||||
name: La Bas au loin
|
||||
la-lumiere:
|
||||
id: la-lumiere
|
||||
members:
|
||||
|
@ -88,6 +54,12 @@ groups:
|
|||
- uveruopo@gic.org
|
||||
- zimpok@gogav.sy
|
||||
name: La lumière
|
||||
la-moins:
|
||||
id: la-moins
|
||||
members:
|
||||
- cuud@cof.sc
|
||||
- gavciawu@huzip.ga
|
||||
name: La Moins
|
||||
la-reclue:
|
||||
id: la-reclue
|
||||
members:
|
||||
|
@ -100,12 +72,21 @@ groups:
|
|||
- pej@je.pk
|
||||
- fotopesu@sumfu.sm
|
||||
name: la Ville Z
|
||||
la-moins:
|
||||
id: la-moins
|
||||
laxe:
|
||||
id: laxe
|
||||
members:
|
||||
- cuud@cof.sc
|
||||
- gavciawu@huzip.ga
|
||||
name: La Moins
|
||||
- couv@rujli.lr
|
||||
- casceci@ziceda.mv
|
||||
name: Laxe
|
||||
le-chauffage:
|
||||
id: le-chauffage
|
||||
members:
|
||||
- jaigo@hevef.gl
|
||||
- du@nozcoze.lt
|
||||
- em@ca.fk
|
||||
- ifegomcic@pi.lt
|
||||
- zow@hanheh.tn
|
||||
name: Le chauffage
|
||||
le-foin:
|
||||
id: le-foin
|
||||
members:
|
||||
|
@ -126,12 +107,20 @@ groups:
|
|||
- rocvibuv@nosmijij.tz
|
||||
- it@za.rs
|
||||
name: Le grand champ
|
||||
ttre:
|
||||
id: ttre
|
||||
les-filles-du-bout:
|
||||
id: les-filles-du-bout
|
||||
members:
|
||||
- hutfiro@aje.gov
|
||||
- ce@bowzodda.in
|
||||
name: TTRE
|
||||
- tecuhmiz@ilmifhaf.edu
|
||||
- pi@hozep.sj
|
||||
name: Les filles du bout
|
||||
mouin:
|
||||
id: mouin
|
||||
members:
|
||||
- rop@uznofkoz.za
|
||||
- ga@ma.gb
|
||||
- fuatogi@bip.sb
|
||||
- te@itiorapa.gn
|
||||
name: Mouin
|
||||
passage-creuse:
|
||||
id: passage-creuse
|
||||
members:
|
||||
|
@ -153,12 +142,6 @@ groups:
|
|||
- budavwa@ciwun.mt
|
||||
- jisafu@huvogu.jm
|
||||
name: Pataudes
|
||||
rs:
|
||||
id: rs
|
||||
members:
|
||||
- bok@rebu.de
|
||||
- beko@noza.bz
|
||||
name: R&S
|
||||
peug:
|
||||
id: peug
|
||||
members:
|
||||
|
@ -169,3 +152,21 @@ groups:
|
|||
- zahpepez@toteppe.hu
|
||||
- fiodvif@fafij.cm
|
||||
name: Peug'
|
||||
pre-du-fond:
|
||||
id: pre-du-fond
|
||||
members:
|
||||
- sakzace@cetbageg.ne
|
||||
- es@worru.cx
|
||||
name: Pré du fond
|
||||
rs:
|
||||
id: rs
|
||||
members:
|
||||
- bok@rebu.de
|
||||
- beko@noza.bz
|
||||
name: R&S
|
||||
ttre:
|
||||
id: ttre
|
||||
members:
|
||||
- hutfiro@aje.gov
|
||||
- ce@bowzodda.in
|
||||
name: TTRE
|
||||
|
|
|
@ -40,7 +40,7 @@ Avant et pendant la distrib :
|
|||
|
||||
Modifier la commande (dates, lieu, référent⋅e, etc)
|
||||
Modifier les produits, les product⋅rices⋅eurs
|
||||
Gérer les groupes / colocs
|
||||
Gérer les foyers
|
||||
|
||||
Une fois les commandes passées (après le dimanche 26 janvier)
|
||||
Télécharger les bons de distribution
|
||||
|
|
Loading…
Reference in a new issue