@asteby/metacore-runtime-react 49.7.0 → 49.9.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 (74) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/business/document-editor-model.d.ts +27 -2
  3. package/dist/business/document-editor-model.d.ts.map +1 -1
  4. package/dist/business/document-editor-model.js +53 -3
  5. package/dist/business/document-editor.d.ts +7 -2
  6. package/dist/business/document-editor.d.ts.map +1 -1
  7. package/dist/business/document-editor.js +121 -22
  8. package/dist/business/document-lines.d.ts +21 -1
  9. package/dist/business/document-lines.d.ts.map +1 -1
  10. package/dist/business/document-lines.js +69 -7
  11. package/dist/business/index.d.ts +1 -0
  12. package/dist/business/index.d.ts.map +1 -1
  13. package/dist/business/index.js +1 -0
  14. package/dist/business/line-items-editor.d.ts +21 -1
  15. package/dist/business/line-items-editor.d.ts.map +1 -1
  16. package/dist/business/line-items-editor.js +74 -15
  17. package/dist/business/line-items.d.ts +28 -0
  18. package/dist/business/line-items.d.ts.map +1 -1
  19. package/dist/business/line-items.js +1 -0
  20. package/dist/business/line-product-cell.d.ts +6 -0
  21. package/dist/business/line-product-cell.d.ts.map +1 -1
  22. package/dist/business/line-product-cell.js +9 -3
  23. package/dist/business/product-display.d.ts +18 -0
  24. package/dist/business/product-display.d.ts.map +1 -0
  25. package/dist/business/product-display.js +40 -0
  26. package/dist/business/product-options.d.ts +6 -1
  27. package/dist/business/product-options.d.ts.map +1 -1
  28. package/dist/business/product-options.js +11 -2
  29. package/dist/business/product-search.d.ts +8 -0
  30. package/dist/business/product-search.d.ts.map +1 -1
  31. package/dist/dialogs/dynamic-record.js +1 -1
  32. package/dist/dynamic-select-field.d.ts +7 -1
  33. package/dist/dynamic-select-field.d.ts.map +1 -1
  34. package/dist/dynamic-select-field.js +18 -4
  35. package/dist/field-grid.d.ts +4 -3
  36. package/dist/field-grid.d.ts.map +1 -1
  37. package/dist/field-grid.js +1 -1
  38. package/dist/index.d.ts +2 -0
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +1 -0
  41. package/dist/option-display.d.ts +68 -0
  42. package/dist/option-display.d.ts.map +1 -0
  43. package/dist/option-display.js +135 -0
  44. package/dist/query-batch.d.ts +1 -1
  45. package/dist/query-batch.d.ts.map +1 -1
  46. package/dist/query-batch.js +3 -1
  47. package/dist/record-picker.d.ts +2 -0
  48. package/dist/record-picker.d.ts.map +1 -1
  49. package/dist/record-picker.js +11 -4
  50. package/dist/use-options-resolver.d.ts +16 -1
  51. package/dist/use-options-resolver.d.ts.map +1 -1
  52. package/dist/use-options-resolver.js +38 -6
  53. package/package.json +1 -1
  54. package/src/__tests__/document-editor-inline-selects.test.tsx +11 -4
  55. package/src/__tests__/line-row-create-edit.test.tsx +312 -0
  56. package/src/__tests__/option-display.test.tsx +241 -0
  57. package/src/business/document-editor-model.ts +60 -2
  58. package/src/business/document-editor.tsx +143 -19
  59. package/src/business/document-lines.ts +79 -7
  60. package/src/business/index.ts +1 -0
  61. package/src/business/line-items-editor.tsx +158 -30
  62. package/src/business/line-items.ts +31 -0
  63. package/src/business/line-product-cell.tsx +32 -5
  64. package/src/business/product-display.ts +62 -0
  65. package/src/business/product-options.tsx +11 -2
  66. package/src/business/product-search.ts +8 -0
  67. package/src/dialogs/dynamic-record.tsx +1 -1
  68. package/src/dynamic-select-field.tsx +45 -1
  69. package/src/field-grid.tsx +5 -4
  70. package/src/index.ts +10 -0
  71. package/src/option-display.tsx +255 -0
  72. package/src/query-batch.ts +2 -0
  73. package/src/record-picker.tsx +16 -3
  74. package/src/use-options-resolver.ts +49 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # @asteby/metacore-runtime-react
2
2
 
3
+ ## 49.9.0
4
+
5
+ ### Minor Changes
6
+
7
+ - be68177: Selectores con presentación declarativa (`option_display` del kernel): el
8
+ `DynamicSelectField` pinta la fila que resolvió el servidor — avatar 32px,
9
+ título a 2 líneas, subtítulo atenuado, badges y métricas a la derecha con
10
+ `tabular-nums` (precio con la moneda de la org, existencia como chip con tono:
11
+ «Agotado» en danger atenuando la fila pero seleccionable, poco stock en
12
+ warning) — y el disparador muestra un resumen compacto; una opción `blocked`
13
+ no se puede elegir. Sin display, la fila de siempre. Nuevos
14
+ `OptionDisplayRow`, `OptionDisplayValue`, `getOptionDisplay`,
15
+ `formatTrailingValue`; `ResolvedOption.display`; contexto del selector
16
+ (`useOptionsResolver({ context })` / `DynamicSelectField optionsContext` →
17
+ `?ctx.<key>=`, también en el token del batch). El buscador de renglones del
18
+ `DocumentEditor` completa sus resultados con una sola consulta `?ids=` al
19
+ catálogo (`withOptionDisplays`, con el almacén del encabezado como contexto),
20
+ así la celda de producto muestra precio + existencia con su tono.
21
+
22
+ ## 49.8.0
23
+
24
+ ### Minor Changes
25
+
26
+ - 13e4aaf: DocumentEditor: crear y editar el producto desde la celda del renglón. «Crear producto «texto»» y «+» abren el alta del modelo de catálogo (metacore:create-record) prellenada con lo buscado y el producto nuevo llena ESE renglón con el mismo mapeo que al elegirlo; el lápiz (hover/foco, accesible por teclado) abre la edición (metacore:edit-record) y al guardar el renglón se actualiza salvo los campos que el usuario sobrescribió (dirty por campo contra la foto del catálogo), con aviso «Precio de catálogo cambió a $X — aplicar». Los renglones de un documento origen no cambian cantidad, tope ni precios. Tarjeta de la contraparte con etiquetas de la metadata (clave corta si la larga no resuelve) y valores de catálogo «G03 · Gastos en general», sin vacíos. Placeholder corto «Buscar producto…» con la ayuda como tooltip y en el estado vacío; el buscador del renglón vacío usa el ancho de las columnas que aún no llena; la unidad bajo el SKU sale con su etiqueta (nunca la clave cruda). RecordPicker: la lista se acota al alto disponible (no se sale del viewport al abrirse hacia arriba), `maxListWidth` y nombres a 2 líneas. FieldLabel y el modal genérico usan sentence case por defecto.
27
+
3
28
  ## 49.7.0
