Facturas de cliente
This commit is contained in:
parent
c8db3099a4
commit
1919a54dc2
@ -85,7 +85,7 @@ export class CreateAccountUseCase {
|
||||
website: dto.website ? Maybe.some(dto.website) : Maybe.none(),
|
||||
legalRecord: dto.legal_record,
|
||||
defaultTax: dto.default_tax,
|
||||
langCode: dto.lang_code,
|
||||
langCode: dto.language_code,
|
||||
currencyCode: dto.currency_code,
|
||||
logo: dto.logo ? Maybe.some(dto.logo) : Maybe.none(),
|
||||
};
|
||||
|
||||
@ -106,8 +106,8 @@ export class UpdateAccountUseCase {
|
||||
validatedData.defaultTax = dto.default_tax;
|
||||
}
|
||||
|
||||
if (dto.lang_code) {
|
||||
validatedData.langCode = dto.lang_code;
|
||||
if (dto.language_code) {
|
||||
validatedData.langCode = dto.language_code;
|
||||
}
|
||||
|
||||
if (dto.currency_code) {
|
||||
|
||||
@ -38,7 +38,7 @@ const sampleAccountPrimitives = {
|
||||
legal_record: "Registro Mercantil XYZ",
|
||||
default_tax: 21,
|
||||
status: "active",
|
||||
lang_code: "es",
|
||||
language_code: "es",
|
||||
currency_code: "EUR",
|
||||
logo: "https://xyz.com/logo.png",
|
||||
};
|
||||
@ -87,7 +87,7 @@ const accountBuilder = (accountData: any) => {
|
||||
: Maybe.none(),
|
||||
legalRecord: sampleAccountPrimitives.legal_record,
|
||||
defaultTax: sampleAccountPrimitives.default_tax,
|
||||
langCode: sampleAccountPrimitives.lang_code,
|
||||
langCode: sampleAccountPrimitives.language_code,
|
||||
currencyCode: sampleAccountPrimitives.currency_code,
|
||||
logo: sampleAccountPrimitives.logo ? Maybe.some(sampleAccountPrimitives.logo) : Maybe.none(),
|
||||
};
|
||||
|
||||
@ -30,7 +30,7 @@ const sampleAccount = {
|
||||
legal_record: "Registro Mercantil XYZ",
|
||||
default_tax: 21,
|
||||
status: "active",
|
||||
lang_code: "es",
|
||||
language_code: "es",
|
||||
currency_code: "EUR",
|
||||
logo: "https://xyz.com/logo.png",
|
||||
};
|
||||
|
||||
@ -64,7 +64,7 @@ export class AccountMapper
|
||||
website: source.website ? Maybe.some(source.website) : Maybe.none(),
|
||||
legalRecord: source.legal_record,
|
||||
defaultTax: source.default_tax,
|
||||
langCode: source.lang_code,
|
||||
langCode: source.language_code,
|
||||
currencyCode: source.currency_code,
|
||||
logo: source.logo ? Maybe.some(source.logo) : Maybe.none(),
|
||||
},
|
||||
@ -94,7 +94,7 @@ export class AccountMapper
|
||||
legal_record: source.legalRecord,
|
||||
default_tax: source.defaultTax,
|
||||
status: source.isActive ? "active" : "inactive",
|
||||
lang_code: source.langCode,
|
||||
language_code: source.langCode,
|
||||
currency_code: source.currencyCode,
|
||||
logo: source.logo.getOrUndefined(),
|
||||
};
|
||||
|
||||
@ -37,7 +37,7 @@ export class AccountModel extends Model<InferAttributes<AccountModel>, AccountCr
|
||||
|
||||
declare default_tax: number;
|
||||
declare status: string;
|
||||
declare lang_code: string;
|
||||
declare language_code: string;
|
||||
declare currency_code: string;
|
||||
declare logo: CreationOptional<string>;
|
||||
}
|
||||
@ -129,7 +129,7 @@ export default (sequelize: Sequelize) => {
|
||||
defaultValue: null,
|
||||
},
|
||||
|
||||
lang_code: {
|
||||
language_code: {
|
||||
type: DataTypes.STRING(2),
|
||||
allowNull: false,
|
||||
defaultValue: "es",
|
||||
|
||||
@ -30,7 +30,7 @@ export const createAccountPresenter: ICreateAccountPresenter = {
|
||||
|
||||
default_tax: ensureNumber(account.defaultTax),
|
||||
status: ensureString(account.isActive ? "active" : "inactive"),
|
||||
lang_code: ensureString(account.langCode),
|
||||
language_code: ensureString(account.langCode),
|
||||
currency_code: ensureString(account.currencyCode),
|
||||
logo: ensureString(account.logo.getOrUndefined()),
|
||||
}),
|
||||
|
||||
@ -30,7 +30,7 @@ export const getAccountPresenter: IGetAccountPresenter = {
|
||||
|
||||
default_tax: ensureNumber(account.defaultTax),
|
||||
status: ensureString(account.isActive ? "active" : "inactive"),
|
||||
lang_code: ensureString(account.langCode),
|
||||
language_code: ensureString(account.langCode),
|
||||
currency_code: ensureString(account.currencyCode),
|
||||
logo: ensureString(account.logo.getOrUndefined()),
|
||||
}),
|
||||
|
||||
@ -31,7 +31,7 @@ export const listAccountsPresenter: IListAccountsPresenter = {
|
||||
|
||||
default_tax: ensureNumber(account.defaultTax),
|
||||
status: ensureString(account.isActive ? "active" : "inactive"),
|
||||
lang_code: ensureString(account.langCode),
|
||||
language_code: ensureString(account.langCode),
|
||||
currency_code: ensureString(account.currencyCode),
|
||||
logo: ensureString(account.logo.getOrUndefined()),
|
||||
})),
|
||||
|
||||
@ -30,7 +30,7 @@ export const updateAccountPresenter: IUpdateAccountPresenter = {
|
||||
|
||||
default_tax: ensureNumber(account.defaultTax),
|
||||
status: ensureString(account.isActive ? "active" : "inactive"),
|
||||
lang_code: ensureString(account.langCode),
|
||||
language_code: ensureString(account.langCode),
|
||||
currency_code: ensureString(account.currencyCode),
|
||||
logo: ensureString(account.logo.getOrUndefined()),
|
||||
}),
|
||||
|
||||
@ -21,7 +21,7 @@ export interface ICreateAccountRequestDTO {
|
||||
legal_record: string;
|
||||
|
||||
default_tax: number;
|
||||
lang_code: string;
|
||||
language_code: string;
|
||||
currency_code: string;
|
||||
logo: string;
|
||||
}
|
||||
@ -46,7 +46,7 @@ export interface IUpdateAccountRequestDTO {
|
||||
legal_record: string;
|
||||
|
||||
default_tax: number;
|
||||
lang_code: string;
|
||||
language_code: string;
|
||||
currency_code: string;
|
||||
logo: string;
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ export interface IListAccountsResponseDTO {
|
||||
|
||||
default_tax: number;
|
||||
status: string;
|
||||
lang_code: string;
|
||||
language_code: string;
|
||||
currency_code: string;
|
||||
logo: string;
|
||||
}
|
||||
@ -49,7 +49,7 @@ export interface IGetAccountResponseDTO {
|
||||
|
||||
default_tax: number;
|
||||
status: string;
|
||||
lang_code: string;
|
||||
language_code: string;
|
||||
currency_code: string;
|
||||
logo: string;
|
||||
}
|
||||
@ -77,7 +77,7 @@ export interface ICreateAccountResponseDTO {
|
||||
|
||||
default_tax: number;
|
||||
status: string;
|
||||
lang_code: string;
|
||||
language_code: string;
|
||||
currency_code: string;
|
||||
logo: string;
|
||||
}
|
||||
@ -108,7 +108,7 @@ export interface IUpdateAccountResponseDTO {
|
||||
|
||||
default_tax: number;
|
||||
status: string;
|
||||
lang_code: string;
|
||||
language_code: string;
|
||||
currency_code: string;
|
||||
logo: string;
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ export const ICreateAccountRequestSchema = z.object({
|
||||
|
||||
default_tax: z.number(),
|
||||
status: z.string(),
|
||||
lang_code: z.string(),
|
||||
language_code: z.string(),
|
||||
currency_code: z.string(),
|
||||
logo: z.string(),
|
||||
});
|
||||
@ -55,7 +55,7 @@ export const IUpdateAccountRequestSchema = z.object({
|
||||
|
||||
default_tax: z.number(),
|
||||
status: z.string(),
|
||||
lang_code: z.string(),
|
||||
language_code: z.string(),
|
||||
currency_code: z.string(),
|
||||
logo: z.string(),
|
||||
});
|
||||
|
||||
@ -63,7 +63,7 @@ export class ContactMapper
|
||||
legalRecord: source.legal_record,
|
||||
defaultTax: source.default_tax,
|
||||
status: source.status,
|
||||
langCode: source.lang_code,
|
||||
langCode: source.language_code,
|
||||
currencyCode: source.currency_code,
|
||||
},
|
||||
idOrError.data
|
||||
@ -96,7 +96,7 @@ export class ContactMapper
|
||||
legal_record: source.legalRecord,
|
||||
default_tax: source.defaultTax,
|
||||
status: source.isActive ? "active" : "inactive",
|
||||
lang_code: source.langCode,
|
||||
language_code: source.langCode,
|
||||
currency_code: source.currencyCode,
|
||||
});
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ export class ContactModel extends Model<
|
||||
|
||||
declare default_tax: number;
|
||||
declare status: string;
|
||||
declare lang_code: string;
|
||||
declare language_code: string;
|
||||
declare currency_code: string;
|
||||
}
|
||||
|
||||
@ -130,7 +130,7 @@ export default (sequelize: Sequelize) => {
|
||||
defaultValue: 2100,
|
||||
},
|
||||
|
||||
lang_code: {
|
||||
language_code: {
|
||||
type: DataTypes.STRING(2),
|
||||
allowNull: false,
|
||||
defaultValue: "es",
|
||||
|
||||
@ -32,7 +32,7 @@ export const listContactsPresenter: IListContactsPresenter = {
|
||||
|
||||
default_tax: ensureNumber(contact.defaultTax),
|
||||
status: ensureString(contact.isActive ? "active" : "inactive"),
|
||||
lang_code: ensureString(contact.langCode),
|
||||
language_code: ensureString(contact.langCode),
|
||||
currency_code: ensureString(contact.currencyCode),
|
||||
})),
|
||||
};
|
||||
|
||||
@ -22,6 +22,6 @@ export interface IListContactsResponseDTO {
|
||||
|
||||
default_tax: number;
|
||||
status: string;
|
||||
lang_code: string;
|
||||
language_code: string;
|
||||
currency_code: string;
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ i18n
|
||||
detection: {
|
||||
order: ["navigator"],
|
||||
},
|
||||
debug: import.meta.env.DEV,
|
||||
debug: false, //import.meta.env.DEV,
|
||||
fallbackLng: "es",
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
|
||||
@ -31,8 +31,6 @@ export const getAppRouter = () => {
|
||||
|
||||
const grouped = groupModulesByLayout(modules);
|
||||
|
||||
console.debug(grouped);
|
||||
|
||||
return createBrowserRouter(
|
||||
createRoutesFromElements(
|
||||
<Route path='/'>
|
||||
|
||||
@ -4,7 +4,7 @@ export interface IGetProfileResponseDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
lang_code: string;
|
||||
language_code: string;
|
||||
roles: string[];
|
||||
dealer: {
|
||||
id: string;
|
||||
@ -16,7 +16,7 @@ export interface IGetProfileResponseDTO {
|
||||
default_quote_validity: string;
|
||||
default_tax: IPercentageDTO;
|
||||
status: string;
|
||||
lang_code: string;
|
||||
language_code: string;
|
||||
currency_code: string;
|
||||
logo: string;
|
||||
};
|
||||
|
||||
@ -7,7 +7,7 @@ export interface ILoginResponseDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
lang_code: string;
|
||||
language_code: string;
|
||||
roles: string[];
|
||||
token: string;
|
||||
refresh_token: string;
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
export type DTO<T = unknown> = T;
|
||||
export type BinaryOutput = Buffer; // Puedes ampliar a Readable si usas streams
|
||||
|
||||
export type IPresenterOutputParams = Record<string, unknown>;
|
||||
|
||||
export interface IPresenter<TSource = unknown, TOutput = DTO> {
|
||||
toOutput(source: TSource): TOutput | Promise<TOutput>;
|
||||
toOutput(source: TSource, params?: IPresenterOutputParams): TOutput | Promise<TOutput>;
|
||||
}
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
import { IPresenterRegistry } from "./presenter-registry.interface";
|
||||
import { IPresenter } from "./presenter.interface";
|
||||
|
||||
export type IPresenterParams = {
|
||||
presenterRegistry: IPresenterRegistry;
|
||||
} & Record<string, unknown>;
|
||||
import { IPresenter, IPresenterOutputParams } from "./presenter.interface";
|
||||
|
||||
export abstract class Presenter<TSource = unknown, TOutput = unknown>
|
||||
implements IPresenter<TSource, TOutput>
|
||||
{
|
||||
constructor(protected presenterRegistry: IPresenterRegistry) {}
|
||||
abstract toOutput(source: TSource): TOutput;
|
||||
abstract toOutput(source: TSource, params?: IPresenterOutputParams): TOutput;
|
||||
}
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
import { QuantityDTO } from "@erp/core";
|
||||
import { Quantity } from "@repo/rdx-ddd";
|
||||
|
||||
export function formatQuantityDTO(quantity_value: QuantityDTO) {
|
||||
const value = Quantity.create({
|
||||
value: Number(quantity_value.value),
|
||||
scale: Number(quantity_value.scale),
|
||||
}).data;
|
||||
|
||||
return value.toNumber;
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
export * from "./format-money-dto";
|
||||
export * from "./format-percentage-dto";
|
||||
export * from "./format-quantity-dto";
|
||||
export * from "./map-dto-to-customer-invoice-props";
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
import { MoneyDTO } from "@erp/core";
|
||||
import { MoneyValue } from "@repo/rdx-ddd";
|
||||
|
||||
export function formatMoneyDTO(amount: MoneyDTO, locale: string) {
|
||||
const money = MoneyValue.create({
|
||||
value: Number(amount.value),
|
||||
currency_code: amount.currency_code,
|
||||
scale: Number(amount.scale),
|
||||
}).data;
|
||||
|
||||
return money.format(locale);
|
||||
}
|
||||
@ -9,7 +9,7 @@ type GetCustomerInvoiceItemByInvoiceIdResponseDTO = ArrayElement<
|
||||
>;
|
||||
|
||||
export class CustomerInvoiceItemsFullPresenter extends Presenter {
|
||||
protected _map(
|
||||
private _mapItem(
|
||||
invoiceItem: CustomerInvoiceItem,
|
||||
index: number
|
||||
): GetCustomerInvoiceItemByInvoiceIdResponseDTO {
|
||||
@ -48,6 +48,6 @@ export class CustomerInvoiceItemsFullPresenter extends Presenter {
|
||||
}
|
||||
|
||||
toOutput(invoiceItems: CustomerInvoiceItems): GetCustomerInvoiceByIdResponseDTO["items"] {
|
||||
return invoiceItems.map(this._map);
|
||||
return invoiceItems.map(this._mapItem);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
import { IPresenterOutputParams, Presenter } from "@erp/core/api";
|
||||
import { GetCustomerInvoiceByIdResponseDTO } from "@erp/customer-invoices/common";
|
||||
import { ArrayElement } from "@repo/rdx-utils";
|
||||
import { formatMoneyDTO, formatPercentageDTO, formatQuantityDTO } from "../../helpers";
|
||||
|
||||
type CustomerInvoiceItemsDTO = GetCustomerInvoiceByIdResponseDTO["items"];
|
||||
type CustomerInvoiceItemDTO = ArrayElement<CustomerInvoiceItemsDTO>;
|
||||
|
||||
export class CustomerInvoiceItemsReportPersenter extends Presenter<
|
||||
CustomerInvoiceItemsDTO,
|
||||
unknown
|
||||
> {
|
||||
private _locale!: string;
|
||||
|
||||
private _mapItem(invoiceItem: CustomerInvoiceItemDTO, index: number) {
|
||||
return {
|
||||
...invoiceItem,
|
||||
|
||||
quantity: formatQuantityDTO(invoiceItem.quantity),
|
||||
unit_amount: formatMoneyDTO(invoiceItem.unit_amount, this._locale),
|
||||
|
||||
subtotal_amount: formatMoneyDTO(invoiceItem.subtotal_amount, this._locale),
|
||||
discount_percetage: formatPercentageDTO(invoiceItem.discount_percentage),
|
||||
discount_amount: formatMoneyDTO(invoiceItem.discount_amount, this._locale),
|
||||
taxes_amount: formatMoneyDTO(invoiceItem.taxes_amount, this._locale),
|
||||
total_amount: formatMoneyDTO(invoiceItem.total_amount, this._locale),
|
||||
};
|
||||
}
|
||||
|
||||
toOutput(invoiceItems: CustomerInvoiceItemsDTO, params: IPresenterOutputParams): unknown {
|
||||
const { locale } = params as {
|
||||
locale: string;
|
||||
};
|
||||
|
||||
this._locale = locale;
|
||||
|
||||
return invoiceItems.map((item, index) => {
|
||||
return this._mapItem(item, index);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -7,12 +7,22 @@ export class CustomerInvoiceReportPresenter extends Presenter<
|
||||
unknown
|
||||
> {
|
||||
toOutput(invoiceDTO: GetCustomerInvoiceByIdResponseDTO) {
|
||||
const itemsPresenter = this.presenterRegistry.getPresenter({
|
||||
resource: "customer-invoice-items",
|
||||
projection: "REPORT",
|
||||
format: "JSON",
|
||||
});
|
||||
|
||||
const locale = invoiceDTO.language_code;
|
||||
const itemsDTO = itemsPresenter.toOutput(invoiceDTO.items, {
|
||||
locale,
|
||||
});
|
||||
|
||||
return {
|
||||
...invoiceDTO,
|
||||
items: itemsDTO,
|
||||
subtotal_amount: formatMoneyDTO(invoiceDTO.subtotal_amount, locale),
|
||||
percentage: formatPercentageDTO(invoiceDTO.discount_percentage),
|
||||
discount_percetage: formatPercentageDTO(invoiceDTO.discount_percentage),
|
||||
discount_amount: formatMoneyDTO(invoiceDTO.discount_amount, locale),
|
||||
taxes_amount: formatMoneyDTO(invoiceDTO.taxes_amount, locale),
|
||||
total_amount: formatMoneyDTO(invoiceDTO.total_amount, locale),
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
export * from "./customer-invoice-items.report.presenter";
|
||||
export * from "./customer-invoice.report.presenter";
|
||||
export * from "./list-customer-invoices.presenter";
|
||||
|
||||
@ -23,6 +23,7 @@ import {
|
||||
} from "../application";
|
||||
|
||||
import { JsonTaxCatalogProvider, spainTaxCatalogProvider } from "@erp/core";
|
||||
import { CustomerInvoiceItemsReportPersenter } from "../application/presenters/queries/customer-invoice-items.report.presenter";
|
||||
import { CustomerInvoiceService } from "../domain";
|
||||
import { CustomerInvoiceDomainMapper, CustomerInvoiceListMapper } from "./mappers";
|
||||
import { CustomerInvoiceRepository } from "./sequelize";
|
||||
@ -108,6 +109,14 @@ export function buildCustomerInvoiceDependencies(params: ModuleParams): Customer
|
||||
},
|
||||
presenter: new CustomerInvoiceReportPresenter(presenterRegistry),
|
||||
},
|
||||
{
|
||||
key: {
|
||||
resource: "customer-invoice-items",
|
||||
projection: "REPORT",
|
||||
format: "JSON",
|
||||
},
|
||||
presenter: new CustomerInvoiceItemsReportPersenter(presenterRegistry),
|
||||
},
|
||||
{
|
||||
key: {
|
||||
resource: "customer-invoice",
|
||||
|
||||
@ -122,7 +122,7 @@
|
||||
"placeholder": "Select default tax",
|
||||
"description": "The default tax rate for the customer"
|
||||
},
|
||||
"lang_code": {
|
||||
"language_code": {
|
||||
"label": "Language",
|
||||
"placeholder": "Select language",
|
||||
"description": "The preferred language of the customer"
|
||||
|
||||
@ -124,7 +124,7 @@
|
||||
"placeholder": "Seleccione el impuesto por defecto",
|
||||
"description": "La tasa de impuesto por defecto para el cliente"
|
||||
},
|
||||
"lang_code": {
|
||||
"language_code": {
|
||||
"label": "Idioma",
|
||||
"placeholder": "Seleccione el idioma",
|
||||
"description": "El idioma preferido del cliente"
|
||||
|
||||
@ -40,10 +40,10 @@ const defaultCustomerData = {
|
||||
country: "ES",
|
||||
postal_code: "28080",
|
||||
province: "Madrid",
|
||||
lang_code: "es",
|
||||
language_code: "es",
|
||||
currency_code: "EUR",
|
||||
legal_record: "Registro Mercantil de Madrid, Tomo 12345, Folio 67, Hoja M-123456",
|
||||
default_tax: ["iva_21", "rec_5_2"],
|
||||
default_taxes: ["iva_21", "rec_5_2"],
|
||||
};
|
||||
|
||||
interface CustomerFormProps {
|
||||
@ -290,20 +290,20 @@ export const CustomerEditForm = ({
|
||||
<CardContent className='grid grid-cols-1 gap-y-8 gap-x-6 @xl:grid-cols-2'>
|
||||
<TaxesMultiSelectField
|
||||
control={form.control}
|
||||
name='default_tax'
|
||||
name='default_taxes'
|
||||
required
|
||||
label={t("form_fields.default_tax.label")}
|
||||
placeholder={t("form_fields.default_tax.placeholder")}
|
||||
description={t("form_fields.default_tax.description")}
|
||||
label={t("form_fields.default_taxes.label")}
|
||||
placeholder={t("form_fields.default_taxes.placeholder")}
|
||||
description={t("form_fields.default_taxes.description")}
|
||||
/>
|
||||
|
||||
<SelectField
|
||||
control={form.control}
|
||||
name='lang_code'
|
||||
name='language_code'
|
||||
required
|
||||
label={t("form_fields.lang_code.label")}
|
||||
placeholder={t("form_fields.lang_code.placeholder")}
|
||||
description={t("form_fields.lang_code.description")}
|
||||
label={t("form_fields.language_code.label")}
|
||||
placeholder={t("form_fields.language_code.placeholder")}
|
||||
description={t("form_fields.language_code.description")}
|
||||
items={[
|
||||
{ value: "es", label: "Español" },
|
||||
{ value: "en", label: "Inglés" },
|
||||
|
||||
@ -33,8 +33,8 @@ export function CustomerBasicInfoFields({ control }: { control: any }) {
|
||||
<FormLabel>{t("form_fields.customer_type.label")}</FormLabel>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
value={field.value ? "1" : "0"}
|
||||
onValueChange={(val) => field.onChange(val === "1")}
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value ? "1" : "0"}
|
||||
className='flex gap-6'
|
||||
>
|
||||
<FormItem className='flex items-center space-x-2'>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user