Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions l10n_fr_department_crm/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
15 changes: 15 additions & 0 deletions l10n_fr_department_crm/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "Leads French department",
"summary": "French Departments for crm leads",
"version": "18.0.0.0.2",
"category": "French Localization",
"author": "Informatique Prog, Odoo Community Association (OCA)",
"maintainers": ["InformatiqueProg"],
"website": "https://github.com/OCA/l10n-france",
"license": "AGPL-3",
"depends": ["crm", "l10n_fr_department"],
"data": ["view/crm_lead.xml"],
"installable": True,
"application": False,
"auto_install": False,
}
33 changes: 33 additions & 0 deletions l10n_fr_department_crm/i18n/fr.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * l10n_fr_department_crm
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 18.0+e-20250618\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-27 12:51+0000\n"
"PO-Revision-Date: 2025-11-27 12:51+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"

#. module: l10n_fr_department_crm
#: model:ir.model.fields,field_description:l10n_fr_department_crm.field_crm_lead__country_department_id
msgid "Country Department"
msgstr "Département"

#. module: l10n_fr_department_crm
#: model_terms:ir.ui.view,arch_db:l10n_fr_department_crm.crm_case_tree_view_oppor
#: model_terms:ir.ui.view,arch_db:l10n_fr_department_crm.crm_lead_view_form
#: model_terms:ir.ui.view,arch_db:l10n_fr_department_crm.view_crm_case_opportunities_filter
msgid "Department"
msgstr "Département"

#. module: l10n_fr_department_crm
#: model:ir.model,name:l10n_fr_department_crm.model_crm_lead
msgid "Lead/Opportunity"
msgstr "Piste/opportunité"
33 changes: 33 additions & 0 deletions l10n_fr_department_crm/i18n/l10n_fr_department_crm-export.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * l10n_fr_department_crm
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 18.0+e-20250618\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-27 11:01+0000\n"
"PO-Revision-Date: 2025-11-27 11:01+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"

#. module: l10n_fr_department_crm
#: model:ir.model.fields,field_description:l10n_fr_department_crm.field_crm_lead__country_department_id
msgid "Country Department"
msgstr ""

#. module: l10n_fr_department_crm
#: model_terms:ir.ui.view,arch_db:l10n_fr_department_crm.crm_case_tree_view_oppor
#: model_terms:ir.ui.view,arch_db:l10n_fr_department_crm.crm_lead_view_form
#: model_terms:ir.ui.view,arch_db:l10n_fr_department_crm.view_crm_case_opportunities_filter
msgid "Department"
msgstr ""

#. module: l10n_fr_department_crm
#: model:ir.model,name:l10n_fr_department_crm.model_crm_lead
msgid "Lead/Opportunity"
msgstr ""
1 change: 1 addition & 0 deletions l10n_fr_department_crm/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import crm_lead
121 changes: 121 additions & 0 deletions l10n_fr_department_crm/models/crm_lead.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
from collections import defaultdict

from odoo import api, fields, models

# TODO after PR 701 merged
# - remove import unicode
# - replace FR_SPECIAL_ZIPCODES code by an import
# https://github.com/OCA/l10n-france/pull/701
# Duplicate from l10n_fr_department/model/res_partner.py **START**
try:
from unidecode import unidecode
except ImportError:
unidecode = None
FR_SPECIAL_ZIPCODES = {
"42620": {"laval": "03"},
"05110": {"claret": "04", "curbans": "04"},
"05130": {"piegut": "04", "venterol": "04"},
"05160": {"pontis": "04"},
"06260": {"rochette": "04", "pierre": "04"},
"48250": {"laveyrune": "07"},
"43450": {"leyvaux": "15"},
"33220": {"fougueyrolles": "24", "ponchapt": "24"},
"05700": {"villebois": "26"},
"01410": {"lajoux": "39"},
"01590": {"chancia": "39", "lavancia": "39"},
"52100": {"eulien": "51", "sapignicourt": "51"},
"21340": {"change": "71"},
"01200": {"eloise": "74"},
"13780": {"riboux": "83"},
"37160": {"buxeuil": "86"},
"94390": {"paray": "91"},
}


# Duplicate from l10n_fr_department/model/res_partner.py **END**


class CrmLead(models.Model):
_inherit = "crm.lead"

country_department_id = fields.Many2one(
"res.country.department",
compute="_compute_country_department",
string="Country Department",
store=True,
)

@api.depends("zip", "city", "country_id")
# If a department code changes, it will have to be manually recomputed
def _compute_country_department(self):
department_code2id, fr_dom_countries_ids = self._fr_department_code()

