@unchainedshop/platform 4.3.4 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +149 -2
  2. package/lib/context/index.d.ts +1 -2
  3. package/lib/context/index.js +1 -2
  4. package/lib/context/setAccessToken.d.ts +1 -2
  5. package/lib/context/setAccessToken.js +0 -1
  6. package/lib/migrations/createMigrationRunner.d.ts +0 -1
  7. package/lib/migrations/createMigrationRunner.js +0 -3
  8. package/lib/migrations/migrationRepository.d.ts +1 -2
  9. package/lib/migrations/migrationRepository.js +1 -1
  10. package/lib/migrations/runMigrations.d.ts +2 -3
  11. package/lib/migrations/runMigrations.js +1 -2
  12. package/lib/platform-index.d.ts +4 -5
  13. package/lib/platform-index.js +4 -5
  14. package/lib/setup/setupAccounts.d.ts +1 -2
  15. package/lib/setup/setupAccounts.js +0 -1
  16. package/lib/setup/setupTemplates.d.ts +11 -11
  17. package/lib/setup/setupTemplates.js +16 -19
  18. package/lib/setup/setupUploadHandlers.d.ts +1 -2
  19. package/lib/setup/setupUploadHandlers.js +0 -1
  20. package/lib/setup/setupWorkqueue.d.ts +2 -3
  21. package/lib/setup/setupWorkqueue.js +1 -7
  22. package/lib/startPlatform.d.ts +6 -7
  23. package/lib/startPlatform.js +15 -16
  24. package/lib/templates/index.d.ts +8 -9
  25. package/lib/templates/index.js +8 -9
  26. package/lib/templates/order-parser/getOrderPositionsData.d.ts +2 -3
  27. package/lib/templates/order-parser/getOrderPositionsData.js +0 -1
  28. package/lib/templates/order-parser/getOrderSummaryData.d.ts +2 -3
  29. package/lib/templates/order-parser/getOrderSummaryData.js +0 -1
  30. package/lib/templates/order-parser/index.d.ts +4 -5
  31. package/lib/templates/order-parser/index.js +2 -3
  32. package/lib/templates/resolveAccountActionTemplate.d.ts +1 -2
  33. package/lib/templates/resolveAccountActionTemplate.js +0 -2
  34. package/lib/templates/resolveEnrollmentStatusTemplate.d.ts +1 -2
  35. package/lib/templates/resolveEnrollmentStatusTemplate.js +0 -1
  36. package/lib/templates/resolveErrorReportTemplate.d.ts +1 -2
  37. package/lib/templates/resolveErrorReportTemplate.js +0 -1
  38. package/lib/templates/resolveForwardDeliveryTemplate.d.ts +1 -2
  39. package/lib/templates/resolveForwardDeliveryTemplate.js +1 -2
  40. package/lib/templates/resolveOrderConfirmationTemplate.d.ts +1 -2
  41. package/lib/templates/resolveOrderConfirmationTemplate.js +1 -2
  42. package/lib/templates/resolveOrderRejectionTemplate.d.ts +1 -2
  43. package/lib/templates/resolveOrderRejectionTemplate.js +1 -2
  44. package/lib/templates/resolveQuotationStatusTemplate.d.ts +1 -2
  45. package/lib/templates/resolveQuotationStatusTemplate.js +0 -1
  46. package/package.json +17 -16
  47. package/lib/context/index.d.ts.map +0 -1
  48. package/lib/context/index.js.map +0 -1
  49. package/lib/context/setAccessToken.d.ts.map +0 -1
  50. package/lib/context/setAccessToken.js.map +0 -1
  51. package/lib/migrations/createMigrationRunner.d.ts.map +0 -1
  52. package/lib/migrations/createMigrationRunner.js.map +0 -1
  53. package/lib/migrations/migrationRepository.d.ts.map +0 -1
  54. package/lib/migrations/migrationRepository.js.map +0 -1
  55. package/lib/migrations/runMigrations.d.ts.map +0 -1
  56. package/lib/migrations/runMigrations.js.map +0 -1
  57. package/lib/platform-index.d.ts.map +0 -1
  58. package/lib/platform-index.js.map +0 -1
  59. package/lib/setup/setupAccounts.d.ts.map +0 -1
  60. package/lib/setup/setupAccounts.js.map +0 -1
  61. package/lib/setup/setupTemplates.d.ts.map +0 -1
  62. package/lib/setup/setupTemplates.js.map +0 -1
  63. package/lib/setup/setupUploadHandlers.d.ts.map +0 -1
  64. package/lib/setup/setupUploadHandlers.js.map +0 -1
  65. package/lib/setup/setupWorkqueue.d.ts.map +0 -1
  66. package/lib/setup/setupWorkqueue.js.map +0 -1
  67. package/lib/startPlatform.d.ts.map +0 -1
  68. package/lib/startPlatform.js.map +0 -1
  69. package/lib/templates/index.d.ts.map +0 -1
  70. package/lib/templates/index.js.map +0 -1
  71. package/lib/templates/order-parser/getOrderPositionsData.d.ts.map +0 -1
  72. package/lib/templates/order-parser/getOrderPositionsData.js.map +0 -1
  73. package/lib/templates/order-parser/getOrderSummaryData.d.ts.map +0 -1
  74. package/lib/templates/order-parser/getOrderSummaryData.js.map +0 -1
  75. package/lib/templates/order-parser/index.d.ts.map +0 -1
  76. package/lib/templates/order-parser/index.js.map +0 -1
  77. package/lib/templates/resolveAccountActionTemplate.d.ts.map +0 -1
  78. package/lib/templates/resolveAccountActionTemplate.js.map +0 -1
  79. package/lib/templates/resolveEnrollmentStatusTemplate.d.ts.map +0 -1
  80. package/lib/templates/resolveEnrollmentStatusTemplate.js.map +0 -1
  81. package/lib/templates/resolveErrorReportTemplate.d.ts.map +0 -1
  82. package/lib/templates/resolveErrorReportTemplate.js.map +0 -1
  83. package/lib/templates/resolveForwardDeliveryTemplate.d.ts.map +0 -1
  84. package/lib/templates/resolveForwardDeliveryTemplate.js.map +0 -1
  85. package/lib/templates/resolveOrderConfirmationTemplate.d.ts.map +0 -1
  86. package/lib/templates/resolveOrderConfirmationTemplate.js.map +0 -1
  87. package/lib/templates/resolveOrderRejectionTemplate.d.ts.map +0 -1
  88. package/lib/templates/resolveOrderRejectionTemplate.js.map +0 -1
  89. package/lib/templates/resolveQuotationStatusTemplate.d.ts.map +0 -1
  90. package/lib/templates/resolveQuotationStatusTemplate.js.map +0 -1
  91. package/src/context/index.ts +0 -1
  92. package/src/context/setAccessToken.ts +0 -22
  93. package/src/migrations/createMigrationRunner.ts +0 -65
  94. package/src/migrations/migrationRepository.ts +0 -22
  95. package/src/migrations/runMigrations.ts +0 -68
  96. package/src/platform-index.ts +0 -19
  97. package/src/setup/setupAccounts.ts +0 -11
  98. package/src/setup/setupTemplates.ts +0 -67
  99. package/src/setup/setupUploadHandlers.ts +0 -22
  100. package/src/setup/setupWorkqueue.ts +0 -104
  101. package/src/startPlatform.ts +0 -128
  102. package/src/templates/index.ts +0 -9
  103. package/src/templates/order-parser/getOrderPositionsData.ts +0 -50
  104. package/src/templates/order-parser/getOrderSummaryData.ts +0 -91
  105. package/src/templates/order-parser/index.ts +0 -65
  106. package/src/templates/resolveAccountActionTemplate.ts +0 -127
  107. package/src/templates/resolveEnrollmentStatusTemplate.ts +0 -34
  108. package/src/templates/resolveErrorReportTemplate.ts +0 -47
  109. package/src/templates/resolveForwardDeliveryTemplate.ts +0 -42
  110. package/src/templates/resolveOrderConfirmationTemplate.ts +0 -40
  111. package/src/templates/resolveOrderRejectionTemplate.ts +0 -41
  112. package/src/templates/resolveQuotationStatusTemplate.ts +0 -45
  113. package/tests/platform-index.test.ts +0 -7
  114. package/tsconfig.json +0 -9
