This commit is contained in:
David Arranz 2026-07-08 11:58:58 +02:00
parent 05f4b1337b
commit a464f9b14e
25 changed files with 218 additions and 88 deletions

View File

@ -1,8 +1,9 @@
import { type StartParams } from "@erp/core/api"; import type { StartParams } from "@erp/core/api";
import { requireIdentityTenant } from "@erp/identity/api"; import { requireIdentityTenant } from "@erp/identity/api";
import { type NextFunction, type Request, type Response, Router } from "express"; import { type NextFunction, type Request, type Response, Router } from "express";
import type { InvoiceSeriesInternalDeps } from "../di/invoice-series.di"; import type { InvoiceSeriesInternalDeps } from "../di/invoice-series.di";
import { ListInvoiceSeriesController } from "./controllers"; import { ListInvoiceSeriesController } from "./controllers";
export const invoiceSeriesRouter = (params: StartParams) => { export const invoiceSeriesRouter = (params: StartParams) => {
@ -20,5 +21,5 @@ export const invoiceSeriesRouter = (params: StartParams) => {
return controller.execute(req, res, next); return controller.execute(req, res, next);
}); });
app.use(`${config.server.apiBasePath}/invoice-series`, router); app.use(`${config.server.apiBasePath}/catalogs/invoice-series`, router);
}; };

View File

