Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.DynamicLinq" Version="10.7.1" />
<PackageReference Include="Shared" Version="2026.3.1" />
<PackageReference Include="Shared.Results" Version="2026.3.1" />
<PackageReference Include="TransactionProcessor.Database" Version="2026.3.1" />
<PackageReference Include="TransactionProcessor.Database" Version="2026.4.2-build346" />
</ItemGroup>

<ItemGroup>
Expand Down
7 changes: 5 additions & 2 deletions EstateReportingAPI.BusinessLogic/ModelFactory.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
namespace EstateReportingAPI.BusinessLogic;

using TransactionProcessor.Database.Entities;
using Merchant = EstateReportingAPI.Models.Merchant;

internal static class ModelFactory {
internal static Merchant ConvertFrom(MerchantData merchant,
decimal balance) {
decimal balance,
List<MerchantOpeningHours> openingHours) {
return new Merchant {
Balance = balance,
CreatedDateTime = merchant.CreatedDateTime,
Expand All @@ -23,7 +25,8 @@ internal static Merchant ConvertFrom(MerchantData merchant,
ContactId = merchant.ContactInfo.ContactId,
ContactName = merchant.ContactInfo.Name,
ContactEmail = merchant.ContactInfo.EmailAddress,
ContactPhone = merchant.ContactInfo.PhoneNumber
ContactPhone = merchant.ContactInfo.PhoneNumber,

};
}

Expand Down
3 changes: 2 additions & 1 deletion EstateReportingAPI.BusinessLogic/Queries/MerchantQueries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public record GetMerchantQuery(Guid EstateId, Guid MerchantId) : IRequest<Result
public record GetMerchantOperatorsQuery(Guid EstateId, Guid MerchantId) : IRequest<Result<List<MerchantOperator>>>;
public record GetMerchantContractsQuery(Guid EstateId, Guid MerchantId) : IRequest<Result<List<MerchantContract>>>;
public record GetMerchantDevicesQuery(Guid EstateId, Guid MerchantId) : IRequest<Result<List<MerchantDevice>>>;

public record GetMerchantOpeningHoursQuery(Guid EstateId, Guid MerchantId) : IRequest<Result<List<MerchantOpeningHour>>>;
public record GetMerchantScheduleQuery(Guid EstateId, Guid MerchantId, Int32 Year) : IRequest<Result<MerchantScheduleResponse>>;
public record MerchantQueryOptions(String Name,String Reference,Int32? SettlementSchedule,String Region, String PostCode);
}
99 changes: 93 additions & 6 deletions EstateReportingAPI.BusinessLogic/ReportingManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public interface IReportingManager
Task<Result<List<MerchantOperator>>> GetMerchantOperators(MerchantQueries.GetMerchantOperatorsQuery request, CancellationToken cancellationToken);
Task<Result<List<MerchantContract>>> GetMerchantContracts(MerchantQueries.GetMerchantContractsQuery request, CancellationToken cancellationToken);
Task<Result<List<MerchantDevice>>> GetMerchantDevices(MerchantQueries.GetMerchantDevicesQuery request, CancellationToken cancellationToken);
Task<Result<List<MerchantOpeningHour>>> GetMerchantOpeningHours(MerchantQueries.GetMerchantOpeningHoursQuery request, CancellationToken cancellationToken);
Task<Result<Operator>> GetOperator(OperatorQueries.GetOperatorQuery request, CancellationToken cancellationToken);
Task<Result<TransactionDetailReportResponse>> GetTransactionDetailReport(TransactionQueries.TransactionDetailReportQuery request,
CancellationToken cancellationToken);
Expand All @@ -58,6 +59,9 @@ Task<Result<List<TodaysSalesByHour>>> GetTodaysSalesByHour(TransactionQueries.To
Task<Result<TodaysSettlement>> GetTodaysSettlement(SettlementQueries.TodaysSettlementQuery request,
CancellationToken cancellationToken);
#endregion

Task<Result<MerchantScheduleResponse>> GetMerchantSchedule(MerchantQueries.GetMerchantScheduleQuery request,
CancellationToken cancellationToken);
}

public class ReportingManager : IReportingManager {
Expand Down Expand Up @@ -625,6 +629,47 @@ public async Task<Result<List<Operator>>> GetOperators(OperatorQueries.GetOperat
return Result.Success(operators);
}

public async Task<Result<List<MerchantOpeningHour>>> GetMerchantOpeningHours(MerchantQueries.GetMerchantOpeningHoursQuery request,
CancellationToken cancellationToken) {
using ResolvedDbContext<EstateManagementContext>? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString());
await using EstateManagementContext context = resolvedContext.Context;

var openingHoursQuery = (from mo in context.MerchantOpeningHours
where mo.MerchantId == request.MerchantId
select new {
SundayOpen = mo.SundayOpening,
SundayClose = mo.SundayClosing,
MondayOpen = mo.MondayOpening,
MondayClose = mo.MondayClosing,
TuesdayOpen = mo.TuesdayOpening,
TuesdayClose = mo.TuesdayClosing,
WednesdayOpen = mo.WednesdayOpening,
WednesdayClose = mo.WednesdayClosing,
ThursdayOpen = mo.ThursdayOpening,
ThursdayClose = mo.ThursdayClosing,
FridayOpen = mo.FridayOpening,
FridayClose = mo.FridayClosing,
SaturdayOpen = mo.SaturdayOpening,
SaturdayClose = mo.SaturdayClosing,
});

var openingHoursResult = await ExecuteQuerySafeSingleOrDefault(openingHoursQuery, cancellationToken, "Error retrieving merchant opening hours");

if (openingHoursResult.IsFailed)
return ResultHelpers.CreateFailure(openingHoursResult);

List<MerchantOpeningHour> openingHours = new List<MerchantOpeningHour>();
openingHours.Add(new MerchantOpeningHour {OpeningTime = openingHoursResult.Data.SundayOpen, ClosingTime = openingHoursResult.Data.SundayClose, DayOfWeek = DayOfWeek.Sunday, MerchantId = request.MerchantId});
openingHours.Add(new MerchantOpeningHour { OpeningTime = openingHoursResult.Data.MondayOpen, ClosingTime = openingHoursResult.Data.MondayClose, DayOfWeek = DayOfWeek.Monday, MerchantId = request.MerchantId });
openingHours.Add(new MerchantOpeningHour { OpeningTime = openingHoursResult.Data.TuesdayOpen, ClosingTime = openingHoursResult.Data.TuesdayClose, DayOfWeek = DayOfWeek.Tuesday, MerchantId = request.MerchantId });
openingHours.Add(new MerchantOpeningHour { OpeningTime = openingHoursResult.Data.WednesdayOpen, ClosingTime = openingHoursResult.Data.WednesdayClose, DayOfWeek = DayOfWeek.Wednesday, MerchantId = request.MerchantId });
openingHours.Add(new MerchantOpeningHour { OpeningTime = openingHoursResult.Data.ThursdayOpen, ClosingTime = openingHoursResult.Data.ThursdayClose, DayOfWeek = DayOfWeek.Thursday, MerchantId = request.MerchantId });
openingHours.Add(new MerchantOpeningHour { OpeningTime = openingHoursResult.Data.FridayOpen, ClosingTime = openingHoursResult.Data.FridayClose, DayOfWeek = DayOfWeek.Friday, MerchantId = request.MerchantId });
openingHours.Add(new MerchantOpeningHour { OpeningTime = openingHoursResult.Data.SaturdayOpen, ClosingTime = openingHoursResult.Data.SaturdayClose, DayOfWeek = DayOfWeek.Saturday, MerchantId = request.MerchantId });

return Result.Success(openingHours);
}

public async Task<Result<Operator>> GetOperator(OperatorQueries.GetOperatorQuery request,
CancellationToken cancellationToken) {
using ResolvedDbContext<EstateManagementContext>? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString());
Expand Down Expand Up @@ -966,16 +1011,19 @@ public async Task<Result<Merchant>> GetMerchant(MerchantQueries.GetMerchantQuery
using ResolvedDbContext<EstateManagementContext>? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString());
await using EstateManagementContext context = resolvedContext.Context;

var merchantQuery = BuildMerchantQuery(context, request.MerchantId);
var merchantQueryResult = await ExecuteQuerySafeSingleOrDefault(merchantQuery, cancellationToken, "Error getting merchant");
IQueryable<MerchantData> merchantQuery = BuildMerchantQuery(context, request.MerchantId);
Result<MerchantData> merchantQueryResult = await ExecuteQuerySafeSingleOrDefault(merchantQuery, cancellationToken, "Error getting merchant");
if (merchantQueryResult.IsFailed) {
return ResultHelpers.CreateFailure(merchantQueryResult);
}

var merchantStateQueryResult = await ExecuteQuerySafeSingleOrDefault(context.MerchantBalanceProjectionState.Where(ms => ms.MerchantId == request.MerchantId), cancellationToken, "Error getting merchant state");
Result<List<MerchantOpeningHours>> openingHoursQueryResult = await ExecuteQuerySafeToList(context.MerchantOpeningHours.Where(m => m.MerchantId == request.MerchantId), cancellationToken, "Error getting merchant opening hours");
if (openingHoursQueryResult.IsFailed) return ResultHelpers.CreateFailure(openingHoursQueryResult);

Result<MerchantBalanceProjectionState> merchantStateQueryResult = await ExecuteQuerySafeSingleOrDefault(context.MerchantBalanceProjectionState.Where(ms => ms.MerchantId == request.MerchantId), cancellationToken, "Error getting merchant state");
if (merchantStateQueryResult.IsFailed) return ResultHelpers.CreateFailure(merchantStateQueryResult);

return Result.Success(ModelFactory.ConvertFrom(merchantQueryResult.Data, merchantStateQueryResult.Data.Balance));
return Result.Success(ModelFactory.ConvertFrom(merchantQueryResult.Data, merchantStateQueryResult.Data.Balance, openingHoursQueryResult.Data));
}

