Jump to content

Repo comfac-hrms CommonEdits: Difference between revisions

From Game in the Brain Wiki
"Repo analysis: comfac-hrms and comfac-webshop (14 pages)"
 
"Fix markdown code blocks → wikitext syntaxhighlight in Repo analysis pages"
 
Line 29: Line 29:
Create <code>hrms/setup.py</code> if not exists:
Create <code>hrms/setup.py</code> if not exists:


```python
<syntaxhighlight lang="python">
import frappe
import frappe


Line 55: Line 55:
                 }).insert()
                 }).insert()
     frappe.db.commit()
     frappe.db.commit()
```
</syntaxhighlight>


Add to <code>hooks.py</code>:
Add to <code>hooks.py</code>:


```python
<syntaxhighlight lang="python">
after_install = "hrms.setup.setup_custom_fields"
after_install = "hrms.setup.setup_custom_fields"
```
</syntaxhighlight>


=== How to Verify ===
=== How to Verify ===
Line 83: Line 83:
Find the <code>validate()</code> method or add to it:
Find the <code>validate()</code> method or add to it:


```python
<syntaxhighlight lang="python">
def validate(self):
def validate(self):
     # ... existing validation code ...
     # ... existing validation code ...
Line 92: Line 92:
             "Leave applications longer than 7 days require a leave approver."
             "Leave applications longer than 7 days require a leave approver."
         ))
         ))
```
</syntaxhighlight>


=== How to Verify ===
=== How to Verify ===
Line 116: Line 116:
'''negative_leave_balance.json:'''
'''negative_leave_balance.json:'''


```json
<syntaxhighlight lang="json">
{
{
  "add_total_row": 0,
  "add_total_row": 0,
Line 142: Line 142:
  ]
  ]
}
}
```
</syntaxhighlight>


'''negative_leave_balance.py:'''
'''negative_leave_balance.py:'''


```python
<syntaxhighlight lang="python">
import frappe
import frappe
from frappe import _
from frappe import _
Line 171: Line 171:
      
      
     return columns, data
     return columns, data
```
</syntaxhighlight>


=== How to Verify ===
=== How to Verify ===
Line 204: Line 204:
Edit print format HTML:
Edit print format HTML:


```html
<syntaxhighlight lang="html">
<div class="print-format">
<div class="print-format">
     <div class="row">
     <div class="row">
Line 217: Line 217:
     <!-- ... rest of format ... -->
     <!-- ... rest of format ... -->
</div>
</div>
```
</syntaxhighlight>


=== How to Verify ===
=== How to Verify ===
Line 259: Line 259:
Create <code>employee_training_record.json</code>:
Create <code>employee_training_record.json</code>:


```json
<syntaxhighlight lang="json">
{
{
  "actions": [],
  "actions": [],
Line 340: Line 340:
  "track_changes": 1
  "track_changes": 1
}
}
```
</syntaxhighlight>


Create <code>employee_training_record.py</code>:
Create <code>employee_training_record.py</code>:


```python
<syntaxhighlight lang="python">
import frappe
import frappe
from frappe.model.document import Document
from frappe.model.document import Document
Line 353: Line 353:
         if self.status == "Completed" and not self.certificate:
         if self.status == "Completed" and not self.certificate:
             frappe.msgprint(_("Warning: No certificate attached for completed training."))
             frappe.msgprint(_("Warning: No certificate attached for completed training."))
```
</syntaxhighlight>


=== How to Verify ===
=== How to Verify ===
Line 406: Line 406:
Add to <code>LeaveApplication</code> class:
Add to <code>LeaveApplication</code> class:


```python
<syntaxhighlight lang="python">
def before_insert(self):
def before_insert(self):
     """Auto-calculate leave balance before saving"""
     """Auto-calculate leave balance before saving"""
Line 422: Line 422:
      
      
     return result[0].balance or 0
     return result[0].balance or 0
```
</syntaxhighlight>


=== How to Verify ===
=== How to Verify ===
Line 450: Line 450:
Add to <code>hrms/overrides/employee_master.py</code>:
Add to <code>hrms/overrides/employee_master.py</code>:


```python
<syntaxhighlight lang="python">
def after_insert(self):
def after_insert(self):
     """Create webshop customer for new employees"""
     """Create webshop customer for new employees"""
Line 465: Line 465:
         # Link back to employee
         # Link back to employee
         self.db_set("webshop_customer", customer.name)
         self.db_set("webshop_customer", customer.name)
```
</syntaxhighlight>


Add to <code>hooks.py</code>:
Add to <code>hooks.py</code>:


```python
<syntaxhighlight lang="python">
doc_events = {
doc_events = {
     "Employee": {
     "Employee": {
Line 475: Line 475:
     }
     }
}
}
```
</syntaxhighlight>


=== How to Verify ===
=== How to Verify ===

Latest revision as of 16:40, 9 March 2026

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