using GB5Shared.DTO.Framework.AutoNumber; using GB5Shared.DTO.Framework.Criteria; using GB5Shared.DTO.Framework.Login; using GB5Shared.GenerateAutoNumber; using GB5Shared.QueryExecutor; using PayRollDAL.CustomeCode.Job; using PayRollDAL.DTO.Job; using System; using System.Collections.Generic; using System.Threading.Tasks; namespace PayRollBLL.Job { public class JobBLL : IJobBLL { private readonly IJobDAL _JobDAL; private readonly AutoNumber _AutoNumber; private readonly VoucherNumberService _VoucherNumberService; private readonly IQueryExecutor _QueryExecutor; public JobBLL( IJobDAL jobDAL, AutoNumber autoNumber, VoucherNumberService voucherNumberService, IQueryExecutor queryExecutor) { _JobDAL = jobDAL; _AutoNumber = autoNumber; _VoucherNumberService = voucherNumberService; _QueryExecutor = queryExecutor; } public async Task GetSelectListJob(int FirstNumber, int MaxResult, CriteriaDTO CriteriaDTO, LoginDTO LoginDTO, bool IsCount = false) { try { return await _JobDAL.GetSelectListJob(FirstNumber, MaxResult, CriteriaDTO, LoginDTO, IsCount).ConfigureAwait(false); } catch (Exception) { throw; } } // ── Get single record ───────────────────────────────────────────── public async Task GetJob(int JobId, LoginDTO LoginDTO) { return await _JobDAL.GetJob(JobId, LoginDTO).ConfigureAwait(false); } // ── Save (insert or update) ─────────────────────────────────────── // // Business rules ported from gb4 JobBLL.SaveJob (see task spec): // 1. RequiredFromDate <= RequiredToDate // 2. Caller (LoginDTO.UserId) must resolve to an MEMPLOYEE row — used // only for the "must be an employee" check + own ReportingToEmployeeId // enrichment. gb5's ADMINREPORTINGTOID is an FK to MUSER (not // MEMPLOYEE), so no employee-to-employee admin chain is walked here. // 3. JobDate must fall inside the MPERIOD [FromDate,ToDate] window // (mirrors TimeSlipBLL's "RULE 2: DATE IN FINANCIAL PERIOD"). // 4. AutoNumber ("JOB") + VNO (VoucherNumberService) on insert only, // inside a single transaction, with rollback of both the counter // and the row on failure — mirrors RelievingBLL.SaveRelieving. public async Task SaveJob(JobDTO JobDTO, LoginDTO LoginDTO) { if (JobDTO == null) throw new ArgumentNullException(nameof(JobDTO)); // RULE 1: RequiredFromDate <= RequiredToDate if (JobDTO.RequiredFromDate != default && JobDTO.RequiredToDate != default && JobDTO.RequiredFromDate > JobDTO.RequiredToDate) throw new InvalidOperationException("Required From Date is greater than To Date."); bool isNew = JobDTO.JobId == 0; AutoNumberDTO? autoNumberDTO = null; var trans = await _QueryExecutor.BeginTransactionAsync(LoginDTO).ConfigureAwait(false); try { // RULE 2: caller must be an employee; enrich ReportingToEmployeeId var employee = await _JobDAL.GetEmployeeForJobAsync(LoginDTO.UserId, LoginDTO, trans).ConfigureAwait(false); if (employee == null) throw new InvalidOperationException("User is not an employee. Record cannot be saved."); if (JobDTO.ReportingToEmployeeId <= 0) JobDTO.ReportingToEmployeeId = employee.ReportingToEmployeeId; // RULE 3: Job Date must be within the financial period var period = await _JobDAL.GetPeriodAsync(JobDTO.PeriodId, LoginDTO, trans).ConfigureAwait(false); if (period == null || JobDTO.JobDate < period.PeriodFromDate || JobDTO.JobDate > period.PeriodToDate) throw new InvalidOperationException("Job Date is Not in Financial Range"); // RULE 4: AutoNumber + VNO on insert if (isNew) { autoNumberDTO = await _AutoNumber.GetNumberAsync(1, "JOB", LoginDTO) .ConfigureAwait(false); JobDTO.JobId = autoNumberDTO.StartNumber; JobDTO.JobCreatedById = LoginDTO.UserId; JobDTO.JobCreatedOn = DateTime.UtcNow; string? generatedNumber = await _VoucherNumberService .GetNextVoucherNumberAsync( JobDTO.BIZTransactionTypeId, JobDTO.JobDate, LoginDTO, transaction: trans) .ConfigureAwait(false); if (generatedNumber != null) JobDTO.JobNumber = generatedNumber; } JobDTO.JobModifiedById = LoginDTO.UserId; JobDTO.JobModifiedOn = DateTime.UtcNow; int rows = isNew ? await _JobDAL.SaveJob(JobDTO, LoginDTO, trans).ConfigureAwait(false) : await _JobDAL.UpdateJob(JobDTO, LoginDTO, trans).ConfigureAwait(false); await _QueryExecutor.CommitAsync(trans).ConfigureAwait(false); if (rows <= 0) return isNew ? "Failed to save Job." : "Failed to update Job."; return isNew ? $"Job Saved Successfully with Number: {JobDTO.JobNumber}" : $"Job Updated Successfully with Number: {JobDTO.JobNumber}"; } catch (Exception) { await _QueryExecutor.RollbackAsync(trans).ConfigureAwait(false); if (isNew && autoNumberDTO != null) await _AutoNumber.RollbackAutoNumber("JOB", autoNumberDTO.StartNumber, LoginDTO) .ConfigureAwait(false); throw; } } // ── Delete ──────────────────────────────────────────────────────── public async Task DeleteJob(int JobId, LoginDTO LoginDTO) { return await _JobDAL.DeleteJob(JobId, LoginDTO).ConfigureAwait(false); } // ── List ────────────────────────────────────────────────────────── public async Task GetJobList(CriteriaDTO CriteriaDTO, LoginDTO LoginDTO) { try { return await _JobDAL.GetJobList(CriteriaDTO, LoginDTO).ConfigureAwait(false); } catch (Exception) { throw; } } } }