@voplus/morpho-csp 11.2.8 → 11.3.0-dev.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 (212) hide show
  1. package/es/components/LegalContactSelect/index.d.ts +7 -0
  2. package/es/components/LegalContactSelect/index.js +127 -12
  3. package/es/components/LegalContactSelect/index.js.map +1 -1
  4. package/es/data/clda-form/conversion.d.ts +9 -6
  5. package/es/data/clda-form/conversion.js +11 -18
  6. package/es/data/clda-form/conversion.js.map +1 -1
  7. package/es/data/clda-form/identityAdapter.d.ts +60 -0
  8. package/es/data/clda-form/identityAdapter.js +196 -0
  9. package/es/data/clda-form/identityAdapter.js.map +1 -0
  10. package/es/data/clda-form/interfaces.d.ts +20 -1
  11. package/es/data/clda-form/saveFormDraft.d.ts +4 -0
  12. package/es/data/clda-form/saveFormDraft.js +21 -0
  13. package/es/data/clda-form/saveFormDraft.js.map +1 -0
  14. package/es/data/clda-form/types.d.ts +31 -2
  15. package/es/modules/corporate/background-investigation/risk-profile/components/IdentityProperties/IdentityShapeFields.d.ts +35 -0
  16. package/es/modules/corporate/background-investigation/risk-profile/components/IdentityProperties/IdentityShapeFields.js +53 -0
  17. package/es/modules/corporate/background-investigation/risk-profile/components/IdentityProperties/IdentityShapeFields.js.map +1 -0
  18. package/es/modules/corporate/background-investigation/risk-profile/components/IdentityProperties/index.js +41 -15
  19. package/es/modules/corporate/background-investigation/risk-profile/components/IdentityProperties/index.js.map +1 -1
  20. package/es/modules/corporate/background-investigation/risk-profile/components/IdentityProperties/state.d.ts +59 -1
  21. package/es/modules/corporate/background-investigation/risk-profile/components/IdentityProperties/state.js +244 -15
  22. package/es/modules/corporate/background-investigation/risk-profile/components/IdentityProperties/state.js.map +1 -1
  23. package/es/modules/corporate/company/components/CorporateOfficerSelect/index.d.ts +5 -0
  24. package/es/modules/corporate/company/components/CorporateOfficerSelect/index.js +13 -7
  25. package/es/modules/corporate/company/components/CorporateOfficerSelect/index.js.map +1 -1
  26. package/es/modules/corporate/company/identity/components/NewLegalContactDialog/Step3.d.ts +2 -2
  27. package/es/modules/corporate/company/identity/components/NewLegalContactDialog/Step3.js +20 -24
  28. package/es/modules/corporate/company/identity/components/NewLegalContactDialog/Step3.js.map +1 -1
  29. package/es/modules/corporate/company/identity/components/NewLegalContactDialog/Step4.d.ts +2 -2
  30. package/es/modules/corporate/company/identity/components/NewLegalContactDialog/Step4.js +38 -11
  31. package/es/modules/corporate/company/identity/components/NewLegalContactDialog/Step4.js.map +1 -1
  32. package/es/modules/corporate/company/identity/components/NewLegalContactDialog/index.d.ts +3 -3
  33. package/es/modules/corporate/company/identity/components/NewLegalContactDialog/index.js.map +1 -1
  34. package/es/modules/corporate/company/identity/components/NewLegalContactDialog/state.d.ts +25 -4
  35. package/es/modules/corporate/company/identity/components/NewLegalContactDialog/state.js +88 -5
  36. package/es/modules/corporate/company/identity/components/NewLegalContactDialog/state.js.map +1 -1
  37. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/DialogShell.d.ts +13 -0
  38. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/DialogShell.js +20 -0
  39. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/DialogShell.js.map +1 -0
  40. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/Page1.js +28 -28
  41. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/Page1.js.map +1 -1
  42. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/Page2.js +3 -4
  43. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/Page2.js.map +1 -1
  44. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/Page3.js +16 -9
  45. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/Page3.js.map +1 -1
  46. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/Page4.d.ts +2 -2
  47. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/Page4.js +128 -61
  48. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/Page4.js.map +1 -1
  49. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/Page5.d.ts +2 -2
  50. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/Page5.js +119 -24
  51. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/Page5.js.map +1 -1
  52. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/index.d.ts +0 -1
  53. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/index.js +16 -8
  54. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/index.js.map +1 -1
  55. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/state.d.ts +274 -23
  56. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/state.js +560 -210
  57. package/es/modules/corporate/company/identity/components/SelectLegalContactDialog/state.js.map +1 -1
  58. package/es/modules/corporate/components/CorporateSelect/index.d.ts +12 -1
  59. package/es/modules/corporate/components/CorporateSelect/index.js +19 -5
  60. package/es/modules/corporate/components/CorporateSelect/index.js.map +1 -1
  61. package/es/modules/corporate/filing-record/filing-type/components/FilingTypeReminderScheduleList/index.js +9 -6
  62. package/es/modules/corporate/filing-record/filing-type/components/FilingTypeReminderScheduleList/index.js.map +1 -1
  63. package/es/modules/corporate/legal-generation/components/AppointOfficer/index.d.ts +6 -1
  64. package/es/modules/corporate/legal-generation/components/AppointOfficer/index.js +3 -2
  65. package/es/modules/corporate/legal-generation/components/AppointOfficer/index.js.map +1 -1
  66. package/es/modules/corporate/legal-generation/components/CorporateRepresentative/index.d.ts +6 -1
  67. package/es/modules/corporate/legal-generation/components/CorporateRepresentative/index.js +3 -2
  68. package/es/modules/corporate/legal-generation/components/CorporateRepresentative/index.js.map +1 -1
  69. package/es/modules/corporate/legal-generation/components/IssueShares/Fields.js +3 -3
  70. package/es/modules/corporate/legal-generation/components/IssueShares/Fields.js.map +1 -1
  71. package/es/modules/corporate/legal-generation/components/IssueShares/interface.d.ts +1 -1
  72. package/es/modules/corporate/legal-generation/components/PowerOfAttroney/Fields.d.ts +16 -1
  73. package/es/modules/corporate/legal-generation/components/PowerOfAttroney/Fields.js +27 -9
  74. package/es/modules/corporate/legal-generation/components/PowerOfAttroney/Fields.js.map +1 -1
  75. package/es/modules/corporate/legal-generation/components/PowerOfAttroney/interface.d.ts +5 -2
  76. package/es/modules/corporate/legal-generation/components/ResignationLetter/Fields.js +1 -1
  77. package/es/modules/corporate/legal-generation/components/ResignationLetter/Fields.js.map +1 -1
  78. package/es/modules/corporate/legal-generation/components/ShareTransfer/Fields.d.ts +13 -1
  79. package/es/modules/corporate/legal-generation/components/ShareTransfer/Fields.js +24 -6
  80. package/es/modules/corporate/legal-generation/components/ShareTransfer/Fields.js.map +1 -1
  81. package/es/modules/corporate/legal-generation/components/ShareTransfer/interface.d.ts +5 -2
  82. package/es/modules/corporate/legal-generation/components/SubmitButton/index.d.ts +11 -3
  83. package/es/modules/corporate/legal-generation/components/SubmitButton/index.js +9 -3
  84. package/es/modules/corporate/legal-generation/components/SubmitButton/index.js.map +1 -1
  85. package/es/modules/corporate/legal-generation/fields/AppointOfficerFields.d.ts +31 -2
  86. package/es/modules/corporate/legal-generation/fields/AppointOfficerFields.js +52 -37
  87. package/es/modules/corporate/legal-generation/fields/AppointOfficerFields.js.map +1 -1
  88. package/es/modules/corporate/legal-generation/fields/CorporateRepresentativeFields.d.ts +27 -2
  89. package/es/modules/corporate/legal-generation/fields/CorporateRepresentativeFields.js +78 -21
  90. package/es/modules/corporate/legal-generation/fields/CorporateRepresentativeFields.js.map +1 -1
  91. package/es/modules/corporate/legal-generation/fields/ParentCompanyDetailsFields.d.ts +17 -0
  92. package/es/modules/corporate/legal-generation/fields/ParentCompanyDetailsFields.js +36 -0
  93. package/es/modules/corporate/legal-generation/fields/ParentCompanyDetailsFields.js.map +1 -0
  94. package/es/modules/corporate/legal-generation/fields/renderCapturedIdentityColumn.d.ts +11 -0
  95. package/es/modules/corporate/legal-generation/fields/renderCapturedIdentityColumn.js +25 -0
  96. package/es/modules/corporate/legal-generation/fields/renderCapturedIdentityColumn.js.map +1 -0
  97. package/es/modules/corporate/legal-generation/fields/state/index.d.ts +7 -0
  98. package/es/modules/corporate/legal-generation/fields/state/index.js +5 -0
  99. package/es/modules/corporate/legal-generation/fields/state/index.js.map +1 -0
  100. package/es/modules/corporate/legal-generation/fields/state/types.d.ts +37 -0
  101. package/es/modules/corporate/legal-generation/fields/state/types.js +27 -0
  102. package/es/modules/corporate/legal-generation/fields/state/types.js.map +1 -0
  103. package/es/modules/corporate/legal-generation/fields/state/useCapturedIdentityField.d.ts +28 -0
  104. package/es/modules/corporate/legal-generation/fields/state/useCapturedIdentityField.js +45 -0
  105. package/es/modules/corporate/legal-generation/fields/state/useCapturedIdentityField.js.map +1 -0
  106. package/es/modules/corporate/legal-generation/fields/state/useIdentityShapeCapture.d.ts +31 -0
  107. package/es/modules/corporate/legal-generation/fields/state/useIdentityShapeCapture.js +47 -0
  108. package/es/modules/corporate/legal-generation/fields/state/useIdentityShapeCapture.js.map +1 -0
  109. package/es/modules/corporate/legal-generation/fields/state/useLegalGenerationIdentityCapture.d.ts +35 -0
  110. package/es/modules/corporate/legal-generation/fields/state/useLegalGenerationIdentityCapture.js +36 -0
  111. package/es/modules/corporate/legal-generation/fields/state/useLegalGenerationIdentityCapture.js.map +1 -0
  112. package/es/modules/corporate/legal-generation/fields/state/useOperateField.d.ts +15 -0
  113. package/es/modules/corporate/legal-generation/fields/state/useOperateField.js +27 -0
  114. package/es/modules/corporate/legal-generation/fields/state/useOperateField.js.map +1 -0
  115. package/es/modules/corporate/legal-generation/form/AppointOfficerForm.d.ts +18 -9
  116. package/es/modules/corporate/legal-generation/form/AppointOfficerForm.js +130 -28
  117. package/es/modules/corporate/legal-generation/form/AppointOfficerForm.js.map +1 -1
  118. package/es/modules/corporate/legal-generation/form/CorporateRepresentativeForm.d.ts +27 -17
  119. package/es/modules/corporate/legal-generation/form/CorporateRepresentativeForm.js +112 -24
  120. package/es/modules/corporate/legal-generation/form/CorporateRepresentativeForm.js.map +1 -1
  121. package/es/modules/corporate/legal-generation/form/legalGenerationSubmit.d.ts +637 -0
  122. package/es/modules/corporate/legal-generation/form/legalGenerationSubmit.js +840 -0
  123. package/es/modules/corporate/legal-generation/form/legalGenerationSubmit.js.map +1 -0
  124. package/es/modules/corporate/legal-generation/form/useLegalGenerationDocument.d.ts +23 -0
  125. package/es/modules/corporate/legal-generation/form/useLegalGenerationDocument.js +55 -0
  126. package/es/modules/corporate/legal-generation/form/useLegalGenerationDocument.js.map +1 -0
  127. package/es/modules/corporate/legal-generation/pages/CorporateLegalGeneration/index.js +38 -8
  128. package/es/modules/corporate/legal-generation/pages/CorporateLegalGeneration/index.js.map +1 -1
  129. package/es/modules/self-registration/clda-form/components/CLDAContactList/index.js +50 -33
  130. package/es/modules/self-registration/clda-form/components/CLDAContactList/index.js.map +1 -1
  131. package/es/modules/self-registration/clda-form/components/ContactFields/index.d.ts +5 -2
  132. package/es/modules/self-registration/clda-form/components/ContactFields/index.js +6 -3
  133. package/es/modules/self-registration/clda-form/components/ContactFields/index.js.map +1 -1
  134. package/es/modules/self-registration/clda-form/components/DataProtectionOfficer/NewDPODialog/index.d.ts +1 -2
  135. package/es/modules/self-registration/clda-form/components/DataProtectionOfficer/NewDPODialog/index.js +7 -18
  136. package/es/modules/self-registration/clda-form/components/DataProtectionOfficer/NewDPODialog/index.js.map +1 -1
  137. package/es/modules/self-registration/clda-form/components/NewDirectorDialog/index.d.ts +1 -2
  138. package/es/modules/self-registration/clda-form/components/NewDirectorDialog/index.js +7 -18
  139. package/es/modules/self-registration/clda-form/components/NewDirectorDialog/index.js.map +1 -1
  140. package/es/modules/self-registration/clda-form/components/NewSecretaryDialog/index.d.ts +1 -2
  141. package/es/modules/self-registration/clda-form/components/NewSecretaryDialog/index.js +7 -18
  142. package/es/modules/self-registration/clda-form/components/NewSecretaryDialog/index.js.map +1 -1
  143. package/es/modules/self-registration/clda-form/components/NewShareholderDialog/index.d.ts +1 -2
  144. package/es/modules/self-registration/clda-form/components/NewShareholderDialog/index.js +7 -18
  145. package/es/modules/self-registration/clda-form/components/NewShareholderDialog/index.js.map +1 -1
  146. package/es/modules/self-registration/clda-form/components/useContactEditSession.d.ts +35 -0
  147. package/es/modules/self-registration/clda-form/components/useContactEditSession.js +51 -0
  148. package/es/modules/self-registration/clda-form/components/useContactEditSession.js.map +1 -0
  149. package/es/modules/self-registration/clda-form/incorporation/appoint-representative/components/NewOfficerDetailItem/index.d.ts +2 -3
  150. package/es/modules/self-registration/clda-form/incorporation/appoint-representative/components/NewOfficerDetailItem/index.js +60 -12
  151. package/es/modules/self-registration/clda-form/incorporation/appoint-representative/components/NewOfficerDetailItem/index.js.map +1 -1
  152. package/es/modules/self-registration/clda-form/incorporation/appoint-representative/components/NewOfficerDetailList/index.d.ts +1 -2
  153. package/es/modules/self-registration/clda-form/incorporation/appoint-representative/components/NewOfficerDetailList/index.js +3 -5
  154. package/es/modules/self-registration/clda-form/incorporation/appoint-representative/components/NewOfficerDetailList/index.js.map +1 -1
  155. package/es/modules/self-registration/clda-form/incorporation/appoint-representative/index.js +7 -21
  156. package/es/modules/self-registration/clda-form/incorporation/appoint-representative/index.js.map +1 -1
  157. package/es/modules/self-registration/clda-form/incorporation/company-general/CompanyGeneralForm.js +6 -5
  158. package/es/modules/self-registration/clda-form/incorporation/company-general/CompanyGeneralForm.js.map +1 -1
  159. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddAllotmentDialog/index.d.ts +1 -2
  160. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddAllotmentDialog/index.js +23 -28
  161. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddAllotmentDialog/index.js.map +1 -1
  162. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddBankOpeningDialog/index.d.ts +1 -2
  163. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddBankOpeningDialog/index.js +11 -24
  164. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddBankOpeningDialog/index.js.map +1 -1
  165. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddDivideDialog/index.d.ts +1 -2
  166. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddDivideDialog/index.js +13 -8
  167. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddDivideDialog/index.js.map +1 -1
  168. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddOfficerDialog/index.d.ts +1 -2
  169. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddOfficerDialog/index.js +11 -24
  170. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddOfficerDialog/index.js.map +1 -1
  171. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddRedonminationDialog/index.d.ts +1 -2
  172. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddRedonminationDialog/index.js +18 -9
  173. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddRedonminationDialog/index.js.map +1 -1
  174. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddRemoveOfficerDialog/index.d.ts +1 -2
  175. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddRemoveOfficerDialog/index.js +11 -24
  176. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddRemoveOfficerDialog/index.js.map +1 -1
  177. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddTransferDialog/index.d.ts +1 -2
  178. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddTransferDialog/index.js +23 -28
  179. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AddTransferDialog/index.js.map +1 -1
  180. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AllotmentItem/index.d.ts +2 -3
  181. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AllotmentItem/index.js +81 -14
  182. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AllotmentItem/index.js.map +1 -1
  183. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AllotmentList/index.d.ts +1 -2
  184. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AllotmentList/index.js +11 -15
  185. package/es/modules/self-registration/clda-form/incorporation/company-general/components/AllotmentList/index.js.map +1 -1
  186. package/es/modules/self-registration/clda-form/incorporation/company-general/components/BankOpeningContactItem/index.d.ts +2 -3
  187. package/es/modules/self-registration/clda-form/incorporation/company-general/components/BankOpeningContactItem/index.js +34 -5
  188. package/es/modules/self-registration/clda-form/incorporation/company-general/components/BankOpeningContactItem/index.js.map +1 -1
  189. package/es/modules/self-registration/clda-form/incorporation/company-general/components/BankOpeningList/index.d.ts +1 -2
  190. package/es/modules/self-registration/clda-form/incorporation/company-general/components/BankOpeningList/index.js +4 -5
  191. package/es/modules/self-registration/clda-form/incorporation/company-general/components/BankOpeningList/index.js.map +1 -1
  192. package/es/modules/self-registration/clda-form/incorporation/company-general/components/CapitalReceiptSelect/index.d.ts +1 -2
  193. package/es/modules/self-registration/clda-form/incorporation/company-general/components/CapitalReceiptSelect/index.js +5 -4
  194. package/es/modules/self-registration/clda-form/incorporation/company-general/components/CapitalReceiptSelect/index.js.map +1 -1
  195. package/es/modules/self-registration/clda-form/incorporation/company-general/components/OfficerDetailItem/index.d.ts +1 -2
  196. package/es/modules/self-registration/clda-form/incorporation/company-general/components/OfficerDetailItem/index.js +32 -12
  197. package/es/modules/self-registration/clda-form/incorporation/company-general/components/OfficerDetailItem/index.js.map +1 -1
  198. package/es/modules/self-registration/clda-form/incorporation/company-general/components/TransferItem/index.d.ts +2 -3
  199. package/es/modules/self-registration/clda-form/incorporation/company-general/components/TransferItem/index.js +80 -13
  200. package/es/modules/self-registration/clda-form/incorporation/company-general/components/TransferItem/index.js.map +1 -1
  201. package/es/modules/self-registration/clda-form/incorporation/company-general/components/TransferList/index.d.ts +1 -2
  202. package/es/modules/self-registration/clda-form/incorporation/company-general/components/TransferList/index.js +7 -9
  203. package/es/modules/self-registration/clda-form/incorporation/company-general/components/TransferList/index.js.map +1 -1
  204. package/es/modules/self-registration/clda-form/incorporation/components/FormContactEditor/index.d.ts +3 -0
  205. package/es/modules/self-registration/clda-form/incorporation/components/FormContactEditor/index.js +4 -2
  206. package/es/modules/self-registration/clda-form/incorporation/components/FormContactEditor/index.js.map +1 -1
  207. package/es/modules/self-registration/clda-form/pages/CLDAFormDefaultView/Content.js +16 -1
  208. package/es/modules/self-registration/clda-form/pages/CLDAFormDefaultView/Content.js.map +1 -1
  209. package/es/routes.d.ts +1 -1
  210. package/es/routes.js +4 -2
  211. package/es/routes.js.map +1 -1
  212. package/package.json +4 -4
