tools.py 81.5 KB
Newer Older
Patrick's avatar
Patrick committed
1 2
# -*- coding: utf-8
from __future__ import unicode_literals
Patrick's avatar
Patrick committed
3 4

import calendar
5
import datetime
Patrick's avatar
Patrick committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19
import json
import time
import urllib2
from smtplib import SMTPRecipientsRefused

from django.conf import settings
from django.core import mail
from django.core import urlresolvers
from django.core.cache import cache
from django.core.mail import EmailMessage, mail_admins
from django.core.serializers.json import DjangoJSONEncoder
from django.db import transaction
from django.db.models import F
from django.db.models import Q, Sum
20
from django.http import Http404
Patrick's avatar
Patrick committed
21
from django.template.loader import render_to_string
Patrick's avatar
Patrick committed
22
from django.utils import timezone
Patrick's avatar
Patrick committed
23
from django.utils import translation
pi's avatar
pi committed
24
from django.utils.formats import number_format
Patrick's avatar
Patrick committed
25 26 27 28 29
from django.utils.safestring import mark_safe
from django.utils.text import slugify
from django.utils.translation import ugettext_lazy as _
from parler.models import TranslationDoesNotExist

Patrick's avatar
Patrick committed
30
import models
Patrick's avatar
Patrick committed
31 32 33
from const import *
from repanier import apps
from repanier.fields.RepanierMoneyField import RepanierMoney
Patrick's avatar
Patrick committed
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49


def ignore_exception(exception=Exception, default_val=0):
    """Returns a decorator that ignores an exception raised by the function it
    decorates.

    Using it as a decorator:

    @ignore_exception(ValueError)
    def my_function():
    pass

    Using it as a function wrapper:

    int_try_parse = ignore_exception(ValueError)(int)
    """
Patrick's avatar
Patrick committed
50

Patrick's avatar
Patrick committed
51 52 53 54 55 56
    def decorator(function):
        def wrapper(*args, **kwargs):
            try:
                return function(*args, **kwargs)
            except exception:
                return default_val
Patrick's avatar
Patrick committed
57

Patrick's avatar
Patrick committed
58
        return wrapper
Patrick's avatar
Patrick committed
59

Patrick's avatar
Patrick committed
60 61
    return decorator

Patrick's avatar
Patrick committed
62

Patrick's avatar
Patrick committed
63
sint = ignore_exception(ValueError)(int)
64
sboolean = ignore_exception(ValueError)(bool)
Patrick's avatar
Patrick committed
65 66 67 68 69 70 71 72 73 74


def next_row(query_iterator):
    try:
        return next(query_iterator)
    except StopIteration:
        # No rows were found, so do nothing.
        return None


Patrick's avatar
Change  
Patrick committed
75 76 77 78 79
def emails_of_testers():
    tester_qs = models.Staff.objects.filter(is_tester=True, is_active=True).order_by("id")
    testers = []
    for tester in tester_qs:
        testers.append(tester.user.email)
80
    return list(set(testers))
Patrick's avatar
Change  
Patrick committed
81 82


83
def send_email(email=None, from_name=EMPTY_STRING, track_customer_on_error=False):
Patrick's avatar
Patrick committed
84 85 86 87
    if settings.DJANGO_SETTINGS_DEMO:
        email.to = [DEMO_EMAIL]
        email.cc = []
        email.bcc = []
88
        send_email_with_error_log(email, from_name)
89 90
    else:
        from repanier.apps import REPANIER_SETTINGS_TEST_MODE
Patrick's avatar
Change  
Patrick committed
91 92 93 94
        if REPANIER_SETTINGS_TEST_MODE:
            email.to = emails_of_testers()
            if len(email.to) > 0:
                # Send the mail only if there is at least one tester
95 96
                email.cc = []
                email.bcc = []
97
                send_email_with_error_log(email, from_name)
98
            else:
Patrick's avatar
Change  
Patrick committed
99 100 101
                print('############################ test mode, without tester...')
        else:
            if settings.DEBUG:
102 103 104 105 106
                print("to : %s" % email.to)
                print("cc : %s" % email.cc)
                print("bcc : %s" % email.bcc)
                print("subject : %s" % slugify(email.subject))
            else:
Patrick's avatar
Change  
Patrick committed
107 108
                # chunks = [email.to[x:x+100] for x in xrange(0, len(email.to), 100)]
                # for chunk in chunks:
109 110 111 112 113
                # Remove duplicates
                send_email_to = list(set(email.to + email.cc + email.bcc))
                email.cc = []
                email.bcc = []
                if len(send_email_to) >= 1:
Patrick's avatar
Change  
Patrick committed
114
                    customer = None
115 116
                    for email_to in send_email_to:
                        email.to = [email_to]
Patrick's avatar
Change  
Patrick committed
117 118 119
                        if track_customer_on_error:
                            # If the email is conained both in user__email and customer__email2
                            # select the customer based on user__email
120
                            customer = models.Customer.objects.filter(user__email=email_to).order_by('?').first()
Patrick's avatar
Change  
Patrick committed
121
                            if customer is None:
122 123 124
                                customer = models.Customer.objects.filter(email2=email_to).order_by('?').first()
                        send_email_with_error_log(email, from_name, customer)
                        time.sleep(1)
Patrick's avatar
Patrick committed
125 126


127
def send_email_with_error_log(email, from_name=EMPTY_STRING, customer=None):
Patrick's avatar
Patrick committed
128 129 130
    with mail.get_connection() as connection:
        email.connection = connection
        message = EMPTY_STRING
131 132
        if not email.from_email.endswith(settings.DJANGO_SETTINGS_ALLOWED_MAIL_EXTENSION):
            email.reply_to = [email.from_email]
133
            email.from_email = "%s <%s>" % (from_name or apps.REPANIER_SETTINGS_GROUP_NAME, settings.DEFAULT_FROM_EMAIL)
134
        else:
135
            email.from_email = "%s <%s>" % (from_name or apps.REPANIER_SETTINGS_GROUP_NAME, email.from_email)
Patrick's avatar
Patrick committed
136 137
        try:
            print("################################## send_email")
138
            reply_to = "reply_to : %s" % email.reply_to
Patrick's avatar
Patrick committed
139 140 141 142
            to = "to : %s" % email.to
            cc = "cc : %s" % email.cc
            bcc = "bcc : %s" % email.bcc
            subject = "subject : %s" % slugify(email.subject)
