# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4

##
## Copyright (C) 2007 Async Open Source <http://www.async.com.br>
## All rights reserved
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., or visit: http://www.gnu.org/.
##
## Author(s): Stoq Team <stoq-devel@async.com.br>
##
"""
Commission management
"""

from decimal import Decimal

from storm.expr import Join
from storm.references import Reference

from stoqlib.database.properties import PercentCol, PriceCol
from stoqlib.database.properties import IntCol
from stoqlib.database.viewable import Viewable
from stoqlib.domain.base import Domain
from stoqlib.domain.payment.payment import Payment
from stoqlib.domain.person import Person, SalesPerson
from stoqlib.domain.sale import Sale
from stoqlib.lib.defaults import quantize
from stoqlib.lib.translation import stoqlib_gettext

_ = stoqlib_gettext


class CommissionSource(Domain):
    """Commission Source object implementation

    A CommissionSource is tied to a |sellablecategory| or |sellable|,
    it's used to determine the value of a commission for a certain
    item which is sold.
    There are two different commission values defined here, one
    which is used when the item is sold directly, eg one installment
    and another one which is used when the item is sold in installments.

    The category and the sellable should not exist when sellable exists
    and the opposite is true.

    See also:
    `schema <http://doc.stoq.com.br/schema/tables/commission_source.html>`__,
    """

    __storm_table__ = 'commission_source'

    #: the commission value to be used in a |sale| with one installment
    direct_value = PercentCol()

    #: the commission value to be used in a |sale| with multiple installments
    installments_value = PercentCol()

    category_id = IntCol(default=None)

    #: the |sellablecategory|
    category = Reference(category_id, 'SellableCategory.id')

    sellable_id = IntCol(default=None)

    #: the |sellable|
    sellable = Reference(sellable_id, 'Sellable.id')


class Commission(Domain):
    """Commission object implementation

    A Commission is the commission received by a |salesperson|
    for a specific |payment| made by a |sale|.

    There is one Commission for each |payment| of a |sale|.

    See also:
    `schema <http://doc.stoq.com.br/schema/tables/commission.html>`__,
    """

    __storm_table__ = 'commission'

    #: use direct commission to calculate the commission amount
    DIRECT = 0

    #: use installments commission to calculate the commission amount
    INSTALLMENTS = 1

    commission_type = IntCol(default=DIRECT)

    #: The commission amount
    value = PriceCol(default=0)

    salesperson_id = IntCol()

    #: who sold the |sale| this commission applies to
    salesperson = Reference(salesperson_id, 'SalesPerson.id')

    sale_id = IntCol()

    #: the |sale| this commission applies to
    sale = Reference(sale_id, 'Sale.id')

    payment_id = IntCol()

    #: the |payment| this commission applies to
    payment = Reference(payment_id, 'Payment.id')

    #
    #  Domain
    #

    def __init__(self, store=None, **kwargs):
        need_calculate_value = not 'value' in kwargs
        super(Commission, self).__init__(store=store, **kwargs)
        if need_calculate_value:
            self._calculate_value()

    #
    #  Private
    #

    def _calculate_value(self):
        """Calculates the commission amount to be paid"""

        relative_percentage = self._get_payment_percentage()

        # The commission is calculated for all sellable items
        # in sale; a relative percentage is given for each payment
        # of the sale.
        # Eg:
        #   If a customer decides to pay a sale in two installments,
        #   Let's say divided in 20%, and 80% of the total value of the items
        #   which was bought in the sale. Then the commission received by the
        #   sales person is also going to be 20% and 80% of the complete
        #   commission amount for the sale when that specific payment is payed.
        value = Decimal(0)
        for sellable_item in self.sale.get_items():
            value += (self._get_commission(sellable_item.sellable) *
                      sellable_item.get_total() *
                      relative_percentage)

        # The calculation above may have produced a number with more than two
        # digits. Round it to only two
        self.value = quantize(value)

    def _get_payment_percentage(self):
        """Return the payment percentage of sale"""
        total = self.sale.get_sale_subtotal()
        if total == 0:
            return 0
        else:
            return self.payment.value / total

    def _get_commission(self, sellable):
        """Return the properly commission percentage to be used to
        calculate the commission amount, for a given sellable.
        """

        store = self.store
        source = store.find(CommissionSource, sellable=sellable).one()
        if not source and sellable.category:
            source = self._get_category_commission(sellable.category)

        value = 0
        if source:
            if self.commission_type == self.DIRECT:
                value = source.direct_value
            else:
                value = source.installments_value
            value /= Decimal(100)

        return value

    def _get_category_commission(self, category):
        if category:
            store = self.store
            source = store.find(CommissionSource, category=category).one()
            if not source:
                return self._get_category_commission(category.category)
            return source

#
# Views
#


class CommissionView(Viewable):
    """ Stores information about commissions and it's related
        sale and payment.
    """

    id = Sale.id
    identifier = Sale.identifier
    sale_status = Sale.status
    code = Commission.id
    commission_value = Commission.value
    commission_percentage = Commission.value / Payment.value * 100
    salesperson_name = Person.name
    payment_id = Payment.id
    payment_value = Payment.value
    confirm_date = Sale.confirm_date
    paid_date = Payment.paid_date

    tables = [
        Sale,
        Join(Commission, Commission.sale_id == Sale.id),
        Join(SalesPerson, SalesPerson.id == Commission.salesperson_id),
        Join(Person, Person.id == SalesPerson.person_id),
        Join(Payment, Payment.id == Commission.payment_id),
       ]

    @property
    def sale(self):
        return self.store.get(Sale, self.id)

    @property
    def payment(self):
        return self.store.get(Payment, self.payment_id)

    def quantity_sold(self):
        if self.sale_returned():
            # zero means 'this sale does not changed our stock'
            return Decimal(0)

        return self.sale.get_items_total_quantity()

    def get_payment_amount(self):
        # the returning payment should be shown as negative one
        if self.payment.is_outpayment():
            return -self.payment_value
        return self.payment_value

    def get_total_amount(self):
        # XXX: No, the sale amount does not change. But I return different
        # values based in type of the payment to guess how I might show the
        # total sale amount.
        if self.payment.is_outpayment():
            return -self.sale.total_amount
        return self.sale.total_amount

    def sale_returned(self):
        return self.sale_status == Sale.STATUS_RETURNED
