odoo/odoo · error · ValidationError

A fiscal position with a foreign VAT already exists in this

Error message

A fiscal position with a foreign VAT already exists in this country.

What it means

Constraint on account.fiscal.position: at most one fiscal position per company may declare a given foreign_vat country. It counts sibling positions (same company domain, same country_id, any different foreign_vat value, excluding self) and raises ValidationError on duplicates, keeping the foreign-VAT registry unambiguous.

Source

Thrown at addons/account/models/partner.py:139

            if record.foreign_vat:
                if not record.country_id:
                    raise ValidationError(_("The country of the foreign VAT number could not be detected. Please assign a country to the fiscal position."))
                if record.country_id == record.company_id.account_fiscal_country_id:
                    if not record.state_ids:
                        if record.company_id.account_fiscal_country_id.state_ids:
                            raise ValidationError(_("You cannot create a fiscal position with a foreign VAT within your fiscal country without assigning it a state."))
                if record.country_group_id and record.country_id:
                    if record.country_id not in record.country_group_id.country_ids:
                        raise ValidationError(_("You cannot create a fiscal position with a country outside of the selected country group."))

                similar_fpos_count = self.env['account.fiscal.position'].search_count([
                    *self.env['account.fiscal.position']._check_company_domain(record.company_id),
                    ('foreign_vat', 'not in', (False, record.foreign_vat)),
                    ('id', '!=', record.id),
                    ('country_id', '=', record.country_id.id),
                ])
                if similar_fpos_count:
                    raise ValidationError(_("A fiscal position with a foreign VAT already exists in this country."))

    @api.onchange('country_id', 'foreign_vat')
    def _onchange_foreign_vat(self):
        self.foreign_vat, _country_code = self.env['res.partner']._run_vat_checks(self.country_id, self.foreign_vat, validation=False)

    def _inverse_foreign_vat(self):
        for record in self:
            if not record.foreign_vat:
                continue

            if record.country_id:
                fp_label = _("fiscal position [%s]", record.name)
                record.foreign_vat, _country_code = self.env['res.partner']._run_vat_checks(record.country_id, record.foreign_vat, partner_name=fp_label)

    def map_tax(self, taxes):
        if not self:
            return taxes
        if not self.tax_ids:

View on GitHub (pinned to 1e661df964)

Solutions

  1. Reuse the existing fiscal position for that country (update its tax/account mappings) instead of creating another.
  2. Delete or archive the obsolete duplicate, then save the new one.
  3. Dedupe in imports: search_count first, and skip when a position for that country exists.

Example fix

# before
env['account.fiscal.position'].create({'name': 'DE 2', 'country_id': de.id, 'foreign_vat': 'DE987'})  # ValidationError

# after
existing = env['account.fiscal.position'].search([
    ('company_id', '=', company.id), ('country_id', '=', de.id),
    ('foreign_vat', '!=', False)], limit=1)
fpos = existing or env['account.fiscal.position'].create({...})
Defensive patterns

Strategy: validation

Validate before calling

def no_duplicate_fpos(env, company, country_id, exclude_id=None):
    return not env['account.fiscal.position'].search_count([
        *env['account.fiscal.position']._check_company_domain(company),
        ('foreign_vat', 'not in', (False, False)),
        ('id', '!=', exclude_id), ('country_id', '=', country_id)])

Prevention

When it happens

Trigger: create()/write() producing a second account.fiscal.position with the same country_id and a foreign_vat, in the same company; e.g. adding 'DE VAT #2' when 'DE VAT' already exists.

Common situations: Duplicate onboarding runs creating positions per registration; imports re-run without dedupe; two users creating the same foreign registration concurrently.

Related errors


AI-assisted analysis of odoo/odoo@1e661df964 (2026-08-15). Data as JSON: /api/errors/760b1d5c941e2508. Report an issue: GitHub.