@bisondesk/core-sdk 1.0.626 → 1.0.628

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.
Files changed (110) hide show
  1. package/lib/apis/opportunities.d.ts +2 -0
  2. package/lib/apis/opportunities.d.ts.map +1 -1
  3. package/lib/apis/opportunities.js +12 -0
  4. package/lib/apis/opportunities.js.map +1 -1
  5. package/lib/apis/opportunities.test.d.ts +2 -0
  6. package/lib/apis/opportunities.test.d.ts.map +1 -0
  7. package/lib/apis/opportunities.test.js +53 -0
  8. package/lib/apis/opportunities.test.js.map +1 -0
  9. package/lib/types/internet-opportunities.d.ts +1 -1
  10. package/lib/types/internet-opportunities.d.ts.map +1 -1
  11. package/lib/types/internet-opportunities.js.map +1 -1
  12. package/lib/types/opportunities.d.ts +4 -1
  13. package/lib/types/opportunities.d.ts.map +1 -1
  14. package/lib/types/opportunities.js +2 -0
  15. package/lib/types/opportunities.js.map +1 -1
  16. package/lib/types/tenants.d.ts +1 -0
  17. package/lib/types/tenants.d.ts.map +1 -1
  18. package/lib/types/tenants.js.map +1 -1
  19. package/lib/utils/opportunities.d.ts +1 -0
  20. package/lib/utils/opportunities.d.ts.map +1 -1
  21. package/lib/utils/opportunities.js +1 -0
  22. package/lib/utils/opportunities.js.map +1 -1
  23. package/lib/utils/tenants.d.ts +1 -0
  24. package/lib/utils/tenants.d.ts.map +1 -1
  25. package/lib/utils/tenants.js +1 -0
  26. package/lib/utils/tenants.js.map +1 -1
  27. package/lib/utils/vehicles.d.ts +2 -0
  28. package/lib/utils/vehicles.d.ts.map +1 -1
  29. package/lib/utils/vehicles.js +2 -0
  30. package/lib/utils/vehicles.js.map +1 -1
  31. package/package.json +2 -2
  32. package/src/apis/opportunities.test.ts +68 -0
  33. package/src/apis/opportunities.ts +14 -0
  34. package/src/types/internet-opportunities.ts +1 -1
  35. package/src/types/opportunities.ts +8 -0
  36. package/src/types/tenants.ts +4 -0
  37. package/src/utils/opportunities.ts +3 -0
  38. package/src/utils/tenants.ts +4 -0
  39. package/src/utils/vehicles.ts +20 -0
  40. package/tsconfig.tsbuildinfo +1 -1
  41. package/src/types/activities.js +0 -13
  42. package/src/types/activities.js.map +0 -1
  43. package/src/types/comparables.js +0 -2
  44. package/src/types/comparables.js.map +0 -1
  45. package/src/types/crm.js +0 -58
  46. package/src/types/crm.js.map +0 -1
  47. package/src/types/dashboard.js +0 -2
  48. package/src/types/dashboard.js.map +0 -1
  49. package/src/types/definitions.js +0 -2
  50. package/src/types/definitions.js.map +0 -1
  51. package/src/types/fields.js +0 -2
  52. package/src/types/fields.js.map +0 -1
  53. package/src/types/insights.js +0 -2
  54. package/src/types/insights.js.map +0 -1
  55. package/src/types/interests.js +0 -6
  56. package/src/types/interests.js.map +0 -1
  57. package/src/types/internal-events.js +0 -18
  58. package/src/types/internal-events.js.map +0 -1
  59. package/src/types/internet-opportunities.js +0 -2
  60. package/src/types/internet-opportunities.js.map +0 -1
  61. package/src/types/internet-vehicles.js +0 -2
  62. package/src/types/internet-vehicles.js.map +0 -1
  63. package/src/types/leasing-administration.js +0 -2
  64. package/src/types/leasing-administration.js.map +0 -1
  65. package/src/types/leasing-debtors.js +0 -2
  66. package/src/types/leasing-debtors.js.map +0 -1
  67. package/src/types/leasing-search.js +0 -2
  68. package/src/types/leasing-search.js.map +0 -1
  69. package/src/types/leasing-settings.js +0 -7
  70. package/src/types/leasing-settings.js.map +0 -1
  71. package/src/types/leasing.js +0 -38
  72. package/src/types/leasing.js.map +0 -1
  73. package/src/types/marketing.js +0 -35
  74. package/src/types/marketing.js.map +0 -1
  75. package/src/types/offers.js +0 -56
  76. package/src/types/opportunities.js +0 -107
  77. package/src/types/opportunities.js.map +0 -1
  78. package/src/types/payments.js +0 -7
  79. package/src/types/picklists.js +0 -2
  80. package/src/types/picklists.js.map +0 -1
  81. package/src/types/quotes.js +0 -42
  82. package/src/types/recently-viewed.js +0 -2
  83. package/src/types/recently-viewed.js.map +0 -1
  84. package/src/types/reservations.js +0 -5
  85. package/src/types/reservations.js.map +0 -1
  86. package/src/types/roi-ratings.js +0 -5
  87. package/src/types/roi-ratings.js.map +0 -1
  88. package/src/types/saved-filters.js +0 -9
  89. package/src/types/saved-filters.js.map +0 -1
  90. package/src/types/search.js +0 -2
  91. package/src/types/search.js.map +0 -1
  92. package/src/types/settings.js +0 -2
  93. package/src/types/settings.js.map +0 -1
  94. package/src/types/templates.js +0 -2
  95. package/src/types/templates.js.map +0 -1
  96. package/src/types/tenants.js +0 -21
  97. package/src/types/tenants.js.map +0 -1
  98. package/src/types/user-performance.js +0 -7
  99. package/src/types/user-performance.js.map +0 -1
  100. package/src/types/utils.js +0 -2
  101. package/src/types/validation.js +0 -2
  102. package/src/types/validation.js.map +0 -1
  103. package/src/types/vehicle-sales.js +0 -6
  104. package/src/types/vehicle-sales.js.map +0 -1
  105. package/src/types/vehicles.js +0 -98
  106. package/src/types/vehicles.js.map +0 -1
  107. package/src/utils/leasing.js +0 -6
  108. package/src/utils/leasing.js.map +0 -1
  109. package/src/utils/opportunities.js +0 -48
  110. package/src/utils/opportunities.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"opportunities.js","sourceRoot":"/","sources":["utils/opportunities.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAOnG,MAAM,wBAAwB,GAAG,CAAC,KAAmB,EAAE,EAAE;IACvD,MAAM,EAAE,aAAa,EAAE,eAAe,EAAE,GAAG,KAAK,CAAC;IAEjD,MAAM,gBAAgB,GAAG,sBAAsB,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACvE,MAAM,kBAAkB,GAAG,sBAAsB,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAE3E,OAAO;QACL,gBAAgB;QAChB,kBAAkB;KACnB,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,aAAgC,EACD,EAAE;IACjC,MAAM,YAAY,GAAG,sBAAsB,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACnE,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO;IACT,CAAC;IACD,OAAO,sBAAsB,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;AAClD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAAyB,EAAiC,EAAE;IACxF,MAAM,WAAW,GAAG,sBAAsB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3D,IAAI,WAAW,IAAI,sBAAsB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrD,OAAO;IACT,CAAC;IACD,OAAO,sBAAsB,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;AACjD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,uCAAuC,GAAG,CAAC,KAAmB,EAAW,EAAE;IACtF,MAAM,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;IAEjF,OAAO,gBAAgB,IAAI,kBAAkB,CAAC;AAChD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,KAAmB,EAAW,EAAE;IAChF,MAAM,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;IAEjF,OAAO,gBAAgB,GAAG,kBAAkB,CAAC;AAC/C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,wCAAwC,GAAG,CAAC,KAAmB,EAAW,EAAE;IACvF,MAAM,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;IAEjF,OAAO,gBAAgB,IAAI,kBAAkB,CAAC;AAChD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kCAAkC,GAAG,CAAC,KAAmB,EAAW,EAAE;IACjF,MAAM,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;IAEjF,OAAO,gBAAgB,GAAG,kBAAkB,CAAC;AAC/C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAAkB,EAAW,EAAE,CAAC,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC;AAEvF,MAAM,CAAC,MAAM,4BAA4B,GAAG,GAAwB,EAAE,CAAC;IACrE,iBAAiB,CAAC,MAAM;IACxB,iBAAiB,CAAC,WAAW;IAC7B,iBAAiB,CAAC,QAAQ;CAC3B,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,KAAkB,EAAW,EAAE,CACnE,4BAA4B,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AASxD,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,KAAkB,EAAW,EAAE,CAC3E,qBAAqB,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;AAO5D,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAC7C,KAA8B,EACV,EAAE,CACtB,KAAK,IAAI,IAAI,IAAI,6BAA6B,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC","sourcesContent":["import { Opportunity, OpportunityStatus, OpportunityStatusOrder } from '../types/opportunities.js';\n\ntype CompareInput = {\n currentStatus: OpportunityStatus;\n referenceStatus: OpportunityStatus;\n};\n\nconst compareOpportunityStatus = (input: CompareInput) => {\n const { currentStatus, referenceStatus } = input;\n\n const currentStatusIdx = OpportunityStatusOrder.indexOf(currentStatus);\n const referenceStatusIdx = OpportunityStatusOrder.indexOf(referenceStatus);\n\n return {\n currentStatusIdx,\n referenceStatusIdx,\n };\n};\n\nexport const getPreviousStatus = (\n currentStatus: OpportunityStatus,\n): OpportunityStatus | undefined => {\n const currentIndex = OpportunityStatusOrder.indexOf(currentStatus);\n if (currentIndex === 0) {\n return;\n }\n return OpportunityStatusOrder[currentIndex - 1];\n};\n\nexport const getNextStatus = (status: OpportunityStatus): OpportunityStatus | undefined => {\n const statusIndex = OpportunityStatusOrder.indexOf(status);\n if (statusIndex >= OpportunityStatusOrder.length - 1) {\n return;\n }\n return OpportunityStatusOrder[statusIndex + 1];\n};\n\nexport const isSameOrAfterReferenceOpportunityStatus = (input: CompareInput): boolean => {\n const { currentStatusIdx, referenceStatusIdx } = compareOpportunityStatus(input);\n\n return currentStatusIdx >= referenceStatusIdx;\n};\n\nexport const isAfterReferenceOpportunityStatus = (input: CompareInput): boolean => {\n const { currentStatusIdx, referenceStatusIdx } = compareOpportunityStatus(input);\n\n return currentStatusIdx > referenceStatusIdx;\n};\n\nexport const isSameOrBeforeReferenceOpportunityStatus = (input: CompareInput): boolean => {\n const { currentStatusIdx, referenceStatusIdx } = compareOpportunityStatus(input);\n\n return currentStatusIdx <= referenceStatusIdx;\n};\n\nexport const isBeforeReferenceOpportunityStatus = (input: CompareInput): boolean => {\n const { currentStatusIdx, referenceStatusIdx } = compareOpportunityStatus(input);\n\n return currentStatusIdx < referenceStatusIdx;\n};\n\nexport const isOpportunityLost = (input: Opportunity): boolean => input.lostAt != null;\n\nexport const getOpportunityReservedStatus = (): OpportunityStatus[] => [\n OpportunityStatus.REVIEW,\n OpportunityStatus.PREPARATION,\n OpportunityStatus.DELIVERY,\n];\n\nexport const isOpportunityReserved = (input: Opportunity): boolean =>\n getOpportunityReservedStatus().includes(input.status);\n\n/**\n * Whether the opportunity currently reserves the vehicle attached to it: reserved-stage\n * (REVIEW onwards) and not lost — independent of whether a reservation row exists. Query-side\n * twin: `getOpportunityReservingVehicle` in core's opportunities/read.ts. Availability\n * propagation, warnings and guards must share this one definition rather than re-composing\n * the two primitives.\n */\nexport const isOpportunityReservingVehicle = (input: Opportunity): boolean =>\n isOpportunityReserved(input) && !isOpportunityLost(input);\n\n/**\n * The vehicle the opportunity currently reserves, if any — undefined outside the reserved\n * range, when lost, without a vehicle, or for a missing opportunity. Compare this value\n * before/after an update to detect a vehicle being taken or freed.\n */\nexport const getOpportunityReservedVehicleId = (\n input: Opportunity | undefined,\n): string | undefined =>\n input != null && isOpportunityReservingVehicle(input) ? input.vehicleId : undefined;\n"]}
