from __future__ import unicode_literals

from decimal import Decimal
import re
from datetime import date, timedelta, datetime
import logging

from django.contrib.sites.models import Site
from django.db.models import Max
from django.utils import translation
from django.utils.encoding import python_2_unicode_compatible
from django.utils.timezone import now
from django_countries.fields import CountryField
from django.core.urlresolvers import reverse
from django.template.base import Template
from django.utils.translation import ugettext_lazy as _, pgettext_lazy
from django.db import models
from ordered_model.models import OrderedModel
import vatnumber
from django.template import Context
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured, ValidationError

from plans.contrib import send_template_email, get_user_language
from plans.enum import Enumeration
from plans.signals import (order_completed, account_activated,
                           account_expired, account_change_plan,
                           account_deactivated)
from .validators import plan_validation
from plans.taxation.eu import EUTaxationPolicy


accounts_logger = logging.getLogger('accounts')

# Create your models here.


@python_2_unicode_compatible
class Plan(OrderedModel):
    """
    Single plan defined in the system. A plan can customized (referred to user)
    which means that only this user can purchase this plan and have it
    selected.
    Plan also can be visible and available. Plan is displayed on the list of
    currently available plans for user if it is visible. User cannot change
    plan to a plan that is not visible. Available means that user can buy a
    plan. If plan is not visible but still available it means that user which
    is using this plan already will be able to extend this plan again. If plan
    is not visible and not available, he will be forced then to change plan
    next time he extends an account. """

    name = models.CharField(_('name'), max_length=100)
    description = models.TextField(_('description'), blank=True)
    default = models.BooleanField(default=False, db_index=True)
    available = models.BooleanField(_('available'), default=False,
                                    db_index=True,
                                    help_text=_(
                                        'Is still available for purchase'))
    visible = models.BooleanField(_('visible'), default=True, db_index=True,
                                  help_text=_('Is visible in current offer'))
    created = models.DateTimeField(_('created'), db_index=True)
    customized = models.ForeignKey(settings.AUTH_USER_MODEL, null=True,
                                   blank=True, verbose_name=_('customized'))
    quotas = models.ManyToManyField('Quota', through='PlanQuota',
                                    verbose_name=_('quotas'))
    url = models.CharField(max_length=200, blank=True, help_text=_(
        'Optional link to page with more information (for clickable pricing table headers)'))

    class Meta:
        ordering = ('order',)
        verbose_name = _("Plan")
        verbose_name_plural = _("Plans")

    def save(self, *args, **kwargs):
        if not self.created:
            self.created = now()


        super(Plan, self).save(*args, **kwargs)

    @classmethod
    def get_default_plan(cls):
        try:
            return cls.objects.filter(default=True)[0]
        except IndexError:
            return None

    def __str__(self): # pragma: no cover
        return self.name

    def get_quota_dict(self):
        quota_dic = {}
        for plan_quota in PlanQuota.objects.filter(plan=self).select_related('quota'):
            quota_dic[plan_quota.quota.codename] = plan_quota.value
        return quota_dic

    def is_free(self):
        return self.planpricing_set.count() == 0

