.
This commit is contained in:
parent
520373cbd6
commit
700445499b
@ -13,6 +13,7 @@ import type {
|
||||
import type { IProformaFullSnapshotBuilder } from "../snapshot-builders/full";
|
||||
import { GetProformaByIdUseCase, ListProformasUseCase, ReportProformaUseCase } from "../use-cases";
|
||||
import { CreateProformaUseCase } from "../use-cases/create-proforma";
|
||||
import { IssueProformaUseCase } from "../use-cases/issue-proforma.use-case";
|
||||
|
||||
export function buildGetProformaByIdUseCase(deps: {
|
||||
finder: IProformaFinder;
|
||||
@ -64,6 +65,10 @@ export function buildCreateProformaUseCase(deps: {
|
||||
});
|
||||
}
|
||||
|
||||
export function buildIssueProformaUseCase(deps: { finder: IProformaFinder }) {
|
||||
return new IssueProformaUseCase(deps.finder);
|
||||
}
|
||||
|
||||
/*export function buildUpdateProformaUseCase(deps: {
|
||||
finder: IProformaFinder;
|
||||
fullSnapshotBuilder: IProformaFullSnapshotBuilder;
|
||||
@ -75,9 +80,6 @@ export function buildDeleteProformaUseCase(deps: { finder: IProformaFinder }) {
|
||||
return new DeleteProformaUseCase(deps.finder);
|
||||
}
|
||||
|
||||
export function buildIssueProformaUseCase(deps: { finder: IProformaFinder }) {
|
||||
return new IssueProformaUseCase(deps.finder);
|
||||
}
|
||||
|
||||
export function buildChangeStatusProformaUseCase(deps: {
|
||||
finder: IProformaFinder;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
export * from "./create-proforma";
|
||||
//export * from "./delete-proforma.use-case";
|
||||
export * from "./get-proforma-by-id.use-case";
|
||||
//export * from "./issue-proforma.use-case";
|
||||
export * from "./issue-proforma.use-case";
|
||||
export * from "./list-proformas.use-case";
|
||||
export * from "./report-proforma.use-case";
|
||||
//export * from "./update-proforma";
|
||||
|
||||
@ -12,6 +12,7 @@ import {
|
||||
import {
|
||||
CreateProformaRequestSchema,
|
||||
GetProformaByIdRequestSchema,
|
||||
IssueProformaByIdParamsRequestSchema,
|
||||
ListProformasRequestSchema,
|
||||
ReportProformaByIdParamsRequestSchema,
|
||||
ReportProformaByIdQueryRequestSchema,
|
||||
@ -129,7 +130,7 @@ export const proformasRouter = (params: ModuleParams, deps: ProformasInternalDep
|
||||
const controller = new ChangeStatusProformaController(useCase);
|
||||
return controller.execute(req, res, next);
|
||||
}
|
||||
);
|
||||
);*/
|
||||
|
||||
router.put(
|
||||
"/:proforma_id/issue",
|
||||
@ -142,7 +143,7 @@ export const proformasRouter = (params: ModuleParams, deps: ProformasInternalDep
|
||||
const controller = new IssuedProformaController(useCase);
|
||||
return controller.execute(req, res, next);
|
||||
}
|
||||
);*/
|
||||
);
|
||||
|
||||
app.use(`${config.server.apiBasePath}/proformas`, router);
|
||||
};
|
||||
|
||||
@ -17,7 +17,7 @@ export class SequelizeProformaNumberGenerator implements IProformaNumberGenerato
|
||||
): Promise<Result<InvoiceNumber, Error>> {
|
||||
const where: WhereOptions = {
|
||||
company_id: companyId.toString(),
|
||||
is_proforma: false,
|
||||
is_proforma: true,
|
||||
};
|
||||
|
||||
series.match(
|
||||
@ -41,12 +41,12 @@ export class SequelizeProformaNumberGenerator implements IProformaNumberGenerato
|
||||
lock: transaction.LOCK.UPDATE, // requiere InnoDB y TX abierta
|
||||
});
|
||||
|
||||
let nextValue = "001"; // valor inicial por defecto
|
||||
let nextValue = "0001"; // valor inicial por defecto
|
||||
|
||||
if (lastInvoice) {
|
||||
const current = Number(lastInvoice.invoice_number);
|
||||
const next = Number.isFinite(current) && current > 0 ? current + 1 : 1;
|
||||
nextValue = String(next).padStart(3, "0");
|
||||
nextValue = String(next).padStart(4, "0");
|
||||
}
|
||||
|
||||
const numberResult = InvoiceNumber.create(nextValue);
|
||||
|
||||
@ -226,6 +226,8 @@ export function useProformasGridColumns(
|
||||
const availableTransitions =
|
||||
PROFORMA_STATUS_TRANSITIONS[proforma.status as ProformaStatus] ?? [];
|
||||
|
||||
console.log(availableTransitions);
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-1">
|
||||
{!isIssued && actionHandlers.onEditClick && (
|
||||
|
||||
@ -36,10 +36,10 @@ export const ProformaListPage = () => {
|
||||
} = useProformaListPageController();
|
||||
|
||||
const columns = useProformasGridColumns({
|
||||
//onEditClick: (proforma) => navigate(`/proformas/${proforma.id}/edit`),
|
||||
//onIssueClick: handleIssueProforma,
|
||||
//onDeleteClick: handleDeleteProforma,
|
||||
//onChangeStatusClick: handleChangeStatusProforma,
|
||||
onEditClick: (proforma) => navigate(`/proformas/${proforma.id}/edit`),
|
||||
onIssueClick: handleIssueProforma,
|
||||
onDeleteClick: handleDeleteProforma,
|
||||
onChangeStatusClick: handleChangeStatusProforma,
|
||||
});
|
||||
|
||||
if (listCtrl.isError || !listCtrl.data) {
|
||||
|
||||
@ -80,8 +80,8 @@
|
||||
"description": "The street address of the customer"
|
||||
},
|
||||
"street2": {
|
||||
"label": "Street",
|
||||
"placeholder": "Enter street",
|
||||
"label": "Street 2",
|
||||
"placeholder": "Enter street 2",
|
||||
"description": "The street address of the customer"
|
||||
},
|
||||
"city": {
|
||||
@ -114,7 +114,6 @@
|
||||
"placeholder": "Enter secondary email",
|
||||
"description": "The secondary email address of the customer"
|
||||
},
|
||||
|
||||
"phone_primary": {
|
||||
"label": "Primary phone",
|
||||
"placeholder": "Enter primary phone number",
|
||||
@ -125,7 +124,6 @@
|
||||
"placeholder": "Enter secondary phone number ",
|
||||
"description": "The secondary phone number of the customer"
|
||||
},
|
||||
|
||||
"mobile_primary": {
|
||||
"label": "Primary mobile",
|
||||
"placeholder": "Enter primary mobile number",
|
||||
|
||||
@ -82,7 +82,7 @@
|
||||
"description": "La dirección de la calle del cliente"
|
||||
},
|
||||
"street2": {
|
||||
"label": "Calle",
|
||||
"label": "Calle (ampliación)",
|
||||
"placeholder": "Ingrese la calle",
|
||||
"description": "La dirección de la calle del cliente"
|
||||
},
|
||||
@ -106,67 +106,56 @@
|
||||
"placeholder": "Seleccione el país",
|
||||
"description": "El país del cliente"
|
||||
},
|
||||
|
||||
"email_primary": {
|
||||
"label": "Email principal",
|
||||
"placeholder": "Ingrese el correo electrónico",
|
||||
"description": "La dirección de correo electrónico principal del cliente"
|
||||
},
|
||||
|
||||
"email_secondary": {
|
||||
"label": "Email secundario",
|
||||
"placeholder": "Ingrese el correo electrónico",
|
||||
"description": "La dirección de correo electrónico secundario del clientºe"
|
||||
},
|
||||
|
||||
"phone_primary": {
|
||||
"label": "Teléfono",
|
||||
"placeholder": "Ingrese el número de teléfono",
|
||||
"description": "El número de teléfono del cliente"
|
||||
},
|
||||
|
||||
"phone_secondary": {
|
||||
"label": "Teléfono secundario",
|
||||
"placeholder": "Ingrese el número de teléfono secundario",
|
||||
"description": "El número de teléfono secundario del cliente"
|
||||
},
|
||||
|
||||
"mobile_primary": {
|
||||
"label": "Teléfono",
|
||||
"placeholder": "Ingrese el número de teléfono",
|
||||
"description": "El número de teléfono del cliente"
|
||||
},
|
||||
|
||||
"mobile_secondary": {
|
||||
"label": "Teléfono secundario",
|
||||
"placeholder": "Ingrese el número de teléfono secundario",
|
||||
"description": "El número de teléfono secundario del cliente"
|
||||
},
|
||||
|
||||
"fax": {
|
||||
"label": "Fax",
|
||||
"placeholder": "Ingrese el número de fax",
|
||||
"description": "El número de fax del cliente"
|
||||
},
|
||||
|
||||
"website": {
|
||||
"label": "Sitio web",
|
||||
"placeholder": "Ingrese la URL del sitio web",
|
||||
"description": "El sitio web del cliente"
|
||||
},
|
||||
|
||||
"default_taxes": {
|
||||
"label": "Impuesto por defecto",
|
||||
"placeholder": "Seleccione el impuesto por defecto",
|
||||
"description": "La tasa de impuesto por defecto para el cliente"
|
||||
},
|
||||
|
||||
"language_code": {
|
||||
"label": "Idioma",
|
||||
"placeholder": "Seleccione el idioma",
|
||||
"description": "El idioma preferido del cliente"
|
||||
},
|
||||
|
||||
"currency_code": {
|
||||
"label": "Moneda",
|
||||
"placeholder": "Seleccione la moneda",
|
||||
|
||||
@ -55,7 +55,8 @@ export const CustomerBasicInfoFields = ({
|
||||
required
|
||||
/>
|
||||
</Field>
|
||||
<Field className="lg:col-span-2">
|
||||
|
||||
<Field className="lg:col-span-1 lg:col-start-1">
|
||||
<FormField
|
||||
control={control}
|
||||
name="is_company"
|
||||
@ -94,6 +95,17 @@ export const CustomerBasicInfoFields = ({
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<Field className="lg:col-span-1 " ref={focusRef}>
|
||||
<TextField
|
||||
control={control}
|
||||
description={t("form_fields.tin.description")}
|
||||
label={t("form_fields.tin.label")}
|
||||
name="tin"
|
||||
placeholder={t("form_fields.tin.placeholder")}
|
||||
required
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<TextField
|
||||
className="lg:col-span-full"
|
||||
control={control}
|
||||
@ -118,10 +130,7 @@ export const CustomerBasicInfoFields = ({
|
||||
name="default_taxes"
|
||||
render={({ field, fieldState }) => (
|
||||
<Field className={"gap-1"} data-invalid={fieldState.invalid}>
|
||||
<FieldLabel
|
||||
className="text-xs text-muted-foreground text-nowrap"
|
||||
htmlFor={"default_taxes"}
|
||||
>
|
||||
<FieldLabel htmlFor={"default_taxes"}>
|
||||
{t("form_fields.default_taxes.label")}
|
||||
</FieldLabel>
|
||||
<CustomerTaxesMultiSelect
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { FormDebug } from "@erp/core/components";
|
||||
import { cn } from "@repo/shadcn-ui/lib/utils";
|
||||
|
||||
import { CustomerAdditionalConfigFields } from "./customer-additional-config-fields";
|
||||
@ -15,6 +16,7 @@ type CustomerFormProps = {
|
||||
export const CustomerEditForm = ({ formId, onSubmit, className, focusRef }: CustomerFormProps) => {
|
||||
return (
|
||||
<form id={formId} noValidate onSubmit={onSubmit}>
|
||||
<FormDebug enabled />
|
||||
<section className={cn("space-y-12 p-6", className)}>
|
||||
<CustomerBasicInfoFields focusRef={focusRef} />
|
||||
<CustomerAddressFields />
|
||||
|
||||
@ -2,6 +2,7 @@ import { DataTableColumnHeader } from "@repo/rdx-ui/components";
|
||||
import {
|
||||
Avatar,
|
||||
AvatarFallback,
|
||||
Badge,
|
||||
Button,
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@ -11,15 +12,12 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
} from "@repo/shadcn-ui/components";
|
||||
import type { ColumnDef } from "@tanstack/react-table";
|
||||
import { EyeIcon, MoreHorizontalIcon } from "lucide-react";
|
||||
import { Building2Icon, EyeIcon, MoreHorizontalIcon, UserIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { useTranslation } from "../../../../i18n";
|
||||
import { CustomerStatusBadge } from "../../../../ui";
|
||||
import type { CustomerSummaryData } from "../../../types";
|
||||
import { AddressCell, ContactCell, Initials } from "../../components";
|
||||
import { KindBadge } from "../../components/kind-badge";
|
||||
import { Soft } from "../../components/soft";
|
||||
|
||||
type GridActionHandlers = {
|
||||
onEditClick?: (customer: CustomerSummaryData) => void;
|
||||
@ -81,8 +79,48 @@ export function useCustomersGridColumns(
|
||||
const customer = row.original;
|
||||
const isCompany = String(customer.is_company).toLowerCase() === "true";
|
||||
return (
|
||||
<div className="flex items-start gap-3">
|
||||
<Avatar className="size-10 border-2 border-background shadow-sm">
|
||||
<AvatarFallback
|
||||
className={
|
||||
customer.status === "active"
|
||||
? "bg-blue-100 text-blue-700"
|
||||
: "bg-muted text-muted-foreground"
|
||||
}
|
||||
>
|
||||
<Initials name={customer.name} />
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium text-foreground">{customer.name}</span>
|
||||
{customer.trade_name && (
|
||||
<span className="text-muted-foreground">({customer.trade_name})</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<span className="font-mono">{customer.tin}</span>
|
||||
<Badge className="gap-1 px-1.5 py-0 text-xs font-normal" variant="outline">
|
||||
{customer.is_company ? (
|
||||
<>
|
||||
<Building2Icon className="size-3" />
|
||||
Empresa
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<UserIcon className="size-3" />
|
||||
Particular
|
||||
</>
|
||||
)}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
/*return (
|
||||
<div className="flex items-start gap-1 my-1.5">
|
||||
<Avatar className="size-10 hidden">
|
||||
<Avatar className="size-10">
|
||||
<AvatarFallback aria-label={customer.name}>
|
||||
<Initials name={customer.name} />
|
||||
</AvatarFallback>
|
||||
@ -99,7 +137,7 @@ export function useCustomersGridColumns(
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);*/
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@ -1,15 +1,30 @@
|
||||
import type { CustomerSummaryData } from "../../types";
|
||||
import { MapPinIcon } from "lucide-react";
|
||||
|
||||
import { Soft } from "./soft";
|
||||
import type { CustomerSummaryData } from "../../types";
|
||||
|
||||
export const AddressCell = ({ customer }: { customer: CustomerSummaryData }) => {
|
||||
const line1 = [customer.street, customer.street2].filter(Boolean).join(", ");
|
||||
const line2 = [customer.postal_code, customer.city].filter(Boolean).join(" ");
|
||||
const line3 = [customer.province, customer.country].filter(Boolean).join(", ");
|
||||
return (
|
||||
<address className="not-italic flex items-start gap-2">
|
||||
<MapPinIcon className="mt-0.5 size-3.5" />
|
||||
<div className="text-sm text-muted-foreground">
|
||||
<p>{line1}</p>
|
||||
<p>
|
||||
{line2} · {line3}
|
||||
</p>
|
||||
</div>
|
||||
</address>
|
||||
);
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
<address className="not-italic grid gap-1 text-foreground text-sm">
|
||||
<div>{line1 || <Soft>-</Soft>}</div>
|
||||
<div>{[line2, line3].filter(Boolean).join(" • ")}</div>
|
||||
</address>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
*/
|
||||
|
||||
@ -2,9 +2,42 @@ import { MailIcon, PhoneIcon } from "lucide-react";
|
||||
|
||||
import type { CustomerSummaryData } from "../../types";
|
||||
|
||||
import { Soft } from "./soft";
|
||||
|
||||
export const ContactCell = ({ customer }: { customer: CustomerSummaryData }) => (
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<a
|
||||
className="flex items-center gap-2 text-sm text-muted-foreground transition-colors hover:text-foreground"
|
||||
href={`mailto:${customer.email_primary}`}
|
||||
>
|
||||
<MailIcon className="size-3.5" />
|
||||
{customer.email_primary}
|
||||
</a>
|
||||
{customer.email_secondary && (
|
||||
<a
|
||||
className="flex items-center gap-2 text-sm text-muted-foreground transition-colors hover:text-foreground"
|
||||
href={`mailto:${customer.email_secondary}`}
|
||||
>
|
||||
<MailIcon className="size-3.5" />
|
||||
{customer.email_secondary}
|
||||
</a>
|
||||
)}
|
||||
{customer.phone_primary ? (
|
||||
<a
|
||||
className="flex items-center gap-2 text-sm text-muted-foreground transition-colors hover:text-foreground"
|
||||
href={`tel:${customer.phone_primary}`}
|
||||
>
|
||||
<PhoneIcon className="size-3.5" />
|
||||
{customer.phone_primary}
|
||||
</a>
|
||||
) : (
|
||||
<span className="flex items-center gap-2 text-sm text-muted-foreground/50">
|
||||
<PhoneIcon className="size-3.5" />-
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
/*
|
||||
|
||||
<div className="grid gap-1 text-foreground text-sm my-1.5">
|
||||
{customer.email_primary && (
|
||||
<div className="flex items-center gap-2">
|
||||
@ -31,4 +64,6 @@ export const ContactCell = ({ customer }: { customer: CustomerSummaryData }) =>
|
||||
</div>
|
||||
{false}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
*/
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
"typescript": "^5.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource-variable/geist": "^5.2.8",
|
||||
"@fontsource-variable/inter": "^5.2.8",
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"add": "^2.0.6",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
@import "@fontsource-variable/geist";
|
||||
@import "@fontsource-variable/inter";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@ -98,7 +98,7 @@
|
||||
|
||||
@layer utilities {
|
||||
body {
|
||||
font-family: "Geist Variable", ui-sans-serif, sans-serif, system-ui;
|
||||
font-family: "Inter", ui-sans-serif, sans-serif, system-ui;
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@
|
||||
**/
|
||||
|
||||
:root {
|
||||
--font-sans: "Geist Variable", ui-sans-serif, sans-serif, system-ui;
|
||||
--font-sans: "Inter", ui-sans-serif, sans-serif, system-ui;
|
||||
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
|
||||
@ -912,7 +912,7 @@ importers:
|
||||
|
||||
packages/shadcn-ui:
|
||||
dependencies:
|
||||
'@fontsource-variable/geist':
|
||||
'@fontsource-variable/inter':
|
||||
specifier: ^5.2.8
|
||||
version: 5.2.8
|
||||
'@hookform/resolvers':
|
||||
@ -1597,8 +1597,8 @@ packages:
|
||||
'@floating-ui/utils@0.2.10':
|
||||
resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
|
||||
|
||||
'@fontsource-variable/geist@5.2.8':
|
||||
resolution: {integrity: sha512-cJ6m9e+8MQ5dCYJsLylfZrgBh6KkG4bOLckB35Tr9J/EqdkEM6QllH5PxqP1dhTvFup+HtMRPuz9xOjxXJggxw==}
|
||||
'@fontsource-variable/inter@5.2.8':
|
||||
resolution: {integrity: sha512-kOfP2D+ykbcX/P3IFnokOhVRNoTozo5/JxhAIVYLpea/UBmCQ/YWPBfWIDuBImXX/15KH+eKh4xpEUyS2sQQGQ==}
|
||||
|
||||
'@hapi/hoek@9.3.0':
|
||||
resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
|
||||
@ -7635,7 +7635,7 @@ snapshots:
|
||||
|
||||
'@floating-ui/utils@0.2.10': {}
|
||||
|
||||
'@fontsource-variable/geist@5.2.8': {}
|
||||
'@fontsource-variable/inter@5.2.8': {}
|
||||
|
||||
'@hapi/hoek@9.3.0': {}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user