@freighttech/shipment-tracking 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/NOTICE +25 -0
- package/dist/generated/entities/bic_config/index.js +21 -0
- package/dist/generated/entities/bic_config/index.js.map +7 -0
- package/dist/generated/entities/carrier_config/index.js +27 -0
- package/dist/generated/entities/carrier_config/index.js.map +7 -0
- package/dist/generated/entities/location_override/index.js +33 -0
- package/dist/generated/entities/location_override/index.js.map +7 -0
- package/dist/generated/entities/shipment/index.js +73 -0
- package/dist/generated/entities/shipment/index.js.map +7 -0
- package/dist/generated/entities/ships_go_config/index.js +31 -0
- package/dist/generated/entities/ships_go_config/index.js.map +7 -0
- package/dist/generated/entities/tracking_event/index.js +101 -0
- package/dist/generated/entities/tracking_event/index.js.map +7 -0
- package/dist/generated/entities/tracking_job/index.js +47 -0
- package/dist/generated/entities/tracking_job/index.js.map +7 -0
- package/dist/generated/entities/webhook/index.js +25 -0
- package/dist/generated/entities/webhook/index.js.map +7 -0
- package/dist/generated/entities/webhook_delivery/index.js +25 -0
- package/dist/generated/entities/webhook_delivery/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +21 -0
- package/dist/generated/entities.ids.generated.js.map +7 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +7 -0
- package/dist/modules/shipment_tracking/acl.js +22 -0
- package/dist/modules/shipment_tracking/acl.js.map +7 -0
- package/dist/modules/shipment_tracking/api/bic-configs/route.js +176 -0
- package/dist/modules/shipment_tracking/api/bic-configs/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/bic-configs/test/route.js +95 -0
- package/dist/modules/shipment_tracking/api/bic-configs/test/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/cargo-events/route.js +93 -0
- package/dist/modules/shipment_tracking/api/cargo-events/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/carrier-configs/route.js +139 -0
- package/dist/modules/shipment_tracking/api/carrier-configs/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/dashboard/ships-map/route.js +177 -0
- package/dist/modules/shipment_tracking/api/dashboard/ships-map/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/location-overrides/route.js +179 -0
- package/dist/modules/shipment_tracking/api/location-overrides/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/openapi.js +27 -0
- package/dist/modules/shipment_tracking/api/openapi.js.map +7 -0
- package/dist/modules/shipment_tracking/api/shipments/route.js +192 -0
- package/dist/modules/shipment_tracking/api/shipments/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/shipsgo-configs/route.js +185 -0
- package/dist/modules/shipment_tracking/api/shipsgo-configs/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/tracking-jobs/route.js +144 -0
- package/dist/modules/shipment_tracking/api/tracking-jobs/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/tracking-jobs/sync/route.js +107 -0
- package/dist/modules/shipment_tracking/api/tracking-jobs/sync/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/utils.js +12 -0
- package/dist/modules/shipment_tracking/api/utils.js.map +7 -0
- package/dist/modules/shipment_tracking/api/webhooks/deliveries/route.js +76 -0
- package/dist/modules/shipment_tracking/api/webhooks/deliveries/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/webhooks/route.js +135 -0
- package/dist/modules/shipment_tracking/api/webhooks/route.js.map +7 -0
- package/dist/modules/shipment_tracking/api/webhooks/test/route.js +71 -0
- package/dist/modules/shipment_tracking/api/webhooks/test/route.js.map +7 -0
- package/dist/modules/shipment_tracking/assets/carriers/cma_cgm.png +0 -0
- package/dist/modules/shipment_tracking/assets/carriers/cosco.png +0 -0
- package/dist/modules/shipment_tracking/assets/carriers/evergreen.png +0 -0
- package/dist/modules/shipment_tracking/assets/carriers/hapag_lloyd.png +0 -0
- package/dist/modules/shipment_tracking/assets/carriers/maersk.png +0 -0
- package/dist/modules/shipment_tracking/assets/carriers/msc.png +0 -0
- package/dist/modules/shipment_tracking/assets/carriers/one.png +0 -0
- package/dist/modules/shipment_tracking/assets/carriers/oocl.png +0 -0
- package/dist/modules/shipment_tracking/assets/carriers/yang_ming.png +0 -0
- package/dist/modules/shipment_tracking/assets/carriers/zim.png +0 -0
- package/dist/modules/shipment_tracking/assets/index.js +52 -0
- package/dist/modules/shipment_tracking/assets/index.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/carrier-configs/page.js +621 -0
- package/dist/modules/shipment_tracking/backend/carrier-configs/page.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/carrier-configs/page.meta.js +19 -0
- package/dist/modules/shipment_tracking/backend/carrier-configs/page.meta.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/location-overrides/page.js +198 -0
- package/dist/modules/shipment_tracking/backend/location-overrides/page.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/location-overrides/page.meta.js +19 -0
- package/dist/modules/shipment_tracking/backend/location-overrides/page.meta.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/shipment-tracking/page.js +279 -0
- package/dist/modules/shipment_tracking/backend/shipment-tracking/page.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/shipment-tracking/page.meta.js +19 -0
- package/dist/modules/shipment_tracking/backend/shipment-tracking/page.meta.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/tracking-jobs/page.js +10 -0
- package/dist/modules/shipment_tracking/backend/tracking-jobs/page.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/tracking-jobs/page.meta.js +19 -0
- package/dist/modules/shipment_tracking/backend/tracking-jobs/page.meta.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/webhooks/page.js +198 -0
- package/dist/modules/shipment_tracking/backend/webhooks/page.js.map +7 -0
- package/dist/modules/shipment_tracking/backend/webhooks/page.meta.js +19 -0
- package/dist/modules/shipment_tracking/backend/webhooks/page.meta.js.map +7 -0
- package/dist/modules/shipment_tracking/ce.js +52 -0
- package/dist/modules/shipment_tracking/ce.js.map +7 -0
- package/dist/modules/shipment_tracking/cli.js +229 -0
- package/dist/modules/shipment_tracking/cli.js.map +7 -0
- package/dist/modules/shipment_tracking/commands/bic-configs.js +66 -0
- package/dist/modules/shipment_tracking/commands/bic-configs.js.map +7 -0
- package/dist/modules/shipment_tracking/commands/carrier-configs.js +65 -0
- package/dist/modules/shipment_tracking/commands/carrier-configs.js.map +7 -0
- package/dist/modules/shipment_tracking/commands/index.js +9 -0
- package/dist/modules/shipment_tracking/commands/index.js.map +7 -0
- package/dist/modules/shipment_tracking/commands/location-overrides.js +93 -0
- package/dist/modules/shipment_tracking/commands/location-overrides.js.map +7 -0
- package/dist/modules/shipment_tracking/commands/shipments.js +200 -0
- package/dist/modules/shipment_tracking/commands/shipments.js.map +7 -0
- package/dist/modules/shipment_tracking/commands/shipsgo-configs.js +75 -0
- package/dist/modules/shipment_tracking/commands/shipsgo-configs.js.map +7 -0
- package/dist/modules/shipment_tracking/commands/tracking-jobs.js +120 -0
- package/dist/modules/shipment_tracking/commands/tracking-jobs.js.map +7 -0
- package/dist/modules/shipment_tracking/commands/tracking.js +140 -0
- package/dist/modules/shipment_tracking/commands/tracking.js.map +7 -0
- package/dist/modules/shipment_tracking/commands/webhooks.js +115 -0
- package/dist/modules/shipment_tracking/commands/webhooks.js.map +7 -0
- package/dist/modules/shipment_tracking/components/CombinedTimestampCell.js +168 -0
- package/dist/modules/shipment_tracking/components/CombinedTimestampCell.js.map +7 -0
- package/dist/modules/shipment_tracking/components/FacilityLocationInput.js +202 -0
- package/dist/modules/shipment_tracking/components/FacilityLocationInput.js.map +7 -0
- package/dist/modules/shipment_tracking/components/LocationOverrideDrawer.js +501 -0
- package/dist/modules/shipment_tracking/components/LocationOverrideDrawer.js.map +7 -0
- package/dist/modules/shipment_tracking/components/ShipmentDetailsDrawer.js +818 -0
- package/dist/modules/shipment_tracking/components/ShipmentDetailsDrawer.js.map +7 -0
- package/dist/modules/shipment_tracking/components/ShipmentDrawer.js +404 -0
- package/dist/modules/shipment_tracking/components/ShipmentDrawer.js.map +7 -0
- package/dist/modules/shipment_tracking/components/ShipsGoConfigSection.js +200 -0
- package/dist/modules/shipment_tracking/components/ShipsGoConfigSection.js.map +7 -0
- package/dist/modules/shipment_tracking/components/TimestampCell.js +108 -0
- package/dist/modules/shipment_tracking/components/TimestampCell.js.map +7 -0
- package/dist/modules/shipment_tracking/components/TrackingJobDrawer.js +343 -0
- package/dist/modules/shipment_tracking/components/TrackingJobDrawer.js.map +7 -0
- package/dist/modules/shipment_tracking/components/VesselTrackingMap.js +496 -0
- package/dist/modules/shipment_tracking/components/VesselTrackingMap.js.map +7 -0
- package/dist/modules/shipment_tracking/components/WebhookDrawer.js +332 -0
- package/dist/modules/shipment_tracking/components/WebhookDrawer.js.map +7 -0
- package/dist/modules/shipment_tracking/data/entities.js +686 -0
- package/dist/modules/shipment_tracking/data/entities.js.map +7 -0
- package/dist/modules/shipment_tracking/data/validators.js +434 -0
- package/dist/modules/shipment_tracking/data/validators.js.map +7 -0
- package/dist/modules/shipment_tracking/di.js +60 -0
- package/dist/modules/shipment_tracking/di.js.map +7 -0
- package/dist/modules/shipment_tracking/encryption.js +12 -0
- package/dist/modules/shipment_tracking/encryption.js.map +7 -0
- package/dist/modules/shipment_tracking/events.js +87 -0
- package/dist/modules/shipment_tracking/events.js.map +7 -0
- package/dist/modules/shipment_tracking/hooks/useVesselTracking.js +137 -0
- package/dist/modules/shipment_tracking/hooks/useVesselTracking.js.map +7 -0
- package/dist/modules/shipment_tracking/i18n/de.json +327 -0
- package/dist/modules/shipment_tracking/i18n/en.json +327 -0
- package/dist/modules/shipment_tracking/i18n/es.json +327 -0
- package/dist/modules/shipment_tracking/i18n/pl.json +327 -0
- package/dist/modules/shipment_tracking/index.js +15 -0
- package/dist/modules/shipment_tracking/index.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/adapters/cma-cgm.js +66 -0
- package/dist/modules/shipment_tracking/lib/adapters/cma-cgm.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/adapters/cosco-types.js +1 -0
- package/dist/modules/shipment_tracking/lib/adapters/cosco-types.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/adapters/cosco.js +236 -0
- package/dist/modules/shipment_tracking/lib/adapters/cosco.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/adapters/evergreen.js +87 -0
- package/dist/modules/shipment_tracking/lib/adapters/evergreen.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/adapters/hapag-lloyd.js +68 -0
- package/dist/modules/shipment_tracking/lib/adapters/hapag-lloyd.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/adapters/index.js +20 -0
- package/dist/modules/shipment_tracking/lib/adapters/index.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/adapters/maersk.js +84 -0
- package/dist/modules/shipment_tracking/lib/adapters/maersk.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/adapters/msc.js +170 -0
- package/dist/modules/shipment_tracking/lib/adapters/msc.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/adapters/zim.js +85 -0
- package/dist/modules/shipment_tracking/lib/adapters/zim.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/audit-helpers.js +20 -0
- package/dist/modules/shipment_tracking/lib/audit-helpers.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/auth/base64url.js +8 -0
- package/dist/modules/shipment_tracking/lib/auth/base64url.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/auth/oauth-client.js +38 -0
- package/dist/modules/shipment_tracking/lib/auth/oauth-client.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/background.js +15 -0
- package/dist/modules/shipment_tracking/lib/background.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/bic-api-client.js +106 -0
- package/dist/modules/shipment_tracking/lib/bic-api-client.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/carrier-adapter.js +17 -0
- package/dist/modules/shipment_tracking/lib/carrier-adapter.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/current-vessel.js +149 -0
- package/dist/modules/shipment_tracking/lib/current-vessel.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/dcsa-event-mapping.js +124 -0
- package/dist/modules/shipment_tracking/lib/dcsa-event-mapping.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/dcsa-event-parser.js +112 -0
- package/dist/modules/shipment_tracking/lib/dcsa-event-parser.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/dcsa-params.js +16 -0
- package/dist/modules/shipment_tracking/lib/dcsa-params.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/empty-return.js +48 -0
- package/dist/modules/shipment_tracking/lib/empty-return.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/location-overrides.js +85 -0
- package/dist/modules/shipment_tracking/lib/location-overrides.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/location-types.js +59 -0
- package/dist/modules/shipment_tracking/lib/location-types.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/logger.js +113 -0
- package/dist/modules/shipment_tracking/lib/logger.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/poi-event-processor.js +183 -0
- package/dist/modules/shipment_tracking/lib/poi-event-processor.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/poi-nats-config.js +52 -0
- package/dist/modules/shipment_tracking/lib/poi-nats-config.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/poi-types.js +43 -0
- package/dist/modules/shipment_tracking/lib/poi-types.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/rate-limiter.js +31 -0
- package/dist/modules/shipment_tracking/lib/rate-limiter.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/route-extraction.js +187 -0
- package/dist/modules/shipment_tracking/lib/route-extraction.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/route-inference.js +106 -0
- package/dist/modules/shipment_tracking/lib/route-inference.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/schedule-generator.js +63 -0
- package/dist/modules/shipment_tracking/lib/schedule-generator.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/shipsgo-client.js +143 -0
- package/dist/modules/shipment_tracking/lib/shipsgo-client.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/shipsgo-mapper.js +204 -0
- package/dist/modules/shipment_tracking/lib/shipsgo-mapper.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/status-machine.js +131 -0
- package/dist/modules/shipment_tracking/lib/status-machine.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/time-extraction.js +41 -0
- package/dist/modules/shipment_tracking/lib/time-extraction.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/timestamp-utils.js +100 -0
- package/dist/modules/shipment_tracking/lib/timestamp-utils.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/vessel-api.js +111 -0
- package/dist/modules/shipment_tracking/lib/vessel-api.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/vessel-cache.js +50 -0
- package/dist/modules/shipment_tracking/lib/vessel-cache.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/vessel-extraction.js +13 -0
- package/dist/modules/shipment_tracking/lib/vessel-extraction.js.map +7 -0
- package/dist/modules/shipment_tracking/lib/webhook-dispatcher.js +62 -0
- package/dist/modules/shipment_tracking/lib/webhook-dispatcher.js.map +7 -0
- package/dist/modules/shipment_tracking/migrations/Migration20260422161208_shipment_tracking.js +48 -0
- package/dist/modules/shipment_tracking/migrations/Migration20260422161208_shipment_tracking.js.map +7 -0
- package/dist/modules/shipment_tracking/migrations/Migration20260529120000_shipment_tracking_poi_region_names.js +15 -0
- package/dist/modules/shipment_tracking/migrations/Migration20260529120000_shipment_tracking_poi_region_names.js.map +7 -0
- package/dist/modules/shipment_tracking/migrations/Migration20260821120000_shipment_tracking_shipsgo.js +47 -0
- package/dist/modules/shipment_tracking/migrations/Migration20260821120000_shipment_tracking_shipsgo.js.map +7 -0
- package/dist/modules/shipment_tracking/notifications.js +145 -0
- package/dist/modules/shipment_tracking/notifications.js.map +7 -0
- package/dist/modules/shipment_tracking/services/carrierRegistry.js +24 -0
- package/dist/modules/shipment_tracking/services/carrierRegistry.js.map +7 -0
- package/dist/modules/shipment_tracking/services/poiNatsService.js +397 -0
- package/dist/modules/shipment_tracking/services/poiNatsService.js.map +7 -0
- package/dist/modules/shipment_tracking/services/shipsGoProvider.js +163 -0
- package/dist/modules/shipment_tracking/services/shipsGoProvider.js.map +7 -0
- package/dist/modules/shipment_tracking/services/trackingService.js +1283 -0
- package/dist/modules/shipment_tracking/services/trackingService.js.map +7 -0
- package/dist/modules/shipment_tracking/services/webhookService.js +263 -0
- package/dist/modules/shipment_tracking/services/webhookService.js.map +7 -0
- package/dist/modules/shipment_tracking/setup.js +72 -0
- package/dist/modules/shipment_tracking/setup.js.map +7 -0
- package/dist/modules/shipment_tracking/subscribers/cargo-event-created.js +60 -0
- package/dist/modules/shipment_tracking/subscribers/cargo-event-created.js.map +7 -0
- package/dist/modules/shipment_tracking/subscribers/dcsa-event-webhook-dispatch.js +58 -0
- package/dist/modules/shipment_tracking/subscribers/dcsa-event-webhook-dispatch.js.map +7 -0
- package/dist/modules/shipment_tracking/subscribers/poi-event-notification.js +82 -0
- package/dist/modules/shipment_tracking/subscribers/poi-event-notification.js.map +7 -0
- package/dist/modules/shipment_tracking/subscribers/poi-event-webhook-dispatch.js +53 -0
- package/dist/modules/shipment_tracking/subscribers/poi-event-webhook-dispatch.js.map +7 -0
- package/dist/modules/shipment_tracking/subscribers/shipment-status-changed.js +28 -0
- package/dist/modules/shipment_tracking/subscribers/shipment-status-changed.js.map +7 -0
- package/dist/modules/shipment_tracking/subscribers/terminal-event-ingest.js +67 -0
- package/dist/modules/shipment_tracking/subscribers/terminal-event-ingest.js.map +7 -0
- package/dist/modules/shipment_tracking/subscribers/tracking-job-initial-poll.js +30 -0
- package/dist/modules/shipment_tracking/subscribers/tracking-job-initial-poll.js.map +7 -0
- package/dist/modules/shipment_tracking/subscribers/tracking-job-webhook-dispatch.js +19 -0
- package/dist/modules/shipment_tracking/subscribers/tracking-job-webhook-dispatch.js.map +7 -0
- package/dist/modules/shipment_tracking/tables/index.js +6 -0
- package/dist/modules/shipment_tracking/tables/index.js.map +7 -0
- package/dist/modules/shipment_tracking/tables/tracking_job/table.client.js +284 -0
- package/dist/modules/shipment_tracking/tables/tracking_job/table.client.js.map +7 -0
- package/dist/modules/shipment_tracking/tables/tracking_job/table.js +36 -0
- package/dist/modules/shipment_tracking/tables/tracking_job/table.js.map +7 -0
- package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/config.js +25 -0
- package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/config.js.map +7 -0
- package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/widget.client.js +355 -0
- package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/widget.client.js.map +7 -0
- package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/widget.js +26 -0
- package/dist/modules/shipment_tracking/widgets/dashboard/ships-map/widget.js.map +7 -0
- package/dist/types/assets.d.js +1 -0
- package/dist/types/assets.d.js.map +7 -0
- package/generated/entities/bic_config/index.ts +20 -0
- package/generated/entities/carrier_config/index.ts +23 -0
- package/generated/entities/location_override/index.ts +26 -0
- package/generated/entities/shipment/index.ts +46 -0
- package/generated/entities/ships_go_config/index.ts +25 -0
- package/generated/entities/tracking_event/index.ts +60 -0
- package/generated/entities/tracking_job/index.ts +33 -0
- package/generated/entities/webhook/index.ts +22 -0
- package/generated/entities/webhook_delivery/index.ts +22 -0
- package/generated/entities.ids.generated.ts +47 -0
- package/package.json +81 -0
- package/src/index.ts +39 -0
- package/src/modules/shipment_tracking/__integration__/TC-TRACK-410-shipsgo-config-api.spec.ts +133 -0
- package/src/modules/shipment_tracking/__integration__/meta.ts +4 -0
- package/src/modules/shipment_tracking/__tests__/empty-return-stop.test.ts +231 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/hapag-lloyd-transship-container.json +702 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/index.ts +434 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/maersk-multi-transship-completed.json +1518 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/maersk-transship-intransit.json +558 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/msc-direct.json +222 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/msc-multi-transship-completed.json +974 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/msc-multi.json +4846 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/msc-transship.json +409 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/poi-events.json +240 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/synthetic/index.ts +138 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/synthetic-eta-updates.json +288 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/synthetic-route-inference.json +201 -0
- package/src/modules/shipment_tracking/__tests__/fixtures/synthetic-voyage-progression.json +410 -0
- package/src/modules/shipment_tracking/__tests__/shipsgo-air-mapper.test.ts +157 -0
- package/src/modules/shipment_tracking/__tests__/shipsgo-client.test.ts +155 -0
- package/src/modules/shipment_tracking/__tests__/shipsgo-mapper.test.ts +201 -0
- package/src/modules/shipment_tracking/__tests__/shipsgo-poll.test.ts +357 -0
- package/src/modules/shipment_tracking/__tests__/shipsgo-provider.test.ts +364 -0
- package/src/modules/shipment_tracking/__tests__/trackingService.integration.test.ts +2248 -0
- package/src/modules/shipment_tracking/__tests__/webhookPayload.test.ts +151 -0
- package/src/modules/shipment_tracking/acl.ts +18 -0
- package/src/modules/shipment_tracking/api/bic-configs/route.ts +206 -0
- package/src/modules/shipment_tracking/api/bic-configs/test/route.ts +108 -0
- package/src/modules/shipment_tracking/api/cargo-events/route.ts +101 -0
- package/src/modules/shipment_tracking/api/carrier-configs/route.ts +142 -0
- package/src/modules/shipment_tracking/api/dashboard/ships-map/route.ts +246 -0
- package/src/modules/shipment_tracking/api/location-overrides/route.ts +185 -0
- package/src/modules/shipment_tracking/api/openapi.ts +28 -0
- package/src/modules/shipment_tracking/api/shipments/route.ts +197 -0
- package/src/modules/shipment_tracking/api/shipsgo-configs/route.ts +215 -0
- package/src/modules/shipment_tracking/api/tracking-jobs/route.ts +149 -0
- package/src/modules/shipment_tracking/api/tracking-jobs/sync/route.ts +116 -0
- package/src/modules/shipment_tracking/api/utils.ts +10 -0
- package/src/modules/shipment_tracking/api/webhooks/deliveries/route.ts +82 -0
- package/src/modules/shipment_tracking/api/webhooks/route.ts +138 -0
- package/src/modules/shipment_tracking/api/webhooks/test/route.ts +84 -0
- package/src/modules/shipment_tracking/assets/carriers/cma_cgm.png +0 -0
- package/src/modules/shipment_tracking/assets/carriers/cosco.png +0 -0
- package/src/modules/shipment_tracking/assets/carriers/evergreen.png +0 -0
- package/src/modules/shipment_tracking/assets/carriers/hapag_lloyd.png +0 -0
- package/src/modules/shipment_tracking/assets/carriers/maersk.png +0 -0
- package/src/modules/shipment_tracking/assets/carriers/msc.png +0 -0
- package/src/modules/shipment_tracking/assets/carriers/one.png +0 -0
- package/src/modules/shipment_tracking/assets/carriers/oocl.png +0 -0
- package/src/modules/shipment_tracking/assets/carriers/yang_ming.png +0 -0
- package/src/modules/shipment_tracking/assets/carriers/zim.png +0 -0
- package/src/modules/shipment_tracking/assets/index.ts +66 -0
- package/src/modules/shipment_tracking/backend/carrier-configs/page.meta.ts +16 -0
- package/src/modules/shipment_tracking/backend/carrier-configs/page.tsx +782 -0
- package/src/modules/shipment_tracking/backend/location-overrides/page.meta.ts +16 -0
- package/src/modules/shipment_tracking/backend/location-overrides/page.tsx +241 -0
- package/src/modules/shipment_tracking/backend/shipment-tracking/page.meta.ts +16 -0
- package/src/modules/shipment_tracking/backend/shipment-tracking/page.tsx +340 -0
- package/src/modules/shipment_tracking/backend/tracking-jobs/page.meta.ts +16 -0
- package/src/modules/shipment_tracking/backend/tracking-jobs/page.tsx +16 -0
- package/src/modules/shipment_tracking/backend/webhooks/page.meta.ts +16 -0
- package/src/modules/shipment_tracking/backend/webhooks/page.tsx +228 -0
- package/src/modules/shipment_tracking/ce.ts +48 -0
- package/src/modules/shipment_tracking/cli.ts +321 -0
- package/src/modules/shipment_tracking/commands/bic-configs.ts +94 -0
- package/src/modules/shipment_tracking/commands/carrier-configs.ts +89 -0
- package/src/modules/shipment_tracking/commands/index.ts +8 -0
- package/src/modules/shipment_tracking/commands/location-overrides.ts +119 -0
- package/src/modules/shipment_tracking/commands/shipments.ts +247 -0
- package/src/modules/shipment_tracking/commands/shipsgo-configs.ts +101 -0
- package/src/modules/shipment_tracking/commands/tracking-jobs.ts +158 -0
- package/src/modules/shipment_tracking/commands/tracking.ts +230 -0
- package/src/modules/shipment_tracking/commands/webhooks.ts +159 -0
- package/src/modules/shipment_tracking/components/CombinedTimestampCell.tsx +236 -0
- package/src/modules/shipment_tracking/components/FacilityLocationInput.tsx +235 -0
- package/src/modules/shipment_tracking/components/LocationOverrideDrawer.tsx +586 -0
- package/src/modules/shipment_tracking/components/ShipmentDetailsDrawer.tsx +1364 -0
- package/src/modules/shipment_tracking/components/ShipmentDrawer.tsx +498 -0
- package/src/modules/shipment_tracking/components/ShipsGoConfigSection.tsx +266 -0
- package/src/modules/shipment_tracking/components/TimestampCell.tsx +146 -0
- package/src/modules/shipment_tracking/components/TrackingJobDrawer.tsx +467 -0
- package/src/modules/shipment_tracking/components/VesselTrackingMap.tsx +761 -0
- package/src/modules/shipment_tracking/components/WebhookDrawer.tsx +434 -0
- package/src/modules/shipment_tracking/data/entities.ts +748 -0
- package/src/modules/shipment_tracking/data/validators.ts +523 -0
- package/src/modules/shipment_tracking/di.ts +73 -0
- package/src/modules/shipment_tracking/encryption.ts +17 -0
- package/src/modules/shipment_tracking/events.ts +122 -0
- package/src/modules/shipment_tracking/hooks/useVesselTracking.ts +297 -0
- package/src/modules/shipment_tracking/i18n/de.json +327 -0
- package/src/modules/shipment_tracking/i18n/en.json +327 -0
- package/src/modules/shipment_tracking/i18n/es.json +327 -0
- package/src/modules/shipment_tracking/i18n/pl.json +327 -0
- package/src/modules/shipment_tracking/index.ts +13 -0
- package/src/modules/shipment_tracking/lib/__tests__/dcsa-event-mapping.test.ts +385 -0
- package/src/modules/shipment_tracking/lib/__tests__/dcsa-event-parser.test.ts +398 -0
- package/src/modules/shipment_tracking/lib/__tests__/empty-return.test.ts +174 -0
- package/src/modules/shipment_tracking/lib/__tests__/poi-event-processor.test.ts +699 -0
- package/src/modules/shipment_tracking/lib/__tests__/route-inference.test.ts +402 -0
- package/src/modules/shipment_tracking/lib/__tests__/status-machine.test.ts +447 -0
- package/src/modules/shipment_tracking/lib/__tests__/time-extraction.test.ts +331 -0
- package/src/modules/shipment_tracking/lib/__tests__/timestamp-utils.test.ts +260 -0
- package/src/modules/shipment_tracking/lib/__tests__/vessel-api.test.ts +125 -0
- package/src/modules/shipment_tracking/lib/__tests__/vessel-extraction.test.ts +108 -0
- package/src/modules/shipment_tracking/lib/adapters/cma-cgm.ts +84 -0
- package/src/modules/shipment_tracking/lib/adapters/cosco-types.ts +145 -0
- package/src/modules/shipment_tracking/lib/adapters/cosco.ts +314 -0
- package/src/modules/shipment_tracking/lib/adapters/evergreen.ts +107 -0
- package/src/modules/shipment_tracking/lib/adapters/hapag-lloyd.ts +88 -0
- package/src/modules/shipment_tracking/lib/adapters/index.ts +18 -0
- package/src/modules/shipment_tracking/lib/adapters/maersk.ts +104 -0
- package/src/modules/shipment_tracking/lib/adapters/msc.ts +223 -0
- package/src/modules/shipment_tracking/lib/adapters/zim.ts +106 -0
- package/src/modules/shipment_tracking/lib/audit-helpers.ts +29 -0
- package/src/modules/shipment_tracking/lib/auth/base64url.ts +4 -0
- package/src/modules/shipment_tracking/lib/auth/oauth-client.ts +53 -0
- package/src/modules/shipment_tracking/lib/background.ts +24 -0
- package/src/modules/shipment_tracking/lib/bic-api-client.ts +199 -0
- package/src/modules/shipment_tracking/lib/carrier-adapter.ts +136 -0
- package/src/modules/shipment_tracking/lib/current-vessel.ts +274 -0
- package/src/modules/shipment_tracking/lib/dcsa-event-mapping.ts +202 -0
- package/src/modules/shipment_tracking/lib/dcsa-event-parser.ts +163 -0
- package/src/modules/shipment_tracking/lib/dcsa-params.ts +14 -0
- package/src/modules/shipment_tracking/lib/empty-return.ts +122 -0
- package/src/modules/shipment_tracking/lib/location-overrides.ts +150 -0
- package/src/modules/shipment_tracking/lib/location-types.ts +154 -0
- package/src/modules/shipment_tracking/lib/logger.ts +195 -0
- package/src/modules/shipment_tracking/lib/poi-event-processor.ts +302 -0
- package/src/modules/shipment_tracking/lib/poi-nats-config.ts +134 -0
- package/src/modules/shipment_tracking/lib/poi-types.ts +198 -0
- package/src/modules/shipment_tracking/lib/rate-limiter.ts +58 -0
- package/src/modules/shipment_tracking/lib/route-extraction.ts +439 -0
- package/src/modules/shipment_tracking/lib/route-inference.ts +238 -0
- package/src/modules/shipment_tracking/lib/schedule-generator.ts +92 -0
- package/src/modules/shipment_tracking/lib/shipsgo-client.ts +350 -0
- package/src/modules/shipment_tracking/lib/shipsgo-mapper.ts +296 -0
- package/src/modules/shipment_tracking/lib/status-machine.ts +230 -0
- package/src/modules/shipment_tracking/lib/time-extraction.ts +85 -0
- package/src/modules/shipment_tracking/lib/timestamp-utils.ts +244 -0
- package/src/modules/shipment_tracking/lib/vessel-api.ts +299 -0
- package/src/modules/shipment_tracking/lib/vessel-cache.ts +102 -0
- package/src/modules/shipment_tracking/lib/vessel-extraction.ts +39 -0
- package/src/modules/shipment_tracking/lib/webhook-dispatcher.ts +93 -0
- package/src/modules/shipment_tracking/migrations/.snapshot-open-mercato.json +2914 -0
- package/src/modules/shipment_tracking/migrations/Migration20260422161208_shipment_tracking.ts +60 -0
- package/src/modules/shipment_tracking/migrations/Migration20260529120000_shipment_tracking_poi_region_names.ts +22 -0
- package/src/modules/shipment_tracking/migrations/Migration20260821120000_shipment_tracking_shipsgo.ts +65 -0
- package/src/modules/shipment_tracking/notifications.ts +151 -0
- package/src/modules/shipment_tracking/services/carrierRegistry.ts +29 -0
- package/src/modules/shipment_tracking/services/poiNatsService.ts +526 -0
- package/src/modules/shipment_tracking/services/shipsGoProvider.ts +231 -0
- package/src/modules/shipment_tracking/services/trackingService.ts +1777 -0
- package/src/modules/shipment_tracking/services/webhookService.ts +347 -0
- package/src/modules/shipment_tracking/setup.ts +99 -0
- package/src/modules/shipment_tracking/subscribers/__tests__/terminal-event-ingest.test.ts +141 -0
- package/src/modules/shipment_tracking/subscribers/cargo-event-created.ts +115 -0
- package/src/modules/shipment_tracking/subscribers/dcsa-event-webhook-dispatch.ts +115 -0
- package/src/modules/shipment_tracking/subscribers/poi-event-notification.ts +144 -0
- package/src/modules/shipment_tracking/subscribers/poi-event-webhook-dispatch.ts +94 -0
- package/src/modules/shipment_tracking/subscribers/shipment-status-changed.ts +41 -0
- package/src/modules/shipment_tracking/subscribers/terminal-event-ingest.ts +109 -0
- package/src/modules/shipment_tracking/subscribers/tracking-job-initial-poll.ts +53 -0
- package/src/modules/shipment_tracking/subscribers/tracking-job-webhook-dispatch.ts +50 -0
- package/src/modules/shipment_tracking/tables/index.ts +4 -0
- package/src/modules/shipment_tracking/tables/tracking_job/table.client.tsx +337 -0
- package/src/modules/shipment_tracking/tables/tracking_job/table.ts +34 -0
- package/src/modules/shipment_tracking/widgets/dashboard/ships-map/config.ts +32 -0
- package/src/modules/shipment_tracking/widgets/dashboard/ships-map/widget.client.tsx +448 -0
- package/src/modules/shipment_tracking/widgets/dashboard/ships-map/widget.ts +25 -0
- package/src/types/assets.d.ts +5 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps ShipsGo shipment responses into the internal `CarrierFetchResult` shape,
|
|
3
|
+
* so the existing ingestion pipeline (Shipment upsert, timestamps, status
|
|
4
|
+
* machine, route inference, unit-sync) consumes ShipsGo exactly like a DCSA
|
|
5
|
+
* carrier adapter.
|
|
6
|
+
*
|
|
7
|
+
* Ocean movement codes are translated onto the DCSA equipment/transport
|
|
8
|
+
* vocabulary the downstream event-mapping already understands
|
|
9
|
+
* (see dcsa-event-mapping.ts), so notifier events resolve unchanged.
|
|
10
|
+
*/
|
|
11
|
+
import type { CarrierFetchResult, CarrierFetchedEvent } from './carrier-adapter'
|
|
12
|
+
import type {
|
|
13
|
+
TrackingEventType,
|
|
14
|
+
TrackingEventClassifierCode,
|
|
15
|
+
ModeOfTransport,
|
|
16
|
+
} from '../data/entities'
|
|
17
|
+
import type {
|
|
18
|
+
ShipsGoOceanContainer,
|
|
19
|
+
ShipsGoOceanMovement,
|
|
20
|
+
ShipsGoOceanShipment,
|
|
21
|
+
ShipsGoAirMovement,
|
|
22
|
+
ShipsGoAirShipment,
|
|
23
|
+
} from './shipsgo-client'
|
|
24
|
+
|
|
25
|
+
// ShipsGo ocean event → { internal DCSA-style eventType + eventCode }.
|
|
26
|
+
// GTIN/LOAD/DISC/GTOT map 1:1; ARRV→ARRI (DCSA spelling); EMSH/EMRT use the
|
|
27
|
+
// closest DCSA equipment code so the notifier resolves (pick-up / drop-off).
|
|
28
|
+
const OCEAN_EVENT_MAP: Record<string, { eventType: TrackingEventType; eventCode: string }> = {
|
|
29
|
+
EMSH: { eventType: 'EQUIPMENT', eventCode: 'PICK' }, // empty released to shipper
|
|
30
|
+
GTIN: { eventType: 'EQUIPMENT', eventCode: 'GTIN' },
|
|
31
|
+
LOAD: { eventType: 'EQUIPMENT', eventCode: 'LOAD' },
|
|
32
|
+
DEPA: { eventType: 'TRANSPORT', eventCode: 'DEPA' },
|
|
33
|
+
ARRV: { eventType: 'TRANSPORT', eventCode: 'ARRI' },
|
|
34
|
+
DISC: { eventType: 'EQUIPMENT', eventCode: 'DISC' },
|
|
35
|
+
GTOT: { eventType: 'EQUIPMENT', eventCode: 'GTOT' },
|
|
36
|
+
EMRT: { eventType: 'EQUIPMENT', eventCode: 'DROP' }, // empty returned to depot
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function classifier(status: 'EST' | 'ACT'): TrackingEventClassifierCode {
|
|
40
|
+
return status === 'ACT' ? 'ACT' : 'EST'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Deterministic id for dedup — ShipsGo movements have no stable identifier.
|
|
45
|
+
* ACT (actual) movements key on their timestamp (immutable once they happen);
|
|
46
|
+
* EST (estimated) movements OMIT the timestamp so a shifting estimate maps to
|
|
47
|
+
* the same row and is refreshed in place (see the persistence loop) rather than
|
|
48
|
+
* inserting a new event every poll.
|
|
49
|
+
*/
|
|
50
|
+
function movementId(container: string, m: ShipsGoOceanMovement): string {
|
|
51
|
+
const tsKey = m.status === 'EST' ? 'EST' : m.timestamp
|
|
52
|
+
return `${container}:${m.event}:${m.status}:${tsKey}`
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isLocode(code: string | null | undefined): boolean {
|
|
56
|
+
return typeof code === 'string' && /^[A-Z]{2}[A-Z0-9]{3}$/.test(code)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Extracts the local UTC offset from a ShipsGo ISO-8601 timestamp. ShipsGo dates
|
|
61
|
+
* carry the movement's local (port/airport) offset — e.g. `+08:00` for a Beijing
|
|
62
|
+
* departure — so `eventDateTime` (the UTC instant) alone loses the local wall
|
|
63
|
+
* clock the carrier reported. We persist the offset in `eventDateTimeOffset` for
|
|
64
|
+
* local-time display, matching the DCSA adapters. Returns null for UTC (`Z` or
|
|
65
|
+
* `+00:00`), which is already the default.
|
|
66
|
+
*/
|
|
67
|
+
function isoOffset(ts: string): string | null {
|
|
68
|
+
const match = ts.match(/([+-]\d{2}:\d{2})$/)
|
|
69
|
+
if (!match || match[1] === '+00:00') return null
|
|
70
|
+
return match[1]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* ShipsGo container size (feet, e.g. 40) + type (e.g. HC, GP, RF) → a readable
|
|
75
|
+
* equipment code ("40HC"). Not a true ISO 6346 code, but it's the container-type
|
|
76
|
+
* display slot and ShipsGo doesn't return the ISO code. Null when neither given.
|
|
77
|
+
*/
|
|
78
|
+
function equipmentCode(container: ShipsGoOceanContainer): string | null {
|
|
79
|
+
const size = container.size != null ? String(container.size) : ''
|
|
80
|
+
const type = container.type ?? ''
|
|
81
|
+
const code = `${size}${type}`.trim()
|
|
82
|
+
return code || null
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function mapOceanMovement(
|
|
86
|
+
container: ShipsGoOceanContainer,
|
|
87
|
+
m: ShipsGoOceanMovement,
|
|
88
|
+
): CarrierFetchedEvent | null {
|
|
89
|
+
const mapping = OCEAN_EVENT_MAP[m.event]
|
|
90
|
+
if (!mapping) return null
|
|
91
|
+
|
|
92
|
+
const ts = new Date(m.timestamp)
|
|
93
|
+
if (Number.isNaN(ts.getTime())) return null
|
|
94
|
+
|
|
95
|
+
const loc = m.location
|
|
96
|
+
const hasVessel = Boolean(m.vessel?.name || m.vessel?.imo)
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
source: 'shipsgo',
|
|
100
|
+
sourceEventId: movementId(container.number, m),
|
|
101
|
+
eventType: mapping.eventType,
|
|
102
|
+
eventCode: mapping.eventCode,
|
|
103
|
+
eventClassifierCode: classifier(m.status),
|
|
104
|
+
eventDateTime: ts,
|
|
105
|
+
eventDateTimeOffset: isoOffset(m.timestamp),
|
|
106
|
+
description: `ShipsGo ${m.event} (${m.status})`,
|
|
107
|
+
rawData: { provider: 'shipsgo', ...m },
|
|
108
|
+
equipmentReference: container.number,
|
|
109
|
+
isoEquipmentCode: equipmentCode(container),
|
|
110
|
+
locationName: loc?.name ?? null,
|
|
111
|
+
locationUnlocode: isLocode(loc?.code) ? loc?.code ?? null : null,
|
|
112
|
+
locationCountry: loc?.country?.code ?? null,
|
|
113
|
+
modeOfTransport: hasVessel ? ('VESSEL' as ModeOfTransport) : null,
|
|
114
|
+
vesselName: m.vessel?.name ?? null,
|
|
115
|
+
vesselImo: m.vessel?.imo != null ? String(m.vessel.imo) : null,
|
|
116
|
+
voyageNumber: m.voyage ?? null,
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* ShipsGo shipment-level route summary → a compact object for `Shipment.extra`.
|
|
122
|
+
* Origin/destination UNLOCODEs are already inferred from movement locations, so
|
|
123
|
+
* this captures the extras: POL/POD names + planned/predicted dates, transit
|
|
124
|
+
* time, transit %, transshipment count and CO2. Returns null when route is absent.
|
|
125
|
+
*/
|
|
126
|
+
function oceanRouteExtra(shipment: ShipsGoOceanShipment): Record<string, unknown> | null {
|
|
127
|
+
const route = shipment.route
|
|
128
|
+
if (!route) return null
|
|
129
|
+
const pol = route.port_of_loading
|
|
130
|
+
const pod = route.port_of_discharge
|
|
131
|
+
const extra: Record<string, unknown> = {}
|
|
132
|
+
if (pol?.location) extra.portOfLoading = { unlocode: pol.location.code ?? null, name: pol.location.name ?? null }
|
|
133
|
+
if (pol?.date_of_loading) extra.dateOfLoading = pol.date_of_loading
|
|
134
|
+
if (pod?.location) extra.portOfDischarge = { unlocode: pod.location.code ?? null, name: pod.location.name ?? null }
|
|
135
|
+
if (pod?.date_of_discharge) extra.dateOfDischarge = pod.date_of_discharge
|
|
136
|
+
if (pod?.date_of_discharge_predicted) extra.dateOfDischargePredicted = pod.date_of_discharge_predicted
|
|
137
|
+
if (route.transit_time != null) extra.transitTimeDays = route.transit_time
|
|
138
|
+
if (route.transit_percentage != null) extra.transitPercentage = route.transit_percentage
|
|
139
|
+
if (route.ts_count != null) extra.transshipmentCount = route.ts_count
|
|
140
|
+
if (route.co2_emission != null) extra.co2Emission = route.co2_emission
|
|
141
|
+
return Object.keys(extra).length ? { shipsgoRoute: extra } : null
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Maps a full ShipsGo ocean shipment (with containers + movements) into a
|
|
146
|
+
* `CarrierFetchResult`. Emits one event per movement per container; the
|
|
147
|
+
* downstream pipeline discovers containers from `equipmentReference`.
|
|
148
|
+
*/
|
|
149
|
+
export function mapOceanShipmentToEvents(shipment: ShipsGoOceanShipment): CarrierFetchResult {
|
|
150
|
+
const events: CarrierFetchedEvent[] = []
|
|
151
|
+
let vesselName: string | null = null
|
|
152
|
+
let vesselImo: string | null = null
|
|
153
|
+
|
|
154
|
+
const containers = shipment.containers ?? []
|
|
155
|
+
for (const container of containers) {
|
|
156
|
+
const movements = container.movements ?? []
|
|
157
|
+
for (const m of movements) {
|
|
158
|
+
const event = mapOceanMovement(container, m)
|
|
159
|
+
if (!event) continue
|
|
160
|
+
events.push(event)
|
|
161
|
+
// Track the most recent vessel seen (movements are chronological).
|
|
162
|
+
if (event.vesselName) vesselName = event.vesselName
|
|
163
|
+
if (event.vesselImo) vesselImo = event.vesselImo
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
events,
|
|
169
|
+
containerNumber: shipment.container_number ?? containers[0]?.number ?? null,
|
|
170
|
+
bookingNumber: shipment.booking_number ?? null,
|
|
171
|
+
bolNumber: null,
|
|
172
|
+
vesselName,
|
|
173
|
+
vesselImo,
|
|
174
|
+
extra: oceanRouteExtra(shipment),
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ─── Air ─────────────────────────────────────────────────────────────────────
|
|
179
|
+
|
|
180
|
+
// ShipsGo air event → internal event code. DEP/ARR are translated to the DCSA
|
|
181
|
+
// departure/arrival codes so the shared time-extraction (ATD/ATA) and timeline
|
|
182
|
+
// reuse unchanged; the rest keep their native code (informational only — air
|
|
183
|
+
// status is derived from ShipsGo's top-level status, see deriveAirShipmentStatus).
|
|
184
|
+
const AIR_EVENT_MAP: Record<string, string> = {
|
|
185
|
+
RCS: 'RCS', // received from shipper
|
|
186
|
+
MAN: 'MAN', // manifested
|
|
187
|
+
DEP: 'DEPA', // departed
|
|
188
|
+
ARR: 'ARRI', // arrived
|
|
189
|
+
RCF: 'RCF', // received from flight
|
|
190
|
+
DLV: 'DLVR', // delivered
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Deterministic id for dedup — air movements have no stable identifier.
|
|
194
|
+
* EST omits the timestamp (refreshed in place); ACT keys on it (immutable). */
|
|
195
|
+
function airMovementId(awb: string, m: ShipsGoAirMovement): string {
|
|
196
|
+
const tsKey = m.status === 'EST' ? 'EST' : m.timestamp
|
|
197
|
+
return `${awb}:${m.event}:${m.status}:${tsKey}`
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function mapAirMovement(awb: string, m: ShipsGoAirMovement): CarrierFetchedEvent | null {
|
|
201
|
+
const eventCode = AIR_EVENT_MAP[m.event]
|
|
202
|
+
if (!eventCode) return null
|
|
203
|
+
|
|
204
|
+
const ts = new Date(m.timestamp)
|
|
205
|
+
if (Number.isNaN(ts.getTime())) return null
|
|
206
|
+
|
|
207
|
+
const loc = m.location
|
|
208
|
+
return {
|
|
209
|
+
source: 'shipsgo',
|
|
210
|
+
sourceEventId: airMovementId(awb, m),
|
|
211
|
+
// Air movements are all transport-leg events (no container/equipment model).
|
|
212
|
+
eventType: 'TRANSPORT',
|
|
213
|
+
eventCode,
|
|
214
|
+
eventClassifierCode: classifier(m.status),
|
|
215
|
+
eventDateTime: ts,
|
|
216
|
+
eventDateTimeOffset: isoOffset(m.timestamp),
|
|
217
|
+
description: `ShipsGo air ${m.event} (${m.status})`,
|
|
218
|
+
rawData: { provider: 'shipsgo', mode: 'air', ...m },
|
|
219
|
+
// Air locations are IATA airports, not UN/LOCODEs — keep only the name/country.
|
|
220
|
+
locationName: loc?.name ?? loc?.iata ?? null,
|
|
221
|
+
locationUnlocode: null,
|
|
222
|
+
locationCountry: loc?.country?.code ?? null,
|
|
223
|
+
modeOfTransport: m.flight ? ('AIR' as ModeOfTransport) : null,
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Maps a full ShipsGo air shipment into a `CarrierFetchResult`. Emits one event
|
|
229
|
+
* per movement (no container fan-out); the downstream air branch creates a
|
|
230
|
+
* single AWB-keyed Shipment and fills flight/airline + the top-level air status.
|
|
231
|
+
*/
|
|
232
|
+
export function mapAirShipmentToEvents(shipment: ShipsGoAirShipment): CarrierFetchResult {
|
|
233
|
+
const awb = shipment.awb_number ?? shipment.reference ?? ''
|
|
234
|
+
const events: CarrierFetchedEvent[] = []
|
|
235
|
+
let flightNumber: string | null = null
|
|
236
|
+
|
|
237
|
+
const movements = shipment.movements ?? []
|
|
238
|
+
for (const m of movements) {
|
|
239
|
+
const event = mapAirMovement(awb, m)
|
|
240
|
+
if (!event) continue
|
|
241
|
+
events.push(event)
|
|
242
|
+
// Track the most recent flight seen (movements are chronological).
|
|
243
|
+
if (m.flight) flightNumber = m.flight
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
events,
|
|
248
|
+
containerNumber: null,
|
|
249
|
+
bookingNumber: null,
|
|
250
|
+
bolNumber: null,
|
|
251
|
+
awbNumber: shipment.awb_number ?? null,
|
|
252
|
+
flightNumber,
|
|
253
|
+
airlineCode: shipment.airline?.iata ?? null,
|
|
254
|
+
airlineName: shipment.airline?.name ?? null,
|
|
255
|
+
airStatus: shipment.status ?? null,
|
|
256
|
+
extra: airExtra(shipment),
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* ShipsGo air shipment-level extras → `Shipment.extra`: cargo (pieces/weight/
|
|
262
|
+
* volume) and the pieces-per-status breakdown. Returns null when neither given.
|
|
263
|
+
*/
|
|
264
|
+
function airExtra(shipment: ShipsGoAirShipment): Record<string, unknown> | null {
|
|
265
|
+
const extra: Record<string, unknown> = {}
|
|
266
|
+
const c = shipment.cargo
|
|
267
|
+
if (c && (c.pieces != null || c.weight != null || c.volume != null)) {
|
|
268
|
+
extra.cargo = {
|
|
269
|
+
pieces: c.pieces ?? null,
|
|
270
|
+
weight: c.weight ?? null,
|
|
271
|
+
weightUnit: c.weight_unit ?? null,
|
|
272
|
+
volume: c.volume ?? null,
|
|
273
|
+
volumeUnit: c.volume_unit ?? null,
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
// Air route mirrors the ocean route (see oceanRouteExtra). Air locations are
|
|
277
|
+
// IATA airports (not UN/LOCODEs), so event-based origin/destination inference
|
|
278
|
+
// yields nothing — this is the only place origin/destination surface for air.
|
|
279
|
+
const route = shipment.route
|
|
280
|
+
if (route) {
|
|
281
|
+
const r: Record<string, unknown> = {}
|
|
282
|
+
const orig = route.origin
|
|
283
|
+
const dest = route.destination
|
|
284
|
+
if (orig?.location) r.origin = { iata: orig.location.iata ?? null, name: orig.location.name ?? null }
|
|
285
|
+
if (orig?.date_of_dep) r.dateOfDeparture = orig.date_of_dep
|
|
286
|
+
if (dest?.location) r.destination = { iata: dest.location.iata ?? null, name: dest.location.name ?? null }
|
|
287
|
+
if (dest?.date_of_rcf) r.dateOfArrival = dest.date_of_rcf
|
|
288
|
+
if (route.transit_time != null) r.transitTimeDays = route.transit_time
|
|
289
|
+
if (route.transit_percentage != null) r.transitPercentage = route.transit_percentage
|
|
290
|
+
if (route.ts_count != null) r.transshipmentCount = route.ts_count
|
|
291
|
+
if (route.co2_emission != null) r.co2Emission = route.co2_emission
|
|
292
|
+
if (Object.keys(r).length) extra.route = r
|
|
293
|
+
}
|
|
294
|
+
if (shipment.status_extended != null) extra.statusExtended = shipment.status_extended
|
|
295
|
+
return Object.keys(extra).length ? { shipsgoAir: extra } : null
|
|
296
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import type { ShipmentStatusEnum, TrackingEventClassifierCode } from '../data/entities'
|
|
2
|
+
|
|
3
|
+
export type ShipmentStatus = ShipmentStatusEnum
|
|
4
|
+
|
|
5
|
+
type EventInput = {
|
|
6
|
+
eventCode: string
|
|
7
|
+
eventClassifierCode?: TrackingEventClassifierCode | null
|
|
8
|
+
locationUnlocode?: string | null
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type ShipmentContext = {
|
|
12
|
+
originUnlocode?: string | null
|
|
13
|
+
destinationUnlocode?: string | null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type TimeContext = {
|
|
17
|
+
eta?: Date | null
|
|
18
|
+
ata?: Date | null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Number of days before ETA when a shipment transitions to PRE_ARRIVAL status.
|
|
23
|
+
* Shipment becomes PRE_ARRIVAL when: ETA is within this many days AND no ATA yet.
|
|
24
|
+
*/
|
|
25
|
+
export const PRE_ARRIVAL_DAYS_THRESHOLD = 7
|
|
26
|
+
|
|
27
|
+
const STATUS_ORDER: ShipmentStatus[] = [
|
|
28
|
+
'PENDING',
|
|
29
|
+
'BOOKED',
|
|
30
|
+
'DEPARTED',
|
|
31
|
+
'IN_TRANSIT',
|
|
32
|
+
'PRE_ARRIVAL',
|
|
33
|
+
'ARRIVED',
|
|
34
|
+
'DELIVERED',
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
function statusRank(status: ShipmentStatus): number {
|
|
38
|
+
return STATUS_ORDER.indexOf(status)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isAtDestination(eventLocation: string | null | undefined, destination: string | null | undefined): boolean {
|
|
42
|
+
if (!eventLocation || !destination) return false
|
|
43
|
+
return eventLocation.toUpperCase() === destination.toUpperCase()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function isAtOrigin(eventLocation: string | null | undefined, origin: string | null | undefined): boolean {
|
|
47
|
+
if (!eventLocation || !origin) return false
|
|
48
|
+
return eventLocation.toUpperCase() === origin.toUpperCase()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function deriveStatusFromEvent(event: EventInput, context: ShipmentContext): ShipmentStatus | null {
|
|
52
|
+
const code = event.eventCode.toUpperCase()
|
|
53
|
+
const classifierCode = event.eventClassifierCode
|
|
54
|
+
|
|
55
|
+
// DEPA at origin = DEPARTED
|
|
56
|
+
if (code === 'DEPA') {
|
|
57
|
+
if (classifierCode === 'ACT') {
|
|
58
|
+
return 'DEPARTED'
|
|
59
|
+
}
|
|
60
|
+
if (classifierCode === 'PLN' || classifierCode === 'EST') {
|
|
61
|
+
return 'BOOKED'
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ARRI at destination = ARRIVED (actual) or IN_TRANSIT (planned/estimated approaching)
|
|
66
|
+
if (code === 'ARRI' && isAtDestination(event.locationUnlocode, context.destinationUnlocode)) {
|
|
67
|
+
if (classifierCode === 'ACT') {
|
|
68
|
+
return 'ARRIVED'
|
|
69
|
+
}
|
|
70
|
+
return 'IN_TRANSIT'
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ARRI at non-destination with actual = IN_TRANSIT (transshipment)
|
|
74
|
+
if (code === 'ARRI' && classifierCode === 'ACT') {
|
|
75
|
+
return 'IN_TRANSIT'
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// DISC (discharge) at destination = ARRIVED
|
|
79
|
+
if (code === 'DISC' && isAtDestination(event.locationUnlocode, context.destinationUnlocode)) {
|
|
80
|
+
if (classifierCode === 'ACT') {
|
|
81
|
+
return 'ARRIVED'
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// LOAD at origin = BOOKED (loaded onto vessel)
|
|
86
|
+
if (code === 'LOAD' && isAtOrigin(event.locationUnlocode, context.originUnlocode)) {
|
|
87
|
+
if (classifierCode === 'ACT') {
|
|
88
|
+
return 'BOOKED'
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Gate out at destination = DELIVERED (GTOT = Gate Out Terminal, PICK = Pick-up)
|
|
93
|
+
if ((code === 'GTOT' || code === 'PICK') && isAtDestination(event.locationUnlocode, context.destinationUnlocode)) {
|
|
94
|
+
if (classifierCode === 'ACT') {
|
|
95
|
+
return 'DELIVERED'
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Gate in at destination = DELIVERED (empty return means cargo was delivered)
|
|
100
|
+
if (code === 'GTIN' && isAtDestination(event.locationUnlocode, context.destinationUnlocode)) {
|
|
101
|
+
if (classifierCode === 'ACT') {
|
|
102
|
+
return 'DELIVERED'
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Available for pick-up at destination = ARRIVED
|
|
107
|
+
if (code === 'AVPU' && isAtDestination(event.locationUnlocode, context.destinationUnlocode)) {
|
|
108
|
+
if (classifierCode === 'ACT') {
|
|
109
|
+
return 'ARRIVED'
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Customs released at destination = ARRIVED
|
|
114
|
+
if (code === 'CUSR' && isAtDestination(event.locationUnlocode, context.destinationUnlocode)) {
|
|
115
|
+
if (classifierCode === 'ACT') {
|
|
116
|
+
return 'ARRIVED'
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Delivery event = DELIVERED
|
|
121
|
+
if (code === 'DLVR' && classifierCode === 'ACT') {
|
|
122
|
+
return 'DELIVERED'
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return null
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Checks if a shipment qualifies for PRE_ARRIVAL status based on time.
|
|
130
|
+
* PRE_ARRIVAL = ETA is within threshold days AND no ATA yet.
|
|
131
|
+
*/
|
|
132
|
+
function shouldUpgradeToPreArrival(timeContext: TimeContext | undefined): boolean {
|
|
133
|
+
if (!timeContext?.eta || timeContext.ata) {
|
|
134
|
+
return false
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const now = new Date()
|
|
138
|
+
const daysUntilEta = (timeContext.eta.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)
|
|
139
|
+
|
|
140
|
+
return daysUntilEta <= PRE_ARRIVAL_DAYS_THRESHOLD && daysUntilEta >= 0
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Derives the highest-priority shipment status from a set of tracking events.
|
|
145
|
+
* Status only moves forward (never downgrades), based on DCSA event codes.
|
|
146
|
+
*
|
|
147
|
+
* @param events - Array of tracking events to evaluate
|
|
148
|
+
* @param context - Location context (origin/destination UNLOCODEs)
|
|
149
|
+
* @param currentStatus - Current shipment status (defaults to PENDING)
|
|
150
|
+
* @param timeContext - Optional time context for PRE_ARRIVAL evaluation (ETA/ATA)
|
|
151
|
+
*/
|
|
152
|
+
export function deriveShipmentStatus(
|
|
153
|
+
events: EventInput[],
|
|
154
|
+
context: ShipmentContext,
|
|
155
|
+
currentStatus: ShipmentStatus = 'PENDING',
|
|
156
|
+
timeContext?: TimeContext,
|
|
157
|
+
): ShipmentStatus {
|
|
158
|
+
let best = currentStatus
|
|
159
|
+
|
|
160
|
+
for (const event of events) {
|
|
161
|
+
const derived = deriveStatusFromEvent(event, context)
|
|
162
|
+
if (derived && statusRank(derived) > statusRank(best)) {
|
|
163
|
+
best = derived
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Time-based PRE_ARRIVAL upgrade:
|
|
168
|
+
// If event-derived status is IN_TRANSIT (or lower) and time conditions are met,
|
|
169
|
+
// upgrade to PRE_ARRIVAL
|
|
170
|
+
if (statusRank(best) <= statusRank('IN_TRANSIT') && shouldUpgradeToPreArrival(timeContext)) {
|
|
171
|
+
const preArrivalRank = statusRank('PRE_ARRIVAL')
|
|
172
|
+
if (preArrivalRank > statusRank(best)) {
|
|
173
|
+
best = 'PRE_ARRIVAL'
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return best
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* ShipsGo air top-level status → internal ShipmentStatusEnum.
|
|
182
|
+
* Air has no container/vessel movement model, so status is driven by ShipsGo's
|
|
183
|
+
* aggregate `status` field rather than event-location matching:
|
|
184
|
+
* NEW/UNTRACKED → PENDING, BOOKED → BOOKED, EN_ROUTE → IN_TRANSIT,
|
|
185
|
+
* LANDED → ARRIVED, DELIVERED → DELIVERED. (INPROGRESS ≈ BOOKED.)
|
|
186
|
+
* Monotonic: never downgrades below the current status.
|
|
187
|
+
*/
|
|
188
|
+
const AIR_STATUS_MAP: Record<string, ShipmentStatus> = {
|
|
189
|
+
NEW: 'PENDING',
|
|
190
|
+
UNTRACKED: 'PENDING',
|
|
191
|
+
INPROGRESS: 'BOOKED',
|
|
192
|
+
BOOKED: 'BOOKED',
|
|
193
|
+
EN_ROUTE: 'IN_TRANSIT',
|
|
194
|
+
LANDED: 'ARRIVED',
|
|
195
|
+
DELIVERED: 'DELIVERED',
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function deriveAirShipmentStatus(
|
|
199
|
+
airStatus: string | null | undefined,
|
|
200
|
+
currentStatus: ShipmentStatus = 'PENDING',
|
|
201
|
+
): ShipmentStatus {
|
|
202
|
+
if (!airStatus) return currentStatus
|
|
203
|
+
const mapped = AIR_STATUS_MAP[airStatus.toUpperCase()]
|
|
204
|
+
if (!mapped) return currentStatus
|
|
205
|
+
return statusRank(mapped) > statusRank(currentStatus) ? mapped : currentStatus
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Evaluates if a shipment should be upgraded to PRE_ARRIVAL status.
|
|
210
|
+
* Used by the scheduled job to check IN_TRANSIT shipments.
|
|
211
|
+
*
|
|
212
|
+
* @param currentStatus - Current shipment status
|
|
213
|
+
* @param timeContext - Time context with ETA and ATA
|
|
214
|
+
* @returns The new status (PRE_ARRIVAL if conditions met, otherwise currentStatus)
|
|
215
|
+
*/
|
|
216
|
+
export function evaluatePreArrivalUpgrade(
|
|
217
|
+
currentStatus: ShipmentStatus,
|
|
218
|
+
timeContext: TimeContext,
|
|
219
|
+
): ShipmentStatus {
|
|
220
|
+
// Only upgrade from IN_TRANSIT to PRE_ARRIVAL
|
|
221
|
+
if (currentStatus !== 'IN_TRANSIT') {
|
|
222
|
+
return currentStatus
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (shouldUpgradeToPreArrival(timeContext)) {
|
|
226
|
+
return 'PRE_ARRIVAL'
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return currentStatus
|
|
230
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { TrackingEventClassifierCode } from '../data/entities'
|
|
2
|
+
|
|
3
|
+
type EventInput = {
|
|
4
|
+
eventCode: string
|
|
5
|
+
eventClassifierCode?: TrackingEventClassifierCode | null
|
|
6
|
+
eventDateTime: Date
|
|
7
|
+
eventDateTimeOffset?: string | null
|
|
8
|
+
eventCreatedDateTime?: Date | null
|
|
9
|
+
locationUnlocode?: string | null
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type ShipmentContext = {
|
|
13
|
+
originUnlocode?: string | null
|
|
14
|
+
destinationUnlocode?: string | null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type ExtractedTimes = {
|
|
18
|
+
etd?: Date | null
|
|
19
|
+
etdOffset?: string | null
|
|
20
|
+
eta?: Date | null
|
|
21
|
+
etaOffset?: string | null
|
|
22
|
+
atd?: Date | null
|
|
23
|
+
atdOffset?: string | null
|
|
24
|
+
ata?: Date | null
|
|
25
|
+
ataOffset?: string | null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isAtLocation(eventLocation: string | null | undefined, target: string | null | undefined): boolean {
|
|
29
|
+
if (!eventLocation || !target) return false
|
|
30
|
+
return eventLocation.toUpperCase() === target.toUpperCase()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Extracts ETD/ETA/ATD/ATA from cargo events.
|
|
35
|
+
*
|
|
36
|
+
* Logic:
|
|
37
|
+
* - DEPA ACT at origin -> ATD
|
|
38
|
+
* - DEPA PLN/EST at origin -> ETD
|
|
39
|
+
* - ARRI ACT at destination -> ATA
|
|
40
|
+
* - ARRI PLN/EST at destination -> ETA
|
|
41
|
+
*
|
|
42
|
+
* When multiple events match the same field, the latest one wins.
|
|
43
|
+
* For EST/PLN events, we use eventCreatedDateTime (when estimate was published)
|
|
44
|
+
* to ensure revised estimates supersede earlier ones.
|
|
45
|
+
*/
|
|
46
|
+
export function extractShipmentTimes(events: EventInput[], context: ShipmentContext): ExtractedTimes {
|
|
47
|
+
const result: ExtractedTimes = {}
|
|
48
|
+
|
|
49
|
+
// Sort events by createdDateTime (when published) if available, else eventDateTime
|
|
50
|
+
// This ensures revised estimates (published later) supersede earlier ones
|
|
51
|
+
const sorted = [...events].sort((eventA, eventB) => {
|
|
52
|
+
const timeA = eventA.eventCreatedDateTime?.getTime() ?? eventA.eventDateTime.getTime()
|
|
53
|
+
const timeB = eventB.eventCreatedDateTime?.getTime() ?? eventB.eventDateTime.getTime()
|
|
54
|
+
return timeA - timeB
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
for (const event of sorted) {
|
|
58
|
+
const code = event.eventCode.toUpperCase()
|
|
59
|
+
const classifierCode = event.eventClassifierCode
|
|
60
|
+
const atOrigin = isAtLocation(event.locationUnlocode, context.originUnlocode)
|
|
61
|
+
const atDestination = isAtLocation(event.locationUnlocode, context.destinationUnlocode)
|
|
62
|
+
|
|
63
|
+
if (code === 'DEPA') {
|
|
64
|
+
if (classifierCode === 'ACT' && (atOrigin || !context.originUnlocode)) {
|
|
65
|
+
result.atd = event.eventDateTime
|
|
66
|
+
result.atdOffset = event.eventDateTimeOffset ?? null
|
|
67
|
+
} else if ((classifierCode === 'PLN' || classifierCode === 'EST') && (atOrigin || !context.originUnlocode)) {
|
|
68
|
+
result.etd = event.eventDateTime
|
|
69
|
+
result.etdOffset = event.eventDateTimeOffset ?? null
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (code === 'ARRI') {
|
|
74
|
+
if (classifierCode === 'ACT' && (atDestination || !context.destinationUnlocode)) {
|
|
75
|
+
result.ata = event.eventDateTime
|
|
76
|
+
result.ataOffset = event.eventDateTimeOffset ?? null
|
|
77
|
+
} else if ((classifierCode === 'PLN' || classifierCode === 'EST') && (atDestination || !context.destinationUnlocode)) {
|
|
78
|
+
result.eta = event.eventDateTime
|
|
79
|
+
result.etaOffset = event.eventDateTimeOffset ?? null
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return result
|
|
85
|
+
}
|