@asteby/metacore-runtime-react 47.0.0 → 48.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 (178) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/action-modal-dispatcher.d.ts.map +1 -1
  3. package/dist/action-modal-dispatcher.js +24 -4
  4. package/dist/audit-info.d.ts +54 -0
  5. package/dist/audit-info.d.ts.map +1 -0
  6. package/dist/audit-info.js +142 -0
  7. package/dist/business/catalog-product-search.d.ts +25 -0
  8. package/dist/business/catalog-product-search.d.ts.map +1 -0
  9. package/dist/business/catalog-product-search.js +57 -0
  10. package/dist/business/catalog.d.ts.map +1 -1
  11. package/dist/business/catalog.js +2 -0
  12. package/dist/business/document-editor-model.d.ts +107 -0
  13. package/dist/business/document-editor-model.d.ts.map +1 -0
  14. package/dist/business/document-editor-model.js +279 -0
  15. package/dist/business/document-editor-panels.d.ts +64 -0
  16. package/dist/business/document-editor-panels.d.ts.map +1 -0
  17. package/dist/business/document-editor-panels.js +58 -0
  18. package/dist/business/document-editor.d.ts +27 -0
  19. package/dist/business/document-editor.d.ts.map +1 -0
  20. package/dist/business/document-editor.js +445 -0
  21. package/dist/business/document-lines.d.ts.map +1 -1
  22. package/dist/business/document-lines.js +5 -1
  23. package/dist/business/index.d.ts +5 -0
  24. package/dist/business/index.d.ts.map +1 -1
  25. package/dist/business/index.js +5 -0
  26. package/dist/business/line-items.d.ts +7 -0
  27. package/dist/business/line-items.d.ts.map +1 -1
  28. package/dist/business/line-items.js +6 -1
  29. package/dist/business/payment-allocator.d.ts +19 -0
  30. package/dist/business/payment-allocator.d.ts.map +1 -0
  31. package/dist/business/payment-allocator.js +31 -0
  32. package/dist/business/product-search.d.ts +4 -0
  33. package/dist/business/product-search.d.ts.map +1 -1
  34. package/dist/dialogs/dynamic-record.d.ts.map +1 -1
  35. package/dist/dialogs/dynamic-record.js +19 -8
  36. package/dist/document-form-dialog.d.ts +31 -2
  37. package/dist/document-form-dialog.d.ts.map +1 -1
  38. package/dist/document-form-dialog.js +43 -11
  39. package/dist/dynamic-columns-helpers.d.ts +9 -0
  40. package/dist/dynamic-columns-helpers.d.ts.map +1 -1
  41. package/dist/dynamic-columns-helpers.js +19 -0
  42. package/dist/dynamic-columns.d.ts +8 -2
  43. package/dist/dynamic-columns.d.ts.map +1 -1
  44. package/dist/dynamic-columns.js +21 -5
  45. package/dist/dynamic-crud-page.d.ts +6 -0
  46. package/dist/dynamic-crud-page.d.ts.map +1 -1
  47. package/dist/dynamic-crud-page.js +14 -5
  48. package/dist/dynamic-form-schema.d.ts.map +1 -1
  49. package/dist/dynamic-form-schema.js +15 -3
  50. package/dist/dynamic-form.d.ts +16 -1
  51. package/dist/dynamic-form.d.ts.map +1 -1
  52. package/dist/dynamic-form.js +81 -20
  53. package/dist/dynamic-kanban.d.ts +9 -3
  54. package/dist/dynamic-kanban.d.ts.map +1 -1
  55. package/dist/dynamic-kanban.js +39 -13
  56. package/dist/dynamic-line-items.d.ts.map +1 -1
  57. package/dist/dynamic-line-items.js +3 -0
  58. package/dist/dynamic-multi-select-field.d.ts.map +1 -1
  59. package/dist/dynamic-multi-select-field.js +2 -0
  60. package/dist/dynamic-relation-helpers.d.ts.map +1 -1
  61. package/dist/dynamic-relation-helpers.js +8 -0
  62. package/dist/dynamic-relation.js +1 -1
  63. package/dist/dynamic-select-field.d.ts.map +1 -1
  64. package/dist/dynamic-select-field.js +4 -0
  65. package/dist/index.d.ts +8 -2
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +17 -2
  68. package/dist/installed-addons-context.d.ts +15 -0
  69. package/dist/installed-addons-context.d.ts.map +1 -0
  70. package/dist/installed-addons-context.js +39 -0
  71. package/dist/option-filter.d.ts +31 -0
  72. package/dist/option-filter.d.ts.map +1 -0
  73. package/dist/option-filter.js +49 -0
  74. package/dist/org-runtime-context.d.ts +8 -0
  75. package/dist/org-runtime-context.d.ts.map +1 -1
  76. package/dist/org-runtime-context.js +8 -0
  77. package/dist/org-runtime-provider.d.ts +3 -1
  78. package/dist/org-runtime-provider.d.ts.map +1 -1
  79. package/dist/org-runtime-provider.js +3 -3
  80. package/dist/primitives/aging.d.ts +34 -0
  81. package/dist/primitives/aging.d.ts.map +1 -0
  82. package/dist/primitives/aging.js +48 -0
  83. package/dist/primitives/allocation.d.ts +42 -0
  84. package/dist/primitives/allocation.d.ts.map +1 -0
  85. package/dist/primitives/allocation.js +59 -0
  86. package/dist/primitives/approvals.d.ts +71 -0
  87. package/dist/primitives/approvals.d.ts.map +1 -0
  88. package/dist/primitives/approvals.js +48 -0
  89. package/dist/primitives/contributions.d.ts +82 -0
  90. package/dist/primitives/contributions.d.ts.map +1 -0
  91. package/dist/primitives/contributions.js +126 -0
  92. package/dist/primitives/document-kinds.d.ts +25 -0
  93. package/dist/primitives/document-kinds.d.ts.map +1 -0
  94. package/dist/primitives/document-kinds.js +26 -0
  95. package/dist/primitives/index.d.ts +8 -0
  96. package/dist/primitives/index.d.ts.map +1 -0
  97. package/dist/primitives/index.js +23 -0
  98. package/dist/primitives/panels.d.ts +68 -0
  99. package/dist/primitives/panels.d.ts.map +1 -0
  100. package/dist/primitives/panels.js +3 -0
  101. package/dist/primitives/use-contributions.d.ts +15 -0
  102. package/dist/primitives/use-contributions.d.ts.map +1 -0
  103. package/dist/primitives/use-contributions.js +30 -0
  104. package/dist/record-prefill-bar.d.ts +11 -0
  105. package/dist/record-prefill-bar.d.ts.map +1 -0
  106. package/dist/record-prefill-bar.js +33 -0
  107. package/dist/types.d.ts +142 -0
  108. package/dist/types.d.ts.map +1 -1
  109. package/dist/use-options-resolver.d.ts +8 -0
  110. package/dist/use-options-resolver.d.ts.map +1 -1
  111. package/dist/use-options-resolver.js +8 -2
  112. package/package.json +5 -5
  113. package/src/__tests__/action-modal-grid-layout.test.tsx +1 -1
  114. package/src/__tests__/apply-sidebar-layout.test.ts +1 -1
  115. package/src/__tests__/audit-info.test.tsx +103 -0
  116. package/src/__tests__/catalog-product-search.test.ts +61 -0
  117. package/src/__tests__/dependent-options.test.tsx +12 -5
  118. package/src/__tests__/document-editor-model.test.ts +167 -0
  119. package/src/__tests__/document-editor.test.tsx +261 -0
  120. package/src/__tests__/document-form-dialog.test.tsx +118 -3
  121. package/src/__tests__/dynamic-form-select-edit.test.tsx +155 -0
  122. package/src/__tests__/dynamic-kanban.test.tsx +24 -3
  123. package/src/__tests__/dynamic-record-extension-label.test.tsx +88 -0
  124. package/src/__tests__/dynamic-table-infinite-scroll.test.tsx +4 -0
  125. package/src/__tests__/installed-addons-context.test.tsx +50 -0
  126. package/src/__tests__/line-item-totals.test.ts +28 -0
  127. package/src/__tests__/line-items-lock-rows.test.tsx +2 -1
  128. package/src/__tests__/option-filter.test.tsx +115 -0
  129. package/src/__tests__/primitives-federated.test.tsx +156 -0
  130. package/src/__tests__/primitives.test.ts +224 -0
  131. package/src/__tests__/record-prefill-bar.test.tsx +50 -0
  132. package/src/__tests__/structured-view-value.test.tsx +5 -1
  133. package/src/__tests__/wizard-action-modal.test.tsx +1 -1
  134. package/src/action-modal-dispatcher.tsx +47 -4
  135. package/src/audit-info.tsx +219 -0
  136. package/src/business/catalog-product-search.ts +87 -0
  137. package/src/business/catalog.ts +2 -0
  138. package/src/business/document-editor-model.ts +325 -0
  139. package/src/business/document-editor-panels.tsx +164 -0
  140. package/src/business/document-editor.tsx +727 -0
  141. package/src/business/document-lines.ts +5 -1
  142. package/src/business/index.ts +5 -0
  143. package/src/business/line-items.ts +15 -1
  144. package/src/business/payment-allocator.tsx +110 -0
  145. package/src/business/product-search.ts +4 -0
  146. package/src/dialogs/dynamic-record.tsx +42 -6
  147. package/src/document-form-dialog.tsx +92 -12
  148. package/src/dropdown-menu-entry.test.ts +23 -0
  149. package/src/dynamic-columns-helpers.ts +22 -0
  150. package/src/dynamic-columns.tsx +23 -6
  151. package/src/dynamic-crud-page.tsx +22 -3
  152. package/src/dynamic-form-schema.ts +15 -2
  153. package/src/dynamic-form.tsx +120 -39
  154. package/src/dynamic-kanban.tsx +41 -10
  155. package/src/dynamic-line-items.tsx +3 -0
  156. package/src/dynamic-multi-select-field.tsx +2 -0
  157. package/src/dynamic-relation-helpers.ts +9 -0
  158. package/src/dynamic-relation.tsx +7 -2
  159. package/src/dynamic-select-field.tsx +4 -0
  160. package/src/index.ts +34 -1
  161. package/src/installed-addons-context.tsx +58 -0
  162. package/src/option-filter.ts +88 -0
  163. package/src/org-runtime-context.ts +10 -0
  164. package/src/org-runtime-provider.tsx +7 -2
  165. package/src/primitives/aging.ts +76 -0
  166. package/src/primitives/allocation.ts +104 -0
  167. package/src/primitives/approvals.ts +103 -0
  168. package/src/primitives/contributions.ts +208 -0
  169. package/src/primitives/document-kinds.ts +62 -0
  170. package/src/primitives/index.ts +23 -0
  171. package/src/primitives/panels.ts +59 -0
  172. package/src/primitives/use-contributions.ts +42 -0
  173. package/src/record-prefill-bar.tsx +62 -0
  174. package/src/row-action-stage-gate.test.ts +33 -0
  175. package/src/types.ts +142 -0
  176. package/src/use-options-resolver.ts +19 -1
  177. package/vitest.config.ts +1 -0
  178. package/vitest.setup.ts +7 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # @asteby/metacore-runtime-react
