fix(regional): rename Italy's duplicate Customer name fields

The Italy regional setup created Custom Fields first_name/last_name on
Customer. Since #46281 added standard quick-entry fields with the same
names, every Italian site carries duplicate field definitions:

- the setup wizard creates the duplicates silently because it skips
  validation, and any later Custom Field on Customer then raises
  UniqueFieldnameError (#50915)
- without the duplicates, creating an Italian company aborts inside
  install_country_fixtures; on MariaDB an interrupted fixture run
  persists Custom Field documents whose columns were never added, after
  which every Company insert fails with "Unknown column
  'fiscal_regime'" (#57215)

Re-land the rename from #50921 (reverted in #53409): the fields become
italy_customer_first_name/italy_customer_last_name and the e-invoice
template reads the new names. The migration patch runs only on sites
with Italy fixtures, re-runs them, explicitly syncs the schema of every
affected doctype (create_custom_fields skips unchanged fields, so its
own schema sync cannot restore missing columns), copies the old column
values wherever the new field is empty (also on sites that removed the
duplicate fields with the documented manual workaround), and deletes
the duplicate Custom Fields last so an interrupted run stays resumable.

The old insert_after anchor "salutation" no longer exists on Customer;
the renamed fields anchor after customer_type.
This commit is contained in:
Mihir Kandoi
2026-08-09 16:20:32 +05:30
parent 691c341edd
commit 110d0a38a6
4 changed files with 65 additions and 9 deletions

View File

@@ -509,3 +509,4 @@ erpnext.patches.v16_0.backfill_repost_accounting_ledger_status
erpnext.patches.v16_0.merge_seeded_item_group_root
erpnext.patches.v16_0.set_stock_uom_in_job_card
erpnext.patches.v16_0.set_work_order_requested_and_picked_qty
erpnext.patches.v16_0.rename_italy_customer_name_fields

View File

@@ -0,0 +1,53 @@
import frappe
RENAMED_FIELDS = {
"first_name": "italy_customer_first_name",
"last_name": "italy_customer_last_name",
}
def execute():
"""Rename Italy's Customer name fields, which clash with the standard quick-entry
first_name/last_name fields, and restore any Italy custom field columns that a
previously interrupted fixture run left missing."""
if not has_italy_fixtures():
return
duplicate_fieldnames = [
fieldname for fieldname in RENAMED_FIELDS if frappe.db.exists("Custom Field", f"Customer-{fieldname}")
]
from erpnext.regional.italy.setup import get_custom_fields, make_custom_fields
make_custom_fields()
for doctype in get_custom_fields():
frappe.clear_cache(doctype=doctype)
frappe.db.updatedb(doctype)
for old_fieldname, new_fieldname in RENAMED_FIELDS.items():
copy_customer_names(old_fieldname, new_fieldname)
for old_fieldname in duplicate_fieldnames:
frappe.delete_doc("Custom Field", f"Customer-{old_fieldname}", force=True)
if duplicate_fieldnames:
frappe.clear_cache(doctype="Customer")
def has_italy_fixtures():
return bool(
frappe.db.exists("Company", {"country": "Italy"})
or frappe.db.exists("Custom Field", "Company-fiscal_regime")
)
def copy_customer_names(old_fieldname, new_fieldname):
customer = frappe.qb.DocType("Customer")
old_column = customer[old_fieldname]
new_column = customer[new_fieldname]
(
frappe.qb.update(customer)
.set(new_column, old_column)
.where(old_column.isnotnull() & (old_column != ""))
.where(new_column.isnull() | (new_column == ""))
).run()

View File

@@ -99,8 +99,8 @@
{%- if doc.customer_data.customer_type == "Individual" %}
<CodiceFiscale>{{ doc.customer_data.fiscal_code }}</CodiceFiscale>
<Anagrafica>
<Nome>{{ doc.customer_data.first_name }}</Nome>
<Cognome>{{ doc.customer_data.last_name }}</Cognome>
<Nome>{{ doc.customer_data.italy_customer_first_name }}</Nome>
<Cognome>{{ doc.customer_data.italy_customer_last_name }}</Cognome>
</Anagrafica>
{%- else %}
<IdFiscaleIVA>

View File

@@ -23,6 +23,10 @@ def setup(company=None, patch=True):
def make_custom_fields(update=True):
create_custom_fields(get_custom_fields(), ignore_validate=frappe.flags.in_patch, update=update)
def get_custom_fields():
invoice_item_fields = [
dict(
fieldname="tax_rate",
@@ -96,7 +100,7 @@ def make_custom_fields(update=True):
),
]
custom_fields = {
return {
"Company": [
dict(
fieldname="sb_e_invoicing",
@@ -232,18 +236,18 @@ def make_custom_fields(update=True):
depends_on='eval:doc.customer_type=="Company"',
),
dict(
fieldname="first_name",
fieldname="italy_customer_first_name",
label="First Name",
fieldtype="Data",
insert_after="salutation",
insert_after="customer_type",
print_hide=1,
depends_on='eval:doc.customer_type!="Company"',
),
dict(
fieldname="last_name",
fieldname="italy_customer_last_name",
label="Last Name",
fieldtype="Data",
insert_after="first_name",
insert_after="italy_customer_first_name",
print_hide=1,
depends_on='eval:doc.customer_type!="Company"',
),
@@ -461,8 +465,6 @@ def make_custom_fields(update=True):
],
}
create_custom_fields(custom_fields, ignore_validate=frappe.flags.in_patch, update=update)
def setup_report():
report_name = "Electronic Invoice Register"