mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 04:08:11 +00:00
feat: Implement general contract booking orders functionality
- Add DTOs for creating booking orders and viewing contract quantities. - Create entities for booking orders and booking order lines. - Implement service for managing general contract operations, including activation after payment and retrieving quantity lines. - Develop UI components for contract detail and list pages, including order placement dialog. - Integrate API service for booking orders, enabling listing and creating orders against contracts. - Enhance contract status display and quantity pool visualization in the UI.
This commit is contained in:
@@ -34,6 +34,11 @@ import {
|
||||
RefundDto,
|
||||
} from "./payments.dto";
|
||||
import { BookingBatchService } from "../train-scheduling/booking-batch.service";
|
||||
import { DropdownSettingsService } from "../dropdown-settings/dropdown-settings.service";
|
||||
|
||||
/** Setting code holding the global ordering window (months) for general contracts. */
|
||||
const CONTRACT_PERIOD_SETTING_CODE = "general_contract_period";
|
||||
const DEFAULT_CONTRACT_PERIOD_MONTHS = 3;
|
||||
|
||||
const STATUS_MAP: Record<string, ProviderPaymentStatus> = {
|
||||
"action-required": ProviderPaymentStatus.REQUIRES_ACTION,
|
||||
@@ -54,8 +59,23 @@ export class PaymentService {
|
||||
private readonly paymentClient: PaymentClientService,
|
||||
@Inject(forwardRef(() => BookingBatchService))
|
||||
private readonly bookingBatchService: BookingBatchService,
|
||||
private readonly dropdownSettings: DropdownSettingsService,
|
||||
) { }
|
||||
|
||||
/** Configured general-contract ordering window in months (defaults to 3). */
|
||||
private async contractPeriodMonths(): Promise<number> {
|
||||
try {
|
||||
const setting = await this.dropdownSettings.getByCode(
|
||||
CONTRACT_PERIOD_SETTING_CODE,
|
||||
);
|
||||
const months = Number(setting.children?.[0]?.value);
|
||||
if (Number.isFinite(months) && months > 0) return months;
|
||||
} catch {
|
||||
// Setting not seeded — fall back to the default.
|
||||
}
|
||||
return DEFAULT_CONTRACT_PERIOD_MONTHS;
|
||||
}
|
||||
|
||||
async getAll(filters: {
|
||||
search?: string;
|
||||
status?: string;
|
||||
@@ -293,15 +313,44 @@ export class PaymentService {
|
||||
|
||||
const paidAt = input.paidAt ?? new Date();
|
||||
|
||||
// A general contract is paid once, up front; it does NOT enter the train
|
||||
// queue (nothing has been ordered yet). Instead it becomes ACTIVE and
|
||||
// opens its ordering window. Orders placed later spawn their own paid
|
||||
// child bookings that go through the normal pipeline.
|
||||
const booking = await this.datasource
|
||||
.getRepository(Booking)
|
||||
.findOne({ where: { id: input.bookingId } });
|
||||
const isGeneralContract = booking?.bookingType === "GENERAL_CONTRACT";
|
||||
|
||||
let contractExpiresAt: Date | null = null;
|
||||
if (isGeneralContract) {
|
||||
const months = await this.contractPeriodMonths();
|
||||
contractExpiresAt = new Date(paidAt);
|
||||
contractExpiresAt.setMonth(contractExpiresAt.getMonth() + months);
|
||||
}
|
||||
|
||||
await this.datasource.transaction(async (mg) => {
|
||||
await mg.update(
|
||||
PaymentEntity,
|
||||
{ id: intent.id },
|
||||
{ status: "success", paidAt, transactionId: input.providerTxnId ?? intent.transactionId },
|
||||
);
|
||||
await mg.update(Booking, { id: input.bookingId }, { paymentStatus: "PAID" ,status:"PAID"});
|
||||
await mg.update(
|
||||
Booking,
|
||||
{ id: input.bookingId },
|
||||
isGeneralContract
|
||||
? { paymentStatus: "PAID", status: "CONTRACT_ACTIVE", expiresAt: contractExpiresAt }
|
||||
: { paymentStatus: "PAID", status: "PAID" },
|
||||
);
|
||||
});
|
||||
|
||||
if (isGeneralContract) {
|
||||
this.logger.log(
|
||||
`General contract ${booking?.reference ?? input.bookingId} ACTIVE — ordering open until ${contractExpiresAt?.toISOString()}`,
|
||||
);
|
||||
return { alreadyFinalized: false };
|
||||
}
|
||||
|
||||
try {
|
||||
await this.bookingBatchService.ensurePaidBookingAllocated(input.bookingId);
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user