mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 12:30:58 +00:00
feat(billing): USD offline bank-transfer payments
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
import { ContractsRepository } from './contracts.repository';
|
||||
|
||||
/**
|
||||
* A ONE_TIME contract stops blocking a duplicate request only once its booking
|
||||
* is PAID. The existing duplicate-guard spec stubs the repository out, so the
|
||||
* candidate SQL itself is unchecked there — this pins the predicate.
|
||||
*/
|
||||
describe('findDuplicateCandidates ONE_TIME paid gate', () => {
|
||||
const candidateSql = (): string => {
|
||||
const conditions: string[] = [];
|
||||
const qb = {
|
||||
leftJoinAndSelect: () => qb,
|
||||
where: () => qb,
|
||||
andWhere: (condition: string) => {
|
||||
if (typeof condition === 'string') conditions.push(condition);
|
||||
return qb;
|
||||
},
|
||||
getMany: async () => [],
|
||||
};
|
||||
|
||||
const repository = new ContractsRepository(
|
||||
{ createQueryBuilder: () => qb } as never,
|
||||
{} as never,
|
||||
);
|
||||
|
||||
void repository.findDuplicateCandidates('company-1', 'svc-1');
|
||||
return conditions.join(' AND ');
|
||||
};
|
||||
|
||||
it('spends the contract on payment, not on the booking row existing', () => {
|
||||
const sql = candidateSql();
|
||||
|
||||
expect(sql).toContain("contract.contract_kind <> 'ONE_TIME'");
|
||||
// The gate: an unpaid booking must NOT free the lane.
|
||||
expect(sql).toContain("b.payment_status = 'PAID'");
|
||||
expect(sql).toContain('b.deleted_at IS NULL');
|
||||
});
|
||||
});
|
||||
@@ -104,15 +104,19 @@ export class ContractsRepository extends BaseRepository<Contract> {
|
||||
.andWhere('contract.status NOT IN (:...terminal)', {
|
||||
terminal: TERMINAL_CONTRACT_STATUSES,
|
||||
})
|
||||
// A ONE_TIME contract allows a single booking, so once that booking
|
||||
// exists the contract is spent and can never carry another shipment.
|
||||
// A ONE_TIME contract allows a single booking, so once that booking is
|
||||
// PAID the contract is spent and can never carry another shipment.
|
||||
// Without this it kept blocking new requests on the same service type +
|
||||
// route until its validity lapsed — locking a customer out of a lane for
|
||||
// the rest of the term after one completed shipment.
|
||||
// Payment is the gate, not the booking row: a DRAFT or abandoned unpaid
|
||||
// booking must keep the contract blocking, otherwise a customer holds an
|
||||
// unpaid booking and requests an identical contract alongside it.
|
||||
.andWhere(
|
||||
`(contract.contract_kind <> 'ONE_TIME' OR NOT EXISTS (
|
||||
SELECT 1 FROM freight.bookings b
|
||||
WHERE b.contract_id = contract.id AND b.deleted_at IS NULL
|
||||
AND b.payment_status = 'PAID'
|
||||
))`,
|
||||
)
|
||||
.getMany();
|
||||
|
||||
Reference in New Issue
Block a user