Skip to content

Commit c1d393e

Browse files
authored
Merge pull request #76 from bcgov/GeneratePaymentLineForERV1
New function to generate PaymentLine for CCOF project
2 parents bcb31e6 + 6cce48a commit c1d393e

File tree

2 files changed

+199
-60
lines changed

2 files changed

+199
-60
lines changed

CCOF.Infrastructure.WebAPI/Services/Processes/Payments/P505GeneratePaymentLinesProvider.cs

Lines changed: 179 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -11,79 +11,101 @@
1111
using CCOF.Infrastructure.WebAPI.Services.Processes;
1212
using CCOF.Infrastructure.WebAPI.Messages;
1313
using Microsoft.Extensions.Options;
14+
using System;
15+
using CCOF.Infrastructure.WebAPI.Services.D365WebAPI;
16+
using CCOF.Core.DataContext;
17+
using System.Drawing.Text;
1418

1519
namespace CCOF.Infrastructure.WebAPI.Services.Processes.Payments
1620
{
17-
public class P505GeneratePaymentLinesProvider( ID365AppUserService appUserService, ID365WebApiService d365WebApiService, ILoggerFactory loggerFactory) : ID365ProcessProvider
21+
public class P505GeneratePaymentLinesProvider(IOptionsSnapshot<ExternalServices> bccasApiSettings, ID365AppUserService appUserService, ID365WebApiService d365WebApiService, ILoggerFactory loggerFactory) : ID365ProcessProvider
1822
{
19-
// private readonly BCCASApi _BCCASApi = bccasApiSettings.Value.BCCASApi;
23+
private readonly BCCASApi _BCCASApi = bccasApiSettings.Value.BCCASApi;
2024
private readonly ID365AppUserService _appUserService = appUserService;
2125
private readonly ID365WebApiService _d365WebApiService = d365WebApiService;
2226
private readonly ILogger _logger = loggerFactory.CreateLogger(LogCategory.Process);
23-
24-
2527
private ProcessParameter? _processParams;
26-
27-
2828
public Int16 ProcessId => Setup.Process.Payments.GeneratePaymentLinesId;
2929
public string ProcessName => Setup.Process.Payments.GeneratePaymentLinesName;
30-
31-
public Task<ProcessData> GetDataAsync()
32-
{
33-
throw new NotImplementedException();
34-
}
35-
3630
#region Data Queries
37-
38-
public string applicationCCFRIs
39-
{
31+
public string BusinessClosuresRequestUri
32+
{ // ofm_holiday_type" operator="eq" value="1" Standard for CCOF. 2 for OFM
4033
get
4134
{
42-
// For reference only
4335
var fetchXml = $$"""
44-
<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="true">
45-
<entity name="ccof_application_ccfri_closure">
46-
<attribute name="ccof_name" />
47-
<attribute name="ccof_application_ccfri_closureid" />
48-
<attribute name="ccof_startdate" />
49-
<attribute name="ccof_enddate" />
50-
<attribute name="ccof_is_full_closure" />
51-
<attribute name="ccof_totalworkdays" />
52-
<attribute name="ccof_totaldays" />
53-
<attribute name="ccof_paidclosure" />
54-
<attribute name="ccof_closure_status" />
55-
<attribute name="ccof_facilityinfo" />
56-
<attribute name="ccof_closure_type" />
57-
<filter type="and">
58-
<condition attribute="ccof_approved_as" operator="eq" value="100000000" />
59-
<condition attribute="ccof_facilityinfo" operator="eq" value="e4077ebe-0310-f011-9989-000d3a09ed17" />
60-
<condition attribute="ccof_closure_status" operator="eq" value="100000003" />
61-
<condition attribute="ccof_program_year" operator="eq" value="fdc2fce3-d1a2-ef11-8a6a-000d3af474a4" uiname="2025-26 FY" uitype="ccof_program_year" />
36+
<fetch>
37+
<entity name="ofm_stat_holiday">
38+
<attribute name="ofm_date_observed" />
39+
<attribute name="ofm_holiday_type" />
40+
<attribute name="ofm_stat_holidayid" />
41+
<filter>
42+
<condition attribute="ofm_holiday_type" operator="eq" value="1" />
6243
</filter>
6344
</entity>
6445
</fetch>
6546
""";
6647

6748
var requestUri = $"""
68-
ccof_application_ccfri_closures?$select=ccof_name,ccof_application_ccfri_closureid,ccof_startdate,ccof_enddate,ccof_is_full_closure,ccof_totalworkdays,ccof_totaldays,ccof_paidclosure,ccof_closure_status,_ccof_facilityinfo_value,ccof_closure_type&$filter=(ccof_approved_as eq 100000000 and ccof_closure_status eq 100000003)
49+
ofm_stat_holidaies?fetchXml={WebUtility.UrlEncode(fetchXml)}
6950
""";
7051

7152
return requestUri;
7253
}
7354
}
74-
75-
55+
public string EnrolmentReportPaymentUri
56+
{
57+
get
58+
{
59+
var fetchXml = $$"""
60+
<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
61+
<entity name="ccof_monthlyenrollmentreport">
62+
<attribute name="ccof_ccfri_external_status" />
63+
<attribute name="ccof_ccfri_internal_status" />
64+
<attribute name="ccof_ccfri_verification" />
65+
<attribute name="ccof_ccof_base_verification" />
66+
<attribute name="ccof_ccof_external_status" />
67+
<attribute name="ccof_ccof_internal_status" />
68+
<attribute name="ccof_facility" />
69+
<attribute name="ccof_grandtotalbase" />
70+
<attribute name="ccof_grandtotalccfri" />
71+
<attribute name="ccof_grandtotalccfriprovider" />
72+
<attribute name="ccof_month" />
73+
<attribute name="ccof_monthlyenrollmentreportid" />
74+
<attribute name="ccof_organization" />
75+
<attribute name="ccof_programyear" />
76+
<attribute name="ccof_reportversion" />
77+
<attribute name="ccof_year" />
78+
<attribute name="statecode" />
79+
<attribute name="statuscode" />
80+
<attribute name="ccof_ccfri_approved_date" />
81+
<attribute name="ccof_ccof_approved_date" />
82+
<link-entity name="account" from="accountid" to="ccof_facility" link-type="inner" alias="facility">
83+
<attribute name="name" />
84+
<attribute name="accountnumber" />
85+
</link-entity>
86+
<filter>
87+
<condition attribute="ccof_monthlyenrollmentreportid" operator="eq" value="{{_processParams.EnrolmentReportid.ToString()}}" />
88+
</filter>
89+
</entity>
90+
</fetch>
91+
""";
92+
var requestUri = $"""
93+
ccof_monthlyenrollmentreports?fetchXml={WebUtility.UrlEncode(fetchXml)}
94+
""";
95+
return requestUri;
96+
}
97+
}
7698
#endregion
7799
public async Task<ProcessData> GetBusinessClosuresDataAsync()
78100
{
79101
_logger.LogDebug(CustomLogEvent.Process, nameof(GetBusinessClosuresDataAsync));
80102

81-
var response = await _d365WebApiService.SendRetrieveRequestAsync(_appUserService.AZSystemAppUser, applicationCCFRIs, false, 0, true);
103+
var response = await _d365WebApiService.SendRetrieveRequestAsync(_appUserService.AZSystemAppUser, BusinessClosuresRequestUri, false, 0, true);
82104

83105
if (!response.IsSuccessStatusCode)
84106
{
85107
var responseBody = await response.Content.ReadAsStringAsync();
86-
_logger.LogError(CustomLogEvent.Process, "Failed to query record information with the server error {responseBody}", responseBody.CleanLog());
108+
_logger.LogError(CustomLogEvent.Process, "Failed to query Funding record information with the server error {responseBody}", responseBody.CleanLog());
87109

88110
return await Task.FromResult(new ProcessData(string.Empty));
89111
}
@@ -95,7 +117,7 @@ public async Task<ProcessData> GetBusinessClosuresDataAsync()
95117
{
96118
if (currentValue?.AsArray().Count == 0)
97119
{
98-
_logger.LogInformation(CustomLogEvent.Process, "No records found with query {requestUri}", applicationCCFRIs.CleanLog());
120+
_logger.LogInformation(CustomLogEvent.Process, "No records found with query {requestUri}", BusinessClosuresRequestUri.CleanLog());
99121
}
100122
d365Result = currentValue!;
101123
}
@@ -104,28 +126,127 @@ public async Task<ProcessData> GetBusinessClosuresDataAsync()
104126

105127
return await Task.FromResult(new ProcessData(d365Result));
106128
}
129+
public async Task<ProcessData> GetDataAsync()
130+
{
131+
_logger.LogDebug(CustomLogEvent.Process, "GetDataAsync");
107132

108-
public async Task<JsonObject> RunProcessAsync(ID365AppUserService appUserService, ID365WebApiService d365WebApiService, ProcessParameter processParams)
133+
var response = await _d365WebApiService.SendRetrieveRequestAsync(_appUserService.AZSystemAppUser, EnrolmentReportPaymentUri);
109134

110-
{
111-
#region Validation & Setup
112-
var entitySetName = "ccof_application_ccfri_closures";
113-
var payload2 = new JsonObject {
114-
{ "ccof_name", "test123" }
115-
116-
};
117-
var requestBody2 = JsonSerializer.Serialize(payload2);
118-
var CreateResponse2 = await d365WebApiService.SendCreateRequestAsync(appUserService.AZSystemAppUser, entitySetName, requestBody2);
119-
_logger.LogTrace(CustomLogEvent.Process, "Start processing payments for the test.");
120-
121-
122-
#endregion
123-
return ProcessResult.Completed(ProcessId).SimpleProcessResult;
124-
125-
}
135+
if (!response.IsSuccessStatusCode)
136+
{
137+
var responseBody = await response.Content.ReadAsStringAsync();
138+
_logger.LogError(CustomLogEvent.Process, "Failed to query Expense record information with the server error {responseBody}", responseBody.CleanLog());
126139

140+
return await Task.FromResult(new ProcessData(string.Empty));
141+
}
127142

128-
}
143+
var jsonObject = await response.Content.ReadFromJsonAsync<JsonObject>();
129144

145+
JsonNode d365Result = string.Empty;
146+
if (jsonObject?.TryGetPropertyValue("value", out var currentValue) == true)
147+
{
148+
if (currentValue?.AsArray().Count == 0)
149+
{
150+
_logger.LogInformation(CustomLogEvent.Process, "No records found with query {requestUri}", EnrolmentReportPaymentUri.CleanLog());
151+
}
152+
d365Result = currentValue!;
153+
}
154+
155+
_logger.LogDebug(CustomLogEvent.Process, "Query Result {queryResult}", d365Result.ToString().CleanLog());
156+
157+
return await Task.FromResult(new ProcessData(d365Result));
158+
}
159+
private async Task<JsonObject> CreateSinglePayment(JsonNode enrolmentReport,
160+
DateTime paymentDate,
161+
decimal? totalAmount,
162+
ProcessParameter processParams,
163+
List<DateTime> holidaysList)
164+
{
165+
DateTime invoiceDate = paymentDate.GetPreviousBusinessDay(holidaysList);
166+
DateTime invoiceReceivedDate = invoiceDate.AddBusinessDays(_BCCASApi.PayableInDays, holidaysList);
167+
DateTime effectiveDate = invoiceDate;
168+
string paymentType = (int)processParams.programapproved switch
169+
{
170+
7 => "CCOF",
171+
8 => "CCFRI",
172+
9 => "CCFRI Provider",
173+
_ => "Unknown"
174+
};
175+
int invoiceLineNumber = (int)processParams.programapproved switch
176+
{
177+
7 => 1,
178+
8 => 2,
179+
9 => 3,
180+
_ => 999999
181+
};
182+
var payload = new JsonObject()
183+
{
184+
{ "ofm_invoice_line_number", invoiceLineNumber},
185+
{ "ofm_amount", totalAmount},
186+
{ "ofm_payment_type", (int) processParams.programapproved},
187+
{ "ccof_monthly_enrollment_report@odata.bind",$"/ccof_monthlyenrollmentreports({enrolmentReport["ccof_monthlyenrollmentreportid"]})" },
188+
{ "ofm_invoice_date", invoiceDate.ToString("yyyy-MM-dd") },
189+
{ "ofm_invoice_received_date", invoiceReceivedDate.ToString("yyyy-MM-dd")},
190+
{ "ofm_effective_date", effectiveDate.ToString("yyyy-MM-dd")},
191+
{ "ccof_program_year@odata.bind",$"/ccof_program_years({enrolmentReport["_ccof_programyear_value"]})" },
192+
{ "statuscode",4 }, //Approved for Payment in PaymentLine table
193+
{ "ofm_facility@odata.bind", $"/accounts({enrolmentReport["_ccof_facility_value"]})" },
194+
{ "ofm_organization@odata.bind", $"/accounts({enrolmentReport["_ccof_organization_value"]})" },
195+
{ "ofm_description", $"{enrolmentReport["facility.name"] +" " +enrolmentReport["ccof_month"]+"/"+enrolmentReport["ccof_year"]+" " +paymentType}" },
196+
};
197+
var requestBody = JsonSerializer.Serialize(payload);
198+
var response = await _d365WebApiService.SendCreateRequestAsync(_appUserService.AZSystemAppUser, "ofm_payments", requestBody);
199+
if (!response.IsSuccessStatusCode)
200+
{
201+
var responseBody = await response.Content.ReadAsStringAsync();
202+
_logger.LogError(CustomLogEvent.Process, "Failed to create a payment with the server error {responseBody}. ProcessParam {param}", responseBody.CleanLog(), JsonValue.Create(processParams)?.ToString());
203+
204+
return ProcessResult.Failure(ProcessId, [responseBody], 0, 0).SimpleProcessResult;
205+
}
130206

207+
return ProcessResult.Completed(ProcessId).SimpleProcessResult;
208+
}
209+
public async Task<JsonObject> RunProcessAsync(ID365AppUserService appUserService, ID365WebApiService d365WebApiService, ProcessParameter processParams)
210+
{
211+
var PSTZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
212+
var pstTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, PSTZone);
213+
_logger.LogInformation(CustomLogEvent.Process, pstTime.ToString("yyyy-MM-dd HH:mm:ss") + "Process " + ProcessId + ": Begin to generate PaymentLine Process");
214+
try
215+
{
216+
_processParams = processParams;
217+
var entitySetName = "ofm_payments";
218+
var enrolmentReportString = await GetDataAsync();
219+
JsonArray? enrolmentReportData = JsonSerializer.Deserialize<JsonArray>(enrolmentReportString.Data);
220+
if (enrolmentReportData is null || !enrolmentReportData.Any())
221+
{
222+
_logger.LogError(CustomLogEvent.Process, "Unable to retrieve the Monthly Enrolment Report record Id {enrolmentReportId}", processParams!.EnrolmentReportid);
223+
return ProcessResult.Completed(ProcessId).SimpleProcessResult;
224+
}
225+
JsonNode? enrolmentReport = enrolmentReportData.First();
226+
var businessClosuresData = await GetBusinessClosuresDataAsync();
227+
var closures = JsonSerializer.Deserialize<JsonArray>(businessClosuresData.Data.ToString());
228+
List<DateTime> holidaysList = closures!.Select(closure => (DateTime)closure["ofm_date_observed"]).ToList();
229+
switch ((int)processParams.programapproved)
230+
{
231+
case 7: // ofm_payment_type CCOF
232+
await CreateSinglePayment(enrolmentReport, (DateTime)enrolmentReport["ccof_ccof_approved_date"], (decimal)enrolmentReport["ccof_grandtotalbase"], processParams!, holidaysList);
233+
break;
234+
case 8: // ofm_payment_type CCFRI
235+
await CreateSinglePayment(enrolmentReport, (DateTime)enrolmentReport["ccof_ccfri_approved_date"], ((decimal?)(enrolmentReport["ccof_grandtotalccfriprovider"]) ?? 0) + ((decimal?)(enrolmentReport["ccof_grandtotalccfri"]) ?? 0), processParams!, holidaysList);
236+
break;
237+
default:
238+
_logger.LogError(CustomLogEvent.Process, "Unable to generate payments for Erolment Report {ERid}. Invalid ApprovedType {programApproved}", processParams?.EnrolmentReportid, processParams?.programapproved);
239+
break;
240+
}
241+
}
242+
catch (Exception ex)
243+
{
244+
Console.WriteLine(ex.Message);
245+
var returnObject = ProcessResult.Failure(ProcessId, new String[] { "Critical error", ex.StackTrace }, 0, 0).ODProcessResult;
246+
return returnObject;
247+
}
248+
_logger.LogInformation(CustomLogEvent.Process, pstTime.ToString("yyyy-MM-dd HH:mm:ss") + "Process " + ProcessId + ": End Process.");
249+
return ProcessResult.Completed(ProcessId).SimpleProcessResult;
250+
}
251+
}
131252
}

