mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 10:52:53 +00:00
103 lines
3.8 KiB
TypeScript
103 lines
3.8 KiB
TypeScript
import { DynamicModule, Module } from "@nestjs/common";
|
|
import { ConfigService } from "@nestjs/config";
|
|
import { TypeOrmModule } from "@nestjs/typeorm";
|
|
import { RabbitMQModule } from "@golevelup/nestjs-rabbitmq";
|
|
import { PAYMENT_EVENTS_DLX, PAYMENT_EVENTS_EXCHANGE } from "@edr/types";
|
|
|
|
import { CutoverModule } from "../cutover/cutover.module";
|
|
import { RevenueMapping } from "./entities/revenue-mapping.entity";
|
|
import { InboundEvent } from "./entities/inbound-event.entity";
|
|
import {
|
|
InboundEventsRepository,
|
|
RevenueMappingsRepository,
|
|
} from "./revenue.repository";
|
|
import { RevenueMappingsService } from "./revenue-mappings.service";
|
|
import { RevenueProjectionService } from "./revenue-projection.service";
|
|
import { RevenuePostingService } from "./revenue-posting.service";
|
|
import { RevenueController } from "./revenue.controller";
|
|
import {
|
|
FINANCE_PAYMENT_BINDING,
|
|
FINANCE_PAYMENT_DLQ,
|
|
PaymentEventsConsumer,
|
|
} from "./payment-events.consumer";
|
|
import { AccountsModule } from "../accounts/accounts.module";
|
|
import { JournalsModule } from "../journals/journals.module";
|
|
|
|
/**
|
|
* The broker is OPTIONAL.
|
|
*
|
|
* Without `PAYMENT_RABBITMQ_URL` this returns nothing and the app boots with
|
|
* every screen and projection working — only live payment ingest is off. That
|
|
* matters because Finance is useful long before it is wired to a broker (the
|
|
* chart, journals, periods and projections need no messaging at all), and
|
|
* because a developer without RabbitMQ should not be blocked from running it.
|
|
*
|
|
* Same gate freight and passenger use, so all three behave alike.
|
|
*/
|
|
function rabbitMQImport(): DynamicModule[] {
|
|
if (!process.env.PAYMENT_RABBITMQ_URL) return [];
|
|
|
|
return [
|
|
RabbitMQModule.forRootAsync({
|
|
inject: [ConfigService],
|
|
useFactory: (config: ConfigService) => ({
|
|
uri:
|
|
config.get<string>("rabbitmq.url") ??
|
|
process.env.PAYMENT_RABBITMQ_URL ??
|
|
"",
|
|
exchanges: [
|
|
{ name: PAYMENT_EVENTS_EXCHANGE, type: "topic", options: { durable: true } },
|
|
{ name: PAYMENT_EVENTS_DLX, type: "topic", options: { durable: true } },
|
|
],
|
|
// The dead-letter queue is declared HERE, not by @RabbitSubscribe —
|
|
// that decorator only declares the main queue and its DLX pointer. With
|
|
// no bound DLQ, a dead-lettered message is silently discarded by the
|
|
// broker. Freight and passenger declare theirs the same way.
|
|
queues: [
|
|
{
|
|
name: FINANCE_PAYMENT_DLQ,
|
|
exchange: PAYMENT_EVENTS_DLX,
|
|
routingKey: FINANCE_PAYMENT_BINDING,
|
|
options: { durable: true },
|
|
},
|
|
],
|
|
prefetchCount: Number(process.env.PAYMENT_EVENTS_PREFETCH ?? 10),
|
|
// Do not block boot on the broker: the API must still serve its
|
|
// read-only screens when messaging is down.
|
|
connectionInitOptions: { wait: false },
|
|
}),
|
|
}),
|
|
];
|
|
}
|
|
|
|
@Module({
|
|
imports: [
|
|
TypeOrmModule.forFeature([RevenueMapping, InboundEvent]),
|
|
AccountsModule,
|
|
JournalsModule,
|
|
// For the cutover boundary: a payment settled before go-live is already
|
|
// inside the opening balances and must not post again.
|
|
CutoverModule,
|
|
...rabbitMQImport(),
|
|
],
|
|
controllers: [RevenueController],
|
|
providers: [
|
|
RevenueMappingsRepository,
|
|
InboundEventsRepository,
|
|
RevenueMappingsService,
|
|
RevenueProjectionService,
|
|
RevenuePostingService,
|
|
// Registered unconditionally. Without RabbitMQModule the @RabbitSubscribe
|
|
// decorator is inert, so the class is simply never invoked — and it stays
|
|
// directly callable, which is how the consumer's idempotency is tested
|
|
// without standing up a broker.
|
|
PaymentEventsConsumer,
|
|
],
|
|
exports: [
|
|
RevenueProjectionService,
|
|
RevenuePostingService,
|
|
RevenueMappingsRepository,
|
|
],
|
|
})
|
|
export class RevenueModule {}
|