class BillingInfo(models.Model):
    """
    Stores customer billing data needed to issue an invoice
    """
    user = models.OneToOneField(settings.AUTH_USER_MODEL, verbose_name=_('user'))
    tax_number = models.CharField(_('VAT ID'), max_length=200, blank=True, db_index=True)
    name = models.CharField(_('name'), max_length=200, db_index=True)
    street = models.CharField(_('street'), max_length=200)
    zipcode = models.CharField(_('zip code'), max_length=200)
    city = models.CharField(_('city'), max_length=200)
    country = CountryField(_("country"))

    shipping_name = models.CharField(_('name (shipping)'), max_length=200,
                                     blank=True, help_text=_('optional'))
    shipping_street = models.CharField(_('street (shipping)'), max_length=200,
                                       blank=True, help_text=_('optional'))
    shipping_zipcode = models.CharField(_('zip code (shipping)'),
                                        max_length=200, blank=True,
                                        help_text=_('optional'))
    shipping_city = models.CharField(_('city (shipping)'), max_length=200,
                                     blank=True, help_text=_('optional'))

    class Meta:
        verbose_name = _("Billing info")
        verbose_name_plural = _("Billing infos")

    @staticmethod
    def clean_tax_number(tax_number, country):
        tax_number = re.sub(r'[^A-Z0-9]', '', tax_number.upper())
        if tax_number and country:

            if country in vatnumber.countries():
                number = tax_number
                if tax_number.startswith(country):
                    number = tax_number[len(country):]

                if not vatnumber.check_vat(country + number):
                    # This is a proper solution to bind ValidationError
                    # to a Field but it is not working due to django bug :(

                    # errors = defaultdict(list)
                    # errors['tax_number'].append(_('VAT ID is not correct'))
                    # raise ValidationError(errors)
                    raise ValidationError(_('VAT ID is not correct'))

            return tax_number
        else:
            return ''


# FIXME: How to make validation in Model clean and attach it to a field? Seems
# that it is not working right now
    # def clean(self):
    #     super(BillingInfo, self).clean()
    #     self.tax_number = BillingInfo.clean_tax_number(self.tax_number,
    #                                                    self.country)

@python_2_unicode_compatible
class UserPlan(models.Model):
    """
    Currently selected plan for user account.
    """
    user = models.OneToOneField(settings.AUTH_USER_MODEL,
                                verbose_name=_('user'))
    plan = models.ForeignKey('Plan', verbose_name=_('plan'), null=True)
    expire = models.DateField(_('expire'), default=None, blank=True,
                              null=True, db_index=True)
    active = models.BooleanField(_('active'), default=True, db_index=True)

    class Meta:
        verbose_name = _("User plan")
        verbose_name_plural = _("Users plans")

    def __str__(self):  # pragma: no cover
        return "%s [%s]" % (self.user, self.plan)

    def is_active(self):
        return self.active

    def is_expired(self):
        if self.expire is None:
            return False
        else:
            return self.expire < date.today()

    def days_left(self):
        if self.expire is None:
            return None
        else:
            return (self.expire - date.today()).days

    def clean_activation(self):
        errors = plan_validation(self.user)
        if not errors['required_to_activate']:
            plan_validation(self.user, on_activation=True)
            self.activate()
        else:
            self.deactivate()
        return errors

    def activate(self):
        if not self.active:
            self.active = True
            self.save()
            account_activated.send(sender=self, user=self.user)

    def deactivate(self):
        if self.active:
            self.active = False
            self.save()
            account_deactivated.send(sender=self, user=self.user)

    def initialize(self):
        """
        Set up user plan for first use
        """
        if not self.is_active() and self.plan is not None:
            # Plans without pricings don't need to expire
            if self.expire is None and self.plan.planpricing_set.count():
                self.expire = now() + timedelta(
                    days=getattr(settings, 'PLANS_DEFAULT_GRACE_PERIOD', 30))
            self.activate()  # this will call self.save()

    def extend_account(self, plan, pricing):
        """
        Manages extending account after plan or pricing order
        :param plan:
        :param pricing: if pricing is None then account will be only upgraded
        :return:
        """

        status = False  # flag; if extending account was successful?
        if pricing is None:
            # Process a plan change request (downgrade or upgrade)
            # No account activation or extending at this point
            self.plan = plan

            if self.expire is not None and not plan.planpricing_set.count():
                # Assume no expiry date for plans without pricing.
                self.expire = None

            self.save()
            account_change_plan.send(sender=self, user=self.user)
            mail_context = {'user': self.user, 'userplan': self, 'plan': plan}
            send_template_email([self.user.email],
                                'mail/change_plan_title.txt',
                                'mail/change_plan_body.txt',
                                mail_context, get_user_language(self.user))
            accounts_logger.info(
                "Account '{0}' [id={1}] plan changed to '{2}' [id={3}]".format(
                    self.user, self.user.pk, plan, plan.pk))
            status = True
        else:
            # Processing standard account extending procedure
            if self.plan == plan or self.plan is None:
                self.plan = plan
                status = True
                if self.expire is not None and self.expire > date.today():
                    self.expire += timedelta(days=pricing.period)
                else:
                    self.expire = date.today() + timedelta(days=pricing.period)

            else:
                # This should not ever happen (as this case should be managed
                # by plan change request) but just in case we consider a case
                # when user has a different plan
                if not self.plan.is_free and self.expire is None:
                    status = True
                elif not self.plan.is_free and self.expire > date.today():
                    status = False
                    accounts_logger.warning(
                        "Account '{0}}' [id={2}}] plan NOT changed to '{3}}' [id={4}}]".format(self.user, self.user.pk, plan, plan.pk))
                else:
                    status = True
                    account_change_plan.send(sender=self, user=self.user)
                    self.plan = plan
                    self.expire = date.today() + timedelta(days=pricing.period)

            if status:
                self.save()
                accounts_logger.info(
                    "Account '{0}' [id={1}] has been extended by {2} days using plan '{3}' [id={4}]".format(self.user, self.user.pk, pricing.period, plan, plan.pk))
                mail_context = {'user': self.user,
                                'userplan': self,
                                'plan': plan,
                                'pricing': pricing}
                send_template_email([self.user.email],
                                    'mail/extend_account_title.txt',
                                    'mail/extend_account_body.txt',
                                    mail_context, get_user_language(self.user))

        if status:
            self.clean_activation()

        return status

    def expire_account(self):
        """manages account expiration"""
        self.deactivate()
        accounts_logger.info(
            "Account '{0}' [id={1}] has expired".format(self.user,
                                                        self.user.pk))
        mail_context = {'user': self.user, 'userplan': self}
        send_template_email([self.user.email],
                            'mail/expired_account_title.txt',
                            'mail/expired_account_body.txt',
                            mail_context, get_user_language(self.user))

        account_expired.send(sender=self, user=self.user)

    def remind_expire_soon(self):
        """reminds about soon account expiration"""
        mail_context = {'user': self.user,
                        'userplan': self,
                        'days': self.days_left()}
        send_template_email([self.user.email],
                            'mail/remind_expire_title.txt',
                            'mail/remind_expire_body.txt',
                            mail_context, get_user_language(self.user))


