@bisondesk/core-sdk 1.0.627 → 1.0.629
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/lib/apis/activities.d.ts.map +1 -1
- package/lib/apis/country-delivery.d.ts.map +1 -1
- package/lib/apis/crm.d.ts.map +1 -1
- package/lib/apis/crm.js.map +1 -1
- package/lib/apis/debtors.d.ts.map +1 -1
- package/lib/apis/email-templates.d.ts.map +1 -1
- package/lib/apis/exchange-rates.d.ts.map +1 -1
- package/lib/apis/insights.d.ts.map +1 -1
- package/lib/apis/internet-vehicles.d.ts.map +1 -1
- package/lib/apis/leasing-administration.d.ts.map +1 -1
- package/lib/apis/leasing.d.ts.map +1 -1
- package/lib/apis/offers.d.ts.map +1 -1
- package/lib/apis/opportunities.d.ts +2 -0
- package/lib/apis/opportunities.d.ts.map +1 -1
- package/lib/apis/opportunities.js +12 -0
- package/lib/apis/opportunities.js.map +1 -1
- package/lib/apis/opportunities.test.d.ts +2 -0
- package/lib/apis/opportunities.test.d.ts.map +1 -0
- package/lib/apis/opportunities.test.js +53 -0
- package/lib/apis/opportunities.test.js.map +1 -0
- package/lib/apis/tenants.d.ts.map +1 -1
- package/lib/apis/users.d.ts.map +1 -1
- package/lib/apis/vehicle-sales.d.ts.map +1 -1
- package/lib/apis/vehicles.d.ts.map +1 -1
- package/lib/types/interests.d.ts.map +1 -1
- package/lib/types/internet-opportunities.d.ts +1 -1
- package/lib/types/internet-opportunities.d.ts.map +1 -1
- package/lib/types/internet-opportunities.js.map +1 -1
- package/lib/types/opportunities.d.ts +2 -1
- package/lib/types/opportunities.d.ts.map +1 -1
- package/lib/types/opportunities.js +1 -0
- package/lib/types/opportunities.js.map +1 -1
- package/lib/types/reports-leasing.d.ts +1 -1
- package/lib/types/reports-leasing.d.ts.map +1 -1
- package/lib/types/reports-sales.d.ts +2 -2
- package/lib/types/reports-sales.d.ts.map +1 -1
- package/lib/types/tenants.d.ts +4 -2
- package/lib/types/tenants.d.ts.map +1 -1
- package/lib/types/tenants.js +2 -1
- package/lib/types/tenants.js.map +1 -1
- package/lib/types/transports.d.ts.map +1 -1
- package/lib/utils/accounting.d.ts.map +1 -1
- package/lib/utils/accounting.test.js.map +1 -1
- package/lib/utils/contacts.d.ts.map +1 -1
- package/lib/utils/debtors-dossiers.d.ts.map +1 -1
- package/lib/utils/exchange-rates.d.ts.map +1 -1
- package/lib/utils/forms.d.ts.map +1 -1
- package/lib/utils/forms.js.map +1 -1
- package/lib/utils/integrations/vehicles.d.ts +1 -1
- package/lib/utils/integrations/vehicles.d.ts.map +1 -1
- package/lib/utils/interests.d.ts.map +1 -1
- package/lib/utils/interests.js.map +1 -1
- package/lib/utils/leasing.d.ts +1 -1
- package/lib/utils/leasing.d.ts.map +1 -1
- package/lib/utils/offers.d.ts.map +1 -1
- package/lib/utils/opportunities.d.ts +1 -0
- package/lib/utils/opportunities.d.ts.map +1 -1
- package/lib/utils/opportunities.js +4 -0
- package/lib/utils/opportunities.js.map +1 -1
- package/lib/utils/search.d.ts +1 -1
- package/lib/utils/search.d.ts.map +1 -1
- package/lib/utils/slug.d.ts.map +1 -1
- package/lib/utils/tenants.d.ts +1 -0
- package/lib/utils/tenants.d.ts.map +1 -1
- package/lib/utils/tenants.js +1 -0
- package/lib/utils/tenants.js.map +1 -1
- package/lib/utils/vehicles.d.ts +4 -3
- package/lib/utils/vehicles.d.ts.map +1 -1
- package/lib/utils/vehicles.js +1 -0
- package/lib/utils/vehicles.js.map +1 -1
- package/package.json +2 -2
- package/src/apis/opportunities.test.ts +68 -0
- package/src/apis/opportunities.ts +14 -0
- package/src/types/internet-opportunities.ts +1 -1
- package/src/types/opportunities.ts +1 -0
- package/src/types/tenants.ts +19 -1
- package/src/utils/opportunities.ts +13 -1
- package/src/utils/tenants.ts +4 -0
- package/src/utils/vehicles.ts +10 -0
- package/tsconfig.tsbuildinfo +1 -1
package/lib/utils/search.d.ts
CHANGED
|
@@ -12,9 +12,9 @@ export declare const getCustomerInformation: (org?: Organization, contact?: Cont
|
|
|
12
12
|
title: string;
|
|
13
13
|
code: number;
|
|
14
14
|
} | {
|
|
15
|
+
code?: undefined;
|
|
15
16
|
countryCode: string | undefined;
|
|
16
17
|
title: string;
|
|
17
|
-
code?: undefined;
|
|
18
18
|
} | undefined;
|
|
19
19
|
export declare const mappings: {
|
|
20
20
|
[key in BusinessEntityIds]?: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"/","sources":["utils/search.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAE9D,MAAM,MAAM,oBAAoB,GAC5B,WAAW,GACX,OAAO,GACP,YAAY,GACZ,eAAe,GACf,cAAc,CAAC;AACnB,MAAM,MAAM,kBAAkB,GAC1B,iBAAiB,GACjB,aAAa,GACb,kBAAkB,GAClB,qBAAqB,GACrB,oBAAoB,CAAC;AAEzB,eAAO,MAAM,sBAAsB,
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"/","sources":["utils/search.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAE9D,MAAM,MAAM,oBAAoB,GAC5B,WAAW,GACX,OAAO,GACP,YAAY,GACZ,eAAe,GACf,cAAc,CAAC;AACnB,MAAM,MAAM,kBAAkB,GAC1B,iBAAiB,GACjB,aAAa,GACb,kBAAkB,GAClB,qBAAqB,GACrB,oBAAoB,CAAC;AAEzB,eAAO,MAAM,sBAAsB,SAAU,YAAY,YAAY,OAAO;;;;;;;;aAe3E,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE;KACpB,GAAG,IAAI,iBAAiB,CAAC,CAAC,EAAE;QAC3B,aAAa,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,oBAAoB,CAAC;QACrD,QAAQ,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,MAAM,CAAC;QAClC,OAAO,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,MAAM,EAAE,CAAC;QACnC,sBAAsB,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,MAAM,EAAE,CAAC;QACnD,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,MAAM,CAAC;QACzC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAChD;CA8EF,CAAC;AAUF,eAAO,MAAM,wBAAwB,qBACjB,iBAAiB,UAC3B,oBAAoB,KAC3B,MAAM,EAAkD,CAAC;AAE5D,eAAO,MAAM,8BAA8B,qBACvB,iBAAiB,UAC3B,kBAAkB,aAY3B,CAAC;AAEF,eAAO,MAAM,+BAA+B,qBACxB,iBAAiB,UAC3B,oBAAoB,WAC4C,CAAC;AAE3E,eAAO,MAAM,qCAAqC,qBAC9B,iBAAiB,UAC3B,kBAAkB,WAS3B,CAAC;AAEF,eAAO,MAAM,6BAA6B,qBACtB,iBAAiB,UAC3B,kBAAkB,yBAC2B,CAAC;AAExD,eAAO,MAAM,yBAAyB,qBAClB,iBAAiB,UAC3B,oBAAoB,WACoB,CAAC;AAEnD,eAAO,MAAM,+BAA+B,qBACxB,iBAAiB,gBACrB,kBAAkB,WAK/B,CAAC;AAEJ,eAAO,MAAM,wBAAwB,qBACjB,iBAAiB,UAC3B,oBAAoB,oCACqB,CAAC;AAEpD,eAAO,MAAM,8BAA8B,qBACvB,iBAAiB,gBACrB,kBAAkB,oCAK/B,CAAC"}
|
package/lib/utils/slug.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slug.d.ts","sourceRoot":"/","sources":["utils/slug.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAE/C,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"slug.d.ts","sourceRoot":"/","sources":["utils/slug.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAE/C,eAAO,MAAM,cAAc,YAAa,OAAO,KAAG,MAejD,CAAC"}
|
package/lib/utils/tenants.d.ts
CHANGED
|
@@ -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,
|
|
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,WAAY,MAAM,YAAY,MAAM,KAAG,MAO5D,CAAC;AAEF,eAAO,MAAM,gBAAgB,WAAY,MAAM,KAAG,MAAiC,CAAC;AAIpF,eAAO,MAAM,oBAAoB,WAAY,MAAM,KAAG,OAAuC,CAAC;AAE9F,eAAO,MAAM,wBAAwB,WAAY,MAAM,YAAY,MAAM,KAAG,MAG3E,CAAC"}
|
package/lib/utils/tenants.js
CHANGED
|
@@ -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;
|
package/lib/utils/tenants.js.map
CHANGED
|
@@ -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;
|
|
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"]}
|
package/lib/utils/vehicles.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Vehicle, VehicleExternalInfo, VehiclePurchase } from '../types/vehicles.js';
|
|
2
2
|
export declare const isDealInMarketing: (purchase: VehiclePurchase | undefined) => boolean;
|
|
3
|
-
export declare const isSourceMastered: (vehicle: Pick<Vehicle,
|
|
4
|
-
export declare const isUnavailableFromSource: (vehicle: Pick<Vehicle,
|
|
5
|
-
export declare const isNewTrailer: (vehicle: Pick<Vehicle,
|
|
3
|
+
export declare const isSourceMastered: (vehicle: Pick<Vehicle, 'original'>, purchase: VehiclePurchase | undefined) => boolean;
|
|
4
|
+
export declare const isUnavailableFromSource: (vehicle: Pick<Vehicle, 'original'>, purchase: VehiclePurchase | undefined) => boolean;
|
|
5
|
+
export declare const isNewTrailer: (vehicle: Pick<Vehicle, 'external'>) => boolean;
|
|
6
6
|
export declare const extractCustomFeatures: (vehicle: VehicleExternalInfo) => string[];
|
|
7
7
|
export declare const getInternalTitle: ({ make, category, modelName, bodywork, modelTypeName, }: {
|
|
8
8
|
category: string;
|
|
@@ -13,6 +13,7 @@ export declare const getInternalTitle: ({ make, category, modelName, bodywork, m
|
|
|
13
13
|
}) => string;
|
|
14
14
|
export declare const convertNumberToLetters: (num: number) => string;
|
|
15
15
|
export declare const convertLettersToNumber: (letters: string) => number;
|
|
16
|
+
export declare const buildStockNumberPattern: (prefixPattern: string) => string;
|
|
16
17
|
export declare const getNextRelatedStockNumber: (stockNumber: string) => string;
|
|
17
18
|
export declare const getVehicleDescription: (external: VehicleExternalInfo, { showStockNumber, showAxleConfiguration, lang, }: {
|
|
18
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,
|
|
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,aAAc,eAAe,GAAG,SAAS,KAAG,OACQ,CAAC;AASnF,eAAO,MAAM,gBAAgB,YAClB,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,YACxB,eAAe,GAAG,SAAS,KACpC,OAAkE,CAAC;AAQtE,eAAO,MAAM,uBAAuB,YACzB,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,YACxB,eAAe,GAAG,SAAS,KACpC,OAC+F,CAAC;AAOnG,eAAO,MAAM,YAAY,YAAa,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,KAAG,OAG1B,CAAC;AAEzC,eAAO,MAAM,qBAAqB,YAAa,mBAAmB,KAAG,MAAM,EAiD1E,CAAC;AAEF,eAAO,MAAM,gBAAgB,4DAM1B;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,QAAS,MAAM,KAAG,MAiBpD,CAAC;AAGF,eAAO,MAAM,sBAAsB,YAAa,MAAM,KAAG,MAWxD,CAAC;AASF,eAAO,MAAM,uBAAuB,kBAAmB,MAAM,KAAG,MACxB,CAAC;AAEzC,eAAO,MAAM,yBAAyB,gBAAiB,MAAM,KAAG,MAa/D,CAAC;AAEF,eAAO,MAAM,qBAAqB,aACtB,mBAAmB,qDAK1B;IACD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,WAyBF,CAAC"}
|
package/lib/utils/vehicles.js
CHANGED
|
@@ -88,6 +88,7 @@ export const convertLettersToNumber = (letters) => {
|
|
|
88
88
|
}
|
|
89
89
|
return result;
|
|
90
90
|
};
|
|
91
|
+
export const buildStockNumberPattern = (prefixPattern) => `(?:${prefixPattern})\\d{5}[A-Z]{0,2}`;
|
|
91
92
|
export const getNextRelatedStockNumber = (stockNumber) => {
|
|
92
93
|
const counterPattern = /^([A-Z]+\d{5})([A-Z]{0,2})$/;
|
|
93
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\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\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.
|
|
3
|
+
"version": "1.0.629",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "TheTruckCompany",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@types/json-logic-js": "2.0.8",
|
|
19
19
|
"@types/lodash-es": "4.17.12",
|
|
20
20
|
"@types/node": "24.1.0",
|
|
21
|
-
"typescript": "
|
|
21
|
+
"typescript": "7.0.2"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
24
|
"@bisondesk/commons-sdk": "*",
|
|
@@ -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
|
//
|
|
@@ -95,6 +95,7 @@ export enum OpportunityWarningsCode {
|
|
|
95
95
|
VEHICLE_UNAVAILABLE_AT_SOURCE = 'vehicle_unavailable_at_source',
|
|
96
96
|
INTERCOMPANY_LINKED = 'intercompany_linked',
|
|
97
97
|
INTERCOMPANY_SOURCE_OPPORTUNITY = 'intercompany_source_opportunity',
|
|
98
|
+
INTERCOMPANY_SOURCE_UNRESERVED = 'intercompany_source_unreserved',
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
export const OpportunityWonColumnId = 'closed_won';
|
package/src/types/tenants.ts
CHANGED
|
@@ -17,15 +17,29 @@ export type PublicTenant = {
|
|
|
17
17
|
export enum TenantModule {
|
|
18
18
|
Administration = 'administration',
|
|
19
19
|
App = 'app',
|
|
20
|
+
/**
|
|
21
|
+
* The tenant's users can put the app into client mode, hiding the internal-only pages while
|
|
22
|
+
* showing a screen to a customer. Left off for a tenant whose users are themselves the
|
|
23
|
+
* customer: it would hide their sign-out to no purpose, and turning it back off needs a PIN
|
|
24
|
+
* they were never given.
|
|
25
|
+
*/
|
|
26
|
+
ClientMode = 'client_mode',
|
|
20
27
|
CRM = 'crm',
|
|
21
28
|
Docs = 'docs',
|
|
22
29
|
Leads = 'leads',
|
|
23
30
|
Leasing = 'leasing',
|
|
24
31
|
Marketing = 'marketing',
|
|
25
32
|
Insights = 'insights',
|
|
33
|
+
/**
|
|
34
|
+
* The tenant has agreed to exchange deal data through BisonDesk automatically, on top of the
|
|
35
|
+
* bare vehicle distribution: it may be named as the buyer on an intercompany sale, which lets
|
|
36
|
+
* the invoice sync and the purchase reconciliation write a purchase onto its copy. Read on the
|
|
37
|
+
* BUYER only. All group tenants have it today; a supplier or client tenant that wants deal
|
|
38
|
+
* data pushed too joins the exchange by being granted it.
|
|
39
|
+
*/
|
|
40
|
+
Intercompany = 'intercompany',
|
|
26
41
|
Opportunities = 'opportunities',
|
|
27
42
|
Offers = 'offers',
|
|
28
|
-
VehiclesMasterData = 'vehicles_master_data',
|
|
29
43
|
Vehicles = 'vehicles',
|
|
30
44
|
Tasks = 'tasks',
|
|
31
45
|
TrackAndTrace = 'track_and_trace',
|
|
@@ -116,6 +130,10 @@ export type Branch = {
|
|
|
116
130
|
externalQuote?: boolean;
|
|
117
131
|
hyperdmsId?: string;
|
|
118
132
|
internalDealsEnabled?: boolean;
|
|
133
|
+
/** The source invoices the end customer directly; this branch collects payment and earns
|
|
134
|
+
* commission. Intercompany source opportunities for this branch's deals take the buyer
|
|
135
|
+
* deal's own client instead of the branch counterparty org. */
|
|
136
|
+
dropshipping?: boolean;
|
|
119
137
|
};
|
|
120
138
|
|
|
121
139
|
export type HdmsVehiclesSync = {
|
|
@@ -64,6 +64,17 @@ export const isOpportunityLost = (input: Opportunity): boolean => input.lostAt !
|
|
|
64
64
|
export const isOpportunityClosed = (input: Opportunity): boolean =>
|
|
65
65
|
input.lostAt != null || input.wonAt != null;
|
|
66
66
|
|
|
67
|
+
/**
|
|
68
|
+
* A finished sale: the deal reached delivery. The intercompany coordination reads it as "the
|
|
69
|
+
* truck already crossed" — never adopted into (a won deal's link stays live forever), never
|
|
70
|
+
* worth linking after the fact, and nothing left to secure at the source.
|
|
71
|
+
*/
|
|
72
|
+
export const isOpportunityDelivered = (input: Opportunity): boolean =>
|
|
73
|
+
isSameOrAfterReferenceOpportunityStatus({
|
|
74
|
+
currentStatus: input.status,
|
|
75
|
+
referenceStatus: OpportunityStatus.DELIVERY,
|
|
76
|
+
});
|
|
77
|
+
|
|
67
78
|
export const getOpportunityReservedStatus = (): OpportunityStatus[] => [
|
|
68
79
|
OpportunityStatus.REVIEW,
|
|
69
80
|
OpportunityStatus.PREPARATION,
|
|
@@ -76,7 +87,8 @@ export const isOpportunityReserved = (input: Opportunity): boolean =>
|
|
|
76
87
|
/**
|
|
77
88
|
* Whether the opportunity currently reserves the vehicle attached to it: reserved-stage
|
|
78
89
|
* (REVIEW onwards) and not lost — independent of whether a reservation row exists. Query-side
|
|
79
|
-
* twin: `getOpportunityReservingVehicle` in core's opportunities/read.ts.
|
|
90
|
+
* twin: `getOpportunityReservingVehicle` in core's opportunities/vehicle-reserved/read.ts.
|
|
91
|
+
* Availability
|
|
80
92
|
* propagation, warnings and guards must share this one definition rather than re-composing
|
|
81
93
|
* the two primitives.
|
|
82
94
|
*/
|
package/src/utils/tenants.ts
CHANGED
|
@@ -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;
|
package/src/utils/vehicles.ts
CHANGED
|
@@ -165,6 +165,16 @@ export const convertLettersToNumber = (letters: string): number => {
|
|
|
165
165
|
return result;
|
|
166
166
|
};
|
|
167
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
|
+
|
|
168
178
|
export const getNextRelatedStockNumber = (stockNumber: string): string => {
|
|
169
179
|
const counterPattern = /^([A-Z]+\d{5})([A-Z]{0,2})$/;
|
|
170
180
|
const match = stockNumber.match(counterPattern);
|