using GB5Shared.DaprCache; using GB5Shared.DTO.Framework.Login; using GB5Shared.EntityHandler; using GB5Shared.GenerateAutoNumber; using GB5Shared.QueryExecutor; using GB5Shared.Resource.Response; using GB5Shared.Telemetry; using Microsoft.Extensions.Logging; using MMDAL.CustomCode.Indent; using MMDAL.DTO.Indent; using Newtonsoft.Json; using static GB5Shared.GB5Constant.Constant; namespace MMBLL.ProcessGroupTransaction { public class ProcessGroupTransactionBLL : IProcessGroupTransactionBLL { private readonly IProcessGroupTransactionDAL _dal; private readonly AutoNumber _autoNumber; private readonly IQueryExecutor _queryExecutor; private readonly KeyInvalidate _keyInvalidate; private readonly BaseEntityAppService _baseEntityAppService; private readonly ILogger _logger; public ProcessGroupTransactionBLL( IProcessGroupTransactionDAL dal, AutoNumber autoNumber, IQueryExecutor queryExecutor, KeyInvalidate keyInvalidate, BaseEntityAppService baseEntityAppService, ILogger logger) { _dal = dal; _autoNumber = autoNumber; _queryExecutor = queryExecutor; _keyInvalidate = keyInvalidate; _baseEntityAppService = baseEntityAppService; _logger = logger; } // ── Get single CWO ──────────────────────────────────────────────────── public async Task GetCWO(int processGroupId, LoginDTO login, CancellationToken ct) { var header = await _dal.GetCWO(processGroupId, login, ct).ConfigureAwait(false); if (header == null) return JsonConvert.SerializeObject(null); header.Details = (await _dal.GetCWODetails(processGroupId, login, ct).ConfigureAwait(false)).ToList(); return JsonConvert.SerializeObject(header); } // ── Get paged list ──────────────────────────────────────────────────── public async Task GetCWOList(int ouId, int firstNumber, int maxResult, LoginDTO login, CancellationToken ct) { var (items, total) = await _dal.GetCWOList(ouId, firstNumber, maxResult, login, ct).ConfigureAwait(false); return JsonConvert.SerializeObject(new { Items = items, TotalCount = total }); } // ── Save (create + update) ──────────────────────────────────────────── public async Task SaveCWO(ProcessGroupTransactionSaveDTO dto, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("validate-cwo", new { dto.ProcessGroupId }); if (dto.BizTransactionTypeId == 0) throw new ArgumentException("BizTransactionTypeId is required."); if (dto.OUId == 0) throw new ArgumentException("OUId is required."); if (dto.PeriodId == 0) throw new ArgumentException("PeriodId is required."); if (dto.Details.Count == 0) throw new ArgumentException("At least one CWO detail is required."); bool isNew = dto.ProcessGroupId == 0; int detailCount = dto.Details.Count; dto.CreatedById = login.UserId; dto.CreatedOn = DateTime.UtcNow; if (isNew) { var headerNum = await _autoNumber.GetNumberAsync(1, AUTONUMBERCONSTANT.PROCESSGROUP, login).ConfigureAwait(false); dto.ProcessGroupId = headerNum.StartNumber; } var detailNum = await _autoNumber.GetNumberAsync(detailCount, AUTONUMBERCONSTANT.PROCESSGROUP, login).ConfigureAwait(false); int nextDetailId = detailNum.StartNumber; for (short i = 0; i < dto.Details.Count; i++) { var d = dto.Details[i]; d.ProcessGroupDetailId = nextDetailId++; d.ProcessGroupId = dto.ProcessGroupId; d.SlNo = (short)(i + 1); } GB5Trace.Step("save-cwo", new { dto.ProcessGroupId, isNew }); var trans = await _queryExecutor.BeginTransactionAsync(login); try { if (isNew) await _dal.SaveCWOHeader(dto, login, trans, ct).ConfigureAwait(false); else { dto.ModifiedById = login.UserId; dto.ModifiedOn = DateTime.UtcNow; await _dal.UpdateCWOHeader(dto, login, trans, ct).ConfigureAwait(false); await _dal.DeleteCWODetails(dto.ProcessGroupId, login, trans, ct).ConfigureAwait(false); } await _dal.BulkSaveCWODetails(dto.Details, login.ClientId, login, trans, ct).ConfigureAwait(false); await _queryExecutor.CommitAsync(trans).ConfigureAwait(false); } catch { await _queryExecutor.RollbackAsync(trans).ConfigureAwait(false); throw; } GB5Trace.Step("event-publish", new { WebServiceConstant.MMSPROCESSGROUPSAVE }); await _baseEntityAppService.ExecuteSaveAsync( EntityConstant.OBJECTPROCESSGROUP, WebServiceConstant.MMSPROCESSGROUPSAVE, dto, login, _ => Task.FromResult(dto.ProcessGroupId), new Dictionary { { "ProcessGroupId", dto.ProcessGroupId } }); var keyGen = new CacheKeyGeneration(); var cacheKey = keyGen.KeyGeneration(dto.ProcessGroupId, EntityConstant.OBJECTPROCESSGROUP, CacheKeyLevel.NOT_REQUIRED, login); await _keyInvalidate.AllInvalidateCache(cacheKey).ConfigureAwait(false); return isNew ? $"{SuccessResponse.SaveSuccessMessage} {dto.ProcessGroupId}" : SuccessResponse.UpdateSuccessMessage; } catch (Exception ex) { GB5Trace.MarkFailed("save-cwo-failed", ex); _logger.LogError(ex, "SaveCWO failed for ProcessGroupId {ProcessGroupId}", dto.ProcessGroupId); throw; } } // ── Delete ──────────────────────────────────────────────────────────── public async Task DeleteCWO(int processGroupId, LoginDTO login, CancellationToken ct) { try { GB5Trace.Step("delete-cwo", new { processGroupId }); var trans = await _queryExecutor.BeginTransactionAsync(login); try { await _dal.DeleteCWODetails(processGroupId, login, trans, ct).ConfigureAwait(false); await _dal.SoftDeleteCWO(processGroupId, login, trans, ct).ConfigureAwait(false); await _queryExecutor.CommitAsync(trans).ConfigureAwait(false); } catch { await _queryExecutor.RollbackAsync(trans).ConfigureAwait(false); throw; } GB5Trace.Step("event-publish", new { WebServiceConstant.MMSPROCESSGROUPDELETE }); await _baseEntityAppService.ExecuteSaveAsync( EntityConstant.OBJECTPROCESSGROUP, WebServiceConstant.MMSPROCESSGROUPDELETE, new ProcessGroupTransactionSaveDTO { ProcessGroupId = processGroupId }, login, _ => Task.FromResult(processGroupId), new Dictionary { { "ProcessGroupId", processGroupId } }) .ConfigureAwait(false); var keyGen = new CacheKeyGeneration(); var cacheKey = keyGen.KeyGeneration(processGroupId, EntityConstant.OBJECTPROCESSGROUP, CacheKeyLevel.NOT_REQUIRED, login); await _keyInvalidate.AllInvalidateCache(cacheKey).ConfigureAwait(false); return SuccessResponse.DeleteSuccessMessage; } catch (Exception ex) { GB5Trace.MarkFailed("delete-cwo-failed", ex); _logger.LogError(ex, "DeleteCWO failed for ProcessGroupId {ProcessGroupId}", processGroupId); throw; } } } }