@python_2_unicode_compatible
class Pricing(models.Model):
    """
    Type of plan period that could be purchased (e.g. 10 days, month, year,
    etc)
    """
    name = models.CharField(_('name'), max_length=100)
    period = models.PositiveIntegerField(_('period'), default=30,
                                         null=True, blank=True, db_index=True)
    url = models.CharField(max_length=200, blank=True, help_text=_(
        'Optional link to page with more information (for clickable pricing table headers)'))

    class Meta:
        ordering = ('period',)
        verbose_name = _("Pricing")
        verbose_name_plural = _("Pricings")

    def __str__(self):  # pragma: no cover
        return "%s (%d " % (self.name, self.period) + "%s)" % _("days")


@python_2_unicode_compatible
class Quota(OrderedModel):
    """
    Single countable or boolean property of system (limitation).
    """
    codename = models.CharField(_('codename'), max_length=50,
                                unique=True, db_index=True)
    name = models.CharField(_('name'), max_length=100)
    unit = models.CharField(_('unit'), max_length=100, blank=True)
    description = models.TextField(_('description'), blank=True)
    is_boolean = models.BooleanField(_('is boolean'), default=False)
    url = models.CharField(max_length=200, blank=True, help_text=_(
        'Optional link to page with more information (for clickable pricing table headers)'))

    class Meta:
        ordering = ('order',)
        verbose_name = _("Quota")
        verbose_name_plural = _("Quotas")

    def __str__(self):  # pragma: no cover
        return "{}".format(self.codename, )


