214 lines
6.8 KiB
Python
214 lines
6.8 KiB
Python
# The COPYRIGHT file at the top level of this repository contains the full
|
|
# copyright notices and license terms.
|
|
from decimal import Decimal
|
|
|
|
from trytond.config import config
|
|
from trytond.model import ModelView, Workflow, fields
|
|
from trytond.pool import PoolMeta, Pool
|
|
from trytond.pyson import Eval
|
|
|
|
__all__ = ['Configuration', 'Invoice', 'InvoiceLine', 'Sale', 'Purchase']
|
|
|
|
DISCOUNT_DIGITS = (16, config.getint('product', 'price_decimal', default=4))
|
|
|
|
|
|
class Configuration(metaclass=PoolMeta):
|
|
__name__ = 'account.configuration'
|
|
discount_product = fields.Many2One('product.product', 'Discount Product')
|
|
|
|
|
|
class Invoice(metaclass=PoolMeta):
|
|
__name__ = 'account.invoice'
|
|
invoice_discount = fields.Numeric('Invoice Discount',
|
|
digits=DISCOUNT_DIGITS, states={
|
|
'readonly': Eval('state') != 'draft',
|
|
}, depends=['state'])
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(Invoice, cls).__setup__()
|
|
cls._error_messages.update({
|
|
'missing_discount_product': ('Invoice "%s" has a discount but '
|
|
'no discount product is configured.'),
|
|
})
|
|
|
|
@staticmethod
|
|
def default_invoice_discount():
|
|
return Decimal(0)
|
|
|
|
@fields.depends('party', 'type')
|
|
def on_change_with_invoice_discount(self):
|
|
if self.party:
|
|
if self.type == 'in':
|
|
return self.party.supplier_invoice_discount
|
|
else:
|
|
return self.party.customer_invoice_discount
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
def compute_discount_global(cls, invoices):
|
|
Line = Pool().get('account.invoice.line')
|
|
|
|
lines = []
|
|
for invoice in invoices:
|
|
if not invoice.invoice_discount:
|
|
continue
|
|
discount_line = invoice._get_discount_global_line()
|
|
if discount_line:
|
|
lines.append(discount_line)
|
|
|
|
if lines:
|
|
Line.create([x._save_values for x in lines])
|
|
cls.update_taxes(invoices)
|
|
|
|
def _get_discount_global_line(self):
|
|
pool = Pool()
|
|
Config = pool.get('account.configuration')
|
|
Line = pool.get('account.invoice.line')
|
|
|
|
config = Config(1)
|
|
product = config.discount_product
|
|
if not product:
|
|
self.raise_user_error('missing_discount_product',
|
|
self.rec_name)
|
|
|
|
# check invoice has a global discount line; not create a new line
|
|
for line in self.lines:
|
|
if (line.type == 'line' and line.product
|
|
and line.product.id == product.id):
|
|
return
|
|
|
|
amount = -1 * self.untaxed_amount * self.invoice_discount
|
|
if amount:
|
|
line = Line()
|
|
line.invoice = self
|
|
line.type = 'line'
|
|
line.product = product
|
|
if self.type == 'in':
|
|
line.account = product.account_expense_used
|
|
else:
|
|
line.account = product.account_revenue_used
|
|
line.description = product.rec_name
|
|
line.quantity = 1
|
|
line.unit = product.default_uom
|
|
line.unit_price = amount
|
|
line.sequence = 9999
|
|
line._update_taxes(self.type, self.party)
|
|
return line
|
|
|
|
@classmethod
|
|
def remove_discount_global(cls, invoices):
|
|
pool = Pool()
|
|
Line = pool.get('account.invoice.line')
|
|
Config = pool.get('account.configuration')
|
|
|
|
config = Config(1)
|
|
product = config.discount_product
|
|
if not product:
|
|
return
|
|
|
|
to_delete = []
|
|
to_update_taxes = []
|
|
for invoice in invoices:
|
|
for line in invoice.lines:
|
|
if (line.type == 'line' and line.product
|
|
and line.product.id == product.id):
|
|
to_delete.append(line)
|
|
to_update_taxes.append(invoice)
|
|
break
|
|
|
|
if to_delete:
|
|
Line.delete(to_delete)
|
|
if to_update_taxes:
|
|
cls.update_taxes(to_update_taxes)
|
|
|
|
def _credit(self):
|
|
credit = super(Invoice, self)._credit()
|
|
credit.invoice_discount = self.invoice_discount
|
|
return credit
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('validated')
|
|
def validate_invoice(cls, invoices):
|
|
cls.compute_discount_global(invoices)
|
|
super(Invoice, cls).validate_invoice(invoices)
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('posted')
|
|
def post(cls, invoices):
|
|
cls.compute_discount_global(invoices)
|
|
super(Invoice, cls).post(invoices)
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('draft')
|
|
def draft(cls, invoices):
|
|
super(Invoice, cls).draft(invoices)
|
|
cls.remove_discount_global(invoices)
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('cancelled')
|
|
def cancel(cls, invoices):
|
|
super(Invoice, cls).cancel(invoices)
|
|
cls.remove_discount_global(invoices)
|
|
|
|
|
|
class InvoiceLine(metaclass=PoolMeta):
|
|
__name__ = 'account.invoice.line'
|
|
|
|
def _update_taxes(self, invoice_type, party):
|
|
Tax = Pool().get('account.tax')
|
|
taxes = []
|
|
pattern = self._get_tax_rule_pattern()
|
|
if invoice_type == 'in':
|
|
for tax in self.product.supplier_taxes_used:
|
|
if party.supplier_tax_rule:
|
|
tax_ids = party.supplier_tax_rule.apply(tax, pattern)
|
|
if tax_ids:
|
|
taxes.extend(tax_ids)
|
|
continue
|
|
taxes.append(tax.id)
|
|
if party.supplier_tax_rule:
|
|
tax_ids = party.supplier_tax_rule.apply(None, pattern)
|
|
if tax_ids:
|
|
taxes.extend(tax_ids)
|
|
else:
|
|
for tax in self.product.customer_taxes_used:
|
|
if party.customer_tax_rule:
|
|
tax_ids = party.customer_tax_rule.apply(tax, pattern)
|
|
if tax_ids:
|
|
taxes.extend(tax_ids)
|
|
continue
|
|
taxes.append(tax.id)
|
|
if party.customer_tax_rule:
|
|
tax_ids = party.customer_tax_rule.apply(None, pattern)
|
|
if tax_ids:
|
|
taxes.extend(tax_ids)
|
|
if taxes:
|
|
self.taxes = Tax.browse(taxes)
|
|
|
|
|
|
class Sale(metaclass=PoolMeta):
|
|
__name__ = 'sale.sale'
|
|
|
|
def _get_invoice_sale(self):
|
|
invoice = super(Sale, self)._get_invoice_sale()
|
|
if invoice:
|
|
invoice.invoice_discount = (
|
|
invoice.on_change_with_invoice_discount())
|
|
return invoice
|
|
|
|
|
|
class Purchase(metaclass=PoolMeta):
|
|
__name__ = 'purchase.purchase'
|
|
|
|
def _get_invoice_purchase(self):
|
|
invoice = super(Purchase, self)._get_invoice_purchase()
|
|
if invoice:
|
|
invoice.invoice_discount = (
|
|
invoice.on_change_with_invoice_discount())
|
|
return invoice
|