cabloy 5.1.127 → 5.1.128

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 (85) hide show
  1. package/.cabloy-version +1 -1
  2. package/CHANGELOG.md +16 -0
  3. package/cabloy-docs/backend/i18n-guide.md +86 -7
  4. package/e2e/specs/a-commerce/commerce.spec.ts +93 -8
  5. package/package.json +1 -1
  6. package/vona/env/.env +2 -0
  7. package/vona/env/.env.prod +6 -2
  8. package/vona/packages-cli/cabloy-cli/package.json +1 -1
  9. package/vona/packages-cli/cli/package.json +1 -1
  10. package/vona/packages-cli/cli-set-api/package.json +1 -1
  11. package/vona/packages-utils/module-glob/package.json +1 -1
  12. package/vona/packages-utils/utils/package.json +1 -1
  13. package/vona/packages-utils/utils/src/vona.ts +8 -1
  14. package/vona/packages-vona/vona/package.json +1 -1
  15. package/vona/packages-vona/vona-core/package.json +1 -1
  16. package/vona/packages-vona/vona-mock/package.json +1 -1
  17. package/vona/pnpm-lock.yaml +10 -39
  18. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/.metadata/index.ts +1 -1
  19. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productSelectResItem.tsx +3 -1
  20. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productView.tsx +3 -1
  21. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/entity/product.tsx +23 -3
  22. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/model/product.ts +1 -0
  23. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/service/product.ts +7 -2
  24. package/vona/src/suite/a-commerce/modules/commerce-catalog/test/productPresentation.test.ts +214 -0
  25. package/vona/src/suite/a-commerce/modules/commerce-payment/src/bean/payScene.commerceOrder.ts +39 -3
  26. package/vona/src/suite/a-commerce/modules/commerce-payment/src/service/commercePayScene.ts +14 -1
  27. package/vona/src/suite/a-commerce/modules/commerce-payment/src/service/paymentAttempt.ts +5 -1
  28. package/vona/src/suite/a-commerce/modules/commerce-trade/src/.metadata/index.ts +17 -0
  29. package/vona/src/suite/a-commerce/modules/commerce-trade/src/controller/checkout.ts +18 -0
  30. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/checkoutCreate.tsx +3 -0
  31. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/checkoutPaymentMethod.tsx +19 -0
  32. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/checkoutPaymentMethods.tsx +17 -0
  33. package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/order.ts +51 -0
  34. package/vona/src/suite/a-commerce/modules/commerce-trade/test/paymentOutcome.test.ts +2 -1
  35. package/vona/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
  36. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/.metadata/index.ts +94 -55
  37. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/bean.payScene.ts +65 -1
  38. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/meta.redlock.ts +2 -0
  39. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/controller/paymentCallback.ts +32 -0
  40. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/controller/paymentSession.ts +26 -8
  41. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/paymentCallback.ts +99 -0
  42. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/paymentSession.ts +26 -3
  43. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/providerOperation.ts +214 -82
  44. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/refundOperation.ts +27 -2
  45. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/types/payScene.ts +9 -0
  46. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/types/payment.ts +5 -0
  47. package/vona/src/suite-vendor/a-pay/modules/pay-mock/package.json +1 -1
  48. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/bean/payProvider.mock.ts +7 -0
  49. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/package.json +1 -1
  50. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/bean/payProvider.paypal.ts +395 -84
  51. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/lib/index.ts +1 -1
  52. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/lib/paypalGateway.ts +129 -0
  53. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/types/index.ts +2 -0
  54. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/package.json +1 -1
  55. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/src/bean/payProvider.stripe.ts +7 -0
  56. package/vona/src/suite-vendor/a-pay/package.json +5 -5
  57. package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
  58. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  59. package/zova/packages-cli/cli/package.json +3 -3
  60. package/zova/packages-cli/cli-set-front/package.json +4 -4
  61. package/zova/packages-utils/zova-jsx/package.json +3 -3
  62. package/zova/packages-utils/zova-vite/package.json +2 -2
  63. package/zova/packages-zova/zova/package.json +3 -3
  64. package/zova/packages-zova/zova-core/package.json +2 -2
  65. package/zova/src/suite/a-commerce/modules/commerce-member/src/api/openapi/schemas.ts +8 -0
  66. package/zova/src/suite/a-commerce/modules/commerce-member/src/api/openapi/types.ts +175 -0
  67. package/zova/src/suite/a-commerce/modules/commerce-trade/cli/openapi.config.ts +1 -0
  68. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/commerceTradeCheckout.ts +16 -0
  69. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/schemas.ts +8 -0
  70. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/types.ts +177 -2
  71. package/zova/src/suite/a-commerce/modules/commerce-trade/src/apiSchema/commerceTradeCheckout.ts +12 -1
  72. package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/cart.ts +11 -0
  73. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/checkout/controller.tsx +25 -1
  74. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/payment/controller.tsx +27 -4
  75. package/zova/src/suite-vendor/a-pay/modules/a-pay/cli/openapi.config.ts +1 -1
  76. package/zova/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
  77. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/api/openapi/schemas.ts +20 -8
  78. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/api/openapi/types.ts +292 -32
  79. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/api/paymentSession.ts +26 -4
  80. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/apiSchema/paymentSession.ts +5 -0
  81. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/component/paymentNextAction/controller.tsx +2 -1
  82. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/model/paymentSession.ts +12 -0
  83. package/zova/src/suite-vendor/a-pay/package.json +2 -2
  84. package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +4 -4
  85. package/zova/src/suite-vendor/a-zova/package.json +2 -2