2
2
 
3
+ ## 48.0.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 7532829: Primitivos de negocio para hosts y addons federados:
8
+
9
+ - `InstalledAddonsProvider` + `useAddonInstalled` / `useCapabilityProvided` / `useInstalledAddons`: una sola respuesta a «¿qué hay instalado?» (sin provider devuelven `undefined`, nunca «ausente»).
10
+ - `allocatePayment` / `validateAllocation` (PaymentAllocator) y `computeAging` / `bucketFor` (AgingTable) como funciones puras.
11
+ - Bandeja de aprobaciones: `approvalsClient`, `registerApprovalCategory`, `groupApprovals` y el tipo `ApprovalRequestDTO`.
12
+ - Contribuciones federadas por documento: `registerDocumentContribution` (con disposer y carga perezosa), `resolveContributions` (pura) y el hook `useDocumentContributions`.
13
+ - Modales federados: `registerFederatedModal` / `resolveFederatedModal`. `ActionModalDispatcher` pinta el modal registrado sin esperar al host y, si el addon del `modal` no está instalado, abre el formulario genérico de la acción de inmediato en lugar de esperar 20 s.
14
+ - Tipos de documento (`registerDocumentKind`, `CORE_DOCUMENT_KINDS`) y contratos de PartyCard / TotalsPanel / PreviewPanel / ValidationChecklist.
15
+ - Manifest `document_forms`: `lines.kind`, `lines.open_documents`, `sources`, `preview` y `submit_action` en los tipos.
16
+ - Ayudantes de captura por modelo: `registerRecordPrefill` (también en `registry.scope(addon)`, se retira en el `unbind`) y `RecordPrefillBar`, que `DynamicRecordDialog` monta arriba del formulario; solo aplica claves que el formulario declara.
17
+
18
+ - 830349b: `DocumentEditor`: alta de documentos en una sola pantalla por secciones cuando el tipo de `document_forms` declara `layout: "editor"` (factura, nota de crédito, cobro/REP, cotización, OC). A la vista solo lo esencial (contraparte con su tarjeta, «Cargar desde…», renglones o reparto del pago, totales); los campos fiscales (`fiscal_data.*`, catálogos con default, contribuciones `header.fields`) van en «Opciones fiscales», plegada por defecto, y la vista previa también es opcional y plegada. Una sola acción primaria. Prefill desde el documento origen (`sources`, `initialSource`, `default_from_record` vía la nueva prop `record`) y validación inline. `lines.kind: "allocation"` reparte el cobro con `PaymentAllocator` (del más vencido o manual, con parcialidad) y guarda por `submit_action`. Nuevos exports: `DocumentEditor`, `PaymentAllocator`, `PartyCard`, `TotalsPanel`, `ValidationChecklist`, `PreviewPanel`, `CollapsibleSection`, `EditorSection` y las reglas puras de `document-editor-model`. Sin `layout: "editor"` el wizard sigue igual.
19
+ - 7d1bdf9: Alta guiada acotada a la vista filtrada: `scopeDocumentFormsToFilter(forms, filter)` y la prop `filter` de `DynamicCRUDPage`. Si el filtro fijo de la vista (el `filter` del nav) va sobre el `type_field` de `document_forms`, «Crear» abre ese tipo directo; si no hay formulario para ese tipo, no se ofrece. Antes «Documentos fiscales → Facturas → Crear» abría el formulario del REP.
20
+ - 9c34b09: Crear factura (DocumentFormDialog): el paso de renglones busca en el catálogo de productos aunque el host no pase `searchProducts`. Antes solo ofrecía «Renglón libre» y la factura se guardaba en $0. Al elegir un producto, el renglón entra con precio de venta, unidad, SKU, claves de extensión (`fiscal_data`), cantidad 1 y la tasa de impuesto del producto o, si no trae, la de la organización (`OrgRuntimeProvider taxRate`, nuevo). Nuevos: `createCatalogProductSearch`, props `productModel`/`defaultTaxRate` y `lines.discount_mode` en `document_forms`. Una cantidad vacía al leer renglones ya no queda en 0.
21
+
22
+ ### Patch Changes
23
+
24
+ - 3c6e8eb: Corrige que al editar un registro (p. ej. un renglón dentro de una relación) un campo select con valor se borrara al guardar sin tocarlo. `DynamicForm` siembra los valores del registro de forma síncrona, ignora el `onValueChange('')` espurio del Select cuando ya hay valor y `''` no es opción válida, y en edición no manda selects vacíos que el usuario no tocó. `deriveRelationFormFields` ahora copia `readonly` de la columna, y el modal de alta/edición de renglón limita su alto al viewport con cuerpo con scroll y botones fijos abajo.
25
+ - 153dff8: El formulario de alta/edición (`DynamicRecordDialog`) ya no muestra claves de catálogo crudas como etiqueta de campo (p. ej. `FISCAL_MEXICO.EXT.CUSTOMER.RFC_RECEPTOR` en los campos que un addon agrega por extensión a Clientes o Productos). Si la etiqueta llega del servidor sin resolver, se traduce con el catálogo del cliente y, si tampoco está ahí, se muestra legible (`Codigo Postal`). Las etiquetas que ya llegan en texto humano no se tocan. Nuevo helper exportado `localizeFieldLabel`.
26
+ - db934ed: Kanban: los carriles de etapas ya no se pintan sobre otra columna. El host sirve un solo `group_by` por modelo, el del ÚLTIMO tablero de la navegación (en Taller, `service_bay_id`). Un tablero abierto sin `?group_by=` (por ejemplo, con el cambio de vista desde la lista) agrupaba las etapas por la bahía: todas las OT caían en «Sin etapa», los conteos de cada carril pedían `f_service_bay_id=diagnosis` (422) y al soltar una tarjeta se enviaba `service_bay_id = 'diagnosis'` (422 «no es un UUID válido»). `withGroupBy` ahora agrupa por el `stage_field` cuando no hay `?group_by=`. El tablero de la columna servida conserva la máquina de etapas solo si esa columna es la de etapas.
27
+ - 4ed3225: Renglones de formularios de acción: cuando la columna `total` es el precio unitario (`price`, `unit_price`…) y el renglón tiene cantidad pero ninguna columna de importe, la fila de total suma cantidad × precio − descuento de cada renglón y ya no los precios sueltos. En la OT de taller, cantidad 2 × 750 mostraba 750 en el total, aunque al guardar quedaba bien (1500). Si hay columna de importe (`subtotal`, `line_total`…), el comportamiento no cambia.
28
+ - f2e31cf: Las acciones por fila con `requiresState` se filtran contra el `stage_field` del modelo cuando hay máquina de etapas, igual que el kernel. Antes se comparaban con `status`, y por eso la OT de taller no mostraba «Procesar», «Esperar refacciones» ni «Cancelar» en la tabla ni en el kanban. Nuevo export `lifecycleStageField(metadata)`.
29
+ - Updated dependencies [7532829]
30
+ - @asteby/metacore-sdk@3.13.0
31
+
32
+ ## 47.1.0
33
+
34
+ ### Minor Changes
35
+
36
+ - 1614e02: `TableMetadata.audit` (columnas de auditoría que sirve el kernel >= v0.188) y nuevo `<AuditInfo>`: «Creado por X · fecha», «Modificado por Y · fecha» y «Eliminado por Z · fecha», colapsado al pie del detalle/edición de `DynamicRecordDialog`. El nombre sale del host (`resolveActor`), del hermano expandido del registro (`created_by`) o, si no, un id corto; el actor de sistema se muestra como «Sistema».
37
+ - 0ee83dd: Filtro de opciones en selectores de relación: el campo (columna o campo de acción) acepta `option_filter` / `optionFilter` (una regla o lista con `field` y `equals` / `not_equals` / `in` / `not_in`) para ocultar opciones, p. ej. facturas con status `cancelada` en el selector de abonos (PIT-059). Se aplica en cliente sobre las columnas extra que devuelve `/options`, nunca oculta la selección actual y es retrocompatible (sin la propiedad no cambia nada). Nuevos exports: `getOptionFilter`, `applyOptionFilter`, `optionPassesRule`, tipos `OptionFilter` y `OptionFilterRule`; `useOptionsResolver` acepta `optionFilter` y `keepValue`.
38
+
39
+ ### Patch Changes
40
+
41
+ - ad1e1b0: Kanban: el menú «…» de la tarjeta ya no tira la app con «`MenuItem` must be used within `Menu`». El `DropdownMenu` de la tarjeta se importa ahora desde la entrada raíz de `@asteby/metacore-ui`, la misma de `RowActionMenuItem`. Así no se mezclan dos contextos de Radix cuando el host comparte la raíz por Module Federation y empaqueta `/primitives` aparte. Se agrega un test que lo vigila.
42
+ - Updated dependencies [66d82f5]
43
+ - @asteby/metacore-ui@2.24.1
44
+
3
45
  ## 47.0.0