class PlanPricingManager(models.Manager):
    def get_query_set(self):
        return super(
            PlanPricingManager, self).get_query_set().select_related('plan',
                                                                     'pricing')


@python_2_unicode_compatible
class PlanPricing(models.Model):
    plan = models.ForeignKey('Plan')
    pricing = models.ForeignKey('Pricing')
    price = models.DecimalField(max_digits=7, decimal_places=2, db_index=True)

    objects = PlanPricingManager()

    class Meta:
        ordering = ('pricing__period', )
        verbose_name = _("Plan pricing")
        verbose_name_plural = _("Plans pricings")

    def __str__(self): # pragma: no cover
        return "%s %s" % (self.plan.name, self.pricing)


class PlanQuotaManager(models.Manager):
    def get_query_set(self):
        return super(
            PlanQuotaManager, self).get_query_set().select_related('plan',
                                                                   'quota')


class PlanQuota(models.Model):
    plan = models.ForeignKey('Plan')
    quota = models.ForeignKey('Quota')
    value = models.IntegerField(default=1, null=True, blank=True)

    objects = PlanQuotaManager()

    class Meta:
        verbose_name = _("Plan quota")
        verbose_name_plural = _("Plans quotas")


@python_2_unicode_compatible
class Order(models.Model):
    """
    Order in this app supports only one item per order. This item is defined by
    plan and pricing attributes. If both are defined the order represents
    buying an account extension.

    If only plan is provided (with pricing set to None) this means that user
    purchased a plan upgrade.
    """
    STATUS = Enumeration([
        (1, 'NEW', pgettext_lazy('Order status', 'new')),
        (2, 'COMPLETED', pgettext_lazy('Order status', 'completed')),
        (3, 'NOT_VALID', pgettext_lazy('Order status', 'not valid')),
        (4, 'CANCELED', pgettext_lazy('Order status', 'canceled')),
        (5, 'RETURNED', pgettext_lazy('Order status', 'returned')),

    ])

    user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('user'))
    flat_name = models.CharField(max_length=200, blank=True, null=True)
    plan = models.ForeignKey('Plan',
                             verbose_name=_('plan'), related_name="plan_order")
    # if pricing is None the order is upgrade plan, not buy new pricing
    pricing = models.ForeignKey('Pricing',
                                blank=True, null=True,
                                verbose_name=_('pricing'))
    created = models.DateTimeField(_('created'), db_index=True)
    completed = models.DateTimeField(_('completed'),
                                     null=True, blank=True, db_index=True)
    amount = models.DecimalField(_('amount'),
                                 max_digits=7, decimal_places=2, db_index=True)
    # Tax=None is when tax is not applicable
    tax = models.DecimalField(_('tax'), max_digits=4, decimal_places=2,
                              db_index=True, null=True, blank=True)
    currency = models.CharField(_('currency'), max_length=3, default='EUR')
    status = models.IntegerField(_('status'),
                                 choices=STATUS, default=STATUS.NEW)

    def save(self, force_insert=False,
             force_update=False, using=None, update_fields=None):
        if self.created is None:
            self.created = now()

        return super(Order, self).save(force_insert, force_update, using)

    def __str__(self):  # pragma: no cover
        return _("Order #%(id)d") % {'id': self.id}

    @property
    def name(self):
        """
        Support for two kind of Order names:
        * (preferred) dynamically generated from Plan and Pricing (if flatname
        is not provided) (translatable)
        * (legacy) just return flat name, which is any text (not translatable)

        Flat names are only introduced for legacy system support, when you need
        to migrate old orders into django-plans and you cannot match
        Plan&Pricings convention.
        """
        if self.flat_name:
            return self.flat_name
        else:
            return "%s %s %s " % (_('Plan'), self.plan.name, "(upgrade)" if self.pricing is None else '- %s' % self.pricing)

    def is_ready_for_payment(self):
        return self.status == self.STATUS.NEW and (now() - self.created).days < getattr(
            settings, 'PLANS_ORDER_EXPIRATION', 14)

    def complete_order(self):
        if self.completed is None:
            status = self.user.userplan.extend_account(self.plan, self.pricing)
            self.completed = now()
            if status:
                self.status = Order.STATUS.COMPLETED
            else:
                self.status = Order.STATUS.NOT_VALID
            self.save()
            order_completed.send(self)
            return True
        else:
            return False

    def get_invoices_proforma(self):
        return Invoice.proforma.filter(order=self)

    def get_invoices(self):
        return Invoice.invoices.filter(order=self)

    def get_all_invoices(self):
        return self.invoice_set.order_by('issued', 'issued_duplicate', 'pk')

    def tax_total(self):
        if self.tax is None:
            return Decimal('0.00')
        else:
            return self.total() - self.amount

    def total(self):
        if self.tax is not None:
            return (self.amount * (self.tax + 100) / 100).quantize(Decimal('1.00'))
        else:
            return self.amount

    def get_absolute_url(self):
        return reverse('order', kwargs={'pk': self.pk})

    class Meta:
        ordering = ('-created', )
        verbose_name = _("Order")
        verbose_name_plural = _("Orders")


