diff --git a/apps/web/src/app.tsx b/apps/web/src/app.tsx index 4283d682..e07bc6f7 100644 --- a/apps/web/src/app.tsx +++ b/apps/web/src/app.tsx @@ -1,6 +1,7 @@ import { Toaster, TooltipProvider } from "@repo/shadcn-ui/components"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; + import { I18nextProvider } from "react-i18next"; import { i18n } from "@/locales"; diff --git a/modules/core/package.json b/modules/core/package.json index 8af5a3ad..f4f4f855 100644 --- a/modules/core/package.json +++ b/modules/core/package.json @@ -27,6 +27,7 @@ "@repo/shadcn-ui": "workspace:*", "@hookform/resolvers": "^5.0.1", "@tanstack/react-query": "^5.75.4", + "ag-grid-community": "^33.3.0", "axios": "^1.9.0", "express": "^4.18.2", "http-status": "^2.1.0", diff --git a/modules/core/src/web/components/form/taxes-multi-select-field.tsx.bak b/modules/core/src/web/components/form/taxes-multi-select-field.tsx.bak deleted file mode 100644 index 127f2c78..00000000 --- a/modules/core/src/web/components/form/taxes-multi-select-field.tsx.bak +++ /dev/null @@ -1,30 +0,0 @@ -import { MultiSelectField, MultiSelectFieldProps } from "@repo/rdx-ui/components"; -import * as React from "react"; -import type { FieldValues } from "react-hook-form"; -import { TaxesList } from "../../constants"; - -/** - * Igual que MultiSelect pero con `options` preconfiguradas a TaxesList. - * Puedes sobreescribir `options` si lo necesitas, se mergean (TaxesList primero). - */ -export type TaxesMultiSelectFieldProps = Omit< - MultiSelectFieldProps, - "options" ->; - -const TaxesMultiSelectFieldInner = React.forwardRef( - ( - props: TaxesMultiSelectFieldProps, - ref: React.Ref - ) => { - return ( - )} options={TaxesList} /> - ); - } -); - -TaxesMultiSelectFieldInner.displayName = "TaxesMultiSelectField"; - -export const TaxesMultiSelectField = TaxesMultiSelectFieldInner as ( - p: TaxesMultiSelectFieldProps & { ref?: React.Ref } -) => React.JSX.Element; diff --git a/modules/core/src/web/components/index.ts b/modules/core/src/web/components/index.ts index 71b467ad..0d332a43 100644 --- a/modules/core/src/web/components/index.ts +++ b/modules/core/src/web/components/index.ts @@ -1,2 +1,6 @@ +import { AllCommunityModule, ModuleRegistry } from "ag-grid-community"; + +ModuleRegistry.registerModules([AllCommunityModule]); + export * from "./form"; export * from "./taxes-multi-select"; diff --git a/modules/customer-invoices/package.json b/modules/customer-invoices/package.json index f1e8e67f..0ac922c0 100644 --- a/modules/customer-invoices/package.json +++ b/modules/customer-invoices/package.json @@ -11,6 +11,7 @@ }, "peerDependencies": { "@tanstack/react-query": "^5.74.11", + "ag-grid-community": "^33.3.0", "dinero.js": "^1.9.1", "express": "^4.18.2", "handlebars": "^4.7.8", @@ -46,7 +47,6 @@ "@repo/rdx-utils": "workspace:*", "@repo/shadcn-ui": "workspace:*", "@tanstack/react-table": "^8.21.3", - "ag-grid-community": "^33.3.0", "ag-grid-react": "^33.3.0", "date-fns": "^4.1.0", "libphonenumber-js": "^1.12.7", diff --git a/modules/customer-invoices/src/common/dto/request/create-customer-invoice.request.dto.ts b/modules/customer-invoices/src/common/dto/request/create-customer-invoice.request.dto.ts index da17ee80..5b7ef87e 100644 --- a/modules/customer-invoices/src/common/dto/request/create-customer-invoice.request.dto.ts +++ b/modules/customer-invoices/src/common/dto/request/create-customer-invoice.request.dto.ts @@ -13,7 +13,6 @@ export const CreateCustomerInvoiceItemRequestSchema = z.object({ export const CreateCustomerInvoiceRequestSchema = z.object({ id: z.uuid(), - company_id: z.uuid(), invoice_number: z.string(), series: z.string().default(""), diff --git a/modules/customer-invoices/src/common/dto/request/update-customer-invoice-by-id.request.dto.ts b/modules/customer-invoices/src/common/dto/request/update-customer-invoice-by-id.request.dto.ts index f5a672f7..ee297e5a 100644 --- a/modules/customer-invoices/src/common/dto/request/update-customer-invoice-by-id.request.dto.ts +++ b/modules/customer-invoices/src/common/dto/request/update-customer-invoice-by-id.request.dto.ts @@ -1,9 +1,24 @@ import { z } from "zod/v4"; export const UpdateCustomerInvoiceByIdParamsRequestSchema = z.object({ - customer_id: z.string(), + invoice_id: z.string(), }); -export const UpdateCustomerByIdRequestSchema = z.object({}); +export const UpdateCustomerInvoiceByIdRequestSchema = z.object({ + invoice_number: z.string(), + series: z.string().default(""), -export type UpdateCustomerByIdRequestDTO = z.infer; + invoice_date: z.string(), + operation_date: z.string().default(""), + + customer_id: z.uuid(), + + notes: z.string().default(""), + + language_code: z.string().toLowerCase().default("es"), + currency_code: z.string().toUpperCase().default("EUR"), +}); + +export type UpdateCustomerInvoiceByIdRequestDTO = Partial< + z.infer +>; diff --git a/modules/customer-invoices/src/common/locales/en.json b/modules/customer-invoices/src/common/locales/en.json index eab82b97..eb666280 100644 --- a/modules/customer-invoices/src/common/locales/en.json +++ b/modules/customer-invoices/src/common/locales/en.json @@ -18,6 +18,11 @@ "series": "Serie", "status": "Status", "invoice_date": "Date", + "recipient_tin": "Customer TIN", + "recipient_name": "Customer name", + "recipient_city": "Customer city", + "recipient_province": "Customer province", + "recipient_postal_code": "Customer postal code", "total_amount": "Total price" } }, diff --git a/modules/customer-invoices/src/web/components/customer-invoices-list-grid.tsx b/modules/customer-invoices/src/web/components/customer-invoices-list-grid.tsx index 65bb0bc0..a340fa3a 100644 --- a/modules/customer-invoices/src/web/components/customer-invoices-list-grid.tsx +++ b/modules/customer-invoices/src/web/components/customer-invoices-list-grid.tsx @@ -5,7 +5,7 @@ import type { SizeColumnsToFitProvidedWidthStrategy, ValueFormatterParams, } from "ag-grid-community"; -import { AllCommunityModule, ColDef, GridOptions, ModuleRegistry } from "ag-grid-community"; +import { ColDef, GridOptions } from "ag-grid-community"; import { useMemo, useState } from "react"; import { MoneyDTO } from "@erp/core"; @@ -19,8 +19,6 @@ import { useCustomerInvoicesQuery } from "../hooks"; import { useTranslation } from "../i18n"; import { CustomerInvoiceStatusBadge } from "./customer-invoice-status-badge"; -ModuleRegistry.registerModules([AllCommunityModule]); - // Create new GridExample component export const CustomerInvoicesListGrid = () => { const { t } = useTranslation(); @@ -58,12 +56,14 @@ export const CustomerInvoicesListGrid = () => { return formatDate(params.value); }, }, - { field: "recipient.tin" }, - { field: "recipient.name" }, - - { field: "recipient.city" }, - { field: "recipient.province" }, - { field: "recipient.postal_code" }, + { field: "recipient.tin", headerName: t("pages.list.grid_columns.recipient_tin") }, + { field: "recipient.name", headerName: t("pages.list.grid_columns.recipient_name") }, + { field: "recipient.city", headerName: t("pages.list.grid_columns.recipient_city") }, + { field: "recipient.province", headerName: t("pages.list.grid_columns.recipient_province") }, + { + field: "recipient.postal_code", + headerName: t("pages.list.grid_columns.recipient_postal_code"), + }, { field: "taxable_amount", headerName: t("pages.list.grid_columns.taxable_amount"), diff --git a/modules/customer-invoices/src/web/hooks/use-create-customer-invoice-mutation.ts b/modules/customer-invoices/src/web/hooks/use-create-customer-invoice-mutation.ts index 87ba2314..45eabf9a 100644 --- a/modules/customer-invoices/src/web/hooks/use-create-customer-invoice-mutation.ts +++ b/modules/customer-invoices/src/web/hooks/use-create-customer-invoice-mutation.ts @@ -1,20 +1,43 @@ -import { useDataSource, useQueryKey } from "@erp/core/hooks"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { CreateCustomerInvoiceRequestDTO } from "../../common/dto"; +import { useDataSource } from "@erp/core/hooks"; +import { UniqueID, ValidationErrorCollection } from "@repo/rdx-ddd"; +import { DefaultError, useMutation, useQueryClient } from "@tanstack/react-query"; +import { CreateCustomerInvoiceRequestSchema } from "../../common"; +import { CustomerInvoiceData, CustomerInvoiceFormData } from "../schemas"; + +type CreateCustomerInvoicePayload = { + data: CustomerInvoiceFormData; +}; export const useCreateCustomerInvoiceMutation = () => { const queryClient = useQueryClient(); const dataSource = useDataSource(); - const keys = useQueryKey(); + const schema = CreateCustomerInvoiceRequestSchema; - return useMutation< - CreateCustomerInvoiceRequestDTO, - Error, - Partial - >({ - mutationFn: (data) => { - console.log(data); - return dataSource.createOne("customer-invoices", data); + return useMutation({ + mutationKey: ["customer-invoice:create"], + + mutationFn: async (payload) => { + const { data } = payload; + const invoiceId = UniqueID.generateNewID(); + + const newInvoiceData = { + ...data, + id: invoiceId.toString(), + }; + + const result = schema.safeParse(newInvoiceData); + if (!result.success) { + // Construye errores detallados + const validationErrors = result.error.issues.map((err) => ({ + field: err.path.join("."), + message: err.message, + })); + + throw new ValidationErrorCollection("Validation failed", validationErrors); + } + + const created = await dataSource.createOne("customer-invoices", newInvoiceData); + return created as CustomerInvoiceData; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["customer-invoices"] }); diff --git a/modules/customer-invoices/src/web/hooks/use-update-customer-invoice-mutation.ts b/modules/customer-invoices/src/web/hooks/use-update-customer-invoice-mutation.ts new file mode 100644 index 00000000..72966db9 --- /dev/null +++ b/modules/customer-invoices/src/web/hooks/use-update-customer-invoice-mutation.ts @@ -0,0 +1,69 @@ +import { useDataSource } from "@erp/core/hooks"; +import { ValidationErrorCollection } from "@repo/rdx-ddd"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { + UpdateCustomerInvoiceByIdRequestDTO, + UpdateCustomerInvoiceByIdRequestSchema, +} from "../../common"; +import { CustomerInvoiceFormData } from "../schemas"; +import { CUSTOMER_INVOICE_QUERY_KEY } from "./use-customer-invoice-query"; + +export const CUSTOMER_INVOICES_LIST_KEY = ["customer-invoices"] as const; + +type UpdateCustomerInvoiceContext = {}; + +type UpdateCustomerInvoicePayload = { + id: string; + data: Partial; +}; + +export function useUpdateCustomerInvoice() { + const queryClient = useQueryClient(); + const dataSource = useDataSource(); + const schema = UpdateCustomerInvoiceByIdRequestSchema; + + return useMutation< + CustomerInvoiceFormData, + Error, + UpdateCustomerInvoicePayload, + UpdateCustomerInvoiceContext + >({ + mutationKey: ["customer-invoice:update"], //, customerId], + + mutationFn: async (payload) => { + const { id: invoiceId, data } = payload; + if (!invoiceId) { + throw new Error("customerInvoiceId is required"); + } + + const result = schema.safeParse(data); + if (!result.success) { + // Construye errores detallados + const validationErrors = result.error.issues.map((err) => ({ + field: err.path.join("."), + message: err.message, + })); + + throw new ValidationErrorCollection("Validation failed", validationErrors); + } + + const updated = await dataSource.updateOne("customer-invoices", invoiceId, data); + return updated as CustomerInvoiceFormData; + }, + onSuccess: (updated: CustomerInvoiceFormData, variables) => { + const { id: invoiceId } = variables; + + // Refresca inmediatamente el detalle + queryClient.setQueryData( + CUSTOMER_INVOICE_QUERY_KEY(invoiceId), + updated + ); + + // Otra opción es invalidar el detalle para forzar refetch: + // queryClient.invalidateQueries({ queryKey: CUSTOMER_QUERY_KEY(customerId) }); + + // Invalida el listado para refrescar desde servidor + queryClient.invalidateQueries({ queryKey: CUSTOMER_INVOICES_LIST_KEY }); + }, + }); +} diff --git a/modules/customer-invoices/src/web/pages/customer-invoices-list.tsx b/modules/customer-invoices/src/web/pages/customer-invoices-list.tsx index e5aeb021..06d6d97a 100644 --- a/modules/customer-invoices/src/web/pages/customer-invoices-list.tsx +++ b/modules/customer-invoices/src/web/pages/customer-invoices-list.tsx @@ -13,7 +13,7 @@ export const CustomerInvoicesList = () => { <> -
+

{t("pages.list.title")}

{t("pages.list.description")}

@@ -28,7 +28,7 @@ export const CustomerInvoicesList = () => {
-
+
diff --git a/modules/customer-invoices/src/web/schemas/customer-invoices.form.schema.ts b/modules/customer-invoices/src/web/schemas/customer-invoices.form.schema.ts index 38dc5bd3..ec550677 100644 --- a/modules/customer-invoices/src/web/schemas/customer-invoices.form.schema.ts +++ b/modules/customer-invoices/src/web/schemas/customer-invoices.form.schema.ts @@ -6,4 +6,10 @@ export const CustomerInvoiceFormSchema = z.object({ series: z.string().optional(), }); -export const CustomerInvoiceFormData = z.infer; +export type CustomerInvoiceFormData = z.infer; + +export const defaultCustomerFormData: CustomerInvoiceFormData = { + invoice_number: "", + status: "draft", + series: "", +}; diff --git a/modules/customers/package.json b/modules/customers/package.json index 9e8407a4..0b8dbf99 100644 --- a/modules/customers/package.json +++ b/modules/customers/package.json @@ -12,6 +12,7 @@ }, "peerDependencies": { "@tanstack/react-query": "^5.74.11", + "ag-grid-community": "^33.3.0", "dinero.js": "^1.9.1", "express": "^4.18.2", "i18next": "^25.1.1", @@ -38,7 +39,6 @@ "@repo/rdx-ui": "workspace:*", "@repo/rdx-utils": "workspace:*", "@repo/shadcn-ui": "workspace:*", - "ag-grid-community": "^33.3.0", "ag-grid-react": "^33.3.0", "lucide-react": "^0.503.0", "react": "^19.1.0", diff --git a/modules/customers/src/web/components/customers-list-grid.tsx b/modules/customers/src/web/components/customers-list-grid.tsx index 8d6f4fac..2a7332ad 100644 --- a/modules/customers/src/web/components/customers-list-grid.tsx +++ b/modules/customers/src/web/components/customers-list-grid.tsx @@ -1,16 +1,15 @@ import { AG_GRID_LOCALE_ES } from "@ag-grid-community/locale"; import type { ValueFormatterParams } from "ag-grid-community"; import { - AllCommunityModule, ColDef, GridOptions, - ModuleRegistry, SizeColumnsToContentStrategy, SizeColumnsToFitGridStrategy, SizeColumnsToFitProvidedWidthStrategy, } from "ag-grid-community"; import { useMemo, useState } from "react"; +import { ErrorOverlay } from "@repo/rdx-ui/components"; import { Button } from "@repo/shadcn-ui/components"; import { AgGridReact } from "ag-grid-react"; import { ChevronRightIcon } from "lucide-react"; @@ -19,8 +18,6 @@ import { useCustomersQuery } from "../hooks"; import { useTranslation } from "../i18n"; import { CustomerStatusBadge } from "./customer-status-badge"; -ModuleRegistry.registerModules([AllCommunityModule]); - // Create new GridExample component export const CustomersListGrid = () => { const { t } = useTranslation(); @@ -124,6 +121,19 @@ export const CustomersListGrid = () => { [autoSizeStrategy, colDefs] ); + if (isLoadError) { + return ( + <> + + + ); + } + // Container: Defines the grid's theme & dimensions. return (
{ const dataSource = useDataSource(); const keys = useQueryKey(); diff --git a/modules/customers/src/web/pages/customer-list.tsx b/modules/customers/src/web/pages/customer-list.tsx index e47e9972..8c2095b7 100644 --- a/modules/customers/src/web/pages/customer-list.tsx +++ b/modules/customers/src/web/pages/customer-list.tsx @@ -21,11 +21,11 @@ export const CustomersList = () => {
-
+
diff --git a/package.json b/package.json index 88c4fc13..bf22a327 100644 --- a/package.json +++ b/package.json @@ -31,5 +31,5 @@ "engines": { "node": ">=18" }, - "packageManager": "pnpm@10.15.0" + "packageManager": "pnpm@10.17.1" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bbec61f3..5e3dd562 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -191,7 +191,7 @@ importers: version: 29.7.0(@types/node@22.15.32)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.32)(typescript@5.8.3)) ts-jest: specifier: ^29.2.5 - version: 29.4.0(@babel/core@7.27.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.4))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.15.32)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.32)(typescript@5.8.3)))(typescript@5.8.3) + version: 29.4.0(@babel/core@7.27.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.4))(esbuild@0.25.5)(jest-util@29.7.0)(jest@29.7.0(@types/node@22.15.32)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.32)(typescript@5.8.3)))(typescript@5.8.3) tsconfig-paths: specifier: ^4.2.0 version: 4.2.0 @@ -395,6 +395,9 @@ importers: '@tanstack/react-query': specifier: ^5.75.4 version: 5.81.2(react@19.1.0) + ag-grid-community: + specifier: ^33.3.0 + version: 33.3.2 axios: specifier: ^1.9.0 version: 1.10.0 @@ -572,8 +575,6 @@ importers: specifier: ^5.8.3 version: 5.8.3 - modules/customer-payments: {} - modules/customers: dependencies: '@ag-grid-community/locale': @@ -705,40 +706,6 @@ importers: specifier: ^4.17.21 version: 4.17.23 - modules/document-numbering: - dependencies: - '@erp/auth': - specifier: workspace:* - version: link:../auth - '@erp/core': - specifier: workspace:* - version: link:../core - '@repo/rdx-criteria': - specifier: workspace:* - version: link:../../packages/rdx-criteria - '@repo/rdx-ddd': - specifier: workspace:* - version: link:../../packages/rdx-ddd - '@repo/rdx-logger': - specifier: workspace:* - version: link:../../packages/rdx-logger - '@repo/rdx-utils': - specifier: workspace:* - version: link:../../packages/rdx-utils - express: - specifier: ^4.18.2 - version: 4.21.2 - sequelize: - specifier: ^6.37.5 - version: 6.37.7(mysql2@3.14.1) - zod: - specifier: ^4.1.11 - version: 4.1.11 - devDependencies: - '@types/express': - specifier: ^4.17.21 - version: 4.17.23 - modules/verifactu: dependencies: '@erp/auth': @@ -12871,7 +12838,7 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.4.0(@babel/core@7.27.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.4))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.15.32)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.32)(typescript@5.8.3)))(typescript@5.8.3): + ts-jest@29.4.0(@babel/core@7.27.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.4))(esbuild@0.25.5)(jest-util@29.7.0)(jest@29.7.0(@types/node@22.15.32)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.15.32)(typescript@5.8.3)))(typescript@5.8.3): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 @@ -12889,6 +12856,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.27.4) + esbuild: 0.25.5 jest-util: 29.7.0 ts-node@10.9.2(@types/node@22.15.32)(typescript@5.8.3):