Jump to content

Repo comfac-hrms CommonEdits

From Game in the Brain Wiki
Revision as of 16:40, 9 March 2026 by Ocjustin260223 (talk | contribs) ("Fix markdown code blocks → wikitext syntaxhighlight in Repo analysis pages")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Repo:comfac-hrms/CommonEdits — Common Edit Scenarios

This page contains practical recipes for common customization tasks.

Scenario 1: Adding a New Field to a Form

Goal

Add "Emergency Contact Phone" field to the Employee form.

Files to Touch

  1. hrms/setup.py OR Frappe Desk UI
  2. (Optional) hrms/overrides/employee_master.py for validation

Method A: Via Frappe Desk (Quickest)

  1. Log in as Administrator
  2. Go to Employee list
  3. Click MenuCustomize
  4. In "Customize Form", scroll to bottom
  5. Click Add Row:
    • Label: Emergency Contact Phone
    • Fieldtype: Data
    • Options: Phone
    • Insert After: emergency_contact_name
  6. Click Update

Method B: Via Code (Reproducible)

Create hrms/setup.py if not exists:

import frappe

def setup_custom_fields():
    custom_fields = {
        "Employee": [
            {
                "fieldname": "custom_emergency_phone",
                "label": "Emergency Contact Phone",
                "fieldtype": "Data",
                "options": "Phone",
                "insert_after": "emergency_contact_name",
                "translatable": 0
            }
        ]
    }
    
    for doctype, fields in custom_fields.items():
        for field in fields:
            if not frappe.db.exists("Custom Field", f"{doctype}-{field['fieldname']}"):
                frappe.get_doc({
                    "doctype": "Custom Field",
                    "dt": doctype,
                    **field
                }).insert()
    frappe.db.commit()

Add to hooks.py:

after_install = "hrms.setup.setup_custom_fields"

How to Verify

  1. Go to Employee form
  2. Check if new field appears after "Emergency Contact Name"
  3. Create test employee, fill in emergency phone
  4. Verify it saves and appears in list view if configured

---

Scenario 2: Changing Validation Logic

Goal

Require manager approval for leave applications longer than 7 days.

Files to Touch

  1. hrms/hr/doctype/leave_application/leave_application.py

What to Change

Find the validate() method or add to it:

def validate(self):
    # ... existing validation code ...
    
    # Custom validation
    if self.total_leave_days > 7 and not self.leave_approver:
        frappe.throw(_(
            "Leave applications longer than 7 days require a leave approver."
        ))

How to Verify

  1. Create leave application for 10 days
  2. Try to submit without selecting approver
  3. Should get error message
  4. Select approver, submit should work

---

Scenario 3: Adding a New Report

Goal

Create a report showing employees with negative leave balances.

Files to Touch

  1. hrms/hr/report/negative_leave_balance/ (new directory)
  2. hrms/hr/report/negative_leave_balance/negative_leave_balance.json
  3. hrms/hr/report/negative_leave_balance/negative_leave_balance.py

What to Create

negative_leave_balance.json:

{
 "add_total_row": 0,
 "columns": [],
 "creation": "2024-01-01 00:00:00.000000",
 "disable_prepared_report": 0,
 "disabled": 0,
 "docstatus": 0,
 "doctype": "Report",
 "filters": [],
 "idx": 0,
 "is_standard": "Yes",
 "modified": "2024-01-01 00:00:00.000000",
 "modified_by": "Administrator",
 "module": "HR",
 "name": "Negative Leave Balance",
 "owner": "Administrator",
 "prepared_report": 0,
 "ref_doctype": "Leave Ledger Entry",
 "report_name": "Negative Leave Balance",
 "report_type": "Script Report",
 "roles": [
  {"role": "HR Manager"},
  {"role": "HR User"}
 ]
}

negative_leave_balance.py:

import frappe
from frappe import _

