feat(bookings): add contract validity window and customs clearing features

- Introduced contract validity days, valid from and valid until fields in the booking model.
- Updated booking acceptance logic to enforce validity window constraints.
- Added customs clearing agent and first/last mile pickup/delivery coordinates to the booking model.
- Implemented LocationPicker component for address selection with map integration using Leaflet.
- Enhanced booking review step to display customs clearing agent details.
- Updated API and DTOs to accommodate new booking fields.
- Added migration scripts for database schema changes.
- Implemented tests for booking acceptance and DTO transformations.
This commit is contained in:
Marshal
2026-06-24 00:21:01 +00:00
parent 07cd7dc111
commit 8ef7641048
31 changed files with 1251 additions and 226 deletions

View File

@@ -0,0 +1,37 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
/**
* Contract validity window. When the backoffice accepts a price-confirmed
* booking, staff define how many days the contract stays valid. The window runs
* from the accept moment (valid_from) through valid_from + N days (valid_until).
* Outside that window the contract is considered expired.
*/
export class AddContractValidityWindow1820000000005
implements MigrationInterface
{
name = 'AddContractValidityWindow1820000000005';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE freight.bookings ADD COLUMN IF NOT EXISTS contract_validity_days integer;`,
);
await queryRunner.query(
`ALTER TABLE freight.bookings ADD COLUMN IF NOT EXISTS contract_valid_from timestamptz;`,
);
await queryRunner.query(
`ALTER TABLE freight.bookings ADD COLUMN IF NOT EXISTS contract_valid_until timestamptz;`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE freight.bookings DROP COLUMN IF EXISTS contract_valid_until;`,
);
await queryRunner.query(
`ALTER TABLE freight.bookings DROP COLUMN IF EXISTS contract_valid_from;`,
);
await queryRunner.query(
`ALTER TABLE freight.bookings DROP COLUMN IF EXISTS contract_validity_days;`,
);
}
}

View File

@@ -0,0 +1,41 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
/**
* Booking now captures:
* - customs clearing as an explicit flag + the customs clearing agent name
* (shown when the service includes customs), and
* - first/last-mile pickup & delivery coordinates (lat/lng) alongside the
* existing address text, so the map picker can store and restore the pin.
*
* Shipping line is no longer collected from the booking form; the column stays
* for historical data and the (now dormant) shipping-line pricing trigger.
*/
export class AddCustomsAgentAndMileCoordinates1820000000006
implements MigrationInterface
{
name = 'AddCustomsAgentAndMileCoordinates1820000000006';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE freight.bookings
ADD COLUMN IF NOT EXISTS first_mile_pickup_lat numeric(10,7) NULL,
ADD COLUMN IF NOT EXISTS first_mile_pickup_lng numeric(10,7) NULL,
ADD COLUMN IF NOT EXISTS last_mile_delivery_lat numeric(10,7) NULL,
ADD COLUMN IF NOT EXISTS last_mile_delivery_lng numeric(10,7) NULL,
ADD COLUMN IF NOT EXISTS customs_clearing_enabled boolean NOT NULL DEFAULT false,
ADD COLUMN IF NOT EXISTS customs_clearing_agent varchar(200) NULL;
`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE freight.bookings
DROP COLUMN IF EXISTS customs_clearing_agent,
DROP COLUMN IF EXISTS customs_clearing_enabled,
DROP COLUMN IF EXISTS last_mile_delivery_lng,
DROP COLUMN IF EXISTS last_mile_delivery_lat,
DROP COLUMN IF EXISTS first_mile_pickup_lng,
DROP COLUMN IF EXISTS first_mile_pickup_lat;
`);
}
}