diff --git a/src/frontend/js/multidatespicker.js b/src/frontend/js/multidatespicker.js index a5dceed8..0f689a26 100644 --- a/src/frontend/js/multidatespicker.js +++ b/src/frontend/js/multidatespicker.js @@ -11,23 +11,70 @@ $(function() { if (elem) initial_dates.push(elem); }); + var ordered_dates = []; + $.each( + ($("#delivery_dates").data("orderedDates") || '').split('|'), + function (idx, elem) { + if (elem) ordered_dates.push(elem); + }); + var ordered_date_tooltip = $("#delivery_dates").data("orderedDateTooltip"); + + + // Make it possible to disable the highlight of today. + // There are already two states: dates to create a new order, and dates with existing orders. + // If we keep the highlight of today as the 3rd state, it would be confusing to the user. + var __old_updateDatepicker = $.datepicker._updateDatepicker; + $.datepicker._updateDatepicker = function (inst) { + var retVal = __old_updateDatepicker.apply(this, arguments); + var disableTodayHighlight = this._get(inst, 'disableTodayHighlight'); + if (disableTodayHighlight) { + // Remove display classes + var container = inst.dpDiv; + var today_td = container.find('.ui-datepicker-today'); + if (today_td) { + var today_a = today_td.find('a'); + today_a.removeClass('ui-state-highlight ui-state-active ui-state-hover'); + } + } + return retVal; + }; + // -- // Init multidatepicker on input directly would be simpler, // but a glitch would appear, so I init multidatespicker on an empty div#delivery_dates // and link it to an HTML input#id_delivery_dates using altField option // @see: https://github.com/dubrox/Multiple-Dates-Picker-for-jQuery-UI/issues/162 - $('#delivery_dates').multiDatesPicker({ - addDates: initial_dates, - dateFormat: "yy-mm-dd", + var mdpConfig = { + dateFormat: "yy-mm-dd", // Example: 2017(yy)-03(mm)-24(dd). separator: "|", minDate: 0, numberOfMonths: 2, + // Don't highlight today. It confuses the user. See the overriden _updateDatepicker method. + disableTodayHighlight: true, + // At the server side, the max length of the altField is 200. + // Therefore 200/11 = 18. + maxPicks: 18, altField: '#id_delivery_dates', onSelect: function (dateText, inst) { $('#form_create_batch #id_is_submit').val("0"); $('#form_create_batch').submit(); + }, + beforeShowDay: function (date) { + var dateStr = $.datepicker.formatDate('yy-mm-dd', date); + if (ordered_dates.indexOf(dateStr) !== -1) { + return [true, 'ordered-date', ordered_date_tooltip]; + } else { + return [true, '', null]; + } } - }); + }; + if ($.isArray(initial_dates) && initial_dates.length > 0) { + // Don't add this when initial_dates is an empty array. + // MultiDatesPicker will raise an error in this case. + mdpConfig.addDates = initial_dates; + } + $('#delivery_dates').multiDatesPicker(mdpConfig); + $('.ui.accordion.meals').show(); // fill in defaults diff --git a/src/frontend/scss/base/multidatespicker.scss b/src/frontend/scss/base/multidatespicker.scss index 94a824d4..776d9d24 100644 --- a/src/frontend/scss/base/multidatespicker.scss +++ b/src/frontend/scss/base/multidatespicker.scss @@ -2,6 +2,15 @@ table.ui-datepicker-calendar {border-collapse: separate;} .ui-datepicker-calendar td {border: 1px solid transparent!important;} /* end: jQuery UI Datepicker moving pixels fix */ + + + +.ui-datepicker .ui-datepicker-calendar .ordered-date a { + border: 1px solid #fcefa1; + background: #fbf9ee none; +} + + /* begin: jQuery UI Datepicker emphasis on selected dates */ .ui-datepicker .ui-datepicker-calendar .ui-state-highlight a { background: #fbbd08 none; /* a color that fits the widget theme */ diff --git a/src/order/locale/en/LC_MESSAGES/django.po b/src/order/locale/en/LC_MESSAGES/django.po index ebede25b..28bb2093 100644 --- a/src/order/locale/en/LC_MESSAGES/django.po +++ b/src/order/locale/en/LC_MESSAGES/django.po @@ -291,6 +291,10 @@ msgstr "" msgid "Place orders" msgstr "" +#: order/templates/order/create_batch.html:38 +msgid "An order already exists on this day." +msgstr "" + #: order/templates/order/create_batch.html:43 msgid "Order items" msgstr "" diff --git a/src/order/locale/fr/LC_MESSAGES/django.po b/src/order/locale/fr/LC_MESSAGES/django.po index 6ca7e096..5bea03d8 100644 --- a/src/order/locale/fr/LC_MESSAGES/django.po +++ b/src/order/locale/fr/LC_MESSAGES/django.po @@ -303,6 +303,10 @@ msgstr "Désolé, aucun résultat trouvé." msgid "Place orders" msgstr "Créer des commandes" +#: order/templates/order/create_batch.html:38 +msgid "An order already exists on this day." +msgstr "" + #: order/templates/order/create_batch.html:43 msgid "Order items" msgstr "Éléments de la commande" diff --git a/src/order/templates/order/create_batch.html b/src/order/templates/order/create_batch.html index c4576409..d3506b9c 100644 --- a/src/order/templates/order/create_batch.html +++ b/src/order/templates/order/create_batch.html @@ -34,7 +34,7 @@

