using GB5Shared.DaprCache; using GB5Shared.DTO.Framework.Criteria; using GB5Shared.DTO.Framework.Login; using GB5Shared.DTO.Qualifier; using GB5Shared.EntityHandler; using GB5Shared.GB5Library.Qualifier; using GB5Shared.GenerateAutoNumber; using GB5Shared.ListQuery; using GB5Shared.QueryExecutor; using MMDAL.CustomCode.SupplyGroup; using MMDAL.DTO.SupplyGroup; using Newtonsoft.Json; using static GB5Shared.GB5Constant.Constant; namespace MMBLL.SupplyGroup { // ───────────────────────────────────────────────────────────────────── // SupplyGroupBLL // // Save pipeline follows the RoleBLL reference pattern: // 1. BeginTransaction // 2. (if new) GetAutoNumber — batch = 1 header + detail count // 3. Build QualifierEngine + QualifierFacade inline // 4. Build BaseEntityAppService inline // 5. ExecuteSaveAsync (runs PreValidate → workflow → PrePersist → DAL → OutBox) // 6. CommitTransaction // 7. KeyInvalidate.AllInvalidateCache after commit // ───────────────────────────────────────────────────────────────────── public class SupplyGroupBLL : ISupplyGroupBLL { private readonly ISupplyGroupDAL _SupplyGroupDAL; private readonly AutoNumber _AutoNumber; private readonly IQueryExecutor _QueryExecutor; private readonly KeyInvalidate _KeyInvalidate; private readonly IListHandler _ListHandler; private readonly BaseEntityAppService _BaseEntityAppService; public SupplyGroupBLL( ISupplyGroupDAL supplyGroupDAL, AutoNumber autoNumber, IQueryExecutor queryExecutor, KeyInvalidate keyInvalidate, IListHandler listHandler, BaseEntityAppService baseEntityAppService) { _SupplyGroupDAL = supplyGroupDAL; _AutoNumber = autoNumber; _QueryExecutor = queryExecutor; _KeyInvalidate = keyInvalidate; _ListHandler = listHandler; _BaseEntityAppService = baseEntityAppService; } // ── Get single record ───────────────────────────────────────────── public async Task GetSupplyGroup(int supplyGroupId, LoginDTO loginDTO) { try { return await _SupplyGroupDAL.GetSupplyGroup(supplyGroupId, loginDTO).ConfigureAwait(false); } catch (Exception) { throw; } } // ── Save (insert or update) ─────────────────────────────────────── public async Task SaveSupplyGroup(SupplyGroupDTO supplyGroupDTO, LoginDTO loginDTO) { if (supplyGroupDTO == null) throw new ArgumentNullException(nameof(supplyGroupDTO)); var Trans = await _QueryExecutor.BeginTransactionAsync(loginDTO); try { bool isNew = supplyGroupDTO.SupplyGroupId == 0; if (isNew) { // Batch auto-number: 1 header + N detail rows (mirrors legacy) int count = 1 + supplyGroupDTO.SupplyGroupDetailArray.Count; var auto = await _AutoNumber.GetNumberAsync(count, AUTONUMBERCONSTANT.SUPPLYGROUP, loginDTO); supplyGroupDTO.SupplyGroupId = auto.StartNumber; // Assign sequential IDs to detail rows from the same batch int offset = 1; foreach (var detail in supplyGroupDTO.SupplyGroupDetailArray) { detail.SupplyGroupDetailId = auto.StartNumber + offset; detail.SupplyGroupId = supplyGroupDTO.SupplyGroupId; offset++; } } await _BaseEntityAppService.ExecuteSaveAsync( EntityConstant.OBJECTSUPPLYGROUP, EventTypeConstant.SAVESUPPLYGROUPEVENTTYPEID, supplyGroupDTO, loginDTO, async tx => { var result = isNew ? await _SupplyGroupDAL.SaveSupplyGroup(supplyGroupDTO, loginDTO, tx) : await _SupplyGroupDAL.UpdateSupplyGroup(supplyGroupDTO, loginDTO, tx); return supplyGroupDTO.SupplyGroupId; }, null, -1, -1, Trans); await _QueryExecutor.CommitAsync(Trans); //// Invalidate read cache after successful write //var cacheKey = KeyGenerator.KeyGeneration( // supplyGroupDTO.SupplyGroupId, // EntityConstant.OBJECTSUPPLYGROUP, // CacheKeyLevel.CLIENT_LEVEL, // loginDTO); //await _KeyInvalidate.AllInvalidateCache(cacheKey); return isNew ? "Details saved successfully." : "Details updated successfully."; } catch (Exception) { await _QueryExecutor.RollbackAsync(Trans); throw; } } // ── Delete ──────────────────────────────────────────────────────── public async Task DeleteSupplyGroup(int supplyGroupId, LoginDTO loginDTO) { var Trans = await _QueryExecutor.BeginTransactionAsync(loginDTO); try { await _BaseEntityAppService.ExecuteSaveAsync( EntityConstant.OBJECTSUPPLYGROUP, EventTypeConstant.DELETESUPPLYGROUPEVENTTYPEID, new SupplyGroupDTO { SupplyGroupId = supplyGroupId }, loginDTO, async tx => { await _SupplyGroupDAL.DeleteSupplyGroup(supplyGroupId, loginDTO, tx); return supplyGroupId; }, null, -1, -1, Trans); // Invalidate read cache //var cacheKey = KeyGenerator.KeyGeneration( // supplyGroupId, // EntityConstant.OBJECTSUPPLYGROUP, // CacheKeyLevel.CLIENT_LEVEL, // loginDTO); //await _KeyInvalidate.AllInvalidateCache(cacheKey); return "Details deleted successfully."; } catch (Exception) { await _QueryExecutor.RollbackAsync(Trans); throw; } } // ── Picklist ────────────────────────────────────────────────────── public async Task GetSelectListSupplyGroup( int firstNumber, int maxResult, CriteriaDTO criteriaDTO, LoginDTO loginDTO) { try { return await _SupplyGroupDAL .GetSelectListSupplyGroup(firstNumber, maxResult, criteriaDTO, loginDTO) .ConfigureAwait(false); } catch (Exception) { throw; } } // ── Paged list (Pattern A) ───────────────────────────────────────── public async Task GetSupplyGroupList( CriteriaDTO criteria, string? searchText, int pageOffset, int pageSize, LoginDTO loginDTO, CancellationToken ct = default) { var merged = CriteriaRouterHelper.WithSearchAndPaging(criteria, searchText, pageOffset, pageSize); var c = CriteriaBinder.Bind(merged); var result = await _ListHandler .HandleAsync(new SupplyGroupListQuery(c), loginDTO, ct) .ConfigureAwait(false); return JsonConvert.SerializeObject(result); } } }