Añadir el IVA como un campo más en ajustes
This commit is contained in:
parent
990e0850f2
commit
0ff5c39023
@ -1,27 +1,37 @@
|
||||
import { FormPercentageField } from "@/components";
|
||||
import { useLocalization } from "@/lib/hooks";
|
||||
import { Card, CardContent, CardDescription, CardTitle, Separator } from "@/ui";
|
||||
import { CurrencyData } from "@shared/contexts";
|
||||
import { t } from "i18next";
|
||||
import { useMemo } from "react";
|
||||
import { useFormContext } from "react-hook-form";
|
||||
|
||||
export const QuotePricesResume = () => {
|
||||
const { watch, register, formState } = useFormContext();
|
||||
const { formatNumber } = useLocalization();
|
||||
|
||||
const currency_code = watch("currency_code");
|
||||
const subtotal_price = formatNumber(watch("subtotal_price"));
|
||||
const discount_price = formatNumber(watch("discount_price"));
|
||||
const tax_price = formatNumber(watch("tax_price"));
|
||||
const total_price = formatNumber(watch("total_price"));
|
||||
|
||||
const currency_symbol = useMemo(() => {
|
||||
const currencyOrError = CurrencyData.createFromCode(currency_code);
|
||||
return currencyOrError.isSuccess ? currencyOrError.object.symbol : "";
|
||||
}, [currency_code]);
|
||||
|
||||
return (
|
||||
<Card className='w-full'>
|
||||
<CardContent className='flex flex-row items-end gap-2 p-4 border-t'>
|
||||
<div className='grid flex-1 h-16 grid-cols-1 auto-rows-max'>
|
||||
<div className='grid gap-1 font-semibold text-muted-foreground'>
|
||||
<CardDescription className='text-sm'>Importe neto</CardDescription>
|
||||
<CardDescription className='text-sm'>
|
||||
{t("quotes.form_fields.subtotal_price.label")}
|
||||
</CardDescription>
|
||||
<CardTitle className='flex items-baseline text-2xl tabular-nums'>
|
||||
{subtotal_price}
|
||||
<span className='ml-1 text-lg tracking-normal'>€</span>
|
||||
<span className='ml-1 text-lg tracking-normal'>{currency_symbol}</span>
|
||||
</CardTitle>
|
||||
</div>
|
||||
</div>
|
||||
@ -41,10 +51,12 @@ export const QuotePricesResume = () => {
|
||||
/>
|
||||
</div>
|
||||
<div className='grid gap-1 font-semibold text-muted-foreground'>
|
||||
<CardDescription className='text-sm'>Imp. descuento</CardDescription>
|
||||
<CardDescription className='text-sm'>
|
||||
{t("quotes.form_fields.discount_price.label")}
|
||||
</CardDescription>
|
||||
<CardTitle className='flex items-baseline text-2xl tabular-nums'>
|
||||
{discount_price}
|
||||
<span className='ml-1 text-lg tracking-normal'>€</span>
|
||||
<span className='ml-1 text-lg tracking-normal'>{currency_symbol}</span>
|
||||
</CardTitle>
|
||||
</div>
|
||||
</div>
|
||||
@ -65,20 +77,24 @@ export const QuotePricesResume = () => {
|
||||
/>
|
||||
</div>
|
||||
<div className='grid gap-1 font-semibold text-muted-foreground'>
|
||||
<CardDescription className='text-sm'>Importe IVA</CardDescription>
|
||||
<CardDescription className='text-sm'>
|
||||
{t("quotes.form_fields.tax_price.label")}
|
||||
</CardDescription>
|
||||
<CardTitle className='flex items-baseline gap-1 text-2xl tabular-nums'>
|
||||
{tax_price}
|
||||
<span className='text-base font-medium tracking-normal'>€</span>
|
||||
<span className='text-base font-medium tracking-normal'>{currency_symbol}</span>
|
||||
</CardTitle>
|
||||
</div>
|
||||
</div>{" "}
|
||||
<Separator orientation='vertical' className='w-px h-16 mx-2' />
|
||||
<div className='grid flex-1 h-16 grid-cols-1 auto-rows-max'>
|
||||
<div className='grid gap-0'>
|
||||
<CardDescription className='text-sm font-semibold'>Importe total</CardDescription>
|
||||
<CardDescription className='text-sm font-semibold'>
|
||||
{t("quotes.form_fields.total_price.label")}
|
||||
</CardDescription>
|
||||
<CardTitle className='flex items-baseline gap-1 text-3xl tabular-nums'>
|
||||
{total_price}
|
||||
<span className='ml-1 text-lg tracking-normal'>€</span>
|
||||
<span className='ml-1 text-lg tracking-normal'>{currency_symbol}</span>
|
||||
</CardTitle>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -15,7 +15,6 @@ import { useQuotes } from "../hooks";
|
||||
export const QuotesDataTable = ({ status = "all" }: { status?: string }) => {
|
||||
const navigate = useNavigate();
|
||||
const { pagination, globalFilter, isFiltered } = useDataTableContext();
|
||||
|
||||
const { useList } = useQuotes();
|
||||
|
||||
const { data, isPending, isError, error } = useList({
|
||||
@ -48,7 +47,12 @@ export const QuotesDataTable = ({ status = "all" }: { status?: string }) => {
|
||||
<div className='text-ellipsis'>
|
||||
{original.customer_information.split("\n").map((item, index) => {
|
||||
return (
|
||||
<span key={index} className={index === 0 ? "font-semibold" : "font-medium"}>
|
||||
<span
|
||||
key={index}
|
||||
className={
|
||||
index === 0 ? "font-medium" : "hidden text-sm text-muted-foreground md:inline"
|
||||
}
|
||||
>
|
||||
{item}
|
||||
<br />
|
||||
</span>
|
||||
@ -78,7 +82,7 @@ export const QuotesDataTable = ({ status = "all" }: { status?: string }) => {
|
||||
id: "total_price" as const,
|
||||
accessor: "total_price",
|
||||
header: () => <div className='text-right'>{t("quotes.list.columns.total_price")}</div>,
|
||||
cell: ({ table, row: { index, original }, column, getValue }) => {
|
||||
cell: ({ row: { original } }) => {
|
||||
const price = MoneyValue.create(original.total_price);
|
||||
return (
|
||||
<div className='text-right'>{price.isSuccess ? price.object.toFormat() : "-"}</div>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ErrorOverlay, FormTextAreaField, LoadingOverlay } from "@/components";
|
||||
import { ErrorOverlay, FormPercentageField, FormTextAreaField, LoadingOverlay } from "@/components";
|
||||
import {
|
||||
Alert,
|
||||
AlertDescription,
|
||||
@ -12,6 +12,7 @@ import {
|
||||
CardTitle,
|
||||
Form,
|
||||
} from "@/ui";
|
||||
import { joiResolver } from "@hookform/resolvers/joi";
|
||||
|
||||
import { t } from "i18next";
|
||||
import { AlertCircleIcon } from "lucide-react";
|
||||
@ -22,13 +23,14 @@ import { Trans } from "react-i18next";
|
||||
import { useUnsavedChangesNotifier } from "@/lib/hooks";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { IUpdateProfile_Request_DTO } from "@shared/contexts";
|
||||
import Joi from "joi";
|
||||
import { toast } from "react-toastify";
|
||||
import { useSettings } from "./hooks";
|
||||
|
||||
type SettingsDataForm = IUpdateProfile_Request_DTO;
|
||||
|
||||
export const SettingsEditor = () => {
|
||||
const [activeSection, setActiveSection] = useState("profile");
|
||||
const [activeSection, setActiveSection] = useState("quotes");
|
||||
const { useOne, useUpdate } = useSettings();
|
||||
|
||||
const { data, status, error: queryError } = useOne();
|
||||
@ -40,6 +42,10 @@ export const SettingsEditor = () => {
|
||||
default_notes: "",
|
||||
default_legal_terms: "",
|
||||
default_quote_validity: "",
|
||||
default_tax: {
|
||||
amount: undefined,
|
||||
scale: 2,
|
||||
},
|
||||
}),
|
||||
[]
|
||||
);
|
||||
@ -50,18 +56,19 @@ export const SettingsEditor = () => {
|
||||
mode: "onBlur",
|
||||
values: data?.dealer,
|
||||
defaultValues,
|
||||
//defaultValues: _defaultValues,
|
||||
/*resolver: joiResolver(
|
||||
resolver: joiResolver(
|
||||
Joi.object({
|
||||
email: Joi.string()
|
||||
.email({ tlds: { allow: false } })
|
||||
.required(),
|
||||
password: Joi.string().min(4).alphanum().required(),
|
||||
}),
|
||||
{
|
||||
messages: SpanishJoiMessages,
|
||||
}
|
||||
),*/
|
||||
contact_information: Joi.string().optional().allow(null).allow("").default(""),
|
||||
default_payment_method: Joi.string().optional().allow(null).allow("").default(""),
|
||||
default_notes: Joi.string().optional().allow(null).allow("").default(""),
|
||||
default_legal_terms: Joi.string().optional().allow(null).allow("").default(""),
|
||||
default_quote_validity: Joi.string().optional().allow(null).allow("").default(""),
|
||||
default_tax: Joi.object({
|
||||
amount: Joi.number().allow(null),
|
||||
scale: Joi.number(),
|
||||
}).required(),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
const { formState, reset, getValues, handleSubmit } = form;
|
||||
@ -159,10 +166,8 @@ export const SettingsEditor = () => {
|
||||
//autoSize
|
||||
rows={8}
|
||||
placeholder={t("settings.form_fields.contact_information.placeholder")}
|
||||
{...form.register("contact_information", {
|
||||
required: true,
|
||||
})}
|
||||
errors={form.formState.errors}
|
||||
name='contact_information'
|
||||
required
|
||||
/>
|
||||
</CardContent>
|
||||
<CardFooter className='px-6 py-4 border-t'>
|
||||
@ -173,6 +178,31 @@ export const SettingsEditor = () => {
|
||||
</Card>
|
||||
</div>
|
||||
<div className={cn("grid gap-6", activeSection === "quotes" ? "visible" : "hidden")}>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>
|
||||
<Trans i18nKey='settings.form_fields.default_tax.label' />
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
<Trans i18nKey='settings.form_fields.default_tax.desc' />
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<FormPercentageField
|
||||
scale={2}
|
||||
disabled={formState.disabled}
|
||||
placeholder={t("settings.form_fields.default_tax.desc")}
|
||||
name='default_tax'
|
||||
required
|
||||
/>
|
||||
</CardContent>
|
||||
<CardFooter className='px-6 py-4 border-t'>
|
||||
<Button>
|
||||
<Trans i18nKey='common.save' />
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>
|
||||
@ -186,9 +216,8 @@ export const SettingsEditor = () => {
|
||||
<FormTextAreaField
|
||||
autoSize
|
||||
placeholder={t("settings.form_fields.default_payment_method.placeholder")}
|
||||
{...form.register("default_payment_method", {
|
||||
required: true,
|
||||
})}
|
||||
name='default_payment_method'
|
||||
required
|
||||
errors={form.formState.errors}
|
||||
/>
|
||||
</CardContent>
|
||||
@ -211,10 +240,8 @@ export const SettingsEditor = () => {
|
||||
<FormTextAreaField
|
||||
autoSize
|
||||
placeholder={t("settings.form_fields.default_quote_validity.placeholder")}
|
||||
{...form.register("default_quote_validity", {
|
||||
required: true,
|
||||
})}
|
||||
errors={form.formState.errors}
|
||||
name='default_quote_validity'
|
||||
required
|
||||
/>
|
||||
</CardContent>
|
||||
<CardFooter className='px-6 py-4 border-t'>
|
||||
@ -237,10 +264,8 @@ export const SettingsEditor = () => {
|
||||
<FormTextAreaField
|
||||
autoSize
|
||||
placeholder={t("settings.form_fields.default_notes.placeholder")}
|
||||
{...form.register("default_notes", {
|
||||
required: true,
|
||||
})}
|
||||
errors={form.formState.errors}
|
||||
name='default_notes'
|
||||
required
|
||||
/>
|
||||
</CardContent>
|
||||
<CardFooter className='px-6 py-4 border-t'>
|
||||
@ -265,10 +290,8 @@ export const SettingsEditor = () => {
|
||||
//autoSize
|
||||
rows={25}
|
||||
placeholder={t("settings.form_fields.default_legal_terms.placeholder")}
|
||||
{...form.register("default_legal_terms", {
|
||||
required: true,
|
||||
})}
|
||||
errors={form.formState.errors}
|
||||
name='default_legal_terms'
|
||||
required
|
||||
/>
|
||||
</CardContent>
|
||||
<CardFooter className='px-6 py-4 border-t'>
|
||||
|
||||
@ -106,7 +106,7 @@ export function DataTable<TData>({
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
className={rowClassName}
|
||||
className={cn(row.getIsSelected() ? "bg-accent" : "", rowClassName)}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id} className={cellClassName}>
|
||||
|
||||
@ -26,12 +26,7 @@ export function DataTableColumnHeader<TData, TValue>({
|
||||
if (!header.column.getCanSort()) {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={cn(
|
||||
"data-[state=open]:bg-accent font-bold text-muted-foreground uppercase text-xs tracking-wide text-ellipsis",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className={cn("data-[state=open]:bg-accent tracking-wide text-ellipsis", className)}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(header.column.columnDef.header, header.getContext())}
|
||||
|
||||
@ -194,21 +194,41 @@
|
||||
"placeholder": "",
|
||||
"desc": "Quote's validity time"
|
||||
},
|
||||
"discount": {
|
||||
"label": "Discount",
|
||||
"placeholder": "%",
|
||||
"desc": "Percentage discount"
|
||||
},
|
||||
"tax": {
|
||||
"label": "Tax",
|
||||
"placeholder": "%",
|
||||
"desc": "Percentage Tax"
|
||||
},
|
||||
"subtotal_price": {
|
||||
"label": "Subtotal",
|
||||
"placeholder": "",
|
||||
"desc": "Quote subtotal"
|
||||
},
|
||||
"discount": {
|
||||
"label": "Discount (%)",
|
||||
"placeholder": "",
|
||||
"desc": "Percentage discount"
|
||||
},
|
||||
"discount_price": {
|
||||
"label": "Discount price",
|
||||
"placeholder": "",
|
||||
"desc": "Percentage discount price"
|
||||
},
|
||||
"before_tax_price": {
|
||||
"label": "Before tax price",
|
||||
"placeholder": "",
|
||||
"desc": "Before tax price"
|
||||
},
|
||||
"tax": {
|
||||
"label": "Tax (%)",
|
||||
"placeholder": "",
|
||||
"desc": "Percentage Tax"
|
||||
},
|
||||
"tax_price": {
|
||||
"label": "Tax price",
|
||||
"placeholder": "",
|
||||
"desc": "Percentage tax price"
|
||||
},
|
||||
"total_price": {
|
||||
"label": "Total price",
|
||||
"placeholder": "",
|
||||
"desc": "Quote total price"
|
||||
},
|
||||
"items": {
|
||||
"quantity": {
|
||||
"label": "Quantity",
|
||||
@ -256,14 +276,19 @@
|
||||
"form_fields": {
|
||||
"image": {
|
||||
"label": "Logotype",
|
||||
"placeholder": "placeholder",
|
||||
"placeholder": "",
|
||||
"desc": "Información de contacto"
|
||||
},
|
||||
"contact_information": {
|
||||
"label": "Your contact information",
|
||||
"placeholder": "placeholder",
|
||||
"placeholder": "",
|
||||
"desc": "Your contact information as a dealer that will appear on the quotes given to your customers."
|
||||
},
|
||||
"default_tax": {
|
||||
"label": "Default tax (%)",
|
||||
"placeholder": "",
|
||||
"desc": "Default tax rate for your quotes"
|
||||
},
|
||||
"default_legal_terms": {
|
||||
"label": "Legal terms",
|
||||
"placeholder": "",
|
||||
@ -271,7 +296,7 @@
|
||||
},
|
||||
"default_payment_method": {
|
||||
"label": "Payment method",
|
||||
"placeholder": "placeholder",
|
||||
"placeholder": "",
|
||||
"desc": "Default payment method to be used for new quotes"
|
||||
},
|
||||
"default_notes": {
|
||||
|
||||
@ -194,21 +194,41 @@
|
||||
"placeholder": "",
|
||||
"desc": "desc"
|
||||
},
|
||||
"discount": {
|
||||
"label": "Descuento",
|
||||
"placeholder": "%",
|
||||
"desc": "Porcentaje de descuento"
|
||||
},
|
||||
"tax": {
|
||||
"label": "IVA",
|
||||
"placeholder": "%",
|
||||
"desc": "Porcentaje de IVA"
|
||||
},
|
||||
"subtotal_price": {
|
||||
"label": "Importe neto",
|
||||
"placeholder": "",
|
||||
"desc": ""
|
||||
},
|
||||
"discount": {
|
||||
"label": "Descuento (%)",
|
||||
"placeholder": "",
|
||||
"desc": "Porcentaje de descuento"
|
||||
},
|
||||
"discount_price": {
|
||||
"label": "Imp. descuento",
|
||||
"placeholder": "",
|
||||
"desc": "Importe del descuento"
|
||||
},
|
||||
"before_tax_price": {
|
||||
"label": "Base imponible",
|
||||
"placeholder": "",
|
||||
"desc": ""
|
||||
},
|
||||
"tax": {
|
||||
"label": "IVA (%)",
|
||||
"placeholder": "",
|
||||
"desc": "Porcentaje de IVA"
|
||||
},
|
||||
"tax_price": {
|
||||
"label": "Imp. descuento",
|
||||
"placeholder": "",
|
||||
"desc": "Importe del descuento"
|
||||
},
|
||||
"total_price": {
|
||||
"label": "Total price",
|
||||
"placeholder": "",
|
||||
"desc": "Quote total price"
|
||||
},
|
||||
"items": {
|
||||
"quantity": {
|
||||
"label": "Cantidad",
|
||||
@ -244,37 +264,39 @@
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"title": "Ajustes",
|
||||
"profile": {
|
||||
"title": "Ajustes de perfil",
|
||||
"items": {
|
||||
"image": {
|
||||
"label": "Información de contacto",
|
||||
"placeholder": "placeholder",
|
||||
"desc": "Información de contacto"
|
||||
},
|
||||
"contact_information": {
|
||||
"label": "Información de contacto",
|
||||
"placeholder": "placeholder",
|
||||
"desc": "Información de contacto"
|
||||
}
|
||||
"edit": {
|
||||
"title": "Ajustes",
|
||||
"subtitle": "",
|
||||
"tabs": {
|
||||
"profile": "Ajustes de perfil",
|
||||
"quotes": "Ajustes legales",
|
||||
"legal": "Ajustes para cotizaciones"
|
||||
}
|
||||
},
|
||||
"legal": {
|
||||
"title": "Ajustes legales",
|
||||
"items": {
|
||||
"default_legal_terms": {
|
||||
"label": "Cláusulas legales",
|
||||
"placeholder": "",
|
||||
"desc": "desc"
|
||||
}
|
||||
}
|
||||
},
|
||||
"quotes": {
|
||||
"title": "Ajustes para cotizaciones",
|
||||
"form_fields": {
|
||||
"image": {
|
||||
"label": "Información de contacto",
|
||||
"placeholder": "",
|
||||
"desc": "Información de contacto"
|
||||
},
|
||||
"contact_information": {
|
||||
"label": "Información de contacto",
|
||||
"placeholder": "",
|
||||
"desc": "Información de contacto"
|
||||
},
|
||||
"default_tax": {
|
||||
"label": "IVA por defecto (%)",
|
||||
"placeholder": "",
|
||||
"desc": "Porcentaje de IVA por defecto para tus cotizaciones"
|
||||
},
|
||||
"default_legal_terms": {
|
||||
"label": "Cláusulas legales",
|
||||
"placeholder": "",
|
||||
"desc": "desc"
|
||||
},
|
||||
"default_payment_method": {
|
||||
"label": "Forma de pago",
|
||||
"placeholder": "placeholder",
|
||||
"placeholder": "",
|
||||
"desc": "desc"
|
||||
},
|
||||
"default_notes": {
|
||||
|
||||
@ -7,7 +7,13 @@ import {
|
||||
import { IRepositoryManager } from "@/contexts/common/domain";
|
||||
import { IInfrastructureError } from "@/contexts/common/infrastructure";
|
||||
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
|
||||
import { IUpdateProfile_Request_DTO, Result, TextValueObject, UniqueID } from "@shared/contexts";
|
||||
import {
|
||||
IUpdateProfile_Request_DTO,
|
||||
Percentage,
|
||||
Result,
|
||||
TextValueObject,
|
||||
UniqueID,
|
||||
} from "@shared/contexts";
|
||||
import { IProfileRepository, Profile } from "../domain";
|
||||
|
||||
export interface IUpdateProfileUseCaseRequest extends IUseCaseRequest {
|
||||
@ -33,8 +39,6 @@ export class UpdateProfileUseCase
|
||||
async execute(request: IUpdateProfileUseCaseRequest): Promise<UpdateProfileResponseOrError> {
|
||||
const { userId, profileDTO } = request;
|
||||
|
||||
console.log(request);
|
||||
|
||||
// Comprobar que existe el profile
|
||||
const exitsOrError = await this._getProfileDealer(userId);
|
||||
if (exitsOrError.isFailure) {
|
||||
@ -55,6 +59,13 @@ export class UpdateProfileUseCase
|
||||
profile.defaultNotes = TextValueObject.create(profileDTO.default_notes).object;
|
||||
profile.defaultQuoteValidity = TextValueObject.create(profileDTO.default_quote_validity).object;
|
||||
|
||||
const taxOrError = Percentage.create(profileDTO.default_tax);
|
||||
if (taxOrError.isFailure) {
|
||||
return Result.fail(taxOrError.error);
|
||||
}
|
||||
|
||||
profile.defaultTax = taxOrError.object;
|
||||
|
||||
// Guardar los cambios
|
||||
return this._saveProfile(profile);
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
Language,
|
||||
Name,
|
||||
Note,
|
||||
Percentage,
|
||||
Result,
|
||||
UniqueID,
|
||||
} from "@shared/contexts";
|
||||
@ -21,6 +22,7 @@ export interface IProfileProps {
|
||||
defaultNotes: Note;
|
||||
defaultLegalTerms: Note;
|
||||
defaultQuoteValidity: Note;
|
||||
defaultTax: Percentage;
|
||||
}
|
||||
|
||||
export interface IProfile {
|
||||
@ -34,7 +36,9 @@ export interface IProfile {
|
||||
defaultPaymentMethod: Note;
|
||||
defaultNotes: Note;
|
||||
defaultLegalTerms: Note;
|
||||
|
||||
defaultQuoteValidity: Note;
|
||||
defaultTax: Percentage;
|
||||
}
|
||||
|
||||
export class Profile extends AggregateRoot<IProfileProps> implements IProfile {
|
||||
@ -98,4 +102,12 @@ export class Profile extends AggregateRoot<IProfileProps> implements IProfile {
|
||||
set defaultQuoteValidity(newDefaultQuoteValidity: Note) {
|
||||
this.props.defaultQuoteValidity = newDefaultQuoteValidity;
|
||||
}
|
||||
|
||||
get defaultTax(): Percentage {
|
||||
return this.props.defaultTax;
|
||||
}
|
||||
|
||||
set defaultTax(newDefaultTax: Percentage) {
|
||||
this.props.defaultTax = newDefaultTax;
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,6 +27,7 @@ export const GetProfilePresenter: IGetProfilePresenter = {
|
||||
default_notes: profile.defaultNotes.toString(),
|
||||
default_legal_terms: profile.defaultLegalTerms.toString(),
|
||||
default_quote_validity: profile.defaultQuoteValidity.toString(),
|
||||
default_tax: profile.defaultTax.convertScale(2).toObject(),
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
@ -15,6 +15,7 @@ export const UpdateProfilePresenter: IUpdateProfilePresenter = {
|
||||
default_notes: profile.defaultNotes.toString(),
|
||||
default_legal_terms: profile.defaultLegalTerms.toString(),
|
||||
default_quote_validity: profile.defaultQuoteValidity.toString(),
|
||||
default_tax: profile.defaultTax.convertScale(2).toObject(),
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@ -5,7 +5,14 @@ import {
|
||||
} from "@/contexts/common/infrastructure";
|
||||
import { DealerStatus } from "@/contexts/sales/domain";
|
||||
import { Dealer_Model, DealerCreationAttributes } from "@/contexts/sales/infrastructure/sequelize";
|
||||
import { CurrencyData, Language, Name, TextValueObject, UniqueID } from "@shared/contexts";
|
||||
import {
|
||||
CurrencyData,
|
||||
Language,
|
||||
Name,
|
||||
Percentage,
|
||||
TextValueObject,
|
||||
UniqueID,
|
||||
} from "@shared/contexts";
|
||||
import { IProfileProps, Profile } from "../../domain";
|
||||
import { IProfileContext } from "../Profile.context";
|
||||
|
||||
@ -44,6 +51,13 @@ class ProfileMapper
|
||||
TextValueObject.create
|
||||
);
|
||||
|
||||
const defaultTax = this.mapsValue(source, "default_tax", (tax) =>
|
||||
Percentage.create({
|
||||
amount: tax,
|
||||
scale: 2,
|
||||
})
|
||||
);
|
||||
|
||||
const props: IProfileProps = {
|
||||
name,
|
||||
status,
|
||||
@ -55,6 +69,7 @@ class ProfileMapper
|
||||
defaultNotes,
|
||||
defaultLegalTerms,
|
||||
defaultQuoteValidity,
|
||||
defaultTax,
|
||||
};
|
||||
|
||||
const id = this.mapsValue(source, "id", UniqueID.create);
|
||||
@ -76,6 +91,7 @@ class ProfileMapper
|
||||
default_notes: source.defaultNotes.toPrimitive(),
|
||||
default_legal_terms: source.defaultLegalTerms.toPrimitive(),
|
||||
default_quote_validity: source.defaultQuoteValidity.toPrimitive(),
|
||||
default_tax: source.defaultTax.convertScale(2).toPrimitive(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,6 +41,8 @@ export class ListQuotesUseCase implements IUseCase<IListQuotesParams, Promise<Li
|
||||
);
|
||||
}
|
||||
|
||||
console.log(queryCriteria?.toJSON());
|
||||
|
||||
return this.findQuotes(queryCriteria);
|
||||
}
|
||||
|
||||
|
||||
@ -87,9 +87,17 @@ export class QuoteRepository extends SequelizeRepository<Quote> implements IQuot
|
||||
return this.mapper.mapToDomain(rawQuote);
|
||||
}
|
||||
|
||||
public async findAll(queryCriteria?: IQueryCriteria): Promise<ICollection<any>> {
|
||||
public async findAll(queryCriteria?: IQueryCriteria): Promise<ICollection<Quote>> {
|
||||
const QuoteItem_Model: ModelDefined<any, any> = this._adapter.getModel("QuoteItem_Model");
|
||||
|
||||
const { rows, count } = await this._findAll("Quote_Model", queryCriteria, {
|
||||
include: [], // esto es para quitar las asociaciones al hacer la consulta
|
||||
/*include: [
|
||||
{
|
||||
model: QuoteItem_Model,
|
||||
as: "items",
|
||||
},
|
||||
],*/
|
||||
order: [
|
||||
["date", "DESC"],
|
||||
["customer_information", "ASC"],
|
||||
|
||||
@ -32,6 +32,7 @@ class DealerMapper
|
||||
["default_notes", source.default_notes],
|
||||
["default_legal_terms", source.default_legal_terms],
|
||||
["default_quote_validity", source.default_quote_validity],
|
||||
["default_tax", source.default_tax],
|
||||
]);
|
||||
|
||||
const props: IDealerProps = {
|
||||
@ -69,6 +70,7 @@ class DealerMapper
|
||||
default_notes: source.additionalInfo.get("default_notes")?.toString() ?? "",
|
||||
default_legal_terms: source.additionalInfo.get("default_legal_terms")?.toString() ?? "",
|
||||
default_quote_validity: source.additionalInfo.get("default_quote_validity")?.toString() ?? "",
|
||||
default_tax: source.additionalInfo.get("default_tax")?.toString() ?? "",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,6 +53,7 @@ export class Dealer_Model extends Model<
|
||||
declare default_notes: CreationOptional<string>;
|
||||
declare default_legal_terms: CreationOptional<string>;
|
||||
declare default_quote_validity: CreationOptional<string>;
|
||||
declare default_tax: CreationOptional<number | null>;
|
||||
declare status: CreationOptional<string>;
|
||||
declare lang_code: CreationOptional<string>;
|
||||
declare currency_code: CreationOptional<string>;
|
||||
@ -85,6 +86,12 @@ export default (sequelize: Sequelize) => {
|
||||
default_legal_terms: DataTypes.TEXT,
|
||||
default_quote_validity: DataTypes.TEXT,
|
||||
|
||||
default_tax: {
|
||||
type: new DataTypes.SMALLINT(),
|
||||
allowNull: true,
|
||||
defaultValue: 2100,
|
||||
},
|
||||
|
||||
lang_code: {
|
||||
type: DataTypes.STRING(2),
|
||||
allowNull: false,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
export const INITIAL_PAGE_INDEX = 0;
|
||||
export const INITIAL_PAGE_SIZE = 5;
|
||||
export const INITIAL_PAGE_SIZE = 10;
|
||||
|
||||
export const MIN_PAGE_INDEX = 0;
|
||||
export const MIN_PAGE_SIZE = 1;
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { IPercentage_DTO } from "../../../../common";
|
||||
|
||||
export interface IGetProfileResponse_DTO {
|
||||
id: string;
|
||||
name: string;
|
||||
@ -12,6 +14,7 @@ export interface IGetProfileResponse_DTO {
|
||||
default_notes: string;
|
||||
default_legal_terms: string;
|
||||
default_quote_validity: string;
|
||||
default_tax: IPercentage_DTO;
|
||||
status: string;
|
||||
lang_code: string;
|
||||
currency_code: string;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import Joi from "joi";
|
||||
import { Result, RuleValidator } from "../../../../common";
|
||||
import { IPercentage_DTO, Result, RuleValidator } from "../../../../common";
|
||||
|
||||
export interface IUpdateProfile_Request_DTO {
|
||||
contact_information: string;
|
||||
@ -7,6 +7,7 @@ export interface IUpdateProfile_Request_DTO {
|
||||
default_notes: string;
|
||||
default_legal_terms: string;
|
||||
default_quote_validity: string;
|
||||
default_tax: IPercentage_DTO;
|
||||
}
|
||||
|
||||
export function ensureUpdateProfile_Request_DTOIsValid(userDTO: IUpdateProfile_Request_DTO) {
|
||||
@ -16,6 +17,10 @@ export function ensureUpdateProfile_Request_DTOIsValid(userDTO: IUpdateProfile_R
|
||||
default_notes: Joi.string().optional().allow(null).allow("").default(""),
|
||||
default_legal_terms: Joi.string().optional().allow(null).allow("").default(""),
|
||||
default_quote_validity: Joi.string().optional().allow(null).allow("").default(""),
|
||||
default_tax: Joi.object({
|
||||
amount: Joi.number().allow(null),
|
||||
scale: Joi.number(),
|
||||
}).optional(),
|
||||
}).unknown(true);
|
||||
|
||||
const result = RuleValidator.validate<IUpdateProfile_Request_DTO>(schema, userDTO);
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { IPercentage_DTO } from "../../../../common";
|
||||
|
||||
export interface IUpdateProfileResponse_DTO {
|
||||
dealer_id: string;
|
||||
contact_information: string;
|
||||
@ -5,4 +7,5 @@ export interface IUpdateProfileResponse_DTO {
|
||||
default_notes: string;
|
||||
default_legal_terms: string;
|
||||
default_quote_validity: string;
|
||||
default_tax: IPercentage_DTO;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user