weaver-work-cli 0.1.25 → 0.1.28

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 (173) hide show
  1. package/README.md +35 -30
  2. package/dist/cmd/root.js +1 -0
  3. package/dist/cmd/setup/index.js +1 -1
  4. package/dist/core/runtime-agent.js +19 -0
  5. package/dist/internal/e10/auth/commands.js +48 -33
  6. package/dist/internal/e10/auth/messages.js +3 -0
  7. package/dist/internal/e10/auth/session.js +8 -0
  8. package/dist/internal/e10/auth/xiaoe.js +62 -43
  9. package/dist/internal/e10/auth-recovery.js +148 -0
  10. package/dist/internal/e10/client.js +47 -15
  11. package/dist/internal/e10/context.js +9 -31
  12. package/dist/internal/e10/index.js +1 -0
  13. package/dist/internal/e10/request-runtime.js +51 -9
  14. package/dist/shortcuts/archive/host.js +3 -2
  15. package/dist/shortcuts/archive/manifest.js +1 -1
  16. package/dist/shortcuts/archive-oa/constants.js +66 -0
  17. package/dist/shortcuts/archive-oa/continuation.js +70 -0
  18. package/dist/shortcuts/archive-oa/errors.js +53 -0
  19. package/dist/shortcuts/archive-oa/host.js +158 -0
  20. package/dist/shortcuts/archive-oa/index.js +225 -0
  21. package/dist/shortcuts/archive-oa/manifest.js +62 -0
  22. package/dist/shortcuts/archive-oa/operations/borrow-car.js +159 -0
  23. package/dist/shortcuts/archive-oa/operations/borrow-url.js +108 -0
  24. package/dist/shortcuts/archive-oa/operations/confirm.js +48 -0
  25. package/dist/shortcuts/archive-oa/operations/contract.js +120 -0
  26. package/dist/shortcuts/archive-oa/operations/locate.js +50 -0
  27. package/dist/shortcuts/archive-oa/operations/registry.js +40 -0
  28. package/dist/shortcuts/archive-oa/operations/shared.js +181 -0
  29. package/dist/shortcuts/archive-oa/operations/types.js +45 -0
  30. package/dist/shortcuts/archive-oa/operations.js +13 -0
  31. package/dist/shortcuts/asset/host.js +7 -6
  32. package/dist/shortcuts/blog/host.js +4 -3
  33. package/dist/shortcuts/caizhilian/host.js +3 -2
  34. package/dist/shortcuts/caizhilian/manifest.js +1 -1
  35. package/dist/shortcuts/calendar/host.js +2 -1
  36. package/dist/shortcuts/contract/host.js +34 -59
  37. package/dist/shortcuts/ehr/host.js +2 -1
  38. package/dist/shortcuts/esb/host.js +2 -1
  39. package/dist/shortcuts/fna/host.js +6 -3
  40. package/dist/shortcuts/fna/manifest.js +10 -5
  41. package/dist/shortcuts/fna/operations/expense-chart.js +162 -0
  42. package/dist/shortcuts/fna/operations/registry.js +2 -0
  43. package/dist/shortcuts/hrm/host.js +2 -1
  44. package/dist/shortcuts/im/host.js +3 -2
  45. package/dist/shortcuts/index.js +2 -0
  46. package/dist/shortcuts/invoice/host.js +9 -7
  47. package/dist/shortcuts/invoice/manifest.js +38 -47
  48. package/dist/shortcuts/invoice/operations/issuing-qys.js +328 -0
  49. package/dist/shortcuts/invoice/operations/registry.js +2 -3
  50. package/dist/shortcuts/jiuchuanhui/host.js +2 -1
  51. package/dist/shortcuts/mail/host.js +2 -1
  52. package/dist/shortcuts/meeting/host.js +2 -1
  53. package/dist/shortcuts/meeting/operations/eb/conflict.js +78 -12
  54. package/dist/shortcuts/okr/host.js +2 -1
  55. package/dist/shortcuts/plan/host.js +2 -1
  56. package/dist/shortcuts/project/host.js +2 -1
  57. package/dist/shortcuts/workflow/endpoints.js +11 -0
  58. package/dist/shortcuts/workflow/host.js +5 -7
  59. package/dist/shortcuts/workflow/index.js +6 -36
  60. package/dist/shortcuts/workflow/operations/chatscene.js +252 -0
  61. package/dist/shortcuts/workflow/operations/form.js +1047 -191
  62. package/dist/shortcuts/workflow/operations/read.js +137 -8
  63. package/dist/shortcuts/workflow/operations/sign-input.js +106 -0
  64. package/dist/shortcuts/workflow/operations/write.js +9 -1
  65. package/dist/shortcuts/workflow/stdout-compact.js +16 -1
  66. package/docs/SKILL.md +1 -1
  67. package/docs/_catalog.md +1 -1
  68. package/docs/agent-invoice.md +1 -1
  69. package/docs/archive-oa.md +79 -0
  70. package/docs/caizhilian.md +1 -1
  71. package/docs/e10-auth.md +7 -6
  72. package/docs/fna.md +7 -3
  73. package/docs/invoice.md +4 -6
  74. package/package.json +1 -1
  75. package/skill-template/business-info.json +20 -1
  76. package/skill-template/domains/archive-oa.md +19 -0
  77. package/skill-template/domains/caizhilian.md +2 -1
  78. package/skill-template/domains/qiyecheng.md +1 -0
  79. package/skill-template/domains/yepiaotong.md +8 -4
  80. package/skill-template/skill-template.md +1 -0
  81. package/skills/weaver-e10-archive-oa/SKILL.md +159 -0
  82. package/skills/weaver-e10-archive-oa/references/archive-locate.md +49 -0
  83. package/skills/weaver-e10-archive-oa/references/borrow-car.md +76 -0
  84. package/skills/weaver-e10-archive-oa/references/borrow-flow.md +53 -0
  85. package/skills/weaver-e10-archive-oa/references/contract-search.md +63 -0
  86. package/skills/weaver-e10-archive-oa/references/disabled-capabilities.md +29 -0
  87. package/skills/weaver-e10-archive-oa/references/qys-contract-search.md +47 -0
  88. package/skills/weaver-e10-archive-oa/references/source-manifest.json +400 -0
  89. package/skills/weaver-e10-blog/SKILL.md +6 -1
  90. package/skills/weaver-e10-blog/references/blog-query.md +3 -1
  91. package/skills/weaver-e10-builder/SKILL.md +22 -0
  92. package/skills/weaver-e10-caizhilian/SKILL.md +47 -16
  93. package/skills/weaver-e10-caizhilian/references/action-flow.md +70 -0
  94. package/skills/weaver-e10-caizhilian/references/agent-entry.md +12 -4
  95. package/skills/weaver-e10-caizhilian/references/auth-and-install.md +207 -0
  96. package/skills/weaver-e10-caizhilian/references/collaboration.md +5 -3
  97. package/skills/weaver-e10-caizhilian/references/document.md +33 -11
  98. package/skills/weaver-e10-caizhilian/references/file-collection.md +57 -54
  99. package/skills/weaver-e10-caizhilian/references/file-upload.md +5 -6
  100. package/skills/weaver-e10-caizhilian/references/files.md +9 -3
  101. package/skills/weaver-e10-caizhilian/references/folder.md +48 -16
  102. package/skills/weaver-e10-caizhilian/references/host-executor-prompt.md +32 -0
  103. package/skills/weaver-e10-caizhilian/references/host-platform-integration.md +46 -0
  104. package/skills/weaver-e10-caizhilian/references/rule-index.md +56 -0
  105. package/skills/weaver-e10-caizhilian/references/setting-attach.md +17 -54
  106. package/skills/weaver-e10-caizhilian/references/setting-backup.md +16 -63
  107. package/skills/weaver-e10-caizhilian/references/setting-encrypt.md +24 -64
  108. package/skills/weaver-e10-caizhilian/references/setting-storage-docs-summary.md +126 -0
  109. package/skills/weaver-e10-caizhilian/references/setting-storage-field-map.md +57 -0
  110. package/skills/weaver-e10-caizhilian/references/setting-storage.md +143 -548
  111. package/skills/weaver-e10-caizhilian/references/source-manifest.json +7 -7
  112. package/skills/weaver-e10-caizhilian/references/task-workflow.md +87 -0
  113. package/skills/weaver-e10-caizhilian/references/withheld.md +21 -45
  114. package/skills/weaver-e10-caizhilian/references/write-protocol.md +7 -0
  115. package/skills/weaver-e10-calendar/SKILL.md +6 -1
  116. package/skills/weaver-e10-esb/SKILL.md +8 -0
  117. package/skills/weaver-e10-esb/references/input-format.md +3 -1
  118. package/skills/weaver-e10-esb/references/source-manifest.json +5 -5
  119. package/skills/weaver-e10-hrm/SKILL.md +6 -1
  120. package/skills/weaver-e10-hrm/references/employee.md +1 -1
  121. package/skills/weaver-e10-hrm/references/org.md +1 -1
  122. package/skills/weaver-e10-hrm/references/position.md +1 -1
  123. package/skills/weaver-e10-jinchengda/SKILL.md +2 -1
  124. package/skills/weaver-e10-jinchengda/references/contract-agent-entry.md +1 -1
  125. package/skills/weaver-e10-jiuchuanhui/SKILL.md +6 -1
  126. package/skills/weaver-e10-jucailin/SKILL.md +6 -1
  127. package/skills/weaver-e10-jucailin/references/hr-contact.md +1 -1
  128. package/skills/weaver-e10-jucailin/references/salary.md +1 -1
  129. package/skills/weaver-e10-mail/SKILL.md +6 -1
  130. package/skills/weaver-e10-meeting/SKILL.md +11 -6
  131. package/skills/weaver-e10-meeting/references/meeting-eb.md +7 -3
  132. package/skills/weaver-e10-meeting/references/meeting-queries.md +3 -1
  133. package/skills/weaver-e10-meeting/references/meeting-verification.md +4 -2
  134. package/skills/weaver-e10-okr/SKILL.md +6 -1
  135. package/skills/weaver-e10-okr/references/source-manifest.json +5 -5
  136. package/skills/weaver-e10-plan/SKILL.md +6 -1
  137. package/skills/weaver-e10-plan/references/plan-overview-and-routing.md +3 -1
  138. package/skills/weaver-e10-plan/references/plan-report-query.md +3 -1
  139. package/skills/weaver-e10-plan/references/source-manifest.json +7 -7
  140. package/skills/weaver-e10-qiyecheng/SKILL.md +16 -6
  141. package/skills/weaver-e10-qiyecheng/references/catalog.md +6 -3
  142. package/skills/weaver-e10-qiyecheng/references/expense-center.md +2 -0
  143. package/skills/weaver-e10-qiyecheng/references/expense-chart.md +78 -0
  144. package/skills/weaver-e10-qiyecheng/references/source-manifest.json +87 -33
  145. package/skills/weaver-e10-shared/SKILL.md +53 -28
  146. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +22 -20
  147. package/skills/weaver-e10-shared/references/non-interactive-environment.md +13 -13
  148. package/skills/weaver-e10-shared/references/weaver-e10-installation.md +3 -1
  149. package/skills/weaver-e10-wenshuding/SKILL.md +6 -1
  150. package/skills/weaver-e10-wenshuding/references/source-manifest.json +3 -3
  151. package/skills/weaver-e10-workflow/references/source-manifest.json +33 -24
  152. package/skills/weaver-e10-workflow/references/workflow-create.md +41 -26
  153. package/skills/weaver-e10-yepiaotong/SKILL.md +23 -18
  154. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-examples.md +87 -45
  155. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-form-fill-rules.md +152 -0
  156. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-workflow.md +210 -0
  157. package/skills/weaver-e10-yepiaotong/references/invoice-reim.md +59 -36
  158. package/skills/weaver-e10-yepiaotong/references/reim-api-reference.md +63 -512
  159. package/skills/weaver-e10-yepiaotong/references/reim-examples.md +205 -346
  160. package/skills/weaver-e10-yepiaotong/references/reim-expense-subject-rules.md +4 -5
  161. package/skills/weaver-e10-yepiaotong/references/reim-form-fill-rules.md +231 -344
  162. package/skills/weaver-e10-yepiaotong/references/reim-phone-subsidy-rules.md +5 -5
  163. package/skills/weaver-e10-yepiaotong/references/reim-workflow.md +201 -162
  164. package/skills/weaver-e10-yepiaotong/references/source-manifest.json +121 -145
  165. package/skills/weaver-e10-yimiaoban/SKILL.md +9 -2
  166. package/skills/weaver-e10-yimiaoban/references/file-user-i18n.md +1 -1
  167. package/skills/weaver-e10-yimiaoban/references/msg-read.md +1 -1
  168. package/skills/weaver-e10-yimiaoban/references/source-manifest.json +9 -9
  169. package/skills/weaver-e10-ziguanjia/SKILL.md +6 -1
  170. package/dist/shortcuts/invoice/operations/issuing.js +0 -198
  171. package/skills/weaver-e10-yepiaotong/references/invoice-issuing-field-rules.md +0 -161
  172. package/skills/weaver-e10-yepiaotong/references/invoice-issuing.md +0 -107
  173. package/skills/weaver-e10-yepiaotong/references/reim-fast-path.md +0 -119
