diff --git a/sale_discount_total/models/sale.py b/sale_discount_total/models/sale.py index 12906a8e6..b37307871 100644 --- a/sale_discount_total/models/sale.py +++ b/sale_discount_total/models/sale.py @@ -68,12 +68,30 @@ class SaleOrder(models.Model): self.supply_rate() return True + class AccountTax(models.Model): _inherit = 'account.tax' - @api.multi + @api.v8 def compute_all(self, price_unit, currency=None, quantity=1.0, product=None, partner=None): - print "hello" + """ Returns all information required to apply taxes (in self + their children in case of a tax goup). + We consider the sequence of the parent for group of taxes. + Eg. considering letters as taxes and alphabetic order as sequence : + [G, B([A, D, F]), E, C] will be computed as [A, D, F, C, E, G] + + RETURN: { + 'total_excluded': 0.0, # Total without taxes + 'total_included': 0.0, # Total with taxes + 'taxes': [{ # One dict for each tax in self and their children + 'id': int, + 'name': str, + 'amount': float, + 'sequence': int, + 'account_id': int, + 'refund_account_id': int, + 'analytic': boolean, + }] + } """ if len(self) == 0: company_id = self.env.user.company_id else: @@ -81,18 +99,25 @@ class AccountTax(models.Model): if not currency: currency = company_id.currency_id taxes = [] + # By default, for each tax, tax amount will first be computed + # and rounded at the 'Account' decimal precision for each + # PO/SO/invoice line and then these rounded amounts will be + # summed, leading to the total amount for that tax. But, if the + # company has tax_calculation_rounding_method = round_globally, + # we still follow the same method, but we use a much larger + # precision when we round the tax amount for each line (we use + # the 'Account' decimal precision + 5), and that way it's like + # rounding after the sum of the tax amounts of each line prec = currency.decimal_places - round_tax = False if company_id.tax_calculation_rounding_method == 'round_globally' else True - round_total = True - if 'round' in self.env.context: - round_tax = bool(self.env.context['round']) - round_total = bool(self.env.context['round']) - - if not round_tax: + if company_id.tax_calculation_rounding_method == 'round_globally' or not bool(self.env.context.get("round", True)): prec += 5 - # total_excluded = total_included = base = round(price_unit * quantity, prec) - total_excluded = total_included = base = (price_unit * quantity) + #total_excluded = total_included = base = round(price_unit * quantity, prec) + total_excluded = total_included = base = round(price_unit * quantity) + # Sorting key is mandatory in this case. When no key is provided, sorted() will perform a + # search. However, the search method is overridden in account.tax in order to add a domain + # depending on the context. This domain might filter out some taxes from self, e.g. in the + # case of group taxes. for tax in self.sorted(key=lambda r: r.sequence): if tax.amount_type == 'group': ret = tax.children_tax_ids.compute_all(price_unit, currency, quantity, product, partner) @@ -104,7 +129,7 @@ class AccountTax(models.Model): continue tax_amount = tax._compute_amount(base, price_unit, quantity, product, partner) - if not round_tax: + if company_id.tax_calculation_rounding_method == 'round_globally' or not bool(self.env.context.get("round", True)): tax_amount = round(tax_amount, prec) else: tax_amount = currency.round(tax_amount) @@ -127,12 +152,10 @@ class AccountTax(models.Model): 'refund_account_id': tax.refund_account_id.id, 'analytic': tax.analytic, }) - print "total_excluded:",total_excluded - print "total_included:",total_included return { 'taxes': sorted(taxes, key=lambda k: k['sequence']), - 'total_excluded': total_excluded, - 'total_included': total_included, + 'total_excluded': currency.round(total_excluded) if bool(self.env.context.get("round", True)) else total_excluded, + 'total_included': currency.round(total_included) if bool(self.env.context.get("round", True)) else total_included, 'base': base, } diff --git a/sale_discount_total/models/sale.py~ b/sale_discount_total/models/sale.py~ new file mode 100644 index 000000000..12906a8e6 --- /dev/null +++ b/sale_discount_total/models/sale.py~ @@ -0,0 +1,145 @@ +from openerp import api, fields, models +import openerp.addons.decimal_precision as dp + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + @api.depends('order_line.price_total') + def _amount_all(self): + """ + Compute the total amounts of the SO. + """ + for order in self: + amount_untaxed = amount_tax = amount_discount = 0.0 + for line in order.order_line: + amount_untaxed += line.price_subtotal + amount_tax += line.price_tax + amount_discount += (line.product_uom_qty * line.price_unit * line.discount)/100 + order.update({ + 'amount_untaxed': order.pricelist_id.currency_id.round(amount_untaxed), + 'amount_tax': order.pricelist_id.currency_id.round(amount_tax), + 'amount_discount': order.pricelist_id.currency_id.round(amount_discount), + 'amount_total': amount_untaxed + amount_tax, + }) + + discount_type = fields.Selection([('percent', 'Percentage'), ('amount', 'Amount')], string='Discount type', + readonly=True,states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, + default='percent') + discount_rate = fields.Float('Discount Rate', digits_compute=dp.get_precision('Account'), + readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}) + amount_untaxed = fields.Monetary(string='Untaxed Amount', store=True, readonly=True, compute='_amount_all', + track_visibility='always') + amount_tax = fields.Monetary(string='Taxes', store=True, readonly=True, compute='_amount_all', + track_visibility='always') + amount_total = fields.Monetary(string='Total', store=True, readonly=True, compute='_amount_all', + track_visibility='always') + amount_discount = fields.Monetary(string='Discount', store=True, readonly=True, compute='_amount_all', + digits_compute=dp.get_precision('Account'), track_visibility='always') + + @api.onchange('discount_type', 'discount_rate', 'order_line') + def supply_rate(self): + for order in self: + if order.discount_type == 'percent': + for line in order.order_line: + line.discount = order.discount_rate + else: + total = discount = 0.0 + for line in order.order_line: + total += round((line.product_uom_qty * line.price_unit)) + if order.discount_rate != 0: + discount = (order.discount_rate / total) * 100 + else: + discount = order.discount_rate + for line in order.order_line: + line.discount = discount + + @api.multi + def _prepare_invoice(self,): + invoice_vals = super(SaleOrder, self)._prepare_invoice() + invoice_vals.update({ + 'discount_type': self.discount_type, + 'discount_rate': self.discount_rate + }) + return invoice_vals + + @api.multi + def button_dummy(self): + self.supply_rate() + return True + +class AccountTax(models.Model): + _inherit = 'account.tax' + + @api.multi + def compute_all(self, price_unit, currency=None, quantity=1.0, product=None, partner=None): + print "hello" + if len(self) == 0: + company_id = self.env.user.company_id + else: + company_id = self[0].company_id + if not currency: + currency = company_id.currency_id + taxes = [] + prec = currency.decimal_places + round_tax = False if company_id.tax_calculation_rounding_method == 'round_globally' else True + round_total = True + if 'round' in self.env.context: + round_tax = bool(self.env.context['round']) + round_total = bool(self.env.context['round']) + + if not round_tax: + prec += 5 + # total_excluded = total_included = base = round(price_unit * quantity, prec) + total_excluded = total_included = base = (price_unit * quantity) + + for tax in self.sorted(key=lambda r: r.sequence): + if tax.amount_type == 'group': + ret = tax.children_tax_ids.compute_all(price_unit, currency, quantity, product, partner) + total_excluded = ret['total_excluded'] + base = ret['base'] + total_included = ret['total_included'] + tax_amount = total_included - total_excluded + taxes += ret['taxes'] + continue + + tax_amount = tax._compute_amount(base, price_unit, quantity, product, partner) + if not round_tax: + tax_amount = round(tax_amount, prec) + else: + tax_amount = currency.round(tax_amount) + + if tax.price_include: + total_excluded -= tax_amount + base -= tax_amount + else: + total_included += tax_amount + + if tax.include_base_amount: + base += tax_amount + + taxes.append({ + 'id': tax.id, + 'name': tax.with_context(**{'lang': partner.lang} if partner else {}).name, + 'amount': tax_amount, + 'sequence': tax.sequence, + 'account_id': tax.account_id.id, + 'refund_account_id': tax.refund_account_id.id, + 'analytic': tax.analytic, + }) + print "total_excluded:",total_excluded + print "total_included:",total_included + return { + 'taxes': sorted(taxes, key=lambda k: k['sequence']), + 'total_excluded': total_excluded, + 'total_included': total_included, + 'base': base, + } + + + +class SaleOrderLine(models.Model): + _inherit = "sale.order.line" + + discount = fields.Float(string='Discount (%)', digits=(16, 20), default=0.0) +