@bisondesk/core-sdk 1.0.677 → 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.
Files changed (159) hide show
  1. package/lib/apis/exchange-rates.d.ts +0 -3
  2. package/lib/apis/exchange-rates.d.ts.map +1 -1
  3. package/lib/apis/exchange-rates.js +0 -29
  4. package/lib/apis/exchange-rates.js.map +1 -1
  5. package/lib/apis/organization-bank-accounts.d.ts +11 -0
  6. package/lib/apis/organization-bank-accounts.d.ts.map +1 -0
  7. package/lib/apis/organization-bank-accounts.js +47 -0
  8. package/lib/apis/organization-bank-accounts.js.map +1 -0
  9. package/lib/types/intercompany-payments.d.ts.map +1 -1
  10. package/lib/types/intercompany-payments.js.map +1 -1
  11. package/lib/types/offers.d.ts +1 -2
  12. package/lib/types/offers.d.ts.map +1 -1
  13. package/lib/types/offers.js.map +1 -1
  14. package/lib/types/opportunities.d.ts +1 -8
  15. package/lib/types/opportunities.d.ts.map +1 -1
  16. package/lib/types/opportunities.js.map +1 -1
  17. package/lib/types/opportunity-payments.d.ts +0 -9
  18. package/lib/types/opportunity-payments.d.ts.map +1 -1
  19. package/lib/types/opportunity-payments.js.map +1 -1
  20. package/lib/types/organization-bank-accounts.d.ts +35 -0
  21. package/lib/types/organization-bank-accounts.d.ts.map +1 -0
  22. package/lib/types/organization-bank-accounts.js +9 -0
  23. package/lib/types/organization-bank-accounts.js.map +1 -0
  24. package/lib/types/payment-exports.d.ts +23 -0
  25. package/lib/types/payment-exports.d.ts.map +1 -0
  26. package/lib/types/payment-exports.js +2 -0
  27. package/lib/types/payment-exports.js.map +1 -0
  28. package/lib/types/tenants.d.ts +1 -1
  29. package/lib/types/tenants.d.ts.map +1 -1
  30. package/lib/types/tenants.js +1 -0
  31. package/lib/types/tenants.js.map +1 -1
  32. package/lib/types/transports.d.ts +1 -1
  33. package/lib/types/transports.d.ts.map +1 -1
  34. package/lib/types/transports.js +1 -1
  35. package/lib/types/transports.js.map +1 -1
  36. package/lib/types/vehicle-purchase-payments.d.ts +52 -8
  37. package/lib/types/vehicle-purchase-payments.d.ts.map +1 -1
  38. package/lib/types/vehicle-purchase-payments.js +6 -0
  39. package/lib/types/vehicle-purchase-payments.js.map +1 -1
  40. package/lib/types/vehicles.d.ts +1 -7
  41. package/lib/types/vehicles.d.ts.map +1 -1
  42. package/lib/types/vehicles.js.map +1 -1
  43. package/lib/utils/organization-bank-accounts.d.ts +3 -0
  44. package/lib/utils/organization-bank-accounts.d.ts.map +1 -0
  45. package/lib/utils/organization-bank-accounts.js +9 -0
  46. package/lib/utils/organization-bank-accounts.js.map +1 -0
  47. package/lib/utils/tenants.d.ts +0 -3
  48. package/lib/utils/tenants.d.ts.map +1 -1
  49. package/lib/utils/tenants.js +0 -3
  50. package/lib/utils/tenants.js.map +1 -1
  51. package/lib/utils/tenants.test.js +1 -27
  52. package/lib/utils/tenants.test.js.map +1 -1
  53. package/lib/utils/transports.d.ts +8 -4
  54. package/lib/utils/transports.d.ts.map +1 -1
  55. package/lib/utils/transports.js +13 -7
  56. package/lib/utils/transports.js.map +1 -1
  57. package/lib/utils/transports.test.d.ts +2 -0
  58. package/lib/utils/transports.test.d.ts.map +1 -0
  59. package/lib/utils/transports.test.js +40 -0
  60. package/lib/utils/transports.test.js.map +1 -0
  61. package/package.json +1 -1
  62. package/src/apis/exchange-rates.ts +0 -49
  63. package/src/apis/organization-bank-accounts.ts +123 -0
  64. package/src/types/intercompany-payments.ts +3 -4
  65. package/src/types/offers.ts +0 -2
  66. package/src/types/opportunities.ts +1 -15
  67. package/src/types/opportunity-payments.ts +0 -23
  68. package/src/types/organization-bank-accounts.ts +56 -0
  69. package/src/types/payment-exports.ts +40 -0
  70. package/src/types/tenants.ts +7 -6
  71. package/src/types/transports.ts +1 -1
  72. package/src/types/vehicle-purchase-payments.ts +114 -13
  73. package/src/types/vehicles.ts +4 -15
  74. package/src/utils/organization-bank-accounts.ts +19 -0
  75. package/src/utils/tenants.test.ts +1 -39
  76. package/src/utils/tenants.ts +0 -17
  77. package/src/utils/transports.test.ts +50 -0
  78. package/src/utils/transports.ts +39 -22
  79. package/tsconfig.tsbuildinfo +1 -1
  80. package/lib/utils/opportunity-payments.d.ts +0 -3
  81. package/lib/utils/opportunity-payments.d.ts.map +0 -1
  82. package/lib/utils/opportunity-payments.js +0 -5
  83. package/lib/utils/opportunity-payments.js.map +0 -1
  84. package/lib/utils/opportunity-payments.test.d.ts +0 -2
  85. package/lib/utils/opportunity-payments.test.d.ts.map +0 -1
  86. package/lib/utils/opportunity-payments.test.js +0 -23
  87. package/lib/utils/opportunity-payments.test.js.map +0 -1
  88. package/src/types/activities.js +0 -13
  89. package/src/types/activities.js.map +0 -1
  90. package/src/types/comparables.js +0 -2
  91. package/src/types/comparables.js.map +0 -1
  92. package/src/types/crm.js +0 -58
  93. package/src/types/crm.js.map +0 -1
  94. package/src/types/dashboard.js +0 -2
  95. package/src/types/dashboard.js.map +0 -1
  96. package/src/types/definitions.js +0 -2
  97. package/src/types/definitions.js.map +0 -1
  98. package/src/types/fields.js +0 -2
  99. package/src/types/fields.js.map +0 -1
  100. package/src/types/insights.js +0 -2
  101. package/src/types/insights.js.map +0 -1
  102. package/src/types/interests.js +0 -6
  103. package/src/types/interests.js.map +0 -1
  104. package/src/types/internal-events.js +0 -18
  105. package/src/types/internal-events.js.map +0 -1
  106. package/src/types/internet-opportunities.js +0 -2
  107. package/src/types/internet-opportunities.js.map +0 -1
  108. package/src/types/internet-vehicles.js +0 -2
  109. package/src/types/internet-vehicles.js.map +0 -1
  110. package/src/types/leasing-administration.js +0 -2
  111. package/src/types/leasing-administration.js.map +0 -1
  112. package/src/types/leasing-debtors.js +0 -2
  113. package/src/types/leasing-debtors.js.map +0 -1
  114. package/src/types/leasing-search.js +0 -2
  115. package/src/types/leasing-search.js.map +0 -1
  116. package/src/types/leasing-settings.js +0 -7
  117. package/src/types/leasing-settings.js.map +0 -1
  118. package/src/types/leasing.js +0 -38
  119. package/src/types/leasing.js.map +0 -1
  120. package/src/types/marketing.js +0 -35
  121. package/src/types/marketing.js.map +0 -1
  122. package/src/types/offers.js +0 -56
  123. package/src/types/opportunities.js +0 -107
  124. package/src/types/opportunities.js.map +0 -1
  125. package/src/types/payments.js +0 -7
  126. package/src/types/picklists.js +0 -2
  127. package/src/types/picklists.js.map +0 -1
  128. package/src/types/quotes.js +0 -42
  129. package/src/types/recently-viewed.js +0 -2
  130. package/src/types/recently-viewed.js.map +0 -1
  131. package/src/types/reservations.js +0 -5
  132. package/src/types/reservations.js.map +0 -1
  133. package/src/types/roi-ratings.js +0 -5
  134. package/src/types/roi-ratings.js.map +0 -1
  135. package/src/types/saved-filters.js +0 -9
  136. package/src/types/saved-filters.js.map +0 -1
  137. package/src/types/search.js +0 -2
  138. package/src/types/search.js.map +0 -1
  139. package/src/types/settings.js +0 -2
  140. package/src/types/settings.js.map +0 -1
  141. package/src/types/templates.js +0 -2
  142. package/src/types/templates.js.map +0 -1
  143. package/src/types/tenants.js +0 -21
  144. package/src/types/tenants.js.map +0 -1
  145. package/src/types/user-performance.js +0 -7
  146. package/src/types/user-performance.js.map +0 -1
  147. package/src/types/utils.js +0 -2
  148. package/src/types/validation.js +0 -2
  149. package/src/types/validation.js.map +0 -1
  150. package/src/types/vehicle-sales.js +0 -6
  151. package/src/types/vehicle-sales.js.map +0 -1
  152. package/src/types/vehicles.js +0 -98
  153. package/src/types/vehicles.js.map +0 -1
  154. package/src/utils/leasing.js +0 -6
  155. package/src/utils/leasing.js.map +0 -1
  156. package/src/utils/opportunities.js +0 -48
  157. package/src/utils/opportunities.js.map +0 -1
  158. package/src/utils/opportunity-payments.test.ts +0 -31
  159. package/src/utils/opportunity-payments.ts +0 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bisondesk/core-sdk",
