cabloy 5.1.131 → 5.1.133

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 (210) hide show
  1. package/.cabloy-version +1 -1
  2. package/.claude/skills/cabloy-worktree-environment/SKILL.md +6 -6
  3. package/.claude/skills/cabloy-worktree-environment/evals/evals.json +2 -2
  4. package/.github/workflows/vona-test-pg.yml +1 -1
  5. package/.github/workflows/vona-test-sqlite3.yml +1 -1
  6. package/CHANGELOG.md +28 -0
  7. package/cabloy-docs/backend/dto-guide.md +39 -4
  8. package/cabloy-docs/backend/dto-infer-generation.md +29 -0
  9. package/cabloy-docs/backend/openapi-guide.md +16 -0
  10. package/cabloy-docs/backend/validation-guide.md +45 -0
  11. package/cabloy-docs/frontend/environment-config-guide.md +8 -15
  12. package/cabloy-docs/frontend/navigation-guards-guide.md +3 -3
  13. package/cabloy-docs/frontend/page-route-guide.md +4 -4
  14. package/cabloy-docs/frontend/ssr-env.md +22 -29
  15. package/cabloy-docs/frontend/theme-guide.md +4 -4
  16. package/cabloy-docs/frontend/zova-router-under-the-hood.md +1 -1
  17. package/cabloy-docs/fullstack/deploy-cloudflare-docker.md +6 -6
  18. package/cabloy-docs/fullstack/parallel-worktree-environment.md +2 -2
  19. package/e2e/specs/a-commerce/commerce.spec.ts +479 -17
  20. package/e2e/specs/cabloy-basic/basic.spec.ts +30 -5
  21. package/package.json +7 -7
  22. package/vona/.claude/skills/detect-ssr-leak/SKILL.md +1 -1
  23. package/vona/packages-vona/vona/package.json +1 -1
  24. package/vona/pnpm-lock.yaml +120 -10
  25. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/skuCreate.tsx +3 -1
  26. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/skuSelectResItem.tsx +13 -1
  27. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/skuUpdate.tsx +3 -1
  28. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/skuView.tsx +13 -1
  29. package/vona/src/suite/a-commerce/modules/commerce-catalog/src/entity/sku.tsx +6 -0
  30. package/vona/src/suite/a-commerce/modules/commerce-catalog/test/skuPresentation.test.ts +227 -0
  31. package/vona/src/suite/a-commerce/modules/commerce-member/src/.metadata/index.ts +46 -36
  32. package/vona/src/suite/a-commerce/modules/commerce-member/src/dto/addressSelectResItem.tsx +5 -13
  33. package/vona/src/suite/a-commerce/modules/commerce-member/src/service/address.ts +16 -1
  34. package/vona/src/suite/a-commerce/modules/commerce-member/test/addressPresentation.test.ts +210 -0
  35. package/vona/src/suite/a-commerce/modules/commerce-payment/src/service/commercePayScene.ts +13 -2
  36. package/vona/src/suite/a-commerce/modules/commerce-payment/test/paymentAttempt.test.ts +12 -0
  37. package/vona/src/suite/a-commerce/modules/commerce-promotion/src/service/coupon.ts +7 -1
  38. package/vona/src/suite/a-commerce/modules/commerce-trade/src/.metadata/index.ts +45 -2
  39. package/vona/src/suite/a-commerce/modules/commerce-trade/src/config/locale/en-us.ts +23 -0
  40. package/vona/src/suite/a-commerce/modules/commerce-trade/src/config/locale/zh-cn.ts +23 -0
  41. package/vona/src/suite/a-commerce/modules/commerce-trade/src/controller/order.ts +31 -0
  42. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderAdminLineBase.tsx +56 -0
  43. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderAdminLineResItem.tsx +17 -0
  44. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderAdminLineView.tsx +13 -0
  45. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderSelectResItem.tsx +26 -2
  46. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/orderView.tsx +197 -3
  47. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/refundRecoveryAction.tsx +19 -0
  48. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/refundRecoveryView.tsx +70 -0
  49. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/stockAuditSelectResItem.tsx +7 -1
  50. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/stockAuditView.tsx +21 -1
  51. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/stockBalanceSelectResItem.tsx +7 -1
  52. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/stockBalanceView.tsx +3 -1
  53. package/vona/src/suite/a-commerce/modules/commerce-trade/src/entity/order.tsx +12 -0
  54. package/vona/src/suite/a-commerce/modules/commerce-trade/src/model/order.ts +15 -2
  55. package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/order.ts +215 -3
  56. package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/stockAudit.ts +25 -2
  57. package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/stockBalance.ts +14 -3
  58. package/vona/src/suite/a-commerce/modules/commerce-trade/test/checkoutReservation.test.ts +4 -4
  59. package/vona/src/suite/a-commerce/modules/commerce-trade/test/orderPresentation.test.ts +358 -0
  60. package/vona/src/suite/a-commerce/modules/commerce-trade/test/orderSnapshot.test.ts +8 -8
  61. package/vona/src/suite/a-commerce/modules/commerce-trade/test/paymentOutcome.test.ts +5 -1
  62. package/vona/src/suite/a-commerce/modules/commerce-trade/test/refundLifecycle.test.ts +50 -1
  63. package/vona/src/suite/a-commerce/modules/commerce-trade/test/stockPresentation.test.ts +324 -0
  64. package/vona/src/suite/a-home/modules/home-base/package.json +3 -2
  65. package/vona/src/suite/a-home/modules/home-base/src/.metadata/index.ts +93 -16
  66. package/vona/src/suite/a-home/modules/home-base/src/controller/siteCatalog.ts +21 -0
  67. package/vona/src/suite/a-home/modules/home-base/src/dto/siteCatalogSelectRes.ts +11 -0
  68. package/vona/src/suite/a-home/modules/home-base/src/dto/siteCatalogSelectResItem.ts +15 -0
  69. package/vona/src/suite/a-home/modules/home-base/src/service/siteCatalog.ts +29 -0
  70. package/vona/src/suite/cabloy-basic/modules/basic-siteadmin/src/bean/ssrSite.admin.ts +3 -0
  71. package/vona/src/suite/cabloy-basic/modules/basic-siteadmin/src/config/locale/en-us.ts +1 -0
  72. package/vona/src/suite/cabloy-basic/modules/basic-siteadmin/src/config/locale/zh-cn.ts +1 -0
  73. package/vona/src/suite/cabloy-basic/modules/basic-siteweb/src/bean/ssrSite.web.ts +3 -0
  74. package/vona/src/suite/cabloy-basic/modules/basic-siteweb/src/config/locale/en-us.ts +1 -0
  75. package/vona/src/suite/cabloy-basic/modules/basic-siteweb/src/config/locale/zh-cn.ts +1 -0
  76. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/cli/ssrSite/boilerplate/{{sceneName}}.{{beanName}}.ts_ +1 -0
  77. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/package.json +1 -1
  78. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/types/env.ts +1 -3
  79. package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/types/ssrSite.ts +2 -1
  80. package/vona/src/suite-vendor/a-cabloy/package.json +1 -1
  81. package/vona/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
  82. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/.metadata/index.ts +67 -0
  83. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/bean.payProvider.ts +1 -0
  84. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/bean.payScene.ts +5 -4
  85. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/meta.index.ts +8 -0
  86. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/meta.version.ts +22 -0
  87. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/schedule.providerOperationDispatch.ts +1 -1
  88. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/paymentSession.tsx +6 -0
  89. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/providerOperation.tsx +3 -0
  90. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/providerOperationRecoveryAudit.tsx +53 -0
  91. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/refundOperation.tsx +6 -0
  92. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/model/providerOperationRecoveryAudit.ts +12 -0
  93. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/paymentSession.ts +6 -2
  94. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/providerOperation.ts +236 -30
  95. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/refundOperation.ts +8 -5
  96. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/webhook.ts +34 -0
  97. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/types/payProvider.ts +4 -0
  98. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/types/payment.ts +6 -0
  99. package/vona/src/suite-vendor/a-pay/modules/pay-mock/package.json +1 -1
  100. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/bean/payProvider.mock.ts +15 -7
  101. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/service/payMock.ts +9 -5
  102. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/package.json +1 -1
  103. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/bean/payProvider.paypal.ts +289 -86
  104. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/lib/paypalGateway.ts +9 -1
  105. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/package.json +1 -1
  106. package/vona/src/suite-vendor/a-pay/package.json +5 -5
  107. package/vona/src/suite-vendor/a-vona/modules/a-openapi/package.json +1 -1
  108. package/vona/src/suite-vendor/a-vona/modules/a-openapi/src/types/rest.ts +1 -0
  109. package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/package.json +1 -1
  110. package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/src/lib/utils.ts +12 -0
  111. package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
  112. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoCreate.ts +5 -1
  113. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoUpdate.ts +5 -1
  114. package/vona/src/suite-vendor/a-vona/modules/a-validation/package.json +1 -1
  115. package/vona/src/suite-vendor/a-vona/modules/a-validation/src/bean/bean.validator.ts +60 -1
  116. package/vona/src/suite-vendor/a-vona/modules/a-web/package.json +1 -1
  117. package/vona/src/suite-vendor/a-vona/modules/a-web/src/lib/decorator/bean.ts +7 -4
  118. package/vona/src/suite-vendor/a-vona/modules/a-web/src/types/dto.ts +2 -1
  119. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  120. package/zova/env/.env +3 -5
  121. package/zova/env/.env.cabloyBasicAdmin +1 -4
  122. package/zova/env/.env.cabloyBasicWeb +1 -5
  123. package/zova/env/.env.cabloyCommerce +1 -5
  124. package/zova/env/.env.cabloyCommerceAdmin +1 -4
  125. package/zova/env/.env.ssr.admin +1 -4
  126. package/zova/env/.env.ssr.web +1 -5
  127. package/zova/package.original.json +1 -1
  128. package/zova/packages-utils/zova-jsx/package.json +2 -2
  129. package/zova/packages-zova/zova/package.json +3 -3
  130. package/zova/packages-zova/zova-core/package.json +2 -1
  131. package/zova/packages-zova/zova-core/src/core/component/locale.ts +10 -4
  132. package/zova/packages-zova/zova-core/src/core/sys/config.ts +14 -6
  133. package/zova/packages-zova/zova-core/src/types/utils/env.ts +4 -2
  134. package/zova/pnpm-lock.yaml +24 -21
  135. package/zova/src/front/config/config/config.ts +22 -7
  136. package/zova/src/suite/a-commerce/modules/commerce-catalog/src/routes.ts +2 -2
  137. package/zova/src/suite/a-commerce/modules/commerce-member/src/api/openapi/schemas.ts +16 -8
  138. package/zova/src/suite/a-commerce/modules/commerce-member/src/api/openapi/types.ts +150 -216
  139. package/zova/src/suite/a-commerce/modules/commerce-member/src/model/addressMine.ts +1 -2
  140. package/zova/src/suite/a-commerce/modules/commerce-member/src/page/address/controller.tsx +5 -8
  141. package/zova/src/suite/a-commerce/modules/commerce-member/src/routes.ts +1 -1
  142. package/zova/src/suite/a-commerce/modules/commerce-promotion/src/model/coupon.ts +1 -2
  143. package/zova/src/suite/a-commerce/modules/commerce-siteadmin/src/routes.ts +3 -1
  144. package/zova/src/suite/a-commerce/modules/commerce-trade/cli/openapi.config.ts +3 -0
  145. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/commerceTradeOrder.ts +72 -0
  146. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/schemas.ts +8 -0
  147. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/types.ts +226 -16
  148. package/zova/src/suite/a-commerce/modules/commerce-trade/src/apiSchema/commerceTradeOrder.ts +15 -0
  149. package/zova/src/suite/a-commerce/modules/commerce-trade/src/component/tableCellActionRefund/controller.tsx +155 -36
  150. package/zova/src/suite/a-commerce/modules/commerce-trade/src/config/locale/en-us.ts +7 -0
  151. package/zova/src/suite/a-commerce/modules/commerce-trade/src/config/locale/zh-cn.ts +7 -0
  152. package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/cart.ts +3 -4
  153. package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/order.ts +42 -0
  154. package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/orderMine.ts +2 -4
  155. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/cart/controller.tsx +5 -1
  156. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/checkout/controller.tsx +14 -10
  157. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/order/controller.tsx +5 -8
  158. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/orders/controller.tsx +5 -8
  159. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/payment/controller.tsx +151 -24
  160. package/zova/src/suite/a-commerce/modules/commerce-trade/src/routes.ts +5 -5
  161. package/zova/src/suite/a-demo/modules/demo-basic/src/routes.ts +26 -7
  162. package/zova/src/suite/a-demo/modules/demo-todo/src/routes.ts +2 -2
  163. package/zova/src/suite/a-devui/modules/devui-adapter/src/bean/meta.themeHandler.ts +1 -1
  164. package/zova/src/suite/a-home/modules/home-base/package.json +1 -0
  165. package/zova/src/suite/a-home/modules/home-base/src/routes.ts +5 -0
  166. package/zova/src/suite/a-home/modules/home-base/src/service/routerGuards.ts +1 -1
  167. package/zova/src/suite/a-home/modules/home-base/src/service/ssrLayout.ts +18 -14
  168. package/zova/src/suite/a-home/modules/home-indexadmin/src/routes.ts +1 -1
  169. package/zova/src/suite/a-home/modules/home-indexweb/src/routes.ts +1 -0
  170. package/zova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/controller.tsx +12 -4
  171. package/zova/src/suite/a-home/modules/home-layoutadmin/src/config/config.ts +8 -0
  172. package/zova/src/suite/a-home/modules/home-layoutadmin/src/model/layout.ts +5 -8
  173. package/zova/src/suite/a-home/modules/home-layoutempty/src/.metadata/index.ts +10 -3
  174. package/zova/src/suite/a-home/modules/home-layoutempty/src/component/layoutEmpty/controller.tsx +9 -3
  175. package/zova/src/suite/a-home/modules/home-layoutempty/src/config/config.ts +12 -0
  176. package/zova/src/suite/a-home/modules/home-layoutweb/src/component/layoutWeb/controller.tsx +13 -8
  177. package/zova/src/suite/a-home/modules/home-layoutweb/src/component/layoutWeb/render.header.tsx +1 -1
  178. package/zova/src/suite/a-home/modules/home-layoutweb/src/config/config.ts +4 -0
  179. package/zova/src/suite/a-home/modules/home-layoutweb/src/model/layout.ts +5 -8
  180. package/zova/src/suite/a-home/modules/home-login/src/routes.ts +1 -0
  181. package/zova/src/suite/a-home/modules/home-passport/src/model/passport.ts +2 -3
  182. package/zova/src/suite/cabloy-basic/modules/basic-metrics/src/routes.ts +3 -1
  183. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
  184. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +1 -1
  185. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/routes.ts +3 -0
  186. package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
  187. package/zova/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
  188. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/model/paymentSession.ts +2 -2
  189. package/zova/src/suite-vendor/a-pay/package.json +2 -2
  190. package/zova/src/suite-vendor/a-zova/modules/a-interceptor/package.json +1 -1
  191. package/zova/src/suite-vendor/a-zova/modules/a-interceptor/src/bean/interceptor.jwt.ts +1 -1
  192. package/zova/src/suite-vendor/a-zova/modules/a-openapi/package.json +1 -1
  193. package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/model/sdk.ts +1 -1
  194. package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/rest.ts +1 -0
  195. package/zova/src/suite-vendor/a-zova/modules/a-ssr/package.json +1 -1
  196. package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/config/config.ts +1 -17
  197. package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/lib/bodyReadyObserver.ts +27 -0
  198. package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/lib/index.ts +2 -0
  199. package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/lib/ssr.ts +33 -0
  200. package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/lib/ssrMetaStore.ts +3 -35
  201. package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/lib/ssrProfile.ts +38 -0
  202. package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/monkey.ts +10 -0
  203. package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/types/config.ts +3 -10
  204. package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/types/ssr.ts +17 -8
  205. package/zova/src/suite-vendor/a-zova/modules/a-ssrserver/package.json +1 -1
  206. package/zova/src/suite-vendor/a-zova/modules/a-ssrserver/src/service/ssrHandler.ts +33 -18
  207. package/zova/src/suite-vendor/a-zova/modules/a-style/package.json +1 -1
  208. package/zova/src/suite-vendor/a-zova/modules/a-style/src/bean/bean.theme.ts +2 -2
  209. package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
  210. package/zova/src/suite-vendor/a-zova/package.json +7 -7
