Merge pull request 'dev_chapal' (#60) from dev_chapal into devqc

Reviewed-on: http://103.197.204.162:3025/CelHRTeam/EchoTex_Payroll/pulls/60
This commit is contained in:
chapal 2026-11-15 11:59:55 +06:00
commit f5e93a006d
5 changed files with 96 additions and 21 deletions

View File

@ -112,6 +112,14 @@ namespace HRM.DA
return rawData; return rawData;
} }
/// <summary>Card tagged with each employee: EmployeeID, CardID, CardNumber.</summary>
internal static IDataReader GetEmployeeCards(TransactionContext tc, string empIDs)
{
return tc.ExecuteReader("SELECT e.EmployeeID, e.CardID, ac.CardNumber FROM Employee e" +
" LEFT JOIN AccessCard ac ON ac.AccessCardID = e.CardID" +
" WHERE e.EmployeeID IN (%q)", empIDs);
}
internal static void Delete(TransactionContext tc, DateTime fromTime, string empIDs) internal static void Delete(TransactionContext tc, DateTime fromTime, string empIDs)
{ {
tc.ExecuteNonQuery("DELETE FROM AttnRawData WHERE PunchTime>=%D and EmployeeID in (%q)", fromTime, empIDs); tc.ExecuteNonQuery("DELETE FROM AttnRawData WHERE PunchTime>=%D and EmployeeID in (%q)", fromTime, empIDs);

View File

@ -1,5 +1,6 @@
using HRM.BO; using DocumentFormat.OpenXml.Drawing.Diagrams;
using Ease.Core.DataAccess; using Ease.Core.DataAccess;
using HRM.BO;
using System; using System;
using System.Data; using System.Data;
@ -221,9 +222,19 @@ namespace HRM.DA
tc.ExecuteNonQuery( tc.ExecuteNonQuery(
"DELETE FROM SalaryMonthlyDetail WHERE SalaryMonthlyID in( Select SalaryMonthlyID from SalaryMonthly Where SalaryProcessID=%n)", "DELETE FROM SalaryMonthlyDetail WHERE SalaryMonthlyID in( Select SalaryMonthlyID from SalaryMonthly Where SalaryProcessID=%n)",
id); id);
tc.ExecuteNonQuery(@"ALTER TABLE OPIPayment NOCHECK CONSTRAINT FK_OPIPayment_SALARYMONTHLY;
ALTER TABLE SALARYMONTHLYDETAIL NOCHECK CONSTRAINT FK_SLRMTHDTL_SLRMTH;
ALTER TABLE SALARYTEMPDETAIL NOCHECK CONSTRAINT FK_SLRTMPDTL_SLRMTH; ");
tc.ExecuteNonQuery("DELETE FROM SalaryMonthly WHERE SalaryProcessID=%n", id); tc.ExecuteNonQuery("DELETE FROM SalaryMonthly WHERE SalaryProcessID=%n", id);
//}
//tc.ExecuteNonQuery("DELETE FROM SalaryProcess WHERE SalaryProcessID=%n", id); tc.ExecuteNonQuery(@" ALTER TABLE OPIPayment WITH CHECK CHECK CONSTRAINT FK_OPIPayment_SALARYMONTHLY;
ALTER TABLE SALARYMONTHLYDETAIL WITH CHECK CHECK CONSTRAINT FK_SLRMTHDTL_SLRMTH;
ALTER TABLE SALARYTEMPDETAIL WITH CHECK CHECK CONSTRAINT FK_SLRTMPDTL_SLRMTH;");
tc.ExecuteNonQuery("DELETE FROM SalaryProcess WHERE SalaryProcessID=%n", id);
} }
internal static void UndoMonthlySalaryByIDs(TransactionContext tc, string ids) internal static void UndoMonthlySalaryByIDs(TransactionContext tc, string ids)

View File

@ -2656,7 +2656,6 @@ namespace HRM.DA
{ {
continue; continue;
} }
// 2. If End of Contract date is less than or equal Attendendence date then no attendence // 2. If End of Contract date is less than or equal Attendendence date then no attendence
if (emp.EndOfContractDate != null && emp.EndOfContractDate.Value <= Attdate) if (emp.EndOfContractDate != null && emp.EndOfContractDate.Value <= Attdate)
{ {

View File

@ -392,6 +392,35 @@ namespace HRM.DA
string empIDs = attnRawDatas.Aggregate(new StringBuilder(), string empIDs = attnRawDatas.Aggregate(new StringBuilder(),
(sb, rData) => sb.AppendFormat("{0},", rData.EmployeeID), sb => sb.ToString().Trim(',')); (sb, rData) => sb.AppendFormat("{0},", rData.EmployeeID), sb => sb.ToString().Trim(','));
AttnRawDataDA.Delete(tc, dMinValue, empIDs); AttnRawDataDA.Delete(tc, dMinValue, empIDs);
// Fill CardID (and CardNo when not supplied) from the card tagged with the employee
var empCards = new Dictionary<int, Tuple<int?, string>>();
string distinctEmpIDs = string.Join(",", attnRawDatas.Where(x => x.EmployeeID > 0)
.Select(x => x.EmployeeID).Distinct());
if (distinctEmpIDs.Length > 0)
{
DataReader dr = new DataReader(AttnRawDataDA.GetEmployeeCards(tc, distinctEmpIDs));
while (dr.Read())
{
empCards[dr.GetInt32("EmployeeID", 0)] =
Tuple.Create(dr.GetInt32("CardID"), dr.GetString("CardNumber"));
}
dr.Close();
}
foreach (AttnRawData item in attnRawDatas)
{
Tuple<int?, string> card;
if (empCards.TryGetValue(item.EmployeeID, out card))
{
if (item.CardID <= 0 && card.Item1.HasValue && card.Item1.Value > 0)
item.CardID = card.Item1.Value;
if (string.IsNullOrWhiteSpace(item.CardNo) && !string.IsNullOrWhiteSpace(card.Item2))
item.CardNo = card.Item2.Trim();
}
if (string.IsNullOrWhiteSpace(item.CardNo))
item.CardNo = item.EmployeeNo ?? string.Empty; // employee has no card tagged
}
DataTable _dtRawData = null; DataTable _dtRawData = null;
_dtRawData = new DataTable("AttnRawData"); _dtRawData = new DataTable("AttnRawData");
// _dtRawData.Columns.Add("ATTNRAWDATAID", typeof(int)); // _dtRawData.Columns.Add("ATTNRAWDATAID", typeof(int));
@ -418,7 +447,7 @@ namespace HRM.DA
// item.DeviceNo, // item.DeviceNo,
// item.PunchTime); // item.PunchTime);
_dtRawData.Rows.Add( _dtRawData.Rows.Add(
null, item.CardID > 0 ? (object)item.CardID : DBNull.Value,
item.EmployeeID, item.EmployeeID,
item.CardNo.Trim(), item.CardNo.Trim(),
item.PunchTime, item.PunchTime,

View File

@ -37,7 +37,7 @@ export class ReadAttendanceRawdataComponent {
public employees: Employee[] = []; public employees: Employee[] = [];
selectedCard: any; selectedCard: any;
entryModes: any = EnumExtension.getNamesAndValues(EnumEntryMode); entryModes: any = EnumExtension.getNamesAndValues(EnumEntryMode);
format: string = ".csv (Column Names: EmpNo,PunchTime,CardNo)"; // (Date Time Format: MM/dd/yyyy h:mm) format: string = ".csv (Column Names: EmpNo,PunchTime[,CardNo]) PunchTime: M/d/yyyy H:mm"; // CardNo is optional
public gridView: GridDataResult; public gridView: GridDataResult;
public gridView2: GridDataResult; public gridView2: GridDataResult;
public mandatoryItems: mandatoryData[] = []; public mandatoryItems: mandatoryData[] = [];
@ -149,22 +149,23 @@ export class ReadAttendanceRawdataComponent {
}); });
//console.log(text); //console.log(text);
var lines = text.split("\n"); var lines = text.split(/\r?\n/); // handle both Windows (\r\n) and Unix (\n) line endings
//console.log(lines); //console.log(lines);
this.uploadedLines = []; this.uploadedLines = [];
const cell = (v: string) => (v === undefined || v === null) ? '' : v.trim().replace(/^"(.*)"$/, '$1').trim();
try { try {
for (var i = 1; i < lines.length; i++) { for (var i = 1; i < lines.length; i++) {
this.uploadeddata = new csvFileData(); this.uploadeddata = new csvFileData();
if (lines[i] !== "") { if (lines[i].trim() !== "") {
//console.log(lines[i]); //console.log(lines[i]);
var temp = lines[i].split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/); //spliting comma separated values but ignoring commas in double quoted string var temp = lines[i].split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/); //spliting comma separated values but ignoring commas in double quoted string
this.uploadeddata.rowNo = i.toString(); this.uploadeddata.rowNo = i.toString();
this.uploadeddata.employeeNo = temp[0].replace(/^"(.*)"$/, '$1'); this.uploadeddata.employeeNo = cell(temp[0]);
this.uploadeddata.punchTime = temp[1].replace(/^"(.*)"$/, '$1'); this.uploadeddata.punchTime = cell(temp[1]);
this.uploadeddata.cardNo = temp[2].replace(/^"(.*)"$/, '$1'); this.uploadeddata.cardNo = cell(temp[2]); // CardNo column is optional
//this.uploadeddata.deviceNo = temp[3].replace(/^"(.*)"$/, '$1'); //this.uploadeddata.deviceNo = temp[3].replace(/^"(.*)"$/, '$1');
//this.uploadeddata.deviceIPAddress = temp[4].replace(/^"(.*)"$/, '$1'); //this.uploadeddata.deviceIPAddress = temp[4].replace(/^"(.*)"$/, '$1');
//this.uploadeddata.entryMode = temp[5].replace(/^"(.*)"$/, '$1'); //this.uploadeddata.entryMode = temp[5].replace(/^"(.*)"$/, '$1');
@ -185,7 +186,8 @@ export class ReadAttendanceRawdataComponent {
//console.log(this.uploadeddatas); //console.log(this.uploadeddatas);
} }
catch (e) { catch (e) {
//this.no.showError(e); this.uploadeddatas = [];
this._notificationService.showError("Failed to read CSV file: " + (e && e.message ? e.message : e));
} }
// //console.log(this.iJobs); // //console.log(this.iJobs);
@ -207,16 +209,27 @@ export class ReadAttendanceRawdataComponent {
} }
public upload() { public upload() {
if (this.uploadeddata == undefined || this.uploadeddatas.length <= 0) { if (this.uploadeddatas == undefined || this.uploadeddatas.length <= 0) {
this._notificationService.showError("Data not defined"); this._notificationService.showError("Data not defined");
return; return;
} }
if (this.mandatoryItems !== undefined && this.mandatoryItems.length > 0) { if (this.employees == undefined || this.employees.length <= 0) {
this._notificationService.showError("Employee list is not loaded yet. Please wait a moment and try again.");
return;
}
// re-validate here: the employee list may still have been loading when the file was selected
this.mandatoryDataChecker(this.uploadeddatas);
this.errorChecker(this.uploadeddatas);
this.skip2 = 0;
this.loadGrid2();
if (this.mandatoryItems.some(x => x.Error !== "Employee Not Found")) {
this._notificationService.showError("Employee No and PunchTime cannot be empty"); this._notificationService.showError("Employee No and PunchTime cannot be empty");
return; return;
} }
if (this.faultyItems !== undefined && this.faultyItems.length > 0) { if (this.faultyItems !== undefined && this.faultyItems.length > 0) {
this._notificationService.showError("Employee No not Found"); const notFound = Array.from(new Set(this.faultyItems.map(x => x.employeeNo))).join(', ');
this._notificationService.showError("Employee No not found: " + notFound);
return; return;
} }
debugger; debugger;
@ -246,10 +259,13 @@ export class ReadAttendanceRawdataComponent {
item.punchTime = new Date; // otherwise error item.punchTime = new Date; // otherwise error
item.punchTimeDateString = x.punchTime; item.punchTimeDateString = x.punchTime;
//console.log(item.punchTime); //console.log(item.punchTime);
item.employeeID = this.employees.find(e => e.employeeNo.trim().toLowerCase() == x.employeeNo.trim().toLowerCase()) == undefined ? 0 : this.employees.find(e => e.employeeNo.trim().toLowerCase() == x.employeeNo.trim().toLowerCase()).id; const matchedEmp = this.findEmployee(x.employeeNo);
item.employeeNo = x.employeeNo, item.employeeID = matchedEmp == undefined ? 0 : matchedEmp.id;
item.employeeNo = matchedEmp == undefined ? x.employeeNo : matchedEmp.employeeNo.trim(),
item.entryMode = (x.entryMode == undefined || x.entryMode.length <= 0) ? 0 : this.entryModes.find(e => e.name.trim().toLowerCase() == x.entryMode.trim().toLowerCase()).value, item.entryMode = (x.entryMode == undefined || x.entryMode.length <= 0) ? 0 : this.entryModes.find(e => e.name.trim().toLowerCase() == x.entryMode.trim().toLowerCase()).value,
item.cardNo = (x.cardNo == undefined || x.cardNo == "" || x.cardNo == null) ? item.employeeNo : x.cardNo, item.cardID = (matchedEmp != undefined && matchedEmp.cardID != undefined && matchedEmp.cardID != null) ? matchedEmp.cardID : 0;
// empty CardNo -> server fills it from the employee's tagged AccessCard (falls back to EmpNo)
item.cardNo = (x.cardNo == undefined || x.cardNo == null) ? "" : x.cardNo.trim(),
item.deviceNo = (x.deviceNo !== undefined || x.deviceNo !== "" || x.deviceNo !== null) ? "" : item.deviceNo, item.deviceNo = (x.deviceNo !== undefined || x.deviceNo !== "" || x.deviceNo !== null) ? "" : item.deviceNo,
item.deviceIPAddress = (x.deviceIPAddress !== undefined || x.deviceIPAddress !== "" || x.deviceIPAddress !== null) ? "" : item.deviceIPAddress item.deviceIPAddress = (x.deviceIPAddress !== undefined || x.deviceIPAddress !== "" || x.deviceIPAddress !== null) ? "" : item.deviceIPAddress
item.isManualEntry = true; item.isManualEntry = true;
@ -423,8 +439,8 @@ export class ReadAttendanceRawdataComponent {
// this.faultyItems.push(item); // this.faultyItems.push(item);
//} //}
var employeeId = 0; var employeeId = 0;
if (this.employees != undefined) { if (this.employees != undefined && this.employees.length > 0) {
var employee = this.employees.find(x => x.employeeNo.trim() === item.employeeNo.trim()); var employee = this.findEmployee(item.employeeNo);
if (employee == undefined) { if (employee == undefined) {
var mandatoryItem = new mandatoryData(); var mandatoryItem = new mandatoryData();
mandatoryItem.ColumnName = "EmployeeNo"; mandatoryItem.ColumnName = "EmployeeNo";
@ -440,6 +456,18 @@ export class ReadAttendanceRawdataComponent {
} }
// Matches an uploaded EmpNo to an employee: trimmed, case-insensitive, and
// tolerant of leading zeros (e.g. "859" matches "00859").
private findEmployee(empNo: string): Employee {
if (empNo == undefined || this.employees == undefined) return undefined;
const norm = (v: string) => (v == undefined || v == null) ? '' : String(v).trim().toLowerCase();
const stripZeros = (v: string) => v.replace(/^0+(?=.)/, '');
const target = norm(empNo);
if (target === '') return undefined;
return this.employees.find(e => norm(e.employeeNo) === target)
|| this.employees.find(e => norm(e.employeeNo) !== '' && stripZeros(norm(e.employeeNo)) === stripZeros(target));
}
public pageChange(event: PageChangeEvent): void { public pageChange(event: PageChangeEvent): void {
this.skip = event.skip; this.skip = event.skip;
this.gridView = { this.gridView = {