permanence.py 24.4 KB
Newer Older
Patrick's avatar
Patrick committed
1
2
3
4
5
6
7
8
# -*- coding: utf-8
from __future__ import unicode_literals

from django.conf import settings
from django.core import urlresolvers
from django.core.cache import cache
from django.db import models
from django.db.models import F
9
10
from django.db.models.signals import post_init
from django.dispatch import receiver
Patrick's avatar
Patrick committed
11
from django.utils import timezone
12
from django.utils import translation
Patrick's avatar
Patrick committed
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from djangocms_text_ckeditor.fields import HTMLField
from menus.menu_pool import menu_pool
from parler.models import TranslatableModel, TranslatedFields
from parler.models import TranslationDoesNotExist

import configuration
import customer
import deliveryboard
import invoice
import permanenceboard
import producer
import purchase
import repanier.apps
from repanier.const import *
from repanier.tools import get_full_status_display


@python_2_unicode_compatible
class Permanence(TranslatableModel):
    translations = TranslatedFields(
Patrick's avatar
Patrick committed
35
        short_name=models.CharField(_("offer name"), max_length=50, blank=True),
Patrick's avatar
Patrick committed
36
        offer_description=HTMLField(_("offer_description"),
37
                                    configuration='CKEDITOR_SETTINGS_MODEL2',
Patrick's avatar
Patrick committed
38
39
40
41
42
                                    help_text=_(
                                        "This message is send by mail to all customers when opening the order or on top "),
                                    blank=True, default=EMPTY_STRING),
        invoice_description=HTMLField(
            _("invoice_description"),
43
            configuration='CKEDITOR_SETTINGS_MODEL2',
Patrick's avatar
Patrick committed
44
45
46
            help_text=_(
                'This message is send by mail to all customers having bought something when closing the permanence.'),
            blank=True, default=EMPTY_STRING),
47
        cache_part_d=HTMLField(configuration='CKEDITOR_SETTINGS_MODEL2', blank=True, default=EMPTY_STRING)
Patrick's avatar
Patrick committed
48
49
50
51
52
53
54
55
    )

    status = models.CharField(
        max_length=3,
        choices=LUT_PERMANENCE_STATUS,
        default=PERMANENCE_PLANNED,
        verbose_name=_("permanence_status"),
        help_text=_('status of the permanence from planned, orders opened, orders closed, send, done'))
Patrick's avatar
Patrick committed
56
    permanence_date = models.DateField(_("date"), db_index=True)
Patrick's avatar
Patrick committed
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
    payment_date = models.DateField(_("payment_date"), blank=True, null=True, db_index=True)
    producers = models.ManyToManyField(
        'Producer',
        verbose_name=_("producers"),
        blank=True
    )
    with_delivery_point = models.BooleanField(
        _("with_delivery_point"), default=False)
    automatically_closed = models.BooleanField(
        _("automatically_closed"), default=False)
    is_updated_on = models.DateTimeField(
        _("is_updated_on"), auto_now=True)
    highest_status = models.CharField(
        max_length=3,
        choices=LUT_PERMANENCE_STATUS,
        default=PERMANENCE_PLANNED,
        verbose_name=_("highest permanence_status"),
        help_text=_('status of the permanence from planned, orders opened, orders closed, send, done'))

    def get_producers(self):
        if self.id is not None:
            if len(self.producers.all()) > 0:
                if self.status == PERMANENCE_PLANNED:
                    changelist_url = urlresolvers.reverse(
                        'admin:repanier_product_changelist',
                    )
                    link = []
                    for p in self.producers.all():
                        link.append(
86
                            '<a href=%s?producer=%d>&nbsp;%s</a>' % (
Patrick's avatar
Patrick committed
87
                                changelist_url, p.id, p.short_profile_name))
88
                    return '<div class="wrap-text">%s</div>' % ", ".join(link)
Patrick's avatar
Patrick committed
89
                elif self.status == PERMANENCE_PRE_OPEN:
