Skip to content

Commit ef87b26

Browse files
committed
Merge branch 'PM-1143_handle-taxForm-status-updated' into HEAD
2 parents e1551a4 + 8b51e3f commit ef87b26

File tree

10 files changed

+160
-20
lines changed

10 files changed

+160
-20
lines changed

prisma/migrations/20250425134520_truncate_tax_forms/migration.sql

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ ALTER TABLE "user_tax_form_associations" DROP CONSTRAINT "user_tax_form_associat
88
DROP TABLE "user_tax_form_associations";
99
DROP TYPE "tax_form_status";
1010

11-
CREATE TYPE "tax_form_status" AS ENUM ('incomplete', 'submitted', 'reviewed', 'voided');
11+
CREATE TYPE "tax_form_status" AS ENUM ('ACTIVE', 'INACTIVE');
1212

1313
-- CreateTable
1414
CREATE TABLE "user_tax_form_associations" (

prisma/schema.prisma

+2-4
Original file line numberDiff line numberDiff line change
@@ -249,10 +249,8 @@ enum reference_type {
249249
}
250250

251251
enum tax_form_status {
252-
incomplete
253-
submitted
254-
reviewed
255-
voided
252+
ACTIVE
253+
INACTIVE
256254
}
257255

258256
enum transaction_status {

src/api/repository/taxForm.repo.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Injectable } from '@nestjs/common';
2-
import { TaxFormStatus } from 'src/dto/adminWinning.dto';
2+
import { tax_form_status } from '@prisma/client';
33
import { PrismaService } from 'src/shared/global/prisma.service';
44

55
@Injectable()
@@ -16,7 +16,7 @@ export class TaxFormRepository {
1616
const count = await this.prisma.user_tax_form_associations.count({
1717
where: {
1818
user_id: userId,
19-
tax_form_status: TaxFormStatus.Reviewed,
19+
tax_form_status: tax_form_status.ACTIVE,
2020
},
2121
});
2222

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Provider } from '@nestjs/common';
22
import { PaymentHandler } from './payment.handler';
3+
import { TaxFormHandler } from './tax-form.handler';
34
import { getWebhooksEventHandlersProvider } from '../../webhooks.event-handlers.provider';
45

56
export const TrolleyWebhookHandlers: Provider[] = [
@@ -9,9 +10,13 @@ export const TrolleyWebhookHandlers: Provider[] = [
910
),
1011

1112
PaymentHandler,
13+
TaxFormHandler,
1214
{
1315
provide: 'TrolleyWebhookHandlers',
14-
useFactory: (paymentHandler: PaymentHandler) => [paymentHandler],
15-
inject: [PaymentHandler],
16+
inject: [PaymentHandler, TaxFormHandler],
17+
useFactory: (
18+
paymentHandler: PaymentHandler,
19+
taxFormHandler: TaxFormHandler,
20+
) => [paymentHandler, taxFormHandler],
1621
},
1722
];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { Injectable } from '@nestjs/common';
2+
import { WebhookEvent } from '../../webhooks.decorators';
3+
import {
4+
TrolleyTaxFormStatus,
5+
TaxFormStatusUpdatedEvent,
6+
TaxFormStatusUpdatedEventData,
7+
TrolleyWebhookEvent,
8+
} from '../trolley.types';
9+
import { PrismaService } from 'src/shared/global/prisma.service';
10+
import { tax_form_status, trolley_recipient } from '@prisma/client';
11+
12+
@Injectable()
13+
export class TaxFormHandler {
14+
constructor(private readonly prisma: PrismaService) {}
15+
16+
getDbRecipientById(id: string) {
17+
return this.prisma.trolley_recipient.findUnique({
18+
where: { trolley_id: id },
19+
});
20+
}
21+
22+
async createOrUpdateTaxFormAssociation(
23+
taxFormId: string,
24+
recipient: trolley_recipient,
25+
taxFormData: TaxFormStatusUpdatedEventData,
26+
) {
27+
const taxFormStatus =
28+
taxFormData.status === TrolleyTaxFormStatus.Reviewed
29+
? tax_form_status.ACTIVE
30+
: tax_form_status.INACTIVE;
31+
32+
const existingFormAssociation =
33+
await this.prisma.user_tax_form_associations.findFirst({
34+
where: {
35+
user_id: recipient.user_id,
36+
tax_form_id: taxFormId,
37+
tax_form_status: tax_form_status.ACTIVE,
38+
},
39+
});
40+
41+
// voided forms associations are removed from DB
42+
if (
43+
taxFormData.status === TrolleyTaxFormStatus.Voided &&
44+
existingFormAssociation
45+
) {
46+
return this.prisma.user_tax_form_associations.delete({
47+
where: {
48+
id: existingFormAssociation.id,
49+
},
50+
});
51+
}
52+
53+
if (!existingFormAssociation) {
54+
return this.prisma.user_tax_form_associations.create({
55+
data: {
56+
user_id: recipient.user_id,
57+
tax_form_status: taxFormStatus,
58+
date_filed: taxFormData.signedAt,
59+
tax_form_id: taxFormId,
60+
},
61+
});
62+
}
63+
64+
return this.prisma.user_tax_form_associations.update({
65+
where: { id: existingFormAssociation?.id },
66+
data: {
67+
tax_form_status: taxFormStatus,
68+
date_filed: taxFormData.signedAt,
69+
},
70+
});
71+
}
72+
73+
/**
74+
* Handles the "TaxFormStatusUpdated" event by processing the tax form data
75+
* and updating the associated recipient information in the database.
76+
*
77+
* @param payload - The event payload containing the updated tax form data.
78+
* @returns A promise that resolves when the operation is complete.
79+
*
80+
* @remarks
81+
* - If the recipient associated with the tax form cannot be found in the database,
82+
* an error is logged and the operation is terminated.
83+
* - If the recipient is found, the tax form association is created or updated
84+
* in the database.
85+
*/
86+
@WebhookEvent(TrolleyWebhookEvent.taxFormStatusUpdated)
87+
async handleTaxFormStatusUpdated(
88+
payload: TaxFormStatusUpdatedEvent,
89+
): Promise<void> {
90+
const taxFormData = payload.taxForm.data;
91+
const recipient = await this.getDbRecipientById(taxFormData.recipientId);
92+
93+
if (!recipient) {
94+
console.error(
95+
`Recipient not found for recipientId '${taxFormData.recipientId}' in taxForm with id '${taxFormData.taxFormId}'`,
96+
);
97+
return;
98+
}
99+
100+
await this.createOrUpdateTaxFormAssociation(
101+
taxFormData.taxFormId,
102+
recipient,
103+
taxFormData,
104+
);
105+
}
106+
}

src/api/webhooks/trolley/trolley.service.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { trolley_webhook_log, webhook_status } from '@prisma/client';
44
import { PrismaService } from 'src/shared/global/prisma.service';
55
import { ENV_CONFIG } from 'src/config';
66

7-
enum TrolleyHeaders {
7+
export enum TrolleyHeaders {
88
id = 'x-paymentrails-delivery',
99
signature = 'x-paymentrails-signature',
1010
created = 'x-paymentrails-created',
+35
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,41 @@
11
export enum TrolleyWebhookEvent {
22
paymentCreated = 'payment.created',
33
paymentUpdated = 'payment.updated',
4+
taxFormStatusUpdated = 'taxForm.status_updated',
45
}
56

67
export type TrolleyEventHandler = (eventPayload: any) => Promise<unknown>;
8+
9+
export enum TrolleyTaxFormStatus {
10+
Incomplete = 'incomplete',
11+
Submitted = 'submitted',
12+
Reviewed = 'reviewed',
13+
Voided = 'voided',
14+
}
15+
16+
export interface TaxFormStatusUpdatedEventData {
17+
recipientId: string;
18+
taxFormId: string;
19+
status: TrolleyTaxFormStatus;
20+
taxFormType: string;
21+
taxFormAddressCountry: string;
22+
mailingAddressCountry: string | null;
23+
registrationCountry: string | null;
24+
createdAt: string;
25+
signedAt: string;
26+
reviewedAt: string;
27+
reviewedBy: string;
28+
voidedAt: string | null;
29+
voidReason: string | null;
30+
voidedBy: string | null;
31+
tinStatus: string;
32+
}
33+
34+
export interface TaxFormStatusUpdatedEvent {
35+
taxForm: {
36+
previousFields: {
37+
status: TrolleyTaxFormStatus;
38+
};
39+
data: TaxFormStatusUpdatedEventData;
40+
};
41+
}

src/api/webhooks/webhooks.controller.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
ForbiddenException,
77
} from '@nestjs/common';
88
import { ApiTags } from '@nestjs/swagger';
9-
import { TrolleyService } from './trolley/trolley.service';
9+
import { TrolleyHeaders, TrolleyService } from './trolley/trolley.service';
1010
import { Public } from 'src/core/auth/decorators';
1111

1212
@Public()
@@ -39,6 +39,9 @@ export class WebhooksController {
3939

4040
// do not proceed any further if event has already been processed
4141
if (!(await this.trolleyService.validateUnique(request.headers))) {
42+
console.info(
43+
`Webhook event '${request.headers[TrolleyHeaders.id]}' has already been processed!`,
44+
);
4245
return;
4346
}
4447

src/dto/adminWinning.dto.ts

-7
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,6 @@ export enum WinningsCategory {
9292
TASK_COPILOT_PAYMENT = 'TASK_COPILOT_PAYMENT',
9393
}
9494

95-
export enum TaxFormStatus {
96-
Incomplete = 'incomplete',
97-
Submitted = 'submitted',
98-
Reviewed = 'reviewed',
99-
Voided = 'voided',
100-
}
101-
10295
export enum PaymentStatus {
10396
PAID = 'PAID',
10497
ON_HOLD = 'ON_HOLD',

src/dto/taxForm.dto.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { TaxFormStatus } from './adminWinning.dto';
1+
import { tax_form_status } from '@prisma/client';
22

33
export class TaxFormQueryResult {
44
id: string;
55
user_id: string;
66
tax_form_id: string;
77
date_filed: Date;
8-
tax_form_status: TaxFormStatus;
8+
tax_form_status: tax_form_status;
99
}

0 commit comments

Comments
 (0)