1
+ {"version":3,"file":"opportunities.js","sourceRoot":"/","sources":["utils/opportunities.ts"],"names":[],"mappings":"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","sourcesContent":["import { Opportunity, OpportunityStatus, OpportunityStatusOrder } from '../types/opportunities.js';\n\ntype CompareInput = {\n currentStatus: OpportunityStatus;\n referenceStatus: OpportunityStatus;\n};\n\nconst compareOpportunityStatus = (input: CompareInput) => {\n const { currentStatus, referenceStatus } = input;\n\n const currentStatusIdx = OpportunityStatusOrder.indexOf(currentStatus);\n const referenceStatusIdx = OpportunityStatusOrder.indexOf(referenceStatus);\n\n return {\n currentStatusIdx,\n referenceStatusIdx,\n };\n};\n\nexport const getPreviousStatus = (\n currentStatus: OpportunityStatus,\n): OpportunityStatus | undefined => {\n const currentIndex = OpportunityStatusOrder.indexOf(currentStatus);\n if (currentIndex === 0) {\n return;\n }\n return OpportunityStatusOrder[currentIndex - 1];\n};\n\nexport const getNextStatus = (status: OpportunityStatus): OpportunityStatus | undefined => {\n const statusIndex = OpportunityStatusOrder.indexOf(status);\n if (statusIndex >= OpportunityStatusOrder.length - 1) {\n return;\n }\n return OpportunityStatusOrder[statusIndex + 1];\n};\n\nexport const isSameOrAfterReferenceOpportunityStatus = (input: CompareInput): boolean => {\n const { currentStatusIdx, referenceStatusIdx } = compareOpportunityStatus(input);\n\n return currentStatusIdx >= referenceStatusIdx;\n};\n\nexport const isAfterReferenceOpportunityStatus = (input: CompareInput): boolean => {\n const { currentStatusIdx, referenceStatusIdx } = compareOpportunityStatus(input);\n\n return currentStatusIdx > referenceStatusIdx;\n};\n\nexport const isSameOrBeforeReferenceOpportunityStatus = (input: CompareInput): boolean => {\n const { currentStatusIdx, referenceStatusIdx } = compareOpportunityStatus(input);\n\n return currentStatusIdx <= referenceStatusIdx;\n};\n\nexport const isBeforeReferenceOpportunityStatus = (input: CompareInput): boolean => {\n const { currentStatusIdx, referenceStatusIdx } = compareOpportunityStatus(input);\n\n return currentStatusIdx < referenceStatusIdx;\n};\n\nexport const isOpportunityLost = (input: Opportunity): boolean => input.lostAt != null;\n\nexport const isOpportunityClosed = (input: Opportunity): boolean =>\n input.lostAt != null || input.wonAt != null;\n\nexport const getOpportunityReservedStatus = (): OpportunityStatus[] => [\n OpportunityStatus.REVIEW,\n OpportunityStatus.PREPARATION,\n OpportunityStatus.DELIVERY,\n];\n\nexport const isOpportunityReserved = (input: Opportunity): boolean =>\n getOpportunityReservedStatus().includes(input.status);\n\n/**\n * Whether the opportunity currently reserves the vehicle attached to it: reserved-stage\n * (REVIEW onwards) and not lost — independent of whether a reservation row exists. Query-side\n * twin: `getOpportunityReservingVehicle` in core's opportunities/read.ts. Availability\n * propagation, warnings and guards must share this one definition rather than re-composing\n * the two primitives.\n */\nexport const isOpportunityReservingVehicle = (input: Opportunity): boolean =>\n isOpportunityReserved(input) && !isOpportunityLost(input);\n\n/**\n * The vehicle the opportunity currently reserves, if any — undefined outside the reserved\n * range, when lost, without a vehicle, or for a missing opportunity. Compare this value\n * before/after an update to detect a vehicle being taken or freed.\n */\nexport const getOpportunityReservedVehicleId = (\n input: Opportunity | undefined,\n): string | undefined =>\n input != null && isOpportunityReservingVehicle(input) ? input.vehicleId : undefined;\n"]}
@@ -1,5 +1,6 @@
1
1
  import { Branch, Tenant } from '../types/tenants.js';