@ -1,7 +1,4 @@
import { import { SequelizeRepository, translateSequelizeError } from "@erp/core/api";
SequelizeRepository,
translateSequelizeError,
} from "@erp/core/api";
import type { UniqueID } from "@repo/rdx-ddd"; import type { UniqueID } from "@repo/rdx-ddd";
import { Collection, Result } from "@repo/rdx-utils"; import { Collection, Result } from "@repo/rdx-utils";
import type { Sequelize, Transaction } from "sequelize"; import type { Sequelize, Transaction } from "sequelize";
@ -11,7 +8,7 @@ import {
InvoiceSeriesConcurrencyError, InvoiceSeriesConcurrencyError,
InvoiceSeriesLockRequiredError, InvoiceSeriesLockRequiredError,
InvoiceSeriesTransactionRequiredError, InvoiceSeriesTransactionRequiredError,
} from "../../../../../application/invoice-series"; } from "../../../../../application";
import type { InvoiceSeries, InvoiceSeriesCode } from "../../../../../domain"; import type { InvoiceSeries, InvoiceSeriesCode } from "../../../../../domain";
import { CustomerInvoiceSeriesModel } from "../../../../common"; import { CustomerInvoiceSeriesModel } from "../../../../common";
import type { InvoiceSeriesDomainMapper } from "../mappers"; import type { InvoiceSeriesDomainMapper } from "../mappers";
@ -27,10 +24,7 @@ export class SequelizeInvoiceSeriesRepository
super({ database }); super({ database });
} }
public async findActiveByCompany(params: { public async findActiveByCompany(params: { companyId: UniqueID; transaction?: unknown }) {
companyId: UniqueID;
transaction?: unknown;
}) {
try { try {
const rows = await CustomerInvoiceSeriesModel.findAll({ const rows = await CustomerInvoiceSeriesModel.findAll({
where: { where: {

View File

@ -16,10 +16,14 @@ import { useEffect, useId, useMemo, useState } from "react";
import type { FieldErrors } from "react-hook-form"; import type { FieldErrors } from "react-hook-form";
import type { Proforma } from "../../shared"; import type { Proforma } from "../../shared";
import { useProformaCreateMutation, useProformasListQuery } from "../../shared/hooks"; import {
buildInvoiceSeriesSelectItems,
useInvoiceSeriesQuery,
useProformaCreateMutation,
} from "../../shared";
import { buildCreateProformaParams } from "../adapters"; import { buildCreateProformaParams } from "../adapters";
import { type ProformaCreateForm, ProformaCreateFormSchema } from "../entities"; import { type ProformaCreateForm, ProformaCreateFormSchema } from "../entities";
import { buildProformaCreateDefault, buildProformaSeriesOptions } from "../utils"; import { buildProformaCreateDefault } from "../utils";
export interface UseCreateProformaControllerOptions { export interface UseCreateProformaControllerOptions {
onCreated?(proforma: Proforma): void; onCreated?(proforma: Proforma): void;
@ -48,12 +52,7 @@ export const useCreateProformaController = (options?: UseCreateProformaControlle
error: createError, error: createError,
} = useProformaCreateMutation(); } = useProformaCreateMutation();
const seriesQuery = useProformasListQuery({ const seriesQuery = useInvoiceSeriesQuery();
criteria: {
pageNumber: 1,
pageSize: 100,
},
});
const initialValues = useMemo<ProformaCreateForm>(() => buildProformaCreateDefault(), []); const initialValues = useMemo<ProformaCreateForm>(() => buildProformaCreateDefault(), []);
@ -64,20 +63,28 @@ export const useCreateProformaController = (options?: UseCreateProformaControlle
}); });
const seriesOptions = useMemo( const seriesOptions = useMemo(
() => buildProformaSeriesOptions(seriesQuery.data), () => buildInvoiceSeriesSelectItems(seriesQuery.data ?? []),
[seriesQuery.data] [seriesQuery.data]
); );
useEffect(() => { const defaultSeries = useMemo(() => {
if (seriesOptions.length !== 1) return; const invoiceSeries = seriesQuery.data ?? [];
if (form.getValues("series")) return;
form.setValue("series", seriesOptions[0].value, { return (
invoiceSeries.find((series) => series.isDefault)?.code ?? invoiceSeries[0]?.code ?? ""
);
}, [seriesQuery.data]);
useEffect(() => {
if (form.getValues("series")) return;
if (!defaultSeries) return;
form.setValue("series", defaultSeries, {
shouldDirty: false, shouldDirty: false,
shouldTouch: false, shouldTouch: false,
shouldValidate: true, shouldValidate: true,
}); });
}, [form, seriesOptions]); }, [defaultSeries, form]);
const setCustomer = (customer: CustomerSelectionOption) => { const setCustomer = (customer: CustomerSelectionOption) => {
setSelectedCustomer(customer); setSelectedCustomer(customer);
@ -163,8 +170,8 @@ export const useCreateProformaController = (options?: UseCreateProformaControlle
setCustomer, setCustomer,
clearCustomer, clearCustomer,
seriesOptions, seriesOptions,
isLoading: seriesQuery.isLoading, isSeriesLoading: seriesQuery.isLoading,
isLoadError: seriesQuery.isError, isSeriesLoadError: seriesQuery.isError,
loadError: seriesQuery.error, seriesLoadError: seriesQuery.error,
}; };
}; };

View File

@ -9,6 +9,7 @@ import { ProformaCreateInitialInfoCard } from "./proforma-create-initial-info-ca
interface ProformaCreateFormProps { interface ProformaCreateFormProps {
formId: string; formId: string;
isSubmitting: boolean; isSubmitting: boolean;
isSeriesLoading?: boolean;
onSubmit: React.SubmitEventHandler<HTMLFormElement>; onSubmit: React.SubmitEventHandler<HTMLFormElement>;
selectedCustomer?: CustomerSelectionOption | null; selectedCustomer?: CustomerSelectionOption | null;
onSelectCustomer: () => void; onSelectCustomer: () => void;
@ -20,6 +21,7 @@ interface ProformaCreateFormProps {
export const ProformaCreateForm = ({ export const ProformaCreateForm = ({
formId, formId,
isSubmitting, isSubmitting,
isSeriesLoading = false,
onSubmit, onSubmit,
selectedCustomer, selectedCustomer,
onSelectCustomer, onSelectCustomer,
@ -33,6 +35,7 @@ export const ProformaCreateForm = ({
<div className="space-y-6"> <div className="space-y-6">
<ProformaCreateInitialInfoCard <ProformaCreateInitialInfoCard
disabled={isSubmitting} disabled={isSubmitting}
isSeriesLoading={isSeriesLoading}
onClearCustomer={onClearCustomer} onClearCustomer={onClearCustomer}
onSelectCustomer={onSelectCustomer} onSelectCustomer={onSelectCustomer}
selectedCustomer={selectedCustomer} selectedCustomer={selectedCustomer}

View File

@ -1,4 +1,10 @@
import { DatePickerField, FormSectionCard, FormSectionGrid, SelectField } from "@repo/rdx-ui/components"; import {
DatePickerField,
FormSectionCard,
FormSectionGrid,
SelectField,
type SelectFieldItem,
} from "@repo/rdx-ui/components";
import { FileTextIcon } from "lucide-react"; import { FileTextIcon } from "lucide-react";
import type { CustomerSelectionOption } from "@erp/customers"; import type { CustomerSelectionOption } from "@erp/customers";
@ -8,14 +14,16 @@ import { ProformaCreateCustomerField } from "./proforma-create-customer-field";
interface ProformaCreateInitialInfoCardProps { interface ProformaCreateInitialInfoCardProps {
disabled?: boolean; disabled?: boolean;
isSeriesLoading?: boolean;
selectedCustomer?: CustomerSelectionOption | null; selectedCustomer?: CustomerSelectionOption | null;
onSelectCustomer: () => void; onSelectCustomer: () => void;
onClearCustomer: () => void; onClearCustomer: () => void;
seriesOptions: { value: string; label: string }[]; seriesOptions: SelectFieldItem[];
} }
export const ProformaCreateInitialInfoCard = ({ export const ProformaCreateInitialInfoCard = ({
disabled = false, disabled = false,
isSeriesLoading = false,
selectedCustomer, selectedCustomer,
onSelectCustomer, onSelectCustomer,
onClearCustomer, onClearCustomer,
@ -44,11 +52,17 @@ export const ProformaCreateInitialInfoCard = ({
<SelectField<ProformaCreateForm> <SelectField<ProformaCreateForm>
className="md:col-span-4" className="md:col-span-4"
disabled={disabled} disabled={disabled || isSeriesLoading}
items={seriesOptions} items={seriesOptions}
label="Serie" label="Serie"
name="series" name="series"
placeholder={seriesOptions.length > 0 ? "Selecciona una serie" : "Sin serie"} placeholder={
isSeriesLoading
? "Cargando series..."
: seriesOptions.length > 0
? "Selecciona una serie"
: "Sin serie"
}
/> />
</FormSectionGrid> </FormSectionGrid>
</FormSectionCard> </FormSectionCard>

View File

@ -1,13 +1,11 @@
import { ErrorAlert } from "@erp/core/components"; import { ErrorAlert } from "@erp/core/components";
import { UnsavedChangesProvider, useReturnToNavigation } from "@erp/core/hooks"; import { UnsavedChangesProvider, useReturnToNavigation } from "@erp/core/hooks";
import { SelectCustomerDialog } from "@erp/customers"; import { SelectCustomerDialog } from "@erp/customers";
import { AppContent, BackHistoryButton } from "@repo/rdx-ui/components";
import { FormProvider } from "react-hook-form"; import { FormProvider } from "react-hook-form";
import { ProformaInfoAlert } from "../../../update/ui/components"; import { ProformaInfoAlert } from "../../../update/ui/components";
import { useCreateProformaPageController } from "../../controllers"; import { useCreateProformaPageController } from "../../controllers";
import { ProformaCreateForm, ProformaCreateHeader } from "../blocks"; import { ProformaCreateForm, ProformaCreateHeader } from "../blocks";
import { ProformaCreateSkeleton } from "../components";
export const ProformaCreatePage = () => { export const ProformaCreatePage = () => {
const { createCtrl, selectCustomerCtrl, returnTo } = useCreateProformaPageController(); const { createCtrl, selectCustomerCtrl, returnTo } = useCreateProformaPageController();
@ -16,29 +14,6 @@ export const ProformaCreatePage = () => {
fallbackPath: returnTo, fallbackPath: returnTo,
}); });
if (createCtrl.isLoading) {
return <ProformaCreateSkeleton />;
}
if (createCtrl.isLoadError) {
return (
<AppContent>
<ErrorAlert
message={
createCtrl.loadError instanceof Error
? createCtrl.loadError.message
: "Inténtalo de nuevo más tarde."
}
title="No se pudo preparar la creación de la proforma"
/>
<div className="flex items-center justify-end">
<BackHistoryButton onClick={() => navigateBack()} />
</div>
</AppContent>
);
}
return ( return (
<div className="fixed inset-0 flex flex-col overflow-hidden"> <div className="fixed inset-0 flex flex-col overflow-hidden">
<FormProvider {...createCtrl.form}> <FormProvider {...createCtrl.form}>
@ -65,9 +40,21 @@ export const ProformaCreatePage = () => {
/> />
)} )}
{createCtrl.isSeriesLoadError && (
<ErrorAlert
message={
createCtrl.seriesLoadError instanceof Error
? createCtrl.seriesLoadError.message
: "Inténtalo de nuevo más tarde."
}
title="No se pudieron cargar las series de facturación"
/>
)}
<div className="flex-1 overflow-y-auto"> <div className="flex-1 overflow-y-auto">
<ProformaCreateForm <ProformaCreateForm
formId={createCtrl.formId} formId={createCtrl.formId}
isSeriesLoading={createCtrl.isSeriesLoading}
isSubmitting={createCtrl.isCreating} isSubmitting={createCtrl.isCreating}
onClearCustomer={createCtrl.clearCustomer} onClearCustomer={createCtrl.clearCustomer}
onSelectCustomer={selectCustomerCtrl.selectCtrl.openDialog} onSelectCustomer={selectCustomerCtrl.selectCtrl.openDialog}

View File

@ -6,7 +6,7 @@ export const buildProformaCreateDefault = (): ProformaCreateForm => {
return { return {
customerId: "", customerId: "",
invoiceDate: DateHelper.buildTodayIsoDate(), invoiceDate: DateHelper.buildTodayIsoDate(),
series: "F26", series: "",
languageCode: "es", languageCode: "es",
currencyCode: "EUR", currencyCode: "EUR",
}; };

View File

@ -1,17 +0,0 @@
import type { SelectFieldItem } from "@repo/rdx-ui/components";
import type { ProformaList } from "../../shared";
export const buildProformaSeriesOptions = (proformas?: ProformaList): SelectFieldItem[] => {
const uniqueSeries = new Set<string>();
for (const item of proformas?.items ?? []) {
const value = item.series?.trim();
if (!value) continue;
uniqueSeries.add(value);
}
return [...uniqueSeries]
.sort((left, right) => left.localeCompare(right))
.map((value) => ({ value, label: value }));
};

View File

@ -1,2 +1 @@
export * from "./build-proforma-create-default"; export * from "./build-proforma-create-default";
export * from "./build-proforma-series-options";

View File

@ -1,3 +1,4 @@
export * from "./get-proforma-by-id.adapter"; export * from "./get-proforma-by-id.adapter";
export * from "./list-invoice-series.adapter";
export * from "./list-proformas.adapter"; export * from "./list-proformas.adapter";
export * from "./proforma-to-list-row-patch.adapter"; export * from "./proforma-to-list-row-patch.adapter";

View File

@ -0,0 +1,18 @@
import type { ListInvoiceSeriesResponseDTO } from "../../../../common";
import type { InvoiceSeriesSummary } from "../entities";
export const mapInvoiceSeriesSummaryDTOToInvoiceSeriesSummary = (
dto: ListInvoiceSeriesResponseDTO["items"][number]
): InvoiceSeriesSummary => ({
id: dto.id,
code: dto.code,
nextNumber: dto.next_number,
paddingLength: dto.padding_length,
isDefault: dto.is_default,
});
export const ListInvoiceSeriesAdapter = {
fromDto(dto: ListInvoiceSeriesResponseDTO): InvoiceSeriesSummary[] {
return dto.items.map(mapInvoiceSeriesSummaryDTOToInvoiceSeriesSummary);
},
};

View File

@ -3,5 +3,6 @@ export * from "./create-proforma.api";
export * from "./delete-proforma-by-id.api"; export * from "./delete-proforma-by-id.api";
export * from "./get-proforma-by-id.api"; export * from "./get-proforma-by-id.api";
export * from "./issue-proforma-by-id.api"; export * from "./issue-proforma-by-id.api";
export * from "./list-invoice-series.api";
export * from "./list-proformas-by-criteria.api"; export * from "./list-proformas-by-criteria.api";
export * from "./update-proforma-by-id.api"; export * from "./update-proforma-by-id.api";

View File

@ -0,0 +1,12 @@
import type { IDataSource } from "@erp/core/client";
import type { ListInvoiceSeriesResponseDTO } from "../../../../common";
export const listInvoiceSeries = (
dataSource: IDataSource,
signal?: AbortSignal
): Promise<ListInvoiceSeriesResponseDTO> => {
return dataSource.getList<ListInvoiceSeriesResponseDTO>("catalogs/invoice-series", {
signal,
});
};

View File

@ -1,4 +1,5 @@
export * from "./forms/proforma-item-form.entity"; export * from "./forms/proforma-item-form.entity";
export * from "./invoice-series-summary.entity";
export * from "./proforma.entity"; export * from "./proforma.entity";
export * from "./proforma-item.entity"; export * from "./proforma-item.entity";
export * from "./proforma-list.entity"; export * from "./proforma-list.entity";

View File

@ -0,0 +1,7 @@
export interface InvoiceSeriesSummary {
id: string;
code: string;
nextNumber: number;
paddingLength: number;
isDefault: boolean;
}

View File

@ -1,4 +1,5 @@
export * from "./keys"; export * from "./keys";
export * from "./use-invoice-series-query";
export * from "./use-proforma-change-status-mutation"; export * from "./use-proforma-change-status-mutation";
export * from "./use-proforma-create-mutation"; export * from "./use-proforma-create-mutation";
export * from "./use-proforma-delete-mutation"; export * from "./use-proforma-delete-mutation";

View File

@ -6,6 +6,7 @@ import type { ProformasListRequestDTO } from "../../../../common";
* Prefijo base para listados * Prefijo base para listados
*/ */
export const LIST_PROFORMAS_QUERY_KEY_PREFIX = ["proformas"] as const; export const LIST_PROFORMAS_QUERY_KEY_PREFIX = ["proformas"] as const;
export const INVOICE_SERIES_QUERY_KEY = ["customer-invoices", "invoice-series"] as const;
/** /**
* Query key para listado de proformas * Query key para listado de proformas

View File

@ -0,0 +1,20 @@
import { useDataSource } from "@erp/core/hooks";
import { type DefaultError, type UseQueryResult, useQuery } from "@tanstack/react-query";
import { ListInvoiceSeriesAdapter } from "../adapters";
import { listInvoiceSeries } from "../api";
import type { InvoiceSeriesSummary } from "../entities";
import { INVOICE_SERIES_QUERY_KEY } from "./keys";
export const useInvoiceSeriesQuery = (): UseQueryResult<InvoiceSeriesSummary[], DefaultError> => {
const dataSource = useDataSource();
return useQuery<InvoiceSeriesSummary[], DefaultError>({
queryKey: INVOICE_SERIES_QUERY_KEY,
queryFn: async ({ signal }) => {
const dto = await listInvoiceSeries(dataSource, signal);
return ListInvoiceSeriesAdapter.fromDto(dto);
},
});
};

View File

@ -0,0 +1,12 @@
import type { SelectFieldItem } from "@repo/rdx-ui/components";
import type { InvoiceSeriesSummary } from "../entities";
export const buildInvoiceSeriesSelectItems = (
invoiceSeries: InvoiceSeriesSummary[]
): SelectFieldItem[] => {
return invoiceSeries.map((series) => ({
value: series.code,
label: series.code,
}));
};

View File

@ -0,0 +1,19 @@
import type { SelectFieldItem } from "@repo/rdx-ui/components";
export const ensureCurrentValueInSelectItems = (
items: SelectFieldItem[],
currentValue?: string | null
): SelectFieldItem[] => {
if (!currentValue) return items;
const exists = items.some((item) => item.value === currentValue);
if (exists) return items;
return [
{
value: currentValue,
label: currentValue,
},
...items,
];
};

View File

@ -1,2 +1,4 @@
export * from "./build-invoice-series-select-items";
export * from "./calculate-proforma-due-dates"; export * from "./calculate-proforma-due-dates";
export * from "./ensure-current-value-in-select-items";
export * from "./proforma-fiscal-options.utils"; export * from "./proforma-fiscal-options.utils";

View File

@ -15,7 +15,13 @@ import type { FieldErrors } from "react-hook-form";
import { useTranslation } from "../../../i18n"; import { useTranslation } from "../../../i18n";
import type { UpdateProformaByIdParams } from "../../shared"; import type { UpdateProformaByIdParams } from "../../shared";
import type { Proforma } from "../../shared/entities"; import type { Proforma } from "../../shared/entities";
import { useProformaGetQuery, useProformaUpdateMutation } from "../../shared/hooks"; import {
buildInvoiceSeriesSelectItems,
ensureCurrentValueInSelectItems,
useInvoiceSeriesQuery,
useProformaGetQuery,
useProformaUpdateMutation,
} from "../../shared";
import { mapProformaToProformaUpdateForm, mapProformaToSelectedCustomer } from "../adapters"; import { mapProformaToProformaUpdateForm, mapProformaToSelectedCustomer } from "../adapters";
import { type ProformaUpdateForm, ProformaUpdateFormSchema } from "../entities"; import { type ProformaUpdateForm, ProformaUpdateFormSchema } from "../entities";
import { import {
@ -72,6 +78,8 @@ export const useUpdateProformaController = (
error: updateError, error: updateError,
} = useProformaUpdateMutation(); } = useProformaUpdateMutation();
const invoiceSeriesQuery = useInvoiceSeriesQuery();
const initialValues = useMemo<ProformaUpdateForm>(() => { const initialValues = useMemo<ProformaUpdateForm>(() => {
if (!proformaData) return buildProformaUpdateDefault(); if (!proformaData) return buildProformaUpdateDefault();
@ -221,7 +229,7 @@ export const useUpdateProformaController = (
options?.onError?.(normalizedError, params); options?.onError?.(normalizedError, params);
} }
}, },
(errors: FieldErrors<ProformaUpdateForm>) => { (_errors: FieldErrors<ProformaUpdateForm>) => {
focusFirstInputFormError(form); focusFirstInputFormError(form);
showWarningToast( showWarningToast(
@ -239,6 +247,12 @@ export const useUpdateProformaController = (
const currencyCode = form.watch("currencyCode"); const currencyCode = form.watch("currencyCode");
const languageCode = form.watch("languageCode"); const languageCode = form.watch("languageCode");
const currentSeries = form.watch("series");
const seriesOptions = useMemo(() => {
const activeSeriesItems = buildInvoiceSeriesSelectItems(invoiceSeriesQuery.data ?? []);
return ensureCurrentValueInSelectItems(activeSeriesItems, currentSeries);
}, [currentSeries, invoiceSeriesQuery.data]);
const taxCtrl = useUpdateProformaTaxController({ const taxCtrl = useUpdateProformaTaxController({
form, form,
@ -263,6 +277,10 @@ export const useUpdateProformaController = (
taxCtrl, taxCtrl,
totalsCtrl, totalsCtrl,
paymentCtrl, paymentCtrl,
seriesOptions,
isSeriesLoading: invoiceSeriesQuery.isLoading,
isSeriesLoadError: invoiceSeriesQuery.isError,
seriesLoadError: invoiceSeriesQuery.error,
// //
currencyCode, currencyCode,

View File

@ -1,6 +1,7 @@
// modules/customer-invoices/src/web/proformas/update/ui/blocks/proforma-update-editor.tsx // modules/customer-invoices/src/web/proformas/update/ui/blocks/proforma-update-editor.tsx
import type { CustomerSelectionOption } from "@erp/customers"; import type { CustomerSelectionOption } from "@erp/customers";
import type { SelectFieldItem } from "@repo/rdx-ui/components";
import { preventEnterKeySubmitForm } from "@repo/rdx-ui/helpers"; import { preventEnterKeySubmitForm } from "@repo/rdx-ui/helpers";
import { cn } from "@repo/shadcn-ui/lib/utils"; import { cn } from "@repo/shadcn-ui/lib/utils";
@ -27,6 +28,7 @@ type ProformaUpdateEditorProps = {
selectedCustomer?: CustomerSelectionOption | null; selectedCustomer?: CustomerSelectionOption | null;
onChangeCustomerClick: () => void; onChangeCustomerClick: () => void;
onCreateCustomerClick: () => void; onCreateCustomerClick: () => void;
seriesOptions: SelectFieldItem[];
itemsCtrl: UseUpdateProformaItemsControllerResult; itemsCtrl: UseUpdateProformaItemsControllerResult;
taxCtrl: UseUpdateProformaTaxControllerResult; taxCtrl: UseUpdateProformaTaxControllerResult;
@ -44,6 +46,7 @@ export const ProformaUpdateEditorForm = ({
selectedCustomer, selectedCustomer,
onChangeCustomerClick, onChangeCustomerClick,
onCreateCustomerClick, onCreateCustomerClick,
seriesOptions,
itemsCtrl, itemsCtrl,
taxCtrl, taxCtrl,
totalsCtrl, totalsCtrl,
@ -58,7 +61,11 @@ export const ProformaUpdateEditorForm = ({
<div className="grid grid-cols-1 2xl:grid-cols-4 gap-6 2xl:gap-12"> <div className="grid grid-cols-1 2xl:grid-cols-4 gap-6 2xl:gap-12">
<div className="2xl:col-span-3 space-y-6 gap-6 2xl:gap-12 2xl:space-y-12"> <div className="2xl:col-span-3 space-y-6 gap-6 2xl:gap-12 2xl:space-y-12">
<div className="grid 2xl:grid-cols-3 gap-6 2xl:gap-6 space-y-12 2xl:space-y-12"> <div className="grid 2xl:grid-cols-3 gap-6 2xl:gap-6 space-y-12 2xl:space-y-12">
<ProformaUpdateHeaderEditor className="2xl:col-span-full" disabled={isSubmitting} /> <ProformaUpdateHeaderEditor
className="2xl:col-span-full"
disabled={isSubmitting}
seriesOptions={seriesOptions}
/>
</div> </div>
<ProformaUpdateItemsEditor <ProformaUpdateItemsEditor

View File

@ -3,6 +3,7 @@ import {
FormSectionCard, FormSectionCard,
FormSectionGrid, FormSectionGrid,
SelectField, SelectField,
type SelectFieldItem,
TextAreaField, TextAreaField,
TextField, TextField,
} from "@repo/rdx-ui/components"; } from "@repo/rdx-ui/components";
@ -12,6 +13,7 @@ import { FileTextIcon } from "lucide-react";
import { useTranslation } from "../../../../i18n"; import { useTranslation } from "../../../../i18n";
interface ProformaUpdateHeaderEditorProps { interface ProformaUpdateHeaderEditorProps {
seriesOptions: SelectFieldItem[];
disabled?: boolean; disabled?: boolean;
readOnly?: boolean; readOnly?: boolean;
@ -19,6 +21,7 @@ interface ProformaUpdateHeaderEditorProps {
} }
export const ProformaUpdateHeaderEditor = ({ export const ProformaUpdateHeaderEditor = ({
seriesOptions,
disabled = false, disabled = false,
readOnly = false, readOnly = false,
className, className,
@ -33,15 +36,6 @@ export const ProformaUpdateHeaderEditor = ({
title={t("form_groups.proformas.basic_info.title")} title={t("form_groups.proformas.basic_info.title")}
> >
<FormSectionGrid> <FormSectionGrid>
<SelectField
className="md:col-span-1"
disabled={disabled}
label={t("form_fields.proformas.series.label")}
name="series"
placeholder={t("form_fields.proformas.series.placeholder")}
readOnly={readOnly}
/>
<TextField <TextField
className="md:col-span-2" className="md:col-span-2"
disabled={disabled} disabled={disabled}
@ -66,6 +60,16 @@ export const ProformaUpdateHeaderEditor = ({
readOnly={readOnly} readOnly={readOnly}
/> />
<SelectField
className="md:col-span-1"
disabled={disabled}
items={seriesOptions}
label={t("form_fields.proformas.series.label")}
name="series"
placeholder={t("form_fields.proformas.series.placeholder")}
readOnly={readOnly}
/>
<DatePickerField <DatePickerField
className="md:col-span-2" className="md:col-span-2"
disabled={disabled} disabled={disabled}

View File

@ -99,6 +99,23 @@ export const ProformaUpdatePage = () => {
title={t("pages.proformas.update.error_title", "No se pudo guardar los cambios")} title={t("pages.proformas.update.error_title", "No se pudo guardar los cambios")}
/> />
)} )}
{updateCtrl.isSeriesLoadError && (
<ErrorAlert
message={
updateCtrl.seriesLoadError instanceof Error
? updateCtrl.seriesLoadError.message
: t(
"pages.proformas.update.series_load_error.message",
"Inténtalo de nuevo más tarde."
)
}
title={t(
"pages.proformas.update.series_load_error.title",
"No se pudieron cargar las series de facturación"
)}
/>
)}
<div className="flex-1 overflow-y-auto"> <div className="flex-1 overflow-y-auto">
<ProformaUpdateEditorForm <ProformaUpdateEditorForm
formId={updateCtrl.formId} formId={updateCtrl.formId}
@ -110,6 +127,7 @@ export const ProformaUpdatePage = () => {
onSubmit={updateCtrl.onSubmit} onSubmit={updateCtrl.onSubmit}
paymentCtrl={updateCtrl.paymentCtrl} paymentCtrl={updateCtrl.paymentCtrl}
selectedCustomer={updateCtrl.selectedCustomer} selectedCustomer={updateCtrl.selectedCustomer}
seriesOptions={updateCtrl.seriesOptions}
taxCtrl={updateCtrl.taxCtrl} taxCtrl={updateCtrl.taxCtrl}
totalsCtrl={updateCtrl.totalsCtrl} totalsCtrl={updateCtrl.totalsCtrl}
/> />