@asteby/metacore-runtime-react 48.0.0 → 49.1.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 (97) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/action-modal-dispatcher.d.ts +33 -3
  3. package/dist/action-modal-dispatcher.d.ts.map +1 -1
  4. package/dist/action-modal-dispatcher.js +84 -11
  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 +7 -0
  9. package/dist/business/catalog-product-search.d.ts.map +1 -1
  10. package/dist/business/catalog-product-search.js +14 -0
  11. package/dist/business/document-editor-model.d.ts +14 -2
  12. package/dist/business/document-editor-model.d.ts.map +1 -1
  13. package/dist/business/document-editor-model.js +42 -4
  14. package/dist/business/document-editor.d.ts.map +1 -1
  15. package/dist/business/document-editor.js +68 -19
  16. package/dist/business/line-items.d.ts +9 -1
  17. package/dist/business/line-items.d.ts.map +1 -1
  18. package/dist/business/line-items.js +8 -1
  19. package/dist/calendar-date.d.ts +12 -0
  20. package/dist/calendar-date.d.ts.map +1 -1
  21. package/dist/calendar-date.js +36 -0
  22. package/dist/document/document-page.d.ts.map +1 -1
  23. package/dist/document/document-page.js +8 -1
  24. package/dist/document/evaluate.d.ts.map +1 -1
  25. package/dist/document/evaluate.js +17 -1
  26. package/dist/document/index.d.ts +1 -0
  27. package/dist/document/index.d.ts.map +1 -1
  28. package/dist/document/index.js +1 -0
  29. package/dist/document/secondary-bar.d.ts +20 -0
  30. package/dist/document/secondary-bar.d.ts.map +1 -0
  31. package/dist/document/secondary-bar.js +44 -0
  32. package/dist/document/types.d.ts +8 -0
  33. package/dist/document/types.d.ts.map +1 -1
  34. package/dist/document-form-dialog.d.ts +23 -2
  35. package/dist/document-form-dialog.d.ts.map +1 -1
  36. package/dist/document-form-dialog.js +48 -9
  37. package/dist/dynamic-columns.d.ts +1 -11
  38. package/dist/dynamic-columns.d.ts.map +1 -1
  39. package/dist/dynamic-columns.js +7 -17
  40. package/dist/dynamic-crud-page.d.ts +19 -0
  41. package/dist/dynamic-crud-page.d.ts.map +1 -1
  42. package/dist/dynamic-crud-page.js +6 -5
  43. package/dist/dynamic-row-actions.d.ts +8 -0
  44. package/dist/dynamic-row-actions.d.ts.map +1 -1
  45. package/dist/dynamic-row-actions.js +12 -2
  46. package/dist/dynamic-table.d.ts.map +1 -1
  47. package/dist/dynamic-table.js +64 -63
  48. package/dist/index.d.ts +5 -2
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +9 -2
  51. package/dist/primitives/contributions.d.ts +14 -3
  52. package/dist/primitives/contributions.d.ts.map +1 -1
  53. package/dist/primitives/contributions.js +50 -13
  54. package/dist/record-actions.d.ts +21 -0
  55. package/dist/record-actions.d.ts.map +1 -0
  56. package/dist/record-actions.js +76 -0
  57. package/dist/row-actions-menu.d.ts +32 -0
  58. package/dist/row-actions-menu.d.ts.map +1 -0
  59. package/dist/row-actions-menu.js +59 -0
  60. package/dist/slot.d.ts +4 -21
  61. package/dist/slot.d.ts.map +1 -1
  62. package/dist/slot.js +14 -36
  63. package/dist/types.d.ts +39 -0
  64. package/dist/types.d.ts.map +1 -1
  65. package/package.json +3 -3
  66. package/src/__tests__/document-editor-model.test.ts +55 -1
  67. package/src/__tests__/document-editor.test.tsx +80 -1
  68. package/src/__tests__/document-form-create-model.test.tsx +143 -0
  69. package/src/__tests__/document-form-dialog.test.tsx +40 -0
  70. package/src/__tests__/field-default-today.test.tsx +116 -0
  71. package/src/__tests__/prefill-scalar-from-record.test.ts +52 -1
  72. package/src/__tests__/row-action-link-navigation.test.ts +17 -0
  73. package/src/__tests__/secondary-actions.test.tsx +261 -0
  74. package/src/action-modal-dispatcher.tsx +97 -17
  75. package/src/action-priority.ts +82 -0
  76. package/src/business/business.test.ts +1 -1
  77. package/src/business/catalog-product-search.ts +18 -0
  78. package/src/business/document-editor-model.ts +48 -6
  79. package/src/business/document-editor.tsx +75 -20
  80. package/src/business/line-items.ts +16 -1
  81. package/src/calendar-date.ts +36 -0
  82. package/src/document/document-page.tsx +15 -0
  83. package/src/document/evaluate.ts +17 -1
  84. package/src/document/index.ts +1 -0
  85. package/src/document/secondary-bar.tsx +92 -0
  86. package/src/document/types.ts +8 -0
  87. package/src/document-form-dialog.tsx +67 -8
  88. package/src/dynamic-columns.tsx +13 -57
  89. package/src/dynamic-crud-page.tsx +31 -4
  90. package/src/dynamic-row-actions.tsx +12 -2
  91. package/src/dynamic-table.tsx +3 -0
  92. package/src/index.ts +31 -0
  93. package/src/primitives/contributions.ts +59 -12
  94. package/src/record-actions.ts +101 -0
  95. package/src/row-actions-menu.tsx +159 -0
  96. package/src/slot.tsx +18 -53
  97. package/src/types.ts +35 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # @asteby/metacore-runtime-react
2
2
 
