diff --git a/FEATURES.md b/FEATURES.md index 95685d6..424a187 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -23,6 +23,7 @@ - Direct Expenses: The sum of consumables and other direct fixed expenses. - Cost of Goods Sold: This includes the cost of the items sold, factoring in product bundles and Bill of Materials (BOM) sub-items. - Gross Profit/Loss: The difference between gross sales and the cost of goods sold. +- Employee Cost: The total Employee Cost for the day, including wages, salaries, benefits, and any other related expenses. This cost is part of the Total Indirect Expense. - Indirect Expenses: The total of indirect fixed expenses and any percentage-based expenses. - Net Profit/Loss: The final profit or loss for the day. diff --git a/SETUP.md b/SETUP.md index cc2373b..b09324d 100644 --- a/SETUP.md +++ b/SETUP.md @@ -34,4 +34,27 @@ Follow these steps to set up URY Pulse after completing basic ERPNext configurat - **Expense** : Provide the expense name. - **Percentage Type** : Choose the percentage type (Net Sales or Gross Sales). - **Percent** : Specify the percentage of the selected type. - - **Depreciation** : Add depreciation amount if applicable. \ No newline at end of file + - Under **Employee Costs**: + - **Employee Costs** : Table to list daily fixed expenses as a part of employee costs. + - **Expense** : Provide the expense name. + - **Amount** : Specify amount for each expense. + - **Depreciation** : Add depreciation amount if applicable. + + + ### Daily Gross Salary Cost is calculated from employees attendance. + +Follow these steps to set up the payment type and payment amount for employees: + +#### Step 1: + +- Navigate to **Employee** in your site. +- Choose the relevant **Employee**. + +#### Step 2: + +- Under the **Salary** tab: + - **Payment Type** : Choose between Salary or Daily Wage. + - **Payment Amount** : Enter the corresponding payment amount. + + +Follow the [Attendance documentation](https://frappehr.com/docs/v14/en/attendance#3-features) for marking the attendance or use the [Employee Attendance Tool](https://frappehr.com/docs/v14/en/employee-attendance-tool#2-how-to-mark-attendance-using-employee-attendance-tool) \ No newline at end of file diff --git a/ury_pulse/fixtures/custom_field.json b/ury_pulse/fixtures/custom_field.json new file mode 100644 index 0000000..18a1afc --- /dev/null +++ b/ury_pulse/fixtures/custom_field.json @@ -0,0 +1,110 @@ +[ + { + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": null, + "docstatus": 0, + "doctype": "Custom Field", + "dt": "Employee", + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "payment_type", + "fieldtype": "Select", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "insert_after": "salary_currency", + "is_system_generated": 0, + "is_virtual": 0, + "label": "Payment Type", + "length": 0, + "mandatory_depends_on": null, + "modified": "2024-01-09 14:49:44.365494", + "module": null, + "name": "Employee-payment_type", + "no_copy": 0, + "non_negative": 0, + "options": "\nDaily Wage\nSalary", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "sort_options": 0, + "translatable": 0, + "unique": 0, + "width": null + }, + { + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": null, + "docstatus": 0, + "doctype": "Custom Field", + "dt": "Employee", + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "payment_amount", + "fieldtype": "Currency", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "insert_after": "payment_type", + "is_system_generated": 0, + "is_virtual": 0, + "label": "Payment Amount", + "length": 0, + "mandatory_depends_on": null, + "modified": "2024-01-09 15:27:00.294408", + "module": null, + "name": "Employee-payment_amount", + "no_copy": 0, + "non_negative": 0, + "options": null, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "sort_options": 0, + "translatable": 0, + "unique": 0, + "width": null + } +] \ No newline at end of file diff --git a/ury_pulse/hooks.py b/ury_pulse/hooks.py index e235790..33d7aa5 100644 --- a/ury_pulse/hooks.py +++ b/ury_pulse/hooks.py @@ -215,3 +215,19 @@ # auth_hooks = [ # "ury_pulse.auth.validate" # ] + +fixtures = [ + { + "doctype": "Custom Field", + "filters": [ + [ + "name", + "in", + { + "Employee-payment_amount", + "Employee-payment_type", + }, + ] + ], + } +] \ No newline at end of file diff --git a/ury_pulse/ury_pulse/doctype/ury_daily_p_and_l/profit_loss_details.html b/ury_pulse/ury_pulse/doctype/ury_daily_p_and_l/profit_loss_details.html index df55047..06c4857 100644 --- a/ury_pulse/ury_pulse/doctype/ury_daily_p_and_l/profit_loss_details.html +++ b/ury_pulse/ury_pulse/doctype/ury_daily_p_and_l/profit_loss_details.html @@ -88,11 +88,11 @@

Date: {{ data.date }}