private static IQueryable<MerchantData> BuildMerchantQuery(EstateManagementContext context,
Expand Down Expand Up @@ -1303,8 +1351,47 @@ public async Task<Result<TodaysSettlement>> GetTodaysSettlement(SettlementQuerie
return response;
}

private async Task<DatabaseProjections.SettlementGroupProjection> GetSettlementSummary(IQueryable<DatabaseProjections.ComparisonSettlementTransactionProjection> query,
CancellationToken cancellationToken) {
public async Task<Result<MerchantScheduleResponse>> GetMerchantSchedule(MerchantQueries.GetMerchantScheduleQuery request,
CancellationToken cancellationToken) {
using ResolvedDbContext<EstateManagementContext>? resolvedContext = this.Resolver.Resolve(EstateManagementDatabaseName, request.EstateId.ToString());
await using EstateManagementContext context = resolvedContext.Context;

var merchantScheduleQuery = from s in context.MerchantSchedules
join d in context.MerchantScheduleMonths on s.MerchantScheduleId equals d.MerchantScheduleId
where s.MerchantId == request.MerchantId && s.Year == request.Year
select new {
s.MerchantId,
s.MerchantScheduleId,
s.Year,
d.Month,
d.ClosedDays
};

var merchantScheduleQueryResult = await ExecuteQuerySafeToList(merchantScheduleQuery, cancellationToken, "Error getting merchant schedule");
if (merchantScheduleQueryResult.IsFailed)
return ResultHelpers.CreateFailure(merchantScheduleQueryResult);

MerchantScheduleResponse response = new MerchantScheduleResponse();
foreach (var item in merchantScheduleQueryResult.Data) {

List<Int32> closedDaysList = item.ClosedDays
.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(s => int.TryParse(s, out var n) ? n : (int?)null)
.Where(n => n.HasValue)
.Select(n => n.Value)
.ToList();

MerchantScheduleMonthResponse? monthSchedule = new() { ClosedDays = closedDaysList, Month = item.Month };

response.Months.Add(monthSchedule);
}
response.Year = request.Year;

return response;
}

private async Task<DatabaseProjections.SettlementGroupProjection> GetSettlementSummary(IQueryable<DatabaseProjections.ComparisonSettlementTransactionProjection> query,
CancellationToken cancellationToken) {
// Get the settleed fees summary
DatabaseProjections.SettlementGroupProjection summary = await BuildSettlementSummaryQuery(query).SingleOrDefaultAsync(cancellationToken);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ public class MerchantRequestHandler : IRequestHandler<MerchantQueries.GetRecentM
IRequestHandler<MerchantQueries.GetMerchantQuery, Result<Merchant>>,
IRequestHandler<MerchantQueries.GetMerchantContractsQuery, Result<List<MerchantContract>>>,
IRequestHandler<MerchantQueries.GetMerchantOperatorsQuery, Result<List<MerchantOperator>>>,
IRequestHandler<MerchantQueries.GetMerchantDevicesQuery, Result<List<MerchantDevice>>>
IRequestHandler<MerchantQueries.GetMerchantDevicesQuery, Result<List<MerchantDevice>>>,
IRequestHandler<MerchantQueries.GetMerchantOpeningHoursQuery, Result<List<MerchantOpeningHour>>>,
IRequestHandler<MerchantQueries.GetMerchantScheduleQuery, Result<MerchantScheduleResponse>>
{
private readonly IReportingManager Manager;
public MerchantRequestHandler(IReportingManager manager)
Expand Down Expand Up @@ -53,4 +55,14 @@ public async Task<Result<List<MerchantDevice>>> Handle(MerchantQueries.GetMercha
CancellationToken cancellationToken) {
return await this.Manager.GetMerchantDevices(request, cancellationToken);
}

public async Task<Result<List<MerchantOpeningHour>>> Handle(MerchantQueries.GetMerchantOpeningHoursQuery request,
CancellationToken cancellationToken) {
return await this.Manager.GetMerchantOpeningHours(request, cancellationToken);
}

public async Task<Result<MerchantScheduleResponse>> Handle(MerchantQueries.GetMerchantScheduleQuery request,
CancellationToken cancellationToken) {
return await this.Manager.GetMerchantSchedule(request, cancellationToken);
}
}
43 changes: 42 additions & 1 deletion EstateReportingAPI.DataTrasferObjects/Merchant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,21 @@ public class Merchant{
[JsonProperty("contact_phone")]
public String ContactPhone { get; set; }


#endregion
}

public class OpeningHoursResponse
{
[JsonProperty("day_of_week")]
public Int32 DayOfWeek { get; set; }

[JsonProperty("opening")]
public string Opening { get; set; }

[JsonProperty("closing")]
public string Closing { get; set; }
}

public class MerchantOperator
{
[JsonProperty("merchant_id")]
Expand Down Expand Up @@ -110,4 +121,34 @@ public class MerchantDevice
[JsonProperty("is_deleted")]
public Boolean IsDeleted { get; set; }
}

public class MerchantOpeningHour
{
[JsonProperty("merchant_id")]
public Guid MerchantId { get; set; }
[JsonProperty("day_of_week")]
public DayOfWeek DayOfWeek { get; set; }
[JsonProperty("opening_time")]
public String OpeningTime { get; set; }
[JsonProperty("closing_time")]
public String ClosingTime { get; set; }
}

public class MerchantScheduleResponse
{
[JsonProperty("year")]
public int Year { get; set; }

[JsonProperty("months")]
public List<MerchantScheduleMonthResponse> Months { get; set; }
}

public class MerchantScheduleMonthResponse
{
[JsonProperty("month")]
public int Month { get; set; }

[JsonProperty("closed_days")]
public List<int> ClosedDays { get; set; }
}
}
Loading
Loading