143
            print(reply_to)
Patrick's avatar
Patrick committed
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
            print(to)
            print(cc)
            print(bcc)
            print(subject)
            message = "%s\n%s\n%s\n%s" % (to, cc, bcc, subject)
            email.send()
            valid_email = True
        except SMTPRecipientsRefused as error_str:
            valid_email = False
            print("################################## send_email error")
            print(error_str)
            time.sleep(2)
            connection = mail.get_connection()
            connection.open()
            mail_admins("ERROR", "%s\n%s" % (message, error_str), connection=connection)
            connection.close()
        except Exception as error_str:
            valid_email = None
            print("################################## send_email error")
            print(error_str)
            time.sleep(2)
            connection = mail.get_connection()
            connection.open()
            mail_admins("ERROR", "%s\n%s" % (message, error_str), connection=connection)
            connection.close()
        if customer is not None:
            customer.valid_email = valid_email
            customer.save(update_fields=['valid_email'])
        print("##################################")


def send_email_to_who(is_email_send, board=False):
    if not is_email_send:
        if board:
Patrick's avatar
Change  
Patrick committed
178 179 180 181
            if apps.REPANIER_SETTINGS_TEST_MODE:
                return True, _("This email will be sent to %s.") % ', '.join(emails_of_testers())
            else:
                if settings.DEBUG:
Patrick's avatar
Patrick committed
182 183
                    return False, _("No email will be sent.")
                else:
Patrick's avatar
Change  
Patrick committed
184
                    return True, _("This email will be sent to the staff.")
Patrick's avatar
Patrick committed
185 186
        else:
            return False, _("No email will be sent.")
Patrick's avatar
Patrick committed
187
    else:
Patrick's avatar
Change  
Patrick committed
188 189
        if apps.REPANIER_SETTINGS_TEST_MODE:
            return True, _("This email will be sent to %s.") % ', '.join(emails_of_testers())
Patrick's avatar
Patrick committed
190
        else:
Patrick's avatar
Change  
Patrick committed
191 192
            if settings.DEBUG:
                return False, _("No email will be sent.")
Patrick's avatar
Patrick committed
193
            else:
Patrick's avatar
Change  
Patrick committed
194 195 196 197
                if board:
                    return True, _("This email will be sent to the preparation team and the staff.")
                else:
                    return True, _("This email will be sent to customers or producers depending of the case.")
Patrick's avatar
Patrick committed
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219


def send_sms(sms_nr=None, sms_msg=None):
    try:
        if sms_nr is not None and sms_msg is not None:
            valid_nr = "0"
            i = 0
            while i < len(sms_nr) and not sms_nr[i] == '4':
                i += 1
            while i < len(sms_nr):
                if '0' <= sms_nr[i] <= '9':
                    valid_nr += sms_nr[i]
                i += 1
            if len(valid_nr) == 10 \
                    and apps.REPANIER_SETTINGS_SMS_GATEWAY_MAIL is not None \
                    and len(apps.REPANIER_SETTINGS_SMS_GATEWAY_MAIL) > 0:
                # Send SMS with free gateway : Sms Gateway - Android.
                email = EmailMessage(valid_nr, sms_msg, "no-reply@repanier.be",
                                     [apps.REPANIER_SETTINGS_SMS_GATEWAY_MAIL, ])
                send_email(email=email)
    except:
        pass
Patrick's avatar
Patrick committed
220 221 222 223


def get_signature(is_reply_to_order_email=False, is_reply_to_invoice_email=False):
    sender_email = None
Patrick's avatar
Patrick committed
224 225
    sender_function = EMPTY_STRING
    signature = EMPTY_STRING
Patrick's avatar
Patrick committed
226
    cc_email_staff = []
Patrick's avatar
Patrick committed
227
    for staff in models.Staff.objects.filter(is_active=True).order_by('?'):
Patrick's avatar
Patrick committed
228 229
        if (is_reply_to_order_email and staff.is_reply_to_order_email) \
                or (is_reply_to_invoice_email and staff.is_reply_to_invoice_email):
230
            cc_email_staff.append(staff.user.email)
Patrick's avatar
Patrick committed
231
            sender_email = staff.user.email
Patrick's avatar
Patrick committed
232 233 234 235
            try:
                sender_function = staff.long_name
            except TranslationDoesNotExist:
                sender_function = EMPTY_STRING
Patrick's avatar
Patrick committed
236 237 238 239 240 241
            r = staff.customer_responsible
            if r:
                if r.long_basket_name:
                    signature = "%s - %s" % (r.long_basket_name, r.phone1)
                else:
                    signature = "%s - %s" % (r.short_basket_name, r.phone1)
Patrick's avatar
Patrick committed
242
                if r.phone2 and len(r.phone2.strip()) > 0:
Patrick's avatar
Patrick committed
243
                    signature += " / %s" % (r.phone2,)
244 245 246
        elif staff.is_coordinator:
            cc_email_staff.append(staff.user.email)

Patrick's avatar
Patrick committed
247
    if sender_email is None:
Patrick's avatar
Patrick committed
248
        sender_email = settings.DEFAULT_FROM_EMAIL
Patrick's avatar
Patrick committed
249
    return sender_email, sender_function, signature, cc_email_staff
pi's avatar
pi committed
250

Patrick's avatar
Patrick committed
251

Patrick's avatar
Patrick committed
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
def get_board_composition(permanence_id):
    board_composition = EMPTY_STRING
    board_composition_and_description = EMPTY_STRING
    for permanenceboard in models.PermanenceBoard.objects.filter(
            permanence=permanence_id).order_by(
        "permanence_role__tree_id",
        "permanence_role__lft"
    ):
        r = permanenceboard.permanence_role
        c = permanenceboard.customer
        if c is not None:
            if c.phone2 is not None:
                c_part = "%s, <b>%s</b>, <b>%s</b>" % (c.long_basket_name, c.phone1, c.phone2)
            else:
                c_part = "%s, <b>%s</b>" % (c.long_basket_name, c.phone1)
            member = "<b>%s</b> : %s, %s<br/>" % (r.short_name, c_part, c.user.email)
            board_composition += member
            board_composition_and_description += "%s%s<br/>" % (member, r.description)

    return board_composition, board_composition_and_description


