Commit 818438a9 authored by MOREAU Ulysse's avatar MOREAU Ulysse

Merge branch 'feat/44-delivery-improvement' into 'master'

Feat/44 delivery improvement

See merge request !60
parents 8b33a983 0d6ffd87
......@@ -101,8 +101,8 @@ class SellForm(forms.Form):
mean = forms.ChoiceField(choices=settings.PAYMENT_MEANS)
customization = forms.CharField(label="Perso", required=False)
option = forms.ChoiceField(label="Option 1", required=False)
second_option = forms.ChoiceField(label="Option 2", required=False)
option = forms.CharField(label="Option 1", required=False)
second_option = forms.CharField(label="Option 2", required=False)
def __init__(self, *args, instance: Optional[OrderItem] = None, **kwargs) -> None:
# adds `instance` parameter to emulate ModelForm interface
......@@ -113,11 +113,14 @@ class SellForm(forms.Form):
user = self.cleaned_data["user"]
mean: str = self.cleaned_data["mean"]
quantity: int = self.cleaned_data["quantity"]
customization: str = self.cleaned_data["customization"]
option: str = self.cleaned_data["option"]
second_option: str = self.cleaned_data["second_option"]
with transaction.atomic():
order = Order.objects.create(user=user, ordered=True)
Payment.objects.create(mean=mean, paid=True, order=order)
order_item = item.add_to_order(order, quantity)
order_item = item.add_to_order(order, quantity, customization, option, second_option)
order.compute_price()
order.save()
order.post_payment()
......
......@@ -159,12 +159,12 @@ class Item(models.Model):
return static("images/default_product.png")
def add_to_order(
self,
order: "Order",
quantity: int = 1,
customization: str = "",
option: str = "",
second_option: str = "",
self,
order: "Order",
quantity: int = 1,
customization: str = "",
option: str = "",
second_option: str = "",
) -> "OrderItem":
oi, created = None, False
if self.customizable:
......@@ -196,7 +196,7 @@ class Item(models.Model):
if not oi.is_pack_item:
break
if (
oi is None or oi.is_pack_item
oi is None or oi.is_pack_item
): # The preceding queries in the try-except block were empty or they
# ended on a pack_item
oi = OrderItem.objects.create(
......@@ -218,12 +218,12 @@ class Item(models.Model):
return oi
def add_to_user(
self,
user,
quantity: int = 1,
customization: str = "",
option: str = "",
second_option: str = "",
self,
user,
quantity: int = 1,
customization: str = "",
option: str = "",
second_option: str = "",
) -> "OrderItem":
if self.hidden or self.end_date < timezone.now():
raise ValidationError(
......@@ -262,6 +262,13 @@ class Order(models.Model):
except Payment.DoesNotExist:
return False
@property
def mean(self) -> str:
try:
return self.payment.mean
except Payment.DoesNotExist:
return 'None'
@property
def contains_cotiz(self) -> bool:
return any(i.item.is_cotiz for i in self.items.all())
......@@ -446,7 +453,7 @@ class OrderItem(models.Model):
)
def delete(
self, *args, override_pack=False, **kwargs
self, *args, override_pack=False, **kwargs
) -> Tuple[int, Dict[str, int]]:
deleted = 0
which: typing.Counter[str] = Counter()
......@@ -476,7 +483,7 @@ class OrderItem(models.Model):
@receiver(pre_save, sender=OrderItem)
@receiver(pre_delete, sender=OrderItem)
def orderitem_price_computing(
sender: Type[OrderItem], instance: OrderItem, **kwargs
sender: Type[OrderItem], instance: OrderItem, **kwargs
) -> None:
if kwargs.get("raw", False):
return
......@@ -488,9 +495,9 @@ def orderitem_price_computing(
@receiver(pre_delete, sender=OrderItem)
@receiver(pre_delete, sender=Order)
def no_delete_for_ordered(
sender: Union[Type[Order], Type[OrderItem]],
instance: Union[Order, OrderItem],
**kwargs,
sender: Union[Type[Order], Type[OrderItem]],
instance: Union[Order, OrderItem],
**kwargs,
) -> None:
if instance.ordered:
raise ValueError("Deletion of ordered instances is not allowed")
......@@ -498,7 +505,7 @@ def no_delete_for_ordered(
@receiver(pre_save, sender=lydiaRequest)
def mark_payment_as_paid(
sender: Type[lydiaRequest], instance: lydiaRequest, **kwargs
sender: Type[lydiaRequest], instance: lydiaRequest, **kwargs
) -> None:
if kwargs.get("raw", False):
return
......
This diff is collapsed.
window.addEventListener("load", function() {
const list = document.querySelector(".item-sell-list");
window.addEventListener("load", function () {
const list = document.querySelector(".item-sell-list");
const items = list.getElementsByTagName('li');
const items = list.getElementsByTagName('li');
for (var i = items.length - 1; i >= 0; i--) {
const item = items[i];
const item_id = item.dataset.pk;
for (var i = items.length - 1; i >= 0; i--) {
const item = items[i];
const item_id = item.dataset.pk;
console.log(item.dataset);
const button = item.lastElementChild;
button.addEventListener("click", function () {
let data = {
"item": item_id,
"user": 0,
"quantity": 1,
"mean": null,
"customization": '',
"option": '',
"second_option": ''
};
const button = item.lastElementChild;
button.addEventListener("click", function() {
let user_id = 0;
let customization_popup = new CustomizationPopup("Options et personalisation", item, function (quantity, customization, option, second_option) {
data.quantity = quantity;
data.customization = customization;
data.option = option;
data.second_option = second_option;
mean_popup.pop();
let mean_popup = new SelectionPopup(
"Moyen de paiement",
{"card": "Carte", "online": "Lydia", "cash": "Espèces", "check": "Chèque"},
function(mean) {
const data = {
"item": item_id,
"user": user_id,
"quantity": 1,
"mean": mean
};
queryJson("", data, function(response) {
add_message(response["status"], response["message"]);
}, "PUT");
});
});
let user_popup = new UserSelectionPopup("Client", function (uid) {
user_id = uid;
mean_popup.pop();
});
user_popup.pop();
});
}
let mean_popup = new SelectionPopup(
"Moyen de paiement",
{"card": "Carte", "online": "Lydia", "cash": "Espèces", "check": "Chèque"},
function (mean) {
data.mean = mean;
console.log(data);
queryJson("", data, function (response) {
add_message(response["status"], response["message"]);
}, "PUT");
});
list.style.removeProperty("display");
let user_popup = new UserSelectionPopup("Client", function (uid, dname) {
console.log(uid);
data.user = uid;
customization_popup.pop();
});
user_popup.pop();
});
}
list.style.removeProperty("display");
});
......@@ -11,36 +11,111 @@
{% endblock %}
{% block main %}
<h1>Livraison</h1>
<table id="deliveries">
<tr>
<th>Produit</th>
<th>Client</th>
<th>Commandé</th>
<th>Livré</th>
<th></th>
</tr>
{% for orderitem in object_list %}
<tr data-id="{{ orderitem.id }}">
<td>{{ orderitem.item }}</td>
<td>{{ orderitem.order.user.profile }}</td>
<td class="quantity_ordered">{{ orderitem.quantity }}</td>
<td class="quantity_delivered">{{ orderitem.quantity_delivered }}</td>
<td><button>Livrer</button></td>
</tr>
{% empty %}
<tr>
<td colspan="5">
Aucun produit en cours de livraison, allez sur la page
d'administration pour lancer une livraison.
</td>
</tr>
{% endfor %}
</table>
<div class="delivery">
<h1>Livraison</h1>
<script>
var url = new URL(window.location.href);
function applyFilter() {
url.searchParams.set('delivered', document.getElementById('delivered').checked);
url.searchParams.set('delivery_in_progress', document.getElementById('delivery_in_progress').checked);
window.location.href = url
}
</script>
<form onchange="generateTable()">
<label for="delivered">Déjà remis :</label>
<select id="delivered"
onchange="generateTable()">
<option value="both">Tous</option>
<option value="true">Oui</option>
<option value="false">Non</option>
</select>
<label for="beeing_delivered">Distribution en cours :</label>
<select id="beeing_delivered"
onchange="generateTable()">
<option value="true">Oui</option>
<option value="false">Non</option>
<option value="both">Tous</option>
</select>
<label for="hidden">Produits cachés :</label>
<select id="hidden"
onchange="generateTable()">
<option value="true">Oui</option>
<option selected value="false">Non</option>
<option value="both">Tous</option>
</select>
<label for="paid">Payé :</label>
<select id="paid"
onchange="generateTable()">
<option value="true">Oui</option>
<option value="false">Non</option>
<option value="both">Tous</option>
</select>
<label for="sort">Tri :</label>
<select id="sort">
<option value="none"> --</option>
<option value="false">Personalisation</option>
<option value="both">Acheteur</option>
<option value="date">Date de commande</option>
<option value="option">Option 1</option>
<option value="second_option">Option 2</option>
</select>
</form>
<form onblur="generateTable()"
onkeypress="onEnter(event)">
<input placeholder="Produit"
type="search"
id="productContains">
<input placeholder="Email"
type="search"
id="emailContains">
<input placeholder="Option (valeur exacte)"
type="search"
id="optionContains">
<input placeholder="Personalisation"
type="search"
id="customizationContains">
</form>
<div id="deliveries"></div>
<script>
window.addEventListener('load', generateTable);
function onEnter(event) {
if (event.keyCode === 13) {
blur(event.target)
generateTable()
}
}
function generateTable() {
var params = new URLSearchParams();
params.set('delivered', document.getElementById('delivered').value);
params.set('beeing_delivered', document.getElementById('beeing_delivered').value);
params.set('hidden', document.getElementById('hidden').value);
params.set('paid', document.getElementById('paid').value);
params.set('customizationContains', document.getElementById('customizationContains').value);
params.set('optionContains', document.getElementById('optionContains').value);
params.set('productContains', document.getElementById('productContains').value);
params.set('emailContains', document.getElementById('emailContains').value);
var orderedItemList = new OrderedItemList('deliveries', '/boutique/delivery?' + params.toString());
}
</script>
</div>
{% endblock %}
{% block scripts %}
<script src="{% static 'js/json_request.js' %}" ></script>
<script src="{% static 'boutique/js/deliver.js' %}" ></script>
<script src="{% static 'js/json_request.js' %}"></script>
<script src="{% static 'js/table_list.js' %}"></script>
<script src="{% static 'js/ordered_items.js' %}"></script>
<script src="{% static 'js/popup.js' %}"></script>
{% endblock %}
......@@ -29,6 +29,12 @@
<span class="price_number"> {{ object.price }}</span>
<span class="currency"></span>
</p>
{% if object.price != object.price_contributor %}
<p class="product_price not_reduc">
<span class="price_number"> prix cotisant :</span>
<span class="price_number"> {{ object.price_contributor }} <span
class="currency"></span> </span>
{% endif %}
{% else %}
<p class="product_price">
......
......@@ -3,7 +3,6 @@
{% load static %}
{% block styles %}
<link rel="stylesheet" href="{% static 'boutique/css/item_manage.css' %}"/>
{% endblock %}
{% block header %}
......@@ -16,35 +15,66 @@
{% block main %}
<h1>Gestion des Produits</h1>
<a href="{% url 'boutique:item_create' %}"><button>Ajouter un produit</button></a>
<a href="{% url 'boutique:delivery' %}" ><button>Livraison</button></a>
<a href="{% url 'boutique:item_sell' %}"><button>Vente pour autrui</button></a>
<a href="{% url 'boutique:manage_category' %}"><button>Ajouter une catégorie</button></a>
<a href="{% url 'boutique:item_create' %}">
<button>Ajouter un produit</button>
</a>
<a href="{% url 'boutique:delivery' %}">
<button>Distribution</button>
</a>
<a href="{% url 'boutique:item_sell' %}">
<button>Vente en directe</button>
</a>
<a href="{% url 'boutique:manage_category' %}">
<button>Ajouter une catégorie</button>
</a>
<br/>
<section class="item-manage-list">
{% for item in object_list %}
{% if item.hidden %}
<article data-id="{{ item.id }}" class="hidden">
{% else %}
<article data-id="{{ item.id }}">
{% endif %}
<img class="image" src="{{ item.get_image_url }}" alt="">
<h2>{{ item.name }}</h2>
<p class="numcommands">{{ item.order_items.count }} commandes</p>
<div>
<a href="{% url 'boutique:item_edit' pk=item.pk %}" class="delivery_list button tooltip">
<i class="fas fa-edit"><span class="tooltiptext">Modifier</span></i>
</a>
<button class="delivery_list tooltip begin-delivery"><i class="fas fa-paper-plane"></i> <span
class="tooltiptext">Commencer la livraison</span>
</button>
<button class="delivery_list tooltip see-orders"><i class="fas fa-eye"></i> <span class="tooltiptext">Voir les commandes</span>
</button>
<button class="delivery_list tooltip dl-orders" onclick="window.location.href = '{% url 'boutique:csvexport' pk=item.id %}'"><i class="fas fa-download"></i> <span class="tooltiptext">Voir les commandes</span>
</button>
</div>
</article>
<article data-id="{{ item.id }}"
data-pk="{{ item.pk }}"
data-customization="{{ item.customization }}"
data-optionLabel="{{ item.option_label }}"
data-optionChoices="{{ item.option }}"
data-secondOptionLabel="{{ item.second_option_label }}"
data-secondOptionChoices="{{ item.second_option }}"
data-customizable="{{ item.customizable }}"
data-special="{{ item.special }}"
{% if item.hidden %}
class="hidden"
{% endif %}
>
<img class="image" src="{{ item.get_image_url }}" alt="">
<h2>{{ item.name }}</h2>
<p class="numcommands">Total commandé: {{ item.total_quantity }} </p>
<p class="numcommands">[{{ item.quantity_delivered }}/{{ item.quantity_to_deliver }} distribué(s)]</p>
<div>
<a href="{% url 'boutique:item_edit' pk=item.pk %}" class="delivery_list button tooltip">
<i class="fas fa-edit"><span class="tooltiptext">Modifier</span></i>
</a>
<button class="delivery_list tooltip begin-delivery"><i class="fas fa-paper-plane"></i> <span
class="tooltiptext">Commencer la distribution des articles commandés</span>
</button>
<button class="delivery_list tooltip stop-delivery"><i class="fas fa-ban"></i> <span
class="tooltiptext">Arrêter la distribution des articles</span>
</button>
<button class="delivery_list tooltip see-orders"><i class="fas fa-eye"></i> <span
class="tooltiptext">Voir les commandes</span>
</button>
<button class="delivery_list tooltip dl-orders"
onclick="window.location.href = '{% url 'boutique:csvexport' pk=item.id %}'"><i
class="fas fa-download"></i> <span class="tooltiptext">Exporter les commandes</span>
</button>
{% if perms.boutique.sell %}
<button class="delivery_list tooltip" onclick="sell(event)"><i class="fas fa-hand-holding-usd"></i> <span
class="tooltiptext">Vente en direct</span>
</button>
{% endif %}
</div>
</article>
{% endfor %}
</section>
......@@ -53,5 +83,6 @@
{% block scripts %}
<script src="{% static 'js/json_request.js' %}"></script>
<script src="{% static 'js/popup.js' %}"></script>
<script src="{% static 'js/table_list.js' %}"></script>
<script src="{% static 'boutique/js/item_manage.js' %}"></script>
{% endblock %}
......@@ -23,7 +23,15 @@
<ul class="item-sell-list" style="display: none;">
{% for item in form.fields.item.queryset %}
<li data-pk="{{ item.pk }}">
<li data-pk="{{ item.pk }}"
data-customization="{{ item.customization }}"
data-optionLabel="{{ item.option_label }}"
data-optionChoices="{{ item.option }}"
data-secondOptionLabel="{{ item.second_option_label }}"
data-secondOptionChoices="{{ item.second_option }}"
data-customizable="{{ item.customizable }}"
data-special="{{ item.special }}">
<h3>{{ item.name }}</h3>
<span>Prix: {{ item.price }}&nbsp;</span>
<span>Prix cotisant: {{ item.price_contributor }}&nbsp;</span>
......
......@@ -36,9 +36,9 @@
{% elif order.payment.lydia_request.state == RequestState.REFUSED or order.rejected %}
<p class="state rejected">Refusée</p>
{% elif order.payment.lydia_request.state == RequestState.ACCEPTED or order.payment.paid %}
<p class="state payed">Payée</p>
<a href="{{ order.payment.lydia_request.mobile_url }}"><p class="state payed">Payée</p></a>
{% else %}
<p class="state pending">En attente de confirmation<a href="{{ cancelurl }}{{ order.pk }}"> <button class="red_button">Demander l'annulation</button></a>
<p class="state pending">En attente de confirmation<a href="{{ cancelurl }}{{ order.pk }}"> <button class="red_button">Demander l'annulation</button></a><a href="{{ order.payment.lydia_request.mobile_url }}"><button>Paiement</button></a>
</p>
{% endif %}
......
......@@ -88,7 +88,7 @@ class ItemListViewTest(ItemTestCase, UserTestCase):
self.assertContains(resp, shown_item.name)
self.assertContains(resp, no_bargain_item.name)
self.assertNotContains(resp, hidden_item.name)
self.assertNotContains(resp, "prix cotisant")
self.assertContains(resp, "prix cotisant")
# Logged in, not contributor
self.client.force_login(self.user)
......@@ -96,7 +96,7 @@ class ItemListViewTest(ItemTestCase, UserTestCase):
self.assertContains(resp, shown_item.name)
self.assertContains(resp, no_bargain_item.name)
self.assertNotContains(resp, hidden_item.name)
self.assertNotContains(resp, "prix cotisant")
self.assertContains(resp, "prix cotisant")
# Logged in, contributor
self.client.force_login(self.contributor_user)
......@@ -571,6 +571,7 @@ class DeliveryViewTest(PaymentTestCase, OrderItemTestCase):
view = DeliveryView()
qs = view.get_queryset()
self.assertEqual(qs.count(), 2)
self.assertEqual(qs.count(), 3)
self.assertIn(paid_ordered_partial_oi, qs.all())
self.assertIn(paid_ordered_undelivered_oi, qs.all())
self.assertIn(paid_ordered_delivered_oi, qs.all())
......@@ -49,4 +49,5 @@ urlpatterns = [
),
),
path("delivery", views.DeliveryView.as_view(), name="delivery"),
]
This diff is collapsed.
......@@ -34,7 +34,7 @@
<a href="{% url 'carshare:edit' aid=announcement.id %}" title="Editer"><i class="fa fa-pencil"></i></a>
{% endif %}
{% if announcement.author == request.user or perms.carshare.delete_announcement %}
<a href="{% url 'carshare:delete' aid=announcement.id %}" class="red confirm" title="Supprimer"><i class="fa fa-trash-o"></i></a>
<a href="{% url 'carshare:delete' aid=announcement.id %}" class="red confirm" title="Supprimer"><i class="fa fa-trash-alt"></i></a>
{% endif %}
</h2>
</header>
......@@ -66,7 +66,7 @@
<h5>
{{ registration.pub_date }}
{% if perms.carshare.delete_registration %}
<a href="{% url 'carshare:delete_registration' rid=registration.id %}" class="red confirm" title="Supprimer" ><i class="fa fa-trash-o"></i></a>
<a href="{% url 'carshare:delete_registration' rid=registration.id %}" class="red confirm" title="Supprimer" ><i class="fa fa-trash-alt"></i></a>
{% endif %}
</h5>
</hgroup>
......
......@@ -126,6 +126,7 @@
<section id="main_container">
<div id='messages'></div>
{% block messages %} {% endblock messages %}
{% if request.user.is_active and not request.user.profile.is_valid %}
<script>
add_message("error", "Veuillez remplir les informations de votre profil svp. (Nom, prénom et/ou surnom)", 0);
......
......@@ -100,7 +100,7 @@
<p>
<a href="{% url 'events:extern' uuid=link.admin_uuid %}">Administration</a>
<a href="{% url 'events:edit_extern' uuid=link.admin_uuid %}"><i class="fa fa-pencil"></i></a>
<a href="{% url 'events:delete_extern' uuid=link.admin_uuid %}"><i class="fa fa-trash-o red confirm"></i></a>
<a href="{% url 'events:delete_extern' uuid=link.admin_uuid %}"><i class="fa fa-trash-alt red confirm"></i></a>
</p>
</p>
</div>
......
......@@ -35,7 +35,7 @@
<a href="{% url 'news:edit' nid=new.id %}" title="Editer"><i class="fa fa-pencil"></i></a>
{% endif %}
{% if perms.news.delete_new %}
<a href="{% url 'news:delete' nid=new.id %}" class="red confirm" title="Supprimer"><i class="fa fa-trash-o"></i></a>
<a href="{% url 'news:delete' nid=new.id %}" class="red confirm" title="Supprimer"><i class="fa fa-trash-alt"></i></a>
{% endif %}
</h2>
</header>
......@@ -58,7 +58,7 @@
<h5>
{{ comment.pub_date }}
{% if perms.news.delete_comment or comment.user == request.user %}
<a href="{% url 'news:del_comment' cid=comment.id %}" class="red confirm" title="Supprimer" ><i class="fa fa-trash-o"></i></a>
<a href="{% url 'news:del_comment' cid=comment.id %}" class="red confirm" title="Supprimer" ><i class="fa fa-trash-alt"></i></a>
{% endif %}
</h5>
</hgroup>
......
......@@ -9,7 +9,7 @@ from . import models
def index(request):
context = {
'news': models.New.objects.order_by('pub_date').all().reverse().select_related('author__profile').annotate(nb_comments=Count('comments'))[:10],
'news': models.New.objects.order_by('pub_date').all().reverse().select_related('author__profile').annotate(nb_comments=Count('comments'))[:20],
}
return render(request, "news/index.html", context)
......
......@@ -36,7 +36,7 @@
<td width="40%">{{ p.description }}</td>
<td><a href="{{ p.urlLink }}"><i class="fa fa-external-link"></i></a></td>
<td width="5%"><a href="{% url 'partnerships:admin_edit' nid=p.id %}" title="Editer"><i class="fa fa-pencil"></i></a></td>
<td width="5%"><a href="{% url 'partnerships:admin_delete' nid=p.id %}" class="red confirm" title="Supprimer"><i class="fa fa-trash-o"></i></a></td>
<td width="5%"><a href="{% url 'partnerships:admin_delete' nid=p.id %}" class="red confirm" title="Supprimer"><i class="fa fa-trash-alt"></i></a></td>
</tr>
{% endfor %}
</tbody>
......
......@@ -37,7 +37,7 @@
{% for perm in permissions %}
<tr>
<td>{{ perm }}</td>
<td class="center"><a href="{% url 'photo:permissions_delete' model=perm.get_class_name pid=perm.id %}" class="confirm"><i class="red fa fa-trash-o"></i></a><td>
<td class="center"><a href="{% url 'photo:permissions_delete' model=perm.get_class_name pid=perm.id %}" class="confirm"><i class="red fa fa-trash-alt"></i></a><td>
</tr>
{% endfor %}