mirror of
https://gitlab.com/flectra-hq/flectra.git
synced 2026-10-25 23:40:05 -05:00
[PATCH] Upstream patch - 14032022
This commit is contained in:
1 parent
626e6d1890
commit
2608624af7
22 files changed
+666
-102
No files matched your search
@@ -3101,6 +3101,20 @@ class AccountMove(models.Model):
|
||||
"""
|
||||
return []
|
||||
|
||||
def _notify_get_groups(self, msg_vals=None):
|
||||
""" Give access button to users and portal customer as portal is integrated
|
||||
in account. Customer and portal group have probably no right to see
|
||||
the document so they don't have the access button. """
|
||||
groups = super(AccountMove, self)._notify_get_groups(msg_vals=msg_vals)
|
||||
|
||||
self.ensure_one()
|
||||
if self.move_type != 'entry':
|
||||
for group_name, _group_method, group_data in groups:
|
||||
if group_name == 'portal_customer':
|
||||
group_data['has_button_access'] = True
|
||||
|
||||
return groups
|
||||
|
||||
class AccountMoveLine(models.Model):
|
||||
_name = "account.move.line"
|
||||
_description = "Journal Item"
|
||||
@@ -3341,67 +3355,20 @@ class AccountMoveLine(models.Model):
|
||||
|
||||
if not self.product_id:
|
||||
return 0.0
|
||||
|
||||
company = self.move_id.company_id
|
||||
currency = self.move_id.currency_id
|
||||
company_currency = company.currency_id
|
||||
product_uom = self.product_id.uom_id
|
||||
fiscal_position = self.move_id.fiscal_position_id
|
||||
is_refund_document = self.move_id.move_type in ('out_refund', 'in_refund')
|
||||
move_date = self.move_id.date or fields.Date.context_today(self)
|
||||
|
||||
if self.move_id.is_sale_document(include_receipts=True):
|
||||
product_price_unit = self.product_id.lst_price
|
||||
product_taxes = self.product_id.taxes_id
|
||||
document_type = 'sale'
|
||||
elif self.move_id.is_purchase_document(include_receipts=True):
|
||||
product_price_unit = self.product_id.standard_price
|
||||
product_taxes = self.product_id.supplier_taxes_id
|
||||
document_type = 'purchase'
|
||||
else:
|
||||
return 0.0
|
||||
product_taxes = product_taxes.filtered(lambda tax: tax.company_id == company)
|
||||
|
||||
# Apply unit of measure.
|
||||
if self.product_uom_id and self.product_uom_id != product_uom:
|
||||
product_price_unit = product_uom._compute_price(product_price_unit, self.product_uom_id)
|
||||
|
||||
# Apply fiscal position.
|
||||
if product_taxes and fiscal_position:
|
||||
product_taxes_after_fp = fiscal_position.map_tax(product_taxes, partner=self.partner_id)
|
||||
|
||||
if set(product_taxes.ids) != set(product_taxes_after_fp.ids):
|
||||
flattened_taxes_before_fp = product_taxes._origin.flatten_taxes_hierarchy()
|
||||
if any(tax.price_include for tax in flattened_taxes_before_fp):
|
||||
taxes_res = flattened_taxes_before_fp.compute_all(
|
||||
product_price_unit,
|
||||
quantity=1.0,
|
||||
currency=company_currency,
|
||||
product=self.product_id,
|
||||
partner=self.partner_id,
|
||||
is_refund=is_refund_document,
|
||||
)
|
||||
product_price_unit = company_currency.round(taxes_res['total_excluded'])
|
||||
|
||||
flattened_taxes_after_fp = product_taxes_after_fp._origin.flatten_taxes_hierarchy()
|
||||
if any(tax.price_include for tax in flattened_taxes_after_fp):
|
||||
taxes_res = flattened_taxes_after_fp.compute_all(
|
||||
product_price_unit,
|
||||
quantity=1.0,
|
||||
currency=company_currency,
|
||||
product=self.product_id,
|
||||
partner=self.partner_id,
|
||||
is_refund=is_refund_document,
|
||||
handle_price_include=False,
|
||||
)
|
||||
for tax_res in taxes_res['taxes']:
|
||||
tax = self.env['account.tax'].browse(tax_res['id'])
|
||||
if tax.price_include:
|
||||
product_price_unit += tax_res['amount']
|
||||
|
||||
# Apply currency rate.
|
||||
if currency and currency != company_currency:
|
||||
product_price_unit = company_currency._convert(product_price_unit, currency, company, move_date)
|
||||
|
||||
return product_price_unit
|
||||
document_type = 'other'
|
||||
return self.product_id._get_tax_included_unit_price(
|
||||
self.move_id.company_id,
|
||||
self.move_id.currency_id,
|
||||
self.move_id.date,
|
||||
document_type,
|
||||
fiscal_position=self.move_id.fiscal_position_id,
|
||||
product_uom=self.product_uom_id
|
||||
)
|
||||
|
||||
def _get_computed_account(self):
|
||||
self.ensure_one()
|
||||
@@ -3544,8 +3511,7 @@ class AccountMoveLine(models.Model):
|
||||
|
||||
# Compute 'price_total'.
|
||||
if taxes:
|
||||
force_sign = -1 if move_type in ('out_invoice', 'in_refund', 'out_receipt') else 1
|
||||
taxes_res = taxes._origin.with_context(force_sign=force_sign).compute_all(line_discount_price_unit,
|
||||
taxes_res = taxes._origin.with_context(force_sign=1).compute_all(line_discount_price_unit,
|
||||
quantity=quantity, currency=currency, product=product, partner=partner, is_refund=move_type in ('out_refund', 'in_refund'))
|
||||
res['price_subtotal'] = taxes_res['total_excluded']
|
||||
res['price_total'] = taxes_res['total_included']
|
||||
|
||||
@@ -459,6 +459,23 @@ class AccountReconcileModel(models.Model):
|
||||
|
||||
return lines_vals_list + writeoff_vals_list
|
||||
|
||||
def _prepare_widget_writeoff_vals(self, st_line_id, write_off_vals):
|
||||
fixed_write_off_vals = dict(write_off_vals, currency_id=st_line_id.company_id.currency_id.id)
|
||||
counterpart_vals = st_line_id._prepare_counterpart_move_line_vals(fixed_write_off_vals)
|
||||
|
||||
return {
|
||||
'name': counterpart_vals['name'],
|
||||
'balance': counterpart_vals['amount_currency'],
|
||||
'debit': counterpart_vals['debit'],
|
||||
'credit': counterpart_vals['credit'],
|
||||
'account_id': counterpart_vals['account_id'],
|
||||
'currency_id': counterpart_vals['currency_id'],
|
||||
'analytic_account_id': counterpart_vals.get('analytic_account_id'),
|
||||
'analytic_tag_ids': counterpart_vals.get('analytic_tag_ids', []),
|
||||
'reconcile_model_id': self.id,
|
||||
'journal_id': counterpart_vals['journal_id']
|
||||
}
|
||||
|
||||
####################################################
|
||||
# RECONCILIATION CRITERIA
|
||||
####################################################
|
||||
|
||||
@@ -530,7 +530,7 @@ class AccountTax(models.Model):
|
||||
price_include = self._context.get('force_price_include', tax.price_include)
|
||||
|
||||
#compute the tax_amount
|
||||
if not skip_checkpoint and price_include and total_included_checkpoints.get(i) and sum_repartition_factor != 0:
|
||||
if not skip_checkpoint and price_include and total_included_checkpoints.get(i) is not None and sum_repartition_factor != 0:
|
||||
# We know the total to reach for that tax, so we make a substraction to avoid any rounding issues
|
||||
tax_amount = total_included_checkpoints[i] - (base + cumulated_tax_included_amount)
|
||||
cumulated_tax_included_amount = 0
|
||||
@@ -542,7 +542,7 @@ class AccountTax(models.Model):
|
||||
tax_amount = round(tax_amount, precision_rounding=prec)
|
||||
factorized_tax_amount = round(tax_amount * sum_repartition_factor, precision_rounding=prec)
|
||||
|
||||
if price_include and not total_included_checkpoints.get(i):
|
||||
if price_include and total_included_checkpoints.get(i) is None:
|
||||
cumulated_tax_included_amount += factorized_tax_amount
|
||||
|
||||
# If the tax affects the base of subsequent taxes, its tax move lines must
|
||||
|
||||
@@ -59,3 +59,76 @@ class ProductProduct(models.Model):
|
||||
|
||||
def _get_product_accounts(self):
|
||||
return self.product_tmpl_id._get_product_accounts()
|
||||
|
||||
@api.model
|
||||
def _get_tax_included_unit_price(self, company, currency, document_date, document_type,
|
||||
is_refund_document=False, product_uom=None, product_currency=None,
|
||||
product_price_unit=None, product_taxes=None, fiscal_position=None
|
||||
):
|
||||
""" Helper to get the price unit from different models.
|
||||
This is needed to compute the same unit price in different models (sale order, account move, etc.) with same parameters.
|
||||
"""
|
||||
|
||||
product = self
|
||||
|
||||
assert document_type
|
||||
|
||||
if product_uom is None:
|
||||
product_uom = product.uom_id
|
||||
if not product_currency:
|
||||
if document_type == 'sale':
|
||||
product_currency = product.currency_id
|
||||
elif document_type == 'purchase':
|
||||
product_currency = company.currency_id
|
||||
if product_price_unit is None:
|
||||
if document_type == 'sale':
|
||||
product_price_unit = product.with_company(company).lst_price
|
||||
elif document_type == 'purchase':
|
||||
product_price_unit = product.with_company(company).standard_price
|
||||
else:
|
||||
return 0.0
|
||||
if product_taxes is None:
|
||||
if document_type == 'sale':
|
||||
product_taxes = product.taxes_id.filtered(lambda x: x.company_id == company)
|
||||
elif document_type == 'purchase':
|
||||
product_taxes = product.supplier_taxes_id.filtered(lambda x: x.company_id == company)
|
||||
# Apply unit of measure.
|
||||
if product_uom and product.uom_id != product_uom:
|
||||
product_price_unit = product.uom_id._compute_price(product_price_unit, product_uom)
|
||||
|
||||
# Apply fiscal position.
|
||||
if product_taxes and fiscal_position:
|
||||
product_taxes_after_fp = fiscal_position.map_tax(product_taxes)
|
||||
|
||||
if set(product_taxes.ids) != set(product_taxes_after_fp.ids):
|
||||
flattened_taxes_before_fp = product_taxes._origin.flatten_taxes_hierarchy()
|
||||
if any(tax.price_include for tax in flattened_taxes_before_fp):
|
||||
taxes_res = flattened_taxes_before_fp.compute_all(
|
||||
product_price_unit,
|
||||
quantity=1.0,
|
||||
currency=currency,
|
||||
product=product,
|
||||
is_refund=is_refund_document,
|
||||
)
|
||||
product_price_unit = taxes_res['total_excluded']
|
||||
|
||||
flattened_taxes_after_fp = product_taxes_after_fp._origin.flatten_taxes_hierarchy()
|
||||
if any(tax.price_include for tax in flattened_taxes_after_fp):
|
||||
taxes_res = flattened_taxes_after_fp.compute_all(
|
||||
product_price_unit,
|
||||
quantity=1.0,
|
||||
currency=currency,
|
||||
product=product,
|
||||
is_refund=is_refund_document,
|
||||
handle_price_include=False,
|
||||
)
|
||||
for tax_res in taxes_res['taxes']:
|
||||
tax = self.env['account.tax'].browse(tax_res['id'])
|
||||
if tax.price_include:
|
||||
product_price_unit += tax_res['amount']
|
||||
|
||||
# Apply currency rate.
|
||||
if currency != product_currency:
|
||||
product_price_unit = product_currency._convert(product_price_unit, currency, company, document_date)
|
||||
|
||||
return product_price_unit
|
||||
@@ -1802,6 +1802,75 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
|
||||
'amount_total': 260.01,
|
||||
})
|
||||
|
||||
def test_out_invoice_line_tax_fixed_price_include_free_product(self):
|
||||
''' Check that fixed tax include are correctly computed while the price_unit is 0
|
||||
'''
|
||||
fixed_tax_price_include = self.env['account.tax'].create({
|
||||
'name': 'BEBAT 0.05',
|
||||
'type_tax_use': 'sale',
|
||||
'amount_type': 'fixed',
|
||||
'amount': 0.05,
|
||||
'price_include': True,
|
||||
'include_base_amount': True,
|
||||
})
|
||||
invoice = self.env['account.move'].create({
|
||||
'move_type': 'out_invoice',
|
||||
'invoice_date': '2022-03-03',
|
||||
'date': '2022-03-03',
|
||||
'partner_id': self.partner_a.id,
|
||||
'invoice_line_ids': [(0, 0, {
|
||||
'name': 'Free product',
|
||||
'price_unit': 0.0,
|
||||
'account_id': self.company_data['default_account_revenue'].id,
|
||||
'tax_ids': [(6, 0, fixed_tax_price_include.ids)],
|
||||
})],
|
||||
})
|
||||
self.assertRecordValues(invoice, [{
|
||||
'amount_untaxed': -0.05,
|
||||
'amount_tax': 0.05,
|
||||
'amount_total': 0.0,
|
||||
}])
|
||||
|
||||
def test_out_invoice_line_taxes_fixed_price_include_free_product(self):
|
||||
''' Check that fixed tax include are correctly computed while the price_unit is 0
|
||||
'''
|
||||
# please ensure this test remains consistent with
|
||||
# test_free_product_and_price_include_fixed_tax in the sale module
|
||||
fixed_tax_price_include_1 = self.env['account.tax'].create({
|
||||
'name': 'BEBAT 0.05',
|
||||
'type_tax_use': 'sale',
|
||||
'amount_type': 'fixed',
|
||||
'amount': 0.05,
|
||||
'price_include': True,
|
||||
'include_base_amount': True,
|
||||
})
|
||||
fixed_tax_price_include_2 = self.env['account.tax'].create({
|
||||
'name': 'Recupel 0.25',
|
||||
'type_tax_use': 'sale',
|
||||
'amount_type': 'fixed',
|
||||
'amount': 0.25,
|
||||
'price_include': True,
|
||||
'include_base_amount': True,
|
||||
})
|
||||
invoice = self.env['account.move'].create({
|
||||
'move_type': 'out_invoice',
|
||||
'invoice_date': '2022-03-03',
|
||||
'date': '2022-03-03',
|
||||
'partner_id': self.partner_a.id,
|
||||
'invoice_line_ids': [(0, 0, {
|
||||
'name': 'Free product',
|
||||
'price_unit': 0.0,
|
||||
'account_id': self.company_data['default_account_revenue'].id,
|
||||
'tax_ids': [(6, 0, (fixed_tax_price_include_1 + fixed_tax_price_include_2).ids)],
|
||||
})],
|
||||
})
|
||||
|
||||
self.assertRecordValues(invoice, [{
|
||||
'amount_untaxed': -0.30,
|
||||
'amount_tax': 0.30,
|
||||
'amount_total': 0.0,
|
||||
}])
|
||||
|
||||
def test_out_invoice_create_refund(self):
|
||||
self.invoice.action_post()
|
||||
|
||||
|
||||
@@ -149,14 +149,15 @@ class ResPartner(models.Model):
|
||||
# This is for API pushes from external platforms where you have no control over VAT numbers.
|
||||
if self.env.context.get('no_vat_validation'):
|
||||
return
|
||||
partners_with_vat = self.filtered('vat')
|
||||
if not partners_with_vat:
|
||||
return
|
||||
if self.env.context.get('company_id'):
|
||||
company = self.env['res.company'].browse(self.env.context['company_id'])
|
||||
else:
|
||||
company = self.env.company
|
||||
eu_countries = self.env.ref('base.europe').country_ids
|
||||
for partner in self:
|
||||
if not partner.vat:
|
||||
continue
|
||||
for partner in partners_with_vat:
|
||||
is_eu_country = partner.commercial_partner_id.country_id in eu_countries
|
||||
if company.vat_check_vies and is_eu_country and partner.is_company:
|
||||
# force full VIES online check
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
help="Worked hours last month">
|
||||
<div class="o_field_widget o_stat_info">
|
||||
<span class="o_stat_value">
|
||||
<field name="hours_last_month_display"/> Hours
|
||||
<field name="hours_last_month_display" widget="float_time"/> Hours
|
||||
</span>
|
||||
<span class="o_stat_text">
|
||||
Last Month
|
||||
@@ -100,7 +100,7 @@
|
||||
help="Worked hours last month">
|
||||
<div class="o_field_widget o_stat_info">
|
||||
<span class="o_stat_value">
|
||||
<field name="hours_last_month_display"/> Hours
|
||||
<field name="hours_last_month_display" widget="float_time"/> Hours
|
||||
</span>
|
||||
<span class="o_stat_text">
|
||||
Last Month
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
from . import chart_template
|
||||
from . import eu_service_tax_rate
|
||||
from . import eu_tax_map
|
||||
from . import eu_tag_map
|
||||
from . import res_company
|
||||
from . import res_config_settings
|
||||
@@ -0,0 +1,214 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
"""
|
||||
The EU_TAG_MAP answers the question: "which tag should I apply on the OSS tax repartition line?"
|
||||
|
||||
{
|
||||
'fiscal_country_code': {
|
||||
'invoice_base_tag': xml_id_of_the_tag or None,
|
||||
'invoice_tax_tag': xml_id_of_the_tag or None,
|
||||
'refund_base_tag': xml_id_of_the_tag or None,
|
||||
'refund_tax_tag': xml_id_of_the_tag or None,
|
||||
},
|
||||
}
|
||||
"""
|
||||
|
||||
EU_TAG_MAP = {
|
||||
# Austria
|
||||
'l10n_at.l10n_at_chart_template': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Belgium
|
||||
'l10n_be.l10nbe_chart_template': {
|
||||
'invoice_base_tag': 'l10n_be.tax_report_line_47',
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': 'l10n_be.tax_report_line_49',
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Bulgaria
|
||||
'BG': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Croatia
|
||||
'l10n_hr.l10n_hr_chart_template_rrif': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Cyprus
|
||||
'CY': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Czech - Done in 13.0 - CoA not available yet
|
||||
'l10n_cz.cz_chart_template': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Denmark
|
||||
'l10n_dk.dk_chart_template': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Estonia - Done in 13.0 - CoA not available yet
|
||||
'EE': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Finland
|
||||
'l10n_fi.fi_chart_template': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# France
|
||||
'l10n_fr.l10n_fr_pcg_chart_template': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Germany SKR03
|
||||
'l10n_de_skr03.l10n_de_chart_template': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Germany SKR04
|
||||
'l10n_de_skr04.l10n_chart_de_skr04': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Greece
|
||||
'l10n_gr.l10n_gr_chart_template': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Hungary
|
||||
'l10n_hu.hungarian_chart_template': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Ireland
|
||||
'l10n_ie.l10n_ie': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Italy
|
||||
'l10n_it.l10n_it_chart_template_generic': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Latvia
|
||||
'LV': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Lithuania
|
||||
'l10n_lt.account_chart_template_lithuania': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Luxembourg
|
||||
'l10n_lu.lu_2011_chart_1': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Malta - Done in 13.0 - CoA not available yet
|
||||
'MT': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Netherlands
|
||||
'l10n_nl.l10nnl_chart_template': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Poland
|
||||
'l10n_pl.pl_chart_template': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Portugal
|
||||
'l10n_pt.pt_chart_template': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Romania
|
||||
'l10n_ro.ro_chart_template': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Slovakia - Done in 13.0 - CoA not available yet
|
||||
'l10n_sk.sk_chart_template': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Slovenia
|
||||
'l10n_si.gd_chart': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Spain
|
||||
'l10n_es.account_chart_template_common': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
# Sweden
|
||||
'l10n_se.l10nse_chart_template': {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
},
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
from flectra import api, fields, models, _
|
||||
from .eu_tax_map import EU_TAX_MAP
|
||||
from .eu_tag_map import EU_TAG_MAP
|
||||
|
||||
|
||||
class Company(models.Model):
|
||||
@@ -28,19 +29,19 @@ class Company(models.Model):
|
||||
taxes = self.env['account.tax'].search([
|
||||
('type_tax_use', '=', 'sale'),
|
||||
('amount_type', '=', 'percent'),
|
||||
('company_id','=', company.id),
|
||||
('company_id', '=', company.id),
|
||||
('tax_group_id', 'not in', oss_tax_groups.mapped('res_id'))])
|
||||
for country in eu_countries - company.account_tax_fiscal_country_id:
|
||||
for destination_country in eu_countries - company.account_tax_fiscal_country_id:
|
||||
mapping = []
|
||||
fpos = self.env['account.fiscal.position'].search([
|
||||
('country_id', '=', country.id),
|
||||
('country_id', '=', destination_country.id),
|
||||
('company_id', '=', company.id),
|
||||
('auto_apply', '=', True),
|
||||
('vat_required', '=', False)], limit=1)
|
||||
if not fpos:
|
||||
fpos = self.env['account.fiscal.position'].create({
|
||||
'name': 'OSS B2C %s' % country.name,
|
||||
'country_id': country.id,
|
||||
'name': f'OSS B2C {destination_country.name}',
|
||||
'country_id': destination_country.id,
|
||||
'company_id': company.id,
|
||||
'auto_apply': True,
|
||||
})
|
||||
@@ -48,25 +49,26 @@ class Company(models.Model):
|
||||
foreign_taxes = {tax.amount: tax for tax in fpos.tax_ids.tax_dest_id if tax.amount_type == 'percent'}
|
||||
|
||||
for domestic_tax in taxes:
|
||||
tax_amount = EU_TAX_MAP.get((company.account_tax_fiscal_country_id.code, domestic_tax.amount, country.code), False)
|
||||
tax_amount = EU_TAX_MAP.get((company.account_tax_fiscal_country_id.code, domestic_tax.amount, destination_country.code), False)
|
||||
if tax_amount and domestic_tax not in fpos.tax_ids.tax_src_id:
|
||||
if not foreign_taxes.get(tax_amount, False):
|
||||
if not self.env['ir.model.data'].xmlid_to_object('l10n_eu_service.oss_tax_group_%s' % str(tax_amount).replace('.','_')):
|
||||
oss_tax_group_local_xml_id = f"oss_tax_group_{str(tax_amount).replace('.', '_')}"
|
||||
if not self.env['ir.model.data'].xmlid_to_object(f"l10n_eu_service.{oss_tax_group_local_xml_id}"):
|
||||
self.env['ir.model.data'].create({
|
||||
'name': 'oss_tax_group_%s' % str(tax_amount).replace('.','_'),
|
||||
'name': oss_tax_group_local_xml_id,
|
||||
'module': 'l10n_eu_service',
|
||||
'model': 'account.tax.group',
|
||||
'res_id': self.env['account.tax.group'].create({'name': 'OSS %s%%' % tax_amount}).id,
|
||||
'res_id': self.env['account.tax.group'].create({'name': f'OSS {tax_amount}%'}).id,
|
||||
'noupdate': True,
|
||||
})
|
||||
})
|
||||
foreign_taxes[tax_amount] = self.env['account.tax'].create({
|
||||
'name': '%(rate)s%% %(country)s %(label)s' % {'rate': tax_amount, 'country': country.code, 'label': country.vat_label},
|
||||
'name': f'{tax_amount}% {destination_country.code} {destination_country.vat_label}',
|
||||
'amount': tax_amount,
|
||||
'invoice_repartition_line_ids': invoice_repartition_lines,
|
||||
'refund_repartition_line_ids': refund_repartition_lines,
|
||||
'type_tax_use': 'sale',
|
||||
'description': "%s%%" % tax_amount,
|
||||
'tax_group_id': self.env.ref('l10n_eu_service.oss_tax_group_%s' % str(tax_amount).replace('.','_')).id,
|
||||
'description': f"{tax_amount}%",
|
||||
'tax_group_id': self.env.ref(f'l10n_eu_service.{oss_tax_group_local_xml_id}').id,
|
||||
'sequence': 1000,
|
||||
'company_id': company.id,
|
||||
})
|
||||
@@ -79,15 +81,19 @@ class Company(models.Model):
|
||||
def _get_repartition_lines_oss(self):
|
||||
self.ensure_one()
|
||||
defaults = self.env['account.tax'].with_company(self).default_get(['invoice_repartition_line_ids', 'refund_repartition_line_ids'])
|
||||
oss_account = self._get_oss_account()
|
||||
if oss_account:
|
||||
defaults['invoice_repartition_line_ids'][1][2]['account_id'] = oss_account.id
|
||||
defaults['refund_repartition_line_ids'][1][2]['account_id'] = oss_account.id
|
||||
oss_account, oss_tags = self._get_oss_account(), self._get_oss_tags()
|
||||
base_line, tax_line, vals = 0, 1, 2
|
||||
for doc_type in 'invoice', 'refund':
|
||||
if oss_account:
|
||||
defaults[f'{doc_type}_repartition_line_ids'][tax_line][vals]['account_id'] = oss_account.id
|
||||
if oss_tags:
|
||||
defaults[f'{doc_type}_repartition_line_ids'][base_line][vals]['tag_ids'] += [(4, oss_tags[f'{doc_type}_base_tag'].id, 0)] if oss_tags[f'{doc_type}_base_tag'] else []
|
||||
defaults[f'{doc_type}_repartition_line_ids'][tax_line][vals]['tag_ids'] += [(4, oss_tags[f'{doc_type}_tax_tag'].id, 0)] if oss_tags[f'{doc_type}_tax_tag'] else []
|
||||
return defaults['invoice_repartition_line_ids'], defaults['refund_repartition_line_ids']
|
||||
|
||||
def _get_oss_account(self):
|
||||
self.ensure_one()
|
||||
if not self.env['ir.model.data'].xmlid_to_object('l10n_eu_service.oss_tax_account_company_%s' % self.id):
|
||||
if not self.env['ir.model.data'].xmlid_to_object(f'l10n_eu_service.oss_tax_account_company_{self.id}'):
|
||||
sales_tax_accounts = self.env['account.tax'].search([
|
||||
('type_tax_use', '=', 'sale'),
|
||||
('company_id', '=', self.id)
|
||||
@@ -96,16 +102,34 @@ class Company(models.Model):
|
||||
return False
|
||||
new_code = self.env['account.account']._search_new_account_code(self, len(sales_tax_accounts[0].code), sales_tax_accounts[0].code[:-2])
|
||||
oss_account = self.env['account.account'].create({
|
||||
'name': '%s OSS' % sales_tax_accounts[0].name,
|
||||
'name': f'{sales_tax_accounts[0].name} OSS',
|
||||
'code': new_code,
|
||||
'user_type_id': sales_tax_accounts[0].user_type_id.id,
|
||||
'company_id': self.id,
|
||||
})
|
||||
self.env['ir.model.data'].create({
|
||||
'name': 'oss_tax_account_company_%s' % self.id,
|
||||
'name': f'oss_tax_account_company_{self.id}',
|
||||
'module': 'l10n_eu_service',
|
||||
'model': 'account.account',
|
||||
'res_id': oss_account.id,
|
||||
'noupdate': True,
|
||||
})
|
||||
return self.env.ref('l10n_eu_service.oss_tax_account_company_%s' % self.id)
|
||||
return self.env.ref(f'l10n_eu_service.oss_tax_account_company_{self.id}')
|
||||
|
||||
def _get_oss_tags(self):
|
||||
[chart_template_xml_id] = self.chart_template_id.get_xml_id().values()
|
||||
tag_for_country = EU_TAG_MAP.get(chart_template_xml_id, {
|
||||
'invoice_base_tag': None,
|
||||
'invoice_tax_tag': None,
|
||||
'refund_base_tag': None,
|
||||
'refund_tax_tag': None,
|
||||
})
|
||||
|
||||
return {
|
||||
repartition_line_key: (
|
||||
self.env.ref(tag_xml_id).tag_ids.filtered(lambda t: not t.tax_negate)
|
||||
if tag_xml_id
|
||||
else None
|
||||
)
|
||||
for repartition_line_key, tag_xml_id in tag_for_country.items()
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import test_oss
|
||||
@@ -0,0 +1,77 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from flectra.addons.l10n_eu_service.models.eu_tag_map import EU_TAG_MAP
|
||||
from flectra.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
from flectra.tests import tagged
|
||||
|
||||
|
||||
@tagged('post_install', 'post_install_l10n', '-at_install')
|
||||
class TestOSSBelgium(AccountTestInvoicingCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(self, chart_template_ref='l10n_be.l10nbe_chart_template'):
|
||||
try:
|
||||
super().setUpClass(chart_template_ref=chart_template_ref)
|
||||
except ValueError as e:
|
||||
if e.args[0] == "External ID not found in the system: l10n_be.l10nbe_chart_template":
|
||||
self.skipTest(self, reason="Belgian CoA is required for this testSuite but l10n_be isn't installed")
|
||||
else:
|
||||
raise e
|
||||
self.company_data['company'].country_id = self.env.ref('base.be')
|
||||
self.company_data['company']._map_eu_taxes()
|
||||
|
||||
def test_country_tag_from_belgium(self):
|
||||
# get an eu country which isn't the current one:
|
||||
another_eu_country_code = (self.env.ref('base.europe').country_ids - self.company_data['company'].country_id)[0].code
|
||||
tax_oss = self.env['account.tax'].search([('name', 'ilike', f'%{another_eu_country_code}%')], limit=1)
|
||||
|
||||
for doc_type, report_line_xml_id in (
|
||||
("invoice", "l10n_be.tax_report_line_47"),
|
||||
("refund", "l10n_be.tax_report_line_49"),
|
||||
):
|
||||
with self.subTest(doc_type=doc_type, report_line_xml_id=report_line_xml_id):
|
||||
oss_tag_id = tax_oss[f"{doc_type}_repartition_line_ids"]\
|
||||
.filtered(lambda x: x.repartition_type == 'base')\
|
||||
.tag_ids
|
||||
|
||||
expected_tag_id = self.env.ref(report_line_xml_id)\
|
||||
.tag_ids\
|
||||
.filtered(lambda t: not t.tax_negate)
|
||||
|
||||
self.assertIn(expected_tag_id, oss_tag_id, f"{doc_type} tag from Belgian CoA not correctly linked")
|
||||
|
||||
|
||||
@tagged('post_install', 'post_install_l10n', '-at_install')
|
||||
class TestOSSUSA(AccountTestInvoicingCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(self, chart_template_ref=None):
|
||||
super().setUpClass(chart_template_ref=chart_template_ref)
|
||||
self.company_data['company'].country_id = self.env.ref('base.us')
|
||||
self.company_data['company']._map_eu_taxes()
|
||||
|
||||
def test_no_oss_tax(self):
|
||||
# get an eu country which isn't the current one:
|
||||
another_eu_country_code = (self.env.ref('base.europe').country_ids - self.company_data['company'].country_id)[0].code
|
||||
tax_oss = self.env['account.tax'].search([('name', 'ilike', f'%{another_eu_country_code}%')], limit=1)
|
||||
|
||||
self.assertFalse(len(tax_oss), "OSS tax shouldn't be instanced on a US company")
|
||||
|
||||
|
||||
|
||||
@tagged('post_install', 'post_install_l10n', '-at_install')
|
||||
class TestOSSMap(AccountTestInvoicingCommon):
|
||||
|
||||
def test_oss_eu_tag_map(self):
|
||||
""" Checks that the xml_id referenced in the map are correct.
|
||||
In case of failure display the couple (chart_template_xml_id, tax_report_line_xml_id).
|
||||
The test doesn't fail for unreferenced char_template or unreferenced tax_report_line.
|
||||
"""
|
||||
chart_templates = self.env['account.chart.template'].search([])
|
||||
for chart_template in chart_templates:
|
||||
[chart_template_xml_id] = chart_template.get_xml_id().values()
|
||||
oss_tags = EU_TAG_MAP.get(chart_template_xml_id, {})
|
||||
for tax_report_line_xml_id in filter(lambda d: d, oss_tags.values()):
|
||||
with self.subTest(chart_template_xml_id=chart_template_xml_id, tax_report_line_xml_id=tax_report_line_xml_id):
|
||||
tag = self.env.ref(tax_report_line_xml_id, raise_if_not_found=False)
|
||||
self.assertIsNotNone(tag, f"The following xml_id is incorrect in EU_TAG_MAP.py:{tax_report_line_xml_id}")
|
||||
@@ -19,7 +19,7 @@ class LunchController(http.Controller):
|
||||
lines = self._get_current_lines(user.id)
|
||||
if lines:
|
||||
lines = [{'id': line.id,
|
||||
'product': (line.product_id.id, line.product_id.name, float_repr(float_round(line.product_id.price, 2), 2)),
|
||||
'product': (line.product_id.id, line.product_id.name, float_repr(float_round(line.price, 2), 2)),
|
||||
'toppings': [(topping.name, float_repr(float_round(topping.price, 2), 2))
|
||||
for topping in line.topping_ids_1 | line.topping_ids_2 | line.topping_ids_3],
|
||||
'quantity': line.quantity,
|
||||
|
||||
@@ -96,7 +96,7 @@ class MailBlackList(models.Model):
|
||||
|
||||
def mail_action_blacklist_remove(self):
|
||||
return {
|
||||
'name': 'Are you sure you want to unblacklist this Email Address?',
|
||||
'name': _('Are you sure you want to unblacklist this Email Address?'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_mode': 'form',
|
||||
'res_model': 'mail.blacklist.remove',
|
||||
|
||||
@@ -116,11 +116,11 @@ class MailBlackListMixin(models.AbstractModel):
|
||||
can_access = self.env['mail.blacklist'].check_access_rights('write', raise_exception=False)
|
||||
if can_access:
|
||||
return {
|
||||
'name': 'Are you sure you want to unblacklist this Email Address?',
|
||||
'name': _('Are you sure you want to unblacklist this Email Address?'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_mode': 'form',
|
||||
'res_model': 'mail.blacklist.remove',
|
||||
'target': 'new',
|
||||
}
|
||||
else:
|
||||
raise AccessError("You do not have the access right to unblacklist emails. Please contact your administrator.")
|
||||
raise AccessError(_("You do not have the access right to unblacklist emails. Please contact your administrator."))
|
||||
@@ -637,6 +637,9 @@
|
||||
year = parts[3] || '';
|
||||
if (year.length > 0) {
|
||||
sep = ' / ';
|
||||
// Force year to 2 digits
|
||||
if (year.length === 4 )
|
||||
year = year.substr(-2);
|
||||
} else if (sep === ' /') {
|
||||
mon = mon.substring(0, 1);
|
||||
sep = '';
|
||||
|
||||
@@ -30,8 +30,8 @@ class AcquirerSips(models.Model):
|
||||
provider = fields.Selection(selection_add=[('sips', 'Sips')], ondelete={'sips': 'set default'})
|
||||
sips_merchant_id = fields.Char('Merchant ID', required_if_provider='sips', groups='base.group_user')
|
||||
sips_secret = fields.Char('Secret Key', size=64, required_if_provider='sips', groups='base.group_user')
|
||||
sips_test_url = fields.Char("Test url", required_if_provider='sips', default='https://payment-webinit.simu.sips-atos.com/paymentInit')
|
||||
sips_prod_url = fields.Char("Production url", required_if_provider='sips', default='https://payment-webinit.sips-atos.com/paymentInit')
|
||||
sips_test_url = fields.Char("Test url", required_if_provider='sips', default='https://payment-webinit.simu.sips-services.com/paymentInit')
|
||||
sips_prod_url = fields.Char("Production url", required_if_provider='sips', default='https://payment-webinit.sips-services.com/paymentInit')
|
||||
sips_version = fields.Char("Interface Version", required_if_provider='sips', default='HP_2.31')
|
||||
sips_key_version = fields.Integer("Secret Key Version", required_if_provider='sips', default=2)
|
||||
|
||||
|
||||
@@ -1716,7 +1716,15 @@ class SaleOrderLine(models.Model):
|
||||
self._compute_tax_id()
|
||||
|
||||
if self.order_id.pricelist_id and self.order_id.partner_id:
|
||||
vals['price_unit'] = self.env['account.tax']._fix_tax_included_price_company(self._get_display_price(product), product.taxes_id, self.tax_id, self.company_id)
|
||||
vals['price_unit'] = product._get_tax_included_unit_price(
|
||||
self.company_id,
|
||||
self.order_id.currency_id,
|
||||
self.order_id.date_order,
|
||||
'sale',
|
||||
fiscal_position=self.order_id.fiscal_position_id,
|
||||
product_price_unit=self._get_display_price(product),
|
||||
product_currency=self.currency_id
|
||||
)
|
||||
self.update(vals)
|
||||
|
||||
title = False
|
||||
@@ -1749,7 +1757,15 @@ class SaleOrderLine(models.Model):
|
||||
uom=self.product_uom.id,
|
||||
fiscal_position=self.env.context.get('fiscal_position')
|
||||
)
|
||||
self.price_unit = self.env['account.tax']._fix_tax_included_price_company(self._get_display_price(product), product.taxes_id, self.tax_id, self.company_id)
|
||||
self.price_unit = product._get_tax_included_unit_price(
|
||||
self.company_id,
|
||||
self.order_id.currency_id,
|
||||
self.order_id.date_order,
|
||||
'sale',
|
||||
fiscal_position=self.order_id.fiscal_position_id,
|
||||
product_price_unit=self._get_display_price(product),
|
||||
product_currency=self.currency_id
|
||||
)
|
||||
|
||||
def name_get(self):
|
||||
result = []
|
||||
|
||||
@@ -65,6 +65,62 @@ class TestOnchangeProductId(TransactionCase):
|
||||
# Check the unit price of SO line
|
||||
self.assertEqual(100, sale_order.order_line[0].price_unit, "The included tax must be subtracted to the price")
|
||||
|
||||
def test_fiscalposition_application(self):
|
||||
"""Test application of a fiscal position mapping
|
||||
price included to price included tax
|
||||
"""
|
||||
|
||||
uom = self.product_uom_model.search([('name', '=', 'Units')])
|
||||
pricelist = self.pricelist_model.search([('name', '=', 'Public Pricelist')])
|
||||
|
||||
partner = self.res_partner_model.create({
|
||||
'name': "George"
|
||||
})
|
||||
tax_include_src = self.tax_model.create({
|
||||
'name': "Include tax",
|
||||
'amount': 21.00,
|
||||
'price_include': True,
|
||||
})
|
||||
tax_include_dst = self.tax_model.create({
|
||||
'name': "Exclude tax",
|
||||
'amount': 6.00,
|
||||
'price_include': True,
|
||||
})
|
||||
|
||||
product_tmpl = self.product_tmpl_model.create({
|
||||
'name': "Voiture",
|
||||
'list_price': 121,
|
||||
'taxes_id': [(6, 0, [tax_include_src.id])]
|
||||
})
|
||||
|
||||
product_product = product_tmpl.product_variant_id
|
||||
|
||||
fpos = self.fiscal_position_model.create({
|
||||
'name': "fiscal position",
|
||||
'sequence': 1
|
||||
})
|
||||
|
||||
fpos_tax = self.fiscal_position_tax_model.create({
|
||||
'position_id' :fpos.id,
|
||||
'tax_src_id': tax_include_src.id,
|
||||
'tax_dest_id': tax_include_dst.id
|
||||
})
|
||||
|
||||
# Create the SO with one SO line and apply a pricelist and fiscal position on it
|
||||
order_form = Form(self.env['sale.order'].with_context(tracking_disable=True))
|
||||
order_form.partner_id = partner
|
||||
order_form.pricelist_id = pricelist
|
||||
order_form.fiscal_position_id = fpos
|
||||
with order_form.order_line.new() as line:
|
||||
line.name = product_product.name
|
||||
line.product_id = product_product
|
||||
line.product_uom_qty = 1.0
|
||||
line.product_uom = uom
|
||||
sale_order = order_form.save()
|
||||
|
||||
# Check the unit price of SO line
|
||||
self.assertRecordValues(sale_order.order_line, [{'price_unit': 106}])
|
||||
|
||||
def test_pricelist_application(self):
|
||||
""" Test different prices are correctly applied based on dates """
|
||||
support_product = self.env['product.product'].create({
|
||||
|
||||
@@ -583,6 +583,46 @@ class TestSaleOrder(TestSaleCommon):
|
||||
self.assertEqual(line.price_subtotal, 17527.41)
|
||||
self.assertEqual(line.untaxed_amount_to_invoice, line.price_subtotal)
|
||||
|
||||
def test_free_product_and_price_include_fixed_tax(self):
|
||||
""" Check that fixed tax include are correctly computed while the price_unit is 0
|
||||
"""
|
||||
# please ensure this test remains consistent with
|
||||
# test_out_invoice_line_onchange_2_taxes_fixed_price_include_free_product in account module
|
||||
sale_order = self.env['sale.order'].create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'order_line': [(0, 0, {
|
||||
'product_id': self.company_data['product_order_no'].id,
|
||||
'product_uom_qty': 1,
|
||||
'price_unit': 0.0,
|
||||
})]
|
||||
})
|
||||
sale_order.action_confirm()
|
||||
line = sale_order.order_line
|
||||
line.tax_id = [
|
||||
(0, 0, {
|
||||
'name': 'BEBAT 0.05',
|
||||
'type_tax_use': 'sale',
|
||||
'amount_type': 'fixed',
|
||||
'amount': 0.05,
|
||||
'price_include': True,
|
||||
'include_base_amount': True,
|
||||
}),
|
||||
(0, 0, {
|
||||
'name': 'Recupel 0.25',
|
||||
'type_tax_use': 'sale',
|
||||
'amount_type': 'fixed',
|
||||
'amount': 0.25,
|
||||
'price_include': True,
|
||||
'include_base_amount': True,
|
||||
}),
|
||||
]
|
||||
sale_order.action_confirm()
|
||||
self.assertRecordValues(sale_order, [{
|
||||
'amount_untaxed': -0.30,
|
||||
'amount_tax': 0.30,
|
||||
'amount_total': 0.0,
|
||||
}])
|
||||
|
||||
def test_sol_name_search(self):
|
||||
# Shouldn't raise
|
||||
self.env['sale.order']._search([('order_line', 'ilike', 'acoustic')])
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="sale_margin_sale_order_pivot">
|
||||
<record model="ir.ui.view" id="sale_margin_sale_order_graph">
|
||||
<field name="name">sale.order.margin.view.graph</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="inherit_id" ref="sale.view_sale_order_graph"/>
|
||||
|
||||
@@ -40,7 +40,7 @@ class Survey(http.Controller):
|
||||
], limit=1)
|
||||
return survey_sudo, answer_sudo
|
||||
|
||||
def _check_validity(self, survey_token, answer_token, ensure_token=True):
|
||||
def _check_validity(self, survey_token, answer_token, ensure_token=True, check_partner=True):
|
||||
""" Check survey is open and can be taken. This does not checks for
|
||||
security rules, only functional / business rules. It returns a string key
|
||||
allowing further manipulation of validity issues
|
||||
@@ -57,6 +57,9 @@ class Survey(http.Controller):
|
||||
:param ensure_token: whether user input existence based on given access token
|
||||
should be enforced or not, depending on the route requesting a token or
|
||||
allowing external world calls;
|
||||
|
||||
:param check_partner: Whether we must check that the partner associated to the target
|
||||
answer corresponds to the active user.
|
||||
"""
|
||||
survey_sudo, answer_sudo = self._fetch_from_access_token(survey_token, answer_token)
|
||||
|
||||
@@ -80,7 +83,7 @@ class Survey(http.Controller):
|
||||
if (not survey_sudo.page_ids and survey_sudo.questions_layout == 'page_per_section') or not survey_sudo.question_ids:
|
||||
return 'survey_void'
|
||||
|
||||
if answer_sudo:
|
||||
if answer_sudo and check_partner:
|
||||
if request.env.user._is_public() and answer_sudo.partner_id:
|
||||
# answers from public user should not have any partner_id; this indicates probably a cookie issue
|
||||
return 'answer_wrong_user'
|
||||
@@ -93,16 +96,17 @@ class Survey(http.Controller):
|
||||
|
||||
return True
|
||||
|
||||
def _get_access_data(self, survey_token, answer_token, ensure_token=True):
|
||||
def _get_access_data(self, survey_token, answer_token, ensure_token=True, check_partner=True):
|
||||
""" Get back data related to survey and user input, given the ID and access
|
||||
token provided by the route.
|
||||
|
||||
: param ensure_token: whether user input existence should be enforced or not(see ``_check_validity``)
|
||||
: param check_partner: whether the partner of the target answer should be checked (see ``_check_validity``)
|
||||
"""
|
||||
survey_sudo, answer_sudo = request.env['survey.survey'].sudo(), request.env['survey.user_input'].sudo()
|
||||
has_survey_access, can_answer = False, False
|
||||
|
||||
validity_code = self._check_validity(survey_token, answer_token, ensure_token=ensure_token)
|
||||
validity_code = self._check_validity(survey_token, answer_token, ensure_token=ensure_token, check_partner=check_partner)
|
||||
if validity_code != 'survey_wrong':
|
||||
survey_sudo, answer_sudo = self._fetch_from_access_token(survey_token, answer_token)
|
||||
try:
|
||||
@@ -569,7 +573,7 @@ class Survey(http.Controller):
|
||||
def survey_print(self, survey_token, review=False, answer_token=None, **post):
|
||||
'''Display an survey in printable view; if <answer_token> is set, it will
|
||||
grab the answers of the user_input_id that has <answer_token>.'''
|
||||
access_data = self._get_access_data(survey_token, answer_token, ensure_token=False)
|
||||
access_data = self._get_access_data(survey_token, answer_token, ensure_token=False, check_partner=False)
|
||||
if access_data['validity_code'] is not True and (
|
||||
access_data['has_survey_access'] or
|
||||
access_data['validity_code'] not in ['token_required', 'survey_closed', 'survey_void']):
|
||||
|
||||
Reference in new issue
Block a user