@asteby/metacore-runtime-react 49.1.0 → 49.2.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.
- package/CHANGELOG.md +26 -0
- package/dist/action-modal-dispatcher.d.ts +1 -0
- package/dist/action-modal-dispatcher.d.ts.map +1 -1
- package/dist/action-modal-dispatcher.js +34 -5
- package/dist/business/document-editor-model.d.ts +6 -1
- package/dist/business/document-editor-model.d.ts.map +1 -1
- package/dist/business/document-editor-model.js +48 -7
- package/dist/business/document-editor-panels.d.ts +18 -0
- package/dist/business/document-editor-panels.d.ts.map +1 -1
- package/dist/business/document-editor-panels.js +9 -0
- package/dist/business/document-editor.d.ts +7 -1
- package/dist/business/document-editor.d.ts.map +1 -1
- package/dist/business/document-editor.js +69 -24
- package/dist/document-form-dialog.d.ts.map +1 -1
- package/dist/document-form-dialog.js +8 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/record-mutation-events.d.ts +12 -0
- package/dist/record-mutation-events.d.ts.map +1 -1
- package/dist/record-mutation-events.js +18 -0
- package/package.json +1 -1
- package/src/__tests__/document-editor-model.test.ts +27 -0
- package/src/__tests__/document-editor.test.tsx +60 -0
- package/src/__tests__/invoice-document-editor.test.tsx +267 -0
- package/src/__tests__/r5-self-label-settled.test.ts +57 -0
- package/src/action-modal-dispatcher.tsx +34 -5
- package/src/business/document-editor-model.ts +50 -7
- package/src/business/document-editor-panels.tsx +49 -0
- package/src/business/document-editor.tsx +246 -164
- package/src/document-form-dialog.tsx +13 -4
- package/src/index.ts +2 -0
- package/src/record-mutation-events.ts +23 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 49.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 4fca0af: DocumentEditor a pantalla completa para documentos con renglones (factura, cotización, pedido, OC).
|
|
8
|
+
|
|
9
|
+
- **Pantalla completa:** DocumentFormDialog abre el editor de un tipo `layout: "editor"` con renglones de venta o compra en un diálogo que ocupa la pantalla (máx. 1440 px). En pantallas anchas va a dos columnas: captura a la izquierda; totales, revisión y vista previa a la derecha. NC y cobro/REP siguen en el diálogo de 960 px.
|
|
10
|
+
- **Divulgación progresiva:** lo obligatorio que el tipo declara queda a la vista aunque sea de extensión (`fiscal_data.metodo_pago`, `fiscal_data.forma_pago` de la factura); las extensiones que llegan del metadata siguen plegadas en «Opciones fiscales».
|
|
11
|
+
- **Tarjeta de la contraparte:** sus filas usan las etiquetas del formulario del modelo de la contraparte (`/metadata/modal/<party.model>`): «RFC», «Régimen fiscal», «Uso CFDI» en vez de `tax_id` o `regimen_receptor`.
|
|
12
|
+
- **«Cargar desde…»:** con varias fuentes, botones por tipo de origen (venta, OT, cotización) en lugar de un `<select>`. Un origen sin dato de impuesto (p. ej. una OT de taller) toma la tasa de la org, como un renglón nuevo.
|
|
13
|
+
- **Vista previa local:** un tipo con renglones y sin acción `preview` del servidor ofrece una vista previa plegada del documento (encabezado, renglones y totales) sin crear borrador.
|
|
14
|
+
- i18n es/en: `documentEditor.source_kind`, `documentEditor.preview`, `documentEditor.advanced`.
|
|
15
|
+
|
|
16
|
+
- 4fca0af: Retest Pitsline r5 (nota de crédito y acciones):
|
|
17
|
+
|
|
18
|
+
- «Crear desde»: un renglón de origen sin descripción propia (InvoiceItem solo
|
|
19
|
+
lleva el producto) se nombra con la etiqueta del producto que resuelve el
|
|
20
|
+
host, y un renglón con producto ya no exige texto para guardar. El fallo de
|
|
21
|
+
`source-lines` (404, 400/500 de un host sin la ruta) cae a la relación.
|
|
22
|
+
- `ActionModalDispatcher` anuncia la mutación ahora y otra vez cuando el
|
|
23
|
+
servidor asienta lo derivado (`emitRecordMutationSettled`,
|
|
24
|
+
`RECORD_MUTATION_SETTLE_DELAYS`): la lista y la pestaña REP se refrescan
|
|
25
|
+
tras timbrar o registrar un pago.
|
|
26
|
+
- Un selector sembrado con el id de la propia fila muestra su folio
|
|
27
|
+
(«Factura a abonar: FAC-00015») y no el UUID.
|
|
28
|
+
|
|
3
29
|
## 49.1.0
|
|
4
30
|
|
|
5
31
|
### Minor Changes
|
|
@@ -106,6 +106,7 @@ export interface FieldDefaultContext {
|
|
|
106
106
|
*/
|
|
107
107
|
export declare function resolveFieldDefault(field: ActionFieldDef, ctx?: FieldDefaultContext): unknown;
|
|
108
108
|
export declare function buildFieldDefaults(fields: ActionFieldDef[], record: any, model?: string, ctx?: FieldDefaultContext): Record<string, any>;
|
|
109
|
+
export declare function seedOptionFromRecord(field: ActionFieldDef, value: any, record?: Record<string, any>): import('./use-options-resolver').ResolvedOption | undefined;
|
|
109
110
|
/** Renders one action field (same widgets as the action modals). Shared with DocumentFormDialog. */
|
|
110
111
|
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;
|
|
111
112
|
//# 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":"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
|
|
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,EAKxB,MAAM,sBAAsB,CAAA;AAQ7B,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;AAgBD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAMzG;AAYD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAsBnG;AAED,wBAAgB,qBAAqB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,MAAM,EACN,QAAQ,EACR,SAAS,EAAE,aAAa,GAC3B,EAAE,gBAAgB,sCAoHlB;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;AA6bD,4DAA4D;AAC5D,MAAM,WAAW,mBAAmB;IAChC,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,qBAAqB;IACrB,GAAG,CAAC,EAAE,IAAI,CAAA;CACb;AAID;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,GAAE,mBAAwB,GAAG,OAAO,CASjG;AAKD,wBAAgB,kBAAkB,CAC9B,MAAM,EAAE,cAAc,EAAE,EACxB,MAAM,EAAE,GAAG,EACX,KAAK,CAAC,EAAE,MAAM,EACd,GAAG,GAAE,mBAAwB,GAC9B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAoBrB;AAwPD,wBAAgB,oBAAoB,CAChC,KAAK,EAAE,cAAc,EACrB,KAAK,EAAE,GAAG,EACV,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC7B,OAAO,wBAAwB,EAAE,cAAc,GAAG,SAAS,CAuB7D;AAED,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"}
|
|
@@ -15,7 +15,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
15
15
|
//
|
|
16
16
|
// The host injects its axios-like client via <ApiProvider>; we no longer
|
|
17
17
|
// depend on a bundler alias to `@/lib/api`.
|
|
18
|
-
import {
|
|
18
|
+
import { emitRecordMutationSettled } from './record-mutation-events';
|
|
19
19
|
import { Suspense, useState, useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';
|
|
20
20
|
import { useTranslation } from 'react-i18next';
|
|
21
21
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Button, Input, Textarea, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Switch, } from '@asteby/metacore-ui/primitives';
|
|
@@ -288,9 +288,11 @@ export function scalarDefaultFromRecord(field, record, model) {
|
|
|
288
288
|
export function ActionModalDispatcher({ open, onOpenChange, action, model, record, endpoint, onSuccess: onSuccessProp, }) {
|
|
289
289
|
// Every successful action may have created/updated/deleted a record of
|
|
290
290
|
// `model` (federated component, wizard, generic form or confirmation), so
|
|
291
|
-
// announce it: mounted lists of the model reload on their own (#1020)
|
|
291
|
+
// announce it: mounted lists of the model reload on their own (#1020) —
|
|
292
|
+
// and again once the server's event subscribers had time to write what the
|
|
293
|
+
// action triggers (estado tras timbrar, REP tras un pago).
|
|
292
294
|
const onSuccess = useCallback(() => {
|
|
293
|
-
|
|
295
|
+
emitRecordMutationSettled(model, 'update');
|
|
294
296
|
onSuccessProp?.();
|
|
295
297
|
}, [model, onSuccessProp]);
|
|
296
298
|
// Reactive read: a federated remote usually registers AFTER the first
|
|
@@ -1107,8 +1109,35 @@ function WizardActionModal({ open, onOpenChange, action, model, record, endpoint
|
|
|
1107
1109
|
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)] }, field.key));
|
|
1108
1110
|
}) })] }), _jsxs(DialogFooter, { className: "shrink-0", children: [stepIndex > 0 ? (_jsx(Button, { variant: "outline", onClick: goBack, disabled: executing, children: t('common.back', { defaultValue: 'Atrás' }) })) : (_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: executing, children: t('common.cancel') })), isLast ? (_jsxs(Button, { onClick: submit, 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)] })) : (_jsx(Button, { onClick: goNext, disabled: executing, style: action.color ? { backgroundColor: action.color, color: 'white' } : undefined, children: t('common.next', { defaultValue: 'Siguiente' }) }))] }), reasonPrompt.dialog] }) }));
|
|
1109
1111
|
}
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
+
/** Columnas con las que se nombra una fila cuando el selector no declara `options.label`. */
|
|
1113
|
+
const RECORD_LABEL_COLUMNS = ['number', 'folio', 'name', 'title', 'code', 'label'];
|
|
1114
|
+
/**
|
|
1115
|
+
* La etiqueta de la PROPIA fila cuando el campo se sembró con su id
|
|
1116
|
+
* («Factura a abonar» al registrar un pago desde la factura): la columna que el
|
|
1117
|
+
* selector usa de etiqueta (`options.label`) o la primera de nombre que tenga.
|
|
1118
|
+
* Sin esto el selector mostraba el UUID crudo (retest Pitsline r5).
|
|
1119
|
+
*/
|
|
1120
|
+
function selfLabel(field, record) {
|
|
1121
|
+
const opts = field.options;
|
|
1122
|
+
const declared = opts && !Array.isArray(opts) && typeof opts === 'object' ? opts.label : undefined;
|
|
1123
|
+
const cols = typeof declared === 'string' && declared ? [declared, ...RECORD_LABEL_COLUMNS] : RECORD_LABEL_COLUMNS;
|
|
1124
|
+
for (const c of cols) {
|
|
1125
|
+
const v = unwrapRecordScalar(record[c]);
|
|
1126
|
+
if ((typeof v === 'string' && v.trim()) || typeof v === 'number')
|
|
1127
|
+
return String(v);
|
|
1128
|
+
}
|
|
1129
|
+
return '';
|
|
1130
|
+
}
|
|
1131
|
+
export function seedOptionFromRecord(field, value, record) {
|
|
1132
|
+
if (!record)
|
|
1133
|
+
return undefined;
|
|
1134
|
+
const selfId = unwrapRecordScalar(record.id);
|
|
1135
|
+
if (value != null && value !== '' && selfId != null && String(selfId) === String(value)) {
|
|
1136
|
+
const label = selfLabel(field, record);
|
|
1137
|
+
if (label)
|
|
1138
|
+
return { id: String(value), value: String(value), label, name: label };
|
|
1139
|
+
}
|
|
1140
|
+
if (!field.key.endsWith('_id'))
|
|
1112
1141
|
return undefined;
|
|
1113
1142
|
const siblingKey = field.key.replace(/_id$/, '');
|
|
1114
1143
|
const sib = record[siblingKey];
|
|
@@ -11,7 +11,9 @@ export declare function isEditorLayout(type: Pick<DocumentFormType, 'layout' | '
|
|
|
11
11
|
export declare function editorLinesConfig(type: DocumentFormType, forms: Pick<DocumentFormsManifest, 'lines_field'>): EditorLinesConfig | undefined;
|
|
12
12
|
/**
|
|
13
13
|
* Divulgación progresiva: ¿el campo va plegado en «Opciones fiscales»?
|
|
14
|
-
* - campos de extensión (`fiscal_data.*`) que aportan otros addons
|
|
14
|
+
* - campos de extensión (`fiscal_data.*`) que aportan otros addons (los que
|
|
15
|
+
* el propio tipo declara obligatorios, p. ej. método y forma de pago de la
|
|
16
|
+
* factura, se marcan esenciales en splitEditorFields);
|
|
15
17
|
* - `section: "fiscal" | "advanced"`;
|
|
16
18
|
* - catálogos opcionales que ya traen un default (método, serie, relación…):
|
|
17
19
|
* el usuario no necesita tocarlos para guardar.
|
|
@@ -83,7 +85,10 @@ export declare function creditStatus(party: Record<string, any> | null, cols: {
|
|
|
83
85
|
export declare function linesFromSource(rows: unknown, map?: Record<string, string>, opts?: {
|
|
84
86
|
kind?: DocumentFormLines['kind'];
|
|
85
87
|
discountMode?: DocumentFormLines['discount_mode'];
|
|
88
|
+
defaultTaxRate?: number;
|
|
86
89
|
}): LineItem[];
|
|
90
|
+
/** El editor ocupa toda la pantalla: documentos comerciales con renglones (factura, cotización, pedido, OC). */
|
|
91
|
+
export declare function isFullscreenEditor(type: DocumentFormType, forms: Pick<DocumentFormsManifest, 'lines_field'>): boolean;
|
|
87
92
|
/** Cuántos renglones del origen se cargaron y cuántos se omitieron por estar ya cubiertos. */
|
|
88
93
|
export declare function sourceLoadSummary(rows: unknown): {
|
|
89
94
|
loaded: number;
|
|
@@ -1 +1 @@
|
|
|
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;AAGrE,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
|
|
1
|
+
{"version":3,"file":"document-editor-model.d.ts","sourceRoot":"","sources":["../../src/business/document-editor-model.ts"],"names":[],"mappings":"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"}
|
|
@@ -37,7 +37,9 @@ function declaredDefault(f) {
|
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
39
|
* Divulgación progresiva: ¿el campo va plegado en «Opciones fiscales»?
|
|
40
|
-
* - campos de extensión (`fiscal_data.*`) que aportan otros addons
|
|
40
|
+
* - campos de extensión (`fiscal_data.*`) que aportan otros addons (los que
|
|
41
|
+
* el propio tipo declara obligatorios, p. ej. método y forma de pago de la
|
|
42
|
+
* factura, se marcan esenciales en splitEditorFields);
|
|
41
43
|
* - `section: "fiscal" | "advanced"`;
|
|
42
44
|
* - catálogos opcionales que ya traen un default (método, serie, relación…):
|
|
43
45
|
* el usuario no necesita tocarlos para guardar.
|
|
@@ -55,7 +57,10 @@ export function isAdvancedField(f, essentialKeys = new Set()) {
|
|
|
55
57
|
}
|
|
56
58
|
/** Reparte los campos del tipo (más los de extensión) en grupos de pantalla. */
|
|
57
59
|
export function splitEditorFields(fields, opts = {}) {
|
|
58
|
-
|
|
60
|
+
// Lo obligatorio que el tipo declara siempre está a la vista, aunque sea de
|
|
61
|
+
// extensión (`fiscal_data.metodo_pago`): es lo que el usuario debe decidir.
|
|
62
|
+
// Las extensiones que llegan del metadata (no declaradas) siguen plegadas.
|
|
63
|
+
const essentialKeys = new Set([...(opts.essentialKeys ?? []), ...fields.filter((f) => f.required).map((f) => f.key)]);
|
|
59
64
|
const out = { essential: [], advanced: [], notes: [] };
|
|
60
65
|
const seen = new Set();
|
|
61
66
|
for (const f of [...fields, ...(opts.extensionFields ?? [])]) {
|
|
@@ -168,13 +173,18 @@ export function linesFromSource(rows, map = {}, opts = {}) {
|
|
|
168
173
|
if (!hasValue(rate)) {
|
|
169
174
|
const sub = toAmount(get('subtotal'));
|
|
170
175
|
const tax = toAmount(get('tax_amount'));
|
|
171
|
-
|
|
176
|
+
// Un origen sin dato de impuesto (p. ej. una OT de taller: solo
|
|
177
|
+
// cantidad y precio) toma la tasa de la org, como un renglón nuevo.
|
|
178
|
+
rate = hasValue(get('tax_amount'))
|
|
179
|
+
? sub > 0 ? Math.round((tax / sub) * 10000) / 10000 : 0
|
|
180
|
+
: opts.defaultTaxRate ?? 0;
|
|
172
181
|
}
|
|
173
182
|
const r2 = toAmount(rate);
|
|
183
|
+
const product = get('product_id');
|
|
174
184
|
const line = makeLine({
|
|
175
|
-
product_id:
|
|
185
|
+
product_id: refValue(product),
|
|
176
186
|
sku: get('sku') ?? undefined,
|
|
177
|
-
description: String(get('description') ?? get('product_name') ?? ''),
|
|
187
|
+
description: String(get('description') ?? get('product_name') ?? refLabel(product) ?? refLabel(r.product) ?? ''),
|
|
178
188
|
quantity: qty,
|
|
179
189
|
unit_price: toAmount(get('unit_price')),
|
|
180
190
|
discount: toAmount(get('discount') ?? 0),
|
|
@@ -195,10 +205,38 @@ export function linesFromSource(rows, map = {}, opts = {}) {
|
|
|
195
205
|
return line;
|
|
196
206
|
});
|
|
197
207
|
}
|
|
208
|
+
/** Id de una celda de relación (`{value,label}`, `{id,name}` o el id plano). */
|
|
209
|
+
function refValue(v) {
|
|
210
|
+
if (v == null || v === '')
|
|
211
|
+
return undefined;
|
|
212
|
+
if (typeof v === 'object') {
|
|
213
|
+
const o = v;
|
|
214
|
+
const id = o.value ?? o.id;
|
|
215
|
+
return id == null || id === '' ? undefined : String(id);
|
|
216
|
+
}
|
|
217
|
+
return String(v);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Nombre de una celda de relación ya resuelta por el host (`{label}`/`{name}`).
|
|
221
|
+
* Un renglón de origen sin descripción propia (InvoiceItem solo lleva el
|
|
222
|
+
* producto) se nombra con su producto en vez de entrar en blanco.
|
|
223
|
+
*/
|
|
224
|
+
function refLabel(v) {
|
|
225
|
+
if (!v || typeof v !== 'object')
|
|
226
|
+
return undefined;
|
|
227
|
+
const o = v;
|
|
228
|
+
const l = o.label ?? o.name;
|
|
229
|
+
return typeof l === 'string' && l.trim() ? l : undefined;
|
|
230
|
+
}
|
|
198
231
|
function isFullyConsumed(raw) {
|
|
199
232
|
const r = (raw ?? {});
|
|
200
233
|
return hasValue(r.remaining_quantity) && toAmount(r.remaining_quantity) <= 0;
|
|
201
234
|
}
|
|
235
|
+
/** El editor ocupa toda la pantalla: documentos comerciales con renglones (factura, cotización, pedido, OC). */
|
|
236
|
+
export function isFullscreenEditor(type, forms) {
|
|
237
|
+
const kind = editorLinesConfig(type, forms)?.kind ?? 'sale';
|
|
238
|
+
return !!type.lines && (kind === 'sale' || kind === 'purchase');
|
|
239
|
+
}
|
|
202
240
|
/** Cuántos renglones del origen se cargaron y cuántos se omitieron por estar ya cubiertos. */
|
|
203
241
|
export function sourceLoadSummary(rows) {
|
|
204
242
|
if (!Array.isArray(rows))
|
|
@@ -218,8 +256,11 @@ export function localIssues(lines, opts = {}) {
|
|
|
218
256
|
issues.push({ field: 'lines', severity: 'error', message: 'Agrega al menos un renglón.' });
|
|
219
257
|
}
|
|
220
258
|
items.forEach((l, i) => {
|
|
221
|
-
|
|
222
|
-
|
|
259
|
+
// Un renglón con producto se describe con él (el servidor lo nombra
|
|
260
|
+
// desde el catálogo): solo un renglón libre exige texto, como en el
|
|
261
|
+
// editor de renglones (validateLineItems).
|
|
262
|
+
if (!l.description.trim() && !l.product_id)
|
|
263
|
+
issues.push({ field: `lines.${i}`, severity: 'error', message: `Renglón ${i + 1}: elige un producto o escribe la descripción.` });
|
|
223
264
|
if (toAmount(l.unit_price) <= 0)
|
|
224
265
|
issues.push({ field: `lines.${i}`, severity: 'warning', message: `Renglón ${i + 1}: precio en cero.` });
|
|
225
266
|
if (l.max_quantity != null && toAmount(l.quantity) > toAmount(l.max_quantity)) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import type { CreditStatus, EditorIssue } from './document-editor-model';
|
|
3
3
|
import type { Formatter } from './format';
|
|
4
|
+
import { type LineItem } from './line-items';
|
|
4
5
|
/** Bloque con título discreto y espaciado generoso (sin bordes pesados). */
|
|
5
6
|
export declare function EditorSection({ title, hint, children, slot }: {
|
|
6
7
|
title?: ReactNode;
|
|
@@ -61,4 +62,21 @@ export interface PreviewContent {
|
|
|
61
62
|
export declare function PreviewPanel({ preview }: {
|
|
62
63
|
preview: PreviewContent | null;
|
|
63
64
|
}): import("react").JSX.Element | null;
|
|
65
|
+
export interface DraftPreviewProps {
|
|
66
|
+
title: string;
|
|
67
|
+
party?: string;
|
|
68
|
+
/** Datos del encabezado ya con su etiqueta y valor legibles. */
|
|
69
|
+
header: Array<{
|
|
70
|
+
label: string;
|
|
71
|
+
value: string;
|
|
72
|
+
}>;
|
|
73
|
+
lines: LineItem[];
|
|
74
|
+
totals: TotalsRow[];
|
|
75
|
+
fmt: Formatter;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Vista previa local del documento (sin servidor ni borrador): cómo queda con
|
|
79
|
+
* lo capturado. Para tipos sin acción `preview`; la del servidor (PDF/XML) gana.
|
|
80
|
+
*/
|
|
81
|
+
export declare function DraftPreview({ title, party, header, lines, totals, fmt }: DraftPreviewProps): import("react").JSX.Element;
|
|
64
82
|
//# sourceMappingURL=document-editor-panels.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-editor-panels.d.ts","sourceRoot":"","sources":["../../src/business/document-editor-panels.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAGvD,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"document-editor-panels.d.ts","sourceRoot":"","sources":["../../src/business/document-editor-panels.tsx"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
6
6
|
import { useId, useState } from 'react';
|
|
7
7
|
import { Badge } from '@asteby/metacore-ui/primitives';
|
|
8
8
|
import { AlertTriangle, ChevronDown, XCircle } from 'lucide-react';
|
|
9
|
+
import { computeLine } from './line-items';
|
|
9
10
|
/** Bloque con título discreto y espaciado generoso (sin bordes pesados). */
|
|
10
11
|
export function EditorSection({ title, hint, children, slot }) {
|
|
11
12
|
return (_jsxs("section", { className: "space-y-3", "data-slot": slot, children: [(title || hint) && (_jsxs("header", { className: "flex items-baseline justify-between gap-3", children: [title && _jsx("h3", { className: "text-sm font-semibold", children: title }), hint && _jsx("span", { className: "text-xs text-muted-foreground", children: hint })] })), children] }));
|
|
@@ -56,3 +57,11 @@ export function PreviewPanel({ preview }) {
|
|
|
56
57
|
return _jsx("pre", { className: "max-h-96 overflow-auto rounded bg-muted p-3 text-[11px] leading-tight", "data-slot": "preview-xml", children: preview.xml });
|
|
57
58
|
return null;
|
|
58
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Vista previa local del documento (sin servidor ni borrador): cómo queda con
|
|
62
|
+
* lo capturado. Para tipos sin acción `preview`; la del servidor (PDF/XML) gana.
|
|
63
|
+
*/
|
|
64
|
+
export function DraftPreview({ title, party, header, lines, totals, fmt }) {
|
|
65
|
+
const items = lines.filter((l) => l.kind === 'item');
|
|
66
|
+
return (_jsxs("article", { className: "space-y-3 rounded border bg-background p-4 text-xs", "data-slot": "preview-draft", children: [_jsxs("header", { className: "flex flex-wrap items-baseline justify-between gap-2 border-b pb-2", children: [_jsx("span", { className: "text-sm font-semibold", children: title }), party && _jsx("span", { className: "font-medium", children: party })] }), header.length > 0 && (_jsx("dl", { className: "grid grid-cols-2 gap-x-4 gap-y-1", children: header.map((h) => (_jsxs("div", { className: "min-w-0", children: [_jsx("dt", { className: "text-muted-foreground", children: h.label }), _jsx("dd", { className: "truncate", children: h.value })] }, h.label))) })), _jsx("table", { className: "w-full tabular-nums", children: _jsx("tbody", { children: items.map((l) => (_jsxs("tr", { className: "border-t align-top", children: [_jsx("td", { className: "py-1 pr-2", children: l.description }), _jsx("td", { className: "py-1 pr-2 text-right", children: l.quantity }), _jsx("td", { className: "py-1 text-right", children: fmt.money(computeLine(l).net) })] }, l.key))) }) }), _jsx(TotalsPanel, { rows: totals })] }));
|
|
67
|
+
}
|
|
@@ -22,6 +22,12 @@ export interface DocumentEditorProps {
|
|
|
22
22
|
currency?: string;
|
|
23
23
|
/** Hoy (días de atraso del reparto; inyectable en tests). */
|
|
24
24
|
today?: Date;
|
|
25
|
+
/**
|
|
26
|
+
* Pantalla completa (DocumentFormDialog la pide para documentos con
|
|
27
|
+
* renglones): a dos columnas en pantallas anchas, con totales, revisión y
|
|
28
|
+
* vista previa a la derecha; en una sola columna en móvil.
|
|
29
|
+
*/
|
|
30
|
+
fullscreen?: boolean;
|
|
25
31
|
}
|
|
26
|
-
export declare function DocumentEditor({ model, endpoint, forms, type, record, initialSource, onSaved, onCancel, searchProducts, productModel, defaultTaxRate, currency, today, }: DocumentEditorProps): import("react").JSX.Element;
|
|
32
|
+
export declare function DocumentEditor({ model, endpoint, forms, type, record, initialSource, onSaved, onCancel, searchProducts, productModel, defaultTaxRate, currency, today, fullscreen, }: DocumentEditorProps): import("react").JSX.Element;
|
|
27
33
|
//# 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;
|
|
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;AA4C1G,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,+BAysBrB"}
|
|
@@ -31,8 +31,8 @@ import { DocumentLinesGrid } from './line-items-editor';
|
|
|
31
31
|
import { computeTotals, serializeLineItems, taxBreakdown } from './line-items';
|
|
32
32
|
import { roundMoney, toAmount, useFormatter } from './format';
|
|
33
33
|
import { createCatalogProductSearch } from './catalog-product-search';
|
|
34
|
-
import { allocationAmountField, allocationIssueMessages, allocationPayload, creditStatus, editorLinesConfig, linesFromSource, localIssues, sourceLoadSummary, sourceTracksRemaining, partyDefaults, partySummaryRows, splitEditorFields, toOpenDocuments, withFriendlyOptions, } from './document-editor-model';
|
|
35
|
-
import { CollapsibleSection, EditorSection, PartyCard, PreviewPanel, TotalsPanel, ValidationChecklist, } from './document-editor-panels';
|
|
34
|
+
import { allocationAmountField, allocationIssueMessages, allocationPayload, creditStatus, editorLinesConfig, linesFromSource, localIssues, sourceLoadSummary, sourceTracksRemaining, partyDefaults, partySummaryRows, splitEditorFields, toOpenDocuments, friendlyOptionLabel, withFriendlyOptions, } from './document-editor-model';
|
|
35
|
+
import { CollapsibleSection, DraftPreview, EditorSection, PartyCard, PreviewPanel, TotalsPanel, ValidationChecklist, } from './document-editor-panels';
|
|
36
36
|
import { PaymentAllocator } from './payment-allocator';
|
|
37
37
|
import { actionErrorBannerMessage, buildFieldDefaults, prefillFromFieldRelationRequest, renderField } from '../action-modal-dispatcher';
|
|
38
38
|
import { extractFieldErrors, localizeFieldErrorMap } from '../server-error';
|
|
@@ -43,7 +43,7 @@ import { evaluateVisibleWhen, getVisibleWhen } from '../dynamic-form-schema';
|
|
|
43
43
|
import { useInstalledAddons } from '../installed-addons-context';
|
|
44
44
|
import { allocatePayment, validateAllocation } from '../primitives/allocation';
|
|
45
45
|
import { useDocumentContributions } from '../primitives/use-contributions';
|
|
46
|
-
export function DocumentEditor({ model, endpoint, forms, type, record, initialSource, onSaved, onCancel, searchProducts, productModel, defaultTaxRate, currency, today, }) {
|
|
46
|
+
export function DocumentEditor({ model, endpoint, forms, type, record, initialSource, onSaved, onCancel, searchProducts, productModel, defaultTaxRate, currency, today, fullscreen = false, }) {
|
|
47
47
|
const { t, i18n } = useTranslation();
|
|
48
48
|
const lang = i18n?.language;
|
|
49
49
|
const api = useApi();
|
|
@@ -64,6 +64,7 @@ export function DocumentEditor({ model, endpoint, forms, type, record, initialSo
|
|
|
64
64
|
const [linesFromSourceDoc, setLinesFromSourceDoc] = useState(false);
|
|
65
65
|
const [extFields, setExtFields] = useState([]);
|
|
66
66
|
const [party, setParty] = useState(null);
|
|
67
|
+
const [partyLabels, setPartyLabels] = useState({});
|
|
67
68
|
const [openDocs, setOpenDocs] = useState([]);
|
|
68
69
|
const [docsLoading, setDocsLoading] = useState(false);
|
|
69
70
|
const [alloc, setAlloc] = useState({ strategy: 'oldest_due_first', manual: {} });
|
|
@@ -137,6 +138,28 @@ export function DocumentEditor({ model, endpoint, forms, type, record, initialSo
|
|
|
137
138
|
cancelled = true;
|
|
138
139
|
};
|
|
139
140
|
}, [api, type.party, partyId]);
|
|
141
|
+
// Etiquetas de la tarjeta (RFC, régimen…): las del formulario del modelo de
|
|
142
|
+
// la contraparte, incluidas sus extensiones; sin ellas se ve la columna cruda.
|
|
143
|
+
const partyModel = type.party?.model;
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
if (!partyModel)
|
|
146
|
+
return;
|
|
147
|
+
let cancelled = false;
|
|
148
|
+
api.get(`/metadata/modal/${partyModel}`)
|
|
149
|
+
.then((res) => {
|
|
150
|
+
const fields = res?.data?.data?.fields ?? res?.data?.fields ?? [];
|
|
151
|
+
const out = {};
|
|
152
|
+
for (const f of fields)
|
|
153
|
+
if (typeof f?.key === 'string' && typeof f?.label === 'string' && f.label)
|
|
154
|
+
out[f.key] = f.label;
|
|
155
|
+
if (!cancelled)
|
|
156
|
+
setPartyLabels(out);
|
|
157
|
+
})
|
|
158
|
+
.catch(() => { });
|
|
159
|
+
return () => {
|
|
160
|
+
cancelled = true;
|
|
161
|
+
};
|
|
162
|
+
}, [api, partyModel]);
|
|
140
163
|
// Cobro: documentos abiertos de la contraparte.
|
|
141
164
|
const openCfg = lineCfg?.open_documents;
|
|
142
165
|
const allocPartyId = openCfg ? header[openCfg.party_field] ?? partyId : undefined;
|
|
@@ -246,9 +269,11 @@ export function DocumentEditor({ model, endpoint, forms, type, record, initialSo
|
|
|
246
269
|
});
|
|
247
270
|
return res?.data?.data ?? [];
|
|
248
271
|
}
|
|
249
|
-
catch
|
|
250
|
-
|
|
251
|
-
|
|
272
|
+
catch {
|
|
273
|
+
// Un host sin `source-lines` (404, o la ruta del registro
|
|
274
|
+
// contestando 400/500 a «source-lines» como id) o un origen que
|
|
275
|
+
// el host aún no sabe calcular: se precarga de la relación y el
|
|
276
|
+
// guardado del servidor sigue validando lo pendiente.
|
|
252
277
|
}
|
|
253
278
|
}
|
|
254
279
|
let rels = relCache.current.get(src.model);
|
|
@@ -271,7 +296,7 @@ export function DocumentEditor({ model, endpoint, forms, type, record, initialSo
|
|
|
271
296
|
try {
|
|
272
297
|
if (!isAllocation) {
|
|
273
298
|
const rows = await fetchSourceRows(src, id);
|
|
274
|
-
const loaded = linesFromSource(rows, src.map, { kind, discountMode: lineCfg.discount_mode });
|
|
299
|
+
const loaded = linesFromSource(rows, src.map, { kind, discountMode: lineCfg.discount_mode, defaultTaxRate: taxRate });
|
|
275
300
|
const { covered } = sourceLoadSummary(rows);
|
|
276
301
|
setLines(loaded);
|
|
277
302
|
setLoadedSource(src);
|
|
@@ -311,7 +336,7 @@ export function DocumentEditor({ model, endpoint, forms, type, record, initialSo
|
|
|
311
336
|
finally {
|
|
312
337
|
setBusy(null);
|
|
313
338
|
}
|
|
314
|
-
}, [api, lineCfg, isAllocation, kind, t, fetchSourceRows]);
|
|
339
|
+
}, [api, lineCfg, isAllocation, kind, t, fetchSourceRows, taxRate]);
|
|
315
340
|
// Prefill al abrir desde un documento origen (acción de fila «Crear NC», «Facturar venta»…).
|
|
316
341
|
const autoLoaded = useRef(false);
|
|
317
342
|
useEffect(() => {
|
|
@@ -430,7 +455,10 @@ export function DocumentEditor({ model, endpoint, forms, type, record, initialSo
|
|
|
430
455
|
setFieldErrors((prev) => clearFieldErrorTree(prev, key));
|
|
431
456
|
};
|
|
432
457
|
const fieldCell = (field, fullWidth = false) => visible(field) ? (_jsxs(FieldCell, { fullWidth: fullWidth, children: [_jsx(FieldLabel, { htmlFor: field.key, required: field.required, children: tl(field.label) }), renderField(withFriendlyOptions(field), header[field.key], (v) => updateField(field.key, v), header, undefined, fieldErrors), fieldErrors[field.key] && _jsx("p", { className: "mt-1 text-xs text-destructive", children: fieldErrors[field.key] })] }, field.key)) : null;
|
|
433
|
-
const summaryRows = partySummaryRows(party, type.party?.summary,
|
|
458
|
+
const summaryRows = partySummaryRows(party, type.party?.summary, {
|
|
459
|
+
...Object.fromEntries(Object.entries(partyLabels).map(([k, v]) => [k, tl(v)])),
|
|
460
|
+
...Object.fromEntries(allFields.map((f) => [f.key, tl(f.label)])),
|
|
461
|
+
});
|
|
434
462
|
const advancedSummary = groups.advanced
|
|
435
463
|
.filter(visible)
|
|
436
464
|
.map((f) => {
|
|
@@ -460,26 +488,43 @@ export function DocumentEditor({ model, endpoint, forms, type, record, initialSo
|
|
|
460
488
|
})),
|
|
461
489
|
{ label: t('documentEditor.total', { defaultValue: 'Total' }), value: fmt.money(totals.total), emphasis: true },
|
|
462
490
|
];
|
|
463
|
-
const activeSource = sources.find((s) => s.key === sourceKey);
|
|
491
|
+
const activeSource = sources.find((s) => s.key === sourceKey) ?? (sources.length === 1 ? sources[0] : undefined);
|
|
464
492
|
const linesTitle = lineCfg?.title
|
|
465
493
|
? tl(lineCfg.title)
|
|
466
494
|
: isAllocation
|
|
467
495
|
? t('documentEditor.allocation_title', { defaultValue: 'Documentos a pagar' })
|
|
468
496
|
: t('documentEditor.lines_title', { defaultValue: 'Conceptos' });
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
497
|
+
// Sin acción de vista previa del servidor, un documento con renglones ofrece
|
|
498
|
+
// la suya local (encabezado, renglones y totales): sin borrador ni red.
|
|
499
|
+
const localPreview = !previewEnabled && !type.preview && !!lineCfg && !isAllocation && kind !== 'credit';
|
|
500
|
+
const essentials = (_jsxs(EditorSection, { slot: "editor-essentials", children: [_jsxs(FieldGrid, { children: [groups.party && fieldCell(groups.party), groups.essential.map((f) => fieldCell(f))] }), party && (_jsx(PartyCard, { name: party.name ?? party.legal_name, rows: summaryRows, credit: credit, fmt: fmt, children: partyContribs.map(({ id, component: C }) => (_jsx(C, { ...contribProps }, id))) }))] }));
|
|
501
|
+
const loadFrom = sources.length > 0 && !isAllocation && (_jsx(EditorSection, { slot: "load-from-source", title: t('documentEditor.load_from', { defaultValue: 'Cargar desde…' }), hint: busy === 'source' ? t('documentEditor.loading_source', { defaultValue: 'Cargando…' }) : sourceNote, children: _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [sources.length > 1 && (_jsx("div", { role: "radiogroup", "aria-label": t('documentEditor.source_kind', { defaultValue: 'Tipo de documento origen' }), className: "inline-flex flex-wrap gap-1 rounded-md border p-1", children: sources.map((s) => (_jsx("button", { type: "button", role: "radio", "aria-checked": s.key === sourceKey, "data-source-kind": s.key, onClick: () => {
|
|
502
|
+
setSourceKey(s.key);
|
|
503
|
+
setSourceId('');
|
|
504
|
+
}, className: `rounded px-3 py-1.5 text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${s.key === sourceKey ? 'bg-primary/10 font-medium text-primary' : 'text-muted-foreground hover:bg-accent'}`, children: tl(s.label) }, s.key))) })), activeSource && (_jsx(SourcePicker, { source: activeSource, value: sourceId, header: header, label: tl(activeSource.label), onPick: (id) => {
|
|
505
|
+
setSourceKey(activeSource.key);
|
|
506
|
+
setSourceId(id);
|
|
507
|
+
if (id)
|
|
508
|
+
void loadFromSource(activeSource, id);
|
|
509
|
+
} }, activeSource.key))] }) }));
|
|
510
|
+
const totalsPanel = _jsx(TotalsPanel, { rows: totalsRows });
|
|
511
|
+
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) => {
|
|
512
|
+
setLines(next);
|
|
513
|
+
setServerIssues([]);
|
|
514
|
+
}, 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 })), !fullscreen && totalsPanel] }));
|
|
515
|
+
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)))] }));
|
|
516
|
+
const contributedSections = [...bodyContribs, ...sideContribs].map(({ id, component: C }) => (_jsx("section", { "data-contribution": id, children: _jsx(C, { ...contribProps }) }, id)));
|
|
517
|
+
const notes = groups.notes.length > 0 && _jsx(FieldGrid, { children: groups.notes.map((f) => fieldCell(f, true)) });
|
|
518
|
+
const previewSection = previewEnabled ? (_jsx(CollapsibleSection, { slot: "preview", title: tl(type.preview.label ?? 'Vista previa'), open: previewOpen, onOpenChange: togglePreview, children: busy === 'preview' ? (_jsxs("p", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": true }), t('documentEditor.preview_loading', { defaultValue: 'Generando vista previa…' })] })) : (_jsxs(_Fragment, { children: [_jsx(PreviewPanel, { preview: preview }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => void runPreview(), disabled: busy !== null, children: t('documentEditor.preview_refresh', { defaultValue: 'Actualizar vista previa' }) })] })) })) : localPreview ? (_jsx(CollapsibleSection, { slot: "preview", title: t('documentEditor.preview', { defaultValue: 'Vista previa' }), open: previewOpen, onOpenChange: setPreviewOpen, children: _jsx(DraftPreview, { title: tl(type.label), party: party ? String(party.name ?? party.legal_name ?? '') : undefined, header: [...groups.essential, ...groups.advanced]
|
|
519
|
+
.filter(visible)
|
|
520
|
+
.map((f) => {
|
|
521
|
+
const v = header[f.key];
|
|
522
|
+
const opt = f.options?.find((o) => String(o.value) === String(v));
|
|
523
|
+
return { label: tl(f.label), value: v == null || v === '' ? '' : opt ? friendlyOptionLabel(tl(String(opt.label ?? v))) : String(v) };
|
|
524
|
+
})
|
|
525
|
+
.filter((r) => r.value), lines: lines, totals: totalsRows, fmt: fmt }) })) : null;
|
|
526
|
+
const checklist = _jsx(ValidationChecklist, { issues: shownIssues, title: t('documentEditor.review', { defaultValue: 'Revisa antes de guardar' }) });
|
|
527
|
+
return (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col", "data-slot": "document-editor", "data-kind": kind, "data-layout": fullscreen ? 'fullscreen' : 'dialog', children: [fullscreen ? (_jsxs("div", { className: "-mx-1 min-h-0 flex-1 overflow-y-auto px-1 py-2 lg:grid lg:grid-cols-[minmax(0,1fr)_340px] lg:gap-8", children: [_jsxs("div", { className: "min-w-0 space-y-8", "data-slot": "editor-main", children: [_jsx(FormErrorBanner, { message: formError }), essentials, loadFrom, linesSection, advanced, contributedSections, notes] }), _jsxs("aside", { className: "mt-8 space-y-6 lg:sticky lg:top-0 lg:mt-0 lg:self-start", "data-slot": "editor-aside", children: [_jsx("div", { className: "rounded-lg border p-4", children: totalsPanel }), checklist, previewSection] })] })) : (_jsxs("div", { className: "-mx-1 min-h-0 flex-1 space-y-8 overflow-y-auto px-1 py-2", children: [_jsx(FormErrorBanner, { message: formError }), essentials, loadFrom, linesSection, advanced, contributedSections, notes, previewSection, checklist] })), _jsxs("footer", { className: "flex shrink-0 flex-wrap items-center justify-end gap-2 border-t pt-4", "data-slot": "editor-actions", children: [footerContribs.map(({ id, component: C }) => (_jsx(C, { ...contribProps }, id))), _jsx(Button, { type: "button", variant: "ghost", onClick: onCancel, disabled: busy === 'save', children: t('common.cancel', { defaultValue: 'Cancelar' }) }), _jsxs(Button, { type: "button", "data-primary": "true", onClick: () => void submit(), disabled: busy !== null, children: [busy === 'save' && _jsx(Loader2, { className: "mr-2 size-4 animate-spin", "aria-hidden": true }), type.submit_label ? tl(type.submit_label) : t('common.save', { defaultValue: 'Guardar' })] })] })] }));
|
|
483
528
|
}
|
|
484
529
|
/** Selector del documento origen (mismo widget de relación que el resto de formularios). */
|
|
485
530
|
function SourcePicker({ source, value, header, label, onPick, }) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-form-dialog.d.ts","sourceRoot":"","sources":["../src/document-form-dialog.tsx"],"names":[],"mappings":"AA6BA,OAAO,EAEH,KAAK,sBAAsB,EAE9B,MAAM,8BAA8B,CAAA;AAerC,OAAO,KAAK,EAER,gBAAgB,EAChB,qBAAqB,EACrB,aAAa,EAChB,MAAM,SAAS,CAAA;AAEhB,MAAM,WAAW,uBAAuB;IACpC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,qBAAqB,CAAA;IAC5B,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uCAAuC;IACvC,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACpC;;;OAGG;IACH,cAAc,CAAC,EAAE,sBAAsB,CAAC,QAAQ,CAAC,CAAA;IACjD,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,0GAA0G;IAC1G,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC5B,gFAAgF;IAChF,aAAa,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;IAC3C;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAA;CACvD;AAED,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC5B,sFAAsF;IACtF,KAAK,EAAE,MAAM,CAAA;IACb,2BAA2B;IAC3B,IAAI,EAAE,gBAAgB,CAAA;CACzB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,qBAAqB,GAAG,SAAS,GAAG,eAAe,GAAG,SAAS,CAIrG;AAED,8EAA8E;AAC9E,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,qBAAqB,GAAG,SAAS,GAAG,qBAAqB,GAAG,SAAS,CAKjH;AAED,uFAAuF;AACvF,wBAAgB,oBAAoB,CAChC,QAAQ,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,IAAI,EACvD,QAAQ,CAAC,EAAE,qBAAqB,GAAG,IAAI,GACxC,qBAAqB,GAAG,SAAS,CAGnC;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACtC,KAAK,EAAE,qBAAqB,GAAG,SAAS,EACxC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GACxC;IAAE,KAAK,EAAE,qBAAqB,GAAG,SAAS,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAQnE;AAID,wBAAgB,kBAAkB,CAAC,EAC/B,IAAI,EACJ,YAAY,EACZ,KAAK,EACL,QAAQ,EACR,KAAK,EACL,WAAW,EACX,OAAO,EACP,cAAc,EACd,YAAY,EACZ,cAAc,EACd,QAAQ,EACR,MAAM,EACN,aAAa,EACb,gBAAgB,GACnB,EAAE,uBAAuB,
|
|
1
|
+
{"version":3,"file":"document-form-dialog.d.ts","sourceRoot":"","sources":["../src/document-form-dialog.tsx"],"names":[],"mappings":"AA6BA,OAAO,EAEH,KAAK,sBAAsB,EAE9B,MAAM,8BAA8B,CAAA;AAerC,OAAO,KAAK,EAER,gBAAgB,EAChB,qBAAqB,EACrB,aAAa,EAChB,MAAM,SAAS,CAAA;AAEhB,MAAM,WAAW,uBAAuB;IACpC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,qBAAqB,CAAA;IAC5B,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uCAAuC;IACvC,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACpC;;;OAGG;IACH,cAAc,CAAC,EAAE,sBAAsB,CAAC,QAAQ,CAAC,CAAA;IACjD,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,0GAA0G;IAC1G,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC5B,gFAAgF;IAChF,aAAa,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;IAC3C;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAA;CACvD;AAED,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC5B,sFAAsF;IACtF,KAAK,EAAE,MAAM,CAAA;IACb,2BAA2B;IAC3B,IAAI,EAAE,gBAAgB,CAAA;CACzB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,qBAAqB,GAAG,SAAS,GAAG,eAAe,GAAG,SAAS,CAIrG;AAED,8EAA8E;AAC9E,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,qBAAqB,GAAG,SAAS,GAAG,qBAAqB,GAAG,SAAS,CAKjH;AAED,uFAAuF;AACvF,wBAAgB,oBAAoB,CAChC,QAAQ,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,IAAI,EACvD,QAAQ,CAAC,EAAE,qBAAqB,GAAG,IAAI,GACxC,qBAAqB,GAAG,SAAS,CAGnC;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACtC,KAAK,EAAE,qBAAqB,GAAG,SAAS,EACxC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GACxC;IAAE,KAAK,EAAE,qBAAqB,GAAG,SAAS,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAQnE;AAID,wBAAgB,kBAAkB,CAAC,EAC/B,IAAI,EACJ,YAAY,EACZ,KAAK,EACL,QAAQ,EACR,KAAK,EACL,WAAW,EACX,OAAO,EACP,cAAc,EACd,YAAY,EACZ,cAAc,EACd,QAAQ,EACR,MAAM,EACN,aAAa,EACb,gBAAgB,GACnB,EAAE,uBAAuB,sCAiVzB"}
|
|
@@ -30,7 +30,7 @@ import { emitRecordMutation } from './record-mutation-events';
|
|
|
30
30
|
import { isLineItemsField, resolveWidget, scopeValueFromFilterToken } from './dynamic-form-schema';
|
|
31
31
|
import { createCatalogProductSearch, withDefaultTaxRate } from './business/catalog-product-search';
|
|
32
32
|
import { DocumentEditor } from './business/document-editor';
|
|
33
|
-
import { editorLinesConfig, isEditorLayout } from './business/document-editor-model';
|
|
33
|
+
import { editorLinesConfig, isEditorLayout, isFullscreenEditor } from './business/document-editor-model';
|
|
34
34
|
/**
|
|
35
35
|
* Si «Crear» de estos formularios es un alta delegada: un único tipo (p. ej.
|
|
36
36
|
* ya acotado por el filtro de la vista) con `create_model`. Con varios tipos el
|
|
@@ -239,9 +239,14 @@ export function DocumentFormDialog({ open, onOpenChange, model, endpoint, forms,
|
|
|
239
239
|
if (open && type && delegatedModel)
|
|
240
240
|
return null;
|
|
241
241
|
// layout "editor": una sola pantalla por secciones (DocumentEditor). Sin él
|
|
242
|
-
// sigue el wizard de siempre (retrocompatible).
|
|
242
|
+
// sigue el wizard de siempre (retrocompatible). Un documento con renglones
|
|
243
|
+
// (factura, cotización, pedido, OC) ocupa toda la pantalla; NC y cobro
|
|
244
|
+
// siguen en el diálogo grande.
|
|
243
245
|
if (open && type && step !== 'type' && (isEditorLayout(type) || !!initialSource)) {
|
|
244
|
-
|
|
246
|
+
const fullscreen = isFullscreenEditor(type, forms);
|
|
247
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: fullscreen ? 'flex flex-col overflow-hidden sm:max-w-none' : 'flex max-h-[92dvh] flex-col overflow-hidden', style: fullscreen
|
|
248
|
+
? { width: 'calc(100vw - 2rem)', maxWidth: '1440px', height: 'calc(100dvh - 2rem)', maxHeight: 'calc(100dvh - 2rem)' }
|
|
249
|
+
: { maxHeight: '92dvh', maxWidth: '960px', width: '95vw' }, "data-slot": "document-editor-dialog", "data-fullscreen": fullscreen ? 'true' : undefined, children: [_jsxs(DialogHeader, { className: "shrink-0", children: [_jsx(DialogTitle, { children: tl(type.label) }), type.description && _jsx(DialogDescription, { children: tl(type.description) })] }), _jsx(DocumentEditor, { model: model, endpoint: endpoint, forms: forms, type: type, record: record, initialSource: initialSource, searchProducts: lineSearch, currency: currency, fullscreen: fullscreen, onCancel: () => (canGoBackToTypes ? setStep('type') : onOpenChange(false)), onSaved: (rec) => {
|
|
245
250
|
onSaved?.(rec);
|
|
246
251
|
onOpenChange(false);
|
|
247
252
|
} }, type.key)] }) }));
|
package/dist/index.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ export { todayInZone, isTodayToken } from './calendar-date';
|
|
|
32
32
|
export { setFederatedActionLoader, type FederatedActionLoader } from './federated-action-loader';
|
|
33
33
|
export { ModelActionToolbar, useModelActions, type ModelActionToolbarProps, type ActionPlacement, } from './model-action-toolbar';
|
|
34
34
|
export { resolveListPrimaryAction, actionReplacesCreate, type ListPrimaryActionInput, type ListPrimaryActionResult, type ListActionRef, type CreateMode, } from './list-primary-action';
|
|
35
|
-
export { emitRecordMutation, subscribeRecordMutations, RECORD_MUTATION_EVENT, type RecordMutationKind, type RecordMutationDetail, } from './record-mutation-events';
|
|
35
|
+
export { emitRecordMutation, emitRecordMutationSettled, subscribeRecordMutations, RECORD_MUTATION_EVENT, RECORD_MUTATION_SETTLE_DELAYS, type RecordMutationKind, type RecordMutationDetail, } from './record-mutation-events';
|
|
36
36
|
export { DocumentFormDialog, resolveDocumentForms, scopeDocumentFormsToFilter, delegatedCreate, withoutDelegatedTypes, type DelegatedCreate, type DocumentFormDialogProps, } from './document-form-dialog';
|
|
37
37
|
export { modelFromNavUrl, capabilityForNavItem, isNavItemAllowed, useNavItemVisible, type NavLeafLike, } from './nav-permissions';
|
|
38
38
|
export { CfdiStampPanel, CfdiStampResultDialog, extractStampResult, type CfdiStampResult, type CfdiStampPanelProps, type CfdiStampResultDialogProps, } from './cfdi-stamp-panel';
|