@@ -79,7 +79,6 @@ const listProperties = {
79
79
  const positiveId = { type: 'string', pattern: '^[1-9]\\d*$' };
80
80
  const positiveIdArray = { type: 'array', minItems: 1, maxItems: 100, items: positiveId };
81
81
  const jsonObject = { type: 'object', additionalProperties: true };
82
- const requestPayload = { type: 'object', minProperties: 1 };
83
82
  const reimFieldValue = {
84
83
  type: 'object',
85
84
  additionalProperties: false,
@@ -653,26 +652,36 @@ const reimAutoPrepareOutputSchema = continuationPreviewOutput(reimTemplatePrevie
653
652
  labels: reimLabelsSchema,
654
653
  warnings: arrayOf({ type: 'string' }),
655
654
  });
656
- const makeInvoicePreviewSchema = objectSchema({
657
- mode: { type: 'string', enum: ['issue', 'ledger'] },
658
- purchaserName: { type: 'string' },
659
- purchaserTaxNo: { type: 'string' },
660
- invoiceType: { type: 'string', enum: ['7', '8', '9', '10'] },
661
- externalDocumentNo: { type: 'string' },
662
- totalAmount: stringOrNumber,
663
- taxAmount: stringOrNumber,
664
- totalAmountWithTax: stringOrNumber,
665
- detailCount: { type: 'integer' },
666
- }, ['mode', 'purchaserName', 'invoiceType', 'externalDocumentNo', 'totalAmount', 'taxAmount', 'totalAmountWithTax', 'detailCount']);
667
- const issuingPrepareOutputSchema = continuationPreviewOutput(makeInvoicePreviewSchema, { normalizedPayload: requestPayload });
668
- const issuingApplyOutputSchema = objectSchema({
669
- mode: { type: 'string', enum: ['issue', 'ledger'] },
670
- serialNo: { type: 'string' },
671
- orderNo: { type: 'string' },
672
- ledgerOnly: { type: 'boolean' },
673
- result: rawObject,
674
- workflow: workflowSchema,
675
- }, ['mode', 'ledgerOnly', 'result', 'workflow']);
655
+ const nullableText = { type: ['string', 'null'] };
656
+ const qysListOutputSchema = objectSchema({
657
+ items: arrayOf(objectSchema({
658
+ issuable: { type: 'boolean' },
659
+ blockers: arrayOf({ type: 'string' }),
660
+ }, ['formDataId', 'issuable', 'blockers'], true)),
661
+ groups: arrayOf(objectSchema({
662
+ contractNo: { type: 'string' },
663
+ contractFlowId: { type: 'string', description: '台账「合同审批流程」的流程 ID,用于填开票流程的「相关合同审批流程」' },
664
+ contractFlowName: { type: 'string' },
665
+ purchaserName: { type: 'string' },
666
+ purchaserTaxNo: { type: 'string' },
667
+ purchaserBankName: { type: 'string', description: '购方开户行名称;台账把名称写进账号列时已拆出' },
668
+ purchaserBankAccount: { type: 'string', description: '购方开户行账号(只含账号);台账混写时已去掉开户行名称' },
669
+ invoiceTypes: arrayOf({ type: 'string' }),
670
+ specialInvoiceWithoutTaxNo: { type: 'boolean', description: '购方无税号但台账发票类型是专票;为 true 时不能发起开票流程,需先在台账改为普票' },
671
+ goodsNames: arrayOf({ type: 'string' }),
672
+ taxRates: arrayOf({ type: 'string' }),
673
+ specifications: arrayOf({ type: 'string' }, { description: '可开票行“规格型号”去重(开票流程主表“所售产品版本号”取此值)' }),
674
+ rowCount: { type: 'integer' },
675
+ formDataIds: arrayOf({ type: 'string' }),
676
+ totalAmount: { type: 'string', description: '可开票行“开票金额”合计,按分累加(开票流程按含税金额使用)' },
677
+ blockedRows: { type: 'integer', description: '同组已开票、开票中或已废弃的行数' },
678
+ warnings: arrayOf({ type: 'string' }),
679
+ }, ['contractNo', 'purchaserName', 'purchaserBankName', 'purchaserBankAccount', 'rowCount', 'formDataIds', 'totalAmount', 'blockedRows', 'specialInvoiceWithoutTaxNo', 'warnings'])),
680
+ matched: { type: 'integer' },
681
+ truncated: { type: 'boolean' },
682
+ scanned: { type: 'integer' },
683
+ scanComplete: { type: 'boolean' },
684
+ }, ['items', 'groups', 'matched', 'truncated', 'scanned', 'scanComplete']);
676
685
  export const invoiceOperations = [
677
686
  {
678
687
  name: 'invoice.list',
@@ -1186,38 +1195,20 @@ export const invoiceOperations = [
1186
1195
  outputSchema: reimAutoPrepareOutputSchema,
1187
1196
  },
1188
1197
  {
1189
- name: 'invoice.issuing.make.prepare',
1190
- risk: 'read-before-write',
1191
- fixed: {
1192
- path: '/api/inc/book/open/makeInvoice',
1193
- sellerName: '上海泛微网络科技股份有限公司',
1194
- taxNo: '9131000070322836XD',
1195
- },
1196
- inputSchema: {
1197
- type: 'object',
1198
- additionalProperties: false,
1199
- properties: {
1200
- mode: { type: 'string', enum: ['issue', 'ledger'] },
1201
- payload: requestPayload,
1202
- },
1203
- required: ['mode', 'payload'],
1204
- },
1205
- outputSchema: issuingPrepareOutputSchema,
1206
- },
1207
- {
1208
- name: 'invoice.issuing.make.apply',
1209
- risk: 'high-risk-write',
1198
+ name: 'invoice.issuing.qys.list',
1199
+ risk: 'read',
1200
+ fixed: { path: '/api/ebuilder/form/eblist/getData', ledger: '契约锁合同商品信息' },
1210
1201
  inputSchema: {
1211
1202
  type: 'object',
1212
1203
  additionalProperties: false,
1213
1204
  properties: {
1214
- payload: requestPayload,
1215
- continuation: { type: 'string', minLength: 1 },
1216
- confirm: { const: true },
1205
+ keyword: { type: 'string', description: '模糊匹配合同号、购买方名称、合同审批流程名称、商品名称或开票名称' },
1206
+ contractNo: { type: 'string', description: '按合同号精确筛选' },
1207
+ includeIssued: { type: 'boolean', default: false, description: '是否包含已开票、开票中或已废弃的行。默认 false 只返回可开票行;groups 始终按合同号+购方汇总,blockedRows 计不可开票行数。' },
1208
+ limit: { type: 'integer', minimum: 1, maximum: 100, default: 20 },
1217
1209
  },
1218
- required: ['payload', 'continuation', 'confirm'],
1219
1210
  },
1220
- outputSchema: issuingApplyOutputSchema,
1211
+ outputSchema: qysListOutputSchema,
1221
1212
  },
1222
1213
  ];
1223
1214
  export const invoiceManifest = {
@@ -0,0 +1,328 @@
1
+ import { InvoiceCliError, invoiceValidationError } from '../errors.js';
2
+ import { asNonEmptyString, assertBusinessSuccess } from './shared.js';
3
+ const LEDGER_PATH = '/api/ebuilder/form/eblist/getData';
4
+ const LEDGER_PAGE_ID = '1314988320430981804';
5
+ const LEDGER_APP_ID = '846297243998732291';
6
+ const LEDGER_MENU_ID = '5419015526244625718';
7
+ const LEDGER_PAGE_TITLE = '5aWR57qm6ZSB5ZCI5ZCM5ZWG5ZOB5L h5oGv';
8
+ const LEDGER_PAGE_SIZE = 50;
9
+ const LEDGER_MAX_PAGES = 40;
10
+ /** 契约锁合同商品信息表:数据 key → 可读字段。 */
11
+ export const QYS_LEDGER_FIELDS = [
12
+ { id: '1314988311673275798', key: 'contractNo', label: '合同号', kind: 'text' },
13
+ { id: '1314988311673275801', key: 'contractFlow', label: '合同审批流程', kind: 'relation' },
14
+ { id: '1314988311673275775', key: 'contractCategory', label: '合同种类', kind: 'text' },
15
+ { id: '1314988311673275790', key: 'productType', label: '产品类型', kind: 'option' },
16
+ { id: '1314988311673275776', key: 'productTypeDetail', label: '产品类型明细', kind: 'text' },
17
+ { id: '1314988311673275783', key: 'accountingCompany', label: '核算公司', kind: 'option' },
18
+ { id: '1314988311673275784', key: 'invoiceGroupFlag', label: '开票分组标识', kind: 'number' },
19
+ { id: '1314988311673275793', key: 'sourceFlag', label: '来源标识', kind: 'text' },
20
+ { id: '1314988311673275808', key: 'flowDetailId', label: '流程明细的id', kind: 'text' },
21
+ { id: '1314988311673275781', key: 'invoiceType', label: '发票类型', kind: 'option' },
22
+ { id: '1314988311673275806', key: 'purchaserName', label: '购买方名称', kind: 'text' },
23
+ { id: '1314988311673275807', key: 'purchaserTaxNo', label: '购买方税号', kind: 'text' },
24
+ { id: '1314988311673275809', key: 'purchaserAddress', label: '购买方地址', kind: 'text' },
25
+ { id: '1314988311673275810', key: 'purchaserTel', label: '购买方电话', kind: 'text' },
26
+ { id: '1314988311673275811', key: 'purchaserBankName', label: '购买方开户行', kind: 'text' },
27
+ { id: '1314988311673275802', key: 'purchaserBankAccount', label: '购买方开户行账号', kind: 'text' },
28
+ { id: '1314988311673275800', key: 'invoiceItemName', label: '开票名称', kind: 'text' },
29
+ { id: '1314988311673275799', key: 'goodsName', label: '商品名称', kind: 'text' },
30
+ { id: '1314988311673275803', key: 'revenueCode', label: '商品税收分类编码', kind: 'text' },
31
+ { id: '1314988311673275804', key: 'taxRate', label: '商品税率', kind: 'relation' },
32
+ { id: '1314988311673275780', key: 'specification', label: '规格型号', kind: 'text' },
33
+ { id: '1314988311673275785', key: 'unit', label: '单位', kind: 'text' },
34
+ { id: '1314988311673275805', key: 'quantity', label: '商品数量', kind: 'number' },
35
+ { id: '1314988311673275794', key: 'unitPrice', label: '商品单价', kind: 'money' },
36
+ { id: '1314988311673275795', key: 'amount', label: '开票金额', kind: 'money' },
37
+ { id: '1314988311673275789', key: 'uninvoicedAmount', label: '未开票金额', kind: 'money' },
38
+ { id: '1314988311673275787', key: 'invoicedAmount', label: '已开票金额', kind: 'money' },
39
+ { id: '1314988311673275788', key: 'invoicingAmount', label: '开票中金额', kind: 'money' },
40
+ { id: '1314988311673275777', key: 'amountDiffers', label: '开票金额存在差异', kind: 'option' },
41
+ { id: '1314988311673275796', key: 'thirdPartyPurchase', label: '是否三方采购商品', kind: 'option' },
42
+ { id: '1314988311673275812', key: 'invoiceNo', label: '发票号码', kind: 'text' },
43
+ { id: '1314988311673275797', key: 'invoiceFlow', label: '开票流程ID', kind: 'relation' },
44
+ { id: '1314988311673275791', key: 'invoiceDate', label: '开票时间', kind: 'date' },
45
+ { id: '1314988311673275792', key: 'discarded', label: '是否废弃', kind: 'option' },
46
+ { id: '1314988311673275778', key: 'discardedDate', label: '废弃时间', kind: 'date' },
47
+ { id: '1314988311673275779', key: 'contactName', label: '客户联系人', kind: 'text' },
48
+ { id: '1314988311673275782', key: 'contactPhone', label: '客户联系人电话', kind: 'text' },
49
+ { id: '1314988311673275786', key: 'contactEmail', label: '客户联系人邮箱', kind: 'text' },
50
+ ];
51
+ function textValue(value) {
52
+ if (value === undefined || value === null)
53
+ return null;
54
+ const text = String(value);
55
+ return text.trim() === '' ? null : text;
56
+ }
57
+ function optionValue(value) {
58
+ const first = Array.isArray(value) ? value[0] : value;
59
+ if (!first || typeof first !== 'object')
60
+ return null;
61
+ const item = first;
62
+ const id = textValue(item.id);
63
+ const name = textValue(item.name);
64
+ if (id === null && name === null)
65
+ return null;
66
+ return { id: id ?? '', name: name ?? '' };
67
+ }
68
+ export function convertQysLedgerRow(raw) {
69
+ const formDataId = textValue(raw.form_data_id) ?? textValue(raw.id);
70
+ if (!formDataId)
71
+ throw invoiceValidationError('ledger_row_invalid', '台账行缺少 form_data_id');
72
+ const row = {
73
+ formDataId,
74
+ title: textValue(raw.name),
75
+ dataStatus: optionValue(raw.data_status)?.name ?? null,
76
+ };
77
+ for (const field of QYS_LEDGER_FIELDS) {
78
+ const value = raw[field.id];
79
+ row[field.key] = field.kind === 'option' || field.kind === 'relation' ? optionValue(value) : textValue(value);
80
+ }
81
+ return row;
82
+ }
83
+ function toCents(value, name) {
84
+ if (value === null || !/^\d+(?:\.\d{1,2})?$/u.test(value)) {
85
+ throw invoiceValidationError('ledger_amount_invalid', `${name} 必须是最多两位小数的非负金额,当前值:${value ?? '空'}`);
86
+ }
87
+ const [whole, fraction = ''] = value.split('.');
88
+ return Number(whole) * 100 + Number(fraction.padEnd(2, '0'));
89
+ }
90
+ function formatCents(cents) {
91
+ return `${Math.floor(cents / 100)}.${String(cents % 100).padStart(2, '0')}`;
92
+ }
93
+ function isPositiveCents(value) {
94
+ return value !== null && /^\d+(?:\.\d+)?$/u.test(value) && Number(value) > 0;
95
+ }
96
+ /** 已开票、开票中或已废弃的行不能再开票,返回原因;可开票时返回空数组。 */
97
+ export function qysRowBlockers(row) {
98
+ const blockers = [];
99
+ if (row.invoiceNo)
100
+ blockers.push(`已有发票号码 ${row.invoiceNo}`);
101
+ if (row.invoiceFlow)
102
+ blockers.push(`已关联开票流程 ${row.invoiceFlow.name || row.invoiceFlow.id}`);
103
+ if (isPositiveCents(row.invoicingAmount))
104
+ blockers.push(`开票中金额 ${row.invoicingAmount}`);
105
+ if (row.discarded?.name === '是' || row.discardedDate)
106
+ blockers.push('已废弃');
107
+ return blockers;
108
+ }
109
+ async function fetchLedgerPage(host, pageNo, operation) {
110
+ const fieldFilter = {
111
+ cusMenuId: LEDGER_MENU_ID,
112
+ urlPageTitle: LEDGER_PAGE_TITLE,
113
+ [`appId_${LEDGER_PAGE_ID}`]: LEDGER_APP_ID,
114
+ cusview: '0',
115
+ };
116
+ const result = await host.invoiceMultipartRequest({
117
+ path: LEDGER_PATH,
118
+ headers: { ebbusinessid: LEDGER_PAGE_ID },
119
+ fields: {
120
+ ...fieldFilter,
121
+ filter: '[]',
122
+ fieldFilter,
123
+ pageFilter: { filter: {}, filterIsNull: true, esbFilter: [] },
124
+ pageAppId: LEDGER_APP_ID,
125
+ compId: 'e00e8244de584b369417a4fea31d7bed',
126
+ pageId: LEDGER_PAGE_ID,
127
+ pageNo: String(pageNo),
128
+ pageSize: String(LEDGER_PAGE_SIZE),
129
+ tableuid: 'a74d995bddd54a4f831551b468071632',
130
+ refPluginPackageId: '933846626099339271',
131
+ },
132
+ operation,
133
+ });
134
+ assertBusinessSuccess(result, 'qys_ledger_query_failed', '契约锁开票台账查询失败');
135
+ const list = result?.data?.list;
136
+ if (!Array.isArray(list)) {
137
+ throw new InvoiceCliError('api', 'qys_ledger_query_failed', '契约锁开票台账返回缺少 data.list', { exitCode: 1 });
138
+ }
139
+ return list;
140
+ }
141
+ /** 接口返回的 count 恒为 0,只能按“本页未取满”判断结束。 */
142
+ async function scanLedger(host, operation, visit) {
143
+ let scanned = 0;
144
+ for (let pageNo = 1; pageNo <= LEDGER_MAX_PAGES; pageNo += 1) {
145
+ const page = await fetchLedgerPage(host, pageNo, operation);
146
+ for (const raw of page) {
147
+ scanned += 1;
148
+ if (visit(convertQysLedgerRow(raw)))
149
+ return { scanned, complete: true };
150
+ }
151
+ if (page.length < LEDGER_PAGE_SIZE)
152
+ return { scanned, complete: true };
153
+ }
154
+ return { scanned, complete: false };
155
+ }
156
+ function matchesKeyword(row, keyword) {
157
+ const haystack = [row.contractNo, row.purchaserName, row.contractFlow?.name, row.goodsName, row.invoiceItemName, row.formDataId];
158
+ return haystack.some((value) => typeof value === 'string' && value.includes(keyword));
159
+ }
160
+ /**
161
+ * 台账常把开户行名称和账号一起写进「购买方开户行账号」(如 `招商银行福田支行 8135 8668 8310 001`)。
162
+ * 拆成名称与账号两段:账号取末尾(或开头)连续的数字段,原样保留其中的空格和连字符。
163
+ */
164
+ export function splitBankNameAndAccount(bankName, bankAccount) {
165
+ const name = (bankName ?? '').trim();
166
+ const account = (bankAccount ?? '').trim();
167
+ const trailing = /^(.*?[^\d\s-])\s*(\d[\d\s-]*\d)$/u.exec(account);
168
+ const leading = trailing ? null : /^(\d[\d\s-]*\d)\s*([^\d\s-].*)$/u.exec(account);
169
+ const rawText = trailing ? trailing[1] : leading ? leading[2] : '';
170
+ const textPart = rawText
171
+ .replace(/[\s,,;;::]*(?:银行账号|银行帐号|账号|帐号|账户|帐户)?[\s,,;;::]*$/u, '')
172
+ .replace(/^[\s,,;;::]*(?:开户行|开户银行)?[\s,,;;::]*/u, '')
173
+ .trim();
174
+ const digitPart = trailing ? trailing[2] : leading ? leading[1] : '';
175
+ if (!textPart || !digitPart)
176
+ return { bankName: name, bankAccount: account, split: false };
177
+ return { bankName: name || textPart, bankAccount: digitPart, split: true };
178
+ }
179
+ export function isSpecialInvoiceTypeName(name) {
180
+ return name.includes('专票') || name.includes('专用发票');
181
+ }
182
+ /** 一张开票流程对应一个“合同号 + 购方”分组;金额只累计可开票行。 */
183
+ export class QysLedgerGrouper {
184
+ groups = new Map();
185
+ add(row, blocked) {
186
+ const contractNo = row.contractNo ?? '';
187
+ const purchaserName = row.purchaserName ?? '';
188
+ const key = `${contractNo}|${purchaserName}`;
189
+ let group = this.groups.get(key);
190
+ if (!group) {
191
+ group = {
192
+ contractNo,
193
+ contractFlowId: row.contractFlow?.id ?? '',
194
+ contractFlowName: row.contractFlow?.name ?? '',
195
+ purchaserName,
196
+ purchaserTaxNos: new Set(),
197
+ invoiceTypes: new Set(),
198
+ goodsNames: new Set(),
199
+ taxRates: new Set(),
200
+ specifications: new Set(),
201
+ bankName: null,
202
+ bankAccount: null,
203
+ formDataIds: [],
204
+ amountCents: 0,
205
+ invalidAmountRows: [],
206
+ blockedRows: 0,
207
+ };
208
+ this.groups.set(key, group);
209
+ }
210
+ if (blocked) {
211
+ group.blockedRows += 1;
212
+ return;
213
+ }
214
+ group.formDataIds.push(row.formDataId);
215
+ if (!group.contractFlowId && row.contractFlow) {
216
+ group.contractFlowId = row.contractFlow.id;
217
+ group.contractFlowName = row.contractFlow.name;
218
+ }
219
+ if (row.purchaserTaxNo)
220
+ group.purchaserTaxNos.add(row.purchaserTaxNo);
221
+ if (row.invoiceType?.name)
222
+ group.invoiceTypes.add(row.invoiceType.name);
223
+ const goodsName = row.invoiceItemName ?? row.goodsName;
224
+ if (goodsName)
225
+ group.goodsNames.add(goodsName);
226
+ if (row.taxRate?.name)
227
+ group.taxRates.add(row.taxRate.name);
228
+ if (row.specification)
229
+ group.specifications.add(row.specification);
230
+ if (group.bankName === null && group.bankAccount === null && (row.purchaserBankName || row.purchaserBankAccount)) {
231
+ group.bankName = row.purchaserBankName ?? null;
232
+ group.bankAccount = row.purchaserBankAccount ?? null;
233
+ }
234
+ if (row.amount !== null && /^\d+(?:\.\d{1,2})?$/u.test(row.amount)) {
235
+ group.amountCents += toCents(row.amount, '开票金额');
236
+ }
237
+ else {
238
+ group.invalidAmountRows.push(row.formDataId);
239
+ }
240
+ }
241
+ result(includeEmpty) {
242
+ const output = [];
243
+ for (const group of this.groups.values()) {
244
+ if (!includeEmpty && group.formDataIds.length === 0)
245
+ continue;
246
+ const warnings = [];
247
+ const specialTypes = [...group.invoiceTypes].filter(isSpecialInvoiceTypeName);
248
+ const specialInvoiceWithoutTaxNo = group.purchaserTaxNos.size === 0 && specialTypes.length > 0;
249
+ if (specialInvoiceWithoutTaxNo) {
250
+ warnings.push(`购方无税号,不能开专票(台账发票类型:${specialTypes.join('、')}),请先在台账改为普票再发起`);
251
+ }
252
+ const bank = splitBankNameAndAccount(group.bankName, group.bankAccount);
253
+ if (bank.split) {
254
+ warnings.push(`台账「购买方开户行账号」混有开户行名称,已拆分为开户行名称「${bank.bankName}」、账号「${bank.bankAccount}」`);
255
+ }
256
+ if (!group.contractNo)
257
+ warnings.push('台账行缺少合同号,无法按合同号发起开票流程');
258
+ if (group.invoiceTypes.size > 1)
259
+ warnings.push(`同组发票类型不一致:${[...group.invoiceTypes].join('、')}`);
260
+ if (group.purchaserTaxNos.size > 1)
261
+ warnings.push(`同组购方税号不一致:${[...group.purchaserTaxNos].join('、')}`);
262
+ if (group.invalidAmountRows.length)
263
+ warnings.push(`开票金额无法识别,未计入合计:${group.invalidAmountRows.join('、')}`);
264
+ if (group.blockedRows > 0) {
265
+ warnings.push(`同合同另有 ${group.blockedRows} 行已开票、开票中或已废弃,流程联动可能一并带出,请在 E10 核对明细`);
266
+ }
267
+ output.push({
268
+ contractNo: group.contractNo,
269
+ contractFlowId: group.contractFlowId,
270
+ contractFlowName: group.contractFlowName,
271
+ purchaserName: group.purchaserName,
272
+ purchaserTaxNo: [...group.purchaserTaxNos].join('、'),
273
+ purchaserBankName: bank.bankName,
274
+ purchaserBankAccount: bank.bankAccount,
275
+ invoiceTypes: [...group.invoiceTypes],
276
+ specialInvoiceWithoutTaxNo,
277
+ goodsNames: [...group.goodsNames],
278
+ taxRates: [...group.taxRates],
279
+ specifications: [...group.specifications],
280
+ rowCount: group.formDataIds.length,
281
+ formDataIds: group.formDataIds,
282
+ totalAmount: formatCents(group.amountCents),
283
+ blockedRows: group.blockedRows,
284
+ warnings,
285
+ });
286
+ }
287
+ return output;
288
+ }
289
+ }
290
+ export class InvoiceIssuingQysListOperation {
291
+ name = 'invoice.issuing.qys.list';
292
+ async execute(input, { host }) {
293
+ const keyword = input.keyword === undefined || input.keyword === '' ? undefined : asNonEmptyString(input.keyword, 'keyword');
294
+ const contractNo = input.contractNo === undefined || input.contractNo === '' ? undefined : asNonEmptyString(input.contractNo, 'contractNo');
295
+ const includeIssued = input.includeIssued === true;
296
+ const limit = input.limit === undefined ? 20 : Number(input.limit);
297
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100)
298
+ throw invoiceValidationError('input_invalid', 'limit 必须是 1-100 的整数');
299
+ const items = [];
300
+ const grouper = new QysLedgerGrouper();
301
+ let matched = 0;
302
+ const scan = await scanLedger(host, this.name, (row) => {
303
+ if (contractNo && row.contractNo !== contractNo)
304
+ return false;
305
+ if (keyword && !matchesKeyword(row, keyword))
306
+ return false;
307
+ const blockers = qysRowBlockers(row);
308
+ grouper.add(row, blockers.length > 0);
309
+ if (!includeIssued && blockers.length)
310
+ return false;
311
+ matched += 1;
312
+ if (items.length < limit)
313
+ items.push({ ...row, issuable: blockers.length === 0, blockers });
314
+ return false;
315
+ });
316
+ return {
317
+ status: 'COMPLETE',
318
+ data: {
319
+ items,
320
+ groups: grouper.result(includeIssued),
321
+ matched,
322
+ truncated: matched > items.length,
323
+ scanned: scan.scanned,
324
+ scanComplete: scan.complete,
325
+ },
326
+ };
327
+ }
328
+ }
@@ -6,7 +6,7 @@ import { InvoiceDeleteApplyOperation, InvoiceDeletePrepareOperation } from './de
6
6
  import { InvoiceDownloadOperation } from './download.js';
7
7
  import { InvoiceGetOperation } from './get.js';
8
8
  import { InvoiceImportApplyOperation, InvoiceImportPrepareOperation } from './import.js';
9
- import { InvoiceIssuingMakeApplyOperation, InvoiceIssuingMakePrepareOperation } from './issuing.js';
9
+ import { InvoiceIssuingQysListOperation } from './issuing-qys.js';
10
10
  import { InvoiceListOperation } from './list.js';
11
11
  import { InvoiceOcrPreviewOperation } from './ocr-preview.js';
12
12
  import { InvoiceReimEmployeeSuperiorsOperation, InvoiceReimFieldSearchOperation, InvoiceReimFileOcrPreviewOperation, InvoiceReimFlowCreateApplyOperation, InvoiceReimFlowCreatePrepareOperation, InvoiceReimFlowUpdateApplyOperation, InvoiceReimFlowUpdatePrepareOperation, InvoiceReimFormStructureOperation, InvoiceReimRequestsListOperation, InvoiceReimRowInfoBatchOperation, InvoiceReimRowInfoOperation, InvoiceReimWorkflowResolveOperation, } from './reim.js';
@@ -50,8 +50,7 @@ function buildRegistry() {
50
50
  new InvoiceReimFlowCreateApplyOperation(),
51
51
  new InvoiceReimFlowUpdatePrepareOperation(),
52
52
  new InvoiceReimFlowUpdateApplyOperation(),
53
- new InvoiceIssuingMakePrepareOperation(),
54
- new InvoiceIssuingMakeApplyOperation(),
53
+ new InvoiceIssuingQysListOperation(),
55
54
  ];
56
55
  for (const handler of handlers) {
57
56
  if (registry.has(handler.name))
@@ -1,3 +1,4 @@
1
+ import { E10_SESSION_EXPIRED_MESSAGE } from '../../internal/e10/auth/messages.js';
1
2
  import { requireE10Session } from '../../internal/e10/context.js';
2
3
  import { getActiveProfile } from '../../internal/e10/auth/session.js';
3
4
  import { E10ApiError, createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
@@ -127,7 +128,7 @@ export function createJiuchuanhuiHost(ctx, dependencies = {}) {
127
128
  mapRuntimeNetworkError(error);
128
129
  }
129
130
  if (response.status === 401 || response.status === 302) {
130
- throw jiuchuanhuiAuthError('session_expired', 'E10 登录态已失效,请先向用户确认 E10 登录域名,再运行 weaver-work-cli auth login --base-url <域名> --agent_type <当前智能体名称> 完成登录', { httpStatus: response.status });
131
+ throw jiuchuanhuiAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
131
132
  }
132
133
  if (response.parseError) {
133
134
  if (response.status < 200 || response.status >= 300) {
@@ -1,4 +1,5 @@
1
1
  import { randomBytes } from 'node:crypto';
2
+ import { E10_SESSION_EXPIRED_MESSAGE } from '../../internal/e10/auth/messages.js';
2
3
  import { requireE10Session } from '../../internal/e10/context.js';
3
4
  import { getActiveProfile } from '../../internal/e10/auth/session.js';
4
5
  import { E10ApiError, createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
@@ -149,7 +150,7 @@ export function createMailHost(ctx, dependencies = {}) {
149
150
  throw error;
150
151
  }
151
152
  if (response.status === 401 || response.status === 302) {
152
- throw mailAuthError('session_expired', 'E10 登录态已失效,请先向用户确认 E10 登录域名,再运行 weaver-work-cli auth login --base-url <域名> 完成登录', { httpStatus: response.status });
153
+ throw mailAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
153
154
  }
154
155
  if (response.parseError) {
155
156
  if (response.status < 200 || response.status >= 300) {
@@ -1,3 +1,4 @@
1
+ import { E10_SESSION_EXPIRED_MESSAGE } from '../../internal/e10/auth/messages.js';
1
2
  import { requireE10Session } from '../../internal/e10/context.js';
2
3
  import { getActiveProfile } from '../../internal/e10/auth/session.js';
3
4
  import { E10ApiError, createE10RequestRuntime } from '../../internal/e10/request-runtime.js';
@@ -142,7 +143,7 @@ export function createMeetingHost(ctx, dependencies = {}) {
142
143
  mapRuntimeNetworkError(error);
143
144
  }
144
145
  if (response.status === 401 || response.status === 302) {
145
- throw meetingAuthError('session_expired', 'E10 登录态已失效,请先向用户确认 E10 登录域名,再运行 weaver-work-cli auth login --base-url <域名> 完成登录', { httpStatus: response.status });
146
+ throw meetingAuthError('session_expired', E10_SESSION_EXPIRED_MESSAGE, { httpStatus: response.status });
146
147
  }
147
148
  if (response.parseError) {
148
149
  if (response.status < 200 || response.status >= 300) {