90
                    return '<div class="wrap-text">%s</div>' % ", ".join([p.short_profile_name + " (" + p.phone1 + ")" for p in self.producers.all()])
Patrick's avatar
Patrick committed
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
                elif self.status in [PERMANENCE_OPENED, PERMANENCE_CLOSED]:
                    close_offeritem_changelist_url = urlresolvers.reverse(
                        'admin:repanier_offeritemclosed_changelist',
                    )
                    send_offeritem_changelist_url = urlresolvers.reverse(
                        'admin:repanier_offeritemsend_changelist',
                    )
                    send_customer_changelist_url = urlresolvers.reverse(
                        'admin:repanier_customersend_changelist',
                    )

                    link = []
                    for p in self.producers.all().only("id"):
                        pi = invoice.ProducerInvoice.objects.filter(producer_id=p.id, permanence_id=self.id) \
                            .order_by('?').first()
                        if pi is not None:
                            if pi.status > PERMANENCE_OPENED:
                                label = ('%s (%s) %s' % (p.short_profile_name, pi.get_total_price_with_tax(), LOCK_UNICODE)).replace(' ',
                                                                                                            '&nbsp;')
                                if pi.status == PERMANENCE_SEND:
                                    if p.invoice_by_basket:
                                        offeritem_changelist_url = send_customer_changelist_url
                                    else:
                                        offeritem_changelist_url = send_offeritem_changelist_url
                                else:
                                    offeritem_changelist_url = close_offeritem_changelist_url
                            else:
                                label = ('%s (%s) ' % (p.short_profile_name, pi.get_total_price_with_tax())).replace(' ', '&nbsp;')
                                offeritem_changelist_url = close_offeritem_changelist_url
                        else:
                            label = ('%s ' % (p.short_profile_name,)).replace(' ', '&nbsp;')
                            offeritem_changelist_url = close_offeritem_changelist_url
                        link.append(
124
                            '<a href="%s?permanence=%s&producer=%d">%s</a>' % (
Patrick's avatar
Patrick committed
125
                                offeritem_changelist_url, self.id, p.id, label))
126
                    return '<div class="wrap-text">%s</div>' % ", ".join(link)
Patrick's avatar
Patrick committed
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
                elif self.status == PERMANENCE_SEND:
                    send_offeritem_changelist_url = urlresolvers.reverse(
                        'admin:repanier_offeritemsend_changelist',
                    )
                    send_customer_changelist_url = urlresolvers.reverse(
                        'admin:repanier_customersend_changelist',
                    )
                    link = []
                    for p in self.producers.all().only("id", "short_profile_name", "invoice_by_basket"):
                        if p.invoice_by_basket:
                            changelist_url = send_customer_changelist_url
                        else:
                            changelist_url = send_offeritem_changelist_url
                        pi = invoice.ProducerInvoice.objects.filter(producer_id=p.id, permanence_id=self.id) \
                            .order_by('?').first()
142
                        # Important : no target="_blank"
Patrick's avatar
Patrick committed
143
144
145
                        if pi is not None:
                            label = '%s (%s) %s' % (p.short_profile_name, pi.get_total_price_with_tax(), LOCK_UNICODE)
                            link.append(
146
                                '<a href="%s?permanence=%d&producer=%d">&nbsp;%s</a>' % (
Patrick's avatar
Patrick committed
147
148
149
150
                                    changelist_url, self.id, p.id, label.replace(' ', '&nbsp;')
                                ))
                        else:
                            link.append(
151
                                '<a href="%s?permanence=%d&producer=%d">&nbsp;%s</a>' % (
Patrick's avatar
Patrick committed
152
153
154
                                    changelist_url, self.id, p.id, p.short_profile_name.replace(' ', '&nbsp;')
                                ))

155
                    return '<div class="wrap-text">%s</div>' % ", ".join(link)
