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:
parent
f7d283cdf1
commit
0a0ef3d032
|
|
@ -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
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.")
|
||||||
|
|
@ -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()
|
||||||
Loading…
Reference in New Issue