Created a new test file for validating the UpdateProfile DTO, ensuring that the default_tax_label property accepts valid inputs and rejects invalid ones.
This commit is contained in:
parent
3212c6ec6d
commit
75de474c16
@ -1,4 +1,10 @@
|
||||
import { ErrorOverlay, FormPercentageField, FormTextAreaField, LoadingOverlay } from "@/components";
|
||||
import {
|
||||
ErrorOverlay,
|
||||
FormPercentageField,
|
||||
FormTextAreaField,
|
||||
FormTextField,
|
||||
LoadingOverlay,
|
||||
} from "@/components";
|
||||
import {
|
||||
Alert,
|
||||
AlertDescription,
|
||||
@ -49,6 +55,7 @@ export const SettingsEditor = () => {
|
||||
amount: undefined,
|
||||
scale: 2,
|
||||
},
|
||||
default_tax_label: "% IVA",
|
||||
}),
|
||||
[]
|
||||
);
|
||||
@ -287,12 +294,21 @@ export const SettingsEditor = () => {
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<FormTextField
|
||||
maxLength={32}
|
||||
placeholder={t("settings.form_fields.default_tax_label.placeholder")}
|
||||
name='default_tax_label'
|
||||
required
|
||||
description={t("settings.form_fields.default_tax_label.desc")}
|
||||
/>
|
||||
<br />
|
||||
<FormPercentageField
|
||||
scale={2}
|
||||
disabled={formState.disabled}
|
||||
placeholder={t("settings.form_fields.default_tax.desc")}
|
||||
name='default_tax'
|
||||
required
|
||||
description={t("settings.form_fields.default_tax.desc")}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardFooter className='px-6 py-4 border-t'>
|
||||
|
||||
@ -162,7 +162,6 @@
|
||||
"sent_to": "Send to Uecko"
|
||||
}
|
||||
},
|
||||
|
||||
"resume": {
|
||||
"title": "Quote",
|
||||
"download_quote": "Download quote",
|
||||
@ -170,7 +169,6 @@
|
||||
"resume": "Resume",
|
||||
"preview": "Preview"
|
||||
},
|
||||
|
||||
"quote_information": "Quote Information",
|
||||
"customer_information": "Customer Information",
|
||||
"payment_information": "Payment Information",
|
||||
@ -455,6 +453,11 @@
|
||||
"placeholder": "",
|
||||
"desc": "Default tax rate for your quotes"
|
||||
},
|
||||
"default_tax_label": {
|
||||
"label": "Tax label",
|
||||
"placeholder": "% VAT",
|
||||
"desc": "Name or label shown alongside the tax"
|
||||
},
|
||||
"default_show_line_discount_column": {
|
||||
"label": "Show discount column by default",
|
||||
"desc": "New quotes will initially show the discount column in their lines. You can change this option later for each quote."
|
||||
@ -482,4 +485,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -162,7 +162,6 @@
|
||||
"sent_to": "Enviar a Uecko"
|
||||
}
|
||||
},
|
||||
|
||||
"resume": {
|
||||
"title": "Cotización",
|
||||
"download_quote": "Descargar",
|
||||
@ -170,7 +169,6 @@
|
||||
"resume": "Resumen",
|
||||
"preview": "Previsualización"
|
||||
},
|
||||
|
||||
"quote_information": "Información",
|
||||
"customer_information": "Cliente",
|
||||
"payment_information": "Pago",
|
||||
@ -243,7 +241,6 @@
|
||||
"description": "Para ayudar a rellenar la cotización, puede añadir bloques de texto predefinidos de esta lista.",
|
||||
"toast_article_added": "Bloque de texto añadido:"
|
||||
},
|
||||
|
||||
"quote_sent_to_editor": {
|
||||
"trigger_button": "Enviar a Uecko",
|
||||
"title": "Enviar la cotización a Uecko",
|
||||
@ -251,7 +248,6 @@
|
||||
"submit_button": "Enviar",
|
||||
"toast_status_changed": "Cotización enviada a Uecko"
|
||||
},
|
||||
|
||||
"quote_status_editor": {
|
||||
"trigger_button": "Cambiar el estado",
|
||||
"title": "Cambiar el estado de la cotización",
|
||||
@ -439,8 +435,8 @@
|
||||
"subtitle": "",
|
||||
"tabs": {
|
||||
"profile": "Ajustes de perfil",
|
||||
"quotes": "Ajustes legales",
|
||||
"legal": "Ajustes para cotizaciones"
|
||||
"quotes": "Ajustes para cotizaciones",
|
||||
"legal": "Ajustes legales"
|
||||
}
|
||||
},
|
||||
"form_fields": {
|
||||
@ -459,9 +455,14 @@
|
||||
"desc": "Esta información de contacto aparecerá en las cotizaciones que entregue a sus clientes."
|
||||
},
|
||||
"default_tax": {
|
||||
"label": "IVA por defecto (%)",
|
||||
"label": "Impuesto por defecto (%)",
|
||||
"placeholder": "",
|
||||
"desc": "Porcentaje de IVA por defecto en las cotizaciones"
|
||||
"desc": "Porcentaje de impuesto por defecto en las cotizaciones"
|
||||
},
|
||||
"default_tax_label": {
|
||||
"label": "Etiqueta del impuesto",
|
||||
"placeholder": "% IVA",
|
||||
"desc": "Nombre o etiqueta que se mostrará junto al impuesto"
|
||||
},
|
||||
"default_show_line_discount_column": {
|
||||
"label": "Mostrar columna descuento por defecto",
|
||||
@ -497,4 +498,4 @@
|
||||
"form_fields": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
dist/client/index.html
vendored
2
dist/client/index.html
vendored
@ -8,7 +8,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link href="https://fonts.upset.dev/css2?family=Poppins&display=swap" rel="stylesheet" />
|
||||
<title>Presupuestador Uecko</title>
|
||||
<script type="module" crossorigin src="/assets/index-D_mpiX-a.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-DzeF1POa.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CBtV2-Es.css">
|
||||
</head>
|
||||
|
||||
|
||||
@ -61,6 +61,15 @@ export class UpdateProfileUseCase
|
||||
profile.defaultShowLineDiscountColumn =
|
||||
profileDTO.default_show_line_discount_column ?? profile.defaultShowLineDiscountColumn;
|
||||
|
||||
if (profileDTO.default_tax_label !== undefined) {
|
||||
const taxLabelOrError = TextValueObject.create(profileDTO.default_tax_label);
|
||||
if (taxLabelOrError.isFailure) {
|
||||
return Result.fail(taxLabelOrError.error);
|
||||
}
|
||||
|
||||
profile.defaultTaxLabel = taxLabelOrError.object;
|
||||
}
|
||||
|
||||
const taxOrError = Percentage.create(profileDTO.default_tax);
|
||||
if (taxOrError.isFailure) {
|
||||
return Result.fail(taxOrError.error);
|
||||
|
||||
@ -24,6 +24,7 @@ export interface IProfileProps {
|
||||
defaultLegalTerms: Note;
|
||||
defaultQuoteValidity: Note;
|
||||
defaultTax: Percentage;
|
||||
defaultTaxLabel: Note;
|
||||
defaultShowLineDiscountColumn: boolean;
|
||||
|
||||
logo: ProfileLogotype;
|
||||
@ -43,6 +44,7 @@ export interface IProfile {
|
||||
|
||||
defaultQuoteValidity: Note;
|
||||
defaultTax: Percentage;
|
||||
defaultTaxLabel: Note;
|
||||
defaultShowLineDiscountColumn: boolean;
|
||||
|
||||
logo: ProfileLogotype;
|
||||
@ -118,6 +120,14 @@ export class Profile extends AggregateRoot<IProfileProps> implements IProfile {
|
||||
this.props.defaultTax = newDefaultTax;
|
||||
}
|
||||
|
||||
get defaultTaxLabel(): Note {
|
||||
return this.props.defaultTaxLabel;
|
||||
}
|
||||
|
||||
set defaultTaxLabel(newDefaultTaxLabel: Note) {
|
||||
this.props.defaultTaxLabel = newDefaultTaxLabel;
|
||||
}
|
||||
|
||||
get defaultShowLineDiscountColumn(): boolean {
|
||||
return this.props.defaultShowLineDiscountColumn;
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@ export const GetProfilePresenter: IGetProfilePresenter = {
|
||||
default_legal_terms: profile.defaultLegalTerms.toString(),
|
||||
default_quote_validity: profile.defaultQuoteValidity.toString(),
|
||||
default_tax: profile.defaultTax.convertScale(2).toObject(),
|
||||
default_tax_label: profile.defaultTaxLabel.toString(),
|
||||
default_show_line_discount_column: profile.defaultShowLineDiscountColumn,
|
||||
|
||||
logo: await profile.logo.toBase64(),
|
||||
|
||||
@ -16,6 +16,7 @@ export const UpdateProfilePresenter: IUpdateProfilePresenter = {
|
||||
default_legal_terms: profile.defaultLegalTerms.toString(),
|
||||
default_quote_validity: profile.defaultQuoteValidity.toString(),
|
||||
default_tax: profile.defaultTax.convertScale(2).toObject(),
|
||||
default_tax_label: profile.defaultTaxLabel.toString(),
|
||||
default_show_line_discount_column: profile.defaultShowLineDiscountColumn,
|
||||
};
|
||||
},
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { IProfileContext } from "../Profile.context";
|
||||
import { createProfileMapper } from "./profile.mapper";
|
||||
|
||||
const source = (defaultShowLineDiscountColumn: boolean | null) =>
|
||||
const source = (defaultShowLineDiscountColumn: boolean | null, defaultTaxLabel?: string | null) =>
|
||||
({
|
||||
default_show_line_discount_column: defaultShowLineDiscountColumn,
|
||||
dataValues: {
|
||||
@ -17,6 +17,7 @@ const source = (defaultShowLineDiscountColumn: boolean | null) =>
|
||||
default_legal_terms: "",
|
||||
default_quote_validity: "",
|
||||
default_tax: 2100,
|
||||
default_tax_label: defaultTaxLabel,
|
||||
},
|
||||
}) as any;
|
||||
|
||||
@ -33,4 +34,15 @@ describe("ProfileMapper default_show_line_discount_column", () => {
|
||||
it("uses true for historical null values", () => {
|
||||
expect(mapper.mapToDomain(source(null)).defaultShowLineDiscountColumn).toBe(true);
|
||||
});
|
||||
|
||||
it("maps a tax label in both persistence directions", () => {
|
||||
const profile = mapper.mapToDomain(source(true, "% IGIC"));
|
||||
|
||||
expect(profile.defaultTaxLabel.toString()).toBe("% IGIC");
|
||||
expect(mapper.mapToPersistence(profile).default_tax_label).toBe("% IGIC");
|
||||
});
|
||||
|
||||
it("uses the default tax label for historical null values", () => {
|
||||
expect(mapper.mapToDomain(source(true, null)).defaultTaxLabel.toString()).toBe("% IVA");
|
||||
});
|
||||
});
|
||||
|
||||
@ -4,7 +4,11 @@ import {
|
||||
SequelizeMapper,
|
||||
} from "@/contexts/common/infrastructure";
|
||||
import { DealerStatus } from "@/contexts/sales/domain";
|
||||
import { Dealer_Model, DealerCreationAttributes } from "@/contexts/sales/infrastructure/sequelize";
|
||||
import {
|
||||
DEFAULT_TAX_LABEL,
|
||||
Dealer_Model,
|
||||
DealerCreationAttributes,
|
||||
} from "@/contexts/sales/infrastructure/sequelize";
|
||||
import {
|
||||
CurrencyData,
|
||||
Language,
|
||||
@ -52,6 +56,9 @@ class ProfileMapper
|
||||
"default_quote_validity",
|
||||
TextValueObject.create
|
||||
);
|
||||
const defaultTaxLabel = this.mapsValue(source, "default_tax_label", (label) =>
|
||||
TextValueObject.create(label ?? DEFAULT_TAX_LABEL)
|
||||
);
|
||||
|
||||
const defaultTax = this.mapsValue(source, "default_tax", (tax) =>
|
||||
Percentage.create({
|
||||
@ -72,6 +79,7 @@ class ProfileMapper
|
||||
defaultLegalTerms,
|
||||
defaultQuoteValidity,
|
||||
defaultTax,
|
||||
defaultTaxLabel,
|
||||
defaultShowLineDiscountColumn: source.default_show_line_discount_column ?? true,
|
||||
|
||||
logo,
|
||||
@ -97,6 +105,7 @@ class ProfileMapper
|
||||
default_legal_terms: source.defaultLegalTerms.toPrimitive(),
|
||||
default_quote_validity: source.defaultQuoteValidity.toPrimitive(),
|
||||
default_tax: source.defaultTax.convertScale(2).toPrimitive(),
|
||||
default_tax_label: source.defaultTaxLabel.toPrimitive(),
|
||||
default_show_line_discount_column: source.defaultShowLineDiscountColumn,
|
||||
logo: source.logo.toPrimitive(),
|
||||
};
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
import { CurrencyData, KeyValueMap, Language, Name, UniqueID } from "@shared/contexts";
|
||||
import { Dealer, DealerLogotype, DealerStatus, IDealerProps } from "../../domain/entities";
|
||||
import { ISalesContext } from "../Sales.context";
|
||||
import { DealerCreationAttributes, Dealer_Model } from "../sequelize";
|
||||
import { DEFAULT_TAX_LABEL, DealerCreationAttributes, Dealer_Model } from "../sequelize";
|
||||
|
||||
export interface IDealerMapper
|
||||
extends ISequelizeMapper<Dealer_Model, DealerCreationAttributes, Dealer> {}
|
||||
@ -35,6 +35,7 @@ class DealerMapper
|
||||
["default_quote_validity", source.default_quote_validity],
|
||||
["default_show_line_discount_column", source.default_show_line_discount_column ?? true],
|
||||
["default_tax", source.default_tax],
|
||||
["default_tax_label", source.default_tax_label ?? DEFAULT_TAX_LABEL],
|
||||
]);
|
||||
|
||||
const props: IDealerProps = {
|
||||
@ -76,6 +77,8 @@ class DealerMapper
|
||||
default_show_line_discount_column:
|
||||
source.additionalInfo.get("default_show_line_discount_column")?.toString() !== "false",
|
||||
default_tax: Number(source.additionalInfo.get("default_tax") ?? 0),
|
||||
default_tax_label:
|
||||
source.additionalInfo.get("default_tax_label")?.toString() ?? DEFAULT_TAX_LABEL,
|
||||
logo: source.logo.toPrimitive(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -19,6 +19,8 @@ export type DealerCreationAttributes = InferCreationAttributes<
|
||||
//quotes?: QuoteCreationAttributes[];
|
||||
};
|
||||
|
||||
export const DEFAULT_TAX_LABEL = "% IVA";
|
||||
|
||||
export class Dealer_Model extends Model<
|
||||
InferAttributes<Dealer_Model, { omit: "user" | "quotes" }>,
|
||||
InferCreationAttributes<Dealer_Model, { omit: "user" | "quotes" }>
|
||||
@ -55,6 +57,7 @@ export class Dealer_Model extends Model<
|
||||
declare default_quote_validity: CreationOptional<string>;
|
||||
declare default_show_line_discount_column: CreationOptional<boolean>;
|
||||
declare default_tax: CreationOptional<number | null>;
|
||||
declare default_tax_label: CreationOptional<string | null>;
|
||||
declare status: CreationOptional<string>;
|
||||
declare lang_code: CreationOptional<string>;
|
||||
declare currency_code: CreationOptional<string>;
|
||||
@ -99,6 +102,12 @@ export default (sequelize: Sequelize) => {
|
||||
defaultValue: 2100,
|
||||
},
|
||||
|
||||
default_tax_label: {
|
||||
type: DataTypes.STRING(32),
|
||||
allowNull: false,
|
||||
defaultValue: DEFAULT_TAX_LABEL,
|
||||
},
|
||||
|
||||
logo: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
|
||||
@ -5,6 +5,10 @@ import { assign } from "lodash";
|
||||
import { DateTime, Settings } from "luxon";
|
||||
|
||||
import { createSequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
|
||||
import {
|
||||
DEFAULT_TAX_LABEL,
|
||||
Dealer_Model,
|
||||
} from "@/contexts/sales/infrastructure/sequelize/dealer.model";
|
||||
import { trace } from "console";
|
||||
import { config } from "../../config";
|
||||
import { app } from "../express/app";
|
||||
@ -106,7 +110,12 @@ const server: http.Server = http
|
||||
|
||||
try {
|
||||
//firebirdConn.sync().then(() => {
|
||||
sequelizeConn.sync({ force: false, alter: true }).then(() => {
|
||||
sequelizeConn.sync({ force: false, alter: true }).then(async () => {
|
||||
await Dealer_Model.update(
|
||||
{ default_tax_label: DEFAULT_TAX_LABEL },
|
||||
{ where: { default_tax_label: null } }
|
||||
);
|
||||
|
||||
initStructure(sequelizeConn.connection);
|
||||
insertUsers();
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ export interface IGetProfileResponse_DTO {
|
||||
default_legal_terms: string;
|
||||
default_quote_validity: string;
|
||||
default_tax: IPercentage_DTO;
|
||||
default_tax_label: string;
|
||||
default_show_line_discount_column: boolean;
|
||||
status: string;
|
||||
lang_code: string;
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
import { ensureUpdateProfile_Request_DTOIsValid } from "./IUpdateProfile_Request.dto";
|
||||
|
||||
const validProfile = {
|
||||
contact_information: "",
|
||||
default_payment_method: "",
|
||||
default_notes: "",
|
||||
default_legal_terms: "",
|
||||
default_quote_validity: "",
|
||||
default_tax: { amount: 2100, scale: 2 },
|
||||
logo: "",
|
||||
};
|
||||
|
||||
describe("ensureUpdateProfile_Request_DTOIsValid default_tax_label", () => {
|
||||
it.each([undefined, "% IVA", "% IGIC", " % VAT "])(
|
||||
"accepts %p",
|
||||
(default_tax_label) => {
|
||||
expect(
|
||||
ensureUpdateProfile_Request_DTOIsValid({ ...validProfile, default_tax_label })
|
||||
).toHaveProperty("isSuccess", true);
|
||||
}
|
||||
);
|
||||
|
||||
it.each(["", " ", "a".repeat(33), 21])("rejects %p", (default_tax_label) => {
|
||||
expect(
|
||||
ensureUpdateProfile_Request_DTOIsValid({ ...validProfile, default_tax_label } as any)
|
||||
).toHaveProperty("isFailure", true);
|
||||
});
|
||||
});
|
||||
@ -8,6 +8,7 @@ export interface IUpdateProfile_Request_DTO {
|
||||
default_legal_terms: string;
|
||||
default_quote_validity: string;
|
||||
default_tax: IPercentage_DTO;
|
||||
default_tax_label?: string;
|
||||
default_show_line_discount_column?: boolean;
|
||||
logo: string;
|
||||
}
|
||||
@ -23,6 +24,7 @@ export function ensureUpdateProfile_Request_DTOIsValid(userDTO: IUpdateProfile_R
|
||||
amount: Joi.number().allow(null),
|
||||
scale: Joi.number(),
|
||||
}).optional(),
|
||||
default_tax_label: Joi.string().trim().min(1).max(32).optional(),
|
||||
default_show_line_discount_column: Joi.boolean(),
|
||||
logo: Joi.string().optional().allow(null).allow("").default(""),
|
||||
}).unknown(true);
|
||||
|
||||
@ -8,5 +8,6 @@ export interface IUpdateProfileResponse_DTO {
|
||||
default_legal_terms: string;
|
||||
default_quote_validity: string;
|
||||
default_tax: IPercentage_DTO;
|
||||
default_tax_label: string;
|
||||
default_show_line_discount_column: boolean;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user