Jump to content

Repo comfac-hrms ExtensionPoints

From Game in the Brain Wiki
Revision as of 16:33, 9 March 2026 by Ocjustin260223 (talk | contribs) ("Repo analysis: comfac-hrms and comfac-webshop (14 pages)")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Repo:comfac-hrms/ExtensionPoints — Extension & Customization

The Safe Edit Zone

Frappe Override Pattern

Frappe HR uses a "don't touch core, override instead" pattern. The safe places to customize are:

Location What to Put There
hrms/overrides/ Python classes that extend/override ERPNext behavior
hrms/public/js/erpnext/ Client scripts for ERPNext DocTypes
hrms/hooks.py Hook registrations (add your functions here)
Custom Fields (via UI) Non-core fields added through Frappe desk
hrms/patches/ Migration scripts for data changes

Files You Should NOT Edit Directly

  • ERPNext core files (in separate repo)
  • Frappe framework files
  • Generated files in public/dist/
  • Third-party libraries in node_modules/

Hooks & Events

How Hooks Work

The hrms/hooks.py file registers event handlers. When a document event fires, Frappe calls the registered functions.

Key Hook Categories

Hook Type Purpose Example in comfac-hrms
doctype_js Attach client scripts to forms Employee, Timesheet, Company
override_doctype_class Replace Python controller class EmployeeMaster, EmployeeTimesheet
doc_events Server-side lifecycle hooks Validate, on_update, on_submit
scheduler_events Background job scheduling Daily reminders, hourly attendance
jinja Template helper functions hrms.utils.get_country

Document Event Hooks

Current doc_events from hooks.py:

DocType Event Handler Function What It Does
User validate hrms.overrides.employee_master.update_approver_user_roles Sync approver roles
Company validate hrms.overrides.company.validate_default_accounts Validate HR accounts
Company on_update hrms.overrides.company.make_company_fixtures Create default data
Timesheet validate hrms.hr.utils.validate_active_employee Check employee status
Payment Entry on_submit hrms.hr.doctype.expense_claim...update_payment_for_expense_claim Update expense claim
Journal Entry on_submit hrms.payroll.doctype.salary_slip...unlink_ref_doc_from_salary_slip Payroll cleanup
Employee validate hrms.overrides.employee_master.validate_onboarding_process Check onboarding
Employee on_update hrms.overrides.employee_master.update_approver_role Update roles
Project validate hrms.controllers.employee_boarding_controller.update_employee_boarding_status Boarding status

Scheduled Events

Frequency Jobs Purpose
all (every few minutes) Interview reminders Send upcoming interview emails
hourly Daily work summary emails Trigger group emails
hourly_long Shift attendance processing Auto check-in/check-out
daily Birthday reminders, work anniversaries, job closing Daily HR tasks
daily_long Leave expiry, earned leave allocation, gratuity Heavy daily tasks
weekly Advance reminder notifications Weekly reminders
monthly Monthly advance reminders Monthly notifications

Override Patterns

Pattern 1: Override a DocType Class

Goal: Change how Employee documents are processed

Current implementation (in hrms/overrides/employee_master.py):

```python from erpnext.setup.doctype.employee.employee import Employee

class EmployeeMaster(Employee):

   def autoname(self):
       # Custom naming logic
       naming_method = frappe.db.get_single_value("HR Settings", "emp_created_by")
       # ... implementation
   
   def validate(self):
       super().validate()  # Call parent
       # Add custom validation here

```

Registration (in hooks.py):

```python override_doctype_class = {

   "Employee": "hrms.overrides.employee_master.EmployeeMaster",

} ```

Pattern 2: Add a Document Event Handler

Goal: Run custom code when a Leave Application is submitted

Steps:

  1. Create function in hrms/hr/utils.py:

```python def my_custom_leave_handler(doc, method=None):

   """doc is the Leave Application document"""
   if doc.status == "Approved":
       frappe.sendmail(
           recipients=[doc.employee],
           subject="Your leave was approved!",
           message=f"Your leave from {doc.from_date} to {doc.to_date} was approved."
       )

```

  1. Register in hooks.py:

```python doc_events = {

   "Leave Application": {
       "on_submit": "hrms.hr.utils.my_custom_leave_handler"
   }

} ```

Pattern 3: Add a Client Script

Goal: Show/hide fields based on user input

Create file hrms/public/js/custom_leave_form.js:

```javascript frappe.ui.form.on('Leave Application', {

   leave_type: function(frm) {
       // Show medical certificate field for sick leave
       frm.toggle_display('medical_certificate', 
           frm.doc.leave_type === 'Sick Leave');
   }

}); ```

Register in hooks.py:

```python doctype_js = {

   "Leave Application": "public/js/custom_leave_form.js",

} ```

Pattern 4: Custom Field vs Forking

Approach When to Use How
Custom Field (UI) Simple fields, no code Frappe Desk → Customize Form → Add Field
Custom Field (Code) Fields that need to be in git setup.py or fixtures
Edit DocType JSON Core app changes Edit */doctype/*/*.json (requires migrate)
Override controller Change behavior Python override class

Patch System

Where Patches Live

hrms/patches/ organized by version:

  • v14_0/
  • v15_0/
  • v16_0/

How to Write a Migration Patch

Example: Create file hrms/patches/v16_0/add_new_leave_field.py:

```python import frappe

def execute():

   """Add custom field to Leave Application"""
   if not frappe.db.exists("Custom Field", "Leave Application-custom_reason_code"):
       frappe.get_doc({
           "doctype": "Custom Field",
           "dt": "Leave Application",
           "fieldname": "custom_reason_code",
           "label": "Reason Code",
           "fieldtype": "Select",
           "options": "Personal\nMedical\nFamily",
           "insert_after": "leave_type"
       }).insert()

```

Register in patches.txt:

``` hrms.patches.v16_0.add_new_leave_field ```

When You Need a Patch

Scenario Solution
Adding a new field to existing DocType Patch + bench migrate
Modifying existing data Patch with frappe.db.sql() or frappe.get_doc()
Creating new DocTypes Just add files, bench migrate syncs automatically
Changing field properties Patch or edit JSON + migrate

Adding New API Endpoints

Whitelisted Methods

Add to any controller (e.g., hrms/hr/doctype/leave_application/leave_application.py):

```python import frappe from frappe import _

class LeaveApplication(Document):

   # ... existing code ...
   
   @frappe.whitelist()
   def get_leave_balance(self):
       """API endpoint to get current leave balance"""
       return frappe.get_all("Leave Ledger Entry",
           filters={
               "employee": self.employee,
               "leave_type": self.leave_type
           },
           fields=["sum(leaves) as balance"]
       )[0].balance or 0

```

Call from client:

```javascript frappe.call({

   method: 'hrms.hr.doctype.leave_application.leave_application.LeaveApplication.get_leave_balance',
   args: { docname: frm.doc.name },
   callback: function(r) {
       console.log('Balance:', r.message);
   }

}); ```

Regional Overrides

The app supports region-specific logic:

```python regional_overrides = {

   "India": {
       "hrms.hr.utils.calculate_annual_eligible_hra_exemption": 
           "hrms.regional.india.utils.calculate_annual_eligible_hra_exemption",
   },

} ```

To add ComFac-specific overrides, create hrms/regional/philippines/ and register functions.