From f1234d9b615812bfc3335bac0b2cdb9a44951c7e Mon Sep 17 00:00:00 2001 From: anjusha Date: Sat, 22 Mar 2025 17:14:28 +0530 Subject: [PATCH 01/40] feat: cost center filter --- .../department_cost_center/__init__.py | 0 .../department_cost_center.json | 33 +++++++ .../department_cost_center.py | 9 ++ beams/beams/overrides/queries.py | 91 +++++++++++++++++++ beams/hooks.py | 4 + beams/setup.py | 17 +++- 6 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 beams/beams/doctype/department_cost_center/__init__.py create mode 100644 beams/beams/doctype/department_cost_center/department_cost_center.json create mode 100644 beams/beams/doctype/department_cost_center/department_cost_center.py create mode 100644 beams/beams/overrides/queries.py diff --git a/beams/beams/doctype/department_cost_center/__init__.py b/beams/beams/doctype/department_cost_center/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/beams/beams/doctype/department_cost_center/department_cost_center.json b/beams/beams/doctype/department_cost_center/department_cost_center.json new file mode 100644 index 000000000..1065541eb --- /dev/null +++ b/beams/beams/doctype/department_cost_center/department_cost_center.json @@ -0,0 +1,33 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2025-03-22 15:52:56.514541", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "cost_center" + ], + "fields": [ + { + "fieldname": "cost_center", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Cost Center", + "options": "Cost Center" + } + ], + "grid_page_length": 50, + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-03-22 15:56:38.422227", + "modified_by": "Administrator", + "module": "BEAMS", + "name": "Department Cost Center", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/beams/beams/doctype/department_cost_center/department_cost_center.py b/beams/beams/doctype/department_cost_center/department_cost_center.py new file mode 100644 index 000000000..f464043ee --- /dev/null +++ b/beams/beams/doctype/department_cost_center/department_cost_center.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, efeone and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class DepartmentCostCenter(Document): + pass diff --git a/beams/beams/overrides/queries.py b/beams/beams/overrides/queries.py new file mode 100644 index 000000000..3954fd887 --- /dev/null +++ b/beams/beams/overrides/queries.py @@ -0,0 +1,91 @@ +import frappe +from erpnext.controllers.queries import get_fields +from frappe.desk.reportview import get_filters_cond, get_match_cond +from frappe.desk.reportview import build_match_conditions, get_filters_cond + +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def get_cost_center_list(doctype, txt, searchfield, start, page_len, filters=None, as_dict=False): + from erpnext.controllers.queries import get_fields + conditions = [] + fields = ["name"] + + fields = get_fields("Cost Center", fields) + + searchfields = frappe.get_meta(doctype).get_search_fields() + searchfields = " or ".join(field + " like %(txt)s" for field in searchfields) + + bureau_user = False + department_user = False + departments = False + cost_center_filter = False + + # Get permitted bureaus + bureau_list = frappe.db.get_all( + 'User Permission', + filters = { + 'user': frappe.session.user, + 'allow': 'Bureau' + }, + fields = ['for_value'] + ) + if len(bureau_list) > 0: + bureaus = tuple(d['for_value'] for d in bureau_list) + cost_center_filter = frappe.get_all( + 'Bureau', + filters={'name': ['in', bureaus]}, + fields=['cost_center'] + ) + + else: + department_list = frappe.db.get_all( + 'User Permission', + filters = { + 'user': frappe.session.user, 'allow': 'Department' + }, + fields = ['for_value'] + ) + if len(department_list) > 0: + departments = tuple(d['for_value'] for d in department_list) + cost_center_filter = frappe.get_all( + 'Department Cost Center', + filters={ + 'parent': ['in', departments], + 'parenttype': 'Department', + }, + fields=['cost_center'], + ) + + cost_center_filter_cond = "" + if cost_center_filter: + cost_center_filter = ', '.join("'"+d['cost_center']+"'" for d in cost_center_filter) + cost_center_filter_cond = " and name in ({0})".format(cost_center_filter) + + query = ''' + select + {fields} + from + `tabCost Center` + where + docstatus < 2 + {cost_center_filter_cond} + and ({scond}) and disabled != 1 + {fcond} {mcond} + order by + idx desc, name + limit %(page_len)s offset %(start)s + ''' + + return frappe.db.sql( + query.format( + **{ + "fields": ", ".join(fields), + "scond": searchfields, + "mcond": get_match_cond(doctype), + "cost_center_filter_cond": cost_center_filter_cond, + "fcond": get_filters_cond(doctype, filters, conditions).replace("%", "%%") + } + ), + {"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len}, + as_dict=as_dict, + ) diff --git a/beams/hooks.py b/beams/hooks.py index 877b2eceb..09f9e4eb4 100644 --- a/beams/hooks.py +++ b/beams/hooks.py @@ -158,6 +158,10 @@ "Shift Type": "beams.beams.custom_scripts.shift_type.shift_type.ShiftTypeOverride" } +standard_queries = { + "Cost Center": "beams.beams.overrides.queries.get_cost_center_list" +} + # Document Events # --------------- # Hook on document methods and events diff --git a/beams/setup.py b/beams/setup.py index 845cbe345..be8e2f972 100644 --- a/beams/setup.py +++ b/beams/setup.py @@ -571,6 +571,19 @@ def get_department_custom_fields(): "label": "Finance Group", "options":"Finance Group", "insert_after": "company" + }, + { + "fieldname": "depart_cost_section", + "fieldtype": "Section Break", + "label": "", + "insert_after": "leave_block_list" + }, + { + "fieldname": "department_cost_center", + "fieldtype": "Table", + "label": "Cost Center", + "options":"Department Cost Center", + "insert_after": "depart_cost_section" } ] } @@ -1383,7 +1396,7 @@ def get_purchase_invoice_custom_fields(): "options": "Bureau", "insert_after": "supplier" } - ] + ], } def get_supplier_custom_fields(): @@ -1513,7 +1526,7 @@ def get_employee_custom_fields(): return { "Employee": [ { - "fieldname": "Bureau", + "fieldname": "bureau", "fieldtype": "Link", "options": "Bureau", "label": "Bureau", From 64f99a8b2e590106a29fb745100caed6332f4d9a Mon Sep 17 00:00:00 2001 From: Arathi Date: Mon, 24 Mar 2025 09:30:04 +0530 Subject: [PATCH 02/40] Updated Batta Claim, Batta Policy, and Work Detail doctypes with field modifications --- .../doctype/batta_claim/batta_claim.json | 15 ++--- .../doctype/batta_policy/batta_policy.json | 12 ++-- .../doctype/work_detail/work_detail.json | 67 ++++++++++++++++++- 3 files changed, 75 insertions(+), 19 deletions(-) diff --git a/beams/beams/doctype/batta_claim/batta_claim.json b/beams/beams/doctype/batta_claim/batta_claim.json index ef8c66c49..a0448321d 100644 --- a/beams/beams/doctype/batta_claim/batta_claim.json +++ b/beams/beams/doctype/batta_claim/batta_claim.json @@ -32,9 +32,8 @@ "column_break_gbiv", "daily_batta_with_overnight_stay", "daily_batta_without_overnight_stay", - "batta", "column_break_jwtq", - "food_allowance", + "batta", "ot_batta", "section_break_osak", "work_detail", @@ -116,6 +115,7 @@ { "fieldname": "batta_based_on", "fieldtype": "Select", + "hidden": 1, "label": "Batta Based On", "options": "Daily\nHours" }, @@ -139,7 +139,7 @@ { "fieldname": "total_daily_batta", "fieldtype": "Currency", - "label": "Total Daily Batta", + "label": "Total Batta", "read_only": 1 }, { @@ -266,13 +266,6 @@ "label": "Daily Batta Without Overnight Stay", "precision": "2" }, - { - "depends_on": "eval:doc.batta_type == \"Internal\"", - "fieldname": "food_allowance", - "fieldtype": "Currency", - "label": "Food Allowance", - "precision": "2" - }, { "fieldname": "attach", "fieldtype": "Attach", @@ -298,7 +291,7 @@ "link_fieldname": "batta_claim_reference" } ], - "modified": "2025-03-21 15:21:24.428732", + "modified": "2025-03-22 12:26:10.349038", "modified_by": "Administrator", "module": "BEAMS", "name": "Batta Claim", diff --git a/beams/beams/doctype/batta_policy/batta_policy.json b/beams/beams/doctype/batta_policy/batta_policy.json index de7457081..5cbd58b39 100644 --- a/beams/beams/doctype/batta_policy/batta_policy.json +++ b/beams/beams/doctype/batta_policy/batta_policy.json @@ -57,7 +57,7 @@ { "fieldname": "room_rent_overnightbills_section", "fieldtype": "Section Break", - "label": "Room Rent for OverNight Stay" + "label": "Room Rent for Overnight Stay" }, { "depends_on": "eval:doc.is_actual__ == \"0\"", @@ -91,7 +91,7 @@ { "fieldname": "daily_batta_with_overnight_stay_section", "fieldtype": "Section Break", - "label": " Daily Batta With OverNight Stay" + "label": " Daily Batta With Overnight Stay" }, { "fieldname": "column_break_xaxj", @@ -110,10 +110,10 @@ "label": "Inside Kerala" }, { - "description": "Travel 50 kms with minimum 8 hours.\n", + "description": "Travel 100 kms with minimum 8 hours.\n", "fieldname": "daily_batta_without_over_night_stay_section", "fieldtype": "Section Break", - "label": "Daily Batta Without OverNight Stay" + "label": "Daily Batta Without Overnight Stay" }, { "fieldname": "column_break_ixxb", @@ -126,7 +126,7 @@ "label": "Inside Kerala" }, { - "description": "Travel 25 kms with minimum 6 hours ", + "description": "Travel 50 kms with minimum 6 hours", "fieldname": "food_allowance_section", "fieldtype": "Section Break", "label": "Food Allowance " @@ -188,7 +188,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2024-10-04 15:14:47.080979", + "modified": "2025-03-22 13:42:15.306374", "modified_by": "Administrator", "module": "BEAMS", "name": "Batta Policy", diff --git a/beams/beams/doctype/work_detail/work_detail.json b/beams/beams/doctype/work_detail/work_detail.json index a7a4605b1..12575486b 100644 --- a/beams/beams/doctype/work_detail/work_detail.json +++ b/beams/beams/doctype/work_detail/work_detail.json @@ -15,10 +15,20 @@ "from_date_and_time", "to_date_and_time", "on_air_date", + "section_break_ikdi", + "breakfast", + "column_break_vnsw", + "lunch", + "column_break_atwg", + "dinner", + "column_break_gaok", + "total_food_allowance", "section_break_wigg", "daily_batta", "column_break_uihk", "ot_batta", + "column_break_cjsx", + "total_batta", "section_break_elqj", "purpose", "total_hours", @@ -53,10 +63,11 @@ "read_only": 1 }, { + "description": "Daily Batta Without Food Allowance", "fieldname": "daily_batta", "fieldtype": "Currency", "in_list_view": 1, - "label": " Daily Batta", + "label": " Batta", "read_only": 1 }, { @@ -129,12 +140,64 @@ { "fieldname": "column_break_uihk", "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_ikdi", + "fieldtype": "Section Break" + }, + { + "fieldname": "breakfast", + "fieldtype": "Currency", + "label": "BreakFast", + "precision": "2" + }, + { + "fieldname": "column_break_vnsw", + "fieldtype": "Column Break" + }, + { + "fieldname": "lunch", + "fieldtype": "Currency", + "label": "Lunch", + "precision": "2" + }, + { + "fieldname": "column_break_atwg", + "fieldtype": "Column Break" + }, + { + "fieldname": "dinner", + "fieldtype": "Currency", + "label": "Dinner", + "precision": "2" + }, + { + "fieldname": "column_break_gaok", + "fieldtype": "Column Break", + "read_only": 1 + }, + { + "fieldname": "total_food_allowance", + "fieldtype": "Currency", + "label": "Total Food Allowance", + "precision": "2" + }, + { + "fieldname": "column_break_cjsx", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_batta", + "fieldtype": "Currency", + "label": "Total Batta", + "precision": "2", + "read_only": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2024-09-25 11:27:06.396834", + "modified": "2025-03-22 12:48:25.552974", "modified_by": "Administrator", "module": "BEAMS", "name": "Work Detail", From 264a5b14b590c91382e7e1eac1cd5596497dcc62 Mon Sep 17 00:00:00 2001 From: anjusha Date: Tue, 25 Mar 2025 09:15:39 +0530 Subject: [PATCH 03/40] feat: Add field reason in Petty Cash Request in dialog and Apply validation for it --- .../voucher_entry/voucher_entry.js | 18 ++++++++++++++++-- .../voucher_entry/voucher_entry.py | 3 ++- .../petty_cash_request/petty_cash_request.js | 9 +++++++++ .../petty_cash_request/petty_cash_request.json | 10 ++++++++-- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/beams/beams/custom_scripts/voucher_entry/voucher_entry.js b/beams/beams/custom_scripts/voucher_entry/voucher_entry.js index 8775094f7..2d699a3c4 100644 --- a/beams/beams/custom_scripts/voucher_entry/voucher_entry.js +++ b/beams/beams/custom_scripts/voucher_entry/voucher_entry.js @@ -55,10 +55,23 @@ function show_petty_cash_dialog(frm) { label: __("Requested Amount"), fieldtype: "Currency", reqd: 1 - } + }, + { + fieldname: "reason", + label: __("Reason"), + fieldtype: "Small Text", + reqd: 1 + }, ], primary_action_label: __("Submit"), primary_action(values) { + if (values.requested_amount <= 0) { + frappe.throw({ + title: __("Invalid Amount"), + message: __("Requested Amount should be greater than 0. Please enter a valid amount."), + indicator: "red" + }); + } submit_petty_cash_request(frm, values, d); } }); @@ -74,7 +87,8 @@ function submit_petty_cash_request(frm, values, dialog) { bureau: values.bureau, mode_of_payment: values.mode_of_payment, account: values.account, - requested_amount: values.requested_amount + requested_amount: values.requested_amount, + reason: values.reason }, callback: function (response) { if (response.message.status === "success") { diff --git a/beams/beams/custom_scripts/voucher_entry/voucher_entry.py b/beams/beams/custom_scripts/voucher_entry/voucher_entry.py index ee01f67fd..2d98d5d33 100644 --- a/beams/beams/custom_scripts/voucher_entry/voucher_entry.py +++ b/beams/beams/custom_scripts/voucher_entry/voucher_entry.py @@ -1,7 +1,7 @@ import frappe @frappe.whitelist() -def create_petty_cash_request(voucher_entry_name, bureau, mode_of_payment, account, requested_amount): +def create_petty_cash_request(voucher_entry_name, bureau, mode_of_payment, account, requested_amount, reason): """Create a Petty Cash Request linked to a Voucher Entry""" # Get Employee ID based on logged-in user @@ -15,6 +15,7 @@ def create_petty_cash_request(voucher_entry_name, bureau, mode_of_payment, accou "account": account, "requested_amount": requested_amount, "reference_voucher": voucher_entry_name, + "reason": reason, "employee": employee }) diff --git a/beams/beams/doctype/petty_cash_request/petty_cash_request.js b/beams/beams/doctype/petty_cash_request/petty_cash_request.js index 50c7613da..fda8d9cbe 100644 --- a/beams/beams/doctype/petty_cash_request/petty_cash_request.js +++ b/beams/beams/doctype/petty_cash_request/petty_cash_request.js @@ -36,6 +36,15 @@ frappe.ui.form.on("Petty Cash Request", { } } }); + }, + validate: function (frm) { + if (frm.doc.requested_amount <= 0) { + frappe.throw({ + title: __("Invalid Amount"), + message: __("Requested Amount should be greater than 0. Please enter a valid amount."), + indicator: "red" + }); + } } }); diff --git a/beams/beams/doctype/petty_cash_request/petty_cash_request.json b/beams/beams/doctype/petty_cash_request/petty_cash_request.json index 2d70f5d93..133d779d1 100644 --- a/beams/beams/doctype/petty_cash_request/petty_cash_request.json +++ b/beams/beams/doctype/petty_cash_request/petty_cash_request.json @@ -14,7 +14,8 @@ "column_break_pflc", "petty_cash_account", "account", - "requested_amount" + "requested_amount", + "reason" ], "fields": [ { @@ -71,12 +72,17 @@ "fieldtype": "Link", "label": "Petty Cash Account", "options": "Mode of Payment" + }, + { + "fieldname": "reason", + "fieldtype": "Small Text", + "label": "Reason" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2025-03-21 15:48:04.534573", + "modified": "2025-03-24 16:15:48.424785", "modified_by": "Administrator", "module": "BEAMS", "name": "Petty Cash Request", From 1a05d48f19dcb5d11d1068146d787a2b07c7a58c Mon Sep 17 00:00:00 2001 From: anjusha Date: Tue, 25 Mar 2025 09:40:31 +0530 Subject: [PATCH 04/40] feat: Add field Department in voucher Entry --- beams/setup.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/beams/setup.py b/beams/setup.py index 3db313e78..cd84a60e6 100644 --- a/beams/setup.py +++ b/beams/setup.py @@ -1872,7 +1872,14 @@ def get_voucher_entry_custom_fields(): "options": "Bureau", "label": "Bureau", "insert_after": "balance" - } + }, + { + "fieldname": "department", + "fieldtype": "Link", + "options": "Department", + "label": "Department", + "insert_after": "company" + }, ] } From 5921f1e24f944bd846457737891a741286213be5 Mon Sep 17 00:00:00 2001 From: Avishna Murali Date: Tue, 25 Mar 2025 11:21:56 +0530 Subject: [PATCH 05/40] fix:Fix TypeError & Optimize Date Handling in total_days_calculate --- .../employee_travel_request.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/beams/beams/doctype/employee_travel_request/employee_travel_request.py b/beams/beams/doctype/employee_travel_request/employee_travel_request.py index 9daa1ae85..384ad8d2a 100644 --- a/beams/beams/doctype/employee_travel_request/employee_travel_request.py +++ b/beams/beams/doctype/employee_travel_request/employee_travel_request.py @@ -86,8 +86,16 @@ def validate_expected_time(self): def total_days_calculate(self): """Calculate the total number of travel days, ensuring at least one day.""" if self.start_date and self.end_date: - start_date = datetime.strptime(self.start_date, "%Y-%m-%d %H:%M:%S").date() - end_date = datetime.strptime(self.end_date, "%Y-%m-%d %H:%M:%S").date() + if isinstance(self.start_date, str): + start_date = datetime.strptime(self.start_date, "%Y-%m-%d %H:%M:%S").date() + else: + start_date = self.start_date.date() + + if isinstance(self.end_date, str): + end_date = datetime.strptime(self.end_date, "%Y-%m-%d %H:%M:%S").date() + else: + end_date = self.end_date.date() + self.total_days = 1 if start_date == end_date else (end_date - start_date).days + 1 @frappe.whitelist() From 9ddd4044c389063e75a53f46d996f0cc104cecab Mon Sep 17 00:00:00 2001 From: Neha Fathima Date: Mon, 24 Mar 2025 16:41:29 +0530 Subject: [PATCH 06/40] fix: fixed auto-creation of petty cash request --- beams/beams/custom_scripts/voucher_entry/voucher_entry.py | 1 - 1 file changed, 1 deletion(-) diff --git a/beams/beams/custom_scripts/voucher_entry/voucher_entry.py b/beams/beams/custom_scripts/voucher_entry/voucher_entry.py index ee01f67fd..718eb2dc4 100644 --- a/beams/beams/custom_scripts/voucher_entry/voucher_entry.py +++ b/beams/beams/custom_scripts/voucher_entry/voucher_entry.py @@ -19,7 +19,6 @@ def create_petty_cash_request(voucher_entry_name, bureau, mode_of_payment, accou }) petty_cash.insert(ignore_permissions=True) - petty_cash.submit() frappe.db.commit() return {"status": "success", "message": "Petty Cash Request Created Successfully!", "docname": petty_cash.name} From b689041e1051a445c326f3c925a4af1dd21054f0 Mon Sep 17 00:00:00 2001 From: Arathi Date: Tue, 25 Mar 2025 11:35:48 +0530 Subject: [PATCH 07/40] feat:Create Doctype for Bureau Trip Sheet --- .../doctype/bureau_trip_sheet/__init__.py | 0 .../bureau_trip_sheet/bureau_trip_sheet.js | 28 +++ .../bureau_trip_sheet/bureau_trip_sheet.json | 179 +++++++++++++++ .../bureau_trip_sheet/bureau_trip_sheet.py | 84 +++++++ .../test_bureau_trip_sheet.py | 9 + beams/beams/doctype/trip_details/__init__.py | 0 .../doctype/trip_details/trip_details.json | 210 ++++++++++++++++++ .../doctype/trip_details/trip_details.py | 9 + beams/setup.py | 8 + 9 files changed, 527 insertions(+) create mode 100644 beams/beams/doctype/bureau_trip_sheet/__init__.py create mode 100644 beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js create mode 100644 beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json create mode 100644 beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py create mode 100644 beams/beams/doctype/bureau_trip_sheet/test_bureau_trip_sheet.py create mode 100644 beams/beams/doctype/trip_details/__init__.py create mode 100644 beams/beams/doctype/trip_details/trip_details.json create mode 100644 beams/beams/doctype/trip_details/trip_details.py diff --git a/beams/beams/doctype/bureau_trip_sheet/__init__.py b/beams/beams/doctype/bureau_trip_sheet/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js new file mode 100644 index 000000000..dc86f9a5d --- /dev/null +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js @@ -0,0 +1,28 @@ +frappe.ui.form.on("Bureau Trip Sheet", { + refresh: function (frm) { + filter_supplier_field(frm); + }, + + is_overnight_stay: function (frm) { + update_daily_batta(frm); + }, + + is_travelling_outside_kerala: function (frm) { + update_daily_batta(frm); + }, + + driver: function (frm) { + update_daily_batta(frm); + } +}); + +// Function to filter the supplier field +function filter_supplier_field(frm) { + frm.set_query("supplier", function () { + return { + filters: { + is_transporter: 1 + } + }; + }); +} diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json new file mode 100644 index 000000000..c24f3b128 --- /dev/null +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json @@ -0,0 +1,179 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "format:BTS-{YY}-{####}", + "creation": "2025-03-24 15:17:02.532199", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "section_break_qq5d", + "supplier", + "bureau", + "column_break_psaw", + "company", + "section_break_qavf", + "is_overnight_stay", + "is_travelling_outside_kerala", + "daily_batta_with_overnight_stay", + "daily_batta_without_overnight_stay", + "column_break_hzfr", + "ot_batta", + "batta", + "section_break_tjbr", + "work_details", + "trip_details_section", + "departure_location", + "destination_location", + "column_break_cdph", + "ending_date_and_time", + "starting_date_and_time", + "amended_from" + ], + "fields": [ + { + "fieldname": "section_break_qq5d", + "fieldtype": "Section Break" + }, + { + "fieldname": "supplier", + "fieldtype": "Link", + "label": "Supplier", + "options": "Supplier" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, + { + "fieldname": "bureau", + "fieldtype": "Link", + "label": "Bureau", + "options": "Bureau" + }, + { + "fieldname": "section_break_qavf", + "fieldtype": "Section Break" + }, + { + "fetch_from": "supplier.ot_batta", + "fieldname": "ot_batta", + "fieldtype": "Currency", + "label": "OT Batta" + }, + { + "fieldname": "daily_batta_without_overnight_stay", + "fieldtype": "Currency", + "label": "Daily Batta Without Overnight Stay" + }, + { + "default": "0", + "fieldname": "is_overnight_stay", + "fieldtype": "Check", + "label": "Is Overnight Stay" + }, + { + "default": "0", + "fieldname": "is_travelling_outside_kerala", + "fieldtype": "Check", + "label": " Is Travelling Outside Kerala" + }, + { + "fieldname": "daily_batta_with_overnight_stay", + "fieldtype": "Currency", + "label": "Daily Batta With Overnight Stay" + }, + { + "fieldname": "batta", + "fieldtype": "Currency", + "label": "Batta" + }, + { + "fieldname": "column_break_hzfr", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_tjbr", + "fieldtype": "Section Break" + }, + { + "fieldname": "work_details", + "fieldtype": "Table", + "label": "Work Details", + "options": "Trip Details" + }, + { + "fieldname": "trip_details_section", + "fieldtype": "Section Break", + "label": "Trip Details" + }, + { + "fieldname": "departure_location", + "fieldtype": "Link", + "label": "Departure Location", + "options": "Location" + }, + { + "fieldname": "destination_location", + "fieldtype": "Link", + "label": "Destination Location", + "options": "Location" + }, + { + "fieldname": "ending_date_and_time", + "fieldtype": "Datetime", + "label": "Ending Date and Time " + }, + { + "fieldname": "starting_date_and_time", + "fieldtype": "Datetime", + "label": "Starting Date and Time " + }, + { + "fieldname": "column_break_cdph", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_psaw", + "fieldtype": "Column Break" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Bureau Trip Sheet", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2025-03-25 10:52:18.820262", + "modified_by": "Administrator", + "module": "BEAMS", + "name": "Bureau Trip Sheet", + "naming_rule": "Expression", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py new file mode 100644 index 000000000..f0759be7f --- /dev/null +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py @@ -0,0 +1,84 @@ +import frappe +from frappe.model.document import Document +from frappe import _ + +class BureauTripSheet(Document): + pass + + + + +@frappe.whitelist() +def get_batta_value(driver): + """ + Fetch the 'outside_kerala' value from Batta Policy for the given driver. + """ + batta_value = frappe.get_value("Batta Policy", {"driver": driver}, "outside_kerala") + return batta_value if batta_value else 0 + +# @frappe.whitelist() +# def update_allowance(doc_name): +# try: +# # Retrieve the Bureau Trip Sheet document +# doc = frappe.get_doc("Bureau Trip Sheet", doc_name) +# +# # Check if the document exists +# if not doc: +# frappe.throw(_("Bureau Trip Sheet document not found.")) +# +# # Fetch the Batta Policy document +# if not doc.batta_policy: +# frappe.throw(_("Batta Policy not selected for the Bureau Trip Sheet.")) +# +# policy = frappe.get_doc("Batta Policy", doc.batta_policy) +# +# # Reset the daily batta fields to 0 initially +# doc.daily_batta_with_overnight_stay = 0 +# doc.daily_batta_without_overnight_stay = 0 +# +# # Determine allowance based on conditions +# if doc.is_travelling_outside_kerala and doc.is_overnight_stay: +# doc.daily_batta_with_overnight_stay = policy.outside_kerala__ +# elif doc.is_overnight_stay: +# doc.daily_batta_with_overnight_stay = policy.inside_kerala__ +# elif doc.is_travelling_outside_kerala: +# doc.daily_batta_without_overnight_stay = policy.outside_kerala +# else: +# doc.daily_batta_without_overnight_stay = policy.inside_kerala +# +# # Save the updated document +# doc.save(ignore_permissions=True) +# frappe.db.commit() +# +# return "Allowance Updated" +# +# except frappe.DoesNotExistError: +# frappe.throw(_("The document with the name '{}' was not found.").format(doc_name)) +# except Exception as e: +# frappe.throw(_("An error occurred: {}").format(str(e))) + +# @frappe.whitelist() +# def update_batta(docname): +# doc = frappe.get_doc("Bureau Trip Sheet", docname) +# +# # Reset the daily batta fields to 0 initially +# doc.daily_batta_with_overnight_stay = 0 +# doc.daily_batta_without_overnight_stay = 0 +# +# # Fetch the policy document (ensure you are retrieving the correct one) +# policy = frappe.get_doc("Batta Policy", "Policy Name") # Replace "Policy Name" with the actual policy name or logic to fetch it +# +# # Determine allowance based on conditions +# if doc.is_travelling_outside_kerala and doc.is_overnight_stay: +# doc.daily_batta_with_overnight_stay = policy.outside_kerala__ +# elif doc.is_overnight_stay: +# doc.daily_batta_with_overnight_stay = policy.inside_kerala__ +# elif doc.is_travelling_outside_kerala: +# doc.daily_batta_without_overnight_stay = policy.outside_kerala +# else: +# doc.daily_batta_without_overnight_stay = policy.inside_kerala +# +# return { +# "daily_batta_with_overnight_stay": doc.daily_batta_with_overnight_stay, +# "daily_batta_without_overnight_stay": doc.daily_batta_without_overnight_stay +# } diff --git a/beams/beams/doctype/bureau_trip_sheet/test_bureau_trip_sheet.py b/beams/beams/doctype/bureau_trip_sheet/test_bureau_trip_sheet.py new file mode 100644 index 000000000..e2d01c208 --- /dev/null +++ b/beams/beams/doctype/bureau_trip_sheet/test_bureau_trip_sheet.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, efeone and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestBureauTripSheet(FrappeTestCase): + pass diff --git a/beams/beams/doctype/trip_details/__init__.py b/beams/beams/doctype/trip_details/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/beams/beams/doctype/trip_details/trip_details.json b/beams/beams/doctype/trip_details/trip_details.json new file mode 100644 index 000000000..067b0df5b --- /dev/null +++ b/beams/beams/doctype/trip_details/trip_details.json @@ -0,0 +1,210 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2025-03-25 10:36:52.888802", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "origin", + "destination", + "distance_travelled_km", + "number_of_days", + "batta_type", + "column_break_aerg", + "from_date_and_time", + "to_date_and_time", + "on_air_date", + "section_break_ikdi", + "breakfast", + "column_break_vnsw", + "lunch", + "column_break_atwg", + "dinner", + "column_break_gaok", + "total_food_allowance", + "section_break_wigg", + "daily_batta", + "column_break_uihk", + "ot_batta", + "column_break_cjsx", + "total_batta", + "section_break_elqj", + "purpose", + "total_hours", + "ot_hours" + ], + "fields": [ + { + "fieldname": "origin", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Origin", + "options": "Location" + }, + { + "fieldname": "destination", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Destination ", + "options": "Location" + }, + { + "fieldname": "distance_travelled_km", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Distance Travelled(km) " + }, + { + "fieldname": "number_of_days", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Number of Days", + "read_only": 1 + }, + { + "fieldname": "batta_type", + "fieldtype": "Select", + "hidden": 1, + "in_list_view": 1, + "label": "Batta Type", + "options": "External\nInternal", + "read_only": 1 + }, + { + "fieldname": "column_break_aerg", + "fieldtype": "Column Break" + }, + { + "fieldname": "from_date_and_time", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "Start Date and Time" + }, + { + "fieldname": "to_date_and_time", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "End Date and Time" + }, + { + "fieldname": "on_air_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "On Air date" + }, + { + "fieldname": "section_break_ikdi", + "fieldtype": "Section Break" + }, + { + "fieldname": "breakfast", + "fieldtype": "Currency", + "label": "BreakFast", + "precision": "2" + }, + { + "fieldname": "column_break_vnsw", + "fieldtype": "Column Break" + }, + { + "fieldname": "lunch", + "fieldtype": "Currency", + "label": "Lunch", + "precision": "2" + }, + { + "fieldname": "column_break_atwg", + "fieldtype": "Column Break" + }, + { + "fieldname": "dinner", + "fieldtype": "Currency", + "label": "Dinner", + "precision": "2" + }, + { + "fieldname": "column_break_gaok", + "fieldtype": "Column Break", + "read_only": 1 + }, + { + "fieldname": "total_food_allowance", + "fieldtype": "Currency", + "label": "Total Food Allowance", + "precision": "2" + }, + { + "fieldname": "section_break_wigg", + "fieldtype": "Section Break" + }, + { + "description": "Daily Batta Without Food Allowance", + "fieldname": "daily_batta", + "fieldtype": "Currency", + "in_list_view": 1, + "label": " Batta", + "read_only": 1 + }, + { + "fieldname": "column_break_uihk", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:doc.batta_type == 'External'", + "fieldname": "ot_batta", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "OT Batta", + "read_only": 1 + }, + { + "fieldname": "column_break_cjsx", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_batta", + "fieldtype": "Currency", + "label": "Total Batta", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "section_break_elqj", + "fieldtype": "Section Break" + }, + { + "fieldname": "purpose", + "fieldtype": "Small Text", + "label": "Purpose" + }, + { + "fieldname": "total_hours", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Total Hours", + "read_only": 1 + }, + { + "depends_on": "eval:doc.batta_type == 'External'", + "fieldname": "ot_hours", + "fieldtype": "Float", + "in_list_view": 1, + "label": "OT Hours", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-03-25 10:36:52.888802", + "modified_by": "Administrator", + "module": "BEAMS", + "name": "Trip Details", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/beams/beams/doctype/trip_details/trip_details.py b/beams/beams/doctype/trip_details/trip_details.py new file mode 100644 index 000000000..1d802f599 --- /dev/null +++ b/beams/beams/doctype/trip_details/trip_details.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, efeone and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class TripDetails(Document): + pass diff --git a/beams/setup.py b/beams/setup.py index 3db313e78..f7e14aa69 100644 --- a/beams/setup.py +++ b/beams/setup.py @@ -1478,6 +1478,14 @@ def get_supplier_custom_fields(): "depends_on": "eval:doc.is_stringer == 1", "insert_after": "bureau" + }, + { + "fieldname": "ot_batta", + "fieldtype": "Currency", + "label": "OT Batta", + "depends_on": "eval:doc.is_transporter == 1", + "insert_after": "is_transporter" + } ] } From 4f54d971d522d4ecf38a9da0f8323a137a952439 Mon Sep 17 00:00:00 2001 From: Arathi Date: Tue, 25 Mar 2025 11:50:58 +0530 Subject: [PATCH 08/40] feat:created Bureau Trip Sheet Doctype --- .../bureau_trip_sheet/bureau_trip_sheet.js | 16 +--- .../bureau_trip_sheet/bureau_trip_sheet.py | 82 +------------------ 2 files changed, 8 insertions(+), 90 deletions(-) diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js index dc86f9a5d..e3625a567 100644 --- a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js @@ -1,19 +1,11 @@ +// Copyright (c) 2024, efeone and contributors +// For license information, please see license.txt + frappe.ui.form.on("Bureau Trip Sheet", { refresh: function (frm) { filter_supplier_field(frm); - }, - - is_overnight_stay: function (frm) { - update_daily_batta(frm); - }, - - is_travelling_outside_kerala: function (frm) { - update_daily_batta(frm); - }, - - driver: function (frm) { - update_daily_batta(frm); } + }); // Function to filter the supplier field diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py index f0759be7f..d86da11b7 100644 --- a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py @@ -1,84 +1,10 @@ +# Copyright (c) 2024, efeone and contributors +# For license information, please see license.txt + + import frappe from frappe.model.document import Document from frappe import _ class BureauTripSheet(Document): pass - - - - -@frappe.whitelist() -def get_batta_value(driver): - """ - Fetch the 'outside_kerala' value from Batta Policy for the given driver. - """ - batta_value = frappe.get_value("Batta Policy", {"driver": driver}, "outside_kerala") - return batta_value if batta_value else 0 - -# @frappe.whitelist() -# def update_allowance(doc_name): -# try: -# # Retrieve the Bureau Trip Sheet document -# doc = frappe.get_doc("Bureau Trip Sheet", doc_name) -# -# # Check if the document exists -# if not doc: -# frappe.throw(_("Bureau Trip Sheet document not found.")) -# -# # Fetch the Batta Policy document -# if not doc.batta_policy: -# frappe.throw(_("Batta Policy not selected for the Bureau Trip Sheet.")) -# -# policy = frappe.get_doc("Batta Policy", doc.batta_policy) -# -# # Reset the daily batta fields to 0 initially -# doc.daily_batta_with_overnight_stay = 0 -# doc.daily_batta_without_overnight_stay = 0 -# -# # Determine allowance based on conditions -# if doc.is_travelling_outside_kerala and doc.is_overnight_stay: -# doc.daily_batta_with_overnight_stay = policy.outside_kerala__ -# elif doc.is_overnight_stay: -# doc.daily_batta_with_overnight_stay = policy.inside_kerala__ -# elif doc.is_travelling_outside_kerala: -# doc.daily_batta_without_overnight_stay = policy.outside_kerala -# else: -# doc.daily_batta_without_overnight_stay = policy.inside_kerala -# -# # Save the updated document -# doc.save(ignore_permissions=True) -# frappe.db.commit() -# -# return "Allowance Updated" -# -# except frappe.DoesNotExistError: -# frappe.throw(_("The document with the name '{}' was not found.").format(doc_name)) -# except Exception as e: -# frappe.throw(_("An error occurred: {}").format(str(e))) - -# @frappe.whitelist() -# def update_batta(docname): -# doc = frappe.get_doc("Bureau Trip Sheet", docname) -# -# # Reset the daily batta fields to 0 initially -# doc.daily_batta_with_overnight_stay = 0 -# doc.daily_batta_without_overnight_stay = 0 -# -# # Fetch the policy document (ensure you are retrieving the correct one) -# policy = frappe.get_doc("Batta Policy", "Policy Name") # Replace "Policy Name" with the actual policy name or logic to fetch it -# -# # Determine allowance based on conditions -# if doc.is_travelling_outside_kerala and doc.is_overnight_stay: -# doc.daily_batta_with_overnight_stay = policy.outside_kerala__ -# elif doc.is_overnight_stay: -# doc.daily_batta_with_overnight_stay = policy.inside_kerala__ -# elif doc.is_travelling_outside_kerala: -# doc.daily_batta_without_overnight_stay = policy.outside_kerala -# else: -# doc.daily_batta_without_overnight_stay = policy.inside_kerala -# -# return { -# "daily_batta_with_overnight_stay": doc.daily_batta_with_overnight_stay, -# "daily_batta_without_overnight_stay": doc.daily_batta_without_overnight_stay -# } From 4a6b8b69342c99cad0dcd0759c81d892cee6d763 Mon Sep 17 00:00:00 2001 From: anjusha Date: Tue, 25 Mar 2025 12:04:38 +0530 Subject: [PATCH 09/40] feat: Make total_amount field read only --- beams/setup.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/beams/setup.py b/beams/setup.py index 3db313e78..3ae7e1067 100644 --- a/beams/setup.py +++ b/beams/setup.py @@ -4066,7 +4066,15 @@ def get_property_setters(): "property": "in_standard_filter", "property_type": "Check", "value":1 - } + }, + { + "doctype_or_field": "DocField", + "doc_type": "Voucher Entry", + "field_name": "total_amount", + "property": "read_only", + "property_type": "Check", + "value": 1 + }, ] def get_material_request_custom_fields(): From 542f30f9bc1d49299be338f6f859446c985b8dc3 Mon Sep 17 00:00:00 2001 From: Arathi Date: Tue, 25 Mar 2025 12:24:59 +0530 Subject: [PATCH 10/40] Update Bureau Trip Sheet field properties --- .../beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json index c24f3b128..75fbe898e 100644 --- a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json @@ -37,6 +37,8 @@ { "fieldname": "supplier", "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, "label": "Supplier", "options": "Supplier" }, @@ -123,11 +125,13 @@ { "fieldname": "ending_date_and_time", "fieldtype": "Datetime", + "in_list_view": 1, "label": "Ending Date and Time " }, { "fieldname": "starting_date_and_time", "fieldtype": "Datetime", + "in_list_view": 1, "label": "Starting Date and Time " }, { @@ -152,7 +156,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2025-03-25 10:52:18.820262", + "modified": "2025-03-25 12:23:17.388355", "modified_by": "Administrator", "module": "BEAMS", "name": "Bureau Trip Sheet", From 7fb931d809e88b741566f51149ad11267472744b Mon Sep 17 00:00:00 2001 From: Avishna Murali Date: Tue, 25 Mar 2025 14:12:40 +0530 Subject: [PATCH 11/40] feat:Trigger total_days_calculate on end_date change --- .../employee_travel_request/employee_travel_request.js | 8 ++++---- .../employee_travel_request/employee_travel_request.py | 7 +++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/beams/beams/doctype/employee_travel_request/employee_travel_request.js b/beams/beams/doctype/employee_travel_request/employee_travel_request.js index 82ff1b21a..35716e51b 100644 --- a/beams/beams/doctype/employee_travel_request/employee_travel_request.js +++ b/beams/beams/doctype/employee_travel_request/employee_travel_request.js @@ -43,16 +43,16 @@ frappe.ui.form.on('Employee Travel Request', { posting_date:function (frm){ frm.call("validate_posting_date"); }, - end_date:function (frm){ - frm.call("validate_dates"); - calculateTotalDays(frm); - }, validate:function(frm){ frm.call("validate_expected_time"); }, start_date:function(frm){ calculateTotalDays(frm); + }, + end_date:function (frm){ + frm.call("total_days_calculate"); } + }); function set_room_criteria_filter(frm) { diff --git a/beams/beams/doctype/employee_travel_request/employee_travel_request.py b/beams/beams/doctype/employee_travel_request/employee_travel_request.py index 384ad8d2a..922c299f7 100644 --- a/beams/beams/doctype/employee_travel_request/employee_travel_request.py +++ b/beams/beams/doctype/employee_travel_request/employee_travel_request.py @@ -20,10 +20,10 @@ def on_cancel(self): def validate(self): self.validate_dates() - self.calculate_total_days() self.validate_expected_time() self.total_days_calculate() + def before_save(self): self.validate_posting_date() if not self.requested_by: @@ -42,9 +42,7 @@ def validate_dates(self): if self.start_date < today(): frappe.throw("Start Date cannot be in the past.") - def calculate_total_days(self): - if self.start_date and self.end_date: - self.total_days = date_diff(self.end_date, self.start_date) + def on_update_after_submit(self): """ @@ -83,6 +81,7 @@ def validate_expected_time(self): if self.expected_check_out_time < self.expected_check_in_time: frappe.throw("Expected Check-out Time cannot be earlier than Expected Check-in Time.") + @frappe.whitelist() def total_days_calculate(self): """Calculate the total number of travel days, ensuring at least one day.""" if self.start_date and self.end_date: From 07ff6d4a41915803957e108dcfd9fa9c0e903239 Mon Sep 17 00:00:00 2001 From: Muhammed Sinan K T <91651425+MhmdSinanKT@users.noreply.github.com> Date: Tue, 25 Mar 2025 14:25:12 +0530 Subject: [PATCH 12/40] feat:Added patches to delete a field in Employee doctype --- beams/patches.txt | 2 ++ beams/patches/delete_employee_fields.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 beams/patches/delete_employee_fields.py diff --git a/beams/patches.txt b/beams/patches.txt index f2e2afa5e..c2e1371cf 100644 --- a/beams/patches.txt +++ b/beams/patches.txt @@ -1,4 +1,6 @@ [pre_model_sync] + +beams.patches.delete_employee_fields #25-02-2024 beams.patches.rename_hod_role #30-10-2024 beams.patches.delete_custom_fields #26-11-2024-1 beams.patches.no_of_children_patch #06-03-2025 diff --git a/beams/patches/delete_employee_fields.py b/beams/patches/delete_employee_fields.py new file mode 100644 index 000000000..97ace3cb6 --- /dev/null +++ b/beams/patches/delete_employee_fields.py @@ -0,0 +1,14 @@ +import frappe + +fields_to_remove = [ + { + 'dt':'Employee', + 'fieldname':'bureau' + } +] + +def execute(): + for field in fields_to_remove: + if frappe.db.exists('Custom Field', field): + frappe.db.delete('Custom Field', field) + frappe.db.commit() From 3c5455ac94dbdac2b1d5e67503065df91327a6fb Mon Sep 17 00:00:00 2001 From: nidhashirinniyas Date: Tue, 25 Mar 2025 16:11:40 +0530 Subject: [PATCH 13/40] feat : filter in mode of travel should not affect if user is Administrator --- .../employee_travel_request.js | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/beams/beams/doctype/employee_travel_request/employee_travel_request.js b/beams/beams/doctype/employee_travel_request/employee_travel_request.js index 82ff1b21a..7d235a77a 100644 --- a/beams/beams/doctype/employee_travel_request/employee_travel_request.js +++ b/beams/beams/doctype/employee_travel_request/employee_travel_request.js @@ -77,24 +77,33 @@ function set_room_criteria_filter(frm) { } function set_mode_of_travel_filter(frm) { + // Check if the session user is Administrator + if (frappe.session.user === "Administrator") { + frm.set_query("mode_of_travel", function() { + return {}; // No filter applied + }); + return; + } + frappe.call({ - method: "beams.beams.doctype.employee_travel_request.employee_travel_request.filter_mode_of_travel", - args: { - batta_policy_name: frm.doc.batta_policy - }, - callback: function (filter_response) { - let mode_of_travel = filter_response.message || []; - frm.set_query("mode_of_travel", function() { - return { - filters: { - name: ["in", mode_of_travel] - } - } - }) - }, + method: "beams.beams.doctype.employee_travel_request.employee_travel_request.filter_mode_of_travel", + args: { + batta_policy_name: frm.doc.batta_policy + }, + callback: function (filter_response) { + let mode_of_travel = filter_response.message || []; + frm.set_query("mode_of_travel", function() { + return { + filters: { + name: ["in", mode_of_travel] + } + }; + }); + }, }); } + function calculateTotalDays(frm) { if (frm.doc.start_date && frm.doc.end_date) { let start = new Date(frm.doc.start_date); From 41da69c5f824c4ff6b223538a79b07dd44bd4bf2 Mon Sep 17 00:00:00 2001 From: nidhashirinniyas Date: Tue, 25 Mar 2025 14:18:59 +0530 Subject: [PATCH 14/40] fix : Removing unwanted file --- .../payment_entry/payment_entry.js | 26 ------------------- 1 file changed, 26 deletions(-) delete mode 100644 beams/beams/custom_scripts/payment_entry/payment_entry.js diff --git a/beams/beams/custom_scripts/payment_entry/payment_entry.js b/beams/beams/custom_scripts/payment_entry/payment_entry.js deleted file mode 100644 index 276ac6290..000000000 --- a/beams/beams/custom_scripts/payment_entry/payment_entry.js +++ /dev/null @@ -1,26 +0,0 @@ -frappe.ui.form.on('Payment Entry', { - payment_type(frm) { - if (frm.doc.payment_type === 'Pay') { - frm.set_value('party_type', 'Employee'); - } - }, - onload_post_render(frm) { - if (frm.is_new() && frm.doc.payment_type === 'Pay') { - frm.set_value('party_type', 'Employee'); - } - }, - party_type(frm) { - if (frm.doc.party_type === "Employee") { - frappe.db.get_list('Employee', { - filters: { user_id: frappe.session.user }, - fields: ['name'] - }).then(records => { - if (records.length > 0) { - frm.set_value("party", records[0].name); - } else { - frappe.msgprint(__("No Employee record found for the current user.")); - } - }); - } - } -}); From ad6ac2b04e953d12f1b78f49a3013e5ed5e96459 Mon Sep 17 00:00:00 2001 From: Sherin KR Date: Tue, 25 Mar 2025 23:59:33 +0530 Subject: [PATCH 15/40] Update master.yml --- .github/workflows/master.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 6b4e8c4dd..f38f081cc 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -18,7 +18,7 @@ jobs: - name: Deploy to dev uses: appleboy/ssh-action@master with: - host: ${{ secrets.DEV_HOST }} + host: ${{ secrets.UAT_HOST }} username: ${{ secrets.USERNAME }} key: ${{ secrets.DEV_KEY }} port: 22 From a66b0f70fb75d07201b3575a7363d524b67da39f Mon Sep 17 00:00:00 2001 From: hridyalakshmi Date: Wed, 26 Mar 2025 12:51:26 +0530 Subject: [PATCH 16/40] feat: Batta Claim Calculation --- .../beams/doctype/batta_claim/batta_claim.js | 533 ++++++++++-------- .../doctype/batta_claim/batta_claim.json | 68 +-- .../beams/doctype/batta_claim/batta_claim.py | 263 +++++---- .../doctype/work_detail/work_detail.json | 32 +- 4 files changed, 494 insertions(+), 402 deletions(-) diff --git a/beams/beams/doctype/batta_claim/batta_claim.js b/beams/beams/doctype/batta_claim/batta_claim.js index 289a0b81a..138411213 100644 --- a/beams/beams/doctype/batta_claim/batta_claim.js +++ b/beams/beams/doctype/batta_claim/batta_claim.js @@ -2,59 +2,85 @@ // For license information, please see license.txt frappe.ui.form.on('Batta Claim', { - onload: function (frm) { + validate: function(frm) { + calculate_total_distance_travelled(frm); + calculate_total_daily_batta(frm); + update_all_daily_batta(frm); + calculate_batta(frm); + calculate_total_hours(frm); + }, + batta: function(frm) { + update_all_daily_batta(frm); + }, + ot_batta: function(frm) { + update_all_daily_batta(frm); + }, + onload: function(frm) { + handle_designation_based_on_batta_type(frm); + }, + batta_type: function(frm) { + handle_designation_based_on_batta_type(frm); set_batta_based_on_options(frm); - calculate_totals(frm); - calculate_batta_totals(frm); + }, + employee: function(frm) { + handle_designation_based_on_batta_type(frm); }, room_rent_batta: function(frm) { - calculate_batta_totals(frm); - }, - daily_batta_with_overnight_stay: function(frm) { - calculate_batta_totals(frm); - }, - daily_batta_without_overnight_stay: function(frm) { - calculate_batta_totals(frm); - }, - food_allowance: function(frm) { - calculate_batta_totals(frm); - }, - origin: function(frm) { - update_work_detail(frm); + calculate_batta(frm); + if (frm.doc.room_rent_batta < 0) { + frappe.msgprint({ + message: "Room Rent Batta cannot be negative.", + indicator: "red" + }); + frm.set_value("room_rent_batta", 0); + } + }, + daily_batta_without_overnight_stay: function(frm) { + calculate_batta(frm); + if (frm.doc.daily_batta_without_overnight_stay < 0) { + frappe.msgprint({ + message: "Daily Batta Without Overnight Stay cannot be negative.", + indicator: "red" + }); + frm.set_value("daily_batta_without_overnight_stay", 0); + } }, - destination: function(frm) { - update_work_detail(frm); + daily_batta_with_overnight_stay: function(frm) { + calculate_batta(frm); + if (frm.doc.daily_batta_with_overnight_stay < 0) { + frappe.msgprint({ + message: "Daily Batta With Overnight Stay cannot be negative.", + indicator: "red" + }); + frm.set_value("daily_batta_with_overnight_stay", 0); + } }, - designation: function(frm) { - frm.trigger('calculate_batta'); + total_distance_travelled_km: function(frm) { + calculate_allowance(frm); + }, + total_hours: function(frm) { + calculate_allowance(frm); }, is_travelling_outside_kerala: function(frm) { - frm.trigger('calculate_batta'); + calculate_allowance(frm); }, is_overnight_stay: function(frm) { - frm.trigger('calculate_batta'); + calculate_allowance(frm); + frm.doc.work_detail.forEach(row => { + set_batta_for_food_allowance(frm, row["doctype"], row["name"]); + set_batta_for_food_allowance(frm, row["doctype"], row["name"]); + }) }, is_avail_room_rent: function(frm) { - frm.trigger('calculate_batta'); + calculate_allowance(frm); }, - total_distance_travelled_km: function(frm) { - frm.trigger('calculate_batta'); + is_delhi_bureau: function(frm) { + frm.doc.work_detail.forEach(row => { + set_batta_for_food_allowance(frm, row["doctype"], row["name"]); + set_batta_for_food_allowance(frm, row["doctype"], row["name"]); + }) }, - work_detail_add: function(frm, cdt, cdn) { - calculate_total_distance_travelled(frm); - }, - work_detail_onform_render: function(frm, cdt, cdn) { - calculate_total_distance_travelled(frm); - }, - work_detail_remove: function(frm, cdt, cdn) { - calculate_total_distance_travelled(frm); - }, - refresh: function (frm) { - set_batta_based_on_options(frm); - calculate_totals(frm); - calculate_total_distance_travelled(frm); - calculate_batta_totals(frm); - + refresh: function(frm) { frappe.call({ method: "beams.beams.doctype.batta_claim.batta_claim.get_batta_policy_values", callback: function(response) { @@ -68,110 +94,171 @@ frappe.ui.form.on('Batta Claim', { frm.set_df_property('daily_batta_without_overnight_stay', 'read_only', is_actual_daily_batta_without_overnight_stay == 0); frm.set_df_property('daily_batta_with_overnight_stay', 'read_only', is_actual_daily_batta_with_overnight_stay == 0); frm.set_df_property('room_rent_batta', 'read_only', is_actual_room_rent_batta == 0); - frm.set_df_property('food_allowance', 'read_only', is_actual_food_allowance == 0); // Refresh the fields to reflect the changes frm.refresh_field('daily_batta_without_overnight_stay'); frm.refresh_field('daily_batta_with_overnight_stay'); frm.refresh_field('room_rent_batta'); - frm.refresh_field('food_allowance'); + + frm.fields_dict['work_detail'].grid.update_docfield_property('breakfast', 'read_only', is_actual_food_allowance == 0); + frm.fields_dict['work_detail'].grid.update_docfield_property('lunch', 'read_only', is_actual_food_allowance == 0); + frm.fields_dict['work_detail'].grid.update_docfield_property('dinner', 'read_only', is_actual_food_allowance == 0); + + // Refresh child table + frm.refresh_field('work_detail'); } } }); + } +}); + +frappe.ui.form.on('Work Detail', { + distance_travelled_km: function(frm, cdt, cdn) { + calculate_total_distance_travelled(frm); + set_batta_for_food_allowance(frm, cdt, cdn) }, - batta_type: function(frm) { - set_batta_based_on_options(frm); - frm.doc.work_detail.forEach(function(row) { - frappe.model.set_value(row.doctype, row.name, 'batta_type', frm.doc.batta_type); - }); - frm.refresh_field('work_detail'); - set_batta_based_on_options(frm); - handle_designation_based_on_batta_type(frm); - frm.set_value('batta', 0); - }, - employee: function (frm) { - handle_designation_based_on_batta_type(frm); + daily_batta: function(frm, cdt, cdn) { + calculate_total_batta(frm, cdt, cdn); }, - batta: function (frm) { - // Loop through each row in the work_detail child table to calculate the row values based on the updated batta - frm.doc.work_detail.forEach(function(row) { - if (frm.doc.batta_based_on === 'Daily') { - row.number_of_days = Math.ceil(row.total_hours / 24); - row.daily_batta = row.number_of_days * frm.doc.batta; - } else if (frm.doc.batta_based_on === 'Hours') { - row.daily_batta = (row.total_hours - row.ot_hours) * frm.doc.batta; - } - - row.ot_batta = row.ot_hours * frm.doc.ot_batta; + breakfast: function(frm, cdt, cdn) { + let child = locals[cdt][cdn]; + if (child.breakfast < 0) { + frappe.msgprint({ + message: "Breakfast cannot be negative.", + indicator: "red" + }); - // Refresh the fields for each row in the child table - frm.refresh_field('work_detail'); - }); - // After updating all the rows, recalculate the total values - calculate_totals(frm); + frappe.model.set_value(cdt, cdn, "breakfast", 0); // Reset to 0 + } + calculate_total_food_allowance(frm, cdt, cdn); + calculate_total_batta(frm, cdt, cdn); }, - calculate_batta: function(frm) { - // Ensure designation and total distance are filled before calling the function - if (frm.doc.designation && frm.doc.total_distance_travelled_km) { - // Sum up total_hours from the work_detail child table - let total_hours = 0; - if (frm.doc.work_detail) { - frm.doc.work_detail.forEach(row => { - total_hours += row.total_hours || 0; - }); - } + lunch: function(frm, cdt, cdn) { + let child = locals[cdt][cdn]; + if (child.lunch < 0) { + frappe.msgprint({ + message: "Lunch cannot be negative.", + indicator: "red" + }); - frappe.call({ - method: "beams.beams.doctype.batta_claim.batta_claim.calculate_batta_allowance", - args: { - designation: frm.doc.designation, - is_travelling_outside_kerala: frm.doc.is_travelling_outside_kerala, - is_avail_room_rent: frm.doc.is_avail_room_rent, - is_overnight_stay: frm.doc.is_overnight_stay, - total_distance_travelled_km: frm.doc.total_distance_travelled_km, - total_hours: total_hours, - }, - callback: function(r) { - if (r.message) { - // Set batta values in the form - frm.set_value('batta', r.message.batta); - frm.set_value('room_rent_batta', r.message.room_rent_batta); - frm.set_value('daily_batta_with_overnight_stay', r.message.daily_batta_with_overnight_stay); - frm.set_value('daily_batta_without_overnight_stay', r.message.daily_batta_without_overnight_stay); - frm.set_value('food_allowance', r.message.food_allowance); - } - } + frappe.model.set_value(cdt, cdn, "lunch", 0); // Reset to 0 + } + calculate_total_food_allowance(frm, cdt, cdn); + calculate_total_batta(frm, cdt, cdn); + }, + dinner: function(frm, cdt, cdn) { + let child = locals[cdt][cdn]; + if (child.dinner < 0) { + frappe.msgprint({ + message: "Dinner cannot be negative.", + indicator: "red" }); + + frappe.model.set_value(cdt, cdn, "dinner", 0); // Reset to 0 } - } -}); + calculate_total_food_allowance(frm, cdt, cdn); + calculate_total_batta(frm, cdt, cdn); + }, + total_batta: function(frm, cdt, cdn) { + calculate_total_daily_batta(frm, cdt, cdn); + }, + total_food_allowance: function(frm, cdt, cdn) { + calculate_total_batta(frm, cdt, cdn); + }, + work_detail_add: function(frm, cdt, cdn) { + const { origin, destination } = frm.doc; + frappe.model.set_value(cdt, cdn, 'origin', origin); + frappe.model.set_value(cdt, cdn, 'destination', destination); -frappe.ui.form.on('Work Detail', { - from_date_and_time: function (frm, cdt, cdn) { - validate_dates_and_calculate(frm, cdt, cdn); + calculate_total_distance_travelled(frm); + calculate_total_daily_batta(frm); + calculate_total_hours(frm); }, - to_date_and_time: function (frm, cdt, cdn) { - validate_dates_and_calculate(frm, cdt, cdn); + work_detail_remove: function(frm, cdt, cdn) { + calculate_total_distance_travelled(frm); + calculate_total_daily_batta(frm); + calculate_total_hours(frm); + }, + total_hours: function(frm, cdt, cdn) { + calculate_daily_batta(frm, cdt, cdn); + calculate_total_hours(frm,cdt,cdn); + set_batta_for_food_allowance(frm, cdt, cdn); }, - origin: function(frm) { - update_work_detail(frm); + ot_hours: function(frm, cdt, cdn) { + calculate_daily_batta(frm, cdt, cdn); }, - destination: function(frm) { - update_work_detail(frm); + from_date_and_time: function(frm, cdt, cdn) { + calculate_hours(frm, cdt, cdn); + calculate_daily_batta(frm, cdt, cdn); + set_batta_for_food_allowance(frm, cdt, cdn); + }, + to_date_and_time: function(frm, cdt, cdn) { + calculate_hours(frm, cdt, cdn); + calculate_daily_batta(frm, cdt, cdn); + set_batta_for_food_allowance(frm, cdt, cdn); } }); -/* Function to set options for Batta Based On field based on Batta Type */ -function set_batta_based_on_options(frm) { - if (frm.doc.batta_type === 'External') { - frm.set_df_property('batta_based_on', 'options', 'Hours'); - frm.set_value('batta_based_on', 'Hours'); - } else { - frm.set_df_property('batta_based_on', 'options', ['Daily']); - frm.set_value('batta_based_on', 'Daily'); +function calculate_total_distance_travelled(frm) { + let totalDistance = 0; + frm.doc.work_detail.forEach(row => { + totalDistance += row.distance_travelled_km || 0; + }); + frm.set_value('total_distance_travelled_km', totalDistance); +} + +function calculate_total_hours(frm) { + let totalHours = 0; + frm.doc.work_detail.forEach(row => { + totalHours += row.total_hours || 0; + }); + frm.set_value('total_hours', totalHours); +} + +function calculate_hours(frm, cdt, cdn) { + let row = frappe.get_doc(cdt, cdn); + if (row.from_date_and_time && row.to_date_and_time) { + let total_hours = (new Date(row.to_date_and_time) - new Date(row.from_date_and_time)) / (1000 * 60 * 60); + + frappe.db.get_single_value('Beams Accounts Settings', 'default_working_hours') + .then(default_hours => { + frappe.model.set_value(cdt, cdn, 'total_hours', total_hours.toFixed(2)); + }); + } +} + +function calculate_daily_batta(frm, cdt, cdn) { + let row = frappe.get_doc(cdt, cdn); + + if (!row.total_hours) row.total_hours = 0; + + let number_of_days = Math.max(1, Math.ceil(row.total_hours / 24)); // Ensure at least 1 day + let daily_batta = 0; + + if (frm.doc.batta_based_on === 'Daily') { + daily_batta = number_of_days * (frm.doc.batta || 0); + } + + frappe.model.set_value(cdt, cdn, 'number_of_days', number_of_days); + frappe.model.set_value(cdt, cdn, 'daily_batta', daily_batta); +} + +function update_all_daily_batta(frm) { + if (frm.doc.work_detail) { + frm.doc.work_detail.forEach(row => { + calculate_daily_batta(frm, row.doctype, row.name); + }); } } +function calculate_total_daily_batta(frm) { + let totalDailyBatta = 0; + frm.doc.work_detail.forEach(row => { + totalDailyBatta += row.total_batta || 0; + }); + frm.set_value('total_daily_batta', totalDailyBatta); +} + /* Function to handle designation field based on batta_type */ function handle_designation_based_on_batta_type(frm) { if (frm.doc.batta_type === 'Internal' && frm.doc.employee) { @@ -183,148 +270,116 @@ function handle_designation_based_on_batta_type(frm) { frappe.msgprint(__('Designation not found for the selected employee.')); } }); - // Clear designation for External } else if (frm.doc.batta_type === 'External') { frm.set_value('designation', ''); } } - -/* Function to validate dates and perform calculations */ -function validate_dates_and_calculate(frm, cdt, cdn) { - let row = locals[cdt][cdn]; - - if (row.from_date_and_time && row.to_date_and_time) { - let from_time = new Date(row.from_date_and_time); - let to_time = new Date(row.to_date_and_time); - - // Validation: From date should not be greater than to date - if (from_time > to_time) { - frappe.msgprint(__('From Date and Time cannot be greater than To Date and Time')); - frappe.model.set_value(cdt, cdn, 'to_date_and_time', ''); - return; - } - calculate_hours_and_totals(frm, cdt, cdn); - } -} - -/* Function to calculate hours and batta */ -function calculate_hours_and_totals(frm, cdt, cdn) { - let row = locals[cdt][cdn]; - - if (row.from_date_and_time && row.to_date_and_time) { - let from_time = new Date(row.from_date_and_time); - let to_time = new Date(row.to_date_and_time); - let diff = (to_time - from_time) / (1000 * 60 * 60); // Difference in hours - - if (diff >= 0) { - frappe.db.get_single_value('Beams Accounts Settings', 'default_working_hours') - .then(default_working_hours => { - row.total_hours = diff; - row.ot_hours = diff > default_working_hours ? diff - default_working_hours : 0; - - if (frm.doc.batta_based_on === 'Daily') { - row.number_of_days = Math.ceil(row.total_hours / 24); - row.daily_batta = row.number_of_days * frm.doc.batta; - } else if (frm.doc.batta_based_on === 'Hours') { - row.number_of_days = Math.ceil(row.total_hours / 24); - row.daily_batta = (row.total_hours - row.ot_hours) * frm.doc.batta; - } - - row.ot_batta = row.ot_hours * frm.doc.ot_batta; - - frm.refresh_field('work_detail'); - calculate_totals(frm); - }); - } +function set_batta_based_on_options(frm) { + if (frm.doc.batta_type === 'External') { + frm.set_df_property('batta_based_on', 'options', 'Hours'); + frm.set_value('batta_based_on', 'Hours'); + } else { + frm.set_df_property('batta_based_on', 'options', ['Daily']); + frm.set_value('batta_based_on', 'Daily'); } } -/* Function to calculate total batta values */ -function calculate_totals(frm) { - frm.call({ - method: "calculate_total_batta", - doc: frm.doc, - callback: function(response) { - // Update the form fields with the calculated totals - frm.set_value({ - 'total_daily_batta': response.message.total_daily_batta, - 'total_ot_batta': response.message.total_ot_batta, - 'total_driver_batta': response.message.total_driver_batta - }); +function calculate_batta(frm) { + let total_batta = (frm.doc.room_rent_batta || 0) + + (frm.doc.daily_batta_without_overnight_stay || 0) + + (frm.doc.daily_batta_with_overnight_stay || 0); - // Refresh the fields to update totals - frm.refresh_field(['total_daily_batta', 'total_ot_batta', 'total_driver_batta']); - } - }); + frm.set_value('batta', total_batta); } -/* Function to calculate batta values */ -function calculate_batta_totals(frm) { - frm.call({ - method: "calculate_batta", // Replace with actual path to the Python function - doc: frm.doc, - callback: function(response) { - // Update the form fields with the calculated totals from the Python method - frm.set_value({ - 'room_rent_batta': response.message.room_rent_batta, - 'daily_batta_with_overnight_stay': response.message.daily_batta_with_overnight_stay, - 'daily_batta_without_overnight_stay': response.message.daily_batta_without_overnight_stay, - 'food_allowance': response.message.food_allowance, - 'batta': response.message.batta // Assuming 'batta' is the total batta field - }); +function calculate_allowance(frm) { + if (!frm.doc.designation.length) { + frappe.msgprint(__("Please select a designation.")); + return; + } - // Refresh the fields to display updated totals - frm.refresh_field(['room_rent_batta', 'daily_batta_with_overnight_stay', 'daily_batta_without_overnight_stay', 'food_allowance', 'batta']); + frappe.call({ + method: "beams.beams.doctype.batta_claim.batta_claim.calculate_batta_allowance", + args: { + designation: frm.doc.designation, + is_travelling_outside_kerala: frm.doc.is_travelling_outside_kerala || 0, + is_overnight_stay: frm.doc.is_overnight_stay || 0, + is_avail_room_rent: frm.doc.is_avail_room_rent || 0, + total_distance_travelled_km: frm.doc.total_distance_travelled_km || 0, + total_hours: frm.doc.total_hours || 0 + }, + callback: function(r) { + if (r.message) { + frm.set_value("room_rent_batta", r.message.room_rent_batta); + frm.set_value("daily_batta_with_overnight_stay", r.message.daily_batta_with_overnight_stay); + frm.set_value("daily_batta_without_overnight_stay", r.message.daily_batta_without_overnight_stay); + frm.set_value("batta", r.message.batta); + } } }); } -frappe.ui.form.on('Batta Claim', { - origin: update_child_table, - destination: update_child_table -}); - -function update_child_table(frm) { - if (!frm.doc.work_detail) return; - - frm.doc.work_detail.forEach(row => { - frappe.model.set_value(row.doctype, row.name, 'origin', frm.doc.origin || ''); - frappe.model.set_value(row.doctype, row.name, 'destination', frm.doc.destination || ''); - }); -} +function set_batta_for_food_allowance(frm, cdt, cdn) { + let child = locals[cdt][cdn]; + let designation = frm.doc.designation; + let is_overnight_stay = frm.doc.is_overnight_stay; + let is_delhi_bureau = frm.doc.is_delhi_bureau; -frappe.ui.form.on('Work Detail', { - work_detail_add: function(frm, cdt, cdn) { - frappe.model.set_value(cdt, cdn, 'origin', frm.doc.origin || ''); - frappe.model.set_value(cdt, cdn, 'destination', frm.doc.destination || ''); + if (designation.length <= 0) { + return; } -}); + let is_eligible = false; -function calculate_total_distance_travelled(frm) { - let totalDistance = 0; - // Sum all distance_travelled_km from the Work Detail child table - frm.doc.work_detail.forEach(function(row) { - if (row.distance_travelled_km) { - totalDistance += row.distance_travelled_km; + if (is_delhi_bureau) { + if (child.distance_travelled_km >= 30 && child.total_hours > 4) { + is_eligible = true; } - }); - // Set the total_distance_travelled_km field with the calculated sum - frm.set_value('total_distance_travelled_km', totalDistance); + } else { + if (child.distance_travelled_km >= 50 && child.distance_travelled_km <= 100 && child.total_hours > 6) { + is_eligible = true; + } + } + + if (is_overnight_stay) { + frappe.model.set_value(child.doctype, child.name, "breakfast", 0); + frappe.model.set_value(child.doctype, child.name, "lunch", 0); + frappe.model.set_value(child.doctype, child.name, "dinner", 0); + frappe.model.set_value(child.doctype, child.name, "total_food_allowance", 0); + return; + } + else if (is_eligible && !is_overnight_stay) { + frappe.call({ + method: "beams.beams.doctype.batta_claim.batta_claim.get_batta_for_food_allowance", + args: { + designation: designation, + from_date_time: child.from_date_and_time, + to_date_time: child.to_date_and_time, + total_hrs: child.total_hours, + is_delhi_bureau: is_delhi_bureau + }, + callback: function (r) { + if (r && r.message) { + let response = r.message; + frappe.model.set_value(child.doctype, child.name, "breakfast", response.break_fast); + frappe.model.set_value(child.doctype, child.name, "lunch", response.lunch); + frappe.model.set_value(child.doctype, child.name, "dinner", response.dinner); + frappe.model.set_value(child.doctype, child.name, "total_food_allowance", response.break_fast + response.lunch + response.dinner); + } + } + }); + } } -function update_work_detail(frm) { - const { origin, destination, work_detail } = frm.doc; +function calculate_total_food_allowance(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + row.total_food_allowance = (row.breakfast || 0) + (row.lunch || 0) + (row.dinner || 0); - // Update existing child rows with parent values - work_detail.forEach((row, index) => { - if (index >= 0) { - if (!row.origin || !row.destination) { - frappe.model.set_value(row.doctype, row.name, 'origin', origin); - frappe.model.set_value(row.doctype, row.name, 'destination', destination); - } - } - }); + frm.refresh_field("work_detail"); +} - frm.refresh_field('work_detail'); +function calculate_total_batta(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + frappe.model.set_value(cdt, cdn, "total_batta", (row.daily_batta || 0) + (row.total_food_allowance || 0)); + frm.refresh_field("work_detail"); } diff --git a/beams/beams/doctype/batta_claim/batta_claim.json b/beams/beams/doctype/batta_claim/batta_claim.json index a0448321d..48e0641a4 100644 --- a/beams/beams/doctype/batta_claim/batta_claim.json +++ b/beams/beams/doctype/batta_claim/batta_claim.json @@ -15,6 +15,7 @@ "designation", "company", "bureau", + "is_delhi_bureau", "purpose", "column_break_lgjy", "cost_centre", @@ -37,13 +38,12 @@ "ot_batta", "section_break_osak", "work_detail", + "section_break_ozsf", "total_distance_travelled_km", + "column_break_fxzl", + "total_hours", "section_break_nsff", - "total_daily_batta", - "column_break_vksq", - "total_ot_batta", - "column_break_ofhv", - "total_driver_batta" + "total_daily_batta" ], "fields": [ { @@ -88,7 +88,8 @@ "fieldname": "designation", "fieldtype": "Link", "label": "Designation", - "options": "Designation" + "options": "Designation", + "read_only": 1 }, { "depends_on": "eval:doc.batta_type == \"External\"\n", @@ -142,27 +143,6 @@ "label": "Total Batta", "read_only": 1 }, - { - "depends_on": "eval:doc.batta_type == \"External\"", - "fieldname": "total_ot_batta", - "fieldtype": "Currency", - "label": "Total OT Batta", - "read_only": 1 - }, - { - "fieldname": "total_driver_batta", - "fieldtype": "Currency", - "label": "Total Driver Batta", - "read_only": 1 - }, - { - "fieldname": "column_break_vksq", - "fieldtype": "Column Break" - }, - { - "fieldname": "column_break_ofhv", - "fieldtype": "Column Break" - }, { "fieldname": "work_detail", "fieldtype": "Table", @@ -205,12 +185,6 @@ "fieldtype": "Small Text", "label": "Purpose" }, - { - "fieldname": "total_distance_travelled_km", - "fieldtype": "Float", - "label": "Total Distance Travelled (KM)", - "read_only": 1 - }, { "depends_on": "eval: doc.batta_type == 'Internal'", "fieldname": "mode_of_travelling", @@ -277,6 +251,32 @@ "fieldname": "is_avail_room_rent", "fieldtype": "Check", "label": "Is Avail Room Rent" + }, + { + "fieldname": "section_break_ozsf", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_fxzl", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_hours", + "fieldtype": "Float", + "label": "Total Hours", + "read_only": 1 + }, + { + "fieldname": "total_distance_travelled_km", + "fieldtype": "Float", + "label": "Total Distance Travelled(KM)", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "is_delhi_bureau", + "fieldtype": "Check", + "label": "Is Delhi Bureau" } ], "index_web_pages_for_search": 1, @@ -291,7 +291,7 @@ "link_fieldname": "batta_claim_reference" } ], - "modified": "2025-03-22 12:26:10.349038", + "modified": "2025-03-25 11:00:55.832977", "modified_by": "Administrator", "module": "BEAMS", "name": "Batta Claim", diff --git a/beams/beams/doctype/batta_claim/batta_claim.py b/beams/beams/doctype/batta_claim/batta_claim.py index a2e6e6fa3..11192180c 100644 --- a/beams/beams/doctype/batta_claim/batta_claim.py +++ b/beams/beams/doctype/batta_claim/batta_claim.py @@ -3,6 +3,7 @@ import frappe import json +from frappe.utils import getdate, get_datetime, date_diff from frappe.model.document import Document @@ -15,21 +16,10 @@ def on_submit(self): self.create_journal_entry_from_batta_claim() def validate(self): - # Call the method to calculate the total distance travelled self.calculate_total_distance_travelled() - - def calculate_total_distance_travelled(self): - total_distance = 0 - - # Loop through the rows in the 'work_detail' child table - if self.work_detail: - for row in self.work_detail: - if row.distance_travelled_km: - total_distance += row.distance_travelled_km - - # Set the 'total_distance_travelled_km' field with the calculated sum - self.total_distance_travelled_km = total_distance - + self.calculate_total_daily_batta() + self.calculate_batta() + self.calculate_total_hours() def create_purchase_invoice_from_batta_claim(self): ''' @@ -84,50 +74,61 @@ def create_journal_entry_from_batta_claim(self): journal_entry.submit() frappe.msgprint(f"Journal Entry {journal_entry.name} has been created successfully.", alert=True,indicator="green") - @frappe.whitelist() - def calculate_total_batta(doc): - '''Function to calculate the Total Daily Batta based on data in work detail child table - and batta + def calculate_total_distance_travelled(self): + ''' + Calculation of Total Distance Travelled(km) + ''' + total_distance = 0 + + if self.work_detail: + for row in self.work_detail: + if row.distance_travelled_km: + total_distance += row.distance_travelled_km + + # Set the 'total_distance_travelled_km' field with the calculated sum + self.total_distance_travelled_km = total_distance + + def calculate_total_hours(self): + ''' + Calculation Of Total Hours + ''' + total_hours = 0 + + if self.work_detail: + for row in self.work_detail: + if row.total_hours: + total_hours += float(row.total_hours) + + self.total_hours = total_hours + + def calculate_total_daily_batta(self): + ''' + Calculation of Total Daily Batta ''' total_daily_batta = 0 - total_ot_batta = 0 - - # Loop through the work_detail child table and ensure default values are integers - for row in doc.get('work_detail', []): - total_daily_batta += row.get('daily_batta', 0) or 0 - total_ot_batta += row.get('ot_batta', 0) or 0 - - # Total batta is the sum of total_daily_batta and total_ot_batta - total_driver_batta = total_daily_batta + total_ot_batta - return { - 'total_daily_batta': total_daily_batta, - 'total_ot_batta': total_ot_batta, - 'total_driver_batta': total_driver_batta - } - - @frappe.whitelist() - def calculate_batta(doc): - # Ensure that all fields default to 0 if they are None - room_rent_batta = doc.get('room_rent_batta', 0) or 0 - daily_batta_with_overnight_stay = doc.get('daily_batta_with_overnight_stay', 0) or 0 - daily_batta_without_overnight_stay = doc.get('daily_batta_without_overnight_stay', 0) or 0 - food_allowance = doc.get('food_allowance', 0) or 0 - - # Calculate the total batta - batta = room_rent_batta + daily_batta_with_overnight_stay + daily_batta_without_overnight_stay + food_allowance - - return { - 'room_rent_batta': room_rent_batta, - 'daily_batta_with_overnight_stay': daily_batta_with_overnight_stay, - 'daily_batta_without_overnight_stay': daily_batta_without_overnight_stay, - 'food_allowance': food_allowance, - 'batta': batta - } - -# Batta Policy + + if self.work_detail: + for row in self.work_detail: + if row.total_batta: + total_daily_batta += row.total_batta + + # Set the 'total_distance_travelled_km' field with the calculated sum + self.total_daily_batta = total_daily_batta + + def calculate_batta(self): + ''' + Calculation of Total Batta based on room rent batta,daily batta with overnight stay and daily batta without Overnight stay + ''' + self.batta = (self.room_rent_batta or 0) \ + + (self.daily_batta_without_overnight_stay or 0) \ + + (self.daily_batta_with_overnight_stay or 0) + @frappe.whitelist() -def calculate_batta_allowance(designation, is_travelling_outside_kerala, is_overnight_stay,is_avail_room_rent, total_distance_travelled_km, total_hours): - # Ensure distance and total_hours are floats or 0 +def calculate_batta_allowance(designation=None, is_travelling_outside_kerala=0, is_overnight_stay=0, is_avail_room_rent=0, total_distance_travelled_km=0, total_hours=0): + ''' + Calculation Of Total Batta Allowance based on Batta Policy + ''' + # Convert inputs to proper types total_distance_travelled_km = float(total_distance_travelled_km or 0) total_hours = float(total_hours or 0) @@ -138,78 +139,126 @@ def calculate_batta_allowance(designation, is_travelling_outside_kerala, is_over return {"batta": 0} policy = batta_policy[0] - is_actual_room_rent = policy.get('is_actual') # Checkbox for Room Rent for Overnight Stay - is_actual_daily_batta_with_overnight_stay = policy.get('is_actual_') # Checkbox for Daily Batta With Overnight Stay - is_actual_daily_batta_without_overnight_stay = policy.get('is_actual__') # Checkbox for Daily Batta Without Overnight Stay - is_actual_food_allowance = policy.get('is_actual___') # Get the first (and only) policy for the designation - total_batta = 0 - # Safely handle NoneType by using a function - def safe_add(value): - return float(value) if value is not None else 0 + # Get policy checkbox values + is_actual_room_rent = policy.get('is_actual') or 0 # Room Rent Checkbox + is_actual_daily_batta = policy.get('is_actual_') or 0 # Daily Batta with Overnight Stay Checkbox + is_actual_daily_batta_without_overnight = policy.get('is_actual__') or 0 # Daily Batta Without Overnight Stay Checkbox - # Convert inputs to booleans + # Convert inputs to boolean is_travelling_outside_kerala = bool(int(is_travelling_outside_kerala or 0)) is_overnight_stay = bool(int(is_overnight_stay or 0)) is_avail_room_rent = bool(int(is_avail_room_rent or 0)) - # Initialize daily batta and room rent variables - daily_batta_without_overnight_stay = 0 + # Initialize batta values room_rent_batta = 0 daily_batta_with_overnight_stay = 0 - food_allowance = 0 - - # Add Daily Batta (Inside Kerala) if distance >= 50 km and total_hours >= 8 - if total_distance_travelled_km >= 50 and total_hours >= 8: - if is_actual_daily_batta_without_overnight_stay == 0: - if not is_overnight_stay: # Ensure the 'is_overnight_stay' checkbox is not checked - if is_travelling_outside_kerala: - outside_kerala_batta = safe_add(policy.get('outside_kerala')) - daily_batta_without_overnight_stay += outside_kerala_batta - else: - inside_kerala_batta = safe_add(policy.get('inside_kerala')) - daily_batta_without_overnight_stay += inside_kerala_batta - - # Add to total_batta - total_batta += daily_batta_without_overnight_stay - - if is_overnight_stay: - if is_avail_room_rent: - # Handle room rent addition - if is_actual_room_rent == 0: # Add room rent only if checkbox is unchecked (value is 0) - if is_travelling_outside_kerala: - room_rent = safe_add(policy.get('outside_kerala_')) - else: - room_rent = safe_add(policy.get('inside_kerala_')) - room_rent_batta += room_rent # Add room rent value to room_rent_batta + daily_batta_without_overnight_stay = 0 - # Handle daily batta with overnight stay addition - if is_actual_daily_batta_with_overnight_stay == 0: # Add daily batta only if checkbox is unchecked (value is 0) + # Calculate Room Rent Batta + if is_overnight_stay and is_avail_room_rent: + if not is_actual_room_rent: # Check if policy is not actual if is_travelling_outside_kerala: - daily_batta_with_overnight_stay = safe_add(policy.get('outside_kerala__')) + room_rent_batta = float(policy.get('outside_kerala_', 0)) else: - daily_batta_with_overnight_stay = safe_add(policy.get('inside_kerala__')) + room_rent_batta = float(policy.get('inside_kerala_', 0)) - # Add Room Rent and Daily Batta with Overnight Stay to total_batta - total_batta += room_rent_batta - total_batta += daily_batta_with_overnight_stay + # Calculate Daily Batta with Overnight Stay + if not is_actual_daily_batta: # Check if policy is not actual + if is_overnight_stay: + if is_travelling_outside_kerala: + daily_batta_with_overnight_stay = float(policy.get('outside_kerala_', 0)) + else: + daily_batta_with_overnight_stay = float(policy.get('inside_kerala_', 0)) - # Add Food Allowance if total distance is >= 25 km and total_hours >= 6 - if total_distance_travelled_km >= 25 and total_hours >= 6: - if is_actual_food_allowance == 0: - food_allowance = safe_add(policy.get('break_fast')) + safe_add(policy.get('lunch')) + safe_add(policy.get('dinner')) - total_batta += food_allowance + # Calculate Daily Batta without Overnight Stay + if not is_actual_daily_batta_without_overnight: # Check if policy is not actual + if not is_overnight_stay: # Ensure overnight stay is NOT checked + if total_distance_travelled_km > 100 and total_hours >= 8: # Additional condition + if is_travelling_outside_kerala: + daily_batta_without_overnight_stay = float(policy.get('outside_kerala', 0)) + else: + daily_batta_without_overnight_stay = float(policy.get('inside_kerala', 0)) - # Return all relevant values in a single dictionary return { "room_rent_batta": room_rent_batta, "daily_batta_with_overnight_stay": daily_batta_with_overnight_stay, - "daily_batta_without_overnight_stay": daily_batta_without_overnight_stay, - "food_allowance": food_allowance, - "batta": total_batta + "daily_batta_without_overnight_stay": daily_batta_without_overnight_stay } @frappe.whitelist() def get_batta_policy_values(): + ''' + Fetch and return the batta policy values from the 'Batta Policy' doctype + ''' result = frappe.db.get_value('Batta Policy', {}, ['is_actual', 'is_actual_', 'is_actual__', 'is_actual___'], as_dict=True) return result + +@frappe.whitelist() +def get_batta_for_food_allowance(designation, from_date_time, to_date_time, total_hrs, is_delhi_bureau=False): + ''' + Method to get Batta for Food + ''' + values = { 'break_fast':0, 'lunch':0, 'dinner':0 } + batta_policy = frappe.db.exists('Batta Policy', { 'designation':designation }) + from_date_time = get_datetime(from_date_time) + to_date_time = get_datetime(to_date_time) + required_hours = 4 if is_delhi_bureau else 6 + if batta_policy and float(total_hrs)>required_hours: + is_actual = frappe.db.get_value('Batta Policy', batta_policy, 'is_actual___') + if is_actual: + return values + break_fast, lunch, dinner = frappe.db.get_value('Batta Policy', batta_policy, ['break_fast', 'lunch', 'dinner']) + same_date = False + if getdate(from_date_time) == getdate(to_date_time): + same_date = True + #Breakfast check + if same_date: + date_threshold = getdate(from_date_time) + break_fast_start_time = get_datetime('{0} {1}'.format(date_threshold, '04:00')) + break_fast_end_time = get_datetime('{0} {1}'.format(date_threshold, '09:00')) + if (from_date_time <= break_fast_start_time <= to_date_time) or (from_date_time <= break_fast_end_time <= to_date_time): + values['break_fast'] = break_fast + lunch_start_time = get_datetime('{0} {1}'.format(date_threshold, '12:30')) + lunch_end_time = get_datetime('{0} {1}'.format(date_threshold, '14:00')) + if (from_date_time <= lunch_start_time <= to_date_time) or (from_date_time <= lunch_end_time <= to_date_time): + values['lunch'] = lunch + dinner_start_time = get_datetime('{0} {1}'.format(date_threshold, '18:00')) + dinner_end_time = get_datetime('{0} {1}'.format(date_threshold, '21:00')) + if (from_date_time <= dinner_start_time <= to_date_time) or (from_date_time <= dinner_end_time <= to_date_time): + values['dinner'] = dinner + else: + #Breakfast check + date_threshold = getdate(from_date_time) #Check with Start Date + break_fast_start_time = get_datetime('{0} {1}'.format(date_threshold, '04:00')) + break_fast_end_time = get_datetime('{0} {1}'.format(date_threshold, '09:00')) + if (from_date_time <= break_fast_start_time <= to_date_time) or (from_date_time <= break_fast_end_time <= to_date_time): + values['break_fast'] = break_fast + date_threshold = getdate(to_date_time) #Check with End Date + break_fast_start_time = get_datetime('{0} {1}'.format(date_threshold, '04:00')) + break_fast_end_time = get_datetime('{0} {1}'.format(date_threshold, '09:00')) + if (from_date_time <= break_fast_start_time <= to_date_time) or (from_date_time <= break_fast_end_time <= to_date_time): + values['break_fast'] = values.get('break_fast', 0) + break_fast + #Lunch Check + date_threshold = getdate(from_date_time) #Check with Start Date + lunch_start_time = get_datetime('{0} {1}'.format(date_threshold, '12:30')) + lunch_end_time = get_datetime('{0} {1}'.format(date_threshold, '14:00')) + if (from_date_time <= lunch_start_time <= to_date_time) or (from_date_time <= lunch_end_time <= to_date_time): + values['lunch'] = lunch + date_threshold = getdate(to_date_time) #Check with End Date + lunch_start_time = get_datetime('{0} {1}'.format(date_threshold, '12:30')) + lunch_end_time = get_datetime('{0} {1}'.format(date_threshold, '14:00')) + if (from_date_time <= lunch_start_time <= to_date_time) or (from_date_time <= lunch_end_time <= to_date_time): + values['lunch'] = values.get('lunch', 0) + lunch + #Dinner Check + date_threshold = getdate(from_date_time) #Check with Start Date + dinner_start_time = get_datetime('{0} {1}'.format(date_threshold, '18:00')) + dinner_end_time = get_datetime('{0} {1}'.format(date_threshold, '21:00')) + if (from_date_time <= dinner_start_time <= to_date_time) or (from_date_time <= dinner_end_time <= to_date_time): + values['dinner'] = dinner + date_threshold = getdate(to_date_time) #Check with End Date + dinner_start_time = get_datetime('{0} {1}'.format(date_threshold, '18:00')) + dinner_end_time = get_datetime('{0} {1}'.format(date_threshold, '21:00')) + if (from_date_time <= dinner_start_time <= to_date_time) or (from_date_time <= dinner_end_time <= to_date_time): + values['dinner'] = values.get('dinner', 0) + dinner + return values diff --git a/beams/beams/doctype/work_detail/work_detail.json b/beams/beams/doctype/work_detail/work_detail.json index 12575486b..60ced23c0 100644 --- a/beams/beams/doctype/work_detail/work_detail.json +++ b/beams/beams/doctype/work_detail/work_detail.json @@ -26,22 +26,22 @@ "section_break_wigg", "daily_batta", "column_break_uihk", - "ot_batta", "column_break_cjsx", "total_batta", "section_break_elqj", "purpose", - "total_hours", - "ot_hours" + "total_hours" ], "fields": [ { + "columns": 2, "fieldname": "from_date_and_time", "fieldtype": "Datetime", "in_list_view": 1, "label": "Start Date and Time" }, { + "columns": 2, "fieldname": "to_date_and_time", "fieldtype": "Datetime", "in_list_view": 1, @@ -54,14 +54,6 @@ "label": "Total Hours", "read_only": 1 }, - { - "depends_on": "eval:doc.batta_type == 'External'", - "fieldname": "ot_hours", - "fieldtype": "Float", - "in_list_view": 1, - "label": "OT Hours", - "read_only": 1 - }, { "description": "Daily Batta Without Food Allowance", "fieldname": "daily_batta", @@ -70,14 +62,6 @@ "label": " Batta", "read_only": 1 }, - { - "depends_on": "eval:doc.batta_type == 'External'", - "fieldname": "ot_batta", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "OT Batta", - "read_only": 1 - }, { "fieldname": "number_of_days", "fieldtype": "Int", @@ -100,6 +84,7 @@ "label": "Purpose" }, { + "columns": 1, "fieldname": "origin", "fieldtype": "Link", "in_list_view": 1, @@ -107,6 +92,7 @@ "options": "Location" }, { + "columns": 1, "fieldname": "destination", "fieldtype": "Link", "in_list_view": 1, @@ -114,6 +100,7 @@ "options": "Location" }, { + "columns": 2, "fieldname": "distance_travelled_km", "fieldtype": "Float", "in_list_view": 1, @@ -148,7 +135,7 @@ { "fieldname": "breakfast", "fieldtype": "Currency", - "label": "BreakFast", + "label": "Breakfast", "precision": "2" }, { @@ -180,7 +167,8 @@ "fieldname": "total_food_allowance", "fieldtype": "Currency", "label": "Total Food Allowance", - "precision": "2" + "precision": "2", + "read_only": 1 }, { "fieldname": "column_break_cjsx", @@ -197,7 +185,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-03-22 12:48:25.552974", + "modified": "2025-03-26 11:50:57.516655", "modified_by": "Administrator", "module": "BEAMS", "name": "Work Detail", From f84099ccc100058a5df8b85398b204731f029427 Mon Sep 17 00:00:00 2001 From: Arathi Date: Wed, 26 Mar 2025 13:23:19 +0530 Subject: [PATCH 17/40] Resolved Conflicts --- .../bureau_trip_sheet/bureau_trip_sheet.js | 230 +++++++++++++++++- .../bureau_trip_sheet/bureau_trip_sheet.json | 77 +++++- .../bureau_trip_sheet/bureau_trip_sheet.py | 74 +++++- .../doctype/trip_details/trip_details.json | 25 +- 4 files changed, 385 insertions(+), 21 deletions(-) diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js index e3625a567..72ead7da8 100644 --- a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js @@ -1,14 +1,114 @@ // Copyright (c) 2024, efeone and contributors // For license information, please see license.txt +frappe.ui.form.on('Trip Details', { + from_date_and_time: function (frm, cdt, cdn) { + calculate_hours_and_days(frm, cdt, cdn); + }, + to_date_and_time: function (frm, cdt, cdn) { + let row = locals[cdt][cdn]; + + if (row.from_date_and_time && row.to_date_and_time) { + let from_date = new Date(row.from_date_and_time); + let to_date = new Date(row.to_date_and_time); + + if (to_date <= from_date) { + frappe.msgprint(__('To Date & Time must be greater than From Date & Time')); + frappe.model.set_value(cdt, cdn, 'to_date_and_time', null); + return; + } + } + + calculate_hours_and_days(frm, cdt, cdn); + }, + total_hours: function (frm, cdt, cdn) { + calculate_daily_batta(frm, cdt, cdn); + calculate_ot_batta(frm, cdt, cdn); + }, + ot_hours: function (frm, cdt, cdn) { + calculate_ot_batta(frm, cdt, cdn); + }, + breakfast: function (frm, cdt, cdn) { + calculate_total_food_allowance(frm, cdt, cdn); + }, + lunch: function (frm, cdt, cdn) { + calculate_total_food_allowance(frm, cdt, cdn); + }, + dinner: function (frm, cdt, cdn) { + calculate_total_food_allowance(frm, cdt, cdn); + }, + distance_travelled_km: function(frm, cdt, cdn) { + calculate_total_distance_travelled(frm, cdt, cdn); + }, + work_details_add: function(frm, cdt, cdn) { + calculate_total_distance_travelled(frm, cdt, cdn); + calculate_hours(frm, cdt, cdn); + calculate_total_daily_batta(frm, cdt, cdn); + calculate_total_ot_batta(frm, cdt, cdn); + }, + work_details_remove: function(frm, cdt, cdn) { + calculate_total_distance_travelled(frm, cdt, cdn); + calculate_hours(frm, cdt, cdn); + calculate_total_daily_batta(frm, cdt, cdn); + calculate_total_ot_batta(frm, cdt, cdn); + }, + total_hours: function(frm, cdt, cdn) { + calculate_hours(frm, cdt, cdn); + }, + daily_batta: function(frm, cdt, cdn) { + calculate_total_food_allowance(frm, cdt, cdn); + }, + total_batta: function(frm, cdt, cdn) { + calculate_total_daily_batta(frm, cdt, cdn); + }, + ot_batta: function(frm, cdt, cdn) { + calculate_total_ot_batta(frm, cdt, cdn); + } + +}); + frappe.ui.form.on("Bureau Trip Sheet", { refresh: function (frm) { filter_supplier_field(frm); + update_all_daily_batta(frm); + update_all_ot_batta(frm); + }, + validate: function (frm) { + update_all_daily_batta(frm); + update_all_ot_batta(frm); + calculate_batta(frm); + calculate_total_distance_travelled(frm); + calculate_hours(frm); + calculate_total_daily_batta(frm); + calculate_total_ot_batta(frm); + }, + batta: function (frm) { + update_all_daily_batta(frm); + }, + ot_batta: function (frm) { + update_all_ot_batta(frm); + }, + daily_batta_with_overnight_stay: function (frm) { + calculate_batta(frm); + }, + daily_batta_without_overnight_stay: function (frm) { + calculate_batta(frm); + }, + total_daily_batta: function (frm) { + calculate_total_driver_batta(frm); + }, + total_ot_batta: function (frm) { + calculate_total_driver_batta(frm); + }, + is_overnight_stay: function (frm) { + calculate_daily_batta(frm); + }, + is_travelling_outside_kerala: function (frm) { + calculate_daily_batta(frm); } }); -// Function to filter the supplier field function filter_supplier_field(frm) { frm.set_query("supplier", function () { return { @@ -18,3 +118,131 @@ function filter_supplier_field(frm) { }; }); } + +function calculate_hours_and_days(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + + if (row.from_date_and_time && row.to_date_and_time) { + let from_date = new Date(row.from_date_and_time); + let to_date = new Date(row.to_date_and_time); + + let total_hours = (to_date - from_date) / (1000 * 60 * 60); + let number_of_days = Math.ceil(total_hours / 24); + + frappe.db.get_single_value('Beams Accounts Settings', 'default_working_hours') + .then(default_hours => { + default_hours = parseFloat(default_hours) || 0; + let ot_hours = Math.max(0, total_hours - default_hours); + + frappe.model.set_value(cdt, cdn, 'total_hours', total_hours.toFixed(2)); + frappe.model.set_value(cdt, cdn, 'ot_hours', ot_hours.toFixed(2)); + frappe.model.set_value(cdt, cdn, 'number_of_days', number_of_days); + + setTimeout(() => { + calculate_daily_batta(frm, cdt, cdn); + calculate_ot_batta(frm, cdt, cdn); + }, 200); + }); + } +} + +function calculate_daily_batta(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + + let total_hours = row.total_hours || 0; + let number_of_days = Math.ceil(total_hours / 24); + let daily_batta = number_of_days * (frm.doc.batta || 0); + + frappe.model.set_value(cdt, cdn, 'daily_batta', daily_batta); + frm.refresh_field('work_details'); +} + +function calculate_ot_batta(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + + let ot_hours = row.ot_hours || 0; + let ot_batta = ot_hours * (frm.doc.ot_batta || 0); + + frappe.model.set_value(cdt, cdn, 'ot_batta', ot_batta); + frm.refresh_field('work_details'); +} + +function update_all_daily_batta(frm) { + if (frm.doc.work_details) { + frm.doc.work_details.forEach(row => { + calculate_daily_batta(frm, row.doctype, row.name); + }); + setTimeout(() => { + frm.refresh_field('work_details'); + }, 200); + } +} + +function update_all_ot_batta(frm) { + if (frm.doc.work_details) { + frm.doc.work_details.forEach(row => { + calculate_ot_batta(frm, row.doctype, row.name); + }); + setTimeout(() => { + frm.refresh_field('work_details'); + }, 200); + } +} + +function calculate_batta(frm) { + let total_batta = (frm.doc.daily_batta_with_overnight_stay || 0) + + (frm.doc.daily_batta_without_overnight_stay || 0); + + frappe.model.set_value(frm.doctype, frm.docname, 'batta', total_batta); +} + +function calculate_total_food_allowance(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + row.total_food_allowance = (row.breakfast || 0) + (row.lunch || 0) + (row.dinner || 0); + row.total_batta = row.total_food_allowance + (row.daily_batta || 0); + + frm.refresh_field("work_details"); + + // Bureau Trip Sheet Doctype-ile total_daily_batta update cheyyan + let total_batta_sum = frm.doc.work_details.reduce((sum, r) => sum + (r.total_batta || 0), 0); + frm.set_value("total_daily_batta", total_batta_sum); +} + +function calculate_total_distance_travelled(frm) { + let totalDistance = 0; + frm.doc.work_details.forEach(row => { + totalDistance += row.distance_travelled_km || 0; + }); + frm.set_value('total_distance_travelled_km', totalDistance); +} + +function calculate_hours(frm) { + let totalHours = 0; + frm.doc.work_details.forEach(row => { + totalHours += row.total_hours || 0; + }); + frm.set_value('total_hours', totalHours); +} + +function calculate_total_daily_batta(frm) { + let totalBatta = 0; + frm.doc.work_details.forEach(row => { + totalBatta += row.total_batta || 0; + }); + frm.set_value('total_daily_batta', totalBatta); +} + +function calculate_total_ot_batta(frm) { + let totalOtBatta = 0; + frm.doc.work_details.forEach(row => { + totalOtBatta += row.ot_batta || 0; + }); + frm.set_value('total_ot_batta', totalOtBatta); +} + +function calculate_total_driver_batta(frm) { + let total_daily_batta = frm.doc.total_daily_batta || 0; + let total_ot_batta = frm.doc.total_ot_batta || 0; + + frm.set_value('total_driver_batta', total_daily_batta + total_ot_batta); +} diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json index 75fbe898e..d877d33a3 100644 --- a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json @@ -21,13 +21,23 @@ "batta", "section_break_tjbr", "work_details", + "section_break_xxsb", + "total_distance_travelled_km", + "column_break_fgob", + "total_hours", "trip_details_section", "departure_location", "destination_location", "column_break_cdph", "ending_date_and_time", "starting_date_and_time", - "amended_from" + "amended_from", + "section_break_dwds", + "total_daily_batta", + "column_break_zjgd", + "total_ot_batta", + "column_break_jevl", + "total_driver_batta" ], "fields": [ { @@ -40,7 +50,8 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Supplier", - "options": "Supplier" + "options": "Supplier", + "reqd": 1 }, { "fieldname": "company", @@ -62,9 +73,11 @@ "fetch_from": "supplier.ot_batta", "fieldname": "ot_batta", "fieldtype": "Currency", - "label": "OT Batta" + "label": "OT Batta", + "read_only": 1 }, { + "depends_on": "eval:doc.is_overnight_stay ==0", "fieldname": "daily_batta_without_overnight_stay", "fieldtype": "Currency", "label": "Daily Batta Without Overnight Stay" @@ -82,6 +95,7 @@ "label": " Is Travelling Outside Kerala" }, { + "depends_on": "eval:doc.is_overnight_stay ==1", "fieldname": "daily_batta_with_overnight_stay", "fieldtype": "Currency", "label": "Daily Batta With Overnight Stay" @@ -89,7 +103,8 @@ { "fieldname": "batta", "fieldtype": "Currency", - "label": "Batta" + "label": "Batta", + "read_only": 1 }, { "fieldname": "column_break_hzfr", @@ -151,12 +166,62 @@ "print_hide": 1, "read_only": 1, "search_index": 1 + }, + { + "fieldname": "section_break_xxsb", + "fieldtype": "Section Break" + }, + { + "fieldname": "total_distance_travelled_km", + "fieldtype": "Float", + "label": "Total Distance Travelled(KM)", + "read_only": 1 + }, + { + "fieldname": "column_break_fgob", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_hours", + "fieldtype": "Float", + "label": "Total Hours", + "read_only": 1 + }, + { + "fieldname": "section_break_dwds", + "fieldtype": "Section Break" + }, + { + "fieldname": "total_daily_batta", + "fieldtype": "Currency", + "label": "Total Batta", + "read_only": 1 + }, + { + "fieldname": "column_break_zjgd", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_ot_batta", + "fieldtype": "Currency", + "label": "Total Ot Batta", + "read_only": 1 + }, + { + "fieldname": "column_break_jevl", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_driver_batta", + "fieldtype": "Currency", + "label": "Total Driver Batta", + "read_only": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2025-03-25 12:23:17.388355", + "modified": "2025-03-25 23:33:01.033127", "modified_by": "Administrator", "module": "BEAMS", "name": "Bureau Trip Sheet", @@ -180,4 +245,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py index d86da11b7..5b3a5afb7 100644 --- a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py @@ -1,10 +1,78 @@ -# Copyright (c) 2024, efeone and contributors +# Copyright (c) 2024, efeone and contributors # For license information, please see license.txt - import frappe from frappe.model.document import Document from frappe import _ class BureauTripSheet(Document): - pass + def validate(self): + self.calculate_batta() + self.calculate_total_distance_travelled() + self.calculate_hours() + self.calculate_total_daily_batta() + self.calculate_total_ot_batta() + + def calculate_batta(self): + ''' + Calculate the total batta (allowance) based on daily batta amounts. + ''' + self.batta = (self.daily_batta_without_overnight_stay or 0) \ + + (self.daily_batta_with_overnight_stay or 0) + + def calculate_total_distance_travelled(self): + ''' + Calculate the total distance travelled by summing up the + distance_travelled_km' values from work details. + ''' + total_distance = 0 + + if self.work_details: + for row in self.work_details: + if row.distance_travelled_km: + total_distance += row.distance_travelled_km + + # Set the 'total_distance_travelled_km' field with the calculated sum + self.total_distance_travelled_km = total_distance + + def calculate_hours(self): + ''' + Calculate the total hours worked by summing up the 'total_hours' values from work details. + ''' + total_hours = 0 + + if self.work_details: + for row in self.work_details: + if row.total_hours: + total_hours += row.total_hours + + # Set the 'total_distance_travelled_km' field with the calculated sum + self.total_hours = total_hours + + def calculate_total_daily_batta(self): + ''' + Calculate the total daily batta by summing up the 'total_batta' values from work details. + ''' + total_batta = 0 + + if self.work_details: + for row in self.work_details: + if row.total_batta: + total_batta += row.total_batta + + # Set the 'total_distance_travelled_km' field with the calculated sum + self.total_daily_batta = total_batta + + def calculate_total_ot_batta(self): + """ + Calculate the total OT batta by summing up the 'ot_batta' values from work details. + """ + total_ot_batta = 0 + + if self.work_details: + for row in self.work_details: + if row.ot_batta: + total_ot_batta += row.ot_batta + + # Set the 'total_distance_travelled_km' field with the calculated sum + self.total_ot_batta = total_ot_batta diff --git a/beams/beams/doctype/trip_details/trip_details.json b/beams/beams/doctype/trip_details/trip_details.json index 067b0df5b..736d15a8f 100644 --- a/beams/beams/doctype/trip_details/trip_details.json +++ b/beams/beams/doctype/trip_details/trip_details.json @@ -133,7 +133,8 @@ "fieldname": "total_food_allowance", "fieldtype": "Currency", "label": "Total Food Allowance", - "precision": "2" + "precision": "2", + "read_only": 1 }, { "fieldname": "section_break_wigg", @@ -151,14 +152,6 @@ "fieldname": "column_break_uihk", "fieldtype": "Column Break" }, - { - "depends_on": "eval:doc.batta_type == 'External'", - "fieldname": "ot_batta", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "OT Batta", - "read_only": 1 - }, { "fieldname": "column_break_cjsx", "fieldtype": "Column Break" @@ -187,18 +180,28 @@ "read_only": 1 }, { + "allow_on_submit": 1, "depends_on": "eval:doc.batta_type == 'External'", "fieldname": "ot_hours", "fieldtype": "Float", "in_list_view": 1, "label": "OT Hours", "read_only": 1 + }, + { + "allow_on_submit": 1, + "depends_on": "eval:doc.batta_type == 'External'", + "fieldname": "ot_batta", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "OT Batta", + "read_only": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-03-25 10:36:52.888802", + "modified": "2025-03-25 23:16:31.784386", "modified_by": "Administrator", "module": "BEAMS", "name": "Trip Details", @@ -207,4 +210,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} From a99652800cde9181fa6e34fcd620e41403802c14 Mon Sep 17 00:00:00 2001 From: Arathi Date: Wed, 26 Mar 2025 12:43:01 +0530 Subject: [PATCH 18/40] fix:Reordered fields in Bureau Trip Sheet for better structure. --- beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json index d877d33a3..65683f6a5 100644 --- a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json @@ -29,8 +29,8 @@ "departure_location", "destination_location", "column_break_cdph", - "ending_date_and_time", "starting_date_and_time", + "ending_date_and_time", "amended_from", "section_break_dwds", "total_daily_batta", @@ -221,7 +221,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2025-03-25 23:33:01.033127", + "modified": "2025-03-26 12:37:56.781474", "modified_by": "Administrator", "module": "BEAMS", "name": "Bureau Trip Sheet", From a5809f0c91d5dab18af6c54a44ad7eaf4cef247e Mon Sep 17 00:00:00 2001 From: hridyalakshmi Date: Wed, 26 Mar 2025 14:42:15 +0530 Subject: [PATCH 19/40] fix: Date Validation --- beams/beams/doctype/batta_claim/batta_claim.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/beams/beams/doctype/batta_claim/batta_claim.js b/beams/beams/doctype/batta_claim/batta_claim.js index 138411213..5f8937d84 100644 --- a/beams/beams/doctype/batta_claim/batta_claim.js +++ b/beams/beams/doctype/batta_claim/batta_claim.js @@ -193,10 +193,22 @@ frappe.ui.form.on('Work Detail', { set_batta_for_food_allowance(frm, cdt, cdn); }, to_date_and_time: function(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + + if (row.from_date_and_time && row.to_date_and_time) { + let from_date = new Date(row.from_date_and_time); + let to_date = new Date(row.to_date_and_time); + + if (to_date <= from_date) { + frappe.msgprint(__('To Date & Time must be greater than From Date & Time')); + frappe.model.set_value(cdt, cdn, 'to_date_and_time', null); + return; + } calculate_hours(frm, cdt, cdn); calculate_daily_batta(frm, cdt, cdn); set_batta_for_food_allowance(frm, cdt, cdn); } + } }); function calculate_total_distance_travelled(frm) { From df6cd56d6396011811b87387fcc7891db6bb0429 Mon Sep 17 00:00:00 2001 From: hridyalakshmi Date: Wed, 26 Mar 2025 16:20:51 +0530 Subject: [PATCH 20/40] fix: Added documentation --- .../beams/doctype/batta_claim/batta_claim.js | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/beams/beams/doctype/batta_claim/batta_claim.js b/beams/beams/doctype/batta_claim/batta_claim.js index 5f8937d84..3f316c04e 100644 --- a/beams/beams/doctype/batta_claim/batta_claim.js +++ b/beams/beams/doctype/batta_claim/batta_claim.js @@ -62,9 +62,11 @@ frappe.ui.form.on('Batta Claim', { calculate_allowance(frm); }, is_travelling_outside_kerala: function(frm) { + update_all_daily_batta(frm); calculate_allowance(frm); }, is_overnight_stay: function(frm) { + update_all_daily_batta(frm); calculate_allowance(frm); frm.doc.work_detail.forEach(row => { set_batta_for_food_allowance(frm, row["doctype"], row["name"]); @@ -72,6 +74,7 @@ frappe.ui.form.on('Batta Claim', { }) }, is_avail_room_rent: function(frm) { + update_all_daily_batta(frm); calculate_allowance(frm); }, is_delhi_bureau: function(frm) { @@ -80,6 +83,7 @@ frappe.ui.form.on('Batta Claim', { set_batta_for_food_allowance(frm, row["doctype"], row["name"]); }) }, + //fetching policy values and setting fields read-only accordingly refresh: function(frm) { frappe.call({ method: "beams.beams.doctype.batta_claim.batta_claim.get_batta_policy_values", @@ -211,6 +215,9 @@ frappe.ui.form.on('Work Detail', { } }); +/* + Calculates the total distance traveled based on all work detail entries. +*/ function calculate_total_distance_travelled(frm) { let totalDistance = 0; frm.doc.work_detail.forEach(row => { @@ -219,6 +226,9 @@ function calculate_total_distance_travelled(frm) { frm.set_value('total_distance_travelled_km', totalDistance); } +/* + Calculates the total hours worked based on all work detail entries. +*/ function calculate_total_hours(frm) { let totalHours = 0; frm.doc.work_detail.forEach(row => { @@ -227,18 +237,20 @@ function calculate_total_hours(frm) { frm.set_value('total_hours', totalHours); } +/* + Calculates hours worked for a specific row based on the from and to date/time fields. +*/ function calculate_hours(frm, cdt, cdn) { let row = frappe.get_doc(cdt, cdn); if (row.from_date_and_time && row.to_date_and_time) { let total_hours = (new Date(row.to_date_and_time) - new Date(row.from_date_and_time)) / (1000 * 60 * 60); - - frappe.db.get_single_value('Beams Accounts Settings', 'default_working_hours') - .then(default_hours => { frappe.model.set_value(cdt, cdn, 'total_hours', total_hours.toFixed(2)); - }); } } +/* + Calculates the daily batta based on the total hours worked and the batta type. +*/ function calculate_daily_batta(frm, cdt, cdn) { let row = frappe.get_doc(cdt, cdn); @@ -255,6 +267,9 @@ function calculate_daily_batta(frm, cdt, cdn) { frappe.model.set_value(cdt, cdn, 'daily_batta', daily_batta); } +/* + Updates daily batta for all child rows in the work detail table. +*/ function update_all_daily_batta(frm) { if (frm.doc.work_detail) { frm.doc.work_detail.forEach(row => { @@ -263,6 +278,9 @@ function update_all_daily_batta(frm) { } } +/* + Calculates the total daily batta across all work detail entries. +*/ function calculate_total_daily_batta(frm) { let totalDailyBatta = 0; frm.doc.work_detail.forEach(row => { @@ -287,6 +305,7 @@ function handle_designation_based_on_batta_type(frm) { } } +/* Sets the batta-based options based on the selected batta type.*/ function set_batta_based_on_options(frm) { if (frm.doc.batta_type === 'External') { frm.set_df_property('batta_based_on', 'options', 'Hours'); @@ -297,6 +316,7 @@ function set_batta_based_on_options(frm) { } } +/* Calculates total batta based on room rent, daily batta with and without overnight stay.*/ function calculate_batta(frm) { let total_batta = (frm.doc.room_rent_batta || 0) + (frm.doc.daily_batta_without_overnight_stay || 0) @@ -332,6 +352,7 @@ function calculate_allowance(frm) { }); } +/* Determines eligibility for food allowance and updates fields accordingly.*/ function set_batta_for_food_allowance(frm, cdt, cdn) { let child = locals[cdt][cdn]; let designation = frm.doc.designation; @@ -383,6 +404,7 @@ function set_batta_for_food_allowance(frm, cdt, cdn) { } } +/* Calculation of Total Food Allowance. */ function calculate_total_food_allowance(frm, cdt, cdn) { let row = locals[cdt][cdn]; row.total_food_allowance = (row.breakfast || 0) + (row.lunch || 0) + (row.dinner || 0); @@ -390,6 +412,7 @@ function calculate_total_food_allowance(frm, cdt, cdn) { frm.refresh_field("work_detail"); } +/* Calculation of total batta based on daily batta and food allowance. */ function calculate_total_batta(frm, cdt, cdn) { let row = locals[cdt][cdn]; frappe.model.set_value(cdt, cdn, "total_batta", (row.daily_batta || 0) + (row.total_food_allowance || 0)); From 63ab66d39574a94d4d670755882e2ce87a276977 Mon Sep 17 00:00:00 2001 From: Arathi Date: Wed, 26 Mar 2025 16:50:34 +0530 Subject: [PATCH 21/40] chore:improve code formatting, add comments --- .../bureau_trip_sheet/bureau_trip_sheet.js | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js index 72ead7da8..9ec9afbd9 100644 --- a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js @@ -108,7 +108,7 @@ frappe.ui.form.on("Bureau Trip Sheet", { } }); - +/* set filter is transporter in Supplier. */ function filter_supplier_field(frm) { frm.set_query("supplier", function () { return { @@ -119,6 +119,7 @@ function filter_supplier_field(frm) { }); } +/* Calculate total hours, number of days, and overtime hours */ function calculate_hours_and_days(frm, cdt, cdn) { let row = locals[cdt][cdn]; @@ -138,6 +139,8 @@ function calculate_hours_and_days(frm, cdt, cdn) { frappe.model.set_value(cdt, cdn, 'ot_hours', ot_hours.toFixed(2)); frappe.model.set_value(cdt, cdn, 'number_of_days', number_of_days); + frm.refresh_field("work_details"); + setTimeout(() => { calculate_daily_batta(frm, cdt, cdn); calculate_ot_batta(frm, cdt, cdn); @@ -146,6 +149,7 @@ function calculate_hours_and_days(frm, cdt, cdn) { } } +/* Calculate daily batta based on number of days and batta rate */ function calculate_daily_batta(frm, cdt, cdn) { let row = locals[cdt][cdn]; @@ -157,6 +161,7 @@ function calculate_daily_batta(frm, cdt, cdn) { frm.refresh_field('work_details'); } +/* Calculate overtime batta based on OT hours and OT batta rate */ function calculate_ot_batta(frm, cdt, cdn) { let row = locals[cdt][cdn]; @@ -167,6 +172,7 @@ function calculate_ot_batta(frm, cdt, cdn) { frm.refresh_field('work_details'); } +/* Update daily batta for all work details rows */ function update_all_daily_batta(frm) { if (frm.doc.work_details) { frm.doc.work_details.forEach(row => { @@ -178,6 +184,8 @@ function update_all_daily_batta(frm) { } } + +/* Update OT batta for all work details rows */ function update_all_ot_batta(frm) { if (frm.doc.work_details) { frm.doc.work_details.forEach(row => { @@ -189,6 +197,7 @@ function update_all_ot_batta(frm) { } } +/* Calculate total batta by summing daily batta values */ function calculate_batta(frm) { let total_batta = (frm.doc.daily_batta_with_overnight_stay || 0) + (frm.doc.daily_batta_without_overnight_stay || 0); @@ -196,6 +205,7 @@ function calculate_batta(frm) { frappe.model.set_value(frm.doctype, frm.docname, 'batta', total_batta); } +/* Calculate total food allowance and total batta for a row */ function calculate_total_food_allowance(frm, cdt, cdn) { let row = locals[cdt][cdn]; row.total_food_allowance = (row.breakfast || 0) + (row.lunch || 0) + (row.dinner || 0); @@ -208,41 +218,51 @@ function calculate_total_food_allowance(frm, cdt, cdn) { frm.set_value("total_daily_batta", total_batta_sum); } +/* Calculate total distance travelled across all work details rows */ function calculate_total_distance_travelled(frm) { - let totalDistance = 0; + let total_distance = 0; frm.doc.work_details.forEach(row => { - totalDistance += row.distance_travelled_km || 0; + total_distance += row.distance_travelled_km || 0; }); - frm.set_value('total_distance_travelled_km', totalDistance); + frm.set_value('total_distance_travelled_km', total_distance); + frm.refresh_field("total_distance_travelled_km"); } +/* Calculate total hours from all work details rows */ function calculate_hours(frm) { - let totalHours = 0; + let total_hours = 0; frm.doc.work_details.forEach(row => { - totalHours += row.total_hours || 0; + total_hours += row.total_hours || 0; }); frm.set_value('total_hours', totalHours); + frm.refresh_field("total_hours"); } +/* Calculate total daily batta for all work details rows */ function calculate_total_daily_batta(frm) { - let totalBatta = 0; + let total_batta = 0; frm.doc.work_details.forEach(row => { - totalBatta += row.total_batta || 0; + total_batta += row.total_batta || 0; }); - frm.set_value('total_daily_batta', totalBatta); + frm.set_value('total_daily_batta', total_batta); + frm.refresh_field("total_daily_batta"); } +/* Calculate total OT batta for all work details rows */ function calculate_total_ot_batta(frm) { - let totalOtBatta = 0; + let total_ot_batta = 0; frm.doc.work_details.forEach(row => { - totalOtBatta += row.ot_batta || 0; + total_ot_batta += row.ot_batta || 0; }); - frm.set_value('total_ot_batta', totalOtBatta); + frm.set_value('total_ot_batta', total_ot_batta); + frm.refresh_field("total_ot_batta"); } +/* Calculate total driver batta as the sum of total daily batta and total OT batta */ function calculate_total_driver_batta(frm) { let total_daily_batta = frm.doc.total_daily_batta || 0; let total_ot_batta = frm.doc.total_ot_batta || 0; frm.set_value('total_driver_batta', total_daily_batta + total_ot_batta); + frm.refresh_field("total_driver_batta"); } From f05961f267b9b43d9990e6ed232c28e29c039910 Mon Sep 17 00:00:00 2001 From: Arathi Date: Thu, 27 Mar 2025 10:11:20 +0530 Subject: [PATCH 22/40] fix:total_hours calculation in Bureau Trip Sheet --- beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js | 2 +- beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js index 9ec9afbd9..0b124d5ce 100644 --- a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js @@ -234,7 +234,7 @@ function calculate_hours(frm) { frm.doc.work_details.forEach(row => { total_hours += row.total_hours || 0; }); - frm.set_value('total_hours', totalHours); + frm.set_value('total_hours', total_hours); frm.refresh_field("total_hours"); } diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py index 5b3a5afb7..4aefd032a 100644 --- a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py @@ -44,7 +44,7 @@ def calculate_hours(self): if self.work_details: for row in self.work_details: if row.total_hours: - total_hours += row.total_hours + total_hours += float(row.total_hours) # Set the 'total_distance_travelled_km' field with the calculated sum self.total_hours = total_hours From 5be15a403d11f3cb63e53acfa0cb50110855854d Mon Sep 17 00:00:00 2001 From: hridyalakshmi Date: Thu, 27 Mar 2025 10:42:53 +0530 Subject: [PATCH 23/40] fix: Update Batta Claim --- beams/beams/doctype/batta_claim/batta_claim.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beams/beams/doctype/batta_claim/batta_claim.py b/beams/beams/doctype/batta_claim/batta_claim.py index 11192180c..4c1ace3c1 100644 --- a/beams/beams/doctype/batta_claim/batta_claim.py +++ b/beams/beams/doctype/batta_claim/batta_claim.py @@ -167,9 +167,9 @@ def calculate_batta_allowance(designation=None, is_travelling_outside_kerala=0, if not is_actual_daily_batta: # Check if policy is not actual if is_overnight_stay: if is_travelling_outside_kerala: - daily_batta_with_overnight_stay = float(policy.get('outside_kerala_', 0)) + daily_batta_with_overnight_stay = float(policy.get('outside_kerala__', 0)) else: - daily_batta_with_overnight_stay = float(policy.get('inside_kerala_', 0)) + daily_batta_with_overnight_stay = float(policy.get('inside_kerala__', 0)) # Calculate Daily Batta without Overnight Stay if not is_actual_daily_batta_without_overnight: # Check if policy is not actual From b17a24b26124bd74d33fee1f1ad7af1581563721 Mon Sep 17 00:00:00 2001 From: hridyalakshmi Date: Thu, 27 Mar 2025 14:57:35 +0530 Subject: [PATCH 24/40] fix: Update Batta Calculation --- .../beams/doctype/batta_claim/batta_claim.js | 21 ++++++++++++++++--- .../doctype/batta_claim/batta_claim.json | 4 ++-- .../beams/doctype/batta_claim/batta_claim.py | 2 +- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/beams/beams/doctype/batta_claim/batta_claim.js b/beams/beams/doctype/batta_claim/batta_claim.js index 3f316c04e..bd8dbdf20 100644 --- a/beams/beams/doctype/batta_claim/batta_claim.js +++ b/beams/beams/doctype/batta_claim/batta_claim.js @@ -119,7 +119,10 @@ frappe.ui.form.on('Batta Claim', { frappe.ui.form.on('Work Detail', { distance_travelled_km: function(frm, cdt, cdn) { calculate_total_distance_travelled(frm); - set_batta_for_food_allowance(frm, cdt, cdn) + setTimeout(() => { + set_batta_for_food_allowance(frm, cdt, cdn); + calculate_batta(frm, cdt, cdn); + }, 30); }, daily_batta: function(frm, cdt, cdn) { calculate_total_batta(frm, cdt, cdn); @@ -177,11 +180,17 @@ frappe.ui.form.on('Work Detail', { calculate_total_distance_travelled(frm); calculate_total_daily_batta(frm); calculate_total_hours(frm); + setTimeout(() => { + calculate_batta(frm, cdt, cdn); + }, 30); }, work_detail_remove: function(frm, cdt, cdn) { calculate_total_distance_travelled(frm); calculate_total_daily_batta(frm); calculate_total_hours(frm); + setTimeout(() => { + calculate_batta(frm, cdt, cdn); + }, 30); }, total_hours: function(frm, cdt, cdn) { calculate_daily_batta(frm, cdt, cdn); @@ -194,7 +203,10 @@ frappe.ui.form.on('Work Detail', { from_date_and_time: function(frm, cdt, cdn) { calculate_hours(frm, cdt, cdn); calculate_daily_batta(frm, cdt, cdn); - set_batta_for_food_allowance(frm, cdt, cdn); + setTimeout(() => { + set_batta_for_food_allowance(frm, cdt, cdn); + calculate_batta(frm, cdt, cdn); + }, 30); }, to_date_and_time: function(frm, cdt, cdn) { let row = locals[cdt][cdn]; @@ -210,7 +222,10 @@ frappe.ui.form.on('Work Detail', { } calculate_hours(frm, cdt, cdn); calculate_daily_batta(frm, cdt, cdn); - set_batta_for_food_allowance(frm, cdt, cdn); + setTimeout(() => { + set_batta_for_food_allowance(frm, cdt, cdn); + calculate_batta(frm, cdt, cdn); + }, 30); } } }); diff --git a/beams/beams/doctype/batta_claim/batta_claim.json b/beams/beams/doctype/batta_claim/batta_claim.json index 48e0641a4..670440e60 100644 --- a/beams/beams/doctype/batta_claim/batta_claim.json +++ b/beams/beams/doctype/batta_claim/batta_claim.json @@ -64,7 +64,7 @@ "fieldname": "batta_type", "fieldtype": "Select", "label": "Batta Type", - "options": "External\nInternal" + "options": "Internal\nExternal" }, { "depends_on": "eval:doc.batta_type == \"Internal\"\n", @@ -291,7 +291,7 @@ "link_fieldname": "batta_claim_reference" } ], - "modified": "2025-03-25 11:00:55.832977", + "modified": "2025-03-27 13:44:09.312155", "modified_by": "Administrator", "module": "BEAMS", "name": "Batta Claim", diff --git a/beams/beams/doctype/batta_claim/batta_claim.py b/beams/beams/doctype/batta_claim/batta_claim.py index 4c1ace3c1..526b466ba 100644 --- a/beams/beams/doctype/batta_claim/batta_claim.py +++ b/beams/beams/doctype/batta_claim/batta_claim.py @@ -130,7 +130,7 @@ def calculate_batta_allowance(designation=None, is_travelling_outside_kerala=0, ''' # Convert inputs to proper types total_distance_travelled_km = float(total_distance_travelled_km or 0) - total_hours = float(total_hours or 0) + total_hours = total_hours or 0 # Fetch the Batta Policy for the given designation batta_policy = frappe.get_all('Batta Policy', filters={'designation': designation}, fields=['*']) From c6e29c35af46998c4a091909d890a83d2b6b3d30 Mon Sep 17 00:00:00 2001 From: MhmdSinanKT Date: Thu, 27 Mar 2025 15:09:19 +0530 Subject: [PATCH 25/40] feat: added option to show budget dimensions --- .../detailed_budget_allocation_report.js | 8 +++- .../detailed_budget_allocation_report.json | 5 ++- .../detailed_budget_allocation_report.py | 38 +++++++++++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.js b/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.js index 319065368..bc62d5517 100644 --- a/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.js +++ b/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.js @@ -111,7 +111,13 @@ frappe.query_reports["Detailed Budget Allocation Report"] = { fieldtype: "Select", options: "ASC\nDESC", default: "DESC" - } + }, + { + fieldname: "budget_amount_only", + label: "Budget Amount Only", + fieldtype: "Check", + default: 1 + }, ], tree: true, treeView: true, diff --git a/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.json b/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.json index 3b9e104bf..8aa3f82c7 100644 --- a/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.json +++ b/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.json @@ -1,5 +1,6 @@ { - "add_total_row": 1, + "add_total_row": 0, + "add_translate_data": 0, "columns": [], "creation": "2025-03-05 20:54:32.054081", "disabled": 0, @@ -9,7 +10,7 @@ "idx": 0, "is_standard": "Yes", "letterhead": null, - "modified": "2025-03-14 15:16:01.173074", + "modified": "2025-03-27 15:14:45.563936", "modified_by": "Administrator", "module": "BEAMS", "name": "Detailed Budget Allocation Report", diff --git a/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.py b/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.py index 0d1a2073f..c7922b6d0 100644 --- a/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.py +++ b/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.py @@ -63,6 +63,18 @@ def get_columns(filters): columns.append( {'label': _('Total Budget'), 'fieldtype': 'Currency', 'fieldname': 'total_budget', 'width': 200} ) + + if not filters.get("budget_amount_only"): + fields = [ + {'label': _('Finance Group'), 'fieldtype': 'Link', 'fieldname': 'finance_group', 'options': 'Finance Group', 'width': 200}, + {'label': _('Department'), 'fieldtype': 'Link', 'fieldname': 'department', 'options': 'Department', 'width': 200}, + {'label': _('Division'), 'fieldtype': 'Link', 'fieldname': 'division', 'options': 'Division', 'width': 200}, + {'label': _('Cost Head'), 'fieldtype': 'Link', 'fieldname': 'cost_head', 'options': 'Cost Head', 'width': 200}, + {'label': _('Cost Subhead'), 'fieldtype': 'Link', 'fieldname': 'cost_subhead', 'options': 'Cost Subhead', 'width': 200} + ] + # Insert all fields at index 1 in one step + columns[1:1] = fields + filters["currency_fields"] = currency_fields return columns @@ -151,6 +163,16 @@ def get_data(filters): 'account': cost_details.get('account', ''), 'total_budget': total_budget } + + if not filters.get("budget_amount_only"): + csh_row.update({ + 'finance_group': fg, + 'department': dept, + 'division': div, + 'cost_head': ch, + 'cost_subhead': csh, + }) + if period != 'Yearly': budget_column_data = get_budget_column_data(period, months_order, row_id) csh_row.update(budget_column_data) @@ -158,17 +180,33 @@ def get_data(filters): # Accumulate child budget into its parent for field in currency_fields: + budget_map[ch_id].update({ + 'finance_group': fg, + 'department': dept, + 'division': div, + 'cost_head': ch, + }) budget_map[ch_id][field] += csh_row.get(field, 0) # Propagate cost head budget to department for field in currency_fields: + budget_map[div].update({ + 'finance_group': fg, + 'department': dept, + 'division': div, + }) budget_map[div][field] += budget_map[ch_id][field] # Propagate division budget to departments for field in currency_fields: + budget_map[dept].update({ + 'finance_group': fg, + 'department': dept, + }) budget_map[dept][field] += budget_map[div][field] # Propagate department budget to finance group for field in currency_fields: + budget_map[fg_id]['finance_group'] = fg budget_map[fg_id][field] += budget_map[dept][field] # Propagate finance group budget to company From e06eaf5274efecd82f4a511c09bc3c4c6025f510 Mon Sep 17 00:00:00 2001 From: hridyalakshmi Date: Thu, 27 Mar 2025 16:58:32 +0530 Subject: [PATCH 26/40] fix: convert string to float --- beams/beams/doctype/batta_claim/batta_claim.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/beams/beams/doctype/batta_claim/batta_claim.py b/beams/beams/doctype/batta_claim/batta_claim.py index 526b466ba..46785a5a1 100644 --- a/beams/beams/doctype/batta_claim/batta_claim.py +++ b/beams/beams/doctype/batta_claim/batta_claim.py @@ -3,6 +3,7 @@ import frappe import json +import re from frappe.utils import getdate, get_datetime, date_diff from frappe.model.document import Document @@ -129,8 +130,16 @@ def calculate_batta_allowance(designation=None, is_travelling_outside_kerala=0, Calculation Of Total Batta Allowance based on Batta Policy ''' # Convert inputs to proper types - total_distance_travelled_km = float(total_distance_travelled_km or 0) - total_hours = total_hours or 0 + def sanitize_number(value): + """Extract a valid float from a string by keeping only the first valid decimal number.""" + if isinstance(value, str): + match = re.search(r'\d+(\.\d+)?', value) + return float(match.group()) if match else 0.0 + return float(value or 0.0) + + # Convert inputs safely + total_distance_travelled_km = sanitize_number(total_distance_travelled_km) + total_hours = sanitize_number(total_hours) # Fetch the Batta Policy for the given designation batta_policy = frappe.get_all('Batta Policy', filters={'designation': designation}, fields=['*']) From 5184ffc865c5fc4782ddb4050bd42b5d87348892 Mon Sep 17 00:00:00 2001 From: hridyalakshmi Date: Fri, 28 Mar 2025 12:23:35 +0530 Subject: [PATCH 27/40] fix: Update Requested amount in Petty cash request --- beams/beams/custom_scripts/voucher_entry/voucher_entry.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/beams/beams/custom_scripts/voucher_entry/voucher_entry.js b/beams/beams/custom_scripts/voucher_entry/voucher_entry.js index 2d699a3c4..4f1206dba 100644 --- a/beams/beams/custom_scripts/voucher_entry/voucher_entry.js +++ b/beams/beams/custom_scripts/voucher_entry/voucher_entry.js @@ -54,7 +54,9 @@ function show_petty_cash_dialog(frm) { fieldname: "requested_amount", label: __("Requested Amount"), fieldtype: "Currency", - reqd: 1 + reqd: 1, + read_only: 1, + default: frm.doc.total_amount - frm.doc.balance }, { fieldname: "reason", From df63b4856be4eca84bf43b7117b09fdfa3686f31 Mon Sep 17 00:00:00 2001 From: Arathi Date: Fri, 28 Mar 2025 16:27:54 +0530 Subject: [PATCH 28/40] refactor:Bureau Trip Sheet calculation --- .../bureau_trip_sheet/bureau_trip_sheet.js | 159 +++++++++++++++++- .../bureau_trip_sheet/bureau_trip_sheet.py | 134 ++++++++++++++- 2 files changed, 287 insertions(+), 6 deletions(-) diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js index 0b124d5ce..55f82f783 100644 --- a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js @@ -4,6 +4,10 @@ frappe.ui.form.on('Trip Details', { from_date_and_time: function (frm, cdt, cdn) { calculate_hours_and_days(frm, cdt, cdn); + setTimeout(() => { + set_batta_for_food_allowance(frm, cdt, cdn); + calculate_batta(frm, cdt, cdn); + }, 30); }, to_date_and_time: function (frm, cdt, cdn) { let row = locals[cdt][cdn]; @@ -17,6 +21,10 @@ frappe.ui.form.on('Trip Details', { frappe.model.set_value(cdt, cdn, 'to_date_and_time', null); return; } + setTimeout(() => { + set_batta_for_food_allowance(frm, cdt, cdn); + calculate_batta(frm, cdt, cdn); + }, 30); } calculate_hours_and_days(frm, cdt, cdn); @@ -24,6 +32,7 @@ frappe.ui.form.on('Trip Details', { total_hours: function (frm, cdt, cdn) { calculate_daily_batta(frm, cdt, cdn); calculate_ot_batta(frm, cdt, cdn); + set_batta_for_food_allowance(frm, cdt, cdn); }, ot_hours: function (frm, cdt, cdn) { calculate_ot_batta(frm, cdt, cdn); @@ -39,21 +48,39 @@ frappe.ui.form.on('Trip Details', { }, distance_travelled_km: function(frm, cdt, cdn) { calculate_total_distance_travelled(frm, cdt, cdn); + setTimeout(() => { + set_batta_for_food_allowance(frm, cdt, cdn); + calculate_batta(frm, cdt, cdn); + }, 30); }, work_details_add: function(frm, cdt, cdn) { + calculate_total_distance_travelled(frm, cdt, cdn); + calculate_hours(frm, cdt, cdn); + calculate_total_daily_batta(frm, cdt, cdn); + calculate_total_ot_batta(frm, cdt, cdn); + + setTimeout(() => { + set_batta_for_food_allowance(frm, cdt, cdn); + calculate_batta(frm, cdt, cdn); + }, 30); }, work_details_remove: function(frm, cdt, cdn) { calculate_total_distance_travelled(frm, cdt, cdn); calculate_hours(frm, cdt, cdn); calculate_total_daily_batta(frm, cdt, cdn); calculate_total_ot_batta(frm, cdt, cdn); + setTimeout(() => { + set_batta_for_food_allowance(frm, cdt, cdn); + calculate_batta(frm, cdt, cdn); + }, 30); }, total_hours: function(frm, cdt, cdn) { calculate_hours(frm, cdt, cdn); + set_batta_for_food_allowance(frm, cdt, cdn); }, daily_batta: function(frm, cdt, cdn) { calculate_total_food_allowance(frm, cdt, cdn); @@ -72,6 +99,7 @@ frappe.ui.form.on("Bureau Trip Sheet", { filter_supplier_field(frm); update_all_daily_batta(frm); update_all_ot_batta(frm); + calculate_allowance(frm); }, validate: function (frm) { update_all_daily_batta(frm); @@ -101,14 +129,66 @@ frappe.ui.form.on("Bureau Trip Sheet", { calculate_total_driver_batta(frm); }, is_overnight_stay: function (frm) { + calculate_allowance(frm); calculate_daily_batta(frm); }, is_travelling_outside_kerala: function (frm) { + calculate_allowance(frm); calculate_daily_batta(frm); - } + }, + total_distance_travelled_km: function (frm) { + calculate_allowance(frm); + }, + total_hours: function(frm) { + calculate_allowance(frm); + }, + is_overnight_stay: function(frm) { + calculate_allowance(frm); + frm.doc.work_details.forEach(row => { + set_batta_for_food_allowance(frm, row["doctype"], row["name"]); + set_batta_for_food_allowance(frm, row["doctype"], row["name"]); + }) + }, + refresh: function(frm) { + // Fetch Batta Policy values and set read-only properties + frappe.call({ + method: "beams.beams.doctype.bureau_trip_sheet.bureau_trip_sheet.get_batta_policy_values", + callback: function(response) { + if (response.message) { + let is_actual_daily_batta_without_overnight_stay = response.message.is_actual__; + let is_actual_daily_batta_with_overnight_stay = response.message.is_actual_; + let is_actual_food_allowance = response.message.is_actual___; + + // Set read-only properties + frm.set_df_property('daily_batta_without_overnight_stay', 'read_only', is_actual_daily_batta_without_overnight_stay == 0); + frm.set_df_property('daily_batta_with_overnight_stay', 'read_only', is_actual_daily_batta_with_overnight_stay == 0); + + // Refresh fields + frm.refresh_field('daily_batta_without_overnight_stay'); + frm.refresh_field('daily_batta_with_overnight_stay'); + + frm.fields_dict['work_details'].grid.update_docfield_property('breakfast', 'read_only', is_actual_food_allowance == 0); + frm.fields_dict['work_details'].grid.update_docfield_property('lunch', 'read_only', is_actual_food_allowance == 0); + frm.fields_dict['work_details'].grid.update_docfield_property('dinner', 'read_only', is_actual_food_allowance == 0); + + // Refresh child table + frm.refresh_field('work_details'); + } + } + }); + + // Call the supplier filter function + filter_supplier_field(frm); + }, + + onload: function(frm) { + // Ensure the filter is applied on form load as well + filter_supplier_field(frm); + } }); -/* set filter is transporter in Supplier. */ + +/* Set filter for supplier field */ function filter_supplier_field(frm) { frm.set_query("supplier", function () { return { @@ -140,7 +220,6 @@ function calculate_hours_and_days(frm, cdt, cdn) { frappe.model.set_value(cdt, cdn, 'number_of_days', number_of_days); frm.refresh_field("work_details"); - setTimeout(() => { calculate_daily_batta(frm, cdt, cdn); calculate_ot_batta(frm, cdt, cdn); @@ -180,7 +259,10 @@ function update_all_daily_batta(frm) { }); setTimeout(() => { frm.refresh_field('work_details'); + calculate_daily_batta(frm, cdt, cdn); + calculate_ot_batta(frm, cdt, cdn); }, 200); + } } @@ -213,8 +295,7 @@ function calculate_total_food_allowance(frm, cdt, cdn) { frm.refresh_field("work_details"); - // Bureau Trip Sheet Doctype-ile total_daily_batta update cheyyan - let total_batta_sum = frm.doc.work_details.reduce((sum, r) => sum + (r.total_batta || 0), 0); + let total_batta_sum = frm.doc.work_details.reduce((sum, r) => sum + (r.total_batta || 0), 0); frm.set_value("total_daily_batta", total_batta_sum); } @@ -266,3 +347,71 @@ function calculate_total_driver_batta(frm) { frm.set_value('total_driver_batta', total_daily_batta + total_ot_batta); frm.refresh_field("total_driver_batta"); } + + +/* Determines eligibility for food allowance and updates fields accordingly.*/ +function set_batta_for_food_allowance(frm, cdt, cdn) { + let child = locals[cdt][cdn]; + + let designation = "Driver"; + let is_overnight_stay = frm.doc.is_overnight_stay; + + let is_eligible = false; + if (child.distance_travelled_km >= 50 && child.distance_travelled_km <= 100 && child.total_hours > 6) { + is_eligible = true; + } + + if (is_overnight_stay) { + frappe.model.set_value(child.doctype, child.name, "breakfast", 0); + frappe.model.set_value(child.doctype, child.name, "lunch", 0); + frappe.model.set_value(child.doctype, child.name, "dinner", 0); + frappe.model.set_value(child.doctype, child.name, "total_food_allowance", 0); + return; + } + + else if (is_eligible && !is_overnight_stay) { + frappe.call({ + method: "beams.beams.doctype.bureau_trip_sheet.bureau_trip_sheet.get_batta_for_food_allowance", + args: { + designation: designation, + from_date_time: child.from_date_and_time, + to_date_time: child.to_date_and_time, + total_hrs: child.total_hours, + }, + callback: function (r) { + if (r && r.message) { + let response = r.message; + frappe.model.set_value(child.doctype, child.name, "breakfast", response.break_fast); + frappe.model.set_value(child.doctype, child.name, "lunch", response.lunch); + frappe.model.set_value(child.doctype, child.name, "dinner", response.dinner); + frappe.model.set_value(child.doctype, child.name, "total_food_allowance", response.break_fast + response.lunch + response.dinner); + } + } + }); + } +} + +function calculate_allowance(frm) { + if (!frm.doc.supplier.length) { + frappe.msgprint(__("Please select a supplier.")); + return; + } + + frappe.call({ + method: "beams.beams.doctype.bureau_trip_sheet.bureau_trip_sheet.calculate_batta_allowance", + args: { + designation: frm.doc.designation, + is_travelling_outside_kerala: frm.doc.is_travelling_outside_kerala || 0, + is_overnight_stay: frm.doc.is_overnight_stay || 0, + total_distance_travelled_km: frm.doc.total_distance_travelled_km || 0, + total_hours: frm.doc.total_hours || 0 + }, + callback: function(r) { + if (r.message) { + frm.set_value("daily_batta_with_overnight_stay", r.message.daily_batta_with_overnight_stay); + frm.set_value("daily_batta_without_overnight_stay", r.message.daily_batta_without_overnight_stay); + frm.set_value("batta", r.message.batta); + } + } + }); +} diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py index 4aefd032a..79d1b0caf 100644 --- a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py @@ -4,6 +4,8 @@ import frappe from frappe.model.document import Document from frappe import _ +from frappe.utils import get_datetime, getdate + class BureauTripSheet(Document): def validate(self): @@ -44,7 +46,7 @@ def calculate_hours(self): if self.work_details: for row in self.work_details: if row.total_hours: - total_hours += float(row.total_hours) + total_hours += float(row.total_hours) # Set the 'total_distance_travelled_km' field with the calculated sum self.total_hours = total_hours @@ -76,3 +78,133 @@ def calculate_total_ot_batta(self): # Set the 'total_distance_travelled_km' field with the calculated sum self.total_ot_batta = total_ot_batta + +@frappe.whitelist() +def get_batta_for_food_allowance(designation, from_date_time, to_date_time, total_hrs): + ''' + Method to get Batta for Food + ''' + values = { 'break_fast':0, 'lunch':0, 'dinner':0 } + batta_policy = frappe.db.exists('Batta Policy', { 'designation':designation }) + from_date_time = get_datetime(from_date_time) + to_date_time = get_datetime(to_date_time) + required_hours = 6 + if batta_policy and float(total_hrs)>required_hours: + break_fast, lunch, dinner = frappe.db.get_value('Batta Policy', batta_policy, ['break_fast', 'lunch', 'dinner']) + same_date = False + if getdate(from_date_time) == getdate(to_date_time): + same_date = True + #Breakfast check + if same_date: + date_threshold = getdate(from_date_time) + break_fast_start_time = get_datetime('{0} {1}'.format(date_threshold, '04:00')) + break_fast_end_time = get_datetime('{0} {1}'.format(date_threshold, '09:00')) + if (from_date_time <= break_fast_start_time <= to_date_time) or (from_date_time <= break_fast_end_time <= to_date_time): + values['break_fast'] = break_fast + lunch_start_time = get_datetime('{0} {1}'.format(date_threshold, '12:30')) + lunch_end_time = get_datetime('{0} {1}'.format(date_threshold, '14:00')) + if (from_date_time <= lunch_start_time <= to_date_time) or (from_date_time <= lunch_end_time <= to_date_time): + values['lunch'] = lunch + dinner_start_time = get_datetime('{0} {1}'.format(date_threshold, '18:00')) + dinner_end_time = get_datetime('{0} {1}'.format(date_threshold, '21:00')) + if (from_date_time <= dinner_start_time <= to_date_time) or (from_date_time <= dinner_end_time <= to_date_time): + values['dinner'] = dinner + else: + #Breakfast check + date_threshold = getdate(from_date_time) #Check with Start Date + break_fast_start_time = get_datetime('{0} {1}'.format(date_threshold, '04:00')) + break_fast_end_time = get_datetime('{0} {1}'.format(date_threshold, '09:00')) + if (from_date_time <= break_fast_start_time <= to_date_time) or (from_date_time <= break_fast_end_time <= to_date_time): + values['break_fast'] = break_fast + date_threshold = getdate(to_date_time) #Check with End Date + break_fast_start_time = get_datetime('{0} {1}'.format(date_threshold, '04:00')) + break_fast_end_time = get_datetime('{0} {1}'.format(date_threshold, '09:00')) + if (from_date_time <= break_fast_start_time <= to_date_time) or (from_date_time <= break_fast_end_time <= to_date_time): + values['break_fast'] = values.get('break_fast', 0) + break_fast + #Lunch Check + date_threshold = getdate(from_date_time) #Check with Start Date + lunch_start_time = get_datetime('{0} {1}'.format(date_threshold, '12:30')) + lunch_end_time = get_datetime('{0} {1}'.format(date_threshold, '14:00')) + if (from_date_time <= lunch_start_time <= to_date_time) or (from_date_time <= lunch_end_time <= to_date_time): + values['lunch'] = lunch + date_threshold = getdate(to_date_time) #Check with End Date + lunch_start_time = get_datetime('{0} {1}'.format(date_threshold, '12:30')) + lunch_end_time = get_datetime('{0} {1}'.format(date_threshold, '14:00')) + if (from_date_time <= lunch_start_time <= to_date_time) or (from_date_time <= lunch_end_time <= to_date_time): + values['lunch'] = values.get('lunch', 0) + lunch + #Dinner Check + date_threshold = getdate(from_date_time) #Check with Start Date + dinner_start_time = get_datetime('{0} {1}'.format(date_threshold, '18:00')) + dinner_end_time = get_datetime('{0} {1}'.format(date_threshold, '21:00')) + if (from_date_time <= dinner_start_time <= to_date_time) or (from_date_time <= dinner_end_time <= to_date_time): + values['dinner'] = dinner + date_threshold = getdate(to_date_time) #Check with End Date + dinner_start_time = get_datetime('{0} {1}'.format(date_threshold, '18:00')) + dinner_end_time = get_datetime('{0} {1}'.format(date_threshold, '21:00')) + if (from_date_time <= dinner_start_time <= to_date_time) or (from_date_time <= dinner_end_time <= to_date_time): + values['dinner'] = values.get('dinner', 0) + dinner + return values + +@frappe.whitelist() +def calculate_batta_allowance(designation=None, is_travelling_outside_kerala=0, is_overnight_stay=0, total_distance_travelled_km=0, total_hours=0): + ''' + Calculation Of Total Batta Allowance based on Batta Policy + ''' + #Convert inputs to proper types + def sanitize_number(value): + try: + return float(value) + except: + return 0 + total_distance_travelled_km = sanitize_number(total_distance_travelled_km) + total_hours = sanitize_number(total_hours) + + # Fetch the Batta Policy for the given designation + batta_policy = frappe.get_all('Batta Policy', filters={'designation': 'Driver'}, fields=['*']) + if not batta_policy: + frappe.throw(f"No Batta Policy found for the designation: {designation}") + return {"batta": 0} + + policy = batta_policy[0] + + # Get policy checkbox values + is_actual_daily_batta = policy.get('is_actual_') or 0 # Daily Batta with Overnight Stay Checkbox + is_actual_daily_batta_without_overnight = policy.get('is_actual__') or 0 # Daily Batta Without Overnight Stay Checkbox + + # Convert inputs to boolean + is_travelling_outside_kerala = bool(int(is_travelling_outside_kerala or 0)) + is_overnight_stay = bool(int(is_overnight_stay or 0)) + + # Initialize batta values + daily_batta_with_overnight_stay = 0 + daily_batta_without_overnight_stay = 0 + + # Calculate Daily Batta with Overnight Stay + if not is_actual_daily_batta: # Check if policy is not actual + if is_overnight_stay: + if is_travelling_outside_kerala: + daily_batta_with_overnight_stay = float(policy.get('outside_kerala__', 0)) + else: + daily_batta_with_overnight_stay = float(policy.get('inside_kerala__', 0)) + + # Calculate Daily Batta without Overnight Stay + if not is_actual_daily_batta_without_overnight: # Check if policy is not actual + if not is_overnight_stay: # Ensure overnight stay is NOT checked + if total_distance_travelled_km > 100 and total_hours >= 8: # Additional condition + if is_travelling_outside_kerala: + daily_batta_without_overnight_stay = float(policy.get('outside_kerala', 0)) + else: + daily_batta_without_overnight_stay = float(policy.get('inside_kerala', 0)) + + return { + "daily_batta_with_overnight_stay": daily_batta_with_overnight_stay, + "daily_batta_without_overnight_stay": daily_batta_without_overnight_stay + } + +@frappe.whitelist() +def get_batta_policy_values(): + ''' + Fetch and return the batta policy values from the 'Batta Policy' doctype + ''' + result = frappe.db.get_value('Batta Policy', {}, ['is_actual', 'is_actual_', 'is_actual__', 'is_actual___'], as_dict=True) + return result From e98e9b8e654e5dc3ec6e1b362b1a31358c2d5739 Mon Sep 17 00:00:00 2001 From: Arathi Date: Tue, 1 Apr 2025 15:53:33 +0530 Subject: [PATCH 29/40] fix:Remove duplicates and unnecessary repetitions for efficiency --- .../bureau_trip_sheet/bureau_trip_sheet.js | 2 +- .../bureau_trip_sheet/bureau_trip_sheet.py | 83 +++++++------------ 2 files changed, 29 insertions(+), 56 deletions(-) diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js index 55f82f783..06d3a5530 100644 --- a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js @@ -295,7 +295,7 @@ function calculate_total_food_allowance(frm, cdt, cdn) { frm.refresh_field("work_details"); - let total_batta_sum = frm.doc.work_details.reduce((sum, r) => sum + (r.total_batta || 0), 0); + let total_batta_sum = frm.doc.work_details.reduce((sum, r) => sum + (r.total_batta || 0), 0); frm.set_value("total_daily_batta", total_batta_sum); } diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py index 79d1b0caf..2fdbd7606 100644 --- a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py @@ -84,67 +84,40 @@ def get_batta_for_food_allowance(designation, from_date_time, to_date_time, tota ''' Method to get Batta for Food ''' - values = { 'break_fast':0, 'lunch':0, 'dinner':0 } - batta_policy = frappe.db.exists('Batta Policy', { 'designation':designation }) + values = {'break_fast': 0, 'lunch': 0, 'dinner': 0} + batta_policy = frappe.db.exists('Batta Policy', {'designation': designation}) from_date_time = get_datetime(from_date_time) to_date_time = get_datetime(to_date_time) required_hours = 6 - if batta_policy and float(total_hrs)>required_hours: + + if batta_policy and float(total_hrs) > required_hours: break_fast, lunch, dinner = frappe.db.get_value('Batta Policy', batta_policy, ['break_fast', 'lunch', 'dinner']) - same_date = False - if getdate(from_date_time) == getdate(to_date_time): - same_date = True - #Breakfast check - if same_date: - date_threshold = getdate(from_date_time) - break_fast_start_time = get_datetime('{0} {1}'.format(date_threshold, '04:00')) - break_fast_end_time = get_datetime('{0} {1}'.format(date_threshold, '09:00')) - if (from_date_time <= break_fast_start_time <= to_date_time) or (from_date_time <= break_fast_end_time <= to_date_time): - values['break_fast'] = break_fast - lunch_start_time = get_datetime('{0} {1}'.format(date_threshold, '12:30')) - lunch_end_time = get_datetime('{0} {1}'.format(date_threshold, '14:00')) - if (from_date_time <= lunch_start_time <= to_date_time) or (from_date_time <= lunch_end_time <= to_date_time): - values['lunch'] = lunch - dinner_start_time = get_datetime('{0} {1}'.format(date_threshold, '18:00')) - dinner_end_time = get_datetime('{0} {1}'.format(date_threshold, '21:00')) - if (from_date_time <= dinner_start_time <= to_date_time) or (from_date_time <= dinner_end_time <= to_date_time): - values['dinner'] = dinner - else: - #Breakfast check - date_threshold = getdate(from_date_time) #Check with Start Date - break_fast_start_time = get_datetime('{0} {1}'.format(date_threshold, '04:00')) - break_fast_end_time = get_datetime('{0} {1}'.format(date_threshold, '09:00')) - if (from_date_time <= break_fast_start_time <= to_date_time) or (from_date_time <= break_fast_end_time <= to_date_time): - values['break_fast'] = break_fast - date_threshold = getdate(to_date_time) #Check with End Date - break_fast_start_time = get_datetime('{0} {1}'.format(date_threshold, '04:00')) - break_fast_end_time = get_datetime('{0} {1}'.format(date_threshold, '09:00')) - if (from_date_time <= break_fast_start_time <= to_date_time) or (from_date_time <= break_fast_end_time <= to_date_time): - values['break_fast'] = values.get('break_fast', 0) + break_fast - #Lunch Check - date_threshold = getdate(from_date_time) #Check with Start Date - lunch_start_time = get_datetime('{0} {1}'.format(date_threshold, '12:30')) - lunch_end_time = get_datetime('{0} {1}'.format(date_threshold, '14:00')) - if (from_date_time <= lunch_start_time <= to_date_time) or (from_date_time <= lunch_end_time <= to_date_time): - values['lunch'] = lunch - date_threshold = getdate(to_date_time) #Check with End Date - lunch_start_time = get_datetime('{0} {1}'.format(date_threshold, '12:30')) - lunch_end_time = get_datetime('{0} {1}'.format(date_threshold, '14:00')) - if (from_date_time <= lunch_start_time <= to_date_time) or (from_date_time <= lunch_end_time <= to_date_time): - values['lunch'] = values.get('lunch', 0) + lunch - #Dinner Check - date_threshold = getdate(from_date_time) #Check with Start Date - dinner_start_time = get_datetime('{0} {1}'.format(date_threshold, '18:00')) - dinner_end_time = get_datetime('{0} {1}'.format(date_threshold, '21:00')) - if (from_date_time <= dinner_start_time <= to_date_time) or (from_date_time <= dinner_end_time <= to_date_time): - values['dinner'] = dinner - date_threshold = getdate(to_date_time) #Check with End Date - dinner_start_time = get_datetime('{0} {1}'.format(date_threshold, '18:00')) - dinner_end_time = get_datetime('{0} {1}'.format(date_threshold, '21:00')) - if (from_date_time <= dinner_start_time <= to_date_time) or (from_date_time <= dinner_end_time <= to_date_time): - values['dinner'] = values.get('dinner', 0) + dinner + same_date = getdate(from_date_time) == getdate(to_date_time) + + meal_times = { + 'break_fast': ('04:00', '09:00', break_fast), + 'lunch': ('12:30', '14:00', lunch), + 'dinner': ('18:00', '21:00', dinner) + } + + for meal, (start_time, end_time, allowance) in meal_times.items(): + if same_date: + date_threshold = getdate(from_date_time) + if check_meal_time(from_date_time, to_date_time, date_threshold, start_time, end_time): + values[meal] = allowance + else: + # Check for both start and end dates + for date_threshold in [getdate(from_date_time), getdate(to_date_time)]: + if check_meal_time(from_date_time, to_date_time, date_threshold, start_time, end_time): + values[meal] += allowance + return values +def check_meal_time(from_date_time, to_date_time, date_threshold, start_time, end_time): + start_datetime = get_datetime('{} {}'.format(date_threshold, start_time)) + end_datetime = get_datetime('{} {}'.format(date_threshold, end_time)) + return (from_date_time <= start_datetime <= to_date_time) or (from_date_time <= end_datetime <= to_date_time) + @frappe.whitelist() def calculate_batta_allowance(designation=None, is_travelling_outside_kerala=0, is_overnight_stay=0, total_distance_travelled_km=0, total_hours=0): ''' From 71708d1c026a5091184929e6d75cac430a341bcd Mon Sep 17 00:00:00 2001 From: Sherin KR Date: Tue, 1 Apr 2025 16:49:56 +0530 Subject: [PATCH 30/40] fix: Include in IBF --- .../sales_invoice/sales_invoice.js | 119 +++++++++--------- 1 file changed, 56 insertions(+), 63 deletions(-) diff --git a/beams/beams/custom_scripts/sales_invoice/sales_invoice.js b/beams/beams/custom_scripts/sales_invoice/sales_invoice.js index 12a8ce4e9..6ac985647 100644 --- a/beams/beams/custom_scripts/sales_invoice/sales_invoice.js +++ b/beams/beams/custom_scripts/sales_invoice/sales_invoice.js @@ -1,83 +1,76 @@ frappe.ui.form.on('Sales Invoice', { - refresh: function(frm) { - setTimeout(() => { - frm.remove_custom_button('Delivery', 'Create'); - frm.remove_custom_button('Timesheet', 'Get Items From'); - frm.remove_custom_button('Delivery Note', 'Get Items From'); - }, 500); - set_actual_customer_query(frm); + refresh: function (frm) { + setTimeout(() => { + frm.remove_custom_button('Delivery', 'Create'); + frm.remove_custom_button('Timesheet', 'Get Items From'); + frm.remove_custom_button('Delivery Note', 'Get Items From'); + }, 500); + set_actual_customer_query(frm); - // Check if the Sales Invoice is being created from a Quotation (i.e., reference_id is set) - if (frm.doc.reference_id) { - // Make the customer field read-only - frm.set_df_property('customer', 'read_only', 1); - } - }, - - sales_type: function(frm) { - if (frm.doc.sales_type) { - set_item_code_query(frm); - } - - // Check and update 'include_in_ibf' based on the current state of the form - check_include_in_ibf(frm); - - // Clear and refresh the items table - frm.clear_table('items'); - frm.refresh_field('items'); - }, + // Check if the Sales Invoice is being created from a Quotation (i.e., reference_id is set) + if (frm.doc.reference_id) { + // Make the customer field read-only + frm.set_df_property('customer', 'read_only', 1); + } + }, + sales_type: function (frm) { + if (frm.doc.sales_type) { + set_item_code_query(frm); + } + check_include_in_ibf(frm); - is_barter_invoice: function(frm) { - check_include_in_ibf(frm); - }, + // Clear and refresh the items table + frm.clear_table('items'); + frm.refresh_field('items'); + }, - is_agent: function(frm) { - check_include_in_ibf(frm); - }, - - onload: function(frm) { - if (frm.is_new) { - check_include_in_ibf(frm); - } - - // Check if the Sales Invoice is being created from a Quotation (i.e., reference_id is set) - if (frm.doc.reference_id) { - // Make the customer field read-only - frm.set_df_property('customer', 'read_only', 1); + is_barter_invoice: function (frm) { + check_include_in_ibf(frm); + }, + is_agent: function (frm) { + check_include_in_ibf(frm); + }, + onload: function (frm) { + // Check if the Sales Invoice is being created from a Quotation (i.e., reference_id is set) + if (frm.doc.reference_id) { + // Make the customer field read-only + frm.set_df_property('customer', 'read_only', 1); + } } - } }); // Function to set query for the 'actual_customer' field function set_actual_customer_query(frm) { - frm.set_query('actual_customer', function() { - return { - filters: { - 'is_agent': 0 - } - }; - }); + frm.set_query('actual_customer', function () { + return { + filters: { + 'is_agent': 0 + } + }; + }); } // Function to set query for 'item_code' field in the 'items' child table based on 'sales_type' function set_item_code_query(frm) { - frm.fields_dict['items'].grid.get_field('item_code').get_query = function(doc, cdt, cdn) { - return { - filters: { - 'sales_type': frm.doc.sales_type - } + frm.fields_dict['items'].grid.get_field('item_code').get_query = function (doc, cdt, cdn) { + return { + filters: { + 'sales_type': frm.doc.sales_type + } + }; }; - }; } // Function to check and set the value of 'include_in_ibf' function check_include_in_ibf(frm) { - frappe.db.get_value('Sales Type', frm.doc.sales_type, 'is_time_sales', function(value) { - if (value && value.is_time_sales && !frm.doc.is_barter_invoice && frm.doc.is_agent) { - frm.set_value('include_in_ibf', 1); // Set 'include_in_ibf' to 1 (true) - } else { - frm.set_value('include_in_ibf', 0); // Set 'include_in_ibf' to 0 (false) + if (frm.is_new()) { + frappe.db.get_value('Sales Type', frm.doc.sales_type, 'is_time_sales', function (value) { + if (value && value.is_time_sales && !frm.doc.is_barter_invoice && frm.doc.is_agent) { + frm.set_value('include_in_ibf', 1); // Set 'include_in_ibf' to 1 (true) + } else { + frm.set_value('include_in_ibf', 0); // Set 'include_in_ibf' to 0 (false) + } + frm.refresh_field('include_in_ibf'); // Refresh the field to reflect the change + }); } - frm.refresh_field('include_in_ibf'); // Refresh the field to reflect the change - }); } From 8fc42a0075240b7189f8cd0c41e7b8b8d200526f Mon Sep 17 00:00:00 2001 From: hridyalakshmi Date: Thu, 3 Apr 2025 09:37:54 +0530 Subject: [PATCH 31/40] fix: Update Food allowance --- .../beams/doctype/batta_claim/batta_claim.py | 84 +++++++------------ 1 file changed, 31 insertions(+), 53 deletions(-) diff --git a/beams/beams/doctype/batta_claim/batta_claim.py b/beams/beams/doctype/batta_claim/batta_claim.py index 46785a5a1..54c43aa77 100644 --- a/beams/beams/doctype/batta_claim/batta_claim.py +++ b/beams/beams/doctype/batta_claim/batta_claim.py @@ -4,7 +4,7 @@ import frappe import json import re -from frappe.utils import getdate, get_datetime, date_diff +from frappe.utils import getdate, get_datetime, date_diff, add_days from frappe.model.document import Document @@ -208,66 +208,44 @@ def get_batta_for_food_allowance(designation, from_date_time, to_date_time, tota ''' Method to get Batta for Food ''' - values = { 'break_fast':0, 'lunch':0, 'dinner':0 } - batta_policy = frappe.db.exists('Batta Policy', { 'designation':designation }) + values = { 'break_fast': 0, 'lunch': 0, 'dinner': 0 } + batta_policy = frappe.db.exists('Batta Policy', { 'designation': designation }) from_date_time = get_datetime(from_date_time) to_date_time = get_datetime(to_date_time) required_hours = 4 if is_delhi_bureau else 6 - if batta_policy and float(total_hrs)>required_hours: + + if batta_policy and float(total_hrs) > required_hours: is_actual = frappe.db.get_value('Batta Policy', batta_policy, 'is_actual___') if is_actual: return values + break_fast, lunch, dinner = frappe.db.get_value('Batta Policy', batta_policy, ['break_fast', 'lunch', 'dinner']) - same_date = False - if getdate(from_date_time) == getdate(to_date_time): - same_date = True - #Breakfast check - if same_date: - date_threshold = getdate(from_date_time) - break_fast_start_time = get_datetime('{0} {1}'.format(date_threshold, '04:00')) - break_fast_end_time = get_datetime('{0} {1}'.format(date_threshold, '09:00')) - if (from_date_time <= break_fast_start_time <= to_date_time) or (from_date_time <= break_fast_end_time <= to_date_time): - values['break_fast'] = break_fast - lunch_start_time = get_datetime('{0} {1}'.format(date_threshold, '12:30')) - lunch_end_time = get_datetime('{0} {1}'.format(date_threshold, '14:00')) - if (from_date_time <= lunch_start_time <= to_date_time) or (from_date_time <= lunch_end_time <= to_date_time): - values['lunch'] = lunch - dinner_start_time = get_datetime('{0} {1}'.format(date_threshold, '18:00')) - dinner_end_time = get_datetime('{0} {1}'.format(date_threshold, '21:00')) - if (from_date_time <= dinner_start_time <= to_date_time) or (from_date_time <= dinner_end_time <= to_date_time): - values['dinner'] = dinner - else: - #Breakfast check - date_threshold = getdate(from_date_time) #Check with Start Date - break_fast_start_time = get_datetime('{0} {1}'.format(date_threshold, '04:00')) - break_fast_end_time = get_datetime('{0} {1}'.format(date_threshold, '09:00')) - if (from_date_time <= break_fast_start_time <= to_date_time) or (from_date_time <= break_fast_end_time <= to_date_time): - values['break_fast'] = break_fast - date_threshold = getdate(to_date_time) #Check with End Date - break_fast_start_time = get_datetime('{0} {1}'.format(date_threshold, '04:00')) - break_fast_end_time = get_datetime('{0} {1}'.format(date_threshold, '09:00')) + + current_date = getdate(from_date_time) + end_date = getdate(to_date_time) + + while current_date <= end_date: + # Define meal timings for the current day + break_fast_start_time = get_datetime(f'{current_date} 04:00') + break_fast_end_time = get_datetime(f'{current_date} 09:00') + lunch_start_time = get_datetime(f'{current_date} 12:30') + lunch_end_time = get_datetime(f'{current_date} 14:00') + dinner_start_time = get_datetime(f'{current_date} 18:00') + dinner_end_time = get_datetime(f'{current_date} 21:00') + + # Check Breakfast if (from_date_time <= break_fast_start_time <= to_date_time) or (from_date_time <= break_fast_end_time <= to_date_time): - values['break_fast'] = values.get('break_fast', 0) + break_fast - #Lunch Check - date_threshold = getdate(from_date_time) #Check with Start Date - lunch_start_time = get_datetime('{0} {1}'.format(date_threshold, '12:30')) - lunch_end_time = get_datetime('{0} {1}'.format(date_threshold, '14:00')) - if (from_date_time <= lunch_start_time <= to_date_time) or (from_date_time <= lunch_end_time <= to_date_time): - values['lunch'] = lunch - date_threshold = getdate(to_date_time) #Check with End Date - lunch_start_time = get_datetime('{0} {1}'.format(date_threshold, '12:30')) - lunch_end_time = get_datetime('{0} {1}'.format(date_threshold, '14:00')) + values['break_fast'] += break_fast + + # Check Lunch if (from_date_time <= lunch_start_time <= to_date_time) or (from_date_time <= lunch_end_time <= to_date_time): - values['lunch'] = values.get('lunch', 0) + lunch - #Dinner Check - date_threshold = getdate(from_date_time) #Check with Start Date - dinner_start_time = get_datetime('{0} {1}'.format(date_threshold, '18:00')) - dinner_end_time = get_datetime('{0} {1}'.format(date_threshold, '21:00')) - if (from_date_time <= dinner_start_time <= to_date_time) or (from_date_time <= dinner_end_time <= to_date_time): - values['dinner'] = dinner - date_threshold = getdate(to_date_time) #Check with End Date - dinner_start_time = get_datetime('{0} {1}'.format(date_threshold, '18:00')) - dinner_end_time = get_datetime('{0} {1}'.format(date_threshold, '21:00')) + values['lunch'] += lunch + + # Check Dinner if (from_date_time <= dinner_start_time <= to_date_time) or (from_date_time <= dinner_end_time <= to_date_time): - values['dinner'] = values.get('dinner', 0) + dinner + values['dinner'] += dinner + + # Move to the next day + current_date = add_days(current_date, 1) + return values From 4751dc433404d8db8da11819fd40d9f353442de2 Mon Sep 17 00:00:00 2001 From: hridyalakshmi Date: Tue, 8 Apr 2025 10:55:41 +0530 Subject: [PATCH 32/40] Update Revenue Budget and Revenue Template --- .../revenue_account/revenue_account.json | 165 +++++++++++++++++- .../doctype/revenue_budget/revenue_budget.js | 62 +++++++ .../revenue_budget/revenue_budget.json | 68 +++++--- .../doctype/revenue_budget/revenue_budget.py | 40 ++++- .../doctype/revenue_centre/revenue_centre.js | 16 +- .../revenue_centre/revenue_centre.json | 15 +- .../revenue_template/revenue_template.json | 37 ++-- .../revenue_template_item.json | 35 ++-- 8 files changed, 378 insertions(+), 60 deletions(-) diff --git a/beams/beams/doctype/revenue_account/revenue_account.json b/beams/beams/doctype/revenue_account/revenue_account.json index 79ea86538..f815aff60 100644 --- a/beams/beams/doctype/revenue_account/revenue_account.json +++ b/beams/beams/doctype/revenue_account/revenue_account.json @@ -8,8 +8,11 @@ "field_order": [ "revenue_centre", "account", + "revenue_group", "column_break_quzd", + "revenue_region", "revenue_amount", + "description", "monthly_amount_distribution_section", "april", "may", @@ -24,7 +27,23 @@ "december", "january", "february", - "march" + "march", + "monthly_amount_distributioninr_section", + "april_inr", + "may_inr", + "june_inr", + "july_inr", + "column_break_ulxk", + "august_inr", + "september_inr", + "october_inr", + "november_inr", + "column_break_poio", + "december_inr", + "january_inr", + "february_inr", + "march_inr", + "revenue_amount_inr" ], "fields": [ { @@ -41,6 +60,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Revenue Amount", + "options": "company_currency", "precision": "2", "reqd": 1 }, @@ -123,17 +143,158 @@ "label": "December" }, { + "fetch_from": "revenue_centre.account", "fieldname": "account", "fieldtype": "Link", "label": "Account", "options": "Account", + "read_only": 1, "reqd": 1 + }, + { + "fieldname": "revenue_group", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Revenue Group", + "options": "Revenue Group", + "reqd": 1 + }, + { + "fieldname": "revenue_region", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Finance Region", + "options": "National\nGCC", + "reqd": 1 + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description" + }, + { + "collapsible": 1, + "fieldname": "monthly_amount_distributioninr_section", + "fieldtype": "Section Break", + "label": "Monthly Amount Distribution (INR)" + }, + { + "fieldname": "may_inr", + "fieldtype": "Currency", + "label": "May (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "june_inr", + "fieldtype": "Currency", + "label": "June (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "july_inr", + "fieldtype": "Currency", + "label": "July (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "column_break_ulxk", + "fieldtype": "Column Break" + }, + { + "fieldname": "august_inr", + "fieldtype": "Currency", + "label": "August (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "september_inr", + "fieldtype": "Currency", + "label": "September (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "october_inr", + "fieldtype": "Currency", + "label": "October (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "november_inr", + "fieldtype": "Currency", + "label": "November (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "column_break_poio", + "fieldtype": "Column Break" + }, + { + "fieldname": "december_inr", + "fieldtype": "Currency", + "label": "December (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "january_inr", + "fieldtype": "Currency", + "label": "January (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "february_inr", + "fieldtype": "Currency", + "label": "February (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "march_inr", + "fieldtype": "Currency", + "label": "March (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "revenue_amount_inr", + "fieldtype": "Currency", + "label": "Revenue Amount (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "april_inr", + "fieldtype": "Currency", + "label": "April (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-03-18 11:44:42.181253", + "modified": "2025-04-07 17:24:47.191935", "modified_by": "Administrator", "module": "BEAMS", "name": "Revenue Account", diff --git a/beams/beams/doctype/revenue_budget/revenue_budget.js b/beams/beams/doctype/revenue_budget/revenue_budget.js index 61df55d20..70166b1b1 100644 --- a/beams/beams/doctype/revenue_budget/revenue_budget.js +++ b/beams/beams/doctype/revenue_budget/revenue_budget.js @@ -6,6 +6,41 @@ frappe.ui.form.on("Revenue Budget", { }, company: function (frm) { set_filters(frm); + fetch_template(frm); + }, + revenue_category: function (frm) { + set_filters(frm); + fetch_template(frm); + }, + revenue_template: function(frm) { + if (frm.doc.revenue_template) { + frappe.call({ + method: 'frappe.client.get', + args: { + doctype: 'Revenue Template', + name: frm.doc.revenue_template + }, + callback: function (response) { + if (response.message) { + let revenue_template = response.message; + + frm.clear_table('revenue_accounts'); + + // Loop through the revenue_template_item child table + let template_items = revenue_template.revenue_template_item || []; + template_items.forEach(function (item) { + let row = frm.add_child('revenue_accounts'); + row.revenue_group = item.revenue_group; + row.account = item.account; + row.revenue_region = item.revenue_region; + row.revenue_centre = item.revenue_centre; + }); + + frm.refresh_field('revenue_accounts'); + } + } + }); + } } }); function set_filters(frm) { @@ -16,6 +51,14 @@ function set_filters(frm) { } }; }); + frm.set_query('revenue_template', function () { + return { + filters: { + company: frm.doc.company, + revenue_category: frm.doc.revenue_category + } + }; + }); } frappe.ui.form.on('Revenue Account', { @@ -85,3 +128,22 @@ function calculate_revenue_amount(frm, cdt, cdn) { frappe.model.set_value(cdt, cdn, 'revenue_amount', total); frm.refresh_field('revenue_account'); } + +function fetch_template(frm) { + if (frm.doc.revenue_category && frm.doc.company) { + frappe.call({ + method: 'beams.beams.doctype.revenue_budget.revenue_budget.get_revenue_template', + args: { + revenue_category: frm.doc.revenue_category, + company: frm.doc.company + }, + callback: function(r) { + if (r.message) { + frm.set_value('revenue_template', r.message); + } else { + frm.set_value('revenue_template', ''); + } + } + }); + } +} diff --git a/beams/beams/doctype/revenue_budget/revenue_budget.json b/beams/beams/doctype/revenue_budget/revenue_budget.json index 7d18f1f5b..4753de269 100644 --- a/beams/beams/doctype/revenue_budget/revenue_budget.json +++ b/beams/beams/doctype/revenue_budget/revenue_budget.json @@ -1,18 +1,20 @@ { "actions": [], "allow_rename": 1, - "autoname": "format:{revenue_group}-{revenue_region}/{fiscal_year}/{###}", + "autoname": "format:{revenue_category} {fiscal_year}-{abbreviation}", "creation": "2025-01-29 09:40:07.549966", "doctype": "DocType", "engine": "InnoDB", "field_order": [ "section_break_epzm", "company", - "revenue_group", + "abbreviation", "revenue_category", + "default_currency", + "company_currency", "column_break_wemo", "fiscal_year", - "revenue_region", + "revenue_template", "naming_series", "total_amount", "section_break", @@ -75,12 +77,38 @@ "read_only": 1 }, { - "fieldname": "revenue_group", + "fieldname": "amended_from", "fieldtype": "Link", - "in_list_view": 1, - "label": "Revenue Group", - "options": "Revenue Group", - "reqd": 1 + "label": "Amended From", + "no_copy": 1, + "options": "Revenue Budget", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "revenue_template", + "fieldtype": "Link", + "label": "Revenue Template", + "options": "Revenue Template" + }, + { + "default": "INR", + "fieldname": "default_currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Default Currency", + "options": "Currency", + "read_only": 1 + }, + { + "fetch_from": "company.default_currency", + "fieldname": "company_currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Company Currency", + "options": "Currency", + "read_only": 1 }, { "fieldname": "revenue_category", @@ -90,27 +118,19 @@ "reqd": 1 }, { - "fieldname": "revenue_region", - "fieldtype": "Select", - "label": "Revenue Region", - "options": "National\nGCC", - "reqd": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Revenue Budget", - "print_hide": 1, - "read_only": 1, - "search_index": 1 + "fetch_from": "company.abbr", + "fieldname": "abbreviation", + "fieldtype": "Data", + "hidden": 1, + "label": "Abbreviation", + "read_only": 1 } ], + "grid_page_length": 50, "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2025-03-18 12:09:29.003630", + "modified": "2025-04-07 15:00:03.238147", "modified_by": "Administrator", "module": "BEAMS", "name": "Revenue Budget", diff --git a/beams/beams/doctype/revenue_budget/revenue_budget.py b/beams/beams/doctype/revenue_budget/revenue_budget.py index 59b21cf17..290ba809e 100644 --- a/beams/beams/doctype/revenue_budget/revenue_budget.py +++ b/beams/beams/doctype/revenue_budget/revenue_budget.py @@ -5,10 +5,48 @@ from frappe.model.document import Document class RevenueBudget(Document): - def before_save(self): self.calculate_total_amount() + def validate(self): + self.convert_currency() + def calculate_total_amount(self): total = sum([row.revenue_amount for row in self.get("revenue_accounts") if row.revenue_amount]) self.total_amount = total + + def convert_currency(self): + """Convert Revenue amounts for non-INR companies""" + company_currency = frappe.db.get_value("Company", self.company, "default_currency") + exchange_rate = 1 + + if company_currency != "INR": + exchange_rate = frappe.db.get_value("Company", self.company, "exchange_rate_to_inr") + if not exchange_rate: + frappe.throw( + f"Please set Exchange Rate from {company_currency} to INR for {self.company}", + title="Message", + ) + + months = [ + "january", "february", "march", "april", "may", "june", + "july", "august", "september", "october", "november", "december" + ] + + def apply_conversion(row): + """Apply exchange rate conversion to a revenue row""" + row.revenue_amount_inr = row.revenue_amount * exchange_rate + for month in months: + setattr(row, f"{month}_inr", (getattr(row, month, 0) or 0) * exchange_rate) + + for row in self.revenue_accounts: + apply_conversion(row) + +@frappe.whitelist() +def get_revenue_template(revenue_category, company): + template = frappe.db.get_value( + "Revenue Template", + {"revenue_category": revenue_category, "company": company}, + "name" + ) + return template diff --git a/beams/beams/doctype/revenue_centre/revenue_centre.js b/beams/beams/doctype/revenue_centre/revenue_centre.js index 9dc746d1c..7991458b1 100644 --- a/beams/beams/doctype/revenue_centre/revenue_centre.js +++ b/beams/beams/doctype/revenue_centre/revenue_centre.js @@ -1,8 +1,14 @@ // Copyright (c) 2025, efeone and contributors // For license information, please see license.txt -// frappe.ui.form.on("Revenue Centre", { -// refresh(frm) { - -// }, -// }); +frappe.ui.form.on("Revenue Centre", { + onload: function(frm) { + frm.set_query("account", function() { + return { + filters: { + account_type: "Income Account" + } + }; + }); + } +}); diff --git a/beams/beams/doctype/revenue_centre/revenue_centre.json b/beams/beams/doctype/revenue_centre/revenue_centre.json index 5193f51d0..8ec048246 100644 --- a/beams/beams/doctype/revenue_centre/revenue_centre.json +++ b/beams/beams/doctype/revenue_centre/revenue_centre.json @@ -6,19 +6,30 @@ "doctype": "DocType", "engine": "InnoDB", "field_order": [ - "revenue_centre" + "revenue_centre", + "account" ], "fields": [ { "fieldname": "revenue_centre", "fieldtype": "Data", + "in_list_view": 1, "label": "Revenue Centre", + "reqd": 1, "unique": 1 + }, + { + "fieldname": "account", + "fieldtype": "Link", + "label": "Account", + "options": "Account", + "reqd": 1 } ], + "grid_page_length": 50, "index_web_pages_for_search": 1, "links": [], - "modified": "2025-03-18 11:35:03.479904", + "modified": "2025-04-07 10:10:07.817821", "modified_by": "Administrator", "module": "BEAMS", "name": "Revenue Centre", diff --git a/beams/beams/doctype/revenue_template/revenue_template.json b/beams/beams/doctype/revenue_template/revenue_template.json index 5089290b6..4606a9729 100644 --- a/beams/beams/doctype/revenue_template/revenue_template.json +++ b/beams/beams/doctype/revenue_template/revenue_template.json @@ -1,24 +1,19 @@ { "actions": [], "allow_rename": 1, - "autoname": "field:template_title", + "autoname": "format:{revenue_category} - {abbreviation}", "creation": "2025-02-17 14:10:03.498118", "doctype": "DocType", "engine": "InnoDB", "field_order": [ - "template_title", "company", + "abbreviation", + "column_break_ofsa", + "revenue_category", "section_break_ybn1", "revenue_template_item" ], "fields": [ - { - "fieldname": "template_title", - "fieldtype": "Data", - "label": "Template Title", - "reqd": 1, - "unique": 1 - }, { "fieldname": "company", "fieldtype": "Link", @@ -36,15 +31,35 @@ "fieldtype": "Table", "label": "Revenue Template Item", "options": "Revenue Template Item" + }, + { + "fieldname": "revenue_category", + "fieldtype": "Link", + "label": "Revenue Category", + "options": "Revenue Category", + "reqd": 1 + }, + { + "fetch_from": "company.abbr", + "fieldname": "abbreviation", + "fieldtype": "Data", + "hidden": 1, + "label": "Abbreviation", + "read_only": 1 + }, + { + "fieldname": "column_break_ofsa", + "fieldtype": "Column Break" } ], + "grid_page_length": 50, "index_web_pages_for_search": 1, "links": [], - "modified": "2025-02-21 13:33:27.864251", + "modified": "2025-04-07 14:45:56.321830", "modified_by": "Administrator", "module": "BEAMS", "name": "Revenue Template", - "naming_rule": "By fieldname", + "naming_rule": "Expression", "owner": "Administrator", "permissions": [ { diff --git a/beams/beams/doctype/revenue_template_item/revenue_template_item.json b/beams/beams/doctype/revenue_template_item/revenue_template_item.json index cca68e269..c2640ef10 100644 --- a/beams/beams/doctype/revenue_template_item/revenue_template_item.json +++ b/beams/beams/doctype/revenue_template_item/revenue_template_item.json @@ -7,9 +7,9 @@ "engine": "InnoDB", "field_order": [ "revenue_centre", - "revenue_group", + "account", "column_break_vrbs", - "revenue_category", + "revenue_group", "revenue_region" ], "fields": [ @@ -18,39 +18,44 @@ "fieldtype": "Link", "in_list_view": 1, "label": "Revenue Group", - "options": "Revenue Group" - }, - { - "fieldname": "revenue_category", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Revenue Category", - "options": "Revenue Category" + "options": "Revenue Group", + "reqd": 1 }, { "fieldname": "revenue_centre", "fieldtype": "Link", "in_list_view": 1, "label": "Revenue Centre", - "options": "Account", + "options": "Revenue Centre", "reqd": 1 }, { "fieldname": "revenue_region", - "fieldtype": "Link", + "fieldtype": "Select", "in_list_view": 1, - "label": "Revenue Region", - "options": "Region" + "label": "Finance Region", + "options": "National\nGCC", + "reqd": 1 }, { "fieldname": "column_break_vrbs", "fieldtype": "Column Break" + }, + { + "fetch_from": "revenue_centre.account", + "fieldname": "account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Account", + "options": "Account", + "reqd": 1 } ], + "grid_page_length": 50, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-02-21 13:33:45.317169", + "modified": "2025-04-07 16:38:54.754188", "modified_by": "Administrator", "module": "BEAMS", "name": "Revenue Template Item", From 8c8c860d26a202e86781af1313ce5f7d7ae9b7a5 Mon Sep 17 00:00:00 2001 From: hridyalakshmi Date: Tue, 8 Apr 2025 11:38:46 +0530 Subject: [PATCH 33/40] fix: Added Documentation --- beams/beams/doctype/revenue_budget/revenue_budget.js | 3 ++- beams/beams/doctype/revenue_budget/revenue_budget.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/beams/beams/doctype/revenue_budget/revenue_budget.js b/beams/beams/doctype/revenue_budget/revenue_budget.js index 70166b1b1..c25d87d0e 100644 --- a/beams/beams/doctype/revenue_budget/revenue_budget.js +++ b/beams/beams/doctype/revenue_budget/revenue_budget.js @@ -12,6 +12,7 @@ frappe.ui.form.on("Revenue Budget", { set_filters(frm); fetch_template(frm); }, + /* Fetch Revenue Template Items in to Revenue Accounts when a Revenue Template is Selected */ revenue_template: function(frm) { if (frm.doc.revenue_template) { frappe.call({ @@ -128,7 +129,7 @@ function calculate_revenue_amount(frm, cdt, cdn) { frappe.model.set_value(cdt, cdn, 'revenue_amount', total); frm.refresh_field('revenue_account'); } - +/* Fetch Revenue Template in Revenue Budget based on Revenue category and Selected company */ function fetch_template(frm) { if (frm.doc.revenue_category && frm.doc.company) { frappe.call({ diff --git a/beams/beams/doctype/revenue_budget/revenue_budget.py b/beams/beams/doctype/revenue_budget/revenue_budget.py index 290ba809e..ae0aa53e2 100644 --- a/beams/beams/doctype/revenue_budget/revenue_budget.py +++ b/beams/beams/doctype/revenue_budget/revenue_budget.py @@ -44,6 +44,7 @@ def apply_conversion(row): @frappe.whitelist() def get_revenue_template(revenue_category, company): + """Get Revenue Template based on Revenue category and selected Company""" template = frappe.db.get_value( "Revenue Template", {"revenue_category": revenue_category, "company": company}, From 56c5fd3cfb949bb3ff423dfba0dd4da183b0c51e Mon Sep 17 00:00:00 2001 From: hridyalakshmi Date: Fri, 25 Apr 2025 12:31:16 +0530 Subject: [PATCH 34/40] fix: Updated daily batta reference in journal entry accounts --- beams/beams/doctype/batta_claim/batta_claim.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beams/beams/doctype/batta_claim/batta_claim.py b/beams/beams/doctype/batta_claim/batta_claim.py index 54c43aa77..b395fdfd5 100644 --- a/beams/beams/doctype/batta_claim/batta_claim.py +++ b/beams/beams/doctype/batta_claim/batta_claim.py @@ -63,13 +63,13 @@ def create_journal_entry_from_batta_claim(self): 'account': batta_payable_account, 'party_type': 'Employee', 'party': self.employee, - 'debit_in_account_currency': self.total_driver_batta, + 'debit_in_account_currency': self.total_daily_batta, 'credit_in_account_currency': 0, }) journal_entry.append('accounts', { 'account': batta_expense_account, 'debit_in_account_currency': 0, - 'credit_in_account_currency': self.total_driver_batta, + 'credit_in_account_currency': self.total_daily_batta, }) journal_entry.insert() journal_entry.submit() From 4c8960b96bb0d19c6f47077b6d62c852edc22ede Mon Sep 17 00:00:00 2001 From: hridyalakshmi Date: Fri, 2 May 2025 09:54:21 +0530 Subject: [PATCH 35/40] fix: Update travelled distance in food calculation and Daily batta without overnight stay --- beams/beams/doctype/batta_claim/batta_claim.js | 2 +- beams/beams/doctype/batta_claim/batta_claim.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/beams/beams/doctype/batta_claim/batta_claim.js b/beams/beams/doctype/batta_claim/batta_claim.js index bd8dbdf20..feef954cc 100644 --- a/beams/beams/doctype/batta_claim/batta_claim.js +++ b/beams/beams/doctype/batta_claim/batta_claim.js @@ -384,7 +384,7 @@ function set_batta_for_food_allowance(frm, cdt, cdn) { is_eligible = true; } } else { - if (child.distance_travelled_km >= 50 && child.distance_travelled_km <= 100 && child.total_hours > 6) { + if (child.distance_travelled_km >= 50 && child.distance_travelled_km < 100 && child.total_hours > 6) { is_eligible = true; } } diff --git a/beams/beams/doctype/batta_claim/batta_claim.py b/beams/beams/doctype/batta_claim/batta_claim.py index b395fdfd5..8d10ddc14 100644 --- a/beams/beams/doctype/batta_claim/batta_claim.py +++ b/beams/beams/doctype/batta_claim/batta_claim.py @@ -183,7 +183,7 @@ def sanitize_number(value): # Calculate Daily Batta without Overnight Stay if not is_actual_daily_batta_without_overnight: # Check if policy is not actual if not is_overnight_stay: # Ensure overnight stay is NOT checked - if total_distance_travelled_km > 100 and total_hours >= 8: # Additional condition + if total_distance_travelled_km >= 100 and total_hours >= 8: # Additional condition if is_travelling_outside_kerala: daily_batta_without_overnight_stay = float(policy.get('outside_kerala', 0)) else: From 64fa1844157d60fafcd1bb1d934399ea479fe667 Mon Sep 17 00:00:00 2001 From: Arathi Date: Wed, 14 May 2025 11:30:45 +0530 Subject: [PATCH 36/40] fix:Fix batta calculation timing by increasing setTimeout delay --- beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js index 06d3a5530..5d3c5b343 100644 --- a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js @@ -7,7 +7,7 @@ frappe.ui.form.on('Trip Details', { setTimeout(() => { set_batta_for_food_allowance(frm, cdt, cdn); calculate_batta(frm, cdt, cdn); - }, 30); + }, 200); }, to_date_and_time: function (frm, cdt, cdn) { let row = locals[cdt][cdn]; @@ -24,7 +24,7 @@ frappe.ui.form.on('Trip Details', { setTimeout(() => { set_batta_for_food_allowance(frm, cdt, cdn); calculate_batta(frm, cdt, cdn); - }, 30); + }, 200); } calculate_hours_and_days(frm, cdt, cdn); From 7b1f8e92f31b63827585c67978a0c0b0f124b27e Mon Sep 17 00:00:00 2001 From: sappu-hub Date: Wed, 14 May 2025 14:22:23 +0530 Subject: [PATCH 37/40] fix : In Batta Claim now date can be edited --- beams/beams/doctype/batta_claim/batta_claim.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beams/beams/doctype/batta_claim/batta_claim.js b/beams/beams/doctype/batta_claim/batta_claim.js index feef954cc..2e7d6da75 100644 --- a/beams/beams/doctype/batta_claim/batta_claim.js +++ b/beams/beams/doctype/batta_claim/batta_claim.js @@ -206,7 +206,7 @@ frappe.ui.form.on('Work Detail', { setTimeout(() => { set_batta_for_food_allowance(frm, cdt, cdn); calculate_batta(frm, cdt, cdn); - }, 30); + }, 500); }, to_date_and_time: function(frm, cdt, cdn) { let row = locals[cdt][cdn]; @@ -225,7 +225,7 @@ frappe.ui.form.on('Work Detail', { setTimeout(() => { set_batta_for_food_allowance(frm, cdt, cdn); calculate_batta(frm, cdt, cdn); - }, 30); + }, 500); } } }); From 757121e0275bc3d212b15d9bb1be336f422a3515 Mon Sep 17 00:00:00 2001 From: Sherin KR Date: Fri, 16 May 2025 12:13:27 +0530 Subject: [PATCH 38/40] Merge pull request #970 from efeone/acc05 Fix Batta Claim date editing problem --- .github/workflows/master.yml | 2 +- beams/api/api.py | 1314 ++- .../custom_scripts/appraisal/appraisal.py | 202 +- .../asset_movement/asset_movement.py | 187 +- .../attendance_request/attendance_request.py | 160 +- .../employee_checkin/employee_checkin.py | 197 +- .../job_applicant/job_applicant.js | 16 + beams/beams/custom_scripts/project/project.py | 8 +- .../allocated_resource_detail/__init__.py | 0 .../allocated_resource_detail.json | 62 + .../allocated_resource_detail.py | 9 + .../beams/doctype/batta_claim/batta_claim.js | 583 +- .../doctype/batta_claim/batta_claim.json | 83 +- .../beams/doctype/batta_claim/batta_claim.py | 258 +- .../doctype/batta_policy/batta_policy.json | 12 +- .../doctype/bureau_trip_sheet/__init__.py | 0 .../bureau_trip_sheet/bureau_trip_sheet.js | 417 + .../bureau_trip_sheet/bureau_trip_sheet.json | 248 + .../bureau_trip_sheet/bureau_trip_sheet.py | 183 + .../test_bureau_trip_sheet.py | 9 + beams/beams/doctype/company_kra/__init__.py | 0 .../doctype/company_kra/company_kra.json | 46 + .../beams/doctype/company_kra/company_kra.py | 9 + .../department_cost_center/__init__.py | 0 .../department_cost_center.json | 33 + .../department_cost_center.py | 9 + .../beams/doctype/department_kra/__init__.py | 0 .../department_kra/department_kra.json | 53 + .../doctype/department_kra/department_kra.py | 9 + beams/beams/doctype/employee_kra/__init__.py | 0 .../doctype/employee_kra/employee_kra.json | 60 + .../doctype/employee_kra/employee_kra.py | 9 + beams/beams/doctype/employees/__init__.py | 0 beams/beams/doctype/employees/employees.json | 35 + beams/beams/doctype/employees/employees.py | 9 + .../beams/doctype/employees_left/__init__.py | 0 .../employees_left/employees_left.json | 33 + .../doctype/employees_left/employees_left.py | 9 + .../doctype/outward_pass/outward_pass.js | 1 - .../petty_cash_request/petty_cash_request.js | 9 + .../petty_cash_request.json | 10 +- .../doctype/reference_document/__init__.py | 0 .../reference_document.json | 40 + .../reference_document/reference_document.py | 9 + .../revenue_account/revenue_account.json | 165 +- .../doctype/revenue_budget/revenue_budget.js | 63 + .../revenue_budget/revenue_budget.json | 68 +- .../doctype/revenue_budget/revenue_budget.py | 41 +- .../doctype/revenue_centre/revenue_centre.js | 16 +- .../revenue_centre/revenue_centre.json | 15 +- .../revenue_template/revenue_template.json | 37 +- .../revenue_template_item.json | 35 +- .../doctype/stringer_bill_date/__init__.py | 0 .../stringer_bill_date.json | 31 + .../stringer_bill_date/stringer_bill_date.py | 9 + .../doctype/trip_details/trip_details.json | 2 +- .../doctype/work_detail/work_detail.json | 93 +- beams/beams/overrides/queries.py | 91 + .../detailed_budget_allocation_report.js | 8 +- .../detailed_budget_allocation_report.json | 5 +- .../detailed_budget_allocation_report.py | 38 + beams/patches.txt | 2 + beams/patches/delete_employee_fields.py | 14 + beams/setup.py | 9445 +++++++++-------- .../job_application_upload/upload_doc.html | 165 +- .../www/job_application_upload/upload_doc.js | 21 +- .../www/job_application_upload/upload_doc.py | 7 - 67 files changed, 8642 insertions(+), 6062 deletions(-) create mode 100644 beams/beams/doctype/allocated_resource_detail/__init__.py create mode 100644 beams/beams/doctype/allocated_resource_detail/allocated_resource_detail.json create mode 100644 beams/beams/doctype/allocated_resource_detail/allocated_resource_detail.py create mode 100644 beams/beams/doctype/bureau_trip_sheet/__init__.py create mode 100644 beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js create mode 100644 beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json create mode 100644 beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py create mode 100644 beams/beams/doctype/bureau_trip_sheet/test_bureau_trip_sheet.py create mode 100644 beams/beams/doctype/company_kra/__init__.py create mode 100644 beams/beams/doctype/company_kra/company_kra.json create mode 100644 beams/beams/doctype/company_kra/company_kra.py create mode 100644 beams/beams/doctype/department_cost_center/__init__.py create mode 100644 beams/beams/doctype/department_cost_center/department_cost_center.json create mode 100644 beams/beams/doctype/department_cost_center/department_cost_center.py create mode 100644 beams/beams/doctype/department_kra/__init__.py create mode 100644 beams/beams/doctype/department_kra/department_kra.json create mode 100644 beams/beams/doctype/department_kra/department_kra.py create mode 100644 beams/beams/doctype/employee_kra/__init__.py create mode 100644 beams/beams/doctype/employee_kra/employee_kra.json create mode 100644 beams/beams/doctype/employee_kra/employee_kra.py create mode 100644 beams/beams/doctype/employees/__init__.py create mode 100644 beams/beams/doctype/employees/employees.json create mode 100644 beams/beams/doctype/employees/employees.py create mode 100644 beams/beams/doctype/employees_left/__init__.py create mode 100644 beams/beams/doctype/employees_left/employees_left.json create mode 100644 beams/beams/doctype/employees_left/employees_left.py create mode 100644 beams/beams/doctype/reference_document/__init__.py create mode 100644 beams/beams/doctype/reference_document/reference_document.json create mode 100644 beams/beams/doctype/reference_document/reference_document.py create mode 100644 beams/beams/doctype/stringer_bill_date/__init__.py create mode 100644 beams/beams/doctype/stringer_bill_date/stringer_bill_date.json create mode 100644 beams/beams/doctype/stringer_bill_date/stringer_bill_date.py create mode 100644 beams/beams/overrides/queries.py create mode 100644 beams/patches/delete_employee_fields.py diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 6b4e8c4dd..f38f081cc 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -18,7 +18,7 @@ jobs: - name: Deploy to dev uses: appleboy/ssh-action@master with: - host: ${{ secrets.DEV_HOST }} + host: ${{ secrets.UAT_HOST }} username: ${{ secrets.USERNAME }} key: ${{ secrets.DEV_KEY }} port: 22 diff --git a/beams/api/api.py b/beams/api/api.py index b02671625..d5092b74f 100644 --- a/beams/api/api.py +++ b/beams/api/api.py @@ -1,21 +1,21 @@ -import frappe import json import re -from frappe import _ + +import frappe from frappe.utils import getdate, now_datetime, time_diff from hrms.api.roster import get_shifts @frappe.whitelist(allow_guest=True) def response(message, data, success, status_code): - ''' - method to generates responses of an API - args: + """ + method to generates responses of an API + args: message : response message string data : json object of the data success : True or False depending on the API response status_code : status of the request - ''' + """ frappe.clear_messages() frappe.local.response["message"] = message frappe.local.response["data"] = data @@ -23,44 +23,46 @@ def response(message, data, success, status_code): frappe.local.response["http_status_code"] = status_code return + @frappe.whitelist(allow_guest=True) def login(login_id, password): - ''' - API for user login - args: + """ + API for user login + args: login_id : username/email of the user password : user password - ''' + """ try: login_manager = frappe.auth.LoginManager() login_manager.authenticate(user=login_id, pwd=password) login_manager.post_login() except frappe.exceptions.AuthenticationError: - return response( "Authentication Error!",{},False,400) + return response("Authentication Error!", {}, False, 400) api_generate = generate_keys(frappe.session.user) user = frappe.get_doc("User", frappe.session.user) roles = frappe.get_roles(frappe.session.user) frappe.local.response["message"] = { - "success_key":1, - "sid": frappe.session.sid, - "api_key": user.api_key, - "api_secret": api_generate, - "is_active": user.enabled, - "user_id": user.username, - "name": user.full_name, - "roles": roles, - "msg": "Authentication Success" - } + "success_key": 1, + "sid": frappe.session.sid, + "api_key": user.api_key, + "api_secret": api_generate, + "is_active": user.enabled, + "user_id": user.username, + "name": user.full_name, + "roles": roles, + "msg": "Authentication Success", + } + def generate_keys(user): - ''' - method generates api secret for a user - args: + """ + method generates api secret for a user + args: user : username of the user - ''' + """ user_details = frappe.get_doc("User", user) - api_secret = frappe.generate_hash(length=15) + api_secret = frappe.generate_hash(length=15) if not user_details.api_key: api_key = frappe.generate_hash(length=15) @@ -71,640 +73,938 @@ def generate_keys(user): return api_secret + def strip_html_tags(text): - ''' - Helper function to strip HTML tags from a string - ''' + """ + Helper function to strip HTML tags from a string + """ from bs4 import BeautifulSoup + return BeautifulSoup(text, "html.parser").text + @frappe.whitelist() def get_region_list(start=0, page_length=20, region=None): - ''' - API to get List of Region - ''' - region_fields = ['name as region_id', 'region'] + """ + API to get List of Region + """ + region_fields = ["name as region_id", "region"] if region: - region_list = frappe.db.get_all('Region', filters={ 'region':['like', '%{0}%'.format(region)] }, fields=region_fields, start=start, page_length=page_length) + region_list = frappe.db.get_all( + "Region", + filters={"region": ["like", "%{0}%".format(region)]}, + fields=region_fields, + start=start, + page_length=page_length, + ) else: - region_list = frappe.db.get_all('Region', fields=region_fields, start=start, page_length=page_length) + region_list = frappe.db.get_all( + "Region", fields=region_fields, start=start, page_length=page_length + ) if region_list: - return response('Data get successfully', region_list, True, 200) + return response("Data get successfully", region_list, True, 200) else: - return response('No Regions found', region_list, True, 200) + return response("No Regions found", region_list, True, 200) + @frappe.whitelist() def get_agency_list(start=0, page_length=20, agency_name=None): - ''' - API to get List of Agency - ''' - agency_fields = ['name as agency_id', 'customer_name as agency_name', 'region', 'gstin', 'pan as pan_no', 'default_currency as currency', 'is_edited'] - filters = { 'is_agent':1 } + """ + API to get List of Agency + """ + agency_fields = [ + "name as agency_id", + "customer_name as agency_name", + "region", + "gstin", + "pan as pan_no", + "default_currency as currency", + "is_edited", + ] + filters = {"is_agent": 1} if agency_name: - filters = { 'is_agent':1, 'customer_name':['like', '%{0}%'.format(agency_name)] } - agency_list = frappe.db.get_all('Customer', filters=filters, fields=agency_fields, start=start, page_length=page_length) + filters = { + "is_agent": 1, + "customer_name": ["like", "%{0}%".format(agency_name)], + } + agency_list = frappe.db.get_all( + "Customer", + filters=filters, + fields=agency_fields, + start=start, + page_length=page_length, + ) if agency_list: - return response('Data get successfully', get_customer_address(agency_list, 1), True, 200) + return response( + "Data get successfully", get_customer_address(agency_list, 1), True, 200 + ) else: - return response('No Agencies found', agency_list, True, 200) + return response("No Agencies found", agency_list, True, 200) + @frappe.whitelist() def get_client_list(start=0, page_length=20, client_name=None): - ''' - API to get List of Client - ''' - client_fields = ['name as client_id', 'customer_name as client_name', 'region', 'gstin', 'pan as pan_no', 'default_currency as currency', 'is_edited'] - filters = { 'is_agent':0 } + """ + API to get List of Client + """ + client_fields = [ + "name as client_id", + "customer_name as client_name", + "region", + "gstin", + "pan as pan_no", + "default_currency as currency", + "is_edited", + ] + filters = {"is_agent": 0} if client_name: - filters = { 'is_agent':0, 'customer_name':['like', '%{0}%'.format(client_name)] } - client_list = frappe.db.get_all('Customer', filters=filters, fields=client_fields, start=start, page_length=page_length) + filters = { + "is_agent": 0, + "customer_name": ["like", "%{0}%".format(client_name)], + } + client_list = frappe.db.get_all( + "Customer", + filters=filters, + fields=client_fields, + start=start, + page_length=page_length, + ) if client_list: - return response('Data get successfully', get_customer_address(client_list), True, 200) + return response( + "Data get successfully", get_customer_address(client_list), True, 200 + ) else: - return response('No Clients found', client_list, True, 200) + return response("No Clients found", client_list, True, 200) + @frappe.whitelist() def get_customer_address(customer_list, agency=0): - ''' - Method fetches address of customer and add it to the list - Args: + """ + Method fetches address of customer and add it to the list + Args: customer_list (list): list of dicts fo customers agency (int, optional): denotes whether the customer list of agents or not. Defaults to 0. - Returns: + Returns: list of dicts: customer list of dicts with address keys - ''' + """ for customer in customer_list: - dynamic_link = frappe.db.exists('Dynamic Link', { 'parenttype':'Address', 'link_doctype':'Customer', 'link_name':customer.get('client_id') if not agency else customer.get('agency_id')}) - address = "" if not dynamic_link else frappe.db.get_value('Dynamic Link', dynamic_link, 'parent') + dynamic_link = frappe.db.exists( + "Dynamic Link", + { + "parenttype": "Address", + "link_doctype": "Customer", + "link_name": ( + customer.get("client_id") + if not agency + else customer.get("agency_id") + ), + }, + ) + address = ( + "" + if not dynamic_link + else frappe.db.get_value("Dynamic Link", dynamic_link, "parent") + ) address_data = { "address_line_1": "", "address_line_2": "", "address_line_3": "", "address_line_4": "", - "pincode": "" + "pincode": "", } if address: - address_data = frappe.db.get_all('Address', filters={ 'name':address }, fields=['address_line1 as address_line_1', 'address_line2 as address_line_2', 'city as address_line_3', 'state as address_line_4', 'pincode'])[0] - customer["address_line_1"] = address_data.get('address_line_1') - customer["address_line_2"] = address_data.get('address_line_2') - customer["address_line_3"] = address_data.get('address_line_3') - customer["address_line_4"] = address_data.get('address_line_4') - customer["pincode"] = address_data.get('pincode') + address_data = frappe.db.get_all( + "Address", + filters={"name": address}, + fields=[ + "address_line1 as address_line_1", + "address_line2 as address_line_2", + "city as address_line_3", + "state as address_line_4", + "pincode", + ], + )[0] + customer["address_line_1"] = address_data.get("address_line_1") + customer["address_line_2"] = address_data.get("address_line_2") + customer["address_line_3"] = address_data.get("address_line_3") + customer["address_line_4"] = address_data.get("address_line_4") + customer["pincode"] = address_data.get("pincode") return customer_list + def get_sales_taxes_and_charges_template(tax_rate, tax_category): - ''' - Method to get Sales Taxes and Charges Template - args: + """ + Method to get Sales Taxes and Charges Template + args: tax_rate : Tax Percentage tax_category : Tax Category - ''' + """ tax_template = None - tax_row = frappe.db.exists('Albatross GST Mapping', { - "parent": "Albatross Settings", - "tax_rate": int(tax_rate), - "tax_category": tax_category, - }) + tax_row = frappe.db.exists( + "Albatross GST Mapping", + { + "parent": "Albatross Settings", + "tax_rate": int(tax_rate), + "tax_category": tax_category, + }, + ) if tax_row: - tax_template = frappe.db.get_value('Albatross GST Mapping', tax_row, 'tax_template') + tax_template = frappe.db.get_value( + "Albatross GST Mapping", tax_row, "tax_template" + ) return tax_template + @frappe.whitelist() def get_currency_list(start=0, page_length=20, currency=None): - ''' - API to get List of Currency - ''' - currency_fields = ['currency_name', 'symbol'] + """ + API to get List of Currency + """ + currency_fields = ["currency_name", "symbol"] if currency: - currency_list = frappe.db.get_all('Currency', filters={ 'currency_name':['like', '%{0}%'.format(currency)], 'enabled':1 }, fields=currency_fields, start=start, page_length=page_length) + currency_list = frappe.db.get_all( + "Currency", + filters={"currency_name": ["like", "%{0}%".format(currency)], "enabled": 1}, + fields=currency_fields, + start=start, + page_length=page_length, + ) else: - currency_list = frappe.db.get_all('Currency', filters={ 'enabled':1 }, fields=currency_fields, start=start, page_length=page_length) + currency_list = frappe.db.get_all( + "Currency", + filters={"enabled": 1}, + fields=currency_fields, + start=start, + page_length=page_length, + ) if currency_list: - return response('Data get successfully', currency_list, True, 200) + return response("Data get successfully", currency_list, True, 200) else: - return response('No Currency found', currency_list, True, 200) + return response("No Currency found", currency_list, True, 200) + @frappe.whitelist() def get_employee_list(start=0, page_length=20, employee_name=None): - ''' - API to get List of Employee - ''' - employee_fields = ['name as employee_id', 'employee_name'] + """ + API to get List of Employee + """ + employee_fields = ["name as employee_id", "employee_name"] if employee_name: - employee_list = frappe.db.get_all('Employee', filters={ 'employee_name':['like', '%{0}%'.format(employee_name)], 'status':'Active' }, fields=employee_fields, start=start, page_length=page_length) + employee_list = frappe.db.get_all( + "Employee", + filters={ + "employee_name": ["like", "%{0}%".format(employee_name)], + "status": "Active", + }, + fields=employee_fields, + start=start, + page_length=page_length, + ) else: - employee_list = frappe.db.get_all('Employee', filters={ 'status':'Active' }, fields=employee_fields, start=start, page_length=page_length) + employee_list = frappe.db.get_all( + "Employee", + filters={"status": "Active"}, + fields=employee_fields, + start=start, + page_length=page_length, + ) if employee_list: - return response('Data get successfully', employee_list, True, 200) + return response("Data get successfully", employee_list, True, 200) else: - return response('No Employees found', employee_list, True, 200) + return response("No Employees found", employee_list, True, 200) + @frappe.whitelist() def create_release_order(): - ''' - API to create Release Order - args: + """ + API to create Release Order + args: JSON string of RO data like { - "ror_no": "M1/0142/2024-25", - "ror_date": "2024-06-01", - "ro_no": "766", - "ro_date": "2024-05-31", - "client_id": "Dias Idea Incubators", - "agency_id": "efeone", - "currency": "INR", - "bill_to": "A", - "option": "Associate Sponsorship", - "product_name": "P M S College", - "program_name": "News At 9", - "no_of_eps": 0, - "commission_per": 15, - "fct_total": 50, - "amount": 50000, - "region": "Trivandrum", - "executive_id": "HR-EMP-00012", - "region_revenue_percentage": 100 + "ror_no": "M1/0142/2024-25", + "ror_date": "2024-06-01", + "ro_no": "766", + "ro_date": "2024-05-31", + "client_id": "Dias Idea Incubators", + "agency_id": "efeone", + "currency": "INR", + "bill_to": "A", + "option": "Associate Sponsorship", + "product_name": "P M S College", + "program_name": "News At 9", + "no_of_eps": 0, + "commission_per": 15, + "fct_total": 50, + "amount": 50000, + "region": "Trivandrum", + "executive_id": "HR-EMP-00012", + "region_revenue_percentage": 100 } - ''' + """ try: input_data = frappe.form_dict - if input_data.get('cmd'): - input_data.pop('cmd') + if input_data.get("cmd"): + input_data.pop("cmd") - albatross_service_item = frappe.db.get_single_value('Albatross Settings', 'albatross_service_item') + albatross_service_item = frappe.db.get_single_value( + "Albatross Settings", "albatross_service_item" + ) if not albatross_service_item: - return response('`albatross_service_item` is not configured in Albatross Settings', {}, False, 400) - - if not input_data.get('bill_to'): - return response('`bill_to` is reuqired to create Release Order', {}, False, 400) + return response( + "`albatross_service_item` is not configured in Albatross Settings", + {}, + False, + 400, + ) + + if not input_data.get("bill_to"): + return response( + "`bill_to` is reuqired to create Release Order", {}, False, 400 + ) else: - if not input_data.get('bill_to') in ['A', 'C']: - return response('`bill_to` should be either `A` or `C`', {}, False, 400) + if input_data.get("bill_to") not in ["A", "C"]: + return response("`bill_to` should be either `A` or `C`", {}, False, 400) # Checking Mandatory fields - if not input_data.get('client_id'): - return response('`client_id` is reuqired to create Release Order', {}, False, 400) + if not input_data.get("client_id"): + return response( + "`client_id` is reuqired to create Release Order", {}, False, 400 + ) else: - if not frappe.db.exists('Customer', input_data.get('client_id')): - return response('Client : `{0}` does not exists'.format(input_data.get('client_id')), {}, False, 404) - - if input_data.get('bill_to') == 'A': - if not input_data.get('agency_id'): - return response('`agency_id` is reuqired to create Release Order', {}, False, 400) + if not frappe.db.exists("Customer", input_data.get("client_id")): + return response( + "Client : `{0}` does not exists".format( + input_data.get("client_id") + ), + {}, + False, + 404, + ) + + if input_data.get("bill_to") == "A": + if not input_data.get("agency_id"): + return response( + "`agency_id` is reuqired to create Release Order", {}, False, 400 + ) else: - if not frappe.db.exists('Customer', input_data.get('agency_id')): - return response('Agency : `{0}` does not exists'.format(input_data.get('agency_id')), {}, False, 404) - - if not input_data.get('ror_no'): - return response('`ror_no` is reuqired to create Release Order', {}, False, 400) - - if not input_data.get('ror_date'): - return response('`ror_date` is reuqired to create Release Order', {}, False, 400) - - if not input_data.get('ro_date'): - return response('`ror_date` is reuqired to create Release Order', {}, False, 400) - - if not input_data.get('option'): - return response('`option` is reuqired to create Release Order', {}, False, 400) - - if input_data.get('executive_id') and not frappe.db.exists('Employee', input_data.get('executive_id')): - return response('Employee : `{0}` does not exists'.format(input_data.get('executive_id')), {}, False, 400) - - if not input_data.get('region_revenue_percentage'): - return response('`region_revenue_percentage` is reuqired to create Release Order', {}, False, 400) - - if input_data.get('region') and not frappe.db.exists('Region', input_data.get('region')): - return response('Region : `{0}` does not exists'.format(input_data.get('region')), {}, False, 400) - - if input_data.get('currency') and not frappe.db.exists('Currency', input_data.get('currency')): - return response('Currency : `{0}` does not exists'.format(input_data.get('currency')), {}, False, 400) + if not frappe.db.exists("Customer", input_data.get("agency_id")): + return response( + "Agency : `{0}` does not exists".format( + input_data.get("agency_id") + ), + {}, + False, + 404, + ) + + if not input_data.get("ror_no"): + return response( + "`ror_no` is reuqired to create Release Order", {}, False, 400 + ) + + if not input_data.get("ror_date"): + return response( + "`ror_date` is reuqired to create Release Order", {}, False, 400 + ) + + if not input_data.get("ro_date"): + return response( + "`ror_date` is reuqired to create Release Order", {}, False, 400 + ) + + if not input_data.get("option"): + return response( + "`option` is reuqired to create Release Order", {}, False, 400 + ) + + if input_data.get("executive_id") and not frappe.db.exists( + "Employee", input_data.get("executive_id") + ): + return response( + "Employee : `{0}` does not exists".format( + input_data.get("executive_id") + ), + {}, + False, + 400, + ) + + if not input_data.get("region_revenue_percentage"): + return response( + "`region_revenue_percentage` is reuqired to create Release Order", + {}, + False, + 400, + ) + + if input_data.get("region") and not frappe.db.exists( + "Region", input_data.get("region") + ): + return response( + "Region : `{0}` does not exists".format(input_data.get("region")), + {}, + False, + 400, + ) + + if input_data.get("currency") and not frappe.db.exists( + "Currency", input_data.get("currency") + ): + return response( + "Currency : `{0}` does not exists".format(input_data.get("currency")), + {}, + False, + 400, + ) # Creating Release Order - ro_doc = frappe.new_doc('Quotation') - ro_doc.transaction_date = getdate(input_data.get('ror_date')) - ro_doc.quotation_to = 'Customer' - if input_data.get('bill_to') == 'A': - ro_doc.party_name = input_data.get('agency_id') - ro_doc.actual_customer = input_data.get('client_id') - elif input_data.get('bill_to') == 'C': - ro_doc.party_name = input_data.get('client_id') - ro_doc.region = input_data.get('region') - ro_doc.executive = input_data.get('executive_id') or '' - ro_doc.executive_name = input_data.get('executive_name') or '' - ro_doc.albatross_ro_id = input_data.get('ror_no') or '' - ro_doc.ro_no = input_data.get('ro_no') or '' - ro_doc.ro_date = getdate(input_data.get('ro_date')) - ro_doc.product_name = input_data.get('product_name') or '' - ro_doc.program_name = input_data.get('program_name') or '' - ro_doc.ro_option = input_data.get('option') or '' - ro_doc.no_of_eps = input_data.get('no_of_eps') or 0 - ro_doc.commission_per = input_data.get('commission_per') or 0 - ro_doc.fct_total = input_data.get('fct_total') or 0 - ro_doc.region_revenue_percentage = input_data.get('region_revenue_percentage') or 0 - ro_item_row = ro_doc.append('items') + ro_doc = frappe.new_doc("Quotation") + ro_doc.transaction_date = getdate(input_data.get("ror_date")) + ro_doc.quotation_to = "Customer" + if input_data.get("bill_to") == "A": + ro_doc.party_name = input_data.get("agency_id") + ro_doc.actual_customer = input_data.get("client_id") + elif input_data.get("bill_to") == "C": + ro_doc.party_name = input_data.get("client_id") + ro_doc.region = input_data.get("region") + ro_doc.executive = input_data.get("executive_id") or "" + ro_doc.executive_name = input_data.get("executive_name") or "" + ro_doc.albatross_ro_id = input_data.get("ror_no") or "" + ro_doc.ro_no = input_data.get("ro_no") or "" + ro_doc.ro_date = getdate(input_data.get("ro_date")) + ro_doc.product_name = input_data.get("product_name") or "" + ro_doc.program_name = input_data.get("program_name") or "" + ro_doc.ro_option = input_data.get("option") or "" + ro_doc.no_of_eps = input_data.get("no_of_eps") or 0 + ro_doc.commission_per = input_data.get("commission_per") or 0 + ro_doc.fct_total = input_data.get("fct_total") or 0 + ro_doc.region_revenue_percentage = ( + input_data.get("region_revenue_percentage") or 0 + ) + ro_item_row = ro_doc.append("items") ro_item_row.qty = 1 ro_item_row.item_code = albatross_service_item - ro_item_row.rate = float(input_data.get('amount') or 0) - ro_item_row.base_rate = float(input_data.get('amount') or 0) + ro_item_row.rate = float(input_data.get("amount") or 0) + ro_item_row.base_rate = float(input_data.get("amount") or 0) ro_doc.ignore_mandatory = True ro_doc.save(ignore_permissions=True) frappe.clear_messages() - return response('Created Release Order Successfully', ro_doc.as_dict(), True, 201) + return response( + "Created Release Order Successfully", ro_doc.as_dict(), True, 201 + ) except frappe.exceptions.CharacterLengthExceededError as e: - frappe.log_error("Character Length Exceeded", "Error in Release Order creation: " + str(e)[:120]) - return response('Character Length Exceeded in Error Log', {}, False, 400) + frappe.log_error( + "Character Length Exceeded", + "Error in Release Order creation: " + str(e)[:120], + ) + return response("Character Length Exceeded in Error Log", {}, False, 400) except Exception as exception: frappe.log_error(frappe.get_traceback(), "Release Order Creation Error") clean_exception_message = strip_html_tags(str(exception)) return response(f"An error occurred: {clean_exception_message}", {}, False, 400) + @frappe.whitelist() def create_sales_order(): - ''' - API to create Sales Order - args: + """ + API to create Sales Order + args: JSON string of Sales Order data like { - "date": "30-10-2024", - "invoice_no": "LB/138", - "client_id": "Dias Idea Incubators", - "currency": "INR", - "executive_id": "HR-EMP-00012", - "taxable_value": 170000, - "gst_rate": 18, - "sgst": 15300, - "cgst": 15300, - "igst": 0, - "ro_no": "CCPL/32/2024-25", - "ref_no": "M1/0139/2024-25", - "region": "Trivandrum" + "date": "30-10-2024", + "invoice_no": "LB/138", + "client_id": "Dias Idea Incubators", + "currency": "INR", + "executive_id": "HR-EMP-00012", + "taxable_value": 170000, + "gst_rate": 18, + "sgst": 15300, + "cgst": 15300, + "igst": 0, + "ro_no": "CCPL/32/2024-25", + "ref_no": "M1/0139/2024-25", + "region": "Trivandrum" } - ''' + """ try: input_data = frappe.form_dict - if input_data.get('cmd'): - input_data.pop('cmd') + if input_data.get("cmd"): + input_data.pop("cmd") - albatross_service_item = frappe.db.get_single_value('Albatross Settings', 'albatross_service_item') + albatross_service_item = frappe.db.get_single_value( + "Albatross Settings", "albatross_service_item" + ) if not albatross_service_item: - return response('`albatross_service_item` is not configured in Albatross Settings', {}, False, 400) + return response( + "`albatross_service_item` is not configured in Albatross Settings", + {}, + False, + 400, + ) # Checking Mandatory fields - if not input_data.get('date'): - return response('`date` is reuqired to create Release Order', {}, False, 400) - - if not input_data.get('client_id'): - return response('`client_id` is reuqired to create Release Order', {}, False, 400) + if not input_data.get("date"): + return response( + "`date` is reuqired to create Release Order", {}, False, 400 + ) + + if not input_data.get("client_id"): + return response( + "`client_id` is reuqired to create Release Order", {}, False, 400 + ) else: - if not frappe.db.exists('Customer', input_data.get('client_id')): - return response('Client : `{0}` does not exists'.format(input_data.get('client_id')), {}, False, 404) - - if not input_data.get('taxable_value'): - return response('`taxable_value` is reuqired to create Release Order', {}, False, 400) - - if input_data.get('currency') and not frappe.db.exists('Currency', input_data.get('currency')): - return response('Currency : `{0}` does not exists'.format(input_data.get('currency')), {}, False, 400) - - if input_data.get('region') and not frappe.db.exists('Region', input_data.get('region')): - return response('Region : `{0}` does not exists'.format(input_data.get('region')), {}, False, 400) + if not frappe.db.exists("Customer", input_data.get("client_id")): + return response( + "Client : `{0}` does not exists".format( + input_data.get("client_id") + ), + {}, + False, + 404, + ) + + if not input_data.get("taxable_value"): + return response( + "`taxable_value` is reuqired to create Release Order", {}, False, 400 + ) + + if input_data.get("currency") and not frappe.db.exists( + "Currency", input_data.get("currency") + ): + return response( + "Currency : `{0}` does not exists".format(input_data.get("currency")), + {}, + False, + 400, + ) + + if input_data.get("region") and not frappe.db.exists( + "Region", input_data.get("region") + ): + return response( + "Region : `{0}` does not exists".format(input_data.get("region")), + {}, + False, + 400, + ) sales_taxes_and_charges_template = None tax_category = None - if input_data.get('gst_rate'): - if input_data.get('igst_rate'): - tax_category_field_name = 'out_state_tax_category' + if input_data.get("gst_rate"): + if input_data.get("igst_rate"): + tax_category_field_name = "out_state_tax_category" else: - tax_category_field_name = 'in_state_tax_category' - tax_category = frappe.db.get_single_value('Albatross Settings', tax_category_field_name) - tax_rate = float(input_data.get('gst_rate')) - sales_taxes_and_charges_template = get_sales_taxes_and_charges_template(tax_rate, tax_category) + tax_category_field_name = "in_state_tax_category" + tax_category = frappe.db.get_single_value( + "Albatross Settings", tax_category_field_name + ) + tax_rate = float(input_data.get("gst_rate")) + sales_taxes_and_charges_template = get_sales_taxes_and_charges_template( + tax_rate, tax_category + ) # Creating Sales Order - ro_doc = frappe.new_doc('Sales Order') - ro_doc.transaction_date = getdate(input_data.get('date')) - ro_doc.delivery_date = getdate(input_data.get('date')) - ro_doc.customer = input_data.get('client_id') - ro_doc.region = input_data.get('region') - ro_doc.executive = input_data.get('executive_id') or '' - ro_doc.reference_id = get_quotation_from_ro_id(input_data.get('ref_no')) or '' - ro_item_row = ro_doc.append('items') + ro_doc = frappe.new_doc("Sales Order") + ro_doc.transaction_date = getdate(input_data.get("date")) + ro_doc.delivery_date = getdate(input_data.get("date")) + ro_doc.customer = input_data.get("client_id") + ro_doc.region = input_data.get("region") + ro_doc.executive = input_data.get("executive_id") or "" + ro_doc.reference_id = get_quotation_from_ro_id(input_data.get("ref_no")) or "" + ro_item_row = ro_doc.append("items") ro_item_row.qty = 1 ro_item_row.item_code = albatross_service_item - ro_item_row.rate = float(input_data.get('taxable_value')) - ro_item_row.base_rate = float(input_data.get('taxable_value')) + ro_item_row.rate = float(input_data.get("taxable_value")) + ro_item_row.base_rate = float(input_data.get("taxable_value")) if sales_taxes_and_charges_template: ro_doc.taxes_and_charges = sales_taxes_and_charges_template else: - ro_doc.taxes_and_charges = '' + ro_doc.taxes_and_charges = "" if tax_category: ro_doc.tax_category = tax_category else: - ro_doc.tax_category = '' + ro_doc.tax_category = "" ro_doc.ignore_mandatory = True ro_doc.save(ignore_permissions=True) frappe.clear_messages() - return response('Created Sales Order Successfully', ro_doc.as_dict(), True, 201) + return response("Created Sales Order Successfully", ro_doc.as_dict(), True, 201) except frappe.exceptions.CharacterLengthExceededError as e: - frappe.log_error("Character Length Exceeded", "Error in Release Order creation: " + str(e)[:120]) - return response('Character Length Exceeded in Error Log', {}, False, 400) + frappe.log_error( + "Character Length Exceeded", + "Error in Release Order creation: " + str(e)[:120], + ) + return response("Character Length Exceeded in Error Log", {}, False, 400) except Exception as exception: frappe.log_error(frappe.get_traceback(), "Release Order Creation Error") clean_exception_message = strip_html_tags(str(exception)) return response(f"An error occurred: {clean_exception_message}", {}, False, 400) + @frappe.whitelist() def get_quotation_from_ro_id(albatross_ro_id): - ''' - Method to get Quotation using albatross_ro_id - ''' + """ + Method to get Quotation using albatross_ro_id + """ quotation = None - if frappe.db.exists('Quotation', { 'albatross_ro_id':albatross_ro_id }): - quotation = frappe.db.get_value('Quotation', { 'albatross_ro_id':albatross_ro_id }) + if frappe.db.exists("Quotation", {"albatross_ro_id": albatross_ro_id}): + quotation = frappe.db.get_value( + "Quotation", {"albatross_ro_id": albatross_ro_id} + ) return quotation + @frappe.whitelist() def get_employees(employee_id=None, department=None): - ''' - Fetch all employees from the same department. - - Args: - employee_id: Fetch employees belonging to the same department as this employee. - department: Fetch employees from a specific department. - ''' - try: - employees = [] - fields = ['employee', 'employee_name', 'department'] - filters = {} - if employee_id: - emp_department = frappe.get_value('Employee', employee_id, 'department') - if not emp_department: - return response(f'Employee {employee_id} not found', {}, False, 404) - filters['department'] = emp_department - elif department: - filters['department'] = department - employees = frappe.get_all('Employee', fields=fields, filters=filters) - if employees: - return response('Employees retrieved successfully', employees, True, 200) - else: - return response('No employees found', employees, False, 200) - except Exception as exception: - frappe.log_error(frappe.get_traceback()) - clean_exception_message = strip_html_tags(str(exception)) - return response(clean_exception_message, {}, False, 401) + """ + Fetch all employees from the same department. + + Args: + employee_id: Fetch employees belonging to the same department as this employee. + department: Fetch employees from a specific department. + """ + try: + employees = [] + fields = ["employee", "employee_name", "department"] + filters = {} + if employee_id: + emp_department = frappe.get_value("Employee", employee_id, "department") + if not emp_department: + return response(f"Employee {employee_id} not found", {}, False, 404) + filters["department"] = emp_department + elif department: + filters["department"] = department + + employees = frappe.db.get_all("Employee", fields=fields, filters=filters) + + if employees: + return response("Employees retrieved successfully", employees, True, 200) + else: + return response("No employees found", employees, False, 200) + except Exception as exception: + frappe.log_error(frappe.get_traceback()) + clean_exception_message = strip_html_tags(str(exception)) + return response(clean_exception_message, {}, False, 401) + @frappe.whitelist() def get_employee_shift(employee: str, date: str): - ''' - Fetch the shift details of an employee for a specific date. + """ + Fetch the shift details of an employee for a specific date. - :param employee: Employee ID (e.g., "EMP-0001") - :param date: Date in YYYY-MM-DD format - :return: Shift details if found, else an empty dictionary - ''' + :param employee: Employee ID (e.g., "EMP-0001") + :param date: Date in YYYY-MM-DD format + :return: Shift details if found, else an empty dictionary + """ - # Define filters - employee_filters = {"name": employee} - shift_filters = {} + # Define filters + employee_filters = {"name": employee} + shift_filters = {} - shifts = get_shifts(date, date, employee_filters, shift_filters) + shifts = get_shifts(date, date, employee_filters, shift_filters) + + # Return the shift details if found + return shifts.get(employee, []) - # Return the shift details if found - return shifts.get(employee, []) @frappe.whitelist() def get_doc(doctype, docname, with_history=False, method=None): - ''' - API to get the data of a document along with attached files - args: - doctype : doctype of the document - docname : name of the document - with_history : returns document history when true - method : returns the data object instead of response when true - ''' - try: - doc = frappe.get_doc(doctype, docname) - values = {'doctype':doctype, 'docname':docname} - attached_files = frappe.db.sql(''' - SELECT - file_name, - file_url - FROM - tabFile - WHERE - attached_to_doctype = %(doctype)s AND - attached_to_name = %(docname)s - ''', values=values, as_dict=1) - comments = get_comments(doctype, docname) - data = doc.__dict__ - data['meta'] = [] - if data.get('meta'): - data.pop('meta') - if data.get('flags'): - data.pop('flags') - if data.get('_table_fieldnames'): - data.pop('_table_fieldnames') - data['_comments'] = comments - data['attachments'] = attached_files - if with_history: - data['history'] = get_doc_history(doctype, docname) - if not method: - data = [data] - return response('Successfully got data of {doctype} {docname}'.format(doctype = doctype, docname = docname), data, True, 200) - else: - return data - except Exception as exception: - frappe.log_error(frappe.get_traceback()) - return response(exception, {}, False, 400) + """ + API to get the data of a document along with attached files + args: + doctype : doctype of the document + docname : name of the document + with_history : returns document history when true + method : returns the data object instead of response when true + """ + try: + doc = frappe.get_doc(doctype, docname) + values = {"doctype": doctype, "docname": docname} + attached_files = frappe.db.sql( + """ + SELECT + file_name, + file_url + FROM + tabFile + WHERE + attached_to_doctype = %(doctype)s AND + attached_to_name = %(docname)s + """, + values=values, + as_dict=1, + ) + comments = get_comments(doctype, docname) + data = doc.__dict__ + data["meta"] = [] + if data.get("meta"): + data.pop("meta") + if data.get("flags"): + data.pop("flags") + if data.get("_table_fieldnames"): + data.pop("_table_fieldnames") + data["_comments"] = comments + data["attachments"] = attached_files + if with_history: + data["history"] = get_doc_history(doctype, docname) + if not method: + data = [data] + return response( + "Successfully got data of {doctype} {docname}".format( + doctype=doctype, docname=docname + ), + data, + True, + 200, + ) + else: + return data + except Exception as exception: + frappe.log_error(frappe.get_traceback()) + return response(exception, {}, False, 400) + def get_comments(doctype, docname): - '''method gets comments of a document, removes html elements from it and then returns it - args: - doctype : doctype of the document - docname : name of the document''' - values = {'doctype':doctype, 'docname':docname} - comments = frappe.db.sql(''' - SELECT - owner, - comment_type, - content - FROM - tabComment - WHERE - reference_doctype = %(doctype)s - AND - reference_name = %(docname)s - ORDER BY - modified desc - ''', values=values, as_dict=1) - for comment in comments: - comment.content = convert_message(comment.content) - return comments + """method gets comments of a document, removes html elements from it and then returns it + args: + doctype : doctype of the document + docname : name of the document + """ + values = {"doctype": doctype, "docname": docname} + comments = frappe.db.sql( + """ + SELECT + owner, + comment_type, + content + FROM + tabComment + WHERE + reference_doctype = %(doctype)s + AND + reference_name = %(docname)s + ORDER BY + modified desc + """, + values=values, + as_dict=1, + ) + for comment in comments: + comment.content = convert_message(comment.content) + return comments + def convert_message(message): - ''' - method removes html elements from string - args: - message: string to be converted - ''' - CLEANR = re.compile('<.*?>') - cleanmessage = re.sub(CLEANR, "",message) - return cleanmessage + """ + method removes html elements from string + args: + message: string to be converted + """ + CLEANR = re.compile("<.*?>") + cleanmessage = re.sub(CLEANR, "", message) + return cleanmessage + def get_doc_history(doctype, docname): - ''' - method to get the histroy of a document - args: - doctype : doctype of the document - docname : name of the document - ''' - history = [] - values = {'doctype':doctype, 'docname':docname} - view_log = frappe.db.sql(''' - SELECT - viewed_by as user, - creation as time - FROM - `tabView Log` - WHERE - reference_doctype = %(doctype)s - AND - reference_name = %(docname)s - ORDER BY - creation asc - ''', values = values, as_dict=1) - for log in view_log: - user = set_current_user_as_you_str(log.user) - log['log'] = '{0} viewed this {1}'.format(user, get_time_diff_in_words(log.time)) - history += view_log - versions = frappe.db.sql(''' - SELECT - owner as user, - creation as time, - data - FROM - tabVersion - WHERE - ref_doctype = %(doctype)s - AND - docname = %(docname)s - ''', values = values, as_dict=1) - for version in versions: - user = set_current_user_as_you_str(version.user) - data = json.loads(version.data) - time_modifier = get_time_diff_in_words(version['time']) - if data['added']: - version['log'] = '{0} added rows for {1} - {2}'.format(user, generate_changes_message(data['added'], 'added'), time_modifier) - elif data['changed']: - version['log'] = '{0} changed values for {1} - {2}'.format(user, generate_changes_message(data['changed'], 'changed'), time_modifier) - elif data['removed']: - version['log'] = '{0} removed rows for {1} - {2}'.format(user, generate_changes_message(data['removed'], 'removed'), time_modifier) - elif data['row_changed']: - version['log'] = '{0} changed values for {1} - {2}'.format(user, generate_changes_message(data['row_changed'], 'row_changed', doctype), time_modifier) - version.pop('data') - history += versions - - history.sort(key=return_keys_of_doct_history_list) - - doc = frappe.get_doc(doctype, docname) - creation_dict = { - 'user': doc.owner, - 'time': doc.creation, - 'log': '{0} created this {1}'.format(set_current_user_as_you_str(doc.owner), get_time_diff_in_words(doc.creation)) - } - modifi_dict = { - 'user': doc.modified_by, - 'time': doc.modified, - 'log': '{0} edited this {1}'.format(set_current_user_as_you_str(doc.modified_by), get_time_diff_in_words(doc.modified)) - } - history = [creation_dict, modifi_dict] + history - - return history + """ + method to get the histroy of a document + args: + doctype : doctype of the document + docname : name of the document + """ + history = [] + values = {"doctype": doctype, "docname": docname} + view_log = frappe.db.sql( + """ + SELECT + viewed_by as user, + creation as time + FROM + `tabView Log` + WHERE + reference_doctype = %(doctype)s + AND + reference_name = %(docname)s + ORDER BY + creation asc + """, + values=values, + as_dict=1, + ) + for log in view_log: + user = set_current_user_as_you_str(log.user) + log["log"] = "{0} viewed this {1}".format( + user, get_time_diff_in_words(log.time) + ) + history += view_log + versions = frappe.db.sql( + """ + SELECT + owner as user, + creation as time, + data + FROM + tabVersion + WHERE + ref_doctype = %(doctype)s + AND + docname = %(docname)s + """, + values=values, + as_dict=1, + ) + for version in versions: + user = set_current_user_as_you_str(version.user) + data = json.loads(version.data) + time_modifier = get_time_diff_in_words(version["time"]) + if data["added"]: + version["log"] = "{0} added rows for {1} - {2}".format( + user, generate_changes_message(data["added"], "added"), time_modifier + ) + elif data["changed"]: + version["log"] = "{0} changed values for {1} - {2}".format( + user, + generate_changes_message(data["changed"], "changed"), + time_modifier, + ) + elif data["removed"]: + version["log"] = "{0} removed rows for {1} - {2}".format( + user, + generate_changes_message(data["removed"], "removed"), + time_modifier, + ) + elif data["row_changed"]: + version["log"] = "{0} changed values for {1} - {2}".format( + user, + generate_changes_message(data["row_changed"], "row_changed", doctype), + time_modifier, + ) + version.pop("data") + history += versions + + history.sort(key=return_keys_of_doct_history_list) + + doc = frappe.get_doc(doctype, docname) + creation_dict = { + "user": doc.owner, + "time": doc.creation, + "log": "{0} created this {1}".format( + set_current_user_as_you_str(doc.owner), get_time_diff_in_words(doc.creation) + ), + } + modifi_dict = { + "user": doc.modified_by, + "time": doc.modified, + "log": "{0} edited this {1}".format( + set_current_user_as_you_str(doc.modified_by), + get_time_diff_in_words(doc.modified), + ), + } + history = [creation_dict, modifi_dict] + history + + return history + def return_keys_of_doct_history_list(log_dict): - '''key generator for history lsit sort''' - return log_dict['time'] + """key generator for history lsit sort""" + return log_dict["time"] + def get_time_diff_in_words(time): - ''' - method used to get the time difference between current time and given time in words - args: - time : datetime object of time to be calculated - ''' - now = now_datetime() - diff = time_diff(now, time) - days = int(diff.days) - if not days: - hours = int(diff.seconds/3600) - if not hours: - minutes = int(diff.seconds/60) - if not minutes: - return 'just now' - elif minutes == 1: - return '1 minute ago' - return '{0} minutes ago'.format(minutes) - if hours == 1: - return '1 hour ago' - return '{0} hours ago'.format(hours) - elif days < 7: - if days == 1: - return '1 day ago' - return '{0} days ago'.format(days) - if time.month == now.month: - weeks = int(days/7) - if weeks > 1: - return '{0} weeks ago'.format(weeks) - return '1 week ago' - years = now.year - time.year - if not years: - months = diff_month(now, time) - if months == 1: - return '1 month ago' - return '{0} months ago'.format(months) - if years == 1: - return '1 year ago' - return '{0} years ago'.format(years) + """ + method used to get the time difference between current time and given time in words + args: + time : datetime object of time to be calculated + """ + now = now_datetime() + diff = time_diff(now, time) + days = int(diff.days) + if not days: + hours = int(diff.seconds / 3600) + if not hours: + minutes = int(diff.seconds / 60) + if not minutes: + return "just now" + elif minutes == 1: + return "1 minute ago" + return "{0} minutes ago".format(minutes) + if hours == 1: + return "1 hour ago" + return "{0} hours ago".format(hours) + elif days < 7: + if days == 1: + return "1 day ago" + return "{0} days ago".format(days) + if time.month == now.month: + weeks = int(days / 7) + if weeks > 1: + return "{0} weeks ago".format(weeks) + return "1 week ago" + years = now.year - time.year + if not years: + months = diff_month(now, time) + if months == 1: + return "1 month ago" + return "{0} months ago".format(months) + if years == 1: + return "1 year ago" + return "{0} years ago".format(years) + def diff_month(d1, d2): - ''' - method to get the month difference between two dates - args: - d1, d2: datetime objects of dates - ''' - return (d1.year - d2.year) * 12 + d1.month - d2.month + """ + method to get the month difference between two dates + args: + d1, d2: datetime objects of dates + """ + return (d1.year - d2.year) * 12 + d1.month - d2.month + def set_current_user_as_you_str(user): - ''' - method used to return self addressal if the user in question is the current user - args: - user : email of the user - ''' - if frappe.session.user == user: - return 'You' - return user - -def generate_changes_message(changes, type, doctype = None): - ''' - method used to generate the messages for document history - args: - changes : a list object contatining the version data - type : type of change happened - ''' - changes_list = [] - for change in changes: - if type == 'changed': - changes_list.append('{0} from {1} to {2}'.format(change[0], change[1], change[2])) - elif type in ['added', 'removed']: - label = frappe.get_meta(change[1]['parenttype']).get_field(change[1]['parentfield']).label - changes_list.append(label) - elif type == 'row_changed': - field_meta = frappe.get_meta(doctype).get_field(change[0]) - label = field_meta.label - for value_change in change[3]: - child_label = frappe.get_meta(field_meta.options).get_field(value_change[0]).label - changes_list.append('{0} from {1} to {2} in row #{3}'.format(child_label, value_change[1], value_change[2], change[1])) - changes_str = ', '.join(changes_list) - return changes_str + """ + method used to return self addressal if the user in question is the current user + args: + user : email of the user + """ + if frappe.session.user == user: + return "You" + return user + + +def generate_changes_message(changes, type, doctype=None): + """ + method used to generate the messages for document history + args: + changes : a list object contatining the version data + type : type of change happened + """ + changes_list = [] + for change in changes: + if type == "changed": + changes_list.append( + "{0} from {1} to {2}".format(change[0], change[1], change[2]) + ) + elif type in ["added", "removed"]: + label = ( + frappe.get_meta(change[1]["parenttype"]) + .get_field(change[1]["parentfield"]) + .label + ) + changes_list.append(label) + elif type == "row_changed": + field_meta = frappe.get_meta(doctype).get_field(change[0]) + label = field_meta.label + for value_change in change[3]: + child_label = ( + frappe.get_meta(field_meta.options).get_field(value_change[0]).label + ) + changes_list.append( + "{0} from {1} to {2} in row #{3}".format( + child_label, value_change[1], value_change[2], change[1] + ) + ) + changes_str = ", ".join(changes_list) + return changes_str diff --git a/beams/beams/custom_scripts/appraisal/appraisal.py b/beams/beams/custom_scripts/appraisal/appraisal.py index a903db4d2..80c66569e 100644 --- a/beams/beams/custom_scripts/appraisal/appraisal.py +++ b/beams/beams/custom_scripts/appraisal/appraisal.py @@ -201,31 +201,25 @@ def get_categories_table(): return categories_html @frappe.whitelist() -def add_to_category_details(parent_docname, category, remarks): - """ - Adds a new row with category details (category, remarks, employee, designation) - to the category_details child table of an Appraisal document and saves it. - """ +def add_to_category_details(parent_docname, category, remarks, employee, designation): + ''' + Adds a new row with category details (category, remarks, employee, designation) to the category_details child table of an Appraisal document and saves it. + ''' try: parent_doc = frappe.get_doc("Appraisal", parent_docname) - employee = parent_doc.employee - designation = parent_doc.designation - - parent_doc.append("category_details", { + child_row = parent_doc.append("category_details", { "category": category, "remarks": remarks, "employee": employee, "designation": designation }) + parent_doc.save() - parent_doc.save(ignore_permissions=True) return "Success" except Exception as e: frappe.log_error(frappe.get_traceback(), "Add to Category Details Error") return "Failed" - - @frappe.whitelist() def map_appraisal_to_event(source_name): ''' @@ -288,52 +282,112 @@ def check_existing_event(appraisal_reference): event = frappe.db.get_value("Event", {"appraisal_reference": appraisal_reference}, "name") return event if event else None + @frappe.whitelist() -def assign_tasks_sequentially(doc): +def assign_tasks_sequentially(doc=None, employee_id=None): """ - Sends an email notification to the assessment officer to review the appraisal. + Assign tasks sequentially to assessment officers listed in the Appraisal Template. + Args: + doc (str/dict): The Appraisal document instance (JSON or dict format). + method (str): The method context in which the function is called (e.g., "on_update"). """ - appraisal = frappe.get_doc("Appraisal", doc) - assessment_officer_id = frappe.db.get_value("Employee", appraisal.employee, "assessment_officer") + try: + if not doc: + frappe.throw("Missing document data.") - if not assessment_officer_id: - frappe.throw(f"Assessment Officer not set for employee {appraisal.employee}") + # If doc is an ID string, fetch the full document + if isinstance(doc, str) and not doc.startswith('{'): + appraisal_doc = frappe.get_doc("Appraisal", doc) + else: + appraisal_doc = doc - user_id, officer_name = frappe.db.get_value("Employee", assessment_officer_id, ["user_id", "employee_name"]) + if not appraisal_doc: + frappe.throw("Invalid Appraisal document.") - if not user_id: - frappe.throw(f"No User ID found for assessment officer {assessment_officer_id}") + appraisal_template_name = appraisal_doc.appraisal_template - # Get email template - template_name = frappe.db.get_single_value("Beams HR Settings", "assessment_reminder_template") - if not template_name: - frappe.throw("Please set 'Assessment Reminder Template' in Beams HR Settings.") + if not appraisal_template_name: + return - template = frappe.get_doc("Email Template", template_name) + # Fetch Appraisal Template and assessment officers + appraisal_template_doc = frappe.get_doc("Appraisal Template", appraisal_template_name) + assessment_officers = appraisal_template_doc.get("assessment_officers") - context = { - "doc": appraisal, - "employee_name": appraisal.employee_name, - "officer_name": officer_name, - } - subject = frappe.render_template(template.subject or '', context) - message = frappe.render_template(template.response or template.message or '', context) + if not assessment_officers: + frappe.log_error("No assessment officers defined in the appraisal template.") + return + + # Find the next officer to assign a task to + assigned_officers = {row.designation for row in appraisal_doc.category_details} + + for officer in assessment_officers: + designation = officer.designation + + # Skip if task is already completed for this designation + if designation in assigned_officers: + continue - frappe.sendmail(recipients=frappe.db.get_value("User", user_id, "email"), subject=subject, message=message) + # Fetch employees for the designation + employees = frappe.get_all( + "Employee", + filters={"designation": designation, "status": "Active"}, + fields=["name", "user_id", "employee_name"] + ) - frappe.get_doc({ - "doctype": "Notification Log", - "subject": subject, - "for_user": user_id, - "type": "Alert", - "document_type": "Appraisal", - "document_name": appraisal, - "from_user": frappe.session.user, - "email_content": message - }).insert(ignore_permissions=True) + if not employees: + continue + + # Assign task to the first available employee with a user ID + for employee in employees: + if employee.get("user_id"): + try: + add_assign({ + "assign_to": [employee.user_id], + "doctype": appraisal_doc.doctype, + "name": appraisal_doc.name, + "description": f"Please add Category for {employee_id}.", + }) + + # Send Notification + frappe.sendmail( + recipients=[employee.user_id], + subject="New Category Task Assigned", + message=f"A new category task has been assigned to you for designation: {designation}." + ) + + return + except Exception as e: + frappe.log_error(f"Failed to assign task to {employee.user_id}: {str(e)}", "Task Assignment") + + return 0 + + except Exception as e: + frappe.log_error(f"Error in task assignment: {str(e)}", "Task Assignment") + frappe.throw(str(e)) + + + +def send_notification(user_id, appraisal_doc): + """ + Sends an email notification to the assigned officer. + + Args: + user_id (str): The user ID of the officer. + appraisal_doc (frappe.Document): The Appraisal document instance. + """ + try: + subject = f"Appraisal Notification: {appraisal_doc.name}" + message = f""" + Dear Officer,

+ You have been assigned to review the appraisal {appraisal_doc.name}. + Please take the necessary action.

+ Regards. + """ + frappe.sendmail(recipients=user_id, subject=subject, message=message) + + except Exception as e: + frappe.log_error("Notification Error", f"Failed to send notification to {user_id}: {str(e)}") - frappe.msgprint(f"Notification sent to {officer_name} for appraisal review.") - return {"status": "ok"} @frappe.whitelist() def get_appraisal_template_criteria(appraisal_template_name): @@ -432,59 +486,3 @@ def set_category_based_on_marks(doc, method): # Update the Appraisal document if category: doc.category_based_on_marks = category - - -@frappe.whitelist() -def send_assessment_reminder(doc): - """ - Sends an email notification and Notification Log to the Assessment Officer to review the appraisal. - """ - appraisal = frappe.get_doc("Appraisal", doc) - assessment_officer_id = frappe.db.get_value("Employee", appraisal.employee, "assessment_officer") - - if not assessment_officer_id: - frappe.throw(f"Assessment Officer not set for employee {appraisal.employee}") - - user_id, officer_name = frappe.db.get_value("Employee", assessment_officer_id, ["user_id", "employee_name"]) - if not user_id: - frappe.throw(f"No User ID found for assessment officer {assessment_officer_id}") - - email_id = frappe.db.get_value("User", user_id, "email") - if not email_id: - frappe.throw(f"No Email ID found for user {user_id}") - - # Fetch Email Template from settings - template_name = frappe.db.get_single_value("Beams HR Settings", "assessment_reminder_template") - if not template_name: - frappe.throw("Please set 'Assessment Reminder Template' in Beams HR Settings.") - - template = frappe.get_doc("Email Template", template_name) - context = { - "doc": appraisal, - "employee_name": appraisal.employee_name, - "officer_name": officer_name, - } - subject = frappe.render_template(template.subject or '', context) - message = frappe.render_template(template.response or template.message or '', context) - - # Send Email - frappe.sendmail( - recipients=email_id, - subject=subject, - message=message - ) - - # Notification Log - frappe.get_doc({ - "doctype": "Notification Log", - "subject": subject, - "for_user": user_id, - "type": "Alert", - "document_type": "Appraisal", - "document_name": appraisal.name, - "from_user": frappe.session.user, - "email_content": message - }).insert(ignore_permissions=True) - - frappe.msgprint(f"Notification sent to {officer_name} for appraisal review.") - return {"status": "ok"} diff --git a/beams/beams/custom_scripts/asset_movement/asset_movement.py b/beams/beams/custom_scripts/asset_movement/asset_movement.py index f7a099dac..81717e6bb 100644 --- a/beams/beams/custom_scripts/asset_movement/asset_movement.py +++ b/beams/beams/custom_scripts/asset_movement/asset_movement.py @@ -1,99 +1,106 @@ import frappe -from frappe.utils import flt + def update_issued_quantity(doc, method): - """ - Updates Issued Quantity in 'Required Items Detail' of an Equipment Request, - and updates or inserts corresponding rows in Project's Allocated Item Details, - based on the Asset Movement. - """ - if not doc.assets: - frappe.throw("No assets found in this Asset Movement.") - - reference_name = doc.reference_name - if not reference_name: - return - - required_items = frappe.get_all( - "Required Items Detail", - filters={"parent": reference_name}, - fields=["name", "required_item", "issued_quantity", "required_quantity"] - ) - - if not required_items: - return - - asset_count = {} - for asset in doc.assets: - if not asset.asset_name: - frappe.throw(f"Asset Name not set for Asset {asset.asset}.") - asset_count[asset.asset_name] = asset_count.get(asset.asset_name, 0) + 1 - - for req in required_items: - item = req.required_item - if item in asset_count: - new_issued_qty = (req.issued_quantity or 0) + asset_count[item] - frappe.db.set_value("Required Items Detail", req.name, "issued_quantity", new_issued_qty) - - project_name = frappe.db.get_value("Equipment Request", reference_name, "project") - if not project_name: - return - - project_doc = frappe.get_doc("Project", project_name) - - for req in required_items: - item = req.required_item - if item in asset_count: - for row in project_doc.allocated_item_details: - if row.required_item == item: - row.issued_quantity = (row.issued_quantity or 0) + asset_count[item] - break - else: - project_doc.append("allocated_item_details", { - "required_item": item, - "required_quantity": req.required_quantity, - "issued_quantity": asset_count[item], - }) - - project_doc.save(ignore_permissions=True) + """ + Updates Issued Quantity in 'Required Items Detail' of an Equipment Request, + and updates or inserts corresponding rows in Project's Allocated Item Details, + based on the Asset Movement. + """ + if not doc.assets: + frappe.throw("No assets found in this Asset Movement.") + + reference_name = doc.reference_name + if not reference_name: + return + + required_items = frappe.get_all( + "Required Items Detail", + filters={"parent": reference_name}, + fields=["name", "required_item", "issued_quantity", "required_quantity"], + ) + + if not required_items: + return + + asset_count = {} + for asset in doc.assets: + if not asset.asset_name: + frappe.throw(f"Asset Name not set for Asset {asset.asset}.") + asset_count[asset.asset_name] = asset_count.get(asset.asset_name, 0) + 1 + + for req in required_items: + item = req.required_item + if item in asset_count: + new_issued_qty = (req.issued_quantity or 0) + asset_count[item] + frappe.db.set_value( + "Required Items Detail", req.name, "issued_quantity", new_issued_qty + ) + + project_name = frappe.db.get_value("Equipment Request", reference_name, "project") + if not project_name: + return + + project_doc = frappe.get_doc("Project", project_name) + + for req in required_items: + item = req.required_item + if item in asset_count: + for row in project_doc.allocated_item_details: + if row.required_item == item: + row.issued_quantity = (row.issued_quantity or 0) + asset_count[item] + break + else: + project_doc.append( + "allocated_item_details", + { + "required_item": item, + "required_quantity": req.required_quantity, + "issued_quantity": asset_count[item], + }, + ) + + project_doc.save(ignore_permissions=True) + def before_save(doc, method): - if doc.assets: - first_asset = doc.assets[0] - if first_asset.to_employee: - doc.new_custodian = first_asset.to_employee + if doc.assets: + first_asset = doc.assets[0] + if first_asset.to_employee: + doc.new_custodian = first_asset.to_employee + + new_custodian_doc = frappe.get_doc("Employee", doc.new_custodian) + if new_custodian_doc.user_id: + doc.user_id = new_custodian_doc.user_id - new_custodian_doc = frappe.get_doc("Employee", doc.new_custodian) - if new_custodian_doc.user_id: - doc.user_id = new_custodian_doc.user_id @frappe.whitelist() def update_asset_location_from_movement(doc, method=None): - """ - Updates the location and physical storage details (room, shelf, row, bin) - of assets listed in an Asset Movement document. - If any of these fields are empty in the movement record, they will also be cleared in the Asset. - """ - if isinstance(doc, str): - doc = frappe.get_doc("Asset Movement", doc) - - for item in doc.assets: - if not item.asset: - continue - - asset = frappe.get_doc("Asset", item.asset) - updated = False - - if item.target_location: - asset.location = item.target_location - updated = True - else: - return - - for field in ["room", "shelf", "row", "bin"]: - item_value = getattr(item, field, None) - setattr(asset, field, item_value if item_value is not None else "") - updated = True - - if updated: - asset.save(ignore_permissions=True) + """ + Updates the location and physical storage details (room, shelf, row, bin) + of assets listed in an Asset Movement document. + If any of these fields are empty in the movement record, they will also be cleared in the Asset. + """ + if isinstance(doc, str): + doc = frappe.get_doc("Asset Movement", doc) + + for item in doc.assets: + if not item.asset: + continue + + asset = frappe.get_doc("Asset", item.asset) + updated = False + + if item.target_location: + asset.location = item.target_location + updated = True + else: + return + + for field in ["room", "shelf", "row", "bin"]: + item_value = getattr(item, field, None) + setattr(asset, field, item_value if item_value is not None else "") + updated = True + + if updated: + asset.save(ignore_permissions=True) diff --git a/beams/beams/custom_scripts/attendance_request/attendance_request.py b/beams/beams/custom_scripts/attendance_request/attendance_request.py index 2df125a82..d055d1acf 100644 --- a/beams/beams/custom_scripts/attendance_request/attendance_request.py +++ b/beams/beams/custom_scripts/attendance_request/attendance_request.py @@ -7,43 +7,47 @@ class AttendanceRequestOverride(AttendanceRequest): def create_or_update_attendance(self, date: str): - ''' - Method to Create or Update Attendance from Attendance Request - ''' + """ + Method to Create or Update Attendance from Attendance Request + """ attendance_name = self.get_attendance_record(date) status = self.get_attendance_status(date) if attendance_name: # Update existing attendance record - doc = frappe.get_doc('Attendance', attendance_name) + doc = frappe.get_doc("Attendance", attendance_name) old_status = doc.status - doc.db_set('attendance_request', self.name) - checkin_time, checkout_time = get_checkin_checkout_time(doc.employee, doc.attendance_date) + doc.db_set("attendance_request", self.name) + checkin_time, checkout_time = get_checkin_checkout_time( + doc.employee, doc.attendance_date + ) if not doc.in_time and checkin_time: - doc.db_set('in_time', checkin_time) + doc.db_set("in_time", checkin_time) if not doc.out_time and checkout_time: - doc.db_set('out_time', checkout_time) + doc.db_set("out_time", checkout_time) if old_status != status: - doc.db_set({'status': status}) - text = _('changed the status from {0} to {1} via Attendance Regularisation').format( - frappe.bold(old_status), frappe.bold(status) - ) - doc.add_comment(comment_type='Info', text=text) + doc.db_set({"status": status}) + text = _( + "changed the status from {0} to {1} via Attendance Regularisation" + ).format(frappe.bold(old_status), frappe.bold(status)) + doc.add_comment(comment_type="Info", text=text) frappe.msgprint( - _('Updated status from {0} to {1} for date {2} in the attendance record {3}').format( + _( + "Updated status from {0} to {1} for date {2} in the attendance record {3}" + ).format( frappe.bold(old_status), frappe.bold(status), frappe.bold(format_date(date)), - get_link_to_form('Attendance', doc.name), + get_link_to_form("Attendance", doc.name), ), - title=_('Attendance Updated'), + title=_("Attendance Updated"), ) else: # Create a new attendance record - doc = frappe.new_doc('Attendance') + doc = frappe.new_doc("Attendance") doc.employee = self.employee doc.attendance_date = date doc.shift = self.shift @@ -53,87 +57,111 @@ def create_or_update_attendance(self, date: str): doc.insert(ignore_permissions=True) doc.submit() + def get_checkin_checkout_time(employee, attendance_date): - ''' - Method to get First Checkin and Last Checkout Time based on attednace date and employee - ''' + """ + Method to get First Checkin and Last Checkout Time based on attednace date and employee + """ checkin_time, checkout_time = None, None - if frappe.db.exists('Attendance', { 'attendance_date':attendance_date, 'employee':employee }): - attendance_doc = frappe.get_doc('Attendance', { 'attendance_date':attendance_date, 'employee':employee }) + if frappe.db.exists( + "Attendance", {"attendance_date": attendance_date, "employee": employee} + ): + attendance_doc = frappe.get_doc( + "Attendance", {"attendance_date": attendance_date, "employee": employee} + ) if attendance_doc.in_time and not attendance_doc.out_time: - checkin_record = frappe.db.get_value('Employee Checkin', { 'attendance':attendance_doc.name, 'time':attendance_doc.in_time }) + checkin_record = frappe.db.get_value( + "Employee Checkin", + {"attendance": attendance_doc.name, "time": attendance_doc.in_time}, + ) checkout_time = get_checkout_time(employee, checkin_record) if attendance_doc.out_time and not attendance_doc.in_time: - checkout_record = frappe.db.get_value('Employee Checkin', { 'attendance':attendance_doc.name, 'time':attendance_doc.out_time }) + checkout_record = frappe.db.get_value( + "Employee Checkin", + {"attendance": attendance_doc.name, "time": attendance_doc.out_time}, + ) checkin_time = get_checkin_time(employee, checkout_record) return checkin_time, checkout_time + def get_checkout_time(employee, checkin_record): - ''' - Fetches the last checkout time for an employee after the provided checkin record. - ''' + """ + Fetches the last checkout time for an employee after the provided checkin record. + """ checkout_time = None - if frappe.db.exists('Employee Checkin', checkin_record): - checkin_time = frappe.db.get_value('Employee Checkin', checkin_record, 'time') + if frappe.db.exists("Employee Checkin", checkin_record): + checkin_time = frappe.db.get_value("Employee Checkin", checkin_record, "time") checkins = frappe.get_all( - 'Employee Checkin', + "Employee Checkin", fields=[ - 'name', - 'employee', - 'log_type', - 'time', - 'shift', - 'shift_start', - 'shift_end', - 'shift_actual_start', - 'shift_actual_end' + "name", + "employee", + "log_type", + "time", + "shift", + "shift_start", + "shift_end", + "shift_actual_start", + "shift_actual_end", ], filters={ - 'skip_auto_attendance': 0, - 'employee': employee, - 'attendance': ('is', 'not set'), - 'time': ('>=', checkin_time) + "skip_auto_attendance": 0, + "employee": employee, + "attendance": ("is", "not set"), + "time": (">=", checkin_time), }, - order_by='time', + order_by="time", ) for checkin in checkins: - if checkin.log_type == 'OUT' and not checkin.shift: + if checkin.log_type == "OUT" and not checkin.shift: checkout_time = checkin.time if checkout_time and checkin.shift: return checkout_time return checkout_time + def get_checkin_time(employee, checkout_record): - ''' - Fetches the first check-in time for an employee before the provided checkout record. - ''' + """ + Fetches the first check-in time for an employee before the provided checkout record. + """ checkin_time = None - if frappe.db.exists('Employee Checkin', checkout_record): - checkout_time = frappe.db.get_value('Employee Checkin', checkout_record, 'time') + if frappe.db.exists("Employee Checkin", checkout_record): + checkout_time = frappe.db.get_value("Employee Checkin", checkout_record, "time") checkins = frappe.get_all( - 'Employee Checkin', + "Employee Checkin", fields=[ - 'name', - 'employee', - 'log_type', - 'time', - 'shift', - 'shift_start', - 'shift_end', - 'shift_actual_start', - 'shift_actual_end' + "name", + "employee", + "log_type", + "time", + "shift", + "shift_start", + "shift_end", + "shift_actual_start", + "shift_actual_end", ], filters={ - 'skip_auto_attendance': 0, - 'employee': employee, - 'attendance': ('is', 'not set'), - 'time': ('<=', checkout_time) + "skip_auto_attendance": 0, + "employee": employee, + "attendance": ("is", "not set"), + "time": ("<=", checkout_time), }, - order_by='time desc', + order_by="time desc", ) for checkin in checkins: - if checkin.log_type == 'IN' and not checkin.shift: + if checkin.log_type == "IN" and not checkin.shift: checkin_time = checkin.time if checkin_time and checkin.shift: return checkin_time return checkin_time + + +@frappe.whitelist() +def validate_to_date(doc, method): + """ + Validates that the 'to_date' field in the Attendance Regularisation doctype + is not set to a future date. + """ + if doc.to_date: + if doc.to_date > today(): + frappe.throw(_("To Date cannot be a Future date")) diff --git a/beams/beams/custom_scripts/employee_checkin/employee_checkin.py b/beams/beams/custom_scripts/employee_checkin/employee_checkin.py index 7380d479e..e15a4326e 100644 --- a/beams/beams/custom_scripts/employee_checkin/employee_checkin.py +++ b/beams/beams/custom_scripts/employee_checkin/employee_checkin.py @@ -5,99 +5,108 @@ def handle_employee_checkin_out(doc, method): - """ - Handles Employee Checkin with type OUT: - - Creates or updates Leave Allocation. - - Triggers Compensatory Leave Log creation in Leave Allocation logic. - """ - if doc.log_type != "OUT": - return - - # Fetch Compensatory Leave Type - compensatory_leave_type = frappe.db.get_single_value("Beams HR Settings", "compensatory_leave_type") - if not compensatory_leave_type: - return - - # Parse time from the Employee Checkin log - doc_time = datetime.strptime(doc.time, "%Y-%m-%d %H:%M:%S") if isinstance(doc.time, str) else doc.time - start_date = doc_time.date() - end_date = add_days(start_date, 30) - today_date = today() - - # Verify Shift Assignment with roster_type 'OT' - shift_assignment = frappe.db.sql(""" - SELECT name FROM `tabShift Assignment` - WHERE employee = %s - AND roster_type = 'Double Shift' - AND %s BETWEEN start_date AND end_date - """, (doc.employee, today_date), as_dict=True) - - if not shift_assignment: - return - - # Fetch Leave Allocation - leave_allocation = frappe.get_all( - "Leave Allocation", - filters={"employee": doc.employee, "leave_type": compensatory_leave_type}, - fields=["name", "to_date", "new_leaves_allocated"], - limit=1 - ) - - if leave_allocation: - # Update existing Leave Allocation - allocation = frappe.get_doc("Leave Allocation", leave_allocation[0].name) - if allocation.to_date < end_date: - allocation.to_date = end_date - allocation.new_leaves_allocated += 1 - allocation.flags.ignore_permissions = True - allocation.save() - else: - # Create new Leave Allocation - leave_allocation_doc = frappe.new_doc("Leave Allocation") - leave_allocation_doc.update({ - "employee": doc.employee, - "leave_type": compensatory_leave_type, - "from_date": start_date, - "to_date": end_date, - "new_leaves_allocated": 1, - }) - leave_allocation_doc.insert(ignore_permissions=True) - leave_allocation_doc.submit() + """ + Handles Employee Checkin with type OUT: + - Creates or updates Leave Allocation. + - Triggers Compensatory Leave Log creation in Leave Allocation logic. + """ + if doc.log_type != "OUT": + return + + # Fetch Compensatory Leave Type + compensatory_leave_type = frappe.db.get_single_value( + "Beams HR Settings", "compensatory_leave_type" + ) + if not compensatory_leave_type: + return + + # Parse time from the Employee Checkin log + doc_time = ( + datetime.strptime(doc.time, "%Y-%m-%d %H:%M:%S") + if isinstance(doc.time, str) + else doc.time + ) + start_date = doc_time.date() + end_date = add_days(start_date, 30) + today_date = today() + + # Verify Shift Assignment with roster_type 'OT' + shift_assignment = frappe.db.sql( + """ + SELECT name FROM `tabShift Assignment` + WHERE employee = %s + AND roster_type = 'Double Shift' + AND %s BETWEEN start_date AND end_date + """, + (doc.employee, today_date), + as_dict=True, + ) + + if not shift_assignment: + return + + # Fetch Leave Allocation + leave_allocation = frappe.get_all( + "Leave Allocation", + filters={"employee": doc.employee, "leave_type": compensatory_leave_type}, + fields=["name", "to_date", "new_leaves_allocated"], + limit=1, + ) + + if leave_allocation: + # Update existing Leave Allocation + allocation = frappe.get_doc("Leave Allocation", leave_allocation[0].name) + if allocation.to_date < end_date: + allocation.to_date = end_date + allocation.new_leaves_allocated += 1 + allocation.flags.ignore_permissions = True + allocation.save() + else: + # Create new Leave Allocation + leave_allocation_doc = frappe.new_doc("Leave Allocation") + leave_allocation_doc.update( + { + "employee": doc.employee, + "leave_type": compensatory_leave_type, + "from_date": start_date, + "to_date": end_date, + "new_leaves_allocated": 1, + } + ) + leave_allocation_doc.insert(ignore_permissions=True) + leave_allocation_doc.submit() -def set_hd_agent_active_status(doc, method=None): - """Update HD Agent's active status based on today's latest employee check-in""" - - employee = doc.employee - - # Get user linked to the employee - user = frappe.db.get_value("Employee", {"name": employee}, "user_id") - - if not user: - return - - start = get_datetime(nowdate() + " 00:00:00") - end = get_datetime(nowdate() + " 23:59:59") - - # Get the latest check-in today - latest_checkin = frappe.db.get_all( - "Employee Checkin", - filters={ - "employee": employee, - "time": ["between", [start, end]] - }, - fields=["name", "log_type", "time"], - order_by="time desc", - limit=1 - ) - - if latest_checkin: - latest_log_type = latest_checkin[0].log_type - new_status = 1 if latest_log_type == "IN" else 0 - else: - # No check-ins today - new_status = 0 - - # Update HD Agent status - if frappe.db.exists("HD Agent", {"user": user}): - frappe.db.set_value("HD Agent", {"user": user}, "is_active", new_status) +def set_hd_agent_active_status(doc, method=None): + """Update HD Agent's active status based on today's latest employee check-in""" + + employee = doc.employee + + # Get user linked to the employee + user = frappe.db.get_value("Employee", {"name": employee}, "user_id") + + if not user: + return + + start = get_datetime(nowdate() + " 00:00:00") + end = get_datetime(nowdate() + " 23:59:59") + + # Get the latest check-in today + latest_checkin = frappe.db.get_all( + "Employee Checkin", + filters={"employee": employee, "time": ["between", [start, end]]}, + fields=["name", "log_type", "time"], + order_by="time desc", + limit=1, + ) + + if latest_checkin: + latest_log_type = latest_checkin[0].log_type + new_status = 1 if latest_log_type == "IN" else 0 + else: + # No check-ins today + new_status = 0 + + # Update HD Agent status + if frappe.db.exists("HD Agent", {"user": user}): + frappe.db.set_value("HD Agent", {"user": user}, "is_active", new_status) diff --git a/beams/beams/custom_scripts/job_applicant/job_applicant.js b/beams/beams/custom_scripts/job_applicant/job_applicant.js index 66df2a947..2f73d851f 100644 --- a/beams/beams/custom_scripts/job_applicant/job_applicant.js +++ b/beams/beams/custom_scripts/job_applicant/job_applicant.js @@ -27,6 +27,22 @@ frappe.ui.form.on('Job Applicant', { // Clear location if checkbox is unchecked frm.set_value('location', ''); } + }, + validate: function (frm) { + const resume_attachment = frm.doc.resume_attachment; + if (resume_attachment) { + const allowed_extensions = ['pdf', 'doc', 'docx', 'xls', 'xlsx']; + const file_extension = resume_attachment.split('.').pop().toLowerCase(); + + if (!allowed_extensions.includes(file_extension)) { + frappe.msgprint({ + title: __('Validation for Resume'), + message: __('Only PDF, DOC, DOCX, XLS, and XLSX files are allowed'), + indicator: 'red' + }); + frappe.validated = false; + } + } } }); diff --git a/beams/beams/custom_scripts/project/project.py b/beams/beams/custom_scripts/project/project.py index 12e4fe549..f87a65960 100644 --- a/beams/beams/custom_scripts/project/project.py +++ b/beams/beams/custom_scripts/project/project.py @@ -1,11 +1,10 @@ import json + import frappe from erpnext.accounts.utils import get_fiscal_year from frappe import _ from frappe.model.mapper import get_mapped_doc -from frappe.utils import nowdate -from frappe.utils import now -from frappe.utils import cint, now +from frappe.utils import cint, now, nowdate def validate_project(doc, method): @@ -215,7 +214,6 @@ def create_technical_request(project_id): project = frappe.get_doc('Project', project_id) - doc = frappe.get_doc({ 'doctype': 'Technical Request', 'project': project_id, @@ -653,7 +651,7 @@ def auto_return_vehicles_on_project_completion(doc, method): updated = True else: - print(f" - Skipped (already returned or reason provided)") + print(" - Skipped (already returned or reason provided)") if updated: log_doc.save(ignore_permissions=True) diff --git a/beams/beams/doctype/allocated_resource_detail/__init__.py b/beams/beams/doctype/allocated_resource_detail/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/beams/beams/doctype/allocated_resource_detail/allocated_resource_detail.json b/beams/beams/doctype/allocated_resource_detail/allocated_resource_detail.json new file mode 100644 index 000000000..8d854488b --- /dev/null +++ b/beams/beams/doctype/allocated_resource_detail/allocated_resource_detail.json @@ -0,0 +1,62 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2025-01-18 12:03:23.303907", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "department", + "designation", + "employee", + "hired_personnel", + "hired_personnel_contact_info" + ], + "fields": [ + { + "fieldname": "designation", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Designation", + "options": "Designation" + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee" + }, + { + "fieldname": "department", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Department", + "options": "Department" + }, + { + "fieldname": "hired_personnel", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Hired Personnel" + }, + { + "fieldname": "hired_personnel_contact_info", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Hired Personnel Contact Info" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-03-19 14:32:56.747852", + "modified_by": "Administrator", + "module": "BEAMS", + "name": "Allocated Resource Detail", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/beams/beams/doctype/allocated_resource_detail/allocated_resource_detail.py b/beams/beams/doctype/allocated_resource_detail/allocated_resource_detail.py new file mode 100644 index 000000000..13a3eacd3 --- /dev/null +++ b/beams/beams/doctype/allocated_resource_detail/allocated_resource_detail.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, efeone and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class AllocatedResourceDetail(Document): + pass diff --git a/beams/beams/doctype/batta_claim/batta_claim.js b/beams/beams/doctype/batta_claim/batta_claim.js index 289a0b81a..2e7d6da75 100644 --- a/beams/beams/doctype/batta_claim/batta_claim.js +++ b/beams/beams/doctype/batta_claim/batta_claim.js @@ -2,59 +2,89 @@ // For license information, please see license.txt frappe.ui.form.on('Batta Claim', { - onload: function (frm) { + validate: function(frm) { + calculate_total_distance_travelled(frm); + calculate_total_daily_batta(frm); + update_all_daily_batta(frm); + calculate_batta(frm); + calculate_total_hours(frm); + }, + batta: function(frm) { + update_all_daily_batta(frm); + }, + ot_batta: function(frm) { + update_all_daily_batta(frm); + }, + onload: function(frm) { + handle_designation_based_on_batta_type(frm); + }, + batta_type: function(frm) { + handle_designation_based_on_batta_type(frm); set_batta_based_on_options(frm); - calculate_totals(frm); - calculate_batta_totals(frm); + }, + employee: function(frm) { + handle_designation_based_on_batta_type(frm); }, room_rent_batta: function(frm) { - calculate_batta_totals(frm); - }, - daily_batta_with_overnight_stay: function(frm) { - calculate_batta_totals(frm); - }, - daily_batta_without_overnight_stay: function(frm) { - calculate_batta_totals(frm); - }, - food_allowance: function(frm) { - calculate_batta_totals(frm); - }, - origin: function(frm) { - update_work_detail(frm); + calculate_batta(frm); + if (frm.doc.room_rent_batta < 0) { + frappe.msgprint({ + message: "Room Rent Batta cannot be negative.", + indicator: "red" + }); + frm.set_value("room_rent_batta", 0); + } + }, + daily_batta_without_overnight_stay: function(frm) { + calculate_batta(frm); + if (frm.doc.daily_batta_without_overnight_stay < 0) { + frappe.msgprint({ + message: "Daily Batta Without Overnight Stay cannot be negative.", + indicator: "red" + }); + frm.set_value("daily_batta_without_overnight_stay", 0); + } }, - destination: function(frm) { - update_work_detail(frm); + daily_batta_with_overnight_stay: function(frm) { + calculate_batta(frm); + if (frm.doc.daily_batta_with_overnight_stay < 0) { + frappe.msgprint({ + message: "Daily Batta With Overnight Stay cannot be negative.", + indicator: "red" + }); + frm.set_value("daily_batta_with_overnight_stay", 0); + } }, - designation: function(frm) { - frm.trigger('calculate_batta'); + total_distance_travelled_km: function(frm) { + calculate_allowance(frm); + }, + total_hours: function(frm) { + calculate_allowance(frm); }, is_travelling_outside_kerala: function(frm) { - frm.trigger('calculate_batta'); + update_all_daily_batta(frm); + calculate_allowance(frm); }, is_overnight_stay: function(frm) { - frm.trigger('calculate_batta'); + update_all_daily_batta(frm); + calculate_allowance(frm); + frm.doc.work_detail.forEach(row => { + set_batta_for_food_allowance(frm, row["doctype"], row["name"]); + set_batta_for_food_allowance(frm, row["doctype"], row["name"]); + }) }, is_avail_room_rent: function(frm) { - frm.trigger('calculate_batta'); + update_all_daily_batta(frm); + calculate_allowance(frm); }, - total_distance_travelled_km: function(frm) { - frm.trigger('calculate_batta'); + is_delhi_bureau: function(frm) { + frm.doc.work_detail.forEach(row => { + set_batta_for_food_allowance(frm, row["doctype"], row["name"]); + set_batta_for_food_allowance(frm, row["doctype"], row["name"]); + }) }, - work_detail_add: function(frm, cdt, cdn) { - calculate_total_distance_travelled(frm); - }, - work_detail_onform_render: function(frm, cdt, cdn) { - calculate_total_distance_travelled(frm); - }, - work_detail_remove: function(frm, cdt, cdn) { - calculate_total_distance_travelled(frm); - }, - refresh: function (frm) { - set_batta_based_on_options(frm); - calculate_totals(frm); - calculate_total_distance_travelled(frm); - calculate_batta_totals(frm); - + //fetching policy values and setting fields read-only accordingly + refresh: function(frm) { frappe.call({ method: "beams.beams.doctype.batta_claim.batta_claim.get_batta_policy_values", callback: function(response) { @@ -68,110 +98,212 @@ frappe.ui.form.on('Batta Claim', { frm.set_df_property('daily_batta_without_overnight_stay', 'read_only', is_actual_daily_batta_without_overnight_stay == 0); frm.set_df_property('daily_batta_with_overnight_stay', 'read_only', is_actual_daily_batta_with_overnight_stay == 0); frm.set_df_property('room_rent_batta', 'read_only', is_actual_room_rent_batta == 0); - frm.set_df_property('food_allowance', 'read_only', is_actual_food_allowance == 0); // Refresh the fields to reflect the changes frm.refresh_field('daily_batta_without_overnight_stay'); frm.refresh_field('daily_batta_with_overnight_stay'); frm.refresh_field('room_rent_batta'); - frm.refresh_field('food_allowance'); + + frm.fields_dict['work_detail'].grid.update_docfield_property('breakfast', 'read_only', is_actual_food_allowance == 0); + frm.fields_dict['work_detail'].grid.update_docfield_property('lunch', 'read_only', is_actual_food_allowance == 0); + frm.fields_dict['work_detail'].grid.update_docfield_property('dinner', 'read_only', is_actual_food_allowance == 0); + + // Refresh child table + frm.refresh_field('work_detail'); } } }); + } +}); + +frappe.ui.form.on('Work Detail', { + distance_travelled_km: function(frm, cdt, cdn) { + calculate_total_distance_travelled(frm); + setTimeout(() => { + set_batta_for_food_allowance(frm, cdt, cdn); + calculate_batta(frm, cdt, cdn); + }, 30); }, - batta_type: function(frm) { - set_batta_based_on_options(frm); - frm.doc.work_detail.forEach(function(row) { - frappe.model.set_value(row.doctype, row.name, 'batta_type', frm.doc.batta_type); - }); - frm.refresh_field('work_detail'); - set_batta_based_on_options(frm); - handle_designation_based_on_batta_type(frm); - frm.set_value('batta', 0); - }, - employee: function (frm) { - handle_designation_based_on_batta_type(frm); + daily_batta: function(frm, cdt, cdn) { + calculate_total_batta(frm, cdt, cdn); }, - batta: function (frm) { - // Loop through each row in the work_detail child table to calculate the row values based on the updated batta - frm.doc.work_detail.forEach(function(row) { - if (frm.doc.batta_based_on === 'Daily') { - row.number_of_days = Math.ceil(row.total_hours / 24); - row.daily_batta = row.number_of_days * frm.doc.batta; - } else if (frm.doc.batta_based_on === 'Hours') { - row.daily_batta = (row.total_hours - row.ot_hours) * frm.doc.batta; - } - - row.ot_batta = row.ot_hours * frm.doc.ot_batta; + breakfast: function(frm, cdt, cdn) { + let child = locals[cdt][cdn]; + if (child.breakfast < 0) { + frappe.msgprint({ + message: "Breakfast cannot be negative.", + indicator: "red" + }); - // Refresh the fields for each row in the child table - frm.refresh_field('work_detail'); - }); - // After updating all the rows, recalculate the total values - calculate_totals(frm); + frappe.model.set_value(cdt, cdn, "breakfast", 0); // Reset to 0 + } + calculate_total_food_allowance(frm, cdt, cdn); + calculate_total_batta(frm, cdt, cdn); }, - calculate_batta: function(frm) { - // Ensure designation and total distance are filled before calling the function - if (frm.doc.designation && frm.doc.total_distance_travelled_km) { - // Sum up total_hours from the work_detail child table - let total_hours = 0; - if (frm.doc.work_detail) { - frm.doc.work_detail.forEach(row => { - total_hours += row.total_hours || 0; - }); - } + lunch: function(frm, cdt, cdn) { + let child = locals[cdt][cdn]; + if (child.lunch < 0) { + frappe.msgprint({ + message: "Lunch cannot be negative.", + indicator: "red" + }); - frappe.call({ - method: "beams.beams.doctype.batta_claim.batta_claim.calculate_batta_allowance", - args: { - designation: frm.doc.designation, - is_travelling_outside_kerala: frm.doc.is_travelling_outside_kerala, - is_avail_room_rent: frm.doc.is_avail_room_rent, - is_overnight_stay: frm.doc.is_overnight_stay, - total_distance_travelled_km: frm.doc.total_distance_travelled_km, - total_hours: total_hours, - }, - callback: function(r) { - if (r.message) { - // Set batta values in the form - frm.set_value('batta', r.message.batta); - frm.set_value('room_rent_batta', r.message.room_rent_batta); - frm.set_value('daily_batta_with_overnight_stay', r.message.daily_batta_with_overnight_stay); - frm.set_value('daily_batta_without_overnight_stay', r.message.daily_batta_without_overnight_stay); - frm.set_value('food_allowance', r.message.food_allowance); - } - } + frappe.model.set_value(cdt, cdn, "lunch", 0); // Reset to 0 + } + calculate_total_food_allowance(frm, cdt, cdn); + calculate_total_batta(frm, cdt, cdn); + }, + dinner: function(frm, cdt, cdn) { + let child = locals[cdt][cdn]; + if (child.dinner < 0) { + frappe.msgprint({ + message: "Dinner cannot be negative.", + indicator: "red" }); + + frappe.model.set_value(cdt, cdn, "dinner", 0); // Reset to 0 } - } -}); + calculate_total_food_allowance(frm, cdt, cdn); + calculate_total_batta(frm, cdt, cdn); + }, + total_batta: function(frm, cdt, cdn) { + calculate_total_daily_batta(frm, cdt, cdn); + }, + total_food_allowance: function(frm, cdt, cdn) { + calculate_total_batta(frm, cdt, cdn); + }, + work_detail_add: function(frm, cdt, cdn) { + const { origin, destination } = frm.doc; + frappe.model.set_value(cdt, cdn, 'origin', origin); + frappe.model.set_value(cdt, cdn, 'destination', destination); -frappe.ui.form.on('Work Detail', { - from_date_and_time: function (frm, cdt, cdn) { - validate_dates_and_calculate(frm, cdt, cdn); + calculate_total_distance_travelled(frm); + calculate_total_daily_batta(frm); + calculate_total_hours(frm); + setTimeout(() => { + calculate_batta(frm, cdt, cdn); + }, 30); }, - to_date_and_time: function (frm, cdt, cdn) { - validate_dates_and_calculate(frm, cdt, cdn); + work_detail_remove: function(frm, cdt, cdn) { + calculate_total_distance_travelled(frm); + calculate_total_daily_batta(frm); + calculate_total_hours(frm); + setTimeout(() => { + calculate_batta(frm, cdt, cdn); + }, 30); + }, + total_hours: function(frm, cdt, cdn) { + calculate_daily_batta(frm, cdt, cdn); + calculate_total_hours(frm,cdt,cdn); + set_batta_for_food_allowance(frm, cdt, cdn); + }, + ot_hours: function(frm, cdt, cdn) { + calculate_daily_batta(frm, cdt, cdn); }, - origin: function(frm) { - update_work_detail(frm); + from_date_and_time: function(frm, cdt, cdn) { + calculate_hours(frm, cdt, cdn); + calculate_daily_batta(frm, cdt, cdn); + setTimeout(() => { + set_batta_for_food_allowance(frm, cdt, cdn); + calculate_batta(frm, cdt, cdn); + }, 500); }, - destination: function(frm) { - update_work_detail(frm); + to_date_and_time: function(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + + if (row.from_date_and_time && row.to_date_and_time) { + let from_date = new Date(row.from_date_and_time); + let to_date = new Date(row.to_date_and_time); + + if (to_date <= from_date) { + frappe.msgprint(__('To Date & Time must be greater than From Date & Time')); + frappe.model.set_value(cdt, cdn, 'to_date_and_time', null); + return; + } + calculate_hours(frm, cdt, cdn); + calculate_daily_batta(frm, cdt, cdn); + setTimeout(() => { + set_batta_for_food_allowance(frm, cdt, cdn); + calculate_batta(frm, cdt, cdn); + }, 500); } + } }); -/* Function to set options for Batta Based On field based on Batta Type */ -function set_batta_based_on_options(frm) { - if (frm.doc.batta_type === 'External') { - frm.set_df_property('batta_based_on', 'options', 'Hours'); - frm.set_value('batta_based_on', 'Hours'); - } else { - frm.set_df_property('batta_based_on', 'options', ['Daily']); - frm.set_value('batta_based_on', 'Daily'); +/* + Calculates the total distance traveled based on all work detail entries. +*/ +function calculate_total_distance_travelled(frm) { + let totalDistance = 0; + frm.doc.work_detail.forEach(row => { + totalDistance += row.distance_travelled_km || 0; + }); + frm.set_value('total_distance_travelled_km', totalDistance); +} + +/* + Calculates the total hours worked based on all work detail entries. +*/ +function calculate_total_hours(frm) { + let totalHours = 0; + frm.doc.work_detail.forEach(row => { + totalHours += row.total_hours || 0; + }); + frm.set_value('total_hours', totalHours); +} + +/* + Calculates hours worked for a specific row based on the from and to date/time fields. +*/ +function calculate_hours(frm, cdt, cdn) { + let row = frappe.get_doc(cdt, cdn); + if (row.from_date_and_time && row.to_date_and_time) { + let total_hours = (new Date(row.to_date_and_time) - new Date(row.from_date_and_time)) / (1000 * 60 * 60); + frappe.model.set_value(cdt, cdn, 'total_hours', total_hours.toFixed(2)); + } +} + +/* + Calculates the daily batta based on the total hours worked and the batta type. +*/ +function calculate_daily_batta(frm, cdt, cdn) { + let row = frappe.get_doc(cdt, cdn); + + if (!row.total_hours) row.total_hours = 0; + + let number_of_days = Math.max(1, Math.ceil(row.total_hours / 24)); // Ensure at least 1 day + let daily_batta = 0; + + if (frm.doc.batta_based_on === 'Daily') { + daily_batta = number_of_days * (frm.doc.batta || 0); + } + + frappe.model.set_value(cdt, cdn, 'number_of_days', number_of_days); + frappe.model.set_value(cdt, cdn, 'daily_batta', daily_batta); +} + +/* + Updates daily batta for all child rows in the work detail table. +*/ +function update_all_daily_batta(frm) { + if (frm.doc.work_detail) { + frm.doc.work_detail.forEach(row => { + calculate_daily_batta(frm, row.doctype, row.name); + }); } } +/* + Calculates the total daily batta across all work detail entries. +*/ +function calculate_total_daily_batta(frm) { + let totalDailyBatta = 0; + frm.doc.work_detail.forEach(row => { + totalDailyBatta += row.total_batta || 0; + }); + frm.set_value('total_daily_batta', totalDailyBatta); +} + /* Function to handle designation field based on batta_type */ function handle_designation_based_on_batta_type(frm) { if (frm.doc.batta_type === 'Internal' && frm.doc.employee) { @@ -183,148 +315,121 @@ function handle_designation_based_on_batta_type(frm) { frappe.msgprint(__('Designation not found for the selected employee.')); } }); - // Clear designation for External } else if (frm.doc.batta_type === 'External') { frm.set_value('designation', ''); } } - -/* Function to validate dates and perform calculations */ -function validate_dates_and_calculate(frm, cdt, cdn) { - let row = locals[cdt][cdn]; - - if (row.from_date_and_time && row.to_date_and_time) { - let from_time = new Date(row.from_date_and_time); - let to_time = new Date(row.to_date_and_time); - - // Validation: From date should not be greater than to date - if (from_time > to_time) { - frappe.msgprint(__('From Date and Time cannot be greater than To Date and Time')); - frappe.model.set_value(cdt, cdn, 'to_date_and_time', ''); - return; - } - calculate_hours_and_totals(frm, cdt, cdn); - } -} - -/* Function to calculate hours and batta */ -function calculate_hours_and_totals(frm, cdt, cdn) { - let row = locals[cdt][cdn]; - - if (row.from_date_and_time && row.to_date_and_time) { - let from_time = new Date(row.from_date_and_time); - let to_time = new Date(row.to_date_and_time); - let diff = (to_time - from_time) / (1000 * 60 * 60); // Difference in hours - - if (diff >= 0) { - frappe.db.get_single_value('Beams Accounts Settings', 'default_working_hours') - .then(default_working_hours => { - row.total_hours = diff; - row.ot_hours = diff > default_working_hours ? diff - default_working_hours : 0; - - if (frm.doc.batta_based_on === 'Daily') { - row.number_of_days = Math.ceil(row.total_hours / 24); - row.daily_batta = row.number_of_days * frm.doc.batta; - } else if (frm.doc.batta_based_on === 'Hours') { - row.number_of_days = Math.ceil(row.total_hours / 24); - row.daily_batta = (row.total_hours - row.ot_hours) * frm.doc.batta; - } - - row.ot_batta = row.ot_hours * frm.doc.ot_batta; - - frm.refresh_field('work_detail'); - calculate_totals(frm); - }); - } +/* Sets the batta-based options based on the selected batta type.*/ +function set_batta_based_on_options(frm) { + if (frm.doc.batta_type === 'External') { + frm.set_df_property('batta_based_on', 'options', 'Hours'); + frm.set_value('batta_based_on', 'Hours'); + } else { + frm.set_df_property('batta_based_on', 'options', ['Daily']); + frm.set_value('batta_based_on', 'Daily'); } } -/* Function to calculate total batta values */ -function calculate_totals(frm) { - frm.call({ - method: "calculate_total_batta", - doc: frm.doc, - callback: function(response) { - // Update the form fields with the calculated totals - frm.set_value({ - 'total_daily_batta': response.message.total_daily_batta, - 'total_ot_batta': response.message.total_ot_batta, - 'total_driver_batta': response.message.total_driver_batta - }); +/* Calculates total batta based on room rent, daily batta with and without overnight stay.*/ +function calculate_batta(frm) { + let total_batta = (frm.doc.room_rent_batta || 0) + + (frm.doc.daily_batta_without_overnight_stay || 0) + + (frm.doc.daily_batta_with_overnight_stay || 0); - // Refresh the fields to update totals - frm.refresh_field(['total_daily_batta', 'total_ot_batta', 'total_driver_batta']); - } - }); + frm.set_value('batta', total_batta); } -/* Function to calculate batta values */ -function calculate_batta_totals(frm) { - frm.call({ - method: "calculate_batta", // Replace with actual path to the Python function - doc: frm.doc, - callback: function(response) { - // Update the form fields with the calculated totals from the Python method - frm.set_value({ - 'room_rent_batta': response.message.room_rent_batta, - 'daily_batta_with_overnight_stay': response.message.daily_batta_with_overnight_stay, - 'daily_batta_without_overnight_stay': response.message.daily_batta_without_overnight_stay, - 'food_allowance': response.message.food_allowance, - 'batta': response.message.batta // Assuming 'batta' is the total batta field - }); +function calculate_allowance(frm) { + if (!frm.doc.designation.length) { + frappe.msgprint(__("Please select a designation.")); + return; + } - // Refresh the fields to display updated totals - frm.refresh_field(['room_rent_batta', 'daily_batta_with_overnight_stay', 'daily_batta_without_overnight_stay', 'food_allowance', 'batta']); + frappe.call({ + method: "beams.beams.doctype.batta_claim.batta_claim.calculate_batta_allowance", + args: { + designation: frm.doc.designation, + is_travelling_outside_kerala: frm.doc.is_travelling_outside_kerala || 0, + is_overnight_stay: frm.doc.is_overnight_stay || 0, + is_avail_room_rent: frm.doc.is_avail_room_rent || 0, + total_distance_travelled_km: frm.doc.total_distance_travelled_km || 0, + total_hours: frm.doc.total_hours || 0 + }, + callback: function(r) { + if (r.message) { + frm.set_value("room_rent_batta", r.message.room_rent_batta); + frm.set_value("daily_batta_with_overnight_stay", r.message.daily_batta_with_overnight_stay); + frm.set_value("daily_batta_without_overnight_stay", r.message.daily_batta_without_overnight_stay); + frm.set_value("batta", r.message.batta); + } } }); } -frappe.ui.form.on('Batta Claim', { - origin: update_child_table, - destination: update_child_table -}); - -function update_child_table(frm) { - if (!frm.doc.work_detail) return; - - frm.doc.work_detail.forEach(row => { - frappe.model.set_value(row.doctype, row.name, 'origin', frm.doc.origin || ''); - frappe.model.set_value(row.doctype, row.name, 'destination', frm.doc.destination || ''); - }); -} +/* Determines eligibility for food allowance and updates fields accordingly.*/ +function set_batta_for_food_allowance(frm, cdt, cdn) { + let child = locals[cdt][cdn]; + let designation = frm.doc.designation; + let is_overnight_stay = frm.doc.is_overnight_stay; + let is_delhi_bureau = frm.doc.is_delhi_bureau; -frappe.ui.form.on('Work Detail', { - work_detail_add: function(frm, cdt, cdn) { - frappe.model.set_value(cdt, cdn, 'origin', frm.doc.origin || ''); - frappe.model.set_value(cdt, cdn, 'destination', frm.doc.destination || ''); + if (designation.length <= 0) { + return; } -}); + let is_eligible = false; -function calculate_total_distance_travelled(frm) { - let totalDistance = 0; - // Sum all distance_travelled_km from the Work Detail child table - frm.doc.work_detail.forEach(function(row) { - if (row.distance_travelled_km) { - totalDistance += row.distance_travelled_km; + if (is_delhi_bureau) { + if (child.distance_travelled_km >= 30 && child.total_hours > 4) { + is_eligible = true; } - }); - // Set the total_distance_travelled_km field with the calculated sum - frm.set_value('total_distance_travelled_km', totalDistance); + } else { + if (child.distance_travelled_km >= 50 && child.distance_travelled_km < 100 && child.total_hours > 6) { + is_eligible = true; + } + } + + if (is_overnight_stay) { + frappe.model.set_value(child.doctype, child.name, "breakfast", 0); + frappe.model.set_value(child.doctype, child.name, "lunch", 0); + frappe.model.set_value(child.doctype, child.name, "dinner", 0); + frappe.model.set_value(child.doctype, child.name, "total_food_allowance", 0); + return; + } + else if (is_eligible && !is_overnight_stay) { + frappe.call({ + method: "beams.beams.doctype.batta_claim.batta_claim.get_batta_for_food_allowance", + args: { + designation: designation, + from_date_time: child.from_date_and_time, + to_date_time: child.to_date_and_time, + total_hrs: child.total_hours, + is_delhi_bureau: is_delhi_bureau + }, + callback: function (r) { + if (r && r.message) { + let response = r.message; + frappe.model.set_value(child.doctype, child.name, "breakfast", response.break_fast); + frappe.model.set_value(child.doctype, child.name, "lunch", response.lunch); + frappe.model.set_value(child.doctype, child.name, "dinner", response.dinner); + frappe.model.set_value(child.doctype, child.name, "total_food_allowance", response.break_fast + response.lunch + response.dinner); + } + } + }); + } } -function update_work_detail(frm) { - const { origin, destination, work_detail } = frm.doc; +/* Calculation of Total Food Allowance. */ +function calculate_total_food_allowance(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + row.total_food_allowance = (row.breakfast || 0) + (row.lunch || 0) + (row.dinner || 0); - // Update existing child rows with parent values - work_detail.forEach((row, index) => { - if (index >= 0) { - if (!row.origin || !row.destination) { - frappe.model.set_value(row.doctype, row.name, 'origin', origin); - frappe.model.set_value(row.doctype, row.name, 'destination', destination); - } - } - }); + frm.refresh_field("work_detail"); +} - frm.refresh_field('work_detail'); +/* Calculation of total batta based on daily batta and food allowance. */ +function calculate_total_batta(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + frappe.model.set_value(cdt, cdn, "total_batta", (row.daily_batta || 0) + (row.total_food_allowance || 0)); + frm.refresh_field("work_detail"); } diff --git a/beams/beams/doctype/batta_claim/batta_claim.json b/beams/beams/doctype/batta_claim/batta_claim.json index ef8c66c49..670440e60 100644 --- a/beams/beams/doctype/batta_claim/batta_claim.json +++ b/beams/beams/doctype/batta_claim/batta_claim.json @@ -15,6 +15,7 @@ "designation", "company", "bureau", + "is_delhi_bureau", "purpose", "column_break_lgjy", "cost_centre", @@ -32,19 +33,17 @@ "column_break_gbiv", "daily_batta_with_overnight_stay", "daily_batta_without_overnight_stay", - "batta", "column_break_jwtq", - "food_allowance", + "batta", "ot_batta", "section_break_osak", "work_detail", + "section_break_ozsf", "total_distance_travelled_km", + "column_break_fxzl", + "total_hours", "section_break_nsff", - "total_daily_batta", - "column_break_vksq", - "total_ot_batta", - "column_break_ofhv", - "total_driver_batta" + "total_daily_batta" ], "fields": [ { @@ -65,7 +64,7 @@ "fieldname": "batta_type", "fieldtype": "Select", "label": "Batta Type", - "options": "External\nInternal" + "options": "Internal\nExternal" }, { "depends_on": "eval:doc.batta_type == \"Internal\"\n", @@ -89,7 +88,8 @@ "fieldname": "designation", "fieldtype": "Link", "label": "Designation", - "options": "Designation" + "options": "Designation", + "read_only": 1 }, { "depends_on": "eval:doc.batta_type == \"External\"\n", @@ -116,6 +116,7 @@ { "fieldname": "batta_based_on", "fieldtype": "Select", + "hidden": 1, "label": "Batta Based On", "options": "Daily\nHours" }, @@ -139,30 +140,9 @@ { "fieldname": "total_daily_batta", "fieldtype": "Currency", - "label": "Total Daily Batta", - "read_only": 1 - }, - { - "depends_on": "eval:doc.batta_type == \"External\"", - "fieldname": "total_ot_batta", - "fieldtype": "Currency", - "label": "Total OT Batta", - "read_only": 1 - }, - { - "fieldname": "total_driver_batta", - "fieldtype": "Currency", - "label": "Total Driver Batta", + "label": "Total Batta", "read_only": 1 }, - { - "fieldname": "column_break_vksq", - "fieldtype": "Column Break" - }, - { - "fieldname": "column_break_ofhv", - "fieldtype": "Column Break" - }, { "fieldname": "work_detail", "fieldtype": "Table", @@ -205,12 +185,6 @@ "fieldtype": "Small Text", "label": "Purpose" }, - { - "fieldname": "total_distance_travelled_km", - "fieldtype": "Float", - "label": "Total Distance Travelled (KM)", - "read_only": 1 - }, { "depends_on": "eval: doc.batta_type == 'Internal'", "fieldname": "mode_of_travelling", @@ -266,13 +240,6 @@ "label": "Daily Batta Without Overnight Stay", "precision": "2" }, - { - "depends_on": "eval:doc.batta_type == \"Internal\"", - "fieldname": "food_allowance", - "fieldtype": "Currency", - "label": "Food Allowance", - "precision": "2" - }, { "fieldname": "attach", "fieldtype": "Attach", @@ -284,6 +251,32 @@ "fieldname": "is_avail_room_rent", "fieldtype": "Check", "label": "Is Avail Room Rent" + }, + { + "fieldname": "section_break_ozsf", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_fxzl", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_hours", + "fieldtype": "Float", + "label": "Total Hours", + "read_only": 1 + }, + { + "fieldname": "total_distance_travelled_km", + "fieldtype": "Float", + "label": "Total Distance Travelled(KM)", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "is_delhi_bureau", + "fieldtype": "Check", + "label": "Is Delhi Bureau" } ], "index_web_pages_for_search": 1, @@ -298,7 +291,7 @@ "link_fieldname": "batta_claim_reference" } ], - "modified": "2025-03-21 15:21:24.428732", + "modified": "2025-03-27 13:44:09.312155", "modified_by": "Administrator", "module": "BEAMS", "name": "Batta Claim", diff --git a/beams/beams/doctype/batta_claim/batta_claim.py b/beams/beams/doctype/batta_claim/batta_claim.py index a2e6e6fa3..8d10ddc14 100644 --- a/beams/beams/doctype/batta_claim/batta_claim.py +++ b/beams/beams/doctype/batta_claim/batta_claim.py @@ -3,6 +3,8 @@ import frappe import json +import re +from frappe.utils import getdate, get_datetime, date_diff, add_days from frappe.model.document import Document @@ -15,21 +17,10 @@ def on_submit(self): self.create_journal_entry_from_batta_claim() def validate(self): - # Call the method to calculate the total distance travelled self.calculate_total_distance_travelled() - - def calculate_total_distance_travelled(self): - total_distance = 0 - - # Loop through the rows in the 'work_detail' child table - if self.work_detail: - for row in self.work_detail: - if row.distance_travelled_km: - total_distance += row.distance_travelled_km - - # Set the 'total_distance_travelled_km' field with the calculated sum - self.total_distance_travelled_km = total_distance - + self.calculate_total_daily_batta() + self.calculate_batta() + self.calculate_total_hours() def create_purchase_invoice_from_batta_claim(self): ''' @@ -72,64 +63,83 @@ def create_journal_entry_from_batta_claim(self): 'account': batta_payable_account, 'party_type': 'Employee', 'party': self.employee, - 'debit_in_account_currency': self.total_driver_batta, + 'debit_in_account_currency': self.total_daily_batta, 'credit_in_account_currency': 0, }) journal_entry.append('accounts', { 'account': batta_expense_account, 'debit_in_account_currency': 0, - 'credit_in_account_currency': self.total_driver_batta, + 'credit_in_account_currency': self.total_daily_batta, }) journal_entry.insert() journal_entry.submit() frappe.msgprint(f"Journal Entry {journal_entry.name} has been created successfully.", alert=True,indicator="green") - @frappe.whitelist() - def calculate_total_batta(doc): - '''Function to calculate the Total Daily Batta based on data in work detail child table - and batta + def calculate_total_distance_travelled(self): + ''' + Calculation of Total Distance Travelled(km) + ''' + total_distance = 0 + + if self.work_detail: + for row in self.work_detail: + if row.distance_travelled_km: + total_distance += row.distance_travelled_km + + # Set the 'total_distance_travelled_km' field with the calculated sum + self.total_distance_travelled_km = total_distance + + def calculate_total_hours(self): + ''' + Calculation Of Total Hours + ''' + total_hours = 0 + + if self.work_detail: + for row in self.work_detail: + if row.total_hours: + total_hours += float(row.total_hours) + + self.total_hours = total_hours + + def calculate_total_daily_batta(self): + ''' + Calculation of Total Daily Batta ''' total_daily_batta = 0 - total_ot_batta = 0 - - # Loop through the work_detail child table and ensure default values are integers - for row in doc.get('work_detail', []): - total_daily_batta += row.get('daily_batta', 0) or 0 - total_ot_batta += row.get('ot_batta', 0) or 0 - - # Total batta is the sum of total_daily_batta and total_ot_batta - total_driver_batta = total_daily_batta + total_ot_batta - return { - 'total_daily_batta': total_daily_batta, - 'total_ot_batta': total_ot_batta, - 'total_driver_batta': total_driver_batta - } - - @frappe.whitelist() - def calculate_batta(doc): - # Ensure that all fields default to 0 if they are None - room_rent_batta = doc.get('room_rent_batta', 0) or 0 - daily_batta_with_overnight_stay = doc.get('daily_batta_with_overnight_stay', 0) or 0 - daily_batta_without_overnight_stay = doc.get('daily_batta_without_overnight_stay', 0) or 0 - food_allowance = doc.get('food_allowance', 0) or 0 - - # Calculate the total batta - batta = room_rent_batta + daily_batta_with_overnight_stay + daily_batta_without_overnight_stay + food_allowance - - return { - 'room_rent_batta': room_rent_batta, - 'daily_batta_with_overnight_stay': daily_batta_with_overnight_stay, - 'daily_batta_without_overnight_stay': daily_batta_without_overnight_stay, - 'food_allowance': food_allowance, - 'batta': batta - } - -# Batta Policy + + if self.work_detail: + for row in self.work_detail: + if row.total_batta: + total_daily_batta += row.total_batta + + # Set the 'total_distance_travelled_km' field with the calculated sum + self.total_daily_batta = total_daily_batta + + def calculate_batta(self): + ''' + Calculation of Total Batta based on room rent batta,daily batta with overnight stay and daily batta without Overnight stay + ''' + self.batta = (self.room_rent_batta or 0) \ + + (self.daily_batta_without_overnight_stay or 0) \ + + (self.daily_batta_with_overnight_stay or 0) + @frappe.whitelist() -def calculate_batta_allowance(designation, is_travelling_outside_kerala, is_overnight_stay,is_avail_room_rent, total_distance_travelled_km, total_hours): - # Ensure distance and total_hours are floats or 0 - total_distance_travelled_km = float(total_distance_travelled_km or 0) - total_hours = float(total_hours or 0) +def calculate_batta_allowance(designation=None, is_travelling_outside_kerala=0, is_overnight_stay=0, is_avail_room_rent=0, total_distance_travelled_km=0, total_hours=0): + ''' + Calculation Of Total Batta Allowance based on Batta Policy + ''' + # Convert inputs to proper types + def sanitize_number(value): + """Extract a valid float from a string by keeping only the first valid decimal number.""" + if isinstance(value, str): + match = re.search(r'\d+(\.\d+)?', value) + return float(match.group()) if match else 0.0 + return float(value or 0.0) + + # Convert inputs safely + total_distance_travelled_km = sanitize_number(total_distance_travelled_km) + total_hours = sanitize_number(total_hours) # Fetch the Batta Policy for the given designation batta_policy = frappe.get_all('Batta Policy', filters={'designation': designation}, fields=['*']) @@ -138,78 +148,104 @@ def calculate_batta_allowance(designation, is_travelling_outside_kerala, is_over return {"batta": 0} policy = batta_policy[0] - is_actual_room_rent = policy.get('is_actual') # Checkbox for Room Rent for Overnight Stay - is_actual_daily_batta_with_overnight_stay = policy.get('is_actual_') # Checkbox for Daily Batta With Overnight Stay - is_actual_daily_batta_without_overnight_stay = policy.get('is_actual__') # Checkbox for Daily Batta Without Overnight Stay - is_actual_food_allowance = policy.get('is_actual___') # Get the first (and only) policy for the designation - total_batta = 0 - # Safely handle NoneType by using a function - def safe_add(value): - return float(value) if value is not None else 0 + # Get policy checkbox values + is_actual_room_rent = policy.get('is_actual') or 0 # Room Rent Checkbox + is_actual_daily_batta = policy.get('is_actual_') or 0 # Daily Batta with Overnight Stay Checkbox + is_actual_daily_batta_without_overnight = policy.get('is_actual__') or 0 # Daily Batta Without Overnight Stay Checkbox - # Convert inputs to booleans + # Convert inputs to boolean is_travelling_outside_kerala = bool(int(is_travelling_outside_kerala or 0)) is_overnight_stay = bool(int(is_overnight_stay or 0)) is_avail_room_rent = bool(int(is_avail_room_rent or 0)) - # Initialize daily batta and room rent variables - daily_batta_without_overnight_stay = 0 + # Initialize batta values room_rent_batta = 0 daily_batta_with_overnight_stay = 0 - food_allowance = 0 - - # Add Daily Batta (Inside Kerala) if distance >= 50 km and total_hours >= 8 - if total_distance_travelled_km >= 50 and total_hours >= 8: - if is_actual_daily_batta_without_overnight_stay == 0: - if not is_overnight_stay: # Ensure the 'is_overnight_stay' checkbox is not checked - if is_travelling_outside_kerala: - outside_kerala_batta = safe_add(policy.get('outside_kerala')) - daily_batta_without_overnight_stay += outside_kerala_batta - else: - inside_kerala_batta = safe_add(policy.get('inside_kerala')) - daily_batta_without_overnight_stay += inside_kerala_batta - - # Add to total_batta - total_batta += daily_batta_without_overnight_stay - - if is_overnight_stay: - if is_avail_room_rent: - # Handle room rent addition - if is_actual_room_rent == 0: # Add room rent only if checkbox is unchecked (value is 0) - if is_travelling_outside_kerala: - room_rent = safe_add(policy.get('outside_kerala_')) - else: - room_rent = safe_add(policy.get('inside_kerala_')) - room_rent_batta += room_rent # Add room rent value to room_rent_batta + daily_batta_without_overnight_stay = 0 - # Handle daily batta with overnight stay addition - if is_actual_daily_batta_with_overnight_stay == 0: # Add daily batta only if checkbox is unchecked (value is 0) + # Calculate Room Rent Batta + if is_overnight_stay and is_avail_room_rent: + if not is_actual_room_rent: # Check if policy is not actual if is_travelling_outside_kerala: - daily_batta_with_overnight_stay = safe_add(policy.get('outside_kerala__')) + room_rent_batta = float(policy.get('outside_kerala_', 0)) else: - daily_batta_with_overnight_stay = safe_add(policy.get('inside_kerala__')) + room_rent_batta = float(policy.get('inside_kerala_', 0)) - # Add Room Rent and Daily Batta with Overnight Stay to total_batta - total_batta += room_rent_batta - total_batta += daily_batta_with_overnight_stay + # Calculate Daily Batta with Overnight Stay + if not is_actual_daily_batta: # Check if policy is not actual + if is_overnight_stay: + if is_travelling_outside_kerala: + daily_batta_with_overnight_stay = float(policy.get('outside_kerala__', 0)) + else: + daily_batta_with_overnight_stay = float(policy.get('inside_kerala__', 0)) - # Add Food Allowance if total distance is >= 25 km and total_hours >= 6 - if total_distance_travelled_km >= 25 and total_hours >= 6: - if is_actual_food_allowance == 0: - food_allowance = safe_add(policy.get('break_fast')) + safe_add(policy.get('lunch')) + safe_add(policy.get('dinner')) - total_batta += food_allowance + # Calculate Daily Batta without Overnight Stay + if not is_actual_daily_batta_without_overnight: # Check if policy is not actual + if not is_overnight_stay: # Ensure overnight stay is NOT checked + if total_distance_travelled_km >= 100 and total_hours >= 8: # Additional condition + if is_travelling_outside_kerala: + daily_batta_without_overnight_stay = float(policy.get('outside_kerala', 0)) + else: + daily_batta_without_overnight_stay = float(policy.get('inside_kerala', 0)) - # Return all relevant values in a single dictionary return { "room_rent_batta": room_rent_batta, "daily_batta_with_overnight_stay": daily_batta_with_overnight_stay, - "daily_batta_without_overnight_stay": daily_batta_without_overnight_stay, - "food_allowance": food_allowance, - "batta": total_batta + "daily_batta_without_overnight_stay": daily_batta_without_overnight_stay } @frappe.whitelist() def get_batta_policy_values(): + ''' + Fetch and return the batta policy values from the 'Batta Policy' doctype + ''' result = frappe.db.get_value('Batta Policy', {}, ['is_actual', 'is_actual_', 'is_actual__', 'is_actual___'], as_dict=True) return result + +@frappe.whitelist() +def get_batta_for_food_allowance(designation, from_date_time, to_date_time, total_hrs, is_delhi_bureau=False): + ''' + Method to get Batta for Food + ''' + values = { 'break_fast': 0, 'lunch': 0, 'dinner': 0 } + batta_policy = frappe.db.exists('Batta Policy', { 'designation': designation }) + from_date_time = get_datetime(from_date_time) + to_date_time = get_datetime(to_date_time) + required_hours = 4 if is_delhi_bureau else 6 + + if batta_policy and float(total_hrs) > required_hours: + is_actual = frappe.db.get_value('Batta Policy', batta_policy, 'is_actual___') + if is_actual: + return values + + break_fast, lunch, dinner = frappe.db.get_value('Batta Policy', batta_policy, ['break_fast', 'lunch', 'dinner']) + + current_date = getdate(from_date_time) + end_date = getdate(to_date_time) + + while current_date <= end_date: + # Define meal timings for the current day + break_fast_start_time = get_datetime(f'{current_date} 04:00') + break_fast_end_time = get_datetime(f'{current_date} 09:00') + lunch_start_time = get_datetime(f'{current_date} 12:30') + lunch_end_time = get_datetime(f'{current_date} 14:00') + dinner_start_time = get_datetime(f'{current_date} 18:00') + dinner_end_time = get_datetime(f'{current_date} 21:00') + + # Check Breakfast + if (from_date_time <= break_fast_start_time <= to_date_time) or (from_date_time <= break_fast_end_time <= to_date_time): + values['break_fast'] += break_fast + + # Check Lunch + if (from_date_time <= lunch_start_time <= to_date_time) or (from_date_time <= lunch_end_time <= to_date_time): + values['lunch'] += lunch + + # Check Dinner + if (from_date_time <= dinner_start_time <= to_date_time) or (from_date_time <= dinner_end_time <= to_date_time): + values['dinner'] += dinner + + # Move to the next day + current_date = add_days(current_date, 1) + + return values diff --git a/beams/beams/doctype/batta_policy/batta_policy.json b/beams/beams/doctype/batta_policy/batta_policy.json index de7457081..5cbd58b39 100644 --- a/beams/beams/doctype/batta_policy/batta_policy.json +++ b/beams/beams/doctype/batta_policy/batta_policy.json @@ -57,7 +57,7 @@ { "fieldname": "room_rent_overnightbills_section", "fieldtype": "Section Break", - "label": "Room Rent for OverNight Stay" + "label": "Room Rent for Overnight Stay" }, { "depends_on": "eval:doc.is_actual__ == \"0\"", @@ -91,7 +91,7 @@ { "fieldname": "daily_batta_with_overnight_stay_section", "fieldtype": "Section Break", - "label": " Daily Batta With OverNight Stay" + "label": " Daily Batta With Overnight Stay" }, { "fieldname": "column_break_xaxj", @@ -110,10 +110,10 @@ "label": "Inside Kerala" }, { - "description": "Travel 50 kms with minimum 8 hours.\n", + "description": "Travel 100 kms with minimum 8 hours.\n", "fieldname": "daily_batta_without_over_night_stay_section", "fieldtype": "Section Break", - "label": "Daily Batta Without OverNight Stay" + "label": "Daily Batta Without Overnight Stay" }, { "fieldname": "column_break_ixxb", @@ -126,7 +126,7 @@ "label": "Inside Kerala" }, { - "description": "Travel 25 kms with minimum 6 hours ", + "description": "Travel 50 kms with minimum 6 hours", "fieldname": "food_allowance_section", "fieldtype": "Section Break", "label": "Food Allowance " @@ -188,7 +188,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2024-10-04 15:14:47.080979", + "modified": "2025-03-22 13:42:15.306374", "modified_by": "Administrator", "module": "BEAMS", "name": "Batta Policy", diff --git a/beams/beams/doctype/bureau_trip_sheet/__init__.py b/beams/beams/doctype/bureau_trip_sheet/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js new file mode 100644 index 000000000..5d3c5b343 --- /dev/null +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.js @@ -0,0 +1,417 @@ +// Copyright (c) 2024, efeone and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Trip Details', { + from_date_and_time: function (frm, cdt, cdn) { + calculate_hours_and_days(frm, cdt, cdn); + setTimeout(() => { + set_batta_for_food_allowance(frm, cdt, cdn); + calculate_batta(frm, cdt, cdn); + }, 200); + }, + to_date_and_time: function (frm, cdt, cdn) { + let row = locals[cdt][cdn]; + + if (row.from_date_and_time && row.to_date_and_time) { + let from_date = new Date(row.from_date_and_time); + let to_date = new Date(row.to_date_and_time); + + if (to_date <= from_date) { + frappe.msgprint(__('To Date & Time must be greater than From Date & Time')); + frappe.model.set_value(cdt, cdn, 'to_date_and_time', null); + return; + } + setTimeout(() => { + set_batta_for_food_allowance(frm, cdt, cdn); + calculate_batta(frm, cdt, cdn); + }, 200); + } + + calculate_hours_and_days(frm, cdt, cdn); + }, + total_hours: function (frm, cdt, cdn) { + calculate_daily_batta(frm, cdt, cdn); + calculate_ot_batta(frm, cdt, cdn); + set_batta_for_food_allowance(frm, cdt, cdn); + }, + ot_hours: function (frm, cdt, cdn) { + calculate_ot_batta(frm, cdt, cdn); + }, + breakfast: function (frm, cdt, cdn) { + calculate_total_food_allowance(frm, cdt, cdn); + }, + lunch: function (frm, cdt, cdn) { + calculate_total_food_allowance(frm, cdt, cdn); + }, + dinner: function (frm, cdt, cdn) { + calculate_total_food_allowance(frm, cdt, cdn); + }, + distance_travelled_km: function(frm, cdt, cdn) { + calculate_total_distance_travelled(frm, cdt, cdn); + setTimeout(() => { + set_batta_for_food_allowance(frm, cdt, cdn); + calculate_batta(frm, cdt, cdn); + }, 30); + }, + work_details_add: function(frm, cdt, cdn) { + + calculate_total_distance_travelled(frm, cdt, cdn); + + calculate_hours(frm, cdt, cdn); + + calculate_total_daily_batta(frm, cdt, cdn); + + calculate_total_ot_batta(frm, cdt, cdn); + + setTimeout(() => { + set_batta_for_food_allowance(frm, cdt, cdn); + calculate_batta(frm, cdt, cdn); + }, 30); + }, + work_details_remove: function(frm, cdt, cdn) { + calculate_total_distance_travelled(frm, cdt, cdn); + calculate_hours(frm, cdt, cdn); + calculate_total_daily_batta(frm, cdt, cdn); + calculate_total_ot_batta(frm, cdt, cdn); + setTimeout(() => { + set_batta_for_food_allowance(frm, cdt, cdn); + calculate_batta(frm, cdt, cdn); + }, 30); + }, + total_hours: function(frm, cdt, cdn) { + calculate_hours(frm, cdt, cdn); + set_batta_for_food_allowance(frm, cdt, cdn); + }, + daily_batta: function(frm, cdt, cdn) { + calculate_total_food_allowance(frm, cdt, cdn); + }, + total_batta: function(frm, cdt, cdn) { + calculate_total_daily_batta(frm, cdt, cdn); + }, + ot_batta: function(frm, cdt, cdn) { + calculate_total_ot_batta(frm, cdt, cdn); + } + +}); + +frappe.ui.form.on("Bureau Trip Sheet", { + refresh: function (frm) { + filter_supplier_field(frm); + update_all_daily_batta(frm); + update_all_ot_batta(frm); + calculate_allowance(frm); + }, + validate: function (frm) { + update_all_daily_batta(frm); + update_all_ot_batta(frm); + calculate_batta(frm); + calculate_total_distance_travelled(frm); + calculate_hours(frm); + calculate_total_daily_batta(frm); + calculate_total_ot_batta(frm); + }, + batta: function (frm) { + update_all_daily_batta(frm); + }, + ot_batta: function (frm) { + update_all_ot_batta(frm); + }, + daily_batta_with_overnight_stay: function (frm) { + calculate_batta(frm); + }, + daily_batta_without_overnight_stay: function (frm) { + calculate_batta(frm); + }, + total_daily_batta: function (frm) { + calculate_total_driver_batta(frm); + }, + total_ot_batta: function (frm) { + calculate_total_driver_batta(frm); + }, + is_overnight_stay: function (frm) { + calculate_allowance(frm); + calculate_daily_batta(frm); + }, + is_travelling_outside_kerala: function (frm) { + calculate_allowance(frm); + calculate_daily_batta(frm); + }, + total_distance_travelled_km: function (frm) { + calculate_allowance(frm); + }, + total_hours: function(frm) { + calculate_allowance(frm); + }, + is_overnight_stay: function(frm) { + calculate_allowance(frm); + frm.doc.work_details.forEach(row => { + set_batta_for_food_allowance(frm, row["doctype"], row["name"]); + set_batta_for_food_allowance(frm, row["doctype"], row["name"]); + }) + + }, + refresh: function(frm) { + // Fetch Batta Policy values and set read-only properties + frappe.call({ + method: "beams.beams.doctype.bureau_trip_sheet.bureau_trip_sheet.get_batta_policy_values", + callback: function(response) { + if (response.message) { + let is_actual_daily_batta_without_overnight_stay = response.message.is_actual__; + let is_actual_daily_batta_with_overnight_stay = response.message.is_actual_; + let is_actual_food_allowance = response.message.is_actual___; + + // Set read-only properties + frm.set_df_property('daily_batta_without_overnight_stay', 'read_only', is_actual_daily_batta_without_overnight_stay == 0); + frm.set_df_property('daily_batta_with_overnight_stay', 'read_only', is_actual_daily_batta_with_overnight_stay == 0); + + // Refresh fields + frm.refresh_field('daily_batta_without_overnight_stay'); + frm.refresh_field('daily_batta_with_overnight_stay'); + + frm.fields_dict['work_details'].grid.update_docfield_property('breakfast', 'read_only', is_actual_food_allowance == 0); + frm.fields_dict['work_details'].grid.update_docfield_property('lunch', 'read_only', is_actual_food_allowance == 0); + frm.fields_dict['work_details'].grid.update_docfield_property('dinner', 'read_only', is_actual_food_allowance == 0); + + // Refresh child table + frm.refresh_field('work_details'); + } + } + }); + + // Call the supplier filter function + filter_supplier_field(frm); + }, + + onload: function(frm) { + // Ensure the filter is applied on form load as well + filter_supplier_field(frm); + } +}); + +/* Set filter for supplier field */ +function filter_supplier_field(frm) { + frm.set_query("supplier", function () { + return { + filters: { + is_transporter: 1 + } + }; + }); +} + +/* Calculate total hours, number of days, and overtime hours */ +function calculate_hours_and_days(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + + if (row.from_date_and_time && row.to_date_and_time) { + let from_date = new Date(row.from_date_and_time); + let to_date = new Date(row.to_date_and_time); + + let total_hours = (to_date - from_date) / (1000 * 60 * 60); + let number_of_days = Math.ceil(total_hours / 24); + + frappe.db.get_single_value('Beams Accounts Settings', 'default_working_hours') + .then(default_hours => { + default_hours = parseFloat(default_hours) || 0; + let ot_hours = Math.max(0, total_hours - default_hours); + + frappe.model.set_value(cdt, cdn, 'total_hours', total_hours.toFixed(2)); + frappe.model.set_value(cdt, cdn, 'ot_hours', ot_hours.toFixed(2)); + frappe.model.set_value(cdt, cdn, 'number_of_days', number_of_days); + + frm.refresh_field("work_details"); + setTimeout(() => { + calculate_daily_batta(frm, cdt, cdn); + calculate_ot_batta(frm, cdt, cdn); + }, 200); + }); + } +} + +/* Calculate daily batta based on number of days and batta rate */ +function calculate_daily_batta(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + + let total_hours = row.total_hours || 0; + let number_of_days = Math.ceil(total_hours / 24); + let daily_batta = number_of_days * (frm.doc.batta || 0); + + frappe.model.set_value(cdt, cdn, 'daily_batta', daily_batta); + frm.refresh_field('work_details'); +} + +/* Calculate overtime batta based on OT hours and OT batta rate */ +function calculate_ot_batta(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + + let ot_hours = row.ot_hours || 0; + let ot_batta = ot_hours * (frm.doc.ot_batta || 0); + + frappe.model.set_value(cdt, cdn, 'ot_batta', ot_batta); + frm.refresh_field('work_details'); +} + +/* Update daily batta for all work details rows */ +function update_all_daily_batta(frm) { + if (frm.doc.work_details) { + frm.doc.work_details.forEach(row => { + calculate_daily_batta(frm, row.doctype, row.name); + }); + setTimeout(() => { + frm.refresh_field('work_details'); + calculate_daily_batta(frm, cdt, cdn); + calculate_ot_batta(frm, cdt, cdn); + }, 200); + + } +} + + +/* Update OT batta for all work details rows */ +function update_all_ot_batta(frm) { + if (frm.doc.work_details) { + frm.doc.work_details.forEach(row => { + calculate_ot_batta(frm, row.doctype, row.name); + }); + setTimeout(() => { + frm.refresh_field('work_details'); + }, 200); + } +} + +/* Calculate total batta by summing daily batta values */ +function calculate_batta(frm) { + let total_batta = (frm.doc.daily_batta_with_overnight_stay || 0) + + (frm.doc.daily_batta_without_overnight_stay || 0); + + frappe.model.set_value(frm.doctype, frm.docname, 'batta', total_batta); +} + +/* Calculate total food allowance and total batta for a row */ +function calculate_total_food_allowance(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + row.total_food_allowance = (row.breakfast || 0) + (row.lunch || 0) + (row.dinner || 0); + row.total_batta = row.total_food_allowance + (row.daily_batta || 0); + + frm.refresh_field("work_details"); + + let total_batta_sum = frm.doc.work_details.reduce((sum, r) => sum + (r.total_batta || 0), 0); + frm.set_value("total_daily_batta", total_batta_sum); +} + +/* Calculate total distance travelled across all work details rows */ +function calculate_total_distance_travelled(frm) { + let total_distance = 0; + frm.doc.work_details.forEach(row => { + total_distance += row.distance_travelled_km || 0; + }); + frm.set_value('total_distance_travelled_km', total_distance); + frm.refresh_field("total_distance_travelled_km"); +} + +/* Calculate total hours from all work details rows */ +function calculate_hours(frm) { + let total_hours = 0; + frm.doc.work_details.forEach(row => { + total_hours += row.total_hours || 0; + }); + frm.set_value('total_hours', total_hours); + frm.refresh_field("total_hours"); +} + +/* Calculate total daily batta for all work details rows */ +function calculate_total_daily_batta(frm) { + let total_batta = 0; + frm.doc.work_details.forEach(row => { + total_batta += row.total_batta || 0; + }); + frm.set_value('total_daily_batta', total_batta); + frm.refresh_field("total_daily_batta"); +} + +/* Calculate total OT batta for all work details rows */ +function calculate_total_ot_batta(frm) { + let total_ot_batta = 0; + frm.doc.work_details.forEach(row => { + total_ot_batta += row.ot_batta || 0; + }); + frm.set_value('total_ot_batta', total_ot_batta); + frm.refresh_field("total_ot_batta"); +} + +/* Calculate total driver batta as the sum of total daily batta and total OT batta */ +function calculate_total_driver_batta(frm) { + let total_daily_batta = frm.doc.total_daily_batta || 0; + let total_ot_batta = frm.doc.total_ot_batta || 0; + + frm.set_value('total_driver_batta', total_daily_batta + total_ot_batta); + frm.refresh_field("total_driver_batta"); +} + + +/* Determines eligibility for food allowance and updates fields accordingly.*/ +function set_batta_for_food_allowance(frm, cdt, cdn) { + let child = locals[cdt][cdn]; + + let designation = "Driver"; + let is_overnight_stay = frm.doc.is_overnight_stay; + + let is_eligible = false; + if (child.distance_travelled_km >= 50 && child.distance_travelled_km <= 100 && child.total_hours > 6) { + is_eligible = true; + } + + if (is_overnight_stay) { + frappe.model.set_value(child.doctype, child.name, "breakfast", 0); + frappe.model.set_value(child.doctype, child.name, "lunch", 0); + frappe.model.set_value(child.doctype, child.name, "dinner", 0); + frappe.model.set_value(child.doctype, child.name, "total_food_allowance", 0); + return; + } + + else if (is_eligible && !is_overnight_stay) { + frappe.call({ + method: "beams.beams.doctype.bureau_trip_sheet.bureau_trip_sheet.get_batta_for_food_allowance", + args: { + designation: designation, + from_date_time: child.from_date_and_time, + to_date_time: child.to_date_and_time, + total_hrs: child.total_hours, + }, + callback: function (r) { + if (r && r.message) { + let response = r.message; + frappe.model.set_value(child.doctype, child.name, "breakfast", response.break_fast); + frappe.model.set_value(child.doctype, child.name, "lunch", response.lunch); + frappe.model.set_value(child.doctype, child.name, "dinner", response.dinner); + frappe.model.set_value(child.doctype, child.name, "total_food_allowance", response.break_fast + response.lunch + response.dinner); + } + } + }); + } +} + +function calculate_allowance(frm) { + if (!frm.doc.supplier.length) { + frappe.msgprint(__("Please select a supplier.")); + return; + } + + frappe.call({ + method: "beams.beams.doctype.bureau_trip_sheet.bureau_trip_sheet.calculate_batta_allowance", + args: { + designation: frm.doc.designation, + is_travelling_outside_kerala: frm.doc.is_travelling_outside_kerala || 0, + is_overnight_stay: frm.doc.is_overnight_stay || 0, + total_distance_travelled_km: frm.doc.total_distance_travelled_km || 0, + total_hours: frm.doc.total_hours || 0 + }, + callback: function(r) { + if (r.message) { + frm.set_value("daily_batta_with_overnight_stay", r.message.daily_batta_with_overnight_stay); + frm.set_value("daily_batta_without_overnight_stay", r.message.daily_batta_without_overnight_stay); + frm.set_value("batta", r.message.batta); + } + } + }); +} diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json new file mode 100644 index 000000000..65683f6a5 --- /dev/null +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.json @@ -0,0 +1,248 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "format:BTS-{YY}-{####}", + "creation": "2025-03-24 15:17:02.532199", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "section_break_qq5d", + "supplier", + "bureau", + "column_break_psaw", + "company", + "section_break_qavf", + "is_overnight_stay", + "is_travelling_outside_kerala", + "daily_batta_with_overnight_stay", + "daily_batta_without_overnight_stay", + "column_break_hzfr", + "ot_batta", + "batta", + "section_break_tjbr", + "work_details", + "section_break_xxsb", + "total_distance_travelled_km", + "column_break_fgob", + "total_hours", + "trip_details_section", + "departure_location", + "destination_location", + "column_break_cdph", + "starting_date_and_time", + "ending_date_and_time", + "amended_from", + "section_break_dwds", + "total_daily_batta", + "column_break_zjgd", + "total_ot_batta", + "column_break_jevl", + "total_driver_batta" + ], + "fields": [ + { + "fieldname": "section_break_qq5d", + "fieldtype": "Section Break" + }, + { + "fieldname": "supplier", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Supplier", + "options": "Supplier", + "reqd": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, + { + "fieldname": "bureau", + "fieldtype": "Link", + "label": "Bureau", + "options": "Bureau" + }, + { + "fieldname": "section_break_qavf", + "fieldtype": "Section Break" + }, + { + "fetch_from": "supplier.ot_batta", + "fieldname": "ot_batta", + "fieldtype": "Currency", + "label": "OT Batta", + "read_only": 1 + }, + { + "depends_on": "eval:doc.is_overnight_stay ==0", + "fieldname": "daily_batta_without_overnight_stay", + "fieldtype": "Currency", + "label": "Daily Batta Without Overnight Stay" + }, + { + "default": "0", + "fieldname": "is_overnight_stay", + "fieldtype": "Check", + "label": "Is Overnight Stay" + }, + { + "default": "0", + "fieldname": "is_travelling_outside_kerala", + "fieldtype": "Check", + "label": " Is Travelling Outside Kerala" + }, + { + "depends_on": "eval:doc.is_overnight_stay ==1", + "fieldname": "daily_batta_with_overnight_stay", + "fieldtype": "Currency", + "label": "Daily Batta With Overnight Stay" + }, + { + "fieldname": "batta", + "fieldtype": "Currency", + "label": "Batta", + "read_only": 1 + }, + { + "fieldname": "column_break_hzfr", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_tjbr", + "fieldtype": "Section Break" + }, + { + "fieldname": "work_details", + "fieldtype": "Table", + "label": "Work Details", + "options": "Trip Details" + }, + { + "fieldname": "trip_details_section", + "fieldtype": "Section Break", + "label": "Trip Details" + }, + { + "fieldname": "departure_location", + "fieldtype": "Link", + "label": "Departure Location", + "options": "Location" + }, + { + "fieldname": "destination_location", + "fieldtype": "Link", + "label": "Destination Location", + "options": "Location" + }, + { + "fieldname": "ending_date_and_time", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "Ending Date and Time " + }, + { + "fieldname": "starting_date_and_time", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "Starting Date and Time " + }, + { + "fieldname": "column_break_cdph", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_psaw", + "fieldtype": "Column Break" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Bureau Trip Sheet", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "section_break_xxsb", + "fieldtype": "Section Break" + }, + { + "fieldname": "total_distance_travelled_km", + "fieldtype": "Float", + "label": "Total Distance Travelled(KM)", + "read_only": 1 + }, + { + "fieldname": "column_break_fgob", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_hours", + "fieldtype": "Float", + "label": "Total Hours", + "read_only": 1 + }, + { + "fieldname": "section_break_dwds", + "fieldtype": "Section Break" + }, + { + "fieldname": "total_daily_batta", + "fieldtype": "Currency", + "label": "Total Batta", + "read_only": 1 + }, + { + "fieldname": "column_break_zjgd", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_ot_batta", + "fieldtype": "Currency", + "label": "Total Ot Batta", + "read_only": 1 + }, + { + "fieldname": "column_break_jevl", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_driver_batta", + "fieldtype": "Currency", + "label": "Total Driver Batta", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2025-03-26 12:37:56.781474", + "modified_by": "Administrator", + "module": "BEAMS", + "name": "Bureau Trip Sheet", + "naming_rule": "Expression", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} diff --git a/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py new file mode 100644 index 000000000..2fdbd7606 --- /dev/null +++ b/beams/beams/doctype/bureau_trip_sheet/bureau_trip_sheet.py @@ -0,0 +1,183 @@ +# Copyright (c) 2024, efeone and contributors +# For license information, please see license.txt + +import frappe +from frappe.model.document import Document +from frappe import _ +from frappe.utils import get_datetime, getdate + + +class BureauTripSheet(Document): + def validate(self): + self.calculate_batta() + self.calculate_total_distance_travelled() + self.calculate_hours() + self.calculate_total_daily_batta() + self.calculate_total_ot_batta() + + def calculate_batta(self): + ''' + Calculate the total batta (allowance) based on daily batta amounts. + ''' + self.batta = (self.daily_batta_without_overnight_stay or 0) \ + + (self.daily_batta_with_overnight_stay or 0) + + def calculate_total_distance_travelled(self): + ''' + Calculate the total distance travelled by summing up the + distance_travelled_km' values from work details. + ''' + total_distance = 0 + + if self.work_details: + for row in self.work_details: + if row.distance_travelled_km: + total_distance += row.distance_travelled_km + + # Set the 'total_distance_travelled_km' field with the calculated sum + self.total_distance_travelled_km = total_distance + + def calculate_hours(self): + ''' + Calculate the total hours worked by summing up the 'total_hours' values from work details. + ''' + total_hours = 0 + + if self.work_details: + for row in self.work_details: + if row.total_hours: + total_hours += float(row.total_hours) + + # Set the 'total_distance_travelled_km' field with the calculated sum + self.total_hours = total_hours + + def calculate_total_daily_batta(self): + ''' + Calculate the total daily batta by summing up the 'total_batta' values from work details. + ''' + total_batta = 0 + + if self.work_details: + for row in self.work_details: + if row.total_batta: + total_batta += row.total_batta + + # Set the 'total_distance_travelled_km' field with the calculated sum + self.total_daily_batta = total_batta + + def calculate_total_ot_batta(self): + """ + Calculate the total OT batta by summing up the 'ot_batta' values from work details. + """ + total_ot_batta = 0 + + if self.work_details: + for row in self.work_details: + if row.ot_batta: + total_ot_batta += row.ot_batta + + # Set the 'total_distance_travelled_km' field with the calculated sum + self.total_ot_batta = total_ot_batta + +@frappe.whitelist() +def get_batta_for_food_allowance(designation, from_date_time, to_date_time, total_hrs): + ''' + Method to get Batta for Food + ''' + values = {'break_fast': 0, 'lunch': 0, 'dinner': 0} + batta_policy = frappe.db.exists('Batta Policy', {'designation': designation}) + from_date_time = get_datetime(from_date_time) + to_date_time = get_datetime(to_date_time) + required_hours = 6 + + if batta_policy and float(total_hrs) > required_hours: + break_fast, lunch, dinner = frappe.db.get_value('Batta Policy', batta_policy, ['break_fast', 'lunch', 'dinner']) + same_date = getdate(from_date_time) == getdate(to_date_time) + + meal_times = { + 'break_fast': ('04:00', '09:00', break_fast), + 'lunch': ('12:30', '14:00', lunch), + 'dinner': ('18:00', '21:00', dinner) + } + + for meal, (start_time, end_time, allowance) in meal_times.items(): + if same_date: + date_threshold = getdate(from_date_time) + if check_meal_time(from_date_time, to_date_time, date_threshold, start_time, end_time): + values[meal] = allowance + else: + # Check for both start and end dates + for date_threshold in [getdate(from_date_time), getdate(to_date_time)]: + if check_meal_time(from_date_time, to_date_time, date_threshold, start_time, end_time): + values[meal] += allowance + + return values + +def check_meal_time(from_date_time, to_date_time, date_threshold, start_time, end_time): + start_datetime = get_datetime('{} {}'.format(date_threshold, start_time)) + end_datetime = get_datetime('{} {}'.format(date_threshold, end_time)) + return (from_date_time <= start_datetime <= to_date_time) or (from_date_time <= end_datetime <= to_date_time) + +@frappe.whitelist() +def calculate_batta_allowance(designation=None, is_travelling_outside_kerala=0, is_overnight_stay=0, total_distance_travelled_km=0, total_hours=0): + ''' + Calculation Of Total Batta Allowance based on Batta Policy + ''' + #Convert inputs to proper types + def sanitize_number(value): + try: + return float(value) + except: + return 0 + total_distance_travelled_km = sanitize_number(total_distance_travelled_km) + total_hours = sanitize_number(total_hours) + + # Fetch the Batta Policy for the given designation + batta_policy = frappe.get_all('Batta Policy', filters={'designation': 'Driver'}, fields=['*']) + if not batta_policy: + frappe.throw(f"No Batta Policy found for the designation: {designation}") + return {"batta": 0} + + policy = batta_policy[0] + + # Get policy checkbox values + is_actual_daily_batta = policy.get('is_actual_') or 0 # Daily Batta with Overnight Stay Checkbox + is_actual_daily_batta_without_overnight = policy.get('is_actual__') or 0 # Daily Batta Without Overnight Stay Checkbox + + # Convert inputs to boolean + is_travelling_outside_kerala = bool(int(is_travelling_outside_kerala or 0)) + is_overnight_stay = bool(int(is_overnight_stay or 0)) + + # Initialize batta values + daily_batta_with_overnight_stay = 0 + daily_batta_without_overnight_stay = 0 + + # Calculate Daily Batta with Overnight Stay + if not is_actual_daily_batta: # Check if policy is not actual + if is_overnight_stay: + if is_travelling_outside_kerala: + daily_batta_with_overnight_stay = float(policy.get('outside_kerala__', 0)) + else: + daily_batta_with_overnight_stay = float(policy.get('inside_kerala__', 0)) + + # Calculate Daily Batta without Overnight Stay + if not is_actual_daily_batta_without_overnight: # Check if policy is not actual + if not is_overnight_stay: # Ensure overnight stay is NOT checked + if total_distance_travelled_km > 100 and total_hours >= 8: # Additional condition + if is_travelling_outside_kerala: + daily_batta_without_overnight_stay = float(policy.get('outside_kerala', 0)) + else: + daily_batta_without_overnight_stay = float(policy.get('inside_kerala', 0)) + + return { + "daily_batta_with_overnight_stay": daily_batta_with_overnight_stay, + "daily_batta_without_overnight_stay": daily_batta_without_overnight_stay + } + +@frappe.whitelist() +def get_batta_policy_values(): + ''' + Fetch and return the batta policy values from the 'Batta Policy' doctype + ''' + result = frappe.db.get_value('Batta Policy', {}, ['is_actual', 'is_actual_', 'is_actual__', 'is_actual___'], as_dict=True) + return result diff --git a/beams/beams/doctype/bureau_trip_sheet/test_bureau_trip_sheet.py b/beams/beams/doctype/bureau_trip_sheet/test_bureau_trip_sheet.py new file mode 100644 index 000000000..e2d01c208 --- /dev/null +++ b/beams/beams/doctype/bureau_trip_sheet/test_bureau_trip_sheet.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, efeone and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestBureauTripSheet(FrappeTestCase): + pass diff --git a/beams/beams/doctype/company_kra/__init__.py b/beams/beams/doctype/company_kra/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/beams/beams/doctype/company_kra/company_kra.json b/beams/beams/doctype/company_kra/company_kra.json new file mode 100644 index 000000000..54b981531 --- /dev/null +++ b/beams/beams/doctype/company_kra/company_kra.json @@ -0,0 +1,46 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-12-09 14:46:30.575713", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "kra", + "rating_by_ao", + "weightage" + ], + "fields": [ + { + "fieldname": "kra", + "fieldtype": "Link", + "in_list_view": 1, + "label": "KRA", + "options": "KRA" + }, + { + "fieldname": "rating_by_ao", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Rating by AO" + }, + { + "fieldname": "weightage", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "Weightage" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-12-10 15:22:00.264851", + "modified_by": "Administrator", + "module": "BEAMS", + "name": "Company KRA", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/beams/beams/doctype/company_kra/company_kra.py b/beams/beams/doctype/company_kra/company_kra.py new file mode 100644 index 000000000..cf43b8bfb --- /dev/null +++ b/beams/beams/doctype/company_kra/company_kra.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, efeone and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class CompanyKRA(Document): + pass diff --git a/beams/beams/doctype/department_cost_center/__init__.py b/beams/beams/doctype/department_cost_center/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/beams/beams/doctype/department_cost_center/department_cost_center.json b/beams/beams/doctype/department_cost_center/department_cost_center.json new file mode 100644 index 000000000..1065541eb --- /dev/null +++ b/beams/beams/doctype/department_cost_center/department_cost_center.json @@ -0,0 +1,33 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2025-03-22 15:52:56.514541", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "cost_center" + ], + "fields": [ + { + "fieldname": "cost_center", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Cost Center", + "options": "Cost Center" + } + ], + "grid_page_length": 50, + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-03-22 15:56:38.422227", + "modified_by": "Administrator", + "module": "BEAMS", + "name": "Department Cost Center", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/beams/beams/doctype/department_cost_center/department_cost_center.py b/beams/beams/doctype/department_cost_center/department_cost_center.py new file mode 100644 index 000000000..f464043ee --- /dev/null +++ b/beams/beams/doctype/department_cost_center/department_cost_center.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, efeone and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class DepartmentCostCenter(Document): + pass diff --git a/beams/beams/doctype/department_kra/__init__.py b/beams/beams/doctype/department_kra/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/beams/beams/doctype/department_kra/department_kra.json b/beams/beams/doctype/department_kra/department_kra.json new file mode 100644 index 000000000..3bbda6dc2 --- /dev/null +++ b/beams/beams/doctype/department_kra/department_kra.json @@ -0,0 +1,53 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-12-09 14:18:12.147592", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "parameter_name", + "rating", + "maximum_marks", + "maximum_marks_awarded_by_ao" + ], + "fields": [ + { + "fieldname": "parameter_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Parameter Name " + }, + { + "fieldname": "rating", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Rating", + "options": "\nOutstanding\nVery Good\nGood\nAverage\nBelow Average\nUnsatisfactory)" + }, + { + "fieldname": "maximum_marks", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Maximum Marks" + }, + { + "fieldname": "maximum_marks_awarded_by_ao", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Maximum Marks Awarded by AO" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-12-09 14:20:59.215655", + "modified_by": "Administrator", + "module": "BEAMS", + "name": "Department KRA", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/beams/beams/doctype/department_kra/department_kra.py b/beams/beams/doctype/department_kra/department_kra.py new file mode 100644 index 000000000..3242363be --- /dev/null +++ b/beams/beams/doctype/department_kra/department_kra.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, efeone and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class DepartmentKRA(Document): + pass diff --git a/beams/beams/doctype/employee_kra/__init__.py b/beams/beams/doctype/employee_kra/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/beams/beams/doctype/employee_kra/employee_kra.json b/beams/beams/doctype/employee_kra/employee_kra.json new file mode 100644 index 000000000..cdad93a09 --- /dev/null +++ b/beams/beams/doctype/employee_kra/employee_kra.json @@ -0,0 +1,60 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-12-09 12:31:12.065337", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "kra", + "goals", + "marks_by_employee", + "maximum_marks", + "marks_awarded_by_ao" + ], + "fields": [ + { + "fieldname": "kra", + "fieldtype": "Link", + "in_list_view": 1, + "label": "KRA", + "options": "KRA" + }, + { + "fieldname": "goals", + "fieldtype": "Long Text", + "in_list_view": 1, + "label": "Goals" + }, + { + "fieldname": "marks_by_employee", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Marks by Employee" + }, + { + "fieldname": "marks_awarded_by_ao", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Marks Awarded by AO" + }, + { + "fieldname": "maximum_marks", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Maximum Marks" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-12-10 12:35:10.081353", + "modified_by": "Administrator", + "module": "BEAMS", + "name": "Employee KRA", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/beams/beams/doctype/employee_kra/employee_kra.py b/beams/beams/doctype/employee_kra/employee_kra.py new file mode 100644 index 000000000..215074d4b --- /dev/null +++ b/beams/beams/doctype/employee_kra/employee_kra.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, efeone and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class EmployeeKRA(Document): + pass diff --git a/beams/beams/doctype/employees/__init__.py b/beams/beams/doctype/employees/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/beams/beams/doctype/employees/employees.json b/beams/beams/doctype/employees/employees.json new file mode 100644 index 000000000..6a0fdf8e1 --- /dev/null +++ b/beams/beams/doctype/employees/employees.json @@ -0,0 +1,35 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "format:EMP-{####}", + "creation": "2025-01-31 17:30:40.328297", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-03-17 13:38:54.789466", + "modified_by": "Administrator", + "module": "BEAMS", + "name": "Employees", + "naming_rule": "Expression", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/beams/beams/doctype/employees/employees.py b/beams/beams/doctype/employees/employees.py new file mode 100644 index 000000000..ac2b852b0 --- /dev/null +++ b/beams/beams/doctype/employees/employees.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, efeone and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class Employees(Document): + pass diff --git a/beams/beams/doctype/employees_left/__init__.py b/beams/beams/doctype/employees_left/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/beams/beams/doctype/employees_left/employees_left.json b/beams/beams/doctype/employees_left/employees_left.json new file mode 100644 index 000000000..8f655ee38 --- /dev/null +++ b/beams/beams/doctype/employees_left/employees_left.json @@ -0,0 +1,33 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-10-05 14:32:59.679775", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-10-05 15:32:02.009067", + "modified_by": "Administrator", + "module": "BEAMS", + "name": "Employees Left", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/beams/beams/doctype/employees_left/employees_left.py b/beams/beams/doctype/employees_left/employees_left.py new file mode 100644 index 000000000..d5f4a8aeb --- /dev/null +++ b/beams/beams/doctype/employees_left/employees_left.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, efeone and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class EmployeesLeft(Document): + pass diff --git a/beams/beams/doctype/outward_pass/outward_pass.js b/beams/beams/doctype/outward_pass/outward_pass.js index 95ca4de54..0d7be9177 100644 --- a/beams/beams/doctype/outward_pass/outward_pass.js +++ b/beams/beams/doctype/outward_pass/outward_pass.js @@ -111,4 +111,3 @@ function mergeArrays(arr1, arr2, key) { ); return unique; } - diff --git a/beams/beams/doctype/petty_cash_request/petty_cash_request.js b/beams/beams/doctype/petty_cash_request/petty_cash_request.js index 50c7613da..fda8d9cbe 100644 --- a/beams/beams/doctype/petty_cash_request/petty_cash_request.js +++ b/beams/beams/doctype/petty_cash_request/petty_cash_request.js @@ -36,6 +36,15 @@ frappe.ui.form.on("Petty Cash Request", { } } }); + }, + validate: function (frm) { + if (frm.doc.requested_amount <= 0) { + frappe.throw({ + title: __("Invalid Amount"), + message: __("Requested Amount should be greater than 0. Please enter a valid amount."), + indicator: "red" + }); + } } }); diff --git a/beams/beams/doctype/petty_cash_request/petty_cash_request.json b/beams/beams/doctype/petty_cash_request/petty_cash_request.json index 2d70f5d93..133d779d1 100644 --- a/beams/beams/doctype/petty_cash_request/petty_cash_request.json +++ b/beams/beams/doctype/petty_cash_request/petty_cash_request.json @@ -14,7 +14,8 @@ "column_break_pflc", "petty_cash_account", "account", - "requested_amount" + "requested_amount", + "reason" ], "fields": [ { @@ -71,12 +72,17 @@ "fieldtype": "Link", "label": "Petty Cash Account", "options": "Mode of Payment" + }, + { + "fieldname": "reason", + "fieldtype": "Small Text", + "label": "Reason" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2025-03-21 15:48:04.534573", + "modified": "2025-03-24 16:15:48.424785", "modified_by": "Administrator", "module": "BEAMS", "name": "Petty Cash Request", diff --git a/beams/beams/doctype/reference_document/__init__.py b/beams/beams/doctype/reference_document/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/beams/beams/doctype/reference_document/reference_document.json b/beams/beams/doctype/reference_document/reference_document.json new file mode 100644 index 000000000..093a1e158 --- /dev/null +++ b/beams/beams/doctype/reference_document/reference_document.json @@ -0,0 +1,40 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "format:RD-{YY}-{####}", + "creation": "2025-01-21 16:03:27.737629", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "remarks", + "document" + ], + "fields": [ + { + "fieldname": "remarks", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Remarks" + }, + { + "fieldname": "document", + "fieldtype": "Attach", + "in_list_view": 1, + "label": "Document" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-03-17 13:26:18.899356", + "modified_by": "Administrator", + "module": "BEAMS", + "name": "Reference Document", + "naming_rule": "Expression", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/beams/beams/doctype/reference_document/reference_document.py b/beams/beams/doctype/reference_document/reference_document.py new file mode 100644 index 000000000..e0da2888a --- /dev/null +++ b/beams/beams/doctype/reference_document/reference_document.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, efeone and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class ReferenceDocument(Document): + pass diff --git a/beams/beams/doctype/revenue_account/revenue_account.json b/beams/beams/doctype/revenue_account/revenue_account.json index 79ea86538..f815aff60 100644 --- a/beams/beams/doctype/revenue_account/revenue_account.json +++ b/beams/beams/doctype/revenue_account/revenue_account.json @@ -8,8 +8,11 @@ "field_order": [ "revenue_centre", "account", + "revenue_group", "column_break_quzd", + "revenue_region", "revenue_amount", + "description", "monthly_amount_distribution_section", "april", "may", @@ -24,7 +27,23 @@ "december", "january", "february", - "march" + "march", + "monthly_amount_distributioninr_section", + "april_inr", + "may_inr", + "june_inr", + "july_inr", + "column_break_ulxk", + "august_inr", + "september_inr", + "october_inr", + "november_inr", + "column_break_poio", + "december_inr", + "january_inr", + "february_inr", + "march_inr", + "revenue_amount_inr" ], "fields": [ { @@ -41,6 +60,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Revenue Amount", + "options": "company_currency", "precision": "2", "reqd": 1 }, @@ -123,17 +143,158 @@ "label": "December" }, { + "fetch_from": "revenue_centre.account", "fieldname": "account", "fieldtype": "Link", "label": "Account", "options": "Account", + "read_only": 1, "reqd": 1 + }, + { + "fieldname": "revenue_group", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Revenue Group", + "options": "Revenue Group", + "reqd": 1 + }, + { + "fieldname": "revenue_region", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Finance Region", + "options": "National\nGCC", + "reqd": 1 + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description" + }, + { + "collapsible": 1, + "fieldname": "monthly_amount_distributioninr_section", + "fieldtype": "Section Break", + "label": "Monthly Amount Distribution (INR)" + }, + { + "fieldname": "may_inr", + "fieldtype": "Currency", + "label": "May (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "june_inr", + "fieldtype": "Currency", + "label": "June (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "july_inr", + "fieldtype": "Currency", + "label": "July (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "column_break_ulxk", + "fieldtype": "Column Break" + }, + { + "fieldname": "august_inr", + "fieldtype": "Currency", + "label": "August (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "september_inr", + "fieldtype": "Currency", + "label": "September (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "october_inr", + "fieldtype": "Currency", + "label": "October (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "november_inr", + "fieldtype": "Currency", + "label": "November (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "column_break_poio", + "fieldtype": "Column Break" + }, + { + "fieldname": "december_inr", + "fieldtype": "Currency", + "label": "December (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "january_inr", + "fieldtype": "Currency", + "label": "January (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "february_inr", + "fieldtype": "Currency", + "label": "February (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "march_inr", + "fieldtype": "Currency", + "label": "March (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "revenue_amount_inr", + "fieldtype": "Currency", + "label": "Revenue Amount (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "april_inr", + "fieldtype": "Currency", + "label": "April (INR)", + "options": "default_currency", + "precision": "2", + "read_only": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-03-18 11:44:42.181253", + "modified": "2025-04-07 17:24:47.191935", "modified_by": "Administrator", "module": "BEAMS", "name": "Revenue Account", diff --git a/beams/beams/doctype/revenue_budget/revenue_budget.js b/beams/beams/doctype/revenue_budget/revenue_budget.js index 61df55d20..c25d87d0e 100644 --- a/beams/beams/doctype/revenue_budget/revenue_budget.js +++ b/beams/beams/doctype/revenue_budget/revenue_budget.js @@ -6,6 +6,42 @@ frappe.ui.form.on("Revenue Budget", { }, company: function (frm) { set_filters(frm); + fetch_template(frm); + }, + revenue_category: function (frm) { + set_filters(frm); + fetch_template(frm); + }, + /* Fetch Revenue Template Items in to Revenue Accounts when a Revenue Template is Selected */ + revenue_template: function(frm) { + if (frm.doc.revenue_template) { + frappe.call({ + method: 'frappe.client.get', + args: { + doctype: 'Revenue Template', + name: frm.doc.revenue_template + }, + callback: function (response) { + if (response.message) { + let revenue_template = response.message; + + frm.clear_table('revenue_accounts'); + + // Loop through the revenue_template_item child table + let template_items = revenue_template.revenue_template_item || []; + template_items.forEach(function (item) { + let row = frm.add_child('revenue_accounts'); + row.revenue_group = item.revenue_group; + row.account = item.account; + row.revenue_region = item.revenue_region; + row.revenue_centre = item.revenue_centre; + }); + + frm.refresh_field('revenue_accounts'); + } + } + }); + } } }); function set_filters(frm) { @@ -16,6 +52,14 @@ function set_filters(frm) { } }; }); + frm.set_query('revenue_template', function () { + return { + filters: { + company: frm.doc.company, + revenue_category: frm.doc.revenue_category + } + }; + }); } frappe.ui.form.on('Revenue Account', { @@ -85,3 +129,22 @@ function calculate_revenue_amount(frm, cdt, cdn) { frappe.model.set_value(cdt, cdn, 'revenue_amount', total); frm.refresh_field('revenue_account'); } +/* Fetch Revenue Template in Revenue Budget based on Revenue category and Selected company */ +function fetch_template(frm) { + if (frm.doc.revenue_category && frm.doc.company) { + frappe.call({ + method: 'beams.beams.doctype.revenue_budget.revenue_budget.get_revenue_template', + args: { + revenue_category: frm.doc.revenue_category, + company: frm.doc.company + }, + callback: function(r) { + if (r.message) { + frm.set_value('revenue_template', r.message); + } else { + frm.set_value('revenue_template', ''); + } + } + }); + } +} diff --git a/beams/beams/doctype/revenue_budget/revenue_budget.json b/beams/beams/doctype/revenue_budget/revenue_budget.json index 7d18f1f5b..4753de269 100644 --- a/beams/beams/doctype/revenue_budget/revenue_budget.json +++ b/beams/beams/doctype/revenue_budget/revenue_budget.json @@ -1,18 +1,20 @@ { "actions": [], "allow_rename": 1, - "autoname": "format:{revenue_group}-{revenue_region}/{fiscal_year}/{###}", + "autoname": "format:{revenue_category} {fiscal_year}-{abbreviation}", "creation": "2025-01-29 09:40:07.549966", "doctype": "DocType", "engine": "InnoDB", "field_order": [ "section_break_epzm", "company", - "revenue_group", + "abbreviation", "revenue_category", + "default_currency", + "company_currency", "column_break_wemo", "fiscal_year", - "revenue_region", + "revenue_template", "naming_series", "total_amount", "section_break", @@ -75,12 +77,38 @@ "read_only": 1 }, { - "fieldname": "revenue_group", + "fieldname": "amended_from", "fieldtype": "Link", - "in_list_view": 1, - "label": "Revenue Group", - "options": "Revenue Group", - "reqd": 1 + "label": "Amended From", + "no_copy": 1, + "options": "Revenue Budget", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "revenue_template", + "fieldtype": "Link", + "label": "Revenue Template", + "options": "Revenue Template" + }, + { + "default": "INR", + "fieldname": "default_currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Default Currency", + "options": "Currency", + "read_only": 1 + }, + { + "fetch_from": "company.default_currency", + "fieldname": "company_currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Company Currency", + "options": "Currency", + "read_only": 1 }, { "fieldname": "revenue_category", @@ -90,27 +118,19 @@ "reqd": 1 }, { - "fieldname": "revenue_region", - "fieldtype": "Select", - "label": "Revenue Region", - "options": "National\nGCC", - "reqd": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Revenue Budget", - "print_hide": 1, - "read_only": 1, - "search_index": 1 + "fetch_from": "company.abbr", + "fieldname": "abbreviation", + "fieldtype": "Data", + "hidden": 1, + "label": "Abbreviation", + "read_only": 1 } ], + "grid_page_length": 50, "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2025-03-18 12:09:29.003630", + "modified": "2025-04-07 15:00:03.238147", "modified_by": "Administrator", "module": "BEAMS", "name": "Revenue Budget", diff --git a/beams/beams/doctype/revenue_budget/revenue_budget.py b/beams/beams/doctype/revenue_budget/revenue_budget.py index 59b21cf17..ae0aa53e2 100644 --- a/beams/beams/doctype/revenue_budget/revenue_budget.py +++ b/beams/beams/doctype/revenue_budget/revenue_budget.py @@ -5,10 +5,49 @@ from frappe.model.document import Document class RevenueBudget(Document): - def before_save(self): self.calculate_total_amount() + def validate(self): + self.convert_currency() + def calculate_total_amount(self): total = sum([row.revenue_amount for row in self.get("revenue_accounts") if row.revenue_amount]) self.total_amount = total + + def convert_currency(self): + """Convert Revenue amounts for non-INR companies""" + company_currency = frappe.db.get_value("Company", self.company, "default_currency") + exchange_rate = 1 + + if company_currency != "INR": + exchange_rate = frappe.db.get_value("Company", self.company, "exchange_rate_to_inr") + if not exchange_rate: + frappe.throw( + f"Please set Exchange Rate from {company_currency} to INR for {self.company}", + title="Message", + ) + + months = [ + "january", "february", "march", "april", "may", "june", + "july", "august", "september", "october", "november", "december" + ] + + def apply_conversion(row): + """Apply exchange rate conversion to a revenue row""" + row.revenue_amount_inr = row.revenue_amount * exchange_rate + for month in months: + setattr(row, f"{month}_inr", (getattr(row, month, 0) or 0) * exchange_rate) + + for row in self.revenue_accounts: + apply_conversion(row) + +@frappe.whitelist() +def get_revenue_template(revenue_category, company): + """Get Revenue Template based on Revenue category and selected Company""" + template = frappe.db.get_value( + "Revenue Template", + {"revenue_category": revenue_category, "company": company}, + "name" + ) + return template diff --git a/beams/beams/doctype/revenue_centre/revenue_centre.js b/beams/beams/doctype/revenue_centre/revenue_centre.js index 9dc746d1c..7991458b1 100644 --- a/beams/beams/doctype/revenue_centre/revenue_centre.js +++ b/beams/beams/doctype/revenue_centre/revenue_centre.js @@ -1,8 +1,14 @@ // Copyright (c) 2025, efeone and contributors // For license information, please see license.txt -// frappe.ui.form.on("Revenue Centre", { -// refresh(frm) { - -// }, -// }); +frappe.ui.form.on("Revenue Centre", { + onload: function(frm) { + frm.set_query("account", function() { + return { + filters: { + account_type: "Income Account" + } + }; + }); + } +}); diff --git a/beams/beams/doctype/revenue_centre/revenue_centre.json b/beams/beams/doctype/revenue_centre/revenue_centre.json index 5193f51d0..8ec048246 100644 --- a/beams/beams/doctype/revenue_centre/revenue_centre.json +++ b/beams/beams/doctype/revenue_centre/revenue_centre.json @@ -6,19 +6,30 @@ "doctype": "DocType", "engine": "InnoDB", "field_order": [ - "revenue_centre" + "revenue_centre", + "account" ], "fields": [ { "fieldname": "revenue_centre", "fieldtype": "Data", + "in_list_view": 1, "label": "Revenue Centre", + "reqd": 1, "unique": 1 + }, + { + "fieldname": "account", + "fieldtype": "Link", + "label": "Account", + "options": "Account", + "reqd": 1 } ], + "grid_page_length": 50, "index_web_pages_for_search": 1, "links": [], - "modified": "2025-03-18 11:35:03.479904", + "modified": "2025-04-07 10:10:07.817821", "modified_by": "Administrator", "module": "BEAMS", "name": "Revenue Centre", diff --git a/beams/beams/doctype/revenue_template/revenue_template.json b/beams/beams/doctype/revenue_template/revenue_template.json index 5089290b6..4606a9729 100644 --- a/beams/beams/doctype/revenue_template/revenue_template.json +++ b/beams/beams/doctype/revenue_template/revenue_template.json @@ -1,24 +1,19 @@ { "actions": [], "allow_rename": 1, - "autoname": "field:template_title", + "autoname": "format:{revenue_category} - {abbreviation}", "creation": "2025-02-17 14:10:03.498118", "doctype": "DocType", "engine": "InnoDB", "field_order": [ - "template_title", "company", + "abbreviation", + "column_break_ofsa", + "revenue_category", "section_break_ybn1", "revenue_template_item" ], "fields": [ - { - "fieldname": "template_title", - "fieldtype": "Data", - "label": "Template Title", - "reqd": 1, - "unique": 1 - }, { "fieldname": "company", "fieldtype": "Link", @@ -36,15 +31,35 @@ "fieldtype": "Table", "label": "Revenue Template Item", "options": "Revenue Template Item" + }, + { + "fieldname": "revenue_category", + "fieldtype": "Link", + "label": "Revenue Category", + "options": "Revenue Category", + "reqd": 1 + }, + { + "fetch_from": "company.abbr", + "fieldname": "abbreviation", + "fieldtype": "Data", + "hidden": 1, + "label": "Abbreviation", + "read_only": 1 + }, + { + "fieldname": "column_break_ofsa", + "fieldtype": "Column Break" } ], + "grid_page_length": 50, "index_web_pages_for_search": 1, "links": [], - "modified": "2025-02-21 13:33:27.864251", + "modified": "2025-04-07 14:45:56.321830", "modified_by": "Administrator", "module": "BEAMS", "name": "Revenue Template", - "naming_rule": "By fieldname", + "naming_rule": "Expression", "owner": "Administrator", "permissions": [ { diff --git a/beams/beams/doctype/revenue_template_item/revenue_template_item.json b/beams/beams/doctype/revenue_template_item/revenue_template_item.json index cca68e269..c2640ef10 100644 --- a/beams/beams/doctype/revenue_template_item/revenue_template_item.json +++ b/beams/beams/doctype/revenue_template_item/revenue_template_item.json @@ -7,9 +7,9 @@ "engine": "InnoDB", "field_order": [ "revenue_centre", - "revenue_group", + "account", "column_break_vrbs", - "revenue_category", + "revenue_group", "revenue_region" ], "fields": [ @@ -18,39 +18,44 @@ "fieldtype": "Link", "in_list_view": 1, "label": "Revenue Group", - "options": "Revenue Group" - }, - { - "fieldname": "revenue_category", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Revenue Category", - "options": "Revenue Category" + "options": "Revenue Group", + "reqd": 1 }, { "fieldname": "revenue_centre", "fieldtype": "Link", "in_list_view": 1, "label": "Revenue Centre", - "options": "Account", + "options": "Revenue Centre", "reqd": 1 }, { "fieldname": "revenue_region", - "fieldtype": "Link", + "fieldtype": "Select", "in_list_view": 1, - "label": "Revenue Region", - "options": "Region" + "label": "Finance Region", + "options": "National\nGCC", + "reqd": 1 }, { "fieldname": "column_break_vrbs", "fieldtype": "Column Break" + }, + { + "fetch_from": "revenue_centre.account", + "fieldname": "account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Account", + "options": "Account", + "reqd": 1 } ], + "grid_page_length": 50, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-02-21 13:33:45.317169", + "modified": "2025-04-07 16:38:54.754188", "modified_by": "Administrator", "module": "BEAMS", "name": "Revenue Template Item", diff --git a/beams/beams/doctype/stringer_bill_date/__init__.py b/beams/beams/doctype/stringer_bill_date/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/beams/beams/doctype/stringer_bill_date/stringer_bill_date.json b/beams/beams/doctype/stringer_bill_date/stringer_bill_date.json new file mode 100644 index 000000000..1865d1953 --- /dev/null +++ b/beams/beams/doctype/stringer_bill_date/stringer_bill_date.json @@ -0,0 +1,31 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-08-31 09:56:58.447444", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "date" + ], + "fields": [ + { + "fieldname": "date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Date" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-08-31 09:57:24.841923", + "modified_by": "Administrator", + "module": "BEAMS", + "name": "Stringer Bill Date", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/beams/beams/doctype/stringer_bill_date/stringer_bill_date.py b/beams/beams/doctype/stringer_bill_date/stringer_bill_date.py new file mode 100644 index 000000000..c4933c2ad --- /dev/null +++ b/beams/beams/doctype/stringer_bill_date/stringer_bill_date.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, efeone and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class StringerBillDate(Document): + pass diff --git a/beams/beams/doctype/trip_details/trip_details.json b/beams/beams/doctype/trip_details/trip_details.json index 0244c0564..3b2279144 100644 --- a/beams/beams/doctype/trip_details/trip_details.json +++ b/beams/beams/doctype/trip_details/trip_details.json @@ -70,4 +70,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/beams/beams/doctype/work_detail/work_detail.json b/beams/beams/doctype/work_detail/work_detail.json index a7a4605b1..60ced23c0 100644 --- a/beams/beams/doctype/work_detail/work_detail.json +++ b/beams/beams/doctype/work_detail/work_detail.json @@ -15,23 +15,33 @@ "from_date_and_time", "to_date_and_time", "on_air_date", + "section_break_ikdi", + "breakfast", + "column_break_vnsw", + "lunch", + "column_break_atwg", + "dinner", + "column_break_gaok", + "total_food_allowance", "section_break_wigg", "daily_batta", "column_break_uihk", - "ot_batta", + "column_break_cjsx", + "total_batta", "section_break_elqj", "purpose", - "total_hours", - "ot_hours" + "total_hours" ], "fields": [ { + "columns": 2, "fieldname": "from_date_and_time", "fieldtype": "Datetime", "in_list_view": 1, "label": "Start Date and Time" }, { + "columns": 2, "fieldname": "to_date_and_time", "fieldtype": "Datetime", "in_list_view": 1, @@ -45,26 +55,11 @@ "read_only": 1 }, { - "depends_on": "eval:doc.batta_type == 'External'", - "fieldname": "ot_hours", - "fieldtype": "Float", - "in_list_view": 1, - "label": "OT Hours", - "read_only": 1 - }, - { + "description": "Daily Batta Without Food Allowance", "fieldname": "daily_batta", "fieldtype": "Currency", "in_list_view": 1, - "label": " Daily Batta", - "read_only": 1 - }, - { - "depends_on": "eval:doc.batta_type == 'External'", - "fieldname": "ot_batta", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "OT Batta", + "label": " Batta", "read_only": 1 }, { @@ -89,6 +84,7 @@ "label": "Purpose" }, { + "columns": 1, "fieldname": "origin", "fieldtype": "Link", "in_list_view": 1, @@ -96,6 +92,7 @@ "options": "Location" }, { + "columns": 1, "fieldname": "destination", "fieldtype": "Link", "in_list_view": 1, @@ -103,6 +100,7 @@ "options": "Location" }, { + "columns": 2, "fieldname": "distance_travelled_km", "fieldtype": "Float", "in_list_view": 1, @@ -129,12 +127,65 @@ { "fieldname": "column_break_uihk", "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_ikdi", + "fieldtype": "Section Break" + }, + { + "fieldname": "breakfast", + "fieldtype": "Currency", + "label": "Breakfast", + "precision": "2" + }, + { + "fieldname": "column_break_vnsw", + "fieldtype": "Column Break" + }, + { + "fieldname": "lunch", + "fieldtype": "Currency", + "label": "Lunch", + "precision": "2" + }, + { + "fieldname": "column_break_atwg", + "fieldtype": "Column Break" + }, + { + "fieldname": "dinner", + "fieldtype": "Currency", + "label": "Dinner", + "precision": "2" + }, + { + "fieldname": "column_break_gaok", + "fieldtype": "Column Break", + "read_only": 1 + }, + { + "fieldname": "total_food_allowance", + "fieldtype": "Currency", + "label": "Total Food Allowance", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "column_break_cjsx", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_batta", + "fieldtype": "Currency", + "label": "Total Batta", + "precision": "2", + "read_only": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2024-09-25 11:27:06.396834", + "modified": "2025-03-26 11:50:57.516655", "modified_by": "Administrator", "module": "BEAMS", "name": "Work Detail", diff --git a/beams/beams/overrides/queries.py b/beams/beams/overrides/queries.py new file mode 100644 index 000000000..3954fd887 --- /dev/null +++ b/beams/beams/overrides/queries.py @@ -0,0 +1,91 @@ +import frappe +from erpnext.controllers.queries import get_fields +from frappe.desk.reportview import get_filters_cond, get_match_cond +from frappe.desk.reportview import build_match_conditions, get_filters_cond + +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def get_cost_center_list(doctype, txt, searchfield, start, page_len, filters=None, as_dict=False): + from erpnext.controllers.queries import get_fields + conditions = [] + fields = ["name"] + + fields = get_fields("Cost Center", fields) + + searchfields = frappe.get_meta(doctype).get_search_fields() + searchfields = " or ".join(field + " like %(txt)s" for field in searchfields) + + bureau_user = False + department_user = False + departments = False + cost_center_filter = False + + # Get permitted bureaus + bureau_list = frappe.db.get_all( + 'User Permission', + filters = { + 'user': frappe.session.user, + 'allow': 'Bureau' + }, + fields = ['for_value'] + ) + if len(bureau_list) > 0: + bureaus = tuple(d['for_value'] for d in bureau_list) + cost_center_filter = frappe.get_all( + 'Bureau', + filters={'name': ['in', bureaus]}, + fields=['cost_center'] + ) + + else: + department_list = frappe.db.get_all( + 'User Permission', + filters = { + 'user': frappe.session.user, 'allow': 'Department' + }, + fields = ['for_value'] + ) + if len(department_list) > 0: + departments = tuple(d['for_value'] for d in department_list) + cost_center_filter = frappe.get_all( + 'Department Cost Center', + filters={ + 'parent': ['in', departments], + 'parenttype': 'Department', + }, + fields=['cost_center'], + ) + + cost_center_filter_cond = "" + if cost_center_filter: + cost_center_filter = ', '.join("'"+d['cost_center']+"'" for d in cost_center_filter) + cost_center_filter_cond = " and name in ({0})".format(cost_center_filter) + + query = ''' + select + {fields} + from + `tabCost Center` + where + docstatus < 2 + {cost_center_filter_cond} + and ({scond}) and disabled != 1 + {fcond} {mcond} + order by + idx desc, name + limit %(page_len)s offset %(start)s + ''' + + return frappe.db.sql( + query.format( + **{ + "fields": ", ".join(fields), + "scond": searchfields, + "mcond": get_match_cond(doctype), + "cost_center_filter_cond": cost_center_filter_cond, + "fcond": get_filters_cond(doctype, filters, conditions).replace("%", "%%") + } + ), + {"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len}, + as_dict=as_dict, + ) diff --git a/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.js b/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.js index 319065368..bc62d5517 100644 --- a/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.js +++ b/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.js @@ -111,7 +111,13 @@ frappe.query_reports["Detailed Budget Allocation Report"] = { fieldtype: "Select", options: "ASC\nDESC", default: "DESC" - } + }, + { + fieldname: "budget_amount_only", + label: "Budget Amount Only", + fieldtype: "Check", + default: 1 + }, ], tree: true, treeView: true, diff --git a/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.json b/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.json index 3b9e104bf..8aa3f82c7 100644 --- a/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.json +++ b/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.json @@ -1,5 +1,6 @@ { - "add_total_row": 1, + "add_total_row": 0, + "add_translate_data": 0, "columns": [], "creation": "2025-03-05 20:54:32.054081", "disabled": 0, @@ -9,7 +10,7 @@ "idx": 0, "is_standard": "Yes", "letterhead": null, - "modified": "2025-03-14 15:16:01.173074", + "modified": "2025-03-27 15:14:45.563936", "modified_by": "Administrator", "module": "BEAMS", "name": "Detailed Budget Allocation Report", diff --git a/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.py b/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.py index 0d1a2073f..c7922b6d0 100644 --- a/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.py +++ b/beams/beams/report/detailed_budget_allocation_report/detailed_budget_allocation_report.py @@ -63,6 +63,18 @@ def get_columns(filters): columns.append( {'label': _('Total Budget'), 'fieldtype': 'Currency', 'fieldname': 'total_budget', 'width': 200} ) + + if not filters.get("budget_amount_only"): + fields = [ + {'label': _('Finance Group'), 'fieldtype': 'Link', 'fieldname': 'finance_group', 'options': 'Finance Group', 'width': 200}, + {'label': _('Department'), 'fieldtype': 'Link', 'fieldname': 'department', 'options': 'Department', 'width': 200}, + {'label': _('Division'), 'fieldtype': 'Link', 'fieldname': 'division', 'options': 'Division', 'width': 200}, + {'label': _('Cost Head'), 'fieldtype': 'Link', 'fieldname': 'cost_head', 'options': 'Cost Head', 'width': 200}, + {'label': _('Cost Subhead'), 'fieldtype': 'Link', 'fieldname': 'cost_subhead', 'options': 'Cost Subhead', 'width': 200} + ] + # Insert all fields at index 1 in one step + columns[1:1] = fields + filters["currency_fields"] = currency_fields return columns @@ -151,6 +163,16 @@ def get_data(filters): 'account': cost_details.get('account', ''), 'total_budget': total_budget } + + if not filters.get("budget_amount_only"): + csh_row.update({ + 'finance_group': fg, + 'department': dept, + 'division': div, + 'cost_head': ch, + 'cost_subhead': csh, + }) + if period != 'Yearly': budget_column_data = get_budget_column_data(period, months_order, row_id) csh_row.update(budget_column_data) @@ -158,17 +180,33 @@ def get_data(filters): # Accumulate child budget into its parent for field in currency_fields: + budget_map[ch_id].update({ + 'finance_group': fg, + 'department': dept, + 'division': div, + 'cost_head': ch, + }) budget_map[ch_id][field] += csh_row.get(field, 0) # Propagate cost head budget to department for field in currency_fields: + budget_map[div].update({ + 'finance_group': fg, + 'department': dept, + 'division': div, + }) budget_map[div][field] += budget_map[ch_id][field] # Propagate division budget to departments for field in currency_fields: + budget_map[dept].update({ + 'finance_group': fg, + 'department': dept, + }) budget_map[dept][field] += budget_map[div][field] # Propagate department budget to finance group for field in currency_fields: + budget_map[fg_id]['finance_group'] = fg budget_map[fg_id][field] += budget_map[dept][field] # Propagate finance group budget to company diff --git a/beams/patches.txt b/beams/patches.txt index f2e2afa5e..c2e1371cf 100644 --- a/beams/patches.txt +++ b/beams/patches.txt @@ -1,4 +1,6 @@ [pre_model_sync] + +beams.patches.delete_employee_fields #25-02-2024 beams.patches.rename_hod_role #30-10-2024 beams.patches.delete_custom_fields #26-11-2024-1 beams.patches.no_of_children_patch #06-03-2025 diff --git a/beams/patches/delete_employee_fields.py b/beams/patches/delete_employee_fields.py new file mode 100644 index 000000000..97ace3cb6 --- /dev/null +++ b/beams/patches/delete_employee_fields.py @@ -0,0 +1,14 @@ +import frappe + +fields_to_remove = [ + { + 'dt':'Employee', + 'fieldname':'bureau' + } +] + +def execute(): + for field in fields_to_remove: + if frappe.db.exists('Custom Field', field): + frappe.db.delete('Custom Field', field) + frappe.db.commit() diff --git a/beams/setup.py b/beams/setup.py index 6519b3f17..f6c10ad52 100644 --- a/beams/setup.py +++ b/beams/setup.py @@ -4,3276 +4,3432 @@ def after_install(): - #Creating BEAMS specific custom fields - create_custom_fields(get_customer_custom_fields(), ignore_validate=True) - create_custom_fields(get_sales_invoice_custom_fields(), ignore_validate=True) - create_custom_fields(get_quotation_custom_fields(), ignore_validate=True) - create_custom_fields(get_purchase_invoice_custom_fields(), ignore_validate=True) - create_custom_fields(get_supplier_custom_fields(), ignore_validate=True) - create_custom_fields(get_item_custom_fields(), ignore_validate=True) - create_custom_fields(get_driver_custom_fields(), ignore_validate=True) - create_custom_fields(get_employee_custom_fields(), ignore_validate=True) - create_custom_fields(get_purchase_order_custom_fields(),ignore_validate=True) - create_custom_fields(get_material_request_custom_fields(), ignore_validate=True) - create_custom_fields(get_sales_order_custom_fields(), ignore_validate=True) - create_custom_fields(get_employee_advance_custom_fields(), ignore_validate=True) - create_custom_fields(get_journal_entry_custom_fields(), ignore_validate=True) - create_custom_fields(get_voucher_entry_custom_fields(), ignore_validate=True) - create_custom_fields(get_contract_custom_fields(),ignore_validate=True) - create_custom_fields(get_department_custom_fields(),ignore_validate=True) - create_custom_fields(get_job_requisition_custom_fields(),ignore_validate=True) - create_custom_fields(get_job_opening_custom_fields(),ignore_validate=True) - create_custom_fields(get_expected_skill_set_custom_fields(),ignore_validate=True) - create_custom_fields(get_interview_round_custom_fields(),ignore_validate=True) - create_custom_fields(get_job_applicant_custom_fields(),ignore_validate=True) - create_custom_fields(get_budget_custom_fields(),ignore_validate=True) - create_custom_fields(get_interview_feedback_custom_fields(),ignore_validate=True) - create_custom_fields(get_skill_assessment_custom_fields(), ignore_validate=True) - create_custom_fields(get_job_offer_custom_fields(), ignore_validate=True) - create_custom_fields(get_company_custom_fields(), ignore_validate=True) - create_custom_fields(get_training_event_employee_custom_fields(), ignore_validate=True) - create_custom_fields(get_attendance_request_custom_fields(),ignore_validate=True) - create_custom_fields(get_shift_assignment_custom_fields(),ignore_validate=True) - create_custom_fields(get_leave_type_custom_fields(),ignore_validate=True) - create_custom_fields(get_leave_application_custom_fields(),ignore_validate=True) - create_custom_fields(get_employee_performance_feedback(),ignore_validate=True) - create_custom_fields(get_employment_type(),ignore_validate=True) - create_custom_fields(get_appointment_letter(),ignore_validate=True) - create_custom_fields(get_employment_type_custom_fields(),ignore_validate=True) - create_custom_fields(get_employee_separation_custom_fields(),ignore_validate=True) - create_custom_fields(get_appraisal_template_custom_fields(),ignore_validate=True) - create_custom_fields(get_employee_feedback_rating_custom_fields(),ignore_validate=True) - create_custom_fields(get_appraisal_custom_fields(),ignore_validate=True) - create_custom_fields(get_appraisal_kra_custom_fields(),ignore_validate=True) - create_custom_fields(get_event_custom_fields(),ignore_validate=True) - create_custom_fields(get_project_custom_fields(),ignore_validate=True) - create_custom_fields(get_Payroll_Settings_custom_fields(),ignore_validate=True) - create_custom_fields(get_asset_custom_fields(),ignore_validate=True) - create_custom_fields(get_vehicle_custom_fields(),ignore_validate=True) - create_custom_fields(get_interview_custom_fields(),ignore_validate=True) - create_custom_fields(get_item_group_custom_fields(),ignore_validate=True) - create_custom_fields(get_hr_settings_custom_fields(),ignore_validate=True) - create_custom_fields(get_asset_category_custom_fields(),ignore_validate=True) - create_custom_fields(get_asset_movement_custom_fields(),ignore_validate=True) - create_custom_fields(get_full_and_final_statement_custom_fields(),ignore_validate=True) - create_custom_fields(get_expense_claim_custom_fields(),ignore_validate=True) - create_custom_fields(get_hd_ticket_custom_fields(),ignore_validate=True) - create_custom_fields(get_hd_ticket_type_custom_fields(),ignore_validate=True) - create_custom_fields(get_expense_claim_type_custom_fields(),ignore_validate=True) - create_custom_fields(get_supplier_quotation_custom_fields(), ignore_validate=True) - - - #Creating BEAMS specific Property Setters - create_property_setters(get_property_setters()) - - #Creating BEAMS specific Roles - create_custom_roles(get_beams_roles()) - - #Creating BEAMS specific Translations - create_translations(get_custom_translations()) - - #Creating BEAMS specific Email Template - create_email_templates(get_email_templates()) + # Creating BEAMS specific custom fields + create_custom_fields(get_customer_custom_fields(), ignore_validate=True) + create_custom_fields(get_sales_invoice_custom_fields(), ignore_validate=True) + create_custom_fields(get_quotation_custom_fields(), ignore_validate=True) + create_custom_fields(get_purchase_invoice_custom_fields(), ignore_validate=True) + create_custom_fields(get_supplier_custom_fields(), ignore_validate=True) + create_custom_fields(get_item_custom_fields(), ignore_validate=True) + create_custom_fields(get_driver_custom_fields(), ignore_validate=True) + create_custom_fields(get_employee_custom_fields(), ignore_validate=True) + create_custom_fields(get_purchase_order_custom_fields(), ignore_validate=True) + create_custom_fields(get_material_request_custom_fields(), ignore_validate=True) + create_custom_fields(get_sales_order_custom_fields(), ignore_validate=True) + create_custom_fields(get_employee_advance_custom_fields(), ignore_validate=True) + create_custom_fields(get_journal_entry_custom_fields(), ignore_validate=True) + create_custom_fields(get_voucher_entry_custom_fields(), ignore_validate=True) + create_custom_fields(get_contract_custom_fields(), ignore_validate=True) + create_custom_fields(get_department_custom_fields(), ignore_validate=True) + create_custom_fields(get_job_requisition_custom_fields(), ignore_validate=True) + create_custom_fields(get_job_opening_custom_fields(), ignore_validate=True) + create_custom_fields(get_expected_skill_set_custom_fields(), ignore_validate=True) + create_custom_fields(get_interview_round_custom_fields(), ignore_validate=True) + create_custom_fields(get_job_applicant_custom_fields(), ignore_validate=True) + create_custom_fields(get_budget_custom_fields(), ignore_validate=True) + create_custom_fields(get_interview_feedback_custom_fields(), ignore_validate=True) + create_custom_fields(get_skill_assessment_custom_fields(), ignore_validate=True) + create_custom_fields(get_job_offer_custom_fields(), ignore_validate=True) + create_custom_fields(get_company_custom_fields(), ignore_validate=True) + create_custom_fields( + get_training_event_employee_custom_fields(), ignore_validate=True + ) + create_custom_fields(get_attendance_request_custom_fields(), ignore_validate=True) + create_custom_fields(get_shift_assignment_custom_fields(), ignore_validate=True) + create_custom_fields(get_leave_type_custom_fields(), ignore_validate=True) + create_custom_fields(get_leave_application_custom_fields(), ignore_validate=True) + create_custom_fields(get_employee_performance_feedback(), ignore_validate=True) + create_custom_fields(get_employment_type(), ignore_validate=True) + create_custom_fields(get_appointment_letter(), ignore_validate=True) + create_custom_fields(get_employment_type_custom_fields(), ignore_validate=True) + create_custom_fields(get_employee_separation_custom_fields(), ignore_validate=True) + create_custom_fields(get_appraisal_template_custom_fields(), ignore_validate=True) + create_custom_fields( + get_employee_feedback_rating_custom_fields(), ignore_validate=True + ) + create_custom_fields(get_appraisal_custom_fields(), ignore_validate=True) + create_custom_fields(get_appraisal_kra_custom_fields(), ignore_validate=True) + create_custom_fields(get_event_custom_fields(), ignore_validate=True) + create_custom_fields(get_project_custom_fields(), ignore_validate=True) + create_custom_fields(get_Payroll_Settings_custom_fields(), ignore_validate=True) + create_custom_fields(get_asset_custom_fields(), ignore_validate=True) + create_custom_fields(get_vehicle_custom_fields(), ignore_validate=True) + create_custom_fields(get_interview_custom_fields(), ignore_validate=True) + create_custom_fields(get_item_group_custom_fields(), ignore_validate=True) + create_custom_fields(get_hr_settings_custom_fields(), ignore_validate=True) + create_custom_fields(get_asset_category_custom_fields(), ignore_validate=True) + create_custom_fields(get_asset_movement_custom_fields(), ignore_validate=True) + create_custom_fields( + get_full_and_final_statement_custom_fields(), ignore_validate=True + ) + create_custom_fields(get_expense_claim_custom_fields(), ignore_validate=True) + create_custom_fields(get_hd_ticket_custom_fields(), ignore_validate=True) + create_custom_fields(get_hd_ticket_type_custom_fields(), ignore_validate=True) + create_custom_fields(get_expense_claim_type_custom_fields(), ignore_validate=True) + create_custom_fields(get_supplier_quotation_custom_fields(), ignore_validate=True) -def after_migrate(): - after_install() + # Creating BEAMS specific Property Setters + create_property_setters(get_property_setters()) -def before_uninstall(): - delete_custom_fields(get_customer_custom_fields()) - delete_custom_fields(get_sales_invoice_custom_fields()) - delete_custom_fields(get_quotation_custom_fields()) - delete_custom_fields(get_purchase_invoice_custom_fields()) - delete_custom_fields(get_supplier_custom_fields()) - delete_custom_fields(get_item_custom_fields()) - delete_custom_fields(get_purchase_order_custom_fields()) - delete_custom_fields(get_driver_custom_fields()) - delete_custom_fields(get_material_request_custom_fields()) - delete_custom_fields(get_sales_order_custom_fields()) - delete_custom_fields(get_employee_advance_custom_fields()) - delete_custom_fields(get_employee_custom_fields()) - delete_custom_fields(get_journal_entry_custom_fields()) - delete_custom_fields(get_voucher_entry_custom_fields()) - delete_custom_fields(get_contract_custom_fields()) - delete_custom_fields(get_department_custom_fields()) - delete_custom_fields(get_job_requisition_custom_fields()) - delete_custom_fields(get_job_opening_custom_fields()) - delete_custom_fields(get_job_applicant_custom_fields()) - delete_custom_fields(get_budget_custom_fields()) - delete_custom_fields(get_expected_skill_set_custom_fields()) - delete_custom_fields(get_interview_round_custom_fields()) - delete_custom_fields(get_skill_assessment_custom_fields()) - delete_custom_fields(get_job_offer_custom_fields()) - delete_custom_fields(get_company_custom_fields()) - delete_custom_fields(get_training_event_employee_custom_fields()) - delete_custom_fields(get_attendance_request_custom_fields()) - delete_custom_fields(get_shift_assignment_custom_fields()) - delete_custom_fields(get_leave_type_custom_fields()) - delete_custom_fields(get_leave_application_custom_fields()) - delete_custom_fields(get_employee_performance_feedback()) - delete_custom_fields(get_employment_type()) - delete_custom_fields(get_appointment_letter()) - delete_custom_fields(get_employment_type_custom_fields()) - delete_custom_fields(get_employee_separation_custom_fields()) - delete_custom_fields(get_appraisal_template_custom_fields()) - delete_custom_fields(get_employee_feedback_rating_custom_fields()) - delete_custom_fields(get_appraisal_custom_fields()) - delete_custom_fields(get_appraisal_kra_custom_fields()) - delete_custom_fields(get_event_custom_fields()) - delete_custom_fields(get_project_custom_fields()) - delete_custom_fields(get_Payroll_Settings_custom_fields()) - delete_custom_fields(get_asset_custom_fields()) - delete_custom_fields(get_vehicle_custom_fields()) - delete_custom_fields(get_interview_custom_fields()) - delete_custom_fields(get_item_group_custom_fields()) - delete_custom_fields(get_hr_settings_custom_fields()) - delete_custom_fields(get_asset_category_custom_fields()) - delete_custom_fields(get_asset_movement_custom_fields()) - delete_custom_fields(get_full_and_final_statement_custom_fields()) - delete_custom_fields(get_expense_claim_custom_fields()) - delete_custom_fields(get_hd_ticket_custom_fields()) - delete_custom_fields(get_hd_ticket_type_custom_fields()) - delete_custom_fields(get_expense_claim_type_custom_fields()) - delete_custom_fields(get_supplier_quotation_custom_fields()) + # Creating BEAMS specific Roles + create_custom_roles(get_beams_roles()) + # Creating BEAMS specific Translations + create_translations(get_custom_translations()) -def delete_custom_fields(custom_fields: dict): - ''' - Method to Delete custom fields - args: - custom_fields: a dict like `{'Task': [{fieldname: 'your_fieldname', ...}]}` - ''' - for doctype, fields in custom_fields.items(): - frappe.db.delete( - "Custom Field", - { - "fieldname": ("in", [field["fieldname"] for field in fields]), - "dt": doctype, - }, - ) - frappe.clear_cache(doctype=doctype) + # Creating BEAMS specific Email Template + create_email_templates(get_email_templates()) -def get_shift_assignment_custom_fields(): - ''' - Custom fields that need to be added to the Shift Assignment DocType - ''' - return { - "Shift Assignment": [ - { - "fieldname": "roster_type", - "fieldtype": "Select", - "label": "Roster Type", - "options":"\nRegular\nDouble Shift", - "insert_after": "shift_type" - }, - { - "fieldname": "user_id", - "label": "User ID", - "fieldtype": "Data", - "insert_after": "end_date", - "options": "Email", - "hidden": 1 - - } - - ] - } - -def get_hd_ticket_type_custom_fields(): - ''' - Custom fields that need to be added to the HD Ticket Type DocType - ''' - return { - "HD Ticket Type": [ - { - "fieldname": "team_name", - "fieldtype": "Link", - "label": "Team Name", - "options":"HD Team", - "insert_after": "is_system" - } - ] - } -def get_Payroll_Settings_custom_fields(): - ''' - Custom fields that need to be added to the Payroll Settings Doctype - ''' - return { - "Payroll Settings": [ - { - "fieldname": "provident_fund_section", - "fieldtype": "Section Break", - "label": "Provident Fund", - "insert_after": "show_leave_balances_in_salary_slip" - }, - { - "fieldname": "employer_pf_contribution", - "label": "Employer PF Contribution", - "fieldtype": "Percent", - "insert_after": "provident_fund_section" - }, - { - "fieldname": "column_break_pf", - "fieldtype": "Column Break", - "insert_after": "employer_pf_contribution" - }, - { - "fieldname": "pf_expense_account", - "label": "PF Expense Account", - "fieldtype": "Link", - "options": "Account", - "insert_after": "column_break_pf" - }, - { - "fieldname": "esi_section", - "fieldtype": "Section Break", - "label": "Employees State Insurance", - "insert_after": "pf_expense_account" - }, - { - "fieldname": "esi_employer_contribution", - "label": "ESI Employer Contribution", - "fieldtype": "Percent", - "insert_after": "esi_section" - }, - { - "fieldname": "column_break_esi", - "fieldtype": "Column Break", - "insert_after": "esi_employer_contribution" - }, - { - "fieldname": "esi_expense_account", - "label": "ESI Expense Account", - "fieldtype": "Link", - "options": "Account", - "insert_after": "column_break_esi" - } - ] - } +def after_migrate(): + after_install() -def get_project_custom_fields(): - ''' - Custom fields that need to be added to the Project Doctype - ''' - return { - "Project": [ - { - "fieldname": "program_section", - "fieldtype": "Section Break", - "label": "Program Details", - "collapsible": 1, - "insert_after": "sales_order" - }, - { - "fieldname": "program_request", - "label": "Program Request", - "fieldtype": "Link", - "options": "Program Request", - "insert_after": "program_section" - }, - { - "fieldname": "bureau", - "label": "Bureau", - "fieldtype": "Link", - "options":"Bureau", - "insert_after": "expected_revenue", - "fetch_from": "program_request.bureau", - "read_only": 1 - - }, - { - "fieldname": "column_break_program", - "fieldtype": "Column Break", - "insert_after": "generates_revenue" - }, - { - "fieldname": "program_type", - "label": "Program Type", - "fieldtype": "Link", - "options": "Program Type", - "insert_after": "column_break_program", - "fetch_from": "program_request.program_type", - "read_only": 1 - }, - { - "fieldname": "budget_expense_types", - "fieldtype": "Table MultiSelect", - "label": "Budget Expense Types", - 'options':"Project Expense Type", - "insert_after": "program_type" - }, - { - "fieldname": "generates_revenue", - "fieldtype": "Check", - "label": "Generates Revenue", - "read_only": 1, - "fetch_from": "program_request.generates_revenue", - "insert_after": "program_request" - }, - { - "fieldname": "expected_revenue", - "fieldtype": "Float", - "label": "Expected Revenue", - "read_only": 1, - "fetch_from": "program_request.expected_revenue", - "insert_after": "generates_revenue" - }, - { - "fieldname": "allocated_resources_details_section", - "fieldtype": "Section Break", - "label": " Allocated Resource Details", - "collapsible": 1, - "insert_after": "program_request" - }, - { - "fieldname": "allocated_manpower_details", - "fieldtype": "Table", - "label": "Allocated Manpower Detail", - "options":"Allocated Manpower Detail", - "insert_after":"allocated_item_details" - }, - { - "fieldname": "allocated_item_details", - "fieldtype": "Table", - "label": "Allocated Item Details", - "options":"Required Items Detail", - "insert_after":"allocated_resources_details_section" - }, - { - "fieldname": "approved_budget", - "fieldtype": "Currency", - "label": "Approved Budget", - "insert_after":"budget_expense_types", - "read_only": 1 - - }, - { - "fieldname": "estimated_budget", - "fieldtype": "Currency", - "label": "Estimated Budget", - "fetch_from": "program_request.estimated_budget", - "insert_after":"approved_budget", - "read_only": 1 - }, - { - "fieldname": "description", - "fieldtype": "Small Text", - "label": "Description", - "fetch_from":"program_request.description", - "insert_after": "bureau" - }, - { - "fieldname": "requirements", - "fieldtype": "Text Editor", - "label": "Requirements", - "fetch_from":"program_request.requirements", - "insert_after": "description" - }, - { - "fieldname": "location", - "fieldtype": "Link", - "label": "Location", - "options":"Location", - "fetch_from":"program_request.location", - "insert_after": "department", - "fetch_on_save_if_empty":1 - }, - { - "fieldname": "requirements_details", - "fieldtype": "Section Break", - "label": "Requirements Details", - "collapsible": 1, - "insert_after": "estimated_budget" - }, - { - "fieldname": "required_items", - "fieldtype": "Table", - "label": "Required Items", - "options": "Required Items Table", - "insert_after": "requirements_details" - }, - { - "fieldname": "required_manpower_details", - "fieldtype": "Table", - "label": "Required Manpower Details", - "options": "Required Manpower Details", - "insert_after": "required_items" - }, - { - "fieldname": "required_vehicle_details", - "fieldtype": "Table", - "label": "Required Vehicle Details", - "options": "Required Vehicle Details", - "insert_after": "required_manpower_details" - }, - { - "fieldname": "allocated_vehicle_details", - "fieldtype": "Table", - "label": "Allocated Vehicle Details", - "options": "Allocated Vehicle Details", - "insert_after": "allocated_manpower_details" - }, - { - "fieldname": "asset_location", - "fieldtype": "Link", - "label": "Asset Location", - "options":"Location", - "insert_after": "estimated_budget" - } - - - - ] - } +def before_uninstall(): + delete_custom_fields(get_customer_custom_fields()) + delete_custom_fields(get_sales_invoice_custom_fields()) + delete_custom_fields(get_quotation_custom_fields()) + delete_custom_fields(get_purchase_invoice_custom_fields()) + delete_custom_fields(get_supplier_custom_fields()) + delete_custom_fields(get_item_custom_fields()) + delete_custom_fields(get_purchase_order_custom_fields()) + delete_custom_fields(get_driver_custom_fields()) + delete_custom_fields(get_material_request_custom_fields()) + delete_custom_fields(get_sales_order_custom_fields()) + delete_custom_fields(get_employee_advance_custom_fields()) + delete_custom_fields(get_employee_custom_fields()) + delete_custom_fields(get_journal_entry_custom_fields()) + delete_custom_fields(get_voucher_entry_custom_fields()) + delete_custom_fields(get_contract_custom_fields()) + delete_custom_fields(get_department_custom_fields()) + delete_custom_fields(get_job_requisition_custom_fields()) + delete_custom_fields(get_job_opening_custom_fields()) + delete_custom_fields(get_job_applicant_custom_fields()) + delete_custom_fields(get_budget_custom_fields()) + delete_custom_fields(get_expected_skill_set_custom_fields()) + delete_custom_fields(get_interview_round_custom_fields()) + delete_custom_fields(get_skill_assessment_custom_fields()) + delete_custom_fields(get_job_offer_custom_fields()) + delete_custom_fields(get_company_custom_fields()) + delete_custom_fields(get_training_event_employee_custom_fields()) + delete_custom_fields(get_attendance_request_custom_fields()) + delete_custom_fields(get_shift_assignment_custom_fields()) + delete_custom_fields(get_leave_type_custom_fields()) + delete_custom_fields(get_leave_application_custom_fields()) + delete_custom_fields(get_employee_performance_feedback()) + delete_custom_fields(get_employment_type()) + delete_custom_fields(get_appointment_letter()) + delete_custom_fields(get_employment_type_custom_fields()) + delete_custom_fields(get_employee_separation_custom_fields()) + delete_custom_fields(get_appraisal_template_custom_fields()) + delete_custom_fields(get_employee_feedback_rating_custom_fields()) + delete_custom_fields(get_appraisal_custom_fields()) + delete_custom_fields(get_appraisal_kra_custom_fields()) + delete_custom_fields(get_event_custom_fields()) + delete_custom_fields(get_project_custom_fields()) + delete_custom_fields(get_Payroll_Settings_custom_fields()) + delete_custom_fields(get_asset_custom_fields()) + delete_custom_fields(get_vehicle_custom_fields()) + delete_custom_fields(get_interview_custom_fields()) + delete_custom_fields(get_item_group_custom_fields()) + delete_custom_fields(get_hr_settings_custom_fields()) + delete_custom_fields(get_asset_category_custom_fields()) + delete_custom_fields(get_asset_movement_custom_fields()) -def get_employment_type_custom_fields(): - ''' - Custom fields that need to be added to the Employment Type DocType - ''' - return { - "Employment Type": [ - { - "fieldname": "penalty_leave_type", - "fieldtype": "Link", - "label": "Penalty Leave Type", - "options": "Leave Type", - "insert_after": "employee_type_name" - } - ] - } -def get_event_custom_fields(): - ''' - Custom fields to be added to the Event Doctype - ''' - return { - "Event": [ - { - "fieldname": "contribution_of_employee", - "fieldtype": "Small Text", - "label": "Contribution of Employee", - "insert_after": "description", - "depends_on": "eval:doc.event_category == 'One to One Meeting'" - }, - { - "fieldname": "improvement_of_employee", - "fieldtype": "Small Text", - "label": "Areas of Improvement of Employee", - "insert_after": "contribution_of_employee", - "depends_on": "eval:doc.event_category == 'One to One Meeting'" - }, - { - "fieldname": "training_needs_of_employee", - "fieldtype": "Small Text", - "label": "Training Needs of Employee", - "insert_after": "improvement_of_employee", - "depends_on": "eval:doc.event_category == 'One to One Meeting'" - }, - { - "fieldname": "is_employee_eligible_for_promotion", - "fieldtype": "Select", - "label": "Is Employee Eligible for Promotion", - "options": "\nYes\nNo", - "insert_after": "training_needs_of_employee", - "depends_on": "eval:doc.event_category == 'One to One Meeting'" - }, - { - "fieldname": "remarks_for_promotion", - "fieldtype": "Small Text", - "label": "Remarks", - "insert_after": "is_employee_eligible_for_promotion", - "depends_on": "eval:(doc.is_employee_eligible_for_promotion == 'Yes' || doc.is_employee_eligible_for_promotion == 'No') && doc.event_category == 'One to One Meeting'" - }, - { - "fieldname": "appraisal_reference", - "fieldtype": "Link", - "label": "Appraisal Reference", - "options": "Appraisal", - "insert_after": "status" - }, - { - "fieldname": "assign_service_unit", - "fieldtype": "Check", - "label": "Assign Service Unit", - "insert_after": "add_video_conferencing" - }, - { - "fieldname": "meeting_room", - "fieldtype": "Link", - "label": "Meeting Room", - "options": "Service Unit", - "depends_on": "eval:doc.assign_service_unit == 1", - "mandatory_depends_on": "eval:doc.assign_service_unit == 1", - "insert_after": "assign_service_unit" - }, - { - "fieldname": "section_break_epd", - "fieldtype": "Section Break", - "label": " ", - "insert_after": "sunday" - }, - { - "fieldname": "external_participants", - "fieldtype": "Table", - "label": "External Participants", - "options": "External Participants Detail", - "insert_after": "section_break_epd" - }, - { - "fieldname": "reason_for_rejection", - "fieldtype": "Small Text", - "label": "Reason for Rejection", - "insert_after": "repeat_this_event" - } - ] - } +def delete_custom_fields(custom_fields: dict): + """ + Method to Delete custom fields + args: + custom_fields: a dict like `{'Task': [{fieldname: 'your_fieldname', ...}]}` + """ + for doctype, fields in custom_fields.items(): + frappe.db.delete( + "Custom Field", + { + "fieldname": ("in", [field["fieldname"] for field in fields]), + "dt": doctype, + }, + ) + frappe.clear_cache(doctype=doctype) -def get_leave_application_custom_fields(): - ''' - Custom fields that need to be added to the Leave Application Doctype - ''' - return { - "Leave Application": [ - { - "fieldname": "medical_certificate", - "fieldtype": "Attach", - "label": "Medical Certificate", - "hidden": 1, - "insert_after": "leave_type" - } - - ] - } -def get_attendance_request_custom_fields(): - """ - Custom fields that need to be added to the Attendance Request DocType. - """ - return { - "Attendance Request": [ - { - "fieldname": "reports_to", - "fieldtype": "Link", - "label": "Reports To", - "options": "Employee", - "fetch_from":"employee.reports_to", - "insert_after": "reason" - }, - { - "fieldname": "reports_to_name", - "fieldtype": "Data", - "label": "Reports To Name", - "insert_after": "reports_to", - "fetch_from": "reports_to.employee_name" - }, - { - "fieldname": "reports_to_user", - "fieldtype": "Link", - "label": "Reports To User", - "options": "User", - "insert_after": "reports_to_name", - "fetch_from": "reports_to.user_id" - } - ] - } -def get_customer_custom_fields(): - ''' - Custom fields that need to be added to the Customer Doctype - ''' - return { - "Customer": [ - { - "fieldname": "msme_status", - "fieldtype": "Select", - "label": "MSME Status", - "options":"\nMSME\nNon-MSME", - "insert_after": "customer_group" - }, - { - "fieldname": "msme_number", - "fieldtype": "Int", - "label": "MSME Number", - "insert_after":"msme_status" - }, - { - "fieldname": "region", - "fieldtype": "Link", - "label": "Region", - "options": "Region", - "reqd": 1, - "insert_after": "msme_number" - }, - { - "fieldname": "is_agent", - "fieldtype": "Check", - "label": "Is Agency", - "insert_after": "region" - }, - { - "fieldname": "is_edited", - "fieldtype": "Check", - "label": "Is Edited", - "hidden": 1, - "default": 0, - "no_copy":1, - "insert_after": "is_agent" - } - ] - } +def get_shift_assignment_custom_fields(): + """ + Custom fields that need to be added to the Shift Assignment DocType + """ + return { + "Shift Assignment": [ + { + "fieldname": "roster_type", + "fieldtype": "Select", + "label": "Roster Type", + "options": "\nRegular\nDouble Shift", + "insert_after": "shift_type", + }, + { + "fieldname": "user_id", + "label": "User ID", + "fieldtype": "Data", + "insert_after": "end_date", + "options": "Email", + "hidden": 1, + }, + ] + } -def get_department_custom_fields(): - ''' - Custom fields that need to be added to the Department Doctype - ''' - return { - "Department": [ - { - "fieldname": "head_of_department", - "fieldtype": "Link", - "label": "Head Of Department", - "options":"Employee", - "insert_after": "department_name" - }, - { - "fieldname": "head_of_department_name", - "fieldtype": "Data", - "label": "Head Of Department(Name)", - "insert_after": "head_of_department", - "fetch_from": "head_of_department.employee_name", - "read_only": 1 - }, - { - "fieldname": "abbreviation", - "fieldtype": "Data", - "label": "Abbreviation", - "reqd":1, - "unique":1, - "insert_after": "head_of_department_name" - }, - { - "fieldname": "threshold_amount", - "fieldtype": "Float", - "label": "Threshold Amount", - "insert_after": "parent_department" - }, - { - "fieldname": "finance_group", - "fieldtype": "Link", - "label": "Finance Group", - "options":"Finance Group", - "insert_after": "company" - } - ] - } -def get_driver_custom_fields(): - ''' - Custom fields that need to be added to the Driver DocType - ''' - return { - "Driver": [ - { - "fieldname": "is_internal", - "fieldtype": "Check", - "label": "Is Internal", - "insert_after": "transporter", - "reqd": 0 - }, - ] - } +def get_hd_ticket_type_custom_fields(): + """ + Custom fields that need to be added to the HD Ticket Type DocType + """ + return { + "HD Ticket Type": [ + { + "fieldname": "team_name", + "fieldtype": "Link", + "label": "Team Name", + "options": "HD Team", + "insert_after": "is_system", + } + ] + } -def get_asset_custom_fields(): - ''' - Custom fields that need to be added to the Asset DocType - ''' - return { - "Asset": [ - { - "fieldname": "bureau", - "fieldtype": "Link", - "options":"Bureau", - "label": "Bureau", - "insert_after": "location" - }, - { - "fieldname": "in_transit", - "fieldtype": "Check", - "label": "In Transit", - "insert_after": "is_composite_asset", - "allow_on_submit": 1, - "read_only":1 - - }, - { - "fieldname": "warranty_details_section", - "fieldtype": "Section Break", - "label": "Warranty Details Section", - "insert_after": "comprehensive_insurance", - "collapsible": 1 - }, - { - "fieldname": "warranty_reference_no", - "fieldtype": "Data", - "label": "Warranty Reference No", - "insert_after": "warranty_details_section" - }, - { - "fieldname": "warranty_till", - "fieldtype": "Date", - "label": "Warranty Till", - "insert_after": "warranty_reference_no" - }, - { - "fieldname": "qr_code", - "fieldtype": "Attach Image", - "label": "QR code", - "insert_after": "department" - }, - { - "fieldname": "asset_details", - "fieldtype": "Attach Image", - "label": "Asset Details", - "insert_after": "qr_code" - }, - { - "fieldname": "room", - "fieldtype": "Link", - "label": "Room", - "options":"Service Unit", - "allow_on_submit": 1, - "insert_after": "journal_entry_for_scrap" - }, - { - "fieldname": "shelf", - "fieldtype": "Link", - "label": "Shelf", - "options":"Shelf", - "allow_on_submit": 1, - "insert_after": "room" - }, - { - "fieldname": "row", - "fieldtype": "Link", - "label": "Row", - "options":"Row", - "allow_on_submit": 1, - "insert_after": "shelf" - }, - { - "fieldname": "bin", - "fieldtype": "Link", - "label": "Bin", - "options":"Container", - "allow_on_submit": 1, - "insert_after": "row" - } - ] - } -def get_job_offer_custom_fields(): - ''' - Custom fields that need to be added to the Job Offer DocType - ''' - return { - "Job Offer": [ - { - "fieldname": "job_proposal", - "fieldtype": "Link", - "label": "Job Proposal", - "options":"Job Proposal", - "insert_after": "applicant_email", - "read_only":1 - }, - { - "fieldname": "ctc", - "fieldtype": "Currency", - "label": "CTC", - "insert_after": "job_proposal", - "fetch_from" : "job_proposal.proposed_ctc" - } - ] - } +def get_Payroll_Settings_custom_fields(): + """ + Custom fields that need to be added to the Payroll Settings Doctype + """ + return { + "Payroll Settings": [ + { + "fieldname": "provident_fund_section", + "fieldtype": "Section Break", + "label": "Provident Fund", + "insert_after": "show_leave_balances_in_salary_slip", + }, + { + "fieldname": "employer_pf_contribution", + "label": "Employer PF Contribution", + "fieldtype": "Percent", + "insert_after": "provident_fund_section", + }, + { + "fieldname": "column_break_pf", + "fieldtype": "Column Break", + "insert_after": "employer_pf_contribution", + }, + { + "fieldname": "pf_expense_account", + "label": "PF Expense Account", + "fieldtype": "Link", + "options": "Account", + "insert_after": "column_break_pf", + }, + { + "fieldname": "esi_section", + "fieldtype": "Section Break", + "label": "Employees State Insurance", + "insert_after": "pf_expense_account", + }, + { + "fieldname": "esi_employer_contribution", + "label": "ESI Employer Contribution", + "fieldtype": "Percent", + "insert_after": "esi_section", + }, + { + "fieldname": "column_break_esi", + "fieldtype": "Column Break", + "insert_after": "esi_employer_contribution", + }, + { + "fieldname": "esi_expense_account", + "label": "ESI Expense Account", + "fieldtype": "Link", + "options": "Account", + "insert_after": "column_break_esi", + }, + ] + } -def get_purchase_order_custom_fields(): - ''' - Custom fields that need to be added to the Purchase Order DocType - ''' - return { - "Purchase Order": [ - { - "fieldname": "is_budget_exceed", - "fieldtype": "Check", - "label": "Is Budget Exceed", - "insert_after": "items_section", - "read_only":1, - "no_copy":1, - "depends_on": "eval:doc.is_budget_exceed == 1" - - }, - { - "fieldname": "attach", - "fieldtype": "Attach", - "label": "Attachments", - "insert_after": "base_net_total" - } - ], - "Purchase Order Item": [ - { - "fieldname": "reference_doctype", - "fieldtype": "Link", - "label": "Reference DocType", - "options":"DocType", - "insert_after": "blanket_order_rate" - }, - { - - "fieldname": "reference_document", - "fieldtype": "Dynamic Link", - "label": "Reference Document", - "options":"reference_doctype", - "insert_after": "reference_doctype" - } - ] - } -def get_budget_custom_fields(): - ''' - Custom fields that need to be added to the Budget DocType - ''' - return { - "Budget": [ - { - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options":"Department", - "reqd": 1, - "insert_after": "company" - }, - { - "fieldname": "finance_group", - "fieldtype": "Link", - "label": "Finance Group", - "options":"Finance Group", - "insert_after": "department", - "read_only":1, - "fetch_from": "department.finance_group" - }, - { - "fieldname": "division", - "fieldtype": "Link", - "label": "Division", - "options":"Division", - "reqd": 1, - "insert_after": "department" - }, - { - "fieldname": "region", - "fieldtype": "Link", - "label": "Region", - "options":"Region", - "insert_after": "budget_template" - }, - { - "fieldname": "budget_template", - "fieldtype": "Link", - "label": "Budget Template", - "options":"Budget Template", - "insert_after": "fiscal_year" - }, - { - "fieldname": "rejection_feedback", - "fieldtype": "Table", - "label": "Rejection Feedback", - "options":"Rejection Feedback", - "insert_after": "december", - "depends_on": "eval: doc.workflow_state.includes('Rejected')" - }, - { - "fieldname": "total_amount", - "fieldtype": "Currency", - "label": "Total Amount", - "read_only": 1, - "insert_after": "region", - "options": "company_currency" - }, - { - "fieldname": "budget_accounts_custom", - "fieldtype": "Table", - "label": "Budget Accounts", - "options": "Budget Account", - "insert_after": "accounts" - }, - { - "fieldname": "budget_accounts_hr", - "fieldtype": "Table", - "label": "Budget Accounts(HR Overheads)", - "options": "Budget Account", - "insert_after": "budget_accounts_custom" - }, - { - "fieldname": "default_currency", - "fieldtype": "Link", - "label": "Default Currency", - "options": "Currency", - "read_only": 1, - "hidden":1, - "insert_after": "budget_accounts_hr", - "default": "INR" - }, - { - "fieldname": "company_currency", - "fieldtype": "Link", - "label": "Company Currency", - "options": "Currency", - "read_only": 1, - "hidden":1, - "insert_after": "default_currency", - "fetch_from": "company.default_currency" - }, - ], - "Budget Account": [ - { - "fieldname": "cost_head", - "fieldtype": "Link", - "label": "Cost Head", - "options":"Cost Head", - "insert_before": "cost_subhead", - "in_list_view":1 - }, - { - "fieldname": "cost_subhead", - "fieldtype": "Link", - "label": "Cost Sub Head", - "options":"Cost Subhead", - "insert_after": "cost_head", - "in_list_view":1 - }, - { - "fieldname": "cost_category", - "fieldtype": "Link", - "label": "Cost Category", - "options":"Cost Category", - "insert_after": "account", - "in_list_view":1 - }, - { - "fieldname": "column_break_cd", - "fieldtype": "Column Break", - "label": " ", - "insert_after": "cost_category" - }, - { - "fieldname": "cost_description", - "fieldtype": "Small Text", - "label": "Cost Description", - "insert_after": "column_break_cd" - }, - { - "fieldname": "equal_monthly_distribution", - "fieldtype": "Check", - "label": "Equal Monthly Distribution ", - "insert_after": "cost_description" - }, - { - "fieldname": "section_break_ab", - "fieldtype": "Section Break", - "label": "Monthly Amount Distribution", - "insert_after": "budget_amount" - }, - { - "fieldname": "january", - "fieldtype": "Currency", - "label": "January", - "insert_after": "section_break_ab" - }, - { - "fieldname": "february", - "fieldtype": "Currency", - "label": "February", - "insert_after": "january" - }, - { - "fieldname": "march", - "fieldtype": "Currency", - "label": "March", - "insert_after": "february" - }, - { - "fieldname": "april", - "fieldtype": "Currency", - "label": "April", - "insert_after": "march" - }, - { - "fieldname": "column_break_bc", - "fieldtype": "Column Break", - "label": " ", - "insert_after": "april" - }, - { - "fieldname": "may", - "fieldtype": "Currency", - "label": "May", - "insert_after": "column_break_bc" - }, - { - "fieldname": "june", - "fieldtype": "Currency", - "label": "June", - "insert_after": "may" - }, - { - "fieldname": "july", - "fieldtype": "Currency", - "label": "July", - "insert_after": "june" - }, - { - "fieldname": "august", - "fieldtype": "Currency", - "label": "August", - "insert_after": "july" - }, - { - "fieldname": "column_break_ab", - "fieldtype": "Column Break", - "label": " ", - "insert_after": "august" - }, - { - "fieldname": "september", - "fieldtype": "Currency", - "label": "September", - "insert_after": "column_break_ab" - }, - { - "fieldname": "october", - "fieldtype": "Currency", - "label": "October", - "insert_after": "september" - }, - { - "fieldname": "november", - "fieldtype": "Currency", - "label": "November", - "insert_after": "october" - }, - { - "fieldname": "december", - "fieldtype": "Currency", - "label": "December", - "insert_after": "november" - }, - { - "fieldname": "section_break_inr", - "fieldtype": "Section Break", - "label": "Monthly Amount Distribution (INR)", - "insert_after": "december", - "collapsible": 1, - "read_only": 1 - }, - { - "fieldname": "january_inr", - "fieldtype": "Currency", - "label": "January (INR)", - "insert_after": "section_break_inr", - "read_only": 1, - "options": "default_currency" - }, - { - "fieldname": "february_inr", - "fieldtype": "Currency", - "label": "February (INR)", - "insert_after": "january_inr", - "read_only": 1, - "options": "default_currency" - }, - { - "fieldname": "march_inr", - "fieldtype": "Currency", - "label": "March (INR)", - "insert_after": "february_inr", - "read_only": 1, - "options": "default_currency" - }, - { - "fieldname": "april_inr", - "fieldtype": "Currency", - "label": "April (INR)", - "insert_after": "march_inr", - "read_only": 1, - "options": "default_currency" - }, - { - "fieldname": "column_break_zz1", - "fieldtype": "Column Break", - "label": " ", - "insert_after": "april_inr" - }, - { - "fieldname": "may_inr", - "fieldtype": "Currency", - "label": "May (INR)", - "insert_after": "column_break_zz1", - "read_only": 1, - "options": "default_currency" - }, - { - "fieldname": "june_inr", - "fieldtype": "Currency", - "label": "June (INR)", - "insert_after": "may_inr", - "read_only": 1, - "options": "default_currency" - }, - { - "fieldname": "july_inr", - "fieldtype": "Currency", - "label": "July (INR)", - "insert_after": "june_inr", - "read_only": 1, - "options": "default_currency" - }, - { - "fieldname": "august_inr", - "fieldtype": "Currency", - "label": "August (INR)", - "insert_after": "july_inr", - "read_only": 1, - "options": "default_currency" - }, - { - "fieldname": "column_break_zz2", - "fieldtype": "Column Break", - "label": " ", - "insert_after": "july_inr" - }, - { - "fieldname": "september_inr", - "fieldtype": "Currency", - "label": "September (INR)", - "insert_after": "column_break_zz2", - "read_only": 1, - "options": "default_currency" - }, - { - "fieldname": "october_inr", - "fieldtype": "Currency", - "label": "October (INR)", - "insert_after": "september_inr", - "read_only": 1, - "options": "default_currency" - }, - { - "fieldname": "november_inr", - "fieldtype": "Currency", - "label": "November (INR)", - "insert_after": "october_inr", - "read_only": 1, - "options": "default_currency" - }, - { - "fieldname": "december_inr", - "fieldtype": "Currency", - "label": "December (INR)", - "insert_after": "november_inr", - "read_only": 1, - "options": "default_currency" - }, - { - "fieldname": "budget_amount_inr", - "fieldtype": "Currency", - "label": "Budget Amount (INR)", - "insert_after": "budget_amount", - "options": "default_currency", - "read_only": 1 - }, - ] - } +def get_project_custom_fields(): + """ + Custom fields that need to be added to the Project Doctype + """ + return { + "Project": [ + { + "fieldname": "program_section", + "fieldtype": "Section Break", + "label": "Program Details", + "collapsible": 1, + "insert_after": "sales_order", + }, + { + "fieldname": "program_request", + "label": "Program Request", + "fieldtype": "Link", + "options": "Program Request", + "insert_after": "program_section", + }, + { + "fieldname": "bureau", + "label": "Bureau", + "fieldtype": "Link", + "options": "Bureau", + "insert_after": "expected_revenue", + "fetch_from": "program_request.bureau", + "read_only": 1, + }, + { + "fieldname": "column_break_program", + "fieldtype": "Column Break", + "insert_after": "generates_revenue", + }, + { + "fieldname": "program_type", + "label": "Program Type", + "fieldtype": "Link", + "options": "Program Type", + "insert_after": "column_break_program", + "fetch_from": "program_request.program_type", + "read_only": 1, + }, + { + "fieldname": "budget_expense_types", + "fieldtype": "Table MultiSelect", + "label": "Budget Expense Types", + "options": "Project Expense Type", + "insert_after": "program_type", + }, + { + "fieldname": "generates_revenue", + "fieldtype": "Check", + "label": "Generates Revenue", + "read_only": 1, + "fetch_from": "program_request.generates_revenue", + "insert_after": "program_request", + }, + { + "fieldname": "expected_revenue", + "fieldtype": "Float", + "label": "Expected Revenue", + "read_only": 1, + "fetch_from": "program_request.expected_revenue", + "insert_after": "generates_revenue", + }, + { + "fieldname": "allocated_resources_details_section", + "fieldtype": "Section Break", + "label": " Allocated Resource Details", + "collapsible": 1, + "insert_after": "program_request", + }, + { + "fieldname": "allocated_manpower_details", + "fieldtype": "Table", + "label": "Allocated Manpower Detail", + "options": "Allocated Manpower Detail", + "insert_after": "allocated_item_details", + }, + { + "fieldname": "allocated_item_details", + "fieldtype": "Table", + "label": "Allocated Item Details", + "options": "Required Items Detail", + "insert_after": "allocated_resources_details_section", + }, + { + "fieldname": "approved_budget", + "fieldtype": "Currency", + "label": "Approved Budget", + "insert_after": "budget_expense_types", + "read_only": 1, + }, + { + "fieldname": "estimated_budget", + "fieldtype": "Currency", + "label": "Estimated Budget", + "fetch_from": "program_request.estimated_budget", + "insert_after": "approved_budget", + "read_only": 1, + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description", + "fetch_from": "program_request.description", + "insert_after": "bureau", + }, + { + "fieldname": "requirements", + "fieldtype": "Text Editor", + "label": "Requirements", + "fetch_from": "program_request.requirements", + "insert_after": "description", + }, + { + "fieldname": "location", + "fieldtype": "Link", + "label": "Location", + "options": "Location", + "fetch_from": "program_request.location", + "insert_after": "department", + "fetch_on_save_if_empty": 1, + }, + { + "fieldname": "requirements_details", + "fieldtype": "Section Break", + "label": "Requirements Details", + "collapsible": 1, + "insert_after": "estimated_budget", + }, + { + "fieldname": "required_items", + "fieldtype": "Table", + "label": "Required Items", + "options": "Required Items Table", + "insert_after": "requirements_details", + }, + { + "fieldname": "required_manpower_details", + "fieldtype": "Table", + "label": "Required Manpower Details", + "options": "Required Manpower Details", + "insert_after": "required_items", + }, + { + "fieldname": "required_vehicle_details", + "fieldtype": "Table", + "label": "Required Vehicle Details", + "options": "Required Vehicle Details", + "insert_after": "required_manpower_details", + }, + { + "fieldname": "allocated_vehicle_details", + "fieldtype": "Table", + "label": "Allocated Vehicle Details", + "options": "Allocated Vehicle Details", + "insert_after": "allocated_manpower_details", + }, + { + "fieldname": "asset_location", + "fieldtype": "Link", + "label": "Asset Location", + "options": "Location", + "insert_after": "estimated_budget", + }, + ] + } -def get_sales_invoice_custom_fields(): - ''' - Custom fields that need to be added to the Sales Invoice Doctype - ''' - return { - "Sales Invoice": [ - { - "fieldname": "actual_customer", - "fieldtype": "Link", - "label": "Actual Customer", - "options": "Customer", - "depends_on": "eval:doc.is_agent == 1", - "insert_after": "is_agent" - }, - { - "fieldname": "is_agent", - "fieldtype": "Check", - "label": "Is Agency", - "read_only":1, - "fetch_from": "customer.is_agent", - "depends_on": "eval:doc.is_agent", - "insert_after": "customer" - }, - { - "fieldname": "actual_customer_group", - "fieldtype": "Link", - "label": "Actual Customer Group", - "options": "Customer Group", - "read_only": 1, - "fetch_from": "actual_customer.customer_group", - "insert_after": "actual_customer" - }, - { - "fieldname": "include_in_ibf", - "fieldtype": "Check", - "label": "Include in IBF", - "read_only": 1, - "insert_after": "actual_customer_group" - }, - { - "fieldname": "region", - "fieldtype": "Link", - "options": "Region", - "label": "Region", - "insert_after": "is_reverse_charge" - }, - { - "fieldname": "executive", - "fieldtype": "Link", - "options": "Employee", - "label": "Executive", - "insert_after": "due_date" - }, - { - "fieldname": "executive_name", - "fieldtype": "Data", - "label": "Executive Name", - "insert_after": "executive", - "fetch_from": "executive.employee_name", - "read_only": 1 - }, - { - "fieldname": "is_barter_invoice", - "fieldtype": "Check", - "label": "Is Barter Invoice", - "read_only": 1, - "insert_after": "include_in_ibf", - "fetch_from": "reference_id.is_barter" - }, - { - "fieldname": "reference_id", - "fieldtype": "Link", - "options":"Quotation", - "label": "Quotation", - "read_only":1, - "insert_after": "naming_series" - }, - { - "fieldname": "sales_type", - "fieldtype": "Link", - "label": "Sales Type", - "insert_after": "naming_series", - "options": "Sales Type" - } - ] - } -def get_quotation_custom_fields(): - ''' - Custom fields that need to be added to the Quotation DocType - ''' - return { - "Quotation": [ - { - "fieldname": "is_agent", - "fieldtype": "Check", - "label": "Is Agency", - "read_only":1, - "fetch_from": "party_name.is_agent", - "depends_on": "eval:doc.is_agent", - "insert_after": "party_name" - }, - { - "fieldname": "actual_customer", - "fieldtype": "Link", - "label": "Actual Customer", - "options": "Customer", - "depends_on": "eval:doc.is_agent == 1", - "insert_after": "is_agent" - }, - { - "fieldname": "actual_customer_group", - "fieldtype": "Link", - "label": "Actual Customer Group", - "options": "Customer Group", - "read_only": 1, - "fetch_from": "actual_customer.customer_group", - "insert_after": "actual_customer" - }, - { - "fieldname": "customer_purchase_order_reference", - "fieldtype": "Data", - "label": "Customer Purchase Order Reference", - "insert_after": "valid_till" - }, - { - "fieldname": "executive", - "fieldtype": "Link", - "label": "Executive", - "options":"Employee", - "insert_after": "customer_purchase_order_reference" - }, - { - "fieldname": "executive_name", - "fieldtype": "Data", - "label": "Executive Name", - "insert_after": "executive", - "fetch_from": "executive.employee_name", - "read_only":1 - }, - { - "fieldname": "is_barter", - "fieldtype": "Check", - "label": "Is Barter", - "insert_after": "amended_from" - }, - { - "fieldname": "purchase_order", - "fieldtype": "Link", - "label": "Purchase Order", - "insert_after": "is_barter", - "depends_on": "eval:doc.is_barter", - "options": "Purchase Order" - }, - { - "fieldname": "sales_type", - "fieldtype": "Link", - "label": "Default Sales Type", - "insert_after": "purchase_order", - "options": "Sales Type" - }, - { - "fieldname": "region", - "fieldtype": "Link", - "label": "Region", - "insert_after": "customer_name", - "options": "Region" - }, - { - "fieldname": "albatross_details_section", - "fieldtype": "Section Break", - "label": "Albatross Details", - "insert_after": "is_barter" - }, - { - "fieldname": "albatross_ro_id", - "fieldtype": "Data", - "label": "Albatross RO ID", - "insert_after": "albatross_details_section", - "read_only":1 - }, - { - "fieldname": "ro_no", - "fieldtype": "Data", - "label": "RO No", - "insert_after": "albatross_ro_id", - "read_only":1 - }, - { - "fieldname": "ro_date", - "fieldtype": "Date", - "label": "RO Date", - "insert_after": "ro_no", - "read_only":1 - }, - { - "fieldname": "ro_option", - "fieldtype": "Data", - "label": "RO Option", - "insert_after": "ro_date", - "read_only":1 - }, - { - "fieldname": "region_revenue_percentage", - "fieldtype": "Percent", - "label": "Region Revenue Percentage", - "insert_after": "ro_option", - "read_only":1 - }, - { - "fieldname": "albatross_column_break", - "fieldtype": "Column Break", - "label": "", - "insert_after": "region_revenue_percentage" - }, - { - "fieldname": "product_name", - "fieldtype": "Data", - "label": "Product Name", - "insert_after": "albatross_column_break", - "read_only":1 - }, - { - "fieldname": "program_name", - "fieldtype": "Data", - "label": "Program Name", - "insert_after": "product_name", - "read_only":1 - }, - { - "fieldname": "no_of_eps", - "fieldtype": "Data", - "label": "No of Episodes", - "insert_after": "program_name", - "read_only":1 - }, - { - "fieldname": "commission_per", - "fieldtype": "Float", - "label": "Commission Per", - "insert_after": "no_of_eps", - "read_only":1 - }, - { - "fieldname": "fct_total", - "fieldtype": "Float", - "label": "FCT Total", - "insert_after": "commission_per", - "read_only":1 - } - ], - "Quotation Item": [ - { - "fieldname": "sales_type", - "fieldtype": "Link", - "label": "Sales Type", - "options": "Sales Type", - "insert_after": "item_name" - } - ] - } +def get_employment_type_custom_fields(): + """ + Custom fields that need to be added to the Employment Type DocType + """ + return { + "Employment Type": [ + { + "fieldname": "penalty_leave_type", + "fieldtype": "Link", + "label": "Penalty Leave Type", + "options": "Leave Type", + "insert_after": "employee_type_name", + } + ] + } -def get_purchase_invoice_custom_fields(): - ''' - Custom fields that need to be added to the Purchase Invoice Doctype - ''' - return { - "Purchase Invoice": [ - { - "fieldname": "barter_invoice", - "fieldtype": "Check", - "label": "Barter Invoice", - "read_only": 1, - "fetch_from": "quotation.is_barter", - "insert_after": "supplier" - }, - { - "fieldname": "quotation", - "fieldtype": "Link", - "label": "Quotation", - "read_only": 1, - "options": "Quotation", - "insert_after": "barter_invoice" - - }, - { - "fieldname": "invoice_type", - "fieldtype": "Select", - "options": "Normal\nStringer Bill", - "default": "Normal", - "label": "Invoice Type", - "insert_after": "naming_series", - "read_only": 1 - }, - { - "fieldname": "purchase_order_id", - "fieldtype": "Link", - "label": "Purchase Order", - "options": "Purchase Order", - "insert_after": "naming_series" - }, - { - "fieldname": "stringer_bill_reference", - "fieldtype": "Link", - "label": "Stringer Bill Reference", - "options": "Stringer Bill", - "depends_on": "eval:doc.invoice_type == 'Stringer Bill' ", - "read_only": 1, - "insert_after": "purchase_order_id" - }, - { - "fieldname": "batta_claim_reference", - "fieldtype": "Link", - "label": "Batta Claim Reference", - "read_only": 1, - "options": "Batta Claim", - "insert_after": "stringer_bill_reference" - }, - { - "fieldname": "bureau", - "fieldtype": "Link", - "label": "Bureau", - "read_only": 1, - "options": "Bureau", - "insert_after": "supplier" - }, - { - "fieldname": "attach", - "fieldtype": "Attach", - "label": "Attachments", - "insert_after": "base_net_total" - } - ] - } -def get_supplier_custom_fields(): - ''' - Custom fields that need to be added to the Supplier Doctype - ''' - return { - "Supplier": [ - { - "fieldname": "is_stringer", - "fieldtype": "Check", - "label": "Is Stringer", - "insert_after": "supplier_name" - }, - { - "fieldname": "bureau", - "fieldtype": "Link", - "label": "Bureau", - "options": "Bureau", - "depends_on": "eval:doc.is_stringer == 1", - "insert_after": "is_stringer" - - }, - { - "fieldname": "area", - "fieldtype": "Data", - "label": "Area", - "depends_on": "eval:doc.is_stringer == 1", - "insert_after": "bureau" - - }, - { - "fieldname": "designation", - "fieldtype": "Link", - "label": "Designation", - "options": "Designation", - "insert_after": "country" - - } - ] - } +def get_event_custom_fields(): + """ + Custom fields to be added to the Event Doctype + """ + return { + "Event": [ + { + "fieldname": "contribution_of_employee", + "fieldtype": "Small Text", + "label": "Contribution of Employee", + "insert_after": "description", + "depends_on": "eval:doc.event_category == 'One to One Meeting'", + }, + { + "fieldname": "improvement_of_employee", + "fieldtype": "Small Text", + "label": "Areas of Improvement of Employee", + "insert_after": "contribution_of_employee", + "depends_on": "eval:doc.event_category == 'One to One Meeting'", + }, + { + "fieldname": "training_needs_of_employee", + "fieldtype": "Small Text", + "label": "Training Needs of Employee", + "insert_after": "improvement_of_employee", + "depends_on": "eval:doc.event_category == 'One to One Meeting'", + }, + { + "fieldname": "is_employee_eligible_for_promotion", + "fieldtype": "Select", + "label": "Is Employee Eligible for Promotion", + "options": "\nYes\nNo", + "insert_after": "training_needs_of_employee", + "depends_on": "eval:doc.event_category == 'One to One Meeting'", + }, + { + "fieldname": "remarks_for_promotion", + "fieldtype": "Small Text", + "label": "Remarks", + "insert_after": "is_employee_eligible_for_promotion", + "depends_on": "eval:(doc.is_employee_eligible_for_promotion == 'Yes' || doc.is_employee_eligible_for_promotion == 'No') && doc.event_category == 'One to One Meeting'", + }, + { + "fieldname": "appraisal_reference", + "fieldtype": "Link", + "label": "Appraisal Reference", + "options": "Appraisal", + "insert_after": "status", + }, + { + "fieldname": "assign_service_unit", + "fieldtype": "Check", + "label": "Assign Service Unit", + "insert_after": "add_video_conferencing", + }, + { + "fieldname": "meeting_room", + "fieldtype": "Link", + "label": "Meeting Room", + "options": "Service Unit", + "depends_on": "eval:doc.assign_service_unit == 1", + "mandatory_depends_on": "eval:doc.assign_service_unit == 1", + "insert_after": "assign_service_unit", + }, + { + "fieldname": "section_break_epd", + "fieldtype": "Section Break", + "label": " ", + "insert_after": "sunday", + }, + { + "fieldname": "external_participants", + "fieldtype": "Table", + "label": "External Participants", + "options": "External Participants Detail", + "insert_after": "section_break_epd", + }, + { + "fieldname": "reason_for_rejection", + "fieldtype": "Small Text", + "label": "Reason for Rejection", + "insert_after": "repeat_this_event", + }, + ] + } -def get_item_group_custom_fields(): - ''' - Custom fields that need to be added to the Quotation Item Doctype - ''' - return { - "Item Group": [ - { - "fieldname": "hireable", - "fieldtype": "Check", - "label": "Hireable", - "fetch_from":"parent_item_group.hireable", - "set_only_once":1, - "fetch_if_empty":1, - "insert_after": "gst_hsn_code" - } - - ] - } -def get_item_custom_fields(): - ''' - Custom fields that need to be added to the Quotation Item Doctype - ''' - return { - "Item": [ - { - "fieldname": "is_production_item", - "fieldtype": "Check", - "label": "Is Production Item", - "insert_after": "stock_uom" - }, - { - "fieldname": "sales_type", - "fieldtype": "Link", - "label": "Sales Type", - "options": "Sales Type", - "insert_after": "is_production_item" - }, - { - "fieldname": "hireable", - "fieldtype": "Check", - "label": "Hireable", - "fetch_from":"item_group.hireable", - "set_only_once":1, - "insert_after": "gst_hsn_code" - }, - { - "fieldname": "service_item", - "fieldtype": "Link", - "label": "Service Item", - "options": "Item", - "read_only":1, - "insert_after": "item_group" - }, - { - "fieldname": "item_audit_notification", - "fieldtype": "Check", - "label": "Periodic Notification for Asset Auditing ", - "depends_on": "eval:doc.is_fixed_asset == 1", - "insert_after": "asset_category" - }, - { - "fieldname": "item_notification_frequency", - "fieldtype": "Select", - "label": "Notification Frequency", - "options":"\nMonthly\nTrimonthly\nQuarterly\nHalf Yearly\nYearly", - "depends_on": "eval:doc.item_audit_notification == 1", - "insert_after": "item_audit_notification" - } , - { - "fieldname": "item_notification_template", - "fieldtype": "Link", - "label": "Notification Template", - "options":"Email Template", - "depends_on": "eval:doc.item_audit_notification == 1", - "insert_after": "item_notification_frequency" - }, - { - "fieldname": "start_notification_from", - "fieldtype": "Select", - "label": "Start Notification From", - "options":"\nJanuary\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember", - "depends_on": "eval:doc.item_audit_notification == 1", - "insert_after": "item_audit_notification" - }, - { - "fieldname": "is_makeup_item", - "fieldtype": "Check", - "label": "Is Makeup Item", - "insert_after": "is_exempt" - } - ] - } +def get_leave_application_custom_fields(): + """ + Custom fields that need to be added to the Leave Application Doctype + """ + return { + "Leave Application": [ + { + "fieldname": "medical_certificate", + "fieldtype": "Attach", + "label": "Medical Certificate", + "hidden": 1, + "insert_after": "leave_type", + } + ] + } -def get_employee_custom_fields(): - ''' - Custom fields that need to be added to the Employee Doctype - ''' - return { - "Employee": [ - { - "fieldname": "bureau", - "fieldtype": "Link", - "options": "Bureau", - "label": "Bureau", - "insert_after": "last_name" - }, - { - "fieldname": "stringer_type", - "fieldtype": "Link", - "options": "Stringer Type", - "label": "Stringer Type", - "insert_after": "Bureau" - }, - { - "fieldname": "leave_policy", - "fieldtype": "Link", - "options": "Leave Policy", - "label": "Leave Policy", - "insert_after": "attendance_device_id" - }, - { - "fieldname": "leave_policy_name", - "fieldtype": "Data", - "label": "Title", - "fetch_from": "leave_policy.title", - "insert_after": "leave_policy", - "read_only": 1 - }, - { - "fieldname": "name_of_father", - "fieldtype": "Data", - "label": "Father's Name", - "insert_after": "date_of_birth" - }, - { - "fieldname": "name_of_spouse", - "fieldtype": "Data", - "label": "Spouse's Name", - "insert_after": "name_of_father" - }, - { - "fieldname": "pincode", - "fieldtype": "Data", - "label": "Pincode", - "insert_after": "address_section" - }, - { - "fieldname": "aadhar_id", - "fieldtype": "Data", - "label": "Aadhar Id", - "insert_after": "marital_status" - }, - { - "fieldname": "date_of_appointment", - "fieldtype": "Date", - "label": "Date of Appointment", - "insert_after": "date_of_joining" - }, - { - "fieldname": "nominee_details_section", - "fieldtype": "Section Break", - "label": "Nominee Details", - "insert_after": "iban" - }, - { - "fieldname": "nominee_details", - "fieldtype": "Table", - "label": "Nominee Details", - "options":"Nominee Details", - "insert_after":"nominee_details_section" - }, - { - "fieldname": "additional_information_section", - "fieldtype": "Section Break", - "label": _("Additional Information"), - "insert_after": "place_of_issue", - "collapsible": 1 - }, - { - "fieldname": "physical_disabilities", - "fieldtype": "Select", - "label": "Do you have a physical disability", - "options":"Yes\nNo", - "default": "No", - "insert_after": "additional_information_section" - }, - { - "fieldname": "disabilities", - "fieldtype": "Data", - "label": "Please specify the disability", - "insert_after": "physical_disabilities", - "depends_on": "eval:doc.physical_disabilities == 'Yes'" - }, - { - "fieldname": "marital_indebtness", - "fieldtype": "Select", - "options":"Yes\nNo", - "default": "No", - "label": "Do you have marital indebtedness", - "insert_after": "disabilities" - }, - { - "fieldname": "training_status", - "fieldtype": "Select", - "options":"Not Started\nIn Progress\nCompleted\nNot Completed\nPartially Completed", - "label": "Training Status", - "insert_after": "status" - }, - { - "fieldname": "court_proceedings", - "fieldtype": "Select", - "options":"Yes\nNo", - "default": "No", - "label": "Are there any ongoing court proceedings", - "insert_after": "marital_indebtness", - }, - { - "fieldname": "court_proceedings_details", - "fieldtype": "Small Text", - "label": "Court Proceedings Details", - "insert_after": "court_proceedings", - "depends_on": "eval:doc.court_proceedings == 'Yes'" - }, - { - "fieldname": "column_break_travel", - "fieldtype": "Column Break", - "insert_after": "court_proceedings_details" - }, - { - "fieldname": "are_you_willing_to_travel", - "label": "Are you willing to travel", - "fieldtype": "Check", - "insert_after": "column_break_travel", - }, - { - "fieldname": "in_india", - "label": "In India", - "fieldtype": "Select", - "options":"Yes\nNo", - "default":"No", - "insert_after": "are_you_willing_to_travel", - "depends_on": "eval:doc.are_you_willing_to_travel == 1" - }, - { - "fieldname": "abroad", - "label": "Abroad", - "fieldtype": "Select", - "options":"Yes\nNo", - "default":"No", - "insert_after": "in_india", - "depends_on": "eval:doc.are_you_willing_to_travel == 1" - }, - { - "fieldname": "state_restrictions_problems", - "label": "State Restrictions/Problems if any", - "fieldtype": "Data", - "insert_after": "abroad", - "depends_on": "eval:doc.are_you_willing_to_travel == 1" - }, - { - "fieldname": "places_to_travel", - "label": "Places/Countries of your choice where you'd like to travel on job", - "fieldtype": "Data", - "insert_after": "state_restrictions_problems", - }, - { - "fieldname": "are_you_related_to_employee", - "label": "Are you related to any of our employees", - "fieldtype": "Check", - "insert_after": "places_to_travel" - }, - { - "fieldname": "related_employee_name", - "label": "Related Employee Name", - "fieldtype": "Data", - "insert_after": "are_you_related_to_employee", - "depends_on": "eval:doc.are_you_related_to_employee == 1", - }, - { - "fieldname": "documents_tab", - "fieldtype": "Tab Break", - "label": "Documents", - "insert_after": "internal_work_history" - }, - { - "fieldname": "employee_documents", - "fieldtype": "Table", - "label": "Employee Documents", - "options":"Employee Documents", - "insert_after":"documents_tab" - }, - { - "fieldname": "no_of_children", - "fieldtype": "Int", - "label": "No.of Children", - "insert_after":"marital_status" - }, - { - "fieldname": "company_number", - "fieldtype": "Data", - "label": "Company Mobile Number", - "options":"Phone", - "insert_after":"cell_number" - }, - { - "fieldname": "landmark", - "fieldtype": "Data", - "label": "Landmark", - "insert_after":"current_address" - }, - { - "fieldname": "landmark_per", - "fieldtype": "Data", - "label": "Landmark", - "insert_after":"permanent_address" - }, - { - "fieldname": "emergency_contact_name", - "fieldtype": "Data", - "label": "Emergency Contact Name", - "insert_after":"person_to_be_contacted" - }, - { - "fieldname": "emergency_phone", - "fieldtype": "Data", - "label": "Emergency Phone", - "insert_after":"emergency_phone_number" - }, - { - "fieldname": "relation_emergency", - "fieldtype": "Data", - "label": "Relation", - "insert_after":"relation" - }, - { - "fieldname": "assessment_officer", - "fieldtype": "Link", - "options": "Employee", - "label": "Assessment Officer", - "insert_after": "reports_to" - } - - ], - - "Employee External Work History":[ - { - "fieldname": "period_from", - "fieldtype": "Date", - "label": "Period From", - "insert_after": "designation" - }, - { - "fieldname": "period_to", - "fieldtype": "Date", - "label": "Period To", - "insert_after": "period_from" - }, - { - "fieldname": "last_position_held", - "fieldtype": "Data", - "label": "Last Position Held", - "insert_after": "period_to" - }, - { - "fieldname": "job_responsibility", - "fieldtype": "Small Text", - "label": "Job Responsibility", - "insert_after": "last_position_held" - }, - { - "fieldname": "designation_of_immediate_superior", - "fieldtype": "Data", - "label": "Designation Of Immediate Superior", - "insert_after": "job_responsibility" - }, - { - "fieldname": "gross_salary_drawn", - "fieldtype": "Float", - "label": "Gross Salary Drawn ", - "insert_after": "designation_of_immediate_superior" - }, - { - "fieldname": "reason_for_leaving", - "fieldtype": "Small Text", - "label": "Reason For Leaving", - "insert_after": "gross_salary_drown" - } - ] - } -def get_voucher_entry_custom_fields(): - ''' - Custom fields that need to be added to the Employee Doctype - ''' - return { - "Voucher Entry": [ - { - "fieldname": "bureau", - "fieldtype": "Link", - "options": "Bureau", - "label": "Bureau", - "insert_after": "balance" - } - ] - } +def get_attendance_request_custom_fields(): + """ + Custom fields that need to be added to the Attendance Request DocType. + """ + return { + "Attendance Request": [ + { + "fieldname": "reports_to", + "fieldtype": "Link", + "label": "Reports To", + "options": "Employee", + "fetch_from": "employee.reports_to", + "insert_after": "reason", + }, + { + "fieldname": "reports_to_name", + "fieldtype": "Data", + "label": "Reports To Name", + "insert_after": "reports_to", + "fetch_from": "reports_to.employee_name", + }, + { + "fieldname": "reports_to_user", + "fieldtype": "Link", + "label": "Reports To User", + "options": "User", + "insert_after": "reports_to_name", + "fetch_from": "reports_to.user_id", + }, + ] + } -def get_expected_skill_set_custom_fields(): - ''' - Custom fields that need to be added to the Expected Skill Set Doctype - ''' - return { - "Expected Skill Set": [ - { - "fieldname": "weight", - "fieldtype": "Float", - "label": "Weight", - "insert_after": "description" - } - ] - } -def get_hd_ticket_custom_fields(): - ''' - Custom fields to be added to the HD Ticket Doctype - ''' - return { - "HD Ticket": [ - { - "fieldname": "ticket_section_break", - "fieldtype": "Section Break", - "label": "", - "insert_after": "ticket_split_from" - }, - { - "fieldname": "spare_part_needed", - "fieldtype": "Check", - "label": "Spare Part Needed", - "insert_after": "ticket_section_break" - }, - { - "fieldname": "spare_part_item_table", - "fieldtype": "Table", - "label": "Spare Part Items", - "insert_after": "spare_part_needed", - "options": "Spare Part Item", - "depends_on": "eval:doc.spare_part_needed == 1" - }, - { - "fieldname": "raised_for", - "fieldtype": "Link", - "options": "User", - "label": "Raised For (Email)", - "insert_after": "raised_by" - }, - { - "fieldname": "attach", - "fieldtype": "Attach", - "label": "Attachments", - "insert_after": "agent_group" - } - ] - } +def get_customer_custom_fields(): + """ + Custom fields that need to be added to the Customer Doctype + """ + return { + "Customer": [ + { + "fieldname": "msme_status", + "fieldtype": "Select", + "label": "MSME Status", + "options": "\nMSME\nNon-MSME", + "insert_after": "customer_group", + }, + { + "fieldname": "msme_number", + "fieldtype": "Int", + "label": "MSME Number", + "insert_after": "msme_status", + }, + { + "fieldname": "region", + "fieldtype": "Link", + "label": "Region", + "options": "Region", + "reqd": 1, + "insert_after": "msme_number", + }, + { + "fieldname": "is_agent", + "fieldtype": "Check", + "label": "Is Agency", + "insert_after": "region", + }, + { + "fieldname": "is_edited", + "fieldtype": "Check", + "label": "Is Edited", + "hidden": 1, + "default": 0, + "no_copy": 1, + "insert_after": "is_agent", + }, + ] + } -def get_interview_round_custom_fields(): - ''' - Custom fields that need to be added to the Interview Round Child Table - ''' - return { - "Interview Round": [ - { - "fieldname": "expected_questions", - "fieldtype": "Table", - "label": "Interview Questions", - "options":"Interview Questions", - "insert_after":"expected_skill_set" - } - ] - } +def get_department_custom_fields(): + """ + Custom fields that need to be added to the Department Doctype + """ + return { + "Department": [ + { + "fieldname": "head_of_department", + "fieldtype": "Link", + "label": "Head Of Department", + "options": "Employee", + "insert_after": "department_name", + }, + { + "fieldname": "head_of_department_name", + "fieldtype": "Data", + "label": "Head Of Department(Name)", + "insert_after": "head_of_department", + "fetch_from": "head_of_department.employee_name", + "read_only": 1, + }, + { + "fieldname": "abbreviation", + "fieldtype": "Data", + "label": "Abbreviation", + "reqd": 1, + "unique": 1, + "insert_after": "head_of_department_name", + }, + { + "fieldname": "threshold_amount", + "fieldtype": "Float", + "label": "Threshold Amount", + "insert_after": "parent_department", + }, + { + "fieldname": "finance_group", + "fieldtype": "Link", + "label": "Finance Group", + "options": "Finance Group", + "insert_after": "company", + }, + { + "fieldname": "depart_cost_section", + "fieldtype": "Section Break", + "label": "", + "insert_after": "leave_block_list", + }, + { + "fieldname": "department_cost_center", + "fieldtype": "Table", + "label": "Cost Center", + "options": "Department Cost Center", + "insert_after": "depart_cost_section", + }, + ] + } -def get_interview_custom_fields(): - ''' - Custom fields that need to be added to the Interview Doctype - ''' - return { - "Interview": [ - { - "fieldname": "department", - "fieldtype": "Link", - "options": "Department", - "label": "Department", - "insert_after": "applicant_name", - "fetch_from": "job_applicant.department" - }, - { - "fieldname": "applicant_name", - "fieldtype": "Data", - "label": "Applicant Name", - "insert_after": "job_applicant", - "fetch_from": "job_applicant.applicant_name", - "read_only": 1 - - }, - { - "fieldname": "applicant_email", - "fieldtype": "Data", - "label": "Applicant Email ID", - "insert_after": "job_applicant", - "fetch_from": "job_applicant.email_id", - "hidden": 1 - } - - ] - } -def get_job_requisition_custom_fields(): - ''' - Custom fields that need to be added to the Job Requisition Doctype - ''' - return { - "Job Requisition": [ - { - "fieldname": "work_details", - "fieldtype": "Section Break", - "label": "Work Details", - "insert_after": "requested_by_designation" - }, - { - "fieldname": "employment_type", - "fieldtype": "Link", - "options": "Employment Type", - "label": "Employment Type", - "insert_after": "department", - "permlevel": 1 - }, - - { - "fieldname": "no_of_days_off", - "fieldtype": "Int", - "label": "Number of Days Off", - "description": "Number Of Days Off within a 30-day Period", - "insert_after": "work_details", - "permlevel": 1 - }, - { - "fieldname": "work_details_column_break", - "fieldtype": "Column Break", - "label": "", - "insert_after": "min_experience" - }, - { - "fieldname": "is_work_shift_needed", - "fieldtype": "Check", - "label": "Is Shift Work Needed", - "insert_after": "work_details_column_break", - "permlevel": 1 - }, - { - "fieldname": "travel_required", - "fieldtype": "Check", - "label": "Travel required for the position", - "insert_after": "is_work_shift_needed", - "permlevel": 1 - }, - { - "fieldname": "driving_license_needed", - "fieldtype": "Check", - "label": "Driving License Needed for this Position", - "depends_on": "eval:doc.travel_required == 1", - "insert_after": "travel_required", - "permlevel": 1 - }, - { - "fieldname": "license_type", - "fieldtype": "Link", - "label": "License Type", - "options": "License Type", - "depends_on": "eval:doc.driving_license_needed == 1", - "mandatory_depends_on":"eval:doc.driving_license_needed == 1", - "insert_after": "driving_license_needed", - "permlevel": 1 - }, - { - "fieldname": "education", - "fieldtype": "Section Break", - "label": "Education and Qualification Details", - "insert_after": "license_type" - }, - { - "fieldname": "min_education_qual", - "fieldtype": "Table MultiSelect", - "label": "Preferred Educational Qualification", - 'options':"Educational Qualifications", - "insert_after": "education", - "permlevel": 1 - }, - { - "fieldname": "min_experience", - "fieldtype": "Float", - "label": "Minimum Experience Required (Years)", - "insert_after": "no_of_days_off", - "permlevel": 1 - }, - { - "fieldname": "reset_column", - "fieldtype": "Section Break", - "label": "", - "insert_after": "license_type" - }, - { - "fieldname": "language_proficiency", - "fieldtype": "Table", - "options": "Language Proficiency", - "label": "Language Proficiency", - "insert_after": "reset_column", - "permlevel": 1 - }, - { - "fieldname": "skill_proficiency", - "fieldtype": "Table", - "options": "Skill Proficiency", - "label": "Skill Proficiency", - "description": "Proficency selected here is the minimum proficency needed.", - "insert_after": "language_proficiency" - }, - { - "fieldname": "job_description_template", - "fieldtype": "Link", - "label": "Job Description Template", - "options": "Job Description Template", - "insert_after": "job_description_tab", - "permlevel": 1 - }, - { - "fieldname": "request_for", - "label": "Request For", - "fieldtype": "Select", - "options": "Employee Replacement\nExisting Vacancy\nNew Vacancy", - "insert_after": "naming_series" - }, - { - "fieldname": "employee_left", - "label": "Employees Who Replaced", - "fieldtype": "Link", - "options": "Employee", - "insert_after": "request_for", - "depends_on": "eval:doc.request_for == 'Employee Replacement'" - }, - { - "fieldname": "relieving_date", - "fieldtype": "Date", - "label": "Relieving Date", - "insert_after": "employee_left", - "fetch_from":"employee_left.relieving_date", - "depends_on":"eval:doc.request_for == 'Employee Replacement'", - "read_only": 1 - }, - { - "fieldname": "interview", - "fieldtype": "Section Break", - "label": "Interview Details", - "insert_after": "requested_by_designation" - }, - { - "fieldname": "interview_rounds", - "fieldtype": "Table MultiSelect", - "options": "Interview Rounds", - "label": "Interview Rounds", - "insert_after": "interview", - "permlevel": 1 - }, - - { - "fieldname": "location", - "label": "Preferred Location", - "fieldtype": "Link", - "options": "Location", - "insert_after": "employment_type", - "permlevel": 1 - }, - { - "fieldname": "job_title", - "fieldtype": "Data", - "label": "Job Title", - "insert_after": "job_description_template", - "reqd": 1, - }, - { - "fieldname": "suggested_designation", - "fieldtype": "Link", - "label": "Suggested Designation", - "options": "Designation", - "insert_after": "request_for", - "permlevel": 2, - "depends_on": "eval:doc.request_for == 'New Vacancy'" - }, - { - "fieldname": "suggestions", - "fieldtype": "Small Text", - "label": "Suggestions/Feedback", - "insert_after": "description", - "permlevel": 3 - }, - { - "fieldname": "publish_on_job_section", - "fieldtype": "Section Break", - "label": "", - "insert_after": "requested_by_designation" - }, - { - "fieldname": "publish_on_job_opening", - "fieldtype": "Check", - "default": "1", - "label": "Publish on Job Opening", - "insert_after": "publish_on_job_section", - "permlevel":4 - } - ] - } +def get_driver_custom_fields(): + """ + Custom fields that need to be added to the Driver DocType + """ + return { + "Driver": [ + { + "fieldname": "is_internal", + "fieldtype": "Check", + "label": "Is Internal", + "insert_after": "transporter", + "reqd": 0, + }, + ] + } -def get_job_applicant_custom_fields(): - ''' - Custom fields that need to be added to the Job Applicant Doctype - ''' - return { - "Job Applicant": [ - { - "fieldname": "date_of_birth", - "fieldtype": "Date", - "label": "Date of Birth", - "insert_after": "email_id" - }, - { - "fieldname": "gender", - "fieldtype": "Link", - "label": "Gender", - "options": "Gender", - "insert_after": "date_of_birth" - }, - { - "fieldname": "willing_to_work_on_location", - "fieldtype": "Check", - "label": "Willing to Work in the Selected Location?", - "insert_after": "country" - }, - { - "fieldname": "father_name", - "fieldtype": "Data", - "label": "Father's Name", - "insert_after": "job_title" - }, - { - "fieldname": "marital_status", - "fieldtype": "Select", - "label": "Marital Status", - "options": "\nSingle\nMarried\nDivorced\nWidowed", - "insert_after": "location" - }, - { - "fieldname": "current_address_session_break", - "fieldtype": "Section Break", - "label": "Current Address", - "insert_after": "marital_status" - }, - { - "fieldname": "current_address", - "fieldtype": "Small Text", - "label": "Current Address", - "insert_after": "current_address_session_break" - }, - { - "fieldname": "current_mobile_no", - "fieldtype": "Data", - "options": "Phone", - "label": "Mobile Number", - "insert_after": "current_address" - }, - { - "fieldname": "current_column_break", - "fieldtype": "Column Break", - "label": "", - "insert_after": "current_mobile_no" - }, - { - "fieldname": "permanent_address_session_break", - "fieldtype": "Section Break", - "label": "Permanent Address", - "insert_after": "current_column_break" - }, - { - "fieldname": "permanent_address", - "fieldtype": "Small Text", - "label": "Permanent Address", - "insert_after": "permanent_address_session_break" - }, - { - "fieldname": "permanent_column_break", - "fieldtype": "Column Break", - "label": "", - "insert_after": "permanent_address" - }, - { - "fieldname": "email_address_session_break", - "fieldtype": "Section Break", - "label": "", - "insert_after": "permanent_column_break" - }, - { - "fieldname": "email_id_1", - "fieldtype": "Data", - "options": "Email", - "label": "Email ID", - "insert_after": "email_address_session_break" - }, - { - "fieldname": "min_education_qual", - "fieldtype": "Link", - "label": "Educational Qualification", - 'options':"Educational Qualification", - "insert_after": "details" - }, - { - "fieldname": "details", - "fieldtype": "Section Break", - "label": "Qualification Details", - "insert_after": "applicant_rating" - }, - { - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department", - "insert_after": "designation" - }, - { - "fieldname": "min_experience", - "fieldtype": "Float", - "label": "Work Experience (Years)", - "insert_after": "details_column_break", - }, - { - "fieldname": "details_column_break", - "fieldtype": "Column Break", - "label": "", - "insert_after": "min_education_qual" - }, - { - "fieldname": "reset_column", - "fieldtype": "Section Break", - "label": "", - "insert_after": "min_experience" - }, - - { - "fieldname": "language_proficiency", - "fieldtype": "Table", - "options": "Language Proficiency", - "label": "Language Proficiency", - "insert_after": "min_experience" - }, - { - "fieldname": "skill_proficiency", - "fieldtype": "Table", - "options": "Skill Proficiency", - "label": "Skill Proficiency", - "insert_after": "language_proficiency" - }, - { - "fieldname": "education_qualification", - "fieldtype": "Table", - "options": "Education Qualification", - "label": "Education Qualification", - "insert_after": "applicant_interview_rounds" - }, - { - "fieldname": "professional_certification", - "fieldtype": "Table", - "options": "Professional Certification", - "label": "Professional Certification", - "insert_after": "education_qualification" - }, - { - "fieldname": "location", - "label": "Location", - "fieldtype": "Link", - "options": "Location", - "insert_after": "status" - }, - { - "fieldname": "interview_process_break", - "fieldtype": "Section Break", - "label": "Interview Process", - "insert_after": "skill_proficiency" - }, - { - "fieldname": "applicant_interview_rounds", - "fieldtype": "Table", - "options": "Applicant Interview Round", - "label": "Interview Rounds", - "insert_after": "interview_process_break" - }, - { - "fieldname": "current_employer_tab_break", - "fieldtype": "Tab Break", - "label": "Current Employer Details", - "insert_after": "upper_range" - }, - { - "fieldname": "current_employer", - "fieldtype": "Section Break", - "label": "Current Employer / Immediate Previous Employer", - "insert_after": "current_employer_tab_break" - }, - { - "fieldname": "name_of_employer", - "fieldtype": "Data", - "label": "Name of Employer", - "insert_after": "current_employer" - }, - { - "fieldname": "employment_period_from", - "fieldtype": "Int", - "label": "Employment Period From", - "insert_after": "name_of_employer" - }, - { - "fieldname": "employment_period_to", - "fieldtype": "Int", - "label": "Employment Period To", - "insert_after": "employment_period_from" - }, - { - "fieldname": "current_employer_1_column_break", - "fieldtype": "Column Break", - "label": "", - "insert_after": "employment_period_to" - }, - { - "fieldname": "current_designation", - "fieldtype": "Data", - "label": "Designation", - "insert_after": "current_employer_1_column_break" - }, - { - "fieldname": "reference_taken", - "fieldtype": "Select", - "label": "Can I take Reference Now?", - "options": "\nYes\nNo", - "insert_after": "current_designation" - }, - { - "fieldname": "was_this_position", - "fieldtype": "Select", - "label": "Was this Position Permanent,Temporary,Contractual?", - "options": "\nPermanent\nTemporary\nContractual", - "insert_after": "reference_taken" - }, - { - "fieldname": "duties_and_reponsibilities", - "fieldtype": "Small Text", - "label": "Duties and Responsibilities", - "insert_after": "column_break_agency" - }, - { - "fieldname": "current_employer_column_break", - "fieldtype": "Column Break", - "label": "", - "insert_after": "was_this_position" - }, - { - "fieldname": "current_department", - "fieldtype": "Data", - "label": "Department", - "insert_after": "current_employer_column_break" - }, - { - "fieldname": "manager_name", - "fieldtype": "Data", - "label": "Manager's Name", - "insert_after": "current_department" - }, - { - "fieldname": "manager_contact_no", - "fieldtype": "Data", - "options": "Phone", - "label": "Manager's Contact No", - "insert_after": "manager_name" - }, - { - "fieldname": "manager_email", - "fieldtype": "Data", - "options": "Email", - "label": "Manager's Email", - "insert_after": "manager_contact_no" - }, - { - "fieldname": "reason_for_leaving", - "fieldtype": "Small Text", - "label": "Reason For Leaving", - "insert_after": "duties_and_reponsibilities" - }, - { - "fieldname": "agency_details", - "fieldtype": "Small Text", - "label": "Agency Details (if Temporary or Contractual)", - "insert_after": "address_of_employer" - }, - { - "fieldname": "column_break_agency", - "fieldtype": "Column Break", - "label": "", - "insert_after": "agency_details" - }, - { - "fieldname": "previous_emp", - "fieldtype": "Section Break", - "insert_after": "manager_email" - }, - { - "fieldname": "address_of_employer", - "fieldtype": "Small Text", - "label": "Address of Employer", - "insert_after": "previous_emp" - }, - { - "fieldname": "previous_emplyoment", - "fieldtype": "Section Break", - "label": "Previous Employment History", - "insert_after": "previous_emp" - }, - { - "fieldname": "prev_emp_his", - "fieldtype": "Table", - "options": "Previous Employment History", - "insert_after": "previous_emplyoment" - }, - { - "fieldname": "more_details_tab_break", - "fieldtype": "Tab Break", - "label": "More Details", - "insert_after": "prev_emp_his" - }, - { - "fieldname": "current_salary", - "fieldtype": "Currency", - "label": "Current Salary", - "insert_after": "more_details_tab_break" - }, - { - "fieldname": "current_salary_column_break", - "fieldtype": "Column Break", - "label": "", - "insert_after": "current_salary" - }, - { - "fieldname": "expected_salary", - "fieldtype": "Currency", - "label": "Expected Salary", - "insert_after": "current_salary_column_break" - }, - { - "fieldname": "expected_salary_column_break", - "fieldtype": "Column Break", - "label": "", - "insert_after": "expected_salary" - }, - { - "fieldname": "telephone_number", - "fieldtype": "Data", - "options": "Phone", - "label": "Telephone Number", - "insert_after": "expected_salary_column_break" - }, - { - "fieldname": "other_achievments_session_break", - "fieldtype": "Section Break", - "label": "", - "insert_after": "current_employer_tab_break" - }, - { - "fieldname": "other_achievments", - "fieldtype": "Small Text", - "label": "Please add details of Professional and other achievements,awards and accomplishments,if any", - "insert_after": "other_achievments_session_break" - }, - { - "fieldname": "interviewed_session_break", - "fieldtype": "Section Break", - "label": "Have you been interviewed before by Madhyamam Group?If yes, Please give details below :", - "insert_after": "other_achievments" - }, - { - "fieldname": "position", - "fieldtype": "Data", - "label": "Position", - "insert_after": "interviewed_session_break" - }, - { - "fieldname": "interviewed_date", - "fieldtype": "Date", - "label": "Date", - "insert_after": "position" - }, - { - "fieldname": "interviewed_column_break", - "fieldtype": "Column Break", - "label": "", - "insert_after": "interviewed_date" - }, - { - "fieldname": "interviewed_location", - "fieldtype": "Link", - "options": "Location", - "label": "Location", - "insert_after": "interviewed_column_break" - }, - { - "fieldname": "interviewed_outcome", - "fieldtype": "Data", - "label": "Outcome", - "insert_after": "interviewed_location" - }, - { - "fieldname": "travel_session_break", - "fieldtype": "Section Break", - "label": "Are you willing to travel :", - "insert_after": "interviewed_outcome" - }, - { - "fieldname": "in_india", - "fieldtype": "Check", - "label": "In India", - "insert_after": "travel_session_break" - }, - { - "fieldname": "state_restriction", - "fieldtype": "Data", - "label": "State Restriction If any", - "depends_on": "eval:doc.in_india", - "insert_after": "in_india" - }, - { - "fieldname": "india_column_break", - "fieldtype": "Column Break", - "label": "", - "insert_after": "state_restriction" - }, - { - "fieldname": "abroad", - "fieldtype": "Check", - "label": "Abroad", - "insert_after": "india_column_break" - }, - { - "fieldname": "related_session_break", - "fieldtype": "Section Break", - "label": "Are you related to any of employee of the Madhyamam Group? If yes,please give details :", - "insert_after": "state_restriction" - }, - { - "fieldname": "related_employee", - "fieldtype": "Data", - "label": "Name", - "insert_after": "related_session_break" - }, - { - "fieldname": "related_employee_org", - "fieldtype": "Data", - "label": "Organization", - "insert_after": "related_employee" - }, - { - "fieldname": "related_column_break", - "fieldtype": "Column Break", - "label": "", - "insert_after": "related_employee_org" - }, - { - "fieldname": "related_employee_pos", - "fieldtype": "Data", - "label": "Position", - "insert_after": "related_column_break", - }, - { - "fieldname": "related_employee_rel", - "fieldtype": "Data", - "label": "Relationship", - "insert_after": "related_employee_pos" - }, - { - "fieldname": "prof_session_break", - "fieldtype": "Section Break", - "label": "", - "insert_after": "related_employee_rel" - }, - { - "fieldname": "professional_org", - "fieldtype": "Small Text", - "label": "Are you a member of any Professional Organization? If yes, Please give details :", - "insert_after": "prof_session_break" - }, - { - "fieldname": "political_org", - "fieldtype": "Small Text", - "label": "Are you a member of any Political Organization? If yes, Please give details :", - "insert_after": "professional_org" - }, - { - "fieldname": "specialised_training", - "fieldtype": "Small Text", - "label": "Have you attended any specialised training program?If yes, Please give detais :", - "insert_after": "political_org" - }, - { - "fieldname": "is_form_submitted", - "fieldtype": "Check", - "label": "Is Form Submitted", - "read_only":1, - "insert_after": "specialised_training" - }, - { - "fieldname": "payslip_month_1", - "fieldtype": "Attach", - "label": "Payslip - Month 1", - "insert_after": "section_break_001" - }, - { - "fieldname": "break_oo1", - "fieldtype": "Column Break", - "insert_after": "payslip_month_1" - }, - { - "fieldname": "payslip_month_2", - "fieldtype": "Attach", - "label": "Payslip - Month 2", - "insert_after": "break_oo1" - }, - { - "fieldname": "section_break_001", - "fieldtype": "Section Break", - "label": "", - "insert_after": "prev_emp_his" - }, - { - "fieldname": "break_oo2", - "fieldtype": "Column Break", - "insert_after": "payslip_month_2" - }, - { - "fieldname": "payslip_month_3", - "fieldtype": "Attach", - "label": "Payslip - Month 3", - "insert_after": "break_oo2" - } - ] - } -def get_contract_custom_fields(): - ''' - Custom fields that need to be added to the Contract Doctype - ''' - return { - "Contract": [ - { - "fieldname": "services_section", - "fieldtype": "Section Break", - "label": "Services", - "insert_after": "ip_address" - }, - { - "fieldname": "services", - "fieldtype": "Table", - "options": "Services", - "label": "Services", - "insert_after": "services_section", - "depends_on": "eval:doc.party_type == 'Supplier'" - }, - { - "fieldname": "total_amount", - "fieldtype": "Currency", - "label": "Total Amount", - "insert_after": "services", - "read_only":1, - "no_copy":1 - } - ] - } +def get_asset_custom_fields(): + """ + Custom fields that need to be added to the Asset DocType + """ + return { + "Asset": [ + { + "fieldname": "bureau", + "fieldtype": "Link", + "options": "Bureau", + "label": "Bureau", + "insert_after": "location", + }, + { + "fieldname": "in_transit", + "fieldtype": "Check", + "label": "In Transit", + "insert_after": "is_composite_asset", + "allow_on_submit": 1, + "read_only": 1, + }, + { + "fieldname": "warranty_details_section", + "fieldtype": "Section Break", + "label": "Warranty Details Section", + "insert_after": "comprehensive_insurance", + "collapsible": 1, + }, + { + "fieldname": "warranty_reference_no", + "fieldtype": "Data", + "label": "Warranty Reference No", + "insert_after": "warranty_details_section", + }, + { + "fieldname": "warranty_till", + "fieldtype": "Date", + "label": "Warranty Till", + "insert_after": "warranty_reference_no", + }, + { + "fieldname": "qr_code", + "fieldtype": "Attach Image", + "label": "QR code", + "insert_after": "department", + }, + { + "fieldname": "asset_details", + "fieldtype": "Attach Image", + "label": "Asset Details", + "insert_after": "qr_code", + }, + { + "fieldname": "room", + "fieldtype": "Link", + "label": "Room", + "options": "Service Unit", + "allow_on_submit": 1, + "insert_after": "journal_entry_for_scrap", + }, + { + "fieldname": "shelf", + "fieldtype": "Link", + "label": "Shelf", + "options": "Shelf", + "allow_on_submit": 1, + "insert_after": "room", + }, + { + "fieldname": "row", + "fieldtype": "Link", + "label": "Row", + "options": "Row", + "allow_on_submit": 1, + "insert_after": "shelf", + }, + { + "fieldname": "bin", + "fieldtype": "Link", + "label": "Bin", + "options": "Container", + "allow_on_submit": 1, + "insert_after": "row", + }, + ] + } -def get_job_opening_custom_fields(): - ''' - Custom fields that need to be added to the Job Opening Doctype - ''' - return { - "Job Opening": [ - { - "fieldname": "job_details", - "fieldtype": "Section Break", - "label": "Job Details", - "insert_after": "location" - }, - { - "fieldname": "no_of_positions", - "fieldtype": "Int", - "label": "No of.Positions", - "insert_after": "employment_type", - }, - { - "fieldname": "no_of_days_off", - "fieldtype": "Int", - "label": "Number of Days Off", - "insert_after": "job_details", - "non_negative": 1 - }, - { - "fieldname": "preffered_location", - "label": "Preffered Location", - "fieldtype": "Link", - "options": "Location", - "insert_after": "no_of_days_off" - }, - { - "fieldname": "job_details_column_break", - "fieldtype": "Column Break", - "label": "", - "insert_after": "preffered_location" - }, - { - "fieldname": "travel_required", - "fieldtype": "Check", - "label": "Travel required for the position", - "insert_after": "job_details_column_break" - }, - { - "fieldname": "driving_license_needed", - "fieldtype": "Check", - "label": "Driving License Needed for this Position", - "depends_on": "eval:doc.travel_required == 1", - "insert_after": "travel_required" - }, - { - "fieldname": "license_type", - "fieldtype": "Link", - "label": "License Type", - "options": "License Type", - "depends_on": "eval:doc.driving_license_needed == 1", - "insert_after": "driving_license_needed" - }, - { - "fieldname": "is_work_shift_needed", - "fieldtype": "Check", - "label": "Is Shift Work Needed", - "insert_after": "license_type" - }, - { - "fieldname": "qualification_details", - "fieldtype": "Section Break", - "label": "Education and Qualification Details", - "insert_after": "license_type", - }, - { - "fieldname": "min_education_qual", - "fieldtype": "Table MultiSelect", - "label": "Preferred Educational Qualification", - 'options':"Educational Qualifications", - "insert_after": "qualification_details" - }, - { - "fieldname": "qualification_details_column_break", - "fieldtype": "Column Break", - "label": "", - "insert_after": "min_education_qual" - }, - { - "fieldname": "min_experience", - "fieldtype": "Float", - "label": "Minimum Experience Required (Years)", - "insert_after": "is_work_shift_needed" - }, - { - "fieldname": "proficiency_break", - "fieldtype": "Section Break", - "label": "", - "insert_after": "min_experience" - }, - { - "fieldname": "language_proficiency", - "fieldtype": "Table", - "options": "Language Proficiency", - "label": "Language Proficiency", - "insert_after": "proficiency_break", - "description": "Proficency selected here is the minimum proficency needed" - }, - { - "fieldname": "skill_proficiency", - "fieldtype": "Table", - "options": "Skill Proficiency", - "label": "Skill Proficiency", - "insert_after": "language_proficiency", - "description": "Proficency selected here is the minimum proficency needed" - }, - { - "fieldname": "interview_details_sb", - "fieldtype": "Section Break", - "label": "Interview Details", - "insert_after": "skill_proficiency" - }, - { - "fieldname": "interview_rounds", - "fieldtype": "Table MultiSelect", - "label": "Interview Rounds", - 'options':"Interview Rounds", - "insert_after": "interview_details_sb" - } - ] - } -def get_company_custom_fields(): - ''' - Custom fields that need to be added to the Company Doctype - ''' - return { - "Company": [ - { - "fieldname": "company_policy_tab", - "fieldtype": "Tab Break", - "label": "Company Policy", - "insert_after": "dashboard_tab" - }, - { - "fieldname": "company_policy", - "fieldtype": "Text Editor", - "label": "Company Policy", - "insert_after": "company_policy_tab" - }, - { - "fieldname": "exception_budget_column", - "fieldtype": "Column Break", - "label": "", - "insert_after": "exception_budget_approver_role" - }, - { - "fieldname": "exchange_rate_to_inr", - "fieldtype": "Float", - "label": "Budget Exchange Rate to INR", - "insert_after": "exception_budget_column", - "description": "1 Unit of Company Currency = [?] INR" - } - ] - } +def get_job_offer_custom_fields(): + """ + Custom fields that need to be added to the Job Offer DocType + """ + return { + "Job Offer": [ + { + "fieldname": "job_proposal", + "fieldtype": "Link", + "label": "Job Proposal", + "options": "Job Proposal", + "insert_after": "applicant_email", + "read_only": 1, + }, + { + "fieldname": "ctc", + "fieldtype": "Currency", + "label": "CTC", + "insert_after": "job_proposal", + "fetch_from": "job_proposal.proposed_ctc", + }, + ] + } + + +def get_purchase_order_custom_fields(): + """ + Custom fields that need to be added to the Purchase Order DocType + """ + return { + "Purchase Order": [ + { + "fieldname": "is_budget_exceed", + "fieldtype": "Check", + "label": "Is Budget Exceed", + "insert_after": "items_section", + "read_only": 1, + "no_copy": 1, + "depends_on": "eval:doc.is_budget_exceed == 1", + }, + { + "fieldname": "attach", + "fieldtype": "Attach", + "label": "Attachments", + "insert_after": "base_net_total", + }, + ], + "Purchase Order Item": [ + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "label": "Reference DocType", + "options": "DocType", + "insert_after": "blanket_order_rate", + }, + { + "fieldname": "reference_document", + "fieldtype": "Dynamic Link", + "label": "Reference Document", + "options": "reference_doctype", + "insert_after": "reference_doctype", + }, + ], + } + + +def get_budget_custom_fields(): + """ + Custom fields that need to be added to the Budget DocType + """ + return { + "Budget": [ + { + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "reqd": 1, + "insert_after": "company", + }, + { + "fieldname": "finance_group", + "fieldtype": "Link", + "label": "Finance Group", + "options": "Finance Group", + "insert_after": "department", + "read_only": 1, + "fetch_from": "department.finance_group", + }, + { + "fieldname": "division", + "fieldtype": "Link", + "label": "Division", + "options": "Division", + "reqd": 1, + "insert_after": "department", + }, + { + "fieldname": "region", + "fieldtype": "Link", + "label": "Region", + "options": "Region", + "insert_after": "budget_template", + }, + { + "fieldname": "budget_template", + "fieldtype": "Link", + "label": "Budget Template", + "options": "Budget Template", + "insert_after": "fiscal_year", + }, + { + "fieldname": "rejection_feedback", + "fieldtype": "Table", + "label": "Rejection Feedback", + "options": "Rejection Feedback", + "insert_after": "december", + "depends_on": "eval: doc.workflow_state.includes('Rejected')", + }, + { + "fieldname": "total_amount", + "fieldtype": "Currency", + "label": "Total Amount", + "read_only": 1, + "insert_after": "region", + "options": "company_currency", + }, + { + "fieldname": "budget_accounts_custom", + "fieldtype": "Table", + "label": "Budget Accounts", + "options": "Budget Account", + "insert_after": "accounts", + }, + { + "fieldname": "budget_accounts_hr", + "fieldtype": "Table", + "label": "Budget Accounts(HR Overheads)", + "options": "Budget Account", + "insert_after": "budget_accounts_custom", + }, + { + "fieldname": "default_currency", + "fieldtype": "Link", + "label": "Default Currency", + "options": "Currency", + "read_only": 1, + "hidden": 1, + "insert_after": "budget_accounts_hr", + "default": "INR", + }, + { + "fieldname": "company_currency", + "fieldtype": "Link", + "label": "Company Currency", + "options": "Currency", + "read_only": 1, + "hidden": 1, + "insert_after": "default_currency", + "fetch_from": "company.default_currency", + }, + ], + "Budget Account": [ + { + "fieldname": "cost_head", + "fieldtype": "Link", + "label": "Cost Head", + "options": "Cost Head", + "insert_before": "cost_subhead", + "in_list_view": 1, + }, + { + "fieldname": "cost_subhead", + "fieldtype": "Link", + "label": "Cost Sub Head", + "options": "Cost Subhead", + "insert_after": "cost_head", + "in_list_view": 1, + }, + { + "fieldname": "cost_category", + "fieldtype": "Link", + "label": "Cost Category", + "options": "Cost Category", + "insert_after": "account", + "in_list_view": 1, + }, + { + "fieldname": "column_break_cd", + "fieldtype": "Column Break", + "label": " ", + "insert_after": "cost_category", + }, + { + "fieldname": "cost_description", + "fieldtype": "Small Text", + "label": "Cost Description", + "insert_after": "column_break_cd", + }, + { + "fieldname": "equal_monthly_distribution", + "fieldtype": "Check", + "label": "Equal Monthly Distribution ", + "insert_after": "cost_description", + }, + { + "fieldname": "section_break_ab", + "fieldtype": "Section Break", + "label": "Monthly Amount Distribution", + "insert_after": "budget_amount", + }, + { + "fieldname": "january", + "fieldtype": "Currency", + "label": "January", + "insert_after": "section_break_ab", + }, + { + "fieldname": "february", + "fieldtype": "Currency", + "label": "February", + "insert_after": "january", + }, + { + "fieldname": "march", + "fieldtype": "Currency", + "label": "March", + "insert_after": "february", + }, + { + "fieldname": "april", + "fieldtype": "Currency", + "label": "April", + "insert_after": "march", + }, + { + "fieldname": "column_break_bc", + "fieldtype": "Column Break", + "label": " ", + "insert_after": "april", + }, + { + "fieldname": "may", + "fieldtype": "Currency", + "label": "May", + "insert_after": "column_break_bc", + }, + { + "fieldname": "june", + "fieldtype": "Currency", + "label": "June", + "insert_after": "may", + }, + { + "fieldname": "july", + "fieldtype": "Currency", + "label": "July", + "insert_after": "june", + }, + { + "fieldname": "august", + "fieldtype": "Currency", + "label": "August", + "insert_after": "july", + }, + { + "fieldname": "column_break_ab", + "fieldtype": "Column Break", + "label": " ", + "insert_after": "august", + }, + { + "fieldname": "september", + "fieldtype": "Currency", + "label": "September", + "insert_after": "column_break_ab", + }, + { + "fieldname": "october", + "fieldtype": "Currency", + "label": "October", + "insert_after": "september", + }, + { + "fieldname": "november", + "fieldtype": "Currency", + "label": "November", + "insert_after": "october", + }, + { + "fieldname": "december", + "fieldtype": "Currency", + "label": "December", + "insert_after": "november", + }, + { + "fieldname": "section_break_inr", + "fieldtype": "Section Break", + "label": "Monthly Amount Distribution (INR)", + "insert_after": "december", + "collapsible": 1, + "read_only": 1, + }, + { + "fieldname": "january_inr", + "fieldtype": "Currency", + "label": "January (INR)", + "insert_after": "section_break_inr", + "read_only": 1, + "options": "default_currency", + }, + { + "fieldname": "february_inr", + "fieldtype": "Currency", + "label": "February (INR)", + "insert_after": "january_inr", + "read_only": 1, + "options": "default_currency", + }, + { + "fieldname": "march_inr", + "fieldtype": "Currency", + "label": "March (INR)", + "insert_after": "february_inr", + "read_only": 1, + "options": "default_currency", + }, + { + "fieldname": "april_inr", + "fieldtype": "Currency", + "label": "April (INR)", + "insert_after": "march_inr", + "read_only": 1, + "options": "default_currency", + }, + { + "fieldname": "column_break_zz1", + "fieldtype": "Column Break", + "label": " ", + "insert_after": "april_inr", + }, + { + "fieldname": "may_inr", + "fieldtype": "Currency", + "label": "May (INR)", + "insert_after": "column_break_zz1", + "read_only": 1, + "options": "default_currency", + }, + { + "fieldname": "june_inr", + "fieldtype": "Currency", + "label": "June (INR)", + "insert_after": "may_inr", + "read_only": 1, + "options": "default_currency", + }, + { + "fieldname": "july_inr", + "fieldtype": "Currency", + "label": "July (INR)", + "insert_after": "june_inr", + "read_only": 1, + "options": "default_currency", + }, + { + "fieldname": "august_inr", + "fieldtype": "Currency", + "label": "August (INR)", + "insert_after": "july_inr", + "read_only": 1, + "options": "default_currency", + }, + { + "fieldname": "column_break_zz2", + "fieldtype": "Column Break", + "label": " ", + "insert_after": "july_inr", + }, + { + "fieldname": "september_inr", + "fieldtype": "Currency", + "label": "September (INR)", + "insert_after": "column_break_zz2", + "read_only": 1, + "options": "default_currency", + }, + { + "fieldname": "october_inr", + "fieldtype": "Currency", + "label": "October (INR)", + "insert_after": "september_inr", + "read_only": 1, + "options": "default_currency", + }, + { + "fieldname": "november_inr", + "fieldtype": "Currency", + "label": "November (INR)", + "insert_after": "october_inr", + "read_only": 1, + "options": "default_currency", + }, + { + "fieldname": "december_inr", + "fieldtype": "Currency", + "label": "December (INR)", + "insert_after": "november_inr", + "read_only": 1, + "options": "default_currency", + }, + { + "fieldname": "budget_amount_inr", + "fieldtype": "Currency", + "label": "Budget Amount (INR)", + "insert_after": "budget_amount", + "options": "default_currency", + "read_only": 1, + }, + ], + } + + +def get_sales_invoice_custom_fields(): + """ + Custom fields that need to be added to the Sales Invoice Doctype + """ + return { + "Sales Invoice": [ + { + "fieldname": "actual_customer", + "fieldtype": "Link", + "label": "Actual Customer", + "options": "Customer", + "depends_on": "eval:doc.is_agent == 1", + "insert_after": "is_agent", + }, + { + "fieldname": "is_agent", + "fieldtype": "Check", + "label": "Is Agency", + "read_only": 1, + "fetch_from": "customer.is_agent", + "depends_on": "eval:doc.is_agent", + "insert_after": "customer", + }, + { + "fieldname": "actual_customer_group", + "fieldtype": "Link", + "label": "Actual Customer Group", + "options": "Customer Group", + "read_only": 1, + "fetch_from": "actual_customer.customer_group", + "insert_after": "actual_customer", + }, + { + "fieldname": "include_in_ibf", + "fieldtype": "Check", + "label": "Include in IBF", + "read_only": 1, + "insert_after": "actual_customer_group", + }, + { + "fieldname": "region", + "fieldtype": "Link", + "options": "Region", + "label": "Region", + "insert_after": "is_reverse_charge", + }, + { + "fieldname": "executive", + "fieldtype": "Link", + "options": "Employee", + "label": "Executive", + "insert_after": "due_date", + }, + { + "fieldname": "executive_name", + "fieldtype": "Data", + "label": "Executive Name", + "insert_after": "executive", + "fetch_from": "executive.employee_name", + "read_only": 1, + }, + { + "fieldname": "is_barter_invoice", + "fieldtype": "Check", + "label": "Is Barter Invoice", + "read_only": 1, + "insert_after": "include_in_ibf", + "fetch_from": "reference_id.is_barter", + }, + { + "fieldname": "reference_id", + "fieldtype": "Link", + "options": "Quotation", + "label": "Quotation", + "read_only": 1, + "insert_after": "naming_series", + }, + { + "fieldname": "sales_type", + "fieldtype": "Link", + "label": "Sales Type", + "insert_after": "naming_series", + "options": "Sales Type", + }, + ] + } + + +def get_quotation_custom_fields(): + """ + Custom fields that need to be added to the Quotation DocType + """ + return { + "Quotation": [ + { + "fieldname": "is_agent", + "fieldtype": "Check", + "label": "Is Agency", + "read_only": 1, + "fetch_from": "party_name.is_agent", + "depends_on": "eval:doc.is_agent", + "insert_after": "party_name", + }, + { + "fieldname": "actual_customer", + "fieldtype": "Link", + "label": "Actual Customer", + "options": "Customer", + "depends_on": "eval:doc.is_agent == 1", + "insert_after": "is_agent", + }, + { + "fieldname": "actual_customer_group", + "fieldtype": "Link", + "label": "Actual Customer Group", + "options": "Customer Group", + "read_only": 1, + "fetch_from": "actual_customer.customer_group", + "insert_after": "actual_customer", + }, + { + "fieldname": "customer_purchase_order_reference", + "fieldtype": "Data", + "label": "Customer Purchase Order Reference", + "insert_after": "valid_till", + }, + { + "fieldname": "executive", + "fieldtype": "Link", + "label": "Executive", + "options": "Employee", + "insert_after": "customer_purchase_order_reference", + }, + { + "fieldname": "executive_name", + "fieldtype": "Data", + "label": "Executive Name", + "insert_after": "executive", + "fetch_from": "executive.employee_name", + "read_only": 1, + }, + { + "fieldname": "is_barter", + "fieldtype": "Check", + "label": "Is Barter", + "insert_after": "amended_from", + }, + { + "fieldname": "purchase_order", + "fieldtype": "Link", + "label": "Purchase Order", + "insert_after": "is_barter", + "depends_on": "eval:doc.is_barter", + "options": "Purchase Order", + }, + { + "fieldname": "sales_type", + "fieldtype": "Link", + "label": "Default Sales Type", + "insert_after": "purchase_order", + "options": "Sales Type", + }, + { + "fieldname": "region", + "fieldtype": "Link", + "label": "Region", + "insert_after": "customer_name", + "options": "Region", + }, + { + "fieldname": "albatross_details_section", + "fieldtype": "Section Break", + "label": "Albatross Details", + "insert_after": "is_barter", + }, + { + "fieldname": "albatross_ro_id", + "fieldtype": "Data", + "label": "Albatross RO ID", + "insert_after": "albatross_details_section", + "read_only": 1, + }, + { + "fieldname": "ro_no", + "fieldtype": "Data", + "label": "RO No", + "insert_after": "albatross_ro_id", + "read_only": 1, + }, + { + "fieldname": "ro_date", + "fieldtype": "Date", + "label": "RO Date", + "insert_after": "ro_no", + "read_only": 1, + }, + { + "fieldname": "ro_option", + "fieldtype": "Data", + "label": "RO Option", + "insert_after": "ro_date", + "read_only": 1, + }, + { + "fieldname": "region_revenue_percentage", + "fieldtype": "Percent", + "label": "Region Revenue Percentage", + "insert_after": "ro_option", + "read_only": 1, + }, + { + "fieldname": "albatross_column_break", + "fieldtype": "Column Break", + "label": "", + "insert_after": "region_revenue_percentage", + }, + { + "fieldname": "product_name", + "fieldtype": "Data", + "label": "Product Name", + "insert_after": "albatross_column_break", + "read_only": 1, + }, + { + "fieldname": "program_name", + "fieldtype": "Data", + "label": "Program Name", + "insert_after": "product_name", + "read_only": 1, + }, + { + "fieldname": "no_of_eps", + "fieldtype": "Data", + "label": "No of Episodes", + "insert_after": "program_name", + "read_only": 1, + }, + { + "fieldname": "commission_per", + "fieldtype": "Float", + "label": "Commission Per", + "insert_after": "no_of_eps", + "read_only": 1, + }, + { + "fieldname": "fct_total", + "fieldtype": "Float", + "label": "FCT Total", + "insert_after": "commission_per", + "read_only": 1, + }, + ], + "Quotation Item": [ + { + "fieldname": "sales_type", + "fieldtype": "Link", + "label": "Sales Type", + "options": "Sales Type", + "insert_after": "item_name", + } + ], + } + + +def get_purchase_invoice_custom_fields(): + """ + Custom fields that need to be added to the Purchase Invoice Doctype + """ + return { + "Purchase Invoice": [ + { + "fieldname": "barter_invoice", + "fieldtype": "Check", + "label": "Barter Invoice", + "read_only": 1, + "fetch_from": "quotation.is_barter", + "insert_after": "supplier", + }, + { + "fieldname": "quotation", + "fieldtype": "Link", + "label": "Quotation", + "read_only": 1, + "options": "Quotation", + "insert_after": "barter_invoice", + }, + { + "fieldname": "invoice_type", + "fieldtype": "Select", + "options": "Normal\nStringer Bill", + "default": "Normal", + "label": "Invoice Type", + "insert_after": "naming_series", + "read_only": 1, + }, + { + "fieldname": "purchase_order_id", + "fieldtype": "Link", + "label": "Purchase Order", + "options": "Purchase Order", + "insert_after": "naming_series", + }, + { + "fieldname": "stringer_bill_reference", + "fieldtype": "Link", + "label": "Stringer Bill Reference", + "options": "Stringer Bill", + "depends_on": "eval:doc.invoice_type == 'Stringer Bill' ", + "read_only": 1, + "insert_after": "purchase_order_id", + }, + { + "fieldname": "batta_claim_reference", + "fieldtype": "Link", + "label": "Batta Claim Reference", + "read_only": 1, + "options": "Batta Claim", + "insert_after": "stringer_bill_reference", + }, + { + "fieldname": "bureau", + "fieldtype": "Link", + "label": "Bureau", + "read_only": 1, + "options": "Bureau", + "insert_after": "supplier", + }, + { + "fieldname": "attach", + "fieldtype": "Attach", + "label": "Attachments", + "insert_after": "base_net_total", + }, + ] + } + + +def get_supplier_custom_fields(): + """ + Custom fields that need to be added to the Supplier Doctype + """ + return { + "Supplier": [ + { + "fieldname": "is_stringer", + "fieldtype": "Check", + "label": "Is Stringer", + "insert_after": "supplier_name", + }, + { + "fieldname": "bureau", + "fieldtype": "Link", + "label": "Bureau", + "options": "Bureau", + "depends_on": "eval:doc.is_stringer == 1", + "insert_after": "is_stringer", + }, + { + "fieldname": "area", + "fieldtype": "Data", + "label": "Area", + "depends_on": "eval:doc.is_stringer == 1", + "insert_after": "bureau", + }, + { + "fieldname": "designation", + "fieldtype": "Link", + "label": "Designation", + "options": "Designation", + "insert_after": "country", + }, + { + "fieldname": "ot_batta", + "fieldtype": "Currency", + "label": "OT Batta", + "depends_on": "eval:doc.is_transporter == 1", + "insert_after": "is_transporter", + }, + ] + } + + +def get_item_group_custom_fields(): + """ + Custom fields that need to be added to the Quotation Item Doctype + """ + return { + "Item Group": [ + { + "fieldname": "hireable", + "fieldtype": "Check", + "label": "Hireable", + "fetch_from": "parent_item_group.hireable", + "set_only_once": 1, + "fetch_if_empty": 1, + "insert_after": "gst_hsn_code", + } + ] + } + + +def get_item_custom_fields(): + """ + Custom fields that need to be added to the Quotation Item Doctype + """ + return { + "Item": [ + { + "fieldname": "is_production_item", + "fieldtype": "Check", + "label": "Is Production Item", + "insert_after": "stock_uom", + }, + { + "fieldname": "sales_type", + "fieldtype": "Link", + "label": "Sales Type", + "options": "Sales Type", + "insert_after": "is_production_item", + }, + { + "fieldname": "hireable", + "fieldtype": "Check", + "label": "Hireable", + "fetch_from": "item_group.hireable", + "set_only_once": 1, + "insert_after": "gst_hsn_code", + }, + { + "fieldname": "service_item", + "fieldtype": "Link", + "label": "Service Item", + "options": "Item", + "read_only": 1, + "insert_after": "item_group", + }, + { + "fieldname": "item_audit_notification", + "fieldtype": "Check", + "label": "Periodic Notification for Asset Auditing ", + "depends_on": "eval:doc.is_fixed_asset == 1", + "insert_after": "asset_category", + }, + { + "fieldname": "item_notification_frequency", + "fieldtype": "Select", + "label": "Notification Frequency", + "options": "\nMonthly\nTrimonthly\nQuarterly\nHalf Yearly\nYearly", + "depends_on": "eval:doc.item_audit_notification == 1", + "insert_after": "item_audit_notification", + }, + { + "fieldname": "item_notification_template", + "fieldtype": "Link", + "label": "Notification Template", + "options": "Email Template", + "depends_on": "eval:doc.item_audit_notification == 1", + "insert_after": "item_notification_frequency", + }, + { + "fieldname": "start_notification_from", + "fieldtype": "Select", + "label": "Start Notification From", + "options": "\nJanuary\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember", + "depends_on": "eval:doc.item_audit_notification == 1", + "insert_after": "item_audit_notification", + }, + { + "fieldname": "is_makeup_item", + "fieldtype": "Check", + "label": "Is Makeup Item", + "insert_after": "is_exempt", + }, + ] + } + + +def get_employee_custom_fields(): + """ + Custom fields that need to be added to the Employee Doctype + """ + return { + "Employee": [ + { + "fieldname": "bureau", + "fieldtype": "Link", + "options": "Bureau", + "label": "Bureau", + "insert_after": "last_name", + }, + { + "fieldname": "stringer_type", + "fieldtype": "Link", + "options": "Stringer Type", + "label": "Stringer Type", + "insert_after": "Bureau", + }, + { + "fieldname": "leave_policy", + "fieldtype": "Link", + "options": "Leave Policy", + "label": "Leave Policy", + "insert_after": "attendance_device_id", + }, + { + "fieldname": "leave_policy_name", + "fieldtype": "Data", + "label": "Title", + "fetch_from": "leave_policy.title", + "insert_after": "leave_policy", + "read_only": 1, + }, + { + "fieldname": "name_of_father", + "fieldtype": "Data", + "label": "Father's Name", + "insert_after": "date_of_birth", + }, + { + "fieldname": "name_of_spouse", + "fieldtype": "Data", + "label": "Spouse's Name", + "insert_after": "name_of_father", + }, + { + "fieldname": "pincode", + "fieldtype": "Data", + "label": "Pincode", + "insert_after": "address_section", + }, + { + "fieldname": "aadhar_id", + "fieldtype": "Data", + "label": "Aadhar Id", + "insert_after": "marital_status", + }, + { + "fieldname": "date_of_appointment", + "fieldtype": "Date", + "label": "Date of Appointment", + "insert_after": "date_of_joining", + }, + { + "fieldname": "nominee_details_section", + "fieldtype": "Section Break", + "label": "Nominee Details", + "insert_after": "iban", + }, + { + "fieldname": "nominee_details", + "fieldtype": "Table", + "label": "Nominee Details", + "options": "Nominee Details", + "insert_after": "nominee_details_section", + }, + { + "fieldname": "additional_information_section", + "fieldtype": "Section Break", + "label": _("Additional Information"), + "insert_after": "place_of_issue", + "collapsible": 1, + }, + { + "fieldname": "physical_disabilities", + "fieldtype": "Select", + "label": "Do you have a physical disability", + "options": "Yes\nNo", + "default": "No", + "insert_after": "additional_information_section", + }, + { + "fieldname": "disabilities", + "fieldtype": "Data", + "label": "Please specify the disability", + "insert_after": "physical_disabilities", + "depends_on": "eval:doc.physical_disabilities == 'Yes'", + }, + { + "fieldname": "marital_indebtness", + "fieldtype": "Select", + "options": "Yes\nNo", + "default": "No", + "label": "Do you have marital indebtedness", + "insert_after": "disabilities", + }, + { + "fieldname": "training_status", + "fieldtype": "Select", + "options": "Not Started\nIn Progress\nCompleted\nNot Completed\nPartially Completed", + "label": "Training Status", + "insert_after": "status", + }, + { + "fieldname": "court_proceedings", + "fieldtype": "Select", + "options": "Yes\nNo", + "default": "No", + "label": "Are there any ongoing court proceedings", + "insert_after": "marital_indebtness", + }, + { + "fieldname": "court_proceedings_details", + "fieldtype": "Small Text", + "label": "Court Proceedings Details", + "insert_after": "court_proceedings", + "depends_on": "eval:doc.court_proceedings == 'Yes'", + }, + { + "fieldname": "column_break_travel", + "fieldtype": "Column Break", + "insert_after": "court_proceedings_details", + }, + { + "fieldname": "are_you_willing_to_travel", + "label": "Are you willing to travel", + "fieldtype": "Check", + "insert_after": "column_break_travel", + }, + { + "fieldname": "in_india", + "label": "In India", + "fieldtype": "Select", + "options": "Yes\nNo", + "default": "No", + "insert_after": "are_you_willing_to_travel", + "depends_on": "eval:doc.are_you_willing_to_travel == 1", + }, + { + "fieldname": "abroad", + "label": "Abroad", + "fieldtype": "Select", + "options": "Yes\nNo", + "default": "No", + "insert_after": "in_india", + "depends_on": "eval:doc.are_you_willing_to_travel == 1", + }, + { + "fieldname": "state_restrictions_problems", + "label": "State Restrictions/Problems if any", + "fieldtype": "Data", + "insert_after": "abroad", + "depends_on": "eval:doc.are_you_willing_to_travel == 1", + }, + { + "fieldname": "places_to_travel", + "label": "Places/Countries of your choice where you'd like to travel on job", + "fieldtype": "Data", + "insert_after": "state_restrictions_problems", + }, + { + "fieldname": "are_you_related_to_employee", + "label": "Are you related to any of our employees", + "fieldtype": "Check", + "insert_after": "places_to_travel", + }, + { + "fieldname": "related_employee_name", + "label": "Related Employee Name", + "fieldtype": "Data", + "insert_after": "are_you_related_to_employee", + "depends_on": "eval:doc.are_you_related_to_employee == 1", + }, + { + "fieldname": "documents_tab", + "fieldtype": "Tab Break", + "label": "Documents", + "insert_after": "internal_work_history", + }, + { + "fieldname": "employee_documents", + "fieldtype": "Table", + "label": "Employee Documents", + "options": "Employee Documents", + "insert_after": "documents_tab", + }, + { + "fieldname": "no_of_children", + "fieldtype": "Int", + "label": "No.of Children", + "insert_after": "marital_status", + }, + { + "fieldname": "company_number", + "fieldtype": "Data", + "label": "Company Mobile Number", + "options": "Phone", + "insert_after": "cell_number", + }, + { + "fieldname": "landmark", + "fieldtype": "Data", + "label": "Landmark", + "insert_after": "current_address", + }, + { + "fieldname": "landmark_per", + "fieldtype": "Data", + "label": "Landmark", + "insert_after": "permanent_address", + }, + { + "fieldname": "emergency_contact_name", + "fieldtype": "Data", + "label": "Emergency Contact Name", + "insert_after": "person_to_be_contacted", + }, + { + "fieldname": "emergency_phone", + "fieldtype": "Data", + "label": "Emergency Phone", + "insert_after": "emergency_phone_number", + }, + { + "fieldname": "relation_emergency", + "fieldtype": "Data", + "label": "Relation", + "insert_after": "relation", + }, + { + "fieldname": "assessment_officer", + "fieldtype": "Link", + "options": "Employee", + "label": "Assessment Officer", + "insert_after": "reports_to", + }, + ], + "Employee External Work History": [ + { + "fieldname": "period_from", + "fieldtype": "Date", + "label": "Period From", + "insert_after": "designation", + }, + { + "fieldname": "period_to", + "fieldtype": "Date", + "label": "Period To", + "insert_after": "period_from", + }, + { + "fieldname": "last_position_held", + "fieldtype": "Data", + "label": "Last Position Held", + "insert_after": "period_to", + }, + { + "fieldname": "job_responsibility", + "fieldtype": "Small Text", + "label": "Job Responsibility", + "insert_after": "last_position_held", + }, + { + "fieldname": "designation_of_immediate_superior", + "fieldtype": "Data", + "label": "Designation Of Immediate Superior", + "insert_after": "job_responsibility", + }, + { + "fieldname": "gross_salary_drawn", + "fieldtype": "Float", + "label": "Gross Salary Drawn ", + "insert_after": "designation_of_immediate_superior", + }, + { + "fieldname": "reason_for_leaving", + "fieldtype": "Small Text", + "label": "Reason For Leaving", + "insert_after": "gross_salary_drown", + }, + ], + } + + +def get_voucher_entry_custom_fields(): + """ + Custom fields that need to be added to the Employee Doctype + """ + return { + "Voucher Entry": [ + { + "fieldname": "bureau", + "fieldtype": "Link", + "options": "Bureau", + "label": "Bureau", + "insert_after": "balance", + }, + { + "fieldname": "department", + "fieldtype": "Link", + "options": "Department", + "label": "Department", + "insert_after": "company", + }, + ] + } + + +def get_expected_skill_set_custom_fields(): + """ + Custom fields that need to be added to the Expected Skill Set Doctype + """ + return { + "Expected Skill Set": [ + { + "fieldname": "weight", + "fieldtype": "Float", + "label": "Weight", + "insert_after": "description", + } + ] + } + + +def get_hd_ticket_custom_fields(): + """ + Custom fields to be added to the HD Ticket Doctype + """ + return { + "HD Ticket": [ + { + "fieldname": "ticket_section_break", + "fieldtype": "Section Break", + "label": "", + "insert_after": "ticket_split_from", + }, + { + "fieldname": "spare_part_needed", + "fieldtype": "Check", + "label": "Spare Part Needed", + "insert_after": "ticket_section_break", + }, + { + "fieldname": "spare_part_item_table", + "fieldtype": "Table", + "label": "Spare Part Items", + "insert_after": "spare_part_needed", + "options": "Spare Part Item", + "depends_on": "eval:doc.spare_part_needed == 1", + }, + { + "fieldname": "raised_for", + "fieldtype": "Link", + "options": "User", + "label": "Raised For (Email)", + "insert_after": "raised_by", + }, + { + "fieldname": "attach", + "fieldtype": "Attach", + "label": "Attachments", + "insert_after": "agent_group", + }, + ] + } + + +def get_interview_round_custom_fields(): + """ + Custom fields that need to be added to the Interview Round Child Table + """ + return { + "Interview Round": [ + { + "fieldname": "expected_questions", + "fieldtype": "Table", + "label": "Interview Questions", + "options": "Interview Questions", + "insert_after": "expected_skill_set", + } + ] + } + + +def get_interview_custom_fields(): + """ + Custom fields that need to be added to the Interview Doctype + """ + return { + "Interview": [ + { + "fieldname": "department", + "fieldtype": "Link", + "options": "Department", + "label": "Department", + "insert_after": "applicant_name", + "fetch_from": "job_applicant.department", + }, + { + "fieldname": "applicant_name", + "fieldtype": "Data", + "label": "Applicant Name", + "insert_after": "job_applicant", + "fetch_from": "job_applicant.applicant_name", + "read_only": 1, + }, + { + "fieldname": "applicant_email", + "fieldtype": "Data", + "label": "Applicant Email ID", + "insert_after": "job_applicant", + "fetch_from": "job_applicant.email_id", + "hidden": 1, + }, + ] + } + + +def get_job_requisition_custom_fields(): + """ + Custom fields that need to be added to the Job Requisition Doctype + """ + return { + "Job Requisition": [ + { + "fieldname": "work_details", + "fieldtype": "Section Break", + "label": "Work Details", + "insert_after": "requested_by_designation", + }, + { + "fieldname": "employment_type", + "fieldtype": "Link", + "options": "Employment Type", + "label": "Employment Type", + "insert_after": "department", + "permlevel": 1, + }, + { + "fieldname": "no_of_days_off", + "fieldtype": "Int", + "label": "Number of Days Off", + "description": "Number Of Days Off within a 30-day Period", + "insert_after": "work_details", + "permlevel": 1, + }, + { + "fieldname": "work_details_column_break", + "fieldtype": "Column Break", + "label": "", + "insert_after": "min_experience", + }, + { + "fieldname": "is_work_shift_needed", + "fieldtype": "Check", + "label": "Is Shift Work Needed", + "insert_after": "work_details_column_break", + "permlevel": 1, + }, + { + "fieldname": "travel_required", + "fieldtype": "Check", + "label": "Travel required for the position", + "insert_after": "is_work_shift_needed", + "permlevel": 1, + }, + { + "fieldname": "driving_license_needed", + "fieldtype": "Check", + "label": "Driving License Needed for this Position", + "depends_on": "eval:doc.travel_required == 1", + "insert_after": "travel_required", + "permlevel": 1, + }, + { + "fieldname": "license_type", + "fieldtype": "Link", + "label": "License Type", + "options": "License Type", + "depends_on": "eval:doc.driving_license_needed == 1", + "mandatory_depends_on": "eval:doc.driving_license_needed == 1", + "insert_after": "driving_license_needed", + "permlevel": 1, + }, + { + "fieldname": "education", + "fieldtype": "Section Break", + "label": "Education and Qualification Details", + "insert_after": "license_type", + }, + { + "fieldname": "min_education_qual", + "fieldtype": "Table MultiSelect", + "label": "Preferred Educational Qualification", + "options": "Educational Qualifications", + "insert_after": "education", + "permlevel": 1, + }, + { + "fieldname": "min_experience", + "fieldtype": "Float", + "label": "Minimum Experience Required (Years)", + "insert_after": "no_of_days_off", + "permlevel": 1, + }, + { + "fieldname": "reset_column", + "fieldtype": "Section Break", + "label": "", + "insert_after": "license_type", + }, + { + "fieldname": "language_proficiency", + "fieldtype": "Table", + "options": "Language Proficiency", + "label": "Language Proficiency", + "insert_after": "reset_column", + "permlevel": 1, + }, + { + "fieldname": "skill_proficiency", + "fieldtype": "Table", + "options": "Skill Proficiency", + "label": "Skill Proficiency", + "description": "Proficency selected here is the minimum proficency needed.", + "insert_after": "language_proficiency", + }, + { + "fieldname": "job_description_template", + "fieldtype": "Link", + "label": "Job Description Template", + "options": "Job Description Template", + "insert_after": "job_description_tab", + "permlevel": 1, + }, + { + "fieldname": "request_for", + "label": "Request For", + "fieldtype": "Select", + "options": "Employee Replacement\nExisting Vacancy\nNew Vacancy", + "insert_after": "naming_series", + }, + { + "fieldname": "employee_left", + "label": "Employees Who Replaced", + "fieldtype": "Link", + "options": "Employee", + "insert_after": "request_for", + "depends_on": "eval:doc.request_for == 'Employee Replacement'", + }, + { + "fieldname": "relieving_date", + "fieldtype": "Date", + "label": "Relieving Date", + "insert_after": "employee_left", + "fetch_from": "employee_left.relieving_date", + "depends_on": "eval:doc.request_for == 'Employee Replacement'", + "read_only": 1, + }, + { + "fieldname": "interview", + "fieldtype": "Section Break", + "label": "Interview Details", + "insert_after": "requested_by_designation", + }, + { + "fieldname": "interview_rounds", + "fieldtype": "Table MultiSelect", + "options": "Interview Rounds", + "label": "Interview Rounds", + "insert_after": "interview", + "permlevel": 1, + }, + { + "fieldname": "location", + "label": "Preferred Location", + "fieldtype": "Link", + "options": "Location", + "insert_after": "employment_type", + "permlevel": 1, + }, + { + "fieldname": "job_title", + "fieldtype": "Data", + "label": "Job Title", + "insert_after": "job_description_template", + "reqd": 1, + }, + { + "fieldname": "suggested_designation", + "fieldtype": "Link", + "label": "Suggested Designation", + "options": "Designation", + "insert_after": "request_for", + "permlevel": 2, + "depends_on": "eval:doc.request_for == 'New Vacancy'", + }, + { + "fieldname": "suggestions", + "fieldtype": "Small Text", + "label": "Suggestions/Feedback", + "insert_after": "description", + "permlevel": 3, + }, + { + "fieldname": "publish_on_job_section", + "fieldtype": "Section Break", + "label": "", + "insert_after": "requested_by_designation", + }, + { + "fieldname": "publish_on_job_opening", + "fieldtype": "Check", + "default": "1", + "label": "Publish on Job Opening", + "insert_after": "publish_on_job_section", + "permlevel": 4, + }, + ] + } + + +def get_job_applicant_custom_fields(): + """ + Custom fields that need to be added to the Job Applicant Doctype + """ + return { + "Job Applicant": [ + { + "fieldname": "date_of_birth", + "fieldtype": "Date", + "label": "Date of Birth", + "insert_after": "email_id", + }, + { + "fieldname": "gender", + "fieldtype": "Link", + "label": "Gender", + "options": "Gender", + "insert_after": "date_of_birth", + }, + { + "fieldname": "willing_to_work_on_location", + "fieldtype": "Check", + "label": "Willing to Work in the Selected Location?", + "insert_after": "country", + }, + { + "fieldname": "father_name", + "fieldtype": "Data", + "label": "Father's Name", + "insert_after": "job_title", + }, + { + "fieldname": "marital_status", + "fieldtype": "Select", + "label": "Marital Status", + "options": "\nSingle\nMarried\nDivorced\nWidowed", + "insert_after": "location", + }, + { + "fieldname": "current_address_session_break", + "fieldtype": "Section Break", + "label": "Current Address", + "insert_after": "marital_status", + }, + { + "fieldname": "current_address", + "fieldtype": "Small Text", + "label": "Current Address", + "insert_after": "current_address_session_break", + }, + { + "fieldname": "current_mobile_no", + "fieldtype": "Data", + "options": "Phone", + "label": "Mobile Number", + "insert_after": "current_address", + }, + { + "fieldname": "current_column_break", + "fieldtype": "Column Break", + "label": "", + "insert_after": "current_mobile_no", + }, + { + "fieldname": "permanent_address_session_break", + "fieldtype": "Section Break", + "label": "Permanent Address", + "insert_after": "current_column_break", + }, + { + "fieldname": "current_period_from", + "fieldtype": "Int", + "label": "Period of From stay", + "insert_after": "current_column_break", + }, + { + "fieldname": "current_period_to", + "fieldtype": "Int", + "label": "Period of To stay", + "insert_after": "current_period_from", + }, + { + "fieldname": "current_residence_no", + "fieldtype": "Int", + "label": "Residence Number", + "insert_after": "current_period_to", + }, + { + "fieldname": "permanent_address_session_break", + "fieldtype": "Section Break", + "label": "Permanent Address", + "insert_after": "current_residence_no", + }, + { + "fieldname": "permanent_address", + "fieldtype": "Small Text", + "label": "Permanent Address", + "insert_after": "permanent_address_session_break", + }, + { + "fieldname": "permanent_column_break", + "fieldtype": "Column Break", + "label": "", + "insert_after": "permanent_address", + }, + { + "fieldname": "permanent_residence_no", + "fieldtype": "Int", + "label": "Residence Number", + "insert_after": "permanent_address", + }, + { + "fieldname": "permanent_column_break", + "fieldtype": "Column Break", + "label": "", + "insert_after": "permanent_residence_no", + }, + { + "fieldname": "permanent_period_from", + "fieldtype": "Int", + "label": "Period of From stay", + "insert_after": "permanent_column_break", + }, + { + "fieldname": "permanent_period_to", + "fieldtype": "Int", + "label": "Period of To stay", + "insert_after": "permanent_period_from", + }, + { + "fieldname": "permananet_email_id", + "fieldtype": "Data", + "options": "Email", + "label": "Email ID", + "insert_after": "permanent_period_to", + }, + { + "fieldname": "email_address_session_break", + "fieldtype": "Section Break", + "label": "", + "insert_after": "permanent_column_break", + }, + { + "fieldname": "email_id_1", + "fieldtype": "Data", + "options": "Email", + "label": "Email ID", + "insert_after": "email_address_session_break", + }, + { + "fieldname": "min_education_qual", + "fieldtype": "Link", + "label": "Educational Qualification", + "options": "Educational Qualification", + "insert_after": "details", + }, + { + "fieldname": "details", + "fieldtype": "Section Break", + "label": "Qualification Details", + "insert_after": "applicant_rating", + }, + { + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "insert_after": "designation", + }, + { + "fieldname": "min_experience", + "fieldtype": "Float", + "label": "Work Experience (Years)", + "insert_after": "details_column_break", + }, + { + "fieldname": "details_column_break", + "fieldtype": "Column Break", + "label": "", + "insert_after": "min_education_qual", + }, + { + "fieldname": "reset_column", + "fieldtype": "Section Break", + "label": "", + "insert_after": "min_experience", + }, + { + "fieldname": "language_proficiency", + "fieldtype": "Table", + "options": "Language Proficiency", + "label": "Language Proficiency", + "insert_after": "min_experience", + }, + { + "fieldname": "skill_proficiency", + "fieldtype": "Table", + "options": "Skill Proficiency", + "label": "Skill Proficiency", + "insert_after": "language_proficiency", + }, + { + "fieldname": "education_qualification", + "fieldtype": "Table", + "options": "Education Qualification", + "label": "Education Qualification", + "insert_after": "applicant_interview_rounds", + }, + { + "fieldname": "professional_certification", + "fieldtype": "Table", + "options": "Professional Certification", + "label": "Professional Certification", + "insert_after": "education_qualification", + }, + { + "fieldname": "location", + "label": "Location", + "fieldtype": "Link", + "options": "Location", + "insert_after": "status", + }, + { + "fieldname": "interview_process_break", + "fieldtype": "Section Break", + "label": "Interview Process", + "insert_after": "skill_proficiency", + }, + { + "fieldname": "applicant_interview_rounds", + "fieldtype": "Table", + "options": "Applicant Interview Round", + "label": "Interview Rounds", + "insert_after": "interview_process_break", + }, + { + "fieldname": "current_employer_tab_break", + "fieldtype": "Tab Break", + "label": "Current Employer Details", + "insert_after": "upper_range", + }, + { + "fieldname": "current_employer", + "fieldtype": "Section Break", + "label": "Current Employer / Immediate Previous Employer", + "insert_after": "current_employer_tab_break", + }, + { + "fieldname": "name_of_employer", + "fieldtype": "Data", + "label": "Name of Employer", + "insert_after": "current_employer", + }, + { + "fieldname": "employment_period_from", + "fieldtype": "Int", + "label": "Employment Period From", + "insert_after": "name_of_employer", + }, + { + "fieldname": "employee_code", + "fieldtype": "Data", + "label": "Employee Code", + "insert_after": "name_of_employer", + }, + { + "fieldname": "telephone_no", + "fieldtype": "Data", + "options": "Phone", + "label": "Telephone No", + "insert_after": "employee_code", + }, + { + "fieldname": "employment_period_from", + "fieldtype": "Int", + "label": "Employment Period From", + "insert_after": "telephone_no", + }, + { + "fieldname": "employment_period_to", + "fieldtype": "Int", + "label": "Employment Period To", + "insert_after": "employment_period_from", + }, + { + "fieldname": "current_employer_1_column_break", + "fieldtype": "Column Break", + "label": "", + "insert_after": "employment_period_to", + }, + { + "fieldname": "address_of_employer", + "fieldtype": "Small Text", + "label": "Address of Employer", + "insert_after": "employment_period_to", + }, + { + "fieldname": "current_employer_1_column_break", + "fieldtype": "Column Break", + "label": "", + "insert_after": "address_of_employer", + }, + { + "fieldname": "first_salary_drawn", + "fieldtype": "Currency", + "label": "First Salary Drawn", + "insert_after": "current_employer_1_column_break", + }, + { + "fieldname": "last_salary_drawn", + "fieldtype": "Currency", + "label": "Last Salary Drawn", + "insert_after": "first_salary_drawn", + }, + { + "fieldname": "current_designation", + "fieldtype": "Data", + "label": "Designation", + "insert_after": "current_employer_1_column_break", + }, + { + "fieldname": "reference_taken", + "fieldtype": "Select", + "label": "Can I take Reference Now?", + "options": "\nYes\nNo", + "insert_after": "current_designation", + }, + { + "fieldname": "was_this_position", + "fieldtype": "Select", + "label": "Was this Position Permanent,Temporary,Contractual?", + "options": "\nPermanent\nTemporary\nContractual", + "insert_after": "reference_taken", + }, + { + "fieldname": "duties_and_reponsibilities", + "fieldtype": "Small Text", + "label": "Duties and Responsibilities", + "insert_after": "column_break_agency", + }, + { + "fieldname": "current_employer_column_break", + "fieldtype": "Column Break", + "label": "", + "insert_after": "was_this_position", + }, + { + "fieldname": "current_department", + "fieldtype": "Data", + "label": "Department", + "insert_after": "current_employer_column_break", + }, + { + "fieldname": "manager_name", + "fieldtype": "Data", + "label": "Manager's Name", + "insert_after": "current_department", + }, + { + "fieldname": "manager_contact_no", + "fieldtype": "Data", + "options": "Phone", + "label": "Manager's Contact No", + "insert_after": "manager_name", + }, + { + "fieldname": "manager_email", + "fieldtype": "Data", + "options": "Email", + "label": "Manager's Email", + "insert_after": "manager_contact_no", + }, + { + "fieldname": "reason_for_leaving", + "fieldtype": "Small Text", + "label": "Reason For Leaving", + "insert_after": "duties_and_reponsibilities", + }, + { + "fieldname": "agency_details", + "fieldtype": "Small Text", + "label": "Agency Details (if Temporary or Contractual)", + "insert_after": "address_of_employer", + }, + { + "fieldname": "column_break_agency", + "fieldtype": "Column Break", + "label": "", + "insert_after": "agency_details", + }, + { + "fieldname": "previous_emp", + "fieldtype": "Section Break", + "insert_after": "manager_email", + }, + { + "fieldname": "address_of_employer", + "fieldtype": "Small Text", + "label": "Address of Employer", + "insert_after": "previous_emp", + }, + { + "fieldname": "previous_emplyoment", + "fieldtype": "Section Break", + "label": "Previous Employment History", + "insert_after": "previous_emp", + }, + { + "fieldname": "prev_emp_his", + "fieldtype": "Table", + "options": "Previous Employment History", + "insert_after": "previous_emplyoment", + }, + { + "fieldname": "more_details_tab_break", + "fieldtype": "Tab Break", + "label": "More Details", + "insert_after": "prev_emp_his", + }, + { + "fieldname": "current_salary", + "fieldtype": "Currency", + "label": "Current Salary", + "insert_after": "more_details_tab_break", + }, + { + "fieldname": "current_salary_column_break", + "fieldtype": "Column Break", + "label": "", + "insert_after": "current_salary", + }, + { + "fieldname": "expected_salary", + "fieldtype": "Currency", + "label": "Expected Salary", + "insert_after": "current_salary_column_break", + }, + { + "fieldname": "expected_salary_column_break", + "fieldtype": "Column Break", + "label": "", + "insert_after": "expected_salary", + }, + { + "fieldname": "telephone_number", + "fieldtype": "Data", + "options": "Phone", + "label": "Telephone Number", + "insert_after": "expected_salary_column_break", + }, + { + "fieldname": "other_achievments_session_break", + "fieldtype": "Section Break", + "label": "", + "insert_after": "current_employer_tab_break", + }, + { + "fieldname": "other_achievments", + "fieldtype": "Small Text", + "label": "Please add details of Professional and other achievements,awards and accomplishments,if any", + "insert_after": "other_achievments_session_break", + }, + { + "fieldname": "interviewed_session_break", + "fieldtype": "Section Break", + "label": "Have you been interviewed before by Madhyamam Group?If yes, Please give details below :", + "insert_after": "other_achievments", + }, + { + "fieldname": "position", + "fieldtype": "Data", + "label": "Position", + "insert_after": "interviewed_session_break", + }, + { + "fieldname": "interviewed_date", + "fieldtype": "Date", + "label": "Date", + "insert_after": "position", + }, + { + "fieldname": "interviewed_column_break", + "fieldtype": "Column Break", + "label": "", + "insert_after": "interviewed_date", + }, + { + "fieldname": "interviewed_location", + "fieldtype": "Link", + "options": "Location", + "label": "Location", + "insert_after": "interviewed_column_break", + }, + { + "fieldname": "interviewed_outcome", + "fieldtype": "Data", + "label": "Outcome", + "insert_after": "interviewed_location", + }, + { + "fieldname": "travel_session_break", + "fieldtype": "Section Break", + "label": "Are you willing to travel :", + "insert_after": "interviewed_outcome", + }, + { + "fieldname": "in_india", + "fieldtype": "Check", + "label": "In India", + "insert_after": "travel_session_break", + }, + { + "fieldname": "state_restriction", + "fieldtype": "Data", + "label": "State Restriction If any", + "depends_on": "eval:doc.in_india", + "insert_after": "in_india", + }, + { + "fieldname": "india_column_break", + "fieldtype": "Column Break", + "label": "", + "insert_after": "state_restriction", + }, + { + "fieldname": "abroad", + "fieldtype": "Check", + "label": "Abroad", + "insert_after": "india_column_break", + }, + { + "fieldname": "related_session_break", + "fieldtype": "Section Break", + "label": "Are you related to any of employee of the Madhyamam Group? If yes,please give details :", + "insert_after": "state_restriction", + }, + { + "fieldname": "related_employee", + "fieldtype": "Data", + "label": "Name", + "insert_after": "related_session_break", + }, + { + "fieldname": "related_employee_org", + "fieldtype": "Data", + "label": "Organization", + "insert_after": "related_employee", + }, + { + "fieldname": "related_column_break", + "fieldtype": "Column Break", + "label": "", + "insert_after": "related_employee_org", + }, + { + "fieldname": "related_employee_pos", + "fieldtype": "Data", + "label": "Position", + "insert_after": "related_column_break", + }, + { + "fieldname": "related_employee_rel", + "fieldtype": "Data", + "label": "Relationship", + "insert_after": "related_employee_pos", + }, + { + "fieldname": "prof_session_break", + "fieldtype": "Section Break", + "label": "", + "insert_after": "related_employee_rel", + }, + { + "fieldname": "professional_org", + "fieldtype": "Small Text", + "label": "Are you a member of any Professional Organization? If yes, Please give details :", + "insert_after": "prof_session_break", + }, + { + "fieldname": "political_org", + "fieldtype": "Small Text", + "label": "Are you a member of any Political Organization? If yes, Please give details :", + "insert_after": "professional_org", + }, + { + "fieldname": "specialised_training", + "fieldtype": "Small Text", + "label": "Have you attended any specialised training program?If yes, Please give detais :", + "insert_after": "political_org", + }, + { + "fieldname": "is_form_submitted", + "fieldtype": "Check", + "label": "Is Form Submitted", + "read_only": 1, + "insert_after": "specialised_training", + }, + { + "fieldname": "payslip_month_1", + "fieldtype": "Attach", + "label": "Payslip - Month 1", + "insert_after": "section_break_001", + }, + { + "fieldname": "break_oo1", + "fieldtype": "Column Break", + "insert_after": "payslip_month_1", + }, + { + "fieldname": "payslip_month_2", + "fieldtype": "Attach", + "label": "Payslip - Month 2", + "insert_after": "break_oo1", + }, + { + "fieldname": "section_break_001", + "fieldtype": "Section Break", + "label": "", + "insert_after": "prev_emp_his", + }, + { + "fieldname": "break_oo2", + "fieldtype": "Column Break", + "insert_after": "payslip_month_2", + }, + { + "fieldname": "payslip_month_3", + "fieldtype": "Attach", + "label": "Payslip - Month 3", + "insert_after": "break_oo2", + }, + ] + } + + +def get_contract_custom_fields(): + """ + Custom fields that need to be added to the Contract Doctype + """ + return { + "Contract": [ + { + "fieldname": "services_section", + "fieldtype": "Section Break", + "label": "Services", + "insert_after": "ip_address", + }, + { + "fieldname": "services", + "fieldtype": "Table", + "options": "Services", + "label": "Services", + "insert_after": "services_section", + "depends_on": "eval:doc.party_type == 'Supplier'", + }, + { + "fieldname": "total_amount", + "fieldtype": "Currency", + "label": "Total Amount", + "insert_after": "services", + "read_only": 1, + "no_copy": 1, + }, + ] + } + + +def get_job_opening_custom_fields(): + """ + Custom fields that need to be added to the Job Opening Doctype + """ + return { + "Job Opening": [ + { + "fieldname": "job_details", + "fieldtype": "Section Break", + "label": "Job Details", + "insert_after": "location", + }, + { + "fieldname": "no_of_positions", + "fieldtype": "Int", + "label": "No of.Positions", + "insert_after": "employment_type", + }, + { + "fieldname": "no_of_days_off", + "fieldtype": "Int", + "label": "Number of Days Off", + "insert_after": "job_details", + "non_negative": 1, + }, + { + "fieldname": "preffered_location", + "label": "Preffered Location", + "fieldtype": "Link", + "options": "Location", + "insert_after": "no_of_days_off", + }, + { + "fieldname": "job_details_column_break", + "fieldtype": "Column Break", + "label": "", + "insert_after": "preffered_location", + }, + { + "fieldname": "travel_required", + "fieldtype": "Check", + "label": "Travel required for the position", + "insert_after": "job_details_column_break", + }, + { + "fieldname": "driving_license_needed", + "fieldtype": "Check", + "label": "Driving License Needed for this Position", + "depends_on": "eval:doc.travel_required == 1", + "insert_after": "travel_required", + }, + { + "fieldname": "license_type", + "fieldtype": "Link", + "label": "License Type", + "options": "License Type", + "depends_on": "eval:doc.driving_license_needed == 1", + "insert_after": "driving_license_needed", + }, + { + "fieldname": "is_work_shift_needed", + "fieldtype": "Check", + "label": "Is Shift Work Needed", + "insert_after": "license_type", + }, + { + "fieldname": "qualification_details", + "fieldtype": "Section Break", + "label": "Education and Qualification Details", + "insert_after": "license_type", + }, + { + "fieldname": "min_education_qual", + "fieldtype": "Table MultiSelect", + "label": "Preferred Educational Qualification", + "options": "Educational Qualifications", + "insert_after": "qualification_details", + }, + { + "fieldname": "qualification_details_column_break", + "fieldtype": "Column Break", + "label": "", + "insert_after": "min_education_qual", + }, + { + "fieldname": "min_experience", + "fieldtype": "Float", + "label": "Minimum Experience Required (Years)", + "insert_after": "is_work_shift_needed", + }, + { + "fieldname": "proficiency_break", + "fieldtype": "Section Break", + "label": "", + "insert_after": "min_experience", + }, + { + "fieldname": "language_proficiency", + "fieldtype": "Table", + "options": "Language Proficiency", + "label": "Language Proficiency", + "insert_after": "proficiency_break", + "description": "Proficency selected here is the minimum proficency needed", + }, + { + "fieldname": "skill_proficiency", + "fieldtype": "Table", + "options": "Skill Proficiency", + "label": "Skill Proficiency", + "insert_after": "language_proficiency", + "description": "Proficency selected here is the minimum proficency needed", + }, + { + "fieldname": "interview_details_sb", + "fieldtype": "Section Break", + "label": "Interview Details", + "insert_after": "skill_proficiency", + }, + { + "fieldname": "interview_rounds", + "fieldtype": "Table MultiSelect", + "label": "Interview Rounds", + "options": "Interview Rounds", + "insert_after": "interview_details_sb", + }, + ] + } + + +def get_company_custom_fields(): + """ + Custom fields that need to be added to the Company Doctype + """ + return { + "Company": [ + { + "fieldname": "company_policy_tab", + "fieldtype": "Tab Break", + "label": "Company Policy", + "insert_after": "dashboard_tab", + }, + { + "fieldname": "company_policy", + "fieldtype": "Text Editor", + "label": "Company Policy", + "insert_after": "company_policy_tab", + }, + { + "fieldname": "exception_budget_column", + "fieldtype": "Column Break", + "label": "", + "insert_after": "exception_budget_approver_role", + }, + { + "fieldname": "exchange_rate_to_inr", + "fieldtype": "Float", + "label": "Budget Exchange Rate to INR", + "insert_after": "exception_budget_column", + }, + { + "fieldname": "exchange_rate_to_inr", + "fieldtype": "Float", + "label": "Exchange Rate to INR", + "insert_after": "exchange_gain_loss_account", + "description": "1 Unit of Company Currency = [?] INR", + }, + ] + } + + +def get_employee_performance_feedback(): + """Custom fields that need to be added to + Employee Performance Feedback doctype + """ + return { + "Employee Performance Feedback": [ + { + "fieldname": "employee_section", + "fieldtype": "Section Break", + "label": "", + "insert_after": "feedback_ratings", + }, + { + "fieldname": "employee_total_score", + "fieldtype": "Float", + "label": "Total Score", + "insert_after": "employee_section", + }, + { + "fieldname": "employee_column", + "fieldtype": "Column Break", + "label": "", + "insert_after": "employee_total_score", + }, + { + "fieldname": "employee_average_score", + "fieldtype": "Float", + "label": "Average Score", + "insert_after": "employee_column", + }, + { + "fieldname": "emp_section", + "fieldtype": "Section Break", + "label": "", + "insert_after": "employee_average_score", + }, + { + "fieldname": "company_criteria", + "fieldtype": "Table", + "options": "Employee Feedback Rating", + "label": "Company Criteria", + "insert_after": "emp_section", + }, + { + "fieldname": "company_section", + "fieldtype": "Section Break", + "label": "", + "insert_after": "company_criteria", + }, + { + "fieldname": "company_total_score", + "fieldtype": "Float", + "label": "Total Score", + "insert_after": "company_section", + }, + { + "fieldname": "company_column", + "fieldtype": "Column Break", + "label": "", + "insert_after": "company_total_score", + }, + { + "fieldname": "company_average_score", + "fieldtype": "Float", + "label": "Average Score", + "insert_after": "company_column", + }, + { + "fieldname": "dept_section", + "fieldtype": "Section Break", + "label": "", + "insert_after": "company_average_score", + }, + { + "fieldname": "department_criteria", + "fieldtype": "Table", + "options": "Employee Feedback Rating", + "label": "Department Criteria", + "insert_after": "dept_section", + }, + { + "fieldname": "dept_section1", + "fieldtype": "Section Break", + "label": "", + "insert_after": "department_criteria", + }, + { + "fieldname": "department_total_score", + "fieldtype": "Float", + "label": "Total Score", + "insert_after": "dept_section1", + }, + { + "fieldname": "dept_column", + "fieldtype": "Column Break", + "label": "", + "insert_after": "department_total_score", + }, + { + "fieldname": "department_average_score", + "fieldtype": "Float", + "label": "Average Score", + "insert_after": "dept_column", + }, + ] + } -def get_employee_performance_feedback(): - '''Custom fields that need to be added to - Employee Performance Feedback doctype - ''' - return { - "Employee Performance Feedback" : [ - { - "fieldname": "employee_section", - "fieldtype": "Section Break", - "label": "", - "insert_after": "feedback_ratings" - }, - { - "fieldname": "employee_total_score", - "fieldtype": "Float", - "label": "Total Score", - "insert_after": "employee_section" - }, - { - "fieldname": "employee_column", - "fieldtype": "Column Break", - "label": "", - "insert_after": "employee_total_score" - }, - { - "fieldname": "employee_average_score", - "fieldtype": "Float", - "label": "Average Score", - "insert_after": "employee_column" - }, - { - "fieldname": "emp_section", - "fieldtype": "Section Break", - "label": "", - "insert_after": "employee_average_score" - }, - { - "fieldname": "company_criteria", - "fieldtype": "Table", - "options": "Employee Feedback Rating", - "label": "Company Criteria", - "insert_after": "emp_section" - }, - { - "fieldname": "company_section", - "fieldtype": "Section Break", - "label": "", - "insert_after": "company_criteria" - }, - { - "fieldname": "company_total_score", - "fieldtype": "Float", - "label": "Total Score", - "insert_after": "company_section" - }, - { - "fieldname": "company_column", - "fieldtype": "Column Break", - "label": "", - "insert_after": "company_total_score" - }, - { - "fieldname": "company_average_score", - "fieldtype": "Float", - "label": "Average Score", - "insert_after": "company_column" - }, - { - "fieldname": "dept_section", - "fieldtype": "Section Break", - "label": "", - "insert_after": "company_average_score" - }, - { - "fieldname": "department_criteria", - "fieldtype": "Table", - "options": "Employee Feedback Rating", - "label": "Department Criteria", - "insert_after": "dept_section" - }, - { - "fieldname": "dept_section1", - "fieldtype": "Section Break", - "label": "", - "insert_after": "department_criteria" - }, - { - "fieldname": "department_total_score", - "fieldtype": "Float", - "label": "Total Score", - "insert_after": "dept_section1" - }, - { - "fieldname": "dept_column", - "fieldtype": "Column Break", - "label": "", - "insert_after": "department_total_score" - }, - { - "fieldname": "department_average_score", - "fieldtype": "Float", - "label": "Average Score", - "insert_after": "dept_column" - } - ] - } def get_leave_type_custom_fields(): - ''' - Custom fields that need to be added to the Leave Type Doctype - ''' - return { - "Leave Type": [ - { - "fieldname": "min_advance_days", - "fieldtype": "Int", - "label": "Minimum Advance Days", - "description": "Specifies the minimum number of days required to apply for this leave.", - "insert_after": "max_continuous_days_allowed" - }, - { - "fieldname": "is_proof_document", - "fieldtype": "Check", - "label": "Is Proof Document Required", - "insert_after": "is_optional_leave" - - }, - { - "fieldname": "medical_leave_required", - "fieldtype": "Float", - "label": "Medical Leave Required for Days", - "depends_on": "eval:doc.is_proof_document", - "insert_after": "is_proof_document" - }, - { - "fieldname": "allow_in_notice_period", - "fieldtype": "Check", - "label": "Allow in Notice Period", - "insert_after": "is_compensatory" - - } - ] - } + """ + Custom fields that need to be added to the Leave Type Doctype + """ + return { + "Leave Type": [ + { + "fieldname": "min_advance_days", + "fieldtype": "Int", + "label": "Minimum Advance Days", + "description": "Specifies the minimum number of days required to apply for this leave.", + "insert_after": "max_continuous_days_allowed", + }, + { + "fieldname": "is_proof_document", + "fieldtype": "Check", + "label": "Is Proof Document Required", + "insert_after": "is_optional_leave", + }, + { + "fieldname": "medical_leave_required", + "fieldtype": "Float", + "label": "Medical Leave Required for Days", + "depends_on": "eval:doc.is_proof_document", + "insert_after": "is_proof_document", + }, + { + "fieldname": "allow_in_notice_period", + "fieldtype": "Check", + "label": "Allow in Notice Period", + "insert_after": "is_compensatory", + }, + ] + } + def get_employee_separation_custom_fields(): - ''' - Custom fields that need to be added to the Employee Separation Doctype - ''' - return { - "Employee Separation": [ - { - "fieldname": "employee_clearance", - "fieldtype": "Table", - "label": "Employee Clearance", - "options": "Employee Clearance", - "insert_after": "activities" - - }, - { - "fieldname": "employee_exit_status", - "fieldtype": "Select", - "label": "Employee Exit Clearance Status", - "options":"Pending\nCompleted", - "insert_after": "employee_clearance" - } - ] - } + """ + Custom fields that need to be added to the Employee Separation Doctype + """ + return { + "Employee Separation": [ + { + "fieldname": "employee_clearance", + "fieldtype": "Table", + "label": "Employee Clearance", + "options": "Employee Clearance", + "insert_after": "activities", + }, + { + "fieldname": "employee_exit_status", + "fieldtype": "Select", + "label": "Employee Exit Clearance Status", + "options": "Pending\nCompleted", + "insert_after": "employee_clearance", + }, + ] + } + def get_appraisal_template_custom_fields(): - ''' - Custom fields that need to be added to the Appraisal Template doctype - ''' - return { - "Appraisal Template": [ - { - "fieldname": "department_rating_criteria", - "fieldtype": " Table", - "options": "Employee Feedback Rating", - "label": "Department Rating Criteria", - "insert_after": "rating_criteria" - }, - { - "fieldname": "company_rating_criteria", - "fieldtype": " Table", - "options": "Employee Feedback Rating", - "label": "Company Rating Criteria", - "insert_after": "label_for_department_kra" - }, - { - "fieldname": "label_for_department_kra", - "fieldtype": "Data", - "label": "Label for Department KRA", - "insert_after": "department_rating_criteria" - }, - { - "fieldname": "label_for_company_kra", - "fieldtype": "Data", - "label": "Label for Company KRA", - "insert_after": "company_rating_criteria" - } - ] - } + """ + Custom fields that need to be added to the Appraisal Template doctype + """ + return { + "Appraisal Template": [ + { + "fieldname": "department_rating_criteria", + "fieldtype": " Table", + "options": "Employee Feedback Rating", + "label": "Department Rating Criteria", + "insert_after": "rating_criteria", + }, + { + "fieldname": "company_rating_criteria", + "fieldtype": " Table", + "options": "Employee Feedback Rating", + "label": "Company Rating Criteria", + "insert_after": "label_for_department_kra", + }, + { + "fieldname": "label_for_department_kra", + "fieldtype": "Data", + "label": "Label for Department KRA", + "insert_after": "department_rating_criteria", + }, + { + "fieldname": "label_for_company_kra", + "fieldtype": "Data", + "label": "Label for Company KRA", + "insert_after": "company_rating_criteria", + }, + { + "fieldname": "designation_section", + "fieldtype": "Section Break", + "label": "", + "insert_after": "label_for_company_kra", + }, + { + "fieldname": "assessment_officers", + "fieldtype": "Table", + "options": "Assessment Officer", + "label": "Assessment Officers", + "insert_after": "designation_section", + }, + ] + } + def get_employee_feedback_rating_custom_fields(): - ''' - Custom fields that need to be added to the Employee Feedback Rating doctype - ''' - return { - "Employee Feedback Rating": [ - { - "fieldname": "marks", - "fieldtype": " Float", - "label": "Marks out of 5", - "insert_after": "per_weightage" - } - ] - } + """ + Custom fields that need to be added to the Employee Feedback Rating doctype + """ + return { + "Employee Feedback Rating": [ + { + "fieldname": "marks", + "fieldtype": " Float", + "label": "Marks out of 5", + "insert_after": "per_weightage", + } + ] + } + def get_appraisal_custom_fields(): - ''' - Custom fields that need to be added to the Appraisal doctype - ''' - return { - "Appraisal": [ - { - "fieldname": "appraisal_summary_tab_break", - "fieldtype": "Tab Break", - "label": "Appraisal Summary", - "insert_after": "amended_from" - }, - { - "fieldname": "appraisal_summary", - "fieldtype": "HTML", - "label": "Appraisal Summary", - "insert_after": "appraisal_summary_tab_break" - }, - { + """ + Custom fields that need to be added to the Appraisal doctype + """ + return { + "Appraisal": [ + { + "fieldname": "appraisal_summary_tab_break", + "fieldtype": "Tab Break", + "label": "Appraisal Summary", + "insert_after": "amended_from", + }, + { + "fieldname": "appraisal_summary", + "fieldtype": "HTML", + "label": "Appraisal Summary", + "insert_after": "appraisal_summary_tab_break", + }, + { "fieldname": "final_assesment_tab_break", "fieldtype": "Tab Break", "label": "Final Assesment", - "insert_after": "appraisal_summary" + "insert_after": "appraisal_summary", }, { "fieldname": "category_html", "fieldtype": "HTML", "label": "Appraisal Summary", - "insert_after": "final_assesment_tab_break" + "insert_after": "final_assesment_tab_break", }, - { + { "fieldname": "category_based_on_marks", "fieldtype": "Link", - "options": "Appraisal Category", + "options": "Appraisal Category", "label": "Category based on marks", "insert_after": "category_html", - "read_only": 1 + "read_only": 1, }, { "fieldname": "category_details", @@ -3282,1447 +3438,1532 @@ def get_appraisal_custom_fields(): "options": "Category Details", "insert_after": "category_based_on_marks", "allow_on_submit": 1, - "read_only": 1 - }, - { - "fieldname": "employee_self_kra_rating", - "fieldtype": "Table", - "label": "Employee Rating", - "options": "Employee Feedback Rating", - "insert_after": "self_score", - }, - { - "fieldname": "total_employee_self_kra_rating", - "fieldtype": "Float", - "label": "Total Employee Self Score", - "insert_after": "employee_self_kra_rating", - "read_only": 1 - }, - { - "fieldname": "avg_employee_self_kra_rating", - "fieldtype": "Float", - "label": "Average Employee Self Score", - "insert_after": "total_employee_self_kra_rating", - "read_only": 1 - }, - { - "fieldname": "dept_self_kra_rating", - "fieldtype": "Table", - "label": "Department Rating", - "options": "Employee Feedback Rating", - "insert_after": "avg_employee_self_kra_rating", - }, - { - "fieldname": "total_dept_self_kra_rating", - "fieldtype": "Float", - "label": "Total Department Self Score", - "insert_after": "dept_self_kra_rating", - "read_only": 1 - }, - { - "fieldname": "avg_dept_self_kra_rating", - "fieldtype": "Float", - "label": "Average Department Self Score", - "insert_after": "total_dept_self_kra_rating", - "read_only": 1 - }, - { - "fieldname": "company_self_kra_rating", - "fieldtype": "Table", - "label": "Company Rating", - "options": "Employee Feedback Rating", - "insert_after": "avg_dept_self_kra_rating", - }, - { - "fieldname": "total_company_self_kra_rating", - "fieldtype": "Float", - "label": "Total Company Self Score", - "insert_after": "company_self_kra_rating", - "read_only": 1 - }, - { - "fieldname": "avg_company_self_kra_rating", - "fieldtype": "Float", - "label": "Average Company Self Score", - "insert_after": "total_company_self_kra_rating", - "read_only": 1 - }, - { - "fieldname": "final_average_score", - "fieldtype": "Float", - "label": "Final Average Score", - "insert_after": "employee_image", - "precision": 3 - } - ] - } + "read_only": 1, + }, + { + "fieldname": "employee_self_kra_rating", + "fieldtype": "Table", + "label": "Employee Rating", + "options": "Employee Feedback Rating", + "insert_after": "self_score", + }, + { + "fieldname": "total_employee_self_kra_rating", + "fieldtype": "Float", + "label": "Total Employee Self Score", + "insert_after": "employee_self_kra_rating", + "read_only": 1, + }, + { + "fieldname": "avg_employee_self_kra_rating", + "fieldtype": "Float", + "label": "Average Employee Self Score", + "insert_after": "total_employee_self_kra_rating", + "read_only": 1, + }, + { + "fieldname": "dept_self_kra_rating", + "fieldtype": "Table", + "label": "Department Rating", + "options": "Employee Feedback Rating", + "insert_after": "avg_employee_self_kra_rating", + }, + { + "fieldname": "total_dept_self_kra_rating", + "fieldtype": "Float", + "label": "Total Department Self Score", + "insert_after": "dept_self_kra_rating", + "read_only": 1, + }, + { + "fieldname": "avg_dept_self_kra_rating", + "fieldtype": "Float", + "label": "Average Department Self Score", + "insert_after": "total_dept_self_kra_rating", + "read_only": 1, + }, + { + "fieldname": "company_self_kra_rating", + "fieldtype": "Table", + "label": "Company Rating", + "options": "Employee Feedback Rating", + "insert_after": "avg_dept_self_kra_rating", + }, + { + "fieldname": "total_company_self_kra_rating", + "fieldtype": "Float", + "label": "Total Company Self Score", + "insert_after": "company_self_kra_rating", + "read_only": 1, + }, + { + "fieldname": "avg_company_self_kra_rating", + "fieldtype": "Float", + "label": "Average Company Self Score", + "insert_after": "total_company_self_kra_rating", + "read_only": 1, + }, + { + "fieldname": "final_average_score", + "fieldtype": "Float", + "label": "Final Average Score", + "insert_after": "employee_image", + "precision": 3, + }, + ] + } + def get_appraisal_kra_custom_fields(): - ''' + """ Custom fields that need to be added to the Appraisal KRA doctype - ''' + """ return { - "Appraisal KRA":[ - { - "fieldname": "kra_goals", - "fieldtype": "Text Editor", - "label": "Goals", - "insert_after": "goal_score", - "in_list_view":1 - } - ] + "Appraisal KRA": [ + { + "fieldname": "kra_goals", + "fieldtype": "Text Editor", + "label": "Goals", + "insert_after": "goal_score", + "in_list_view": 1, + } + ] } + def create_property_setters(property_setter_datas): - ''' - Method to create custom property setters - args: - property_setter_datas : list of dict of property setter obj - ''' - for property_setter_data in property_setter_datas: - if frappe.db.exists("Property Setter", property_setter_data): - continue - property_setter = frappe.new_doc("Property Setter") - property_setter.update(property_setter_data) - property_setter.flags.ignore_permissions = True - property_setter.insert() + """ + Method to create custom property setters + args: + property_setter_datas : list of dict of property setter obj + """ + for property_setter_data in property_setter_datas: + if frappe.db.exists("Property Setter", property_setter_data): + continue + property_setter = frappe.new_doc("Property Setter") + property_setter.update(property_setter_data) + property_setter.flags.ignore_permissions = True + property_setter.insert() + def get_property_setters(): - ''' - BEAMS specific property setters that need to be added to the Customer ,Account and Supplier DocTypes - ''' - return [ - - { - "doctype_or_field": "DocField", - "doc_type": "Customer", - "field_name": "disabled", - "property": "default", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Appraisal Template", - "field_name": "rating_criteria", - "property": "label", - "value": "Employee Criteria" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Employee Feedback Rating", - "field_name": "rating_criteria", - "property": "read_only", - "property_type": "Table", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Requisition", - "field_name": "status", - "property": "read_only", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Leave Allocation", - "field_name": "to_date", - "property": "allow_on_submit", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Customer", - "field_name": "disabled", - "property": "read_only", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Account", - "field_name": "disabled", - "property": "default", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Account", - "field_name": "disabled", - "property": "read_only", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Supplier", - "field_name": "disabled", - "property": "default", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Supplier", - "field_name": "disabled", - "property": "read_only", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Employee Advance", - "field_name": "purpose", - "property": "hidden", - "property_type": "Small Text", - "value":1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Purchase Invoice", - "field_name": "update_stock", - "property": "hidden", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Customer", - "field_name": "sales_team_tab", - "property": "hidden", - "property_type": "TabBreak", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Purchase Invoice", - "field_name": "is_subcontracted", - "property": "hidden", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Purchase Invoice", - "field_name": "scan_barcode", - "property": "hidden", - "property_type": "Data", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Employee Advance", - "field_name": "naming_series", - "property": "hidden", - "property_type": "Data", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Item", - "field_name": "grant_commission", - "property": "hidden", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Quotation", - "field_name": "scan_barcode", - "property": "hidden", - "property_type": "Data", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Quotation", - "field_name": "shipping_rule", - "property": "hidden", - "property_type": "Link", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Quotation", - "field_name": "incoterm", - "property": "hidden", - "property_type": "Link", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Customer", - "field_name": "dn_required", - "property": "hidden", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Item", - "field_name": "include_item_in_manufacturing", - "property": "default", - "property_type": "Check", - "value": 0 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Item", - "field_name": "inspection_required_before_delivery", - "property": "hidden", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Item", - "field_name": "manufacturing", - "property": "depends_on", - "property_type": "TabBreak", - "value": "eval:doc.is_stock_item == 0" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Applicant", - "field_name": "status", - "property": "options", - "value": "Open\nReplied\nRejected\nShortlisted from Interview\nLocal Enquiry Started\nLocal Enquiry Completed\nLocal Enquiry Rejected\nLocal Enquiry Approved\nSelected\nHold\nAccepted\nTraining Completed\nJob Proposal Created\nJob Proposal Accepted\nInterview Scheduled\nInterview Ongoing\nInterview Completed\nShortlisted\nPending Document Upload\nDocument Uploaded" - }, - { - "doctype_or_field": "DocType", - "doc_type": "Item", - "property": "quick_entry", - "property_type": "Check", - "value": 0 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Requisition", - "field_name": "posting_date", - "property": "read_only", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Leave Application", - "field_name": "posting_date", - "property": "read_only", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Requisition", - "field_name": "status", - "property": "options", - "value": "Pending\nOpen & Approved\nRejected\nOn Hold\nCancelled" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Event", - "field_name": "event_category", - "property": "options", - "value": "Event\nMeeting\nCall\nSent/Received Email\nOne to One Meeting\nOther" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Opening", - "field_name": "location", - "property": "hidden", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Employee Boarding Activity", - "field_name": "required_for_employee_creation", - "property": "hidden", - "property_type": "Check", - "value":1 - }, - { - "doctype_or_field":"DocField", - "doc_type": "Attendance Request", - "field_name": "reason", - "property": "options", - "value": "\nWork From Home\nOn Duty\nOn Deputation\nForgot to Checkin\nForgot to Checkout\nPermitted Late Arrival\nPermitted Early Exit" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Skill Assessment", - "field_name": "rating", - "property": "reqd", - "property_type": "Check", - "value":0 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Skill Assessment", - "field_name": "rating", - "property": "read_only", - "property_type": "Check", - "value":1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Employee External Work History", - "field_name": "designation", - "property": "label", - "value":"Designation At The Time Of Joining" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Project", - "field_name": "expected_start_date", - "property": "fieldtype", - "value":"Datetime" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Project", - "field_name": "expected_end_date", - "property": "fieldtype", - "value":"Datetime" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Requisition", - "field_name": "designation", - "property": "fetch_from", - "property_type": "Link", - "value":"employee_left.designation" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Requisition", - "field_name": "department", - "property": "fetch_from", - "property_type": "Link", - "value":"employee_left.department" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Employee Performance Feedback", - "field_name": "total_score", - "property": "hidden", - "property_type": "Float", - "value":1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Employee Feedback Rating", - "field_name": "Rating", - "property": "read_only", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Employee Performance Feedback", - "field_name": "feedback_ratings", - "property": "label", - "property_type": "Table", - "value":"Employee Criteria" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Appraisal", - "field_name": "rate_goals_manually", - "property": "hidden", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Appraisal", - "field_name": "goal_score_percentage", - "property": "hidden", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Appraisal", - "field_name": "total_score", - "property": "hidden", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Appraisal KRA", - "field_name": "goal_completion", - "property": "hidden", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Appraisal KRA", - "field_name": "goal_score", - "property": "hidden", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Employee Feedback Rating", - "field_name": "rating", - "property": "read_only", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Appraisal", - "field_name": "goals", - "property": "hidden", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Appraisal", - "field_name": "appraisal_kra", - "property": "label", - "property_type": "Table", - "value":"KRA's", - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Applicant", - "field_name": "resume_link", - "property": "hidden", - "property_type": "Data", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Appraisal", - "field_name": "self_ratings", - "property": "hidden", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Appraisal", - "field_name": "self_score", - "property": "hidden", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Budget", - "field_name": "monthly_distribution", - "property": "hidden", - "property_type": "Link", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Requisition", - "field_name": "designation", - "property": "reqd", - "property_type": "Check", - "value": 0 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Vehicle", - "field_name": "insurance_details", - "property": "hidden", - "property_type": "Section Break", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Interview", - "field_name": "resume_link", - "property": "hidden", - "property_type": "", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "HR Settings", - "field_name": "emp_created_by", - "property": "depends_on", - "property_type": "Code", - "value": "eval: doc.employee_naming_by_department === 0 " - }, - { - "doctype_or_field": "DocField", - "doc_type": "Budget Account", - "field_name": "account", - "property": "read_only", - "property_type": "Link", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Requisition", - "field_name": "no_of_positions", - "property": "reqd", - "property_type": "Check", - "value": 0 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Requisition", - "field_name": "expected_compensation", - "property": "reqd", - "value": 0 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Requisition", - "field_name": "expected_compensation", - "property": "default", - "value": 0.0 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Requisition", - "field_name": "expected_compensation", - "property": "mandatory_depends_on", - "value": "eval: frappe.user_roles.includes('HR Manager')" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Requisition", - "field_name": "expected_compensation", - "property": "depends_on", - "value": "eval: frappe.user_roles.includes('HR Manager')" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Requisition", - "field_name": "employee_left", - "property": "ignore_user_permissions", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Requisition", - "field_name": "requested_by", - "property": "ignore_user_permissions", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Shift Assignment", - "field_name": "swap_with_employee", - "property": "ignore_user_permissions", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Shift Assignment", - "field_name": "start_date", - "property": "read_only_depends_on", - "value": "eval:doc.docstatus == 1" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Shift Assignment", - "field_name": "end_date", - "property": "read_only_depends_on", - "value": "eval:doc.docstatus == 1" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Shift Assignment", - "field_name": "start_date", - "property": "allow_on_submit", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Shift Assignment", - "field_name": "end_date", - "property": "allow_on_submit", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Shift Assignment", - "field_name": "employee", - "property": "ignore_user_permissions", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Requisition", - "field_name": "designation", - "property": "reqd", - "property_type": "Check", - "value": 0 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Requisition", - "field_name": "department", - "property_type": "Check", - "property": "reqd", - "value": 0 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Requisition", - "field_name": "section_break_7", - "property": "collapsible", - "property_type": "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Job Requisition", - "field_name": "designation", - "property": "depends_on", - "value": "eval: !(doc.workflow_state == 'Draft' && doc.request_for == 'New Vacancy')" - }, - { - "doctype_or_field": "DocType", - "doc_type": "Job Applicant", - "property": "show_title_field_in_link", - "property_type" : "Check", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Employee", - "field_name": "permanent_accommodation_type", - "property": "hidden", - "property_type": "Select", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Attendance Request", - "field_name": "reports_to", - "property": "ignore_user_permissions", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Employee", - "field_name": "current_accommodation_type", - "property": "hidden", - "property_type": "Select", - "value": 1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Employee", - "field_name": "cell_number", - "property": "label", - "value": "Personal Mobile Number" - }, - { - "doctype_or_field": "DocType", - "doc_type": "Employee Feedback Rating", - "property": "field_order", - "value": "[\"criteria\", \"per_weightage\", \"marks\", \"rating\"]" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Employee Feedback Rating", - "field_name": "rating", - "property": "in_list_view", - "property_type": "Check", - "value": 0 - }, - { - "doctype_or_field": "DocType", - "doc_type": "Job Applicant", - "property": "show_title_field_in_link", - "property_type" : "Check", - "value": 1 - }, - { - "doc_type": "Sales Order", - "doctype_or_field": "DocField", - "field_name": "set_warehouse", - "property": "hidden", - "property_type": "Link", - "value":1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Sales Order", - "field_name": "scan_barcode", - "property": "hidden", - "property_type": "Data", - "value":1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Quotation", - "field_name": "quotation_to", - "property": "default", - "property_type": "Link", - "value":"Customer" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Quotation", - "field_name": "quotation_to", - "property": "read_only", - "property_type": "Link", - "value":1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Quotation", - "field_name": "order_type", - "property": "default", - "property_type": "Link", - "value":"Sales" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Quotation", - "field_name": "order_type", - "property": "read_only", - "property_type": "Link", - "value":1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Quotation Item", - "field_name": "shopping_cart_section", - "property": "hidden", - "property_type": "Section Break", - "value":1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Quotation Item", - "field_name": "item_weight_details", - "property": "hidden", - "property_type": "Section Break", - "value":1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Quotation Item", - "field_name": "available_quantity_section", - "property": "hidden", - "property_type": "Section Break", - "value":1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Quotation Item", - "field_name": "gst_details_section", - "property": "hidden", - "property_type": "Section Break", - "value":1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Quotation Item", - "field_name": "item_code", - "property": "label", - "property_type": "Link", - "value":"Service Item" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Quotation", - "field_name": "coupon_code", - "property": "hidden", - "property_type": "Link", - "value":1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Budget", - "field_name": "accounts", - "property": "hidden", - "value":1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Budget", - "field_name": "accounts", - "property": "read_only", - "value":1 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Budget", - "field_name": "accounts", - "property": "reqd", - "value":0 - }, - { - "doctype_or_field": "DocField", - "doc_type": "Payment Entry", - "field_name": "party_type", - "property": "default", - "property_type": "Link", - "value":"Employee" - }, - { - "doctype_or_field": "DocField", - "doc_type": "Voucher Entry", - "field_name": "bureau", - "property": "in_standard_filter", - "property_type": "Check", - "value":1 - }, - { - "doctype_or_field": "DocType", - "doc_type": "Job Requisition", - "property": "field_order", - "value": '["workflow_state", "naming_series", "request_for", "employee_left", "relieving_date", "suggested_designation", "designation", "department", "location", "employment_type", "column_break_qkna", "no_of_positions", "expected_compensation", "reason_for_requesting", "column_break_4", "company", "status", "section_break_7", "requested_by", "requested_by_name", "column_break_10", "requested_by_dept", "requested_by_designation", "interview", "interview_rounds", "work_details", "no_of_days_off", "min_experience", "work_details_column_break", "is_work_shift_needed", "travel_required", "driving_license_needed", "license_type", "education", "min_education_qual", "reset_column", "language_proficiency", "skill_proficiency", "publish_on_job_section", "publish_on_job_opening", "timelines_tab", "posting_date", "completed_on", "column_break_15", "expected_by", "time_to_fill", "job_description_tab", "job_description_template", "job_title", "description", "suggestions", "connections_tab"]' - }, - { - "doctype_or_field": "DocField", - "doc_type": "Asset", - "field_name": "location", - "property": "allow_on_submit", - "value": 1 - } - ] + """ + BEAMS specific property setters that need to be added to the Customer ,Account and Supplier DocTypes + """ + return [ + { + "doctype_or_field": "DocField", + "doc_type": "Customer", + "field_name": "disabled", + "property": "default", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Appraisal Template", + "field_name": "rating_criteria", + "property": "label", + "value": "Employee Criteria", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Employee Feedback Rating", + "field_name": "rating_criteria", + "property": "read_only", + "property_type": "Table", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "status", + "property": "read_only", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Leave Allocation", + "field_name": "to_date", + "property": "allow_on_submit", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Customer", + "field_name": "disabled", + "property": "read_only", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Account", + "field_name": "disabled", + "property": "default", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Account", + "field_name": "disabled", + "property": "read_only", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Supplier", + "field_name": "disabled", + "property": "default", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Supplier", + "field_name": "disabled", + "property": "read_only", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Employee Advance", + "field_name": "purpose", + "property": "hidden", + "property_type": "Small Text", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Purchase Invoice", + "field_name": "update_stock", + "property": "hidden", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Customer", + "field_name": "sales_team_tab", + "property": "hidden", + "property_type": "TabBreak", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Purchase Invoice", + "field_name": "is_subcontracted", + "property": "hidden", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Purchase Invoice", + "field_name": "scan_barcode", + "property": "hidden", + "property_type": "Data", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Employee Advance", + "field_name": "naming_series", + "property": "hidden", + "property_type": "Data", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Item", + "field_name": "grant_commission", + "property": "hidden", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Quotation", + "field_name": "scan_barcode", + "property": "hidden", + "property_type": "Data", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Quotation", + "field_name": "shipping_rule", + "property": "hidden", + "property_type": "Link", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Quotation", + "field_name": "incoterm", + "property": "hidden", + "property_type": "Link", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Customer", + "field_name": "dn_required", + "property": "hidden", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Item", + "field_name": "include_item_in_manufacturing", + "property": "default", + "property_type": "Check", + "value": 0, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Item", + "field_name": "inspection_required_before_delivery", + "property": "hidden", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Item", + "field_name": "manufacturing", + "property": "depends_on", + "property_type": "TabBreak", + "value": "eval:doc.is_stock_item == 0", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Applicant", + "field_name": "status", + "property": "options", + "value": "Open\nReplied\nRejected\nShortlisted from Interview\nLocal Enquiry Started\nLocal Enquiry Completed\nLocal Enquiry Rejected\nLocal Enquiry Approved\nSelected\nHold\nAccepted\nTraining Completed\nJob Proposal Created\nJob Proposal Accepted\nInterview Scheduled\nInterview Ongoing\nInterview Completed\nShortlisted\nPending Document Upload\nDocument Uploaded", + }, + { + "doctype_or_field": "DocType", + "doc_type": "Item", + "property": "quick_entry", + "property_type": "Check", + "value": 0, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "posting_date", + "property": "read_only", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Leave Application", + "field_name": "posting_date", + "property": "read_only", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "status", + "property": "options", + "value": "Pending\nOpen & Approved\nRejected\nOn Hold\nCancelled", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Event", + "field_name": "event_category", + "property": "options", + "value": "Event\nMeeting\nCall\nSent/Received Email\nOne to One Meeting\nOther", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Opening", + "field_name": "location", + "property": "hidden", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Employee Boarding Activity", + "field_name": "required_for_employee_creation", + "property": "hidden", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Attendance Request", + "field_name": "reason", + "property": "options", + "value": "\nWork From Home\nOn Duty\nOn Deputation\nForgot to Checkin\nForgot to Checkout\nPermitted Late Arrival\nPermitted Early Exit", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Skill Assessment", + "field_name": "rating", + "property": "reqd", + "property_type": "Check", + "value": 0, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Skill Assessment", + "field_name": "rating", + "property": "read_only", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Employee External Work History", + "field_name": "designation", + "property": "label", + "value": "Designation At The Time Of Joining", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Project", + "field_name": "expected_start_date", + "property": "fieldtype", + "value": "Datetime", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Project", + "field_name": "expected_end_date", + "property": "fieldtype", + "value": "Datetime", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "designation", + "property": "fetch_from", + "property_type": "Link", + "value": "employee_left.designation", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "department", + "property": "fetch_from", + "property_type": "Link", + "value": "employee_left.department", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Employee Performance Feedback", + "field_name": "total_score", + "property": "hidden", + "property_type": "Float", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Employee Feedback Rating", + "field_name": "Rating", + "property": "read_only", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Employee Performance Feedback", + "field_name": "feedback_ratings", + "property": "label", + "property_type": "Table", + "value": "Employee Criteria", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Appraisal", + "field_name": "rate_goals_manually", + "property": "hidden", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Appraisal", + "field_name": "goal_score_percentage", + "property": "hidden", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Appraisal", + "field_name": "total_score", + "property": "hidden", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Appraisal KRA", + "field_name": "goal_completion", + "property": "hidden", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Appraisal KRA", + "field_name": "goal_score", + "property": "hidden", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Employee Feedback Rating", + "field_name": "rating", + "property": "read_only", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Appraisal", + "field_name": "goals", + "property": "hidden", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Appraisal", + "field_name": "appraisal_kra", + "property": "label", + "property_type": "Table", + "value": "KRA's", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "designation", + "property": "reqd", + "property_type": "Check", + "value": 0, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Applicant", + "field_name": "resume_link", + "property": "hidden", + "property_type": "Data", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Appraisal", + "field_name": "self_ratings", + "property": "hidden", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Appraisal", + "field_name": "self_score", + "property": "hidden", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Budget", + "field_name": "monthly_distribution", + "property": "hidden", + "property_type": "Link", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "designation", + "property": "reqd", + "property_type": "Check", + "value": 0, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Vehicle", + "field_name": "insurance_details", + "property": "hidden", + "property_type": "Section Break", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Interview", + "field_name": "resume_link", + "property": "hidden", + "property_type": "", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "HR Settings", + "field_name": "emp_created_by", + "property": "depends_on", + "property_type": "Code", + "value": "eval: doc.employee_naming_by_department === 0 ", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Budget Account", + "field_name": "account", + "property": "read_only", + "property_type": "Link", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "no_of_positions", + "property": "reqd", + "property_type": "Check", + "value": 0, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "expected_compensation", + "property": "reqd", + "value": 0, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "expected_compensation", + "property": "default", + "value": 0.0, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "expected_compensation", + "property": "mandatory_depends_on", + "value": "eval: frappe.user_roles.includes('HR Manager')", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "expected_compensation", + "property": "depends_on", + "value": "eval: frappe.user_roles.includes('HR Manager')", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "employee_left", + "property": "ignore_user_permissions", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "requested_by", + "property": "ignore_user_permissions", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Shift Assignment", + "field_name": "swap_with_employee", + "property": "ignore_user_permissions", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Shift Assignment", + "field_name": "start_date", + "property": "read_only_depends_on", + "value": "eval:doc.docstatus == 1", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Shift Assignment", + "field_name": "end_date", + "property": "read_only_depends_on", + "value": "eval:doc.docstatus == 1", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Shift Assignment", + "field_name": "start_date", + "property": "allow_on_submit", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Shift Assignment", + "field_name": "end_date", + "property": "allow_on_submit", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Shift Assignment", + "field_name": "employee", + "property": "ignore_user_permissions", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "designation", + "property": "reqd", + "property_type": "Check", + "value": 0, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "department", + "property": "hidden", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "department", + "property_type": "Check", + "value": 0, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "department", + "property_type": "Check", + "property": "reqd", + "value": 0, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "section_break_7", + "property": "collapsible", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Job Requisition", + "field_name": "designation", + "property": "depends_on", + "value": "eval: !(doc.workflow_state == 'Draft' && doc.request_for == 'New Vacancy')", + }, + { + "doctype_or_field": "DocType", + "doc_type": "Job Requisition", + "property": "field_order", + "value": '["naming_series", "request_for", "employee_left", "relieving_date", "suggested_designation", "designation", "department", "employment_type", "location", "column_break_qkna", "no_of_positions", "expected_compensation", "reason_for_requesting", "column_break_4", "company", "status", "interview", "interview_rounds", "work_details", "no_of_days_off", "work_details_column_break", "is_work_shift_needed", "travel_required", "driving_license_needed", "license_type", "education", "min_education_qual", "education_column_break", "min_experience", "reset_column", "language_proficiency", "skill_proficiency", "section_break_7", "requested_by", "requested_by_name", "column_break_10", "requested_by_dept", "requested_by_designation", "timelines_tab", "posting_date", "completed_on", "column_break_15", "expected_by", "time_to_fill", "job_description_tab", "job_description_template", "job_title", "description", "suggestions", "connections_tab"]', + }, + { + "doctype_or_field": "DocType", + "doc_type": "Job Applicant", + "property": "show_title_field_in_link", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Employee", + "field_name": "permanent_accommodation_type", + "property": "hidden", + "property_type": "Select", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Attendance Request", + "field_name": "reports_to", + "property": "ignore_user_permissions", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Employee", + "field_name": "current_accommodation_type", + "property": "hidden", + "property_type": "Select", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Employee", + "field_name": "cell_number", + "property": "label", + "value": "Personal Mobile Number", + }, + { + "doctype_or_field": "DocType", + "doc_type": "Employee Feedback Rating", + "property": "field_order", + "value": '["criteria", "per_weightage", "marks", "rating"]', + }, + { + "doctype_or_field": "DocField", + "doc_type": "Employee Feedback Rating", + "field_name": "rating", + "property": "in_list_view", + "property_type": "Check", + "value": 0, + }, + { + "doctype_or_field": "DocType", + "doc_type": "Job Applicant", + "property": "show_title_field_in_link", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocType", + "doc_type": "Job Requisition", + "property": "field_order", + "value": '["basic_details_tab", "basic_information", "employee", "naming_series", "salutation", "first_name", "middle_name", "last_name", "bureau", "stringer_type", "employee_name", "column_break_9", "gender", "date_of_birth", "name_of_father", "name_of_spouse", "column_break1", "date_of_joining", "date_of_appointment", "image", "status", "training_status", "erpnext_user", "user_id", "create_user", "create_user_permission", "company_details_section", "company", "department", "employee_number", "column_break_25", "designation", "reports_to", "column_break_18", "branch", "grade", "employment_details", "job_applicant", "scheduled_confirmation_date", "column_break_32", "final_confirmation_date", "contract_end_date", "col_break_22", "notice_number_of_days", "date_of_retirement", "contact_details", "cell_number", "company_number", "column_break_40", "personal_email", "company_email", "column_break4", "prefered_contact_email", "prefered_email", "unsubscribed", "address_section", "pincode", "current_address", "landmark", "current_accommodation_type", "column_break_46", "permanent_address", "landmark_per", "permanent_accommodation_type", "emergency_contact_details", "person_to_be_contacted", "emergency_contact_name", "column_break_55", "emergency_phone_number", "emergency_phone", "column_break_19", "relation", "relation_emergency", "attendance_and_leave_details", "attendance_device_id", "leave_policy", "leave_policy_name", "column_break_44", "holiday_list", "default_shift", "approvers_section", "expense_approver", "leave_approver", "column_break_45", "shift_request_approver", "leave_approver_name", "expense_approver_name", "salary_information", "ctc", "salary_currency", "salary_mode", "salary_cb", "payroll_cost_center", "pan_number", "provident_fund_account", "bank_details_section", "bank_name", "column_break_heye", "bank_ac_no", "bank_cb", "ifsc_code", "micr_code", "iban", "nominee_details_section", "nominee_details", "personal_details", "marital_status", "aadhar_id", "no_of_children", "family_background", "column_break6", "blood_group", "health_details", "health_insurance_section", "health_insurance_provider", "health_insurance_no", "passport_details_section", "passport_number", "valid_upto", "column_break_73", "date_of_issue", "place_of_issue", "additional_information_section", "physical_disabilities", "disabilities", "marital_indebtness", "court_proceedings", "court_proceedings_details", "column_break_travel", "are_you_willing_to_travel", "in_india", "abroad", "state_restrictions_problems", "places_to_travel", "are_you_related_to_employee", "related_employee_name", "profile_tab", "bio", "educational_qualification", "education", "previous_work_experience", "external_work_history", "history_in_company", "internal_work_history", "documents_tab", "employee_documents", "exit", "resignation_letter_date", "relieving_date", "exit_interview_details", "held_on", "new_workplace", "column_break_99", "leave_encashed", "encashment_date", "feedback_section", "reason_for_leaving", "column_break_104", "feedback", "lft", "rgt", "old_parent", "connections_tab"]', + }, + { + "doc_type": "Sales Order", + "doctype_or_field": "DocField", + "field_name": "set_warehouse", + "property": "hidden", + "property_type": "Link", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Sales Order", + "field_name": "scan_barcode", + "property": "hidden", + "property_type": "Data", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Quotation", + "field_name": "quotation_to", + "property": "default", + "property_type": "Link", + "value": "Customer", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Quotation", + "field_name": "quotation_to", + "property": "read_only", + "property_type": "Link", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Quotation", + "field_name": "order_type", + "property": "default", + "property_type": "Link", + "value": "Sales", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Quotation", + "field_name": "order_type", + "property": "read_only", + "property_type": "Link", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Quotation Item", + "field_name": "shopping_cart_section", + "property": "hidden", + "property_type": "Section Break", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Quotation Item", + "field_name": "item_weight_details", + "property": "hidden", + "property_type": "Section Break", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Quotation Item", + "field_name": "available_quantity_section", + "property": "hidden", + "property_type": "Section Break", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Quotation Item", + "field_name": "gst_details_section", + "property": "hidden", + "property_type": "Section Break", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Quotation Item", + "field_name": "item_code", + "property": "label", + "property_type": "Link", + "value": "Service Item", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Quotation", + "field_name": "coupon_code", + "property": "hidden", + "property_type": "Link", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Budget", + "field_name": "accounts", + "property": "hidden", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Budget", + "field_name": "accounts", + "property": "read_only", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Budget", + "field_name": "accounts", + "property": "reqd", + "value": 0, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Payment Entry", + "field_name": "party_type", + "property": "default", + "property_type": "Link", + "value": "Employee", + }, + { + "doctype_or_field": "DocField", + "doc_type": "Voucher Entry", + "field_name": "bureau", + "property": "in_standard_filter", + "property_type": "Check", + "value": 1, + }, + { + "doctype_or_field": "DocType", + "doc_type": "Job Requisition", + "property": "field_order", + "value": '["workflow_state", "naming_series", "request_for", "employee_left", "relieving_date", "suggested_designation", "designation", "department", "location", "employment_type", "column_break_qkna", "no_of_positions", "expected_compensation", "reason_for_requesting", "column_break_4", "company", "status", "section_break_7", "requested_by", "requested_by_name", "column_break_10", "requested_by_dept", "requested_by_designation", "interview", "interview_rounds", "work_details", "no_of_days_off", "min_experience", "work_details_column_break", "is_work_shift_needed", "travel_required", "driving_license_needed", "license_type", "education", "min_education_qual", "reset_column", "language_proficiency", "skill_proficiency", "publish_on_job_section", "publish_on_job_opening", "timelines_tab", "posting_date", "completed_on", "column_break_15", "expected_by", "time_to_fill", "job_description_tab", "job_description_template", "job_title", "description", "suggestions", "connections_tab"]', + }, + { + "doctype_or_field": "DocField", + "doc_type": "Asset", + "field_name": "location", + "property": "allow_on_submit", + "value": 1, + }, + { + "doctype_or_field": "DocField", + "doc_type": "Voucher Entry", + "field_name": "total_amount", + "property": "read_only", + "property_type": "Check", + "value": 1, + }, + ] + def get_material_request_custom_fields(): - ''' - Custom fields that need to be added to the Material Request Doctype - ''' - return { - "Material Request": [ - { - "fieldname": "budget_exceeded", - "fieldtype": "Check", - "label": "Budget Exceeded", - "insert_after": "schedule_date", - "read_only":1, - "no_copy":1, - "depends_on": "eval:doc.budget_exceeded == 1" - - }, - { - "fieldname": "requested_by", - "fieldtype": "Link", - "label": "Requested By", - "insert_after": "material_request_type", - "options": "Employee" - } - ] - } + """ + Custom fields that need to be added to the Material Request Doctype + """ + return { + "Material Request": [ + { + "fieldname": "budget_exceeded", + "fieldtype": "Check", + "label": "Budget Exceeded", + "insert_after": "schedule_date", + "read_only": 1, + "no_copy": 1, + "depends_on": "eval:doc.budget_exceeded == 1", + }, + { + "fieldname": "requested_by", + "fieldtype": "Link", + "label": "Requested By", + "insert_after": "material_request_type", + "options": "Employee", + }, + ] + } + def get_sales_order_custom_fields(): - ''' - Custom fields that need to be added to the Sales Order Doctype - ''' - return { - "Sales Order": [ - { - "fieldname": "sales_type", - "fieldtype": "Link", - "label": "Sales Type", - "insert_after": "naming_series", - "options": "Sales Type" - }, - { - "fieldname": "actual_customer", - "fieldtype": "Link", - "label": "Actual Customer", - "options": "Customer", - "depends_on": "eval:doc.is_agent == 1", - "insert_after": "is_agent" - }, - { - "fieldname": "is_agent", - "fieldtype": "Check", - "label": "Is Agency", - "read_only":1, - "fetch_from": "customer.is_agent", - "depends_on": "eval:doc.is_agent", - "insert_after": "customer" - }, - { - "fieldname": "actual_customer_group", - "fieldtype": "Link", - "label": "Actual Customer Group", - "options": "Customer Group", - "read_only": 1, - "fetch_from": "actual_customer.customer_group", - "insert_after": "actual_customer" - }, - { - "fieldname": "include_in_ibf", - "fieldtype": "Check", - "label": "Include in IBF", - "read_only": 1, - "insert_after": "actual_customer_group" - }, - { - "fieldname": "region", - "fieldtype": "Link", - "label": "Region", - "options": "Region", - "insert_after": "is_reverse_charge" - }, - { - "fieldname": "executive", - "fieldtype": "Link", - "label": "Executive", - "options": "Employee", - "insert_after": "delivery_date" - }, - { - "fieldname": "executive_name", - "fieldtype": "Data", - "label": "Executive Name", - "fetch_from": "executive.employee_name", - "insert_after": "executive", - "read_only": 1 - }, - { - "fieldname": "is_barter_invoice", - "fieldtype": "Check", - "label": "Is Barter Invoice", - "read_only": 1, - "insert_after": "include_in_ibf", - "fetch_from": "reference_id.is_barter" - }, - { - "fieldname": "reference_id", - "fieldtype": "Link", - "options":"Quotation", - "label": "Quotation", - "read_only":1, - "insert_after": "naming_series" - } - ] - } + """ + Custom fields that need to be added to the Sales Order Doctype + """ + return { + "Sales Order": [ + { + "fieldname": "sales_type", + "fieldtype": "Link", + "label": "Sales Type", + "insert_after": "naming_series", + "options": "Sales Type", + }, + { + "fieldname": "actual_customer", + "fieldtype": "Link", + "label": "Actual Customer", + "options": "Customer", + "depends_on": "eval:doc.is_agent == 1", + "insert_after": "is_agent", + }, + { + "fieldname": "is_agent", + "fieldtype": "Check", + "label": "Is Agency", + "read_only": 1, + "fetch_from": "customer.is_agent", + "depends_on": "eval:doc.is_agent", + "insert_after": "customer", + }, + { + "fieldname": "actual_customer_group", + "fieldtype": "Link", + "label": "Actual Customer Group", + "options": "Customer Group", + "read_only": 1, + "fetch_from": "actual_customer.customer_group", + "insert_after": "actual_customer", + }, + { + "fieldname": "include_in_ibf", + "fieldtype": "Check", + "label": "Include in IBF", + "read_only": 1, + "insert_after": "actual_customer_group", + }, + { + "fieldname": "region", + "fieldtype": "Link", + "label": "Region", + "options": "Region", + "insert_after": "is_reverse_charge", + }, + { + "fieldname": "executive", + "fieldtype": "Link", + "label": "Executive", + "options": "Employee", + "insert_after": "delivery_date", + }, + { + "fieldname": "executive_name", + "fieldtype": "Data", + "label": "Executive Name", + "fetch_from": "executive.employee_name", + "insert_after": "executive", + "read_only": 1, + }, + { + "fieldname": "is_barter_invoice", + "fieldtype": "Check", + "label": "Is Barter Invoice", + "read_only": 1, + "insert_after": "include_in_ibf", + "fetch_from": "reference_id.is_barter", + }, + { + "fieldname": "reference_id", + "fieldtype": "Link", + "options": "Quotation", + "label": "Quotation", + "read_only": 1, + "insert_after": "naming_series", + }, + ] + } + def get_employee_advance_custom_fields(): - ''' - Custom fields that need to be added to the Employee Advance Doctype - ''' - return { - "Employee Advance": [ - { - "fieldname": "purpose", - "fieldtype": "Link", - "label": "Purpose", - "options": "Employee Advance Purpose", - "insert_after":"currency" - }, - { - "fieldname": "purpose", - "fieldtype": "Link", - "label": "Purpose", - "options": "Employee Advance Purpose", - "insert_after": "currency", - "reqd": 1 - } - - ] - } + """ + Custom fields that need to be added to the Employee Advance Doctype + """ + return { + "Employee Advance": [ + { + "fieldname": "purpose", + "fieldtype": "Link", + "label": "Purpose", + "options": "Employee Advance Purpose", + "insert_after": "currency", + }, + { + "fieldname": "purpose", + "fieldtype": "Link", + "label": "Purpose", + "options": "Employee Advance Purpose", + "insert_after": "currency", + "reqd": 1, + }, + ] + } + def get_journal_entry_custom_fields(): - ''' - Custom fields that need to be added to the Journal Entry Doctype. - ''' - return { - "Journal Entry": [ - { - "fieldname": "cost_center", - "fieldtype": "Link", - "label": "Cost Center", - "read_only": 1, - "options": "Cost Center", - "in_list_view": 1, - "insert_after": "naming_series" - }, - { - "fieldname": "batta_claim_reference", - "fieldtype": "Link", - "label": "Batta Claim Reference", - "read_only": 1, - "options": "Batta Claim", - "insert_after": "voucher_type" - }, - { - "fieldname": "substitute_booking_reference", - "fieldtype": "Link", - "label": "Substitute Booking Reference", - "read_only": 1, - "options": "Substitute Booking", - "insert_after": "batta_claim_reference" - }, - { - "fieldname": "employee_travel_request", - "fieldtype": "Link", - "label": "Employee Travel Request", - "options": "Employee Travel Request", - "insert_after":"posting_date", - "read_only": 1 - } - - ] - } + """ + Custom fields that need to be added to the Journal Entry Doctype. + """ + return { + "Journal Entry": [ + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "read_only": 1, + "options": "Cost Center", + "in_list_view": 1, + "insert_after": "naming_series", + }, + { + "fieldname": "batta_claim_reference", + "fieldtype": "Link", + "label": "Batta Claim Reference", + "read_only": 1, + "options": "Batta Claim", + "insert_after": "voucher_type", + }, + { + "fieldname": "substitute_booking_reference", + "fieldtype": "Link", + "label": "Substitute Booking Reference", + "read_only": 1, + "options": "Substitute Booking", + "insert_after": "batta_claim_reference", + }, + { + "fieldname": "employee_travel_request", + "fieldtype": "Link", + "label": "Employee Travel Request", + "options": "Employee Travel Request", + "insert_after": "posting_date", + "read_only": 1, + }, + ] + } + def create_custom_roles(roles): - ''' - Method to create custom Role - args: - roles : Role List (list of string) - example: - ["HOD", "Manager"] - ''' - for role in roles: - if not frappe.db.exists("Role", role): - role_doc = frappe.get_doc({ - "doctype": "Role", - "role_name": role - }) - role_doc.insert(ignore_permissions=True) - frappe.db.commit() + """ + Method to create custom Role + args: + roles : Role List (list of string) + example: + ["HOD", "Manager"] + """ + for role in roles: + if not frappe.db.exists("Role", role): + role_doc = frappe.get_doc({"doctype": "Role", "role_name": role}) + role_doc.insert(ignore_permissions=True) + frappe.db.commit() + def create_translations(translations): - for translation in translations: - if not frappe.db.exists(translation): - frappe.get_doc(translation).insert(ignore_permissions=True) - frappe.db.commit() + for translation in translations: + if not frappe.db.exists(translation): + frappe.get_doc(translation).insert(ignore_permissions=True) + frappe.db.commit() + def create_email_templates(email_templates): - ''' - Method to Create Email Template - args: - email_templates : Email Template List - ''' - for email_template in email_templates: - if not frappe.db.exists('Email Template', email_template.get('name')): - frappe.get_doc(email_template).insert(ignore_permissions=True) - frappe.db.commit() + """ + Method to Create Email Template + args: + email_templates : Email Template List + """ + for email_template in email_templates: + if not frappe.db.exists("Email Template", email_template.get("name")): + frappe.get_doc(email_template).insert(ignore_permissions=True) + frappe.db.commit() + def get_interview_feedback_custom_fields(): - ''' - Custom fields that need to be added to the Interview Feedback - ''' - return { - "Interview Feedback": [ - { - "fieldname": "interview_question_result", - "label": "Interview Question Result", - "fieldtype": "Table", - "options": "Interview Question Result", - "insert_after": "skill_assessment" - } - ] - } + """ + Custom fields that need to be added to the Interview Feedback + """ + return { + "Interview Feedback": [ + { + "fieldname": "interview_question_result", + "label": "Interview Question Result", + "fieldtype": "Table", + "options": "Interview Question Result", + "insert_after": "skill_assessment", + } + ] + } + def get_skill_assessment_custom_fields(): - ''' - Custom fields that need to be added to the Skill Assessment Child Table - ''' - return { - "Skill Assessment": [ - { - "fieldname": "score", - "fieldtype": "Float", - "label": "Score (Out of 10)", - "reqd": 1, - "insert_after":"skill", - "in_list_view": 1 - }, - { - "fieldname": "remarks", - "fieldtype": "Small Text", - "label": "Remarks", - "insert_after":"score" - }, - { - "fieldname": "weight", - "fieldtype": "Float", - "label": "Weight", - "insert_after":"remarks" - } - - ] - } + """ + Custom fields that need to be added to the Skill Assessment Child Table + """ + return { + "Skill Assessment": [ + { + "fieldname": "score", + "fieldtype": "Float", + "label": "Score (Out of 10)", + "reqd": 1, + "insert_after": "skill", + "in_list_view": 1, + }, + { + "fieldname": "remarks", + "fieldtype": "Small Text", + "label": "Remarks", + "insert_after": "score", + }, + { + "fieldname": "weight", + "fieldtype": "Float", + "label": "Weight", + "insert_after": "remarks", + }, + ] + } + def get_training_event_employee_custom_fields(): - ''' - Custom fields to be added to the Training Event Employee Doctype - ''' - return { - "Training Event Employee": [ - { - "fieldname": "training_request", - "fieldtype": "Link", - "label": "Training Request", - "options": "Training Request", - "insert_after": "employee_name", - "in_list_view": 1, - "width": 2 - } - ] - } + """ + Custom fields to be added to the Training Event Employee Doctype + """ + return { + "Training Event Employee": [ + { + "fieldname": "training_request", + "fieldtype": "Link", + "label": "Training Request", + "options": "Training Request", + "insert_after": "employee_name", + "in_list_view": 1, + "width": 2, + } + ] + } + def get_beams_roles(): - ''' - Method to get BEAMS specific roles - ''' - return ['Production Manager', 'CEO', 'Company Secretary', 'HOD','Enquiry Officer','Enquiry Manager','Shift Publisher','Program Producer','Operations Head','Operations User','Admin','Driver','Budget User','Technical Store Head','Budget Verifier','Budget Verifier Finance','Budget Approver','Admin User','Bureau User','Coordinating Editor','News Coordinator','Security'] + """ + Method to get BEAMS specific roles + """ + return [ + "Production Manager", + "CEO", + "Company Secretary", + "HOD", + "Enquiry Officer", + "Enquiry Manager", + "Shift Publisher", + "Program Producer", + "Operations Head", + "Operations User", + "Admin", + "Driver", + "Budget User", + "Technical Store Head", + "Budget Verifier", + "Budget Verifier Finance", + "Budget Approver", + "Admin User", + "Bureau User", + "Coordinating Editor", + "News Coordinator", + "Security", + ] + def get_custom_translations(): - ''' - Method to get Translations - ''' - return [ - { - 'doctype': 'Translation', - 'source_text': 'Quotation To', - 'translated_text': 'Release Order To', - 'language': 'en' - }, - { - 'doctype': 'Translation', - 'source_text': 'Quotation', - 'translated_text': 'Release Order', - 'language': 'en' - }, - { - 'doctype': 'Translation', - 'source_text':'Attendance Request', - 'translated_text':'Attendance Regularisation', - 'language':'en' - } - ] + """ + Method to get Translations + """ + return [ + { + "doctype": "Translation", + "source_text": "Quotation To", + "translated_text": "Release Order To", + "language": "en", + }, + { + "doctype": "Translation", + "source_text": "Quotation", + "translated_text": "Release Order", + "language": "en", + }, + { + "doctype": "Translation", + "source_text": "Attendance Request", + "translated_text": "Attendance Regularisation", + "language": "en", + }, + ] + def get_email_templates(): - ''' - Method to get Email Templates - ''' - return [ - { - 'doctype': 'Email Template', - 'name': 'Job Applicant Follow Up', - 'subject': "{{applicant_name}}, Complete your Application", - 'response': """Dear {{ applicant_name }}, - We're excited to move forward with your application! - To continue, please upload the required documents by clicking the link: Click Here. - Thank you for your interest in joining us! - If you have any questions, feel free to reach out. - Best regards, - HR Manager""" - } -] + """ + Method to get Email Templates + """ + return [ + { + "doctype": "Email Template", + "name": "Job Applicant Follow Up", + "subject": "{{applicant_name}}, Complete your Application", + "response": """Dear {{ applicant_name }}, + We're excited to move forward with your application! + To continue, please upload the required documents by clicking the link: Click Here. + Thank you for your interest in joining us! + If you have any questions, feel free to reach out. + Best regards, + HR Manager""", + } + ] + def get_employment_type(): - ''' - Custom fields to be added to the Employment Type Doctype - ''' - return { - "Employment Type": [ - { - "fieldname": "notice_period", - "fieldtype": "Int", - "label": "Notice Period", - "insert_after": "employment_type" - } - ] - } + """ + Custom fields to be added to the Employment Type Doctype + """ + return { + "Employment Type": [ + { + "fieldname": "notice_period", + "fieldtype": "Int", + "label": "Notice Period", + "insert_after": "employment_type", + } + ] + } + def get_appointment_letter(): - ''' - Custom fields that need to be added to the Appointment Letter DocType - ''' - return { - "Appointment Letter": [ - { - "fieldname": "notice_period", - "fieldtype": "Int", - "label": "Notice Period", - "insert_after": "applicant_name" - } - ] - } + """ + Custom fields that need to be added to the Appointment Letter DocType + """ + return { + "Appointment Letter": [ + { + "fieldname": "notice_period", + "fieldtype": "Int", + "label": "Notice Period", + "insert_after": "applicant_name", + } + ] + } + def get_vehicle_custom_fields(): - ''' - Custom fields that need to be added to the Vehicle DocType - ''' - return { - "Vehicle": [ - { - "fieldname": "vehicle_section_break", - "fieldtype": "Section Break", - "label": "Vehicle Details", - "insert_after": "doors" - }, - { - "fieldname": "vehicle_documents", - "fieldtype": "Table", - "label": "Vehicle Documents", - "options": "Vehicle Documents", - "insert_after": "vehicle_section_break" - }, - { - "fieldname": "vehicle_safety_inspection", - "fieldtype": "Link", - "label": "Vehicle Safety Inspection", - "options": "Vehicle Safety Inspection", - "insert_after": "carbon_check_date" - } - - ] - } + """ + Custom fields that need to be added to the Vehicle DocType + """ + return { + "Vehicle": [ + { + "fieldname": "vehicle_section_break", + "fieldtype": "Section Break", + "label": "Vehicle Details", + "insert_after": "doors", + }, + { + "fieldname": "vehicle_documents", + "fieldtype": "Table", + "label": "Vehicle Documents", + "options": "Vehicle Documents", + "insert_after": "vehicle_section_break", + }, + { + "fieldname": "vehicle_safety_inspection", + "fieldtype": "Link", + "label": "Vehicle Safety Inspection", + "options": "Vehicle Safety Inspection", + "insert_after": "carbon_check_date", + }, + ] + } + def get_hr_settings_custom_fields(): - ''' - Custom fields that need to be added to the HR Settings DocType - ''' - return { - "HR Settings": [ - { - "fieldname": "employee_naming_by_department", - "fieldtype": "Check", - "label": "Employee Naming By Department", - "insert_after": "employee_settings" - } - ] - } + """ + Custom fields that need to be added to the HR Settings DocType + """ + return { + "HR Settings": [ + { + "fieldname": "employee_naming_by_department", + "fieldtype": "Check", + "label": "Employee Naming By Department", + "insert_after": "employee_settings", + } + ] + } + + def get_asset_movement_custom_fields(): - ''' - Custom fields that need to be added to the Asset Movement DocType - ''' - return { - "Asset Movement": [ - { - "fieldname": "new_custodian", - "fieldtype": "Link", - "label": "New Custodian", - "options": "Employee", - "insert_after": "assets", - "read_only": 1 - }, - { - "fieldname": "user_id", - "label": "User ID", - "fieldtype": "Data", - "insert_after": "new_custodian", - "options": "Email", - "read_only": 1 - } - ], - "Asset Movement Item": [ - { - "fieldname": "room", - "fieldtype": "Link", - "label": "Room", - "options":"Service Unit", - "allow_on_submit": 1, - "insert_after": "from_employee" - }, - { - "fieldname": "shelf", - "fieldtype": "Link", - "label": "Shelf", - "options":"Shelf", - "allow_on_submit": 1, - "insert_after": "room" - }, - { - "fieldname": "row", - "fieldtype": "Link", - "label": "Row", - "options":"Row", - "allow_on_submit": 1, - "insert_after": "to_employee" - }, - { - "fieldname": "bin", - "fieldtype": "Link", - "label": "Bin", - "options":"Container", - "allow_on_submit": 1, - "insert_after": "row" - } - ] - } + """ + Custom fields that need to be added to the Asset Movement DocType + """ + return { + "Asset Movement": [ + { + "fieldname": "new_custodian", + "fieldtype": "Link", + "label": "New Custodian", + "options": "Employee", + "insert_after": "assets", + "read_only": 1, + }, + { + "fieldname": "user_id", + "label": "User ID", + "fieldtype": "Data", + "insert_after": "new_custodian", + "options": "Email", + "read_only": 1, + }, + ], + "Asset Movement Item": [ + { + "fieldname": "room", + "fieldtype": "Link", + "label": "Room", + "options": "Service Unit", + "allow_on_submit": 1, + "insert_after": "from_employee", + }, + { + "fieldname": "shelf", + "fieldtype": "Link", + "label": "Shelf", + "options": "Shelf", + "allow_on_submit": 1, + "insert_after": "room", + }, + { + "fieldname": "row", + "fieldtype": "Link", + "label": "Row", + "options": "Row", + "allow_on_submit": 1, + "insert_after": "to_employee", + }, + { + "fieldname": "bin", + "fieldtype": "Link", + "label": "Bin", + "options": "Container", + "allow_on_submit": 1, + "insert_after": "row", + }, + ], + } + + def get_asset_category_custom_fields(): - ''' - Custom fields that need to be added to the Asset Category DocType - ''' - return { - "Asset Category": [ - { - "fieldname": "parent_asset_category", - "fieldtype": "Link", - "label": "Parent Asset Category", - "options": "Asset Category", - "insert_after": "asset_category_name" - } - ] - } + """ + Custom fields that need to be added to the Asset Category DocType + """ + return { + "Asset Category": [ + { + "fieldname": "parent_asset_category", + "fieldtype": "Link", + "label": "Parent Asset Category", + "options": "Asset Category", + "insert_after": "asset_category_name", + } + ] + } + + def get_full_and_final_statement_custom_fields(): - ''' - Custom fields that need to be added to the Full and Final Statement DocType - ''' - return { - "Full and Final Statement": [ - { - "fieldname": "allocated_bundles", - "fieldtype": "Table", - "label": "Allocated Bundles", - "options": "Full and Final Bundle", - "insert_after": "assets_allocated" - } - ] - } + """ + Custom fields that need to be added to the Full and Final Statement DocType + """ + return { + "Full and Final Statement": [ + { + "fieldname": "allocated_bundles", + "fieldtype": "Table", + "label": "Allocated Bundles", + "options": "Full and Final Bundle", + "insert_after": "assets_allocated", + } + ] + } + def get_expense_claim_custom_fields(): - ''' - Custom fields that need to be added to the Expense Claim DocType - ''' - return { - "Expense Claim": [ - { - "fieldname": "travel_request", - "fieldtype": "Link", - "label": "Travel Request", - "options": "Employee Travel Request", - "insert_after": "approval_status", - "read_only": 1 - } - ] - } + """ + Custom fields that need to be added to the Expense Claim DocType + """ + return { + "Expense Claim": [ + { + "fieldname": "travel_request", + "fieldtype": "Link", + "label": "Travel Request", + "options": "Employee Travel Request", + "insert_after": "approval_status", + "read_only": 1, + } + ] + } + def get_expense_claim_type_custom_fields(): - ''' - Custom fields that need to be added to the Expense Claim Type DocType - ''' - return { - "Expense Claim Type": [ - { - "fieldname": "vehicle_against", - "fieldtype": "Check", - "label": "For Vehicle Incident", - "insert_after": "deferred_expense_account" - } - ] - } + """ + Custom fields that need to be added to the Expense Claim Type DocType + """ + return { + "Expense Claim Type": [ + { + "fieldname": "vehicle_against", + "fieldtype": "Check", + "label": "For Vehicle Incident", + "insert_after": "deferred_expense_account", + } + ] + } + def get_supplier_quotation_custom_fields(): - ''' - Custom fields that need to be added to the Supplier Quotation DocType - ''' - return { - "Supplier Quotation": [ - { - "fieldname": "attach", - "fieldtype": "Attach", - "label": "Attachments", - "insert_after": "base_net_total" - } - ] - } \ No newline at end of file + """ + Custom fields that need to be added to the Supplier Quotation DocType + """ + return { + "Supplier Quotation": [ + { + "fieldname": "attach", + "fieldtype": "Attach", + "label": "Attachments", + "insert_after": "base_net_total", + } + ] + } diff --git a/beams/www/job_application_upload/upload_doc.html b/beams/www/job_application_upload/upload_doc.html index cef94f453..41f3c3069 100644 --- a/beams/www/job_application_upload/upload_doc.html +++ b/beams/www/job_application_upload/upload_doc.html @@ -86,7 +86,16 @@
Current Address
- + +
+
+ + +
+
+ +
Permanent Address
@@ -100,8 +109,17 @@
Permanent Address
- +
+
+ + +
+
+ + +
Contact Details
@@ -112,11 +130,7 @@
Contact Details
-
-
- - -
+
@@ -132,7 +146,6 @@
Educational Qualification
Date Attended To Result(%) Attachment - @@ -144,7 +157,6 @@
Educational Qualification
- @@ -162,10 +174,9 @@
Professional Certification
Institute Name Date Attended From Date Attended To - Type of Cerfification + Type of Cerfificate Subject Major Attachment - @@ -180,7 +191,6 @@
Professional Certification
- @@ -196,12 +206,12 @@
Professional Certification
- - + +
- - + +
@@ -210,11 +220,11 @@
Professional Certification
- +
- +
@@ -227,13 +237,25 @@
Professional Certification
+
+
+ +
- - -
+ + +
+
+ + +
+
+ + +
@@ -249,9 +271,17 @@
Professional Certification
-
- - +
+
+ + +
+
+ + +
@@ -276,12 +306,11 @@
Previous Employment History
No Organization Name Designation -  Last Salary CTC + Last Salary Drawn Name of Manager/Reporting Head Period of Employment Reason for Leaving Attachment - @@ -294,7 +323,6 @@
Previous Employment History
- @@ -302,28 +330,6 @@
Previous Employment History
- -
-
-
- -
- - -
- -
- - -
- -
- - -
-
@@ -397,7 +403,6 @@
Languages Known
Speak Read Write - @@ -438,7 +443,6 @@
Languages Known
- @@ -446,7 +450,21 @@
Languages Known
- +
+ + + + + + + + + + + +
+
In IndiaAbroadState Restrictions if any
+