package/.cabloy-version CHANGED
@@ -1 +1 @@
1
- 5.1.127
1
+ 5.1.128
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.1.128
4
+
5
+ ### Features
6
+
7
+ - Add payment session support.
8
+ - Combine resource name components.
9
+ - Add a PayPal Commerce runbook.
10
+ - Implement the product semantic presentation contract.
11
+ - Complete the PayPal Commerce payment flow.
12
+
13
+ ### Bug Fixes
14
+
15
+ - Preserve changes when resolving main branch conflicts.
16
+ - Enforce payment provider availability.
17
+ - Harden PayPal Commerce reconciliation.
18
+
3
19
  ## 5.1.127
4
20
 
5
21
  ### Features
@@ -127,17 +127,96 @@ declare module 'vona' {
127
127
 
128
128
  In the VSCode workflow, the `recordlocale` snippet can generate the augmentation skeleton.
129
129
 
130
- ## Plural support
130
+ ## Formatting locale text
131
131
 
132
- Locale resources can express plural-aware strings through naming conventions such as:
132
+ Locale values use printf-style placeholders. Pass formatting values after the locale key:
133
133
 
134
- - `TestApples_`
135
- - `TestApples_0`
136
- - `TestApples_1`
134
+ ```typescript
135
+ // en-us.ts
136
+ export default {
137
+ Apples_: '%d apples',
138
+ Greeting: '%s has %d apples',
139
+ };
140
+ ```
141
+
142
+ ```typescript
143
+ this.scope.locale.Apples_.locale('en-us', 2);
144
+ // 2 apples
145
+
146
+ this.scope.locale.Greeting.locale('en-us', 'Mike', 2);
147
+ // Mike has 2 apples
148
+ ```
149
+
150
+ Common placeholders include:
151
+
152
+ - `%s`: string
153
+ - `%d`: number
154
+ - `%i`: integer
155
+ - `%f`: floating-point number
156
+ - `%j`: JSON
157
+ - `%%`: literal percent sign
158
+
159
+ ## Plural and value-specific support
160
+
161
+ Locale resources can express special messages for exact numeric values through key suffixes:
162
+
163
+ ```typescript
164
+ export default {
165
+ Apples_: '%d apples',
166
+ Apples_0: 'no apples',
167
+ Apples_1: 'one apple',
168
+ };
169
+ ```
170
+
171
+ The suffix selects a special message based on the first formatting argument:
172
+
173
+ ```typescript
174
+ this.scope.locale.Apples_.locale('en-us', 0);
175
+ // no apples
176
+
177
+ this.scope.locale.Apples_.locale('en-us', 1);
178
+ // one apple
137
179
 
138
- Multiple-parameter variants are also supported through ordinal-aware suffix conventions.
180
+ this.scope.locale.Apples_.locale('en-us', 2);
181
+ // 2 apples
182
+ ```
183
+
184
+ The base key is used when no value-specific key matches. Therefore, it is not necessary to define a special key for every possible number.
185
+
186
+ For a numeric value that is not the first formatting argument, use this naming convention:
187
+
188
+ ```text
189
+ <baseKey>_<matchedValue>_<argumentIndex>
190
+ ```
191
+
192
+ The argument index is zero-based. For example:
193
+
194
+ ```typescript
195
+ export default {
196
+ NameApples_: '%s has %d apples',
197
+ NameApples_0_1: '%s has no apples',
198
+ NameApples_1_1: '%s has one apple',
199
+ };
200
+ ```
201
+
202
+ Here:
203
+
204
+ - `NameApples_0_1` matches `args[1] === 0`, the second formatting argument;
205
+ - `NameApples_1_1` matches `args[1] === 1`, the second formatting argument;
206
+ - `NameApples_` is used for other values.
207
+
208
+ ```typescript
209
+ this.scope.locale.NameApples_.locale('en-us', 'Mike', 0);
210
+ // Mike has no apples
211
+
212
+ this.scope.locale.NameApples_.locale('en-us', 'Mike', 1);
213
+ // Mike has one apple
214
+
215
+ this.scope.locale.NameApples_.locale('en-us', 'Mike', 2);
216
+ // Mike has 2 apples
217
+ ```
139
218
 
140
- This makes plural handling part of the localization model instead of forcing each project to invent its own formatting layer.
219
+ This is exact numeric-value selection, not automatic `Intl.PluralRules` or CLDR plural-category resolution. It makes common plural handling part of the localization model while allowing each locale to define its own wording.
141
220
 
142
221
  ## Timezone support
143
222
 
@@ -415,7 +415,7 @@ test(
415
415
  const startResponse = waitForApiResponse(
416
416
  page,
417
417
  'POST',
418
- new RegExp(`/api/payment-session/${checkout.paymentSessionId}/start$`),
418
+ new RegExp(`/api/pay/payment-session/${checkout.paymentSessionId}/start$`),
419
419
  );
420
420
  await page.getByRole('button', { name: 'Start payment', exact: true }).click();
421
421
  expect((await startResponse).ok()).toBeTruthy();
@@ -535,9 +535,7 @@ test(
535
535
  await orderCard.getByRole('button', { name: 'View', exact: true }).click();
536
536
  await expect(page).toHaveURL(new RegExp(`/commerce/order/${checkout.orderId}(?:/|$)`));
537
537
  await historyDetailResponse;
538
- await expect(
539
- page.getByRole('heading', { name: `Order #${checkout.orderId}`, level: 1 }),
540
- ).toBeVisible();
538
+ await expect(page.getByRole('heading', { name: `Order #${checkout.orderId}` })).toBeVisible();
541
539
  await expect(page.getByText('1 × $45.99 = $45.99')).toBeVisible();
542
540
  await expect(page.getByRole('alert')).toHaveCount(0);
543
541
  expect(pageErrors).toEqual([]);
@@ -586,7 +584,7 @@ test(
586
584
  const startResponse = waitForApiResponse(
587
585
  page,
588
586
  'POST',
589
- new RegExp(`/api/payment-session/${checkout.paymentSessionId}/start$`),
587
+ new RegExp(`/api/pay/payment-session/${checkout.paymentSessionId}/start$`),
590
588
  );
591
589
  await page.getByRole('button', { name: 'Start payment', exact: true }).click();
592
590
  expect((await startResponse).ok()).toBeTruthy();
@@ -654,7 +652,7 @@ test(
654
652
  const startResponse = waitForApiResponse(
655
653
  page,
656
654
  'POST',
657
- new RegExp(`/api/payment-session/${checkout.paymentSessionId}/start$`),
655
+ new RegExp(`/api/pay/payment-session/${checkout.paymentSessionId}/start$`),
658
656
  );
659
657
  await page.getByRole('button', { name: 'Start payment', exact: true }).click();
660
658
  expect((await startResponse).ok()).toBeTruthy();
@@ -738,8 +736,7 @@ test(
738
736
  { timeout: 40_000 },
739
737
  )
740
738
  .toContain('refunded');
741
- const refundedOrderRow = await getAdminOrderRow(adminPage, checkout.orderId);
742
- await expect(refundedOrderRow).toContainText('refunded');
739
+ await expect(await getAdminOrderRow(adminPage, checkout.orderId)).toContainText('refunded');
743
740
  expect(adminPageErrors).toEqual([]);
744
741
  } finally {
745
742
  await adminContext.close().catch(() => {});
@@ -1016,6 +1013,94 @@ test(
1016
1013
  },
1017
1014
  );
1018
1015
 
1016
+ test(
1017
+ 'ATP-SPC-02: Product renders semantic Admin relation and publication controls',
1018
+ { tag: ['@admin', '@flow', '@product'] },
1019
+ async ({ browser }, testInfo) => {
1020
+ test.setTimeout(60_000);
1021
+ const suffix = `${testInfo.workerIndex}-${testInfo.parallelIndex ?? testInfo.retry}-${Date.now()}`;
1022
+ const categoryName = `E2E Product Category ${suffix}`;
1023
+ const productTitle = `E2E Product ${suffix}`;
1024
+ const adminContext = await browser.newContext();
1025
+ const adminPage = await adminContext.newPage();
1026
+ const adminPageErrors = collectPageErrors(adminPage);
1027
+ const productCreatePath = '/commerce-admin/rest/resource/commerce-catalog%3Aproduct/create';
1028
+ const productListPath = '/commerce-admin/rest/resource/commerce-catalog%3Aproduct';
1029
+ const categoryActionPath = '/api/commerce/catalog/category';
1030
+ const productActionPath = '/api/commerce/catalog/product';
1031
+ let categoryId: number | string | undefined;
1032
+ let productId: number | string | undefined;
1033
+ let headers: { Authorization: string } | undefined;
1034
+ try {
1035
+ await adminPage.setViewportSize({ width: 1440, height: 900 });
1036
+ await login(adminPage, '/commerce-admin/', 'admin', '123456', 'commerceAdmin');
1037
+ const accessToken = (await adminContext.cookies()).find(
1038
+ cookie => cookie.name === 'token',
1039
+ )?.value;
1040
+ expect(accessToken).toBeTruthy();
1041
+ headers = { Authorization: `Bearer ${accessToken}` };
1042
+
1043
+ const categoryResponse = await adminPage.request.post(categoryActionPath, {
1044
+ data: { name: categoryName, published: true },
1045
+ headers,
1046
+ });
1047
+ expect(categoryResponse.ok()).toBeTruthy();
1048
+ categoryId = (await categoryResponse.json()).data;
1049
+ expectTableIdentity(categoryId);
1050
+
1051
+ await adminPage.goto(productCreatePath, { waitUntil: 'load' });
1052
+ const categoryPicker = adminPage
1053
+ .getByRole('group', { name: 'Category' })
1054
+ .getByRole('combobox');
1055
+ await expect(categoryPicker).toBeVisible();
1056
+ await categoryPicker.selectOption({ label: categoryName });
1057
+ await expect(categoryPicker).toHaveValue(String(categoryId));
1058
+ const publication = adminPage.getByRole('group', { name: 'Published' }).getByRole('combobox');
1059
+ await expect(publication).toBeVisible();
1060
+ await publication.selectOption({ label: 'Unpublished' });
1061
+ await expect(publication).toHaveValue('false');
1062
+
1063
+ const productResponse = await adminPage.request.post(productActionPath, {
1064
+ data: { categoryId, title: productTitle, published: false },
1065
+ headers,
1066
+ });
1067
+ expect(productResponse.ok()).toBeTruthy();
1068
+ productId = (await productResponse.json()).data;
1069
+ expectTableIdentity(productId);
1070
+
1071
+ await adminPage.goto(productListPath, { waitUntil: 'load' });
1072
+ const productRow = adminPage.getByRole('row', { name: new RegExp(productTitle) });
1073
+ await expect(productRow).toBeVisible();
1074
+ await expect(productRow.getByText(categoryName, { exact: true })).toBeVisible();
1075
+ await expect(productRow.getByText('Unpublished', { exact: true })).toBeVisible();
1076
+
1077
+ await adminPage.goto(`${productListPath}/${productId}`, { waitUntil: 'load' });
1078
+ await expect(
1079
+ adminPage.getByRole('group', { name: 'Category' }).getByRole('textbox'),
1080
+ ).toHaveValue(categoryName);
1081
+ await expect(
1082
+ adminPage.getByRole('group', { name: 'Published' }).getByRole('textbox'),
1083
+ ).toHaveValue('Unpublished');
1084
+ await expect(adminPage.getByRole('button', { name: 'Submit', exact: true })).toHaveCount(0);
1085
+ expect(adminPageErrors).toEqual([]);
1086
+ } finally {
1087
+ if (productId && headers) {
1088
+ const response = await adminPage.request.delete(`${productActionPath}/${productId}`, {
1089
+ headers,
1090
+ });
1091
+ expect(response.ok()).toBeTruthy();
1092
+ }
1093
+ if (categoryId && headers) {
1094
+ const response = await adminPage.request.delete(`${categoryActionPath}/${categoryId}`, {
1095
+ headers,
1096
+ });
1097
+ expect(response.ok()).toBeTruthy();
1098
+ }
1099
+ await adminContext.close().catch(() => {});
1100
+ }
1101
+ },
1102
+ );
1103
+
1019
1104
  test(
1020
1105
  'Commerce Cart: anonymous browser is redirected to login',
1021
1106
  { tag: ['@web', '@cart'] },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cabloy",
3
- "version": "5.1.127",
3
+ "version": "5.1.128",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A Node.js fullstack framework",
6
6
  "keywords": [
package/vona/env/.env CHANGED
@@ -91,6 +91,8 @@ PAY_MOCK_SECONDARY_WEBHOOK = pay-mock-dev-secondary-webhook
91
91
  PAYPAL_CLIENT_ID =
92
92
  PAYPAL_CLIENT_SECRET =
93
93
  PAYPAL_WEBHOOK_ID =
94
+ PAYPAL_MERCHANT_REFERENCE =
95
+ PAYPAL_ENVIRONMENT = sandbox
94
96
  STRIPE_SECRET_KEY =
95
97
  STRIPE_WEBHOOK_SECRET =
96
98
 
@@ -24,5 +24,9 @@ DATABASE_CLIENT_MYSQL_DATABASE = xxx
24
24
 
25
25
  REDIS_DEFAULT_HOST = 127.0.0.1
26
26
  REDIS_DEFAULT_PORT = 6379
27
- REDIS_DEFAULT_PASSWORD =
28
- REDIS_DEFAULT_DB = 0
27
+ REDIS_DEFAULT_PASSWORD =
28
+ REDIS_DEFAULT_DB = 0
29
+
30
+ # payment
31
+
32
+ PAYPAL_ENVIRONMENT = live
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cabloy/cli",
3
- "version": "3.1.28",
3
+ "version": "3.1.29",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "@cabloy/cli",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-cli",
3
- "version": "1.1.137",
3
+ "version": "1.1.138",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "vona cli",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-cli-set-api",
3
- "version": "1.1.135",
3
+ "version": "1.1.136",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "vona cli-set-api",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cabloy/module-glob",
3
- "version": "5.3.17",
3
+ "version": "5.3.18",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "cabloy module-glob",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cabloy/utils",
3
- "version": "2.1.26",
3
+ "version": "2.1.27",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "cabloy utils",
6
6
  "keywords": [
@@ -17,7 +17,14 @@ export function combineResourceNameParts(
17
17
  // path
18
18
  const res: string[] = [];
19
19
  if (!simplifyProviderId || parts[0] !== 'a') res.push(parts[0]);
20
- if (!simplify || !resourceName.startsWith(parts[1])) res.push(parts[1]);
20
+ if (
21
+ !simplify ||
22
+ (resourceName !== parts[1] &&
23
+ !resourceName.startsWith(`${parts[1]}-`) &&
24
+ !resourceName.startsWith(`${parts[1]}/`))
25
+ ) {
26
+ res.push(parts[1]);
27
+ }
21
28
  if (resourceName) res.push(resourceName);
22
29
  return res;
23
30
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona",
3
- "version": "5.1.84",
3
+ "version": "5.1.85",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "Vona is an intuitive, elegant and powerful Node.js framework for rapidly developing enterprise applications of any size",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-core",
3
- "version": "5.1.36",
3
+ "version": "5.1.37",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "vona",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vona-mock",
3
- "version": "6.1.36",
3
+ "version": "6.1.37",
4
4
  "gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
5
5
  "description": "vona mock",
6
6
  "keywords": [
@@ -24,7 +24,7 @@ importers:
24
24
  dependencies:
25
25
  '@babel/core':
26
26
  specifier: ^7.29.0
27
- version: 7.29.7(supports-color@5.5.0)
27
+ version: 7.29.7
28
28
  '@babel/traverse':
29
29
  specifier: ^7.29.0
30
30
  version: 7.29.7
@@ -69,7 +69,7 @@ importers:
69
69
  version: 8.0.1
70
70
  bullmq:
71
71
  specifier: ^5.74.1
72
- version: 5.79.1(supports-color@5.5.0)
72
+ version: 5.79.1
73
73
  chalk:
74
74
  specifier: ^5.6.2
75
75
  version: 5.6.2
@@ -81,7 +81,7 @@ importers:
81
81
  version: 11.3.5
82
82
  ioredis:
83
83
  specifier: ^5.10.1
84
- version: 5.11.1(supports-color@5.5.0)
84
+ version: 5.11.1
85
85
  knex:
86
86
  specifier: ^3.3.0
87
87
  version: 3.3.0(better-sqlite3@12.11.1)
@@ -2756,7 +2756,7 @@ importers:
2756
2756
  dependencies:
2757
2757
  bullmq:
2758
2758
  specifier: ^5.74.1
2759
- version: 5.79.1(supports-color@5.5.0)
2759
+ version: 5.79.1
2760
2760
  devDependencies:
2761
2761
  clean-package:
2762
2762
  specifier: ^2.2.0
@@ -2778,7 +2778,7 @@ importers:
2778
2778
  dependencies:
2779
2779
  ioredis:
2780
2780
  specifier: ^5.10.1
2781
- version: 5.11.1(supports-color@5.5.0)
2781
+ version: 5.11.1
2782
2782
  devDependencies:
2783
2783
  clean-package:
2784
2784
  specifier: ^2.2.0
@@ -8962,26 +8962,6 @@ snapshots:
8962
8962
  transitivePeerDependencies:
8963
8963
  - supports-color
8964
8964
 
8965
- '@babel/core@7.29.7(supports-color@5.5.0)':
8966
- dependencies:
8967
- '@babel/code-frame': 7.29.7
8968
- '@babel/generator': 7.29.7
8969
- '@babel/helper-compilation-targets': 7.29.7
8970
- '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@5.5.0))
8971
- '@babel/helpers': 7.29.7
8972
- '@babel/parser': 7.29.7
8973
- '@babel/template': 7.29.7
8974
- '@babel/traverse': 7.29.7
8975
- '@babel/types': 7.29.7
8976
- '@jridgewell/remapping': 2.3.5
8977
- convert-source-map: 2.0.0
8978
- debug: 4.4.3(supports-color@5.5.0)
8979
- gensync: 1.0.0-beta.2
8980
- json5: 2.2.3
8981
- semver: 6.3.1
8982
- transitivePeerDependencies:
8983
- - supports-color
8984
-
8985
8965
  '@babel/generator@7.29.7':
8986
8966
  dependencies:
8987
8967
  '@babel/parser': 7.29.7
@@ -9031,15 +9011,6 @@ snapshots:
9031
9011
  transitivePeerDependencies:
9032
9012
  - supports-color
9033
9013
 
9034
- '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@5.5.0))':
9035
- dependencies:
9036
- '@babel/core': 7.29.7(supports-color@5.5.0)
9037
- '@babel/helper-module-imports': 7.29.7
9038
- '@babel/helper-validator-identifier': 7.29.7
9039
- '@babel/traverse': 7.29.7
9040
- transitivePeerDependencies:
9041
- - supports-color
9042
-
9043
9014
  '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)':
9044
9015
  dependencies:
9045
9016
  '@babel/core': 7.29.7
@@ -10006,7 +9977,7 @@ snapshots:
10006
9977
 
10007
9978
  '@sesamecare-oss/redlock@1.4.0(ioredis@5.11.1)':
10008
9979
  dependencies:
10009
- ioredis: 5.11.1(supports-color@5.5.0)
9980
+ ioredis: 5.11.1
10010
9981
 
10011
9982
  '@sindresorhus/merge-streams@2.3.0': {}
10012
9983
 
@@ -10912,10 +10883,10 @@ snapshots:
10912
10883
  base64-js: 1.5.1
10913
10884
  ieee754: 1.2.1
10914
10885
 
10915
- bullmq@5.79.1(supports-color@5.5.0):
10886
+ bullmq@5.79.1:
10916
10887
  dependencies:
10917
10888
  cron-parser: 4.9.0
10918
- ioredis: 5.10.1(supports-color@5.5.0)
10889
+ ioredis: 5.10.1
10919
10890
  msgpackr: 2.0.2
10920
10891
  node-abort-controller: 3.1.1
10921
10892
  semver: 7.8.1
@@ -12061,7 +12032,7 @@ snapshots:
12061
12032
 
12062
12033
  interpret@2.2.0: {}
12063
12034
 
12064
- ioredis@5.10.1(supports-color@5.5.0):
12035
+ ioredis@5.10.1:
12065
12036
  dependencies:
12066
12037
  '@ioredis/commands': 1.5.1
12067
12038
  cluster-key-slot: 1.1.2
@@ -12075,7 +12046,7 @@ snapshots:
12075
12046
  transitivePeerDependencies:
12076
12047
  - supports-color
12077
12048
 
12078
- ioredis@5.11.1(supports-color@5.5.0):
12049
+ ioredis@5.11.1:
12079
12050
  dependencies:
12080
12051
  '@ioredis/commands': 1.10.0
12081
12052
  cluster-key-slot: 1.1.1
@@ -149,7 +149,7 @@ declare module 'vona-module-commerce-catalog' {
149
149
  }
150
150
  export interface IModelOptionsProduct {
151
151
  relations: {
152
- category: IModelRelationBelongsTo<'commerce-catalog:product', 'commerce-catalog:category', false, '*'>;
152
+ category: IModelRelationBelongsTo<'commerce-catalog:product', 'commerce-catalog:category', false, 'id'|'name'|'published'>;
153
153
  skus: IModelRelationHasMany<'commerce-catalog:sku', 'productId', false, '*', undefined, undefined, undefined>;
154
154
  skuAvailables: IModelRelationHasMany<'commerce-catalog:sku', 'productId', false, 'id'|'code'|'productId'|'priceCents', ['commerce-trade:stockBalance'], undefined, undefined>;
155
155
  };
@@ -46,7 +46,9 @@ export interface IDtoOptionsProductSelectResItem extends IDecoratorDtoOptions {}
46
46
  }),
47
47
  ],
48
48
  })
49
- export class DtoProductSelectResItem extends $Dto.get(() => ModelProduct) {
49
+ export class DtoProductSelectResItem extends $Dto.get(() => ModelProduct, {
50
+ include: { category: true },
51
+ }) {
50
52
  @Api.field(
51
53
  v.title($locale('Operations')),
52
54
  ZovaRender.order(1, 'max'),
@@ -22,4 +22,6 @@ export interface IDtoOptionsProductView extends IDecoratorDtoOptions {}
22
22
  }),
23
23
  ],
24
24
  })
25
- export class DtoProductView extends $Dto.get(() => ModelProduct) {}
25
+ export class DtoProductView extends $Dto.get(() => ModelProduct, {
26
+ include: { category: true },
27
+ }) {}
@@ -1,11 +1,12 @@
1
1
  import type { TableIdentity } from 'table-identity';
2
2
  import type { IDecoratorEntityOptions } from 'vona-module-a-orm';
3
3
 
4
- import { $makeMetadata, Api, v } from 'vona-module-a-openapiutils';
4
+ import { $makeMetadata, $resourceName, Api, v } from 'vona-module-a-openapiutils';
5
5
  import { Entity, EntityBase } from 'vona-module-a-orm';
6
6
  import { ZovaRender } from 'zova-rest-cabloy-basic-admin';
7
7
 
8
8
  import { $locale } from '../.metadata/locales.ts';
9
+ import { categoryPublicationItems } from './category.tsx';
9
10
 
10
11
  export interface IEntityOptionsProduct extends IDecoratorEntityOptions {}
11
12
 
@@ -37,10 +38,29 @@ export class EntityProduct extends EntityBase {
37
38
  )
38
39
  title: string;
39
40
 
40
- @Api.field(v.title($locale('Category')), v.required(), ZovaRender.order(2), v.tableIdentity())
41
+ @Api.field(
42
+ v.title($locale('Category')),
43
+ v.required(),
44
+ ZovaRender.order(2),
45
+ ZovaRender.field('basic-resource:formFieldResourcePicker', {
46
+ resource: $resourceName('commerce-catalog:category'),
47
+ relationName: 'category',
48
+ }),
49
+ ZovaRender.cell('basic-resource:resourcePicker', {
50
+ resource: $resourceName('commerce-catalog:category'),
51
+ relationName: 'category',
52
+ }),
53
+ v.tableIdentity(),
54
+ )
41
55
  categoryId: TableIdentity;
42
56
 
43
- @Api.field(v.title($locale('Published')), v.default(false), ZovaRender.order(3))
57
+ @Api.field(
58
+ v.title($locale('Published')),
59
+ v.default(false),
60
+ ZovaRender.order(3),
61
+ ZovaRender.field('basic-select:formFieldSelect', { items: categoryPublicationItems }),
62
+ ZovaRender.cell('basic-select:select', { items: categoryPublicationItems }),
63
+ )
44
64
  published: boolean;
45
65
 
46
66
  @Api.field(v.title($locale('Description')), v.optional(), ZovaRender.order(4))
@@ -13,6 +13,7 @@ export interface IModelOptionsProduct extends IDecoratorModelOptions<EntityProdu
13
13
  'commerce-catalog:product',
14
14
  'commerce-catalog:category',
15
15
  'categoryId',
16
+ { columns: ['id', 'name', 'published'] },
16
17
  ),
17
18
  skus: $relation.hasMany('commerce-catalog:sku', 'productId'),
18
19
  skuAvailables: $relation.hasMany(
@@ -22,7 +22,10 @@ export class ServiceProduct extends BeanBase {
22
22
  }
23
23
 
24
24
  async select(params?: IQueryParams<ModelProduct>): Promise<DtoProductSelectRes> {
25
- return await this.scope.model.product.selectAndCount(params);
25
+ return await this.scope.model.product.selectAndCount({
26
+ ...params,
27
+ include: { ...params?.include, category: true },
28
+ });
26
29
  }
27
30
 
28
31
  async selectPublic(params: IQueryParams<ModelProduct>): Promise<DtoProductPublicSelectRes> {
@@ -50,7 +53,9 @@ export class ServiceProduct extends BeanBase {
50
53
  }
51
54
 
52
55
  async view(id: TableIdentity): Promise<DtoProductView | undefined> {
53
- return await this.scope.model.product.getById(id);
56
+ return await this.scope.model.product.getById(id, {
57
+ include: { category: true },
58
+ });
54
59
  }
55
60
 
56
61
  async viewPublic(id: TableIdentity): Promise<DtoProductPublic | undefined> {