.
This commit is contained in:
parent
a845edb9c7
commit
47802ab932
@ -57,7 +57,7 @@
|
|||||||
"react-currency-input-field": "^3.8.0",
|
"react-currency-input-field": "^3.8.0",
|
||||||
"react-day-picker": "^8.10.1",
|
"react-day-picker": "^8.10.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hook-form": "^7.51.5",
|
"react-hook-form": "^7.52.1",
|
||||||
"react-hook-form-persist": "^2.1.0",
|
"react-hook-form-persist": "^2.1.0",
|
||||||
"react-i18next": "^14.1.2",
|
"react-i18next": "^14.1.2",
|
||||||
"react-resizable-panels": "^2.0.19",
|
"react-resizable-panels": "^2.0.19",
|
||||||
|
|||||||
@ -42,26 +42,27 @@ import { useCallback, useMemo, useState } from "react";
|
|||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
import { FieldValues, UseFieldArrayReturn } from "react-hook-form";
|
import { FieldValues, UseFieldArrayReturn } from "react-hook-form";
|
||||||
import { AddNewRowButton } from "./AddNewRowButton";
|
import { AddNewRowButton } from "./AddNewRowButton";
|
||||||
import { SortableDataTableToolbar } from "./SortableDataTableToolbar";
|
import { QuoteItemsSortableDataTableToolbar } from "./QuoteItemsSortableDataTableToolbar";
|
||||||
import { SortableTableRow } from "./SortableTableRow";
|
import { QuoteItemsSortableTableRow } from "./QuoteItemsSortableTableRow";
|
||||||
|
|
||||||
declare module "@tanstack/react-table" {
|
declare module "@tanstack/react-table" {
|
||||||
interface TableMeta<TData extends RowData> {
|
interface TableMeta<TData extends RowData> {
|
||||||
insertItem: (rowIndex: number, data: TData) => void;
|
insertItem: (rowIndex: number, data?: unknown) => void;
|
||||||
appendItem: (data: TData) => void;
|
appendItem: (data?: unknown) => void;
|
||||||
duplicateItems: (rowIndex?: number) => void;
|
duplicateItems: (rowIndex?: number) => void;
|
||||||
deleteItems: (rowIndex?: number | number[]) => void;
|
deleteItems: (rowIndex?: number | number[]) => void;
|
||||||
updateItem: (rowIndex: number, rowData: TData, fieldName: string, value: unknown) => void;
|
updateItem: (rowIndex: number, rowData: TData, fieldName: string, value: unknown) => void;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SortableProps {
|
export interface QuoteItemsSortableProps {
|
||||||
id: UniqueIdentifier;
|
id: UniqueIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SortableDataTableProps = {
|
export type QuoteItemsSortableDataTableProps = {
|
||||||
columns: ColumnDef<unknown, unknown>[];
|
columns: ColumnDef<unknown, unknown>[];
|
||||||
data: Record<"id", string>[];
|
data: Record<"id", string>[];
|
||||||
|
defaultValues: Readonly<{ [x: string]: any }> | undefined;
|
||||||
actions: Omit<UseFieldArrayReturn<FieldValues, "items">, "fields">;
|
actions: Omit<UseFieldArrayReturn<FieldValues, "items">, "fields">;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -94,34 +95,12 @@ const dropAnimationConfig: DropAnimation = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/*const defaultColumn: Partial<ColumnDef<unknown>> = {
|
export function QuoteItemsSortableDataTable({
|
||||||
cell: ({ table, row: { index, original }, column, getValue }) => {
|
columns,
|
||||||
const initialValue = getValue();
|
data,
|
||||||
|
defaultValues,
|
||||||
// We need to keep and update the state of the cell normally
|
actions,
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
}: QuoteItemsSortableDataTableProps) {
|
||||||
const [value, setValue] = useState(initialValue);
|
|
||||||
|
|
||||||
// If the initialValue is changed external, sync it up with our state
|
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
||||||
useEffect(() => {
|
|
||||||
setValue(initialValue);
|
|
||||||
}, [initialValue]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<input
|
|
||||||
value={value as string}
|
|
||||||
onChange={(e) => setValue(e.target.value)}
|
|
||||||
onBlur={() => {
|
|
||||||
console.log(column.id, value);
|
|
||||||
table.options.meta?.updateItem(index, original, column.id, value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};*/
|
|
||||||
|
|
||||||
export function SortableDataTable({ columns, data, actions }: SortableDataTableProps) {
|
|
||||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||||
const [activeId, setActiveId] = useState<UniqueIdentifier | null>();
|
const [activeId, setActiveId] = useState<UniqueIdentifier | null>();
|
||||||
|
|
||||||
@ -154,11 +133,11 @@ export function SortableDataTable({ columns, data, actions }: SortableDataTableP
|
|||||||
maxSize: 96, //enforced during column resizing
|
maxSize: 96, //enforced during column resizing
|
||||||
},
|
},
|
||||||
meta: {
|
meta: {
|
||||||
insertItem: (rowIndex: number, data: object = {}) => {
|
insertItem: (rowIndex: number, data?: unknown) => {
|
||||||
actions.insert(rowIndex, data, { shouldFocus: true });
|
actions.insert(rowIndex, data || defaultValues?.items[0], { shouldFocus: true });
|
||||||
},
|
},
|
||||||
appendItem: (data: object = {}) => {
|
appendItem: (data?: unknown) => {
|
||||||
actions.append(data, { shouldFocus: true });
|
actions.append(data || defaultValues?.items[0], { shouldFocus: true });
|
||||||
},
|
},
|
||||||
duplicateItems: (rowIndex?: number) => {
|
duplicateItems: (rowIndex?: number) => {
|
||||||
if (rowIndex != undefined) {
|
if (rowIndex != undefined) {
|
||||||
@ -323,7 +302,7 @@ export function SortableDataTable({ columns, data, actions }: SortableDataTableP
|
|||||||
onDragCancel={handleDragCancel}
|
onDragCancel={handleDragCancel}
|
||||||
collisionDetection={closestCenter}
|
collisionDetection={closestCenter}
|
||||||
>
|
>
|
||||||
<SortableDataTableToolbar table={table} />
|
<QuoteItemsSortableDataTableToolbar table={table} />
|
||||||
<Table className='table-fixed'>
|
<Table className='table-fixed'>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
@ -346,13 +325,13 @@ export function SortableDataTable({ columns, data, actions }: SortableDataTableP
|
|||||||
strategy={verticalListSortingStrategy}
|
strategy={verticalListSortingStrategy}
|
||||||
>
|
>
|
||||||
{filterItems(table.getRowModel().rows).map((row) => (
|
{filterItems(table.getRowModel().rows).map((row) => (
|
||||||
<SortableTableRow key={row.id} id={row.id}>
|
<QuoteItemsSortableTableRow key={row.id} id={row.id}>
|
||||||
{row.getVisibleCells().map((cell) => (
|
{row.getVisibleCells().map((cell) => (
|
||||||
<TableCell key={cell.id} className='px-2 py-1 align-top'>
|
<TableCell key={cell.id} className='px-2 py-1 align-top'>
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
))}
|
))}
|
||||||
</SortableTableRow>
|
</QuoteItemsSortableTableRow>
|
||||||
))}
|
))}
|
||||||
</SortableContext>
|
</SortableContext>
|
||||||
</TableBody>
|
</TableBody>
|
||||||
@ -27,7 +27,7 @@ import {
|
|||||||
Trash2Icon,
|
Trash2Icon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
export const SortableDataTableToolbar = ({ table }: { table: Table<unknown> }) => {
|
export const QuoteItemsSortableDataTableToolbar = ({ table }: { table: Table<unknown> }) => {
|
||||||
const selectedRowsCount = table.getSelectedRowModel().rows.length;
|
const selectedRowsCount = table.getSelectedRowModel().rows.length;
|
||||||
|
|
||||||
if (selectedRowsCount) {
|
if (selectedRowsCount) {
|
||||||
@ -4,20 +4,20 @@ import { DraggableSyntheticListeners } from "@dnd-kit/core";
|
|||||||
import { defaultAnimateLayoutChanges, useSortable } from "@dnd-kit/sortable";
|
import { defaultAnimateLayoutChanges, useSortable } from "@dnd-kit/sortable";
|
||||||
import { CSS } from "@dnd-kit/utilities";
|
import { CSS } from "@dnd-kit/utilities";
|
||||||
import { CSSProperties, PropsWithChildren, createContext, useMemo } from "react";
|
import { CSSProperties, PropsWithChildren, createContext, useMemo } from "react";
|
||||||
import { SortableProps } from "./SortableDataTable";
|
import { QuoteItemsSortableProps } from "./QuoteItemsSortableDataTable";
|
||||||
|
|
||||||
interface Context {
|
interface Context {
|
||||||
attributes: Record<string, any>;
|
attributes: Record<string, any>;
|
||||||
listeners: DraggableSyntheticListeners;
|
listeners: DraggableSyntheticListeners;
|
||||||
ref(node: HTMLElement | null): void;
|
ref(node: HTMLElement | null): void;
|
||||||
}
|
}
|
||||||
export const SortableTableRowContext = createContext<Context>({
|
export const QuoteItemsSortableTableRowContext = createContext<Context>({
|
||||||
attributes: {},
|
attributes: {},
|
||||||
listeners: undefined,
|
listeners: undefined,
|
||||||
ref() {},
|
ref() {},
|
||||||
});
|
});
|
||||||
|
|
||||||
function animateLayoutChanges(args) {
|
function animateLayoutChanges(args: any) {
|
||||||
if (args.isSorting || args.wasDragging) {
|
if (args.isSorting || args.wasDragging) {
|
||||||
return defaultAnimateLayoutChanges(args);
|
return defaultAnimateLayoutChanges(args);
|
||||||
}
|
}
|
||||||
@ -25,7 +25,10 @@ function animateLayoutChanges(args) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SortableTableRow({ id, children }: PropsWithChildren<SortableProps>) {
|
export function QuoteItemsSortableTableRow({
|
||||||
|
id,
|
||||||
|
children,
|
||||||
|
}: PropsWithChildren<QuoteItemsSortableProps>) {
|
||||||
const {
|
const {
|
||||||
attributes,
|
attributes,
|
||||||
isDragging,
|
isDragging,
|
||||||
@ -54,7 +57,7 @@ export function SortableTableRow({ id, children }: PropsWithChildren<SortablePro
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SortableTableRowContext.Provider value={context}>
|
<QuoteItemsSortableTableRowContext.Provider value={context}>
|
||||||
<TableRow
|
<TableRow
|
||||||
key={id}
|
key={id}
|
||||||
id={String(id)}
|
id={String(id)}
|
||||||
@ -67,6 +70,6 @@ export function SortableTableRow({ id, children }: PropsWithChildren<SortablePro
|
|||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</SortableTableRowContext.Provider>
|
</QuoteItemsSortableTableRowContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1,8 +1,7 @@
|
|||||||
import { Badge, Button, Card, CardContent } from "@/ui";
|
import { Badge, Button, Card, CardContent } from "@/ui";
|
||||||
|
|
||||||
import { DataTableSkeleton, ErrorOverlay, SimpleEmptyState } from "@/components";
|
import { DataTable, DataTableSkeleton, ErrorOverlay, SimpleEmptyState } from "@/components";
|
||||||
|
|
||||||
import { DataTable } from "@/components";
|
|
||||||
import { DataTableToolbar } from "@/components/DataTable/DataTableToolbar";
|
import { DataTableToolbar } from "@/components/DataTable/DataTableToolbar";
|
||||||
import { useDataTable, useDataTableContext } from "@/lib/hooks";
|
import { useDataTable, useDataTableContext } from "@/lib/hooks";
|
||||||
import { IListQuotes_Response_DTO, MoneyValue, UTCDateValue } from "@shared/contexts";
|
import { IListQuotes_Response_DTO, MoneyValue, UTCDateValue } from "@shared/contexts";
|
||||||
|
|||||||
@ -12,16 +12,18 @@ import { t } from "i18next";
|
|||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { useFieldArray, useFormContext } from "react-hook-form";
|
import { useFieldArray, useFormContext } from "react-hook-form";
|
||||||
import { useDetailColumns } from "../../hooks";
|
import { useDetailColumns } from "../../hooks";
|
||||||
import { SortableDataTable } from "../SortableDataTable";
|
import { QuoteItemsSortableDataTable } from "../QuoteItemsSortableDataTable";
|
||||||
|
|
||||||
export const QuoteDetailsCardEditor = ({
|
export const QuoteDetailsCardEditor = ({
|
||||||
currency,
|
currency,
|
||||||
language,
|
language,
|
||||||
|
defaultValues,
|
||||||
}: {
|
}: {
|
||||||
currency: CurrencyData;
|
currency: CurrencyData;
|
||||||
language: Language;
|
language: Language;
|
||||||
|
defaultValues: Readonly<{ [x: string]: any }> | undefined;
|
||||||
}) => {
|
}) => {
|
||||||
const { control, register, getValues } = useFormContext();
|
const { control, register } = useFormContext();
|
||||||
|
|
||||||
const { fields, ...fieldActions } = useFieldArray({
|
const { fields, ...fieldActions } = useFieldArray({
|
||||||
control,
|
control,
|
||||||
@ -186,7 +188,7 @@ export const QuoteDetailsCardEditor = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleInsertArticle = useCallback(
|
const handleInsertArticle = useCallback(
|
||||||
(newArticle) => {
|
(newArticle: any) => {
|
||||||
fieldActions.append({
|
fieldActions.append({
|
||||||
...newArticle,
|
...newArticle,
|
||||||
quantity: {
|
quantity: {
|
||||||
@ -204,7 +206,14 @@ export const QuoteDetailsCardEditor = ({
|
|||||||
const defaultLayout = [265, 440, 655];
|
const defaultLayout = [265, 440, 655];
|
||||||
const navCollapsedSize = 4;
|
const navCollapsedSize = 4;
|
||||||
|
|
||||||
return <SortableDataTable actions={fieldActions} columns={columns} data={fields} />;
|
return (
|
||||||
|
<QuoteItemsSortableDataTable
|
||||||
|
actions={fieldActions}
|
||||||
|
columns={columns}
|
||||||
|
data={fields}
|
||||||
|
defaultValues={defaultValues}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ResizablePanelGroup
|
<ResizablePanelGroup
|
||||||
@ -226,7 +235,7 @@ export const QuoteDetailsCardEditor = ({
|
|||||||
}}
|
}}
|
||||||
className={cn(isCollapsed && "min-w-[50px] transition-all duration-300 ease-in-out")}
|
className={cn(isCollapsed && "min-w-[50px] transition-all duration-300 ease-in-out")}
|
||||||
>
|
>
|
||||||
<SortableDataTable actions={fieldActions} columns={columns} data={fields} />
|
<QuoteItemsSortableDataTable actions={fieldActions} columns={columns} data={fields} />
|
||||||
</ResizablePanel>
|
</ResizablePanel>
|
||||||
<ResizableHandle withHandle className='mx-3' />
|
<ResizableHandle withHandle className='mx-3' />
|
||||||
<ResizablePanel defaultSize={defaultLayout[1]} minSize={10}>
|
<ResizablePanel defaultSize={defaultLayout[1]} minSize={10}>
|
||||||
|
|||||||
@ -6,6 +6,12 @@ import { useFormContext } from "react-hook-form";
|
|||||||
export const QuoteGeneralCardEditor = () => {
|
export const QuoteGeneralCardEditor = () => {
|
||||||
const { register, formState } = useFormContext();
|
const { register, formState } = useFormContext();
|
||||||
|
|
||||||
|
console.log({
|
||||||
|
...register("customer_information", {
|
||||||
|
required: true,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='grid gap-6 md:grid-cols-6'>
|
<div className='grid gap-6 md:grid-cols-6'>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
|
|||||||
@ -48,7 +48,7 @@ export const QuoteCreate = () => {
|
|||||||
setWarnWhen(false);
|
setWarnWhen(false);
|
||||||
mutate(formData, {
|
mutate(formData, {
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
navigate(`/quotes/edit/${data.id}`, { relative: "path" });
|
navigate(`/quotes/edit/${data.id}`, { relative: "path", replace: true });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@ -8,16 +8,20 @@ import {
|
|||||||
import { calculateItemTotals } from "@/lib/calc";
|
import { calculateItemTotals } from "@/lib/calc";
|
||||||
import { useUrlId } from "@/lib/hooks/useUrlId";
|
import { useUrlId } from "@/lib/hooks/useUrlId";
|
||||||
import { Badge, Button, Form, Tabs, TabsContent, TabsList, TabsTrigger } from "@/ui";
|
import { Badge, Button, Form, Tabs, TabsContent, TabsList, TabsTrigger } from "@/ui";
|
||||||
import { CurrencyData, IUpdateQuote_Request_DTO, Language, MoneyValue } from "@shared/contexts";
|
import { CurrencyData, IGetQuote_Response_DTO, Language, MoneyValue } from "@shared/contexts";
|
||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { SubmitHandler, useForm } from "react-hook-form";
|
import { SubmitHandler, useForm } from "react-hook-form";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import { QuoteDetailsCardEditor, QuoteGeneralCardEditor } from "./components/editors";
|
import { QuoteDetailsCardEditor, QuoteGeneralCardEditor } from "./components/editors";
|
||||||
import { useQuotes } from "./hooks";
|
import { useQuotes } from "./hooks";
|
||||||
|
|
||||||
interface QuoteDataForm extends IUpdateQuote_Request_DTO {}
|
/*type QuoteDataForm = Omit<IGetQuote_Response_DTO, "items"> & {
|
||||||
|
items: IGetQuote_QuoteItem_Response_DTO;
|
||||||
|
};*/
|
||||||
|
|
||||||
|
type QuoteDataForm = IGetQuote_Response_DTO;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
export const QuoteEdit = () => {
|
export const QuoteEdit = () => {
|
||||||
@ -42,12 +46,8 @@ export const QuoteEdit = () => {
|
|||||||
|
|
||||||
const { data, status, error: queryError } = useOne(quoteId);
|
const { data, status, error: queryError } = useOne(quoteId);
|
||||||
|
|
||||||
const { mutate } = useUpdate(String(quoteId));
|
const defaultValues = useMemo(
|
||||||
|
() => ({
|
||||||
const form = useForm<QuoteDataForm>({
|
|
||||||
mode: "onBlur",
|
|
||||||
values: data,
|
|
||||||
defaultValues: {
|
|
||||||
date: "",
|
date: "",
|
||||||
reference: "",
|
reference: "",
|
||||||
customer_information: "",
|
customer_information: "",
|
||||||
@ -59,7 +59,7 @@ export const QuoteEdit = () => {
|
|||||||
subtotal_price: {
|
subtotal_price: {
|
||||||
amount: undefined,
|
amount: undefined,
|
||||||
scale: 2,
|
scale: 2,
|
||||||
currency_code: data?.currency_code,
|
currency_code: data?.currency_code ?? quoteCurrency.code,
|
||||||
},
|
},
|
||||||
discount: {
|
discount: {
|
||||||
amount: undefined,
|
amount: undefined,
|
||||||
@ -68,32 +68,46 @@ export const QuoteEdit = () => {
|
|||||||
total_price: {
|
total_price: {
|
||||||
amount: undefined,
|
amount: undefined,
|
||||||
scale: 2,
|
scale: 2,
|
||||||
currency_code: data?.currency_code,
|
currency_code: data?.currency_code ?? quoteCurrency.code,
|
||||||
},
|
},
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
description: "",
|
description: "",
|
||||||
quantity: {
|
quantity: {
|
||||||
amount: undefined,
|
amount: 1,
|
||||||
scale: 0,
|
scale: 0,
|
||||||
},
|
},
|
||||||
subtotal_price: {
|
unit_price: {
|
||||||
amount: undefined,
|
amount: null,
|
||||||
scale: 4,
|
scale: 4,
|
||||||
currency_code: data?.currency_code,
|
currency_code: data?.currency_code ?? quoteCurrency.code,
|
||||||
|
},
|
||||||
|
subtotal_price: {
|
||||||
|
amount: null,
|
||||||
|
scale: 4,
|
||||||
|
currency_code: data?.currency_code ?? quoteCurrency.code,
|
||||||
},
|
},
|
||||||
discount: {
|
discount: {
|
||||||
amount: undefined,
|
amount: null,
|
||||||
scale: 2,
|
scale: 2,
|
||||||
},
|
},
|
||||||
total_price: {
|
total_price: {
|
||||||
amount: undefined,
|
amount: null,
|
||||||
scale: 4,
|
scale: 4,
|
||||||
currency_code: data?.currency_code,
|
currency_code: data?.currency_code ?? quoteCurrency.code,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
}),
|
||||||
|
[data, quoteCurrency]
|
||||||
|
);
|
||||||
|
|
||||||
|
const { mutate } = useUpdate(String(quoteId));
|
||||||
|
|
||||||
|
const form = useForm<QuoteDataForm>({
|
||||||
|
mode: "onBlur",
|
||||||
|
values: data,
|
||||||
|
defaultValues,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { watch, getValues, setValue, formState } = form;
|
const { watch, getValues, setValue, formState } = form;
|
||||||
@ -117,6 +131,7 @@ export const QuoteEdit = () => {
|
|||||||
mutate(data, {
|
mutate(data, {
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.debug(error);
|
console.debug(error);
|
||||||
|
toast.error(error.message);
|
||||||
//alert(error.message);
|
//alert(error.message);
|
||||||
},
|
},
|
||||||
//onSettled: () => {},
|
//onSettled: () => {},
|
||||||
@ -132,9 +147,6 @@ export const QuoteEdit = () => {
|
|||||||
const { unsubscribe } = watch((_, { name, type }) => {
|
const { unsubscribe } = watch((_, { name, type }) => {
|
||||||
const value = getValues();
|
const value = getValues();
|
||||||
|
|
||||||
console.log("USEEFFECT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
|
||||||
console.log(name);
|
|
||||||
|
|
||||||
if (name) {
|
if (name) {
|
||||||
if (name === "currency_code") {
|
if (name === "currency_code") {
|
||||||
setQuoteCurrency(
|
setQuoteCurrency(
|
||||||
@ -241,7 +253,11 @@ export const QuoteEdit = () => {
|
|||||||
<QuoteGeneralCardEditor />
|
<QuoteGeneralCardEditor />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value='items'>
|
<TabsContent value='items'>
|
||||||
<QuoteDetailsCardEditor currency={quoteCurrency} language={quoteLanguage} />
|
<QuoteDetailsCardEditor
|
||||||
|
currency={quoteCurrency}
|
||||||
|
language={quoteLanguage}
|
||||||
|
defaultValues={defaultValues}
|
||||||
|
/>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value='history'></TabsContent>
|
<TabsContent value='history'></TabsContent>
|
||||||
|
|||||||
@ -110,6 +110,9 @@ export const FormCurrencyField = React.forwardRef<HTMLInputElement, FormCurrency
|
|||||||
)}
|
)}
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<CurrencyInput
|
<CurrencyInput
|
||||||
|
intlConfig={{
|
||||||
|
locale: language.code,
|
||||||
|
}}
|
||||||
name={field.name}
|
name={field.name}
|
||||||
//ref={field.ref} <-- no activar que hace cosas raras
|
//ref={field.ref} <-- no activar que hace cosas raras
|
||||||
onBlur={field.onBlur}
|
onBlur={field.onBlur}
|
||||||
|
|||||||
@ -48,7 +48,7 @@ import { SortableTableRow } from "./SortableTableRow";
|
|||||||
declare module "@tanstack/react-table" {
|
declare module "@tanstack/react-table" {
|
||||||
interface TableMeta<TData extends RowData> {
|
interface TableMeta<TData extends RowData> {
|
||||||
insertItem: (rowIndex: number, data: TData) => void;
|
insertItem: (rowIndex: number, data: TData) => void;
|
||||||
appendItem: (data: TData) => void;
|
appendItem: (data?: TData) => void;
|
||||||
duplicateItems: (rowIndex?: number) => void;
|
duplicateItems: (rowIndex?: number) => void;
|
||||||
deleteItems: (rowIndex?: number | number[]) => void;
|
deleteItems: (rowIndex?: number | number[]) => void;
|
||||||
updateItem: (rowIndex: number, rowData: TData, fieldName: string, value: unknown) => void;
|
updateItem: (rowIndex: number, rowData: TData, fieldName: string, value: unknown) => void;
|
||||||
|
|||||||
@ -11,5 +11,5 @@ export * from "./Layout";
|
|||||||
export * from "./LoadingIndicator";
|
export * from "./LoadingIndicator";
|
||||||
export * from "./LoadingOverlay";
|
export * from "./LoadingOverlay";
|
||||||
export * from "./ProtectedRoute";
|
export * from "./ProtectedRoute";
|
||||||
export * from "./SorteableDataTable";
|
//export * from "./SorteableDataTable";
|
||||||
export * from "./TailwindIndicator";
|
export * from "./TailwindIndicator";
|
||||||
|
|||||||
@ -1,24 +1,13 @@
|
|||||||
import { MoneyValue, Percentage, Quantity } from "@shared/contexts";
|
import { MoneyValue, Percentage, Quantity } from "@shared/contexts";
|
||||||
import { IMoney, IPercentage, IQuantity } from "./types";
|
|
||||||
|
|
||||||
export const calculateItemTotals = (item: {
|
export const calculateItemTotals = (item: any) => {
|
||||||
quantity?: IQuantity;
|
|
||||||
unit_price?: IMoney;
|
|
||||||
discount?: IPercentage;
|
|
||||||
}): {
|
|
||||||
quantity: Quantity;
|
|
||||||
unitPrice: MoneyValue;
|
|
||||||
subtotalPrice: MoneyValue;
|
|
||||||
discount: Percentage;
|
|
||||||
totalPrice: MoneyValue;
|
|
||||||
} | null => {
|
|
||||||
console.log(item);
|
console.log(item);
|
||||||
|
|
||||||
const { quantity: quantity_dto, unit_price: unit_price_dto, discount: discount_dto } = item;
|
const { quantity: quantity_dto, unit_price: unit_price_dto, discount: discount_dto } = item;
|
||||||
|
|
||||||
if (quantity_dto === "" || unit_price_dto === "") {
|
/*if (quantity_dto.amount === null || unit_price_dto.amount === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
const quantityOrError = Quantity.create(quantity_dto);
|
const quantityOrError = Quantity.create(quantity_dto);
|
||||||
if (quantityOrError.isFailure) {
|
if (quantityOrError.isFailure) {
|
||||||
|
|||||||
@ -7,33 +7,29 @@ import {
|
|||||||
import { NullOr } from "@shared/utilities";
|
import { NullOr } from "@shared/utilities";
|
||||||
import Joi from "joi";
|
import Joi from "joi";
|
||||||
|
|
||||||
export interface IArticleIdentifierOptions
|
export interface IArticleIdentifierOptions extends INullableValueObjectOptions {}
|
||||||
extends INullableValueObjectOptions {}
|
|
||||||
|
|
||||||
export class ArticleIdentifier extends NullableValueObject<number> {
|
export class ArticleIdentifier extends NullableValueObject<number> {
|
||||||
protected static validate(
|
protected static validate(
|
||||||
value: NullOr<number | string>,
|
value: NullOr<number | string>,
|
||||||
options: IArticleIdentifierOptions = {},
|
options: IArticleIdentifierOptions = {}
|
||||||
) {
|
) {
|
||||||
const ruleNull = RuleValidator.RULE_ALLOW_NULL_OR_UNDEFINED.default(null);
|
const ruleNull = RuleValidator.RULE_ALLOW_NULL_OR_UNDEFINED.default(null);
|
||||||
|
|
||||||
const ruleNumber = RuleValidator.RULE_IS_TYPE_NUMBER.label(
|
const ruleNumber = RuleValidator.RULE_IS_TYPE_NUMBER.label(
|
||||||
options.label ? options.label : "ArticleIdentifier",
|
options.label ? options.label : "ArticleIdentifier"
|
||||||
);
|
);
|
||||||
|
|
||||||
const ruleString = RuleValidator.RULE_IS_TYPE_STRING.regex(
|
const ruleString = RuleValidator.RULE_IS_TYPE_STRING.regex(/^[-]?\d+$/).label(
|
||||||
/^[-]?\d+$/,
|
options.label ? options.label : "ArticleIdentifier"
|
||||||
).label(options.label ? options.label : "ArticleIdentifier");
|
);
|
||||||
|
|
||||||
const rules = Joi.alternatives(ruleNull, ruleNumber, ruleString);
|
const rules = Joi.alternatives(ruleNull, ruleNumber, ruleString);
|
||||||
|
|
||||||
return RuleValidator.validate<NullOr<number>>(rules, value);
|
return RuleValidator.validate<NullOr<number>>(rules, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static create(
|
public static create(value: NullOr<number | string>, options: IArticleIdentifierOptions = {}) {
|
||||||
value: NullOr<number | string>,
|
|
||||||
options: IArticleIdentifierOptions = {},
|
|
||||||
) {
|
|
||||||
const _options = {
|
const _options = {
|
||||||
label: "ArticleIdentifier",
|
label: "ArticleIdentifier",
|
||||||
...options,
|
...options,
|
||||||
@ -64,8 +60,8 @@ export class ArticleIdentifier extends NullableValueObject<number> {
|
|||||||
return this.isNull() ? "" : String(this.value);
|
return this.isNull() ? "" : String(this.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public toPrimitive(): number {
|
public toPrimitive(): number | null {
|
||||||
return this.toNumber();
|
return this.isNull() ? null : this.toNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
public increment(amount: number = 1) {
|
public increment(amount: number = 1) {
|
||||||
|
|||||||
@ -190,7 +190,10 @@ export class CreateQuoteUseCase
|
|||||||
QuoteItem.create({
|
QuoteItem.create({
|
||||||
articleId: item.article_id,
|
articleId: item.article_id,
|
||||||
description: Description.create(item.description).object,
|
description: Description.create(item.description).object,
|
||||||
quantity: Quantity.create(item.quantity).object,
|
quantity: Quantity.create({
|
||||||
|
amount: item.quantity.amount,
|
||||||
|
scale: item.quantity.scale,
|
||||||
|
}).object,
|
||||||
unitPrice: UnitPrice.create({
|
unitPrice: UnitPrice.create({
|
||||||
amount: item.unit_price?.amount,
|
amount: item.unit_price?.amount,
|
||||||
currencyCode: item.unit_price?.currency_code,
|
currencyCode: item.unit_price?.currency_code,
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import {
|
|||||||
UnitPrice,
|
UnitPrice,
|
||||||
} from "@shared/contexts";
|
} from "@shared/contexts";
|
||||||
|
|
||||||
|
import { ArticleIdentifier } from "@/contexts/catalog/domain";
|
||||||
import { IUpdateQuote_Request_DTO } from "@shared/contexts";
|
import { IUpdateQuote_Request_DTO } from "@shared/contexts";
|
||||||
import {
|
import {
|
||||||
Dealer,
|
Dealer,
|
||||||
@ -183,28 +184,73 @@ export class UpdateQuoteUseCase
|
|||||||
return Result.fail(discountOrError.error);
|
return Result.fail(discountOrError.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const items = new Collection<QuoteItem>(
|
let items: Collection<QuoteItem>;
|
||||||
quoteDTO.items?.map(
|
|
||||||
(item) =>
|
try {
|
||||||
QuoteItem.create({
|
items = new Collection<QuoteItem>(
|
||||||
articleId: item.article_id,
|
quoteDTO.items?.map((item) => {
|
||||||
description: Description.create(item.description).object,
|
const articleIdOrError = ArticleIdentifier.create(item.article_id);
|
||||||
quantity: Quantity.create({
|
if (articleIdOrError.isFailure) {
|
||||||
amount: item.quantity.amount,
|
throw articleIdOrError.error;
|
||||||
scale: item.quantity.scale,
|
}
|
||||||
}).object,
|
|
||||||
unitPrice: UnitPrice.create({
|
const descriptionOrError = Description.create(item.description);
|
||||||
amount: item.unit_price?.amount,
|
if (descriptionOrError.isFailure) {
|
||||||
currencyCode: item.unit_price?.currency_code,
|
throw descriptionOrError.error;
|
||||||
scale: item.unit_price?.scale,
|
}
|
||||||
}).object,
|
|
||||||
discount: Percentage.create({
|
const quantityOrError = Quantity.create({
|
||||||
amount: item.discount?.amount,
|
amount: item.quantity.amount,
|
||||||
scale: item.discount?.scale,
|
scale: item.quantity.scale,
|
||||||
}).object,
|
});
|
||||||
}).object
|
if (quantityOrError.isFailure) {
|
||||||
)
|
throw quantityOrError.error;
|
||||||
);
|
}
|
||||||
|
|
||||||
|
const unitPriceOrError = UnitPrice.create({
|
||||||
|
amount: item.unit_price?.amount,
|
||||||
|
currencyCode: item.unit_price?.currency_code,
|
||||||
|
scale: item.unit_price?.scale,
|
||||||
|
});
|
||||||
|
if (unitPriceOrError.isFailure) {
|
||||||
|
throw unitPriceOrError.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const percentageOrError = Percentage.create({
|
||||||
|
amount: item.discount?.amount,
|
||||||
|
scale: item.discount?.scale,
|
||||||
|
});
|
||||||
|
if (percentageOrError.isFailure) {
|
||||||
|
throw percentageOrError.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const quoteItemOrError = QuoteItem.create({
|
||||||
|
articleId: articleIdOrError.object,
|
||||||
|
description: descriptionOrError.object,
|
||||||
|
quantity: quantityOrError.object,
|
||||||
|
unitPrice: unitPriceOrError.object,
|
||||||
|
discount: percentageOrError.object,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (quoteItemOrError.isFailure) {
|
||||||
|
throw quoteItemOrError.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return quoteItemOrError.object;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} catch (e: unknown) {
|
||||||
|
//let error = e as Error;
|
||||||
|
/*if (error.name === "ValidationError") {
|
||||||
|
error = e as ValidationError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.name === "DomainError") {
|
||||||
|
error = e as DomainError;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
return Result.fail(e as IDomainError);
|
||||||
|
}
|
||||||
|
|
||||||
return Quote.create(
|
return Quote.create(
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ArticleIdentifier } from "@/contexts/catalog/domain";
|
||||||
import {
|
import {
|
||||||
Description,
|
Description,
|
||||||
Entity,
|
Entity,
|
||||||
@ -11,7 +12,7 @@ import {
|
|||||||
} from "@shared/contexts";
|
} from "@shared/contexts";
|
||||||
|
|
||||||
export interface IQuoteItemProps extends IEntityProps {
|
export interface IQuoteItemProps extends IEntityProps {
|
||||||
articleId: string | null;
|
articleId: ArticleIdentifier;
|
||||||
description: Description; // Descripción del artículo o servicio
|
description: Description; // Descripción del artículo o servicio
|
||||||
quantity: Quantity; // Cantidad de unidades
|
quantity: Quantity; // Cantidad de unidades
|
||||||
unitPrice: MoneyValue; // Precio unitario en la moneda de la factura
|
unitPrice: MoneyValue; // Precio unitario en la moneda de la factura
|
||||||
@ -21,7 +22,7 @@ export interface IQuoteItemProps extends IEntityProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IQuoteItem {
|
export interface IQuoteItem {
|
||||||
articleId: string | null;
|
articleId: ArticleIdentifier;
|
||||||
description: Description;
|
description: Description;
|
||||||
quantity: Quantity;
|
quantity: Quantity;
|
||||||
unitPrice: MoneyValue;
|
unitPrice: MoneyValue;
|
||||||
@ -35,7 +36,7 @@ export class QuoteItem extends Entity<IQuoteItemProps> implements IQuoteItem {
|
|||||||
return Result.ok(new QuoteItem(props, id));
|
return Result.ok(new QuoteItem(props, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
get articleId(): string | null {
|
get articleId(): ArticleIdentifier {
|
||||||
return this.props.articleId;
|
return this.props.articleId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ArticleIdentifier } from "@/contexts/catalog/domain";
|
||||||
import { ISequelizeMapper, SequelizeMapper } from "@/contexts/common/infrastructure";
|
import { ISequelizeMapper, SequelizeMapper } from "@/contexts/common/infrastructure";
|
||||||
import { Description, MoneyValue, Percentage, Quantity, UniqueID } from "@shared/contexts";
|
import { Description, MoneyValue, Percentage, Quantity, UniqueID } from "@shared/contexts";
|
||||||
import { IQuoteItemProps, Quote, QuoteItem } from "../../domain";
|
import { IQuoteItemProps, Quote, QuoteItem } from "../../domain";
|
||||||
@ -22,8 +23,10 @@ class QuoteItemMapper
|
|||||||
const { sourceParent } = params;
|
const { sourceParent } = params;
|
||||||
const id = this.mapsValue(source, "item_id", UniqueID.create);
|
const id = this.mapsValue(source, "item_id", UniqueID.create);
|
||||||
|
|
||||||
|
const articleId = this.mapsValue(source, "id_article", ArticleIdentifier.create);
|
||||||
|
|
||||||
const props: IQuoteItemProps = {
|
const props: IQuoteItemProps = {
|
||||||
articleId: source.id_article === "" ? null : source.id_article,
|
articleId,
|
||||||
description: this.mapsValue(source, "description", Description.create),
|
description: this.mapsValue(source, "description", Description.create),
|
||||||
quantity: this.mapsValue(source, "quantity", (quantity) =>
|
quantity: this.mapsValue(source, "quantity", (quantity) =>
|
||||||
Quantity.create({
|
Quantity.create({
|
||||||
@ -83,7 +86,7 @@ class QuoteItemMapper
|
|||||||
item_id: source.id.toString(),
|
item_id: source.id.toString(),
|
||||||
quote_id: sourceParent.id.toPrimitive(),
|
quote_id: sourceParent.id.toPrimitive(),
|
||||||
position: index,
|
position: index,
|
||||||
id_article: source.articleId,
|
id_article: source.articleId.toPrimitive(),
|
||||||
description: source.description.toPrimitive(),
|
description: source.description.toPrimitive(),
|
||||||
quantity: source.quantity.convertScale(2).toPrimitive(),
|
quantity: source.quantity.convertScale(2).toPrimitive(),
|
||||||
unit_price: source.unitPrice.convertScale(4).toPrimitive(),
|
unit_price: source.unitPrice.convertScale(4).toPrimitive(),
|
||||||
|
|||||||
@ -30,7 +30,7 @@ export class QuoteItem_Model extends Model<
|
|||||||
|
|
||||||
declare quote_id: string;
|
declare quote_id: string;
|
||||||
declare item_id: string;
|
declare item_id: string;
|
||||||
declare id_article: CreationOptional<string | null>;
|
declare id_article: CreationOptional<number | null>;
|
||||||
declare position: number;
|
declare position: number;
|
||||||
declare description: CreationOptional<string | null>;
|
declare description: CreationOptional<string | null>;
|
||||||
declare quantity: CreationOptional<number | null>;
|
declare quantity: CreationOptional<number | null>;
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import Joi from "joi";
|
|||||||
import { Result, RuleValidator } from "../../domain";
|
import { Result, RuleValidator } from "../../domain";
|
||||||
|
|
||||||
export interface IMoney_DTO {
|
export interface IMoney_DTO {
|
||||||
amount: number;
|
amount: number | null;
|
||||||
scale: number;
|
scale: number;
|
||||||
currency_code: string;
|
currency_code: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
export interface IPercentage_DTO {
|
export interface IPercentage_DTO {
|
||||||
amount: number;
|
amount: number | null;
|
||||||
scale: number;
|
scale: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import Joi from "joi";
|
import Joi from "joi";
|
||||||
import { Result, RuleValidator } from "../../domain";
|
import { Result, RuleValidator } from "../../domain";
|
||||||
|
|
||||||
export interface IQuantity_Request_DTO {
|
export interface IQuantity_DTO {
|
||||||
amount: number;
|
amount: number | null;
|
||||||
scale: number;
|
scale: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,4 +21,5 @@ export function ensureQuantity_DTOIsValid(quantity: IQuantity_Request_DTO) {
|
|||||||
return Result.ok(true);
|
return Result.ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IQuantity_Response_DTO extends IQuantity_Request_DTO {}
|
export interface IQuantity_Request_DTO extends IQuantity_DTO {}
|
||||||
|
export interface IQuantity_Response_DTO extends IQuantity_DTO {}
|
||||||
|
|||||||
@ -1,29 +1,23 @@
|
|||||||
import Joi from "joi";
|
import Joi from "joi";
|
||||||
import { UndefinedOr } from "../../../../utilities";
|
import { UndefinedOr } from "../../../../utilities";
|
||||||
|
import { DomainError, handleDomainError } from "../errors";
|
||||||
import { RuleValidator } from "../RuleValidator";
|
import { RuleValidator } from "../RuleValidator";
|
||||||
import { Result } from "./Result";
|
import { Result } from "./Result";
|
||||||
import {
|
import { IStringValueObjectOptions, StringValueObject } from "./StringValueObject";
|
||||||
IStringValueObjectOptions,
|
|
||||||
StringValueObject,
|
|
||||||
} from "./StringValueObject";
|
|
||||||
|
|
||||||
export class Description extends StringValueObject {
|
export class Description extends StringValueObject {
|
||||||
protected static validate(
|
protected static validate(value: UndefinedOr<string>, options: IStringValueObjectOptions) {
|
||||||
value: UndefinedOr<string>,
|
|
||||||
options: IStringValueObjectOptions
|
|
||||||
) {
|
|
||||||
const ruleIsEmpty = Joi.string()
|
const ruleIsEmpty = Joi.string()
|
||||||
.optional()
|
.optional()
|
||||||
|
.allow(null)
|
||||||
|
.allow("")
|
||||||
.default("")
|
.default("")
|
||||||
.label(String(options.label));
|
.label(String(options.label));
|
||||||
|
|
||||||
return RuleValidator.validate<string>(ruleIsEmpty, value);
|
return RuleValidator.validate<string>(ruleIsEmpty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static create(
|
public static create(value: UndefinedOr<string>, options: IStringValueObjectOptions = {}) {
|
||||||
value: UndefinedOr<string>,
|
|
||||||
options: IStringValueObjectOptions = {}
|
|
||||||
) {
|
|
||||||
const _options = {
|
const _options = {
|
||||||
label: "description",
|
label: "description",
|
||||||
...options,
|
...options,
|
||||||
@ -32,7 +26,9 @@ export class Description extends StringValueObject {
|
|||||||
const validationResult = Description.validate(value, _options);
|
const validationResult = Description.validate(value, _options);
|
||||||
|
|
||||||
if (validationResult.isFailure) {
|
if (validationResult.isFailure) {
|
||||||
return Result.fail(validationResult.error);
|
return Result.fail(
|
||||||
|
handleDomainError(DomainError.INVALID_INPUT_DATA, validationResult.error.message, _options)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.ok(new Description(validationResult.object));
|
return Result.ok(new Description(validationResult.object));
|
||||||
|
|||||||
@ -1,27 +1,17 @@
|
|||||||
import { UndefinedOr } from "../../../../utilities";
|
import { UndefinedOr } from "../../../../utilities";
|
||||||
|
import { DomainError, handleDomainError } from "../errors";
|
||||||
import { RuleValidator } from "../RuleValidator";
|
import { RuleValidator } from "../RuleValidator";
|
||||||
import { Result } from "./Result";
|
import { Result } from "./Result";
|
||||||
import {
|
import { IStringValueObjectOptions, StringValueObject } from "./StringValueObject";
|
||||||
IStringValueObjectOptions,
|
|
||||||
StringValueObject,
|
|
||||||
} from "./StringValueObject";
|
|
||||||
|
|
||||||
export class Measure extends StringValueObject {
|
export class Measure extends StringValueObject {
|
||||||
protected static validate(
|
protected static validate(value: UndefinedOr<string>, options: IStringValueObjectOptions) {
|
||||||
value: UndefinedOr<string>,
|
const ruleIsEmpty = RuleValidator.RULE_ALLOW_EMPTY.default("").label(String(options.label));
|
||||||
options: IStringValueObjectOptions,
|
|
||||||
) {
|
|
||||||
const ruleIsEmpty = RuleValidator.RULE_ALLOW_EMPTY.default("").label(
|
|
||||||
String(options.label),
|
|
||||||
);
|
|
||||||
|
|
||||||
return RuleValidator.validate<string>(ruleIsEmpty, value);
|
return RuleValidator.validate<string>(ruleIsEmpty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static create(
|
public static create(value: UndefinedOr<string>, options: IStringValueObjectOptions = {}) {
|
||||||
value: UndefinedOr<string>,
|
|
||||||
options: IStringValueObjectOptions = {},
|
|
||||||
) {
|
|
||||||
const _options = {
|
const _options = {
|
||||||
label: "description",
|
label: "description",
|
||||||
...options,
|
...options,
|
||||||
@ -30,7 +20,9 @@ export class Measure extends StringValueObject {
|
|||||||
const validationResult = Measure.validate(value, _options);
|
const validationResult = Measure.validate(value, _options);
|
||||||
|
|
||||||
if (validationResult.isFailure) {
|
if (validationResult.isFailure) {
|
||||||
return Result.fail(validationResult.error);
|
return Result.fail(
|
||||||
|
handleDomainError(DomainError.INVALID_INPUT_DATA, validationResult.error.message, _options)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.ok(new Measure(validationResult.object));
|
return Result.ok(new Measure(validationResult.object));
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import DineroFactory, { Currency, Dinero } from "dinero.js";
|
|||||||
import Joi from "joi";
|
import Joi from "joi";
|
||||||
import { isNull } from "lodash";
|
import { isNull } from "lodash";
|
||||||
import { NullOr, UndefinedOr } from "../../../../utilities";
|
import { NullOr, UndefinedOr } from "../../../../utilities";
|
||||||
|
import { DomainError, handleDomainError } from "../errors";
|
||||||
import { RuleValidator } from "../RuleValidator";
|
import { RuleValidator } from "../RuleValidator";
|
||||||
import { CurrencyData } from "./CurrencyData";
|
import { CurrencyData } from "./CurrencyData";
|
||||||
import { Result } from "./Result";
|
import { Result } from "./Result";
|
||||||
@ -144,7 +145,9 @@ export class MoneyValue extends ValueObject<Dinero> implements IMoneyValue {
|
|||||||
const validationResult = MoneyValue.validate(amount, options);
|
const validationResult = MoneyValue.validate(amount, options);
|
||||||
|
|
||||||
if (validationResult.isFailure) {
|
if (validationResult.isFailure) {
|
||||||
return Result.fail(validationResult.error);
|
return Result.fail(
|
||||||
|
handleDomainError(DomainError.INVALID_INPUT_DATA, validationResult.error.message, options)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const _amount: NullOr<number> = MoneyValue.sanitize(validationResult.object);
|
const _amount: NullOr<number> = MoneyValue.sanitize(validationResult.object);
|
||||||
|
|||||||
@ -218,7 +218,7 @@ export class Percentage extends NullableValueObject<IPercentage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get scale(): number {
|
get scale(): number {
|
||||||
return this.isNull() ? 0 : Number(this.props?.scale);
|
return Number(this.props?.scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getAmount(): NullOr<number> {
|
public getAmount(): NullOr<number> {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import Joi from "joi";
|
import Joi from "joi";
|
||||||
import { NullOr } from "../../../../utilities";
|
import { NullOr } from "../../../../utilities";
|
||||||
|
import { DomainError, handleDomainError } from "../errors";
|
||||||
import { RuleValidator } from "../RuleValidator";
|
import { RuleValidator } from "../RuleValidator";
|
||||||
import { INullableValueObjectOptions, NullableValueObject } from "./NullableValueObject";
|
import { INullableValueObjectOptions, NullableValueObject } from "./NullableValueObject";
|
||||||
import { Result } from "./Result";
|
import { Result } from "./Result";
|
||||||
@ -98,7 +99,9 @@ export class Quantity extends NullableValueObject<IQuantity> {
|
|||||||
const validationResult = Quantity.validate(amount, scale, _options);
|
const validationResult = Quantity.validate(amount, scale, _options);
|
||||||
|
|
||||||
if (validationResult.isFailure) {
|
if (validationResult.isFailure) {
|
||||||
return Result.fail(validationResult.error);
|
return Result.fail(
|
||||||
|
handleDomainError(DomainError.INVALID_INPUT_DATA, validationResult.error.message, _options)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let _amount: NullOr<number> = Quantity._sanitize(amount);
|
let _amount: NullOr<number> = Quantity._sanitize(amount);
|
||||||
|
|||||||
@ -22,14 +22,8 @@ export class FieldCriteria extends StringValueObject implements IFieldCriteria {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected static validate(value: UndefinedOr<string>) {
|
protected static validate(value: UndefinedOr<string>) {
|
||||||
if (
|
if (RuleValidator.validate(RuleValidator.RULE_NOT_NULL_OR_UNDEFINED, value).isSuccess) {
|
||||||
RuleValidator.validate(RuleValidator.RULE_NOT_NULL_OR_UNDEFINED, value)
|
const stringOrError = RuleValidator.validate(RuleValidator.RULE_IS_TYPE_STRING, value);
|
||||||
.isSuccess
|
|
||||||
) {
|
|
||||||
const stringOrError = RuleValidator.validate(
|
|
||||||
RuleValidator.RULE_IS_TYPE_STRING,
|
|
||||||
value
|
|
||||||
);
|
|
||||||
|
|
||||||
if (stringOrError.isFailure) {
|
if (stringOrError.isFailure) {
|
||||||
return stringOrError;
|
return stringOrError;
|
||||||
|
|||||||
@ -1,21 +1,16 @@
|
|||||||
import Joi from "joi";
|
import Joi from "joi";
|
||||||
import { UndefinedOr } from "../../../../utilities";
|
import { UndefinedOr } from "../../../../utilities";
|
||||||
|
|
||||||
|
import { DomainError, handleDomainError } from "../errors";
|
||||||
import { RuleValidator } from "../RuleValidator";
|
import { RuleValidator } from "../RuleValidator";
|
||||||
import { Result } from "./Result";
|
import { Result } from "./Result";
|
||||||
import {
|
import { IStringValueObjectOptions, StringValueObject } from "./StringValueObject";
|
||||||
IStringValueObjectOptions,
|
|
||||||
StringValueObject,
|
|
||||||
} from "./StringValueObject";
|
|
||||||
|
|
||||||
export class Slug extends StringValueObject {
|
export class Slug extends StringValueObject {
|
||||||
protected static readonly MIN_LENGTH = 2;
|
protected static readonly MIN_LENGTH = 2;
|
||||||
protected static readonly MAX_LENGTH = 100;
|
protected static readonly MAX_LENGTH = 100;
|
||||||
|
|
||||||
protected static validate(
|
protected static validate(value: UndefinedOr<string>, options: IStringValueObjectOptions) {
|
||||||
value: UndefinedOr<string>,
|
|
||||||
options: IStringValueObjectOptions
|
|
||||||
) {
|
|
||||||
const rule = Joi.string()
|
const rule = Joi.string()
|
||||||
.allow(null)
|
.allow(null)
|
||||||
.allow("")
|
.allow("")
|
||||||
@ -66,10 +61,7 @@ export class Slug extends StringValueObject {
|
|||||||
return slug ? slug.trim() : "";
|
return slug ? slug.trim() : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static create(
|
public static create(value: UndefinedOr<string>, options: IStringValueObjectOptions = {}) {
|
||||||
value: UndefinedOr<string>,
|
|
||||||
options: IStringValueObjectOptions = {}
|
|
||||||
) {
|
|
||||||
const _options = {
|
const _options = {
|
||||||
label: "slug",
|
label: "slug",
|
||||||
...options,
|
...options,
|
||||||
@ -78,7 +70,9 @@ export class Slug extends StringValueObject {
|
|||||||
const validationResult = Slug.validate(value, _options);
|
const validationResult = Slug.validate(value, _options);
|
||||||
|
|
||||||
if (validationResult.isFailure) {
|
if (validationResult.isFailure) {
|
||||||
return Result.fail(validationResult.error);
|
return Result.fail(
|
||||||
|
handleDomainError(DomainError.INVALID_INPUT_DATA, validationResult.error.message, _options)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const slugValue = Slug.sanitize(validationResult.object);
|
const slugValue = Slug.sanitize(validationResult.object);
|
||||||
|
|||||||
@ -1,8 +1,4 @@
|
|||||||
import {
|
import { IMoney_DTO, IPercentage_DTO, IQuantity_DTO } from "../../../../../common";
|
||||||
IMoney_Response_DTO,
|
|
||||||
IPercentage_Response_DTO,
|
|
||||||
IQuantity_Response_DTO,
|
|
||||||
} from "../../../../../common";
|
|
||||||
|
|
||||||
export interface IGetQuote_Response_DTO {
|
export interface IGetQuote_Response_DTO {
|
||||||
id: string;
|
id: string;
|
||||||
@ -17,9 +13,9 @@ export interface IGetQuote_Response_DTO {
|
|||||||
notes: string;
|
notes: string;
|
||||||
validity: string;
|
validity: string;
|
||||||
|
|
||||||
subtotal_price: IMoney_Response_DTO;
|
subtotal_price: IMoney_DTO;
|
||||||
discount: IPercentage_Response_DTO;
|
discount: IPercentage_DTO;
|
||||||
total_price: IMoney_Response_DTO;
|
total_price: IMoney_DTO;
|
||||||
|
|
||||||
items: IGetQuote_QuoteItem_Response_DTO[];
|
items: IGetQuote_QuoteItem_Response_DTO[];
|
||||||
|
|
||||||
@ -28,10 +24,10 @@ export interface IGetQuote_Response_DTO {
|
|||||||
|
|
||||||
export interface IGetQuote_QuoteItem_Response_DTO {
|
export interface IGetQuote_QuoteItem_Response_DTO {
|
||||||
article_id: string;
|
article_id: string;
|
||||||
quantity: IQuantity_Response_DTO;
|
quantity: IQuantity_DTO;
|
||||||
description: string;
|
description: string;
|
||||||
unit_price: IMoney_Response_DTO;
|
unit_price: IMoney_DTO;
|
||||||
subtotal_price: IMoney_Response_DTO;
|
subtotal_price: IMoney_DTO;
|
||||||
discount: IPercentage_Response_DTO;
|
discount: IPercentage_DTO;
|
||||||
total_price: IMoney_Response_DTO;
|
total_price: IMoney_DTO;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,49 +50,49 @@ export function ensureUpdateQuote_Request_DTOIsValid(quoteDTO: IUpdateQuote_Requ
|
|||||||
validity: Joi.string().optional().allow(null).allow("").default(""),
|
validity: Joi.string().optional().allow(null).allow("").default(""),
|
||||||
|
|
||||||
subtotal_price: Joi.object({
|
subtotal_price: Joi.object({
|
||||||
amount: Joi.number(),
|
amount: Joi.number().allow(null),
|
||||||
scale: Joi.number(),
|
scale: Joi.number(),
|
||||||
currency_code: Joi.string(),
|
currency_code: Joi.string(),
|
||||||
}).unknown(),
|
}).optional(),
|
||||||
|
|
||||||
discount: Joi.object({
|
discount: Joi.object({
|
||||||
amount: Joi.number(),
|
amount: Joi.number().allow(null),
|
||||||
scale: Joi.number(),
|
scale: Joi.number(),
|
||||||
}).unknown(),
|
}).optional(),
|
||||||
|
|
||||||
total_price: Joi.object({
|
total_price: Joi.object({
|
||||||
amount: Joi.number(),
|
amount: Joi.number().allow(null),
|
||||||
scale: Joi.number(),
|
scale: Joi.number(),
|
||||||
currency_code: Joi.string(),
|
currency_code: Joi.string(),
|
||||||
}).unknown(),
|
}).optional(),
|
||||||
|
|
||||||
items: Joi.array().items(
|
items: Joi.array().items(
|
||||||
Joi.object({
|
Joi.object({
|
||||||
article_id: Joi.string().optional().allow(null).allow("").default(""),
|
article_id: Joi.number().optional().allow(null).allow("").default(""),
|
||||||
quantity: Joi.object({
|
quantity: Joi.object({
|
||||||
amount: Joi.number(),
|
amount: Joi.number().allow(null),
|
||||||
scale: Joi.number(),
|
scale: Joi.number(),
|
||||||
}).unknown(),
|
}).optional(),
|
||||||
description: Joi.string(),
|
description: Joi.string().optional().allow(null).allow("").default(""),
|
||||||
unit_price: Joi.object({
|
unit_price: Joi.object({
|
||||||
amount: Joi.number(),
|
amount: Joi.number().allow(null),
|
||||||
scale: Joi.number(),
|
scale: Joi.number(),
|
||||||
currency_code: Joi.string(),
|
currency_code: Joi.string(),
|
||||||
}).unknown(),
|
}).optional(),
|
||||||
subtotal_price: Joi.object({
|
subtotal_price: Joi.object({
|
||||||
amount: Joi.number(),
|
amount: Joi.number().allow(null),
|
||||||
scale: Joi.number(),
|
scale: Joi.number(),
|
||||||
currency_code: Joi.string(),
|
currency_code: Joi.string(),
|
||||||
}).unknown(),
|
}).optional(),
|
||||||
discount: Joi.object({
|
discount: Joi.object({
|
||||||
amount: Joi.number(),
|
amount: Joi.number().allow(null),
|
||||||
scale: Joi.number(),
|
scale: Joi.number(),
|
||||||
}).unknown(),
|
}).optional(),
|
||||||
total_price: Joi.object({
|
total_price: Joi.object({
|
||||||
amount: Joi.number(),
|
amount: Joi.number().allow(null),
|
||||||
scale: Joi.number(),
|
scale: Joi.number(),
|
||||||
currency_code: Joi.string(),
|
currency_code: Joi.string(),
|
||||||
}).unknown(),
|
}).optional(),
|
||||||
}).unknown(true)
|
}).unknown(true)
|
||||||
),
|
),
|
||||||
}).unknown(true);
|
}).unknown(true);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user