assign-sa-to-group-of-employees
diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.js b/erpnext/hr/doctype/salary_structure/salary_structure.js
index 4a111e7..033938d 100755
--- a/erpnext/hr/doctype/salary_structure/salary_structure.js
+++ b/erpnext/hr/doctype/salary_structure/salary_structure.js
@@ -58,6 +58,9 @@
 				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 @@
 		});
 	},
 
+	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 a36d820..7ead140 100644
--- a/erpnext/hr/doctype/salary_structure/salary_structure.py
+++ b/erpnext/hr/doctype/salary_structure/salary_structure.py
@@ -65,6 +65,76 @@
 		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 1a16db7..1a660d9 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 @@
 		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: