@asteby/metacore-runtime-react 47.1.0 → 49.0.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 (190) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/dist/action-modal-dispatcher.d.ts +17 -3
  3. package/dist/action-modal-dispatcher.d.ts.map +1 -1
  4. package/dist/action-modal-dispatcher.js +80 -13
  5. package/dist/action-priority.d.ts +34 -0
  6. package/dist/action-priority.d.ts.map +1 -0
  7. package/dist/action-priority.js +60 -0
  8. package/dist/business/catalog-product-search.d.ts +32 -0
  9. package/dist/business/catalog-product-search.d.ts.map +1 -0
  10. package/dist/business/catalog-product-search.js +71 -0
  11. package/dist/business/catalog.d.ts.map +1 -1
  12. package/dist/business/catalog.js +2 -0
  13. package/dist/business/document-editor-model.d.ts +119 -0
  14. package/dist/business/document-editor-model.d.ts.map +1 -0
  15. package/dist/business/document-editor-model.js +317 -0
  16. package/dist/business/document-editor-panels.d.ts +64 -0
  17. package/dist/business/document-editor-panels.d.ts.map +1 -0
  18. package/dist/business/document-editor-panels.js +58 -0
  19. package/dist/business/document-editor.d.ts +27 -0
  20. package/dist/business/document-editor.d.ts.map +1 -0
  21. package/dist/business/document-editor.js +493 -0
  22. package/dist/business/document-lines.d.ts.map +1 -1
  23. package/dist/business/document-lines.js +5 -1
  24. package/dist/business/index.d.ts +5 -0
  25. package/dist/business/index.d.ts.map +1 -1
  26. package/dist/business/index.js +5 -0
  27. package/dist/business/line-items.d.ts +16 -1
  28. package/dist/business/line-items.d.ts.map +1 -1
  29. package/dist/business/line-items.js +14 -2
  30. package/dist/business/payment-allocator.d.ts +19 -0
  31. package/dist/business/payment-allocator.d.ts.map +1 -0
  32. package/dist/business/payment-allocator.js +31 -0
  33. package/dist/business/product-search.d.ts +4 -0
  34. package/dist/business/product-search.d.ts.map +1 -1
  35. package/dist/dialogs/dynamic-record.d.ts.map +1 -1
  36. package/dist/dialogs/dynamic-record.js +14 -7
  37. package/dist/document/document-page.d.ts.map +1 -1
  38. package/dist/document/document-page.js +8 -1
  39. package/dist/document/evaluate.d.ts.map +1 -1
  40. package/dist/document/evaluate.js +17 -1
  41. package/dist/document/index.d.ts +1 -0
  42. package/dist/document/index.d.ts.map +1 -1
  43. package/dist/document/index.js +1 -0
  44. package/dist/document/secondary-bar.d.ts +20 -0
  45. package/dist/document/secondary-bar.d.ts.map +1 -0
  46. package/dist/document/secondary-bar.js +44 -0
  47. package/dist/document/types.d.ts +8 -0
  48. package/dist/document/types.d.ts.map +1 -1
  49. package/dist/document-form-dialog.d.ts +31 -2
  50. package/dist/document-form-dialog.d.ts.map +1 -1
  51. package/dist/document-form-dialog.js +46 -11
  52. package/dist/dynamic-columns-helpers.d.ts +9 -0
  53. package/dist/dynamic-columns-helpers.d.ts.map +1 -1
  54. package/dist/dynamic-columns-helpers.js +19 -0
  55. package/dist/dynamic-columns.d.ts +9 -13
  56. package/dist/dynamic-columns.d.ts.map +1 -1
  57. package/dist/dynamic-columns.js +27 -21
  58. package/dist/dynamic-crud-page.d.ts +18 -0
  59. package/dist/dynamic-crud-page.d.ts.map +1 -1
  60. package/dist/dynamic-crud-page.js +15 -6
  61. package/dist/dynamic-form-schema.d.ts.map +1 -1
  62. package/dist/dynamic-form-schema.js +15 -3
  63. package/dist/dynamic-form.d.ts +16 -1
  64. package/dist/dynamic-form.d.ts.map +1 -1
  65. package/dist/dynamic-form.js +78 -20
  66. package/dist/dynamic-kanban.d.ts +9 -3
  67. package/dist/dynamic-kanban.d.ts.map +1 -1
  68. package/dist/dynamic-kanban.js +31 -12
  69. package/dist/dynamic-relation-helpers.d.ts.map +1 -1
  70. package/dist/dynamic-relation-helpers.js +8 -0
  71. package/dist/dynamic-relation.js +1 -1
  72. package/dist/dynamic-row-actions.d.ts +8 -0
  73. package/dist/dynamic-row-actions.d.ts.map +1 -1
  74. package/dist/dynamic-row-actions.js +12 -2
  75. package/dist/dynamic-table.d.ts.map +1 -1
  76. package/dist/dynamic-table.js +64 -63
  77. package/dist/index.d.ts +7 -2
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +19 -2
  80. package/dist/installed-addons-context.d.ts +15 -0
  81. package/dist/installed-addons-context.d.ts.map +1 -0
  82. package/dist/installed-addons-context.js +39 -0
  83. package/dist/org-runtime-context.d.ts +8 -0
  84. package/dist/org-runtime-context.d.ts.map +1 -1
  85. package/dist/org-runtime-context.js +8 -0
  86. package/dist/org-runtime-provider.d.ts +3 -1
  87. package/dist/org-runtime-provider.d.ts.map +1 -1
  88. package/dist/org-runtime-provider.js +3 -3
  89. package/dist/primitives/aging.d.ts +34 -0
  90. package/dist/primitives/aging.d.ts.map +1 -0
  91. package/dist/primitives/aging.js +48 -0
  92. package/dist/primitives/allocation.d.ts +42 -0
  93. package/dist/primitives/allocation.d.ts.map +1 -0
  94. package/dist/primitives/allocation.js +59 -0
  95. package/dist/primitives/approvals.d.ts +71 -0
  96. package/dist/primitives/approvals.d.ts.map +1 -0
  97. package/dist/primitives/approvals.js +48 -0
  98. package/dist/primitives/contributions.d.ts +93 -0
  99. package/dist/primitives/contributions.d.ts.map +1 -0
  100. package/dist/primitives/contributions.js +163 -0
  101. package/dist/primitives/document-kinds.d.ts +25 -0
  102. package/dist/primitives/document-kinds.d.ts.map +1 -0
  103. package/dist/primitives/document-kinds.js +26 -0
  104. package/dist/primitives/index.d.ts +8 -0
  105. package/dist/primitives/index.d.ts.map +1 -0
  106. package/dist/primitives/index.js +23 -0
  107. package/dist/primitives/panels.d.ts +68 -0
  108. package/dist/primitives/panels.d.ts.map +1 -0
  109. package/dist/primitives/panels.js +3 -0
  110. package/dist/primitives/use-contributions.d.ts +15 -0
  111. package/dist/primitives/use-contributions.d.ts.map +1 -0
  112. package/dist/primitives/use-contributions.js +30 -0
  113. package/dist/record-actions.d.ts +21 -0
  114. package/dist/record-actions.d.ts.map +1 -0
  115. package/dist/record-actions.js +76 -0
  116. package/dist/record-prefill-bar.d.ts +11 -0
  117. package/dist/record-prefill-bar.d.ts.map +1 -0
  118. package/dist/record-prefill-bar.js +33 -0
  119. package/dist/row-actions-menu.d.ts +32 -0
  120. package/dist/row-actions-menu.d.ts.map +1 -0
  121. package/dist/row-actions-menu.js +59 -0
  122. package/dist/slot.d.ts +4 -21
  123. package/dist/slot.d.ts.map +1 -1
  124. package/dist/slot.js +14 -36
  125. package/dist/types.d.ts +134 -0
  126. package/dist/types.d.ts.map +1 -1
  127. package/package.json +3 -3
  128. package/src/__tests__/catalog-product-search.test.ts +61 -0
  129. package/src/__tests__/document-editor-model.test.ts +221 -0
  130. package/src/__tests__/document-editor.test.tsx +340 -0
  131. package/src/__tests__/document-form-dialog.test.tsx +158 -3
  132. package/src/__tests__/dynamic-form-select-edit.test.tsx +155 -0
  133. package/src/__tests__/dynamic-kanban.test.tsx +24 -3
  134. package/src/__tests__/dynamic-record-extension-label.test.tsx +88 -0
  135. package/src/__tests__/installed-addons-context.test.tsx +50 -0
  136. package/src/__tests__/line-item-totals.test.ts +28 -0
  137. package/src/__tests__/prefill-scalar-from-record.test.ts +52 -1
  138. package/src/__tests__/primitives-federated.test.tsx +156 -0
  139. package/src/__tests__/primitives.test.ts +224 -0
  140. package/src/__tests__/record-prefill-bar.test.tsx +50 -0
  141. package/src/__tests__/row-action-link-navigation.test.ts +17 -0
  142. package/src/__tests__/secondary-actions.test.tsx +261 -0
  143. package/src/action-modal-dispatcher.tsx +97 -12
  144. package/src/action-priority.ts +82 -0
  145. package/src/business/business.test.ts +1 -1
  146. package/src/business/catalog-product-search.ts +105 -0
  147. package/src/business/catalog.ts +2 -0
  148. package/src/business/document-editor-model.ts +367 -0
  149. package/src/business/document-editor-panels.tsx +164 -0
  150. package/src/business/document-editor.tsx +781 -0
  151. package/src/business/document-lines.ts +5 -1
  152. package/src/business/index.ts +5 -0
  153. package/src/business/line-items.ts +31 -2
  154. package/src/business/payment-allocator.tsx +110 -0
  155. package/src/business/product-search.ts +4 -0
  156. package/src/dialogs/dynamic-record.tsx +29 -6
  157. package/src/document/document-page.tsx +15 -0
  158. package/src/document/evaluate.ts +17 -1
  159. package/src/document/index.ts +1 -0
  160. package/src/document/secondary-bar.tsx +92 -0
  161. package/src/document/types.ts +8 -0
  162. package/src/document-form-dialog.tsx +96 -12
  163. package/src/dynamic-columns-helpers.ts +22 -0
  164. package/src/dynamic-columns.tsx +35 -62
  165. package/src/dynamic-crud-page.tsx +33 -5
  166. package/src/dynamic-form-schema.ts +15 -2
  167. package/src/dynamic-form.tsx +117 -39
  168. package/src/dynamic-kanban.tsx +34 -7
  169. package/src/dynamic-relation-helpers.ts +9 -0
  170. package/src/dynamic-relation.tsx +7 -2
  171. package/src/dynamic-row-actions.tsx +12 -2
  172. package/src/dynamic-table.tsx +3 -0
  173. package/src/index.ts +46 -1
  174. package/src/installed-addons-context.tsx +58 -0
  175. package/src/org-runtime-context.ts +10 -0
  176. package/src/org-runtime-provider.tsx +7 -2
  177. package/src/primitives/aging.ts +76 -0
  178. package/src/primitives/allocation.ts +104 -0
  179. package/src/primitives/approvals.ts +103 -0
  180. package/src/primitives/contributions.ts +255 -0
  181. package/src/primitives/document-kinds.ts +62 -0
  182. package/src/primitives/index.ts +23 -0
  183. package/src/primitives/panels.ts +59 -0
  184. package/src/primitives/use-contributions.ts +42 -0
  185. package/src/record-actions.ts +101 -0
  186. package/src/record-prefill-bar.tsx +62 -0
  187. package/src/row-action-stage-gate.test.ts +33 -0
  188. package/src/row-actions-menu.tsx +159 -0
  189. package/src/slot.tsx +18 -53
  190. package/src/types.ts +129 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"allocation.d.ts","sourceRoot":"","sources":["../../src/primitives/allocation.ts"],"names":[],"mappings":"AAQA,yFAAyF;AACzF,MAAM,WAAW,YAAY;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,0EAA0E;IAC1E,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAClC;AAED,MAAM,WAAW,UAAU;IACvB,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,yGAAyG;IACzG,cAAc,EAAE,MAAM,CAAA;IACtB,aAAa,EAAE,MAAM,CAAA;IACrB,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,MAAM,kBAAkB,GAAG,kBAAkB,GAAG,qBAAqB,GAAG,QAAQ,CAAA;AAEtF,MAAM,WAAW,gBAAgB;IAC7B,WAAW,EAAE,UAAU,EAAE,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CACpB;AAMD,wFAAwF;AACxF,wBAAgB,eAAe,CAC3B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,SAAS,YAAY,EAAE,EAC7B,QAAQ,GAAE,kBAAuC,EACjD,MAAM,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,GAC9C,gBAAgB,CAsBlB;AAED,MAAM,WAAW,eAAe;IAC5B,IAAI,EAAE,cAAc,GAAG,aAAa,GAAG,mBAAmB,GAAG,WAAW,GAAG,cAAc,CAAA;IACzF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAA;CAChC;AAED,+FAA+F;AAC/F,wBAAgB,kBAAkB,CAC9B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,SAAS,YAAY,EAAE,EAC7B,WAAW,EAAE,SAAS,UAAU,EAAE,EAClC,IAAI,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC,eAAe,EAAE,CAcnB"}
@@ -0,0 +1,59 @@
1
+ // PaymentAllocator — reglas puras para aplicar un pago a varios documentos
2
+ // abiertos (CxC: facturas del cliente; CxP: facturas del proveedor).
3
+ //
4
+ // Lo usan: cobro multi-factura + REP (customers / fiscal_mexico), CxC
5
+ // «Registrar abono», CxP «Aplicar pago», abonos de apartado. Sin React y sin
6
+ // host: la UI (PaymentAllocator) y el backend (wasm) pueden validar con lo mismo.
7
+ import { roundMoney } from '../business/format';
8
+ function sortKey(d, s) {
9
+ return s === 'oldest_issued_first' ? d.issued_at : (d.due_at ?? d.issued_at);
10
+ }
11
+ /** Reparte `amount` sobre `docs` según la estrategia. `manual` respeta `manual[id]`. */
12
+ export function allocatePayment(amount, docs, strategy = 'oldest_due_first', manual = {}) {
13
+ let left = roundMoney(Math.max(0, amount));
14
+ const ordered = strategy === 'manual' ? [...docs] : [...docs].sort((a, b) => sortKey(a, strategy).localeCompare(sortKey(b, strategy)));
15
+ const allocations = [];
16
+ for (const d of ordered) {
17
+ if (left <= 0)
18
+ break;
19
+ const balance = roundMoney(d.balance);
20
+ if (balance <= 0)
21
+ continue;
22
+ const want = strategy === 'manual' ? roundMoney(manual[d.id] ?? 0) : balance;
23
+ const pay = roundMoney(Math.min(want, balance, left));
24
+ if (pay <= 0)
25
+ continue;
26
+ allocations.push({
27
+ document_id: d.id,
28
+ amount: pay,
29
+ balance_before: balance,
30
+ balance_after: roundMoney(balance - pay),
31
+ installment: (d.installments_paid ?? 0) + 1,
32
+ });
33
+ left = roundMoney(left - pay);
34
+ }
35
+ const applied = roundMoney(allocations.reduce((s, a) => s + a.amount, 0));
36
+ return { allocations, applied, unapplied: roundMoney(Math.max(0, amount) - applied) };
37
+ }
38
+ /** Revisión previa a guardar (la UI la pinta en ValidationChecklist; el backend la repite). */
39
+ export function validateAllocation(amount, currency, docs, allocations, opts = {}) {
40
+ const byId = new Map(docs.map(d => [d.id, d]));
41
+ const issues = [];
42
+ if (amount <= 0)
43
+ issues.push({ code: 'non_positive', severity: 'error' });
44
+ for (const a of allocations) {
45
+ const d = byId.get(a.document_id);
46
+ if (!d)
47
+ continue;
48
+ if (a.amount > roundMoney(d.balance) + 0.005)
49
+ issues.push({ code: 'over_balance', document_id: d.id, severity: 'error' });
50
+ if (d.currency !== currency)
51
+ issues.push({ code: 'currency_mismatch', document_id: d.id, severity: 'error' });
52
+ }
53
+ const applied = roundMoney(allocations.reduce((s, a) => s + a.amount, 0));
54
+ if (applied > roundMoney(amount) + 0.005)
55
+ issues.push({ code: 'over_amount', severity: 'error' });
56
+ else if (applied < roundMoney(amount) - 0.005)
57
+ issues.push({ code: 'unapplied', severity: opts.allowUnapplied ? 'warning' : 'error' });
58
+ return issues;
59
+ }
@@ -0,0 +1,71 @@
1
+ import type { ApiClient } from '../api-context';
2
+ /** Espejo de dynamic.ApprovalRequest (JSON). jsonb llega como string. */
3
+ export interface ApprovalRequestDTO {
4
+ id: string;
5
+ addon_key: string;
6
+ model_key: string;
7
+ record_id: string;
8
+ action_key: string;
9
+ constraint_key: string;
10
+ kind: 'constraint' | 'action' | 'explicit' | 'pin';
11
+ label: string;
12
+ status: 'pending' | 'approved' | 'applied' | 'failed' | 'rejected' | 'expired';
13
+ requested_by: string;
14
+ requested_by_role: string;
15
+ requested_at: string;
16
+ expires_at: string | null;
17
+ roles: string | string[];
18
+ reason_required: boolean;
19
+ payload: string | Record<string, unknown>;
20
+ snapshot: string | Record<string, unknown>;
21
+ violation: string | Record<string, unknown>;
22
+ decided_by: string | null;
23
+ decided_at: string | null;
24
+ reason: string;
25
+ error: string;
26
+ }
27
+ export interface ApprovalListQuery {
28
+ status?: ApprovalRequestDTO['status'];
29
+ kind?: ApprovalRequestDTO['kind'];
30
+ model?: string;
31
+ /** Solo las que yo pedí. */
32
+ mine?: boolean;
33
+ /** Solo las que mis roles pueden decidir (default de la bandeja). */
34
+ for_me?: boolean;
35
+ page?: number;
36
+ per_page?: number;
37
+ }
38
+ /** Categoría visible en la bandeja. El mapeo policy → categoría es extensible por addons. */
39
+ export interface ApprovalCategory {
40
+ key: string;
41
+ label: string;
42
+ icon?: string;
43
+ /** Coincide si devuelve true (p. ej. por action_key, constraint_key o model_key). */
44
+ match: (r: ApprovalRequestDTO) => boolean;
45
+ }
46
+ /** Los addons registran su categoría al cargar (purchases: «Compras», fiscal_mexico: «Cancelación CFDI»…). */
47
+ export declare function registerApprovalCategory(c: ApprovalCategory): () => void;
48
+ export declare function categorize(r: ApprovalRequestDTO): string;
49
+ export declare function groupApprovals(items: readonly ApprovalRequestDTO[]): Record<string, ApprovalRequestDTO[]>;
50
+ export declare function parseJSONField<T = Record<string, unknown>>(v: string | T | null | undefined): T | null;
51
+ /** Cliente fino sobre las rutas del kernel; la UI lo usa vía useApi(). */
52
+ export declare function approvalsClient(api: ApiClient): {
53
+ list: (q?: ApprovalListQuery) => Promise<{
54
+ items: ApprovalRequestDTO[];
55
+ total: number;
56
+ }>;
57
+ count: () => Promise<number>;
58
+ approve: (id: string, reason?: string) => Promise<{
59
+ data: any;
60
+ headers?: any;
61
+ }>;
62
+ reject: (id: string, reason: string) => Promise<{
63
+ data: any;
64
+ headers?: any;
65
+ }>;
66
+ approveWithPin: (id: string, pin: string, reason: string) => Promise<{
67
+ data: any;
68
+ headers?: any;
69
+ }>;
70
+ };
71
+ //# sourceMappingURL=approvals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approvals.d.ts","sourceRoot":"","sources":["../../src/primitives/approvals.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE/C,yEAAyE;AACzE,MAAM,WAAW,kBAAkB;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,cAAc,EAAE,MAAM,CAAA;IACtB,IAAI,EAAE,YAAY,GAAG,QAAQ,GAAG,UAAU,GAAG,KAAK,CAAA;IAClD,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAA;IAC9E,YAAY,EAAE,MAAM,CAAA;IACpB,iBAAiB,EAAE,MAAM,CAAA;IACzB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACxB,eAAe,EAAE,OAAO,CAAA;IACxB,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACzC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC1C,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC3C,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,iBAAiB;IAC9B,MAAM,CAAC,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAA;IACrC,IAAI,CAAC,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAA;IACjC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,4BAA4B;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,qEAAqE;IACrE,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,6FAA6F;AAC7F,MAAM,WAAW,gBAAgB;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,qFAAqF;IACrF,KAAK,EAAE,CAAC,CAAC,EAAE,kBAAkB,KAAK,OAAO,CAAA;CAC5C;AAID,8GAA8G;AAC9G,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,gBAAgB,GAAG,MAAM,IAAI,CAMxE;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAExD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,kBAAkB,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,kBAAkB,EAAE,CAAC,CAIzG;AAED,wBAAgB,cAAc,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,CAAC,GAAG,IAAI,CAQtG;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,GAAG,EAAE,SAAS;eAOtB,iBAAiB;eAEa,kBAAkB,EAAE;;;;kBAGpD,MAAM,WAAW,MAAM;;;;iBACxB,MAAM,UAAU,MAAM;;;;yBACd,MAAM,OAAO,MAAM,UAAU,MAAM;;;;EAE/D"}
@@ -0,0 +1,48 @@
1
+ const categories = [];
2
+ /** Los addons registran su categoría al cargar (purchases: «Compras», fiscal_mexico: «Cancelación CFDI»…). */
3
+ export function registerApprovalCategory(c) {
4
+ categories.push(c);
5
+ return () => {
6
+ const i = categories.indexOf(c);
7
+ if (i >= 0)
8
+ categories.splice(i, 1);
9
+ };
10
+ }
11
+ export function categorize(r) {
12
+ return categories.find(c => c.match(r))?.key ?? 'other';
13
+ }
14
+ export function groupApprovals(items) {
15
+ const out = {};
16
+ for (const r of items)
17
+ (out[categorize(r)] ??= []).push(r);
18
+ return out;
19
+ }
20
+ export function parseJSONField(v) {
21
+ if (v == null || v === '')
22
+ return null;
23
+ if (typeof v !== 'string')
24
+ return v;
25
+ try {
26
+ return JSON.parse(v);
27
+ }
28
+ catch {
29
+ return null;
30
+ }
31
+ }
32
+ /** Cliente fino sobre las rutas del kernel; la UI lo usa vía useApi(). */
33
+ export function approvalsClient(api) {
34
+ const qs = (q) => Object.entries(q)
35
+ .filter(([, v]) => v !== undefined && v !== '')
36
+ .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)
37
+ .join('&');
38
+ return {
39
+ list: async (q = { status: 'pending', for_me: true }) => {
40
+ const res = await api.get(`/approvals?${qs(q)}`);
41
+ return { items: (res.data?.data ?? []), total: Number(res.data?.meta?.total ?? 0) };
42
+ },
43
+ count: async () => Number((await api.get('/approvals/count')).data?.data?.count ?? 0),
44
+ approve: (id, reason) => api.post(`/approvals/${id}/approve`, { reason }),
45
+ reject: (id, reason) => api.post(`/approvals/${id}/reject`, { reason }),
46
+ approveWithPin: (id, pin, reason) => api.post(`/approvals/${id}/approve-pin`, { pin, reason }),
47
+ };
48
+ }
@@ -0,0 +1,93 @@
1
+ import { type ComponentType } from 'react';
2
+ /** Regiones del DocumentEditor/DocumentPage donde se puede contribuir. */
3
+ export type DocumentRegion = 'header.fields' | 'party.card' | 'lines.columns' | 'side.panels' | 'body.sections' | 'footer.actions';
4
+ export interface DocumentEditorContext {
5
+ kind: string;
6
+ model: string;
7
+ values: Record<string, unknown>;
8
+ mode: 'create' | 'edit' | 'view';
9
+ }
10
+ export interface DocumentContribution<P = DocumentContributionProps> {
11
+ /** Id estable `addon.region.name` (dedupe y orden). Re-registrar el mismo id reemplaza. */
12
+ id: string;
13
+ kinds: string[] | '*';
14
+ region: DocumentRegion;
15
+ /** Addon dueño. Si no está instalado, no se renderiza (y el server revalida). */
16
+ requiresAddon?: string;
17
+ /** Mayor = primero. Default 0. */
18
+ priority?: number;
19
+ when?: (ctx: DocumentEditorContext) => boolean;
20
+ /** Componente o loader perezoso del remote (`() => import('fiscal_mexico/CfdiSection')`). */
21
+ component?: ComponentType<P>;
22
+ load?: () => Promise<{
23
+ default: ComponentType<P>;
24
+ }>;
25
+ }
26
+ /**
27
+ * Contribución resuelta: `component` siempre presente (si se registró con
28
+ * `load`, es un envoltorio lazy + Suspense que no pinta nada mientras carga).
29
+ */
30
+ export type ResolvedDocumentContribution<P = DocumentContributionProps> = DocumentContribution<P> & {
31
+ component: ComponentType<P>;
32
+ };
33
+ export interface DocumentContributionProps {
34
+ ctx: DocumentEditorContext;
35
+ setValue: (key: string, value: unknown) => void;
36
+ issues: {
37
+ report: (key: string, issue: {
38
+ severity: 'error' | 'warning';
39
+ message: string;
40
+ }) => void;
41
+ };
42
+ }
43
+ /** Predicado «¿está instalado?»; `useInstalledAddons` lo arma en React. */
44
+ export type IsAddonInstalled = (addonKey: string) => boolean;
45
+ /** Suscripción para useSyncExternalStore (contribuciones y modales). */
46
+ export declare function subscribeContributions(listener: () => void): () => void;
47
+ /** Versión monotónica: cambia en cada register/dispose. */
48
+ export declare function contributionsVersion(): number;
49
+ export declare function slotIdFor(kind: string, region: DocumentRegion): string;
50
+ /**
51
+ * Registra la contribución y la publica en slotRegistry bajo cada `doc:<kind>:<region>`
52
+ * (kinds '*' → `doc:*:<region>`). Devuelve el disposer: AddonLoader lo llama al
53
+ * desmontar o recargar el remote (fiber), así la sección desaparece al desinstalar.
54
+ * Re-registrar el mismo `id` (recarga en caliente) reemplaza la anterior.
55
+ */
56
+ export declare function registerDocumentContribution<P = DocumentContributionProps>(c: DocumentContribution<P>, source?: string): () => void;
57
+ /**
58
+ * Contribuciones aplicables a `region` para el documento `ctx`, ordenadas por
59
+ * prioridad (desc) y luego id. Pura: no lee React; `useDocumentContributions`
60
+ * la envuelve con useInstalledAddons.
61
+ */
62
+ export declare function resolveContributions(ctx: DocumentEditorContext, region: DocumentRegion, isInstalled: IsAddonInstalled): ResolvedDocumentContribution<any>[];
63
+ export interface FederatedModal<P = any> {
64
+ /** Clave `<addon>.<nombre>`, la misma que `modal` en el manifest. */
65
+ key: string;
66
+ addon: string;
67
+ /** El dispatcher le pasa ActionModalProps (open, onOpenChange, action, model, record…). */
68
+ load: () => Promise<{
69
+ default: ComponentType<P>;
70
+ }>;
71
+ /** Presente cuando el modal se registró ya cargado (`api.registry.registerModal`). */
72
+ component?: ComponentType<P>;
73
+ }
74
+ /**
75
+ * @deprecated Usa `registerModalComponent({ slug, load, owner })` de
76
+ * `@asteby/metacore-sdk` o `api.registry.registerModal` desde el plugin. Se
77
+ * mantiene como alias compatible: escribe en el mismo store.
78
+ */
79
+ export declare function registerFederatedModal(m: FederatedModal): () => void;
80
+ /**
81
+ * Modal del slug en el store canónico, o null si no está registrado o su addon
82
+ * dueño no está instalado. Nunca espera.
83
+ */
84
+ export declare function resolveFederatedModal(key: string, isInstalled: IsAddonInstalled): FederatedModal | null;
85
+ /**
86
+ * Componente del modal resuelto: el ya cargado si lo hay; si no, un
87
+ * `React.lazy` memoizado por registro (no se re-crea en cada render; quien lo
88
+ * pinta lo envuelve en `<Suspense>`).
89
+ */
90
+ export declare function federatedModalComponent<P = any>(m: FederatedModal<P>): ComponentType<P>;
91
+ /** Solo para tests. */
92
+ export declare function __resetContributions(): void;
93
+ //# sourceMappingURL=contributions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contributions.d.ts","sourceRoot":"","sources":["../../src/primitives/contributions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,163 @@
1
+ // Registro de contribuciones federadas — cómo un addon mete secciones,
2
+ // columnas, modales, fuentes de «Cargar desde» y vistas previas en la pantalla
3
+ // de OTRO addon sin que ese addon lo conozca.
4
+ //
5
+ // Se monta sobre slotRegistry (slot.tsx) — no es un segundo sistema: cada
6
+ // contribución es un SlotEntry con id canónico `doc:<kind>:<region>` — y añade
7
+ // lo que slot no tiene: `requiresAddon` (se oculta solo si el addon no está
8
+ // instalado o se desinstala en caliente), orden estable, `when` por documento y
9
+ // carga perezosa del componente (import() del remote federado).
10
+ //
11
+ // Contrato estable para remotes federados: los tipos de este archivo. Todo
12
+ // `register*` devuelve un disposer; el AddonLoader lo llama al desmontar o
13
+ // recargar el remote y la contribución desaparece sin recargar la página.
14
+ import { createElement, lazy, Suspense } from 'react';
15
+ import { getModalComponent, registerModalComponent, subscribeActionComponents, } from '@asteby/metacore-sdk';
16
+ import { slotRegistry } from '../slot';
17
+ const contribs = new Map();
18
+ const listeners = new Set();
19
+ let version = 0;
20
+ function notify() {
21
+ version += 1;
22
+ for (const l of [...listeners])
23
+ l();
24
+ }
25
+ // Los modales viven en el store canónico del SDK (action-registry): cualquier
26
+ // cambio ahí (Registry.registerModal, registerModalComponent, unbind) también
27
+ // re-pinta a quien escucha contribuciones.
28
+ subscribeActionComponents(notify);
29
+ /** Suscripción para useSyncExternalStore (contribuciones y modales). */
30
+ export function subscribeContributions(listener) {
31
+ listeners.add(listener);
32
+ return () => {
33
+ listeners.delete(listener);
34
+ };
35
+ }
36
+ /** Versión monotónica: cambia en cada register/dispose. */
37
+ export function contributionsVersion() {
38
+ return version;
39
+ }
40
+ export function slotIdFor(kind, region) {
41
+ return `doc:${kind}:${region}`;
42
+ }
43
+ /** `load` → componente lazy envuelto en Suspense (fallback vacío: sin saltos de layout). */
44
+ function lazyComponent(load) {
45
+ const Lazy = lazy(load);
46
+ return (props) => createElement(Suspense, { fallback: null }, createElement(Lazy, props));
47
+ }
48
+ /**
49
+ * Registra la contribución y la publica en slotRegistry bajo cada `doc:<kind>:<region>`
50
+ * (kinds '*' → `doc:*:<region>`). Devuelve el disposer: AddonLoader lo llama al
51
+ * desmontar o recargar el remote (fiber), así la sección desaparece al desinstalar.
52
+ * Re-registrar el mismo `id` (recarga en caliente) reemplaza la anterior.
53
+ */
54
+ export function registerDocumentContribution(c, source) {
55
+ if (!c.component && !c.load)
56
+ throw new Error(`contribution ${c.id}: component o load es obligatorio`);
57
+ contribs.get(c.id)?.dispose();
58
+ const resolved = { ...c, component: c.component ?? lazyComponent(c.load) };
59
+ const kinds = c.kinds === '*' ? ['*'] : c.kinds;
60
+ const slotDisposers = kinds.map(k => slotRegistry.register(slotIdFor(k, c.region), resolved.component, {
61
+ priority: c.priority ?? 0,
62
+ source: source ?? c.requiresAddon ?? c.id,
63
+ owner: c.requiresAddon,
64
+ }));
65
+ let disposed = false;
66
+ const entry = {
67
+ c: resolved,
68
+ dispose: () => {
69
+ if (disposed)
70
+ return;
71
+ disposed = true;
72
+ slotDisposers.forEach(d => d());
73
+ if (contribs.get(c.id) === entry)
74
+ contribs.delete(c.id);
75
+ notify();
76
+ },
77
+ };
78
+ contribs.set(c.id, entry);
79
+ notify();
80
+ return entry.dispose;
81
+ }
82
+ /**
83
+ * Contribuciones aplicables a `region` para el documento `ctx`, ordenadas por
84
+ * prioridad (desc) y luego id. Pura: no lee React; `useDocumentContributions`
85
+ * la envuelve con useInstalledAddons.
86
+ */
87
+ export function resolveContributions(ctx, region, isInstalled) {
88
+ return [...contribs.values()]
89
+ .map(e => e.c)
90
+ .filter(c => c.region === region)
91
+ .filter(c => c.kinds === '*' || c.kinds.includes(ctx.kind))
92
+ .filter(c => !c.requiresAddon || isInstalled(c.requiresAddon))
93
+ .filter(c => !c.when || c.when(ctx))
94
+ .sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0) || a.id.localeCompare(b.id));
95
+ }
96
+ /** Vista FederatedModal de cada entrada del store (identidad estable por entrada). */
97
+ const views = new WeakMap();
98
+ const modalComponents = new WeakMap();
99
+ /** Disposers de lo registrado por este alias (sólo __resetContributions los usa). */
100
+ const aliasDisposers = new Set();
101
+ /**
102
+ * @deprecated Usa `registerModalComponent({ slug, load, owner })` de
103
+ * `@asteby/metacore-sdk` o `api.registry.registerModal` desde el plugin. Se
104
+ * mantiene como alias compatible: escribe en el mismo store.
105
+ */
106
+ export function registerFederatedModal(m) {
107
+ const dispose = registerModalComponent({ slug: m.key, owner: m.addon, load: m.load, component: m.component });
108
+ const stored = getModalComponent(m.key);
109
+ if (stored)
110
+ views.set(stored, m);
111
+ aliasDisposers.add(dispose);
112
+ return () => {
113
+ aliasDisposers.delete(dispose);
114
+ dispose();
115
+ };
116
+ }
117
+ function viewOf(e) {
118
+ let v = views.get(e);
119
+ if (!v) {
120
+ const component = e.component;
121
+ v = {
122
+ key: e.slug,
123
+ addon: e.owner ?? '',
124
+ load: e.load ?? (async () => ({ default: component })),
125
+ component,
126
+ };
127
+ views.set(e, v);
128
+ }
129
+ return v;
130
+ }
131
+ /**
132
+ * Modal del slug en el store canónico, o null si no está registrado o su addon
133
+ * dueño no está instalado. Nunca espera.
134
+ */
135
+ export function resolveFederatedModal(key, isInstalled) {
136
+ const m = getModalComponent(key);
137
+ return m && (!m.owner || isInstalled(m.owner)) ? viewOf(m) : null;
138
+ }
139
+ /**
140
+ * Componente del modal resuelto: el ya cargado si lo hay; si no, un
141
+ * `React.lazy` memoizado por registro (no se re-crea en cada render; quien lo
142
+ * pinta lo envuelve en `<Suspense>`).
143
+ */
144
+ export function federatedModalComponent(m) {
145
+ if (m.component)
146
+ return m.component;
147
+ let C = modalComponents.get(m);
148
+ if (!C) {
149
+ C = lazy(m.load);
150
+ modalComponents.set(m, C);
151
+ }
152
+ return C;
153
+ }
154
+ /** Solo para tests. */
155
+ export function __resetContributions() {
156
+ for (const e of [...contribs.values()])
157
+ e.dispose();
158
+ contribs.clear();
159
+ for (const d of [...aliasDisposers])
160
+ d();
161
+ aliasDisposers.clear();
162
+ notify();
163
+ }
@@ -0,0 +1,25 @@
1
+ import type { DocumentFormPreview, DocumentFormSource } from '../types';
2
+ export type PartyRole = 'customer' | 'supplier' | 'none';
3
+ export type LinesMode = 'sale' | 'purchase' | 'credit' | 'allocation' | 'workorder' | 'none';
4
+ export interface DocumentKind {
5
+ key: string;
6
+ label: string;
7
+ model: string;
8
+ party: PartyRole;
9
+ partyModel?: string;
10
+ lines: LinesMode;
11
+ /** Campo de renglones del modelo (one_to_many) o del payload de la acción. */
12
+ linesField?: string;
13
+ sources?: DocumentFormSource[];
14
+ preview?: DocumentFormPreview;
15
+ /** Estados del documento (StageMachine): los muestra StageStepper. */
16
+ stages?: string[];
17
+ /** Addon dueño del tipo (si no está, el tipo no se ofrece). */
18
+ addon: string;
19
+ }
20
+ export declare function registerDocumentKind(k: DocumentKind): () => void;
21
+ export declare function getDocumentKind(key: string): DocumentKind | undefined;
22
+ export declare function listDocumentKinds(isInstalled: (addon: string) => boolean): DocumentKind[];
23
+ /** Tipos base que el core ya tiene (modelos verificados en addons main 9cd69bde). */
24
+ export declare const CORE_DOCUMENT_KINDS: DocumentKind[];
25
+ //# sourceMappingURL=document-kinds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-kinds.d.ts","sourceRoot":"","sources":["../../src/primitives/document-kinds.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAEvE,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAA;AACxD,MAAM,MAAM,SAAS,GACf,MAAM,GACN,UAAU,GACV,QAAQ,GACR,YAAY,GACZ,WAAW,GACX,MAAM,CAAA;AAEZ,MAAM,WAAW,YAAY;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,SAAS,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,SAAS,CAAA;IAChB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,kBAAkB,EAAE,CAAA;IAC9B,OAAO,CAAC,EAAE,mBAAmB,CAAA;IAC7B,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAA;CAChB;AAID,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,IAAI,CAKhE;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAErE;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,YAAY,EAAE,CAEzF;AAED,qFAAqF;AACrF,eAAO,MAAM,mBAAmB,EAAE,YAAY,EAU7C,CAAA"}
@@ -0,0 +1,26 @@
1
+ const kinds = new Map();
2
+ export function registerDocumentKind(k) {
3
+ kinds.set(k.key, k);
4
+ return () => {
5
+ if (kinds.get(k.key) === k)
6
+ kinds.delete(k.key);
7
+ };
8
+ }
9
+ export function getDocumentKind(key) {
10
+ return kinds.get(key);
11
+ }
12
+ export function listDocumentKinds(isInstalled) {
13
+ return [...kinds.values()].filter(k => isInstalled(k.addon));
14
+ }
15
+ /** Tipos base que el core ya tiene (modelos verificados en addons main 9cd69bde). */
16
+ export const CORE_DOCUMENT_KINDS = [
17
+ { key: 'invoice', label: 'Factura', model: 'customers.Invoice', party: 'customer', partyModel: 'customers.Customer', lines: 'sale', linesField: 'items', addon: 'customers' },
18
+ { key: 'quote', label: 'Cotización', model: 'quotes.Quote', party: 'customer', partyModel: 'customers.Customer', lines: 'sale', linesField: 'items', addon: 'quotes' },
19
+ { key: 'sales_order', label: 'Pedido', model: 'customers.SalesOrder', party: 'customer', partyModel: 'customers.Customer', lines: 'sale', linesField: 'items', addon: 'customers' },
20
+ { key: 'payment', label: 'Cobro', model: 'customers.Payment', party: 'customer', partyModel: 'customers.Customer', lines: 'allocation', linesField: 'allocations', addon: 'customers' },
21
+ { key: 'credit_note', label: 'Nota de crédito', model: 'fiscal_mexico.CreditNote', party: 'customer', partyModel: 'customers.Customer', lines: 'credit', linesField: 'lines', addon: 'fiscal_mexico' },
22
+ { key: 'purchase_order', label: 'Orden de compra', model: 'purchases.PurchaseOrder', party: 'supplier', partyModel: 'purchases.Supplier', lines: 'purchase', linesField: 'items', addon: 'purchases' },
23
+ { key: 'supplier_invoice', label: 'Factura de proveedor', model: 'purchases.SupplierInvoice', party: 'supplier', partyModel: 'purchases.Supplier', lines: 'purchase', addon: 'purchases' },
24
+ { key: 'supplier_payment', label: 'Pago a proveedor', model: 'purchases.PurchasePayment', party: 'supplier', partyModel: 'purchases.Supplier', lines: 'allocation', addon: 'purchases' },
25
+ { key: 'work_order', label: 'Orden de trabajo', model: 'workshop.WorkOrder', party: 'customer', partyModel: 'customers.Customer', lines: 'workorder', linesField: 'items', addon: 'workshop' },
26
+ ];
@@ -0,0 +1,8 @@
1
+ export * from './allocation';
2
+ export * from './aging';
3
+ export * from './approvals';
4
+ export * from './contributions';
5
+ export * from './document-kinds';
6
+ export * from './panels';
7
+ export * from './use-contributions';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/primitives/index.ts"],"names":[],"mappings":"AAgBA,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,kBAAkB,CAAA;AAChC,cAAc,UAAU,CAAA;AACxB,cAAc,qBAAqB,CAAA"}
@@ -0,0 +1,23 @@
1
+ // Primitivos de negocio — contrato estable para hosts y remotes federados.
2
+ // Las funciones son puras (sin React ni host) salvo use-contributions; la UI y
3
+ // el backend (wasm) pueden validar con las mismas reglas.
4
+ //
5
+ // allocation PaymentAllocator: allocatePayment / validateAllocation
6
+ // (cobro multi-factura, REP, CxC «Registrar abono», CxP «Aplicar pago»).
7
+ // aging AgingTable: computeAging / daysOverdue / bucketFor (antigüedad de saldos).
8
+ // approvals ApprovalInbox: approvalsClient (rutas /approvals del kernel),
9
+ // registerApprovalCategory / groupApprovals.
10
+ // contributions Secciones y modales federados: registerDocumentContribution,
11
+ // resolveContributions (pura), registerFederatedModal /
12
+ // resolveFederatedModal. Todo register* devuelve su disposer.
13
+ // document-kinds registerDocumentKind / listDocumentKinds / CORE_DOCUMENT_KINDS.
14
+ // panels Tipos de PartyCard / TotalsPanel / PreviewPanel /
15
+ // ValidationChecklist / LoadFromDocument + blockingChecks.
16
+ // use-contributions Hooks: useDocumentContributions, useIsAddonInstalled.
17
+ export * from './allocation';
18
+ export * from './aging';
19
+ export * from './approvals';
20
+ export * from './contributions';
21
+ export * from './document-kinds';
22
+ export * from './panels';
23
+ export * from './use-contributions';
@@ -0,0 +1,68 @@
1
+ import type { LineItemsTotals } from '../business/line-items';
2
+ /** PartyCard: lo que se muestra de la contraparte. */
3
+ export interface PartySummary {
4
+ id: string;
5
+ name: string;
6
+ tax_id?: string;
7
+ email?: string;
8
+ phone?: string;
9
+ currency?: string;
10
+ /** Filas extra (incluye las de extensiones `fiscal_data.*`). */
11
+ rows: Array<{
12
+ key: string;
13
+ label: string;
14
+ value: string;
15
+ tone?: 'ok' | 'warn' | 'err';
16
+ }>;
17
+ credit?: {
18
+ limit: number;
19
+ balance: number;
20
+ available: number;
21
+ overdue: number;
22
+ status?: 'active' | 'suspended' | 'delinquent';
23
+ hold_reason?: string;
24
+ };
25
+ }
26
+ /** TotalsPanel: desglose; los impuestos por tasa los aporta el addon fiscal o el core. */
27
+ export interface TotalsBreakdown extends LineItemsTotals {
28
+ taxes: Array<{
29
+ label: string;
30
+ base: number;
31
+ amount: number;
32
+ }>;
33
+ /** Renglones extra (aplicaciones, saldo a favor, sin aplicar…). */
34
+ extra?: Array<{
35
+ label: string;
36
+ amount: number;
37
+ emphasis?: boolean;
38
+ }>;
39
+ amount_in_words?: string;
40
+ }
41
+ /** PreviewPanel: resultado de la acción de vista previa (`analyze: true`, no escribe). */
42
+ export interface PreviewResult {
43
+ pdf_base64?: string;
44
+ xml?: string;
45
+ html?: string;
46
+ checks: ValidationCheck[];
47
+ }
48
+ /** ValidationChecklist: una revisión (local o del servidor). */
49
+ export interface ValidationCheck {
50
+ key: string;
51
+ severity: 'ok' | 'warning' | 'error';
52
+ message: string;
53
+ /** Campo a enfocar al hacer clic. */
54
+ field?: string;
55
+ /** Si es error y `approval_policy` está, el botón ofrece «Pedir autorización». */
56
+ approval_policy?: string;
57
+ }
58
+ /** LoadFromDocument: candidato a cargar (venta, cotización, ticket, OT, factura origen de NC). */
59
+ export interface LoadCandidate {
60
+ source: string;
61
+ id: string;
62
+ label: string;
63
+ party_id?: string;
64
+ total?: number;
65
+ state?: string;
66
+ }
67
+ export declare function blockingChecks(checks: readonly ValidationCheck[]): ValidationCheck[];
68
+ //# sourceMappingURL=panels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panels.d.ts","sourceRoot":"","sources":["../../src/primitives/panels.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAE7D,sDAAsD;AACtD,MAAM,WAAW,YAAY;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gEAAgE;IAChE,IAAI,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,KAAK,CAAA;KAAE,CAAC,CAAA;IACxF,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,QAAQ,GAAG,WAAW,GAAG,YAAY,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CACxJ;AAED,0FAA0F;AAC1F,MAAM,WAAW,eAAgB,SAAQ,eAAe;IACpD,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC7D,mEAAmE;IACnE,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IACpE,eAAe,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,0FAA0F;AAC1F,MAAM,WAAW,aAAa;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,eAAe,EAAE,CAAA;CAC5B;AAED,gEAAgE;AAChE,MAAM,WAAW,eAAe;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,IAAI,GAAG,SAAS,GAAG,OAAO,CAAA;IACpC,OAAO,EAAE,MAAM,CAAA;IACf,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,kFAAkF;IAClF,eAAe,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,kGAAkG;AAClG,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,GAAG,eAAe,EAAE,CAEpF"}
@@ -0,0 +1,3 @@
1
+ export function blockingChecks(checks) {
2
+ return checks.filter(c => c.severity === 'error' && !c.approval_policy);
3
+ }
@@ -0,0 +1,15 @@
1
+ import { type InstalledAddonsValue } from '../installed-addons-context';
2
+ import { type DocumentEditorContext, type DocumentRegion, type IsAddonInstalled, type ResolvedDocumentContribution } from './contributions';
3
+ /**
4
+ * Predicado de instalación a partir del InstalledAddonsProvider. Sin provider
5
+ * devuelve siempre true: el host solo carga remotes de addons instalados, así
6
+ * que si la contribución existe, su addon está (el servidor revalida igual).
7
+ */
8
+ export declare function installedPredicate(value: InstalledAddonsValue | null): IsAddonInstalled;
9
+ /** `installedPredicate(useInstalledAddons())`, estable entre renders. */
10
+ export declare function useIsAddonInstalled(): IsAddonInstalled;
11
+ /** Re-pinta en cada register/dispose de contribuciones o modales federados. */
12
+ export declare function useContributionsVersion(): number;
13
+ /** Contribuciones aplicables a `region` del documento `ctx` (ya filtradas y ordenadas). */
14
+ export declare function useDocumentContributions(ctx: DocumentEditorContext, region: DocumentRegion): ResolvedDocumentContribution<any>[];
15
+ //# sourceMappingURL=use-contributions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-contributions.d.ts","sourceRoot":"","sources":["../../src/primitives/use-contributions.ts"],"names":[],"mappings":"AAKA,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;AAC3F,OAAO,EAIH,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,4BAA4B,EACpC,MAAM,iBAAiB,CAAA;AAExB;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,oBAAoB,GAAG,IAAI,GAAG,gBAAgB,CAEvF;AAED,yEAAyE;AACzE,wBAAgB,mBAAmB,IAAI,gBAAgB,CAGtD;AAED,+EAA+E;AAC/E,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD;AAED,2FAA2F;AAC3F,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,qBAAqB,EAAE,MAAM,EAAE,cAAc,GAAG,4BAA4B,CAAC,GAAG,CAAC,EAAE,CAIhI"}