Patrick's avatar
Patrick committed
156
157
158
159
                elif self.status in [PERMANENCE_DONE, PERMANENCE_ARCHIVED]:
                    link = []
                    for pi in invoice.ProducerInvoice.objects.filter(permanence_id=self.id).select_related(
                            "producer").order_by('producer'):
160
                        label = "%s (%s)" % (
Patrick's avatar
Patrick committed
161
                            pi.producer.short_profile_name,
162
                            pi.get_total_price_with_tax()
Patrick's avatar
Patrick committed
163
                        )
164
165
                        # Important : target="_blank" because the invoices must be displayed without the cms_toolbar
                        # Such that they can be accessed by the producer and by the staff
Patrick's avatar
Patrick committed
166
                        link.append(
167
                            '<a href="%s?producer=%d" target="_blank">%s</a>'
Patrick's avatar
Patrick committed
168
169
170
171
172
173
                            % (
                                urlresolvers.reverse('producer_invoice_view', args=(pi.id,)),
                                pi.producer_id,
                                label.replace(' ', '&nbsp;')))
                    producers = ", ".join(link)
                    msg_html = """
174
                        <div class="wrap-text"><button
Patrick's avatar
Patrick committed
175
176
177
178
179
180
181
182
                        onclick="django.jQuery('#id_get_producers_%d').toggle();
                            if(django.jQuery(this).html()=='%s'){
                                django.jQuery(this).html('%s')
                            }else{
                                django.jQuery(this).html('%s')
                            };
                            return false;"
                        >%s</button>
183
                        <div id="id_get_producers_%d" style="display:none;">%s</div></div>
Patrick's avatar
Patrick committed
184
185
186
187
188
                    """ % (
                        self.id, _("Show"), _("Hide"), _("Show"), _("Show"), self.id, producers
                    )
                    return msg_html
                else:
189
                    return '<div class="wrap-text">%s</div>' % ", ".join([p.short_profile_name
Patrick's avatar
Patrick committed
190
191
192
193
194
                                           for p in
                                           producer.Producer.objects.filter(
                                               producerinvoice__permanence_id=self.id).only(
                                               'short_profile_name')])
            else:
195
                return '<div class="wrap-text">%s</div>' % _("No offer")
Patrick's avatar
Patrick committed
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
        return "?"

    get_producers.short_description = (_("producers in this permanence"))
    get_producers.allow_tags = True

    def get_customers(self):
        if self.id is not None:
            if self.status in [
                PERMANENCE_OPENED, PERMANENCE_CLOSED, PERMANENCE_SEND
            ]:
                changelist_url = urlresolvers.reverse(
                    'admin:repanier_purchase_changelist',
                )
                link = []
                for ci in invoice.CustomerInvoice.objects.filter(permanence_id=self.id).select_related(
                    "customer").order_by('customer'):
212
                    ci_customer = ci.customer
Patrick's avatar
Patrick committed
213
                    if ci.is_order_confirm_send:
214
215
                        label = '%s%s (%s) %s%s' % (
                            "<b><i>" if ci_customer.is_group else EMPTY_STRING,
Patrick's avatar
Patrick committed
216
                            ci.customer.short_basket_name, ci.get_total_price_with_tax(customer_who_pays=True),
217
218
219
                            ci.get_is_order_confirm_send_display(),
                            "</i></b>" if ci_customer.is_group else EMPTY_STRING,
                        )
Patrick's avatar
Patrick committed
220
                    else:
221
222
                        label = '%s%s (%s) %s%s' % (
                            "<b><i>" if ci_customer.is_group else EMPTY_STRING,
Patrick's avatar
Patrick committed
223
                            ci.customer.short_basket_name, ci.total_price_with_tax,
224
225
226
                            ci.get_is_order_confirm_send_display(),
                            "</i></b>" if ci_customer.is_group else EMPTY_STRING,
                        )
227
                    # Important : no target="_blank"
