Merge pull request #1160 from Tria-plc/freight_feature/usermanagement

fix permission
This commit is contained in:
marshal
2026-08-07 14:20:03 +03:00
committed by GitHub

View File

@@ -1074,16 +1074,23 @@ export class ContractsController {
// ── Booking under contract (Path A customer / Path B GL ET) ────────────────
@Post(':id/bookings')
@BookingStaff(FREIGHT_PERMS.contracts.createBooking)
// Path A is a customer flow — both audiences must reach the service, whose
// assertGate decides per role. Staff still need contracts:create_booking.
@MixedAudience(FREIGHT_PERMS.contracts.createBooking)
@ApiOperation({
summary:
'Create a shipment booking under a contract — Path A (customer) or Path B (GL Ethiopia).',
})
createBooking(
async createBooking(
@Param('id', ParseUUIDPipe) id: string,
@Body() dto: CreateBookingUnderContractDto,
@CurrentUser() user: AuthUserPayload,
@CurrentUser() user: TCurrentUser & { sub?: string },
) {
// Customer callers may only book on their own contract.
if (!hasFreightPermission(user, FREIGHT_PERMS.contracts.createBooking)) {
const contract = await this.contractsService.findById(id);
await this.contractsService.assertCustomerCanAccessContract(user?.id, contract);
}
// The service decides the execution path from the contract:
// Path A (customs disabled) → customer/staff create; status checks apply.
// Path B (customs enabled) → GL Ethiopia only, once clearance is ready.
@@ -1096,16 +1103,25 @@ export class ContractsController {
}
@Post(':id/bookings/initiate')
@BookingStaff(FREIGHT_PERMS.contracts.createBooking)
// Customer initiates their own ONE_TIME instance; GL initiates on customs
// contracts — the service's assertGate decides per role, so both audiences
// must reach it. Staff still need contracts:create_booking.
@MixedAudience(FREIGHT_PERMS.contracts.createBooking)
@ApiOperation({
summary:
'Initiate a bare booking instance under an import/export contract (ONE_TIME or GENERAL) — no cargo, no date; enters per-booking clearance (AWAITING_DOCUMENTS). ONE_TIME customs instances are opened by the customer (or GL); GENERAL customs comes from a shipment request.',
})
initiateBooking(
async initiateBooking(
@Param('id', ParseUUIDPipe) id: string,
@Body() dto: CreateBookingUnderContractDto,
@CurrentUser() user: AuthUserPayload,
@CurrentUser() user: TCurrentUser & { sub?: string },
) {
// Customer callers may only initiate on their own contract; the service's
// assertGate then decides what a customer may do on it.
if (!hasFreightPermission(user, FREIGHT_PERMS.contracts.createBooking)) {
const contract = await this.contractsService.findById(id);
await this.contractsService.assertCustomerCanAccessContract(user?.id, contract);
}
return this.contractBookingService.initiateUnderContract(
id,
{ contractRouteId: dto?.contractRouteId },
@@ -1115,17 +1131,24 @@ export class ContractsController {
}
@Post(':id/bookings/:bookingId/complete')
@BookingStaff(FREIGHT_PERMS.contracts.createBooking)
// Customers complete their own initiated (non-customs) instances; the
// service keeps customs completion GL-only via the actor's permissions.
@MixedAudience(FREIGHT_PERMS.contracts.createBooking)
@ApiOperation({
summary:
'Complete an initiated booking after Operations finalized its clearance — cargo + binding day, window and departure checks, pricing and invoicing.',
})
completeBooking(
async completeBooking(
@Param('id', ParseUUIDPipe) id: string,
@Param('bookingId', ParseUUIDPipe) bookingId: string,
@Body() dto: CreateBookingUnderContractDto,
@CurrentUser() user: AuthUserPayload,
@CurrentUser() user: TCurrentUser & { sub?: string },
) {
// Customer callers may only complete bookings on their own contract.
if (!hasFreightPermission(user, FREIGHT_PERMS.contracts.createBooking)) {
const contract = await this.contractsService.findById(id);
await this.contractsService.assertCustomerCanAccessContract(user?.id, contract);
}
// Customs (Path B) instances may only be completed by GL Ethiopia — the
// service checks the actor's contracts:create_booking permission.
return this.contractBookingService.completeUnderContract(