if not department_code2id:
self.country_department_id = False
else:
dpt_code2lead_ids = defaultdict(list)
for lead in self:
if lead.country_id.id in fr_dom_countries_ids and lead.zip:
dpt_code = self._fr_zipcode_city_to_department_code(
lead.zip, lead.city
)
dpt_code2lead_ids[dpt_code].append(lead.id)
else:
dpt_code2lead_ids[None].append(lead.id)
for dpt_code, partner_ids in dpt_code2lead_ids.items():
self.browse(partner_ids).country_department_id = department_code2id.get(
dpt_code
)

def _fr_department_code(self):
# Duplicate from l10n_fr_department/model/res_partner.py **START**
fr_dom_countries_codes = ("FR", "GP", "MQ", "GF", "RE", "YT")
fr_dom_countries_domain = [("code", "in", fr_dom_countries_codes)]
fr_dom_countries_sr = self.env["res.country"].search_read(
fr_dom_countries_domain, ["id"]
)
fr_dom_countries_ids = [country["id"] for country in fr_dom_countries_sr]
# Retrieve all available departments by normalized department zip code
department_sr = self.env["res.country.department"].search_read(
[("country_id", "in", fr_dom_countries_ids)], ["code"]
)
department_code2id = {dpt["code"]: dpt["id"] for dpt in department_sr}
# Duplicate from l10n_fr_department/model/res_partner.py **END**
return department_code2id, fr_dom_countries_ids

# TODO remove after PR 701 merged
# https://github.com/OCA/l10n-france/pull/701
# Duplicate from l10n_fr_department/model/res_partner.py **START**
@api.model
def _fr_zipcode_city_to_department_code(self, zipcode, city):
# https://fr.wikipedia.org/wiki/Liste_des_communes_de_France_dont_le_code_postal_ne_correspond_pas_au_d%C3%A9partement # noqa
zipcode = zipcode.replace(" ", "")
if len(zipcode) != 5:
return None
if city and zipcode in FR_SPECIAL_ZIPCODES:
city = unidecode(city).lower()
for city_keyword, dpt_code in FR_SPECIAL_ZIPCODES[zipcode].items():
if city_keyword in city:
return dpt_code
code = zipcode[0:2]
# La Réunion
if code == "97":
code = zipcode[0:3]
# Le Port
if code == "978":
code = "974"
elif code == "20":
try:
zipcode = int(zipcode)
except ValueError:
return "20"
if 20000 <= zipcode < 20200:
# Corse du Sud / 2A
code = "2A"
elif 20200 <= zipcode <= 20620:
code = "2B"
else:
code = "20"
return code

# Duplicate from l10n_fr_department/model/res_partner.py **END**
3 changes: 3 additions & 0 deletions l10n_fr_department_crm/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- Sylvain LE GAL ([Twitter](https://twitter.com/legalsylvain)), GRAP
(Groupement Régional Alimentaire de Proximité)
- Informatique Prog \<<[email protected]>\>
2 changes: 2 additions & 0 deletions l10n_fr_department_crm/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
This module adds a computed many2one *country_department_id* field on the
*crm_lead* object.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done with LLM ? ;-)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 62 additions & 0 deletions l10n_fr_department_crm/view/crm_lead.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_crm_case_opportunities_filter" model="ir.ui.view">
<field name="name">l10n_fr_department_crm.opportunities.search</field>
<field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.view_crm_case_opportunities_filter" />
<field name="arch" type="xml">
<filter name="country" position="after">
<filter
name="country_department_groupby"
context="{'group_by': 'country_department_id'}"
string="Department"
/>
</filter>
</field>
</record>

<record id="crm_case_tree_view_oppor" model="ir.ui.view">
<field name="name">l10n_fr_department_crm.opportunities.tree</field>
<field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.crm_case_tree_view_oppor" />
<field name="arch" type="xml">
<field name="state_id" position="before">
<field
name="country_department_id"
optional="hide"
string="Department"
/>
</field>
</field>
</record>

<record id="crm_lead_view_form" model="ir.ui.view">
<field name="name">l10n_fr_department_crm.department_leads.form</field>
<field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.crm_lead_view_form" />
<field name="arch" type="xml">
<xpath
expr="//page[@name='lead']//div[hasclass('o_address_format')]"
position="after"
>
<field
name="country_department_id"
groups="base.group_no_one"
invisible="not country_department_id"
string="Department"
/>
</xpath>
<xpath
expr="//group[@name='lead_partner']//div[hasclass('o_address_format')]"
position="after"
>
<field
name="country_department_id"
groups="base.group_no_one"
invisible="not country_department_id"
string="Department"
/>
</xpath>
</field>
</record>
</odoo>
Loading