using AccountsDAL.CustomCode.Account; using AccountsDAL.CustomCode.PartyOU; using AccountsDAL.DTO.Account; using AccountsDAL.DTO.CostCenterPattern; using AccountsDAL.DTO.PartyOU; using AccountsDAL.Query.PartyOU; using Dapr.Client; using GB5Shared.DaprCache; using GB5Shared.DTO.Framework.Criteria; using GB5Shared.DTO.Framework.HybridCache; using GB5Shared.DTO.Framework.Login; using GB5Shared.DTO.Qualifier; using GB5Shared.EntityHandler; using GB5Shared.EventLogPublish; using GB5Shared.GB5Library.Qualifier; using GB5Shared.GenerateAutoNumber; using GB5Shared.PubSub.OutBox; using GB5Shared.QueryExecutor; using GB5Shared.Resource.GB5Solution; using GB5Shared.Resource.Response; using GB5Shared.WorkFlow.WorkFlowEngine; using Microsoft.Extensions.Logging; using System.Security.AccessControl; using System.Security.Principal; using static GB5Shared.GB5Constant.Constant; namespace AccountsBLL.Account { public class AccountBLL : IAccountBLL { private readonly IAccountDAL _AccountDAL; private readonly AutoNumber _AutoNumber; private readonly IWorkFlowEngine _WorkFlowEngine; private readonly IQueryExecutor _QueryExecutor; private readonly IOutBox _OutBox; private readonly KeyInvalidate _KeyInvalidate; private readonly DaprClient _DaprClient; private readonly BaseEntityAppService _BaseEntityAppService; private readonly CodeGeneration _codeGeneration; private readonly IPartyOUDAL _PartyOUDAL; private readonly Microsoft.Extensions.Logging.ILogger _Logger; public AccountBLL( IAccountDAL accountDAL, AutoNumber autoNumber, IWorkFlowEngine workFlowEngine, IQueryExecutor queryExecutor, CodeGeneration codeGeneration, IOutBox outBox, KeyInvalidate keyInvalidate, BaseEntityAppService baseEntityAppService, DaprClient daprClient, IPartyOUDAL partyOUDAL, Microsoft.Extensions.Logging.ILogger logger) { _AccountDAL = accountDAL; _AutoNumber = autoNumber; _WorkFlowEngine = workFlowEngine; _QueryExecutor = queryExecutor; _codeGeneration = codeGeneration; _OutBox = outBox; _KeyInvalidate = keyInvalidate; _BaseEntityAppService = baseEntityAppService; _DaprClient = daprClient; _PartyOUDAL = partyOUDAL; _Logger = logger; } // ── Read methods ────────────────────────────────────────────────────── public async Task GetAccount(int AccountId, LoginDTO LoginDTO) { try { return await _AccountDAL.GetAccount(AccountId, LoginDTO).ConfigureAwait(false); } catch (Exception) { throw; } } public async Task GetSelectListAccount( int FirstNumber, int MaxResult, CriteriaDTO CriteriaDTO, LoginDTO LoginDTO) { try { return await _AccountDAL.GetSelectListAccount(FirstNumber, MaxResult, CriteriaDTO, LoginDTO) .ConfigureAwait(false); } catch (Exception) { throw; } } public async Task GetAccountList(CriteriaDTO CriteriaDTO, LoginDTO LoginDTO) { try { return await _AccountDAL.GetAccountList(CriteriaDTO, LoginDTO) .ConfigureAwait(false); } catch (Exception) { throw; } } public async Task GetAccountParameter(int AccountId, LoginDTO LoginDTO) { try { return await _AccountDAL.GetAccountParameter(AccountId, LoginDTO).ConfigureAwait(false); } catch (Exception) { throw; } } public async Task GetAccountDetailsSQL(int accountId, LoginDTO loginDTO) { try { return await _AccountDAL.GetAccountDetailsSQL(accountId, loginDTO).ConfigureAwait(false); } catch (Exception) { throw; } } public async Task GetAccountPicklistForAccountPopup(CriteriaDTO criteriaDTO, LoginDTO loginDTO) { try { return await _AccountDAL.GetAccountPicklistForAccountPopup(criteriaDTO, loginDTO) .ConfigureAwait(false); } catch (Exception) { throw; } } public async Task GetSelectListAccountVsCompany( int firstNumber, int maxResult, CriteriaDTO criteriaDTO, LoginDTO loginDTO) { try { return await _AccountDAL.GetSelectListAccountVsCompany(firstNumber, maxResult, criteriaDTO, loginDTO) .ConfigureAwait(false); } catch (Exception) { throw; } } public async Task GetPopUpDetails(int accountId, LoginDTO loginDTO) { try { return await _AccountDAL.GetPopUpDetails(accountId, loginDTO).ConfigureAwait(false); } catch (Exception) { throw; } } // ── Save (insert or update) ─────────────────────────────────────────── public async Task SaveAccount(AccountDTO dto, LoginDTO loginDTO) { if (dto == null) throw new ArgumentNullException(nameof(dto)); if (string.IsNullOrWhiteSpace(dto.AccountCode)) throw new ArgumentException("Account code is required."); if (string.IsNullOrWhiteSpace(dto.AccountName)) throw new ArgumentException("Account name is required."); if (dto.AccountGroupId == 0) throw new ArgumentException("Account group is required."); var Trans = await _QueryExecutor.BeginTransactionAsync(loginDTO); // PartyOUDAL _PartyOUDAL = new PartyOUDAL(); PartyOUDTO partoudto = new PartyOUDTO(); try { bool isNew = dto.AccountId == 0; dto.BaseTenantId = loginDTO.ClientId; if (isNew) { var auto = await _AutoNumber.GetNumberAsync(1, AUTONUMBERCONSTANT.ACCOUNT, loginDTO) .ConfigureAwait(false); dto.AccountId = auto.StartNumber; // Auto-generated codes are assigned once, at creation, never regenerated on // update — AssignCode has no isNew-awareness itself (it just re-runs the Auto // GENERATIONTYPE path every call), so it must only be invoked here. Matches the // same fix already applied to PartyBLL.SaveParty for the identical bug. dto = await _codeGeneration.AssignCode("ACCOUNT", dto, loginDTO); var partouAuto = await _AutoNumber.GetNumberAsync(1, AUTONUMBERCONSTANT.PARTYOU, loginDTO) .ConfigureAwait(false); partoudto.PartyOUId = partouAuto.StartNumber; partoudto.AccountId = dto.AccountId; partoudto.AgentId = -1; partoudto.PartyOUType = 1; partoudto.InchargeId = -1; partoudto.OUId = loginDTO.WorkOUId; partoudto.PartyBranchId = -1; partoudto.PartyId = -1; partoudto.PartyOUCreditAllowed = dto.AccountIsCreditAllowed; partoudto.PartyOUDebitAllowed = dto.AccountIsDebitAllowed; partoudto.PartyOUCreditLimit = dto.AccountCreditLimit; partoudto.PartyOUDebitLimit = dto.AccountDebitLimit; partoudto.PartyOUIsPurchaseApplicable = 1; partoudto.PartyOUIsSalesApplicable = 1; partoudto.PartyOUSequenceNumber = "1"; partoudto.PartyOUStatus = 1; partoudto.PurchasePaymentTermId = -1; partoudto.PurchasePriceListId = -1; partoudto.PurchaseTermsSetId = -1; partoudto.RouteId = -1; //PartyDTO.PartyBranchDTO.PartyBranchRouteId; partoudto.SalesInchargeId = -1; partoudto.SalesPaymentTermId = -1; partoudto.SalesPriceListId = -1; partoudto.SalesTermsSetId = -1; partoudto.TaxTypeId = -1; partoudto.TransporterId = -1; partoudto.PartyOURouteIds = ""; partoudto.MRPPartyTypeId = -1; partoudto.PartyOUSortOrder = 9999; partoudto.PartyOUVersion = 1; partoudto.PartyOUStatus = 1; partoudto.PartyOUSourceType = loginDTO.SourceType; partoudto.PartyOUCreatedById = loginDTO.UserId; partoudto.PartyOUModifiedById = loginDTO.UserId; partoudto.PartyOUCreatedOn = DateTime.Now; partoudto.PartyOUModifiedOn = DateTime.Now; partoudto.PartyOUNumberOfBills = 0; } else { var partyOu = await _PartyOUDAL.GetPartyAccountOU( dto.AccountId, loginDTO.WorkOUId, -1, -1, loginDTO, Trans); if (partyOu != null) { partoudto.PartyOUId = partyOu.PartyOUId; partoudto.PartyOUVersion = Convert.ToInt16(partyOu.PartyOUVersion+1); partoudto.AccountId = dto.AccountId; partoudto.AgentId = -1; partoudto.PartyOUType = 1; partoudto.InchargeId = -1; partoudto.OUId = loginDTO.WorkOUId; partoudto.PartyBranchId = -1; partoudto.PartyId = -1; partoudto.PartyOUCreditAllowed = dto.AccountIsCreditAllowed; partoudto.PartyOUDebitAllowed = dto.AccountIsDebitAllowed; partoudto.PartyOUCreditLimit = dto.AccountCreditLimit; partoudto.PartyOUDebitLimit = dto.AccountDebitLimit; partoudto.PartyOUIsPurchaseApplicable = 1; partoudto.PartyOUIsSalesApplicable = 1; partoudto.PartyOUSequenceNumber = "1"; partoudto.PartyOUStatus = 1; partoudto.PurchasePaymentTermId = -1; partoudto.PurchasePriceListId = -1; partoudto.PurchaseTermsSetId = -1; partoudto.RouteId = -1; //PartyDTO.PartyBranchDTO.PartyBranchRouteId; partoudto.SalesInchargeId = -1; partoudto.SalesPaymentTermId = -1; partoudto.SalesPriceListId = -1; partoudto.SalesTermsSetId = -1; partoudto.TaxTypeId = -1; partoudto.TransporterId = -1; partoudto.PartyOURouteIds = ""; partoudto.MRPPartyTypeId = -1; partoudto.PartyOUSortOrder = 9999; partoudto.PartyOUStatus = 1; partoudto.PartyOUSourceType = loginDTO.SourceType; partoudto.PartyOUCreatedById = loginDTO.UserId; partoudto.PartyOUModifiedById = loginDTO.UserId; partoudto.PartyOUCreatedOn = DateTime.Now; partoudto.PartyOUModifiedOn = DateTime.Now; partoudto.PartyOUNumberOfBills = 0; } else { var partouAuto = await _AutoNumber.GetNumberAsync(1, AUTONUMBERCONSTANT.PARTYOU, loginDTO) .ConfigureAwait(false); partoudto.PartyOUId = partouAuto.StartNumber; partoudto.AccountId = dto.AccountId; partoudto.AgentId = -1; partoudto.PartyOUType = 1; partoudto.InchargeId = -1; partoudto.OUId = loginDTO.WorkOUId; partoudto.PartyBranchId = -1; partoudto.PartyId = -1; partoudto.PartyOUCreditAllowed = dto.AccountIsCreditAllowed; partoudto.PartyOUDebitAllowed = dto.AccountIsDebitAllowed; partoudto.PartyOUCreditLimit = dto.AccountCreditLimit; partoudto.PartyOUDebitLimit = dto.AccountDebitLimit; partoudto.PartyOUIsPurchaseApplicable = 1; partoudto.PartyOUIsSalesApplicable = 1; partoudto.PartyOUSequenceNumber = "1"; partoudto.PartyOUStatus = 1; partoudto.PurchasePaymentTermId = -1; partoudto.PurchasePriceListId = -1; partoudto.PurchaseTermsSetId = -1; partoudto.RouteId = -1; //PartyDTO.PartyBranchDTO.PartyBranchRouteId; partoudto.SalesInchargeId = -1; partoudto.SalesPaymentTermId = -1; partoudto.SalesPriceListId = -1; partoudto.SalesTermsSetId = -1; partoudto.TaxTypeId = -1; partoudto.TransporterId = -1; partoudto.PartyOURouteIds = ""; partoudto.MRPPartyTypeId = -1; partoudto.PartyOUSortOrder = 9999; partoudto.PartyOUVersion = 1; partoudto.PartyOUStatus = 1; partoudto.PartyOUSourceType = loginDTO.SourceType; partoudto.PartyOUCreatedById = loginDTO.UserId; partoudto.PartyOUModifiedById = loginDTO.UserId; partoudto.PartyOUCreatedOn = DateTime.Now; partoudto.PartyOUModifiedOn = DateTime.Now; partoudto.PartyOUNumberOfBills = 0; } } // var definitions = new List(); //var executors = new List(); //var engine = new QualifierEngine((IQualifierDefinitionLoader)definitions, executors); //var facade = new QualifierFacade(engine); //var baseSvc = new BaseEntityAppService( //_WorkFlowEngine, _OutBox, _QueryExecutor, _DaprClient); await _BaseEntityAppService.ExecuteSaveAsync( EntityConstant.OBJECTACCOUNT, EventTypeConstant.SAVEACCOUNTEVENTTYPEID, dto, loginDTO, async tx => { if (isNew) { await _AccountDAL.SaveAccount(dto, loginDTO, tx).ConfigureAwait(false); await _PartyOUDAL.SavePartyOU(partoudto, loginDTO, tx).ConfigureAwait(false); } else { await _AccountDAL.UpdateAccount(dto, loginDTO, tx).ConfigureAwait(false); await _PartyOUDAL.UpdatePartyOU(partoudto, loginDTO, tx).ConfigureAwait(false); } return dto.AccountId; }, null, -1, -1, Trans); await _QueryExecutor.CommitAsync(Trans); return isNew ? SuccessResponse.SaveSuccess : SuccessResponse.UpdateSuccess; } catch (Exception) { await _QueryExecutor.RollbackAsync(Trans); throw; } } public async Task GetControlAccountPicklist(CriteriaDTO criteriaDTO, LoginDTO loginDTO) { try { return await _AccountDAL.GetControlAccountPicklist(criteriaDTO, loginDTO); } catch (Exception) { throw; } } public async Task GetSelectListGeneralLedger(CriteriaDTO criteriaDTO, LoginDTO loginDTO) { try { return await _AccountDAL.GetSelectListGeneralLedger(criteriaDTO, loginDTO); } catch (Exception) { throw; } } public async Task DeleteAccount(int accountId, LoginDTO loginDTO) { if (await _AccountDAL.AccountExistsInVouchers(accountId, loginDTO).ConfigureAwait(false)) throw new InvalidOperationException("Account is referenced in vouchers and cannot be deleted."); if (await _AccountDAL.AccountLinkedToParty(accountId, loginDTO).ConfigureAwait(false)) throw new InvalidOperationException( "Account is linked to a Party (as its control or linked account) and cannot be deleted."); var Trans = await _QueryExecutor.BeginTransactionAsync(loginDTO); try { await _BaseEntityAppService.ExecuteSaveAsync( EntityConstant.OBJECTACCOUNT, EventTypeConstant.DELETEACCOUNTEVENTTYPEID, new AccountDTO { AccountId = accountId }, loginDTO, async tx => { await _AccountDAL.DeleteAccount(accountId, loginDTO, tx).ConfigureAwait(false); return accountId; }, null, -1, -1, Trans); await _QueryExecutor.CommitAsync(Trans); // Best-effort cache invalidation — the delete already committed above, so a Dapr // statestore hiccup here (e.g. sidecar not running) must not surface as a failed // delete. KeyInvalidate.AllInvalidateCache rethrows on any Dapr error (unlike // BaseEndPoint's own cache read/write, which log-and-continue), so guard it here. try { var cacheKey = CacheKeyGenerator.KeyGeneration( accountId, EntityConstant.OBJECTACCOUNT, CacheKeyLevel.CLIENT_LEVEL, string.Empty, loginDTO); await _KeyInvalidate.AllInvalidateCache(cacheKey); } catch (Exception cacheEx) { _Logger.LogWarning(cacheEx, "Cache invalidation failed after successful delete of Account {AccountId}", accountId); } return SuccessResponse.DeleteSuccessMessage; } catch (Exception) { await _QueryExecutor.RollbackAsync(Trans); throw; } } } }