From f4ca60c02c5ac82324bc787d72631e594c1d5fde Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 17:31:38 +0000 Subject: [PATCH 1/2] Initial plan From e8c879bf49cb629cd98640aa83a7e46b26ee7cf4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 17:39:41 +0000 Subject: [PATCH 2/2] Refactor: Extract LoadProductsWithFees helper to reduce GetContract complexity below 50 lines Co-authored-by: StuartFerguson <16325469+StuartFerguson@users.noreply.github.com> --- .../ReportingManager.cs | 67 ++++++++++--------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/EstateReportingAPI.BusinessLogic/ReportingManager.cs b/EstateReportingAPI.BusinessLogic/ReportingManager.cs index 0f9f12a..0b634ca 100644 --- a/EstateReportingAPI.BusinessLogic/ReportingManager.cs +++ b/EstateReportingAPI.BusinessLogic/ReportingManager.cs @@ -301,7 +301,7 @@ public async Task> GetContract(ContractQueries.GetContractQuery using ResolvedDbContext? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString()); await using EstateManagementContext context = resolvedContext.Context; - // Step 1: load contracts with operator name via a left-join (translatable) + // Step 1: load contract with operator name via a left-join (translatable) var baseContractQuery = (from c in context.Contracts join o in context.Operators on c.OperatorId equals o.OperatorId into ops from o in ops.DefaultIfEmpty() @@ -322,9 +322,27 @@ from o in ops.DefaultIfEmpty() var baseContract = baseContractQueryResult.Data; + // Steps 2 & 3: load products and fees, then assemble + var productsResult = await this.LoadProductsWithFees(context, baseContract.ContractId, cancellationToken); - // Step 2: load related products for all contracts in one query - var productsQuery = context.ContractProducts.Where(cp => cp.ContractId == baseContract.ContractId).Select(cp => new { + if (productsResult.IsFailed) + return ResultHelpers.CreateFailure(productsResult); + + return Result.Success(new Contract { + ContractId = baseContract.ContractId, + ContractReportingId = baseContract.ContractReportingId, + Description = baseContract.Description, + EstateId = baseContract.EstateId, + OperatorName = baseContract.OperatorName, + OperatorId = baseContract.OperatorId, + Products = productsResult.Data + }); + } + + private async Task>> LoadProductsWithFees(EstateManagementContext context, + Guid contractId, + CancellationToken cancellationToken) { + var productsQuery = context.ContractProducts.Where(cp => cp.ContractId == contractId).Select(cp => new { cp.ContractProductId, cp.ContractId, cp.DisplayText, @@ -341,7 +359,6 @@ from o in ops.DefaultIfEmpty() var products = productsQueryResult.Data; var productIds = products.Select(p => p.ContractProductId).ToList(); - // Step 3: load fees for those products in one query var feesQuery = context.ContractProductTransactionFees.Where(tf => productIds.Contains(tf.ContractProductId)).Select(tf => new { tf.CalculationType, tf.ContractProductTransactionFeeId, @@ -357,33 +374,23 @@ from o in ops.DefaultIfEmpty() return ResultHelpers.CreateFailure(feesQueryResult); var fees = feesQueryResult.Data; - - // Assemble the model in memory - Contract result = new Contract { - ContractId = baseContract.ContractId, - ContractReportingId = baseContract.ContractReportingId, - Description = baseContract.Description, - EstateId = baseContract.EstateId, - OperatorName = baseContract.OperatorName, - OperatorId = baseContract.OperatorId, - Products = products.Where(p => p.ContractId == baseContract.ContractId).Select(p => new Models.ContractProduct { - ContractId = p.ContractId, - ProductId = p.ContractProductId, - DisplayText = p.DisplayText, - ProductName = p.ProductName, - ProductType = p.ProductType, - Value = p.Value, - TransactionFees = fees.Where(f => f.ContractProductId == p.ContractProductId).Select(f => new ContractProductTransactionFee { - Description = f.Description, - Value = f.Value, - CalculationType = f.CalculationType, - FeeType = f.FeeType, - TransactionFeeId = f.ContractProductTransactionFeeId - }).ToList() + var feesLookup = fees.ToLookup(f => f.ContractProductId); + + return Result.Success(products.Select(p => new Models.ContractProduct { + ContractId = p.ContractId, + ProductId = p.ContractProductId, + DisplayText = p.DisplayText, + ProductName = p.ProductName, + ProductType = p.ProductType, + Value = p.Value, + TransactionFees = feesLookup[p.ContractProductId].Select(f => new ContractProductTransactionFee { + Description = f.Description, + Value = f.Value, + CalculationType = f.CalculationType, + FeeType = f.FeeType, + TransactionFeeId = f.ContractProductTransactionFeeId }).ToList() - }; - - return Result.Success(result); + }).ToList()); } public async Task>> GetRecentContracts(ContractQueries.GetRecentContractsQuery request,