Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#4330 cas send invoice #4377

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
@@ -0,0 +1,18 @@
import { MigrationInterface, QueryRunner } from "typeorm";
import { getSQLFileData } from "../utilities/sqlLoader";

export class AddQueueCASSendInvoices1740093886236
implements MigrationInterface
{
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
getSQLFileData("Add-cas-send-invoices.sql", "Queue"),
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
getSQLFileData("Rollback-add-cas-send-invoices.sql", "Queue"),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
INSERT INTO
sims.queue_configurations(queue_name, queue_configuration, queue_settings)
VALUES
(
'cas-send-invoices',
'{
"cron": "0 5,12,17 * * 1-5",
"retry": 3,
"cleanUpPeriod": 2592000000,
"retryInterval": 180000,
"dashboardReadonly": false,
"pollingRecordLimit": 2000
}',
'{
"maxStalledCount": 0,
"lockDuration": 60000,
"lockRenewTime": 5000
}'
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
DELETE FROM
sims.queue_configurations
WHERE
queue_name = 'cas-send-invoices';
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ export * from "./schedulers/esdc-integration/application-changes-report-integrat
export * from "./schedulers/student-application-notifications/student-application-notifications.scheduler";
export * from "./schedulers/sfas-integration/sims-to-sfas-integration.scheduler";
export * from "./schedulers/cas-integration/cas-invoices-batches-creation.scheduler";
export * from "./schedulers/cas-integration/cas-send-invoices.scheduler";
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { INestApplication } from "@nestjs/common";
import { SystemUsersService } from "@sims/services";
import {
SupplierStatus,
DisbursementValueType,
CASInvoiceBatchApprovalStatus,
OfferingIntensity,
} from "@sims/sims-db";
import {
E2EDataSources,
createE2EDataSources,
createFakeDisbursementValue,
createFakeCASInvoiceBatch,
saveFakeInvoiceIntoBatchWithInvoiceDetails,
} from "@sims/test-utils";
import { getPSTPDTDateTime, QueueNames } from "@sims/utilities";
import {
createTestingAppModule,
describeProcessorRootTest,
mockBullJob,
} from "../../../../../test/helpers";
import { CASSendInvoicesScheduler } from "../cas-send-invoices.scheduler";

const CAS_INVOICE_BATCH_SEQUENCE_NAME = "CAS_INVOICE_BATCH";
const CAS_INVOICE_SEQUENCE_NAME = "CAS_INVOICE";