4
46
 
5
47
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"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"}
@@ -16,7 +16,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
16
16
  // The host injects its axios-like client via <ApiProvider>; we no longer
17
17
  // depend on a bundler alias to `@/lib/api`.
18
18
  import { emitRecordMutation } from './record-mutation-events';
19
- import { useState, useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';
19
+ import { Suspense, useState, useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';
20
20
  import { useTranslation } from 'react-i18next';
21
21
  import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Button, Input, Textarea, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Switch, } from '@asteby/metacore-ui/primitives';
22
22
  import { Loader2 } from 'lucide-react';
@@ -46,6 +46,9 @@ import { ViewValue, objectLabel, relationSiblingValue, isLineItemsField as isRec
46
46
  // Canonical registry lives in @asteby/metacore-sdk
47
47
  import { getActionComponent, subscribeActionComponents, } from '@asteby/metacore-sdk';
48
48
  import { requestFederatedAction } from './federated-action-loader';
49
+ import { useInstalledAddons } from './installed-addons-context';
50
+ import { federatedModalComponent, resolveFederatedModal } from './primitives/contributions';
51
+ import { installedPredicate, useContributionsVersion } from './primitives/use-contributions';
49
52
  export function isPrefillSpec(v) {
50
53
  return (typeof v === 'object' &&
51
54
  v !== null &&
@@ -248,13 +251,26 @@ export function ActionModalDispatcher({ open, onOpenChange, action, model, recor
248
251
  // render, and a memoized lookup would pin the fallback forever.
249
252
  const readComponent = () => getActionComponent(model, action.key);
250
253
  const CustomComponent = useSyncExternalStore(subscribeActionComponents, readComponent, readComponent);
254
+ // Modal federado declarativo (registerFederatedModal). Si el addon dueño
255
+ // del slug consta como NO instalado, el `modal` se ignora y se usa el
256
+ // formulario genérico de la acción de inmediato (sin esperar el timeout).
257
+ useContributionsVersion();
258
+ const installed = useInstalledAddons();
259
+ const slug = action.modal || '';
260
+ const federatedModal = slug ? resolveFederatedModal(slug, installedPredicate(installed)) : null;
261
+ const modalAddonMissing = !!slug && !federatedModal && !!installed && !installed.addons.has(slug.split('.')[0]);
251
262
  if (CustomComponent) {
252
263
  return (_jsx(CustomComponent, { open: open, onOpenChange: onOpenChange, action: action, model: model, record: record, endpoint: endpoint, onSuccess: onSuccess }));
253
264
  }
265
+ if (federatedModal) {
266
+ const FederatedComponent = federatedModalComponent(federatedModal);
267
+ return (_jsx(Suspense, { fallback: _jsx(LoadingActionDialog, { open: open, onOpenChange: onOpenChange, action: action }), children: _jsx(FederatedComponent, { open: open, onOpenChange: onOpenChange, action: action, model: model, record: record, endpoint: endpoint, onSuccess: onSuccess }) }));
268
+ }
254
269
  // Declarative custom slot (`modal: "addon.action"`). Wait for the remote,
255
270
  // then a hard error — confirm/fields generics look "fine" and hide a
256
- // missing remote.
257
- if (action.modal) {
271
+ // missing remote. Exception: the slug's addon is known NOT installed — the
272
+ // remote will never come, so fall through to the generic form now.
273
+ if (action.modal && !modalAddonMissing) {
258
274
  return (_jsx(PendingCustomActionModal, { open: open, onOpenChange: onOpenChange, action: action, model: model }));
259
275
  }
260
276
  if (action.steps && action.steps.length > 0) {
@@ -288,7 +304,6 @@ const MODAL_SLUG_RE = /^[\w-]+(\.[\w-]+)+$/;
288
304
  * override from another addon that registerAction()s over it.
289
305
  */
290
306
  function PendingCustomActionModal({ open, onOpenChange, action, model, }) {
291
- const { t } = useTranslation();
292
307
  const slug = action.modal || '';
293
308
  const [timedOut, setTimedOut] = useState(false);
294
309
  useEffect(() => {
@@ -329,6 +344,11 @@ function PendingCustomActionModal({ open, onOpenChange, action, model, }) {
329
344
  if (timedOut) {
330
345
  return _jsx(MissingCustomActionModal, { open: open, onOpenChange: onOpenChange, action: action, model: model });
331
346
  }
347
+ return _jsx(LoadingActionDialog, { open: open, onOpenChange: onOpenChange, action: action });
348
+ }
349
+ /** Diálogo «Cargando…» mientras llega el componente federado de la acción. */
350
+ function LoadingActionDialog({ open, onOpenChange, action, }) {
351
+ const { t } = useTranslation();
332
352
  return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: t(action.label, { defaultValue: action.label }) }), _jsx(DialogDescription, { children: t('dynamic.action_modal_loading', { defaultValue: 'Cargando…' }) })] }), _jsx("div", { className: "flex justify-center py-6", "data-testid": "federated-action-loading", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground" }) })] }) }));
333
353
  }
334
354
  /** Shown when the action declares `modal` but no federated component registered. */
@@ -0,0 +1,54 @@
1
+ /**
2
+ * audit-info.tsx
3
+ *
4
+ * <AuditInfo> — discreet "when and who" footer for a record: created, modified
5
+ * and (if tombstoned) deleted, each with the actor and a relative + absolute
6
+ * date. Driven only by the kernel's `TableMetadata.audit` (column names) and
7
+ * the record row — manifests declare nothing.
8
+ *
9
+ * Actor resolution (no extra requests), in order:
10
+ * 1. the host's `resolveActor(id)` when it returns a name;
11
+ * 2. the record's expanded sibling (`created_by_id` → `row.created_by`,
12
+ * `{ label | name | title }` or a plain string);
13
+ * 3. the system actor id → "Sistema" / "System";
14
+ * 4. a short id (first 8 chars) — never a long uuid.
15
+ *
16
+ * Renders nothing when `audit` is absent or the record has none of its values.
17
+ */
18
+ import * as React from 'react';
19
+ import type { AuditMeta } from './types';
20
+ /** The kernel's actor for unattended work (schedules, webhooks, connectors). */
21
+ export declare const SYSTEM_ACTOR_ID = "00000000-0000-0000-0000-000000000001";
22
+ /**
23
+ * Defensive reader for the served `audit` block: keeps only known keys whose
24
+ * value is a non-empty string; returns `undefined` when nothing usable remains.
25
+ */
26
+ export declare function readAuditMeta(raw: unknown): AuditMeta | undefined;
27
+ export interface AuditActor {
28
+ /** Display text. */
29
+ name: string;
30
+ /** True when the name is a real resolved label (not a short id). */
31
+ resolved: boolean;
32
+ }
33
+ /** Resolves the actor stored in `row[idColumn]` to something human. */
34
+ export declare function resolveAuditActor(row: Record<string, unknown>, idColumn: string | undefined, opts: {
35
+ resolveActor?: (id: string) => string | undefined;
36
+ systemLabel: string;
37
+ }): AuditActor | null;
38
+ export interface AuditInfoProps {
39
+ /** The record row (keys are the column names `audit` points at). */
40
+ record: Record<string, unknown> | null | undefined;
41
+ /** `TableMetadata.audit`. Absent → renders nothing. */
42
+ audit: AuditMeta | null | undefined;
43
+ /** Optional host resolver uuid → display name (e.g. from a users cache). */
44
+ resolveActor?: (id: string) => string | undefined;
45
+ /** IANA timezone; defaults to the org runtime timezone. */
46
+ timeZone?: string;
47
+ /** BCP-47 locale; defaults to the i18n language. */
48
+ locale?: string;
49
+ /** Start expanded. Default: collapsed. */
50
+ defaultOpen?: boolean;
51
+ className?: string;
52
+ }
53
+ export declare function AuditInfo({ record, audit, resolveActor, timeZone, locale, defaultOpen, className, }: AuditInfoProps): React.JSX.Element | null;
54
+ //# sourceMappingURL=audit-info.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-info.d.ts","sourceRoot":"","sources":["../src/audit-info.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAU9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAExC,gFAAgF;AAChF,eAAO,MAAM,eAAe,yCAAyC,CAAA;AAWrE;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CASjE;AAED,MAAM,WAAW,UAAU;IACvB,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,oEAAoE;IACpE,QAAQ,EAAE,OAAO,CAAA;CACpB;AAED,uEAAuE;AACvE,wBAAgB,iBAAiB,CAC7B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,IAAI,EAAE;IAAE,YAAY,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACjF,UAAU,GAAG,IAAI,CAmBnB;AAyBD,MAAM,WAAW,cAAc;IAC3B,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,CAAA;IAClD,uDAAuD;IACvD,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,CAAA;IACnC,4EAA4E;IAC5E,YAAY,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;IACjD,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0CAA0C;IAC1C,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AAUD,wBAAgB,SAAS,CAAC,EACtB,MAAM,EACN,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,MAAM,EACN,WAAmB,EACnB,SAAS,GACZ,EAAE,cAAc,4BA0EhB"}
@@ -0,0 +1,142 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * audit-info.tsx
4
+ *
5
+ * <AuditInfo> — discreet "when and who" footer for a record: created, modified
6
+ * and (if tombstoned) deleted, each with the actor and a relative + absolute
7
+ * date. Driven only by the kernel's `TableMetadata.audit` (column names) and
8
+ * the record row — manifests declare nothing.
9
+ *
10
+ * Actor resolution (no extra requests), in order:
11
+ * 1. the host's `resolveActor(id)` when it returns a name;
12
+ * 2. the record's expanded sibling (`created_by_id` → `row.created_by`,
13
+ * `{ label | name | title }` or a plain string);
14
+ * 3. the system actor id → "Sistema" / "System";
15
+ * 4. a short id (first 8 chars) — never a long uuid.
16
+ *
17
+ * Renders nothing when `audit` is absent or the record has none of its values.
18
+ */
19
+ import * as React from 'react';
20
+ import { useTranslation } from 'react-i18next';
21
+ import { formatDistanceToNow } from 'date-fns';
22
+ import { es, enUS } from 'date-fns/locale';
23
+ import { ChevronDown, ChevronRight, History } from 'lucide-react';
24
+ import { cn } from '@asteby/metacore-ui/lib';
25
+ import { formatDateCell } from './dynamic-columns';
26
+ import { objectLabel } from './dynamic-relation-helpers';
27
+ import { isNilUuid } from './nil-uuid';
28
+ import { useTimeZone } from './org-runtime-context';
29
+ /** The kernel's actor for unattended work (schedules, webhooks, connectors). */
30
+ export const SYSTEM_ACTOR_ID = '00000000-0000-0000-0000-000000000001';
31
+ const AUDIT_KEYS = [
32
+ 'created_at',
33
+ 'created_by',
34
+ 'updated_at',
35
+ 'updated_by',
36
+ 'deleted_at',
37
+ 'deleted_by',
38
+ ];
39
+ /**
40
+ * Defensive reader for the served `audit` block: keeps only known keys whose
41
+ * value is a non-empty string; returns `undefined` when nothing usable remains.
42
+ */
43
+ export function readAuditMeta(raw) {
44
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw))
45
+ return undefined;
46
+ const src = raw;
47
+ const out = {};
48
+ for (const k of AUDIT_KEYS) {
49
+ const v = src[k];
50
+ if (typeof v === 'string' && v.trim() !== '')
51
+ out[k] = v;
52
+ }
53
+ return Object.keys(out).length ? out : undefined;
54
+ }
55
+ /** Resolves the actor stored in `row[idColumn]` to something human. */
56
+ export function resolveAuditActor(row, idColumn, opts) {
57
+ if (!idColumn)
58
+ return null;
59
+ const raw = row[idColumn];
60
+ // The column itself may already be an expanded object.
61
+ const own = objectLabel(raw);
62
+ const id = typeof raw === 'string' ? raw.trim() : '';
63
+ if (!own && (!id || isNilUuid(id)))
64
+ return null;
65
+ if (id.toLowerCase() === SYSTEM_ACTOR_ID)
66
+ return { name: opts.systemLabel, resolved: true };
67
+ if (id) {
68
+ const fromHost = opts.resolveActor?.(id);
69
+ if (fromHost)
70
+ return { name: fromHost, resolved: true };
71
+ }
72
+ if (own)
73
+ return { name: own, resolved: true };
74
+ const base = idColumn.endsWith('_id') ? idColumn.slice(0, -3) : idColumn;
75
+ const sibling = base === idColumn ? undefined : row[base];
76
+ const siblingName = objectLabel(sibling) ?? (typeof sibling === 'string' && sibling.trim() !== '' ? sibling : undefined);
77
+ if (siblingName)
78
+ return { name: siblingName, resolved: true };
79
+ return { name: id.length > 8 ? id.slice(0, 8) : id, resolved: false };
80
+ }
81
+ const COPY = {
82
+ es: {
83
+ title: 'Historial del registro',
84
+ created: 'Creado por {{who}}',
85
+ updated: 'Modificado por {{who}}',
86
+ deleted: 'Eliminado por {{who}}',
87
+ createdAt: 'Creado',
88
+ updatedAt: 'Modificado',
89
+ deletedAt: 'Eliminado',
90
+ system: 'Sistema',
91
+ },
92
+ en: {
93
+ title: 'Record history',
94
+ created: 'Created by {{who}}',
95
+ updated: 'Modified by {{who}}',
96
+ deleted: 'Deleted by {{who}}',
97
+ createdAt: 'Created',
98
+ updatedAt: 'Modified',
99
+ deletedAt: 'Deleted',
100
+ system: 'System',
101
+ },
102
+ };
103
+ export function AuditInfo({ record, audit, resolveActor, timeZone, locale, defaultOpen = false, className, }) {
104
+ const { t, i18n } = useTranslation();
105
+ const ctxTz = useTimeZone();
106
+ const [open, setOpen] = React.useState(defaultOpen);
107
+ const meta = readAuditMeta(audit);
108
+ if (!meta || !record)
109
+ return null;
110
+ const lang = (locale || i18n?.language || 'es').toLowerCase().startsWith('en') ? 'en' : 'es';
111
+ const copy = COPY[lang];
112
+ const dfLocale = lang === 'en' ? enUS : es;
113
+ const tz = timeZone ?? ctxTz;
114
+ const tt = (key, vars) => t(`audit.${key}`, { defaultValue: copy[key], ...vars });
115
+ const build = (key, atCol, byCol, whoKey, whenKey) => {
116
+ const f = atCol ? formatDateCell(record[atCol], undefined, dfLocale, tz) : null;
117
+ if (!f)
118
+ return null;
119
+ const actor = resolveAuditActor(record, byCol, { resolveActor, systemLabel: tt('system') });
120
+ const date = new Date(record[atCol]);
121
+ const relative = formatDistanceToNow(date, { addSuffix: true, locale: dfLocale });
122
+ return {
123
+ key,
124
+ text: actor ? tt(whoKey, { who: actor.name }) : tt(whenKey),
125
+ absolute: f.display,
126
+ title: f.title,
127
+ relative,
128
+ };
129
+ };
130
+ const lines = [
131
+ build('created', meta.created_at, meta.created_by, 'created', 'createdAt'),
132
+ // Hide "modified" when it is the creation instant itself (never edited).
133
+ meta.updated_at && record[meta.updated_at] !== record[meta.created_at ?? '']
134
+ ? build('updated', meta.updated_at, meta.updated_by, 'updated', 'updatedAt')
135
+ : null,
136
+ build('deleted', meta.deleted_at, meta.deleted_by, 'deleted', 'deletedAt'),
137
+ ].filter((l) => l !== null);
138
+ if (lines.length === 0)
139
+ return null;
140
+ const Chevron = open ? ChevronDown : ChevronRight;
141
+ return (_jsxs("div", { className: cn('text-xs text-muted-foreground', className), "data-testid": "audit-info", children: [_jsxs("button", { type: "button", className: "flex items-center gap-1.5 hover:text-foreground", "aria-expanded": open, onClick: () => setOpen(o => !o), children: [_jsx(Chevron, { className: "h-3.5 w-3.5" }), _jsx(History, { className: "h-3.5 w-3.5" }), _jsx("span", { children: tt('title') })] }), open && (_jsx("ul", { className: "mt-2 space-y-1 pl-6", children: lines.map(l => (_jsxs("li", { "data-audit-line": l.key, children: [_jsx("span", { children: l.text }), ' · ', _jsx("time", { title: l.title, children: l.absolute }), _jsxs("span", { className: "opacity-70", children: [" (", l.relative, ")"] })] }, l.key))) }))] }));
142
+ }
@@ -0,0 +1,25 @@
1
+ import type { ProductQuery, ProductResult } from './product-search';
2
+ interface ApiLike {
3
+ get: (url: string, cfg?: {
4
+ params?: Record<string, unknown>;
5
+ signal?: AbortSignal;
6
+ }) => Promise<{
7
+ data: any;
8
+ }>;
9
+ }
10
+ export interface CatalogProductSearchOptions {
11
+ /** Modelo de catálogo. Default `products.Product`. */
12
+ model?: string;
13
+ /** Endpoint de listado. Default `/data/<model>`. */
14
+ endpoint?: string;
15
+ limit?: number;
16
+ /** Tasa de impuesto cuando el producto no trae la suya (config fiscal de la org; p. ej. 0.16). */
17
+ defaultTaxRate?: number;
18
+ }
19
+ /** Proyección pura registro del catálogo → ProductResult (exportada para tests). */
20
+ export declare function catalogRecordToProduct(rec: Record<string, any>, defaultTaxRate?: number): ProductResult;
21
+ export declare function createCatalogProductSearch(api: ApiLike, opts?: CatalogProductSearchOptions): (query: Exclude<ProductQuery, {
22
+ kind: "empty";
23
+ }>, signal: AbortSignal) => Promise<ProductResult[]>;
24
+ export {};
25
+ //# sourceMappingURL=catalog-product-search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog-product-search.d.ts","sourceRoot":"","sources":["../../src/business/catalog-product-search.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAEnE,UAAU,OAAO;IACb,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAA;KAAE,CAAC,CAAA;CACjH;AAED,MAAM,WAAW,2BAA2B;IACxC,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,kGAAkG;IAClG,cAAc,CAAC,EAAE,MAAM,CAAA;CAC1B;AA4BD,oFAAoF;AACpF,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,aAAa,CAkBvG;AAED,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,GAAE,2BAAgC,IAI7E,OAAO,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,EAAE,QAAQ,WAAW,KAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAShH"}
@@ -0,0 +1,57 @@
1
+ const num = (v) => {
2
+ const n = typeof v === 'string' && v.trim() !== '' ? Number(v) : typeof v === 'number' ? v : NaN;
3
+ return Number.isFinite(n) ? n : undefined;
4
+ };
5
+ const str = (v) => (typeof v === 'string' && v.trim() ? v.trim() : undefined);
6
+ /** Tasa como fracción: acepta 0.16 o 16. */
7
+ const rate = (v) => {
8
+ const n = num(v);
9
+ if (n == null || n < 0)
10
+ return undefined;
11
+ return n > 1 ? n / 100 : n;
12
+ };
13
+ const bag = (v) => {
14
+ if (typeof v === 'string') {
15
+ try {
16
+ const parsed = JSON.parse(v);
17
+ return parsed && typeof parsed === 'object' ? parsed : {};
18
+ }
19
+ catch {
20
+ return {};
21
+ }
22
+ }
23
+ return v && typeof v === 'object' ? v : {};
24
+ };
25
+ /** Proyección pura registro del catálogo → ProductResult (exportada para tests). */
26
+ export function catalogRecordToProduct(rec, defaultTaxRate) {
27
+ // Campos de extensión de otros addons (p. ej. claves SAT de fiscal_mexico).
28
+ const extensions = Object.fromEntries(Object.entries(bag(rec.fiscal_data)).filter(([, v]) => typeof v === 'string' && v !== ''));
29
+ const pt = String(rec.product_type ?? '');
30
+ return {
31
+ id: String(rec.id ?? rec.value ?? ''),
32
+ name: String(rec.name ?? rec.label ?? ''),
33
+ sku: str(rec.sku),
34
+ barcode: str(rec.barcode),
35
+ kind: pt === 'service' ? 'service' : 'product',
36
+ price: num(rec.unit_price ?? rec.sale_price ?? rec.price),
37
+ cost: num(rec.cost_price ?? rec.cost),
38
+ tax_rate: rate(rec.tax_rate) ?? defaultTaxRate,
39
+ unit: str(rec.unit_of_measure) ?? str(rec.unit),
40
+ extensions: Object.keys(extensions).length ? extensions : undefined,
41
+ };
42
+ }
43
+ export function createCatalogProductSearch(api, opts = {}) {
44
+ const model = opts.model ?? 'products.Product';
45
+ const url = opts.endpoint ?? `/data/${model}`;
46
+ const limit = opts.limit ?? 20;
47
+ return async (query, signal) => {
48
+ const search = query.kind === 'barcode' ? query.barcode : query.raw.trim();
49
+ const res = await api.get(url, { params: { search, per_page: limit }, signal });
50
+ const rows = res?.data?.data;
51
+ if (!Array.isArray(rows))
52
+ return [];
53
+ return rows
54
+ .filter((r) => r && typeof r === 'object' && r.is_active !== false)
55
+ .map((r) => catalogRecordToProduct(r, opts.defaultTaxRate));
56
+ };
57
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/business/catalog.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,qBAAqB;IAClC,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAA;IACZ,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAA;IACb,uCAAuC;IACvC,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,kDAAkD;IAClD,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,uDAAuD;IACvD,OAAO,EAAE,MAAM,EAAE,CAAA;CACpB;AAED,eAAO,MAAM,mBAAmB,EAAE,SAAS,qBAAqB,EAsGtD,CAAA"}
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/business/catalog.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,qBAAqB;IAClC,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAA;IACZ,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAA;IACb,uCAAuC;IACvC,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,kDAAkD;IAClD,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,uDAAuD;IACvD,OAAO,EAAE,MAAM,EAAE,CAAA;CACpB;AAED,eAAO,MAAM,mBAAmB,EAAE,SAAS,qBAAqB,EAwGtD,CAAA"}
@@ -33,6 +33,8 @@ export const BUSINESS_COMPONENTS = [
33
33
  'applyProductToLine',
34
34
  'addProductLine',
35
35
  'lineGridKeyCommand',
36
+ 'createCatalogProductSearch',
37
+ 'catalogRecordToProduct',
36
38
  ],
37
39
  },
38
40
  {
@@ -0,0 +1,107 @@
1
+ import { type LineItem } from './line-items';
2
+ import type { Allocation, AllocationIssue, OpenDocument } from '../primitives/allocation';
3
+ import type { ActionFieldDef, DocumentFormLines, DocumentFormOpenDocuments, DocumentFormsManifest, DocumentFormType } from '../types';
4
+ export declare const EXTENSION_PREFIX = "fiscal_data.";
5
+ export type EditorLinesConfig = DocumentFormLines & {
6
+ field: string;
7
+ };
8
+ /** true si el tipo se abre en el DocumentEditor (`layout: "editor"`). */
9
+ export declare function isEditorLayout(type: Pick<DocumentFormType, 'layout'> | null | undefined): boolean;
10
+ /** Paso de renglones efectivo del tipo (o undefined si no tiene). */
11
+ export declare function editorLinesConfig(type: DocumentFormType, forms: Pick<DocumentFormsManifest, 'lines_field'>): EditorLinesConfig | undefined;
12
+ /**
13
+ * Divulgación progresiva: ¿el campo va plegado en «Opciones fiscales»?
14
+ * - campos de extensión (`fiscal_data.*`) que aportan otros addons;
15
+ * - `section: "fiscal" | "advanced"`;
16
+ * - catálogos opcionales que ya traen un default (método, serie, relación…):
17
+ * el usuario no necesita tocarlos para guardar.
18
+ * Lo obligatorio sin default, la contraparte y el monto siempre están a la vista.
19
+ */
20
+ export declare function isAdvancedField(f: ActionFieldDef, essentialKeys?: ReadonlySet<string>): boolean;
21
+ export interface EditorFieldGroups {
22
+ /** Campo que elige la contraparte (cliente/proveedor), si el tipo la declara. */
23
+ party?: ActionFieldDef;
24
+ /** A la vista: lo esencial del encabezado. */
25
+ essential: ActionFieldDef[];
26
+ /** Plegados en «Opciones fiscales». */
27
+ advanced: ActionFieldDef[];
28
+ /** Notas largas (textarea): al final. */
29
+ notes: ActionFieldDef[];
30
+ }
31
+ /** Reparte los campos del tipo (más los de extensión) en grupos de pantalla. */
32
+ export declare function splitEditorFields(fields: readonly ActionFieldDef[], opts?: {
33
+ partyField?: string;
34
+ essentialKeys?: readonly string[];
35
+ extensionFields?: readonly ActionFieldDef[];
36
+ }): EditorFieldGroups;
37
+ /**
38
+ * Etiqueta clara con el código como dato secundario:
39
+ * `03 · Transferencia electrónica` → `Transferencia electrónica · 03`.
40
+ * Solo reordena el patrón «CÓDIGO · texto»; cualquier otra etiqueta pasa igual.
41
+ */
42
+ export declare function friendlyOptionLabel(label: string): string;
43
+ /** El mismo campo con sus opciones en formato «texto · código». */
44
+ export declare function withFriendlyOptions(f: ActionFieldDef): ActionFieldDef;
45
+ /**
46
+ * Defaults del encabezado que salen de la contraparte: los campos de extensión
47
+ * con la MISMA clave en ambos modelos (p. ej. `fiscal_data.uso_cfdi` de Customer
48
+ * → `fiscal_data.uso_cfdi` del documento) y la moneda.
49
+ * Solo llena lo que está vacío: nunca pisa lo que el usuario ya capturó.
50
+ */
51
+ export declare function partyDefaults(party: Record<string, any> | null, documentFieldKeys: readonly string[], current: Record<string, unknown>): Record<string, unknown>;
52
+ /** Filas de la tarjeta de contraparte: resumen declarado + extensiones (máximo `limit`). */
53
+ export declare function partySummaryRows(party: Record<string, any> | null, summary?: readonly string[], labels?: Record<string, string>, limit?: number): Array<{
54
+ key: string;
55
+ label: string;
56
+ value: string;
57
+ }>;
58
+ export interface CreditStatus {
59
+ limit?: number;
60
+ balance?: number;
61
+ overdue?: number;
62
+ available?: number;
63
+ holdReason?: string;
64
+ /** `ok` | `warn` (supera 80 % o hay vencido) | `block` (retenido o sin crédito disponible). */
65
+ level: 'ok' | 'warn' | 'block';
66
+ }
67
+ export declare function creditStatus(party: Record<string, any> | null, cols: {
68
+ limit?: string;
69
+ balance?: string;
70
+ overdue?: string;
71
+ hold_reason?: string;
72
+ } | undefined, documentTotal: number): CreditStatus | null;
73
+ /**
74
+ * Renglones de un documento origen → LineItem. `map` traduce nombres
75
+ * (`description: product_name`, `discount: discount_pct`). Si el origen trae
76
+ * `tax_amount` y `subtotal` pero no la tasa, la deriva. `credit` (NC) topa la
77
+ * cantidad en lo facturado (o en `max_quantity` si el origen la trae).
78
+ */
79
+ export declare function linesFromSource(rows: unknown, map?: Record<string, string>, opts?: {
80
+ kind?: DocumentFormLines['kind'];
81
+ }): LineItem[];
82
+ export interface EditorIssue {
83
+ field?: string;
84
+ severity: 'error' | 'warning';
85
+ message: string;
86
+ }
87
+ /** Revisión local (instantánea) de los renglones. Sin ruido: solo lo que hay que mirar. */
88
+ export declare function localIssues(lines: LineItem[], opts?: {
89
+ requireLines?: boolean;
90
+ requireTax?: boolean;
91
+ }): EditorIssue[];
92
+ /** Documento abierto (fila de /data) → OpenDocument para PaymentAllocator. */
93
+ export declare function toOpenDocuments(rows: unknown, cfg: DocumentFormOpenDocuments, currency: string): OpenDocument[];
94
+ /** Monto actual de cada documento, para pasar a modo manual sin perder lo repartido. */
95
+ export declare function manualFromAllocations(allocations: readonly Allocation[]): Record<string, number>;
96
+ /** Renglones del payload: solo los dos campos que declara la acción (`invoice_id`, `amount`). */
97
+ export declare function allocationPayload(allocations: readonly Allocation[], cfg: DocumentFormOpenDocuments): Array<Record<string, unknown>>;
98
+ /**
99
+ * Campo del encabezado con el monto recibido: `amount` si existe; si no, el
100
+ * primer campo numérico/moneda. Convención documentada (el schema no lo nombra).
101
+ */
102
+ export declare function allocationAmountField(fields: readonly ActionFieldDef[]): string | undefined;
103
+ /** AllocationIssue → mensaje en español claro (con el número del documento). */
104
+ export declare function allocationIssueMessages(issues: readonly AllocationIssue[], docs: readonly OpenDocument[]): EditorIssue[];
105
+ /** Días de atraso de un documento abierto (0 si no está vencido o no tiene vencimiento). */
106
+ export declare function overdueDays(doc: Pick<OpenDocument, 'due_at'>, today?: Date): number;
107
+ //# sourceMappingURL=document-editor-model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-editor-model.d.ts","sourceRoot":"","sources":["../../src/business/document-editor-model.ts"],"names":[],"mappings":"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"}