Patrick's avatar
Patrick committed
228
                    link.append(
229
                        '<a href="%s?permanence=%d&customer=%d">%s</a>'
Patrick's avatar
Patrick committed
230
231
232
233
234
235
                        % (changelist_url, self.id, ci.customer_id, label.replace(' ', '&nbsp;')))
                customers = ", ".join(link)
            elif self.status == PERMANENCE_DONE:
                link = []
                for ci in invoice.CustomerInvoice.objects.filter(permanence_id=self.id).select_related(
                    "customer").order_by('customer'):
236
237
238
239
                    ci_customer = ci.customer
                    label = "%s%s (%s) %s%s" % (
                        "<b><i>" if ci_customer.is_group else EMPTY_STRING,
                        ci_customer.short_basket_name,
Patrick's avatar
Patrick committed
240
                        ci.get_total_price_with_tax(customer_who_pays=True),
241
242
                        ci.get_is_order_confirm_send_display(),
                        "</i></b>" if ci_customer.is_group else EMPTY_STRING,
Patrick's avatar
Patrick committed
243
                    )
244
245
                    # Important : target="_blank" because the invoices must be displayed without the cms_toolbar
                    # Such that they can be accessed by the customer and by the staff
Patrick's avatar
Patrick committed
246
                    link.append(
247
                        '<a href="%s?customer=%d" target="_blank">%s</a>'
Patrick's avatar
Patrick committed
248
249
250
251
252
253
254
255
256
257
258
259
260
261
                        % (
                            urlresolvers.reverse('customer_invoice_view', args=(ci.id,)),
                            ci.customer_id,
                            label.replace(' ', '&nbsp;')
                        )
                    )
                customers = ", ".join(link)
            else:
                customers = ", ".join([c.short_basket_name
                                       for c in
                                       customer.Customer.objects.filter(customerinvoice__permanence_id=self.id).only(
                                           'short_basket_name')])
            if len(customers) > 0:
                msg_html = """
262
                    <div class="wrap-text"><button
Patrick's avatar
Patrick committed
263
264
265
266
267
268
269
270
                    onclick="django.jQuery('#id_get_customers_%d').toggle();
                        if(django.jQuery(this).html()=='%s'){
                            django.jQuery(this).html('%s')
                        }else{
                            django.jQuery(this).html('%s')
                        };
                        return false;"
                    >%s</button>
271
                    <div id="id_get_customers_%d" style="display:none;">%s</div></div>
Patrick's avatar
Patrick committed
272
273
274
275
276
                """ % (
                    self.id, _("Show"), _("Hide"), _("Show"), _("Show"), self.id, customers
                )
                return msg_html
            else:
277
                return '<div class="wrap-text">%s</div>' % _("No purchase")
Patrick's avatar
Patrick committed
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
        return "?"

    get_customers.short_description = (_("customers in this permanence"))
    get_customers.allow_tags = True

    def get_board(self):
        if self.id is not None:
            permanenceboard_set = permanenceboard.PermanenceBoard.objects.filter(
                permanence=self, permanence_role__rght=F('permanence_role__lft') + 1
            ).order_by("permanence_role__tree_id", "permanence_role__lft")
            first_board = True
            board = EMPTY_STRING
            if permanenceboard_set:
                for permanenceboard_row in permanenceboard_set:
                    r_link = EMPTY_STRING
                    r = permanenceboard_row.permanence_role
                    if r:
                        r_url = urlresolvers.reverse(
                            'admin:repanier_lut_permanencerole_change',
                            args=(r.id,)
                        )
                        r_link = '<a href="' + r_url + \
300
                                 '" target="_blank">' + r.short_name.replace(' ', '&nbsp;') + '</a>'
Patrick's avatar
Patrick committed
301
302
303
304
305
306
307
308
                    c_link = EMPTY_STRING
                    c = permanenceboard_row.customer
                    if c:
                        c_url = urlresolvers.reverse(
                            'admin:repanier_customer_change',
                            args=(c.id,)
                        )
                        c_link = '&nbsp;->&nbsp;<a href="' + c_url + \
309
                                 '" target="_blank">' + c.short_basket_name.replace(' ', '&nbsp;') + '</a>'
