mirror of
https://gitlab.com/flectra-hq/flectra.git
synced 2026-08-19 01:34:43 -05:00
[PATCH] Upstream patch - 24122022
This commit is contained in:
@@ -6,6 +6,8 @@ from flectra import SUPERUSER_ID
|
||||
from flectra.exceptions import UserError, ValidationError
|
||||
from flectra.http import request
|
||||
from flectra.addons.account.models.account_tax import TYPE_TAX_USE
|
||||
from flectra.tools import html_escape
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
@@ -30,6 +32,129 @@ def preserve_existing_tags_on_taxes(cr, registry, module):
|
||||
if xml_records:
|
||||
cr.execute("update ir_model_data set noupdate = 't' where id in %s", [tuple(xml_records.ids)])
|
||||
|
||||
def update_taxes_from_templates(cr, chart_template_xmlid):
|
||||
def _create_tax_from_template(company, template, old_tax=None):
|
||||
"""
|
||||
Create a new tax from template with template xmlid, if there was already an old tax with that xmlid we
|
||||
remove the xmlid from it but don't modify anything else.
|
||||
"""
|
||||
def _remove_xml_id(xml_id):
|
||||
module, name = xml_id.split(".", 1)
|
||||
env['ir.model.data'].search([('module', '=', module), ('name', '=', name)]).unlink()
|
||||
|
||||
template_vals = template._get_tax_vals_complete(company)
|
||||
chart_template = env["account.chart.template"].with_context(default_company_id=company.id)
|
||||
if old_tax:
|
||||
xml_id = old_tax.get_xml_id().get(old_tax.id)
|
||||
if xml_id:
|
||||
_remove_xml_id(xml_id)
|
||||
chart_template.create_record_with_xmlid(company, template, "account.tax", template_vals)
|
||||
|
||||
def _update_tax_from_template(template, tax):
|
||||
# -> update the tax : we only updates tax tags
|
||||
tax_rep_lines = tax.invoice_repartition_line_ids + tax.refund_repartition_line_ids
|
||||
template_rep_lines = template.invoice_repartition_line_ids + template.refund_repartition_line_ids
|
||||
for tax_line, template_line in zip(tax_rep_lines, template_rep_lines):
|
||||
tags_to_add = template_line._get_tags_to_add()
|
||||
tags_to_unlink = tax_line.tag_ids
|
||||
if tags_to_add != tags_to_unlink:
|
||||
tax_line.write({"tag_ids": [(6, 0, tags_to_add.ids)]})
|
||||
_cleanup_tags(tags_to_unlink)
|
||||
|
||||
def _get_template_to_tax_xmlid_mapping(company):
|
||||
"""
|
||||
This function uses ir_model_data to return a mapping between the tax templates and the taxes, using their xmlid
|
||||
:returns: {
|
||||
account.tax.template.id: account.tax.id
|
||||
}
|
||||
"""
|
||||
env['ir.model.data'].flush()
|
||||
env.cr.execute(
|
||||
"""
|
||||
SELECT template.res_id AS template_res_id,
|
||||
tax.res_id AS tax_res_id
|
||||
FROM ir_model_data tax
|
||||
JOIN ir_model_data template
|
||||
ON template.name = substr(tax.name, strpos(tax.name, '_') + 1)
|
||||
WHERE tax.model = 'account.tax'
|
||||
AND tax.name LIKE %s
|
||||
-- tax.name is of the form: {company_id}_{account.tax.template.name}
|
||||
""",
|
||||
[r"%s\_%%" % company.id],
|
||||
)
|
||||
tuples = env.cr.fetchall()
|
||||
return dict(tuples)
|
||||
|
||||
def _is_tax_and_template_same(template, tax):
|
||||
"""
|
||||
This function compares account.tax and account.tax.template repartition lines.
|
||||
A tax is considered the same as the template if they have the same:
|
||||
- amount_type
|
||||
- amount
|
||||
- repartition lines percentages in the same order
|
||||
"""
|
||||
tax_rep_lines = tax.invoice_repartition_line_ids + tax.refund_repartition_line_ids
|
||||
template_rep_lines = template.invoice_repartition_line_ids + template.refund_repartition_line_ids
|
||||
return (
|
||||
tax.amount_type == template.amount_type
|
||||
and tax.amount == template.amount
|
||||
and len(tax_rep_lines) == len(template_rep_lines)
|
||||
and all(
|
||||
rep_line_tax.factor_percent == rep_line_template.factor_percent
|
||||
for rep_line_tax, rep_line_template in zip(tax_rep_lines, template_rep_lines)
|
||||
)
|
||||
)
|
||||
|
||||
def _cleanup_tags(tags):
|
||||
"""
|
||||
Checks if the tags are still used in taxes or move lines. If not we delete it.
|
||||
"""
|
||||
for tag in tags:
|
||||
tax_using_tag = env['account.tax.repartition.line'].sudo().search([('tag_ids', 'in', tag.id)], limit=1)
|
||||
aml_using_tag = env['account.move.line'].sudo().search([('tax_tag_ids', 'in', tag.id)], limit=1)
|
||||
report_line_using_tag = env['account.tax.report.line'].sudo().search([('tag_ids', 'in', tag.id)], limit=1)
|
||||
if not (aml_using_tag or tax_using_tag or report_line_using_tag):
|
||||
tag.unlink()
|
||||
|
||||
def _notify_accountant_managers(taxes_to_check):
|
||||
accountant_manager_group = env.ref("account.group_account_manager")
|
||||
partner_managers_ids = accountant_manager_group.users.mapped('partner_id')
|
||||
flectrabot = env.ref('base.partner_root')
|
||||
message_body = _(
|
||||
"Please check these taxes. They might be outdated. We did not update them. "
|
||||
"Indeed, they do not exactly match the taxes of the original version of the localization module.<br/>"
|
||||
"You might want to archive or adapt them.<br/><ul>"
|
||||
)
|
||||
for account_tax in taxes_to_check:
|
||||
message_body += f"<li>{html_escape(account_tax.name)}</li>"
|
||||
message_body += "</ul>"
|
||||
partner_managers_ids.message_post(
|
||||
subject=_('Your taxes have been updated !'),
|
||||
author_id=flectrabot.id,
|
||||
body=message_body,
|
||||
message_type='notification',
|
||||
subtype_xmlid='mail.mt_comment',
|
||||
partner_ids=[partner.id for partner in partner_managers_ids],
|
||||
)
|
||||
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
chart_template_id = env['ir.model.data'].xmlid_to_res_id(chart_template_xmlid)
|
||||
companies = env['res.company'].search([('chart_template_id', '=', chart_template_id)])
|
||||
outdated_taxes = []
|
||||
for company in companies:
|
||||
template_to_tax = _get_template_to_tax_xmlid_mapping(company)
|
||||
templates = env['account.tax.template'].search([("chart_template_id", "=", chart_template_id)])
|
||||
for template in templates:
|
||||
tax = env["account.tax"].browse(template_to_tax.get(template.id))
|
||||
if not tax or not _is_tax_and_template_same(template, tax):
|
||||
_create_tax_from_template(company, template, old_tax=tax)
|
||||
if tax:
|
||||
outdated_taxes.append(tax)
|
||||
else:
|
||||
_update_tax_from_template(template, tax)
|
||||
if outdated_taxes:
|
||||
_notify_accountant_managers(outdated_taxes)
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Account Templates: Account, Tax, Tax Code and chart. + Wizard
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
{
|
||||
'name': 'Luxembourg - Accounting',
|
||||
'version': '2.0',
|
||||
'version': '2.1',
|
||||
'category': 'Accounting/Localizations/Account Charts',
|
||||
'description': """
|
||||
This is the base module to manage the accounting chart for Luxembourg.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
||||
from flectra.addons.account.models.chart_template import update_taxes_from_templates
|
||||
|
||||
|
||||
def migrate(cr, version):
|
||||
update_taxes_from_templates(cr, 'l10n_lu.lu_2011_chart_1')
|
||||
@@ -53,22 +53,21 @@ class AccountMove(models.Model):
|
||||
debit_pdiff_account = move.fiscal_position_id.map_account(debit_pdiff_account)
|
||||
if not debit_pdiff_account:
|
||||
continue
|
||||
# Retrieve stock valuation moves.
|
||||
valuation_stock_moves = self.env['stock.move'].search([
|
||||
('purchase_line_id', '=', line.purchase_line_id.id),
|
||||
('state', '=', 'done'),
|
||||
('product_qty', '!=', 0.0),
|
||||
]) if line.purchase_line_id else self.env['stock.move']
|
||||
if move.move_type == 'in_refund':
|
||||
valuation_stock_moves = valuation_stock_moves.filtered(lambda stock_move: stock_move._is_out())
|
||||
else:
|
||||
valuation_stock_moves = valuation_stock_moves.filtered(lambda stock_move: stock_move._is_in())
|
||||
|
||||
if line.product_id.cost_method != 'standard' and line.purchase_line_id:
|
||||
po_currency = line.purchase_line_id.currency_id
|
||||
po_company = line.purchase_line_id.company_id
|
||||
|
||||
# Retrieve stock valuation moves.
|
||||
valuation_stock_moves = self.env['stock.move'].search([
|
||||
('purchase_line_id', '=', line.purchase_line_id.id),
|
||||
('state', '=', 'done'),
|
||||
('product_qty', '!=', 0.0),
|
||||
])
|
||||
if move.move_type == 'in_refund':
|
||||
valuation_stock_moves = valuation_stock_moves.filtered(lambda stock_move: stock_move._is_out())
|
||||
else:
|
||||
valuation_stock_moves = valuation_stock_moves.filtered(lambda stock_move: stock_move._is_in())
|
||||
|
||||
if valuation_stock_moves:
|
||||
valuation_price_unit_total, valuation_total_qty = valuation_stock_moves._get_valuation_price_and_qty(line, move.currency_id)
|
||||
valuation_price_unit = valuation_price_unit_total / valuation_total_qty
|
||||
@@ -91,9 +90,10 @@ class AccountMove(models.Model):
|
||||
else:
|
||||
# Valuation_price unit is always expressed in invoice currency, so that it can always be computed with the good rate
|
||||
price_unit = line.product_id.uom_id._compute_price(line.product_id.standard_price, line.product_uom_id)
|
||||
valuation_date = valuation_stock_moves and max(valuation_stock_moves.mapped('date')) or move.date
|
||||
valuation_price_unit = line.company_currency_id._convert(
|
||||
price_unit, move.currency_id,
|
||||
move.company_id, fields.Date.today(), round=False
|
||||
move.company_id, valuation_date, round=False
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
||||
from flectra.addons.stock_account.tests.test_anglo_saxon_valuation_reconciliation_common import ValuationReconciliationTestCommon
|
||||
from flectra.tests.common import Form, tagged
|
||||
from flectra import fields
|
||||
|
||||
from freezegun import freeze_time
|
||||
|
||||
@@ -256,6 +257,80 @@ class TestValuationReconciliation(ValuationReconciliationTestCommon):
|
||||
picking = self.env['stock.picking'].search([('purchase_id', '=', purchase_order.id)])
|
||||
self.check_reconciliation(invoice, picking)
|
||||
|
||||
@freeze_time('2021-01-03')
|
||||
def test_price_difference_exchange_difference_accounting_date(self):
|
||||
test_product = self.test_product_delivery
|
||||
test_product.categ_id.write({"property_cost_method": "standard"})
|
||||
test_product.write({'standard_price': 100.0})
|
||||
date_po_receipt = '2021-01-02'
|
||||
rate_po_receipt = 25.0
|
||||
date_bill = '2021-01-01'
|
||||
rate_bill = 30.0
|
||||
date_accounting = '2021-01-03'
|
||||
rate_accounting = 26.0
|
||||
|
||||
foreign_currency = self.currency_data['currency']
|
||||
company_currency = self.env.company.currency_id
|
||||
self.env['res.currency.rate'].create([
|
||||
{
|
||||
'name': date_po_receipt,
|
||||
'rate': rate_po_receipt,
|
||||
'currency_id': foreign_currency.id,
|
||||
'company_id': self.env.company.id,
|
||||
}, {
|
||||
'name': date_bill,
|
||||
'rate': rate_bill,
|
||||
'currency_id': foreign_currency.id,
|
||||
'company_id': self.env.company.id,
|
||||
}, {
|
||||
'name': date_accounting,
|
||||
'rate': rate_accounting,
|
||||
'currency_id': foreign_currency.id,
|
||||
'company_id': self.env.company.id,
|
||||
}, {
|
||||
'name': date_po_receipt,
|
||||
'rate': 1.0,
|
||||
'currency_id': company_currency.id,
|
||||
'company_id': self.env.company.id,
|
||||
}, {
|
||||
'name': date_accounting,
|
||||
'rate': 1.0,
|
||||
'currency_id': company_currency.id,
|
||||
'company_id': self.env.company.id,
|
||||
}, {
|
||||
'name': date_bill,
|
||||
'rate': 1.0,
|
||||
'currency_id': company_currency.id,
|
||||
'company_id': self.env.company.id,
|
||||
}])
|
||||
|
||||
#purchase order created in foreign currency
|
||||
purchase_order = self._create_purchase(test_product, date_po_receipt, quantity=10, price_unit=3000)
|
||||
with freeze_time(date_po_receipt):
|
||||
self._process_pickings(purchase_order.picking_ids)
|
||||
invoice = self._create_invoice_for_po(purchase_order, date_bill)
|
||||
with Form(invoice) as move_form:
|
||||
move_form.invoice_date = fields.Date.from_string(date_bill)
|
||||
move_form.date = fields.Date.from_string(date_accounting)
|
||||
invoice.action_post()
|
||||
|
||||
price_diff_line = invoice.line_ids.filtered(lambda l: l.account_id == self.stock_account_product_categ.property_account_creditor_price_difference_categ)
|
||||
self.assertTrue(len(price_diff_line) == 1, "A price difference line should be created")
|
||||
self.assertAlmostEqual(price_diff_line.balance, 192.31)
|
||||
self.assertAlmostEqual(price_diff_line.price_total, 5000.0)
|
||||
|
||||
picking = self.env['stock.picking'].search([('purchase_id', '=', purchase_order.id)])
|
||||
self.check_reconciliation(invoice, picking)
|
||||
interim_account_id = self.company_data['default_account_stock_in'].id
|
||||
|
||||
valuation_line = picking.move_lines.mapped('account_move_ids.line_ids').filtered(lambda x: x.account_id.id == interim_account_id)
|
||||
self.assertTrue(valuation_line.full_reconcile_id, "The reconciliation should be total at that point.")
|
||||
|
||||
exchange_move = valuation_line.full_reconcile_id.exchange_move_id
|
||||
self.assertTrue(exchange_move, "An exchange move should exists.")
|
||||
exchange_difference = exchange_move.line_ids.filtered(lambda l: l.account_id.id == interim_account_id).balance
|
||||
self.assertAlmostEqual(exchange_difference, 38.46, "Exchange amount is incorrect")
|
||||
|
||||
def test_reconcile_cash_basis_bill(self):
|
||||
''' Test the generation of the CABA move after bill payment
|
||||
'''
|
||||
|
||||
@@ -269,7 +269,7 @@ var Dialog = Widget.extend({
|
||||
this.$modal.remove();
|
||||
}
|
||||
|
||||
var modals = $('body > .modal').filter(':visible');
|
||||
const modals = $('body .modal').filter(':visible');
|
||||
if (modals.length) {
|
||||
if (!isFocusSet) {
|
||||
modals.last().focus();
|
||||
|
||||
Reference in New Issue
Block a user