3
- "version": "1.0.677",
3
+ "version": "1.0.679",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "author": "TheTruckCompany",
@@ -1,9 +1,5 @@
1
1
  import { XError } from '@bisondesk/commons-sdk/errors';
2
2
  import { getAdminAuth } from '@bisondesk/commons-sdk/fetch';
3
- import {
4
- CurrencyConversionRequest,
5
- CurrencyConversionResponse,
6
- } from '@bisondesk/commons-sdk/types';
7
3
  import fetch, { Response } from 'node-fetch';
8
4
  import { ExchangeRate } from '../types/exchange-rates.js';
9
5
 
@@ -50,51 +46,6 @@ export const getExchangeRate = async (
50
46
  throw new XError(response.statusText, { url, body, fromCurrency, toCurrency });
51
47
  };
52
48
 
53
- /** The daily rates of the rate provider, as the quote and invoice forms get them. */
54
- export const getCurrencyConversionRates = async (
55
- request: CurrencyConversionRequest,
56
- ): Promise<CurrencyConversionResponse> => {
57
- const auth = await getAdminAuth();
58
- const url = `${process.env.CORE_API_ORIGIN}/api/currency/rates`;
59
- const response: Response = await fetch(url, {
60
- method: 'POST',
61
- headers: {
62
- Authorization: auth,
63
- 'Content-Type': 'application/json',
64
- },
65
- body: JSON.stringify(request),
66
- });
67
-
68
- if (response.ok) {
69
- return response.json() as any;
70
- }
71
-
72
- const body = await response.text();
73
- throw new XError(response.statusText, { url, body, request });
74
- };
75
-
76
- /**
77
- * The plain daily rate of `date`: how many units of `toCurrency` equal 1 unit of `fromCurrency`,
78
- * without the bank-fee markup. Undefined when the provider has no rate for the pair.
79
- */
80
- export const getDailyConversionRate = async (
81
- fromCurrency: string,
82
- toCurrency: string,
83
- date?: string,
84
- ): Promise<string | undefined> => {
85
- if (fromCurrency === toCurrency) {
86
- return '1';
87
- }
88
-
89
- const { rates } = await getCurrencyConversionRates({
90
- sourceCurrency: fromCurrency,
91
- targetCurrency: toCurrency,
92
- date,
93
- bankFeeMarkup: false,
94
- });
95
- return rates[toCurrency];
96
- };
97
-
98
49
  /**
99
50
  * Direction-agnostic conversion multiplier: how many units of `toCurrency`
100
51
  * equal 1 unit of `fromCurrency`. Tries a direct pair first, then falls back
@@ -0,0 +1,123 @@
1
+ import { SERVICE_ID_ADMIN_HEADER, TENANT_ID_ADMIN_HEADER } from '@bisondesk/commons-sdk/constants';
2
+ import { XError } from '@bisondesk/commons-sdk/errors';
3
+ import { cleanHeaders, getAdminAuth } from '@bisondesk/commons-sdk/fetch';
4
+ import { DataRecord } from '@bisondesk/commons-sdk/utils';
5
+ import fetch, { Response } from 'node-fetch';
6
+ import {
7
+ NewOrganizationBankAccount,
8
+ OrganizationBankAccount,
9
+ OrganizationBankAccountMeta,
10
+ } from '../types/organization-bank-accounts.js';
11
+
12
+ type ServiceOptions = { serviceId?: string };
13
+
14
+ /**
15
+ * Calls an organisation bank-account route as a service. The service id becomes the user core
16
+ * records as having made the change.
17
+ */
18
+ const callBankAccountsRoute = async (
19
+ tenantId: string,
20
+ path: string,
21
+ init: { method: string; body?: unknown },
22
+ opts?: ServiceOptions,
23
+ ): Promise<Response> => {
24
+ const auth = await getAdminAuth();
25
+ const url = `${process.env.CORE_API_ORIGIN}${path}`;
26
+ const response: Response = await fetch(url, {
27
+ method: init.method,
28
+ headers: cleanHeaders({
29
+ Authorization: auth,
30
+ 'Content-Type': init.body != null ? 'application/json' : undefined,
31
+ [TENANT_ID_ADMIN_HEADER]: tenantId,
32
+ [SERVICE_ID_ADMIN_HEADER]: opts?.serviceId,
33
+ }),
34
+ body: init.body != null ? JSON.stringify(init.body) : undefined,
35
+ });
36
+ if (!response.ok) {
37
+ const body = await response.text();
38
+ throw new XError(response.statusText, { method: init.method, url, body, tenantId, opts });
39
+ }
40
+ return response;
41
+ };
42
+
43
+ const bankAccountsPath = (organizationId: string) =>
44
+ `/api/crm/organizations/${organizationId}/bank-accounts`;
45
+
46
+ /** An organisation's bank accounts, the default first. */
47
+ export const listOrganizationBankAccounts = async (
48
+ tenantId: string,
49
+ organizationId: string,
50
+ opts?: ServiceOptions,
51
+ ): Promise<OrganizationBankAccount[]> => {
52
+ const response = await callBankAccountsRoute(
53
+ tenantId,
54
+ bankAccountsPath(organizationId),
55
+ { method: 'GET' },
56
+ opts,
57
+ );
58
+ const records = (await response.json()) as DataRecord<
59
+ OrganizationBankAccount,
60
+ OrganizationBankAccountMeta
61
+ >[];
62
+ return records.map((entry) => entry.record);
63
+ };
64
+
65
+ export const createOrganizationBankAccount = async (
66
+ tenantId: string,
67
+ input: NewOrganizationBankAccount,
68
+ opts?: ServiceOptions,
69
+ ): Promise<OrganizationBankAccount> => {
70
+ const { organizationId, ...details } = input;
71
+ const response = await callBankAccountsRoute(
72
+ tenantId,
73
+ bankAccountsPath(organizationId),
74
+ { method: 'POST', body: details },
75
+ opts,
76
+ );
77
+ return (await response.json()) as OrganizationBankAccount;
78
+ };
79
+
80
+ export const updateOrganizationBankAccount = async (
81
+ tenantId: string,
82
+ bankAccountId: string,
83
+ input: NewOrganizationBankAccount,
84
+ opts?: ServiceOptions,
85
+ ): Promise<OrganizationBankAccount> => {
86
+ const { organizationId, ...details } = input;
87
+ const response = await callBankAccountsRoute(
88
+ tenantId,
89
+ `${bankAccountsPath(organizationId)}/${bankAccountId}`,
90
+ { method: 'PUT', body: details },
91
+ opts,
92
+ );
93
+ return (await response.json()) as OrganizationBankAccount;
94
+ };
95
+
96
+ export const deleteOrganizationBankAccount = async (
97
+ tenantId: string,
98
+ organizationId: string,
99
+ bankAccountId: string,
100
+ opts?: ServiceOptions,
101
+ ): Promise<void> => {
102
+ await callBankAccountsRoute(
103
+ tenantId,
104
+ `${bankAccountsPath(organizationId)}/${bankAccountId}`,
105
+ { method: 'DELETE' },
106
+ opts,
107
+ );
108
+ };
109
+
110
+ export const setDefaultOrganizationBankAccount = async (
111
+ tenantId: string,
112
+ organizationId: string,
113
+ bankAccountId: string,
114
+ opts?: ServiceOptions,
115
+ ): Promise<OrganizationBankAccount> => {
116
+ const response = await callBankAccountsRoute(
117
+ tenantId,
118
+ `${bankAccountsPath(organizationId)}/${bankAccountId}/default`,
119
+ { method: 'POST' },
120
+ opts,
121
+ );
122
+ return (await response.json()) as OrganizationBankAccount;
123
+ };
@@ -13,9 +13,8 @@ export type IntercompanyPaymentHop = {
13
13
  /** Deep link to the hop opportunity on the target tenant's own subdomain. */
14
14
  opportunityUrl: string;
15
15
  /**
16
- * Currency of the amounts: the hop opportunity's quote currency, or the quote's alternative
17
- * currency when every payment of the hop arrived in it. A reserved-stage opportunity always
18
- * carries a quote; hops missing one (malformed) are skipped.
16
+ * Currency of the amounts: the hop opportunity's quote currency. A reserved-stage
17
+ * opportunity always carries a quote; hops missing one (malformed) are skipped.
19
18
  */
20
19
  currency: string;
21
20
  vehicle: {
@@ -33,7 +32,7 @@ export type IntercompanyPaymentHop = {
33
32
  * total payment before delivery). Absent until that hop's quote is manager-accepted.
34
33
  */
35
34
  expectedTotal?: string;
36
- /** Sum of the hop opportunity's non-deleted payment amounts, in `currency`. */
35
+ /** Sum of the hop opportunity's non-deleted payment amounts. */
37
36
  paid: string;
38
37
  };
39
38
 
@@ -9,7 +9,6 @@ import {
9
9
  SearchVehicle,
10
10
  VehicleExternalInfo,
11
11
  VehicleInternalInfo,
12
- VehiclePurchaseDealForeign,
13
12
  VehiclePurchaseDealStatus,
14
13
  VehiclePurchaseDeliveryType,
15
14
  } from './vehicles.js';
@@ -331,7 +330,6 @@ export type OfferPurchase = {
331
330
  | VehiclePurchaseDealStatus.Marketing;
332
331
  costs: {
333
332
  purchase: string;
334
- foreign?: VehiclePurchaseDealForeign;
335
333
  purchaseVatPercentage: string;
336
334
  internalGarage?: string;
337
335
  externalGarage?: string;
@@ -251,23 +251,8 @@ export type OpportunityUpdate = {
251
251
  };
252
252
 
253
253
  export type OpportunityPaymentSummary = {
254
- /** Branch currency. */
255
254
  total: string;
256
- /** Sum of `amount` over the payments, in branch currency. */
257
255
  paid: string;
258
- /**
259
- * The same pair in the quote's alternative currency, present when the branch can be paid in it.
260
- * `paid` sums the payments that arrived in that currency.
261
- */
262
- foreign?: {
263
- total: string;
264
- paid: string;
265
- currencyCode: string;
266
- exchangeRate: string;
267
- };
268
- /** Fully paid when either currency reaches its total. */
269
- status: OpportunityPaymentStatus;
270
- percentagePaid: string;
271
256
  entries: DataRecord<OpportunityPayment>[];
272
257
  };
273
258
 
@@ -536,6 +521,7 @@ export type HpLeasingDealRunDataCi = HpLeasingDealRunDataBase & {
536
521
  dueeDuLeasingEnMois: number;
537
522
  travauxHorsNormesNecessaire: boolean;
538
523
  descriptionDesTravauxHorsNormes: string;
524
+ prixTravaexSupplementaire: string;
539
525
  };
540
526
 
541
527
  export type HpLeasingDealRequest = {
@@ -7,27 +7,8 @@ export enum OpportunityPaymentActions {
7
7
 
8
8
  export const OPPORTUNITY_PAYMENT_NOTIFICATION_ORIGIN = 'opportunity-payment-notification';
9
9
 
10
- /**
11
- * The money as it arrived, when it arrived in the quote's alternative currency. `amount` then
12
- * holds its value in branch currency: `foreign.amount × foreign.exchangeRate`.
13
- */
14
- export type OpportunityPaymentForeign = {
15
- amount: string;
16
- currencyCode: string;
17
- /** Branch currency units per 1 unit of `currencyCode`, the quote's rate. */
18
- exchangeRate: string;
19
- };
20
-
21
- /** What a user sends for a payment in another currency. The backend takes the rate from the quote
22
- * and derives `amount`. */
23
- export type OpportunityPaymentForeignRequest = Pick<
24
- OpportunityPaymentForeign,
25
- 'amount' | 'currencyCode'
26
- >;
27
-
28
10
  type BaseOpportunityPayment = {
29
11
  opportunityId: string;
30
- /** Branch currency. */
31
12
  amount: string;
32
13
  description?: string;
33
14
  vehicleId: string;
@@ -43,7 +24,6 @@ export type NewOpportunityPayment = BaseOpportunityPayment & {
43
24
  deletedAt?: undefined;
44
25
  deletedBy?: undefined;
45
26
  paidAt: string;
46
- foreign?: OpportunityPaymentForeignRequest;
47
27
  };
48
28
 
49
29
  export type OpportunityPayment = BaseOpportunityPayment & {
@@ -55,14 +35,11 @@ export type OpportunityPayment = BaseOpportunityPayment & {
55
35
  deletedAt?: string;
56
36
  deletedBy?: string;
57
37
  paidAt?: string;
58
- foreign?: OpportunityPaymentForeign;
59
38
  };
60
39
 
61
40
  export type OpportunityPaymentUpdate = {
62
41
  description?: string;
63
42
  amount?: string;
64
- /** `null` turns the payment back into a branch currency payment. */
65
- foreign?: OpportunityPaymentForeignRequest | null;
66
43
  paidAt?: string;
67
44
  };
68
45
 
@@ -0,0 +1,56 @@
1
+ import { BankAccountDetails } from '@bisondesk/commons-sdk/bank-accounts';
2
+
3
+ /**
4
+ * An accountant's confirmation of one account number. It only counts while the account still
5
+ * holds that number, so trust never carries over to a new number however the account was edited.
6
+ */
7
+ export type OrganizationBankAccountVerification = {
8
+ accountNumber: string;
9
+ verifiedBy: string;
10
+ verifiedAt: string;
11
+ };
12
+
13
+ export enum OrganizationBankAccountActions {
14
+ VERIFY = 'verify',
15
+ }
16
+
17
+ export enum OrganizationBankAccountActionBlockerCode {
18
+ CHANGED_NEEDS_VERIFICATION = 'changed_needs_verification',
19
+ }
20
+
21
+ export type OrganizationBankAccountActionBlocker = {
22
+ code: OrganizationBankAccountActionBlockerCode;
23
+ };
24
+
25
+ /**
26
+ * Why an action the user would otherwise see is refused right now. A missing permission hides the
27
+ * action instead.
28
+ */
29
+ export type OrganizationBankAccountBlockedAction = {
30
+ action: OrganizationBankAccountActions;
31
+ reasons: OrganizationBankAccountActionBlocker[];
32
+ };
33
+
34
+ export type OrganizationBankAccountMeta = {
35
+ blockedActions: OrganizationBankAccountBlockedAction[];
36
+ };
37
+
38
+ export type NewOrganizationBankAccount = BankAccountDetails & {
39
+ organizationId: string;
40
+ };
41
+
42
+ /**
43
+ * An organisation's bank account for outgoing SEPA transfers, never shared across companies. An
44
+ * organisation can hold several; at most one is its `default`, the one payment exports pay to.
45
+ * Editing the account number or holder name clears the manual verification, so a valid checksum is
46
+ * never mistaken for a confirmed account holder.
47
+ */
48
+ export type OrganizationBankAccount = NewOrganizationBankAccount & {
49
+ id: string;
50
+ default?: boolean;
51
+ createdAt: string;
52
+ createdBy: string;
53
+ modifiedAt: string;
54
+ modifiedBy: string;
55
+ verification?: OrganizationBankAccountVerification;
56
+ };
@@ -0,0 +1,40 @@
1
+ import { BankAccountDetails } from '@bisondesk/commons-sdk/bank-accounts';
2
+ import { AttachmentValue } from '@bisondesk/commons-sdk/types';
3
+
4
+ /**
5
+ * The transfer details of one payment, frozen at export time. A later edit to the payment or the
6
+ * supplier bank account must not rewrite what the sent file said, so the export keeps its own copy
7
+ * of every value that went into the XML. It also records the payment batch the payment was sent to
8
+ * processing in, so the export is a self-contained audit record even though one file can span
9
+ * several batches.
10
+ */
11
+ export type PaymentExportLine = {
12
+ paymentId: string;
13
+ vehicleId: string;
14
+ amount: string;
15
+ creditor: BankAccountDetails;
16
+ paymentBatchId: string;
17
+ transferReference?: string;
18
+ };
19
+
20
+ export type NewPaymentExport = {
21
+ /** Single execution date (`yyyy-MM-dd`) written to the file's `ReqdExctnDt`. */
22
+ executionDate: string;
23
+ /** Σ of the exported line amounts (string; computed with decimal arithmetic). */
24
+ totalAmount: string;
25
+ paymentIds: string[];
26
+ lines: PaymentExportLine[];
27
+ /** The generated SEPA file, stored in S3 and re-downloadable from Processing. */
28
+ xmlFile: AttachmentValue;
29
+ };
30
+
31
+ /**
32
+ * A generated SEPA file for a selection of Processing payments, plus the frozen record of exactly what
33
+ * was sent. Re-downloading serves the same file and never re-exports; a payment cancelled out of the
34
+ * file returns to Allowed and leaves this record intact.
35
+ */
36
+ export type PaymentExport = NewPaymentExport & {
37
+ id: string;
38
+ exportedAt: string;
39
+ exportedBy: string;
40
+ };
@@ -55,6 +55,13 @@ export enum TenantModule {
55
55
  Intercompany = 'intercompany',
56
56
  Opportunities = 'opportunities',
57
57
  Offers = 'offers',
58
+ /**
59
+ * The tenant pays suppliers through Isabel file exports (SEPA transfer files). It grants the
60
+ * export itself and the verification of the supplier bank accounts it pays to, and requires those
61
+ * account numbers to be valid IBANs. Without it, Processing payments are simply marked paid, and
62
+ * a bank account can hold any account number.
63
+ */
64
+ PaymentExports = 'payment_exports',
58
65
  Vehicles = 'vehicles',
59
66
  Tasks = 'tasks',
60
67
  TrackAndTrace = 'track_and_trace',
@@ -148,12 +155,6 @@ export type Branch = {
148
155
  exactAccounting?: boolean;
149
156
  accounting?: AccountingProvider;
150
157
  currency: string;
151
- /**
152
- * The currencies money moves in at this branch, its own `currency` included. Customer payments,
153
- * purchase prices, supplier invoice drafts and Exact bookings are supported in these currencies
154
- * only. Absent = the branch's own `currency` only. Set by script.
155
- */
156
- paymentCurrencies?: [string, ...string[]];
157
158
  adminNumberPrefix: string;
158
159
  stockNumberPrefix: string;
159
160
  vatNumber: string;
@@ -340,7 +340,7 @@ export const KANBAN_TRANSPORT_ITEM_FIELDS = [
340
340
  'org.name',
341
341
  'org.countryCode',
342
342
  'org.externalId',
343
- 'opportunity.deliveredAt',
343
+ 'opportunity.branch',
344
344
  'purchase.logistics.status',
345
345
  'purchase.logistics.availableWeek',
346
346
  'purchase.logistics.pickupAddress',
@@ -1,41 +1,142 @@
1
1
  import { BaseEvent } from './internal-events.js';
2
+ import { OrganizationBankAccountActions } from './organization-bank-accounts.js';
2
3
  import { SearchVehicle } from './vehicles.js';
3
4
 
4
5
  export const VEHICLE_PURCHASE_PAYMENT_ALLOWED_NOTIFICATION_ORIGIN =
5
6
  'vehicle-purchase-payment-allowed-notification';
6
7
 
7
8
  /**
8
- * A payment on a purchase priced in another currency is authorized and paid in that currency.
9
- * `amount` then holds its value in branch currency: `foreign.amount × foreign.exchangeRate`.
9
+ * The stage a supplier payment sits at, driving the Unpaid/Allowed/Processing/Executed tabs.
10
+ * A payment only ever moves forward: an authorised payment is `allowed`; sending it to processing
11
+ * groups it into a payment batch and hands it to accounting (`processing`), which exports it
12
+ * to the Isabel bank file; marking it paid moves it to `executed`.
13
+ * (`unpaid` is not a stage — it is an agreed purchase with an amount still to authorise.)
10
14
  */
11
- export type VehiclePurchasePaymentForeign = {
12
- amount: string;
13
- currencyCode: string;
14
- /** Branch currency units per 1 unit of `currencyCode`, the purchase's rate. */
15
- exchangeRate: string;
16
- };
15
+ export enum PaymentStage {
16
+ Allowed = 'allowed',
17
+ Processing = 'processing',
18
+ Executed = 'executed',
19
+ }
17
20
 
18
21
  export type NewVehiclePurchasePayment = {
19
22
  vehicleId: string;
20
- /** Branch currency. Derived from `foreign` when the purchase is priced in another currency. */
21
23
  amount: string;
22
- /** The backend takes the rate from the purchase. */
23
- foreign?: Pick<VehiclePurchasePaymentForeign, 'amount' | 'currencyCode'>;
24
24
  description?: string;
25
25
  paidAt?: string;
26
26
  paidBy?: string;
27
27
  };
28
28
 
29
- export type VehiclePurchasePayment = Omit<NewVehiclePurchasePayment, 'foreign'> & {
29
+ export type VehiclePurchasePayment = NewVehiclePurchasePayment & {
30
30
  id: string;
31
31
  createdBy: string;
32
32
  createdAt: string;
33
- foreign?: VehiclePurchasePaymentForeign;
33
+ stage: PaymentStage;
34
+ /** Manual settlement priority within the Allowed tab; lower is paid first. */
35
+ priority: number;
36
+ /** Set once the payment has been exported; links it to its payment export. */
37
+ exportId?: string;
38
+ /** The payment batch this payment was sent to processing in; unset while Allowed, and on payments
39
+ * marked paid straight from Allowed. */
40
+ paymentBatchId?: string;
41
+ /**
42
+ * Supplier-facing communication that becomes the SEPA transfer's remittance information (`RmtInf`),
43
+ * so the supplier can reconcile the payment to an invoice. Editable while Processing and not yet
44
+ * exported; prefilled from the latest purchase invoice. This is not the file's per-line
45
+ * `EndToEndId`.
46
+ */
47
+ transferReference?: string;
48
+ /** Who sent the payment to processing, and when — set at the Allowed → Processing step. */
49
+ sentToProcessingBy?: string;
50
+ sentToProcessingAt?: string;
51
+ };
52
+
53
+ /**
54
+ * One send to processing: the Allowed payments an accountant moved to Processing together, and who
55
+ * sent them. The Processing tab groups its rows by batch. A batch holds no amount of its own; its
56
+ * total is the sum of the payments still in it.
57
+ */
58
+ export type PaymentBatch = {
59
+ id: string;
60
+ createdAt: string;
61
+ createdBy: string;
62
+ };
63
+
64
+ /**
65
+ * Whether a payment's supplier can be paid in an export. Mirrors the export gate: a transfer needs a
66
+ * supplier bank account that carries an IBAN and holder name and has been verified. Shown as an
67
+ * export-blocked marker on the Processing rows, where the accountant clears it before exporting.
68
+ * (The invoice / pro-forma document half of the gate is not reflected here — its source is a Q3
69
+ * residual the backend export gate also defers.)
70
+ *
71
+ * The bank account is the supplier's default one, `bankAccountId`, which is the account an export
72
+ * pays. `bankAccountActions` lists what the current user may do on it, so a row can point out the
73
+ * accounts they can verify themselves. Once the account number or holder name changed, verify is
74
+ * left out for accountants, and for whichever Senior Accountant made the change (four-eyes).
75
+ */
76
+ export type PaymentExportChecks = {
77
+ bankAccountId?: string;
78
+ hasBankAccount: boolean;
79
+ bankAccountVerified: boolean;
80
+ bankAccountActions: OrganizationBankAccountActions[];
34
81
  };
35
82
 
36
83
  export type VehiclePurchasePaymentBff = {
37
84
  payment: VehiclePurchasePayment;
38
85
  vehicle: SearchVehicle;
86
+ /** Name of the supplier organisation (`vehicle.purchase.organizationId`), resolved for display. */
87
+ supplierName?: string;
88
+ /** Export checks on the supplier bank account; filled for the Processing list only. */
89
+ exportChecks?: PaymentExportChecks;
90
+ };
91
+
92
+ /**
93
+ * A row on the Unpaid tab: an agreed purchase that still has an amount to authorise for payment.
94
+ * Derived from the purchase deal and the payments already authorised, not a payment record.
95
+ */
96
+ export type UnpaidPurchase = {
97
+ vehicle: SearchVehicle;
98
+ /** The agreed purchase price including VAT (`deal.purchase` grossed up by `deal.purchaseVatRate`),
99
+ * so it matches the VAT-inclusive cost the vehicle purchase tab authorises against. */
100
+ purchaseAmount: string;
101
+ /** Sum of amounts already authorised (payments in any stage) for this vehicle. */
102
+ allowedAmount: string;
103
+ /** Sum of the authorised amounts already settled (payments in Executed) for this vehicle. */
104
+ paidAmount: string;
105
+ /** `purchaseAmount − allowedAmount`; always greater than zero for rows shown here. */
106
+ remainingAmount: string;
107
+ /** Due date of the purchase invoice. */
108
+ invoiceDueDate?: string;
109
+ /** Name of the supplier organisation (`vehicle.purchase.organizationId`), resolved for display. */
110
+ supplierName?: string;
111
+ };
112
+
113
+ /** Body of the export request: the Processing payments to put in one file, the paying account, and the execution date. */
114
+ export type ExportPaymentsRequest = {
115
+ paymentIds: string[];
116
+ /** The bank account (from documents settings) to pay the file from — the file's debtor. */
117
+ debtorBankAccountId: string;
118
+ /** Single execution date (`yyyy-MM-dd`) written to `ReqdExctnDt`; defaults to today. */
119
+ executionDate?: string;
120
+ };
121
+
122
+ /** Body of the mark-paid request: the payments (Allowed or Processing) to settle in one call. */
123
+ export type MarkPaymentsPaidRequest = {
124
+ paymentIds: string[];
125
+ };
126
+
127
+ /** Body of the send-to-processing request: the Allowed payments to send together as one batch. */
128
+ export type SendPaymentsToProcessingRequest = {
129
+ paymentIds: string[];
130
+ };
131
+
132
+ /**
133
+ * How many rows sit behind each payment tab, for the tab counters. Executed is unbounded and has no
134
+ * counter, so it is left out.
135
+ */
136
+ export type PurchasePaymentCounts = {
137
+ unpaid: number;
138
+ allowed: number;
139
+ processing: number;
39
140
  };
40
141
 
41
142
  export type VehiclePurchasePaymentCreateEvent = {
@@ -170,7 +170,6 @@ export type NewVehicleRequest = {
170
170
  dealStatus: VehiclePurchaseDealStatus;
171
171
  costs: {
172
172
  purchase: string;
173
- foreign?: VehiclePurchaseDealForeign;
174
173
  purchaseVatPercentage: string;
175
174
  commission?: string;
176
175
  externalTransport?: string;
@@ -812,18 +811,6 @@ export type VehiclePriceUpdateResponse =
812
811
  errors: VehiclePriceUpdateError[];
813
812
  };
814
813
 
815
- /**
816
- * The price the supplier asked, in the supplier's currency: one of the payment currencies of the
817
- * vehicle's branch. The purchase's `deal.purchase` is then `purchase × exchangeRate`, and payments
818
- * are authorized in `currencyCode`.
819
- */
820
- export type VehiclePurchaseDealForeign = {
821
- purchase: string; // exclusive VAT
822
- currencyCode: string;
823
- /** Branch currency units per 1 unit of `currencyCode`. */
824
- exchangeRate: string;
825
- };
826
-
827
814
  export type VehiclePurchase = {
828
815
  organizationId: string;
829
816
  contactId: string;
@@ -859,13 +846,15 @@ export type VehiclePurchase = {
859
846
  checkedInBy?: string;
860
847
  };
861
848
 
849
+ /** Due date of the supplier purchase invoice; editable from the payments page. */
850
+ invoiceDueDate?: string;
851
+
862
852
  deal: {
863
853
  status: VehiclePurchaseDealStatus; // partially set by the user, if there are payments then it is set automatically by the backend
864
854
  readyToDeliver?: boolean;
865
855
 
866
856
  purchaseVatRate?: string;
867
- purchase?: string; // exclusive VAT; derived from `foreign` when present
868
- foreign?: VehiclePurchaseDealForeign;
857
+ purchase?: string; // exclusive VAT
869
858
 
870
859
  bonus?: string; // sometimes manufacturers pay us a bonus when we reach a target
871
860
 
@@ -0,0 +1,19 @@
1
+ import { normalizeIban } from '@bisondesk/commons-sdk/iban';
2
+ import { OrganizationBankAccount } from '../types/organization-bank-accounts.js';
3
+
4
+ /**
5
+ * Whether an organisation's bank account can be paid: it carries a verification, and that
6
+ * verification was given for the account number the account holds now. A verification recorded
7
+ * without an account number, or for a different one, does not count.
8
+ *
9
+ * Shared so the export gate and the Processing rows read one rule.
10
+ */
11
+ export const isOrganizationBankAccountVerified = (
12
+ account: OrganizationBankAccount | undefined,
13
+ ): boolean => {
14
+ const verifiedAccountNumber = account?.verification?.accountNumber;
15
+ if (account == null || verifiedAccountNumber == null) {
16
+ return false;
17
+ }
18
+ return normalizeIban(verifiedAccountNumber) === normalizeIban(account.accountNumber);
19
+ };