def execute(filters=None):
    columns = [
        {"label": _("Employee"), "fieldname": "employee", "fieldtype": "Link", "options": "Employee", "width": 150},
        {"label": _("Employee Name"), "fieldname": "employee_name", "fieldtype": "Data", "width": 200},
        {"label": _("Leave Type"), "fieldname": "leave_type", "fieldtype": "Link", "options": "Leave Type", "width": 150},
        {"label": _("Balance"), "fieldname": "balance", "fieldtype": "Float", "width": 120},
    ]
    
    data = frappe.db.sql("""
        SELECT 
            employee,
            (SELECT employee_name FROM tabEmployee e WHERE e.name = lle.employee) as employee_name,
            leave_type,
            SUM(leaves) as balance
        FROM `tabLeave Ledger Entry` lle
        GROUP BY employee, leave_type
        HAVING balance < 0
        ORDER BY balance ASC
    """, as_dict=True)
    
    return columns, data

How to Verify

  1. Run bench migrate
  2. Go to HR module → Reports
  3. Look for "Negative Leave Balance"
  4. Run report, verify data

---

Scenario 4: Modifying a Print Format

Goal

Add company logo to Salary Slip print format.

Files to Touch

  1. Frappe Desk UI (Print Format Builder)

What to Change

  1. Go to PayrollSalary Slip
  2. Open any salary slip
  3. Click PrintCustomize
  4. In Print Format Builder:
  5. Save as new print format or update standard

Alternative: Raw HTML

Edit print format HTML:

<div class="print-format">
    <div class="row">
        <div class="col-xs-6">
            <img src="{{ frappe.db.get_value('Company', doc.company, 'company_logo') }}" style="max-height: 60px;">
        </div>
        <div class="col-xs-6 text-right">
            <h2>Salary Slip</h2>
            <p>{{ doc.name }}</p>
        </div>
    </div>
    <!-- ... rest of format ... -->
</div>

How to Verify

  1. Open any salary slip
  2. Click Print
  3. Verify logo appears in preview
  4. Check PDF export

---

Scenario 5: Adding a New DocType

Goal

Create "Employee Training Record" DocType to track completed trainings.

Files to Touch

  1. hrms/hr/doctype/employee_training_record/ (new directory)
  2. hrms/hr/doctype/employee_training_record/employee_training_record.json
  3. hrms/hr/doctype/employee_training_record/employee_training_record.py

What to Create

Option A: Via Frappe Desk (Recommended)

  1. Go to DocType List (search in awesome bar)
  2. Click New
  3. Fill:
    • Name: Employee Training Record
    • Module: HR
    • Fields:
      • Employee (Link → Employee)
      • Training Name (Data)
      • Training Date (Date)
      • Trainer (Data)
      • Certificate (Attach)
      • Status (Select: Completed/In Progress/Planned)
  4. Save, then bench export-fixtures OR manually create files

Option B: Manual JSON

Create employee_training_record.json:

{
 "actions": [],
 "autoname": "format:TRN-{YYYY}-{#####}",
 "creation": "2024-01-01 00:00:00.000000",
 "doctype": "DocType",
 "engine": "InnoDB",
 "fields": [
  {
   "fieldname": "employee",
   "fieldtype": "Link",
   "label": "Employee",
   "options": "Employee",
   "reqd": 1
  },
  {
   "fieldname": "training_name",
   "fieldtype": "Data",
   "label": "Training Name",
   "reqd": 1
  },
  {
   "fieldname": "training_date",
   "fieldtype": "Date",
   "label": "Training Date"
  },
  {
   "fieldname": "trainer",
   "fieldtype": "Data",
   "label": "Trainer"
  },
  {
   "fieldname": "certificate",
   "fieldtype": "Attach",
   "label": "Certificate"
  },
  {
   "fieldname": "status",
   "fieldtype": "Select",
   "label": "Status",
   "options": "Planned\nIn Progress\nCompleted"
  }
 ],
 "index_web_pages_for_search": 1,
 "links": [],
 "modified": "2024-01-01 00:00:00.000000",
 "module": "HR",
 "name": "Employee Training Record",
 "naming_rule": "Expression",
 "owner": "Administrator",
 "permissions": [
  {
   "create": 1,
   "delete": 1,
   "email": 1,
   "export": 1,
   "print": 1,
   "read": 1,
   "report": 1,
   "role": "HR Manager",
   "share": 1,
   "write": 1
  },
  {
   "create": 1,
   "delete": 0,
   "email": 1,
   "export": 1,
   "print": 1,
   "read": 1,
   "report": 1,
   "role": "HR User",
   "share": 1,
   "write": 1
  }
 ],
 "sort_field": "modified",
 "sort_order": "DESC",
 "states": [],
 "track_changes": 1
}