CCOF.Infrastructure.WebAPI/Services/Processes/ProcessParameter.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ public record ProcessParameter
3939

4040
[property: JsonPropertyName("dataImportId")]
4141
public Guid? DataImportId { get; set; }
42-
42+
[property: JsonPropertyName("enrolmentReportid")]
43+
public Guid? EnrolmentReportid { get; set; }
44+
[property: JsonPropertyName("organization")]
45+
public OrganizationParameter? Organization { get; set; }
46+
[property:JsonPropertyName("programApproved")]
47+
public int? programapproved { get; set; }
4348

4449
#region Inner Parameter Record Objects
4550

@@ -68,4 +73,17 @@ public record InitialEnrolmentReportParameter
6873
public string[]? FacilityGuid { get; set; }
6974
}
7075
#endregion
71-
}
76+
public record OrganizationParameter
77+
{
78+
[property: JsonPropertyName("organizationId")]
79+
public Guid? organizationId { get; set; }
80+
[property: JsonPropertyName("facilityId")]
81+
public Guid? facilityId { get; set; }
82+
83+
[property: JsonPropertyName("legalName")]
84+
public string? legalName { get; set; }
85+
86+
[property: JsonPropertyName("incorporationNumber")]
87+
public string? incorporationNumber { get; set; }
88+
}
89+
}

0 commit comments

Comments
 (0)