@@ -1,11 +0,0 @@
1
- import { UnchainedCore } from '@unchainedshop/core';
2
- import { subscribe } from '@unchainedshop/events';
3
-
4
- export const setupAccounts = (unchainedAPI: UnchainedCore) => {
5
- subscribe('USER_ACCOUNT_ACTION', async ({ payload }: { payload: any }) => {
6
- await unchainedAPI.services.worker.addMessage('ACCOUNT_ACTION', {
7
- recipientEmail: payload.address,
8
- ...payload,
9
- });
10
- });
11
- };
@@ -1,67 +0,0 @@
1
- import { MessagingDirector, UnchainedCore } from '@unchainedshop/core';
2
- import { subscribe } from '@unchainedshop/events';
3
- import { Order, OrderStatus } from '@unchainedshop/core-orders';
4
- import { RawPayloadType } from '@unchainedshop/events';
5
- import { resolveOrderRejectionTemplate } from '../templates/resolveOrderRejectionTemplate.js';
6
- import { resolveAccountActionTemplate } from '../templates/resolveAccountActionTemplate.js';
7
- import { resolveForwardDeliveryTemplate } from '../templates/resolveForwardDeliveryTemplate.js';
8
- import { resolveOrderConfirmationTemplate } from '../templates/resolveOrderConfirmationTemplate.js';
9
- import { resolveQuotationStatusTemplate } from '../templates/resolveQuotationStatusTemplate.js';
10
- import { resolveEnrollmentStatusTemplate } from '../templates/resolveEnrollmentStatusTemplate.js';
11
- import { resolveErrorReportTemplate } from '../templates/resolveErrorReportTemplate.js';
12
-
13
- export enum MessageTypes {
14
- ACCOUNT_ACTION = 'ACCOUNT_ACTION',
15
- DELIVERY = 'DELIVERY',
16
- ORDER_CONFIRMATION = 'ORDER_CONFIRMATION',
17
- ORDER_REJECTION = 'ORDER_REJECTION',
18
- QUOTATION_STATUS = 'QUOTATION_STATUS',
19
- ENROLLMENT_STATUS = 'ENROLLMENT_STATUS',
20
- ERROR_REPORT = 'ERROR_REPORT',
21
- }
22
-
23
- export const setupTemplates = (unchainedAPI: UnchainedCore) => {
24
- MessagingDirector.registerTemplate(MessageTypes.ACCOUNT_ACTION, resolveAccountActionTemplate);
25
- MessagingDirector.registerTemplate(MessageTypes.DELIVERY, resolveForwardDeliveryTemplate);
26
- MessagingDirector.registerTemplate(MessageTypes.ORDER_CONFIRMATION, resolveOrderConfirmationTemplate);
27
- MessagingDirector.registerTemplate(MessageTypes.ORDER_REJECTION, resolveOrderRejectionTemplate);
28
- MessagingDirector.registerTemplate(MessageTypes.QUOTATION_STATUS, resolveQuotationStatusTemplate);
29
- MessagingDirector.registerTemplate(MessageTypes.ENROLLMENT_STATUS, resolveEnrollmentStatusTemplate);
30
- MessagingDirector.registerTemplate(MessageTypes.ERROR_REPORT, resolveErrorReportTemplate);
31
-
32
- subscribe('ORDER_CHECKOUT', async ({ payload }: RawPayloadType<{ order: Order }>) => {
33
- const { order } = payload;
34
- const user = await unchainedAPI.modules.users.findUserById(order.userId);
35
- const locale = unchainedAPI.modules.users.userLocale(user);
36
-
37
- if (order.status === OrderStatus.PENDING) {
38
- // We only send the "confirmation" when pending, else we let the other events handle it
39
- await unchainedAPI.services.worker.addMessage(MessageTypes.ORDER_CONFIRMATION, {
40
- locale: locale.baseName,
41
- orderId: order._id,
42
- });
43
- }
44
- });
45
-
46
- subscribe('ORDER_CONFIRMED', async ({ payload }: RawPayloadType<{ order: Order }>) => {
47
- const { order } = payload;
48
- const user = await unchainedAPI.modules.users.findUserById(order.userId);
49
- const locale = unchainedAPI.modules.users.userLocale(user);
50
-
51
- await unchainedAPI.services.worker.addMessage(MessageTypes.ORDER_CONFIRMATION, {
52
- locale: locale.baseName,
53
- orderId: order._id,
54
- });
55
- });
56
-
57
- subscribe('ORDER_REJECTED', async ({ payload }: RawPayloadType<{ order: Order }>) => {
58
- const { order } = payload;
59
- const user = await unchainedAPI.modules.users.findUserById(order.userId);
60
- const locale = unchainedAPI.modules.users.userLocale(user);
61
-
62
- await unchainedAPI.services.worker.addMessage(MessageTypes.ORDER_REJECTION, {
63
- locale: locale.baseName,
64
- orderId: order._id,
65
- });
66
- });
67
- };
@@ -1,22 +0,0 @@
1
- import { UnchainedCore } from '@unchainedshop/core';
2
- import { FileDirector } from '@unchainedshop/file-upload';
3
-
4
- export const setupUploadHandlers = ({ services, modules }: UnchainedCore) => {
5
- FileDirector.registerFileUploadCallback('user-avatars', async (file) => {
6
- return services.users.updateUserAvatarAfterUpload({ file });
7
- });
8
-
9
- FileDirector.registerFileUploadCallback('product-media', async (file) => {
10
- await modules.products.media.create({
11
- productId: file.meta?.productId as string,
12
- mediaId: file._id,
13
- });
14
- });
15
-
16
- FileDirector.registerFileUploadCallback('assortment-media', async (file) => {
17
- await modules.assortments.media.create({
18
- assortmentId: file.meta?.assortmentId as string,
19
- mediaId: file._id,
20
- });
21
- });
22
- };
@@ -1,104 +0,0 @@
1
- import {
2
- EventListenerWorker,
3
- FailedRescheduler,
4
- FailedReschedulerParams,
5
- IntervalWorker,
6
- IntervalWorkerParams,
7
- IScheduler,
8
- IWorker,
9
- UnchainedCore,
10
- } from '@unchainedshop/core';
11
- import { runMigrations } from '../migrations/runMigrations.js';
12
- import { MigrationRepository } from '@unchainedshop/mongodb';
13
- import { createLogger } from '@unchainedshop/logger';
14
- const logger = createLogger('unchained:worker');
15
-
16
- export type WorkQueueQueueManager = IWorker<any> | IScheduler<any>;
17
- export interface SetupWorkqueueOptions extends IntervalWorkerParams, FailedReschedulerParams {
18
- enabledQueueManagers?: WorkQueueQueueManager[];
19
- invalidateProviders?: boolean;
20
- providerInvalidationMaxAgeDays?: number;
21
- assignCartForUsers?: boolean;
22
- disableWorker?: boolean;
23
- skipInvalidationOnStartup?: boolean;
24
- }
25
-
26
- const {
27
- UNCHAINED_DISABLE_PROVIDER_INVALIDATION,
28
- UNCHAINED_DISABLE_WORKER,
29
- UNCHAINED_ASSIGN_CART_FOR_USERS,
30
- } = process.env;
31
-
32
- export const defaultQueueManagers: WorkQueueQueueManager[] = [
33
- FailedRescheduler,
34
- EventListenerWorker,
35
- IntervalWorker,
36
- ];
37
-
38
- export const queueWorkers: any[] = [];
39
-
40
- export async function setupWorkqueue({
41
- unchainedAPI,
42
- migrationRepository,
43
- ...workQueueOptions
44
- }: {
45
- unchainedAPI: UnchainedCore;
46
- migrationRepository: MigrationRepository<UnchainedCore>;
47
- } & SetupWorkqueueOptions) {
48
- if (workQueueOptions.disableWorker || UNCHAINED_DISABLE_WORKER) return;
49
-
50
- // Run migrations
51
- await runMigrations({ migrationRepository, unchainedAPI });
52
-
53
- // Start queue managers
54
- (workQueueOptions?.enabledQueueManagers || defaultQueueManagers).forEach((f) => {
55
- const handler = f.actions(workQueueOptions, unchainedAPI);
56
- queueWorkers.push(handler);
57
- handler.start();
58
- });
59
-
60
- // Invalidate providers on carts
61
- if (workQueueOptions.invalidateProviders ?? !UNCHAINED_DISABLE_PROVIDER_INVALIDATION) {
62
- const orders = await unchainedAPI.modules.orders.findCartsToInvalidate(
63
- workQueueOptions.providerInvalidationMaxAgeDays,
64
- );
65
- await Promise.allSettled(
66
- orders.map(async (order) => {
67
- await unchainedAPI.services.orders.updateCalculation(order._id);
68
- }),
69
- );
70
- }
71
-
72
- // Ensure users have carts
73
- if (workQueueOptions.assignCartForUsers ?? Boolean(UNCHAINED_ASSIGN_CART_FOR_USERS)) {
74
- const users = await unchainedAPI.modules.users.findUsers({});
75
-
76
- await Promise.all(
77
- users.map((user) => {
78
- const locale = unchainedAPI.modules.users.userLocale(user);
79
- if (!locale.region) return null;
80
- return unchainedAPI.services.orders.nextUserCart({
81
- user,
82
- countryCode: locale.region,
83
- });
84
- }),
85
- );
86
- }
87
-
88
- // Setup filter cache
89
- if (!workQueueOptions?.skipInvalidationOnStartup) {
90
- unchainedAPI.services.filters
91
- .invalidateFilterCache()
92
- .then(() => {
93
- logger.info('Filter cache rebuilt');
94
- })
95
- .catch(logger.warn);
96
- }
97
- }
98
-
99
- export function stopWorkqueue() {
100
- queueWorkers.forEach((worker) => {
101
- worker?.stop();
102
- });
103
- queueWorkers.length = 0;
104
- }
@@ -1,128 +0,0 @@
1
- import { startAPIServer, roles, UnchainedServerOptions } from '@unchainedshop/api';
2
- import { initCore, UnchainedCoreOptions } from '@unchainedshop/core';
3
- import { initDb, mongodb, stopDb } from '@unchainedshop/mongodb';
4
- import { defaultLogger } from '@unchainedshop/logger';
5
- import { UnchainedCore } from '@unchainedshop/core';
6
- import { setupAccounts } from './setup/setupAccounts.js';
7
- import { setupUploadHandlers } from './setup/setupUploadHandlers.js';
8
- import { setupTemplates, MessageTypes } from './setup/setupTemplates.js';
9
- import { SetupWorkqueueOptions, stopWorkqueue, setupWorkqueue } from './setup/setupWorkqueue.js';
10
- import { createMigrationRepository } from './migrations/migrationRepository.js';
11
- import { IRoleOptionConfig } from '@unchainedshop/roles';
12
-
13
- export { MessageTypes };
14
-
15
- export type PlatformOptions = {
16
- rolesOptions?: IRoleOptionConfig;
17
- workQueueOptions?: SetupWorkqueueOptions;
18
- } & Omit<UnchainedCoreOptions, 'migrationRepository' | 'db'> &
19
- Omit<UnchainedServerOptions, 'roles' | 'unchainedAPI'>;
20
-
21
- const REQUIRED_ENV_VARIABLES = [
22
- 'EMAIL_WEBSITE_NAME',
23
- 'EMAIL_WEBSITE_URL',
24
- 'EMAIL_FROM',
25
- 'ROOT_URL',
26
- 'UNCHAINED_TOKEN_SECRET',
27
- ];
28
-
29
- const exitOnMissingEnvironmentVariables = () => {
30
- const failedEnv = REQUIRED_ENV_VARIABLES.filter((key) => !process.env[key]);
31
- if (failedEnv.length > 0) {
32
- defaultLogger.error(`Missing required environment variables at boot time: ${failedEnv.join(', ')}`);
33
- process.exit(1);
34
- }
35
- };
36
-
37
- const existOnInvalidEnvironmentVariables = () => {
38
- if ((process.env?.UNCHAINED_TOKEN_SECRET || '').length < 32) {
39
- defaultLogger.error(
40
- 'UNCHAINED_TOKEN_SECRET must be assigned a string that has a length of 32 or greater',
41
- );
42
- process.exit(1);
43
- }
44
- };
45
-
46
- export const startPlatform = async ({
47
- modules,
48
- services,
49
- options,
50
- rolesOptions,
51
- bulkImporter,
52
- workQueueOptions,
53
- ...arbitraryAPIServerConfiguration
54
- }: PlatformOptions): Promise<{
55
- unchainedAPI: UnchainedCore;
56
- graphqlHandler: any;
57
- db: mongodb.Db;
58
- }> => {
59
- const start = performance.now();
60
-
61
- exitOnMissingEnvironmentVariables();
62
- existOnInvalidEnvironmentVariables();
63
-
64
- const configuredRoles = roles.configureRoles(rolesOptions || {});
65
-
66
- // Configure database
67
- const db = await initDb();
68
-
69
- // Prepare Migrations
70
- const migrationRepository = createMigrationRepository(db);
71
-
72
- // Initialise core api using the database
73
- const unchainedAPI = await initCore({
74
- db,
75
- migrationRepository,
76
- bulkImporter,
77
- modules,
78
- services,
79
- options,
80
- });
81
-
82
- // Setup Accounts specific extensions and event handlers
83
- setupAccounts(unchainedAPI);
84
-
85
- // Setup Messaging Templates
86
- setupTemplates(unchainedAPI);
87
-
88
- // Setup File Upload Handlers
89
- setupUploadHandlers(unchainedAPI);
90
-
91
- // Start GraphQL Server
92
- const graphqlHandler = await startAPIServer({
93
- unchainedAPI,
94
- roles: configuredRoles,
95
- ...arbitraryAPIServerConfiguration,
96
- });
97
-
98
- // Setup Work Queue
99
- await setupWorkqueue({
100
- unchainedAPI,
101
- migrationRepository,
102
- ...workQueueOptions,
103
- });
104
-
105
- const { default: packageJson } = await import(`${import.meta.dirname}/../package.json`, {
106
- with: { type: 'json' },
107
- });
108
- defaultLogger.info(`Unchained Engine running`, { version: packageJson.version });
109
-
110
- const cleanup = (signal) => async () => {
111
- defaultLogger.debug('Stopping Workqueue', { signal });
112
- stopWorkqueue();
113
- defaultLogger.debug('Stopping GraphQL server', { signal });
114
- await graphqlHandler.dispose();
115
- defaultLogger.debug('Stopping DB Connection', { signal });
116
- await stopDb();
117
- defaultLogger.debug(`Unchained Engine exiting`, { signal, version: packageJson.version });
118
- process.exit(0);
119
- };
120
-
121
- process.on('SIGTERM', cleanup('SIGTERM'));
122
- process.on('SIGINT', cleanup('SIGINT'));
123
-
124
- const end = performance.now();
125
- defaultLogger.debug(`Unchained Engine started in ${(end - start).toFixed(0)} ms`);
126
-
127
- return { unchainedAPI, graphqlHandler, db };
128
- };
@@ -1,9 +0,0 @@
1
- export { resolveOrderRejectionTemplate } from './resolveOrderRejectionTemplate.js';
2
- export { resolveAccountActionTemplate } from './resolveAccountActionTemplate.js';
3
- export { resolveForwardDeliveryTemplate } from './resolveForwardDeliveryTemplate.js';
4
- export { resolveOrderConfirmationTemplate } from './resolveOrderConfirmationTemplate.js';
5
- export { resolveQuotationStatusTemplate } from './resolveQuotationStatusTemplate.js';
6
- export { resolveEnrollmentStatusTemplate } from './resolveEnrollmentStatusTemplate.js';
7
- export { resolveErrorReportTemplate } from './resolveErrorReportTemplate.js';
8
-
9
- export * as parser from './order-parser/index.js';
@@ -1,50 +0,0 @@
1
- import { UnchainedCore } from '@unchainedshop/core';
2
- import { Order } from '@unchainedshop/core-orders';
3
- import { ProductPricingSheet } from '@unchainedshop/core';
4
- import { ch } from '@unchainedshop/utils';
5
-
6
- type PriceFormatter = ({ amount, currencyCode }: { amount: number; currencyCode: string }) => string;
7
-
8
- export const getOrderPositionsData = async (
9
- order: Order,
10
- params: { locale: Intl.Locale; useNetPrice?: boolean; format?: PriceFormatter },
11
- context: UnchainedCore,
12
- ) => {
13
- const { modules } = context;
14
- const { useNetPrice, format = ch.priceToString } = params || {};
15
- const orderPositions = await modules.orders.positions.findOrderPositions({
16
- orderId: order._id,
17
- });
18
-
19
- return Promise.all(
20
- orderPositions.map(async (orderPosition) => {
21
- const productTexts = await modules.products.texts.findLocalizedText({
22
- productId: orderPosition.productId,
23
- locale: params.locale,
24
- });
25
-
26
- const positionPricing = ProductPricingSheet({
27
- calculation: orderPosition.calculation,
28
- currencyCode: order.currencyCode,
29
- quantity: orderPosition.quantity,
30
- });
31
-
32
- const total = positionPricing.total({ useNetPrice });
33
- const unitPrice = positionPricing.unitPrice({ useNetPrice });
34
-
35
- const { quantity } = positionPricing;
36
- return {
37
- productId: orderPosition.productId,
38
- configuration: orderPosition.configuration,
39
- productTexts,
40
- quantity,
41
- rawPrices: {
42
- unitPrice,
43
- total,
44
- },
45
- unitPrice: format(unitPrice),
46
- total: format(total),
47
- };
48
- }),
49
- );
50
- };
@@ -1,91 +0,0 @@
1
- import { Order } from '@unchainedshop/core-orders';
2
- import { UnchainedCore, OrderPricingSheet, OrderPricingRowCategory } from '@unchainedshop/core';
3
- import { ch } from '@unchainedshop/utils';
4
-
5
- type PriceFormatter = ({ amount, currencyCode }: { amount: number; currencyCode: string }) => string;
6
-
7
- export const getOrderSummaryData = async (
8
- order: Order,
9
- params: { locale: Intl.Locale; useNetPrice?: boolean; format?: PriceFormatter },
10
- context: UnchainedCore,
11
- ) => {
12
- const { modules } = context;
13
- const { useNetPrice, format = ch.priceToString } = params || {};
14
- const orderDelivery =
15
- order.deliveryId &&
16
- (await modules.orders.deliveries.findDelivery({
17
- orderDeliveryId: order.deliveryId,
18
- }));
19
-
20
- if (!orderDelivery) throw new Error('Order delivery not found');
21
-
22
- const orderPayment =
23
- order.paymentId &&
24
- (await modules.orders.payments.findOrderPayment({
25
- orderPaymentId: order.paymentId,
26
- }));
27
-
28
- if (!orderPayment) throw new Error('Order payment not found');
29
-
30
- const paymentProvider = await modules.payment.paymentProviders.findProvider({
31
- paymentProviderId: orderPayment.paymentProviderId,
32
- });
33
- const deliveryProvider = await modules.delivery.findProvider({
34
- deliveryProviderId: orderDelivery.deliveryProviderId,
35
- });
36
-
37
- const deliveryAddress = ch.addressToString(
38
- orderDelivery?.context?.deliveryAddress || order.billingAddress,
39
- );
40
- const billingAddress = ch.addressToString(order.billingAddress);
41
- const orderPricing = OrderPricingSheet({
42
- calculation: order.calculation,
43
- currencyCode: order.currencyCode,
44
- });
45
-
46
- const paymentTotal = orderPricing.total({
47
- category: OrderPricingRowCategory.Payment,
48
- useNetPrice,
49
- });
50
-
51
- const deliveryTotal = orderPricing.total({
52
- category: OrderPricingRowCategory.Delivery,
53
- useNetPrice,
54
- });
55
-
56
- const taxesTotal = orderPricing.total({
57
- category: OrderPricingRowCategory.Taxes,
58
- useNetPrice,
59
- });
60
-
61
- const itemsTotal = orderPricing.total({
62
- category: OrderPricingRowCategory.Items,
63
- useNetPrice,
64
- });
65
-
66
- const total = orderPricing.total({ useNetPrice });
67
-
68
- const payment = paymentProvider?.adapterKey;
69
- const delivery = deliveryProvider?.adapterKey;
70
-
71
- return {
72
- rawPrices: {
73
- items: itemsTotal,
74
- taxes: taxesTotal,
75
- delivery: deliveryTotal,
76
- payment: paymentTotal,
77
- gross: total,
78
- },
79
- prices: {
80
- items: format(itemsTotal),
81
- taxes: format(taxesTotal),
82
- delivery: format(deliveryTotal),
83
- payment: format(paymentTotal),
84
- gross: format(total),
85
- },
86
- payment,
87
- delivery,
88
- deliveryAddress,
89
- billingAddress,
90
- };
91
- };
@@ -1,65 +0,0 @@
1
- import { getOrderPositionsData } from './getOrderPositionsData.js';
2
- import { getOrderSummaryData } from './getOrderSummaryData.js';
3
- import { Order } from '@unchainedshop/core-orders';
4
- import { UnchainedCore } from '@unchainedshop/core';
5
-
6
- export const transformOrderToText = async (
7
- { order, locale }: { order: Order; locale: Intl.Locale },
8
- context: UnchainedCore,
9
- ) => {
10
- const orderDate = new Date(order.ordered!).toLocaleString();
11
- const orderNumber = order.orderNumber;
12
- const summary = await getOrderSummaryData(order, { locale }, context);
13
- const positions = await getOrderPositionsData(order, { locale }, context);
14
-
15
- const positionsText = positions
16
- .map((pos) => ` ${pos.quantity}x ${pos.productTexts.title} ${pos.total}`)
17
- .join('\n');
18
-
19
- const subtotalText = summary.rawPrices.items.amount ? ` Subtotal ${summary.prices.items}\n` : '';
20
- const deliveryFeesText = summary.rawPrices.delivery.amount
21
- ? ` Delivery ${summary.prices.delivery}\n`
22
- : '';
23
- const paymentFeesText = summary.rawPrices.payment.amount
24
- ? ` Payment ${summary.prices.payment}\n`
25
- : '';
26
- const totalLine = ` ${'─'.repeat(50)}\n Order Total ${summary.prices.gross}`;
27
- const taxesText = summary.rawPrices.taxes.amount ? `\n (incl. VAT ${summary.prices.taxes})` : '';
28
-
29
- return `═════════════════════════════════════════════════════
30
- ORDER CONFIRMATION
31
- ═════════════════════════════════════════════════════
32
-
33
- Order Number: ${orderNumber}
34
- Order Date: ${orderDate}
35
-
36
- ─────────────────────────────────────────────────────
37
- DELIVERY INFORMATION
38
- ─────────────────────────────────────────────────────
39
-
40
- Delivery Method: ${summary.delivery}
41
-
42
- Delivery Address:
43
- ${summary.deliveryAddress}
44
-
45
- ─────────────────────────────────────────────────────
46
- PAYMENT INFORMATION
47
- ─────────────────────────────────────────────────────
48
-
49
- Payment Method: ${summary.payment}
50
-
51
- Billing Address:
52
- ${summary.billingAddress}
53
-
54
- ─────────────────────────────────────────────────────
55
- ORDER DETAILS
56
- ─────────────────────────────────────────────────────
57
-
58
- ${positionsText}
59
-
60
- ${subtotalText}${deliveryFeesText}${paymentFeesText}${totalLine}${taxesText}
61
- ═════════════════════════════════════════════════════
62
- `;
63
- };
64
-
65
- export { getOrderPositionsData, getOrderSummaryData };
@@ -1,127 +0,0 @@
1
- import { TemplateResolver } from '@unchainedshop/core';
2
-
3
- const { EMAIL_FROM, EMAIL_WEBSITE_URL, EMAIL_WEBSITE_NAME } = process.env;
4
-
5
- const verifyEmailEnglishConfig = {
6
- buttonText: 'Verify email address',
7
- message: 'We need to ensure that this email is yours as a commercial partner.',
8
- subject: `${EMAIL_WEBSITE_NAME}: Verify your email address`,
9
- };
10
-
11
- const passwordChangedEmailEnglishConfig = {
12
- message: `
13
- Your account password has been successfully changed.\n
14
- `,
15
- subject: `${EMAIL_WEBSITE_NAME}: Your password has been changed`,
16
- };
17
-
18
- const buildTextTemplate = ({
19
- message,
20
- buttonText,
21
- url,
22
- }: {
23
- message: string;
24
- buttonText: string;
25
- url: string | null;
26
- }) => {
27
- const urlSection = url
28
- ? `
29
-
30
- -----------------
31
- ${buttonText}: ${url}
32
- -----------------
33
- `
34
- : '';
35
-
36
- return ` ${message}${urlSection}`;
37
- };
38
-
39
- const emailConfig = {
40
- 'enroll-account': {
41
- url: (token) => `${EMAIL_WEBSITE_URL}/enroll-account?token=${token}`,
42
- en: {
43
- buttonText: 'Set password',
44
- message: `${EMAIL_WEBSITE_NAME} has set up an user account for you. Please click below to set up a password and to finish the registration.`,
45
- subject: `${EMAIL_WEBSITE_NAME}: You got a new account`,
46
- },
47
- de: {
48
- buttonText: 'Passwort setzen',
49
- message: `${EMAIL_WEBSITE_NAME} hat für dich ein Benutzerkonto angelegt. Bitte klicke unten um ein Passwort zu setzen und damit die Registrierung abzuschliessen.`,
50
- subject: `${EMAIL_WEBSITE_NAME}: Für dich wurde ein Konto eingerichtet`,
51
- },
52
- fr: {
53
- buttonText: 'Set password',
54
- message: `${EMAIL_WEBSITE_NAME} has set up an user account for you. Please click below to set up a password and to finish the registration.`,
55
- subject: `${EMAIL_WEBSITE_NAME}: You got a new account`,
56
- },
57
- },
58
- 'reset-password': {
59
- url: (token) => `${EMAIL_WEBSITE_URL}/reset-password?token=${token}`,
60
- en: {
61
- buttonText: 'Set new password',
62
- message: `
63
- Click on „Set new password". If you don’t want to set a new password, just ignore this email.\n
64
- `,
65
- subject: `${EMAIL_WEBSITE_NAME}: Set password`,
66
- },
67
- de: {
68
- buttonText: 'Neues Passwort setzen',
69
- message: `
70
- Klicke auf „Neues Passwort setzen", um ein neues Passwort anzulegen.\n
71
- Falls du dein Passwort nicht zurücksetzen möchtest, ignoriere dieses E-Mail einfach.\n
72
- `,
73
- subject: `${EMAIL_WEBSITE_NAME}: Passwort zurücksetzen`,
74
- },
75
- fr: {
76
- buttonText: 'Set new password',
77
- message: `
78
- Click on „Set new password". If you don’t want to set a new password, just ignore this email.\n
79
- `,
80
- subject: `${EMAIL_WEBSITE_NAME}: Set password`,
81
- },
82
- },
83
- 'verify-email': {
84
- url: (token) => `${EMAIL_WEBSITE_URL}/verify-email?token=${token}`,
85
- en: verifyEmailEnglishConfig,
86
- de: verifyEmailEnglishConfig,
87
- fr: verifyEmailEnglishConfig,
88
- },
89
- '': {
90
- // password changed!
91
- url: () => null,
92
- en: passwordChangedEmailEnglishConfig,
93
- de: passwordChangedEmailEnglishConfig,
94
- fr: passwordChangedEmailEnglishConfig,
95
- },
96
- };
97
-
98
- export const resolveAccountActionTemplate: TemplateResolver = async (
99
- { userId, action, recipientEmail, token },
100
- { modules },
101
- ) => {
102
- if (!token || !action) return [];
103
-
104
- const user = await modules.users.findUserById(userId);
105
- const locale = modules.users.userLocale(user);
106
-
107
- const { url } = emailConfig[action];
108
- const { subject, message, buttonText } = emailConfig[action][locale.language];
109
-
110
- const text = buildTextTemplate({
111
- buttonText,
112
- message,
113
- url: url(token),
114
- });
115
-
116
- return [
117
- {
118
- type: 'EMAIL',
119
- input: {
120
- from: EMAIL_FROM || 'noreply@unchained.local',
121
- to: recipientEmail || modules.users.primaryEmail(user)?.address,
122
- subject,
123
- text,
124
- },
125
- },
126
- ];
127
- };