From e04a753d53014918bfc7fbdd8a2c77c2314ff0c7 Mon Sep 17 00:00:00 2001 From: alsum Date: Tue, 13 Nov 2018 11:43:58 +0200 Subject: [PATCH 1/5] assign-sa-to-group-of-employees --- .../salary_structure/salary_structure.js | 40 +++++++++++ .../salary_structure/salary_structure.py | 70 +++++++++++++++++++ .../salary_structure/test_salary_structure.py | 13 ++++ 3 files changed, 123 insertions(+) diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.js b/erpnext/hr/doctype/salary_structure/salary_structure.js index 4a111e7d4b..033938de36 100755 --- a/erpnext/hr/doctype/salary_structure/salary_structure.js +++ b/erpnext/hr/doctype/salary_structure/salary_structure.js @@ -58,6 +58,9 @@ frappe.ui.form.on('Salary Structure', { doc.company = frm.doc.company; frappe.set_route('Form', 'Salary Structure Assignment', doc.name); }); + frm.add_custom_button(__("Assign to Employees"),function () { + frm.trigger('assign_to_employees') + }) } let fields_read_only = ["is_tax_applicable", "is_flexible_benefit", "variable_based_on_taxable_salary"]; fields_read_only.forEach(function(field) { @@ -65,6 +68,43 @@ frappe.ui.form.on('Salary Structure', { }); }, + assign_to_employees:function (frm) { + var d = new frappe.ui.Dialog({ + title: __("Assign to Employees"), + fields: [ + {fieldname: "sec_break", fieldtype: "Section Break", label: __("Filter Employees By (Optional)")}, + {fieldname: "grade", fieldtype: "Link", options: "Employee Grade", label: __("Employee Grade")}, + {fieldname:'department', fieldtype:'Link', options: 'Department', label: __('Department')}, + {fieldname:'designation', fieldtype:'Link', options: 'Designation', label: __('Designation')}, + {fieldname:"employee", fieldtype: "Link", options: "Employee", label: __("Employee")}, + {fieldname:'base_variable', fieldtype:'Section Break'}, + {fieldname:'from_date', fieldtype:'Date', label: __('From Date'), "reqd": 1}, + {fieldname:'base_col_br', fieldtype:'Column Break'}, + {fieldname:'base', fieldtype:'Currency', label: __('Base')}, + {fieldname:'variable', fieldtype:'Currency', label: __('Variable')} + ], + primary_action: function() { + var data = d.get_values(); + + frappe.call({ + doc: frm.doc, + method: "assign_salary_structure", + args: data, + callback: function(r) { + if(!r.exc) { + d.hide(); + frm.reload_doc(); + } + } + }); + }, + primary_action_label: __('Assign') + }); + + + d.show(); + }, + salary_slip_based_on_timesheet: function(frm) { frm.trigger("toggle_fields") }, diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.py b/erpnext/hr/doctype/salary_structure/salary_structure.py index a36d820f2c..7ead14030f 100644 --- a/erpnext/hr/doctype/salary_structure/salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/salary_structure.py @@ -65,6 +65,76 @@ class SalaryStructure(Document): if not have_a_flexi and flt(self.max_benefits) > 0: frappe.throw(_("Salary Structure should have flexible benefit component(s) to dispense benefit amount")) + def get_employees(self, **kwargs): + conditions, values = [], [] + for field, value in kwargs.items(): + if value: + conditions.append("{0}=%s".format(field)) + values.append(value) + + condition_str = " and " + " and ".join(conditions) if conditions else "" + + employees = frappe.db.sql_list("select name from tabEmployee where status='Active' {condition}" + .format(condition=condition_str), tuple(values)) + + return employees + + @frappe.whitelist() + def assign_salary_structure(self, grade=None, department=None, designation=None,employee=None, + from_date=None, base=None,variable=None): + employees = self.get_employees(grade= grade,department= department,designation= designation,name=employee) + + if employees: + if len(employees) > 20: + frappe.enqueue(assign_salary_structure_for_employees, timeout=600, + employees=employees, salary_structure=self,from_date=from_date, base=base,variable=variable) + else: + assign_salary_structure_for_employees(employees, self,from_date=from_date, base=base,variable=variable) + else: + frappe.msgprint(_("No Employee Found")) + + + +def assign_salary_structure_for_employees(employees, salary_structure,from_date=None, base=None,variable=None): + salary_structures_assignments = [] + existing_assignments_for = get_existing_assignments(employees, salary_structure.name,from_date) + count=0 + for employee in employees: + if employee in existing_assignments_for: + continue + count +=1 + + salary_structures_assignment = create_salary_structures_assignment(employee, salary_structure, from_date, base, variable) + salary_structures_assignments.append(salary_structures_assignment) + frappe.publish_progress(count*100/len(set(employees) - set(existing_assignments_for)), title = _("Assigning Structures...")) + + if salary_structures_assignments: + frappe.msgprint(_("Structures have been assigned successfully")) + + +def create_salary_structures_assignment(employee, salary_structure, from_date, base, variable): + assignment = frappe.new_doc("Salary Structure Assignment") + assignment.employee = employee + assignment.salary_structure = salary_structure.name + assignment.from_date = from_date + assignment.base = base + assignment.variable = variable + assignment.save(ignore_permissions = True) + assignment.submit() + return assignment.name + + +def get_existing_assignments(employees, salary_structure,from_date): + salary_structures_assignments = frappe.db.sql_list(""" + select distinct employee from `tabSalary Structure Assignment` + where salary_structure=%s and employee in (%s) + and from_date=%s and docstatus=1 + """ % ('%s', ', '.join(['%s']*len(employees)),'%s'), [salary_structure] + employees+[from_date]) + if salary_structures_assignments: + frappe.msgprint(_("Skipping Salary Structure Assignment for the following employees, as Salary Structure Assignment records already exists against them. {0}") + .format("\n".join(salary_structures_assignments))) + return salary_structures_assignments + @frappe.whitelist() def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None): def postprocess(source, target): diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.py b/erpnext/hr/doctype/salary_structure/test_salary_structure.py index 1a16db74a5..1a660d90ec 100644 --- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/test_salary_structure.py @@ -71,6 +71,19 @@ class TestSalaryStructure(unittest.TestCase): for row in salary_structure.deductions: self.assertFalse(("\n" in row.formula) or ("\n" in row.condition)) + def test_salary_structures_assignment(self): + salary_structure = make_salary_structure("Salary Structure Sample", "Monthly") + employee = "test_assign_stucture@salary.com" + employee_doc_name = make_employee(employee) + # clear the already assigned stuctures + frappe.db.sql('''delete from `tabSalary Structure Assignment` where employee=%s and salary_structure=%s ''', + ("test_assign_stucture@salary.com",salary_structure.name)) + #test structure_assignment + salary_structure.assign_salary_structure(employee=employee_doc_name,from_date='2013-01-01',base=5000,variable=200) + salary_structure_assignment = frappe.get_doc("Salary Structure Assignment",{'employee':employee_doc_name, 'from_date':'2013-01-01'}) + self.assertEqual(salary_structure_assignment.docstatus, 1) + self.assertEqual(salary_structure_assignment.base, 5000) + self.assertEqual(salary_structure_assignment.variable, 200) def make_salary_structure(salary_structure, payroll_frequency, employee=None, dont_submit=False, other_details=None, test_tax=False): if test_tax: From fe6202482425e21faea3a359cc90e207143fbfc7 Mon Sep 17 00:00:00 2001 From: Frappe Bot Date: Tue, 13 Nov 2018 11:23:06 +0000 Subject: [PATCH 2/5] bumped to version 11.0.3-beta.21 --- erpnext/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 87b7942070..f48b48b6f8 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -12,7 +12,7 @@ app_license = "GNU General Public License (v3)" source_link = "https://github.com/frappe/erpnext" develop_version = '12.x.x-develop' -staging_version = '11.0.3-beta.20' +staging_version = '11.0.3-beta.21' error_report_email = "support@erpnext.com" From d34bfa8c2cb642b50d981011261346232c351418 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 13 Nov 2018 18:19:58 +0530 Subject: [PATCH 3/5] fix(patch): Fetch missing gst hsn code in transactions and set hsn-wise taxes --- erpnext/patches.txt | 3 +- .../patches/v11_0/set_missing_gst_hsn_code.py | 43 +++++++++++++++++++ erpnext/regional/india/setup.py | 2 +- 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 erpnext/patches/v11_0/set_missing_gst_hsn_code.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ca256679e8..f771181f8d 100755 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -572,9 +572,10 @@ execute:frappe.delete_doc("Page", "Sales Analytics") execute:frappe.delete_doc("Page", "Purchase Analytics") execute:frappe.delete_doc("Page", "Stock Analytics") execute:frappe.delete_doc("Page", "Production Analytics") -erpnext.patches.v11_0.ewaybill_fields_gst_india +erpnext.patches.v11_0.ewaybill_fields_gst_india #2018-11-13 erpnext.patches.v11_0.drop_column_max_days_allowed erpnext.patches.v11_0.change_healthcare_desktop_icons erpnext.patches.v10_0.update_user_image_in_employee erpnext.patches.v11_0.update_delivery_trip_status erpnext.patches.v10_0.repost_gle_for_purchase_receipts_with_rejected_items +erpnext.patches.v11_0.set_missing_gst_hsn_code diff --git a/erpnext/patches/v11_0/set_missing_gst_hsn_code.py b/erpnext/patches/v11_0/set_missing_gst_hsn_code.py new file mode 100644 index 0000000000..9d28a4a3f3 --- /dev/null +++ b/erpnext/patches/v11_0/set_missing_gst_hsn_code.py @@ -0,0 +1,43 @@ +import frappe +from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_html + +def execute(): + company = frappe.db.sql_list("select name from tabCompany where country = 'India'") + if not company: + return + + doctypes = ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", + "Supplier Quotation", "Purchase Order", "Purchase Receipt", "Purchase Invoice"] + + for dt in doctypes: + date_field = "posting_date" + if dt in ["Quotation", "Sales Order", "Supplier Quotation", "Purchase Order"]: + date_field = "transaction_date" + + transactions = frappe.db.sql(""" + select dt.name, dt_item.name as child_name + from `tab{dt}` dt, `tab{dt} Item` dt_item + where dt.name = dt_item.parent + and dt.`{date_field}` > '2018-06-01' + and dt.docstatus = 1 + and ifnull(dt_item.gst_hsn_code, '') = '' + and ifnull(dt_item.item_code, '') != '' + and dt.company in ({company}) + """.format(dt=dt, date_field=date_field, company=", ".join(['%s']*len(company))), tuple(company), as_dict=1) + + if not transactions: + continue + + transaction_rows_name = [d.child_name for d in transactions] + + frappe.db.sql(""" + update `tab{dt} Item` dt_item + set dt_item.gst_hsn_code = (select gst_hsn_code from tabItem where name=dt_item.item_code) + where dt_item.name in ({rows_name}) + """.format(dt=dt, rows_name=", ".join(['%s']*len(transaction_rows_name))), tuple(transaction_rows_name)) + + for t in transactions: + print(t.name) + trans_doc = frappe.get_doc(dt, t.name) + hsnwise_tax = get_itemised_tax_breakup_html(trans_doc) + frappe.db.set_value(dt, t.name, "other_charges_calculation", hsnwise_tax, update_modified=False) \ No newline at end of file diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index c754121299..d282f5c3fd 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -85,7 +85,7 @@ def add_print_formats(): def make_custom_fields(update=True): hsn_sac_field = dict(fieldname='gst_hsn_code', label='HSN/SAC', - fieldtype='Data', options='item_code.gst_hsn_code', insert_after='description', + fieldtype='Data', fetch_from='item_code.gst_hsn_code', insert_after='description', allow_on_submit=1, print_hide=1) invoice_gst_fields = [ dict(fieldname='gst_section', label='GST Details', fieldtype='Section Break', From 917dda1b76aef30461b6bf0247c4c5a9681f81b3 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 14 Nov 2018 01:18:41 +0530 Subject: [PATCH 4/5] [Fix] On save Allow Transfer for Manufacture reset to default value even if user has changed manually --- erpnext/manufacturing/doctype/bom/bom.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 0eab9826c3..d72f00af8f 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -108,7 +108,8 @@ class BOM(WebsiteGenerator): "item_code": item.item_code, "item_name": item.item_name, "bom_no": item.bom_no, - "stock_qty": item.stock_qty + "stock_qty": item.stock_qty, + "allow_transfer_for_manufacture": item.allow_transfer_for_manufacture }) for r in ret: if not item.get(r): @@ -127,6 +128,8 @@ class BOM(WebsiteGenerator): self.validate_rm_item(item) args['bom_no'] = args['bom_no'] or item and cstr(item[0]['default_bom']) or '' + args['transfer_for_manufacture'] = (cstr(args.get('allow_transfer_for_manufacture', '')) or + item and item[0].allow_transfer_for_manufacture or 0) args.update(item[0]) rate = self.get_rm_rate(args) @@ -142,7 +145,7 @@ class BOM(WebsiteGenerator): 'qty' : args.get("qty") or args.get("stock_qty") or 1, 'stock_qty' : args.get("qty") or args.get("stock_qty") or 1, 'base_rate' : rate, - 'allow_transfer_for_manufacture': item and args['allow_transfer_for_manufacture'] or 0 + 'allow_transfer_for_manufacture': cint(args['transfer_for_manufacture']) or 0 } return ret_item From fac49983a0b54eb62935d709b77706e45fd0171f Mon Sep 17 00:00:00 2001 From: Frappe Bot Date: Wed, 14 Nov 2018 11:32:34 +0000 Subject: [PATCH 5/5] bumped to version 11.0.3-beta.22 --- erpnext/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index f48b48b6f8..6947c02618 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -12,7 +12,7 @@ app_license = "GNU General Public License (v3)" source_link = "https://github.com/frappe/erpnext" develop_version = '12.x.x-develop' -staging_version = '11.0.3-beta.21' +staging_version = '11.0.3-beta.22' error_report_email = "support@erpnext.com"