class InvoiceManager(models.Manager):
    def get_query_set(self):
        return super(
            InvoiceManager, self).get_query_set().filter(
                type=Invoice.INVOICE_TYPES['INVOICE'])


class InvoiceProformaManager(models.Manager):
    def get_query_set(self):
        return super(
            InvoiceProformaManager, self).get_query_set().filter(
                type=Invoice.INVOICE_TYPES['PROFORMA'])


class InvoiceDuplicateManager(models.Manager):
    def get_query_set(self):
        return super(
            InvoiceDuplicateManager, self).get_query_set().filter(
                type=Invoice.INVOICE_TYPES['DUPLICATE'])


@python_2_unicode_compatible
class Invoice(models.Model):
    """
    Single invoice document.
    """

    INVOICE_TYPES = Enumeration([
        (1, 'INVOICE', _('Invoice')),
        (2, 'DUPLICATE', _('Invoice Duplicate')),
        (3, 'PROFORMA', pgettext_lazy('proforma', 'Order confirmation')),

    ])

    objects = models.Manager()
    invoices = InvoiceManager()
    proforma = InvoiceProformaManager()
    duplicates = InvoiceDuplicateManager()

    class NUMBERING:
        """Used as a choices for settings.PLANS_INVOICE_COUNTER_RESET"""

        DAILY = 1
        MONTHLY = 2
        ANNUALLY = 3

    user = models.ForeignKey(settings.AUTH_USER_MODEL)
    order = models.ForeignKey('Order')
    number = models.IntegerField(db_index=True)
    full_number = models.CharField(max_length=200)
    # FIXME: This should be changed type is a reserved word for Python
    type = models.IntegerField(choices=INVOICE_TYPES,
                               default=INVOICE_TYPES.INVOICE, db_index=True)
    issued = models.DateField(db_index=True)
    issued_duplicate = models.DateField(db_index=True, null=True, blank=True)
    selling_date = models.DateField(db_index=True, null=True, blank=True)
    payment_date = models.DateField(db_index=True)
    unit_price_net = models.DecimalField(max_digits=7, decimal_places=2)
    quantity = models.IntegerField(default=1)
    total_net = models.DecimalField(max_digits=7, decimal_places=2)
    total = models.DecimalField(max_digits=7, decimal_places=2)
    tax_total = models.DecimalField(max_digits=7, decimal_places=2)
    # Tax=None is when tax is not applicable
    tax = models.DecimalField(max_digits=4, decimal_places=2,
                              db_index=True, null=True, blank=True)
    rebate = models.DecimalField(max_digits=4,
                                 decimal_places=2, default=Decimal(0))
    currency = models.CharField(max_length=3, default='EUR')
    item_description = models.CharField(max_length=200)
    buyer_name = models.CharField(max_length=200, verbose_name=_("Name"))
    buyer_street = models.CharField(max_length=200, verbose_name=_("Street"))
    buyer_zipcode = models.CharField(max_length=200,
                                     verbose_name=_("Zip code"))
    buyer_city = models.CharField(max_length=200, verbose_name=_("City"))
    buyer_country = CountryField(verbose_name=_("Country"), default='PL')
    buyer_tax_number = models.CharField(max_length=200,
                                        blank=True,
                                        verbose_name=_("TAX/VAT number"))
    shipping_name = models.CharField(max_length=200, verbose_name=_("Name"))
    shipping_street = models.CharField(max_length=200,
                                       verbose_name=_("Street"))
    shipping_zipcode = models.CharField(max_length=200,
                                        verbose_name=_("Zip code"))
    shipping_city = models.CharField(max_length=200, verbose_name=_("City"))
    shipping_country = CountryField(verbose_name=_("Country"), default='PL')
    require_shipment = models.BooleanField(default=False, db_index=True)
    issuer_name = models.CharField(max_length=200, verbose_name=_("Name"))
    issuer_street = models.CharField(max_length=200, verbose_name=_("Street"))
    issuer_zipcode = models.CharField(max_length=200,
                                      verbose_name=_("Zip code"))
    issuer_city = models.CharField(max_length=200, verbose_name=_("City"))
    issuer_country = CountryField(verbose_name=_("Country"), default='PL')
    issuer_tax_number = models.CharField(max_length=200,
                                         blank=True,
                                         verbose_name=_("TAX/VAT number"))

    class Meta:
        verbose_name = _("Invoice")
        verbose_name_plural = _("Invoices")

    def __str__(self):  # pragma: no cover
        return self.full_number

    def get_absolute_url(self):
        return reverse('invoice_preview', kwargs={'pk': self.pk})

    def clean(self):
        if self.number is None:
            invoice_counter_reset = getattr(settings,
                                            'PLANS_INVOICE_COUNTER_RESET',
                                            Invoice.NUMBERING.MONTHLY)

            if invoice_counter_reset == Invoice.NUMBERING.DAILY:
                last_number = Invoice.objects.filter(
                    issued=self.issued, type=self.type).aggregate(
                        Max('number'))['number__max'] or 0

            elif invoice_counter_reset == Invoice.NUMBERING.MONTHLY:
                last_number = Invoice.objects.filter(
                    issued__year=self.issued.year,
                    issued__month=self.issued.month,
                    type=self.type).aggregate(
                        Max('number'))['number__max'] or 0

            elif invoice_counter_reset == Invoice.NUMBERING.ANNUALLY:
                last_number = Invoice.objects.filter(
                    issued__year=self.issued.year,
                    type=self.type).aggregate(
                        Max('number'))['number__max'] or 0

            else:
                raise ImproperlyConfigured(
                    "PLANS_INVOICE_COUNTER_RESET can be set only to these values: daily, monthly, yearly.")

            self.number = last_number + 1

        if self.full_number == "":
            self.full_number = self.get_full_number()

        super(Invoice, self).clean()

        # def validate_unique(self, exclude=None):
        #     super(Invoice, self).validate_unique(exclude)
        #     if self.type == Invoice.INVOICE_TYPES.INVOICE:
        #         if Invoice.objects.filter(order=self.order).count():
        #            raise ValidationError("Duplicate invoice for order")
        #
        #     if self.type in (Invoice.INVOICE_TYPES.INVOICE,
        #                      Invoice.INVOICE_TYPES.PROFORMA):
        #         pass

    def get_full_number(self):
        """
        Generates on the fly invoice full number from template provided by
        ``settings.PLANS_INVOICE_NUMBER_FORMAT``. ``Invoice`` object is
        provided as ``invoice`` variable to the template, therefore all object
        fieldscan be used to generate full number format.

        .. warning::

            This is only used to prepopulate ``full_number`` field on saving
            new invoice. To get invoice full number always use ``full_number``
            field.

        :return: string (generated full number)
        """
        invoice_format = getattr(
            settings, "PLANS_INVOICE_NUMBER_FORMAT",
            "{{ invoice.number }}/{% ifequal invoice.type invoice.INVOICE_TYPES.PROFORMA %}PF{% else %}FV{% endifequal %}/{{ invoice.issued|date:'m/Y' }}")

        return Template(invoice_format).render(Context({'invoice': self}))

    def set_issuer_invoice_data(self):
        """
        Fills models object with issuer data copied from
        ``settings.PLANS_INVOICE_ISSUER``

        :raise: ImproperlyConfigured
        """
        try:
            issuer = getattr(settings, 'PLANS_INVOICE_ISSUER')
        except:
            raise ImproperlyConfigured("Please set PLANS_INVOICE_ISSUER in order to make an invoice.")

        self.issuer_name = issuer['issuer_name']
        self.issuer_street = issuer['issuer_street']
        self.issuer_zipcode = issuer['issuer_zipcode']
        self.issuer_city = issuer['issuer_city']
        self.issuer_country = issuer['issuer_country']
        self.issuer_tax_number = issuer['issuer_tax_number']

    def set_buyer_invoice_data(self, billing_info):
        """
        Fill buyer invoice billing and shipping data by copy them from provided
        user's ``BillingInfo`` object.

        :param billing_info: BillingInfo object
        :type billing_info: BillingInfo
        """
        self.buyer_name = billing_info.name
        self.buyer_street = billing_info.street
        self.buyer_zipcode = billing_info.zipcode
        self.buyer_city = billing_info.city
        self.buyer_country = billing_info.country
        self.buyer_tax_number = billing_info.tax_number

        self.shipping_name = billing_info.shipping_name or billing_info.name
        self.shipping_street = billing_info.shipping_street or billing_info.street
        self.shipping_zipcode = billing_info.shipping_zipcode or billing_info.zipcode
        self.shipping_city = billing_info.shipping_city or billing_info.city
        #TODO: Should allow shipping to other country? Not think so
        self.shipping_country = billing_info.country

    def copy_from_order(self, order):
        """
        Filling orders details likes totals, taxes, etc and linking provided
        ``Order`` object with an invoice

        :param order: Order object
        :type order: Order
        """
        self.order = order
        self.user = order.user
        self.unit_price_net = order.amount
        self.total_net = order.amount
        self.total = order.total()
        self.tax_total = order.total() - order.amount
        self.tax = order.tax
        self.currency = order.currency
        self.item_description = "{0} - {1}".format(
            Site.objects.get_current().name, order.name)

    @classmethod
    def create(cls, order, invoice_type):
        language_code = get_user_language(order.user)

        if language_code is not None:
            translation.activate(language_code)

        try:
            billing_info = BillingInfo.objects.get(user=order.user)
        except BillingInfo.DoesNotExist:
            return

        day = date.today()
        pday = order.completed
        if invoice_type == Invoice.INVOICE_TYPES['PROFORMA']:
            pday = day + timedelta(days=14)
        # FIXME: 14 - this should set accordingly to ORDER_TIMEOUT in days
        invoice = cls(issued=day,
                      selling_date=order.completed, payment_date=pday)
        invoice.type = invoice_type
        invoice.copy_from_order(order)
        invoice.set_issuer_invoice_data()
        invoice.set_buyer_invoice_data(billing_info)
        invoice.clean()
        invoice.save()
        if language_code is not None:
            translation.deactivate()

    def send_invoice_by_email(self):
        language_code = get_user_language(self.user)

        if language_code is not None:
            translation.activate(language_code)

        mail_context = {'user': self.user,
                        'invoice_type': self.get_type_display(),
                        'invoice_number': self.get_full_number(),
                        'order': self.order.id,
                        'url': self.get_absolute_url(),
                        }

        if language_code is not None:
            translation.deactivate()

        send_template_email([self.user.email],
                            'mail/invoice_created_title.txt',
                            'mail/invoice_created_body.txt',
                            mail_context, language_code)

    def is_UE_customer(self):
        return EUTaxationPolicy.is_in_EU(self.buyer_country.code)
