using GB5Shared.DaprCache; using GB5Shared.DTO.Framework.Login; using GB5Shared.EntityHandler; using GB5Shared.GB5Library.Qualifier; using GB5Shared.GenerateAutoNumber; using GB5Shared.QueryExecutor; using GB5Shared.Resource.Response; using MMDAL.CustomCode.MasterSchedule; using MMDAL.DTO.MasterSchedule; using static GB5Shared.GB5Constant.Constant; namespace MMBLL.MasterSchedule { public class MasterScheduleBLL : IMasterScheduleBLL { private readonly IMasterScheduleDAL _MasterScheduleDAL; private readonly AutoNumber _AutoNumber; private readonly IQueryExecutor _QueryExecutor; private readonly KeyInvalidate _KeyInvalidate; private readonly BaseEntityAppService _BaseEntityAppService; public MasterScheduleBLL( IMasterScheduleDAL masterScheduleDAL, AutoNumber autoNumber, IQueryExecutor queryExecutor, KeyInvalidate keyInvalidate, BaseEntityAppService baseEntityAppService) { _MasterScheduleDAL = masterScheduleDAL; _AutoNumber = autoNumber; _QueryExecutor = queryExecutor; _KeyInvalidate = keyInvalidate; _BaseEntityAppService = baseEntityAppService; } // ── Get single record ───────────────────────────────────────────── public async Task GetMasterSchedule( int masterScheduleId, LoginDTO loginDTO) { return await _MasterScheduleDAL .GetMasterSchedule(masterScheduleId, loginDTO) .ConfigureAwait(false); } // ── Save (insert or update) ─────────────────────────────────────── public async Task SaveMasterSchedule(MasterScheduleDTO dto, LoginDTO loginDTO) { if (dto is null) throw new ArgumentNullException(nameof(dto)); if (string.IsNullOrWhiteSpace(dto.MasterScheduleCode)) throw new ArgumentException("Master Schedule Code is required."); if (string.IsNullOrWhiteSpace(dto.MasterScheduleName)) throw new ArgumentException("Master Schedule Name is required."); var Trans = await _QueryExecutor.BeginTransactionAsync(loginDTO); try { bool isNew = dto.MasterScheduleId == 0; if (isNew) { int detailCount = dto.MasterScheduleDetailArray.Count; var auto = await _AutoNumber.GetNumberAsync( 1 + detailCount, AUTONUMBERCONSTANT.MASTERSCHEDULE, loginDTO); dto.MasterScheduleId = auto.StartNumber; int offset = 1; foreach (var detail in dto.MasterScheduleDetailArray) { detail.MasterScheduleDetailId = auto.StartNumber + offset; detail.MasterScheduleId = dto.MasterScheduleId; offset++; } } await _BaseEntityAppService.ExecuteSaveAsync( EntityConstant.OBJECTMASTERSCHEDULE, EventTypeConstant.SAVEMASTERSCHEDULEEVENTTYPEID, dto, loginDTO, async tx => { var result = isNew ? await _MasterScheduleDAL.SaveMasterSchedule(dto, loginDTO, tx) : await _MasterScheduleDAL.UpdateMasterSchedule(dto, loginDTO, tx); return dto.MasterScheduleId; }, null, -1, -1, Trans); await _QueryExecutor.CommitAsync(Trans); //var cacheKey = KeyGenerator.KeyGeneration( // dto.MasterScheduleId, // EntityConstant.OBJECTMASTERSCHEDULE, // CacheKeyLevel.CLIENT_LEVEL, // loginDTO); //await _KeyInvalidate.AllInvalidateCache(cacheKey); return isNew ? SuccessResponse.SaveSuccess : SuccessResponse.UpdateSuccess; } catch (Exception) { await _QueryExecutor.RollbackAsync(Trans); throw; } } // ── Delete ──────────────────────────────────────────────────────── public async Task DeleteMasterSchedule(int masterScheduleId, LoginDTO loginDTO) { var Trans = await _QueryExecutor.BeginTransactionAsync(loginDTO); try { await _BaseEntityAppService.ExecuteSaveAsync( EntityConstant.OBJECTMASTERSCHEDULE, EventTypeConstant.DELETEMASTERSCHEDULEEVENTTYPEID, new MasterScheduleDTO { MasterScheduleId = masterScheduleId }, loginDTO, async tx => { await _MasterScheduleDAL.DeleteMasterSchedule(masterScheduleId, loginDTO, tx); return masterScheduleId; }, null, -1, -1, Trans); await _QueryExecutor.CommitAsync(Trans); //var cacheKey = KeyGenerator.KeyGeneration( // masterScheduleId, // EntityConstant.OBJECTMASTERSCHEDULE, // CacheKeyLevel.CLIENT_LEVEL, // loginDTO); //await _KeyInvalidate.AllInvalidateCache(cacheKey); return SuccessResponse.DeleteSuccessMessage; } catch (Exception) { await _QueryExecutor.RollbackAsync(Trans); throw; } } // ── Paged list ──────────────────────────────────────────────────── public async Task> GetMasterScheduleList( int pageOffset, int pageSize, LoginDTO loginDTO) { return await _MasterScheduleDAL .GetMasterScheduleList(pageOffset, pageSize, loginDTO) .ConfigureAwait(false); } // ── Picklist ────────────────────────────────────────────────────── public async Task> GetSelectListMasterSchedule( LoginDTO loginDTO) { return await _MasterScheduleDAL .GetSelectListMasterSchedule(loginDTO) .ConfigureAwait(false); } // ── Load demand from source ─────────────────────────────────────── // sourceType: 0=Forecast, 1=SalesOrder, 2=SalesOrder+Forecast public async Task LoadMasterSchedule( int masterScheduleId, byte sourceType, LoginDTO loginDTO) { if (masterScheduleId <= 0) throw new ArgumentException("MasterScheduleId is required."); if (sourceType > 2) throw new ArgumentException("SourceType must be 0 (Forecast), 1 (SalesOrder) or 2 (Both)."); // Reserve a block of auto-numbers for the detail rows being inserted. // We use 5000 as a conservative ceiling; unused numbers are returned automatically // when the auto-number sequence advances. Adjust if a single load produces > 5000 rows. var auto = await _AutoNumber.GetNumberAsync( 5000, AUTONUMBERCONSTANT.MASTERSCHEDULEDETAIL, loginDTO); var Trans = await _QueryExecutor.BeginTransactionAsync(loginDTO); try { await _MasterScheduleDAL .LoadMasterSchedule(masterScheduleId, sourceType, auto.StartNumber, loginDTO, Trans) .ConfigureAwait(false); await _QueryExecutor.CommitAsync(Trans); //var cacheKey = KeyGenerator.KeyGeneration( // masterScheduleId, // EntityConstant.OBJECTMASTERSCHEDULE, // CacheKeyLevel.CLIENT_LEVEL, // loginDTO); //await _KeyInvalidate.AllInvalidateCache(cacheKey); return SuccessResponse.SaveSuccess; } catch (Exception) { await _QueryExecutor.RollbackAsync(Trans); throw; } } // ── Set demand priority ─────────────────────────────────────────── public async Task SetDemandPriority(SetDemandPriorityDTO dto, LoginDTO loginDTO) { if (dto is null) throw new ArgumentNullException(nameof(dto)); if (dto.Lines is null || dto.Lines.Count == 0) throw new ArgumentException("At least one priority line is required."); foreach (var line in dto.Lines) if (line.Priority < 0) throw new ArgumentException($"Priority must be >= 0 (line {line.MasterScheduleDetailId})."); await _MasterScheduleDAL.SetDemandPriority(dto, loginDTO).ConfigureAwait(false); //var cacheKey = KeyGenerator.KeyGeneration( // dto.MasterScheduleId, // EntityConstant.OBJECTMASTERSCHEDULE, // CacheKeyLevel.CLIENT_LEVEL, // loginDTO); //await _KeyInvalidate.AllInvalidateCache(cacheKey); return SuccessResponse.UpdateSuccess; } // ── Set allocation priority ─────────────────────────────────────── public async Task SetAllocationPriority(SetAllocationPriorityDTO dto, LoginDTO loginDTO) { if (dto is null) throw new ArgumentNullException(nameof(dto)); if (dto.AllocationId <= 0) throw new ArgumentException("AllocationId is required."); if (dto.Priority < 0) throw new ArgumentException("Priority must be >= 0."); await _MasterScheduleDAL.SetAllocationPriority(dto, loginDTO).ConfigureAwait(false); return SuccessResponse.UpdateSuccess; } // ── Clear old schedule ──────────────────────────────────────────── public async Task ClearOldSchedule(ClearScheduleDTO dto, LoginDTO loginDTO) { if (dto is null) throw new ArgumentNullException(nameof(dto)); if (dto.MasterScheduleId >= 0) throw new ArgumentException("MasterScheduleId is required."); await _MasterScheduleDAL.ClearOldSchedule(dto, loginDTO).ConfigureAwait(false); return SuccessResponse.DeleteSuccessMessage; } } }