3
+ ## 49.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - eee67cb: Los campos fecha de un formulario declarativo (document_forms, acciones y asistentes) pueden declarar `default: "$today"` (o `"today"`) y nacen con el día de hoy en la zona horaria de la organización (`OrgRuntimeProvider`), no en la del navegador ni en UTC.
8
+
9
+ - `buildFieldDefaults` lee el `default` del manifest además del `defaultValue` del host. Antes los defaults escalares de los campos de document_forms se ignoraban.
10
+ - Un `$token` desconocido nunca se siembra en un campo fecha: el campo queda vacío y no se manda un literal que el backend rechaza.
11
+ - Nuevos exports: `todayInZone`, `isTodayToken`, `resolveFieldDefault`.
12
+
13
+ Retest r4 de Pitsline: FAC-00014 se guardó con fecha 26/27 sep. `invoice_date` era obligatoria y no tenía default, y el calendario abre en el mes actual con los últimos días del mes anterior en la primera fila.
14
+
15
+ - 40cf304: `document_forms.types[].create_model`: un tipo puede delegar su alta a otro modelo (p. ej. `customers.Invoice`). «Crear» no pinta un formulario: llama a `onDelegateCreate({ model, type })` del host, que abre la página de ese modelo con su alta abierta.
16
+
17
+ - `DocumentFormDialog` y `DynamicCRUDPage` aceptan `onDelegateCreate`. Sin él, los tipos delegados no se ofrecen: una vista acotada a uno de ellos sigue sin «Crear», como hasta ahora.
18
+ - Nuevos exports: `delegatedCreate(forms)`, `withoutDelegatedTypes(forms)` y el tipo `DelegatedCreate`.
19
+
20
+ Retest r4 de Pitsline: Documentos fiscales → Facturas se había quedado sin «Crear» porque FiscalDocument no tiene formulario de factura. La factura nace en customers y se timbra después.
21
+
22
+ ## 49.0.0
23
+
24
+ ### Minor Changes
25
+
26
+ - 4d97e6b: «Crear desde» con renglones y cantidad pendiente en el DocumentEditor. Una fuente (`document_forms.types[].sources[]`) que declara `line_link_field`, `remaining_qty_field` o `remaining_endpoint` precarga sus renglones desde lo que sirve el host (`<endpoint>/source-lines`, kernel ≥ v0.191): la cantidad sugerida y el tope son lo pendiente (cantidad − lo ya facturado/devuelto, calculado en el servidor), los renglones ya cubiertos se omiten con un aviso breve y cada renglón guarda `source_line_id`, que se envía en la columna `line_link_field` para que el servidor valide al guardar (su rechazo en español se muestra tal cual). Sin `source-lines` (404) cae a la relación como antes. Un tipo con `sources` usa el editor salvo `layout: "wizard"`; `DocumentFormDialog` abre el editor cuando recibe `initialSource`, y `DynamicCRUDPage` acepta `initialCreate` para abrir el alta con tipo y origen (p. ej. desde la URL). Las acciones de fila `type: "link"` con query (`?create=…&from_id={id}`) navegan con `search` (antes la query quedaba en el path y la ruta no casaba); los valores interpolados van codificados. `serializeLineItems(lines, { sourceLineField })`, `sourceLoadSummary` y `sourceTracksRemaining` exportados del modelo del editor; cada renglón serializado lleva además `subtotal` y `tax_amount` calculados (modelos de renglón sin tasa no pierden el IVA) y `linesFromSource` respeta `discount_mode: "amount"`. i18n es/en de los avisos de carga.
27
+ - f9616d1: Un solo registro para modales, slots y acciones, y acciones secundarias fuera de la vista principal:
28
+
29
+ - `@asteby/metacore-sdk` es la única fuente de verdad: `registerModalComponent` (modales por slug, con addon dueño y `load` perezoso), `registerRecordAction` (acciones secundarias sobre registros con su proveedor `requires: { addon | capability }`) y `slotStore`. `Registry.scope(addon).registerModal/registerAction/registerSlot` escriben ahí (antes `registerModal` no tenía lector) y `unbind` lo retira todo. `adaptActionProps` entrega a un remote ambos contratos de props (ActionModalProps y recordId/payload/close).
30
+ - `registerFederatedModal` queda como alias deprecado y compatible de `registerModalComponent`; `slotRegistry` y `<Slot>` de runtime-react leen el `slotStore` del SDK y no pintan contribuciones de addons no instalados.
31
+ - Acciones primarias/secundarias: `priority` del manifest v3 (o convención por clave: print, share, email, mail, whatsapp, chat, download, pdf, xml, acuse). `RowActionsMenu` (menú «…» por defecto de DynamicTable) muestra una primaria destacada y agrupa las secundarias y las aportadas por addons en «Más…»; las acciones por capacidad sin proveedor activo no se ofrecen. `DocumentPage` manda las secundarias de su layout a `DocumentSecondaryBar` (pie) y `resolveActions` devuelve el nuevo grupo `footer`.
32
+ - i18n: `datatable.more_actions`, `datatable.open_menu`, `datatable.secondary_actions` (es/en).
33
+
34
+ ### Patch Changes
35
+
36
+ - b8a2ba3: Acciones abiertas desde una fila y buscador del host en documentos (retest Pitsline 2026-10-05)
37
+
38
+ - Un campo de acción que apunta al MISMO modelo de la fila y que la fila no tiene como columna se precarga con el id de esa fila: «Registrar pago» desde una factura abre con la factura elegida (`invoice_id` → Invoice). Una columna propia vacía (`parent_id` sin padre) no se siembra.
39
+ - `DocumentFormDialog` aplica el IVA de la org (`OrgRuntimeProvider` `taxRate`) a los productos del buscador del host (`searchProducts`) que no traen su tasa; antes sólo el buscador por defecto lo hacía y el renglón entraba sin IVA.
40
+
41
+ - Updated dependencies [f9616d1]
42
+ - @asteby/metacore-sdk@3.14.0
43
+
3
44
  ## 48.0.0
4
45
 
5
46
  ### Minor Changes
@@ -65,8 +65,22 @@ export declare function prefillFromFieldRelationRequest(req: PrefillFromFieldReq
65
65
  } | null;
