feat(vhrms): TETRA-1420 defensive Custom Field column sync on migrate #60
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/TETRA-1420-defensive-custom-field-db-sync"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Jira
TETRA-1420 — Defensive sync Custom Fields → DB columns trên mỗi
bench migrateVấn đề
Operational drift:
tabCustom Fieldrow tồn tại nhưng column thật chưa được tạo trêntab*table. Xảy ra khi:bench migratebị interrupted giữa Custom Field insert và ALTER TABLE step.on_updatekhông fire.Symptom local repro 2026-04-22:
seed_employeesPhase A fail vớiMySQLdb.OperationalError (1054, Unknown column custom_gross_salary in INSERT INTO), rồicustom_is_pregnant,custom_pregnancy_status. Cùng class risk áp dụng choSalary Component.custom_is_pay_insurance(TETRA-1407, v0.6.0).Cách tiếp cận
Thêm
sync_custom_field_columns()vàovhrms/setup/custom_fields.py, gọi từ cảafter_install+after_migratengay saucreate_custom_fields(...):get_custom_fields()(chỉ vhrms-declared fields, không police custom fields của app khác).tabCustom Fieldrow tồn tại + DB column thiếu → gom theo doctype, gọifrappe.db.updatedb(doctype)1 lần per doctype.updatedbchính là API mà Frappe coreCustom Field.on_updatedùng — không raw ALTER TABLE, tôn trọng type mapping + default value + metadata tracking (AC #5).Edge cases handled
frappe.db.has_columnraiseTableMissingErrorcho Single doctype (vd Vietnam HRMS Settings, Payroll Settings) → fallbackfrappe.get_meta(doctype).has_field(). Singleton lưu giá trị ởtabSinglesnên không có DDL drift cần khôi phục.has_column.Asset Assignment) — skip gracefully, không raise.create_custom_fieldsở step trước đã handle).AC checklist
sync_custom_field_columns()ởvhrms/setup/custom_fields.py(AC #1).after_migrate+after_installngay saucreate_custom_fields()(AC #2).frappe.db.updatedb(AC #5).Test plan
Unit test
vhrms/tests/test_ensure_columns.py(7 cases, all passing):test_missing_column_triggers_updatedb— Custom Field row + column thiếu →updatedbfires.test_idempotent_when_columns_present— column đã có → KHÔNG gọiupdatedb.test_layout_fieldtypes_skipped— Section Break / Column Break / Tab Break không có DB column → bỏ qua.test_skip_when_custom_field_row_missing—tabCustom Fieldrow chưa tồn tại → defensive sync no-op cho field đó.test_singleton_doctype_falls_back_to_meta—has_columnraise TableMissingError → fallbackmeta.has_field, KHÔNG re-raise.test_doctype_not_installed_is_skipped— DocType không có trên site → skip, không raise.test_multiple_missing_columns_one_updatedb_per_doctype— N column drift trên cùng doctype → vẫn chỉ 1 ALTER pass.Deviations from Jira description
Jira pseudocode references
frappe.custom.doctype.custom_field.custom_field.create_custom_field_in_db— function này không tồn tại trong Frappe v16 vendor checkout (vendor/frappe/.../custom_field.pychỉ cócreate_custom_field,create_custom_fields,create_custom_field_if_values_exist). Column creation được trigger quafrappe.db.updatedb(doctype)trongCustom Field.on_updatehook (line 216) — và đó cũng là API màcreate_custom_fields(line 379) gọi sau khi insert tất cả Custom Field rows. Ticket spirit là "đi qua Frappe API thay vì raw ALTER" —updatedbđáp ứng đúng intent.Version
Bump 0.9.4 → 0.9.5 (MINOR — feature add per semver).
Files
vhrms/__init__.py— version bumpvhrms/setup/custom_fields.py— thêmsync_custom_field_columns+_column_exists+_LAYOUT_FIELDTYPESvhrms/setup/__init__.py— wire vào after_install + after_migrate, expose qua__all__vhrms/tests/test_ensure_columns.py— 7 test casesCHANGELOG.md— Unreleased entry