Patrick's avatar
Patrick committed
274
LENGTH_BY_PREFIX = [
275 276 277 278 279
    (0xC0, 2),  # first byte mask, total codepoint length
    (0xE0, 3),
    (0xF0, 4),
    (0xF8, 5),
    (0xFC, 6),
Patrick's avatar
Patrick committed
280 281
]

282

Patrick's avatar
Patrick committed
283 284
def codepoint_length(first_byte):
    if first_byte < 128:
285
        return 1  # ASCII
Patrick's avatar
Patrick committed
286 287
    for mask, length in LENGTH_BY_PREFIX:
        if first_byte & 0xF0 == mask:
288
            return length
Patrick's avatar
Patrick committed
289
        elif first_byte & 0xF8 == 0xF8:
290
            return length
Patrick's avatar
Patrick committed
291 292
    assert False, 'Invalid byte %r' % first_byte

293

Patrick's avatar
Patrick committed
294
def cap_to_bytes_length(unicode_text, byte_limit):
Patrick's avatar
Patrick committed
295
    utf8_bytes = unicode_text.encode("utf8")
Patrick's avatar
Patrick committed
296 297 298 299 300 301 302 303 304 305
    cut_index = 0
    while cut_index < len(utf8_bytes):
        step = codepoint_length(ord(utf8_bytes[cut_index]))
        if cut_index + step > byte_limit:
            # can't go a whole codepoint further, time to cut
            return utf8_bytes[:cut_index] + '...'
        else:
            cut_index += step
    return utf8_bytes

306

Patrick's avatar
Patrick committed
307
def cap(s, l):
Patrick's avatar
Patrick committed
308
    if s is not None:
309 310
        if not isinstance(s, basestring):
            s = str(s)
Patrick's avatar
Patrick committed
311
        s = s if len(s) <= l else s[0:l - 4] + '...'
312
        return s
Patrick's avatar
Patrick committed
313
    else:
314 315 316
        return None


317 318 319 320 321 322
def permanence_ok_or_404(permanence):
    if permanence is None:
        raise Http404
    if permanence.status not in [PERMANENCE_OPENED, PERMANENCE_CLOSED, PERMANENCE_SEND]:
        if permanence.status in [PERMANENCE_DONE, PERMANENCE_ARCHIVED]:
            if permanence.permanence_date < (
Patrick's avatar
Patrick committed
323
                        timezone.now() - datetime.timedelta(weeks=LIMIT_DISPLAYED_PERMANENCE)
324 325 326 327 328 329
            ).date():
                raise Http404
        else:
            raise Http404


330 331
def get_invoice_unit(order_unit=PRODUCT_ORDER_UNIT_PC, qty=0):
    if order_unit in [PRODUCT_ORDER_UNIT_KG, PRODUCT_ORDER_UNIT_PC_KG]:
Patrick's avatar
Patrick committed
332
        unit = _("/ kg")
333
    elif order_unit == PRODUCT_ORDER_UNIT_LT:
Patrick's avatar
Patrick committed
334
        unit = _("/ l")
Patrick's avatar
Patrick committed
335
    else:
336
        if qty < 2:
Patrick's avatar
Patrick committed
337
            unit = _("/ piece")
338
        else:
Patrick's avatar
Patrick committed
339
            unit = _("/ pieces")
340 341 342
    return unit


Patrick's avatar
Patrick committed
343
def get_preparator_unit(order_unit=PRODUCT_ORDER_UNIT_PC):
344
    # Used when producing the preparation list.
345 346
    if order_unit in [PRODUCT_ORDER_UNIT_PC, PRODUCT_ORDER_UNIT_PC_PRICE_KG, PRODUCT_ORDER_UNIT_PC_PRICE_LT,
                      PRODUCT_ORDER_UNIT_PC_PRICE_PC]:
Patrick's avatar
Patrick committed
347
        unit = _("Piece(s) :")
348
    elif order_unit in [PRODUCT_ORDER_UNIT_KG, PRODUCT_ORDER_UNIT_PC_KG]:
Patrick's avatar
Patrick committed
349
        unit = _("%s or kg :") % (apps.REPANIER_SETTINGS_CURRENCY_DISPLAY.decode('utf-8'),)
350
    elif order_unit == PRODUCT_ORDER_UNIT_LT:
Patrick's avatar
Patrick committed
351
        unit = _("L :")
352
    else:
Patrick's avatar
Patrick committed
353
        unit = _("Kg :")
354 355 356
    return unit


Patrick's avatar
Patrick committed
357 358
def get_base_unit(qty=0, order_unit=PRODUCT_ORDER_UNIT_PC, status=None):
    if order_unit == PRODUCT_ORDER_UNIT_KG or (status >= PERMANENCE_SEND and order_unit == PRODUCT_ORDER_UNIT_PC_KG):
359
        if qty == DECIMAL_ZERO:
Patrick's avatar
Patrick committed
360
            base_unit = EMPTY_STRING
361 362 363 364
        else:
            base_unit = _('kg')
    elif order_unit == PRODUCT_ORDER_UNIT_LT:
        if qty == DECIMAL_ZERO:
Patrick's avatar
Patrick committed
365
            base_unit = EMPTY_STRING
366 367 368 369
        else:
            base_unit = _('l')
    else:
        if qty == DECIMAL_ZERO:
Patrick's avatar
Patrick committed
370
            base_unit = EMPTY_STRING
371 372 373 374 375 376 377
        elif qty < 2:
            base_unit = _('piece')
        else:
            base_unit = _('pieces')
    return base_unit


Patrick's avatar
Patrick committed
378 379
def get_display(qty=0, order_average_weight=0, order_unit=PRODUCT_ORDER_UNIT_PC, unit_price_amount=None,
                for_customer=True, for_order_select=False, without_price_display=False):
380 381
    magnitude = None
    display_qty = True
382
    if order_unit == PRODUCT_ORDER_UNIT_KG:
383
        if qty == DECIMAL_ZERO:
Patrick's avatar
Patrick committed
384
            unit = EMPTY_STRING
385 386 387
        elif for_customer and qty < 1:
            unit = "%s" % (_('gr'))
            magnitude = 1000
388
        else:
389
            unit = "%s" % (_('kg'))
390
    elif order_unit == PRODUCT_ORDER_UNIT_LT:
391
        if qty == DECIMAL_ZERO:
Patrick's avatar
Patrick committed
392
            unit = EMPTY_STRING
393 394 395
        elif for_customer and qty < 1:
            unit = "%s" % (_('cl'))
            magnitude = 100
396
        else:
397
            unit = "%s" % (_('l'))
398
    elif order_unit in [PRODUCT_ORDER_UNIT_PC_KG, PRODUCT_ORDER_UNIT_PC_PRICE_KG]:
399
        # display_qty = not (order_average_weight == 1 and order_unit == PRODUCT_ORDER_UNIT_PC_PRICE_KG)
400 401 402
        average_weight = order_average_weight
        if for_customer:
            average_weight *= qty
Patrick's avatar
Patrick committed
403 404
        if order_unit == PRODUCT_ORDER_UNIT_PC_KG and unit_price_amount is not None:
            unit_price_amount *= order_average_weight
405
        if average_weight < 1:
Patrick's avatar
Patrick committed
406
            average_weight_unit = _('gr')
407 408
            average_weight *= 1000
        else:
Patrick's avatar
Patrick committed
409
            average_weight_unit = _('kg')
410 411 412 413 414 415 416
        decimal = 3
        if average_weight == int(average_weight):
            decimal = 0
        elif average_weight * 10 == int(average_weight * 10):
            decimal = 1
        elif average_weight * 100 == int(average_weight * 100):
            decimal = 2
Patrick's avatar
Patrick committed
417
        tilde = EMPTY_STRING
418 419
        if order_unit == PRODUCT_ORDER_UNIT_PC_KG:
            tilde = '~'
420
        if for_customer:
Patrick's avatar
Patrick committed
421
            if qty == DECIMAL_ZERO:
Patrick's avatar
Patrick committed
422
                unit = EMPTY_STRING
423
            else:
424
                if order_average_weight == 1 and order_unit == PRODUCT_ORDER_UNIT_PC_PRICE_KG:
425
                    unit = "%s%s %s" % (tilde, number_format(average_weight, decimal), average_weight_unit)
426 427
                else:
                    unit = "%s%s%s" % (tilde, number_format(average_weight, decimal), average_weight_unit)
428
        else:
Patrick's avatar
Patrick committed
429
            if qty == DECIMAL_ZERO:
Patrick's avatar
Patrick committed
430
                unit = EMPTY_STRING
431
            else:
432
                unit = "%s%s%s" % (tilde, number_format(average_weight, decimal), average_weight_unit)
433
    elif order_unit == PRODUCT_ORDER_UNIT_PC_PRICE_LT:
434
        display_qty = order_average_weight != 1
435 436 437
        average_weight = order_average_weight
        if for_customer:
            average_weight *= qty
438
        if average_weight < 1:
Patrick's avatar
Patrick committed
439
            average_weight_unit = _('cl')
440 441
            average_weight *= 100
        else:
Patrick's avatar
Patrick committed
442
            average_weight_unit = _('l')
443 444 445 446 447 448 449
        decimal = 3
        if average_weight == int(average_weight):
            decimal = 0
        elif average_weight * 10 == int(average_weight * 10):
            decimal = 1
        elif average_weight * 100 == int(average_weight * 100):
            decimal = 2
450
        if for_customer:
Patrick's avatar
Patrick committed
451
            if qty == DECIMAL_ZERO:
Patrick's avatar
Patrick committed
452
                unit = EMPTY_STRING
453
            else:
454 455 456 457
                if display_qty:
                    unit = "%s%s" % (number_format(average_weight, decimal), average_weight_unit)
                else:
                    unit = "%s %s" % (number_format(average_weight, decimal), average_weight_unit)
458
        else:
Patrick's avatar
Patrick committed
459
            if qty == DECIMAL_ZERO:
Patrick's avatar
Patrick committed
460
                unit = EMPTY_STRING
461
            else:
462
                unit = "%s%s" % (number_format(average_weight, decimal), average_weight_unit)
463
    elif order_unit == PRODUCT_ORDER_UNIT_PC_PRICE_PC:
464
        display_qty = order_average_weight != 1
465 466 467
        average_weight = order_average_weight
        if for_customer:
            average_weight *= qty
Patrick's avatar
Patrick committed
468
            if qty == DECIMAL_ZERO:
Patrick's avatar
Patrick committed
469
                unit = EMPTY_STRING
470
            else:
Patrick's avatar
Patrick committed
471
                if average_weight < 2:
472 473 474 475 476
                    pc_pcs = _('pc')
                else:
                    pc_pcs = _('pcs')
                if display_qty:
                    unit = "%s%s" % (number_format(average_weight, 0), pc_pcs)
Patrick's avatar
Patrick committed
477
                else:
478
                    unit = "%s %s" % (number_format(average_weight, 0), pc_pcs)
479
        else:
Patrick's avatar
Patrick committed
480
            if average_weight == DECIMAL_ZERO:
Patrick's avatar
Patrick committed
481
                unit = EMPTY_STRING
Patrick's avatar
Patrick committed
482
            elif average_weight < 2:
483
                unit = '%s %s' % (number_format(average_weight, 0), _('pc'))
484
            else:
485 486
                unit = '%s %s' % (number_format(average_weight, 0), _('pcs'))
    else:
Patrick's avatar
Patrick committed
487 488 489 490 491 492 493
        if for_order_select:
            if qty == DECIMAL_ZERO:
                unit = EMPTY_STRING
            elif qty < 2:
                unit = "%s" % (_('unit'))
            else:
                unit = "%s" % (_('units'))
494
        else:
Patrick's avatar
Patrick committed
495 496
            unit = EMPTY_STRING
    if unit_price_amount is not None:
497
        price_display = " = %s" % RepanierMoney(unit_price_amount * qty)
Patrick's avatar
Patrick committed
498
    else:
Patrick's avatar
Patrick committed
499
        price_display = EMPTY_STRING
500 501
    if magnitude is not None:
        qty *= magnitude
502 503 504 505 506 507 508
    decimal = 3
    if qty == int(qty):
        decimal = 0
    elif qty * 10 == int(qty * 10):
        decimal = 1
    elif qty * 100 == int(qty * 100):
        decimal = 2
Patrick's avatar
Patrick committed
509
    if for_customer or for_order_select:
510 511 512
        if unit:
            if display_qty:
                qty_display = "%s (%s)" % (number_format(qty, decimal), unit)
513
            else:
514 515 516
                qty_display = "%s" % unit
        else:
            qty_display = "%s" % number_format(qty, decimal)
Patrick's avatar
Patrick committed
517
    else:
518 519
        if unit:
            qty_display = "(%s)" % unit