66
66
  export declare function unwrapRecordScalar(value: unknown): unknown;
67
67
  export declare function readRecordPath(record: any, path: string): unknown;
68
- /** Scalar seed for one action field from the row being acted on. */
69
- export declare function scalarDefaultFromRecord(field: ActionFieldDef, record: any): unknown;
68
+ /**
69
+ * ¿`ref` (FK de un campo) apunta al modelo `model` de la fila? Tolera prefijo de
70
+ * addon, PascalCase vs tabla y el plural de la tabla (`Invoice` ↔ `invoices`,
71
+ * `CustomerAddress` ↔ `customer_addresses`).
72
+ */
73
+ export declare function refTargetsModel(ref: string | undefined | null, model: string | undefined | null): boolean;
74
+ /**
75
+ * Scalar seed for one action field from the row being acted on.
76
+ *
77
+ * Sin `default_from_record` declarado, un campo se siembra con la columna
78
+ * homónima de la fila; y un selector que apunta al MISMO modelo de la fila y
79
+ * que la fila no tiene como columna (`invoice_id` → Invoice en «Registrar pago»
80
+ * de una factura) se siembra con el id de esa fila: la acción se abrió desde
81
+ * ella (retest Pitsline 2026-10-05).
82
+ */
83
+ export declare function scalarDefaultFromRecord(field: ActionFieldDef, record: any, model?: string): unknown;
70
84
  export declare function ActionModalDispatcher({ open, onOpenChange, action, model, record, endpoint, onSuccess: onSuccessProp, }: ActionModalProps): import("react").JSX.Element | null;
71
85
  /** How long an action with `modal` waits for its federated component. */
72
86
  export declare const FEDERATED_ACTION_MODAL_TIMEOUT_MS = 20000;
@@ -75,7 +89,23 @@ export declare const FEDERATED_ACTION_MODAL_TIMEOUT_MS = 20000;
75
89
  * other failure (500, network) -> the server's own message and cause. Unlike
76
90
  * the toast it stays on screen until the next attempt. */
77
91
  export declare function actionErrorBannerMessage(err: unknown, fields: readonly ActionFieldDef[] | undefined, t: Translate, language?: string): string;
78
- export declare function buildFieldDefaults(fields: ActionFieldDef[], record: any): Record<string, any>;
92
+ /** Context a declared field default is resolved against. */
93
+ export interface FieldDefaultContext {
94
+ /** Org IANA timezone: `$today` is the org's day, not the browser's or UTC's. */
95
+ timeZone?: string;
96
+ /** Clock (tests). */
97
+ now?: Date;
98
+ }
99
+ /**
100
+ * The declared scalar default of a field, resolved: `defaultValue` (host
101
+ * camelCase) or `default` (raw manifest, the kernel serves action/document-form
102
+ * fields without renaming it). A date field may declare `$today` / `today` and
103
+ * gets the org's calendar day. An unknown `$token` never lands in a date field
104
+ * (undefined): a literal "$today" posted as invoice_date is a 422, an empty
105
+ * field is just a required field to fill.
106
+ */
107
+ export declare function resolveFieldDefault(field: ActionFieldDef, ctx?: FieldDefaultContext): unknown;
108
+ export declare function buildFieldDefaults(fields: ActionFieldDef[], record: any, model?: string, ctx?: FieldDefaultContext): Record<string, any>;
79
109
  /** Renders one action field (same widgets as the action modals). Shared with DocumentFormDialog. */
80
110
  export declare function renderField(field: ActionFieldDef, value: any, onChange: (value: any) => void, formValues?: Record<string, any>, record?: Record<string, any>, fieldErrors?: Record<string, string>): import("react").JSX.Element;
81
111
  //# sourceMappingURL=action-modal-dispatcher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"AAmDA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AA8B/C,OAAO,KAAK,EAAE,cAAc,EAAmC,MAAM,SAAS,CAAA;AAE9E,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAGxB,MAAM,sBAAsB,CAAA;AAM7B,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAA;AAiBhD,MAAM,WAAW,WAAW;IACxB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,SAAS,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1D;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,WAAW,CAM1D;AAsBD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CAStE;AAGD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAkB3F;AAYD,MAAM,WAAW,sBAAsB;IACnC,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,WAAW,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;CAC1C;AAED,wBAAgB,uBAAuB,CACnC,MAAM,EAAE,cAAc,EAAE,GAAG,SAAS,EACpC,MAAM,EAAE,GAAG,EACX,SAAS,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,GAC5H,sBAAsB,EAAE,CAyB1B;AASD,MAAM,WAAW,uBAAuB;IACpC,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,WAAW,CAAA;IACjB,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,CAAA;IAChB,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAA;CAChB;AAED,wBAAgB,wBAAwB,CACpC,MAAM,EAAE,cAAc,EAAE,GAAG,SAAS,EACpC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC9B,uBAAuB,EAAE,CAe3B;AAKD,wBAAgB,+BAA+B,CAC3C,GAAG,EAAE,uBAAuB,EAC5B,SAAS,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,GAC5H;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;CAAE,GAAG,IAAI,CActE;AASD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAY1D;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CASjE;AAUD,oEAAoE;AACpE,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAgBnF;AAED,wBAAgB,qBAAqB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,MAAM,EACN,QAAQ,EACR,SAAS,EAAE,aAAa,GAC3B,EAAE,gBAAgB,sCAqHlB;AAED,yEAAyE;AACzE,eAAO,MAAM,iCAAiC,QAAS,CAAA;AA0WvD;;;2DAG2D;AAC3D,wBAAgB,wBAAwB,CACpC,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,SAAS,cAAc,EAAE,GAAG,SAAS,EAC7C,CAAC,EAAE,SAAS,EACZ,QAAQ,CAAC,EAAE,MAAM,GAClB,MAAM,CAWR;AA+bD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,EAAE,EAAE,MAAM,EAAE,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAoB7F;AAyPD,oGAAoG;AACpG,wBAAgB,WAAW,CACvB,KAAK,EAAE,cAAc,EACrB,KAAK,EAAE,GAAG,EACV,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,EAK9B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAChC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,+BAwEvC"}