4
29
 
5
30
  ### Minor Changes
@@ -61,8 +61,33 @@ export declare function gatedOptionFixes(fields: readonly ActionFieldDef[], valu
61
61
  * Solo llena lo que está vacío: nunca pisa lo que el usuario ya capturó.
62
62
  */
63
63
  export declare function partyDefaults(party: Record<string, any> | null, documentFieldKeys: readonly string[], current: Record<string, unknown>): Record<string, unknown>;
64
- /** Filas de la tarjeta de contraparte: resumen declarado + extensiones (máximo `limit`). */
65
- export declare function partySummaryRows(party: Record<string, any> | null, summary?: readonly string[], labels?: Record<string, string>, limit?: number): Array<{
64
+ /** Etiqueta y opciones de catálogo de un campo (de la metadata del modelo o del propio tipo). */
65
+ export interface FieldDisplayMeta {
66
+ label?: string;
67
+ options?: ReadonlyArray<{
68
+ value: unknown;
69
+ label?: unknown;
70
+ }>;
71
+ }
72
+ /**
73
+ * Metadata de pantalla por clave a partir de listas de campos/columnas
74
+ * (`/metadata/modal` → fields, `/metadata/table` → columns, campos del tipo).
75
+ * La primera lista que trae etiqueta u opciones para una clave gana.
76
+ */
77
+ export declare function fieldDisplayMeta(lists: ReadonlyArray<ReadonlyArray<unknown> | null | undefined>, translate?: (s: string) => string): Record<string, FieldDisplayMeta>;
78
+ /** `tax_id` → «Tax id»: último recurso cuando ningún metadata etiqueta la columna. */
79
+ export declare function humanizeKey(key: string): string;
80
+ /**
81
+ * Valor de catálogo legible: `G03` con la opción «Gastos en general» →
82
+ * «G03 · Gastos en general» (si la etiqueta ya trae el código, tal cual).
83
+ */
84
+ export declare function formatOptionValue(value: unknown, options?: FieldDisplayMeta['options']): string;
85
+ /**
86
+ * Filas de la tarjeta de contraparte: resumen declarado + extensiones (máximo
87
+ * `limit`). Etiqueta del campo (metadata / labels declarados, ya traducidos),
88
+ * valores de catálogo con su texto y nunca claves crudas ni vacíos.
89
+ */
90
+ export declare function partySummaryRows(party: Record<string, any> | null, summary?: readonly string[], labels?: Record<string, string>, limit?: number, meta?: Record<string, FieldDisplayMeta>): Array<{
66
91
  key: string;
67
92
  label: string;
68
93
  value: string;
@@ -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":"AAMA,OAAO,EAA2B,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAA;AAIrE,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACzF,OAAO,KAAK,EACR,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,yBAAyB,EACzB,qBAAqB,EACrB,gBAAgB,EACnB,MAAM,UAAU,CAAA;AAEjB,eAAO,MAAM,gBAAgB,iBAAiB,CAAA;AAK9C,MAAM,MAAM,iBAAiB,GAAG,iBAAiB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAErE,yEAAyE;AACzE,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,GAAG,SAAS,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAI7G;AAED,qEAAqE;AACrE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,IAAI,CAAC,qBAAqB,EAAE,aAAa,CAAC,GAAG,iBAAiB,GAAG,SAAS,CAK1I;AAeD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,cAAc,EAAE,aAAa,GAAE,WAAW,CAAC,MAAM,CAAa,GAAG,OAAO,CAM1G;AAED,MAAM,WAAW,iBAAiB;IAC9B,iFAAiF;IACjF,KAAK,CAAC,EAAE,cAAc,CAAA;IACtB,8CAA8C;IAC9C,SAAS,EAAE,cAAc,EAAE,CAAA;IAC3B,uCAAuC;IACvC,QAAQ,EAAE,cAAc,EAAE,CAAA;IAC1B,yCAAyC;IACzC,KAAK,EAAE,cAAc,EAAE,CAAA;CAC1B;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAC7B,MAAM,EAAE,SAAS,cAAc,EAAE,EACjC,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,eAAe,CAAC,EAAE,SAAS,cAAc,EAAE,CAAA;CAAO,GACnH,iBAAiB,CAgBnB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,cAAc,GAAG,cAAc,CAGrE;AAOD,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc,CAG/F;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,cAAc,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAc5H;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CACzB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,EACjC,iBAAiB,EAAE,SAAS,MAAM,EAAE,EACpC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAYzB;AAED,iGAAiG;AACjG,MAAM,WAAW,gBAAgB;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,aAAa,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;CAC/D;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC5B,KAAK,EAAE,aAAa,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,EAC/D,SAAS,GAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAiB,GAC5C,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAelC;AAED,sFAAsF;AACtF,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG/C;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,gBAAgB,CAAC,SAAS,CAAC,GAAG,MAAM,CAQ/F;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC5B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,EACjC,OAAO,GAAE,SAAS,MAAM,EAAO,EAC/B,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACnC,KAAK,SAAI,EACT,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAM,GAC5C,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CActD;AAED,MAAM,WAAW,YAAY;IACzB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,+FAA+F;IAC/F,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,OAAO,CAAA;CACjC;AAED,wBAAgB,YAAY,CACxB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,EACjC,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,EAC9F,aAAa,EAAE,MAAM,GACtB,YAAY,GAAG,IAAI,CAarB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC3B,IAAI,EAAE,OAAO,EACb,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EAChC,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAAC,YAAY,CAAC,EAAE,iBAAiB,CAAC,eAAe,CAAC,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5H,QAAQ,EAAE,CA+CZ;AA8BD,gHAAgH;AAChH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,IAAI,CAAC,qBAAqB,EAAE,aAAa,CAAC,GAAG,OAAO,CAGrH;AAED,8FAA8F;AAC9F,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAIpF;AAED,+FAA+F;AAC/F,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,IAAI,CAAC,kBAAkB,EAAE,iBAAiB,GAAG,qBAAqB,GAAG,oBAAoB,CAAC,GAAG,OAAO,CAE9I;AAED,uFAAuF;AACvF,MAAM,WAAW,SAAS;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CAChB;AAWD,4FAA4F;AAC5F,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAO/G;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC7B,GAAG,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,GAAG,YAAY,CAAC,EACtD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACxB,EAAE,EAAE,MAAM,EACV,MAAM,GAAE,SAAS,cAAc,EAAO,GACvC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAa3B;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,EAChC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC7B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAMjC;AAED,MAAM,WAAW,WAAW;IACxB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,OAAO,EAAE,MAAM,CAAA;CAClB;AAED,2FAA2F;AAC3F,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,IAAI,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,WAAW,EAAE,CA0BzH;AAID,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,yBAAyB,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,EAAE,CA0B/G;AAED,wFAAwF;AACxF,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,SAAS,UAAU,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEhG;AAED,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,SAAS,UAAU,EAAE,EAAE,GAAG,EAAE,yBAAyB,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAEpI;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,cAAc,EAAE,GAAG,MAAM,GAAG,SAAS,CAK3F;AAUD,gFAAgF;AAChF,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,EAAE,IAAI,EAAE,SAAS,YAAY,EAAE,GAAG,WAAW,EAAE,CAUxH;AAED,4FAA4F;AAC5F,wBAAgB,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,EAAE,KAAK,GAAE,IAAiB,GAAG,MAAM,CAM/F"}
@@ -152,8 +152,55 @@ export function partyDefaults(party, documentFieldKeys, current) {
152
152
  }
153
153
  return out;
154
154
  }
155
- /** Filas de la tarjeta de contraparte: resumen declarado + extensiones (máximo `limit`). */
156
- export function partySummaryRows(party, summary = [], labels = {}, limit = 6) {
155
+ /**
156
+ * Metadata de pantalla por clave a partir de listas de campos/columnas
157
+ * (`/metadata/modal` → fields, `/metadata/table` → columns, campos del tipo).
158
+ * La primera lista que trae etiqueta u opciones para una clave gana.
159
+ */
160
+ export function fieldDisplayMeta(lists, translate = (s) => s) {
161
+ const out = {};
162
+ for (const list of lists) {
163
+ for (const raw of list ?? []) {
164
+ const f = raw;
165
+ const key = typeof f?.key === 'string' ? f.key : typeof f?.name === 'string' ? f.name : undefined;
166
+ if (!key)
167
+ continue;
168
+ const cur = (out[key] ??= {});
169
+ if (!cur.label && typeof f.label === 'string' && f.label)
170
+ cur.label = translate(f.label);
171
+ if (!cur.options && Array.isArray(f.options) && f.options.length > 0) {
172
+ cur.options = f.options.map((o) => ({ value: o?.value, label: typeof o?.label === 'string' ? translate(o.label) : o?.label }));
173
+ }
174
+ }
175
+ }
176
+ return out;
177
+ }
178
+ /** `tax_id` → «Tax id»: último recurso cuando ningún metadata etiqueta la columna. */
179
+ export function humanizeKey(key) {
180
+ const base = key.replace(EXTENSION_PREFIX, '').replace(/[_.]+/g, ' ').trim();
181
+ return base ? base.charAt(0).toUpperCase() + base.slice(1) : key;
182
+ }
183
+ /**
184
+ * Valor de catálogo legible: `G03` con la opción «Gastos en general» →
185
+ * «G03 · Gastos en general» (si la etiqueta ya trae el código, tal cual).
186
+ */
187
+ export function formatOptionValue(value, options) {
188
+ const raw = String(value);
189
+ const opt = options?.find((o) => String(o.value) === raw);
190
+ const label = opt && typeof opt.label === 'string' ? opt.label.trim() : '';
191
+ if (!label || label === raw)
192
+ return raw;
193
+ if (label.includes(raw))
194
+ return label;
195
+ const codeLike = /^[A-Z0-9]{1,5}$/.test(raw) && /\d/.test(raw);
196
+ return codeLike ? `${raw} · ${label}` : label;
197
+ }
198
+ /**
199
+ * Filas de la tarjeta de contraparte: resumen declarado + extensiones (máximo
200
+ * `limit`). Etiqueta del campo (metadata / labels declarados, ya traducidos),
201
+ * valores de catálogo con su texto y nunca claves crudas ni vacíos.
202
+ */
203
+ export function partySummaryRows(party, summary = [], labels = {}, limit = 6, meta = {}) {
157
204
  if (!party)
158
205
  return [];
159
206
  const keys = new Set(summary);
@@ -166,7 +213,10 @@ export function partySummaryRows(party, summary = [], labels = {}, limit = 6) {
166
213
  const v = readPath(party, key);
167
214
  if (!hasValue(v) || typeof v === 'object')
168
215
  continue;
169
- rows.push({ key, label: labels[key] ?? key.replace(EXTENSION_PREFIX, ''), value: String(v) });
216
+ if (typeof v === 'string' && !v.trim())
217
+ continue;
218
+ const m = meta[key];
219
+ rows.push({ key, label: labels[key] ?? m?.label ?? humanizeKey(key), value: formatOptionValue(v, m?.options) });
170
220
  }
171
221
  return rows.slice(0, limit);
172
222
  }
@@ -16,8 +16,13 @@ export interface DocumentEditorProps {
16
16
  onSaved?: (record?: unknown) => void;
17
17
  onCancel: () => void;
18
18
  searchProducts?: DocumentLinesGridProps['search'];
19
- /** Modelo de catálogo del buscador por defecto. Default `products.Product`. */
19
+ /**
20
+ * Modelo de catálogo: buscador por defecto y alta/edición del producto
21
+ * desde la celda del renglón. Default `products.Product`.
22
+ */
20
23
  productModel?: string;
24
+ /** Columna del modelo de catálogo que recibe lo buscado al crear. Default `name`. */
25
+ productLabelField?: string;
21
26
  defaultTaxRate?: number;
22
27
  currency?: string;
23
28
  /** Hoy (días de atraso del reparto; inyectable en tests). */
@@ -29,5 +34,5 @@ export interface DocumentEditorProps {
29
34
  */
30
35
  fullscreen?: boolean;
31
36
  }
32
- export declare function DocumentEditor({ model, endpoint, forms, type, record, initialSource, onSaved, onCancel, searchProducts, productModel, defaultTaxRate, currency, today, fullscreen, }: DocumentEditorProps): import("react").JSX.Element;
37
+ export declare function DocumentEditor({ model, endpoint, forms, type, record, initialSource, onSaved, onCancel, searchProducts, productModel, productLabelField, defaultTaxRate, currency, today, fullscreen, }: DocumentEditorProps): import("react").JSX.Element;
33
38
  //# sourceMappingURL=document-editor.d.ts.map
@@ -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;AAkD1G,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;IACZ;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;CACvB;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,EACL,UAAkB,GACrB,EAAE,mBAAmB,+BA4xBrB"}
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;AAuD1G,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;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,qFAAqF;IACrF,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,IAAI,CAAA;IACZ;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;CACvB;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,iBAA0B,EAC1B,cAAc,EACd,QAAQ,EACR,KAAK,EACL,UAAkB,GACrB,EAAE,mBAAmB,+BA42BrB"}
@@ -30,8 +30,10 @@ import { FormErrorBanner } from './feedback';
30
30
  import { DocumentLinesGrid } from './line-items-editor';
31
31
  import { computeTotals, serializeLineItems, taxBreakdown } from './line-items';
32
32
  import { roundMoney, toAmount, useFormatter } from './format';
33
- import { createCatalogProductSearch } from './catalog-product-search';
34
- import { allocationAmountField, allocationIssueMessages, allocationPayload, creditStatus, editorLinesConfig, linesFromSource, localIssues, sourceLoadSummary, sourceTracksRemaining, partyDefaults, partySummaryRows, recordLabel, seedRecord, sourceHeaderSeeds, splitEditorFields, toOpenDocuments, friendlyOptionLabel, gatedOptionFixes, gatedOptions, withFriendlyOptions, } from './document-editor-model';
33
+ import { catalogRecordToProduct, createCatalogProductSearch } from './catalog-product-search';
34
+ import { withOptionDisplays } from './product-display';
35
+ import { requestRecordCreate, requestRecordEdit, withSearchPrefill } from '../record-picker-actions';
36
+ import { allocationAmountField, allocationIssueMessages, allocationPayload, creditStatus, editorLinesConfig, fieldDisplayMeta, linesFromSource, localIssues, sourceLoadSummary, sourceTracksRemaining, partyDefaults, partySummaryRows, recordLabel, seedRecord, sourceHeaderSeeds, splitEditorFields, toOpenDocuments, friendlyOptionLabel, gatedOptionFixes, gatedOptions, withFriendlyOptions, } from './document-editor-model';
35
37
  import { CollapsibleSection, DraftPreview, EditorSection, PartyCard, PreviewPanel, TotalsPanel, ValidationChecklist, } from './document-editor-panels';
36
38
  import { PaymentAllocator } from './payment-allocator';
37
39
  import { actionErrorBannerMessage, buildFieldDefaults, prefillFromFieldRelationRequest, renderField } from '../action-modal-dispatcher';
@@ -43,7 +45,7 @@ import { evaluateVisibleWhen, getVisibleWhen } from '../dynamic-form-schema';
43
45
  import { useInstalledAddons } from '../installed-addons-context';
44
46
  import { allocatePayment, validateAllocation } from '../primitives/allocation';
45
47
  import { useDocumentContributions } from '../primitives/use-contributions';
46
- export function DocumentEditor({ model, endpoint, forms, type, record, initialSource, onSaved, onCancel, searchProducts, productModel, defaultTaxRate, currency, today, fullscreen = false, }) {
48
+ export function DocumentEditor({ model, endpoint, forms, type, record, initialSource, onSaved, onCancel, searchProducts, productModel, productLabelField = 'name', defaultTaxRate, currency, today, fullscreen = false, }) {
47
49
  const { t, i18n } = useTranslation();
48
50
  const lang = i18n?.language;
49
51
  const api = useApi();
@@ -64,7 +66,8 @@ export function DocumentEditor({ model, endpoint, forms, type, record, initialSo
64
66
  const [linesFromSourceDoc, setLinesFromSourceDoc] = useState(false);
65
67
  const [extFields, setExtFields] = useState([]);
66
68
  const [party, setParty] = useState(null);
67
- const [partyLabels, setPartyLabels] = useState({});
69
+ const [partyFieldLists, setPartyFieldLists] = useState([]);
70
+ const [catalogMeta, setCatalogMeta] = useState(null);
68
71
  // Etiquetas de los selectores que el editor llena por código (contraparte y
69
72
  // documento origen de «Cargar desde…»): sin ellas el selector cerrado pinta el UUID.
70
73
  const [seeds, setSeeds] = useState({});
@@ -90,7 +93,10 @@ export function DocumentEditor({ model, endpoint, forms, type, record, initialSo
90
93
  const [sourceSeed, setSourceSeed] = useState(null);
91
94
  const relCache = useRef(new Map());
92
95
  const taxRate = defaultTaxRate ?? orgTaxRate;
93
- const search = useMemo(() => searchProducts ?? createCatalogProductSearch(api, { model: productModel, defaultTaxRate: taxRate }), [searchProducts, api, productModel, taxRate]);
96
+ // The header's warehouse (when the document type declares one) scopes the
97
+ // stock the catalog's option_display shows in the product rows.
98
+ const headerWarehouse = typeof header.warehouse_id === 'string' ? header.warehouse_id : '';
99
+ const search = useMemo(() => withOptionDisplays(searchProducts ?? createCatalogProductSearch(api, { model: productModel, defaultTaxRate: taxRate }), api, { model: productModel ?? 'products.Product', context: { warehouse_id: headerWarehouse } }), [searchProducts, api, productModel, taxRate, headerWarehouse]);
94
100
  const isInstalled = useCallback((k) => !k || !installed || installed.addons.has(k), [installed]);
95
101
  const sources = useMemo(() => (type.sources ?? []).filter((s) => isInstalled(s.requires_addon)), [type.sources, isInstalled]);
96
102
  const previewEnabled = !!type.preview && !type.submit_action && isInstalled(type.preview.requires_addon);
@@ -156,28 +162,86 @@ export function DocumentEditor({ model, endpoint, forms, type, record, initialSo
156
162
  cancelled = true;
157
163
  };
158
164
  }, [api, type.party, partyId]);
159
- // Etiquetas de la tarjeta (RFC, régimen…): las del formulario del modelo de
160
- // la contraparte, incluidas sus extensiones; sin ellas se ve la columna cruda.
165
+ // Etiquetas de la tarjeta (RFC, régimen…) y texto de sus catálogos: la
166
+ // metadata del modelo de la contraparte (formulario, con extensiones, y
167
+ // tabla); sin ellas se veía la columna cruda (`tax_id`).
161
168
  const partyModel = type.party?.model;
162
169
  useEffect(() => {
163
170
  if (!partyModel)
164
171
  return;
165
172
  let cancelled = false;
166
- api.get(`/metadata/modal/${partyModel}`)
167
- .then((res) => {
168
- const fields = res?.data?.data?.fields ?? res?.data?.fields ?? [];
169
- const out = {};
170
- for (const f of fields)
171
- if (typeof f?.key === 'string' && typeof f?.label === 'string' && f.label)
172
- out[f.key] = f.label;
173
+ void loadModelFieldLists(api, partyModel).then((lists) => {
173
174
  if (!cancelled)
174
- setPartyLabels(out);
175
- })
176
- .catch(() => { });
175
+ setPartyFieldLists(lists);
176
+ });
177
177
  return () => {
178
178
  cancelled = true;
179
179
  };
180
180
  }, [api, partyModel]);
181
+ // Modelo de catálogo del renglón: alta/edición desde la celda y etiquetas
182
+ // de sus catálogos (unidad «unit» → «Pieza»).
183
+ const catalogModel = productModel ?? 'products.Product';
184
+ // Perezoso: solo cuando ya hay un renglón con producto (no compite con la
185
+ // carga inicial del editor).
186
+ const hasCatalogLine = lines.some((l) => l.kind === 'item' && !!l.product_id);
187
+ const wantsCatalogMeta = !!lineCfg && !isAllocation && hasCatalogLine;
188
+ useEffect(() => {
189
+ if (!wantsCatalogMeta)
190
+ return;
191
+ let cancelled = false;
192
+ void loadModelFieldLists(api, catalogModel).then((lists) => {
193
+ if (!cancelled)
194
+ setCatalogMeta(fieldDisplayMeta(lists, tl));
195
+ });
196
+ return () => {
197
+ cancelled = true;
198
+ };
199
+ }, [api, catalogModel, tl, wantsCatalogMeta]);
200
+ /** Lee el producto guardado con la misma proyección del buscador (catalogRecordToProduct). */
201
+ const loadCatalogProduct = useCallback(async (id, saved) => {
202
+ try {
203
+ const res = await api.get(`/data/${catalogModel}`, { params: { f_id: `eq:${id}`, per_page: 1 } });
204
+ const rows = res?.data?.data;
205
+ const row = Array.isArray(rows) ? rows[0] : null;
206
+ if (row)
207
+ return catalogRecordToProduct({ ...(saved ?? {}), ...row }, taxRate);
208
+ }
209
+ catch {
210
+ // Sin lectura, lo que devolvió el guardado.
211
+ }
212
+ return saved && saved.id != null ? catalogRecordToProduct(saved, taxRate) : null;
213
+ }, [api, catalogModel, taxRate]);
214
+ const createProduct = useCallback((query) => new Promise((resolve) => {
215
+ requestRecordCreate({
216
+ model: catalogModel,
217
+ defaults: withSearchPrefill(query, productLabelField),
218
+ onCreated: (rec) => {
219
+ if (rec?.id == null)
220
+ return resolve(null);
221
+ void loadCatalogProduct(String(rec.id), rec).then(resolve);
222
+ },
223
+ });
224
+ }), [catalogModel, productLabelField, loadCatalogProduct]);
225
+ const editProduct = useCallback((line) => new Promise((resolve) => {
226
+ const id = line.catalog?.product_ref ?? line.product_id;
227
+ if (!id)
228
+ return resolve(null);
229
+ requestRecordEdit({
230
+ model: catalogModel,
231
+ recordId: id,
232
+ onSaved: (rec) => void loadCatalogProduct(id, rec && typeof rec === 'object' ? { id, ...rec } : undefined).then(resolve),
233
+ });
234
+ }), [catalogModel, loadCatalogProduct]);
235
+ /** Unidad visible: la opción del catálogo; sin opciones declaradas, el valor tal cual. */
236
+ const unitLabel = useCallback((u) => {
237
+ if (!catalogMeta)
238
+ return undefined;
239
+ const opts = catalogMeta.unit_of_measure?.options ?? catalogMeta.unit?.options;
240
+ if (!opts)
241
+ return u;
242
+ const o = opts.find((x) => String(x.value) === u);
243
+ return typeof o?.label === 'string' && o.label && o.label !== u ? o.label : undefined;
244
+ }, [catalogMeta]);
181
245
  // Cobro: documentos abiertos de la contraparte.
182
246
  const openCfg = lineCfg?.open_documents;
183
247
  const allocPartyId = openCfg ? header[openCfg.party_field] ?? partyId : undefined;
@@ -483,10 +547,10 @@ export function DocumentEditor({ model, endpoint, forms, type, record, initialSo
483
547
  };
484
548
  const fieldRecord = useMemo(() => seedRecord(seeds, record), [seeds, record]);
485
549
  const fieldCell = (field, fullWidth = false, className) => visible(field) ? (_jsxs(FieldCell, { fullWidth: fullWidth, className: className, children: [_jsx(FieldLabel, { htmlFor: field.key, required: field.required, tone: "sentence", children: tl(field.label) }), renderField(withFriendlyOptions(gatedOptions(field, header)), header[field.key], (v) => updateField(field.key, v), header, fieldRecord, fieldErrors), fieldErrors[field.key] && _jsx("p", { className: "mt-1 text-xs text-destructive", children: fieldErrors[field.key] })] }, field.key)) : null;
486
- const summaryRows = partySummaryRows(party, type.party?.summary, {
487
- ...Object.fromEntries(Object.entries(partyLabels).map(([k, v]) => [k, tl(v)])),
488
- ...Object.fromEntries(allFields.map((f) => [f.key, tl(f.label)])),
489
- });
550
+ // Etiquetas y catálogos: la metadata de la contraparte y, para las
551
+ // extensiones compartidas (`fiscal_data.uso_cfdi`), los campos del propio documento.
552
+ const summaryMeta = useMemo(() => fieldDisplayMeta([...partyFieldLists, allFields], tl), [partyFieldLists, allFields, tl]);
553
+ const summaryRows = partySummaryRows(party, type.party?.summary, {}, 6, summaryMeta);
490
554
  const advancedSummary = groups.advanced
491
555
  .filter(visible)
492
556
  .map((f) => {
@@ -546,7 +610,7 @@ export function DocumentEditor({ model, endpoint, forms, type, record, initialSo
546
610
  const linesSection = lineCfg && (_jsxs(EditorSection, { slot: isAllocation ? 'editor-allocation' : 'editor-lines', title: linesTitle, hint: isAllocation && openBalance > 0 && amountKey ? (_jsx("button", { type: "button", className: "underline-offset-2 hover:underline", onClick: () => updateField(amountKey, openBalance), children: t('documentEditor.pay_all', { defaultValue: 'Cobrar todo ({{amount}})', amount: fmt.money(openBalance) }) })) : undefined, children: [isAllocation ? (allocPartyId ? (_jsx(PaymentAllocator, { documents: openDocs, result: allocation, value: alloc, onChange: setAlloc, currency: currency, loading: docsLoading, today: today })) : (_jsx("p", { className: "text-sm text-muted-foreground", children: t('documentEditor.pick_party_first', { defaultValue: 'Elige el cliente para ver sus documentos con saldo.' }) }))) : (_jsx(DocumentLinesGrid, { value: lines, onChange: (next) => {
547
611
  setLines(next);
548
612
  setServerIssues([]);
549
- }, columns: lineCfg.columns ?? ['discount', 'tax'], priceSource: lineCfg.price_source ?? (kind === 'purchase' ? 'cost' : 'sale'), discountMode: lineCfg.discount_mode, mode: linesFromSourceDoc ? 'from_source' : 'free', policy: kind === 'credit' ? { allowZeroQuantity: linesFromSourceDoc } : undefined, search: search, currency: currency, showTotals: false })), !fullscreen && totalsPanel] }));
613
+ }, columns: lineCfg.columns ?? ['discount', 'tax'], priceSource: lineCfg.price_source ?? (kind === 'purchase' ? 'cost' : 'sale'), discountMode: lineCfg.discount_mode, mode: linesFromSourceDoc ? 'from_source' : 'free', policy: kind === 'credit' ? { allowZeroQuantity: linesFromSourceDoc } : undefined, search: search, currency: currency, showTotals: false, onCreateProduct: kind === 'credit' ? undefined : createProduct, onEditProduct: editProduct, unitLabel: unitLabel })), !fullscreen && totalsPanel] }));
550
614
  const advanced = (groups.advanced.length > 0 || headerContribs.length > 0) && (_jsxs(CollapsibleSection, { slot: "advanced-options", title: t('documentEditor.advanced', { defaultValue: 'Opciones fiscales' }), summary: advancedSummary || t('documentEditor.advanced_defaults', { defaultValue: 'Valores sugeridos' }), open: advancedOpen, onOpenChange: setAdvancedOpen, children: [groups.advanced.length > 0 && _jsx(FieldGrid, { children: groups.advanced.map((f) => fieldCell(f)) }), headerContribs.map(({ id, component: C }) => (_jsx(C, { ...contribProps }, id)))] }));
551
615
  const contributedSections = [...bodyContribs, ...sideContribs].map(({ id, component: C }) => (_jsx("section", { "data-contribution": id, children: _jsx(C, { ...contribProps }) }, id)));
552
616
  const notes = groups.notes.length > 0 && _jsx(FieldGrid, { children: groups.notes.map((f) => fieldCell(f, true)) });
@@ -580,3 +644,38 @@ function SourcePicker({ source, value, seed, header, label, onPick, }) {
580
644
  }), [source, label]);
581
645
  return (_jsx("div", { className: "min-w-60 flex-1", "data-source": source.key, children: renderField(field, value, (v) => onPick(v == null || v === '' ? '' : String(v)), header, seed ? { id: seed.value, label: seed.label } : undefined) }));
582
646
  }
647
+ /**
648
+ * Campos y columnas de un modelo para etiquetar sus valores: formulario
649
+ * (`/metadata/modal`, con extensiones) y tabla (`/metadata/table`). Si la
650
+ * clave con módulo (`customers.Customer`) no resuelve en el host, prueba la
651
+ * corta (`Customer`), como el puente de alta del host. Nunca lanza.
652
+ */
653
+ async function loadModelFieldLists(api, model) {
654
+ const short = model.includes('.') ? model.slice(model.lastIndexOf('.') + 1) : undefined;
655
+ // `undefined` = la petición falló (el host no conoce esa clave); `[]` = respondió sin campos.
656
+ const read = async (url, key) => {
657
+ try {
658
+ const res = await api.get(url);
659
+ const list = res?.data?.data?.[key] ?? res?.data?.[key];
660
+ return Array.isArray(list) ? list : [];
661
+ }
662
+ catch {
663
+ return undefined;
664
+ }
665
+ };
666
+ // En serie y solo lo necesario (una lectura en el caso normal): el
667
+ // formulario; la clave corta solo si la larga falló; la tabla solo si
668
+ // ningún formulario respondió con campos.
669
+ const candidates = short ? [model, short] : [model];
670
+ for (const kind of ['modal', 'table']) {
671
+ for (const m of candidates) {
672
+ const list = await read(`/metadata/${kind}/${m}`, kind === 'modal' ? 'fields' : 'columns');
673
+ if (list === undefined)
674
+ continue;
675
+ if (list.length > 0)
676
+ return [list];
677
+ break;
678
+ }
679
+ }
680
+ return [];
681
+ }
@@ -1,4 +1,4 @@
1
- import { type LineItem } from './line-items';
1
+ import { type CatalogLineField, type LineItem } from './line-items';
2
2
  import { type ProductResult, type ProductVariant } from './product-search';
3
3
  export type PriceSource = 'sale' | 'cost' | 'origin';
4
4
  export type DocumentLinesMode = 'free' | 'from_source';
@@ -10,6 +10,26 @@ export declare function applyProductToLine(line: LineItem, product: ProductResul
10
10
  warehouseId?: string;
11
11
  quantity?: number;
12
12
  }): LineItem;
13
+ /**
14
+ * Campos que el usuario cambió a mano respecto de lo que puso el catálogo
15
+ * («dirty» por campo). Sin foto (renglón libre, o cargado de un documento
16
+ * origen con sus propios precios) todo cuenta como capturado: nada se pisa.
17
+ */
18
+ export declare function lineOverrides(line: LineItem): Set<CatalogLineField>;
19
+ /**
20
+ * El producto del renglón cambió en el catálogo (se editó desde la celda):
21
+ * actualiza SOLO los campos que siguen igual a lo que el catálogo había puesto;
22
+ * lo que el usuario sobrescribió se respeta y el valor nuevo queda en
23
+ * `catalog_pending` para ofrecer «aplicar». Nunca toca la cantidad, el tope de
24
+ * lo pendiente ni el vínculo con el documento origen. Siempre refresca lo que
25
+ * no se captura en el renglón (precio de catálogo, costo, extensiones, existencia).
26
+ */
27
+ export declare function refreshLineFromProduct(line: LineItem, product: ProductResult, opts?: {
28
+ priceSource?: PriceSource;
29
+ warehouseId?: string;
30
+ }): LineItem;
31
+ /** «Aplicar» el aviso del catálogo: los valores pendientes entran al renglón. */
32
+ export declare function applyCatalogPending(line: LineItem): LineItem;
13
33
  /**
14
34
  * Agrega el producto como renglón. Por defecto, un segundo clic del mismo
15
35
  * producto incrementa la cantidad (como el catálogo de Compras) y respeta `max_quantity`.
@@ -1 +1 @@
1
- {"version":3,"file":"document-lines.d.ts","sourceRoot":"","sources":["../../src/business/document-lines.ts"],"names":[],"mappings":"AAGA,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EAAkB,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAE1F,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAA;AACpD,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,aAAa,CAAA;AAEtD,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAC5B,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,cAAc,GAAG,SAAS,EACnC,MAAM,EAAE,WAAW,GACpB,MAAM,CAKR;AAED,uGAAuG;AACvG,wBAAgB,kBAAkB,CAC9B,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE,cAAc,EACxB,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,WAAW,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClF,QAAQ,CAuBV;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC1B,KAAK,EAAE,QAAQ,EAAE,EACjB,OAAO,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE,cAAc,EACxB,IAAI,GAAE;IACF,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gBAAgB,CAAC,EAAE,OAAO,CAAA;CACxB,GACP,QAAQ,EAAE,CAeZ;AAED,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEhE,2GAA2G;AAC3G,wBAAgB,kBAAkB,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,GAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,iBAAiB,CAAA;CAAO,GACxF,eAAe,CAKjB"}
1
+ {"version":3,"file":"document-lines.d.ts","sourceRoot":"","sources":["../../src/business/document-lines.ts"],"names":[],"mappings":"AAGA,OAAO,EAAiC,KAAK,gBAAgB,EAA4B,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5H,OAAO,EAAkB,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAE1F,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAA;AACpD,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,aAAa,CAAA;AAEtD,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAC5B,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,cAAc,GAAG,SAAS,EACnC,MAAM,EAAE,WAAW,GACpB,MAAM,CAKR;AAED,uGAAuG;AACvG,wBAAgB,kBAAkB,CAC9B,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE,cAAc,EACxB,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,WAAW,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClF,QAAQ,CAsBV;AAwBD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,GAAG,CAAC,gBAAgB,CAAC,CAInE;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAClC,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,aAAa,EACtB,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,WAAW,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/D,QAAQ,CAmBV;AAED,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAI5D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC1B,KAAK,EAAE,QAAQ,EAAE,EACjB,OAAO,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE,cAAc,EACxB,IAAI,GAAE;IACF,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gBAAgB,CAAC,EAAE,OAAO,CAAA;CACxB,GACP,QAAQ,EAAE,CAeZ;AAED,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEhE,2GAA2G;AAC3G,wBAAgB,kBAAkB,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,GAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,iBAAiB,CAAA;CAAO,GACxF,eAAe,CAKjB"}
@@ -1,7 +1,7 @@
1
1
  // Helpers del editor único de renglones (DocumentLinesGrid / LineItemsEditor).
2
2
  // Puros: el componente solo pinta. `serializeLineItems` sigue siendo la puerta al backend.
3
3
  import { toAmount } from './format';
4
- import { makeLine } from './line-items';
4
+ import { CATALOG_LINE_FIELDS, makeLine } from './line-items';
5
5
  import { availableStock } from './product-search';
6
6
  /** Precio que se escribe en el renglón según el documento. */
7
7
  export function priceFromProduct(product, variant, source) {
@@ -20,22 +20,84 @@ export function applyProductToLine(line, product, variant, opts = {}) {
20
20
  const catalog = source === 'cost' ? (cost ?? list) : list;
21
21
  // Nunca cantidad 0: un renglón vacío (o con la celda borrada) entra con 1.
22
22
  const qty = opts.quantity ?? toAmount(line.quantity);
23
+ const values = catalogLineValues(line, product, variant, source);
24
+ const { catalog_pending: _pending, ...rest } = line;
23
25
  return {
24
- ...line,
26
+ ...rest,
25
27
  kind: 'item',
26
28
  product_id: variant?.id ?? product.id,
27
- sku: variant?.sku ?? product.sku,
28
- description: variant ? `${product.name} · ${variant.label}` : product.name,
29
+ ...values,
29
30
  quantity: qty !== 0 ? qty : 1,
30
- unit_price: priceFromProduct(product, variant, source),
31
31
  catalog_price: catalog,
32
32
  cost,
33
- tax_rate: product.tax_rate ?? line.tax_rate ?? 0,
34
- unit: product.unit ?? line.unit,
35
33
  extensions: product.extensions ? { ...(line.extensions ?? {}), ...product.extensions } : line.extensions,
36
34
  available: availableStock(src, opts.warehouseId) ?? availableStock(product, opts.warehouseId),
35
+ catalog: { ...values, product_ref: product.id, variant },
36
+ };
37
+ }
38
+ /**
39
+ * Lo que el producto escribe en los campos editables del renglón. Es EL mapeo
40
+ * producto → renglón: lo usan elegir, crear y editar desde la celda.
41
+ */
42
+ function catalogLineValues(line, product, variant, source) {
43
+ return {
44
+ description: variant ? `${product.name} · ${variant.label}` : product.name,
45
+ sku: variant?.sku ?? product.sku,
46
+ unit_price: priceFromProduct(product, variant, source),
47
+ tax_rate: product.tax_rate ?? line.tax_rate ?? 0,
48
+ unit: product.unit ?? line.unit,
37
49
  };
38
50
  }
51
+ const sameValue = (a, b) => typeof a === 'number' || typeof b === 'number' ? toAmount(a) === toAmount(b) : (a ?? '') === (b ?? '');
52
+ /**
53
+ * Campos que el usuario cambió a mano respecto de lo que puso el catálogo
54
+ * («dirty» por campo). Sin foto (renglón libre, o cargado de un documento
55
+ * origen con sus propios precios) todo cuenta como capturado: nada se pisa.
56
+ */
57
+ export function lineOverrides(line) {
58
+ const snap = line.catalog;
59
+ if (!snap)
60
+ return new Set(CATALOG_LINE_FIELDS);
61
+ return new Set(CATALOG_LINE_FIELDS.filter((f) => !sameValue(line[f], snap[f])));
62
+ }
63
+ /**
64
+ * El producto del renglón cambió en el catálogo (se editó desde la celda):
65
+ * actualiza SOLO los campos que siguen igual a lo que el catálogo había puesto;
66
+ * lo que el usuario sobrescribió se respeta y el valor nuevo queda en
67
+ * `catalog_pending` para ofrecer «aplicar». Nunca toca la cantidad, el tope de
68
+ * lo pendiente ni el vínculo con el documento origen. Siempre refresca lo que
69
+ * no se captura en el renglón (precio de catálogo, costo, extensiones, existencia).
70
+ */
71
+ export function refreshLineFromProduct(line, product, opts = {}) {
72
+ const source = opts.priceSource ?? 'sale';
73
+ const variant = line.catalog?.variant;
74
+ const fresh = applyProductToLine(line, product, variant, { ...opts, quantity: toAmount(line.quantity) });
75
+ const overrides = lineOverrides(line);
76
+ const next = { ...fresh, product_id: line.product_id ?? fresh.product_id, quantity: line.quantity, max_quantity: line.max_quantity, source_line_id: line.source_line_id };
77
+ const pending = {};
78
+ const values = catalogLineValues(line, product, variant, source);
79
+ for (const f of CATALOG_LINE_FIELDS) {
80
+ if (!overrides.has(f))
81
+ continue;
82
+ next[f] = line[f];
83
+ if (!sameValue(values[f], line[f]))
84
+ pending[f] = values[f];
85
+ }
86
+ // La foto avanza a los valores nuevos del catálogo: el override sigue siéndolo.
87
+ next.catalog = { ...values, product_ref: line.catalog?.product_ref ?? product.id, variant };
88
+ if (Object.keys(pending).length > 0)
89
+ next.catalog_pending = pending;
90
+ else
91
+ delete next.catalog_pending;
92
+ return next;
93
+ }
94
+ /** «Aplicar» el aviso del catálogo: los valores pendientes entran al renglón. */
95
+ export function applyCatalogPending(line) {
96
+ if (!line.catalog_pending)
97
+ return line;
98
+ const { catalog_pending: pending, ...rest } = line;
99
+ return { ...rest, ...pending };
100
+ }
39
101
  /**
40
102
  * Agrega el producto como renglón. Por defecto, un segundo clic del mismo
41
103
  * producto incrementa la cantidad (como el catálogo de Compras) y respeta `max_quantity`.
@@ -22,6 +22,7 @@ export * from './vehicle-picker';
22
22
  export * from './relate-documents';
23
23
  export * from './print-send-dialog';
24
24
  export * from './catalog-product-search';
25
+ export * from './product-display';
25
26
  export * from './document-editor-model';
26
27
  export * from './document-editor-panels';
27
28
  export * from './payment-allocator';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/business/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,kBAAkB,CAAA;AAChC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,WAAW,CAAA;AACzB,cAAc,mBAAmB,CAAA;AACjC,cAAc,UAAU,CAAA;AACxB,cAAc,sBAAsB,CAAA;AACpC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA;AACjC,cAAc,WAAW,CAAA;AACzB,cAAc,UAAU,CAAA;AACxB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA;AAC5B,cAAc,wBAAwB,CAAA;AACtC,cAAc,kBAAkB,CAAA;AAChC,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA;AACnC,cAAc,0BAA0B,CAAA;AACxC,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA;AACxC,cAAc,qBAAqB,CAAA;AACnC,cAAc,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/business/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,kBAAkB,CAAA;AAChC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,WAAW,CAAA;AACzB,cAAc,mBAAmB,CAAA;AACjC,cAAc,UAAU,CAAA;AACxB,cAAc,sBAAsB,CAAA;AACpC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA;AACjC,cAAc,WAAW,CAAA;AACzB,cAAc,UAAU,CAAA;AACxB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA;AAC5B,cAAc,wBAAwB,CAAA;AACtC,cAAc,kBAAkB,CAAA;AAChC,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA;AACnC,cAAc,0BAA0B,CAAA;AACxC,cAAc,mBAAmB,CAAA;AACjC,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA;AACxC,cAAc,qBAAqB,CAAA;AACnC,cAAc,mBAAmB,CAAA"}
@@ -22,6 +22,7 @@ export * from './vehicle-picker';
22
22
  export * from './relate-documents';
23
23
  export * from './print-send-dialog';
24
24
  export * from './catalog-product-search';
25
+ export * from './product-display';
25
26
  export * from './document-editor-model';
26
27
  export * from './document-editor-panels';
27
28
  export * from './payment-allocator';
@@ -45,10 +45,30 @@ export interface DocumentLinesGridProps {
45
45
  * ya muestra sus propios totales (DocumentEditor) lo apaga.
46
46
  */
47
47
  showTotals?: boolean;
48
+ /**
49
+ * Alta del producto desde la celda («+» y «Crear producto «texto»»). Resuelve
50
+ * con el producto guardado (o nada si se canceló): el renglón se llena con
51
+ * el mismo mapeo que al elegir uno existente.
52
+ */
53
+ onCreateProduct?: (query: string) => Promise<ProductResult | null | undefined>;
54
+ /**
55
+ * Edición del producto del renglón (lápiz en la celda). Resuelve con el
56
+ * producto ya guardado: el renglón se actualiza salvo los campos que el
57
+ * usuario sobrescribió (ver `refreshLineFromProduct`).
58
+ */
59
+ onEditProduct?: (line: LineItem) => Promise<ProductResult | null | undefined>;
60
+ /** Nombre del modelo de catálogo para «Crear …» / «Editar …» (default «producto»). */
61
+ productLabel?: string;
62
+ /**
63
+ * Etiqueta visible de la unidad (`unit` → «pza»). Si devuelve vacío, la
64
+ * unidad no se muestra bajo el SKU (nunca la clave cruda). Sin esta prop se
65
+ * muestra el valor tal cual.
66
+ */
67
+ unitLabel?: (unit: string) => string | undefined;
48
68
  }
49
69
  /** @deprecated Usa `DocumentLinesGridProps`. Es el mismo contrato. */
50
70
  export type LineItemsEditorProps = DocumentLinesGridProps;
51
- export declare function DocumentLinesGrid({ value, onChange, columns, policy, serverErrors, onValidate, onRequestProduct, search, priceSource, mode, discountMode, quantityStepper, mergeSameProduct, warehouseId, allowSections, readOnly, editPermission, currency, showTotals, }: DocumentLinesGridProps): import("react").JSX.Element;
71
+ export declare function DocumentLinesGrid({ value, onChange, columns, policy, serverErrors, onValidate, onRequestProduct, search, priceSource, mode, discountMode, quantityStepper, mergeSameProduct, warehouseId, allowSections, readOnly, editPermission, currency, showTotals, onCreateProduct, onEditProduct, productLabel, unitLabel, }: DocumentLinesGridProps): import("react").JSX.Element;
52
72
  /** Mismo componente. El nombre anterior sigue exportado. */
53
73
  export declare const LineItemsEditor: typeof DocumentLinesGrid;
54
74
  //# sourceMappingURL=line-items-editor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"line-items-editor.d.ts","sourceRoot":"","sources":["../../src/business/line-items-editor.tsx"],"names":[],"mappings":"AASA,OAAO,EAIH,KAAK,iBAAiB,EACtB,KAAK,WAAW,EACnB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAKH,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,mBAAmB,EAC3B,MAAM,cAAc,CAAA;AAErB,OAAO,EACH,KAAK,YAAY,EACjB,KAAK,aAAa,EAErB,MAAM,kBAAkB,CAAA;AAEzB,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,KAAK,GAAG,YAAY,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAA;AAExF,MAAM,WAAW,sBAAsB;IACnC,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,gEAAgE;IAChE,QAAQ,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAA;IACrC,6DAA6D;IAC7D,OAAO,CAAC,EAAE,eAAe,EAAE,CAAA;IAC3B,MAAM,CAAC,EAAE,eAAe,CAAA;IACxB,sFAAsF;IACtF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACrC,uFAAuF;IACvF,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,mBAAmB,KAAK,IAAI,CAAA;IAC7C,mFAAmF;IACnF,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC7B;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,YAAY,EAAE;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC,CAAA;IAC3G,4FAA4F;IAC5F,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,mFAAmF;IACnF,IAAI,CAAC,EAAE,iBAAiB,CAAA;IACxB,oFAAoF;IACpF,YAAY,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAA;IAC5C,6DAA6D;IAC7D,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,gFAAgF;IAChF,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,sEAAsE;AACtE,MAAM,MAAM,oBAAoB,GAAG,sBAAsB,CAAA;AAIzD,wBAAgB,iBAAiB,CAAC,EAC9B,KAAK,EACL,QAAQ,EACR,OAAsB,EACtB,MAAM,EACN,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,MAAM,EACN,WAAoB,EACpB,IAAa,EACb,YAAwB,EACxB,eAAe,EACf,gBAAuB,EACvB,WAAW,EACX,aAAoB,EACpB,QAAgB,EAChB,cAAc,EACd,QAAQ,EACR,UAAiB,GACpB,EAAE,sBAAsB,+BAwbxB;AAED,4DAA4D;AAC5D,eAAO,MAAM,eAAe,0BAAoB,CAAA"}
1
+ {"version":3,"file":"line-items-editor.d.ts","sourceRoot":"","sources":["../../src/business/line-items-editor.tsx"],"names":[],"mappings":"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"}