Skip to content

Commit d025801

Browse files
Merge branch 'main' into copilot/refactor-transaction-summary-method
2 parents ecfcb10 + 4aa6a56 commit d025801

1 file changed

Lines changed: 101 additions & 83 deletions

File tree

EstateReportingAPI.BusinessLogic/ReportingManager.cs

Lines changed: 101 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -516,41 +516,31 @@ public async Task<Result<List<Merchant>>> GetRecentMerchants(MerchantQueries.Get
516516
var merchantsQuery = context.Merchants.Select(m => new {
517517
MerchantReportingId = m.MerchantReportingId,
518518
Name = m.Name,
519-
LastSaleDateTime = m.LastSaleDateTime,
520-
LastSale = m.LastSaleDate,
521519
CreatedDateTime = m.CreatedDateTime,
522-
LastStatement = m.LastStatementGenerated,
523520
MerchantId = m.MerchantId,
524521
Reference = m.Reference,
525-
AddressInfo = context.MerchantAddresses.Where(ma => ma.MerchantId == m.MerchantId).OrderByDescending(ma => ma.CreatedDateTime).Select(ma => new {
526-
ma.AddressLine1,
527-
ma.AddressLine2,
528-
ma.Country,
529-
ma.PostalCode,
530-
ma.Region,
531-
ma.Town
532-
// Add more properties as needed
533-
}).FirstOrDefault(), // Get the first matching MerchantAddress or null
534-
ContactInfo = context.MerchantContacts.Where(mc => mc.MerchantId == m.MerchantId).OrderByDescending(mc => mc.CreatedDateTime).Select(mc => new { mc.Name, mc.EmailAddress, mc.PhoneNumber }).FirstOrDefault(), // Get the first matching MerchantContact or null
535-
EstateReportingId = context.Estates.Single(e => e.EstateId == m.EstateId).EstateReportingId
522+
AddressInfo = context.MerchantAddresses.Where(ma => ma.MerchantId == m.MerchantId)
523+
.OrderByDescending(ma => ma.CreatedDateTime)
524+
.Select(ma => new { ma.AddressLine1, ma.AddressLine2, ma.Country, ma.PostalCode, ma.Region, ma.Town })
525+
.FirstOrDefault(),
526+
ContactInfo = context.MerchantContacts.Where(mc => mc.MerchantId == m.MerchantId)
527+
.OrderByDescending(mc => mc.CreatedDateTime)
528+
.Select(mc => new { mc.Name, mc.EmailAddress, mc.PhoneNumber })
529+
.FirstOrDefault()
536530
}).OrderByDescending(m => m.CreatedDateTime).Take(3);
537531

538532
var recentMerchantsResult = await ExecuteQuerySafeToList(merchantsQuery, cancellationToken, "Error retrieving recent merchants");
539533
if (recentMerchantsResult.IsFailed)
540534
return ResultHelpers.CreateFailure(recentMerchantsResult);
541535

542-
var recentMerchants = recentMerchantsResult.Data;
543-
544-
List<Merchant> merchantList = new();
545-
foreach (var merchant in recentMerchants) {
536+
List<Merchant> merchantList = recentMerchantsResult.Data.Select(merchant => {
546537
Merchant model = new() {
547538
MerchantId = merchant.MerchantId,
548539
Name = merchant.Name,
549540
Reference = merchant.Reference,
550541
MerchantReportingId = merchant.MerchantReportingId,
551542
CreatedDateTime = merchant.CreatedDateTime,
552543
};
553-
554544
if (merchant.AddressInfo != null) {
555545
model.AddressLine1 = merchant.AddressInfo.AddressLine1;
556546
model.AddressLine2 = merchant.AddressInfo.AddressLine2;
@@ -559,15 +549,13 @@ public async Task<Result<List<Merchant>>> GetRecentMerchants(MerchantQueries.Get
559549
model.Town = merchant.AddressInfo.Town;
560550
model.Region = merchant.AddressInfo.Region;
561551
}
562-
563552
if (merchant.ContactInfo != null) {
564553
model.ContactName = merchant.ContactInfo.Name;
565554
model.ContactEmail = merchant.ContactInfo.EmailAddress;
566555
model.ContactPhone = merchant.ContactInfo.PhoneNumber;
567556
}
568-
569-
merchantList.Add(model);
570-
}
557+
return model;
558+
}).ToList();
571559

572560
return Result.Success(merchantList);
573561
}
@@ -665,12 +653,26 @@ public async Task<Result<Operator>> GetOperator(OperatorQueries.GetOperatorQuery
665653

666654
public async Task<Result<TransactionDetailReportResponse>> GetTransactionDetailReport(TransactionQueries.TransactionDetailReportQuery request,
667655
CancellationToken cancellationToken) {
668-
669-
TransactionDetailReportResponse response = null;
670656
using ResolvedDbContext<EstateManagementContext>? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString());
671657
await using EstateManagementContext context = resolvedContext.Context;
672658

673-
var query = from t in context.Transactions
659+
var query = ApplyTransactionDetailFilters(BuildTransactionDetailBaseQuery(context, request.Request), request.Request);
660+
var queryResult = await ExecuteQuerySafeToList(query, cancellationToken, "Error retrieving transaction details report");
661+
662+
if (queryResult.IsFailed)
663+
return ResultHelpers.CreateFailure(queryResult);
664+
665+
var queryResults = queryResult.Data;
666+
667+
if (queryResults.Any() == false)
668+
return new TransactionDetailReportResponse { Summary = new TransactionDetailSummary(), Transactions = new List<TransactionDetail>() };
669+
670+
return Result.Success(MapToTransactionDetailResponse(queryResults));
671+
}
672+
673+
private static IQueryable<TransactionDetailQueryResult> BuildTransactionDetailBaseQuery(EstateManagementContext context,
674+
TransactionDetailReportRequest request) {
675+
return from t in context.Transactions
674676
join cp in context.ContractProducts on new { t.ContractProductId, t.ContractId } equals new { cp.ContractProductId, cp.ContractId }
675677
join m in context.Merchants on t.MerchantId equals m.MerchantId
676678
join o in context.Operators on t.OperatorId equals o.OperatorId
@@ -679,10 +681,10 @@ from msf in msfJoin.DefaultIfEmpty()
679681
// left join Settlements (msf may be null)
680682
join s in context.Settlements on msf.SettlementId equals s.SettlementId into sJoin
681683
from s in sJoin.DefaultIfEmpty()
682-
where t.TransactionType != "Logon" && t.TransactionDate >= request.Request.StartDate && t.TransactionDate <= request.Request.EndDate
683-
select new {
684-
t.TransactionId,
685-
t.TransactionDateTime,
684+
where t.TransactionType != "Logon" && t.TransactionDate >= request.StartDate && t.TransactionDate <= request.EndDate
685+
select new TransactionDetailQueryResult {
686+
TransactionId = t.TransactionId,
687+
TransactionDateTime = t.TransactionDateTime,
686688
MerchantId = m.MerchantId,
687689
MerchantReportingId = m.MerchantReportingId,
688690
MerchantName = m.Name,
@@ -698,33 +700,21 @@ from s in sJoin.DefaultIfEmpty()
698700
FeeValue = msf != null ? msf.FeeValue : 0m,
699701
SettlementId = s != null ? s.SettlementId : Guid.Empty,
700702
};
703+
}
701704

702-
// Now apply the filters
703-
if (request.Request.Merchants != null && request.Request.Merchants.Any()) {
704-
query = query.Where(q => request.Request.Merchants.Contains(q.MerchantReportingId));
705-
}
706-
707-
if (request.Request.Products != null && request.Request.Products.Any()) {
708-
query = query.Where(q => request.Request.Products.Contains(q.ContractProductReportingId));
709-
}
710-
711-
if (request.Request.Operators != null && request.Request.Operators.Any()) {
712-
query = query.Where(q => request.Request.Operators.Contains(q.OperatorReportingId));
713-
}
714-
715-
var queryResult = await ExecuteQuerySafeToList(query, cancellationToken, "Error retrieving transaction details report");
716-
717-
if (queryResult.IsFailed)
718-
return ResultHelpers.CreateFailure(queryResult);
719-
720-
// Ok now enumerate the results
721-
var queryResults = queryResult.Data;
722-
723-
if (queryResults.Any() == false)
724-
return new TransactionDetailReportResponse { Summary = new TransactionDetailSummary(), Transactions = new List<TransactionDetail>() };
705+
private static IQueryable<TransactionDetailQueryResult> ApplyTransactionDetailFilters(IQueryable<TransactionDetailQueryResult> query,
706+
TransactionDetailReportRequest request) {
707+
if (request.Merchants != null && request.Merchants.Any())
708+
query = query.Where(q => request.Merchants.Contains(q.MerchantReportingId));
709+
if (request.Products != null && request.Products.Any())
710+
query = query.Where(q => request.Products.Contains(q.ContractProductReportingId));
711+
if (request.Operators != null && request.Operators.Any())
712+
query = query.Where(q => request.Operators.Contains(q.OperatorReportingId));
713+
return query;
714+
}
725715

726-
// Now to translate the results
727-
response = new TransactionDetailReportResponse {
716+
private static TransactionDetailReportResponse MapToTransactionDetailResponse(List<TransactionDetailQueryResult> queryResults) {
717+
return new TransactionDetailReportResponse {
728718
Transactions = queryResults.Select(q => new TransactionDetail {
729719
Id = q.TransactionId,
730720
DateTime = q.TransactionDateTime,
@@ -745,8 +735,6 @@ from s in sJoin.DefaultIfEmpty()
745735
}).ToList(),
746736
Summary = new TransactionDetailSummary { TransactionCount = queryResults.Count(), TotalValue = queryResults.Sum(q => q.Value), TotalFees = queryResults.Sum(q => q.FeeValue) }
747737
};
748-
749-
return Result.Success(response);
750738
}
751739

752740
public async Task<Result<TransactionSummaryByMerchantResponse>> GetTransactionSummaryByMerchantReport(TransactionQueries.TransactionSummaryByMerchantQuery request,
@@ -1152,44 +1140,48 @@ public async Task<Result<ProductPerformanceResponse>> GetProductPerformanceRepor
11521140

11531141
using ResolvedDbContext<EstateManagementContext>? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString());
11541142
await using EstateManagementContext context = resolvedContext.Context;
1155-
var grandTotalAmountQuery = (from t in context.Transactions where t.TransactionType == "Sale" && t.TransactionDate >= new DateTime(2025, 12, 21) && t.TransactionDate <= new DateTime(2025, 12, 25) select t.TransactionAmount);
11561143

1144+
var grandTotalAmountQuery = (from t in context.Transactions where t.TransactionType == "Sale" && t.TransactionDate >= new DateTime(2025, 12, 21) && t.TransactionDate <= new DateTime(2025, 12, 25) select t.TransactionAmount);
11571145
var grandTotalAmountResult = await ExecuteQuerySafeSum<decimal>(grandTotalAmountQuery, cancellationToken);
11581146
if (grandTotalAmountResult.IsFailed)
11591147
return ResultHelpers.CreateFailure(grandTotalAmountResult);
11601148
var grandTotalAmount = grandTotalAmountResult.Data;
11611149

1162-
var query = from t in context.Transactions
1150+
var query = BuildProductPerformanceQuery(context, request.StartDate, request.EndDate, grandTotalAmount);
1151+
var queryResult = await ExecuteQuerySafeToList(query, cancellationToken);
1152+
if (queryResult.IsFailed)
1153+
return ResultHelpers.CreateFailure(queryResult);
1154+
var queryResults = queryResult.Data;
1155+
if (queryResults.Any() == false)
1156+
return Result.Success(new ProductPerformanceResponse() { Summary = new ProductPerformanceSummary(), ProductDetails = new List<ProductPerformanceDetail>() });
1157+
1158+
return Result.Success(BuildProductPerformanceResponse(queryResults));
1159+
}
1160+
1161+
private static IQueryable<ProductPerformanceItemData> BuildProductPerformanceQuery(EstateManagementContext context,
1162+
DateTime startDate,
1163+
DateTime endDate,
1164+
decimal grandTotalAmount) {
1165+
return from t in context.Transactions
11631166
join cp in context.ContractProducts on new { t.ContractProductId, t.ContractId } equals new { cp.ContractProductId, cp.ContractId }
11641167
join c in context.Contracts on t.ContractId equals c.ContractId
1165-
where t.TransactionType == "Sale" && t.TransactionDate >= request.StartDate && t.TransactionDate <= request.EndDate
1166-
group t by new {
1167-
cp.ProductName,
1168-
cp.ContractProductId,
1169-
cp.ContractProductReportingId,
1170-
c.ContractId,
1171-
c.ContractReportingId
1172-
}
1168+
where t.TransactionType == "Sale" && t.TransactionDate >= startDate && t.TransactionDate <= endDate
1169+
group t by new { cp.ProductName, cp.ContractProductId, cp.ContractProductReportingId, c.ContractId, c.ContractReportingId }
11731170
into g
1174-
select new {
1175-
g.Key.ProductName,
1176-
g.Key.ContractProductId,
1177-
g.Key.ContractProductReportingId,
1178-
g.Key.ContractId,
1179-
g.Key.ContractReportingId,
1171+
select new ProductPerformanceItemData {
1172+
ProductName = g.Key.ProductName,
1173+
ContractProductId = g.Key.ContractProductId,
1174+
ContractProductReportingId = g.Key.ContractProductReportingId,
1175+
ContractId = g.Key.ContractId,
1176+
ContractReportingId = g.Key.ContractReportingId,
11801177
TransactionCount = g.Count(),
11811178
TotalAmount = g.Sum(x => x.TransactionAmount),
11821179
PercentOfTotalAmount = grandTotalAmount == 0 ? 0 : 100.0m * g.Sum(x => x.TransactionAmount) / grandTotalAmount
11831180
};
1181+
}
11841182

1185-
var queryResult = await ExecuteQuerySafeToList(query, cancellationToken);
1186-
if (queryResult.IsFailed)
1187-
return ResultHelpers.CreateFailure(queryResult);
1188-
var queryResults = queryResult.Data;
1189-
if (queryResults.Any() == false)
1190-
return Result.Success(new ProductPerformanceResponse() { Summary = new ProductPerformanceSummary(), ProductDetails = new List<ProductPerformanceDetail>() });
1191-
1192-
ProductPerformanceResponse response = new ProductPerformanceResponse() {
1183+
private ProductPerformanceResponse BuildProductPerformanceResponse(List<ProductPerformanceItemData> queryResults) {
1184+
return new ProductPerformanceResponse {
11931185
ProductDetails = queryResults.Select(q => new ProductPerformanceDetail {
11941186
ProductName = q.ProductName,
11951187
ProductId = q.ContractProductId,
@@ -1202,8 +1194,6 @@ into g
12021194
}).ToList(),
12031195
Summary = new ProductPerformanceSummary { TotalCount = queryResults.Sum(q => q.TransactionCount), TotalValue = queryResults.Sum(q => q.TotalAmount), AveragePerProduct = this.SafeDivide(queryResults.Sum(q => q.TotalAmount), queryResults.Count), TotalProducts = queryResults.Count() }
12041196
};
1205-
1206-
return Result.Success(response);
12071197
}
12081198

12091199
public async Task<Result<List<TodaysSalesByHour>>> GetTodaysSalesByHour(TransactionQueries.TodaysSalesByHour request,
@@ -1418,6 +1408,23 @@ private sealed class MerchantTransactionFinalProjection {
14181408
public int AuthorisedCount { get; init; }
14191409
public int DeclinedCount { get; init; }
14201410
public decimal AuthorisedPercentage { get; init; }
1411+
private sealed class TransactionDetailQueryResult {
1412+
public Guid TransactionId { get; init; }
1413+
public DateTime TransactionDateTime { get; init; }
1414+
public Guid MerchantId { get; init; }
1415+
public int MerchantReportingId { get; init; }
1416+
public string? MerchantName { get; init; }
1417+
public Guid OperatorId { get; init; }
1418+
public int OperatorReportingId { get; init; }
1419+
public string? OperatorName { get; init; }
1420+
public string? ProductName { get; init; }
1421+
public Guid ContractProductId { get; init; }
1422+
public int ContractProductReportingId { get; init; }
1423+
public string? TransactionType { get; init; }
1424+
public string? Status { get; init; }
1425+
public decimal Value { get; init; }
1426+
public decimal FeeValue { get; init; }
1427+
public Guid SettlementId { get; init; }
14211428
}
14221429

14231430
private sealed class ContractBaseData {
@@ -1448,6 +1455,17 @@ private sealed class ContractFeeData {
14481455
public bool IsEnabled { get; init; }
14491456
}
14501457

1458+
private sealed class ProductPerformanceItemData {
1459+
public string? ProductName { get; init; }
1460+
public Guid ContractProductId { get; init; }
1461+
public int ContractProductReportingId { get; init; }
1462+
public Guid ContractId { get; init; }
1463+
public int ContractReportingId { get; init; }
1464+
public int TransactionCount { get; init; }
1465+
public decimal TotalAmount { get; init; }
1466+
public decimal PercentOfTotalAmount { get; init; }
1467+
}
1468+
14511469
#endregion
14521470
}
14531471

0 commit comments

Comments
 (0)