{% trans 'Place orders' %}

-
{{ form.delivery_dates }} +
{{ form.delivery_dates }}
{% if delivery_dates %} diff --git a/src/order/tests.py b/src/order/tests.py index 46c7d358..48dc57ab 100644 --- a/src/order/tests.py +++ b/src/order/tests.py @@ -11,6 +11,7 @@ from django.test import TestCase from django.contrib.auth.models import User from django.urls import reverse, reverse_lazy +from django.utils import timezone from django.utils.translation import ugettext as _ from django.core.exceptions import ValidationError from django.core.management import call_command @@ -806,6 +807,41 @@ def test_view_post_success(self): ).count() self.assertEqual(created_orders, 2) + def test_view_display_ordered_dates(self): + """ + The user interface should be notified of such existing orders: + 1. The orders are of today or a later date. + 2. The orders should not be cancelled. + """ + today = timezone.datetime.today() + future = today + datetime.timedelta(days=1) + past = today - datetime.timedelta(days=1) + + client = self.episodic_client[1] + order1 = OrderFactory( + delivery_date=today, + status='O', + client=client + ) + order2 = OrderFactory( + delivery_date=future, + status='O', + client=client + ) + order0 = OrderFactory( + delivery_date=past, + status='O', + client=client + ) + response = self.client.post(reverse('order:create_batch'), { + 'client': self.episodic_client[1].pk, + 'is_submit': '0' + }) + self.assertEqual(response.status_code, 200) + self.assertContains(response, 'data-ordered-dates="{}|{}"'.format( + today.strftime('%Y-%m-%d'), future.strftime('%Y-%m-%d') + )) + def test_view_post_override_yes(self): # Create an order batch, create an order for the same day, # set an override date, ensure order was overridden diff --git a/src/order/views.py b/src/order/views.py index 82d1c64a..940e2a00 100644 --- a/src/order/views.py +++ b/src/order/views.py @@ -10,6 +10,7 @@ from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib import messages from django.urls import reverse_lazy, reverse +from django.utils import timezone from django.shortcuts import get_object_or_404 from extra_views import CreateWithInlinesView, UpdateWithInlinesView @@ -165,6 +166,18 @@ def get_context_data(self, **kwargs): else: meals_default_dict = dict(c.meals_schedule) context['client'] = c + + # The dates where an order already exists. + today = timezone.datetime.today() + ordered_dates = c.orders.filter( + delivery_date__gte=today + ).exclude(status='C').order_by('delivery_date').values_list( + 'delivery_date', flat=True + ) + context['ordered_dates'] = '|'.join(map( + lambda d: d.strftime('%Y-%m-%d'), + ordered_dates + )) else: meals_default_dict = dict( map(