describe(describeProcessorRootTest(QueueNames.CASSendInvoices), () => {
let app: INestApplication;
let processor: CASSendInvoicesScheduler;
let db: E2EDataSources;
let systemUsersService: SystemUsersService;

beforeAll(async () => {
const { nestApplication, dataSource } = await createTestingAppModule();
app = nestApplication;
systemUsersService = nestApplication.get(SystemUsersService);
db = createE2EDataSources(dataSource);
// Processor under test.
processor = app.get(CASSendInvoicesScheduler);
});

beforeEach(async () => {
jest.clearAllMocks();
// Update existing records to avoid conflicts between tests.
await db.disbursementReceiptValue.update(
{
grantType: "BCSG",
},
{ grantAmount: 0 },
);
// Delete all existing invoices and related data.
await db.casInvoiceDetail.delete({});
await db.casInvoice.delete({});
await db.casInvoiceBatch.delete({});
// Reset sequence numbers.
await db.sequenceControl.delete({
sequenceName: CAS_INVOICE_BATCH_SEQUENCE_NAME,
});
await db.sequenceControl.delete({
sequenceName: CAS_INVOICE_SEQUENCE_NAME,
});
});

it.only("Should send invoices.", async () => {
// Create invoice batch to generate the report.
const casInvoiceBatch = await db.casInvoiceBatch.save(
createFakeCASInvoiceBatch(
{
creator: systemUsersService.systemUser,
},
{
initialValue: {
approvalStatus: CASInvoiceBatchApprovalStatus.Approved,
},
},
),
);
// Creates full-time application with receipts, and invoices details.
const fullTimeInvoice = await saveFakeInvoiceIntoBatchWithInvoiceDetails(
db,
{
casInvoiceBatch,
creator: systemUsersService.systemUser,
// Full-time BC grants.
disbursementValues: [
createFakeDisbursementValue(
DisbursementValueType.BCGrant,
"BCAG",
10,
{
effectiveAmount: 5,
},
),
createFakeDisbursementValue(
DisbursementValueType.BCGrant,
"BGPD",
20,
{
effectiveAmount: 15,
},
),
createFakeDisbursementValue(
DisbursementValueType.BCGrant,
"SBSD",
30,
{
effectiveAmount: 25,
},
),
createFakeDisbursementValue(
DisbursementValueType.BCTotalGrant,
"BCSG",
60,
{ effectiveAmount: 45 },
),
],
},
{
offeringIntensity: OfferingIntensity.fullTime,
casSupplierInitialValues: {
supplierStatus: SupplierStatus.VerifiedManually,
supplierNumber: "111111",
},
},
);
// Creating variables to provide easy access to some nested values.
// Full-time related variables.
fullTimeInvoice.disbursementReceipt.disbursementSchedule.studentAssessment
.application.student;
fullTimeInvoice.disbursementReceipt.disbursementSchedule.documentNumber.toString();
getPSTPDTDateTime(fullTimeInvoice.disbursementReceipt.createdAt);

// Queued job.
const mockedJob = mockBullJob<void>();

// Act
const result = await processor.processQueue(mockedJob.job);

// Assert
expect(result).toStrictEqual(["Process finalized with success."]);
expect(mockedJob.containLogMessages(["CAS send invoices executed."])).toBe(
true,
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { InjectQueue, Processor } from "@nestjs/bull";
import { QueueService } from "@sims/services/queue";
import { QueueNames } from "@sims/utilities";
import {
InjectLogger,
LoggerService,
ProcessSummary,
} from "@sims/utilities/logger";
import { Job, Queue } from "bull";
import { BaseScheduler } from "../base-scheduler";
import { CASInvoiceService } from "../../../services";

/**
* Scheduler to send invoices to CAS.
*/
@Processor(QueueNames.CASSendInvoices)
export class CASSendInvoicesScheduler extends BaseScheduler<void> {
constructor(
@InjectQueue(QueueNames.CASSendInvoices)
schedulerQueue: Queue<void>,
queueService: QueueService,
private readonly casInvoiceService: CASInvoiceService,
) {
super(schedulerQueue, queueService);
}

/**
* Scheduler to send invoices to CAS.
* Checks for pending invoices on the approved batch and send them to CAS.
* @param _job process job.
* @param processSummary process summary for logging.
* @returns process summary.
*/
protected async process(
_job: Job<void>,
processSummary: ProcessSummary,
): Promise<string[]> {
processSummary.info("Checking for pending invoices.");
const pendingInvoices = await this.casInvoiceService.getPendingInvoices();
let invoicesUpdated = 0;
if (!pendingInvoices.length) {
processSummary.info("No pending invoices found.");
}
processSummary.info("Executing CAS send invoices.");
const serviceProcessSummary = new ProcessSummary();
processSummary.children(serviceProcessSummary);
invoicesUpdated = await this.casInvoiceService.sendInvoices(
serviceProcessSummary,
pendingInvoices,
);
processSummary.info("CAS send invoices executed.");
return ["Process finalized with success."];
}

@InjectLogger()
logger: LoggerService;
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
StudentApplicationNotificationsScheduler,
SIMSToSFASIntegrationScheduler,
CASInvoicesBatchesCreationScheduler,
CASSendInvoicesScheduler,
} from "./processors";
import {
DisbursementScheduleSharedService,
Expand Down Expand Up @@ -75,6 +76,7 @@ import {
StudentPDPPDReminderNotification,
StudentSecondDisbursementReminderNotification,
CASInvoiceBatchService,
CASInvoiceService,
} from "./services";
import { SFASIntegrationModule } from "@sims/integrations/sfas-integration";
import { ATBCIntegrationModule } from "@sims/integrations/atbc-integration";
Expand Down Expand Up @@ -175,7 +177,9 @@ import { QueuesMetricsModule } from "./queues-metrics.module.module";
StudentPDPPDReminderNotification,
StudentSecondDisbursementReminderNotification,
CASInvoiceBatchService,
CASInvoiceService,
CASInvoicesBatchesCreationScheduler,
CASSendInvoicesScheduler,
],
controllers: [HealthController, MetricsController],
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export class PendingInvoiceResult {
isInvoiceUpdated: boolean;
knownErrors?: string[];
}
Loading