mirror of
https://gitlab.com/flectra-hq/flectra.git
synced 2026-10-25 15:30:05 -05:00
Merge branch '2.0-vivek-account-discount-changes' into 'master'
[MODIFIED] account discount module discount proportionate See merge request flectra-hq/flectra!494
This commit is contained in:
commit
74d2cc8fbe
3 files changed
+113
-184
No files matched your search
@@ -15,7 +15,7 @@
|
||||
'account',
|
||||
],
|
||||
'data': [
|
||||
'views/res_config_settings_views.xml',
|
||||
# 'views/res_config_settings_views.xml',
|
||||
'views/account_move_views.xml',
|
||||
'reports/invoice_report_views.xml',
|
||||
],
|
||||
|
||||
@@ -3,6 +3,7 @@ from functools import partial
|
||||
from flectra import models, fields, api, _
|
||||
from flectra.tools import float_is_zero, float_round, float_compare
|
||||
from flectra.tools.misc import formatLang
|
||||
from flectra.exceptions import UserError
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -11,221 +12,152 @@ class AccountMove(models.Model):
|
||||
_inherit = 'account.move'
|
||||
|
||||
discount_type = fields.Selection(
|
||||
selection=[
|
||||
('fixed', 'Fixed'),
|
||||
('percent', 'Percent')
|
||||
],
|
||||
string="Discount Type",
|
||||
default="percent",
|
||||
selection=[
|
||||
('fixed', 'Fixed'),
|
||||
('percent', 'Percent')
|
||||
],
|
||||
string="Discount Type",
|
||||
default="percent",
|
||||
)
|
||||
|
||||
discount_value = fields.Float(
|
||||
string='Discount',
|
||||
tracking=True,
|
||||
string='Discount',
|
||||
tracking=True,
|
||||
)
|
||||
|
||||
discount_value_percent = fields.Float(
|
||||
string='Discount (%)',
|
||||
digits='Discount',
|
||||
compute='_compute_document_discount',
|
||||
compute_sudo=True,
|
||||
string='Discount (%)',
|
||||
digits='Discount',
|
||||
compute='_compute_document_discount',
|
||||
compute_sudo=True,
|
||||
)
|
||||
|
||||
amount_gross = fields.Monetary(
|
||||
string='Gross Amount',
|
||||
store=True,
|
||||
readonly=True,
|
||||
compute='_compute_amount',
|
||||
string='Gross Amount',
|
||||
store=True,
|
||||
readonly=True,
|
||||
compute='_compute_amount',
|
||||
)
|
||||
|
||||
document_discount = fields.Monetary(
|
||||
string='Document Discount',
|
||||
store=True,
|
||||
readonly=True,
|
||||
compute='_compute_document_discount',
|
||||
string='Document Discount',
|
||||
store=True,
|
||||
readonly=True,
|
||||
compute='_compute_document_discount',
|
||||
)
|
||||
|
||||
document_discount_tax_amount = fields.Monetary(
|
||||
string='Document Discount Tax',
|
||||
store=True,
|
||||
readonly=True,
|
||||
compute='_compute_document_discount',
|
||||
string='Document Discount Tax',
|
||||
store=True,
|
||||
readonly=True,
|
||||
compute='_compute_document_discount',
|
||||
)
|
||||
|
||||
has_document_discount = fields.Boolean(
|
||||
string='Has Document Discount',
|
||||
store=True,
|
||||
readonly=True,
|
||||
compute='_compute_document_discount',
|
||||
string='Has Document Discount',
|
||||
store=True,
|
||||
readonly=True,
|
||||
compute='_compute_document_discount',
|
||||
)
|
||||
|
||||
@api.depends(
|
||||
'line_ids.amount_currency',
|
||||
'line_ids.price_subtotal',
|
||||
'line_ids.price_total',
|
||||
'discount_type',
|
||||
'discount_value',
|
||||
'line_ids.amount_currency',
|
||||
'line_ids.price_subtotal',
|
||||
'line_ids.price_total',
|
||||
'line_ids.product_id',
|
||||
'discount_type',
|
||||
'discount_value',
|
||||
)
|
||||
def _compute_document_discount(self):
|
||||
for move in self:
|
||||
document_discount = 0
|
||||
discount_used = move.discount_type and not float_is_zero(move.discount_value, precision_digits=move.currency_id.decimal_places)
|
||||
discount_used = move.discount_type and not float_is_zero(move.discount_value,
|
||||
precision_digits=move.currency_id.decimal_places)
|
||||
if discount_used:
|
||||
amount_gross = 0
|
||||
for line in move.invoice_line_ids:
|
||||
amount_gross += line.quantity * line.price_unit
|
||||
|
||||
untaxed_amount = amount_gross
|
||||
taxed_amt = 0
|
||||
|
||||
for line in move.line_ids:
|
||||
if line.tax_ids:
|
||||
for tax in line.tax_ids:
|
||||
taxed_amt += line.price_unit * tax.amount / 100
|
||||
total_amt = untaxed_amount + taxed_amt
|
||||
|
||||
if move.discount_type == 'fixed':
|
||||
document_discount = move.discount_value * -1
|
||||
document_discount = move.discount_value
|
||||
if document_discount >= total_amt:
|
||||
raise UserError(_("Discount Cannot be more than or equal to Total Amount"))
|
||||
for line in move.invoice_line_ids:
|
||||
if line.product_id:
|
||||
tax = 0
|
||||
if line.tax_ids:
|
||||
for taxes_line in line.tax_ids:
|
||||
tax += line.price_unit * taxes_line.amount / 100
|
||||
line_price = line.price_unit + tax
|
||||
discount_value = line_price * document_discount / total_amt
|
||||
discount_ratio = discount_value / line_price * 100
|
||||
if discount_ratio:
|
||||
line.update({
|
||||
'discount': discount_ratio
|
||||
})
|
||||
move._move_autocomplete_invoice_lines_values()
|
||||
|
||||
else:
|
||||
document_discount = (move.amount_gross * (move.discount_value / 100)) * -1
|
||||
document_discount = (total_amt * (move.discount_value / 100))
|
||||
|
||||
document_discount = float_round(document_discount, precision_digits=move.currency_id.decimal_places)
|
||||
|
||||
discount_lines = move.line_ids.filtered(lambda f: f.is_document_discount_line)
|
||||
if discount_lines:
|
||||
document_discount_tax_amount = float_round(
|
||||
sum(line.price_total - line.price_subtotal for line in discount_lines),
|
||||
precision_digits=move.currency_id.decimal_places
|
||||
)
|
||||
if document_discount >= total_amt:
|
||||
raise UserError(_("Discount Cannat be more than or equal to Total Amount"))
|
||||
for line in move.invoice_line_ids:
|
||||
if line.product_id:
|
||||
tax = 0
|
||||
if line.tax_ids:
|
||||
for taxes_line in line.tax_ids:
|
||||
tax += line.price_unit * taxes_line.amount / 100
|
||||
line_price = line.price_unit + tax
|
||||
discount_value = line_price * document_discount / total_amt
|
||||
discount_ratio = discount_value / line_price * 100
|
||||
if discount_ratio:
|
||||
line.update({
|
||||
'discount': discount_ratio
|
||||
})
|
||||
move._move_autocomplete_invoice_lines_values()
|
||||
else:
|
||||
document_discount_tax_amount = 0
|
||||
move.update({
|
||||
'document_discount': document_discount,
|
||||
'document_discount_tax_amount': document_discount_tax_amount,
|
||||
'has_document_discount': discount_used,
|
||||
'discount_value_percent': move.discount_value,
|
||||
})
|
||||
for line in move.invoice_line_ids:
|
||||
line.update({
|
||||
'discount': 0.0
|
||||
})
|
||||
move._move_autocomplete_invoice_lines_values()
|
||||
|
||||
@api.depends(
|
||||
'line_ids.matched_debit_ids.debit_move_id.move_id.line_ids.amount_residual',
|
||||
'line_ids.matched_debit_ids.debit_move_id.move_id.line_ids.amount_residual_currency',
|
||||
'line_ids.matched_credit_ids.credit_move_id.move_id.line_ids.amount_residual',
|
||||
'line_ids.matched_credit_ids.credit_move_id.move_id.line_ids.amount_residual_currency',
|
||||
'line_ids.debit',
|
||||
'line_ids.credit',
|
||||
'line_ids.currency_id',
|
||||
'line_ids.amount_currency',
|
||||
'line_ids.amount_residual',
|
||||
'line_ids.amount_residual_currency',
|
||||
'line_ids.payment_id.state',
|
||||
'line_ids.full_reconcile_id',
|
||||
'line_ids.price_total',
|
||||
'document_discount',
|
||||
'line_ids.matched_debit_ids.debit_move_id.move_id.line_ids.amount_residual',
|
||||
'line_ids.matched_debit_ids.debit_move_id.move_id.line_ids.amount_residual_currency',
|
||||
'line_ids.matched_credit_ids.credit_move_id.move_id.line_ids.amount_residual',
|
||||
'line_ids.matched_credit_ids.credit_move_id.move_id.line_ids.amount_residual_currency',
|
||||
'line_ids.debit',
|
||||
'line_ids.credit',
|
||||
'line_ids.currency_id',
|
||||
'line_ids.amount_currency',
|
||||
'line_ids.amount_residual',
|
||||
'line_ids.amount_residual_currency',
|
||||
'line_ids.payment_id.state',
|
||||
'line_ids.full_reconcile_id',
|
||||
'line_ids.price_total',
|
||||
'document_discount',
|
||||
)
|
||||
def _compute_amount(self):
|
||||
super(AccountMove, self)._compute_amount()
|
||||
for move in self:
|
||||
if move.line_ids:
|
||||
amount_gross = sum(move.line_ids.filtered(lambda f: not f.exclude_from_invoice_tab).mapped('price_subtotal'))
|
||||
amount_gross = sum(
|
||||
move.line_ids.filtered(lambda f: not f.exclude_from_invoice_tab).mapped('price_subtotal'))
|
||||
move.update({
|
||||
'amount_gross': amount_gross,
|
||||
'amount_untaxed': move.amount_gross + move.document_discount,
|
||||
'amount_untaxed': amount_gross
|
||||
})
|
||||
move._generate_document_discount_move_line()
|
||||
# move._generate_document_discount_move_line()
|
||||
move.update({
|
||||
'amount_total': move.amount_untaxed + move.amount_tax,
|
||||
})
|
||||
|
||||
def _generate_document_discount_move_line(self):
|
||||
|
||||
aml = self.env['account.move.line'].with_context(check_move_validity=False)
|
||||
|
||||
update_needed = False
|
||||
|
||||
for move in self:
|
||||
|
||||
update_needed = False
|
||||
|
||||
in_draft_mode = move != move._origin
|
||||
if float_is_zero(move.document_discount, precision_digits=move.currency_id.decimal_places):
|
||||
to_del = move.line_ids.filtered(lambda f: f.is_document_discount_line)
|
||||
if in_draft_mode:
|
||||
move.update({'line_ids': [(2, line.id) for line in to_del]})
|
||||
update_needed = True
|
||||
continue
|
||||
|
||||
currency = move.currency_id or move.company_id.currency_id
|
||||
if move.discount_type == 'fixed':
|
||||
account_id = move.company_id.document_discount_fixed_account_id
|
||||
else:
|
||||
account_id = move.company_id.document_discount_percent_account_id
|
||||
|
||||
create_method = in_draft_mode and aml.new or aml.create
|
||||
distributions = move._get_document_tax_distribution()
|
||||
for distribution in distributions.values():
|
||||
existing_line = move.line_ids.filtered(lambda f: f.is_document_discount_line and f.tax_ids.ids == distribution['tax'].ids)
|
||||
balance = currency._convert(
|
||||
move.document_discount * distribution['factor'],
|
||||
move.journal_id.company_id.currency_id,
|
||||
move.journal_id.company_id,
|
||||
move.date or fields.Date.context_today(move),
|
||||
)
|
||||
price_unit = move.document_discount * distribution['factor']
|
||||
amount_currency = price_unit * -1
|
||||
if existing_line:
|
||||
line_values = {
|
||||
'amount_currency': amount_currency,
|
||||
'price_unit': price_unit,
|
||||
'debit': balance < 0.0 and -balance or 0.0,
|
||||
'credit': balance > 0.0 and balance or 0.0,
|
||||
}
|
||||
if existing_line.credit == line_values['credit'] and existing_line.debit == line_values['debit']:
|
||||
continue
|
||||
|
||||
update_needed = True
|
||||
if in_draft_mode:
|
||||
existing_line.update(line_values)
|
||||
else:
|
||||
existing_line.write(line_values)
|
||||
else:
|
||||
line_values = {
|
||||
'account_id': account_id.id,
|
||||
'amount_currency': amount_currency,
|
||||
'price_unit': price_unit,
|
||||
'currency_id': currency.id,
|
||||
'debit': balance < 0.0 and -balance or 0.0,
|
||||
'credit': balance > 0.0 and balance or 0.0,
|
||||
'name': _('Document Discount'),
|
||||
'move_id': move.id,
|
||||
'partner_id': move.partner_id.id,
|
||||
'company_id': move.company_id.id,
|
||||
'company_currency_id': currency.id,
|
||||
'exclude_from_invoice_tab': True,
|
||||
'is_document_discount_line': True,
|
||||
'tax_ids': distribution['tax'].ids,
|
||||
}
|
||||
create_method(line_values)
|
||||
update_needed = True
|
||||
|
||||
if update_needed:
|
||||
self.with_context(check_move_validity=False)._recompute_dynamic_lines(recompute_all_taxes=True)
|
||||
|
||||
def _get_document_tax_distribution(self):
|
||||
"""
|
||||
This function calculates distribution factor for right split
|
||||
of negative tax amount of document discount amount.
|
||||
@param amount_gross: the base amount (total untaxed amount) used as 100%
|
||||
@return: dict with tax.id as key and tax data includin the distribution factor
|
||||
"""
|
||||
self.ensure_one()
|
||||
res = {}
|
||||
tax_lines = self.line_ids.filtered(lambda f: f.tax_line_id)
|
||||
if not tax_lines:
|
||||
return res
|
||||
total_amount = sum(tax_lines.mapped('tax_base_amount'))
|
||||
for line in tax_lines:
|
||||
key = line.tax_line_id.id
|
||||
res.setdefault(key, {'tax': line.tax_line_id, 'factor': 0.0})
|
||||
res[key]['factor'] = line.tax_base_amount / total_amount
|
||||
return res
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
rslt = super(AccountMove, self).create(vals_list)
|
||||
rslt._compute_document_discount()
|
||||
rslt._compute_amount()
|
||||
rslt._generate_document_discount_move_line()
|
||||
return rslt
|
||||
|
||||
|
||||
class AccountMoveLine(models.Model):
|
||||
_inherit = 'account.move.line'
|
||||
|
||||
is_document_discount_line = fields.Boolean(help="Technical field used to identify document discount lines.")
|
||||
@@ -7,23 +7,20 @@
|
||||
<field name="arch" type="xml">
|
||||
<field name="amount_untaxed" position="before">
|
||||
<field name="has_document_discount" invisible="1"/>
|
||||
<field name="amount_gross" attrs="{'invisible': [('has_document_discount', '=', False)]}"/>
|
||||
<field name="document_discount" attrs="{'invisible': [('has_document_discount', '=', False)]}"/>
|
||||
<field name="amount_gross"/>
|
||||
</field>
|
||||
<field name="narration" position="after">
|
||||
<group>
|
||||
<group>
|
||||
<label for="discount_type" string="Document Discount"/>
|
||||
<div class="o_row">
|
||||
<field name="discount_type"/>
|
||||
<field name="discount_value" attrs="{'invisible': [('discount_type', '=', False)]}"/>
|
||||
<field name="discount_type" attrs="{'readonly': [('state','=','posted')]}"/>
|
||||
<field name="discount_value"
|
||||
attrs="{'invisible': [('discount_type', '=', False)], 'readonly': [('state','=','posted')]}"/>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
</field>
|
||||
<xpath expr="//page[@id='aml_tab']//tree/field[@name='account_id']" position="after">
|
||||
<field name="is_document_discount_line" invisible="True"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</flectra>
|
||||
Reference in new issue
Block a user