Customers
This commit is contained in:
parent
ce1c2c9b76
commit
b35b89ee2b
@ -149,7 +149,7 @@ export class Customer extends AggregateRoot<CustomerInternalProps> implements IC
|
|||||||
}
|
}
|
||||||
|
|
||||||
public update(partialCustomer: CustomerPatchProps): Result<void, Error> {
|
public update(partialCustomer: CustomerPatchProps): Result<void, Error> {
|
||||||
const { address: partialAddress, ...rest } = partialCustomer;
|
const { address: partialAddress, defaultTaxes: partialTaxes, ...rest } = partialCustomer;
|
||||||
|
|
||||||
Object.assign(this.props, rest);
|
Object.assign(this.props, rest);
|
||||||
|
|
||||||
@ -161,6 +161,14 @@ export class Customer extends AggregateRoot<CustomerInternalProps> implements IC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (partialTaxes) {
|
||||||
|
const taxesResult = this.defaultTaxes.update(partialTaxes);
|
||||||
|
|
||||||
|
if (taxesResult.isFailure) {
|
||||||
|
return Result.fail(taxesResult.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Result.ok();
|
return Result.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,11 +17,18 @@ export interface ICustomerItemTaxes {
|
|||||||
toKey(): string; // Clave para representar un trío.
|
toKey(): string; // Clave para representar un trío.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CustomerTaxesPatchProps = Partial<CustomerTaxesProps>;
|
||||||
|
|
||||||
export class CustomerTaxes extends ValueObject<CustomerTaxesProps> implements ICustomerItemTaxes {
|
export class CustomerTaxes extends ValueObject<CustomerTaxesProps> implements ICustomerItemTaxes {
|
||||||
static create(props: CustomerTaxesProps) {
|
static create(props: CustomerTaxesProps) {
|
||||||
return Result.ok(new CustomerTaxes(props));
|
return Result.ok(new CustomerTaxes(props));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public update(partial: CustomerTaxesPatchProps): Result<CustomerTaxes, Error> {
|
||||||
|
Object.assign(this.props, partial);
|
||||||
|
return Result.ok();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reconstruye una instancia de CustomerTaxes a partir de una clave serializada.
|
* Reconstruye una instancia de CustomerTaxes a partir de una clave serializada.
|
||||||
* Este método es la operación inversa de toKey().
|
* Este método es la operación inversa de toKey().
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import { FormFieldLabel, TextAreaField, TextField } from "@repo/rdx-ui/components";
|
import { FormFieldLabel, TextAreaField, TextField } from "@repo/rdx-ui/components";
|
||||||
import {
|
import {
|
||||||
Field,
|
Field,
|
||||||
|
FieldContent,
|
||||||
FieldDescription,
|
FieldDescription,
|
||||||
FieldError,
|
FieldError,
|
||||||
FieldGroup,
|
FieldGroup,
|
||||||
FieldLabel,
|
FieldLabel,
|
||||||
FieldLegend,
|
FieldLegend,
|
||||||
FieldSet,
|
FieldSet,
|
||||||
FormControl,
|
|
||||||
RadioGroup,
|
RadioGroup,
|
||||||
RadioGroupItem,
|
RadioGroupItem,
|
||||||
} from "@repo/shadcn-ui/components";
|
} from "@repo/shadcn-ui/components";
|
||||||
@ -19,15 +19,9 @@ import type { CustomerUpdateForm } from "../../entities";
|
|||||||
|
|
||||||
import { CustomerTaxesMultiSelect } from "./customer-taxes-multi-select";
|
import { CustomerTaxesMultiSelect } from "./customer-taxes-multi-select";
|
||||||
|
|
||||||
interface CustomerBasicInfoFieldsProps extends React.ComponentProps<typeof FieldSet> {
|
interface CustomerBasicInfoFieldsProps extends React.ComponentProps<typeof FieldSet> {}
|
||||||
focusRef?: React.RefObject<HTMLInputElement | null>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const CustomerBasicInfoFields = ({
|
export const CustomerBasicInfoFields = ({ className, ...props }: CustomerBasicInfoFieldsProps) => {
|
||||||
focusRef,
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: CustomerBasicInfoFieldsProps) => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { control, setFocus } = useFormContext<CustomerUpdateForm>();
|
const { control, setFocus } = useFormContext<CustomerUpdateForm>();
|
||||||
|
|
||||||
@ -50,49 +44,68 @@ export const CustomerBasicInfoFields = ({
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Field className="lg:col-span-1 lg:col-start-1">
|
<Controller
|
||||||
<Controller
|
control={control}
|
||||||
control={control}
|
name="isCompany"
|
||||||
name="isCompany"
|
render={({ field, fieldState }) => {
|
||||||
render={({ field, fieldState }) => (
|
console.log(field.value);
|
||||||
<Field className="gap-1" data-invalid={fieldState.invalid}>
|
return (
|
||||||
|
<Field
|
||||||
|
className="gap-1 lg:col-span-1 lg:col-start-1"
|
||||||
|
data-invalid={fieldState.invalid}
|
||||||
|
>
|
||||||
<FormFieldLabel required>{t("form_fields.customer_type.label")}</FormFieldLabel>
|
<FormFieldLabel required>{t("form_fields.customer_type.label")}</FormFieldLabel>
|
||||||
|
|
||||||
<FormControl>
|
<RadioGroup
|
||||||
<RadioGroup
|
className="gap-3"
|
||||||
className="gap-3"
|
disabled={field.disabled}
|
||||||
name={field.name}
|
name={field.name}
|
||||||
onValueChange={(value) => field.onChange(value === "true")}
|
onValueChange={(value) => {
|
||||||
value={String(field.value)}
|
console.log("Pongo ", value);
|
||||||
>
|
field.onChange(value === "true");
|
||||||
<div className="flex items-start gap-2">
|
}}
|
||||||
<RadioGroupItem id="customer-type-company" value="true" />
|
required
|
||||||
<label
|
value={field.value ? "true" : "false"}
|
||||||
|
>
|
||||||
|
<Field data-invalid={fieldState.invalid} orientation="horizontal">
|
||||||
|
<FieldContent>
|
||||||
|
<RadioGroupItem
|
||||||
|
aria-invalid={fieldState.invalid}
|
||||||
|
id="customer-type-company"
|
||||||
|
value="true"
|
||||||
|
/>
|
||||||
|
<FieldLabel
|
||||||
className="cursor-pointer text-sm font-medium leading-none"
|
className="cursor-pointer text-sm font-medium leading-none"
|
||||||
htmlFor="customer-type-company"
|
htmlFor="customer-type-company"
|
||||||
>
|
>
|
||||||
{t("form_fields.customer_type.company")}
|
{t("form_fields.customer_type.company")}
|
||||||
</label>
|
</FieldLabel>
|
||||||
</div>
|
</FieldContent>
|
||||||
|
</Field>
|
||||||
|
|
||||||
<div className="flex items-start gap-2">
|
<Field data-invalid={fieldState.invalid} orientation="horizontal">
|
||||||
<RadioGroupItem id="customer-type-individual" value="false" />
|
<FieldContent>
|
||||||
<label
|
<RadioGroupItem
|
||||||
|
aria-invalid={fieldState.invalid}
|
||||||
|
id="customer-type-individual"
|
||||||
|
value="false"
|
||||||
|
/>
|
||||||
|
<FieldLabel
|
||||||
className="cursor-pointer text-sm font-medium leading-none"
|
className="cursor-pointer text-sm font-medium leading-none"
|
||||||
htmlFor="customer-type-individual"
|
htmlFor="customer-type-individual"
|
||||||
>
|
>
|
||||||
{t("form_fields.customer_type.individual")}
|
{t("form_fields.customer_type.individual")}
|
||||||
</label>
|
</FieldLabel>
|
||||||
</div>
|
</FieldContent>
|
||||||
</RadioGroup>
|
</Field>
|
||||||
</FormControl>
|
</RadioGroup>
|
||||||
|
|
||||||
<FieldDescription>{t("form_fields.customer_type.description")}</FieldDescription>
|
<FieldDescription>{t("form_fields.customer_type.description")}</FieldDescription>
|
||||||
<FieldError errors={[fieldState.error]} />
|
<FieldError errors={[fieldState.error]} />
|
||||||
</Field>
|
</Field>
|
||||||
)}
|
);
|
||||||
/>
|
}}
|
||||||
</Field>
|
/>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
className="lg:col-span-1"
|
className="lg:col-span-1"
|
||||||
|
|||||||
@ -9,14 +9,13 @@ type CustomerFormProps = {
|
|||||||
formId: string;
|
formId: string;
|
||||||
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void;
|
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
focusRef?: React.RefObject<HTMLInputElement>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CustomerEditForm = ({ formId, onSubmit, className, focusRef }: CustomerFormProps) => {
|
export const CustomerEditForm = ({ formId, onSubmit, className }: CustomerFormProps) => {
|
||||||
return (
|
return (
|
||||||
<form id={formId} noValidate onSubmit={onSubmit}>
|
<form id={formId} noValidate onSubmit={onSubmit}>
|
||||||
<section className={cn("space-y-12 p-6", className)}>
|
<section className={cn("space-y-12 p-6", className)}>
|
||||||
<CustomerBasicInfoFields focusRef={focusRef} />
|
<CustomerBasicInfoFields />
|
||||||
<CustomerAddressFields />
|
<CustomerAddressFields />
|
||||||
<CustomerContactFields />
|
<CustomerContactFields />
|
||||||
<CustomerAdditionalConfigFields />
|
<CustomerAdditionalConfigFields />
|
||||||
|
|||||||
@ -5,33 +5,34 @@ import { useCallback, useMemo } from "react";
|
|||||||
|
|
||||||
import { useTranslation } from "../../../i18n";
|
import { useTranslation } from "../../../i18n";
|
||||||
|
|
||||||
interface CustomerTaxesMultiSelect {
|
interface CustomerTaxesMultiSelectProps {
|
||||||
value?: string[];
|
value?: string[];
|
||||||
onChange: (selectedValues: string[]) => void;
|
onChange: (selectedValues: string[]) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
inputId?: string;
|
inputId?: string;
|
||||||
[key: string]: any; // Allow other props to be passed
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CustomerTaxesMultiSelect = (props: CustomerTaxesMultiSelect) => {
|
export const CustomerTaxesMultiSelect = ({
|
||||||
const { value, onChange, className, inputId, ...otherProps } = props;
|
value,
|
||||||
|
onChange,
|
||||||
|
className,
|
||||||
|
inputId,
|
||||||
|
...otherProps
|
||||||
|
}: CustomerTaxesMultiSelectProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const taxCatalog = useMemo(() => SpainTaxCatalogProvider(), []);
|
const taxCatalog = useMemo(() => SpainTaxCatalogProvider(), []);
|
||||||
const catalogLookup = useMemo(() => SpainTaxCatalogProvider().toOptionLookup(), []);
|
const catalogLookup = useMemo(() => taxCatalog.toOptionLookup(), [taxCatalog]);
|
||||||
|
|
||||||
/**
|
|
||||||
* Filtra para mantener solo un elemento por grupo.
|
|
||||||
* Si hay duplicados dentro del mismo grupo, se queda con el último.
|
|
||||||
*/
|
|
||||||
const filterSelectedByGroup = useCallback(
|
const filterSelectedByGroup = useCallback(
|
||||||
(selectedValues: string[]) => {
|
(selectedValues: string[]) => {
|
||||||
const groupMap = new Map<string | undefined, string>();
|
const groupMap = new Map<string, string>();
|
||||||
|
|
||||||
for (const code of selectedValues) {
|
for (const code of selectedValues) {
|
||||||
const item = taxCatalog.findByCode(code).getOrUndefined();
|
const item = taxCatalog.findByCode(code).getOrUndefined();
|
||||||
const group = item?.group ?? "ungrouped";
|
const group = item?.group ?? "ungrouped";
|
||||||
groupMap.set(group, code); // Sobrescribe el anterior del mismo grupo
|
groupMap.set(group, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.from(groupMap.values());
|
return Array.from(groupMap.values());
|
||||||
@ -43,19 +44,19 @@ export const CustomerTaxesMultiSelect = (props: CustomerTaxesMultiSelect) => {
|
|||||||
<div className={cn("w-full", "max-w-md")}>
|
<div className={cn("w-full", "max-w-md")}>
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
animation={0}
|
animation={0}
|
||||||
autoFilter={true}
|
autoFilter
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex w-full -mt-0.5 px-1 py-0.5 rounded-md border border-input min-h-8 h-auto items-center justify-between hover:bg-inherit [&_svg]:pointer-events-auto",
|
"flex w-full -mt-0.5 px-1 py-0.5 rounded-md border border-input min-h-8 h-auto items-center justify-between hover:bg-inherit [&_svg]:pointer-events-auto",
|
||||||
"hover:border-ring hover:ring-ring/50 hover:ring-2 font-medium bg-muted/50",
|
"hover:border-ring hover:ring-ring/50 hover:ring-2 font-medium bg-muted/50",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
defaultValue={value}
|
|
||||||
filterSelected={filterSelectedByGroup}
|
filterSelected={filterSelectedByGroup}
|
||||||
id={inputId}
|
id={inputId}
|
||||||
maxCount={3}
|
maxCount={3}
|
||||||
onValueChange={onChange}
|
onValueChange={onChange}
|
||||||
options={catalogLookup}
|
options={catalogLookup}
|
||||||
placeholder={t("components.customer_invoice_taxes_multi_select.placeholder")}
|
placeholder={t("components.customer_invoice_taxes_multi_select.placeholder")}
|
||||||
|
value={value ?? []}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user