From cd9c836c62a209f22e3d6b234de7f61dd50bca59 Mon Sep 17 00:00:00 2001 From: Mohaimen Hasan Date: Mon, 30 Jun 2025 10:19:54 +0600 Subject: [PATCH] Employee Search (Short Profile, Long Profile, Lifecycle), job card remarks --- HRM.BO/SearchReport/SearchEmployee.cs | 3 +- HRM.DA/DA/SearchReport/SearchEmployeeDA.cs | 168 +++++++++++++++++- .../SearchReport/SearchEmployeeService.cs | 39 +++- .../_services/employee/employee.service.ts | 6 + HRM.UI/ClientApp/src/app/app.api.service.ts | 4 + ...al-edit-for-single-employee.component.html | 3 + ...nual-edit-for-single-employee.component.ts | 4 + ...al-edit-for-multiple-employee.component.ts | 14 +- .../employee-profile.component.html | 2 +- .../employee-payroll-profile.component.html | 2 +- .../life-cycle-entry.component.html | 1 + .../app/picker/employee-picker.component.ts | 43 ++++- .../Employee/EmployeeController.cs | 49 ++++- 13 files changed, 321 insertions(+), 17 deletions(-) diff --git a/HRM.BO/SearchReport/SearchEmployee.cs b/HRM.BO/SearchReport/SearchEmployee.cs index 5fd4ee5..fda87ba 100644 --- a/HRM.BO/SearchReport/SearchEmployee.cs +++ b/HRM.BO/SearchReport/SearchEmployee.cs @@ -370,7 +370,8 @@ namespace HRM.BO List FindCordinator(int? id, int? payrollTypeID); List FindEmpCodeName(int payrolltypeid, string code, string name); List FindEmpCodeNameForEmployeePicker(int userid, int payrolltypeid, string code, string name); - SearchEmployee get(int empid); + List FindEmpCodeNameForEmployeePickerNew(int userid, int payrolltypeid, string code, string name, bool isForLifeCycle); + SearchEmployee get(int empid); List GetEmployeeNotYetUser(int payrollTypeID); List GetTeam(int employeeid); List GetTeam(int employeeid, EnumStatus enumStatus); diff --git a/HRM.DA/DA/SearchReport/SearchEmployeeDA.cs b/HRM.DA/DA/SearchReport/SearchEmployeeDA.cs index 20dc698..5efb28a 100644 --- a/HRM.DA/DA/SearchReport/SearchEmployeeDA.cs +++ b/HRM.DA/DA/SearchReport/SearchEmployeeDA.cs @@ -400,8 +400,170 @@ END;"; return tc.ExecuteReader(finalSQl); } - #endregion - } + internal static IDataReader SearchForEmployeePickerNew(TransactionContext tc, int userID, int payrollTypeID, string code, string name, bool isForLifeCycle) + { + string orderby = "name"; + string sqlClause = ""; + string top = ""; - #endregion + string recurSqlClause = SQLParser.MakeSQL(@" + DECLARE @userid INT = %n; + DECLARE @payrolltypeid INT = %n; + DECLARE @permissionstatus INT = %n; + WITH RecursiveCategory AS + ( + SELECT + CATEGORYID + FROM + dbo.CATEGORY + WHERE + CATEGORYID IN ( + SELECT REFERENCEID + FROM DATAPERMISSION + WHERE USERID = @userid + AND PAYROLLTYPEID = @payrolltypeid + AND PERMISSIONSTATUS = @permissionstatus + AND PERMISSIONTYPE = %n + ) + ), + RecursiveGrade AS + ( + SELECT + GRADEID + FROM + dbo.GRADES + WHERE + GRADEID IN ( + SELECT REFERENCEID + FROM DATAPERMISSION + WHERE USERID = @userid + AND PAYROLLTYPEID = @payrolltypeid + AND PERMISSIONSTATUS = @permissionstatus + AND PERMISSIONTYPE = %n + ) + ), + RecursiveDepartment AS + ( + SELECT + DEPARTMENTID + FROM + dbo.DEPARTMENT + WHERE + DEPARTMENTID IN ( + SELECT REFERENCEID + FROM DATAPERMISSION + WHERE USERID = @userid + AND PAYROLLTYPEID = @payrolltypeid + AND PERMISSIONSTATUS = @permissionstatus + AND PERMISSIONTYPE = %n + ) + UNION ALL + SELECT + d.DEPARTMENTID + FROM + dbo.DEPARTMENT d + INNER JOIN + RecursiveDepartment rd + ON + d.PARENTID = rd.DEPARTMENTID + ), + RecursiveLocation AS + ( + SELECT + LOCATIONID + FROM + dbo.LOCATION + WHERE + LOCATIONID IN ( + SELECT REFERENCEID + FROM DATAPERMISSION + WHERE USERID = @userid + AND PAYROLLTYPEID = @payrolltypeid + AND PERMISSIONSTATUS = @permissionstatus + AND PERMISSIONTYPE = %n + ) + UNION ALL + SELECT + l.LOCATIONID + FROM + dbo.LOCATION l + INNER JOIN + RecursiveLocation rl + ON + l.PARENTID = rl.LOCATIONID + )", userID, payrollTypeID, EnumMenuPermissionStatus.Approved, EnumDataPermissionType.Cagtegory, EnumDataPermissionType.Grade, EnumDataPermissionType.Department, EnumDataPermissionType.Location); + string recurWhereClause = SQLParser.MakeSQL(@" + AND + ( + ( + EXISTS (SELECT 1 FROM RecursiveCategory) + AND CATEGORYID IN (SELECT CATEGORYID FROM RecursiveCategory) + ) + OR + ( + EXISTS (SELECT 1 FROM RecursiveGrade) + AND GRADEID IN (SELECT GRADEID FROM RecursiveGrade) + ) + OR + ( + EXISTS (SELECT 1 FROM RecursiveDepartment) + AND DEPARTMENTID IN (SELECT DEPARTMENTID FROM RecursiveDepartment) + ) + OR + ( + EXISTS (SELECT 1 FROM RecursiveLocation) + AND LOCATIONID IN (SELECT LOCATIONID FROM RecursiveLocation) + ) + )"); + + //Previous Code For only Live Employee + //sqlClause = SQLParser.TagSQL(sqlClause) + SQLParser.MakeSQL("PayrollTypeID =%n AND Status = %n", payrollTypeID, EnumStatus.Active); + + //New Code For live And Waitiong for join Employee + if (isForLifeCycle == true) + { + sqlClause = SQLParser.TagSQL(sqlClause) + SQLParser.MakeSQL("PayrollTypeID =%n ", payrollTypeID); + } + else + { + sqlClause = SQLParser.TagSQL(sqlClause) + SQLParser.MakeSQL("PayrollTypeID =%n AND (Status = %n OR Status = %n)", payrollTypeID, EnumEmployeeStatus.Live, EnumEmployeeStatus.Waitingforjoin); + } + + if (code != string.Empty) + { + //Previous with suggestion + //sqlClause = SQLParser.TagSQL(sqlClause) + SQLParser.MakeSQL("EmployeeNo LIKE %s", ("%" + code + "%")); + //orderby = "EmployeeNo"; + + //Using TOP + //sqlClause = SQLParser.TagSQL(sqlClause) + SQLParser.MakeSQL("EmployeeNo LIKE %s", ( code + "%")); // Using LIKE Operator + sqlClause = SQLParser.TagSQL(sqlClause) + SQLParser.MakeSQL("EmployeeNo = %s", (code)); // Without Using LIKE Operator + orderby = "EmployeeNo"; + top = "TOP 5"; + + //Without suggestion + //sqlClause = SQLParser.TagSQL(sqlClause) + SQLParser.MakeSQL("EmployeeNo = %s", code ); + //orderby = "EmployeeNo"; + } + + if (name != string.Empty) + sqlClause = SQLParser.TagSQL(sqlClause) + SQLParser.MakeSQL("Name LIKE %s", ("%" + name + "%")); + //string finalSQl = SQLParser.MakeSQL( + // "%q Select %q EmployeeID, EmployeeNo, Name, categoryID, GradeID, LocationID, designationid, DepartmentID From Employee %q %q Order by %s", + // recurSqlClause, top, sqlClause, recurWhereClause, orderby); + + + string finalSQl = SQLParser.MakeSQL( + "%q Select %q EmployeeID, EmployeeNo, Name, categoryID, GradeID, LocationID, designationid, DepartmentID From Employee %q %q " + + " UNION Select %q EmployeeID, EmployeeNo, Name, categoryID, GradeID, LocationID, designationid, DepartmentID From Employee %q " + + " Order by %s", + recurSqlClause, top, sqlClause, recurWhereClause, top, sqlClause, orderby); + + return tc.ExecuteReader(finalSQl); + } + + #endregion + } + + #endregion } \ No newline at end of file diff --git a/HRM.DA/Service/SearchReport/SearchEmployeeService.cs b/HRM.DA/Service/SearchReport/SearchEmployeeService.cs index 7af11e0..9449fb0 100644 --- a/HRM.DA/Service/SearchReport/SearchEmployeeService.cs +++ b/HRM.DA/Service/SearchReport/SearchEmployeeService.cs @@ -326,8 +326,45 @@ namespace HRM.DA return searchEmployees; } + public List FindEmpCodeNameForEmployeePickerNew(int userID, int payrollTypeID, string code, string name, bool isForLifeCycle) + { + List searchEmployees = new List(); - public List GetEmployeeNotYetUser(int payrollTypeID) + TransactionContext tc = null; + try + { + tc = TransactionContext.Begin(); + + DataReader dr = new DataReader(SearchEmployeeDA.SearchForEmployeePickerNew(tc, userID, payrollTypeID, code, name, isForLifeCycle)); + searchEmployees = this.CreateObjects(dr); + //while (dr.Read()) + //{ + // SearchEmployee item = new SearchEmployee(); + // item.Name = dr.GetString("name"); + // item.EmployeeNo = dr.GetString("employeeNo"); + + // searchEmployees.Add(item); + //} + dr.Close(); + tc.End(); + } + catch (Exception e) + { + #region Handle Exception + + if (tc != null) + tc.HandleError(); + ExceptionLog.Write(e); + + throw new ServiceException(e.Message, e); + + #endregion + } + + return searchEmployees; + } + + public List GetEmployeeNotYetUser(int payrollTypeID) { List searchEmployees = new List(); diff --git a/HRM.UI/ClientApp/src/app/_services/employee/employee.service.ts b/HRM.UI/ClientApp/src/app/_services/employee/employee.service.ts index 3bf17b3..aed9ed3 100644 --- a/HRM.UI/ClientApp/src/app/_services/employee/employee.service.ts +++ b/HRM.UI/ClientApp/src/app/_services/employee/employee.service.ts @@ -105,6 +105,12 @@ export class EmployeeServices { return this.apiService.httpGet('/Employee/getEmpCodeNameForEmployeePickerInput' + '/' + ncode + '/' + nname); } + getEmpCodeNameForEmployeePickerInputNew(code?: string, name?: string, isForLifeCycle? : boolean) { + let nname = this.apiService.getApiDefaultData(name); + let ncode = this.apiService.getApiDefaultData(code); + let nIsForLifeCycle = this.apiService.getApiDefaultBoolData(isForLifeCycle); + return this.apiService.httpGet('/Employee/getEmpCodeNameForEmployeePickerInputNew' + '/' + ncode + '/' + nname + '/' + nIsForLifeCycle); + } getEmployees() { return this.apiService.httpGet(this.apiService.base_url + 'getemployees'); diff --git a/HRM.UI/ClientApp/src/app/app.api.service.ts b/HRM.UI/ClientApp/src/app/app.api.service.ts index 1448727..ba10d27 100644 --- a/HRM.UI/ClientApp/src/app/app.api.service.ts +++ b/HRM.UI/ClientApp/src/app/app.api.service.ts @@ -168,6 +168,10 @@ export class ApiService { return (str === undefined || str === null || str.trim() === '') ? undefined : str; } + getApiDefaultBoolData(value: boolean) { + return (value === undefined || value === null) ? false : value; + } + getApiDateString(dDate: Date) { if (dDate === undefined || dDate === null) { return dDate; diff --git a/HRM.UI/ClientApp/src/app/attendance/attendance-manual-edit-for-Single-employee/attendance-manual-edit-for-single-employee.component.html b/HRM.UI/ClientApp/src/app/attendance/attendance-manual-edit-for-Single-employee/attendance-manual-edit-for-single-employee.component.html index 7ae0d71..9f1c079 100644 --- a/HRM.UI/ClientApp/src/app/attendance/attendance-manual-edit-for-Single-employee/attendance-manual-edit-for-single-employee.component.html +++ b/HRM.UI/ClientApp/src/app/attendance/attendance-manual-edit-for-Single-employee/attendance-manual-edit-for-single-employee.component.html @@ -282,6 +282,9 @@ set + + + diff --git a/HRM.UI/ClientApp/src/app/attendance/attendance-manual-edit-for-Single-employee/attendance-manual-edit-for-single-employee.component.ts b/HRM.UI/ClientApp/src/app/attendance/attendance-manual-edit-for-Single-employee/attendance-manual-edit-for-single-employee.component.ts index da7ea0d..862d8b6 100644 --- a/HRM.UI/ClientApp/src/app/attendance/attendance-manual-edit-for-Single-employee/attendance-manual-edit-for-single-employee.component.ts +++ b/HRM.UI/ClientApp/src/app/attendance/attendance-manual-edit-for-Single-employee/attendance-manual-edit-for-single-employee.component.ts @@ -39,6 +39,7 @@ export class AttendanceManualEditForSingleEmployeeComponent implements OnInit { selectedOutTime: Date; dailyAttenProcessList: DailyAttnProcess[]; _overTime: number = 0; + _remarks: string = ""; statusList = [ { value: 1, name: 'Present' }, { value: 2, name: 'Absent' }, @@ -223,6 +224,9 @@ export class AttendanceManualEditForSingleEmployeeComponent implements OnInit { } this.dailyAttenProcessList.forEach(x => { + if(this._remarks){ + x.comments = this._remarks; + } x.isManualEntry = true; if (x.wfStatus != EnumWFAttnStatus.None) { this.notificationService.showError(x.attnDate.toDateString() +" is in approval stage, so manual edit is not allowed"); diff --git a/HRM.UI/ClientApp/src/app/attendance/attendance-manual-edit-for-multiple-employee/attendance-manual-edit-for-multiple-employee.component.ts b/HRM.UI/ClientApp/src/app/attendance/attendance-manual-edit-for-multiple-employee/attendance-manual-edit-for-multiple-employee.component.ts index 6ed35fb..c731eb4 100644 --- a/HRM.UI/ClientApp/src/app/attendance/attendance-manual-edit-for-multiple-employee/attendance-manual-edit-for-multiple-employee.component.ts +++ b/HRM.UI/ClientApp/src/app/attendance/attendance-manual-edit-for-multiple-employee/attendance-manual-edit-for-multiple-employee.component.ts @@ -207,7 +207,6 @@ export class AttendanceManualEditForMultipleEmployeeComponent implements OnInit } updateobject(type: number) { - debugger; if (this.selectedRemarks != undefined) { if (this.remarksList.find(y => y.value == this.selectedRemarks).name == "Other") { this.isOtherRemarks = true; @@ -215,6 +214,13 @@ export class AttendanceManualEditForMultipleEmployeeComponent implements OnInit this.isOtherRemarks = false; } } + else if (type == 3){ + let oShift : Shift = this.shifts.find(x => x.id === this.selectedShiftID); + if(oShift && oShift.id > 0){ + this.selectInTime = new Date(oShift.inTime); + this.selectedOutTime = new Date(oShift.outTime); + } + } this.dailyAttenProcessList.forEach(x => { if (type == 1) { x.actualInTime = x.inTime; @@ -366,6 +372,12 @@ export class AttendanceManualEditForMultipleEmployeeComponent implements OnInit } var msg: string = ""; this.dailyAttenProcessList.forEach(x => { + if(this.selectedRemarks){ + let remarks = this.remarksList.find(y => y.value == this.selectedRemarks); + if(remarks){ + x.comments = remarks.name; + } + } x.isManualEntry = true; if (x.wfStatus != EnumWFAttnStatus.None) { msg = msg + "Employee: " + x.employee.employeeNo + ". Datais in approval stage, so manual edit is not allowed; "; diff --git a/HRM.UI/ClientApp/src/app/core-hr/employee-profile/employee-profile.component.html b/HRM.UI/ClientApp/src/app/core-hr/employee-profile/employee-profile.component.html index ed3b9cd..bc59449 100644 --- a/HRM.UI/ClientApp/src/app/core-hr/employee-profile/employee-profile.component.html +++ b/HRM.UI/ClientApp/src/app/core-hr/employee-profile/employee-profile.component.html @@ -11,7 +11,7 @@
-
diff --git a/HRM.UI/ClientApp/src/app/payroll/career-and-profile/employee-payroll-profile/employee-payroll-profile.component.html b/HRM.UI/ClientApp/src/app/payroll/career-and-profile/employee-payroll-profile/employee-payroll-profile.component.html index b22ccb2..efa0338 100644 --- a/HRM.UI/ClientApp/src/app/payroll/career-and-profile/employee-payroll-profile/employee-payroll-profile.component.html +++ b/HRM.UI/ClientApp/src/app/payroll/career-and-profile/employee-payroll-profile/employee-payroll-profile.component.html @@ -15,7 +15,7 @@
diff --git a/HRM.UI/ClientApp/src/app/payroll/career-and-profile/life-cycle-entry/life-cycle-entry.component.html b/HRM.UI/ClientApp/src/app/payroll/career-and-profile/life-cycle-entry/life-cycle-entry.component.html index ecf9880..535e63e 100644 --- a/HRM.UI/ClientApp/src/app/payroll/career-and-profile/life-cycle-entry/life-cycle-entry.component.html +++ b/HRM.UI/ClientApp/src/app/payroll/career-and-profile/life-cycle-entry/life-cycle-entry.component.html @@ -15,6 +15,7 @@ (ItemSelected)="GetSelectedEmployee($event)" [setSelectedEmp]="_pickerSelecteEmp" [isActive]="empPickerActive" + [ForLifeCycleSearch]="true" > diff --git a/HRM.UI/ClientApp/src/app/picker/employee-picker.component.ts b/HRM.UI/ClientApp/src/app/picker/employee-picker.component.ts index 44ea4b5..d99d255 100644 --- a/HRM.UI/ClientApp/src/app/picker/employee-picker.component.ts +++ b/HRM.UI/ClientApp/src/app/picker/employee-picker.component.ts @@ -102,10 +102,10 @@ export class EmployeePickerComponent implements OnInit { isSpanArrowDown: boolean = true; isSpanArrowUp: boolean = false; gridHeight: number = 305; + @Input() public payrollTypeID: number; @Input() public isRecruitment: number; @Input() public set isClear(value) { - debugger; if (value) { this.selectedItems = []; // this.count=0; @@ -187,6 +187,11 @@ export class EmployeePickerComponent implements OnInit { /** role-permission-entry ctor */ @Input() fixedGrades: number[] = undefined; + private _isForLifeCycle = false; + @Input() + public set ForLifeCycleSearch(value: boolean) { + this._isForLifeCycle = value ?? false; + } public pageSize = 25; public skip = 0; @@ -454,10 +459,10 @@ export class EmployeePickerComponent implements OnInit { } else { name = value; } - this.loadingEmployee = true; - // this.empSrvc.getEmpCodeName(code, name) - this.empSrvc.getEmpCodeNameForEmployeePickerInput(code, name) - .subscribe( + debugger; + if(this._isForLifeCycle === false){ + this.loadingEmployee = true; + this.empSrvc.getEmpCodeNameForEmployeePickerInput(code, name).subscribe( (resp: any) => { this.searchEmployees = resp; }, @@ -470,14 +475,36 @@ export class EmployeePickerComponent implements OnInit { this.loadingEmployee = false; this.empCodeNameListSource = []; this.searchEmployees.forEach(x => { - this.empCodeNameListSource.push(x.employeeNo + ' ' + x.name); }); this.empCodeNameList = this.empCodeNameListSource.filter((s) => - s.toLowerCase().indexOf(value.toLowerCase()) !== -1); - + s.toLowerCase().indexOf(value.toLowerCase()) !== -1); } ); + } + else{ + this.loadingEmployee = true; + this.empSrvc.getEmpCodeNameForEmployeePickerInputNew(code, name, this._isForLifeCycle).subscribe( + (resp: any) => { + this.searchEmployees = resp; + }, + (err: any) => { + // ON ERROR + this.loadingEmployee = false; + }, + () => { + // ON Success + this.loadingEmployee = false; + this.empCodeNameListSource = []; + this.searchEmployees.forEach(x => { + this.empCodeNameListSource.push(x.employeeNo + ' ' + x.name); + }); + this.empCodeNameList = this.empCodeNameListSource.filter((s) => + s.toLowerCase().indexOf(value.toLowerCase()) !== -1); + } + ); + } + /*} else { this.empCodeNameList = this.empCodeNameListSource.filter((s) => diff --git a/HRM.UI/Controllers/Employee/EmployeeController.cs b/HRM.UI/Controllers/Employee/EmployeeController.cs index 235bc5f..b18f097 100644 --- a/HRM.UI/Controllers/Employee/EmployeeController.cs +++ b/HRM.UI/Controllers/Employee/EmployeeController.cs @@ -822,7 +822,54 @@ namespace HRM.UI.Controllers return Ok(olist); } - [HttpGet("getEmployeeAttachments/{empId}")] + [HttpGet("getEmpCodeNameForEmployeePickerInputNew/{code}/{name}/{isForLifeCycle}")] + public ActionResult getEmpCodeNameForEmployeePickerInputNew(string code, string name, bool isForLifeCycle) + { + code = GlobalFunctions.GetApiDefaultData(code); + name = GlobalFunctions.GetApiDefaultData(name); + + CurrentUser currentUser = CurrentUser.GetCurrentUser(HttpContext.User); + List olist = new List(); + try + { + //olist = _serachManager.FindEmpCodeName((int) currentUser.PayrollTypeID, code, name); + if (code != "") + { + List unorderedList = _serachManager.FindEmpCodeNameForEmployeePickerNew((int)currentUser.UserID, (int)currentUser.PayrollTypeID, code, name, isForLifeCycle); + + olist = unorderedList + .OrderBy(item => item.EmployeeNo != code) // False (0) for priority value, True (1) for others + .ThenBy(item => item.EmployeeNo).ToList(); + } + else + { + olist = _serachManager.FindEmpCodeNameForEmployeePickerNew((int)currentUser.UserID, (int)currentUser.PayrollTypeID, code, name, isForLifeCycle); + } + + //List grades = new GradeService().Get(EnumStatus.Regardless, (int)currentUser.PayrollTypeID); + //List designations = new DesignationService().Get(EnumStatus.Regardless, (int)currentUser.PayrollTypeID); + //List departments = new DepartmentService().Get(EnumStatus.Regardless, (int)currentUser.PayrollTypeID); + //olist.ForEach(x=> + //{ + // var grd = grades.FirstOrDefault(d => d.ID == x.GradeID); + // if(grd != null) x.gradeName = grd.Name; + + // var designation = designations.FirstOrDefault(d => d.ID == x.designationID); + // if (designation != null) x.designationName = designation.Name; + // var department = departments.FirstOrDefault(d => d.ID == x.DepartmentID); + // if (department != null) x.departmentName = department.Name; + //}); // Secondary ordering (alphabetical) + + } + catch (Exception ex) + { + return StatusCode(StatusCodes.Status500InternalServerError, ex.Message); + } + + return Ok(olist); + } + + [HttpGet("getEmployeeAttachments/{empId}")] public ActionResult GetEmployeeAttachments(int empId) { List items = new List();