Source code for stoqlib.domain.commission
# -*- 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
"""
# pylint: enable=E1101
from decimal import Decimal
from storm.expr import Join, Cast
from storm.references import Reference
from stoqlib.database.properties import PercentCol, PriceCol
from stoqlib.database.properties import IntCol, IdCol
from stoqlib.database.viewable import Viewable
from stoqlib.domain.base import Domain
from stoqlib.domain.payment.method import PaymentMethod
from stoqlib.domain.payment.payment import Payment
from stoqlib.domain.person import Person, SalesPerson, Branch
from stoqlib.domain.sale import Sale
from stoqlib.lib.defaults import quantize
from stoqlib.lib.translation import stoqlib_gettext
_ = stoqlib_gettext
[docs]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 = IdCol(default=None)
#: the |sellablecategory|
category = Reference(category_id, 'SellableCategory.id')
sellable_id = IdCol(default=None)
#: the |sellable|
sellable = Reference(sellable_id, 'Sellable.id')
[docs]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)
sale_id = IdCol()
#: the |sale| this commission applies to
sale = Reference(sale_id, 'Sale.id')
payment_id = IdCol()
#: 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
#
[docs]class CommissionView(Viewable):
""" Stores information about commissions and it's related
sale and payment.
"""
#: the branch this commission was generated
branch = Branch
payment = Payment
sale = Sale
# Sale
id = Sale.id
identifier = Sale.identifier
identifier_str = Cast(Sale.identifier, 'text')
sale_status = Sale.status
confirm_date = Sale.confirm_date
# Commission
code = Commission.id
commission_value = Commission.value
commission_percentage = Commission.value / Payment.value * 100
# Payment
payment_id = Payment.id
payment_value = Payment.value
method_name = PaymentMethod.method_name
paid_date = Payment.paid_date
# Salesperson
salesperson_id = SalesPerson.id
salesperson_name = Person.name
tables = [
Sale,
Join(Branch, Sale.branch_id == Branch.id),
Join(Commission, Commission.sale_id == Sale.id),
Join(SalesPerson, SalesPerson.id == Sale.salesperson_id),
Join(Person, Person.id == SalesPerson.person_id),
Join(Payment, Payment.id == Commission.payment_id),
Join(PaymentMethod, Payment.method_id == PaymentMethod.id),
]
@property
def method_description(self):
from stoqlib.domain.payment.operation import get_payment_operation
return get_payment_operation(self.method_name).description
# pylint: disable=E1120
@property
def quantity_sold(self):
if self.sale_returned:
# zero means 'this sale does not changed our stock'
return Decimal(0)
# FIXME: This is doing one extra query per row when printing the report
return self.sale.get_items_total_quantity()
@property
def 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
@property
def 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
# pylint: enable=E1120
@property
def sale_returned(self):
return self.sale_status == Sale.STATUS_RETURNED