520
        else:
Patrick's avatar
Patrick committed
521 522 523 524
            qty_display = EMPTY_STRING
    if without_price_display:
        return qty_display
    else:
525
        display = "%s%s" % (qty_display, price_display)
Patrick's avatar
Patrick committed
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
        return display


def on_hold_movement_message(customer, bank_not_invoiced=None, order_not_invoiced=None, total_price_with_tax=REPANIER_MONEY_ZERO):
    # If permanence_id is None, only "customer_on_hold_movement" is calculated
    if customer is None:
        customer_on_hold_movement = EMPTY_STRING
    else:
        if apps.REPANIER_SETTINGS_INVOICE:
            bank_not_invoiced = bank_not_invoiced if bank_not_invoiced is not None else customer.get_bank_not_invoiced()
            order_not_invoiced = order_not_invoiced if order_not_invoiced is not None else customer.get_order_not_invoiced()
            other_order_not_invoiced = order_not_invoiced - total_price_with_tax
        else:
            bank_not_invoiced = REPANIER_MONEY_ZERO
            other_order_not_invoiced = REPANIER_MONEY_ZERO

        if other_order_not_invoiced.amount != DECIMAL_ZERO or bank_not_invoiced.amount != DECIMAL_ZERO:
            if other_order_not_invoiced.amount != DECIMAL_ZERO:
                if bank_not_invoiced.amount == DECIMAL_ZERO:
                    customer_on_hold_movement = \
                        _('This balance does not take account of any unbilled sales %(other_order)s.') % {
                            'other_order': other_order_not_invoiced
                        }
                else:
                    customer_on_hold_movement = \
                        _(
                            'This balance does not take account of any unrecognized payments %(bank)s and any unbilled order %(other_order)s.') \
                        % {
                            'bank'       : bank_not_invoiced,
                            'other_order': other_order_not_invoiced
                        }
557
            else:
Patrick's avatar
Patrick committed
558 559 560 561 562
                customer_on_hold_movement = \
                    _(
                        'This balance does not take account of any unrecognized payments %(bank)s.') % {
                        'bank': bank_not_invoiced
                    }
563
        else:
Patrick's avatar
Patrick committed
564 565 566 567 568 569 570 571 572 573 574 575
            customer_on_hold_movement = EMPTY_STRING

    return customer_on_hold_movement


def payment_message(customer, permanence):
    # If permanence_id is None, only "customer_on_hold_movement" is calculated
    customer_last_balance = EMPTY_STRING
    if customer is None or permanence is None:
        customer_order_amount = EMPTY_STRING
        customer_payment_needed = EMPTY_STRING
        customer_on_hold_movement = EMPTY_STRING
576
    else:
Patrick's avatar
Patrick committed
577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600
        customer_invoice = models.CustomerInvoice.objects.filter(
            customer_id=customer.id,
            permanence_id=permanence.id
        ).order_by('?').first()
        if customer_invoice is None:
            total_price_with_tax = REPANIER_MONEY_ZERO
        else:
            total_price_with_tax = customer_invoice.get_total_price_with_tax()
        customer_order_amount = \
            _('The amount of your order is %(amount)s.') % {
                'amount': total_price_with_tax
            }
        if apps.REPANIER_SETTINGS_INVOICE:
            bank_not_invoiced = customer.get_bank_not_invoiced()
            order_not_invoiced = customer.get_order_not_invoiced()
            payment_needed = - (customer.balance - order_not_invoiced + bank_not_invoiced)
            # other_order_not_invoiced = order_not_invoiced - total_price_with_tax
        else:
            bank_not_invoiced = REPANIER_MONEY_ZERO
            order_not_invoiced = DECIMAL_ZERO
            payment_needed = total_price_with_tax
            # other_order_not_invoiced = REPANIER_MONEY_ZERO

        if customer_invoice.customer_id != customer_invoice.customer_who_pays_id:
601
            customer_payment_needed = '<font color="green">%s</font>' % (
Patrick's avatar
Patrick committed
602 603 604 605
                _('Invoices for this delivery point are sent to %(name)s who is responsible for collecting the payments.') % {
                    'name': customer_invoice.customer_who_pays.long_basket_name
                }
            )
606
            customer_on_hold_movement = EMPTY_STRING
Patrick's avatar
Patrick committed
607
        else:
608 609 610 611 612 613 614 615 616 617 618 619
            customer_on_hold_movement = on_hold_movement_message(customer, bank_not_invoiced, order_not_invoiced, total_price_with_tax)
            if apps.REPANIER_SETTINGS_INVOICE:
                if customer.balance.amount != DECIMAL_ZERO:
                    if customer.balance.amount < DECIMAL_ZERO:
                        balance = '<font color="#bd0926">%s</font>' % customer.balance
                    else:
                        balance = '%s' % customer.balance
                    customer_last_balance = \
                        _('The balance of your account as of %(date)s is %(balance)s.') % {
                            'date'   : customer.date_balance.strftime(settings.DJANGO_SETTINGS_DATE),
                            'balance': balance
                        }
Patrick's avatar
Patrick committed
620
                else:
621
                    customer_last_balance = EMPTY_STRING
Patrick's avatar
Patrick committed
622

623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
            bank_account_number = apps.REPANIER_SETTINGS_BANK_ACCOUNT
            if bank_account_number is not None:
                if payment_needed.amount > DECIMAL_ZERO:
                    if permanence.short_name:
                        communication = "%s (%s)" % (customer.short_basket_name, permanence.short_name)
                    else:
                        communication = customer.short_basket_name
                    group_name = apps.REPANIER_SETTINGS_GROUP_NAME
                    customer_payment_needed = '<br/><font color="#bd0926">%s</font>' % (
                        _('Please pay %(payment)s to the bank account %(name)s %(number)s with communication %(communication)s.') % {
                            'payment': payment_needed,
                            'name': group_name,
                            'number': bank_account_number,
                            'communication': communication
                        }
                    )
Patrick's avatar
Patrick committed
639 640

                else:
641 642 643 644 645 646 647
                    if customer.balance.amount != DECIMAL_ZERO:
                        customer_payment_needed = '<br/><font color="green">%s.</font>' % (_('Your account balance is sufficient'))
                    else:
                        customer_payment_needed = EMPTY_STRING
            else:
                customer_payment_needed = EMPTY_STRING