@@ -0,0 +1,840 @@
1
+ import { ApiError, RequestBuilder } from "@voplus/api";
2
+ import { CAPTURED_FIELD_LABEL } from "@voplus/morpho-compliance/es/data/identityShape/constants";
3
+ import { utcdayjs } from "@voplus/morpho-ui/es/controls/UTCDatePicker/state";
4
+ import { message } from "antd";
5
+ // spec:3850 — CSP: 法律文书生成表单提交时把组合后的载荷送交转换
6
+ /** The route a legal-generation form's composed payload is posted to (spec:3850). */
7
+ export const LEGAL_GENERATION_SUBMIT_ROUTE = "/api/document-assistants/legal-generation/submit";
8
+ /** The existing gallery templates support exactly these appointment positions. */
9
+ export const SUPPORTED_OFFICER_POSITIONS = ["Director", "Secretary"];
10
+ /** Reports whether an input position is supported by the existing gallery templates. */
11
+ export function isSupportedOfficerPosition(position) {
12
+ return SUPPORTED_OFFICER_POSITIONS.includes(position);
13
+ }
14
+ /** 检查服务端副本的显示类型,允许 legacy 缺项与明确空值。 */
15
+ function readParentCompanyDetails(value) {
16
+ if (!value || typeof value !== "object" || Array.isArray(value))
17
+ return undefined;
18
+ const data = value;
19
+ for (const key of ["name", "address", "email", "phone"])
20
+ if (data[key] != null && typeof data[key] !== "string")
21
+ return undefined;
22
+ if (data.identity != null && (typeof data.identity !== "object" || Array.isArray(data.identity)))
23
+ return undefined;
24
+ const identity = data.identity;
25
+ if (identity?.number != null && typeof identity.number !== "string")
26
+ return undefined;
27
+ return { name: data.name, address: data.address,
28
+ email: data.email, phone: data.phone,
29
+ number: identity?.number };
30
+ }
31
+ /** The CLDA meta key every legal-generation submission persists under (contract §1). */
32
+ export const LEGAL_GENERATION_META_KEY = "CHO";
33
+ // spec:3848 — CSP form identities retain their selected shape snapshot
34
+ /**
35
+ * The frozen snapshot of `shape` as the payload carries it (spec:3848): a copy of the members a
36
+ * snapshot owns, taken from the capture's own shape and never resolved against the shared catalog.
37
+ *
38
+ * @param shape the capture's frozen shape.
39
+ * @returns the snapshot the submit body and the form-content JSON column carry.
40
+ */
41
+ export function toIdentityShapeSnapshot(shape) {
42
+ return {
43
+ sourceNumber: shape.source,
44
+ name: shape.name,
45
+ family: shape.family,
46
+ subject: shape.subject,
47
+ aliasPolicy: shape.policies.alias,
48
+ issuingCountryPolicy: shape.policies.issuingCountry,
49
+ expiryPolicy: shape.policies.expiryDate,
50
+ };
51
+ }
52
+ /**
53
+ * The identity values of one manual branch. The captured alias / issuing country / expiry date
54
+ * travel only where the captured shape's policy does not omit them (spec:3713), so a field the
55
+ * shape hides can never reach the payload even if its control once held a value.
56
+ */
57
+ function toSubmitIdentity(branch, name) {
58
+ const capture = branch.Identity;
59
+ const policies = capture?.shape.policies;
60
+ return {
61
+ fullName: name,
62
+ number: branch.IdentityNumber === undefined ? undefined : String(branch.IdentityNumber),
63
+ country: branch.Country,
64
+ ...(policies?.alias !== "omitted" ? { alias: capture?.values.alias } : {}),
65
+ ...(policies?.issuingCountry !== "omitted"
66
+ ? { issuingCountry: capture?.values.issuingCountry }
67
+ : {}),
68
+ ...(policies?.expiryDate !== "omitted" ? { expiryDate: capture?.values.expiryDate } : {}),
69
+ };
70
+ }
71
+ /**
72
+ * The manual branch's person. The full name is typed as `FullName` by the AppointOfficer branch and
73
+ * as `IdentityName` by the CorporateRepresentative branch; both name the same person field.
74
+ */
75
+ function toSubmitPerson(branch, documentName) {
76
+ const name = branch.FullName ?? branch.IdentityName;
77
+ const capture = branch.Identity;
78
+ return {
79
+ name,
80
+ ...(documentName === "Corporate Representative"
81
+ ? {
82
+ representative: name,
83
+ representativePassport: branch.IdentityNumber === undefined ? undefined : String(branch.IdentityNumber),
84
+ }
85
+ : {}),
86
+ email: branch.Email,
87
+ phone: branch.Contact,
88
+ type: "Person",
89
+ types: branch.OfficerType ? [branch.OfficerType] : undefined,
90
+ address: branch.Address,
91
+ identity: toSubmitIdentity(branch, name),
92
+ identityShape: capture ? toIdentityShapeSnapshot(capture.shape) : undefined,
93
+ };
94
+ }
95
+ // spec:1930 — CSP: 使用“Select existing”模式时,表单发送所选实体的 id;手动模式发送用户键入的字段
96
+ /**
97
+ * The `YYYY-MM-DD` date a submit body carries, or `null` when its control held no formattable date
98
+ * (spec:1928). The forms normalise their controls with `dayjs(value).format("YYYY-MM-DD")`, which
99
+ * answers the literal `"Invalid Date"` for an empty one, and the endpoint's `DateTime?` binding
100
+ * rejects that value with an opaque 400 — so an absent date is what the body must carry instead.
101
+ *
102
+ * @param date the date a form normalised.
103
+ * @returns the date the body carries, or null when there is none.
104
+ */
105
+ function toSubmitDate(date) {
106
+ return date && /^\d{4}-\d{2}-\d{2}$/.test(date) ? date : null;
107
+ }
108
+ /**
109
+ * Builds the submit request body from one form's composed submission (spec:1930): select-existing
110
+ * carries the picked entity's id and omits every typed person field, manual carries the typed
111
+ * person with its captured snapshot and omits the id.
112
+ *
113
+ * @param submission the document name plus the branch the form composed.
114
+ * @returns the body the conversion endpoint reads.
115
+ */
116
+ export function buildLegalGenerationSubmitBody(submission) {
117
+ const { documentName, mode, branch, company, parentCompany, parentCompanyCountry, parentCompanyDetails, documentDate, effectiveDate, } = submission;
118
+ const entityId = branch.OfficerId ?? branch.IdentityId;
119
+ return {
120
+ documentName,
121
+ mode,
122
+ entityId: mode === "select-existing" ? entityId ?? null : null,
123
+ // The Company picker hands over the corporate's own camelCase read model, while a reopened
124
+ // document's restore seeds the PascalCase pair; both are read so the body never carries the
125
+ // empty id a PascalCase-only extraction produced.
126
+ company: company
127
+ ? { id: company.id ?? company.Id ?? "", name: company.name ?? company.Name ?? "" }
128
+ : undefined,
129
+ ...(documentName === "Corporate Representative"
130
+ ? {
131
+ parentCompany: parentCompany
132
+ ? {
133
+ id: parentCompany.id ?? parentCompany.Id ?? "",
134
+ name: parentCompany.name ?? parentCompany.Name ?? "",
135
+ }
136
+ : undefined,
137
+ parentCompanyCountry,
138
+ ...(parentCompanyDetails !== undefined ? { parentCompanyDetails } : {}),
139
+ }
140
+ : {}),
141
+ documentDate: toSubmitDate(documentDate),
142
+ effectiveDate: toSubmitDate(effectiveDate),
143
+ person: mode === "manual" ? toSubmitPerson(branch, documentName) : undefined,
144
+ };
145
+ }
146
+ /**
147
+ * The message a failed submit carries (spec:3850). `ApiEndPoint` rejects with
148
+ * `` `${status} - ${bodyText}` `` and the conversion endpoint answers its failure message as a JSON
149
+ * string literal, so the body is unwrapped and anything else falls back to the raw text.
150
+ *
151
+ * @param error the rejection the endpoint raised.
152
+ * @returns the message the form shows.
153
+ */
154
+ export function legalGenerationFailureMessage(error) {
155
+ const text = error instanceof Error ? error.message : String(error);
156
+ const separator = text.indexOf(" - ");
157
+ const body = separator < 0 ? text : text.slice(separator + 3);
158
+ try {
159
+ const parsed = JSON.parse(body);
160
+ if (typeof parsed === "string" && parsed.length > 0) {
161
+ return parsed;
162
+ }
163
+ }
164
+ catch {
165
+ // The body was not a JSON string literal.
166
+ }
167
+ return body || text;
168
+ }
169
+ /** Validate required and omitted values against the immutable policies before create or same-id save. */
170
+ export function validateLegalGenerationCapture(capture) {
171
+ const errors = {};
172
+ if (!capture)
173
+ return errors;
174
+ for (const field of ["alias", "issuingCountry", "expiryDate"]) {
175
+ const populated = typeof capture.values[field] === "string" && !!capture.values[field]?.trim();
176
+ if (capture.shape.policies[field] === "mandatory" && !populated)
177
+ errors[field] = `${CAPTURED_FIELD_LABEL[field]} is required for ${capture.shape.name}.`;
178
+ if (capture.shape.policies[field] === "omitted" && populated)
179
+ errors[field] = `${CAPTURED_FIELD_LABEL[field]} is not captured by ${capture.shape.name}.`;
180
+ }
181
+ return errors;
182
+ }
183
+ // spec:3850 — CSP: 法律文书生成表单提交时把组合后的载荷送交转换
184
+ /** Fetch the ZIP with a bearer header and open a short-lived local URL, keeping credentials out of browser URLs. */
185
+ export async function downloadLegalGenerationDocument(document, accessToken, endpoint, open = (url) => {
186
+ const link = window.document.createElement("a");
187
+ link.href = url;
188
+ link.download = `${document.name || document.id}.zip`;
189
+ link.hidden = true;
190
+ window.document.body.appendChild(link);
191
+ try {
192
+ link.click();
193
+ }
194
+ finally {
195
+ link.remove();
196
+ }
197
+ }) {
198
+ const response = await endpoint.get(document.downloadUrl, new RequestBuilder().bearer(accessToken));
199
+ if (response.headers.get("Content-Type")?.split(";")[0].trim() !== "application/zip")
200
+ throw new Error("The document download did not return a ZIP archive.");
201
+ const url = URL.createObjectURL(await response.blob());
202
+ try {
203
+ open(url);
204
+ }
205
+ finally {
206
+ window.setTimeout(() => URL.revokeObjectURL(url), 60_000);
207
+ }
208
+ }
209
+ // spec:3848 — CSP form identities retain their selected shape snapshot
210
+ // spec:3850 — CSP: 法律文书生成表单提交时把组合后的载荷送交转换
211
+ /** Report delivery failures separately from persistence so the caller can retain the canonical saved id for retry. */
212
+ async function deliverLegalGeneration(result, endpoint, accessToken, open) {
213
+ try {
214
+ await downloadLegalGenerationDocument(result.document, accessToken, endpoint, open);
215
+ return { ok: true, result, document: result.document };
216
+ }
217
+ catch (error) {
218
+ const failure = `The document was saved, but download failed: ${legalGenerationFailureMessage(error)}. Please retry.`;
219
+ message.error(failure);
220
+ return { ok: false, message: failure, result, document: result.document };
221
+ }
222
+ }
223
+ // spec:3850 — CSP: 法律文书生成表单提交时把组合后的载荷送交转换
224
+ /**
225
+ * Submits a legal-generation form's composed payload to the document conversion and delivers both
226
+ * continuations (spec:3850): on success the generated document is opened for download, and on
227
+ * failure the endpoint's message is shown and nothing is downloaded or reset — the caller's entered
228
+ * values and captured snapshot stay exactly as they were, so the form stays open for a correction
229
+ * and a retry.
230
+ *
231
+ * @param options the endpoint, the caller's access token and the composed submission.
232
+ * @returns the handoff outcome.
233
+ */
234
+ export async function submitLegalGeneration(options) {
235
+ const { endpoint, accessToken, submission, open } = options;
236
+ let result;
237
+ try {
238
+ const response = await endpoint.post(LEGAL_GENERATION_SUBMIT_ROUTE, new RequestBuilder().json(buildLegalGenerationSubmitBody(submission)));
239
+ const body = await response.json();
240
+ if (!isSubmitResult(body))
241
+ throw new Error("The generated document response is invalid. Please retry.");
242
+ result = body;
243
+ }
244
+ catch (error) {
245
+ const failure = legalGenerationFailureMessage(error);
246
+ message.error(failure);
247
+ return { ok: false, message: failure };
248
+ }
249
+ return deliverLegalGeneration(result, endpoint, accessToken, open);
250
+ }
251
+ /** The highest `source` the built-in shapes occupy; custom shapes are numbered above it. */
252
+ const LAST_BUILT_IN_SOURCE = 3;
253
+ // spec:3848 — CSP form identities retain their selected shape snapshot
254
+ /**
255
+ * Restores the capture a saved form value holds, from the frozen snapshot alone: no catalog lookup
256
+ * runs, so a source shape later renamed, re-policied or deleted still restores exactly what was
257
+ * captured (spec:3848). The snapshot omits the catalog's `builtIn` flag and its concurrency token,
258
+ * so the restored shape marks itself built-in from its `source` — built-ins are 1-3 (spec:3712).
259
+ *
260
+ * @param person the form person a load route returned.
261
+ * @returns the capture the manual-entry block reopens on, or undefined for a legacy value.
262
+ */
263
+ export function toIdentityCapture(person) {
264
+ const snapshot = person.identityShape;
265
+ if (!snapshot) {
266
+ return undefined;
267
+ }
268
+ return {
269
+ shape: {
270
+ source: snapshot.sourceNumber,
271
+ name: snapshot.name,
272
+ family: snapshot.family,
273
+ subject: snapshot.subject,
274
+ builtIn: snapshot.sourceNumber <= LAST_BUILT_IN_SOURCE,
275
+ policies: {
276
+ alias: snapshot.aliasPolicy,
277
+ issuingCountry: snapshot.issuingCountryPolicy,
278
+ expiryDate: snapshot.expiryPolicy,
279
+ },
280
+ },
281
+ values: {
282
+ // A save writes `null` for a captured field the frozen shape omits (spec:3713); the
283
+ // capture itself reads that as "no value", which is what its controls render.
284
+ alias: person.identity?.alias ?? undefined,
285
+ issuingCountry: person.identity?.issuingCountry ?? undefined,
286
+ expiryDate: person.identity?.expiryDate ?? undefined,
287
+ },
288
+ };
289
+ }
290
+ /** The route a persisted form document is loaded from and written back to (contract §4). */
291
+ export function legalGenerationFormRoute(id) {
292
+ return `/api/document-assistants/forms/${id}`;
293
+ }
294
+ /**
295
+ * The appointed person of an Appoint Officer document: the contact the officer appointments name,
296
+ * falling back to the composed contact when the appointments name none.
297
+ */
298
+ function appointedPerson(content, contacts) {
299
+ const officerIds = (content.newOfficerAppointments?.officers ?? [])
300
+ .map((officer) => officer.officerId)
301
+ .filter((id) => !!id);
302
+ return (officerIds
303
+ .map((id) => contacts.find((contact) => contact.id === id))
304
+ .find((contact) => !!contact) ??
305
+ contacts.find((contact) => contact.idType === "NewId") ??
306
+ contacts[0]);
307
+ }
308
+ // spec:3848 — CSP form identities retain their selected shape snapshot
309
+ /** Completes a restored address before Ant Form can inject it into the eager Address control. */
310
+ function completeRestoredAddress(address) {
311
+ return {
312
+ line1: address?.line1 ?? "",
313
+ line2: address?.line2 ?? "",
314
+ city: address?.city ?? "",
315
+ state: address?.state ?? "",
316
+ postalCode: address?.postalCode ?? "",
317
+ country: address?.country ?? "",
318
+ };
319
+ }
320
+ // spec:3848 — CSP form identities retain their selected shape snapshot
321
+ /**
322
+ * Maps a supported named gallery document onto its first relevant captured person (spec:3848).
323
+ * The frozen snapshot restores its shape and values verbatim without a catalog lookup.
324
+ * The foreign parent remains separate from the Singapore company and representative identity.
325
+ *
326
+ * @param content the camelCase content a loaded Company General document carries.
327
+ * @returns what the gallery form reopens with, or undefined when no section of this endpoint's two
328
+ * is populated (a document another surface composed).
329
+ */
330
+ export function toLegalGenerationRestore(content, documentName) {
331
+ const contacts = content.contacts ?? [];
332
+ const company = content.corporate?.id
333
+ ? { Id: content.corporate.id, Name: content.corporate.name ?? "" }
334
+ : undefined;
335
+ // The persisted date is a C# `DateTime?` string, and both forms seed their `UTCDatePicker` with
336
+ // it, so it is converted here — at the restore boundary — with the same helper the platform's
337
+ // own company-general form reads the very same member with. An absent date stays undefined
338
+ // rather than becoming an `Invalid Date` the control would render as a broken value (spec:1928).
339
+ const date = content.date ? utcdayjs(content.date) : undefined;
340
+ if (documentName === "Appoint Officer" &&
341
+ (content.newOfficerAppointments?.officers ?? []).length > 0) {
342
+ const person = appointedPerson(content, contacts);
343
+ if (!person) {
344
+ return undefined;
345
+ }
346
+ return {
347
+ documentName: "Appoint Officer",
348
+ mode: person.idType === "ExistingContactId" ? "select-existing" : "manual",
349
+ person,
350
+ identity: toIdentityCapture(person),
351
+ values: {
352
+ FullName: person.name,
353
+ Country: person.identity?.country,
354
+ OfficerType: person.types?.[0],
355
+ Email: person.email,
356
+ Contact: person.phone,
357
+ IdentityNumber: person.identity?.number,
358
+ Address: completeRestoredAddress(person.address),
359
+ Company: company,
360
+ DocumentDate: date,
361
+ EffectiveDate: content.effectiveDate ? utcdayjs(content.effectiveDate) : undefined,
362
+ Officer: person.selectedEntityId
363
+ ? { id: person.selectedEntityId, name: person.name ?? "" }
364
+ : undefined,
365
+ },
366
+ };
367
+ }
368
+ if (documentName !== "Corporate Representative")
369
+ return undefined;
370
+ // A representative the endpoint resolved, or a snapshot the composition captured: either is the
371
+ // Corporate Representative section, the only other section this endpoint composes (contract §1/§3).
372
+ const person = contacts.find((contact) => !!contact.representative || !!contact.identityShape || !!contact.selectedIdentityId);
373
+ if (!person) {
374
+ return undefined;
375
+ }
376
+ const parentDetails = readParentCompanyDetails(content.parentCompanyData);
377
+ return {
378
+ documentName: "Corporate Representative",
379
+ parentCompanyBaseline: content.parentCompany?.id && parentDetails ? { parentId: content.parentCompany.id, details: parentDetails } : undefined,
380
+ mode: person.idType === "ExistingContactId" ? "select-existing" : "manual",
381
+ person,
382
+ identity: toIdentityCapture(person),
383
+ values: {
384
+ ParentCompany: content.parentCompany?.id
385
+ ? { Id: content.parentCompany.id, Name: content.parentCompany.name ?? "" }
386
+ : undefined,
387
+ // spec:3848 — 代表身份国家与母公司国家分别恢复;签发国家仍由快照字段恢复。
388
+ Country: content.parentCompanyCountry ?? undefined,
389
+ RepresentativeCountry: person.identity?.country ?? undefined,
390
+ IdentityName: person.identity?.fullName ?? person.name,
391
+ IdentityNumber: person.identity?.number,
392
+ Company: company,
393
+ Date: date,
394
+ LegalContact: person.selectedEntityId || person.selectedIdentityId
395
+ ? {
396
+ id: person.selectedEntityId ?? person.selectedIdentityId,
397
+ name: person.name ?? "",
398
+ }
399
+ : undefined,
400
+ },
401
+ };
402
+ }
403
+ /** Whether a section differs from the empty default CHO payload. */
404
+ function hasPopulatedSection(value) {
405
+ if (Array.isArray(value))
406
+ return value.length > 0;
407
+ if (value && typeof value === "object")
408
+ return Object.values(value).some(hasPopulatedSection);
409
+ return value !== null && value !== undefined && value !== "" && value !== false && value !== 0;
410
+ }
411
+ // spec:3848 — CSP form identities retain their selected shape snapshot
412
+ /** 用规范模板识别逻辑类型;未标记旧文书仍需原有名称及独立内容证据。 */
413
+ export function resolveGalleryDocumentKind(document) {
414
+ if (document.metaKey !== LEGAL_GENERATION_META_KEY || document.country !== "SGP")
415
+ return undefined;
416
+ const keys = document.content?.generationTemplateKeys;
417
+ const kind = keys == null ? document.name : Array.isArray(keys) && keys.length === 1 && keys[0] === "RepresentativeCert" ? "Corporate Representative" : "Appoint Officer";
418
+ if (kind !== "Appoint Officer" && kind !== "Corporate Representative")
419
+ return undefined;
420
+ const content = document.content;
421
+ if (!content)
422
+ return undefined;
423
+ const contacts = content.contacts ?? [];
424
+ const appointments = content.newOfficerAppointments?.officers ?? [];
425
+ if (!Array.isArray(contacts) || !Array.isArray(appointments))
426
+ return undefined;
427
+ if (contacts.some((contact) => !contact || typeof contact !== "object") ||
428
+ appointments.some((position) => !position || typeof position !== "object"))
429
+ return undefined;
430
+ const people = kind === "Appoint Officer"
431
+ ? appointments
432
+ .map((position) => contacts.find((contact) => contact.id === position.officerId))
433
+ .filter(Boolean)
434
+ : contacts.filter((contact) => typeof contact.representative === "string" && !!contact.representative.trim());
435
+ if (!people.length || (kind === "Appoint Officer" && !contacts.some((contact) => contact.id === appointments[0]?.officerId)))
436
+ return undefined;
437
+ if (appointments[0]?.positions != null && !Array.isArray(appointments[0].positions))
438
+ return undefined;
439
+ const positions = [...new Set(appointments[0]?.positions ?? [])];
440
+ if (kind === "Appoint Officer" &&
441
+ (!positions.length ||
442
+ positions.some((position) => !isSupportedOfficerPosition(position))))
443
+ return undefined;
444
+ const expected = kind === "Corporate Representative"
445
+ ? ["RepresentativeCert"]
446
+ : [
447
+ "BoardResolution",
448
+ ...(positions.includes("Director") ? ["Form45"] : []),
449
+ ...(positions.includes("Secretary") ? ["Form45B"] : []),
450
+ ];
451
+ if (content.generationTemplateKeys != null)
452
+ return (Array.isArray(content.generationTemplateKeys) &&
453
+ JSON.stringify(content.generationTemplateKeys) === JSON.stringify(expected)) ? kind : undefined;
454
+ if (document.parentNode != null ||
455
+ !!document.parentNodeId ||
456
+ contacts.length !== 1 ||
457
+ people.length !== 1)
458
+ return undefined;
459
+ const allowed = [
460
+ "corporate",
461
+ "date",
462
+ "effectiveDate",
463
+ "contacts",
464
+ "newOfficerAppointments",
465
+ "generationTemplateKeys",
466
+ "parentCompany",
467
+ "parentCompanyCountry",
468
+ "parentCompanyData",
469
+ ];
470
+ return Object.entries(content).every(([key, value]) => allowed.includes(key) || !hasPopulatedSection(value)) ? kind : undefined;
471
+ }
472
+ // spec:3848 — CSP form identities retain their selected shape snapshot
473
+ /**
474
+ * The document a legal-generation page was opened on, when the id it carries names a persisted
475
+ * Company General document this endpoint composed (contract §1/§4). Anything else — another id, a
476
+ * document family this gallery does not compose, a content whose sections are both empty — answers
477
+ * undefined; the saved-id load gate treats unsupported responses as a failure.
478
+ *
479
+ * @param document the document a load returned, when one was loaded at all.
480
+ * @returns the reopened document, or undefined when the page composes a new one.
481
+ */
482
+ export function toReopenedLegalGeneration(document) {
483
+ if (!document ||
484
+ document.metaKey !== LEGAL_GENERATION_META_KEY ||
485
+ !resolveGalleryDocumentKind(document)) {
486
+ return undefined;
487
+ }
488
+ const content = document.content ?? {};
489
+ const restore = toLegalGenerationRestore(content, resolveGalleryDocumentKind(document));
490
+ if (!restore) {
491
+ return undefined;
492
+ }
493
+ return { id: document.id, content, ...restore };
494
+ }
495
+ /**
496
+ * Maps the camelCase address a load route returns onto the PascalCase one the save route reads.
497
+ */
498
+ function toAddressEdit(address) {
499
+ return {
500
+ Line1: address.line1,
501
+ Line2: address.line2 ?? null,
502
+ City: address.city ?? null,
503
+ State: address.state ?? null,
504
+ PostalCode: address.postalCode ?? null,
505
+ Country: address.country,
506
+ };
507
+ }
508
+ /**
509
+ * Fixture/legacy helper mapping a loaded person onto the generic PascalCase edit contract, copying
510
+ * every value verbatim — including the captured snapshot, whose frozen `Name` and three policies
511
+ * are written back exactly as they were read (spec:3848).
512
+ */
513
+ function toFormPersonEdit(person) {
514
+ return {
515
+ Id: person.id ?? "",
516
+ IdType: person.idType ?? "NewId",
517
+ // spec:3848 — Preserve selected provenance alongside the frozen values on unchanged save.
518
+ SelectedEntityId: person.selectedEntityId ?? null,
519
+ SelectedIdentityId: person.selectedIdentityId ?? null,
520
+ Name: person.name ?? "",
521
+ Email: person.email ?? null,
522
+ Phone: person.phone ?? null,
523
+ Type: person.type ?? null,
524
+ Types: person.types ?? null,
525
+ Representative: person.representative ?? null,
526
+ RepresentativePassport: person.representativePassport ?? null,
527
+ Address: person.address ? toAddressEdit(person.address) : null,
528
+ Identity: person.identity
529
+ ? {
530
+ FullName: person.identity.fullName ?? null,
531
+ Number: person.identity.number ?? null,
532
+ Country: person.identity.country ?? null,
533
+ Alias: person.identity.alias ?? null,
534
+ IssuingCountry: person.identity.issuingCountry ?? null,
535
+ ExpiryDate: person.identity.expiryDate ?? null,
536
+ }
537
+ : null,
538
+ IdentityShape: person.identityShape
539
+ ? {
540
+ SourceNumber: person.identityShape.sourceNumber,
541
+ Name: person.identityShape.name,
542
+ Family: person.identityShape.family,
543
+ Subject: person.identityShape.subject,
544
+ AliasPolicy: person.identityShape.aliasPolicy,
545
+ IssuingCountryPolicy: person.identityShape.issuingCountryPolicy,
546
+ ExpiryPolicy: person.identityShape.expiryPolicy,
547
+ }
548
+ : null,
549
+ };
550
+ }
551
+ // spec:3848 — CSP form identities retain their selected shape snapshot
552
+ /**
553
+ * Maps the form content a load route returned onto the PascalCase
554
+ * `CompanyGeneral.SGP.FormEditOptions` the platform save route reads (contract §4). Every value is
555
+ * copied unchanged — in particular the frozen `IdentityShape` snapshot — so save → reopen → resubmit
556
+ * round-trips the snapshot byte-identically, with no catalog resolution and no dependence on the
557
+ * source shape still existing (spec:3848).
558
+ *
559
+ * Two members are deliberately shaped by the route's own semantics: `Corporate` is never sent,
560
+ * because a company whose id differs from the stored one reads as "the document moved company" and
561
+ * unions the whole roster into `Contacts`; and `Contacts` carries the **complete** list, because a
562
+ * sent list replaces the stored one wholesale — omitting an entry would delete it.
563
+ *
564
+ * @param content the camelCase content a loaded legal-generation form carries.
565
+ * @returns the payload the double-encoded save body carries.
566
+ */
567
+ export function toFormContentEditPayload(content) {
568
+ const contacts = content.contacts;
569
+ const officers = content.newOfficerAppointments?.officers;
570
+ return {
571
+ Date: content.date ?? null,
572
+ ...(contacts ? { Contacts: contacts.map(toFormPersonEdit) } : {}),
573
+ ...(officers && officers.length > 0
574
+ ? {
575
+ // The route binds this body with case-SENSITIVE `System.Text.Json`, so the
576
+ // read model's camelCase members have to be mapped onto the contract's
577
+ // PascalCase ones — sent verbatim they bind to nothing and wipe the link.
578
+ NewOfficerAppointments: {
579
+ Officers: officers.map((officer) => ({
580
+ Positions: officer.positions ?? [],
581
+ OfficerId: officer.officerId,
582
+ })),
583
+ },
584
+ }
585
+ : {}),
586
+ };
587
+ }
588
+ /**
589
+ * One captured identity field of a reopened person's save, decided from the **frozen snapshot's own
590
+ * policy** rather than from what the capture happens to hold (spec:3713): the replacement shape's
591
+ * `omitted` policy clears the persisted value to `null` — the capture has already dropped the key,
592
+ * and spreading it would leave the forbidden value in place, where no control can clear it — while a
593
+ * captured policy emits the value the user left there, or `null` when the capture holds none. A
594
+ * person with no capture at all has no policy to consult, so the persisted value it carries is kept.
595
+ *
596
+ * @param person the loaded person whose persisted identity the save writes back.
597
+ * @param capture the capture the re-composed branch holds, when it holds one.
598
+ * @param field the captured identity field to decide.
599
+ * @returns the value the saved identity carries for that field.
600
+ */
601
+ function toEditedIdentityValue(person, capture, field) {
602
+ if (!capture) {
603
+ return person.identity?.[field];
604
+ }
605
+ return capture.shape.policies[field] === "omitted" ? null : capture.values[field] ?? null;
606
+ }
607
+ /**
608
+ * The person a reopened document's save writes back: the contact the load returned, with the values
609
+ * the form re-composed folded into it. The contact's own identity — its id, the type the endpoint
610
+ * resolved and the members this form does not edit — is copied unchanged, so the save can never
611
+ * drop a value the document carries (contract §4).
612
+ */
613
+ function toLegalGenerationEditedPerson(person, branch) {
614
+ const capture = branch.Identity;
615
+ const name = branch.FullName ?? branch.IdentityName ?? person.name;
616
+ return {
617
+ ...person,
618
+ name,
619
+ email: branch.Email ?? person.email,
620
+ phone: branch.Contact ?? person.phone,
621
+ address: branch.Address ?? person.address,
622
+ identity: {
623
+ ...person.identity,
624
+ fullName: name,
625
+ number: branch.IdentityNumber === undefined
626
+ ? person.identity?.number
627
+ : String(branch.IdentityNumber),
628
+ country: branch.Country ?? person.identity?.country,
629
+ // Each captured field is decided by the frozen snapshot's own policy rather than by what
630
+ // the capture happens to hold, so a field the shape omits can never survive into the
631
+ // saved document (spec:3713).
632
+ alias: toEditedIdentityValue(person, capture, "alias"),
633
+ issuingCountry: toEditedIdentityValue(person, capture, "issuingCountry"),
634
+ expiryDate: toEditedIdentityValue(person, capture, "expiryDate"),
635
+ },
636
+ identityShape: capture ? toIdentityShapeSnapshot(capture.shape) : person.identityShape,
637
+ };
638
+ }
639
+ // spec:3848 — CSP form identities retain their selected shape snapshot
640
+ /**
641
+ * The content a reopened form's save writes back (contract §4): the document's own content with the
642
+ * branch the user re-composed folded into the section's contact, and every other contact, the officer
643
+ * appointments and the document date copied unchanged — so the list the save replaces wholesale is
644
+ * still the complete one (spec:3848).
645
+ *
646
+ * Only a manual branch folds. A select-existing save keeps the contact the endpoint resolved when it
647
+ * composed the document: the id the picker posts is an officer / legal-contact id, and only the
648
+ * server can resolve it to the contact id the document addresses its people by (contract §3.1).
649
+ *
650
+ * @param reopened the document the form was reopened on.
651
+ * @param branch the branch the form re-composed.
652
+ * @param documentDate the date the form carries, already normalised to `YYYY-MM-DD`.
653
+ * @returns the content {@link toFormContentEditPayload} writes back.
654
+ */
655
+ export function toLegalGenerationSavedContent(reopened, branch, documentDate) {
656
+ const date = documentDate && /^\d{4}-\d{2}-\d{2}$/.test(documentDate) ? documentDate : reopened.content.date;
657
+ if (reopened.mode !== "manual") {
658
+ return { ...reopened.content, date };
659
+ }
660
+ const edited = toLegalGenerationEditedPerson(reopened.person, branch);
661
+ const contacts = (reopened.content.contacts ?? []).map((contact) => contact.id === edited.id ? edited : contact);
662
+ // An Appoint Officer document records the appointment on its own section, addressed by the
663
+ // contact's id, so the position follows the officer type the form re-composed (contract §3).
664
+ const officers = reopened.content.newOfficerAppointments?.officers?.map((officer) => officer.officerId === edited.id && branch.OfficerType
665
+ ? { ...officer, positions: [branch.OfficerType] }
666
+ : officer);
667
+ return {
668
+ ...reopened.content,
669
+ date,
670
+ contacts,
671
+ ...(officers ? { newOfficerAppointments: { officers } } : {}),
672
+ };
673
+ }
674
+ // spec:3848 — CSP form identities retain their selected shape snapshot
675
+ /** The message a load failure other than a missing document surfaces (spec:3848). */
676
+ export const LEGAL_GENERATION_LOAD_FAILURE_MESSAGE = "Failed to load the saved document. Please reopen it from the document list and try again.";
677
+ /** Reject malformed or unsupported saved documents before controls can consume them. */
678
+ function isSavedDocument(value, id) {
679
+ if (!value || typeof value !== "object")
680
+ return false;
681
+ const document = value;
682
+ if (document.id !== id ||
683
+ document.metaKey !== LEGAL_GENERATION_META_KEY ||
684
+ document.country !== "SGP" ||
685
+ !document.content ||
686
+ typeof document.content !== "object")
687
+ return false;
688
+ const content = document.content;
689
+ const company = content.corporate;
690
+ if (!company || typeof company.id !== "string" || !company.id || typeof company.name !== "string")
691
+ return false;
692
+ if (content.parentCompany != null) {
693
+ if (typeof content.parentCompany !== "object" || Array.isArray(content.parentCompany))
694
+ return false;
695
+ const parent = content.parentCompany;
696
+ if (typeof parent.id !== "string" || !parent.id || typeof parent.name !== "string")
697
+ return false;
698
+ }
699
+ if (content.parentCompanyCountry != null && typeof content.parentCompanyCountry !== "string")
700
+ return false;
701
+ if (content.parentCompanyData != null && !readParentCompanyDetails(content.parentCompanyData))
702
+ return false;
703
+ if (!Array.isArray(content.contacts) ||
704
+ content.contacts.some((person) => !person ||
705
+ typeof person !== "object" ||
706
+ typeof person.id !== "string"))
707
+ return false;
708
+ for (const person of content.contacts) {
709
+ if (person.representative != null && typeof person.representative !== "string")
710
+ return false;
711
+ if (person.identity &&
712
+ (typeof person.identity !== "object" ||
713
+ Array.isArray(person.identity) ||
714
+ ["fullName", "number", "country", "alias", "issuingCountry", "expiryDate"].some((key) => {
715
+ const field = person.identity?.[key];
716
+ return field != null && typeof field !== "string";
717
+ })))
718
+ return false;
719
+ const shape = person.identityShape;
720
+ if (shape &&
721
+ (typeof shape !== "object" ||
722
+ !Number.isInteger(shape.sourceNumber) ||
723
+ shape.sourceNumber < 1 ||
724
+ typeof shape.name !== "string" ||
725
+ !["Passport", "NationalIdentity", "BusinessRegistration", "Other"].includes(shape.family) ||
726
+ !["Person", "Organization", "Both"].includes(shape.subject) ||
727
+ [shape.aliasPolicy, shape.issuingCountryPolicy, shape.expiryPolicy].some((policy) => !["omitted", "available", "mandatory"].includes(policy))))
728
+ return false;
729
+ }
730
+ if (content.newOfficerAppointments != null) {
731
+ if (typeof content.newOfficerAppointments !== "object")
732
+ return false;
733
+ const officers = content.newOfficerAppointments.officers;
734
+ if (!Array.isArray(officers) ||
735
+ officers.some((officer) => !officer ||
736
+ typeof officer !== "object" ||
737
+ typeof officer.officerId !== "string" ||
738
+ !Array.isArray(officer.positions) ||
739
+ officer.positions.some((position) => typeof position !== "string") ||
740
+ !content.contacts.some((contact) => contact.id === officer.officerId)))
741
+ return false;
742
+ }
743
+ for (const field of ["date", "effectiveDate"]) {
744
+ if (content[field] != null &&
745
+ (typeof content[field] !== "string" || !utcdayjs(content[field]).isValid()))
746
+ return false;
747
+ }
748
+ return !!toReopenedLegalGeneration(value);
749
+ }
750
+ // spec:3848 — CSP form identities retain their selected shape snapshot
751
+ /** Load a saved CHO composition; only 404 is missing, other failures remain failures. */
752
+ export async function loadLegalGenerationForm(options) {
753
+ try {
754
+ const builder = new RequestBuilder();
755
+ builder.FetchRequest({ signal: options.signal });
756
+ const response = await options.endpoint.get(legalGenerationFormRoute(options.id), builder);
757
+ const document = await response.json();
758
+ if (!isSavedDocument(document, options.id))
759
+ throw new Error(LEGAL_GENERATION_LOAD_FAILURE_MESSAGE);
760
+ return document;
761
+ }
762
+ catch (error) {
763
+ if (error instanceof ApiError && error.status === 404)
764
+ return undefined;
765
+ throw error;
766
+ }
767
+ }
768
+ // spec:3848 — CSP form identities retain their selected shape snapshot
769
+ /**
770
+ * Fixture/legacy generic-save helper writing re-composed content to its existing document
771
+ * (contract §4): the PascalCase payload of {@link toFormContentEditPayload}, sent as the JSON **text**
772
+ * the platform save route binds with case-sensitive `System.Text.Json`.
773
+ *
774
+ * This generic route remains for fixture/legacy callers. Production gallery editors call
775
+ * saveLegalGeneration, whose dedicated /save route owns selected composition and regeneration.
776
+ *
777
+ * @param options the endpoint, the persisted document's id and the content to write back.
778
+ * @returns the save outcome; a failure is surfaced through the same channel the submit uses.
779
+ */
780
+ export async function saveLegalGenerationContent(options) {
781
+ const { endpoint, id, content } = options;
782
+ try {
783
+ // The payload is the JSON TEXT of the edit options, exactly as the platform's own form save
784
+ // posts it (`new RequestBuilder().json(JSON.stringify(data))`), not the object itself.
785
+ await endpoint.post(legalGenerationFormRoute(id), new RequestBuilder().json(JSON.stringify(toFormContentEditPayload(content))));
786
+ return { ok: true };
787
+ }
788
+ catch (error) {
789
+ const failure = legalGenerationFailureMessage(error);
790
+ message.error(failure);
791
+ return { ok: false, message: failure };
792
+ }
793
+ }
794
+ // spec:1930 — CSP: 使用“Select existing”模式时,表单发送所选实体的 id;手动模式发送用户键入的字段
795
+ // spec:3848 — CSP form identities retain their selected shape snapshot
796
+ // spec:3850 — CSP: 法律文书生成表单提交时把组合后的载荷送交转换
797
+ /** Production gallery save: resolve the complete edited branch through the dedicated /save route, then download the regenerated existing ZIP. */
798
+ export async function saveLegalGeneration(options) {
799
+ let saved;
800
+ try {
801
+ const response = await options.endpoint.post(`/api/document-assistants/legal-generation/${encodeURIComponent(options.id)}/save`, new RequestBuilder().json(buildLegalGenerationSubmitBody(options.submission)));
802
+ const result = await response.json();
803
+ if (!isSubmitResult(result, options.id))
804
+ throw new Error("The saved document response is invalid. Please retry.");
805
+ saved = result;
806
+ }
807
+ catch (error) {
808
+ const failure = legalGenerationFailureMessage(error);
809
+ message.error(failure);
810
+ return { ok: false, message: failure };
811
+ }
812
+ return deliverLegalGeneration(saved, options.endpoint, options.accessToken, options.open);
813
+ }
814
+ // spec:3848 — CSP form identities retain their selected shape snapshot
815
+ // spec:3850 — CSP: 法律文书生成表单提交时把组合后的载荷送交转换
816
+ /** Validate canonical returned content and the same-id ZIP reference before reseeding or downloading. */
817
+ function isSubmitResult(value, savedId) {
818
+ if (!value || typeof value !== "object")
819
+ return false;
820
+ const result = value;
821
+ if (typeof result.id !== "string" ||
822
+ !result.id ||
823
+ (savedId && result.id !== savedId) ||
824
+ !["Appoint Officer", "Corporate Representative"].includes(result.documentName) ||
825
+ !["manual", "select-existing"].includes(result.mode))
826
+ return false;
827
+ return (isSavedDocument({
828
+ id: result.id,
829
+ metaKey: result.metaKey,
830
+ country: "SGP",
831
+ name: result.documentName,
832
+ content: result.content,
833
+ }, result.id) &&
834
+ resolveGalleryDocumentKind({ id: result.id, metaKey: result.metaKey, country: "SGP", name: result.documentName, content: result.content }) === result.documentName &&
835
+ result.document?.id === result.id &&
836
+ result.document.contentType === "application/zip" &&
837
+ result.document.downloadUrl ===
838
+ `/api/document-assistants/legal-generation/${encodeURIComponent(result.id)}/download`);
839
+ }
840
+ //# sourceMappingURL=legalGenerationSubmit.js.map