text: # Copyright (c) 2016 Presslabs SRL
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #    http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  
  
  from django_fsm import transition
  
  from django.core.exceptions import ValidationError
  from django.db.models.signals import pre_delete
  from django.dispatch import receiver
  
  from silver.models.billing_entities import Provider
  from silver.models.documents.base import (
      BillingDocumentBase, BillingDocumentManager, BillingDocumentQuerySet)
  from silver.models.documents.entries import DocumentEntry
  from silver.models.documents.invoice import Invoice
  
  
  class ProformaManager(BillingDocumentManager):
      def get_queryset(self):
          queryset = super(BillingDocumentManager, self).get_queryset()
          return queryset.filter(kind='proforma').prefetch_related('proforma_entries__product_code',
                                                                   'proforma_entries__invoice')
  
  
  class Proforma(BillingDocumentBase):
      objects = ProformaManager.from_queryset(BillingDocumentQuerySet)()
  
      class Meta:
          proxy = True
  
      def __init__(self, *args, **kwargs):
          super(Proforma, self).__init__(*args, **kwargs)
  
          provider_field = self._meta.get_field("provider")
          provider_field.related_name = "proformas"
  
          customer_field = self._meta.get_field("customer")
          customer_field.related_name = "proformas"
  
      @property
      def transactions(self):
          return self.proforma_transactions.all()
  
      def clean(self):
          super(Proforma, self).clean()
          if not self.series:
              if not hasattr(self, 'provider'):
                  # the clean method is called even if the clean_fields method
                  # raises exceptions, to we check if the provider was specified
                  pass
              elif not self.provider.proforma_series:
                  err_msg = {'series': 'You must either specify the series or '
                                       'set a default proforma_series for the '
                                       'provider.'}
                  raise ValidationError(err_msg)
  
      @transition(field='state', source=BillingDocumentBase.STATES.DRAFT,
                  target=BillingDocumentBase.STATES.ISSUED)
      def issue(self, issue_date=None, due_date=None):
          self.archived_provider = self.provider.get_proforma_archivable_field_values()
  
          super(Proforma, self)._issue(issue_date, due_date)
  
      @transition(field='state', source=BillingDocumentBase.STATES.ISSUED,
                  target=BillingDocumentBase.STATES.PAID)
      def pay(self, paid_date=None):
          super(Proforma, self)._pay(paid_date)
  
          if not self.related_document:
              self.related_document = self._new_invoice()
              self.related_document.issue()
              self.related_document.pay(paid_date=paid_date)
  
              # if the proforma is paid, the invoice due_date should be issue_date
              self.related_document.due_date = self.related_document.issue_date
  
              self.related_document.save()
              self.save()
  
      def create_invoice(self):
          if self.state != BillingDocumentBase.STATES.ISSUED:
              raise ValueError("You can't create an invoice from a %s proforma, "
                               "only from an issued one" % self.state)
  
          if self.related_document:
              raise ValueError("This proforma already has an invoice { %s }"
                               % self.related_document)
  
          self.related_document = self._new_invoice()
          self.related_document.issue()
  
          self.save()
  
          return self.related_document
  
      def _new_invoice(self):
          # Generate the new invoice based this proforma
          invoice_fields = self.fields_for_automatic_invoice_generation
          invoice_fields.update({'related_document': self})
          invoice = Invoice.objects.create(**invoice_fields)
  
          # For all the entries in the proforma => add the link to the new
          # invoice
          DocumentEntry.objects.filter(proforma=self).update(invoice=invoice)
          return invoice
  
      @property
      def _starting_number(self):
          return self.provider.proforma_starting_number
  
      @property
      def default_series(self):
          try:
              return self.provider.proforma_series
          except Provider.DoesNotExist:
              return ''
  
      @property
      def fields_for_automatic_invoice_generation(self):
          fields = ['customer', 'provider', 'archived_customer',
                    'archived_provider', 'paid_date', 'cancel_date',
                    'sales_tax_percent', 'sales_tax_name', 'currency',
                    'transaction_currency', 'transaction_xe_rate',
                    'transaction_xe_date']
          return {field: getattr(self, field, None) for field in fields}
  
      @property
      def entries(self):
          return self.proforma_entries.all()
  
  
  @receiver(pre_delete, sender=Proforma)
  def delete_proforma_pdf_from_storage(sender, instance, **kwargs):
      if instance.pdf:
          # Delete the proforma's PDF
          instance.pdf.pdf_file.delete(False)
  