From 04d5412754a8bda5789c8670f67117174c73ed38 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 30 Jul 2018 11:09:05 +0530 Subject: [PATCH] Fixed conflict --- .../additional_salary/additional_salary.json | 90 ++++++++-------- .../additional_salary/additional_salary.py | 65 ++++-------- .../leave_encashment/leave_encashment.py | 3 +- .../hr/doctype/payroll_entry/payroll_entry.py | 100 ++++++++---------- erpnext/hr/doctype/salary_slip/salary_slip.js | 30 +++--- .../hr/doctype/salary_slip/salary_slip.json | 35 +++++- erpnext/hr/doctype/salary_slip/salary_slip.py | 21 +++- .../salary_structure_assignment.json | 13 +-- 8 files changed, 185 insertions(+), 172 deletions(-) diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.json b/erpnext/hr/doctype/additional_salary/additional_salary.json index e669e1014fc..b29b30b7245 100644 --- a/erpnext/hr/doctype/additional_salary/additional_salary.json +++ b/erpnext/hr/doctype/additional_salary/additional_salary.json @@ -60,7 +60,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 1, + "in_list_view": 0, "in_standard_filter": 0, "label": "Company", "length": 0, @@ -94,7 +94,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, + "in_standard_filter": 1, "label": "Employee", "length": 0, "no_copy": 0, @@ -107,7 +107,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "translatable": 0, "unique": 0 @@ -127,7 +127,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Employee Name", "length": 0, @@ -161,7 +161,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, + "in_standard_filter": 1, "label": "Salary Component", "length": 0, "no_copy": 0, @@ -174,7 +174,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "translatable": 0, "unique": 0 @@ -251,7 +251,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "from_date", + "fieldname": "payroll_date", "fieldtype": "Date", "hidden": 0, "ignore_user_permissions": 0, @@ -260,7 +260,7 @@ "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, - "label": "From Date", + "label": "Payroll Date", "length": 0, "no_copy": 0, "permlevel": 0, @@ -271,39 +271,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "to_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "To Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "translatable": 0, "unique": 0 @@ -322,7 +290,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Amount", "length": 0, @@ -374,6 +342,39 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "salary_slip", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Salary Slip", + "length": 0, + "no_copy": 0, + "options": "Salary Slip", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -417,7 +418,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-07-26 12:05:16.337082", + "modified": "2018-07-28 17:50:25.725444", "modified_by": "Administrator", "module": "HR", "name": "Additional Salary", @@ -471,5 +472,6 @@ "sort_order": "DESC", "title_field": "employee", "track_changes": 1, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.py b/erpnext/hr/doctype/additional_salary/additional_salary.py index e7582271f82..b54d726f16e 100644 --- a/erpnext/hr/doctype/additional_salary/additional_salary.py +++ b/erpnext/hr/doctype/additional_salary/additional_salary.py @@ -17,11 +17,9 @@ class AdditionalSalary(Document): def validate_dates(self): date_of_joining, relieving_date = frappe.db.get_value("Employee", self.employee, ["date_of_joining", "relieving_date"]) - if getdate(self.from_date) > getdate(self.to_date): - frappe.throw(_("To date can not be less than from date")) - elif date_of_joining and getdate(self.from_date) < getdate(date_of_joining): - frappe.throw(_("From date can not be less than employee's joining date")) - elif relieving_date and getdate(self.to_date) > getdate(relieving_date): + if date_of_joining and getdate(self.payroll_date) < getdate(date_of_joining): + frappe.throw(_("Payroll date can not be less than employee's joining date")) + elif relieving_date and getdate(self.payroll_date) > getdate(relieving_date): frappe.throw(_("To date can not greater than employee's relieving date")) def get_amount(self, sal_start_date, sal_end_date): @@ -39,46 +37,27 @@ class AdditionalSalary(Document): @frappe.whitelist() def get_additional_salary_component(employee, start_date, end_date): additional_components = frappe.db.sql(""" - select name from `tabAdditional Salary` - where employee=%(employee)s - and docstatus = 1 - and ( - (%(from_date)s between from_date and to_date) - or (%(to_date)s between from_date and to_date) - or (from_date between %(from_date)s and %(to_date)s) - )""", { + select salary_component, sum(amount) as amount from `tabAdditional Salary` + where employee=%(employee)s + and docstatus = 1 + and payroll_date between %(from_date)s and %(to_date)s + group by salary_component + """, { 'employee': employee, 'from_date': start_date, 'to_date': end_date - }) + }, as_dict=1) - if additional_components: - additional_components_array = [] - for additional_component in additional_components: - additional_component_obj = frappe.get_doc("Additional Salary", additional_component[0]) - amount = additional_component_obj.get_amount(start_date, end_date) - salary_component = frappe.get_doc("Salary Component", additional_component_obj.salary_component) - added = False - for added_component in additional_components_array: - if added_component["struct_row"]["salary_component"] == salary_component.name: - added_component["amount"] += amount - added = True - if added: - continue - struct_row = {} - additional_components_dict = {} - struct_row['depends_on_lwp'] = salary_component.depends_on_lwp - struct_row['salary_component'] = salary_component.name - struct_row['abbr'] = salary_component.salary_component_abbr - struct_row['do_not_include_in_total'] = salary_component.do_not_include_in_total - struct_row['is_tax_applicable'] = salary_component.is_tax_applicable - struct_row['variable_based_on_taxable_salary'] = salary_component.variable_based_on_taxable_salary - struct_row['is_additional_component'] = salary_component.is_additional_component - additional_components_dict['amount'] = amount - additional_components_dict['struct_row'] = struct_row - additional_components_dict['type'] = salary_component.type - additional_components_array.append(additional_components_dict) + additional_components_list = [] + for d in additional_components: + component = frappe.get_doc("Salary Component", d.salary_component) + struct_row = {'salary_component': d.salary_component} + for field in ["depends_on_lwp", "abbr", "is_tax_applicable", "variable_based_on_taxable_salary", "is_additional_component"]: + struct_row[field] = component.get(field) - if len(additional_components_array) > 0: - return additional_components_array - return False + additional_components_list.append({ + 'amount': d.amount, + 'type': component.type, + 'struct_row': struct_row + }) + return additional_components_list \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index a61210c7294..9944bc53806 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -30,8 +30,7 @@ class LeaveEncashment(Document): additional_salary.company = frappe.get_value("Employee", self.employee, "company") additional_salary.employee = self.employee additional_salary.salary_component = frappe.get_value("Leave Type", self.leave_type, "earning_component") - additional_salary.from_date = self.encashment_date - additional_salary.to_date = self.encashment_date + additional_salary.payroll_date = self.encashment_date additional_salary.amount = self.encashment_amount additional_salary.submit() diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.py b/erpnext/hr/doctype/payroll_entry/payroll_entry.py index 2d866bac688..7eaa063752e 100644 --- a/erpnext/hr/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/hr/doctype/payroll_entry/payroll_entry.py @@ -12,7 +12,6 @@ from erpnext.accounts.utils import get_fiscal_year from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee class PayrollEntry(Document): - def on_submit(self): self.create_salary_slips() @@ -97,41 +96,25 @@ class PayrollEntry(Document): Creates salary slip for selected employees if already not created """ self.check_permission('write') - self.created = 1; - emp_list = self.get_emp_list() - ss_list = [] + self.created = 1 + emp_list = [d.employee for d in self.get_emp_list()] if emp_list: - for emp in emp_list: - if not frappe.db.sql("""select - name from `tabSalary Slip` - where - docstatus!= 2 and - employee = %s and - start_date >= %s and - end_date <= %s and - company = %s - """, (emp['employee'], self.start_date, self.end_date, self.company)): - ss = frappe.get_doc({ - "doctype": "Salary Slip", - "salary_slip_based_on_timesheet": self.salary_slip_based_on_timesheet, - "payroll_frequency": self.payroll_frequency, - "start_date": self.start_date, - "end_date": self.end_date, - "employee": emp['employee'], - "employee_name": frappe.get_value("Employee", {"name":emp['employee']}, "employee_name"), - "company": self.company, - "posting_date": self.posting_date, - "deduct_tax_for_unclaimed_employee_benefits": self.deduct_tax_for_unclaimed_employee_benefits, - "deduct_tax_for_unsubmitted_tax_exemption_proof": self.deduct_tax_for_unsubmitted_tax_exemption_proof - }) - ss.insert() - ss_dict = {} - ss_dict["Employee Name"] = ss.employee_name - ss_dict["Total Pay"] = fmt_money(ss.rounded_total,currency = frappe.defaults.get_global_default("currency")) - ss_dict["Salary Slip"] = format_as_links(ss.name)[0] - ss_list.append(ss_dict) - return create_log(ss_list) - + args = frappe._dict({ + "salary_slip_based_on_timesheet": self.salary_slip_based_on_timesheet, + "payroll_frequency": self.payroll_frequency, + "start_date": self.start_date, + "end_date": self.end_date, + "company": self.company, + "posting_date": self.posting_date, + "deduct_tax_for_unclaimed_employee_benefits": self.deduct_tax_for_unclaimed_employee_benefits, + "deduct_tax_for_unsubmitted_tax_exemption_proof": self.deduct_tax_for_unsubmitted_tax_exemption_proof, + "payroll_entry": self.payroll_entry + }) + if len(emp_list) > 50: + frappe.enqueue(create_salary_slips_for_employees, timeout=600, employees=emp_list, args=args) + else: + create_salary_slips_for_employees(emp_list, args, publish_progress=False) + def get_sal_slip_list(self, ss_status, as_dict=False): """ Returns list of salary slips based on selected criteria @@ -151,9 +134,6 @@ class PayrollEntry(Document): """ self.check_permission('write') - # self.create_salary_slips() - - jv_name = "" ss_list = self.get_sal_slip_list(ss_status=0) submitted_ss = [] not_submitted_ss = [] @@ -175,14 +155,15 @@ class PayrollEntry(Document): except frappe.ValidationError: not_submitted_ss.append(ss_dict) + if submitted_ss: - jv_name = self.make_accrual_jv_entry() + self.make_accrual_jv_entry() frappe.msgprint(_("Salary Slip submitted for period from {0} to {1}") .format(ss_obj.start_date, ss_obj.end_date)) self.email_salary_slip(submitted_ss) - return create_submit_log(submitted_ss, not_submitted_ss, jv_name) + return create_submit_log(submitted_ss, not_submitted_ss) def email_salary_slip(self, submitted_ss): if frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee"): @@ -509,28 +490,16 @@ def get_month_details(year, month): else: frappe.throw(_("Fiscal Year {0} not found").format(year)) - -@frappe.whitelist() -def create_log(ss_list): - if not ss_list: - frappe.throw( - _("There's no employee for the given criteria. Check that Salary Slips have not already been created."), - title='Error' - ) - return ss_list - - def format_as_links(salary_slip): return ['{0}'.format(salary_slip)] -def create_submit_log(submitted_ss, not_submitted_ss, jv_name): - +def create_submit_log(submitted_ss, not_submitted_ss): if not submitted_ss and not not_submitted_ss: frappe.msgprint(_("No salary slip found to submit for the above selected criteria OR salary slip already submitted")) if not_submitted_ss: - frappe.msgprint(_("Could not submit any Salary Slip
\ + frappe.msgprint(_("Could not submit some Salary Slips
\ Possible reasons:
\ 1. Net pay is less than 0.
\ 2. Company Email Address specified in employee master is not valid.
")) @@ -584,3 +553,26 @@ def payroll_entry_has_bank_entries(name): response['submitted'] = 1 if bank_entries else 0 return response + +def create_salary_slips_for_employees(employees, args, publish_progress=True): + salary_slips_exists_for = get_existing_salary_slips(employees, args) + count=0 + for emp in employees: + if emp not in salary_slips_exists_for: + ss = frappe.new_doc("Salary Slip") + ss.employee = emp + ss.update(args) + ss.insert() + count+=1 + if publish_progress: + frappe.publish_progress(count*100/len(set(employees) - set(salary_slips_exists_for)), + title = _("Creating Salary Slips...")) + +def get_existing_salary_slips(employees, args): + return frappe.db.sql_list(""" + select distinct employee from `tabSalary Slip` + where docstatus!= 2 and company = %s + and start_date >= %s and end_date <= %s + and employee in (%s) + """ % ('%s', '%s', '%s', ', '.join(['%s']*len(employees))), + [args.company, args.start_date, args.end_date] + employees) \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.js b/erpnext/hr/doctype/salary_slip/salary_slip.js index 755bb40edb7..cbbc9e96aa3 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.js +++ b/erpnext/hr/doctype/salary_slip/salary_slip.js @@ -39,10 +39,13 @@ frappe.ui.form.on("Salary Slip", { start_date: function(frm, dt, dn){ if(frm.doc.start_date){ frm.trigger("set_end_date"); - get_emp_and_leave_details(frm.doc, dt, dn); } }, + end_date: function(frm, dt, dn) { + get_emp_and_leave_details(frm.doc, dt, dn); + }, + set_end_date: function(frm){ frappe.call({ method: 'erpnext.hr.doctype.payroll_entry.payroll_entry.get_end_date', @@ -82,8 +85,6 @@ frappe.ui.form.on("Salary Slip", { payroll_frequency: function(frm, dt, dn) { frm.trigger("toggle_fields"); frm.set_value('end_date', ''); - frm.set_value('start_date', ''); - get_emp_and_leave_details(frm.doc, dt, dn); }, employee: function(frm, dt, dn) { @@ -121,21 +122,14 @@ frappe.ui.form.on('Salary Slip Timesheet', { // Get leave details //--------------------------------------------------------------------- var get_emp_and_leave_details = function(doc, dt, dn) { - if(!doc.start_date){ - return frappe.call({ - method: 'get_emp_and_leave_details', - doc: locals[dt][dn], - callback: function(r, rt) { - cur_frm.refresh(); - calculate_all(doc, dt, dn); - } - }); - } -} - -cur_frm.cscript.employee = function(doc,dt,dn){ - doc.salary_structure = '' - get_emp_and_leave_details(doc, dt, dn); + return frappe.call({ + method: 'get_emp_and_leave_details', + doc: locals[dt][dn], + callback: function(r, rt) { + cur_frm.refresh(); + calculate_all(doc, dt, dn); + } + }); } cur_frm.cscript.leave_without_pay = function(doc,dt,dn){ diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.json b/erpnext/hr/doctype/salary_slip/salary_slip.json index 473d6e3ce68..2e2856d5346 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.json +++ b/erpnext/hr/doctype/salary_slip/salary_slip.json @@ -318,6 +318,39 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "payroll_entry", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Payroll Entry", + "length": 0, + "no_copy": 0, + "options": "Payroll Entry", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -1872,7 +1905,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-06-28 12:18:50.538159", + "modified": "2018-07-27 19:23:35.587516", "modified_by": "Administrator", "module": "HR", "name": "Salary Slip", diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 5a91796b4d9..f6e3208630b 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -241,10 +241,14 @@ class SalarySlip(TransactionBase): if self.payroll_frequency: cond += """and ss.payroll_frequency = '%(payroll_frequency)s'""" % {"payroll_frequency": self.payroll_frequency} - st_name = frappe.db.sql("""select sa.salary_structure from `tabSalary Structure Assignment` sa - join `tabSalary Structure` ss where sa.salary_structure=ss.name - and sa.docstatus = 1 and ss.docstatus = 1 and ss.is_active ='Yes' %s - order by sa.from_date desc limit 1 """ %cond, {'employee': self.employee, 'start_date': self.start_date, + st_name = frappe.db.sql(""" + select sa.salary_structure + from `tabSalary Structure Assignment` sa join `tabSalary Structure` ss + where sa.salary_structure=ss.name + and sa.docstatus = 1 and ss.docstatus = 1 and ss.is_active ='Yes' %s + order by sa.from_date desc + limit 1 + """ %cond, {'employee': self.employee, 'start_date': self.start_date, 'end_date': self.end_date, 'joining_date': joining_date}) if st_name: @@ -480,17 +484,26 @@ class SalarySlip(TransactionBase): else: self.set_status() self.update_status(self.name) + self.update_salary_slip_in_additional_salary() if (frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee")) and not frappe.flags.via_payroll_entry: self.email_salary_slip() def on_cancel(self): self.set_status() self.update_status() + self.update_salary_slip_in_additional_salary() def on_trash(self): from frappe.model.naming import revert_series_if_last revert_series_if_last(self.series, self.name) + def update_salary_slip_in_additional_salary(self): + salary_slip = self.name if self.docstatus==1 else None + frappe.db.sql(""" + update `tabAdditional Salary` set salary_slip=%s + where employee=%s and payroll_date between %s and %s and docstatus=1 + """, (salary_slip, self.employee, self.start_date, self.end_date)) + def email_salary_slip(self): receiver = frappe.db.get_value("Employee", self.employee, "prefered_email") diff --git a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json b/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json index 95308dcafbf..66cb910b7e3 100644 --- a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json +++ b/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json @@ -28,7 +28,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, + "in_standard_filter": 1, "label": "Employee", "length": 0, "no_copy": 0, @@ -41,7 +41,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "translatable": 0, "unique": 0 @@ -129,7 +129,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, + "in_standard_filter": 1, "label": "Salary Structure", "length": 0, "no_copy": 0, @@ -142,7 +142,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "translatable": 0, "unique": 0 @@ -412,7 +412,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-06-22 14:54:05.494234", + "modified": "2018-07-27 16:23:17.490097", "modified_by": "Administrator", "module": "HR", "name": "Salary Structure Assignment", @@ -485,5 +485,6 @@ "sort_order": "DESC", "title_field": "employee_name", "track_changes": 1, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } \ No newline at end of file