{{ frappe.utils.fmt_money(data.gross_profit or '', currency=currency) }} {{ data.gross_profit_percent }}% - + {% for expense in data.indirect_expenses_breakup %} {{ expense.breakup }} diff --git a/ury_pulse/ury_pulse/doctype/ury_daily_p_and_l/ury_daily_p_and_l.json b/ury_pulse/ury_pulse/doctype/ury_daily_p_and_l/ury_daily_p_and_l.json index 9060098..63ba1b1 100644 --- a/ury_pulse/ury_pulse/doctype/ury_daily_p_and_l/ury_daily_p_and_l.json +++ b/ury_pulse/ury_pulse/doctype/ury_daily_p_and_l/ury_daily_p_and_l.json @@ -369,7 +369,6 @@ "depends_on": "eval:doc.docstatus==1", "fieldname": "total_employee_costs", "fieldtype": "Currency", - "hidden": 1, "label": "Employee Cost", "precision": "2", "read_only": 1 @@ -378,7 +377,6 @@ "depends_on": "eval:doc.docstatus==1", "fieldname": "total_employee_costs_percent", "fieldtype": "Percent", - "hidden": 1, "label": "Employee Cost Percent", "read_only": 1 }, @@ -402,7 +400,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-11-15 00:18:37.012201", + "modified": "2024-01-09 14:15:52.566841", "modified_by": "Administrator", "module": "URY Pulse", "name": "URY Daily P and L", diff --git a/ury_pulse/ury_pulse/doctype/ury_daily_p_and_l/ury_daily_p_and_l.py b/ury_pulse/ury_pulse/doctype/ury_daily_p_and_l/ury_daily_p_and_l.py index 9252c89..983e838 100644 --- a/ury_pulse/ury_pulse/doctype/ury_daily_p_and_l/ury_daily_p_and_l.py +++ b/ury_pulse/ury_pulse/doctype/ury_daily_p_and_l/ury_daily_p_and_l.py @@ -262,7 +262,7 @@ def before_submit(self): self.net_sales = self.gross_sales - self.cash_discount_round_off - self.tax if self.net_sales == 0.0: - self.gross_sales_percent = self.cash_discount_round_off_percent = self.tax_percent = self.cogs_percent = self.total_direct_expenses_percent = self.gross_profit_percent = self.other_expenses_percent = self.depreciation_percent = self.total_indirect_expenses_percent = self.net_profit_percent = 0.0 + self.gross_sales_percent = self.cash_discount_round_off_percent = self.tax_percent = self.cogs_percent = self.total_direct_expenses_percent = self.gross_profit_percent = self.total_employee_costs_percent = self.other_expenses_percent = self.depreciation_percent = self.total_indirect_expenses_percent = self.net_profit_percent = 0.0 else: self.gross_sales_percent = round(((self.gross_sales / self.net_sales) * 100),2) self.cash_discount_round_off_percent = round(((self.cash_discount_round_off / self.net_sales) * 100),2) @@ -298,9 +298,115 @@ def before_submit(self): '''INDIRECT EXPENSES''' + self.total_indirect_expenses = 0 + + + #EMPLOYEE COSTS + salary_cost_gross = 0 + self.total_employee_costs = 0 + + attendance_count = frappe.db.sql(''' + SELECT + %(date)s AS "Date", + COUNT(b.`name`) AS "Total Attendance" + FROM `tabAttendance` b + LEFT JOIN `tabEmployee` c ON + c.name = b.employee + WHERE + b.`attendance_date` = %(date)s + AND c.`branch` = %(branch)s + AND b.`docstatus` = 1 + AND b.`status` IN ("Present", "Half Day") + ''', {"branch": self.branch, "date": self.date}, as_dict=True) + + attendance_count = attendance_count[0] + + if attendance_count['Total Attendance'] == 0: + frappe.throw(title='No Attendance !',msg=("Attendance not marked")) + + ns_employee_attendance_list = frappe.db.sql(''' + SELECT + b.`employee_name` AS "Name" + FROM `tabAttendance` b + LEFT JOIN `tabEmployee` c ON ( + c.name = b.employee + AND ( + (c.`payment_amount` IS NULL OR c.`payment_amount` = 0.0 ) + OR + (c.`payment_type` IS NULL OR c.`payment_type` = "") + ) + ) + WHERE + b.`attendance_date` = %(date)s + AND c.`branch` = %(branch)s + GROUP BY + b.`employee_name` + ''', {"branch": self.branch, "date": self.date}, as_dict=True) + + if len(ns_employee_attendance_list) > 0: + ns_employee_attendance_list = json.dumps(ns_employee_attendance_list) + frappe.throw(title='Set Payment Type/Amount',msg=("Employees: {0}").format(ns_employee_attendance_list)) + + employee_attendance_dw_list = frappe.db.sql(''' + SELECT + %(date)s AS "Date", + b.`employee` AS "Employee", + b.`status` AS "Status", + c.`payment_amount` AS "Salary" + FROM `tabAttendance` b + LEFT JOIN `tabEmployee` c ON c.name = b.employee + WHERE + b.`attendance_date` = %(date)s + AND c.`branch` = %(branch)s + AND c.`payment_type` = "Daily Wage" + ''', {"branch": self.branch, "date": self.date}, as_dict=True) + + for attendance in employee_attendance_dw_list: + if attendance["Status"] == "Half Day": + salary_cost_gross = round((salary_cost_gross + 0.5 * attendance["Salary"]),2) + if attendance["Status"] == "Present": + salary_cost_gross = round((salary_cost_gross + attendance["Salary"]),2) + + date_str = self.date + date_obj = datetime.strptime(date_str, '%Y-%m-%d') + year = date_obj.year + month_number = date_obj.month + days = calendar.monthrange(year, month_number)[1] + + employee_attendance_sl_list = frappe.db.sql(''' + SELECT + %(date)s AS "Date", + b.`name` AS "Employee", + b.`payment_amount` AS "Salary" + FROM `tabEmployee` b + WHERE + b.`branch` = %(branch)s + AND b.`payment_type` = "Salary" + ''', {"branch": self.branch, "date": self.date}, as_dict=True) + + for attendance in employee_attendance_sl_list: + salary_cost_gross = round((salary_cost_gross + attendance["Salary"]/days),2) + + if self.net_sales != 0.0: + salary_cost_gross_percent = round(((salary_cost_gross / self.net_sales) * 100),3) + else: + salary_cost_gross_percent = 0.0 + self.append("employee_costs_breakup", {"breakup": "Salary Cost Gross", "amount": salary_cost_gross,"percent":salary_cost_gross_percent}) + self.total_employee_costs += salary_cost_gross + + for expense in report_settings.employee_costs: + if self.net_sales != 0.0: + expense_percent = round(((expense.amount / self.net_sales) * 100),3) + else: + expense_percent = 0.0 + self.append("employee_costs_breakup", {"breakup": expense.expense, "amount": expense.amount,"percent":expense_percent}) + self.total_employee_costs += expense.amount + if self.net_sales != 0.0: + self.total_employee_costs_percent = round(((self.total_employee_costs / self.net_sales) * 100),3) + self.total_indirect_expenses += self.total_employee_costs + #INDIRECT EXPENSES - self.total_indirect_expenses = 0 # Calculate and append Electricity electricity_charges = electricity_reading * report_settings.electricity_charges if self.net_sales != 0.0: diff --git a/ury_pulse/ury_pulse/doctype/ury_report_settings/ury_report_settings.json b/ury_pulse/ury_pulse/doctype/ury_report_settings/ury_report_settings.json index f4a5211..2440ed6 100644 --- a/ury_pulse/ury_pulse/doctype/ury_report_settings/ury_report_settings.json +++ b/ury_pulse/ury_pulse/doctype/ury_report_settings/ury_report_settings.json @@ -20,9 +20,7 @@ "indirect_fixed_expenses", "percentage_expenses", "employee_costs_section", - "bonus", - "staff_accommodation_charges", - "staff_food_charges", + "employee_costs", "section_break_4c2bz", "depreciation" ], @@ -109,31 +107,12 @@ "description": "
  • Daily Gross Salary Cost is calculated from employees attendance.
  • \n
    ", "fieldname": "employee_costs_section", "fieldtype": "Section Break", - "hidden": 1, "label": "Employee Costs" }, - { - "fieldname": "bonus", - "fieldtype": "Currency", - "label": "Bonus", - "precision": "2" - }, - { - "fieldname": "staff_food_charges", - "fieldtype": "Currency", - "label": "Staff Food Charges", - "precision": "2" - }, { "fieldname": "section_break_4c2bz", "fieldtype": "Section Break" }, - { - "fieldname": "staff_accommodation_charges", - "fieldtype": "Currency", - "label": "Staff Accommodation Charges", - "precision": "2" - }, { "description": "Per Unit", "fieldname": "electricity_charges", @@ -145,11 +124,18 @@ "fieldtype": "Table", "label": "Burning Materials (Other Consumables)", "options": "URY Materials" + }, + { + "description": "Daily Fixed", + "fieldname": "employee_costs", + "fieldtype": "Table", + "label": "Employee Costs", + "options": "URY Fixed Expenses" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2023-10-25 18:42:45.149783", + "modified": "2024-01-09 15:20:17.567008", "modified_by": "Administrator", "module": "URY Pulse", "name": "URY Report Settings",