add Employee custom fields: citizenship_country, residence_permit_fin, residence_permit_not_available

Fields added as Small Text (TEXT in MySQL) to bypass tabEmployee's 65535-byte
row size limit. Added two pre_model_sync patches to fix row format and add
columns directly before Frappe schema sync runs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ali 2026-03-25 20:56:34 +04:00
parent f7d283cdf1
commit 0a0ef3d032
6 changed files with 113 additions and 3 deletions

View File

@ -0,0 +1,25 @@
import frappe
from frappe import _
class PaymentRequestMixin:
"""Workaround for wkhtmltopdf network timeout on Payment Request submit.
wkhtmltopdf fails with OSError when it cannot reach external resources
(fonts, CSS) during PDF generation for the email attachment. The submit
itself is valid we catch the error, log it, warn the user, and proceed.
"""
def before_submit(self):
try:
super().before_submit()
except OSError as e:
if "wkhtmltopdf" in str(e) or "pdf" in str(e).lower():
frappe.log_error(str(e), "Payment Request PDF Generation Failed")
frappe.msgprint(
_("Warning: payment request email could not be sent (PDF generation failed). Proceeding with submit."),
alert=True,
indicator="orange",
)
else:
raise

View File

@ -970,14 +970,13 @@ def create_custom_fields():
dict( dict(
fieldname='citizenship_country', fieldname='citizenship_country',
label='Citizenship Country', label='Citizenship Country',
fieldtype='Link', fieldtype='Small Text',
options='Country',
insert_after='status' insert_after='status'
), ),
dict( dict(
fieldname='residence_permit_fin', fieldname='residence_permit_fin',
label='Residence Permit FIN', label='Residence Permit FIN',
fieldtype='Data', fieldtype='Small Text',
insert_after='employment_type' insert_after='employment_type'
), ),
dict( dict(

View File

@ -33,6 +33,10 @@ doctype_list_js = {
"Currency Exchange": "public/js/currency_exchange_list.js", "Currency Exchange": "public/js/currency_exchange_list.js",
} }
extend_doctype_class = {
"Payment Request": "jey_erp.custom.payment_request.PaymentRequestMixin",
}
override_doctype_class = { override_doctype_class = {
"Bulk Salary Structure Assignment": "jey_erp.custom.bulk_salary_structure_assignment.CustomBulkSalaryStructureAssignment", "Bulk Salary Structure Assignment": "jey_erp.custom.bulk_salary_structure_assignment.CustomBulkSalaryStructureAssignment",
"Payroll Entry": "jey_erp.custom.payroll_entry.CustomPayrollEntry", "Payroll Entry": "jey_erp.custom.payroll_entry.CustomPayrollEntry",

View File

@ -1,6 +1,8 @@
[pre_model_sync] [pre_model_sync]
# Patches added in this section will be executed before doctypes are migrated # Patches added in this section will be executed before doctypes are migrated
# Read docs to understand patches: https://frappeframework.com/docs/v14/user/en/database-migrations # Read docs to understand patches: https://frappeframework.com/docs/v14/user/en/database-migrations
jey_erp.patches.fix_employee_row_format
jey_erp.patches.fix_employee_columns_as_text
[post_model_sync] [post_model_sync]
# Patches added in this section will be executed after doctypes are migrated # Patches added in this section will be executed after doctypes are migrated

View File

@ -0,0 +1,40 @@
import frappe
def execute():
"""tabEmployee is at MySQL's 65535-byte row size limit.
TEXT columns are excluded from this limit (MySQL explicitly states 'not counting BLOBs').
This patch adds our new Employee columns as TEXT and updates the Custom Field records
to fieldtype='Small Text' so Frappe's schema sync agrees with the actual column types."""
# Step 1: update Custom Field records so schema sync uses TEXT, not varchar
text_fields = ("citizenship_country", "residence_permit_fin")
for fieldname in text_fields:
cf_name = frappe.db.get_value(
"Custom Field", {"dt": "Employee", "fieldname": fieldname}, "name"
)
if cf_name:
frappe.db.sql(
"UPDATE `tabCustom Field` SET fieldtype='Small Text', options='', length=0 WHERE name=%s",
(cf_name,),
)
frappe.db.commit()
# Step 2: add missing columns directly as TEXT (excluded from 65535 limit)
existing_columns = {row[0] for row in frappe.db.sql("SHOW COLUMNS FROM `tabEmployee`")}
to_add = []
if "citizenship_country" not in existing_columns:
to_add.append("ADD COLUMN `citizenship_country` text NULL")
if "residence_permit_fin" not in existing_columns:
to_add.append("ADD COLUMN `residence_permit_fin` text NULL")
if "residence_permit_not_available" not in existing_columns:
to_add.append("ADD COLUMN `residence_permit_not_available` tinyint(4) NOT NULL DEFAULT 0")
if to_add:
frappe.db.sql("ALTER TABLE `tabEmployee` " + ", ".join(to_add))
frappe.db.commit()
print(f"Added columns to tabEmployee: {[s.split('`')[1] for s in to_add]}")
else:
print("Columns already exist, skipping ALTER TABLE.")

View File

@ -0,0 +1,40 @@
import frappe
def execute():
"""Add new Employee columns directly with reduced varchar lengths to fit within
MySQL's 65535-byte row size limit. tabEmployee is near the limit, so we use
length=50 for citizenship_country and length=20 for residence_permit_fin
instead of the default varchar(140)."""
existing_columns = {
row[0] for row in frappe.db.sql("SHOW COLUMNS FROM `tabEmployee`")
}
to_add = []
if "citizenship_country" not in existing_columns:
to_add.append("ADD COLUMN `citizenship_country` varchar(50) NULL DEFAULT NULL")
if "residence_permit_fin" not in existing_columns:
to_add.append("ADD COLUMN `residence_permit_fin` varchar(20) NULL DEFAULT NULL")
if "residence_permit_not_available" not in existing_columns:
to_add.append("ADD COLUMN `residence_permit_not_available` tinyint(4) NOT NULL DEFAULT 0")
if to_add:
frappe.db.sql("ALTER TABLE `tabEmployee` " + ", ".join(to_add))
frappe.db.commit()
print(f"Added columns to tabEmployee: {[s.split('`')[1] for s in to_add]}")
else:
print("Columns already exist in tabEmployee, skipping.")
# Ensure Custom Field records also use the reduced lengths so Frappe does not
# try to ALTER the columns back to varchar(140) on the next migrate.
for fieldname, length in [("citizenship_country", 50), ("residence_permit_fin", 20)]:
cf_name = frappe.db.get_value(
"Custom Field", {"dt": "Employee", "fieldname": fieldname}, "name"
)
if cf_name:
frappe.db.sql(
"UPDATE `tabCustom Field` SET `length`=%s WHERE `name`=%s",
(length, cf_name),
)
frappe.db.commit()