text: # Copyright (c) 2015 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 __future__ import absolute_import
  
  from datetime import date, timedelta
  from decimal import Decimal
  
  from six.moves import zip
  
  from django.test import TestCase
  
  from silver.models import DocumentEntry, Proforma, Invoice
  from silver.fixtures.factories import (ProformaFactory, InvoiceFactory,
                                         DocumentEntryFactory, CustomerFactory)
  
  
  class TestInvoice(TestCase):
      def test_pay_invoice_related_proforma_state_change_to_paid(self):
          proforma = ProformaFactory.create()
          proforma.issue()
          proforma.create_invoice()
  
          assert proforma.related_document.state == Invoice.STATES.ISSUED
  
          proforma.related_document.pay()
  
          assert proforma.related_document.state == Invoice.STATES.PAID
          assert proforma.state == Proforma.STATES.PAID
  
      def test_clone_invoice_into_draft(self):
          invoice = InvoiceFactory.create()
          invoice.issue()
          invoice.pay()
  
          entries = DocumentEntryFactory.create_batch(3)
          invoice.invoice_entries.add(*entries)
  
          clone = invoice.clone_into_draft()
  
          assert clone.state == Invoice.STATES.DRAFT
          assert clone.paid_date is None
          assert clone.issue_date is None
          assert clone.related_document is None
          assert (clone.series != invoice.series or
                  clone.number != invoice.number)
          assert clone.sales_tax_percent == invoice.sales_tax_percent
          assert clone.sales_tax_name == invoice.sales_tax_name
  
          assert not clone.archived_customer
          assert not clone.archived_provider
          assert clone.customer == invoice.customer
          assert clone.provider == invoice.provider
  
          assert clone.currency == invoice.currency
          assert clone._last_state == clone.state
          assert clone.pk != invoice.pk
          assert clone.id != invoice.id
          assert not clone.pdf
  
          assert clone.invoice_entries.count() == 3
          assert invoice.invoice_entries.count() == 3
  
          entry_fields = [entry.name for entry in DocumentEntry._meta.get_fields()]
          for clone_entry, original_entry in zip(clone.invoice_entries.all(),
                                                 invoice.invoice_entries.all()):
              for entry in entry_fields:
                  if entry not in ('id', 'proforma', 'invoice'):
                      assert getattr(clone_entry, entry) == \
                          getattr(original_entry, entry)
          assert invoice.state == Invoice.STATES.PAID
  
      def test_cancel_issued_invoice_with_related_proforma(self):
          proforma = ProformaFactory.create()
          proforma.issue()
  
          if not proforma.related_document:
              proforma.create_invoice()
  
          proforma.related_document.cancel()
  
          assert proforma.related_document.state == proforma.state == Invoice.STATES.CANCELED
  
      def _get_decimal_places(self, number):
          return max(0, -number.as_tuple().exponent)
  
      def test_invoice_total_decimal_points(self):
          invoice_entries = DocumentEntryFactory.create_batch(3)
          invoice = InvoiceFactory.create(invoice_entries=invoice_entries)
  
          assert self._get_decimal_places(invoice.total) == 2
  
      def test_invoice_total_before_tax_decimal_places(self):
          invoice_entries = DocumentEntryFactory.create_batch(3)
          invoice = InvoiceFactory.create(invoice_entries=invoice_entries)
  
          invoice.sales_tax_percent = Decimal('20.00')
  
          assert self._get_decimal_places(invoice.total_before_tax) == 2
  
      def test_invoice_tax_value_decimal_places(self):
          invoice_entries = DocumentEntryFactory.create_batch(3)
          invoice = InvoiceFactory.create(invoice_entries=invoice_entries)
  
          invoice.sales_tax_percent = Decimal('20.00')
  
          assert self._get_decimal_places(invoice.tax_value) == 2
  
      def test_invoice_total_with_tax_integrity(self):
          invoice_entries = DocumentEntryFactory.create_batch(5)
          invoice = InvoiceFactory.create(invoice_entries=invoice_entries)
  
          invoice.sales_tax_percent = Decimal('20.00')
  
          self.assertEqual(invoice.total, invoice.total_before_tax + invoice.tax_value)
  
      def test_draft_invoice_series_number(self):
          invoice = InvoiceFactory.create()
          invoice.number = None
  
          assert invoice.series_number == '%s-draft-id:%d' % (invoice.series,
                                                              invoice.pk)
  
          invoice.series = None
  
          assert invoice.series_number == 'draft-id:%d' % invoice.pk
  
      def test_issues_invoice_series_number(self):
          invoice = InvoiceFactory.create(state=Invoice.STATES.ISSUED)
  
          assert invoice.series_number == '%s-%s' % (invoice.series,
                                                     invoice.number)
  
      def test_invoice_due_today_queryset(self):
          invoices = InvoiceFactory.create_batch(5)
  
          invoices[0].due_date = date.today()
          invoices[0].save()
  
          invoices[1].due_date = date.today()
          invoices[1].issue()
  
          invoices[2].due_date = date.today() - timedelta(days=1)
          invoices[2].issue()
          invoices[2].pay()
  
          invoices[3].due_date = date.today()
          invoices[3].issue()
          invoices[3].cancel()
  
          invoices[4].due_date = date.today() + timedelta(days=1)
          invoices[4].issue()
  
          queryset = Invoice.objects.due_today()
  
          assert queryset.count() == 1
          assert invoices[1] in queryset
  
      def test_invoice_due_this_month_queryset(self):
          invoices = InvoiceFactory.create_batch(4)
  
          invoices[0].due_date = date.today().replace(day=20)
          invoices[0].issue()
  
          invoices[1].due_date = date.today().replace(day=1)
          invoices[1].issue()
  
          invoices[2].due_date = date.today() - timedelta(days=31)
          invoices[2].issue()
  
          invoices[3].issue()
          invoices[3].cancel()
  
          queryset = Invoice.objects.due_this_month()
  
          assert queryset.count() == 2
          for invoice in invoices[:2]:
              assert invoice in queryset
  
      def test_invoice_overdue_queryset(self):
          invoices = InvoiceFactory.create_batch(3)
  
          invoices[0].due_date = date.today() - timedelta(days=1)
          invoices[0].issue()
  
          invoices[1].due_date = date.today() - timedelta(days=3)
          invoices[1].issue()
  
          invoices[2].due_date = date.today() - timedelta(days=31)
          invoices[2].issue()
          invoices[2].pay()
  
          queryset = Invoice.objects.overdue()
  
          assert queryset.count() == 2
          for invoice in invoices[:2]:
              assert invoice in queryset
  
      def test_invoice_overdue_since_last_month_queryset(self):
          invoices = InvoiceFactory.create_batch(3)
  
          invoices[0].due_date = date.today().replace(day=1)
          invoices[0].issue()
  
          invoices[1].due_date = date.today() - timedelta(days=31)
          invoices[1].issue()
  
          queryset = Invoice.objects.overdue_since_last_month()
  
          assert queryset.count() == 1
          assert invoices[1] in queryset
  
      def test_customer_currency_used_for_transaction_currency(self):
          customer = CustomerFactory.create(currency='EUR')
          invoice = InvoiceFactory.create(customer=customer,
                                          transaction_currency=None)
  
          self.assertEqual(invoice.transaction_currency, 'EUR')
  
      def test_invoice_currency_used_for_transaction_currency(self):
          customer = CustomerFactory.create(currency=None)
          invoice = InvoiceFactory.create(customer=customer,
                                          currency='EUR',
                                          transaction_currency=None)
  
          self.assertEqual(invoice.transaction_currency, 'EUR')
  
      def test_invoice_create_storno_from_canceled_state(self):
          invoice = InvoiceFactory.create(invoice_entries=[DocumentEntryFactory.create()])
          invoice.issue()
          invoice.cancel()
          storno = invoice.create_storno()
  
          assert -invoice.total == storno.total != 0
          assert storno.related_document == invoice
          assert invoice.customer == storno.customer
          assert invoice.provider == storno.provider
  
      def test_invoice_create_storno_from_paid_state(self):
          invoice = InvoiceFactory.create(invoice_entries=[DocumentEntryFactory.create()])
          invoice.issue()
          invoice.pay()
          storno = invoice.create_storno()
  
          assert -invoice.total == storno.total != 0
          assert storno.related_document == invoice
          assert invoice.customer == storno.customer
          assert invoice.provider == storno.provider
  
      def test_invoice_create_storno_from_unallowed_states(self):
          invoice = InvoiceFactory.create()
          assert invoice.state == invoice.STATES.DRAFT
          self.assertRaises(ValueError, invoice.create_storno)
  
          invoice.issue()
          self.assertRaises(ValueError, invoice.create_storno)
  
      def test_invoice_storno_issue(self):
          invoice = InvoiceFactory.create()
          invoice.issue()
          invoice.pay()
          storno = invoice.create_storno()
  
          storno.issue()
          assert storno.state == storno.STATES.ISSUED
          assert storno.issue_date == date.today()
          assert not storno.due_date
  