Create employee_training_record.py:

import frappe
from frappe.model.document import Document

class EmployeeTrainingRecord(Document):
    def validate(self):
        # Custom validation
        if self.status == "Completed" and not self.certificate:
            frappe.msgprint(_("Warning: No certificate attached for completed training."))

How to Verify

  1. Run bench migrate
  2. Go to HR module
  3. Search for "Employee Training Record"
  4. Create test record
  5. Verify validations work

---

Scenario 6: Changing a Workflow

Goal

Add "Pending HR Review" step to Leave Application workflow.

Files to Touch

  1. Frappe Desk → Workflow List

What to Change

  1. Go to Workflow list
  2. Open Leave Application
  3. Add new state:
    • State: Pending HR Review
    • Update Field: Status
    • Update Value: Pending HR Review
  4. Add new transitions:
    • From: Open → To: Pending HR Review, Action: Send to HR, Condition: doc.total_leave_days > 5
    • From: Pending HR Review → To: Approved, Action: HR Approve
    • From: Pending HR Review → To: Rejected, Action: HR Reject
  5. Save

How to Verify

  1. Create leave application > 5 days
  2. Submit, should go to "Pending HR Review"
  3. Verify HR Manager can approve/reject
  4. Check that shorter leaves skip this step

---

Scenario 7: Adding a Server Script

Goal

Auto-calculate leave balance when leave application is created.

Files to Touch

  1. hrms/hr/doctype/leave_application/leave_application.py

What to Change

Add to LeaveApplication class:

def before_insert(self):
    """Auto-calculate leave balance before saving"""
    if self.employee and self.leave_type:
        balance = get_leave_balance(self.employee, self.leave_type, self.from_date)
        self.leave_balance = balance

def get_leave_balance(employee, leave_type, date):
    """Helper function to calculate balance"""
    result = frappe.db.sql("""
        SELECT SUM(leaves) as balance 
        FROM `tabLeave Ledger Entry`
        WHERE employee = %s AND leave_type = %s AND transaction_date <= %s
    """, (employee, leave_type, date), as_dict=True)
    
    return result[0].balance or 0

How to Verify

  1. Create new leave application
  2. Select employee and leave type
  3. Leave Balance field should auto-populate

---

Scenario 8: Connecting to Another App

Goal

Link Employee to comfac-webshop Customer account.

Files to Touch

  1. Custom field in Employee (via desk or code)
  2. (Optional) hrms/overrides/employee_master.py

What to Change

Add custom field to Employee:

  • Fieldname: webshop_customer
  • Label: Webshop Customer Account
  • Fieldtype: Link
  • Options: Customer (from ERPNext)

Add to hrms/overrides/employee_master.py:

def after_insert(self):
    """Create webshop customer for new employees"""
    if not self.webshop_customer:
        customer = frappe.get_doc({
            "doctype": "Customer",
            "customer_name": self.employee_name,
            "customer_type": "Individual",
            "customer_group": "Employee",
            "territory": "Philippines"
        })
        customer.insert(ignore_permissions=True)
        
        # Link back to employee
        self.db_set("webshop_customer", customer.name)

Add to hooks.py:

doc_events = {
    "Employee": {
        "after_insert": "hrms.overrides.employee_master.after_insert"
    }
}

How to Verify

  1. Create new employee
  2. Check if Customer is auto-created
  3. Verify link appears in Employee form
  4. Check customer appears in ERPNext Sales module