diff --git a/country.diff b/country.diff
new file mode 100644
index 0000000..f26a863
--- /dev/null
+++ b/country.diff
@@ -0,0 +1,12603 @@
+diff --git a/tryton/modules/country/__init__.py b/tryton/modules/country/__init__.py
+index 80dff9d0ce..dcc3549d5f 100644
+--- a/tryton/modules/country/__init__.py
++++ b/tryton/modules/country/__init__.py
+@@ -8,6 +8,9 @@ def register():
+ # Prevent to import backend when importing scripts
+ from . import country
+ Pool.register(
++ country.Organization,
++ country.OrganizationMember,
++ country.Region,
+ country.Country,
+ country.Subdivision,
+ country.PostalCode,
+diff --git a/tryton/modules/country/country.py b/tryton/modules/country/country.py
+index 735606d5a6..1d681b5184 100644
+--- a/tryton/modules/country/country.py
++++ b/tryton/modules/country/country.py
+@@ -1,26 +1,173 @@
+ # This file is part of Tryton. The COPYRIGHT file at the top level of
+ # this repository contains the full copyright notices and license terms.
++import datetime as dt
++
++from sql import Literal
++from sql.conditionals import Coalesce
++
+ from trytond import backend
+-from trytond.model import DeactivableMixin, ModelSQL, ModelView, fields
++from trytond.model import DeactivableMixin, ModelSQL, ModelView, fields, tree
+ from trytond.pool import Pool
+-from trytond.pyson import Eval
+-from trytond.tools import lstrip_wildcard
++from trytond.pyson import Eval, If
++from trytond.tools import is_full_text, lstrip_wildcard
+ from trytond.transaction import Transaction
+
+
++class Organization(ModelSQL, ModelView):
++ "Organization"
++ __name__ = 'country.organization'
++
++ name = fields.Char("Name", required=True, translate=True)
++ code = fields.Char("Code")
++ members = fields.One2Many(
++ 'country.organization.member', 'organization', "Members",
++ filter=[
++ ('active', 'in', [True, False]),
++ ])
++ countries = fields.Many2Many(
++ 'country.organization.member', 'organization', 'country', "Countries",
++ readonly=True)
++
++
++class OrganizationMember(ModelSQL, ModelView):
++ "Organization Member"
++ __name__ = 'country.organization.member'
++
++ organization = fields.Many2One(
++ 'country.organization', "Organization", required=True)
++ country = fields.Many2One(
++ 'country.country', "Country", required=True)
++ from_date = fields.Date(
++ "From Date",
++ domain=[
++ If(Eval('from_date') & Eval('to_date'),
++ ('from_date', '<=', Eval('to_date')),
++ ()),
++ ])
++ to_date = fields.Date(
++ "To Date",
++ domain=[
++ If(Eval('from_date') & Eval('to_date'),
++ ('to_date', '>=', Eval('from_date')),
++ ()),
++ ])
++ active = fields.Function(fields.Boolean("Active"), 'on_change_with_active')
++
++ @classmethod
++ def __setup__(cls):
++ super().__setup__()
++ cls._order.insert(0, ('country', None))
++ cls._order.insert(1, ('from_date', 'ASC NULLS FIRST'))
++ cls.__access__.add('organization')
++
++ @classmethod
++ def default_active(cls):
++ return True
++
++ @fields.depends('from_date', 'to_date')
++ def on_change_with_active(self, name=None):
++ pool = Pool()
++ Date = pool.get('ir.date')
++ context = Transaction().context
++ date = context.get('date', Date.today())
++
++ from_date = self.from_date or dt.date.min
++ to_date = self.to_date or dt.date.max
++ return from_date <= date <= to_date
++
++ @classmethod
++ def domain_active(cls, domain, tables):
++ pool = Pool()
++ Date = pool.get('ir.date')
++ context = Transaction().context
++ table, _ = tables[None]
++ _, operator, operand = domain
++ date = context.get('date', Date.today())
++
++ from_date = Coalesce(table.from_date, dt.date.min)
++ to_date = Coalesce(table.to_date, dt.date.max)
++
++ expression = (from_date <= date) & (to_date >= date)
++
++ if operator in {'=', '!='}:
++ if (operator == '=') != operand:
++ expression = ~expression
++ elif operator in {'in', 'not in'}:
++ if True in operand and False not in operand:
++ pass
++ elif False in operand and True not in operand:
++ expression = ~expression
++ else:
++ expression = Literal(True)
++ else:
++ expression = Literal(True)
++ return expression
++
++
++class Region(tree(), ModelSQL, ModelView):
++ "Region"
++ __name__ = 'country.region'
++
++ name = fields.Char("Name", required=True, translate=True)
++ code_numeric = fields.Char(
++ "Numeric Code", size=3,
++ help="UN M49 region code.")
++ parent = fields.Many2One('country.region', "Parent")
++ subregions = fields.One2Many('country.region', 'parent', "Subregions")
++ countries = fields.One2Many(
++ 'country.country', 'region', "Countries",
++ add_remove=[
++ ('region', '=', None),
++ ])
++
++ @classmethod
++ def __setup__(cls):
++ super().__setup__()
++ cls._order.insert(0, ('name', 'ASC'))
++
++ @classmethod
++ def search_rec_name(cls, name, clause):
++ if clause[1].startswith('!') or clause[1].startswith('not '):
++ bool_op = 'AND'
++ else:
++ bool_op = 'OR'
++ code_value = clause[2]
++ if clause[1].endswith('like'):
++ code_value = lstrip_wildcard(clause[2])
++ return [bool_op,
++ ('name',) + tuple(clause[1:]),
++ ('code_numeric', clause[1], code_value) + tuple(clause[3:]),
++ ]
++
++
+ class Country(DeactivableMixin, ModelSQL, ModelView):
+ 'Country'
+ __name__ = 'country.country'
+- name = fields.Char('Name', required=True, translate=True,
+- help="The main identifier of the country.", select=True)
+- code = fields.Char('Code', size=2, select=True,
+- help="The 2 chars ISO country code.")
+- code3 = fields.Char('3-letters Code', size=3, select=True,
++ name = fields.Char(
++ "Name", required=True, translate=True,
++ help="The main identifier of the country.")
++ code = fields.Char(
++ "Code", size=2,select=True,
++ help="The 2 chars ISO country code.")
++ code3 = fields.Char(
++ "3-letters Code", size=3, select=True,
+ help="The 3 chars ISO country code.")
+- code_numeric = fields.Char('Numeric Code', select=True,
++ code_numeric = fields.Char(
++ "Numeric Code", select=True,
+ help="The ISO numeric country code.")
++ flag = fields.Function(fields.Char("Flag"), 'on_change_with_flag')
++ region = fields.Many2One(
++ 'country.region', "Region", ondelete='SET NULL')
+ subdivisions = fields.One2Many('country.subdivision',
+ 'country', 'Subdivisions')
++ members = fields.One2Many(
++ 'country.organization.member', 'country', "Members",
++ filter=[
++ ('active', 'in', [True, False]),
++ ])
++ organizations = fields.Many2Many(
++ 'country.organization.member', 'country', 'organization',
++ "Organizations", readonly=True)
+
+ @classmethod
+ def __setup__(cls):
+@@ -36,35 +183,53 @@ class Country(DeactivableMixin, ModelSQL, ModelView):
+
+ super(Country, cls).__register__(module_name)
+
+- table = cls.__table_handler__(module_name)
+-
+- # Migration from 3.4: drop unique constraints from name and code
+- table.drop_constraint('name_uniq')
+- table.drop_constraint('code_uniq')
+-
+- # Migration from 3.8: remove required on code
+- table.not_null_action('code', 'remove')
+-
+ # Migration from 5.2: remove country data
+ cursor.execute(*data.delete(where=(data.module == 'country')
+ & (data.model == cls.__name__)))
+
++ @fields.depends('code')
++ def on_change_with_flag(self, name=None):
++ if self.code:
++ return ''.join(map(chr, map(lambda c: 127397 + ord(c), self.code)))
++
++ def get_rec_name(self, name):
++ name = self.name
++ if self.flag:
++ name = ' '.join([self.flag, self.name])
++ return name
++
+ @classmethod
+ def search_rec_name(cls, name, clause):
+- if clause[1].startswith('!') or clause[1].startswith('not '):
++ _, operator, operand, *extra = clause
++ if operator.startswith('!') or operator.startswith('not '):
+ bool_op = 'AND'
+ else:
+ bool_op = 'OR'
+- code_value = clause[2]
+- if clause[1].endswith('like'):
+- code_value = lstrip_wildcard(clause[2])
++ code_value = operand
++ if operator.endswith('like') and is_full_text(operand):
++ code_value = lstrip_wildcard(operand)
+ return [bool_op,
+- ('name',) + tuple(clause[1:]),
+- ('code', clause[1], code_value) + tuple(clause[3:]),
+- ('code3', clause[1], code_value) + tuple(clause[3:]),
+- ('code_numeric', clause[1], code_value) + tuple(clause[3:]),
++ ('name', operator, operand, *extra),
++ ('code', operator, code_value, *extra),
++ ('code3', operator, code_value, *extra),
++ ('code_numeric', operator, code_value, *extra),
+ ]
+
++ def is_member(self, organization, date=None):
++ """Return if the country is in the organization at the date
++ organization can be an XML id"""
++ pool = Pool()
++ Date = pool.get('ir.date')
++ ModelData = pool.get('ir.model.data')
++ Organization = pool.get('country.organization')
++ if date is None:
++ date = Date.today()
++ if isinstance(organization, str):
++ organization = ModelData.get_id(organization)
++ with Transaction().set_context(date=date):
++ organization = Organization(organization)
++ return self in organization.countries
++
+ @classmethod
+ def create(cls, vlist):
+ vlist = [x.copy() for x in vlist]
+@@ -90,13 +255,16 @@ class Country(DeactivableMixin, ModelSQL, ModelView):
+ class Subdivision(DeactivableMixin, ModelSQL, ModelView):
+ "Subdivision"
+ __name__ = 'country.subdivision'
+- country = fields.Many2One('country.country', 'Country',
+- required=True, select=True,
++ country = fields.Many2One(
++ 'country.country', "Country", required=True,
+ help="The country where this subdivision is.")
+- name = fields.Char('Name', required=True, select=True, translate=True,
++ name = fields.Char(
++ "Name", required=True, translate=True,
+ help="The main identifier of the subdivision.")
+- code = fields.Char('Code', required=True, select=True,
++ code = fields.Char(
++ "Code",
+ help="The ISO code of the subdivision.")
++ flag = fields.Function(fields.Char("Flag"), 'on_change_with_flag')
+ type = fields.Selection([
+ (None, ""),
+ ('administration', 'Administration'),
+@@ -270,18 +438,29 @@ class Subdivision(DeactivableMixin, ModelSQL, ModelView):
+ # Migration from 6.2: remove type required
+ table_h.not_null_action('type', action='remove')
+
++ # Migration from 6.4: remove required on code
++ table_h.not_null_action('code', action='remove')
++
++ @fields.depends('code')
++ def on_change_with_flag(self, name=None):
++ if self.code:
++ return '🏴' + ''.join(map(chr, map(
++ lambda c: 917504 + ord(c),
++ self.code.replace('-', '').lower()))) + '\U000e007f'
++
+ @classmethod
+ def search_rec_name(cls, name, clause):
+- if clause[1].startswith('!') or clause[1].startswith('not '):
++ _, operator, operand, *extra = clause
++ if operator.startswith('!') or operator.startswith('not '):
+ bool_op = 'AND'
+ else:
+ bool_op = 'OR'
+- code_value = clause[2]
+- if clause[1].endswith('like'):
+- code_value = lstrip_wildcard(clause[2])
++ code_value = operand
++ if operator.endswith('like') and is_full_text(operand):
++ code_value = lstrip_wildcard(operand)
+ return [bool_op,
+- ('name',) + tuple(clause[1:]),
+- ('code', clause[1], code_value) + tuple(clause[3:]),
++ ('name', operator, operand, *extra),
++ ('code', operator, code_value, *extra),
+ ]
+
+ @classmethod
+@@ -307,11 +486,11 @@ class Subdivision(DeactivableMixin, ModelSQL, ModelView):
+ class PostalCode(ModelSQL, ModelView):
+ "Postal Code"
+ __name__ = 'country.postal_code'
+- country = fields.Many2One('country.country', 'Country', required=True,
+- select=True, ondelete='CASCADE',
++ country = fields.Many2One(
++ 'country.country', "Country", required=True, ondelete='CASCADE',
+ help="The country that contains the postal code.")
+- subdivision = fields.Many2One('country.subdivision', 'Subdivision',
+- select=True, ondelete='CASCADE',
++ subdivision = fields.Many2One(
++ 'country.subdivision', "Subdivision", ondelete='CASCADE',
+ domain=[('country', '=', Eval('country', -1))],
+ help="The subdivision where the postal code is.")
+ postal_code = fields.Char('Postal Code')
+@@ -341,14 +520,15 @@ class PostalCode(ModelSQL, ModelView):
+
+ @classmethod
+ def search_rec_name(cls, name, clause):
+- if clause[1].startswith('!') or clause[1].startswith('not '):
++ _, operator, operand, *extra = clause
++ if operator.startswith('!') or operator.startswith('not '):
+ bool_op = 'AND'
+ else:
+ bool_op = 'OR'
+- code_value = clause[2]
+- if clause[1].endswith('like'):
+- code_value = lstrip_wildcard(clause[2])
++ code_value = operand
++ if operator.endswith('like') and is_full_text(operand):
++ code_value = lstrip_wildcard(operand)
+ return [bool_op,
+- ('postal_code', clause[1], code_value) + tuple(clause[3:]),
+- ('city',) + tuple(clause[1:]),
++ ('postal_code', operator, code_value, *extra),
++ ('city', operator, operand, *extra),
+ ]
+diff --git a/tryton/modules/country/country.xml b/tryton/modules/country/country.xml
+index b41f30de04..97b5906142 100644
+--- a/tryton/modules/country/country.xml
++++ b/tryton/modules/country/country.xml
+@@ -8,6 +8,156 @@ this repository contains the full copyright notices and license terms. -->
+ icons/tryton-country.svg
+
+
++
++
++
++
++ country.organization
++ form
++ organization_form
++
++
++
++ country.organization
++ tree
++ organization_list
++
++
++
++ Organizations
++ country.organization
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++ country.organization.member
++ form
++ organization_member_form
++
++
++
++ country.organization.member
++ tree
++ organization_member_list
++
++
++
++ country.region
++ form
++ region_form
++
++
++
++ country.region
++ tree
++
++ region_list
++
++
++
++ country.region
++ tree
++
++ subregions
++ region_tree
++
++
++
++ Areas
++ country.region
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++ Regions
++ country.region
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
+
+ country.country
+ form
+@@ -33,15 +183,39 @@ this repository contains the full copyright notices and license terms. -->
+
+
+
+-