Patrick's avatar
Patrick committed
648 649 650 651 652

    return customer_last_balance, customer_on_hold_movement, customer_payment_needed, customer_order_amount


def recalculate_order_amount(permanence_id,
Patrick's avatar
Patrick committed
653 654
                             customer_id=None,
                             offer_item_queryset=None,
Patrick's avatar
Patrick committed
655 656
                             all_producers=True,
                             producers_id=None,
Patrick's avatar
Patrick committed
657
                             send_to_producer=False,
658
                             re_init=False):
Patrick's avatar
Patrick committed
659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715
    if send_to_producer or re_init:
        if all_producers:
            models.ProducerInvoice.objects.filter(
                permanence_id=permanence_id
            ).update(
                total_price_with_tax=DECIMAL_ZERO,
                total_vat=DECIMAL_ZERO,
                total_deposit=DECIMAL_ZERO,
                total_profit_with_tax=DECIMAL_ZERO,
                total_profit_vat=DECIMAL_ZERO
            )
            models.CustomerInvoice.objects.filter(
                permanence_id=permanence_id
            ).update(
                total_price_with_tax=DECIMAL_ZERO,
                total_vat=DECIMAL_ZERO,
                total_deposit=DECIMAL_ZERO
            )
            models.CustomerProducerInvoice.objects.filter(
                permanence_id=permanence_id
            ).update(
                total_purchase_with_tax=DECIMAL_ZERO,
                total_selling_with_tax=DECIMAL_ZERO
            )
            models.OfferItem.objects.filter(
                permanence_id=permanence_id
            ).update(
                quantity_invoiced=DECIMAL_ZERO,
                total_purchase_with_tax=DECIMAL_ZERO,
                total_selling_with_tax=DECIMAL_ZERO
            )
            for offer_item in models.OfferItem.objects.filter(
                    permanence_id=permanence_id,
                    is_active=True,
                    manage_replenishment=True
            ).exclude(add_2_stock=DECIMAL_ZERO).order_by('?'):
                # Recalculate the total_price_with_tax of ProducerInvoice and
                # the total_purchase_with_tax of OfferItem
                # taking into account "add_2_stock"
                offer_item.previous_add_2_stock = DECIMAL_ZERO
                offer_item.save()
        else:
            models.ProducerInvoice.objects.filter(
                permanence_id=permanence_id, producer_id__in=producers_id
            ).update(
                total_price_with_tax=DECIMAL_ZERO,
                total_vat=DECIMAL_ZERO,
                total_deposit=DECIMAL_ZERO
            )
            for ci in models.CustomerInvoice.objects.filter(
                    permanence_id=permanence_id):
                result_set = models.CustomerProducerInvoice.objects.filter(
                    permanence_id=permanence_id,
                    customer_id=ci.customer_id,
                    producer_id__in=producers_id
                ).order_by('?').aggregate(
                    Sum('total_selling_with_tax'),
Patrick's avatar
Patrick committed
716
                )
Patrick's avatar
Patrick committed
717 718 719 720 721 722 723 724
                if result_set["total_selling_with_tax__sum"] is not None:
                    sum_total_selling_with_tax = result_set["total_selling_with_tax__sum"]
                else:
                    sum_total_selling_with_tax = DECIMAL_ZERO
                models.CustomerInvoice.objects.filter(
                    permanence_id=permanence_id
                ).update(
                    total_price_with_tax=F('total_price_with_tax') - sum_total_selling_with_tax
Patrick's avatar
Patrick committed
725
                )
Patrick's avatar
Patrick committed
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746
            models.CustomerProducerInvoice.objects.filter(
                permanence_id=permanence_id,
                producer_id__in=producers_id
            ).update(
                total_purchase_with_tax=DECIMAL_ZERO,
                total_selling_with_tax=DECIMAL_ZERO
            )
            models.OfferItem.objects.filter(
                permanence_id=permanence_id,
                producer_id__in=producers_id
            ).update(
                quantity_invoiced=DECIMAL_ZERO,
                total_purchase_with_tax=DECIMAL_ZERO,
                total_selling_with_tax=DECIMAL_ZERO
            )
            for offer_item in models.OfferItem.objects.filter(
                    permanence_id=permanence_id,
                    producer_id__in=producers_id,
                    is_active=True,
                    manage_replenishment=True
            ).exclude(add_2_stock=DECIMAL_ZERO).order_by('?'):
Patrick's avatar
Patrick committed
747 748 749 750 751
                # Recalculate the total_price_with_tax of ProducerInvoice and
                # the total_purchase_with_tax of OfferItem
                # taking into account "add_2_stock"
                offer_item.previous_add_2_stock = DECIMAL_ZERO
                offer_item.save()
Patrick's avatar
Patrick committed
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784

    if customer_id is None:
        if offer_item_queryset is not None:
            purchase_set = models.Purchase.objects \
                .filter(permanence_id=permanence_id, offer_item__in=offer_item_queryset) \
                .order_by('?')
        else:
            purchase_set = models.Purchase.objects \
                .filter(permanence_id=permanence_id) \
                .order_by('?')
            if not all_producers:
                purchase_set = purchase_set.filter(producer_id__in=producers_id)
    else:
        purchase_set = models.Purchase.objects \
            .filter(permanence_id=permanence_id, customer_id=customer_id) \
            .order_by('?')
        if not all_producers:
            purchase_set = purchase_set.filter(producer_id__in=producers_id)

    for purchase in purchase_set.select_related("offer_item"):
        # Recalcuate the total_price_with_tax of ProducerInvoice,
        # the total_price_with_tax of CustomerInvoice,
        # the total_purchase_with_tax + total_selling_with_tax of CustomerProducerInvoice,
        # and quantity_invoiced + total_purchase_with_tax + total_selling_with_tax of OfferItem
        offer_item = purchase.offer_item
        if send_to_producer or re_init:
            # purchase.admin_update = True
            purchase.previous_quantity = DECIMAL_ZERO
            purchase.previous_purchase_price = DECIMAL_ZERO
            purchase.previous_selling_price = DECIMAL_ZERO
            purchase.previous_producer_vat = DECIMAL_ZERO
            purchase.previous_customer_vat = DECIMAL_ZERO
            purchase.previous_deposit = DECIMAL_ZERO
Patrick's avatar
Patrick committed
785 786
            if send_to_producer:
                if offer_item.order_unit == PRODUCT_ORDER_UNIT_PC_KG:
Patrick's avatar
Patrick committed
787
                    purchase.quantity_invoiced = (purchase.quantity_ordered * offer_item.order_average_weight) \
Patrick's avatar
Patrick committed
788 789 790 791 792
                        .quantize(FOUR_DECIMALS)
                    if offer_item.wrapped:
                        purchase.quantity_for_preparation_sort_order = DECIMAL_ZERO
                    else:
                        purchase.quantity_for_preparation_sort_order = purchase.quantity_ordered
793 794 795 796 797 798
                elif offer_item.order_unit == PRODUCT_ORDER_UNIT_KG:
                    purchase.quantity_invoiced = purchase.quantity_ordered
                    if offer_item.wrapped:
                        purchase.quantity_for_preparation_sort_order = DECIMAL_ZERO
                    else:
                        purchase.quantity_for_preparation_sort_order = purchase.quantity_ordered
Patrick's avatar
Patrick committed
799 800 801
                else:
                    purchase.quantity_invoiced = purchase.quantity_ordered
                    purchase.quantity_for_preparation_sort_order = DECIMAL_ZERO
Patrick's avatar
Patrick committed
802 803 804
        purchase.save()


805
def display_selected_value(offer_item, quantity_ordered):
Patrick's avatar
Patrick committed
806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
    if offer_item.may_order:
        if quantity_ordered <= DECIMAL_ZERO:
            q_min = offer_item.customer_minimum_order_quantity
            if offer_item.limit_order_quantity_to_stock:
                q_alert = offer_item.stock - offer_item.quantity_invoiced
                if q_alert < DECIMAL_ZERO:
                    q_alert = DECIMAL_ZERO
            else:
                q_alert = offer_item.customer_alert_order_quantity
            if q_min <= q_alert:
                qs = models.ProducerInvoice.objects.filter(
                    permanence__offeritem=offer_item.id,
                    producer__offeritem=offer_item.id,
                    status=PERMANENCE_OPENED
                ).order_by('?')
                if qs.exists():
                    option_dict = {
                        'id'  : "#offer_item%d" % offer_item.id,
                        'html': '<option value="0" selected>---</option>'
                    }
                else:
                    closed = _("Closed")
                    option_dict = {
                        'id'  : "#offer_item%d" % offer_item.id,
                        'html': '<option value="0" selected>%s</option>' % closed
                    }
            else:
                sold_out = _("Sold out")
                option_dict = {
                    'id'  : "#offer_item%d" % offer_item.id,
                    'html': '<option value="0" selected>%s</option>' % sold_out
                }

        else:
            unit_price_amount = offer_item.customer_unit_price.amount + offer_item.unit_deposit.amount
            display = get_display(
                qty=quantity_ordered,
                order_average_weight=offer_item.order_average_weight,
                order_unit=offer_item.order_unit,
                unit_price_amount=unit_price_amount,
                for_order_select=True
            )
            option_dict = {
                'id'  : "#offer_item%d" % offer_item.id,
                'html': '<option value="%d" selected>%s</option>' % (quantity_ordered, display,)
            }
    else:
        option_dict = {
            'id'  : "#box_offer_item%d" % offer_item.id,
            'html': ''
        }
    return option_dict


860
def display_selected_box_value(customer, offer_item, box_purchase):
Patrick's avatar
Patrick committed
861 862 863 864
    if offer_item.is_box_content:
        # box_name = _not_lazy("Composition")
        box_name = BOX_UNICODE
        # Select one purchase
865
        if box_purchase is not None:
Patrick's avatar
Patrick committed
866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
            if box_purchase.quantity_ordered > DECIMAL_ZERO:
                qty_display = get_display(
                    qty=box_purchase.quantity_ordered,
                    order_average_weight=offer_item.order_average_weight,
                    order_unit=offer_item.order_unit,
                    for_order_select=True,
                    without_price_display=True
                )
                option_dict = {
                    'id'  : "#box_offer_item%d" % offer_item.id,
                    'html': '<select name="box_offer_item%d" disabled class="form-control"><option value="0" selected>☑ %s %s</option></select>'
                            % (offer_item.id, qty_display, box_name)
                }
            else:
                option_dict = {
                    'id'  : "#box_offer_item%d" % offer_item.id,
                    'html': '<select name="box_offer_item%d" disabled class="form-control"><option value="0" selected>☑ --- %s</option></select>'
                            % (offer_item.id, box_name)
                }
        else:
            option_dict = {
                'id'  : "#box_offer_item%d" % offer_item.id,
                'html': '<select name="box_offer_item%d" disabled class="form-control"><option value="0" selected>☑ --- %s</option></select>'
                        % (offer_item.id, box_name)
            }
    else:
        option_dict = {
            'id'  : "#box_offer_item%d" % offer_item.id,
            'html': ''
        }
    return option_dict
897 898 899


@transaction.atomic
900
def update_or_create_purchase(customer=None, offer_item_id=None, q_order=None, value_id=None, basket=False, batch_job=False):
Patrick's avatar
Patrick committed
901
    to_json = []
902
    if offer_item_id is not None and (q_order is not None or value_id is not None) and customer is not None:
Patrick's avatar
Patrick committed
903
        offer_item = models.OfferItem.objects.select_for_update(nowait=False) \
904
            .filter(id=offer_item_id, is_active=True, may_order=True) \
Patrick's avatar
Patrick committed
905
            .order_by('?').select_related("product", "producer").first()
906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929
        if offer_item is not None:
            if q_order is None:
                # Transform value_id into a q_order.
                # This is done here and not in the order_ajax to avoid to access twice to offer_item
                q_min = offer_item.customer_minimum_order_quantity
                q_step = offer_item.customer_increment_order_quantity
                if value_id <= 0:
                    q_order = DECIMAL_ZERO
                elif value_id == 1:
                    q_order = q_min
                else:
                    if q_min < q_step:
                        # 1; 2; 4; 6; 8 ... q_min = 1; q_step = 2
                        # 0,5; 1; 2; 3 ... q_min = 0,5; q_step = 1
                        if value_id == 2:
                            q_order = q_step
                        else:
                            q_order = q_step * (value_id - 1)
                    else:
                        # 1; 2; 3; 4 ... q_min = 1; q_step = 1
                        # 0,125; 0,175; 0,225 ... q_min = 0,125; q_step = 0,50
                        q_order = q_min + q_step * (value_id - 1)
            if q_order < DECIMAL_ZERO:
                q_order = DECIMAL_ZERO