1
+ {"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"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"}
@@ -44,11 +44,13 @@ import { FieldGrid, FieldCell, FieldLabel } from './field-grid';
44
44
  import { useMetadataCache } from './metadata-cache';
45
45
  import { ViewValue, objectLabel, relationSiblingValue, isLineItemsField as isRecordLineItemsField, } from './dialogs/dynamic-record';
46
46
  // Canonical registry lives in @asteby/metacore-sdk
47
- import { getActionComponent, subscribeActionComponents, } from '@asteby/metacore-sdk';
47
+ import { adaptActionProps, getActionComponent, subscribeActionComponents, } from '@asteby/metacore-sdk';
48
48
  import { requestFederatedAction } from './federated-action-loader';
49
49
  import { useInstalledAddons } from './installed-addons-context';
50
50
  import { federatedModalComponent, resolveFederatedModal } from './primitives/contributions';
51
51
  import { installedPredicate, useContributionsVersion } from './primitives/use-contributions';
52
+ import { isTodayToken, todayInZone } from './calendar-date';
53
+ import { useTimeZone } from './org-runtime-context';
52
54
  export function isPrefillSpec(v) {
53
55
  return (typeof v === 'object' &&
54
56
  v !== null &&
@@ -215,8 +217,46 @@ function defaultFromRecordSpec(field) {
215
217
  const f = field;
216
218
  return f.defaultFromRecord ?? f.default_from_record;
217
219
  }
218
- /** Scalar seed for one action field from the row being acted on. */
219
- export function scalarDefaultFromRecord(field, record) {
220
+ /** `customers.Invoice`, `Invoice`, `invoices`, `sales_orders` → forma comparable. */
221
+ function modelToken(m) {
222
+ const last = m.split('.').pop() ?? m;
223
+ return last.replace(/[^a-z0-9]/gi, '').toLowerCase();
224
+ }
225
+ /**
226
+ * ¿`ref` (FK de un campo) apunta al modelo `model` de la fila? Tolera prefijo de
227
+ * addon, PascalCase vs tabla y el plural de la tabla (`Invoice` ↔ `invoices`,
228
+ * `CustomerAddress` ↔ `customer_addresses`).
229
+ */
230
+ export function refTargetsModel(ref, model) {
231
+ if (!ref || !model)
232
+ return false;
233
+ const a = modelToken(ref);
234
+ const b = modelToken(model);
235
+ if (!a || !b)
236
+ return false;
237
+ return a === b || a + 's' === b || a + 'es' === b || b + 's' === a || b + 'es' === a;
238
+ }
239
+ function fieldRef(field) {
240
+ if (field.ref)
241
+ return field.ref;
242
+ const opts = field.options;
243
+ if (opts && !Array.isArray(opts) && typeof opts === 'object') {
244
+ const src = opts.source;
245
+ if (typeof src === 'string' && src)
246
+ return src;
247
+ }
248
+ return undefined;
249
+ }
250
+ /**
251
+ * Scalar seed for one action field from the row being acted on.
252
+ *
253
+ * Sin `default_from_record` declarado, un campo se siembra con la columna
254
+ * homónima de la fila; y un selector que apunta al MISMO modelo de la fila y
255
+ * que la fila no tiene como columna (`invoice_id` → Invoice en «Registrar pago»
256
+ * de una factura) se siembra con el id de esa fila: la acción se abrió desde
257
+ * ella (retest Pitsline 2026-10-05).
258
+ */
259
+ export function scalarDefaultFromRecord(field, record, model) {
220
260
  if (!record)
221
261
  return undefined;
222
262
  const spec = defaultFromRecordSpec(field);
@@ -236,6 +276,12 @@ export function scalarDefaultFromRecord(field, record) {
236
276
  const v = readRecordPath(record, field.key);
237
277
  if (v !== undefined && v !== null && v !== '')
238
278
  return v;
279
+ // Sólo si la fila NO tiene esa columna: un `parent_id` vacío de una
280
+ // categoría es «sin padre», no «yo mismo».
281
+ const id = unwrapRecordScalar(record.id);
282
+ if (!(field.key in record) && id !== undefined && id !== null && id !== '' && refTargetsModel(fieldRef(field), model)) {
283
+ return id;
284
+ }
239
285
  }
240
286
  return undefined;
241
287
  }
@@ -263,8 +309,11 @@ export function ActionModalDispatcher({ open, onOpenChange, action, model, recor
263
309
  return (_jsx(CustomComponent, { open: open, onOpenChange: onOpenChange, action: action, model: model, record: record, endpoint: endpoint, onSuccess: onSuccess }));
264
310
  }
265
311
  if (federatedModal) {
312
+ // Modal por slug del store canónico: lo registró `api.registry.registerModal`
313
+ // (contrato recordId/payload/close) o registerFederatedModal
314
+ // (ActionModalProps). Recibe ambos contratos.
266
315
  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 }) }));
316
+ return (_jsx(Suspense, { fallback: _jsx(LoadingActionDialog, { open: open, onOpenChange: onOpenChange, action: action }), children: _jsx(FederatedComponent, { ...adaptActionProps({ open, onOpenChange, action, model, record, endpoint, onSuccess }) }) }));
268
317
  }
269
318
  // Declarative custom slot (`modal: "addon.action"`). Wait for the remote,
270
319
  // then a hard error — confirm/fields generics look "fine" and hide a
@@ -639,6 +688,7 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
639
688
  const reasonPrompt = useReasonPrompt();
640
689
  const supervisor = useSupervisor();
641
690
  const branchGate = useBranchCreateGate();
691
+ const orgTimeZone = useTimeZone();
642
692
  const [formData, setFormData] = useState({});
643
693
  const [executing, setExecuting] = useState(false);
644
694
  // Per-field validation errors (localized), shown inline under each input.