Patrick's avatar
Patrick committed
310
311
312
313
314
315
316
                    if not first_board:
                        board += '<br/>'
                    board += r_link + c_link
                    first_board = False
            if not first_board:
                # At least one role is defined in the permanence board
                msg_html = """
317
                    <div class="wrap-text"><button
Patrick's avatar
Patrick committed
318
319
320
321
322
323
324
325
                    onclick="django.jQuery('#id_get_board_%d').toggle();
                        if(django.jQuery(this).html()=='%s'){
                            django.jQuery(this).html('%s')
                        }else{
                            django.jQuery(this).html('%s')
                        };
                        return false;"
                    >%s</button>
326
                    <div id="id_get_board_%d" style="display:none;">%s</div></div>
Patrick's avatar
Patrick committed
327
328
329
330
331
                """ % (
                    self.id, _("Show"), _("Hide"), _("Show"), _("Show"), self.id, board
                )
                return msg_html
            else:
332
                return '<div class="wrap-text">%s</div>' % _("Empty board")
Patrick's avatar
Patrick committed
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
        return "?"

    get_board.short_description = (_("permanence board"))
    get_board.allow_tags = True

    def set_status(self, new_status, all_producers=True, producers_id=None, update_payment_date=False,
                   payment_date=None, allow_downgrade=True):
        if all_producers:
            now = timezone.now().date()
            self.is_updated_on = now
            self.status = new_status
            if self.highest_status < new_status:
                self.highest_status = new_status
            if update_payment_date:
                if payment_date is None:
                    self.payment_date = now
                else:
                    self.payment_date = payment_date
                self.save(
                    update_fields=['status', 'is_updated_on', 'highest_status', 'with_delivery_point', 'payment_date'])
            else:
                self.save(update_fields=['status', 'is_updated_on', 'with_delivery_point', 'highest_status'])
            menu_pool.clear()
            cache.clear()
            if new_status == PERMANENCE_WAIT_FOR_OPEN:
                self.with_delivery_point = deliveryboard.DeliveryBoard.objects.filter(
                    permanence_id=self.id
                ).order_by('?').exists()
                if self.with_delivery_point and not repanier.apps.REPANIER_SETTINGS_CUSTOMERS_MUST_CONFIRM_ORDERS:
                    config = configuration.Configuration.objects.filter(id=DECIMAL_ONE).first()
                    # Important : Customer must confirm order if deliveries points are used
                    config.customers_must_confirm_orders = True
                    config.save()
                for a_producer in producer.Producer.objects.filter(
                        permanence=self.id
                ).only('id').order_by('?'):
                    # Create ProducerInvoice to be able to close those producer on demand
                    if not invoice.ProducerInvoice.objects.filter(
                            permanence_id=self.id,
                            producer_id=a_producer.id
                    ).order_by('?').exists():
                        invoice.ProducerInvoice.objects.create(
                            permanence_id=self.id,
                            producer_id=a_producer.id
                        )
            if self.with_delivery_point:
                qs = deliveryboard.DeliveryBoard.objects.filter(
                    permanence_id=self.id
                ).exclude(status=new_status).order_by('?')
                for delivery_point in qs:
                    if allow_downgrade or delivery_point.status < new_status:
                        # --> or delivery_point.status < new_status -->
                        # Set new status except if PERMANENCE_SEND, PERMANENCE_WAIT_FOR_SEND
                        #  -> PERMANENCE_CLOSED, PERMANENCE_WAIT_FOR_CLOSED
                        # This occur if directly sending order of a opened delivery point
                        delivery_point.set_status(new_status)
            invoice.CustomerInvoice.objects.filter(
                permanence_id=self.id,
                delivery__isnull=True
            ).order_by('?').update(
                status=new_status
            )
            purchase.Purchase.objects.filter(
                permanence_id=self.id,
                customer_invoice__delivery__isnull=True
            ).order_by('?').update(
                status=new_status)
            invoice.ProducerInvoice.objects.filter(
                permanence_id=self.id
            ).order_by('?').update(
                status=new_status
            )
            if update_payment_date:
                if payment_date is None:
                    self.payment_date = now
                else:
                    self.payment_date = payment_date
                self.save(
                    update_fields=['status', 'is_updated_on', 'highest_status', 'with_delivery_point', 'payment_date'])
            else:
                self.save(update_fields=['status', 'is_updated_on', 'with_delivery_point', 'highest_status'])
            menu_pool.clear()
            cache.clear()
        else:
            # /!\ If one delivery point has been closed, I may not close anymore by producer
            purchase.Purchase.objects.filter(permanence_id=self.id, producer__in=producers_id).order_by('?').update(
                status=new_status)
            invoice.ProducerInvoice.objects.filter(permanence_id=self.id, producer__in=producers_id).order_by(
                '?').update(status=new_status)

    def get_full_status_display(self):
        return get_full_status_display(self)

    get_full_status_display.short_description = (_("permanence_status"))
    get_full_status_display.allow_tags = True

    def get_permanence_display(self, with_status=True):
