mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
feat(warehouse): stamp actor as display name instead of UUID
Add actorLabel(user) — resolves the authenticated user to a readable name (name → username → email → id) — and use it for the performed_by audit stamp on every warehouse action, so the activity log shows a person, not a UUID. The freight DB has no users table to join, so the name is stamped at write time. approve-delivery keeps the raw user id (it is an id argument, not the audit label). Existing rows keep their prior value; this applies going forward. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
import type { TCurrentUser } from '@tria-plc/api-common/modules/auth/types/current-user.type';
|
||||
|
||||
/**
|
||||
* Human-readable actor label for audit stamps (`performed_by` / `moved_by`).
|
||||
* Prefers a display name, then username/email, so the activity log shows a
|
||||
* person rather than a UUID. Returns undefined when there is no authenticated
|
||||
* user (internal/cron calls), letting callers fall back to their prior value.
|
||||
*/
|
||||
export function actorLabel(user?: TCurrentUser | null): string | undefined {
|
||||
if (!user) return undefined;
|
||||
const name = user.name?.en?.trim() || user.name?.am?.trim();
|
||||
return name || user.username || user.email || user.id || undefined;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import type { Response } from 'express';
|
||||
import { CurrentUser } from '@edr/api-common';
|
||||
import type { TCurrentUser } from '@tria-plc/api-common/modules/auth/types/current-user.type';
|
||||
|
||||
import { actorLabel } from './current-actor.util';
|
||||
import { BookingStaff } from '../../common/booking-guards';
|
||||
import { FREIGHT_PERMS } from '../../seed/freight-permissions.registry';
|
||||
import { BulkReceiveDto } from './dto/bulk-receive.dto';
|
||||
@@ -130,7 +131,7 @@ export class WarehouseInventoryController {
|
||||
@BookingStaff(FREIGHT_PERMS.warehouseInventory.receive)
|
||||
@ApiOperation({ summary: 'Bulk-receive selected eligible PAID bookings into a location' })
|
||||
receiveBulk(@Body() dto: BulkReceiveDto, @CurrentUser() user: TCurrentUser) {
|
||||
dto.performedBy = user?.id ?? dto.performedBy;
|
||||
dto.performedBy = actorLabel(user) ?? dto.performedBy;
|
||||
return this.inventoryService.bulkReceive(dto);
|
||||
}
|
||||
|
||||
@@ -178,14 +179,14 @@ export class WarehouseInventoryController {
|
||||
@Body() dto: { inventoryIds: string[]; performedBy?: string },
|
||||
@CurrentUser() user: TCurrentUser,
|
||||
) {
|
||||
return this.inventoryService.loadItemsOntoTrain(scheduleId, dto.inventoryIds ?? [], user?.id ?? dto.performedBy);
|
||||
return this.inventoryService.loadItemsOntoTrain(scheduleId, dto.inventoryIds ?? [], actorLabel(user) ?? dto.performedBy);
|
||||
}
|
||||
|
||||
@Post('bulk-dispatch-export')
|
||||
@BookingStaff(FREIGHT_PERMS.warehouseInventory.dispatch)
|
||||
@ApiOperation({ summary: 'Bulk-dispatch loaded EXPORT inventory (LOADED → DISPATCHED)' })
|
||||
bulkDispatchExport(@Body() dto: { inventoryIds: string[]; performedBy?: string }, @CurrentUser() user: TCurrentUser) {
|
||||
return this.inventoryService.bulkDispatchExport(dto.inventoryIds ?? [], user?.id ?? dto.performedBy);
|
||||
return this.inventoryService.bulkDispatchExport(dto.inventoryIds ?? [], actorLabel(user) ?? dto.performedBy);
|
||||
}
|
||||
|
||||
@Post('bulk-mark-inspected')
|
||||
@@ -213,7 +214,7 @@ export class WarehouseInventoryController {
|
||||
@Body('performedBy') performedBy: string | undefined,
|
||||
@CurrentUser() user: TCurrentUser,
|
||||
) {
|
||||
return this.inventoryService.gateClearance(id, user?.id ?? performedBy);
|
||||
return this.inventoryService.gateClearance(id, actorLabel(user) ?? performedBy);
|
||||
}
|
||||
|
||||
@Get('import/arrive-queue')
|
||||
@@ -241,7 +242,7 @@ export class WarehouseInventoryController {
|
||||
}, @CurrentUser() user: TCurrentUser) {
|
||||
return this.inventoryService.autoUnloadArrivedBookings(
|
||||
dto.scheduleId,
|
||||
user?.id ?? dto.performedBy,
|
||||
actorLabel(user) ?? dto.performedBy,
|
||||
dto.warehouseId,
|
||||
dto.assignments,
|
||||
);
|
||||
@@ -284,7 +285,7 @@ export class WarehouseInventoryController {
|
||||
@BookingStaff(FREIGHT_PERMS.warehouseInventory.unload)
|
||||
@ApiOperation({ summary: 'Unload all eligible export items assigned to an arrived Djibouti-side train' })
|
||||
autoUnloadExportAtDjibouti(@Body() dto: { scheduleId: string; performedBy?: string }, @CurrentUser() user: TCurrentUser) {
|
||||
return this.inventoryService.autoUnloadExportAtDjibouti(dto.scheduleId, user?.id ?? dto.performedBy);
|
||||
return this.inventoryService.autoUnloadExportAtDjibouti(dto.scheduleId, actorLabel(user) ?? dto.performedBy);
|
||||
}
|
||||
|
||||
@Get('import/pickup-ready-queue')
|
||||
@@ -312,7 +313,7 @@ export class WarehouseInventoryController {
|
||||
@BookingStaff(FREIGHT_PERMS.warehouseInventory.receive)
|
||||
@ApiOperation({ summary: 'Receive inventory at a warehouse location' })
|
||||
receive(@Body() dto: ReceiveWarehouseInventoryDto, @CurrentUser() user: TCurrentUser) {
|
||||
dto.performedBy = user?.id ?? dto.performedBy;
|
||||
dto.performedBy = actorLabel(user) ?? dto.performedBy;
|
||||
return this.inventoryService.receive(dto);
|
||||
}
|
||||
|
||||
@@ -320,7 +321,7 @@ export class WarehouseInventoryController {
|
||||
@BookingStaff(FREIGHT_PERMS.warehouseInventory.move)
|
||||
@ApiOperation({ summary: 'Reserve stored inventory for a PAID booking' })
|
||||
reserve(@Body() dto: ReserveInventoryDto, @CurrentUser() user: TCurrentUser) {
|
||||
dto.performedBy = user?.id ?? dto.performedBy;
|
||||
dto.performedBy = actorLabel(user) ?? dto.performedBy;
|
||||
return this.inventoryService.reserve(dto);
|
||||
}
|
||||
|
||||
@@ -356,7 +357,7 @@ export class WarehouseInventoryController {
|
||||
@BookingStaff(FREIGHT_PERMS.warehouseInventory.move)
|
||||
@ApiOperation({ summary: 'Mark received inventory as STORED (optional explicit warehouse/yard/zone)' })
|
||||
store(@Param('id', ParseUUIDPipe) id: string, @Body() dto: StoreInventoryDto, @CurrentUser() user: TCurrentUser) {
|
||||
return this.inventoryService.store(id, user?.id ?? dto.performedBy, dto);
|
||||
return this.inventoryService.store(id, actorLabel(user) ?? dto.performedBy, dto);
|
||||
}
|
||||
|
||||
@Post(':id/ready-for-loading')
|
||||
@@ -367,7 +368,7 @@ export class WarehouseInventoryController {
|
||||
@Body('performedBy') performedBy: string | undefined,
|
||||
@CurrentUser() user: TCurrentUser,
|
||||
) {
|
||||
return this.inventoryService.readyForLoading(id, user?.id ?? performedBy);
|
||||
return this.inventoryService.readyForLoading(id, actorLabel(user) ?? performedBy);
|
||||
}
|
||||
|
||||
@Post(':id/load')
|
||||
@@ -385,7 +386,7 @@ export class WarehouseInventoryController {
|
||||
@Body('performedBy') performedBy: string | undefined,
|
||||
@CurrentUser() user: TCurrentUser,
|
||||
) {
|
||||
return this.inventoryService.readyForPickup(id, user?.id ?? performedBy);
|
||||
return this.inventoryService.readyForPickup(id, actorLabel(user) ?? performedBy);
|
||||
}
|
||||
|
||||
@Post(':id/release')
|
||||
@@ -514,7 +515,7 @@ export class WarehouseInventoryController {
|
||||
@BookingStaff(FREIGHT_PERMS.warehouseInventory.deliver)
|
||||
@ApiOperation({ summary: 'Deliver import goods to the customer + capture proof of delivery' })
|
||||
deliver(@Param('id', ParseUUIDPipe) id: string, @Body() dto: DeliverInventoryDto, @CurrentUser() user: TCurrentUser) {
|
||||
dto.performedBy = user?.id ?? dto.performedBy;
|
||||
dto.performedBy = actorLabel(user) ?? dto.performedBy;
|
||||
return this.inventoryService.deliver(id, dto);
|
||||
}
|
||||
|
||||
@@ -526,6 +527,6 @@ export class WarehouseInventoryController {
|
||||
@Body('performedBy') performedBy: string | undefined,
|
||||
@CurrentUser() user: TCurrentUser,
|
||||
) {
|
||||
return this.inventoryService.dispatch(id, user?.id ?? performedBy);
|
||||
return this.inventoryService.dispatch(id, actorLabel(user) ?? performedBy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { Response } from 'express';
|
||||
import { CurrentUser } from '@edr/api-common';
|
||||
import type { TCurrentUser } from '@tria-plc/api-common/modules/auth/types/current-user.type';
|
||||
|
||||
import { actorLabel } from './current-actor.util';
|
||||
import { BookingStaff } from '../../common/booking-guards';
|
||||
import { FREIGHT_PERMS } from '../../seed/freight-permissions.registry';
|
||||
import { PayInvoiceDto as GatewayPayInvoiceDto } from '../billing/dto/pay-invoice.dto';
|
||||
@@ -20,7 +21,7 @@ export class WarehouseInvoiceController {
|
||||
@BookingStaff(FREIGHT_PERMS.warehouseFeeInvoices.generate)
|
||||
@ApiOperation({ summary: 'Generate a warehouse fee invoice from Batch 5 fee calculation' })
|
||||
generate(@Param('id', ParseUUIDPipe) id: string, @Body() dto: GenerateInvoiceDto, @CurrentUser() user: TCurrentUser) {
|
||||
dto.performedBy = user?.id ?? dto.performedBy;
|
||||
dto.performedBy = actorLabel(user) ?? dto.performedBy;
|
||||
return this.invoiceService.generateForInventory(id, dto);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user