@@ -677,11 +727,11 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
677
727
  }, [open, model, recordId, api]);
678
728
  useEffect(() => {
679
729
  if (open && action.fields) {
680
- setFormData(buildFieldDefaults(action.fields, record));
730
+ setFormData(buildFieldDefaults(action.fields, record, model, { timeZone: orgTimeZone }));
681
731
  setFieldErrors({});
682
732
  setFormError(undefined);
683
733
  }
684
- }, [open, action.fields, record]);
734
+ }, [open, action.fields, record, model, orgTimeZone]);
685
735
  // Lines the list row does not carry: load them from the declared relation
686
736
  // (see prefillRelationRequests) and seed the line-items grid.
687
737
  useEffect(() => {
@@ -887,10 +937,32 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
887
937
  return (_jsxs(FieldCell, { fullWidth: fullWidth, children: [_jsx(FieldLabel, { htmlFor: field.key, required: field.required, children: tl(field.label) }), renderField(field, formData[field.key], (v) => updateField(field.key, v), formData, record, fieldErrors), fieldErrors[field.key] && (_jsx("p", { className: "text-destructive text-xs mt-1", children: fieldErrors[field.key] }))] }, field.key));
888
938
  }), embedRelations && relations.length > 0 && (_jsx(FieldCell, { fullWidth: true, children: _jsx(DynamicRelations, { record: record, relations: relations, lineSubtable: true, embedOnly: true }) }))] })] }), _jsxs(DialogFooter, { className: "shrink-0", children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: executing, children: t('common.cancel') }), _jsxs(Button, { onClick: execute, disabled: executing, style: action.color ? { backgroundColor: action.color, color: 'white' } : undefined, children: [executing ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(DynamicIcon, { name: action.icon, className: "mr-2 h-4 w-4" }), tl(action.label)] })] }), reasonPrompt.dialog] }) }));
889
939
  }
940
+ const DATE_FIELD_TYPES = new Set(['date', 'datetime', 'timestamp', 'timestamptz']);
941
+ /**
942
+ * The declared scalar default of a field, resolved: `defaultValue` (host
943
+ * camelCase) or `default` (raw manifest, the kernel serves action/document-form
944
+ * fields without renaming it). A date field may declare `$today` / `today` and
945
+ * gets the org's calendar day. An unknown `$token` never lands in a date field
946
+ * (undefined): a literal "$today" posted as invoice_date is a 422, an empty
947
+ * field is just a required field to fill.
948
+ */
949
+ export function resolveFieldDefault(field, ctx = {}) {
950
+ const f = field;
951
+ const raw = f.defaultValue ?? f.default;
952
+ if (raw === undefined || raw === null)
953
+ return undefined;
954
+ if (DATE_FIELD_TYPES.has(String(field.type ?? '').toLowerCase()) && typeof raw === 'string') {
955
+ if (isTodayToken(raw))
956
+ return todayInZone(ctx.timeZone, ctx.now);
957
+ if (raw.trim().startsWith('$'))
958
+ return undefined;
959
+ }
960
+ return raw;
961
+ }
890
962
  // buildFieldDefaults seeds formData for a set of action fields, honoring the
891
963
  // same line-items prefill spec + boolean/empty rules GenericActionModal uses, so
892
964
  // wizard steps and single-page forms initialize identically.