430
431
432
433
        permanence_display = '%s%s' % (
            repanier.apps.REPANIER_SETTINGS_PERMANENCE_ON_NAME,
            self.permanence_date.strftime(settings.DJANGO_SETTINGS_DATE)
        )
Patrick's avatar
Patrick committed
434
        try:
435
436
            if self.short_name:
                permanence_display = "%s" % self.short_name
Patrick's avatar
Patrick committed
437
438
        except TranslationDoesNotExist:
            pass
439

Patrick's avatar
Patrick committed
440
441
442
443
444
445
446
447
448
449
450
        if with_status:
            if self.with_delivery_point:
                if self.status == PERMANENCE_OPENED:
                    deliveries_count = 0
                else:
                    deliveries_qs = deliveryboard.DeliveryBoard.objects.filter(permanence_id=self.id,
                                                                               status=PERMANENCE_OPENED).order_by('?')
                    deliveries_count = deliveries_qs.count()
            else:
                deliveries_count = 0
            if deliveries_count == 0:
451
452
                return "%s - %s" % (permanence_display, self.get_status_display())
        return permanence_display
Patrick's avatar
Patrick committed
453
454

    def get_permanence_customer_display(self, with_status=True):
455
        permanence_display = self.get_permanence_display(with_status=False)
Patrick's avatar
Patrick committed
456
457
458
459
460
461
462
463
464
465
466
467
468
469
        if with_status:
            if self.with_delivery_point:
                if self.status == PERMANENCE_OPENED:
                    deliveries_count = 0
                else:
                    deliveries_qs = deliveryboard.DeliveryBoard.objects.filter(
                        permanence_id=self.id,
                        status=PERMANENCE_OPENED
                    ).order_by('?')
                    deliveries_count = deliveries_qs.count()
            else:
                deliveries_count = 0
            if deliveries_count == 0:
                if self.status != PERMANENCE_SEND:
470
                    return "%s - %s" % (permanence_display, self.get_status_display())
Patrick's avatar
Patrick committed
471
                else:
472
473
                    return "%s - %s" % (permanence_display, _('orders closed'))
        return permanence_display
Patrick's avatar
Patrick committed
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498

    def __str__(self):
        return self.get_permanence_display(with_status=False)

    class Meta:
        verbose_name = repanier.apps.REPANIER_SETTINGS_PERMANENCE_NAME or _("permanence")
        verbose_name_plural = repanier.apps.REPANIER_SETTINGS_PERMANENCES_NAME or _("permanences")

        index_together = [
            ["permanence_date"],
        ]


class PermanenceInPreparation(Permanence):
    class Meta:
        proxy = True
        verbose_name = _("X in preparation")
        verbose_name_plural = _("Xs in preparation")


class PermanenceDone(Permanence):
    class Meta:
        proxy = True
        verbose_name = _("X done")
        verbose_name_plural = _("Xs done")