@@ -4,7 +4,10 @@ import type { IJsxRenderContextTableCell } from 'zova-module-a-table';
4
4
  import { BeanControllerBase, Use } from 'zova';
5
5
  import { Controller } from 'zova-module-a-bean';
6
6
 
7
- import type { ApiSchemaCommerceTradeDtoOrderSelectResItem } from '../../api/openapi/schemas.js';
7
+ import type {
8
+ ApiSchemaCommerceTradeDtoOrderSelectResItem,
9
+ ApiSchemaCommerceTradeDtoRefundRecoveryView,
10
+ } from '../../api/openapi/schemas.js';
8
11
  import type { ITableCellOptionsActionRefund } from '../../bean/tableCell.actionRefund.js';
9
12
  import type { ModelOrder } from '../../model/order.js';
10
13
 
@@ -22,8 +25,14 @@ export class ControllerTableCellActionRefund extends BeanControllerBase {
22
25
 
23
26
  reason = '';
24
27
  confirmed = false;
28
+ recoveryOpen = false;
29
+ recoveryLoaded = false;
30
+ recoveryReason = '';
31
+ retryRiskAcknowledged = false;
25
32
  approveIdempotencyKey?: string;
26
33
  rejectIdempotencyKey?: string;
34
+ reconcileIdempotencyKey?: string;
35
+ retryIdempotencyKey?: string;
27
36
 
28
37
  private async _approve() {
29
38
  const reason = this.reason.trim();
@@ -51,47 +60,157 @@ export class ControllerTableCellActionRefund extends BeanControllerBase {
51
60
  await this.$$modelOrder.executeRefund(orderId).mutateAsync(undefined);
52
61
  }
53
62
 
63
+ private _recovery() {
64
+ const orderId = this.$$renderContext.cellContext.row.id as TableIdentity;
65
+ return this.$$modelOrder.refundRecovery(orderId);
66
+ }
67
+
68
+ private async _toggleRecovery() {
69
+ this.recoveryOpen = !this.recoveryOpen;
70
+ if (this.recoveryOpen && !this.recoveryLoaded) {
71
+ await this._recovery().refetch();
72
+ this.recoveryLoaded = true;
73
+ }
74
+ }
75
+
76
+ private async _reconcile() {
77
+ const reason = this.recoveryReason.trim();
78
+ if (!reason) return;
79
+ const orderId = this.$$renderContext.cellContext.row.id as TableIdentity;
80
+ const actionIdempotencyKey = this.reconcileIdempotencyKey ?? crypto.randomUUID();
81
+ this.reconcileIdempotencyKey = actionIdempotencyKey;
82
+ await this.$$modelOrder
83
+ .reconcileRefund(orderId)
84
+ .mutateAsync({ reason, actionIdempotencyKey, acknowledgeRetryRisk: false });
85
+ this.reconcileIdempotencyKey = undefined;
86
+ await this._recovery().refetch();
87
+ }
88
+
89
+ private async _retry() {
90
+ const reason = this.recoveryReason.trim();
91
+ if (!reason || !this.retryRiskAcknowledged) return;
92
+ const orderId = this.$$renderContext.cellContext.row.id as TableIdentity;
93
+ const actionIdempotencyKey = this.retryIdempotencyKey ?? crypto.randomUUID();
94
+ this.retryIdempotencyKey = actionIdempotencyKey;
95
+ await this.$$modelOrder
96
+ .retryRefund(orderId)
97
+ .mutateAsync({ reason, actionIdempotencyKey, acknowledgeRetryRisk: true });
98
+ this.retryIdempotencyKey = undefined;
99
+ await this._recovery().refetch();
100
+ }
101
+
54
102
  protected render() {
55
103
  const props = this.$props as ControllerTableCellActionRefundProps;
56
104
  const order = this.$$renderContext.cellContext.row
57
105
  .original as ApiSchemaCommerceTradeDtoOrderSelectResItem;
58
106
  const requested = order.state === 'refund_requested';
107
+ const recovery = this.recoveryOpen ? this._recovery() : undefined;
108
+ const recoveryData = recovery?.data as ApiSchemaCommerceTradeDtoRefundRecoveryView | undefined;
109
+ const showExecute = !recoveryData;
59
110
  return (
60
- <div class="join">
61
- {requested && (
62
- <input
63
- class="input input-bordered input-sm join-item w-40"
64
- name="reason"
65
- placeholder={this.scope.locale.RefundDecisionReason()}
66
- v-model={this.reason}
67
- />
68
- )}
69
- <label class="label cursor-pointer join-item gap-1 px-2">
70
- <input
71
- class="checkbox checkbox-sm"
72
- name="confirmed"
73
- type="checkbox"
74
- v-model={this.confirmed}
75
- />
76
- <span class="label-text text-xs">{this.scope.locale.Confirm()}</span>
77
- </label>
78
- {requested ? (
79
- <>
80
- <button class={props.class} type="button" onClick={() => this._approve()}>
81
- {this.scope.locale.ApproveRefund()}
82
- </button>
83
- <button
84
- class="btn btn-outline btn-error join-item"
85
- type="button"
86
- onClick={() => this._reject()}
87
- >
88
- {this.scope.locale.RejectRefund()}
89
- </button>
90
- </>
91
- ) : (
92
- <button class={props.class} type="button" onClick={() => this._execute()}>
93
- {this.scope.locale.ExecuteRefund()}
94
- </button>
111
+ <div class="flex flex-col items-start gap-2">
112
+ <div class="join">
113
+ {requested && (
114
+ <input
115
+ class="input input-bordered input-sm join-item w-40"
116
+ name="reason"
117
+ placeholder={this.scope.locale.RefundDecisionReason()}
118
+ v-model={this.reason}
119
+ />
120
+ )}
121
+ <label class="label cursor-pointer join-item gap-1 px-2">
122
+ <input
123
+ class="checkbox checkbox-sm"
124
+ name="confirmed"
125
+ type="checkbox"
126
+ v-model={this.confirmed}
127
+ />
128
+ <span class="label-text text-xs">{this.scope.locale.Confirm()}</span>
129
+ </label>
130
+ {requested ? (
131
+ <>
132
+ <button class={props.class} type="button" onClick={() => this._approve()}>
133
+ {this.scope.locale.ApproveRefund()}
134
+ </button>
135
+ <button
136
+ class="btn btn-outline btn-error join-item"
137
+ type="button"
138
+ onClick={() => this._reject()}
139
+ >
140
+ {this.scope.locale.RejectRefund()}
141
+ </button>
142
+ </>
143
+ ) : (
144
+ <>
145
+ {showExecute && (
146
+ <button class={props.class} type="button" onClick={() => this._execute()}>
147
+ {this.scope.locale.ExecuteRefund()}
148
+ </button>
149
+ )}
150
+ <button
151
+ class="btn btn-outline join-item"
152
+ type="button"
153
+ onClick={() => this._toggleRecovery()}
154
+ >
155
+ {this.scope.locale.InspectRefundRecovery()}
156
+ </button>
157
+ </>
158
+ )}
159
+ </div>
160
+ {recoveryData && (
161
+ <section class="w-full rounded border border-base-300 p-3 text-sm">
162
+ <p>
163
+ {this.scope.locale.RefundRecoveryState({
164
+ state: recoveryData.providerOperationState,
165
+ attemptCount: recoveryData.attemptCount,
166
+ })}
167
+ </p>
168
+ {recoveryData.providerRefundId && (
169
+ <p>
170
+ {this.scope.locale.RefundRecoveryProviderRefundId({
171
+ providerRefundId: recoveryData.providerRefundId,
172
+ })}
173
+ </p>
174
+ )}
175
+ {recoveryData.errorSummary && <p>{recoveryData.errorSummary}</p>}
176
+ <p class="text-base-content/70">{recoveryData.recoveryMessage}</p>
177
+ {recoveryData.recoveryDisposition !== 'none' && (
178
+ <input
179
+ class="input input-bordered input-sm mt-2 w-full"
180
+ name="recoveryReason"
181
+ placeholder={this.scope.locale.RefundRecoveryReason()}
182
+ v-model={this.recoveryReason}
183
+ />
184
+ )}
185
+ {(recoveryData.recoveryDisposition === 'query_only' ||
186
+ recoveryData.recoveryDisposition === 'reconcile_only') && (
187
+ <button
188
+ class="btn btn-outline btn-sm mt-2"
189
+ type="button"
190
+ onClick={() => this._reconcile()}
191
+ >
192
+ {this.scope.locale.ReconcileProviderRefund()}
193
+ </button>
194
+ )}
195
+ {recoveryData.recoveryDisposition === 'retry_same_key' && (
196
+ <>
197
+ <label class="label cursor-pointer mt-2 justify-start gap-2">
198
+ <input
199
+ class="checkbox checkbox-sm"
200
+ name="retryRiskAcknowledged"
201
+ type="checkbox"
202
+ v-model={this.retryRiskAcknowledged}
203
+ />
204
+ <span class="label-text text-xs">
205
+ {this.scope.locale.AcknowledgeRefundRetryRisk()}
206
+ </span>
207
+ </label>
208
+ <button class="btn btn-warning btn-sm" type="button" onClick={() => this._retry()}>
209
+ {this.scope.locale.RetryRefund()}
210
+ </button>
211
+ </>
212
+ )}
213
+ </section>
95
214
  )}
96
215
  </div>
97
216
  );
@@ -14,4 +14,11 @@ export default {
14
14
  ApproveRefund: 'Approve refund',
15
15
  RejectRefund: 'Reject refund',
16
16
  ExecuteRefund: 'Execute refund',
17
+ InspectRefundRecovery: 'Inspect refund recovery',
18
+ ReconcileProviderRefund: 'Reconcile provider refund',
19
+ RetryRefund: 'Retry refund',
20
+ RefundRecoveryReason: 'Recovery reason',
21
+ AcknowledgeRefundRetryRisk: 'I understand this retry reuses the original provider request.',
22
+ RefundRecoveryState: 'Provider operation: {state}; attempts: {attemptCount}',
23
+ RefundRecoveryProviderRefundId: 'Provider refund ID: {providerRefundId}',
17
24
  };
@@ -14,4 +14,11 @@ export default {
14
14
  ApproveRefund: '批准退款',
15
15
  RejectRefund: '拒绝退款',
16
16
  ExecuteRefund: '执行退款',
17
+ InspectRefundRecovery: '查看退款恢复',
18
+ ReconcileProviderRefund: '对账支付服务商退款',
19
+ RetryRefund: '重试退款',
20
+ RefundRecoveryReason: '恢复原因',
21
+ AcknowledgeRefundRetryRisk: '我了解本次重试会复用原支付服务商请求。',
22
+ RefundRecoveryState: '服务商操作:{state};尝试次数:{attemptCount}',
23
+ RefundRecoveryProviderRefundId: '服务商退款 ID:{providerRefundId}',
17
24
  };
@@ -13,13 +13,13 @@ export interface IModelOptionsCart extends IDecoratorModelOptions {}
13
13
  @Model<IModelOptionsCart>()
14
14
  export class ModelCart extends BeanModelBase {
15
15
  current() {
16
- if (!process.env.CLIENT || !this.$passport.isAuthenticated) return;
16
+ if (this.$ssr.cookieDisabledOnServer) return;
17
+ if (!this.$passport.isAuthenticated) return;
17
18
  return this.$useStateData({
18
19
  queryKey: ['current'],
19
20
  queryFn: async () => {
20
21
  return await this.scope.api.commerceTradeCart.current();
21
22
  },
22
- meta: { disableSuspenseOnInit: true },
23
23
  });
24
24
  }
25
25
 
@@ -66,13 +66,12 @@ export class ModelCart extends BeanModelBase {
66
66
  }
67
67
 
68
68
  paymentMethods() {
69
- if (!process.env.CLIENT || !this.$passport.isAuthenticated) return;
69
+ if (!this.$passport.isAuthenticated) return;
70
70
  return this.$useStateData({
71
71
  queryKey: ['paymentMethods'],
72
72
  queryFn: async () => {
73
73
  return await this.scope.api.commerceTradeCheckout.paymentMethods();
74
74
  },
75
- meta: { disableSuspenseOnInit: true },
76
75
  });
77
76
  }
78
77
 
@@ -7,7 +7,10 @@ import { BeanModelBase, Model } from 'zova-module-a-model';
7
7
 
8
8
  import type {
9
9
  ApiApiCommerceTradeOrderapproveRefundRequestBody,
10
+ ApiApiCommerceTradeOrderreconcileRefundRequestBody,
11
+ ApiApiCommerceTradeOrderrefundRecoveryResponseBody,
10
12
  ApiApiCommerceTradeOrderrejectRefundRequestBody,
13
+ ApiApiCommerceTradeOrderretryRefundRequestBody,
11
14
  ApiApiCommerceTradeOrdershipRequestBody,
12
15
  } from '../api/commerceTradeOrder.js';
13
16
 
@@ -75,4 +78,43 @@ export class ModelOrder extends BeanModelBase {
75
78
  },
76
79
  });
77
80
  }
81
+
82
+ refundRecovery(id: TableIdentity) {
83
+ return this.$$modelResource.queryItem<ApiApiCommerceTradeOrderrefundRecoveryResponseBody>({
84
+ id,
85
+ action: 'refundRecovery',
86
+ queryFn: async () => {
87
+ return await this.scope.api.commerceTradeOrder.refundRecovery({ params: { id } });
88
+ },
89
+ meta: {
90
+ disableSuspenseOnInit: true,
91
+ },
92
+ });
93
+ }
94
+
95
+ reconcileRefund(id: TableIdentity) {
96
+ return this.$$modelResource.mutationItem<
97
+ unknown,
98
+ ApiApiCommerceTradeOrderreconcileRefundRequestBody
99
+ >({
100
+ id,
101
+ action: 'reconcileRefund',
102
+ mutationFn: async payload => {
103
+ return await this.scope.api.commerceTradeOrder.reconcileRefund(payload, { params: { id } });
104
+ },
105
+ });
106
+ }
107
+
108
+ retryRefund(id: TableIdentity) {
109
+ return this.$$modelResource.mutationItem<
110
+ unknown,
111
+ ApiApiCommerceTradeOrderretryRefundRequestBody
112
+ >({
113
+ id,
114
+ action: 'retryRefund',
115
+ mutationFn: async payload => {
116
+ return await this.scope.api.commerceTradeOrder.retryRefund(payload, { params: { id } });
117
+ },
118
+ });
119
+ }
78
120
  }
@@ -10,24 +10,22 @@ export interface IModelOptionsOrderMine extends IDecoratorModelOptions {}
10
10
  @Model<IModelOptionsOrderMine>()
11
11
  export class ModelOrderMine extends BeanModelBase {
12
12
  mine(query: { pageNo: number; pageSize: number }) {
13
- if (!process.env.CLIENT || !this.$passport.isAuthenticated) return;
13
+ if (!this.$passport.isAuthenticated) return;
14
14
  return this.$useStateData({
15
15
  queryKey: ['mine', query],
16
16
  queryFn: async () => {
17
17
  return await this.scope.api.commerceTradeOrder.mine({ query });
18
18
  },
19
- meta: { disableSuspenseOnInit: true },
20
19
  });
21
20
  }
22
21
 
23
22
  viewMine(id: TableIdentity) {
24
- if (!process.env.CLIENT || !this.$passport.isAuthenticated) return;
23
+ if (!this.$passport.isAuthenticated) return;
25
24
  return this.$useStateData({
26
25
  queryKey: ['viewMine', id],
27
26
  queryFn: async () => {
28
27
  return await this.scope.api.commerceTradeOrder.viewMine({ params: { id } });
29
28
  },
30
- meta: { disableSuspenseOnInit: true },
31
29
  });
32
30
  }
33
31
 
@@ -2,6 +2,7 @@ import { RouterLink } from '@cabloy/vue-router';
2
2
  import { z } from 'zod';
3
3
  import { BeanControllerPageBase, Use } from 'zova';
4
4
  import { Controller } from 'zova-module-a-bean';
5
+ import { $QueryEnsureLoaded } from 'zova-module-a-model';
5
6
  import { ZPage } from 'zova-module-home-base';
6
7
 
7
8
  import { ModelCart } from '../../model/cart.js';
@@ -16,8 +17,11 @@ export class ControllerPageCart extends BeanControllerPageBase {
16
17
  @Use()
17
18
  $$modelCart: ModelCart;
18
19
 
20
+ protected async __init__() {
21
+ await $QueryEnsureLoaded(() => this.queryCurrent);
22
+ }
23
+
19
24
  get queryCurrent() {
20
- if (process.env.SERVER) return;
21
25
  return this.$$modelCart.current();
22
26
  }
23
27
 
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { BeanControllerPageBase, Use } from 'zova';
3
3
  import { Controller } from 'zova-module-a-bean';
4
+ import { $QueriesEnsureLoaded } from 'zova-module-a-model';
4
5
  import { ModelAddressMine } from 'zova-module-commerce-member';
5
6
  import { ModelCoupon } from 'zova-module-commerce-promotion';
6
7
  import { ZPage } from 'zova-module-home-base';
@@ -28,13 +29,19 @@ export class ControllerPageCheckout extends BeanControllerPageBase {
28
29
  providerCandidateKey?: string;
29
30
  submitting = false;
30
31
 
32
+ protected async __init__() {
33
+ await $QueriesEnsureLoaded(
34
+ () => this.queryAddresses,
35
+ () => this.queryCoupons,
36
+ () => this.queryPaymentMethods,
37
+ );
38
+ }
39
+
31
40
  get queryAddresses() {
32
- if (!this.$ssr.isRuntimeSsrHydrated) return;
33
41
  return this.$$modelAddressMine.mine({ pageNo: 1, pageSize: 100 });
34
42
  }
35
43
 
36
44
  get queryCoupons() {
37
- if (!this.$ssr.isRuntimeSsrHydrated) return;
38
45
  return this.$$modelCoupon.mine();
39
46
  }
40
47
 
@@ -66,17 +73,14 @@ export class ControllerPageCheckout extends BeanControllerPageBase {
66
73
  }
67
74
 
68
75
  protected render() {
69
- if (!this.$ssr.isRuntimeSsrHydrated) {
70
- return (
71
- <ZPage>
72
- <section class="mx-auto max-w-3xl p-6" aria-busy="true" />
73
- </ZPage>
74
- );
75
- }
76
76
  const addresses = this.queryAddresses?.data?.list ?? [];
77
77
  const coupons = this.queryCoupons?.data ?? [];
78
78
  const paymentMethods = this.queryPaymentMethods?.data;
79
- if (!this.providerCandidateKey && paymentMethods?.defaultKey) {
79
+ if (
80
+ this.$ssr.isRuntimeSsrHydrated &&
81
+ !this.providerCandidateKey &&
82
+ paymentMethods?.defaultKey
83
+ ) {
80
84
  this.providerCandidateKey = paymentMethods.defaultKey;
81
85
  }
82
86
  return (
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { BeanControllerPageBase, Use } from 'zova';
3
3
  import { Controller } from 'zova-module-a-bean';
4
+ import { $QueryEnsureLoaded } from 'zova-module-a-model';
4
5
  import { ZPage } from 'zova-module-home-base';
5
6
 
6
7
  import { ModelOrderMine } from '../../model/orderMine.js';
@@ -16,12 +17,15 @@ export class ControllerPageOrder extends BeanControllerPageBase {
16
17
  @Use()
17
18
  $$modelOrderMine: ModelOrderMine;
18
19
 
20
+ protected async __init__() {
21
+ await $QueryEnsureLoaded(() => this.queryOrder);
22
+ }
23
+
19
24
  get orderId() {
20
25
  return this.$params.id;
21
26
  }
22
27
 
23
28
  get queryOrder() {
24
- if (!this.$ssr.isRuntimeSsrHydrated) return;
25
29
  return this.$$modelOrderMine.viewMine(this.orderId);
26
30
  }
27
31
 
@@ -43,13 +47,6 @@ export class ControllerPageOrder extends BeanControllerPageBase {
43
47
  }
44
48
 
45
49
  protected render() {
46
- if (!this.$ssr.isRuntimeSsrHydrated) {
47
- return (
48
- <ZPage>
49
- <section class="mx-auto max-w-4xl p-6" aria-busy="true" />
50
- </ZPage>
51
- );
52
- }
53
50
  const query = this.queryOrder;
54
51
  const order = query?.data;
55
52
  return (
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { BeanControllerPageBase, Use } from 'zova';
3
3
  import { Controller } from 'zova-module-a-bean';
4
+ import { $QueryEnsureLoaded } from 'zova-module-a-model';
4
5
  import { ZPage } from 'zova-module-home-base';
5
6
 
6
7
  import { ModelOrderMine } from '../../model/orderMine.js';
@@ -18,8 +19,11 @@ export class ControllerPageOrders extends BeanControllerPageBase {
18
19
  pageNo = 1;
19
20
  readonly pageSize = 10;
20
21
 
22
+ protected async __init__() {
23
+ await $QueryEnsureLoaded(() => this.queryOrders);
24
+ }
25
+
21
26
  get queryOrders() {
22
- if (!this.$ssr.isRuntimeSsrHydrated) return;
23
27
  return this.$$modelOrderMine.mine({ pageNo: this.pageNo, pageSize: this.pageSize });
24
28
  }
25
29
 
@@ -28,13 +32,6 @@ export class ControllerPageOrders extends BeanControllerPageBase {
28
32
  }
29
33
 
30
34
  protected render() {
31
- if (!this.$ssr.isRuntimeSsrHydrated) {
32
- return (
33
- <ZPage>
34
- <section class="mx-auto max-w-4xl p-6" aria-busy="true" />
35
- </ZPage>
36
- );
37
- }
38
35
  const query = this.queryOrders;
39
36
  const orders = query?.data?.list ?? [];
40
37
  const pageCount = query?.data?.pageCount ?? 0;