allow the custoemr to book while the payment window time

This commit is contained in:
Marshal
2026-08-04 08:10:00 +00:00
parent 05450d4693
commit 2eb752e8c2
4 changed files with 0 additions and 59 deletions

View File

@@ -927,13 +927,6 @@ export class BookingTransitionService {
"OPERATION_CHANGES_REQUESTED", "OPERATION_CHANGES_REQUESTED",
]); ]);
// A company sitting on another unpaid hold commits nothing new — this is
// the moment export capacity locks, so the lock applies here too.
// Government bookings allocate without paying and are exempt.
if (!booking.isGovernment) {
await this.bookingsService.assertNoUnpaidHold(booking.companyId);
}
// A bare initiated instance (clearance-first flow) carries no cargo or // A bare initiated instance (clearance-first flow) carries no cargo or
// price — it must go through the contract completion endpoint, which // price — it must go through the contract completion endpoint, which
// persists cargo, prices, invoices and only then lands here itself. // persists cargo, prices, invoices and only then lands here itself.

View File

@@ -1396,16 +1396,6 @@ export class BookingsRepository extends BaseRepository<Booking> {
.getMany(); .getMany();
} }
/** Open unpaid holds (wagons reserved, pay window running) for a company. */
countUnpaidHoldsForCompany(companyId: string): Promise<number> {
return this.repository.count({
where: {
companyId,
status: In(['SELECTED_FOR_BATCH', 'AWAITING_PAYMENT']),
},
});
}
/** Bookings currently reserved (SELECTED_FOR_BATCH) against a schedule. */ /** Bookings currently reserved (SELECTED_FOR_BATCH) against a schedule. */
findReservedForSchedule(scheduleId: string): Promise<Booking[]> { findReservedForSchedule(scheduleId: string): Promise<Booking[]> {
return this.repository return this.repository

View File

@@ -909,24 +909,6 @@ export class BookingsService {
return result.booking; return result.booking;
} }
/**
* A company with an open unpaid hold (SELECTED_FOR_BATCH — wagons reserved,
* pay window running) may not take more capacity until it pays or the hold
* dies: otherwise one customer can lock a train's wagons over and over
* without ever paying. EXPIRED / CANCELLED holds free the lock.
*/
async assertNoUnpaidHold(companyId?: string | null): Promise<void> {
if (!companyId) return;
const holds =
await this.bookingsRepository.countUnpaidHoldsForCompany(companyId);
if (holds > 0) {
throw new ConflictException(
'You already have a booking waiting for payment. Pay it or cancel it ' +
'before making a new booking.',
);
}
}
/** Create a new freight booking. */ /** Create a new freight booking. */
async create( async create(
dto: CreateBookingDto, dto: CreateBookingDto,
@@ -989,10 +971,6 @@ export class BookingsService {
companyId = company.id; companyId = company.id;
} }
// Government bookings allocate without paying, so the unpaid-hold lock
// only applies to commercial companies.
if (!isGovernment) await this.assertNoUnpaidHold(companyId);
if (dto.trainScheduleId) { if (dto.trainScheduleId) {
// Staff manual pin: the schedule must be OPEN and on the same route. // Staff manual pin: the schedule must be OPEN and on the same route.
const schedule = await this.dataSource const schedule = await this.dataSource

View File

@@ -109,23 +109,6 @@ export class ContractBookingService {
private readonly bookingTransitionService: BookingTransitionService, private readonly bookingTransitionService: BookingTransitionService,
) {} ) {}
/**
* Mirrors BookingsService.assertNoUnpaidHold for the contract booking paths:
* a company sitting on an unpaid hold (SELECTED_FOR_BATCH) books nothing new
* until it pays or the hold dies.
*/
private async assertNoUnpaidHold(companyId?: string | null): Promise<void> {
if (!companyId) return;
const holds =
await this.bookingsRepository.countUnpaidHoldsForCompany(companyId);
if (holds > 0) {
throw new ConflictException(
'You already have a booking waiting for payment. Pay it or cancel it ' +
'before making a new booking.',
);
}
}
async createUnderContract( async createUnderContract(
contractId: string, contractId: string,
dto: CreateBookingUnderContractDto, dto: CreateBookingUnderContractDto,
@@ -186,8 +169,6 @@ export class ContractBookingService {
// remainder; the customer cannot start any other booking on the contract. // remainder; the customer cannot start any other booking on the contract.
// If the remainder splits again the same rule repeats until the cap is // If the remainder splits again the same rule repeats until the cap is
// exhausted and the contract completes. // exhausted and the contract completes.
await this.assertNoUnpaidHold(contract.companyId);
if (contract.contractKind === 'ONE_TIME') { if (contract.contractKind === 'ONE_TIME') {
if (await this.hasSplitBooking(contractId)) { if (await this.hasSplitBooking(contractId)) {
await this.assertExactRemainder(contract, dto); await this.assertExactRemainder(contract, dto);
@@ -475,7 +456,6 @@ export class ContractBookingService {
); );
} }
} }
await this.assertNoUnpaidHold(contract.companyId);
const route = await this.resolveRoute(contract, dto.contractRouteId); const route = await this.resolveRoute(contract, dto.contractRouteId);