893
- export function buildFieldDefaults(fields, record) {
965
+ export function buildFieldDefaults(fields, record, model, ctx = {}) {
894
966
  const defaults = {};
895
967
  for (const field of fields) {
896
968
  if (isLineItemsField(field)) {
@@ -902,12 +974,12 @@ export function buildFieldDefaults(fields, record) {
902
974
  : [];
903
975
  continue;
904
976
  }
905
- const fromRecord = scalarDefaultFromRecord(field, record);
977
+ const fromRecord = scalarDefaultFromRecord(field, record, model);
906
978
  if (fromRecord !== undefined && fromRecord !== null && fromRecord !== '') {
907
979
  defaults[field.key] = fromRecord;
908
980
  continue;
909
981
  }
910
- defaults[field.key] = field.defaultValue ?? (field.type === 'boolean' ? false : '');
982
+ defaults[field.key] = resolveFieldDefault(field, ctx) ?? (field.type === 'boolean' ? false : '');
911
983
  }
912
984
  return defaults;
913
985
  }
@@ -933,6 +1005,7 @@ function WizardActionModal({ open, onOpenChange, action, model, record, endpoint
933
1005
  const api = useApi();
934
1006
  const reasonPrompt = useReasonPrompt();
935
1007
  const supervisor = useSupervisor();
1008
+ const orgTimeZone = useTimeZone();
936
1009
  const steps = action.steps ?? [];
937
1010
  const [stepIndex, setStepIndex] = useState(0);
938
1011
  const [formData, setFormData] = useState({});
@@ -945,10 +1018,10 @@ function WizardActionModal({ open, onOpenChange, action, model, record, endpoint
945
1018
  if (!open)
946
1019
  return;
947
1020
  const allFields = steps.flatMap((s) => s.fields ?? []);
948
- setFormData(buildFieldDefaults(allFields, record));
1021
+ setFormData(buildFieldDefaults(allFields, record, model, { timeZone: orgTimeZone }));
949
1022
  setStepIndex(0);
950
1023
  setFormError(undefined);
951
- }, [open, action.steps, record]);
1024
+ }, [open, action.steps, record, model, orgTimeZone]);
952
1025
  const updateField = (key, value) => setFormData((prev) => ({ ...prev, [key]: value }));
953
1026
  const step = steps[stepIndex];
954
1027
  const isLast = stepIndex === steps.length - 1;
@@ -0,0 +1,34 @@
1
+ import type { ActionPriority } from '@asteby/metacore-sdk';
2
+ import type { InstalledAddonsValue } from './installed-addons-context';
3
+ export type { ActionPriority };
4
+ /** Lo mínimo que se lee de una acción de metadata para clasificarla. */
5
+ export interface PrioritizableAction {
6
+ key: string;
7
+ priority?: string;
8
+ trigger?: {
9
+ type?: string;
10
+ capability?: string;
11
+ } | null;
12
+ }
13
+ /** "primary" | "secondary" | undefined (= acción normal del menú). */
14
+ export declare function classifyActionPriority(action: PrioritizableAction): ActionPriority | undefined;
15
+ export interface SplitActions<A> {
16
+ /** La única destacada (la primera que se declara `primary`). */
17
+ primary?: A;
18
+ /** El resto de acciones principales, en orden. */
19
+ main: A[];
20
+ /** Compartir / imprimir / correo / chat… */
21
+ secondary: A[];
22
+ }
23
+ /**
24
+ * Separa primaria, principales y secundarias conservando el orden. Si varias
25
+ * reclaman `primary`, sólo la primera lo es; las demás quedan como principales.
26
+ */
27
+ export declare function splitActionsByPriority<A extends PrioritizableAction>(actions: readonly A[]): SplitActions<A>;
28
+ /**
29
+ * Una acción que despacha por capacidad (`trigger.type: "capability"`) sólo se
30
+ * ofrece si algún addon instalado la provee. Sin provider = desconocido → se
31
+ * muestra (el servidor revalida igual).
32
+ */
33
+ export declare function isActionProviderActive(action: PrioritizableAction, installed: InstalledAddonsValue | null): boolean;
34
+ //# sourceMappingURL=action-priority.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-priority.d.ts","sourceRoot":"","sources":["../src/action-priority.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AAEtE,YAAY,EAAE,cAAc,EAAE,CAAA;AAE9B,wEAAwE;AACxE,MAAM,WAAW,mBAAmB;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;CAC1D;AAqBD,sEAAsE;AACtE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,mBAAmB,GAAG,cAAc,GAAG,SAAS,CAS9F;AAED,MAAM,WAAW,YAAY,CAAC,CAAC;IAC3B,gEAAgE;IAChE,OAAO,CAAC,EAAE,CAAC,CAAA;IACX,kDAAkD;IAClD,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,4CAA4C;IAC5C,SAAS,EAAE,CAAC,EAAE,CAAA;CACjB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,mBAAmB,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC,CAW5G;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,mBAAmB,EAAE,SAAS,EAAE,oBAAoB,GAAG,IAAI,GAAG,OAAO,CAInH"}
@@ -0,0 +1,60 @@
1
+ // Segmentos de la clave (separados por _ . -) que marcan una acción de
2
+ // compartir/salida. Por segmento: `create_mailbox` no es «mail», `send` solo
3
+ // (enviar una cotización) sigue siendo flujo principal.
4
+ const SECONDARY_SEGMENTS = new Set([
5
+ 'print',
6
+ 'reprint',
7
+ 'share',
8
+ 'email',
9
+ 'mail',
10
+ 'whatsapp',
11
+ 'chat',
12
+ 'download',
13
+ 'pdf',
14
+ 'xml',
15
+ 'acuse',
16
+ ]);
17
+ const CRUD_KEYS = new Set(['view', 'edit', 'delete']);
18
+ /** "primary" | "secondary" | undefined (= acción normal del menú). */
19
+ export function classifyActionPriority(action) {
20
+ if (action.priority === 'primary' || action.priority === 'secondary')
21
+ return action.priority;
22
+ if (CRUD_KEYS.has(action.key))
23
+ return undefined;
24
+ return action.key
25
+ .toLowerCase()
26
+ .split(/[_.-]+/)
27
+ .some((seg) => SECONDARY_SEGMENTS.has(seg))
28
+ ? 'secondary'
29
+ : undefined;
30
+ }
31
+ /**
32
+ * Separa primaria, principales y secundarias conservando el orden. Si varias
33
+ * reclaman `primary`, sólo la primera lo es; las demás quedan como principales.
34
+ */
35
+ export function splitActionsByPriority(actions) {
36
+ let primary;
37
+ const main = [];
38
+ const secondary = [];
39
+ for (const a of actions) {
40
+ const p = classifyActionPriority(a);
41
+ if (p === 'secondary')
42
+ secondary.push(a);
43
+ else if (p === 'primary' && !primary)
44
+ primary = a;
45
+ else
46
+ main.push(a);
47
+ }
48
+ return { primary, main, secondary };
49
+ }
50
+ /**
51
+ * Una acción que despacha por capacidad (`trigger.type: "capability"`) sólo se
52
+ * ofrece si algún addon instalado la provee. Sin provider = desconocido → se
53
+ * muestra (el servidor revalida igual).
54
+ */
55
+ export function isActionProviderActive(action, installed) {
56
+ const t = action.trigger;
57
+ if (!installed || !t || t.type !== 'capability' || !t.capability)
58
+ return true;
59
+ return installed.capabilities.has(t.capability);
60
+ }
@@ -21,5 +21,12 @@ export declare function catalogRecordToProduct(rec: Record<string, any>, default
21
21
  export declare function createCatalogProductSearch(api: ApiLike, opts?: CatalogProductSearchOptions): (query: Exclude<ProductQuery, {
22
22
  kind: "empty";
23
23
  }>, signal: AbortSignal) => Promise<ProductResult[]>;
24
+ /**
25
+ * Buscador del host (`searchProducts`) con el IVA de la org como respaldo: un
26
+ * producto que no trae su tasa entra con la de la org en vez de 0 (retest
27
+ * Pitsline 2026-10-05: el buscador de ops no devolvía tasa y el renglón de la
28
+ * factura quedaba sin IVA). Sin tasa de org, el buscador queda tal cual.
29
+ */
30
+ export declare function withDefaultTaxRate<Q, S>(search: (query: Q, signal: S) => Promise<ProductResult[]>, defaultTaxRate: number | undefined): (query: Q, signal: S) => Promise<ProductResult[]>;
24
31
  export {};
25
32
  //# sourceMappingURL=catalog-product-search.d.ts.map
@@ -1 +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"}
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;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EACnC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC,EACzD,cAAc,EAAE,MAAM,GAAG,SAAS,GACnC,CAAC,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC,CAMnD"}
@@ -55,3 +55,17 @@ export function createCatalogProductSearch(api, opts = {}) {
55
55
  .map((r) => catalogRecordToProduct(r, opts.defaultTaxRate));
56
56
  };
57
57
  }
58
+ /**
59
+ * Buscador del host (`searchProducts`) con el IVA de la org como respaldo: un
60
+ * producto que no trae su tasa entra con la de la org en vez de 0 (retest
61
+ * Pitsline 2026-10-05: el buscador de ops no devolvía tasa y el renglón de la
62
+ * factura quedaba sin IVA). Sin tasa de org, el buscador queda tal cual.
63
+ */
64
+ export function withDefaultTaxRate(search, defaultTaxRate) {
65
+ if (defaultTaxRate == null)
66
+ return search;
67
+ return async (query, signal) => {
68
+ const rows = await search(query, signal);
69
+ return rows.map((p) => (p.tax_rate == null ? { ...p, tax_rate: defaultTaxRate } : p));
70
+ };
71
+ }
@@ -1,12 +1,12 @@
1
1
  import { type LineItem } from './line-items';
2
2
  import type { Allocation, AllocationIssue, OpenDocument } from '../primitives/allocation';
3
- import type { ActionFieldDef, DocumentFormLines, DocumentFormOpenDocuments, DocumentFormsManifest, DocumentFormType } from '../types';
3
+ import type { ActionFieldDef, DocumentFormLines, DocumentFormSource, DocumentFormOpenDocuments, DocumentFormsManifest, DocumentFormType } from '../types';
4
4
  export declare const EXTENSION_PREFIX = "fiscal_data.";
5
5
  export type EditorLinesConfig = DocumentFormLines & {
6
6
  field: string;
7
7
  };
8
8
  /** true si el tipo se abre en el DocumentEditor (`layout: "editor"`). */
9
- export declare function isEditorLayout(type: Pick<DocumentFormType, 'layout'> | null | undefined): boolean;
9
+ export declare function isEditorLayout(type: Pick<DocumentFormType, 'layout' | 'sources'> | null | undefined): boolean;
10
10
  /** Paso de renglones efectivo del tipo (o undefined si no tiene). */
11
11
  export declare function editorLinesConfig(type: DocumentFormType, forms: Pick<DocumentFormsManifest, 'lines_field'>): EditorLinesConfig | undefined;
12
12
  /**
@@ -75,10 +75,22 @@ export declare function creditStatus(party: Record<string, any> | null, cols: {
75
75
  * (`description: product_name`, `discount: discount_pct`). Si el origen trae
76
76
  * `tax_amount` y `subtotal` pero no la tasa, la deriva. `credit` (NC) topa la
77
77
  * cantidad en lo facturado (o en `max_quantity` si el origen la trae).
78
+ *
79
+ * Con `remaining_quantity` (lo sirve el host: cantidad − lo ya facturado o
80
+ * devuelto) la cantidad sugerida y el tope son lo pendiente y los renglones ya
81
+ * cubiertos se omiten. Cada línea guarda `source_line_id` para el vínculo.
78
82
  */
79
83
  export declare function linesFromSource(rows: unknown, map?: Record<string, string>, opts?: {
80
84
  kind?: DocumentFormLines['kind'];
85
+ discountMode?: DocumentFormLines['discount_mode'];
81
86
  }): LineItem[];
87
+ /** Cuántos renglones del origen se cargaron y cuántos se omitieron por estar ya cubiertos. */
88
+ export declare function sourceLoadSummary(rows: unknown): {
89
+ loaded: number;
90
+ covered: number;
91
+ };
92
+ /** Fuente que precarga desde lo pendiente servido por el host (no desde la relación cruda). */
93
+ export declare function sourceTracksRemaining(src: Pick<DocumentFormSource, 'line_link_field' | 'remaining_qty_field' | 'remaining_endpoint'>): boolean;
82
94
  export interface EditorIssue {
83
95
  field?: string;
84
96
  severity: 'error' | 'warning';
@@ -1 +1 @@
1
- {"version":3,"file":"document-editor-model.d.ts","sourceRoot":"","sources":["../../src/business/document-editor-model.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"document-editor-model.d.ts","sourceRoot":"","sources":["../../src/business/document-editor-model.ts"],"names":[],"mappings":"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"}
@@ -11,7 +11,9 @@ export const EXTENSION_PREFIX = 'fiscal_data.';
11
11
  const ADVANCED_SECTIONS = new Set(['fiscal', 'advanced']);
12
12
  /** true si el tipo se abre en el DocumentEditor (`layout: "editor"`). */
13
13
  export function isEditorLayout(type) {
14
- return type?.layout === 'editor';
14
+ // «Cargar desde…» vive en el editor: un tipo con `sources` lo usa salvo que
15
+ // pida explícitamente el wizard.
16
+ return type?.layout === 'editor' || (type?.layout !== 'wizard' && (type?.sources?.length ?? 0) > 0);
15
17
  }
16
18
  /** Paso de renglones efectivo del tipo (o undefined si no tiene). */
17
19
  export function editorLinesConfig(type, forms) {
@@ -149,14 +151,19 @@ export function creditStatus(party, cols, documentTotal) {
149
151
  * (`description: product_name`, `discount: discount_pct`). Si el origen trae
150
152
  * `tax_amount` y `subtotal` pero no la tasa, la deriva. `credit` (NC) topa la
151
153
  * cantidad en lo facturado (o en `max_quantity` si el origen la trae).
154
+ *
155
+ * Con `remaining_quantity` (lo sirve el host: cantidad − lo ya facturado o
156
+ * devuelto) la cantidad sugerida y el tope son lo pendiente y los renglones ya
157
+ * cubiertos se omiten. Cada línea guarda `source_line_id` para el vínculo.
152
158
  */
153
159
  export function linesFromSource(rows, map = {}, opts = {}) {
154
160
  if (!Array.isArray(rows))
155
161
  return [];
156
- return rows.map((raw) => {
162
+ return rows.filter((raw) => !isFullyConsumed(raw)).map((raw) => {
157
163
  const r = (raw ?? {});
158
164
  const get = (k) => r[map[k] ?? k];
159
- const qty = toAmount(get('quantity') ?? 1);
165
+ const pending = hasValue(r.remaining_quantity) ? toAmount(r.remaining_quantity) : undefined;
166
+ const qty = pending ?? toAmount(get('quantity') ?? 1);
160
167
  let rate = get('tax_rate');
161
168
  if (!hasValue(rate)) {
162
169
  const sub = toAmount(get('subtotal'));
@@ -174,11 +181,35 @@ export function linesFromSource(rows, map = {}, opts = {}) {
174
181
  tax_rate: r2 > 1 ? r2 / 100 : r2,
175
182
  unit: get('unit') ?? undefined,
176
183
  });
177
- if (opts.kind === 'credit')
184
+ // El descuento del origen se lee en la unidad del documento (importe en
185
+ // modelos cuyo subtotal resta un monto).
186
+ if (opts.discountMode === 'amount')
187
+ line.discount_kind = 'amount';
188
+ if (pending != null)
189
+ line.max_quantity = pending;
190
+ else if (opts.kind === 'credit')
178
191
  line.max_quantity = hasValue(get('max_quantity')) ? toAmount(get('max_quantity')) : qty;
192
+ const sourceLine = r.source_line_id ?? r.id;
193
+ if (hasValue(sourceLine))
194
+ line.source_line_id = String(sourceLine);
179
195
  return line;
180
196
  });
181
197
  }
198
+ function isFullyConsumed(raw) {
199
+ const r = (raw ?? {});
200
+ return hasValue(r.remaining_quantity) && toAmount(r.remaining_quantity) <= 0;
201
+ }
202
+ /** Cuántos renglones del origen se cargaron y cuántos se omitieron por estar ya cubiertos. */
203
+ export function sourceLoadSummary(rows) {
204
+ if (!Array.isArray(rows))
205
+ return { loaded: 0, covered: 0 };
206
+ const covered = rows.filter(isFullyConsumed).length;
207
+ return { loaded: rows.length - covered, covered };
208
+ }
209
+ /** Fuente que precarga desde lo pendiente servido por el host (no desde la relación cruda). */
210
+ export function sourceTracksRemaining(src) {
211
+ return !!(src.line_link_field || src.remaining_qty_field || src.remaining_endpoint);
212
+ }
182
213
  /** Revisión local (instantánea) de los renglones. Sin ruido: solo lo que hay que mirar. */
183
214
  export function localIssues(lines, opts = {}) {
184
215
  const issues = [];
@@ -191,6 +222,13 @@ export function localIssues(lines, opts = {}) {
191
222
  issues.push({ field: `lines.${i}`, severity: 'error', message: `Renglón ${i + 1}: falta la descripción.` });
192
223
  if (toAmount(l.unit_price) <= 0)
193
224
  issues.push({ field: `lines.${i}`, severity: 'warning', message: `Renglón ${i + 1}: precio en cero.` });
225
+ if (l.max_quantity != null && toAmount(l.quantity) > toAmount(l.max_quantity)) {
226
+ issues.push({
227
+ field: `lines.${i}`,
228
+ severity: 'error',
229
+ message: `Renglón ${i + 1}: la cantidad excede lo pendiente del documento origen (${l.max_quantity}).`,
230
+ });
231
+ }
194
232
  if (opts.requireTax && toAmount(l.tax_rate) === 0) {
195
233
  issues.push({ field: `lines.${i}`, severity: 'warning', message: `Renglón ${i + 1}: sin impuesto.` });
196
234
  }
@@ -1 +1 @@
1
- {"version":3,"file":"document-editor.d.ts","sourceRoot":"","sources":["../../src/business/document-editor.tsx"],"names":[],"mappings":"AA4BA,OAAO,EAAqB,KAAK,sBAAsB,EAAwB,MAAM,qBAAqB,CAAA;AAwC1G,OAAO,KAAK,EAAsC,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAA;AAE3G,MAAM,WAAW,mBAAmB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,qBAAqB,CAAA;IAC5B,IAAI,EAAE,gBAAgB,CAAA;IACtB,sFAAsF;IACtF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC5B,oEAAoE;IACpE,aAAa,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;IAC3C,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACpC,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,cAAc,CAAC,EAAE,sBAAsB,CAAC,QAAQ,CAAC,CAAA;IACjD,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,IAAI,CAAA;CACf;AAID,wBAAgB,cAAc,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,MAAM,EACN,aAAa,EACb,OAAO,EACP,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,cAAc,EACd,QAAQ,EACR,KAAK,GACR,EAAE,mBAAmB,+BA2kBrB"}
1
+ {"version":3,"file":"document-editor.d.ts","sourceRoot":"","sources":["../../src/business/document-editor.tsx"],"names":[],"mappings":"AA4BA,OAAO,EAAqB,KAAK,sBAAsB,EAAwB,MAAM,qBAAqB,CAAA;AA0C1G,OAAO,KAAK,EAAsC,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAA;AAE3G,MAAM,WAAW,mBAAmB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,qBAAqB,CAAA;IAC5B,IAAI,EAAE,gBAAgB,CAAA;IACtB,sFAAsF;IACtF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC5B,oEAAoE;IACpE,aAAa,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;IAC3C,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACpC,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,cAAc,CAAC,EAAE,sBAAsB,CAAC,QAAQ,CAAC,CAAA;IACjD,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,IAAI,CAAA;CACf;AAID,wBAAgB,cAAc,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,MAAM,EACN,aAAa,EACb,OAAO,EACP,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,cAAc,EACd,QAAQ,EACR,KAAK,GACR,EAAE,mBAAmB,+BAgoBrB"}