@bisondesk/core-sdk 1.0.678 → 1.0.679

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.
@@ -0,0 +1,50 @@
1
+ import { BusinessEntityIds } from '../constants.js';
2
+ import { TransportStatus, TransportType } from '../types/transports.js';
3
+ import { VehiclePurchaseLogisticsStatus } from '../types/vehicles.js';
4
+ import { getTransportDocumentOwner, isTransportCompleted } from './transports.js';
5
+
6
+ describe('getTransportDocumentOwner', () => {
7
+ test.each([
8
+ {
9
+ description: 'a sale transport files on its deal',
10
+ transport: { type: TransportType.Sale, vehicleId: 'V1', opportunityId: 'OPP1' },
11
+ expected: { businessEntityId: BusinessEntityIds.Opportunities, recordId: 'OPP1' },
12
+ },
13
+ {
14
+ description: 'a purchase transport files on its vehicle',
15
+ transport: { type: TransportType.Purchase, vehicleId: 'V1' },
16
+ expected: { businessEntityId: BusinessEntityIds.Vehicles, recordId: 'V1' },
17
+ },
18
+ {
19
+ description: 'a sale transport without a deal has no owner',
20
+ transport: { type: TransportType.Sale, vehicleId: 'V1' },
21
+ expected: undefined,
22
+ },
23
+ ])('$description', ({ transport, expected }) => {
24
+ expect(getTransportDocumentOwner(transport)).toEqual(expected);
25
+ });
26
+ });
27
+
28
+ describe('isTransportCompleted', () => {
29
+ test.each([
30
+ { status: TransportStatus.Done, expected: true },
31
+ { status: TransportStatus.OngoingInternal, expected: false },
32
+ ])('a sale transport at $status is completed: $expected', ({ status, expected }) => {
33
+ expect(isTransportCompleted({ type: TransportType.Sale, status }, {})).toBe(expected);
34
+ });
35
+
36
+ test.each([
37
+ { logistics: VehiclePurchaseLogisticsStatus.Arrived, expected: true },
38
+ { logistics: VehiclePurchaseLogisticsStatus.AtOrigin, expected: false },
39
+ ])('a purchase transport whose vehicle is $logistics is completed: $expected', ({
40
+ logistics,
41
+ expected,
42
+ }) => {
43
+ expect(
44
+ isTransportCompleted(
45
+ { type: TransportType.Purchase, status: TransportStatus.OngoingInternal },
46
+ { purchase: { logistics: { status: logistics } } },
47
+ ),
48
+ ).toBe(expected);
49
+ });
50
+ });
@@ -1,7 +1,7 @@
1
+ import { BusinessEntityIds } from '../constants.js';
1
2
  import { PurchaseDeliveryTypes } from '../types/deliveries.js';
2
- import { Opportunity } from '../types/opportunities.js';
3
3
  import { PickFieldIds } from '../types/search.js';
4
- import { SearchTransport, Transport, TransportType } from '../types/transports.js';
4
+ import { SearchTransport, Transport, TransportStatus, TransportType } from '../types/transports.js';
5
5
  import { VehiclePurchase } from '../types/vehicles.js';
6
6
  import { hasPurchaseArrived } from './vehicles.js';
7
7
 
@@ -14,39 +14,56 @@ export const isPurchaseDropOffTransport = (
14
14
  isPurchaseTransport(transport) && transport.delivery.type === PurchaseDeliveryTypes.DROP_OFF;
15
15
 
16
16
  /**
17
- * A transport is finished when the journey it describes has demonstrably happened: the customer has
18
- * the vehicle for a sale delivery, the vehicle is on our park for a purchase. From then on
19
- * the record is history and the server refuses edits to it.
17
+ * A transport is finished when the journey it describes is over. From then on the record is
18
+ * history and the server refuses edits to it.
20
19
  *
21
- * Not the same thing as the transport's own `doneAt`. Delivery can be declared on the opportunity
22
- * (a CMR, a delivery confirmation) and arrival on the purchase, without the transport ever being
23
- * moved — and reversing either of those is what unlocks the transport again, which a status stamped
24
- * on the transport itself could not express.
20
+ * A sale delivery is over when the transport itself is at Done: the transport drives the deal's
21
+ * delivery, not the other way around, so a deal delivered some other way (a CMR or the Deliver
22
+ * action, before that rule) does not lock a transport that never got there. Moving it out of Done
23
+ * unlocks it again.
24
+ *
25
+ * A purchase is over when the vehicle is on our park. The park declares the arrival on the purchase,
26
+ * without the transport ever being moved, and putting the purchase back to At origin is what
27
+ * unlocks the transport again.
25
28
  *
26
29
  * Shared so the board, the drawer and the server all answer the question the same way.
27
30
  */
28
31
  export const isTransportCompleted = (
29
- transport: Pick<Transport, 'type'>,
30
- context: {
31
- opportunity?: Pick<Opportunity, 'deliveredAt'>;
32
- purchase?: { logistics: Pick<VehiclePurchase['logistics'], 'status'> };
33
- },
32
+ transport: Pick<Transport, 'type' | 'status'>,
33
+ context: { purchase?: { logistics: Pick<VehiclePurchase['logistics'], 'status'> } } = {},
34
34
  ): boolean => {
35
35
  if (transport.type === TransportType.Sale) {
36
- return context.opportunity?.deliveredAt != null;
36
+ return transport.status === TransportStatus.Done;
37
37
  }
38
38
 
39
39
  return context.purchase != null && hasPurchaseArrived(context.purchase);
40
40
  };
41
41
 
42
- /** The same question asked of a search document, which already carries both sides. */
42
+ /** The same question asked of a search document, which already carries the purchase. */
43
43
  export const isSearchTransportCompleted = (
44
44
  record: PickFieldIds<
45
45
  SearchTransport,
46
- 'transport.type' | 'opportunity.deliveredAt' | 'purchase.logistics.status'
46
+ 'transport.type' | 'transport.status' | 'purchase.logistics.status'
47
47
  >,
48
- ): boolean =>
49
- isTransportCompleted(record.transport, {
50
- opportunity: record.opportunity,
51
- purchase: record.purchase,
52
- });
48
+ ): boolean => isTransportCompleted(record.transport, { purchase: record.purchase });
49
+
50
+ export type TransportDocumentOwner = {
51
+ businessEntityId: BusinessEntityIds.Opportunities | BusinessEntityIds.Vehicles;
52
+ recordId: string;
53
+ };
54
+
55
+ /**
56
+ * Where a transport's documents are filed. A transport owns none: a sale's belong to the deal and a
57
+ * purchase's to the vehicle, whose purchase tab lists them. Undefined for a sale without a deal.
58
+ */
59
+ export const getTransportDocumentOwner = (
60
+ transport: Pick<Transport, 'type' | 'vehicleId' | 'opportunityId'>,
61
+ ): TransportDocumentOwner | undefined => {
62
+ if (isPurchaseTransport(transport)) {
63
+ return { businessEntityId: BusinessEntityIds.Vehicles, recordId: transport.vehicleId };
64
+ }
65
+
66
+ return transport.opportunityId == null
67
+ ? undefined
68
+ : { businessEntityId: BusinessEntityIds.Opportunities, recordId: transport.opportunityId };
69
+ };