first commit
This commit is contained in:
250
modules/commission/invoice.py
Normal file
250
modules/commission/invoice.py
Normal file
@@ -0,0 +1,250 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
from collections import defaultdict
|
||||
from itertools import groupby
|
||||
|
||||
from trytond.model import ModelView, Workflow, fields
|
||||
from trytond.modules.product import round_price
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval
|
||||
from trytond.tools import grouped_slice
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
|
||||
class Invoice(metaclass=PoolMeta):
|
||||
__name__ = 'account.invoice'
|
||||
agent = fields.Many2One('commission.agent', 'Commission Agent',
|
||||
domain=[
|
||||
('type_', '=', 'agent'),
|
||||
('company', '=', Eval('company', -1)),
|
||||
],
|
||||
states={
|
||||
'invisible': Eval('type') == 'in',
|
||||
'readonly': Eval('state', '') != 'draft',
|
||||
},
|
||||
help="The agent who receives a commission for the invoice.")
|
||||
|
||||
@classmethod
|
||||
def _journal_types(cls, invoice_type):
|
||||
return super()._journal_types(invoice_type) + ['commission']
|
||||
|
||||
@classmethod
|
||||
def _post(cls, invoices):
|
||||
# Create commission only the first time the invoice is posted
|
||||
to_commission = [i for i in invoices
|
||||
if i.state not in ['posted', 'paid']]
|
||||
super()._post(invoices)
|
||||
cls.create_commissions(to_commission)
|
||||
|
||||
@classmethod
|
||||
def create_commissions(cls, invoices):
|
||||
pool = Pool()
|
||||
Commission = pool.get('commission')
|
||||
all_commissions = []
|
||||
for invoice in invoices:
|
||||
for line in invoice.lines:
|
||||
commissions = line.get_commissions()
|
||||
if commissions:
|
||||
all_commissions.extend(commissions)
|
||||
|
||||
Commission.save(all_commissions)
|
||||
return all_commissions
|
||||
|
||||
@classmethod
|
||||
def set_commissions_date(cls, invoices):
|
||||
pool = Pool()
|
||||
Date = pool.get('ir.date')
|
||||
Commission = pool.get('commission')
|
||||
date2commissions = defaultdict(list)
|
||||
for company, c_invoices in groupby(invoices, key=lambda i: i.company):
|
||||
with Transaction().set_context(company=company.id):
|
||||
today = Date.today()
|
||||
for sub_invoices in grouped_slice(list(c_invoices)):
|
||||
ids = [i.id for i in sub_invoices]
|
||||
for commission in Commission.search([
|
||||
('date', '=', None),
|
||||
('origin.invoice', 'in', ids,
|
||||
'account.invoice.line'),
|
||||
]):
|
||||
date = commission.origin.invoice.reconciled or today
|
||||
date2commissions[date].append(commission)
|
||||
to_write = []
|
||||
for date, commissions in date2commissions.items():
|
||||
to_write.append(commissions)
|
||||
to_write.append({
|
||||
'date': date,
|
||||
})
|
||||
if to_write:
|
||||
Commission.write(*to_write)
|
||||
|
||||
@classmethod
|
||||
@Workflow.transition('paid')
|
||||
def paid(cls, invoices):
|
||||
super().paid(invoices)
|
||||
cls.set_commissions_date(invoices)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('cancelled')
|
||||
def cancel(cls, invoices):
|
||||
pool = Pool()
|
||||
Commission = pool.get('commission')
|
||||
|
||||
invoices_to_revert_commission = []
|
||||
invoices_to_set_date = []
|
||||
for invoice in invoices:
|
||||
if invoice.move:
|
||||
invoices_to_set_date.append(invoice)
|
||||
else:
|
||||
invoices_to_revert_commission.append(invoice)
|
||||
|
||||
super().cancel(invoices)
|
||||
cls.set_commissions_date(invoices_to_set_date)
|
||||
|
||||
to_delete = []
|
||||
to_save = []
|
||||
for sub_invoices in grouped_slice(invoices_to_revert_commission):
|
||||
ids = [i.id for i in sub_invoices]
|
||||
to_delete += Commission.search([
|
||||
('invoice_line', '=', None),
|
||||
('origin.invoice', 'in', ids, 'account.invoice.line'),
|
||||
])
|
||||
to_cancel = Commission.search([
|
||||
('invoice_line', '!=', None),
|
||||
('origin.invoice', 'in', ids, 'account.invoice.line'),
|
||||
])
|
||||
for commission in Commission.copy(to_cancel):
|
||||
commission.amount *= -1
|
||||
to_save.append(commission)
|
||||
|
||||
Commission.delete(to_delete)
|
||||
Commission.save(to_save)
|
||||
|
||||
def _credit(self, **values):
|
||||
values.setdefault('agent', self.agent)
|
||||
return super()._credit(**values)
|
||||
|
||||
|
||||
class InvoiceLine(metaclass=PoolMeta):
|
||||
__name__ = 'account.invoice.line'
|
||||
principal = fields.Many2One('commission.agent', 'Commission Principal',
|
||||
domain=[
|
||||
('type_', '=', 'principal'),
|
||||
('company', '=', Eval('company', -1)),
|
||||
],
|
||||
states={
|
||||
'invisible': Eval('invoice_type') == 'in',
|
||||
'readonly': Eval('invoice_state') != 'draft',
|
||||
},
|
||||
help="The principal who pays a commission for the invoice line.")
|
||||
commissions = fields.One2Many('commission', 'origin', 'Commissions',
|
||||
readonly=True,
|
||||
states={
|
||||
'invisible': ~Eval('commissions'),
|
||||
})
|
||||
from_commissions = fields.One2Many('commission', 'invoice_line',
|
||||
'From Commissions', readonly=True,
|
||||
states={
|
||||
'invisible': ~Eval('from_commissions'),
|
||||
})
|
||||
|
||||
@property
|
||||
def agent_plans_used(self):
|
||||
"List of agent, plan tuple"
|
||||
used = []
|
||||
if self.invoice.agent:
|
||||
used.append((self.invoice.agent, self.invoice.agent.plan))
|
||||
if self.principal:
|
||||
used.append((self.principal, self.principal.plan))
|
||||
return used
|
||||
|
||||
def get_commissions(self):
|
||||
pool = Pool()
|
||||
Commission = pool.get('commission')
|
||||
Currency = pool.get('currency.currency')
|
||||
Date = pool.get('ir.date')
|
||||
|
||||
if self.type != 'line':
|
||||
return []
|
||||
|
||||
with Transaction().set_context(company=self.invoice.company.id):
|
||||
today = Date.today()
|
||||
commissions = []
|
||||
for agent, plan in self.agent_plans_used:
|
||||
if not plan:
|
||||
continue
|
||||
with Transaction().set_context(date=self.invoice.currency_date):
|
||||
base_amount = Currency.compute(self.invoice.currency,
|
||||
self.amount, agent.currency, round=False)
|
||||
base_amount = round_price(base_amount)
|
||||
amount = self._get_commission_amount(base_amount, plan)
|
||||
if amount:
|
||||
amount = round_price(amount)
|
||||
if not amount:
|
||||
continue
|
||||
|
||||
commission = Commission()
|
||||
commission.origin = self
|
||||
if plan.commission_method == 'posting':
|
||||
commission.date = self.invoice.invoice_date or today
|
||||
elif (plan.commission_method == 'payment'
|
||||
and self.invoice.state == 'paid'):
|
||||
commission.date = self.invoice.reconciled or today
|
||||
commission.agent = agent
|
||||
commission.product = plan.commission_product
|
||||
commission.base_amount = base_amount
|
||||
commission.amount = amount
|
||||
commissions.append(commission)
|
||||
return commissions
|
||||
|
||||
def _get_commission_amount(self, amount, plan, pattern=None):
|
||||
return plan.compute(amount, self.product, pattern=pattern)
|
||||
|
||||
@fields.depends('product', 'principal')
|
||||
def on_change_product(self):
|
||||
super().on_change_product()
|
||||
if self.product:
|
||||
if self.product.principals:
|
||||
if self.principal not in self.product.principals:
|
||||
self.principal = self.product.principal
|
||||
elif self.principal:
|
||||
self.principal = None
|
||||
|
||||
@classmethod
|
||||
def view_attributes(cls):
|
||||
return super().view_attributes() + [
|
||||
('//page[@id="commissions"]', 'states', {
|
||||
'invisible': Eval('type') != 'line',
|
||||
})]
|
||||
|
||||
@classmethod
|
||||
def copy(cls, lines, default=None):
|
||||
if default is None:
|
||||
default = {}
|
||||
else:
|
||||
default = default.copy()
|
||||
default.setdefault('commissions', None)
|
||||
default.setdefault('from_commissions', None)
|
||||
return super().copy(lines, default=default)
|
||||
|
||||
|
||||
class CreditInvoiceStart(metaclass=PoolMeta):
|
||||
__name__ = 'account.invoice.credit.start'
|
||||
with_agent = fields.Boolean(
|
||||
"With Agent",
|
||||
help="Check to keep the original invoice's agent.")
|
||||
|
||||
@classmethod
|
||||
def default_with_agent(cls):
|
||||
return True
|
||||
|
||||
|
||||
class CreditInvoice(metaclass=PoolMeta):
|
||||
__name__ = 'account.invoice.credit'
|
||||
|
||||
@property
|
||||
def _credit_options(self):
|
||||
options = super()._credit_options
|
||||
if not self.start.with_agent:
|
||||
options['agent'] = None
|
||||
return options
|
||||
Reference in New Issue
Block a user