using System; using System.Threading; using System.Threading.Tasks; using GB5Shared.DTO.Framework.Login; using GB5Shared.DTO.Promotion; using GB5Shared.Promotion; using Newtonsoft.Json; using WiBLL.WiSubProcess; using WiDAL.CustomCode.Promotion; using WiDAL.DTOs; namespace WiBLL.Promotion { // ============================================================ // WiSubProcessPromotionAdapter — IPromotableEntity for MWISUBPROCESS ("WISUBPROCESS"). No // child rows of its own. Natural key WiSubProcessCode // (UK_MWISUBPROCESS_WISUBPROCESSCODE_TENANTID, confirmed via schema). // // WiSubProcessDTO DOES declare a WiProcessCode property, but WiSubProcessBLL.GetById's own // query (checked directly, not assumed) never selects it — only WIPROCESSNAME is joined in. // Export therefore always resolves WiProcessId -> WiProcessCode via IWiProcessCodeLookupDAL, // never trusting the DTO field to be populated (the same "lite vs full" trap class as // ReportView's own Lite bug, caught here by checking the real query before assuming). // ============================================================ public class WiSubProcessPromotionAdapter : IPromotableEntity { public string EntityTypeCode => "WISUBPROCESS"; private readonly IWiSubProcessBLL _WiSubProcessBLL; private readonly IWiProcessCodeLookupDAL _WiProcessCodeLookup; private readonly IWiSubProcessCodeLookupDAL _WiSubProcessCodeLookup; public WiSubProcessPromotionAdapter( IWiSubProcessBLL wiSubProcessBLL, IWiProcessCodeLookupDAL wiProcessCodeLookup, IWiSubProcessCodeLookupDAL wiSubProcessCodeLookup) { _WiSubProcessBLL = wiSubProcessBLL; _WiProcessCodeLookup = wiProcessCodeLookup; _WiSubProcessCodeLookup = wiSubProcessCodeLookup; } private class PortablePayload { public string WiSubProcessCode { get; set; } = string.Empty; public string WiSubProcessName { get; set; } = string.Empty; public string WiProcessCode { get; set; } = string.Empty; public short DisplayOrder { get; set; } public string? Description { get; set; } public int IsActive { get; set; } public short SortOrder { get; set; } = 9999; } public async Task ExportForPromotionAsync( int originEntityId, LoginDTO login, CancellationToken ct) { var json = await _WiSubProcessBLL.GetById(originEntityId, login, ct).ConfigureAwait(false); var dto = JsonConvert.DeserializeObject(json) ?? throw new InvalidOperationException($"WiSubProcess {originEntityId} not found."); var wiProcessCode = await _WiProcessCodeLookup.GetWiProcessCodeById(dto.WiProcessId, login, ct) .ConfigureAwait(false) ?? throw new InvalidOperationException( $"WiSubProcess {originEntityId} references WiProcessId {dto.WiProcessId}, which has no WiProcessCode — cannot promote."); var payload = new PortablePayload { WiSubProcessCode = dto.WiSubProcessCode, WiSubProcessName = dto.WiSubProcessName, WiProcessCode = wiProcessCode, DisplayOrder = dto.DisplayOrder, Description = dto.Description, IsActive = dto.IsActive, SortOrder = dto.SortOrder }; return new PromotionBundleDTO { EntityTypeCode = EntityTypeCode, OriginEntityId = originEntityId, PayloadJson = System.Text.Json.JsonSerializer.Serialize(payload) }; } public Task ValidateForPromotionAsync( PromotionBundleDTO bundle, CancellationToken ct) { var payload = System.Text.Json.JsonSerializer.Deserialize(bundle.PayloadJson); if (payload is null) return Task.FromResult(PromotionValidationResultDTO.Block("WiSubProcess payload could not be parsed.")); var reasons = new System.Collections.Generic.List(); if (string.IsNullOrWhiteSpace(payload.WiSubProcessCode)) reasons.Add("WiSubProcess.WiSubProcessCode is required to promote."); if (string.IsNullOrWhiteSpace(payload.WiProcessCode)) reasons.Add("WiSubProcess.WiProcessId has no resolvable WiProcessCode — cannot promote."); var result = reasons.Count == 0 ? PromotionValidationResultDTO.Ok() : new PromotionValidationResultDTO { IsPromotable = false, BlockingReasons = reasons }; return Task.FromResult(result); } public async Task ImportPromotedAsync( PromotionBundleDTO bundle, byte destinationSourceType, int? existingDestinationId, LoginDTO login, CancellationToken ct) { var payload = System.Text.Json.JsonSerializer.Deserialize(bundle.PayloadJson) ?? throw new InvalidOperationException("WiSubProcess promotion payload could not be parsed."); var wiProcessId = await _WiProcessCodeLookup.GetWiProcessIdByCode(payload.WiProcessCode, login, ct) .ConfigureAwait(false) ?? throw new InvalidOperationException( $"No WiProcess with WiProcessCode '{payload.WiProcessCode}' exists at this destination."); var resolvedId = existingDestinationId ?? await _WiSubProcessCodeLookup.GetWiSubProcessIdByCode(payload.WiSubProcessCode, login, ct).ConfigureAwait(false) ?? 0; var dto = new WiSubProcessDTO { WiSubProcessId = resolvedId, WiProcessId = wiProcessId, WiSubProcessCode = payload.WiSubProcessCode, WiSubProcessName = payload.WiSubProcessName, DisplayOrder = payload.DisplayOrder, Description = payload.Description, IsActive = payload.IsActive, SortOrder = payload.SortOrder, SourceType = destinationSourceType }; await _WiSubProcessBLL.Save(dto, login, ct).ConfigureAwait(false); return dto.WiSubProcessId; // mutated in place by Save when isNew } } }