Patrick's avatar
Patrick committed
930 931 932 933 934 935 936 937 938
            purchase = None
            permanence_id = offer_item.permanence_id
            updated = True
            if offer_item.is_box:
                # Select one purchase
                purchase = models.Purchase.objects.filter(
                    customer_id=customer.id,
                    offer_item_id=offer_item.id,
                    is_box_content=False
939
                ).order_by('?').first()
Patrick's avatar
Patrick committed
940
                if purchase is not None:
941
                    delta_q_order = q_order - purchase.quantity_ordered
Patrick's avatar
Patrick committed
942
                else:
943
                    delta_q_order = q_order
Patrick's avatar
Patrick committed
944 945 946 947
                with transaction.atomic():
                    sid = transaction.savepoint()
                    # This code executes inside a transaction.
                    for content in models.BoxContent.objects.filter(
948
                        box=offer_item.product_id
Patrick's avatar
Patrick committed
949
                    ).only(
950
                        "product_id", "content_quantity"
Patrick's avatar
Patrick committed
951 952 953 954 955 956 957 958 959 960
                    ).order_by('?'):
                        box_offer_item = models.OfferItem.objects \
                            .filter(product_id=content.product_id, permanence_id=offer_item.permanence_id) \
                            .order_by('?').select_related("producer").first()
                        if box_offer_item is not None:
                            # Select one purchase
                            purchase = models.Purchase.objects.filter(
                                customer_id=customer.id,
                                offer_item_id=box_offer_item.id,
                                is_box_content=True
961
                            ).order_by('?').first()
Patrick's avatar
Patrick committed
962
                            if purchase is not None:
963
                                quantity_ordered = purchase.quantity_ordered + delta_q_order * content.content_quantity
Patrick's avatar
Patrick committed
964
                            else:
965
                                quantity_ordered = delta_q_order * content.content_quantity
Patrick's avatar
Patrick committed
966 967 968
                            if quantity_ordered < DECIMAL_ZERO:
                                quantity_ordered = DECIMAL_ZERO
                            purchase, updated = create_or_update_one_purchase(
969
                                customer, box_offer_item, q_order=quantity_ordered, batch_job=batch_job, is_box_content=True
Patrick's avatar
Patrick committed
970
                            )
Patrick's avatar
Patrick committed
971
                        else:
Patrick's avatar
Patrick committed
972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987
                            updated = False
                        if not updated:
                            break
                    if updated:
                        for content in models.BoxContent.objects.filter(box=offer_item.product_id).only(
                                "product_id").order_by('?'):
                            box_offer_item = models.OfferItem.objects.filter(
                                product_id=content.product_id,
                                permanence_id=offer_item.permanence_id
                            ).order_by('?').first()
                            if box_offer_item is not None:
                                # Select one purchase
                                purchase = models.Purchase.objects.filter(
                                    customer_id=customer.id,
                                    offer_item_id=box_offer_item.id,
                                    is_box_content=False
988
                                ).order_by('?').first()
989 990 991 992 993 994 995 996 997
                                option_dict = display_selected_value(
                                    box_offer_item,
                                    purchase.quantity_ordered if purchase is not None else DECIMAL_ZERO
                                )
                                to_json.append(option_dict)
                                box_purchase = models.Purchase.objects.filter(
                                    customer_id=customer.id,
                                    offer_item_id=box_offer_item.id,
                                    is_box_content=True
998
                                ).order_by('?').first()
999
                                option_dict = display_selected_box_value(customer, box_offer_item, box_purchase)
Patrick's avatar
Patrick committed
1000 1001 1002 1003 1004
                                to_json.append(option_dict)
                        transaction.savepoint_commit(sid)
                    else:
                        transaction.savepoint_rollback(sid)
            if not offer_item.is_box or updated:
1005 1006 1007 1008
                purchase, updated = create_or_update_one_purchase(
                    customer, offer_item, q_order=q_order, batch_job=batch_job,
                    is_box_content=False
                )
Patrick's avatar
Patrick committed
1009 1010 1011 1012
                if not batch_job and apps.REPANIER_SETTINGS_DISPLAY_PRODUCER_ON_ORDER_FORM:
                    producer_invoice = models.ProducerInvoice.objects.filter(
                        producer_id=offer_item.producer_id, permanence_id=offer_item.permanence_id
                    ).only("total_price_with_tax").order_by('?').first()
1013 1014
                    if producer_invoice is not None and offer_item.producer.minimum_order_value.amount > DECIMAL_ZERO:
                        ratio = producer_invoice.total_price_with_tax.amount / offer_item.producer.minimum_order_value.amount
Patrick's avatar
Patrick committed
1015 1016
                        if ratio >= DECIMAL_ONE:
                            ratio = 100
Patrick's avatar
Patrick committed
1017
                        else:
Patrick's avatar
Patrick committed
1018
                            ratio *= 100
1019 1020 1021
                        option_dict = {'id'  : "#order_procent" + str(offer_item.producer_id),
                                       'html': "%s%%" % number_format(ratio, 0)}
                        to_json.append(option_dict)
Patrick's avatar
Patrick committed
1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038
            elif not batch_job:
                # Select one purchase
                purchase = models.Purchase.objects.filter(
                    customer_id=customer.id,
                    offer_item_id=offer_item.id,
                    is_box_content=False
                ).order_by('?').first()

            if not batch_job:
                if purchase is None:
                    if offer_item.is_box:
                        sold_out = _("Sold out")
                        option_dict = {
                            'id'  : "#offer_item%d" % offer_item.id,
                            'html': '<option value="0" selected>%s</option>' % sold_out
                        }
                    else:
1039
                        option_dict = display_selected_value(offer_item, DECIMAL_ZERO)
Patrick's avatar
Patrick committed
1040 1041 1042 1043
                    to_json.append(option_dict)
                else:
                    offer_item = models.OfferItem.objects.filter(id=offer_item_id).order_by('?').first()
                    if offer_item is not None:
1044
                        option_dict = display_selected_value(offer_item, purchase.quantity_ordered)
Patrick's avatar
Patrick committed
1045
                        to_json.append(option_dict)
Patrick's avatar
Patrick committed
1046

Patrick's avatar
Patrick committed
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
                customer_invoice = models.CustomerInvoice.objects.filter(
                    permanence_id=permanence_id,
                    customer_id=customer.id
                ).order_by('?').first()
                permanence = models.