2
2
  export declare const getBranch: (tenant: Tenant, branchId: string) => Branch;
3
3
  export declare const getDefaultBranch: (tenant: Tenant) => Branch;
4
+ export declare const isDropshippingBranch: (branch: Branch) => boolean;
4
5
  export declare const getBranchStandardVatRate: (tenant: Tenant, branchId: string) => string;
5
6
  //# sourceMappingURL=tenants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tenants.d.ts","sourceRoot":"/","sources":["utils/tenants.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAErD,eAAO,MAAM,SAAS,GAAI,QAAQ,MAAM,EAAE,UAAU,MAAM,KAAG,MAO5D,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,QAAQ,MAAM,KAAG,MAAiC,CAAC;AAEpF,eAAO,MAAM,wBAAwB,GAAI,QAAQ,MAAM,EAAE,UAAU,MAAM,KAAG,MAG3E,CAAC"}
1
+ {"version":3,"file":"tenants.d.ts","sourceRoot":"/","sources":["utils/tenants.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAErD,eAAO,MAAM,SAAS,GAAI,QAAQ,MAAM,EAAE,UAAU,MAAM,KAAG,MAO5D,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,QAAQ,MAAM,KAAG,MAAiC,CAAC;AAIpF,eAAO,MAAM,oBAAoB,GAAI,QAAQ,MAAM,KAAG,OAAuC,CAAC;AAE9F,eAAO,MAAM,wBAAwB,GAAI,QAAQ,MAAM,EAAE,UAAU,MAAM,KAAG,MAG3E,CAAC"}
@@ -8,6 +8,7 @@ export const getBranch = (tenant, branchId) => {
8
8
  return branch;
9
9
  };
10
10
  export const getDefaultBranch = (tenant) => first(tenant.branches);
11
+ export const isDropshippingBranch = (branch) => branch.dropshipping === true;
11
12
  export const getBranchStandardVatRate = (tenant, branchId) => {
12
13
  const branch = getBranch(tenant, branchId);
13
14
  return branch.vatRates.standard;
@@ -1 +1 @@
1
- {"version":3,"file":"tenants.js","sourceRoot":"/","sources":["utils/tenants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AACvD,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAGlC,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,MAAc,EAAE,QAAgB,EAAU,EAAE;IACpE,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAc,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC;IAChF,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,MAAM,CAAC,0BAA0B,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;IAClF,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,MAAc,EAAU,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAE,CAAC;AAEpF,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,MAAc,EAAE,QAAgB,EAAU,EAAE;IACnF,MAAM,MAAM,GAAW,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACnD,OAAO,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;AAClC,CAAC,CAAC","sourcesContent":["import { XError } from '@bisondesk/commons-sdk/errors';\nimport { first } from 'lodash-es';\nimport { Branch, Tenant } from '../types/tenants.js';\n\nexport const getBranch = (tenant: Tenant, branchId: string): Branch => {\n const branch = tenant.branches.find((branch: Branch) => branch.id === branchId);\n if (branch == null) {\n throw new XError('tenants.branch-not-found', { tenantId: tenant.id, branchId });\n }\n\n return branch;\n};\n\nexport const getDefaultBranch = (tenant: Tenant): Branch => first(tenant.branches)!;\n\nexport const getBranchStandardVatRate = (tenant: Tenant, branchId: string): string => {\n const branch: Branch = getBranch(tenant, branchId);\n return branch.vatRates.standard;\n};\n"]}
1
+ {"version":3,"file":"tenants.js","sourceRoot":"/","sources":["utils/tenants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AACvD,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAGlC,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,MAAc,EAAE,QAAgB,EAAU,EAAE;IACpE,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAc,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC;IAChF,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,MAAM,CAAC,0BAA0B,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;IAClF,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,MAAc,EAAU,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAE,CAAC;AAIpF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,MAAc,EAAW,EAAE,CAAC,MAAM,CAAC,YAAY,KAAK,IAAI,CAAC;AAE9F,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,MAAc,EAAE,QAAgB,EAAU,EAAE;IACnF,MAAM,MAAM,GAAW,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACnD,OAAO,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;AAClC,CAAC,CAAC","sourcesContent":["import { XError } from '@bisondesk/commons-sdk/errors';\nimport { first } from 'lodash-es';\nimport { Branch, Tenant } from '../types/tenants.js';\n\nexport const getBranch = (tenant: Tenant, branchId: string): Branch => {\n const branch = tenant.branches.find((branch: Branch) => branch.id === branchId);\n if (branch == null) {\n throw new XError('tenants.branch-not-found', { tenantId: tenant.id, branchId });\n }\n\n return branch;\n};\n\nexport const getDefaultBranch = (tenant: Tenant): Branch => first(tenant.branches)!;\n\n/** The source invoices the end customer directly; this branch collects payment and earns\n * commission (see {@link Branch.dropshipping}). */\nexport const isDropshippingBranch = (branch: Branch): boolean => branch.dropshipping === true;\n\nexport const getBranchStandardVatRate = (tenant: Tenant, branchId: string): string => {\n const branch: Branch = getBranch(tenant, branchId);\n return branch.vatRates.standard;\n};\n"]}
@@ -2,6 +2,7 @@ import { Vehicle, VehicleExternalInfo, VehiclePurchase } from '../types/vehicles
2
2
  export declare const isDealInMarketing: (purchase: VehiclePurchase | undefined) => boolean;
3
3
  export declare const isSourceMastered: (vehicle: Pick<Vehicle, "original">, purchase: VehiclePurchase | undefined) => boolean;
4
4
  export declare const isUnavailableFromSource: (vehicle: Pick<Vehicle, "original">, purchase: VehiclePurchase | undefined) => boolean;
5
+ export declare const isNewTrailer: (vehicle: Pick<Vehicle, "external">) => boolean;
5
6
  export declare const extractCustomFeatures: (vehicle: VehicleExternalInfo) => string[];
6
7
  export declare const getInternalTitle: ({ make, category, modelName, bodywork, modelTypeName, }: {
7
8
  category: string;
@@ -12,6 +13,7 @@ export declare const getInternalTitle: ({ make, category, modelName, bodywork, m
12
13
  }) => string;
13
14
  export declare const convertNumberToLetters: (num: number) => string;
14
15
  export declare const convertLettersToNumber: (letters: string) => number;
16
+ export declare const buildStockNumberPattern: (prefixPattern: string) => string;
15
17
  export declare const getNextRelatedStockNumber: (stockNumber: string) => string;
16
18
  export declare const getVehicleDescription: (external: VehicleExternalInfo, { showStockNumber, showAxleConfiguration, lang, }: {
17
19
  showStockNumber?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"vehicles.d.ts","sourceRoot":"/","sources":["utils/vehicles.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,OAAO,EACP,mBAAmB,EACnB,eAAe,EAEhB,MAAM,sBAAsB,CAAC;AAO9B,eAAO,MAAM,iBAAiB,GAAI,UAAU,eAAe,GAAG,SAAS,KAAG,OACQ,CAAC;AASnF,eAAO,MAAM,gBAAgB,GAC3B,SAAS,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAClC,UAAU,eAAe,GAAG,SAAS,KACpC,OAAkE,CAAC;AAQtE,eAAO,MAAM,uBAAuB,GAClC,SAAS,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAClC,UAAU,eAAe,GAAG,SAAS,KACpC,OAC+F,CAAC;AAEnG,eAAO,MAAM,qBAAqB,GAAI,SAAS,mBAAmB,KAAG,MAAM,EAiD1E,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,yDAM9B;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,KAAG,MAcH,CAAC;AAKF,eAAO,MAAM,sBAAsB,GAAI,KAAK,MAAM,KAAG,MAiBpD,CAAC;AAGF,eAAO,MAAM,sBAAsB,GAAI,SAAS,MAAM,KAAG,MAWxD,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAI,aAAa,MAAM,KAAG,MAa/D,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,UAAU,mBAAmB,EAC7B,mDAIG;IACD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,WAyBF,CAAC"}
1
+ {"version":3,"file":"vehicles.d.ts","sourceRoot":"/","sources":["utils/vehicles.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,OAAO,EACP,mBAAmB,EACnB,eAAe,EAEhB,MAAM,sBAAsB,CAAC;AAO9B,eAAO,MAAM,iBAAiB,GAAI,UAAU,eAAe,GAAG,SAAS,KAAG,OACQ,CAAC;AASnF,eAAO,MAAM,gBAAgB,GAC3B,SAAS,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAClC,UAAU,eAAe,GAAG,SAAS,KACpC,OAAkE,CAAC;AAQtE,eAAO,MAAM,uBAAuB,GAClC,SAAS,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAClC,UAAU,eAAe,GAAG,SAAS,KACpC,OAC+F,CAAC;AAOnG,eAAO,MAAM,YAAY,GAAI,SAAS,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,KAAG,OAG1B,CAAC;AAEzC,eAAO,MAAM,qBAAqB,GAAI,SAAS,mBAAmB,KAAG,MAAM,EAiD1E,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,yDAM9B;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,KAAG,MAcH,CAAC;AAKF,eAAO,MAAM,sBAAsB,GAAI,KAAK,MAAM,KAAG,MAiBpD,CAAC;AAGF,eAAO,MAAM,sBAAsB,GAAI,SAAS,MAAM,KAAG,MAWxD,CAAC;AASF,eAAO,MAAM,uBAAuB,GAAI,eAAe,MAAM,KAAG,MACxB,CAAC;AAEzC,eAAO,MAAM,yBAAyB,GAAI,aAAa,MAAM,KAAG,MAa/D,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,UAAU,mBAAmB,EAC7B,mDAIG;IACD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,WAyBF,CAAC"}
@@ -5,6 +5,7 @@ import { VehiclePurchaseDealStatus, } from '../types/vehicles.js';
5
5
  export const isDealInMarketing = (purchase) => purchase == null || purchase.deal.status === VehiclePurchaseDealStatus.Marketing;
6
6
  export const isSourceMastered = (vehicle, purchase) => vehicle.original != null && isDealInMarketing(purchase);
7
7
  export const isUnavailableFromSource = (vehicle, purchase) => isSourceMastered(vehicle, purchase) && (vehicle.original?.salesConditions?.unavailable ?? false);
8
+ export const isNewTrailer = (vehicle) => [Categories.Trailer, Categories.SemiTrailer].includes(vehicle.external.general.category) && !vehicle.external.condition?.used;
8
9
  export const extractCustomFeatures = (vehicle) => {
9
10
  const features = [
10
11
  {
@@ -87,6 +88,7 @@ export const convertLettersToNumber = (letters) => {
87
88
  }
88
89
  return result;
89
90
  };
91
+ export const buildStockNumberPattern = (prefixPattern) => `(?:${prefixPattern})\\d{5}[A-Z]{0,2}`;
90
92
  export const getNextRelatedStockNumber = (stockNumber) => {
91
93
  const counterPattern = /^([A-Z]+\d{5})([A-Z]{0,2})$/;
92
94
  const match = stockNumber.match(counterPattern);
@@ -1 +1 @@
1
- {"version":3,"file":"vehicles.js","sourceRoot":"/","sources":["utils/vehicles.ts"],"names":[],"mappings":"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","sourcesContent":["import { XError } from '@bisondesk/commons-sdk/errors';\nimport { joinWithSeparator } from '@bisondesk/commons-sdk/formatting';\nimport { Categories } from '../constants.js';\nimport {\n Vehicle,\n VehicleExternalInfo,\n VehiclePurchase,\n VehiclePurchaseDealStatus,\n} from '../types/vehicles.js';\n\n/**\n * The deal has not been committed: there is no purchase yet, or its deal is still in\n * `Marketing`. Copies are created in this state; it ends when the owner agrees to buy\n * (e.g. an ICO invoice moves the deal to PurchaseAgreed).\n */\nexport const isDealInMarketing = (purchase: VehiclePurchase | undefined): boolean =>\n purchase == null || purchase.deal.status === VehiclePurchaseDealStatus.Marketing;\n\n/**\n * A cross-tenant copy whose source still masters it: it has an `original` link and its deal is\n * still in Marketing ({@link isDealInMarketing}). While this holds, the next sync overwrites\n * `external` (specs, pictures, videos) and `salesConditions` from the source; once the deal\n * progresses, the vehicle is the owner's own stock and the sync hands off. Native vehicles are\n * never source-mastered.\n */\nexport const isSourceMastered = (\n vehicle: Pick<Vehicle, 'original'>,\n purchase: VehiclePurchase | undefined,\n): boolean => vehicle.original != null && isDealInMarketing(purchase);\n\n/**\n * A cross-tenant copy whose source is not sellable, as stamped on the last sync hop. Only\n * meaningful while the source still masters the copy ({@link isSourceMastered}): past Marketing\n * the vehicle is the owner's own stock, and a still-present flag is merely stale — the sync\n * clears it on the next source event — so it must be ignored.\n */\nexport const isUnavailableFromSource = (\n vehicle: Pick<Vehicle, 'original'>,\n purchase: VehiclePurchase | undefined,\n): boolean =>\n isSourceMastered(vehicle, purchase) && (vehicle.original?.salesConditions?.unavailable ?? false);\n\nexport const extractCustomFeatures = (vehicle: VehicleExternalInfo): string[] => {\n const features: {\n getValue: (vehicle: VehicleExternalInfo) => any;\n picklistValueId: string;\n }[] = [\n {\n getValue: (vehicle) => vehicle.superstructure?.crane?.present,\n picklistValueId: 'crane',\n },\n {\n getValue: (vehicle) => vehicle.superstructure?.tailgate?.present,\n picklistValueId: 'tailgate',\n },\n { getValue: (vehicle) => vehicle.accessories.adr, picklistValueId: 'adr' },\n {\n getValue: (vehicle) => vehicle.accessories.retarderIntarder,\n picklistValueId: 'retarderIntarder',\n },\n {\n getValue: (vehicle) => vehicle.accessories.hydraulicTipperKit,\n picklistValueId: 'hydraulicTipperKit',\n },\n {\n getValue: (vehicle) => vehicle.accessories.lowDeck,\n picklistValueId: 'lowDeck',\n },\n {\n getValue: (vehicle) => vehicle.accessories.coil,\n picklistValueId: 'coil',\n },\n {\n getValue: (vehicle) => vehicle.accessories.doubleStock,\n picklistValueId: 'doubleStock',\n },\n {\n getValue: (vehicle) => vehicle.accessories.mega,\n picklistValueId: 'mega',\n },\n {\n getValue: (vehicle) =>\n vehicle.salesConditions?.manufacturerWarranty?.available ||\n vehicle.salesConditions?.manufacturerWarranty?.included,\n picklistValueId: 'warranty',\n },\n ];\n\n return features\n .filter((feature) => !!feature.getValue(vehicle))\n .map((feature) => feature.picklistValueId);\n};\n\nexport const getInternalTitle = ({\n make,\n category,\n modelName,\n bodywork,\n modelTypeName,\n}: {\n category: string;\n make?: string;\n modelName?: string;\n modelTypeName?: string;\n bodywork?: string;\n}): string => {\n if (category === Categories.Trailer || category === Categories.SemiTrailer) {\n return [make, modelName, modelTypeName, bodywork]\n .filter((v): v is string => !!v)\n .map((v) => v.trim())\n .join(' ')\n .trim();\n }\n\n return [make, modelName, modelTypeName]\n .filter((v): v is string => !!v)\n .map((v) => v.trim())\n .join(' ')\n .trim();\n};\n\nconst ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';\n\n// example: 1 -> A, 2 -> B, 26 -> Z, 27 -> AA, 28 -> AB, 52 -> AZ, 53 -> BA, 54 -> BB, 702 -> ZZ\nexport const convertNumberToLetters = (num: number): string => {\n const lettersLength = ALPHABET.length;\n\n if (num < 1) {\n return '';\n }\n\n let remainder = num % lettersLength;\n let quotient = Math.floor(num / lettersLength);\n\n // If remainder is 0, it means we should map it to 'Z' and reduce the quotient by 1\n if (remainder === 0) {\n remainder = lettersLength;\n quotient -= 1;\n }\n\n return convertNumberToLetters(quotient) + ALPHABET.charAt(remainder - 1);\n};\n\n// example: A -> 1, B -> 2, Z -> 26, AA -> 27, AB -> 28, AZ -> 52, BA -> 53, BB -> 54, ZZ -> 702\nexport const convertLettersToNumber = (letters: string): number => {\n const base = ALPHABET.length;\n\n let result = 0;\n\n for (let i = 0; i < letters.length; i++) {\n const charValue = ALPHABET.indexOf(letters[i]) + 1;\n result = result * base + charValue;\n }\n\n return result;\n};\n\nexport const getNextRelatedStockNumber = (stockNumber: string): string => {\n const counterPattern = /^([A-Z]+\\d{5})([A-Z]{0,2})$/;\n const match = stockNumber.match(counterPattern);\n if (match == null) {\n throw new XError('core-sdk.vehicles.bad-stock-number', { stockNumber });\n }\n\n const baseStockNumber = match[1];\n const counterLetters = match[2];\n const counterNumber = convertLettersToNumber(counterLetters);\n const nextCounterNumber = counterNumber + 1;\n const nextCounterLetters = convertNumberToLetters(nextCounterNumber);\n return `${baseStockNumber}${nextCounterLetters}`;\n};\n\nexport const getVehicleDescription = (\n external: VehicleExternalInfo,\n {\n showStockNumber = false,\n showAxleConfiguration = false,\n lang,\n }: {\n showStockNumber?: boolean;\n showAxleConfiguration?: boolean;\n lang?: string;\n },\n) => {\n const dimensions = [\n external.superstructure?.dimensions?.extended,\n external.superstructure?.dimensions?.heightened,\n ]\n .filter((value) => !!value)\n .join('');\n const kmFormatter = Intl.NumberFormat(lang, {\n style: 'unit',\n unit: 'kilometer',\n });\n\n const text = joinWithSeparator([\n showStockNumber ? external.identification.stockNumber : undefined,\n external.history?.advertisingYear,\n external.condition?.odometer?.km != null\n ? kmFormatter.format(external.condition.odometer.km)\n : undefined,\n external.powertrain?.emissions?.subclass ?? external.powertrain?.emissions?.class,\n dimensions,\n showAxleConfiguration ? external.powertrain?.axles?.configuration : undefined,\n ]);\n\n return text;\n};\n"]}
1
+ {"version":3,"file":"vehicles.js","sourceRoot":"/","sources":["utils/vehicles.ts"],"names":[],"mappings":"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","sourcesContent":["import { XError } from '@bisondesk/commons-sdk/errors';\nimport { joinWithSeparator } from '@bisondesk/commons-sdk/formatting';\nimport { Categories } from '../constants.js';\nimport {\n Vehicle,\n VehicleExternalInfo,\n VehiclePurchase,\n VehiclePurchaseDealStatus,\n} from '../types/vehicles.js';\n\n/**\n * The deal has not been committed: there is no purchase yet, or its deal is still in\n * `Marketing`. Copies are created in this state; it ends when the owner agrees to buy\n * (e.g. an ICO invoice moves the deal to PurchaseAgreed).\n */\nexport const isDealInMarketing = (purchase: VehiclePurchase | undefined): boolean =>\n purchase == null || purchase.deal.status === VehiclePurchaseDealStatus.Marketing;\n\n/**\n * A cross-tenant copy whose source still masters it: it has an `original` link and its deal is\n * still in Marketing ({@link isDealInMarketing}). While this holds, the next sync overwrites\n * `external` (specs, pictures, videos) and `salesConditions` from the source; once the deal\n * progresses, the vehicle is the owner's own stock and the sync hands off. Native vehicles are\n * never source-mastered.\n */\nexport const isSourceMastered = (\n vehicle: Pick<Vehicle, 'original'>,\n purchase: VehiclePurchase | undefined,\n): boolean => vehicle.original != null && isDealInMarketing(purchase);\n\n/**\n * A cross-tenant copy whose source is not sellable, as stamped on the last sync hop. Only\n * meaningful while the source still masters the copy ({@link isSourceMastered}): past Marketing\n * the vehicle is the owner's own stock, and a still-present flag is merely stale — the sync\n * clears it on the next source event — so it must be ignored.\n */\nexport const isUnavailableFromSource = (\n vehicle: Pick<Vehicle, 'original'>,\n purchase: VehiclePurchase | undefined,\n): boolean =>\n isSourceMastered(vehicle, purchase) && (vehicle.original?.salesConditions?.unavailable ?? false);\n\n/**\n * A trailer or semi-trailer that has never been used — what the sales ladder calls a new\n * trailer, and the reason it has opportunity types of its own (`NEW_TRAILER`,\n * `NEW_TRAILER_NEW_PRODUCTION`) instead of going through plain sales.\n */\nexport const isNewTrailer = (vehicle: Pick<Vehicle, 'external'>): boolean =>\n [Categories.Trailer, Categories.SemiTrailer].includes(\n vehicle.external.general.category as Categories,\n ) && !vehicle.external.condition?.used;\n\nexport const extractCustomFeatures = (vehicle: VehicleExternalInfo): string[] => {\n const features: {\n getValue: (vehicle: VehicleExternalInfo) => any;\n picklistValueId: string;\n }[] = [\n {\n getValue: (vehicle) => vehicle.superstructure?.crane?.present,\n picklistValueId: 'crane',\n },\n {\n getValue: (vehicle) => vehicle.superstructure?.tailgate?.present,\n picklistValueId: 'tailgate',\n },\n { getValue: (vehicle) => vehicle.accessories.adr, picklistValueId: 'adr' },\n {\n getValue: (vehicle) => vehicle.accessories.retarderIntarder,\n picklistValueId: 'retarderIntarder',\n },\n {\n getValue: (vehicle) => vehicle.accessories.hydraulicTipperKit,\n picklistValueId: 'hydraulicTipperKit',\n },\n {\n getValue: (vehicle) => vehicle.accessories.lowDeck,\n picklistValueId: 'lowDeck',\n },\n {\n getValue: (vehicle) => vehicle.accessories.coil,\n picklistValueId: 'coil',\n },\n {\n getValue: (vehicle) => vehicle.accessories.doubleStock,\n picklistValueId: 'doubleStock',\n },\n {\n getValue: (vehicle) => vehicle.accessories.mega,\n picklistValueId: 'mega',\n },\n {\n getValue: (vehicle) =>\n vehicle.salesConditions?.manufacturerWarranty?.available ||\n vehicle.salesConditions?.manufacturerWarranty?.included,\n picklistValueId: 'warranty',\n },\n ];\n\n return features\n .filter((feature) => !!feature.getValue(vehicle))\n .map((feature) => feature.picklistValueId);\n};\n\nexport const getInternalTitle = ({\n make,\n category,\n modelName,\n bodywork,\n modelTypeName,\n}: {\n category: string;\n make?: string;\n modelName?: string;\n modelTypeName?: string;\n bodywork?: string;\n}): string => {\n if (category === Categories.Trailer || category === Categories.SemiTrailer) {\n return [make, modelName, modelTypeName, bodywork]\n .filter((v): v is string => !!v)\n .map((v) => v.trim())\n .join(' ')\n .trim();\n }\n\n return [make, modelName, modelTypeName]\n .filter((v): v is string => !!v)\n .map((v) => v.trim())\n .join(' ')\n .trim();\n};\n\nconst ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';\n\n// example: 1 -> A, 2 -> B, 26 -> Z, 27 -> AA, 28 -> AB, 52 -> AZ, 53 -> BA, 54 -> BB, 702 -> ZZ\nexport const convertNumberToLetters = (num: number): string => {\n const lettersLength = ALPHABET.length;\n\n if (num < 1) {\n return '';\n }\n\n let remainder = num % lettersLength;\n let quotient = Math.floor(num / lettersLength);\n\n // If remainder is 0, it means we should map it to 'Z' and reduce the quotient by 1\n if (remainder === 0) {\n remainder = lettersLength;\n quotient -= 1;\n }\n\n return convertNumberToLetters(quotient) + ALPHABET.charAt(remainder - 1);\n};\n\n// example: A -> 1, B -> 2, Z -> 26, AA -> 27, AB -> 28, AZ -> 52, BA -> 53, BB -> 54, ZZ -> 702\nexport const convertLettersToNumber = (letters: string): number => {\n const base = ALPHABET.length;\n\n let result = 0;\n\n for (let i = 0; i < letters.length; i++) {\n const charValue = ALPHABET.indexOf(letters[i]) + 1;\n result = result * base + charValue;\n }\n\n return result;\n};\n\n/**\n * Canonical stock-number shape: a branch prefix of 1 to 3 letters followed by exactly 5 digits\n * and up to 2 trailing counter letters (e.g. B49397AB, TC12345 — see\n * {@link getNextRelatedStockNumber}). Callers supply the prefix pattern (concrete branch\n * prefixes, or a policy like `[A-Z]{2}`) and wrap their own boundary/anchor policy around the\n * returned pattern.\n */\nexport const buildStockNumberPattern = (prefixPattern: string): string =>\n `(?:${prefixPattern})\\\\d{5}[A-Z]{0,2}`;\n\nexport const getNextRelatedStockNumber = (stockNumber: string): string => {\n const counterPattern = /^([A-Z]+\\d{5})([A-Z]{0,2})$/;\n const match = stockNumber.match(counterPattern);\n if (match == null) {\n throw new XError('core-sdk.vehicles.bad-stock-number', { stockNumber });\n }\n\n const baseStockNumber = match[1];\n const counterLetters = match[2];\n const counterNumber = convertLettersToNumber(counterLetters);\n const nextCounterNumber = counterNumber + 1;\n const nextCounterLetters = convertNumberToLetters(nextCounterNumber);\n return `${baseStockNumber}${nextCounterLetters}`;\n};\n\nexport const getVehicleDescription = (\n external: VehicleExternalInfo,\n {\n showStockNumber = false,\n showAxleConfiguration = false,\n lang,\n }: {\n showStockNumber?: boolean;\n showAxleConfiguration?: boolean;\n lang?: string;\n },\n) => {\n const dimensions = [\n external.superstructure?.dimensions?.extended,\n external.superstructure?.dimensions?.heightened,\n ]\n .filter((value) => !!value)\n .join('');\n const kmFormatter = Intl.NumberFormat(lang, {\n style: 'unit',\n unit: 'kilometer',\n });\n\n const text = joinWithSeparator([\n showStockNumber ? external.identification.stockNumber : undefined,\n external.history?.advertisingYear,\n external.condition?.odometer?.km != null\n ? kmFormatter.format(external.condition.odometer.km)\n : undefined,\n external.powertrain?.emissions?.subclass ?? external.powertrain?.emissions?.class,\n dimensions,\n showAxleConfiguration ? external.powertrain?.axles?.configuration : undefined,\n ]);\n\n return text;\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bisondesk/core-sdk",
3
- "version": "1.0.626",
3
+ "version": "1.0.628",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "author": "TheTruckCompany",
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "scripts": {
38
38
  "clean": "rm -rf build dist lib *.tsbuildinfo",
39
- "unittest": "echo \"No test specified\"",
39
+ "unittest": "npx vitest run src/",
40
40
  "integrationtest": "echo \"No test specified\"",
41
41
  "build": "npx tsc --build tsconfig.json",
42
42
  "watch": "npx tsc --build --watch"
@@ -0,0 +1,68 @@
1
+ import { TENANT_ID_ADMIN_HEADER } from '@bisondesk/commons-sdk/constants';
2
+ import { XError } from '@bisondesk/commons-sdk/errors';
3
+ import { InternetOpportunityEvent } from '../types/internet-opportunities.js';
4
+ import { createInternetOpportunity } from './opportunities.js';
5
+
6
+ const ADMIN_KEY = 'test-admin-key';
7
+ const ORIGIN = 'https://core.example.com';
8
+
9
+ const event: InternetOpportunityEvent = {
10
+ tenantId: 'ttc',
11
+ branchId: 'ttc-nl',
12
+ name: 'John Doe',
13
+ phoneNumber: '+31612345678',
14
+ source: 'website',
15
+ };
16
+
17
+ const mockFetch = vi.fn();
18
+
19
+ const okResponse = (): Response =>
20
+ ({
21
+ ok: true,
22
+ status: 202,
23
+ json: async () => ({ queued: true }),
24
+ }) as unknown as Response;
25
+
26
+ const errorResponse = (status: number, statusText: string): Response =>
27
+ ({
28
+ ok: false,
29
+ status,
30
+ statusText,
31
+ text: async () => statusText,
32
+ }) as unknown as Response;
33
+
34
+ beforeEach(() => {
35
+ vi.resetAllMocks();
36
+ vi.stubGlobal('fetch', mockFetch);
37
+ process.env.ADMIN_KEY = ADMIN_KEY;
38
+ process.env.CORE_API_ORIGIN = ORIGIN;
39
+ });
40
+
41
+ afterEach(() => {
42
+ vi.unstubAllGlobals();
43
+ });
44
+
45
+ describe('createInternetOpportunity', () => {
46
+ test('POSTs to /api/opportunities/internet with admin auth + tenant headers and JSON body', async () => {
47
+ mockFetch.mockResolvedValueOnce(okResponse());
48
+
49
+ await createInternetOpportunity(event);
50
+
51
+ expect(mockFetch).toHaveBeenCalledTimes(1);
52
+ const [url, init] = mockFetch.mock.calls[0];
53
+ expect(url.toString()).toBe(`${ORIGIN}/api/opportunities/internet`);
54
+ expect(init.method).toBe('POST');
55
+ expect(init.body).toBe(JSON.stringify(event));
56
+
57
+ const headers = Object.fromEntries(init.headers as [string, string][]);
58
+ expect(headers.Authorization).toBe(`AdminKey ${ADMIN_KEY}`);
59
+ expect(headers['Content-Type']).toBe('application/json');
60
+ expect(headers[TENANT_ID_ADMIN_HEADER]).toBe(event.tenantId);
61
+ });
62
+
63
+ test('throws XError when the response is a non-2xx', async () => {
64
+ mockFetch.mockResolvedValueOnce(errorResponse(500, 'Internal Server Error'));
65
+
66
+ await expect(createInternetOpportunity(event)).rejects.toBeInstanceOf(XError);
67
+ });
68
+ });
@@ -1,5 +1,6 @@
1
1
  import { TENANT_ID_ADMIN_HEADER } from '@bisondesk/commons-sdk/constants';
2
2
  import { cleanHeaders, fetchJson, getAdminAuth } from '@bisondesk/commons-sdk/fetch';
3
+ import { InternetOpportunityEvent } from '../types/internet-opportunities.js';
3
4
  import {
4
5
  Bid,
5
6
  ListOpportunitiesFilters,
@@ -101,6 +102,19 @@ export const createOpportunity = async (
101
102
  return response;
102
103
  };
103
104
 
105
+ export const createInternetOpportunity = async (event: InternetOpportunityEvent): Promise<void> => {
106
+ const auth = await getAdminAuth();
107
+ await fetchJson(`${process.env.CORE_API_ORIGIN}/api/opportunities/internet`, {
108
+ method: 'POST',
109
+ headers: cleanHeaders({
110
+ Authorization: auth,
111
+ [TENANT_ID_ADMIN_HEADER]: event.tenantId,
112
+ 'Content-Type': 'application/json',
113
+ }),
114
+ body: JSON.stringify(event),
115
+ });
116
+ };
117
+
104
118
  //
105
119
  // Bids
106
120
  //
@@ -5,7 +5,7 @@ type BaseInternetOpportunity = {
5
5
  // ids
6
6
  tenantId: string;
7
7
  branchId: string;
8
- emailId: string;
8
+ emailId?: string;
9
9
 
10
10
  // raw data
11
11
  emailHtml?: string;
@@ -93,6 +93,8 @@ export enum OpportunityWarningsCode {
93
93
  QUOTE_MISSING_FINANCING_DETAILS = 'quote_missing_financing_details',
94
94
  VEHICLE_BRANCH_MISMATCH = 'vehicle_branch_mismatch',
95
95
  VEHICLE_UNAVAILABLE_AT_SOURCE = 'vehicle_unavailable_at_source',
96
+ INTERCOMPANY_LINKED = 'intercompany_linked',
97
+ INTERCOMPANY_SOURCE_OPPORTUNITY = 'intercompany_source_opportunity',
96
98
  }
97
99
 
98
100
  export const OpportunityWonColumnId = 'closed_won';
@@ -266,6 +268,12 @@ export type OpportunityStatusRequirement = { code: OpportunityRequirementsCode;
266
268
  type OportunityWarningActionLink = {
267
269
  businessEntityId: BusinessEntityIds;
268
270
  recordId: string;
271
+ /**
272
+ * Absolute URL, set only when the record lives at another tenant — in-app routing cannot
273
+ * reach it, so the client opens it in a new tab. Same shape the intercompany payments walk
274
+ * already hands the client (ADR-0024's `opportunityUrl`).
275
+ */
276
+ url?: string;
269
277
  };
270
278
 
271
279
  type OportunityWarningActionData = { [key: string]: string | number };
@@ -116,6 +116,10 @@ export type Branch = {
116
116
  externalQuote?: boolean;
117
117
  hyperdmsId?: string;
118
118
  internalDealsEnabled?: boolean;
119
+ /** The source invoices the end customer directly; this branch collects payment and earns
120
+ * commission. Intercompany source opportunities for this branch's deals take the buyer
121
+ * deal's own client instead of the branch counterparty org. */
122
+ dropshipping?: boolean;
119
123
  };
120
124
 
121
125
  export type HdmsVehiclesSync = {
@@ -61,6 +61,9 @@ export const isBeforeReferenceOpportunityStatus = (input: CompareInput): boolean
61
61
 
62
62
  export const isOpportunityLost = (input: Opportunity): boolean => input.lostAt != null;
63
63
 
64
+ export const isOpportunityClosed = (input: Opportunity): boolean =>
65
+ input.lostAt != null || input.wonAt != null;
66
+
64
67
  export const getOpportunityReservedStatus = (): OpportunityStatus[] => [
65
68
  OpportunityStatus.REVIEW,
66
69
  OpportunityStatus.PREPARATION,
@@ -13,6 +13,10 @@ export const getBranch = (tenant: Tenant, branchId: string): Branch => {
13
13
 
14
14
  export const getDefaultBranch = (tenant: Tenant): Branch => first(tenant.branches)!;
15
15
 
16
+ /** The source invoices the end customer directly; this branch collects payment and earns
17
+ * commission (see {@link Branch.dropshipping}). */
18
+ export const isDropshippingBranch = (branch: Branch): boolean => branch.dropshipping === true;
19
+
16
20
  export const getBranchStandardVatRate = (tenant: Tenant, branchId: string): string => {
17
21
  const branch: Branch = getBranch(tenant, branchId);
18
22
  return branch.vatRates.standard;
@@ -40,6 +40,16 @@ export const isUnavailableFromSource = (
40
40
  ): boolean =>
41
41
  isSourceMastered(vehicle, purchase) && (vehicle.original?.salesConditions?.unavailable ?? false);
42
42
 
43
+ /**
44
+ * A trailer or semi-trailer that has never been used — what the sales ladder calls a new
45
+ * trailer, and the reason it has opportunity types of its own (`NEW_TRAILER`,
46
+ * `NEW_TRAILER_NEW_PRODUCTION`) instead of going through plain sales.
47
+ */
48
+ export const isNewTrailer = (vehicle: Pick<Vehicle, 'external'>): boolean =>
49
+ [Categories.Trailer, Categories.SemiTrailer].includes(
50
+ vehicle.external.general.category as Categories,
51
+ ) && !vehicle.external.condition?.used;
52
+
43
53
  export const extractCustomFeatures = (vehicle: VehicleExternalInfo): string[] => {
44
54
  const features: {
45
55
  getValue: (vehicle: VehicleExternalInfo) => any;
@@ -155,6 +165,16 @@ export const convertLettersToNumber = (letters: string): number => {
155
165
  return result;
156
166
  };
157
167
 
168
+ /**
169
+ * Canonical stock-number shape: a branch prefix of 1 to 3 letters followed by exactly 5 digits
170
+ * and up to 2 trailing counter letters (e.g. B49397AB, TC12345 — see
171
+ * {@link getNextRelatedStockNumber}). Callers supply the prefix pattern (concrete branch
172
+ * prefixes, or a policy like `[A-Z]{2}`) and wrap their own boundary/anchor policy around the
173
+ * returned pattern.
174
+ */
175
+ export const buildStockNumberPattern = (prefixPattern: string): string =>
176
+ `(?:${prefixPattern})\\d{5}[A-Z]{0,2}`;
177
+
158
178
  export const getNextRelatedStockNumber = (stockNumber: string): string => {
159
179
  const counterPattern = /^([A-Z]+\d{5})([A-Z]{0,2})$/;
160
180
  const match = stockNumber.match(counterPattern);