[PATCH] Upstream patch - 19062022

This commit is contained in:
Parthiv Patel committed 2022-06-19 08:34:29 +00:00
1 parent d13fa4b670
commit 7fcc8ab048
22 files changed
+233 -109

No files matched your search

+2 -2
View File
@@ -184,9 +184,9 @@ class AccountMove(models.Model):
partner_id = fields.Many2one('res.partner', readonly=True, tracking=True,
states={'draft': [('readonly', False)]},
check_company=True,
string='Partner', change_default=True)
string='Partner', change_default=True, ondelete='restrict')
commercial_partner_id = fields.Many2one('res.partner', string='Commercial Entity', store=True, readonly=True,
compute='_compute_commercial_partner_id')
compute='_compute_commercial_partner_id', ondelete='restrict')
country_code = fields.Char(related='company_id.country_id.code', readonly=True)
user_id = fields.Many2one(string='User', related='invoice_user_id',
help='Technical field used to fit the generic behavior in mail templates.')
+12 -1
View File
@@ -8,7 +8,7 @@ from psycopg2 import sql, DatabaseError
from flectra import api, fields, models, _
from flectra.tools import DEFAULT_SERVER_DATETIME_FORMAT
from flectra.exceptions import ValidationError
from flectra.exceptions import ValidationError, UserError
from flectra.addons.base.models.res_partner import WARNING_MESSAGE, WARNING_HELP
_logger = logging.getLogger(__name__)
@@ -499,6 +499,17 @@ class ResPartner(models.Model):
vals['supplier_rank'] = 1
return super().create(vals_list)
def unlink(self):
"""
Prevent the deletion of a partner "Individual", child of a company if:
- partner in 'account.move'
- state: all states (draft and posted)
"""
moves = self.sudo().env['account.move'].search_count([('partner_id', 'in', self.ids), ('state', 'in', ['draft', 'posted'])])
if moves:
raise UserError(_("Record cannot be deleted. Partner used in Accounting"))
return super(ResPartner, self).unlink()
def _increase_rank(self, field, n=1):
if self.ids and field in ['customer_rank', 'supplier_rank']:
try:
+88 -73
View File
@@ -218,11 +218,12 @@ class AccountEdiFormat(models.Model):
# Import methods to override based on EDI Format
####################################################
def _create_invoice_from_xml_tree(self, filename, tree):
def _create_invoice_from_xml_tree(self, filename, tree, journal=None):
""" Create a new invoice with the data inside the xml.
:param filename: The name of the xml.
:param tree: The tree of the xml to import.
:param journal: The journal on which importing the invoice.
:returns: The created invoice.
"""
# TO OVERRIDE
@@ -504,96 +505,107 @@ class AccountEdiFormat(models.Model):
element = xml_element.xpath(xpath, namespaces=namespaces)
return element[0].text if element else None
def _retrieve_partner(self, name=None, phone=None, mail=None, vat=None):
'''Search all partners and find one that matches one of the parameters.
@api.model
def _retrieve_partner_with_vat(self, vat, extra_domain):
if not vat:
return None
# Sometimes, the vat is specified with some whitespaces.
normalized_vat = vat.replace(' ', '')
country_prefix = re.match('^[a-zA-Z]{2}|^', vat).group()
partner = self.env['res.partner'].search(extra_domain + [('vat', 'in', (normalized_vat, vat))], limit=1)
# Try to remove the country code prefix from the vat.
if not partner and country_prefix:
partner = self.env['res.partner'].search(extra_domain + [
('vat', 'in', (normalized_vat[2:], vat[2:])),
('country_id.code', '=', country_prefix.upper()),
], limit=1)
# The country could be not specified on the partner.
if not partner:
partner = self.env['res.partner'].search(extra_domain + [
('vat', 'in', (normalized_vat[2:], vat[2:])),
('country_id', '=', False),
], limit=1)
# The vat could be a string of alphanumeric values without country code but with missing zeros at the
# beginning.
if not partner:
try:
vat_only_numeric = str(int(re.sub(r'^\D{2}', '', normalized_vat) or 0))
except ValueError:
vat_only_numeric = None
if vat_only_numeric:
query = self.env['res.partner']._where_calc(extra_domain + [('active', '=', True)])
tables, where_clause, where_params = query.get_sql()
if country_prefix:
vat_prefix_regex = f'({country_prefix})?'
else:
vat_prefix_regex = '([A-z]{2})?'
self._cr.execute(f'''
SELECT res_partner.id
FROM {tables}
WHERE {where_clause}
AND res_partner.vat ~ %s
LIMIT 1
''', where_params + ['^%s0*%s$' % (vat_prefix_regex, vat_only_numeric)])
partner_row = self._cr.fetchone()
if partner_row:
partner = self.env['res.partner'].browse(partner_row[0])
return partner
@api.model
def _retrieve_partner_with_phone_mail(self, phone, mail, extra_domain):
domains = []
if phone:
domains.append([('phone', '=', phone)])
domains.append([('mobile', '=', phone)])
if mail:
domains.append([('email', '=', mail)])
if not domains:
return None
domain = expression.OR(domains)
if extra_domain:
domain = expression.AND([domain, extra_domain])
return self.env['res.partner'].search(domain, limit=1)
@api.model
def _retrieve_partner_with_name(self, name, extra_domain):
if not name:
return None
return self.env['res.partner'].search([('name', 'ilike', name)] + extra_domain, limit=1)
def _retrieve_partner(self, name=None, phone=None, mail=None, vat=None, domain=None):
'''Search all partners and find one that matches one of the parameters.
:param name: The name of the partner.
:param phone: The phone or mobile of the partner.
:param mail: The mail of the partner.
:param vat: The vat number of the partner.
:returns: A partner or an empty recordset if not found.
'''
def search_with_vat(extra_domain):
if not vat:
return None
# Sometimes, the vat is specified with some whitespaces.
normalized_vat = vat.replace(' ', '')
country_prefix = re.match('^[a-zA-Z]{2}|^', vat).group()
partner = self.env['res.partner'].search(extra_domain + [('vat', 'in', (normalized_vat, vat))], limit=1)
# Try to remove the country code prefix from the vat.
if not partner and country_prefix:
partner = self.env['res.partner'].search(extra_domain + [
('vat', 'in', (normalized_vat[2:], vat[2:])),
('country_id.code', '=', country_prefix.upper()),
], limit=1)
# The country could be not specified on the partner.
if not partner:
partner = self.env['res.partner'].search(extra_domain + [
('vat', 'in', (normalized_vat[2:], vat[2:])),
('country_id', '=', False),
], limit=1)
# The vat could be a string of alphanumeric values without country code but with missing zeros at the
# beginning.
if not partner:
try:
vat_only_numeric = str(int(re.sub('^\D{2}', '', normalized_vat) or 0))
except ValueError:
vat_only_numeric = None
if vat_only_numeric:
query = self.env['res.partner']._where_calc(extra_domain + [('active', '=', True)])
tables, where_clause, where_params = query.get_sql()
if country_prefix:
vat_prefix_regex = f'({country_prefix})?'
else:
vat_prefix_regex = '([A-z]{2})?'
self._cr.execute(f'''
SELECT res_partner.id
FROM {tables}
WHERE {where_clause}
AND res_partner.vat ~ %s
LIMIT 1
''', where_params + ['^%s0*%s$' % (vat_prefix_regex, vat_only_numeric)])
partner_row = self._cr.fetchone()
if partner_row:
partner = self.env['res.partner'].browse(partner_row[0])
return partner
return self._retrieve_partner_with_vat(vat, extra_domain)
def search_with_phone_mail(extra_domain):
domains = []
if phone:
domains.append([('phone', '=', phone)])
domains.append([('mobile', '=', phone)])
if mail:
domains.append([('email', '=', mail)])
if not domains:
return None
domain = expression.OR(domains)
if extra_domain:
domain = expression.AND([domain, extra_domain])
return self.env['res.partner'].search(domain, limit=1)
return self._retrieve_partner_with_phone_mail(phone, mail, extra_domain)
def search_with_name(extra_domain):
if not name:
return None
return self.env['res.partner'].search([('name', 'ilike', name)] + extra_domain, limit=1)
return self._retrieve_partner_with_name(name, extra_domain)
for search_method in (search_with_vat, search_with_phone_mail, search_with_name):
for extra_domain in ([('company_id', '=', self.env.company.id)], []):
partner = search_method(extra_domain)
if partner:
return partner
return self.env['res.partner']
def _retrieve_product(self, name=None, default_code=None, barcode=None):
@@ -648,3 +660,6 @@ class AccountEdiFormat(models.Model):
def _format_error_message(self, error_title, errors):
bullet_list_msg = ''.join('<li>%s</li>' % html_escape(msg) for msg in errors)
return '%s<ul>%s</ul>' % (error_title, bullet_list_msg)
def _is_account_edi_ubl_cii_available(self):
return hasattr(self, '_infer_xml_builder_from_tree')
+5 -1
View File
@@ -31,8 +31,12 @@ class AccountJournal(models.Model):
('edi_format_id', 'in', diff_edi_format_ids.ids),
('state', 'in', ('to_cancel', 'to_send')),
])
if documents:
# If the formats we are unchecking do not need a webservice, we don't need them to be correctly sent
if documents.filtered(lambda d: d.edi_format_id._needs_web_services()):
raise UserError(_('Cannot deactivate (%s) on this journal because not all documents are synchronized', ', '.join(documents.edi_format_id.mapped('display_name'))))
# remove these documents which: do not need a web service & are linked to the edi formats we are unchecking
if documents:
documents.unlink()
return res
else:
return super().write(vals)
+56
View File
@@ -264,6 +264,34 @@ class AccountMove(models.Model):
return invoice_global_tax_details
def _prepare_edi_vals_to_export(self):
''' The purpose of this helper is to prepare values in order to export an invoice through the EDI system.
This includes the computation of the tax details for each invoice line that could be very difficult to
handle regarding the computation of the base amount.
:return: A python dict containing default pre-processed values.
'''
self.ensure_one()
res = {
'record': self,
'balance_multiplicator': -1 if self.is_inbound() else 1,
'invoice_line_vals_list': [],
}
# Invoice lines details.
for index, line in enumerate(self.invoice_line_ids.filtered(lambda line: not line.display_type), start=1):
line_vals = line._prepare_edi_vals_to_export()
line_vals['index'] = index
res['invoice_line_vals_list'].append(line_vals)
# Totals.
res.update({
'total_price_subtotal_before_discount': sum(x['price_subtotal_before_discount'] for x in res['invoice_line_vals_list']),
'total_price_discount': sum(x['price_discount'] for x in res['invoice_line_vals_list']),
})
return res
def _update_payments_edi_documents(self):
''' Update the edi documents linked to the current journal entries. These journal entries must be linked to an
account.payment of an account.bank.statement.line. This additional method is needed because the payment flow is
@@ -416,6 +444,34 @@ class AccountMoveLine(models.Model):
# Export Electronic Document
####################################################
def _prepare_edi_vals_to_export(self):
''' The purpose of this helper is the same as '_prepare_edi_vals_to_export' but for a single invoice line.
This includes the computation of the tax details for each invoice line or the management of the discount.
Indeed, in some EDI, we need to provide extra values depending the discount such as:
- the discount as an amount instead of a percentage.
- the price_unit but after subtraction of the discount.
:return: A python dict containing default pre-processed values.
'''
self.ensure_one()
if self.discount == 100.0:
gross_price_subtotal = self.currency_id.round(self.price_unit * self.quantity)
else:
gross_price_subtotal = self.currency_id.round(self.price_subtotal / (1 - self.discount / 100.0))
res = {
'line': self,
'price_unit_after_discount': self.currency_id.round(self.price_unit * (1 - (self.discount / 100.0))),
'price_subtotal_before_discount': gross_price_subtotal,
'price_subtotal_unit': self.currency_id.round(self.price_subtotal / self.quantity) if self.quantity else 0.0,
'price_total_unit': self.currency_id.round(self.price_total / self.quantity) if self.quantity else 0.0,
'price_discount': gross_price_subtotal - self.price_subtotal,
'price_discount_unit': (gross_price_subtotal - self.price_subtotal) / self.quantity if self.quantity else 0.0,
'gross_price_total_unit': self.currency_id.round(gross_price_subtotal / self.quantity) if self.quantity else 0.0,
'unece_uom_code': self.product_id.product_tmpl_id.uom_id._get_unece_code(),
}
return res
def reconcile(self):
# OVERRIDE
# In some countries, the payments must be sent to the government under some condition. One of them could be
+10
View File
@@ -1 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
from . import models
from flectra import api, SUPERUSER_ID
def _post_init_hook(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, {})
modules = env['ir.module.module'].search([('name', '=', 'account_edi_ubl_cii'), ('state', '=', 'uninstalled')])
modules.sudo().button_install()
@@ -9,6 +9,7 @@
'data/facturx_templates.xml',
],
'installable': True,
'post_init_hook': '_post_init_hook',
'application': False,
'auto_install': True,
'license': 'LGPL-3',
@@ -26,13 +26,13 @@ class AccountEdiFormat(models.Model):
def _is_compatible_with_journal(self, journal):
self.ensure_one()
res = super()._is_compatible_with_journal(journal)
if self.code != 'facturx_1_0_05':
if self.code != 'facturx_1_0_05' or self._is_account_edi_ubl_cii_available():
return res
return journal.type == 'sale'
def _post_invoice_edi(self, invoices, test_mode=False):
self.ensure_one()
if self.code != 'facturx_1_0_05':
if self.code != 'facturx_1_0_05' or self._is_account_edi_ubl_cii_available():
return super()._post_invoice_edi(invoices, test_mode=test_mode)
res = {}
for invoice in invoices:
@@ -53,7 +53,7 @@ class AccountEdiFormat(models.Model):
def _prepare_invoice_report(self, pdf_writer, edi_document):
self.ensure_one()
if self.code != 'facturx_1_0_05':
if self.code != 'facturx_1_0_05' or self._is_account_edi_ubl_cii_available():
return super()._prepare_invoice_report(pdf_writer, edi_document)
if not edi_document.attachment_id:
return
@@ -175,15 +175,15 @@ class AccountEdiFormat(models.Model):
def _is_facturx(self, filename, tree):
return self.code == 'facturx_1_0_05' and tree.tag == '{urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100}CrossIndustryInvoice'
def _create_invoice_from_xml_tree(self, filename, tree):
def _create_invoice_from_xml_tree(self, filename, tree, journal=None):
self.ensure_one()
if self._is_facturx(filename, tree):
if self._is_facturx(filename, tree) and not self._is_account_edi_ubl_cii_available():
return self._import_facturx(tree, self.env['account.move'])
return super()._create_invoice_from_xml_tree(filename, tree)
return super()._create_invoice_from_xml_tree(filename, tree, journal=journal)
def _update_invoice_from_xml_tree(self, filename, tree, invoice):
self.ensure_one()
if self._is_facturx(filename, tree):
if self._is_facturx(filename, tree) and not self._is_account_edi_ubl_cii_available():
return self._import_facturx(tree, invoice)
return super()._update_invoice_from_xml_tree(filename, tree, invoice)
@@ -11,6 +11,12 @@ class TestAccountEdiFacturx(AccountEdiTestCommon):
def setUpClass(cls, chart_template_ref=None, edi_format_ref='account_edi_facturx.edi_facturx_1_0_05'):
super().setUpClass(chart_template_ref=chart_template_ref, edi_format_ref=edi_format_ref)
if cls.env['ir.module.module'].search(
[('name', '=', 'account_edi_ubl_cii'), ('state', '=', 'installed')],
limit=1,
):
cls.skipTest(cls, "Factur-X Tests skipped because account_edi_ubl_cii is installed.")
# ==== Init ====
cls.tax_10_include = cls.env['account.tax'].create({
+7
View File
@@ -2,3 +2,10 @@
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
from . import models
from flectra import api, SUPERUSER_ID
def _post_init_hook(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, {})
modules = env['ir.module.module'].search([('name', '=', 'account_edi_ubl_cii'), ('state', '=', 'uninstalled')])
modules.sudo().button_install()
+1
View File
@@ -17,5 +17,6 @@ Belgian e-invoicing uses the UBL 2.0 using the e-fff protocol.
],
'installable': True,
'auto_install': True,
'post_init_hook': '_post_init_hook',
'license': 'LGPL-3',
}
@@ -11,28 +11,27 @@ class AccountEdiFormat(models.Model):
def _is_efff(self, filename, tree):
return self.code == 'efff_1' and tree.tag == '{urn:oasis:names:specification:ubl:schema:xsd:Invoice-2}Invoice'
def _create_invoice_from_xml_tree(self, filename, tree):
def _create_invoice_from_xml_tree(self, filename, tree, journal=None):
self.ensure_one()
if self._is_efff(filename, tree):
if self._is_efff(filename, tree) and not self._is_account_edi_ubl_cii_available():
return self._create_invoice_from_ubl(tree)
return super()._create_invoice_from_xml_tree(filename, tree)
return super()._create_invoice_from_xml_tree(filename, tree, journal=journal)
def _update_invoice_from_xml_tree(self, filename, tree, invoice):
self.ensure_one()
if self._is_efff(filename, tree):
if self._is_efff(filename, tree) and not self._is_account_edi_ubl_cii_available():
return self._update_invoice_from_ubl(tree, invoice)
return super()._update_invoice_from_xml_tree(filename, tree, invoice)
def _is_compatible_with_journal(self, journal):
self.ensure_one()
res = super()._is_compatible_with_journal(journal)
if self.code != 'efff_1':
return res
if self.code != 'efff_1' or self._is_account_edi_ubl_cii_available():
return super()._is_compatible_with_journal(journal)
return journal.type == 'sale' and journal.country_code == 'BE'
def _post_invoice_edi(self, invoices, test_mode=False):
self.ensure_one()
if self.code != 'efff_1':
if self.code != 'efff_1' or self._is_account_edi_ubl_cii_available():
return super()._post_invoice_edi(invoices, test_mode=test_mode)
res = {}
for invoice in invoices:
@@ -14,6 +14,12 @@ class TestUBLBE(AccountEdiTestCommon):
def setUpClass(cls, chart_template_ref='l10n_be.l10nbe_chart_template', edi_format_ref='l10n_be_edi.edi_efff_1'):
super().setUpClass(chart_template_ref=chart_template_ref, edi_format_ref=edi_format_ref)
if cls.env['ir.module.module'].search(
[('name', '=', 'account_edi_ubl_cii'), ('state', '=', 'installed')],
limit=1,
):
cls.skipTest(cls, "L10n_be_edi Tests skipped because account_edi_ubl_cii is installed.")
cls.partner_a.write({
'street': "Chaussée de Namur 40",
'zip': "1367",
+6
View File
@@ -9,6 +9,12 @@ class TestUBL(AccountEdiTestCommon):
def setUpClass(cls, chart_template_ref='l10n_be.l10nbe_chart_template', edi_format_ref='l10n_be_edi.edi_efff_1'):
super().setUpClass(chart_template_ref=chart_template_ref, edi_format_ref=edi_format_ref)
if cls.env['ir.module.module'].search(
[('name', '=', 'account_edi_ubl_cii'), ('state', '=', 'installed')],
limit=1,
):
cls.skipTest(cls, "L10n_be_edi Tests skipped because account_edi_ubl_cii is installed.")
cls.partner_a.vat = 'BE0477472701'
def test_invoice_edi_xml_update(self):
@@ -169,8 +169,8 @@
<AliquotaIVA t-esc="format_numbers(tax_dict['tax'].amount)"/>
<Natura t-if="tax_dict['tax'].l10n_it_has_exoneration" t-esc="tax_dict['tax'].l10n_it_kind_exoneration"/>
<Arrotondamento t-if="tax_dict.get('rounding')" t-esc="format_numbers(tax_dict['rounding'])"/>
<ImponibileImporto t-esc="format_monetary(abs(tax_dict['base_amount']), currency)"/>
<Imposta t-esc="format_monetary(abs(tax_dict['tax_amount']), currency)"/>
<ImponibileImporto t-esc="format_monetary(abs(tax_dict['base_amount_currency']), currency)"/>
<Imposta t-esc="format_monetary(abs(tax_dict['tax_amount_currency']), currency)"/>
<EsigibilitaIVA t-if="not tax_dict['tax'].l10n_it_has_exoneration or tax_dict['tax'].l10n_it_kind_exoneration=='N6'" t-esc="tax_dict['tax'].l10n_it_vat_due_date"/>
<RiferimentoNormativo t-if="tax_dict['tax'].l10n_it_has_exoneration" t-esc="format_alphanumeric(tax_dict['tax'].l10n_it_law_reference[:100])"/>
</DatiRiepilogo>
@@ -233,11 +233,11 @@ class AccountEdiFormat(models.Model):
def _is_fattura_pa(self, filename, tree):
return self.code == 'fattura_pa' and self._check_filename_is_fattura_pa(filename)
def _create_invoice_from_xml_tree(self, filename, tree):
def _create_invoice_from_xml_tree(self, filename, tree, journal=None):
self.ensure_one()
if self._is_fattura_pa(filename, tree):
return self._import_fattura_pa(tree, self.env['account.move'])
return super()._create_invoice_from_xml_tree(filename, tree)
return super()._create_invoice_from_xml_tree(filename, tree, journal=journal)
def _update_invoice_from_xml_tree(self, filename, tree, invoice):
self.ensure_one()
+3 -3
View File
@@ -167,14 +167,14 @@ class AccountMove(models.Model):
# taxable base = sum(taxable base for each unit) + Arrotondamento
tax_details = self._prepare_edi_tax_details()
for _tax_name, tax_dict in tax_details['tax_details'].items():
base_amount = tax_dict['base_amount']
tax_amount = tax_dict['tax_amount']
base_amount = tax_dict['base_amount_currency']
tax_amount = tax_dict['tax_amount_currency']
tax_rate = tax_dict['tax'].amount
if tax_dict['tax'].price_include and tax_dict['tax'].amount_type == 'percent':
expected_base_amount = tax_amount * 100 / tax_rate if tax_rate else False
if expected_base_amount and float_compare(base_amount, expected_base_amount, 2):
tax_dict['rounding'] = base_amount - (tax_amount * 100 / tax_rate)
tax_dict['base_amount'] = base_amount - tax_dict['rounding']
tax_dict['base_amount_currency'] = base_amount - tax_dict['rounding']
# Create file content.
template_values = {
+1
View File
@@ -28,5 +28,6 @@ class PosAdyenController(http.Controller):
payment_method.adyen_latest_diagnosis = data['SaleToPOIResponse']['MessageHeader']['ServiceID']
else:
payment_method.adyen_latest_response = json.dumps(data)
_logger.info('notification writed from adyen\n%s', data)
else:
_logger.error('received a message for a terminal not registered in Flectra: %s', terminal_identifier)
@@ -79,6 +79,7 @@ class PosPaymentMethod(models.Model):
}
def get_latest_adyen_status(self, pos_config_name):
_logger.info('get_latest_adyen_status\n%s', pos_config_name)
self.ensure_one()
# Poll the status of the terminal if there's no new
+6 -2
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
from flectra import api, models
from flectra import models
class ResUsers(models.Model):
@@ -8,4 +8,8 @@ class ResUsers(models.Model):
def _has_unsplash_key_rights(self):
self.ensure_one()
return self.has_group('base.group_erp_manager')
# Website has no dependency to web_unsplash, we cannot warranty the order of the execution
# of the overwrite done in 5ef8300.
# So to avoid to create a new module bridge, with a lot of code, we prefer to make a check
# here for website's user.
return self.has_group('base.group_erp_manager') or self.has_group('website.group_website_designer')
+3 -1
View File
@@ -3,8 +3,10 @@
import logging
from werkzeug.urls import url_join
from flectra import api, fields, models, _
from flectra.addons.http_routing.models.ir_http import url_for
from flectra.http import request
from flectra.osv import expression
from flectra.exceptions import AccessError
@@ -50,7 +52,7 @@ class SeoMetadata(models.AbstractModel):
'og:type': 'website',
'og:title': title,
'og:site_name': company.name,
'og:url': request.httprequest.url,
'og:url': url_join(request.httprequest.url_root, url_for(request.httprequest.path)),
'og:image': img,
}
# Default meta for Twitter
+1 -7
View File
@@ -2,7 +2,7 @@
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
import logging
from flectra import api, fields, models, tools, _
from flectra import api, fields, models, _
from flectra.exceptions import ValidationError
from flectra.http import request
@@ -19,12 +19,6 @@ class ResUsers(models.Model):
('login_key', 'unique (login, website_id)', 'You can not have two users with the same login!'),
]
def _has_unsplash_key_rights(self):
self.ensure_one()
if self.has_group('website.group_website_designer'):
return True
return super(ResUsers, self)._has_unsplash_key_rights()
@api.constrains('login', 'website_id')
def _check_login(self):
""" Do not allow two users with the same login without website """