# -*- coding: utf-8 from __future__ import unicode_literals import datetime 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 from django.utils import timezone, translation from django.utils.encoding import python_2_unicode_compatible from django.utils.formats import number_format 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, TranslationDoesNotExist import customer import deliveryboard import invoice import permanenceboard import producer import purchase import repanier.apps from repanier.picture.const import SIZE_L from repanier.picture.fields import AjaxPictureField from repanier.fields.RepanierMoneyField import ModelMoneyField from repanier.const import * from repanier.tools import get_full_status_display, cap # def verbose_name(): # if repanier.apps.DJANGO_IS_MIGRATION_RUNNING: # return EMPTY_STRING # return lambda: "%s" % repanier.apps.REPANIER_SETTINGS_PERMANENCE_NAME # # # def verbose_name_plural(): # if repanier.apps.DJANGO_IS_MIGRATION_RUNNING: # return EMPTY_STRING # return lambda: "%s" % repanier.apps.REPANIER_SETTINGS_PERMANENCES_NAME # # # def verbose_name_in_preparation(): # if repanier.apps.DJANGO_IS_MIGRATION_RUNNING: # return EMPTY_STRING # return lambda: _("%(name)s in preparation list") % {'name': repanier.apps.REPANIER_SETTINGS_PERMANENCES_NAME} # # # def verbose_name_plural_in_preparation(): # if repanier.apps.DJANGO_IS_MIGRATION_RUNNING: # return EMPTY_STRING # return lambda: _("%(name)s in preparation list") % {'name': repanier.apps.REPANIER_SETTINGS_PERMANENCES_NAME} # # # def verbose_name_done(): # if repanier.apps.DJANGO_IS_MIGRATION_RUNNING: # return EMPTY_STRING # return lambda: _("%(name)s done list") % { # 'name': repanier.apps.REPANIER_SETTINGS_PERMANENCES_NAME} if repanier.apps.REPANIER_SETTINGS_INVOICE else "%s" % _( # "%(name)s archived list") % {'name': repanier.apps.REPANIER_SETTINGS_PERMANENCES_NAME} # # # def verbose_name_plural_done(): # if repanier.apps.DJANGO_IS_MIGRATION_RUNNING: # return EMPTY_STRING # return lambda: _("%(name)s done list") % { # 'name': repanier.apps.REPANIER_SETTINGS_PERMANENCES_NAME} if repanier.apps.REPANIER_SETTINGS_INVOICE else "%s" % _( # "%(name)s archived list") % {'name': repanier.apps.REPANIER_SETTINGS_PERMANENCES_NAME} @python_2_unicode_compatible class Permanence(TranslatableModel): translations = TranslatedFields( short_name=models.CharField(_("offer name"), max_length=50, blank=True), offer_description=HTMLField(_("offer_description"), configuration='CKEDITOR_SETTINGS_MODEL2', 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"), configuration='CKEDITOR_SETTINGS_MODEL2', help_text=_( 'This message is send by mail to all customers having bought something when closing the permanence.'), blank=True, default=EMPTY_STRING), cache_part_d=HTMLField(configuration='CKEDITOR_SETTINGS_MODEL2', blank=True, default=EMPTY_STRING) ) 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')) permanence_date = models.DateField(_("date"), db_index=True) payment_date = models.DateField(_("payment_date"), blank=True, null=True, db_index=True) producers = models.ManyToManyField( 'Producer', verbose_name=_("producers"), blank=True ) total_price_wo_tax = ModelMoneyField( _("Total amount"), help_text=_('Total purchase amount vat excluded'), default=DECIMAL_ZERO, max_digits=8, decimal_places=2) total_profit = ModelMoneyField( _("Total profit"), help_text=_('Total profit'), default=DECIMAL_ZERO, max_digits=8, decimal_places=2) 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')) master_permanence = models.ForeignKey( 'Permanence', verbose_name=_("master permanence"), related_name='child_permanence', blank=True, null=True, default=None, on_delete=models.PROTECT, db_index=True) invoice_sort_order = models.IntegerField( _("invoice sort order"), default=None, blank=True, null=True) offer_description_on_home_page = models.BooleanField( _("Publish the offer description on the home page when the permanence is open"), default=True) picture = AjaxPictureField( verbose_name=_("picture"), null=True, blank=True, upload_to="permanence", size=SIZE_L) def get_producers(self): if self.status == PERMANENCE_PLANNED: if len(self.producers.all()) > 0: changelist_url = urlresolvers.reverse( 'admin:repanier_product_changelist', ) link = [] for p in self.producers.all(): link.append( ' %s' % ( changelist_url, p.id, p.short_profile_name)) return '
%s
' % ", ".join(link) else: return '
%s
' % _("No offer") elif self.status == PERMANENCE_PRE_OPEN: return '
%s
' % ", ".join([p.short_profile_name + " (" + p.phone1 + ")" for p in self.producers.all()]) elif self.status in [PERMANENCE_OPENED, PERMANENCE_CLOSED]: close_offeritem_changelist_url = urlresolvers.reverse( 'admin:repanier_offeritemclosed_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) ' % (p.short_profile_name, pi.get_total_price_with_tax())).replace( ' ', ' ') offeritem_changelist_url = close_offeritem_changelist_url else: label = ('%s (%s) %s' % ( p.short_profile_name, pi.get_total_price_with_tax(), LOCK_UNICODE)).replace(' ', ' ') offeritem_changelist_url = close_offeritem_changelist_url else: label = ('%s ' % (p.short_profile_name,)).replace(' ', ' ') offeritem_changelist_url = close_offeritem_changelist_url link.append( '%s' % ( offeritem_changelist_url, self.id, p.id, label)) return '
%s
' % ", ".join(link) elif self.status in [PERMANENCE_SEND, PERMANENCE_INVOICED, PERMANENCE_ARCHIVED]: send_offeritem_changelist_url = urlresolvers.reverse( 'admin:repanier_offeritemsend_changelist', ) send_customer_changelist_url = urlresolvers.reverse( 'admin:repanier_customersend_changelist', ) link = [] at_least_one_permanence_send = False for pi in invoice.ProducerInvoice.objects.filter(permanence_id=self.id).select_related( "producer").order_by('producer'): if pi.status == PERMANENCE_SEND: at_least_one_permanence_send = True if pi.producer.invoice_by_basket: changelist_url = send_customer_changelist_url else: changelist_url = send_offeritem_changelist_url # Important : no target="_blank" label = '%s (%s) %s' % ( pi.producer.short_profile_name, pi.get_total_price_with_tax(), LOCK_UNICODE) link.append( ' %s' % ( changelist_url, self.id, pi.producer_id, label.replace(' ', ' ') )) else: if pi.invoice_reference: if pi.to_be_invoiced_balance != DECIMAL_ZERO: label = "%s (%s - %s)" % ( pi.producer.short_profile_name, pi.to_be_invoiced_balance, cap(pi.invoice_reference, 15) ) else: label = "%s (%s)" % ( pi.producer.short_profile_name, cap(pi.invoice_reference, 15) ) else: if pi.to_be_invoiced_balance != DECIMAL_ZERO: label = "%s (%s)" % ( pi.producer.short_profile_name, pi.to_be_invoiced_balance ) else: # label = "%s" % ( # pi.producer.short_profile_name # ) continue # 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 link.append( '%s' % ( urlresolvers.reverse('producer_invoice_view', args=(pi.id,)), pi.producer_id, label.replace(' ', ' '))) producers = ", ".join(link) if at_least_one_permanence_send: msg_html = '
%s
' % producers else: msg_html = """
""" % ( self.id, _("Show"), _("Hide"), _("Show"), _("Show"), self.id, producers ) return msg_html else: return '
%s
' % ", ".join([p.short_profile_name for p in producer.Producer.objects.filter( producerinvoice__permanence_id=self.id).only( 'short_profile_name')]) get_producers.short_description = (_("producers in this permanence")) get_producers.allow_tags = True def get_customers(self): 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'): ci_customer = ci.customer if ci.is_order_confirm_send: label = '%s%s (%s) %s%s' % ( "" if ci_customer.is_group else EMPTY_STRING, ci.customer.short_basket_name, ci.get_total_price_with_tax(customer_charged=True), ci.get_is_order_confirm_send_display(), "" if ci_customer.is_group else EMPTY_STRING, ) else: label = '%s%s (%s) %s%s' % ( "" if ci_customer.is_group else EMPTY_STRING, ci.customer.short_basket_name, ci.total_price_with_tax, ci.get_is_order_confirm_send_display(), "" if ci_customer.is_group else EMPTY_STRING, ) # Important : no target="_blank" link.append( '%s' % (changelist_url, self.id, ci.customer_id, label.replace(' ', ' '))) customers = ", ".join(link) elif self.status in [PERMANENCE_INVOICED, PERMANENCE_ARCHIVED]: link = [] for ci in invoice.CustomerInvoice.objects.filter(permanence_id=self.id).select_related( "customer").order_by('customer'): ci_customer = ci.customer label = "%s%s (%s) %s%s" % ( "" if ci_customer.is_group else EMPTY_STRING, ci_customer.short_basket_name, ci.get_total_price_with_tax(customer_charged=True), ci.get_is_order_confirm_send_display(), "" if ci_customer.is_group else EMPTY_STRING, ) # 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 link.append( '%s' % ( urlresolvers.reverse('customer_invoice_view', args=(ci.id,)), ci.customer_id, label.replace(' ', ' ') ) ) 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 = """
""" % ( self.id, _("Show"), _("Hide"), _("Show"), _("Show"), self.id, customers ) return msg_html else: return '
%s
' % _("No purchase") get_customers.short_description = (_("customers in this permanence")) get_customers.allow_tags = True def get_board(self): 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 = '' + r.short_name.replace(' ', ' ') + '' c_link = EMPTY_STRING c = permanenceboard_row.customer if c: c_url = urlresolvers.reverse( 'admin:repanier_customer_change', args=(c.id,) ) c_link = ' -> ' + c.short_basket_name.replace(' ', ' ') + '' if not first_board: board += '
' board += r_link + c_link first_board = False if not first_board: # At least one role is defined in the permanence board msg_html = """
""" % ( self.id, _("Show"), _("Hide"), _("Show"), _("Show"), self.id, board ) return msg_html else: return '
%s
' % _("Empty board") 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', 'payment_date']) else: self.save(update_fields=['status', 'is_updated_on', 'highest_status']) if new_status == PERMANENCE_WAIT_FOR_OPEN: 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 ) self.with_delivery_point = deliveryboard.DeliveryBoard.objects.filter( permanence_id=self.id ).order_by('?').exists() 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', 'highest_status', 'with_delivery_point']) 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 duplicate(self, repeat_counter=0, repeat_step=1): creation_counter = 0 if 1 <= repeat_counter * repeat_step <= 54: # 54 weeks in a year repeat_counter += 1 starting_date = self.permanence_date short_name = self.safe_translation_getter( 'short_name', any_language=True, default=EMPTY_STRING ) cur_language = translation.get_language() every_x_days = 7 * int(repeat_step) for i in range(1, repeat_counter): new_date = starting_date + datetime.timedelta(days=every_x_days * i) # Mandatory because of Parler if short_name != EMPTY_STRING: already_exists = Permanence.objects.filter( permanence_date=new_date, translations__language_code=cur_language, translations__short_name=short_name ).exists() else: already_exists = False for existing_permanence in Permanence.objects.filter( permanence_date=new_date ): try: short_name = existing_permanence.short_name already_exists = short_name == EMPTY_STRING except TranslationDoesNotExist: already_exists = True if already_exists: break if not already_exists: creation_counter += 1 new_permanence = Permanence.objects.create( permanence_date=new_date ) self.duplicate_short_name( new_permanence, cur_language=translation.get_language(), ) for permanence_board in permanenceboard.PermanenceBoard.objects.filter( permanence=self ): permanenceboard.PermanenceBoard.objects.create( permanence=new_permanence, permanence_role=permanence_board.permanence_role ) for delivery_board in deliveryboard.DeliveryBoard.objects.filter( permanence=self ): new_delivery_board = deliveryboard.DeliveryBoard.objects.create( permanence=new_permanence, delivery_point=delivery_board.delivery_point, delivery_date=delivery_board.delivery_date + datetime.timedelta(days=every_x_days * i) ) for language in settings.PARLER_LANGUAGES[settings.SITE_ID]: language_code = language["code"] translation.activate(language_code) new_delivery_board.set_current_language(language_code) delivery_board.set_current_language(language_code) try: new_delivery_board.delivery_comment = delivery_board.delivery_comment new_delivery_board.save() except TranslationDoesNotExist: pass translation.activate(cur_language) for producer in self.producers.all(): new_permanence.producers.add(producer) return creation_counter def duplicate_short_name(self, new_permanence, cur_language): for language in settings.PARLER_LANGUAGES[settings.SITE_ID]: language_code = language["code"] translation.activate(language_code) new_permanence.set_current_language(language_code) self.set_current_language(language_code) try: new_permanence.short_name = self.short_name new_permanence.save() except TranslationDoesNotExist: pass translation.activate(cur_language) return new_permanence def create_child(self, status): child_permanence = Permanence.objects.create( permanence_date=self.permanence_date, master_permanence_id=self.id, status=status ) return self.duplicate_short_name( child_permanence, cur_language=translation.get_language(), ) 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): short_name = self.safe_translation_getter( 'short_name', any_language=True ) if short_name: permanence_display = "%s" % short_name else: permanence_display = '%s%s' % ( repanier.apps.REPANIER_SETTINGS_PERMANENCE_ON_NAME, self.permanence_date.strftime(settings.DJANGO_SETTINGS_DATE) ) return permanence_display def get_permanence_admin_display(self): if self.total_price_wo_tax != DECIMAL_ZERO: if self.total_profit.amount != DECIMAL_ZERO: return '%s
%s %s
৺ %s
%s%%' % ( self.get_permanence_display(), _('wo tax'), self.total_price_wo_tax, self.total_profit, number_format((self.total_profit.amount / self.total_price_wo_tax.amount) * 100, 2) ) else: return '%s
%s %s' % ( self.get_permanence_display(), _('wo tax'), self.total_price_wo_tax) else: if self.total_profit.amount != DECIMAL_ZERO: return '%s
%s ৺ %s' % (self.get_permanence_display(), _('wo tax'), self.total_profit) else: return self.get_permanence_display() get_permanence_admin_display.short_description = lambda: "%s" % repanier.apps.REPANIER_SETTINGS_PERMANENCES_NAME get_permanence_admin_display.allow_tags = True # def get_permanence_order_display(self): # 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: # return "%s - %s" % (self.get_permanence_display(), self.get_status_display()) # return self.get_permanence_display() def get_permanence_customer_display(self, with_status=True): 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: return "%s - %s" % (self.get_permanence_display(), self.get_status_display()) else: return "%s - %s" % (self.get_permanence_display(), _('orders closed')) return self.get_permanence_display() def __str__(self): return self.get_permanence_display() class Meta: verbose_name = _('order') verbose_name_plural = _('orders') index_together = [ ["permanence_date"], ] class PermanenceInPreparation(Permanence): class Meta: proxy = True verbose_name = _('In preparation') verbose_name_plural = _('In preparation') class PermanenceDone(Permanence): class Meta: proxy = True verbose_name = _('In billing') verbose_name_plural = _('In billing')