Compare commits
2 Commits
bf1dd43c78
...
dd6d33366e
| Author | SHA1 | Date | |
|---|---|---|---|
| dd6d33366e | |||
| b2a9836436 |
@ -1 +1,2 @@
|
||||
VITE_API_SERVER_URL=http://172.31.142.243:3002/api/v1
|
||||
VITE_FORCE_INITIAL_ONBOARDING=true
|
||||
51
apps/web/src/onboarding/api/initial-onboarding.api.test.ts
Normal file
51
apps/web/src/onboarding/api/initial-onboarding.api.test.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
completeInitialOnboarding,
|
||||
getInitialOnboardingStatus,
|
||||
provisionInitialOnboarding,
|
||||
toCompleteInitialOnboardingDTO,
|
||||
} from "./initial-onboarding.api";
|
||||
|
||||
const ds = {
|
||||
custom: async (p: any) => {
|
||||
if (p.method === "get") return { status: "pending", company_id: null };
|
||||
if (p.path.endsWith("/complete")) {
|
||||
assert.equal(p.data.address.street_2, "2B");
|
||||
return { company_id: "c1" };
|
||||
}
|
||||
return { status: "pending", company_id: null };
|
||||
},
|
||||
} as any;
|
||||
test("maps onboarding status and provision responses", async () => {
|
||||
assert.deepEqual(await getInitialOnboardingStatus(ds), { status: "pending", companyId: null });
|
||||
assert.deepEqual(await provisionInitialOnboarding(ds), { status: "pending", companyId: null });
|
||||
});
|
||||
test("maps complete payload and response", async () => {
|
||||
const input: any = {
|
||||
legalName: "Acme",
|
||||
tradeName: "",
|
||||
tin: "B1",
|
||||
email: "",
|
||||
phone: "",
|
||||
website: "",
|
||||
address: {
|
||||
street: "Main",
|
||||
street2: "2B",
|
||||
city: "Madrid",
|
||||
postalCode: "28001",
|
||||
province: "Madrid",
|
||||
countryCode: "ES",
|
||||
},
|
||||
};
|
||||
assert.equal(await completeInitialOnboarding(ds, input), "c1");
|
||||
assert.deepEqual(toCompleteInitialOnboardingDTO(input).address, {
|
||||
street: "Main",
|
||||
street_2: "2B",
|
||||
city: "Madrid",
|
||||
postal_code: "28001",
|
||||
province: "Madrid",
|
||||
country_code: "ES",
|
||||
});
|
||||
});
|
||||
110
apps/web/src/onboarding/api/initial-onboarding.api.ts
Normal file
110
apps/web/src/onboarding/api/initial-onboarding.api.ts
Normal file
@ -0,0 +1,110 @@
|
||||
import type { IDataSource } from "@erp/core/client";
|
||||
|
||||
export type InitialOnboardingStatus = "not_provisioned" | "pending" | "completed";
|
||||
export interface InitialOnboardingStatusModel {
|
||||
status: InitialOnboardingStatus;
|
||||
companyId: string | null;
|
||||
}
|
||||
export interface InitialCompanyInput {
|
||||
legalName: string;
|
||||
tradeName?: string;
|
||||
tin: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
website?: string;
|
||||
address: {
|
||||
street: string;
|
||||
street2?: string;
|
||||
city: string;
|
||||
postalCode: string;
|
||||
province: string;
|
||||
countryCode: string;
|
||||
};
|
||||
}
|
||||
interface StatusDTO {
|
||||
status: InitialOnboardingStatus;
|
||||
company_id: string | null;
|
||||
}
|
||||
interface CompleteResponseDTO {
|
||||
company_id: string;
|
||||
}
|
||||
interface CompanyDTO {
|
||||
legal_name: string;
|
||||
trade_name?: string | null;
|
||||
tin: string;
|
||||
email?: string | null;
|
||||
phone?: string | null;
|
||||
website?: string | null;
|
||||
address: {
|
||||
street: string;
|
||||
street_2?: string | null;
|
||||
city: string;
|
||||
postal_code: string;
|
||||
province: string;
|
||||
country_code: string;
|
||||
};
|
||||
}
|
||||
const mapStatus = (v: StatusDTO): InitialOnboardingStatusModel => ({
|
||||
status: v.status,
|
||||
companyId: v.company_id,
|
||||
});
|
||||
export const toCompleteInitialOnboardingDTO = (v: InitialCompanyInput): CompanyDTO => ({
|
||||
legal_name: v.legalName,
|
||||
trade_name: v.tradeName || null,
|
||||
tin: v.tin,
|
||||
email: v.email || null,
|
||||
phone: v.phone || null,
|
||||
website: v.website || null,
|
||||
address: {
|
||||
street: v.address.street,
|
||||
street_2: v.address.street2 || null,
|
||||
city: v.address.city,
|
||||
postal_code: v.address.postalCode,
|
||||
province: v.address.province,
|
||||
country_code: v.address.countryCode,
|
||||
},
|
||||
});
|
||||
export const getInitialOnboardingStatus = (ds: IDataSource, signal?: AbortSignal) =>
|
||||
ds
|
||||
.custom<Record<string, never>, StatusDTO>({
|
||||
path: "onboarding/initial",
|
||||
method: "get",
|
||||
signal,
|
||||
data: {},
|
||||
})
|
||||
.then(mapStatus);
|
||||
export const provisionInitialOnboarding = (ds: IDataSource, signal?: AbortSignal) =>
|
||||
ds
|
||||
.custom<Record<string, never>, StatusDTO>({
|
||||
path: "onboarding/initial",
|
||||
method: "post",
|
||||
signal,
|
||||
data: {},
|
||||
})
|
||||
.then(mapStatus);
|
||||
export const completeInitialOnboarding = (
|
||||
ds: IDataSource,
|
||||
input: InitialCompanyInput,
|
||||
signal?: AbortSignal
|
||||
) =>
|
||||
ds
|
||||
.custom<CompanyDTO, CompleteResponseDTO>({
|
||||
path: "onboarding/initial/complete",
|
||||
method: "post",
|
||||
signal,
|
||||
data: toCompleteInitialOnboardingDTO(input),
|
||||
})
|
||||
.then((v) => v.company_id);
|
||||
|
||||
export const validateInitialCompanyInput = (value: InitialCompanyInput): string[] => {
|
||||
const errors: string[] = [];
|
||||
if (!value.legalName.trim()) errors.push("legalName");
|
||||
if (!value.tin.trim()) errors.push("tin");
|
||||
if (!value.address?.street.trim()) errors.push("address.street");
|
||||
if (!value.address?.city.trim()) errors.push("address.city");
|
||||
if (!value.address?.province.trim()) errors.push("address.province");
|
||||
if (!/^\d{5}$/.test(value.address?.postalCode ?? "")) errors.push("address.postalCode");
|
||||
if (!/^[A-Z]{2}$/.test(value.address?.countryCode ?? "")) errors.push("address.countryCode");
|
||||
if (value.email && !/^\S+@\S+\.\S+$/.test(value.email)) errors.push("email");
|
||||
return errors;
|
||||
};
|
||||
38
apps/web/src/onboarding/api/initial-onboarding.form.test.ts
Normal file
38
apps/web/src/onboarding/api/initial-onboarding.form.test.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { type InitialCompanyInput, validateInitialCompanyInput } from "./initial-onboarding.api";
|
||||
|
||||
const valid: InitialCompanyInput = {
|
||||
legalName: "Acme",
|
||||
tradeName: "",
|
||||
tin: "B1",
|
||||
email: "user@example.com",
|
||||
phone: "",
|
||||
website: "",
|
||||
address: {
|
||||
street: "Main",
|
||||
street2: "",
|
||||
city: "Madrid",
|
||||
postalCode: "28001",
|
||||
province: "Madrid",
|
||||
countryCode: "ES",
|
||||
},
|
||||
};
|
||||
for (const [name, mutate] of Object.entries({
|
||||
valid: () => valid,
|
||||
legalName: () => ({ ...valid, legalName: "" }),
|
||||
tin: () => ({ ...valid, tin: "" }),
|
||||
address: () => ({ ...valid, address: undefined as never }),
|
||||
street: () => ({ ...valid, address: { ...valid.address, street: "" } }),
|
||||
city: () => ({ ...valid, address: { ...valid.address, city: "" } }),
|
||||
province: () => ({ ...valid, address: { ...valid.address, province: "" } }),
|
||||
postalCode: () => ({ ...valid, address: { ...valid.address, postalCode: "bad" } }),
|
||||
countryCode: () => ({ ...valid, address: { ...valid.address, countryCode: "ESP" } }),
|
||||
email: () => ({ ...valid, email: "bad" }),
|
||||
}))
|
||||
test(name, () => {
|
||||
const errors = validateInitialCompanyInput(mutate());
|
||||
if (name === "valid") assert.deepEqual(errors, []);
|
||||
else assert.ok(errors.length > 0);
|
||||
});
|
||||
33
apps/web/src/onboarding/hooks/use-initial-onboarding.ts
Normal file
33
apps/web/src/onboarding/hooks/use-initial-onboarding.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { useDataSource } from "@erp/core/hooks";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import {
|
||||
type InitialCompanyInput,
|
||||
completeInitialOnboarding,
|
||||
getInitialOnboardingStatus,
|
||||
provisionInitialOnboarding,
|
||||
} from "../api/initial-onboarding.api";
|
||||
export const initialOnboardingStatusKey = ["onboarding", "initial"] as const;
|
||||
export const useInitialOnboardingStatus = () => {
|
||||
const ds = useDataSource();
|
||||
return useQuery({
|
||||
queryKey: initialOnboardingStatusKey,
|
||||
queryFn: ({ signal }) => getInitialOnboardingStatus(ds, signal),
|
||||
});
|
||||
};
|
||||
export const useProvisionInitialOnboarding = () => {
|
||||
const ds = useDataSource();
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: () => provisionInitialOnboarding(ds),
|
||||
onSuccess: (v) => qc.setQueryData(initialOnboardingStatusKey, v),
|
||||
});
|
||||
};
|
||||
export const useCompleteInitialOnboarding = () => {
|
||||
const ds = useDataSource();
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (v: InitialCompanyInput) => completeInitialOnboarding(ds, v),
|
||||
onSuccess: (v) => qc.setQueryData(initialOnboardingStatusKey, v),
|
||||
});
|
||||
};
|
||||
@ -0,0 +1,39 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { completeInitialOnboardingAndRestoreSession } from "./complete-initial-onboarding-and-restore-session";
|
||||
|
||||
test("selects created company among multiple companies", async () => {
|
||||
let selected = "";
|
||||
const r = await completeInitialOnboardingAndRestoreSession("x", {
|
||||
restoreSession: async () => ({}),
|
||||
getAvailableCompanies: () => [{ id: "a" }, { id: "x" }],
|
||||
changeActiveCompany: async (id) => {
|
||||
selected = id;
|
||||
return { id };
|
||||
},
|
||||
});
|
||||
assert.deepEqual(r, { kind: "application", companyId: "x" });
|
||||
assert.equal(selected, "x");
|
||||
});
|
||||
test("reports missing company without selecting another", async () => {
|
||||
let selected = false;
|
||||
const r = await completeInitialOnboardingAndRestoreSession("x", {
|
||||
restoreSession: async () => ({}),
|
||||
getAvailableCompanies: () => [{ id: "a" }, { id: "b" }],
|
||||
changeActiveCompany: async () => {
|
||||
selected = true;
|
||||
return null;
|
||||
},
|
||||
});
|
||||
assert.deepEqual(r, { kind: "recoverable", companyId: "x", reason: "company_missing" });
|
||||
assert.equal(selected, false);
|
||||
});
|
||||
test("reports restore failure without repeating completion", async () => {
|
||||
const r = await completeInitialOnboardingAndRestoreSession("x", {
|
||||
restoreSession: async () => null,
|
||||
getAvailableCompanies: () => [],
|
||||
changeActiveCompany: async () => null,
|
||||
});
|
||||
assert.deepEqual(r, { kind: "recoverable", companyId: "x", reason: "restore_failed" });
|
||||
});
|
||||
@ -0,0 +1,27 @@
|
||||
export interface RestoredCompany {
|
||||
id: string;
|
||||
}
|
||||
export interface OnboardingSessionActions {
|
||||
restoreSession: () => Promise<unknown>;
|
||||
getAvailableCompanies: () => RestoredCompany[];
|
||||
changeActiveCompany: (companyId: string) => Promise<RestoredCompany | null>;
|
||||
}
|
||||
export type CompletionTransition =
|
||||
| { kind: "application"; companyId: string }
|
||||
| { kind: "recoverable"; companyId: string; reason: "company_missing" | "restore_failed" };
|
||||
export async function completeInitialOnboardingAndRestoreSession(
|
||||
companyId: string,
|
||||
actions: OnboardingSessionActions
|
||||
): Promise<CompletionTransition> {
|
||||
try {
|
||||
const restored = await actions.restoreSession();
|
||||
if (restored === null) return { kind: "recoverable", companyId, reason: "restore_failed" };
|
||||
} catch {
|
||||
return { kind: "recoverable", companyId, reason: "restore_failed" };
|
||||
}
|
||||
const companies = actions.getAvailableCompanies();
|
||||
const created = companies.find((company) => company.id === companyId);
|
||||
if (!created) return { kind: "recoverable", companyId, reason: "company_missing" };
|
||||
if (companies.length > 1) await actions.changeActiveCompany(companyId);
|
||||
return { kind: "application", companyId };
|
||||
}
|
||||
115
apps/web/src/onboarding/ui/initial-onboarding-page.tsx
Normal file
115
apps/web/src/onboarding/ui/initial-onboarding-page.tsx
Normal file
@ -0,0 +1,115 @@
|
||||
import { useIdentityAuthSession } from "@erp/identity/client";
|
||||
import { LoadingOverlay } from "@repo/rdx-ui/components";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import {
|
||||
type InitialCompanyInput,
|
||||
useCompleteInitialOnboarding,
|
||||
} from "../hooks/use-initial-onboarding";
|
||||
|
||||
const empty: InitialCompanyInput = {
|
||||
legalName: "",
|
||||
tradeName: "",
|
||||
tin: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
website: "",
|
||||
address: { street: "", street2: "", city: "", postalCode: "", province: "", countryCode: "ES" },
|
||||
};
|
||||
export const InitialOnboardingPage = () => {
|
||||
const [v, setV] = useState(empty);
|
||||
const [recoverableError, setRecoverableError] = useState<string | null>(null);
|
||||
const m = useCompleteInitialOnboarding();
|
||||
const { restoreSession, availableCompanies, changeActiveCompany } = useIdentityAuthSession();
|
||||
useEffect(() => {
|
||||
if (!m.data) return;
|
||||
const found = availableCompanies.find((c) => c.id === m.data);
|
||||
if (!found) {
|
||||
if (availableCompanies.length > 0) setRecoverableError("company_missing");
|
||||
return;
|
||||
}
|
||||
if (availableCompanies.length > 1)
|
||||
changeActiveCompany(m.data).catch(() => setRecoverableError("company_selection_failed"));
|
||||
}, [m.data, availableCompanies, changeActiveCompany]);
|
||||
const set = (k: string, x: string) => setV((s) => ({ ...s, [k]: x }));
|
||||
const setA = (k: string, x: string) => setV((s) => ({ ...s, address: { ...s.address, [k]: x } }));
|
||||
if (m.isSuccess) return <LoadingOverlay />;
|
||||
return (
|
||||
<main className="mx-auto flex min-h-screen max-w-3xl flex-col justify-center gap-6 p-6">
|
||||
<div>
|
||||
<h1 className="text-3xl font-semibold">Configura tu empresa</h1>
|
||||
<p className="text-muted-foreground">Completa los datos fiscales para comenzar.</p>
|
||||
</div>
|
||||
<form
|
||||
className="grid gap-4"
|
||||
onSubmit={async (e) => {
|
||||
e.preventDefault();
|
||||
await m.mutateAsync(v);
|
||||
await restoreSession();
|
||||
}}
|
||||
>
|
||||
{[
|
||||
["legalName", "Razón social", true],
|
||||
["tradeName", "Nombre comercial", false],
|
||||
["tin", "NIF/CIF", true],
|
||||
["email", "Email", false],
|
||||
["phone", "Teléfono", false],
|
||||
["website", "Web", false],
|
||||
].map(([k, l, r]) => (
|
||||
<label className="grid gap-1" key={k as string}>
|
||||
<span>
|
||||
{l as string}
|
||||
{r ? " *" : ""}
|
||||
</span>
|
||||
<input
|
||||
className="rounded border p-2"
|
||||
onChange={(e) => set(k as string, e.target.value)}
|
||||
required={Boolean(r)}
|
||||
value={String(v[k as keyof InitialCompanyInput] ?? "")}
|
||||
/>
|
||||
</label>
|
||||
))}
|
||||
<h2 className="mt-4 text-xl font-medium">Dirección fiscal</h2>
|
||||
{[
|
||||
["street", "Calle", true],
|
||||
["street2", "Complemento", false],
|
||||
["city", "Ciudad", true],
|
||||
["postalCode", "Código postal", true],
|
||||
["province", "Provincia", true],
|
||||
["countryCode", "País", true],
|
||||
].map(([k, l, r]) => (
|
||||
<label className="grid gap-1" key={k as string}>
|
||||
<span>
|
||||
{l as string}
|
||||
{r ? " *" : ""}
|
||||
</span>
|
||||
<input
|
||||
className="rounded border p-2"
|
||||
onChange={(e) => setA(k as string, e.target.value)}
|
||||
required={Boolean(r)}
|
||||
value={String(v.address[k as keyof typeof v.address] ?? "")}
|
||||
/>
|
||||
</label>
|
||||
))}
|
||||
{recoverableError && (
|
||||
<p className="text-red-600">
|
||||
No se pudo restaurar la empresa ({recoverableError}). Puedes reintentar la restauración
|
||||
o cerrar sesión.
|
||||
</p>
|
||||
)}
|
||||
{m.isError && (
|
||||
<p className="text-red-600">
|
||||
No se pudo completar el alta. Revisa los datos e inténtalo de nuevo.
|
||||
</p>
|
||||
)}
|
||||
<button
|
||||
className="rounded bg-primary px-4 py-2 text-primary-foreground"
|
||||
disabled={m.isPending}
|
||||
type="submit"
|
||||
>
|
||||
{m.isPending ? "Guardando…" : "Crear empresa"}
|
||||
</button>
|
||||
</form>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
@ -1 +1,2 @@
|
||||
export * from "./app-routes";
|
||||
export * from "./resolve-initial-onboarding-navigation";
|
||||
|
||||
@ -1,29 +1,64 @@
|
||||
import { useIdentityAuthSession } from "@erp/identity/client";
|
||||
import { LoadingOverlay } from "@repo/rdx-ui/components";
|
||||
import type { ReactNode } from "react";
|
||||
import { type ReactNode, useEffect } from "react";
|
||||
import { Navigate, useLocation } from "react-router-dom";
|
||||
|
||||
import {
|
||||
useInitialOnboardingStatus,
|
||||
useProvisionInitialOnboarding,
|
||||
} from "@/onboarding/hooks/use-initial-onboarding";
|
||||
import { InitialOnboardingPage } from "@/onboarding/ui/initial-onboarding-page";
|
||||
|
||||
import { resolveCompanyGuardDecision } from "./resolve-company-guard-decision";
|
||||
|
||||
interface RequireCompanyRouteGuardProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const RequireCompanyRouteGuard = ({ children }: RequireCompanyRouteGuardProps) => {
|
||||
const location = useLocation();
|
||||
const { activeCompany, availableCompanies, isLoading } = useIdentityAuthSession();
|
||||
|
||||
if (isLoading) {
|
||||
const statusQuery = useInitialOnboardingStatus();
|
||||
const provision = useProvisionInitialOnboarding();
|
||||
useEffect(() => {
|
||||
if (
|
||||
availableCompanies.length === 0 &&
|
||||
statusQuery.data?.status === "not_provisioned" &&
|
||||
!provision.isPending &&
|
||||
!provision.isSuccess &&
|
||||
!provision.isError
|
||||
)
|
||||
provision.mutate();
|
||||
}, [
|
||||
availableCompanies.length,
|
||||
statusQuery.data?.status,
|
||||
provision.isPending,
|
||||
provision.isSuccess,
|
||||
provision.mutate,
|
||||
provision.isError,
|
||||
]);
|
||||
if (
|
||||
isLoading ||
|
||||
(availableCompanies.length === 0 && (statusQuery.isLoading || provision.isPending))
|
||||
)
|
||||
return <LoadingOverlay />;
|
||||
}
|
||||
|
||||
if (activeCompany) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
const navigation = statusQuery.data
|
||||
? resolveCompanyGuardDecision({
|
||||
isAuthenticated: true,
|
||||
availableCompaniesCount: availableCompanies.length,
|
||||
onboardingStatus: statusQuery.data.status,
|
||||
})
|
||||
: null;
|
||||
if (activeCompany || navigation === "application") return <>{children}</>;
|
||||
if (statusQuery.isError || provision.isError)
|
||||
return <div className="p-8">No se pudo cargar el estado de onboarding.</div>;
|
||||
if (statusQuery.data?.status === "pending" || provision.isSuccess)
|
||||
return <InitialOnboardingPage />;
|
||||
if (statusQuery.data?.status === "completed")
|
||||
return (
|
||||
<div className="p-8">
|
||||
La cuenta tiene un onboarding completado, pero no hay una empresa disponible.
|
||||
</div>
|
||||
);
|
||||
const returnTo = `${location.pathname}${location.search}`;
|
||||
|
||||
if (availableCompanies.length === 0) {
|
||||
return <Navigate replace to="/no-companies" />;
|
||||
}
|
||||
|
||||
return <Navigate replace to={`/company-selection?returnTo=${encodeURIComponent(returnTo)}`} />;
|
||||
};
|
||||
|
||||
31
apps/web/src/routes/resolve-company-guard-decision.test.ts
Normal file
31
apps/web/src/routes/resolve-company-guard-decision.test.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { resolveCompanyGuardDecision } from "./resolve-company-guard-decision";
|
||||
|
||||
const d = (n: number, s: any, a = false) =>
|
||||
resolveCompanyGuardDecision({
|
||||
isAuthenticated: true,
|
||||
availableCompaniesCount: n,
|
||||
hasActiveCompany: a,
|
||||
onboardingStatus: s,
|
||||
});
|
||||
test("companies have priority", () => {
|
||||
assert.equal(d(2, "pending"), "application");
|
||||
assert.equal(d(2, "pending"), "application");
|
||||
});
|
||||
test("zero companies route by onboarding state", () => {
|
||||
assert.equal(d(0, "pending"), "onboarding");
|
||||
assert.equal(d(0, "not_provisioned"), "provision_onboarding");
|
||||
assert.equal(d(0, "completed"), "onboarding_inconsistent");
|
||||
});
|
||||
test("unauthenticated goes to login", () =>
|
||||
assert.equal(
|
||||
resolveCompanyGuardDecision({
|
||||
isAuthenticated: false,
|
||||
availableCompaniesCount: 0,
|
||||
hasActiveCompany: false,
|
||||
onboardingStatus: "pending",
|
||||
}),
|
||||
"login"
|
||||
));
|
||||
15
apps/web/src/routes/resolve-company-guard-decision.ts
Normal file
15
apps/web/src/routes/resolve-company-guard-decision.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import {
|
||||
type InitialOnboardingNavigation,
|
||||
resolveInitialOnboardingNavigation,
|
||||
} from "./resolve-initial-onboarding-navigation";
|
||||
export const resolveCompanyGuardDecision = (input: {
|
||||
isAuthenticated: boolean;
|
||||
availableCompaniesCount: number;
|
||||
hasActiveCompany: boolean;
|
||||
onboardingStatus: "not_provisioned" | "pending" | "completed";
|
||||
}): InitialOnboardingNavigation =>
|
||||
resolveInitialOnboardingNavigation({
|
||||
isAuthenticated: input.isAuthenticated,
|
||||
availableCompaniesCount: input.availableCompaniesCount,
|
||||
onboardingStatus: input.onboardingStatus,
|
||||
});
|
||||
@ -0,0 +1,42 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { resolveInitialOnboardingNavigation } from "./resolve-initial-onboarding-navigation";
|
||||
|
||||
const cases = [
|
||||
[false, 0, "pending", "login"],
|
||||
[true, 1, "pending", "application"],
|
||||
[true, 1, "completed", "application"],
|
||||
[true, 1, "not_provisioned", "application"],
|
||||
[true, 0, "pending", "onboarding"],
|
||||
[true, 0, "not_provisioned", "provision_onboarding"],
|
||||
[true, 0, "completed", "onboarding_inconsistent"],
|
||||
[true, 2, "pending", "application"],
|
||||
] as const;
|
||||
|
||||
for (const [isAuthenticated, companies, status, expected] of cases) {
|
||||
test(`${String(isAuthenticated)} + ${companies} companies + ${status}`, () => {
|
||||
assert.equal(
|
||||
resolveInitialOnboardingNavigation({
|
||||
isAuthenticated,
|
||||
availableCompaniesCount: companies,
|
||||
onboardingStatus: status,
|
||||
}),
|
||||
expected
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
test("pending with available companies has no side effects", () => {
|
||||
const input = {
|
||||
isAuthenticated: true,
|
||||
availableCompaniesCount: 1,
|
||||
onboardingStatus: "pending" as const,
|
||||
};
|
||||
assert.equal(resolveInitialOnboardingNavigation(input), "application");
|
||||
assert.deepEqual(input, {
|
||||
isAuthenticated: true,
|
||||
availableCompaniesCount: 1,
|
||||
onboardingStatus: "pending",
|
||||
});
|
||||
});
|
||||
23
apps/web/src/routes/resolve-initial-onboarding-navigation.ts
Normal file
23
apps/web/src/routes/resolve-initial-onboarding-navigation.ts
Normal file
@ -0,0 +1,23 @@
|
||||
export type InitialOnboardingNavigation =
|
||||
| "login"
|
||||
| "application"
|
||||
| "onboarding"
|
||||
| "provision_onboarding"
|
||||
| "onboarding_inconsistent";
|
||||
|
||||
export type InitialOnboardingNavigationInput = {
|
||||
isAuthenticated: boolean;
|
||||
availableCompaniesCount: number;
|
||||
onboardingStatus: "not_provisioned" | "pending" | "completed";
|
||||
};
|
||||
|
||||
/** Decide la navegación sin leer ni modificar persistencia. */
|
||||
export function resolveInitialOnboardingNavigation(
|
||||
input: InitialOnboardingNavigationInput
|
||||
): InitialOnboardingNavigation {
|
||||
if (!input.isAuthenticated) return "login";
|
||||
if (input.availableCompaniesCount > 0) return "application";
|
||||
if (input.onboardingStatus === "pending") return "onboarding";
|
||||
if (input.onboardingStatus === "completed") return "onboarding_inconsistent";
|
||||
return "provision_onboarding";
|
||||
}
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { CreatePaymentMethodRequestDTO } from "../../../../../common";
|
||||
import type { CreatePaymentMethodUseCase } from "../../../../application";
|
||||
@ -16,11 +11,6 @@ export class CreatePaymentMethodController extends ExpressController {
|
||||
this.errorMapper = paymentMethodsApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { DeletePaymentMethodByIdUseCase } from "../../../../application";
|
||||
import { paymentMethodsApiErrorMapper } from "../payment-methods-api-error-mapper";
|
||||
@ -15,11 +10,6 @@ export class DeletePaymentMethodByIdController extends ExpressController {
|
||||
this.errorMapper = paymentMethodsApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { DisablePaymentMethodByIdUseCase } from "../../../../application";
|
||||
import { paymentMethodsApiErrorMapper } from "../payment-methods-api-error-mapper";
|
||||
@ -15,11 +10,6 @@ export class DisablePaymentMethodByIdController extends ExpressController {
|
||||
this.errorMapper = paymentMethodsApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { EnablePaymentMethodByIdUseCase } from "../../../../application";
|
||||
import { paymentMethodsApiErrorMapper } from "../payment-methods-api-error-mapper";
|
||||
@ -15,11 +10,6 @@ export class EnablePaymentMethodByIdController extends ExpressController {
|
||||
this.errorMapper = paymentMethodsApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { GetPaymentMethodByIdUseCase } from "../../../../application";
|
||||
import { paymentMethodsApiErrorMapper } from "../payment-methods-api-error-mapper";
|
||||
@ -15,11 +10,6 @@ export class GetPaymentMethodByIdController extends ExpressController {
|
||||
this.errorMapper = paymentMethodsApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
import { Criteria } from "@repo/rdx-criteria/server";
|
||||
|
||||
import type { ListPaymentMethodsUseCase } from "../../../../application";
|
||||
@ -15,11 +10,6 @@ export class ListPaymentMethodsController extends ExpressController {
|
||||
this.errorMapper = paymentMethodsApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
private getCriteriaWithDefaultOrder() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { SetDefaultPaymentMethodByIdUseCase } from "../../../../application";
|
||||
import { paymentMethodsApiErrorMapper } from "../payment-methods-api-error-mapper";
|
||||
@ -13,12 +8,6 @@ export class SetDefaultPaymentMethodByIdController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = paymentMethodsApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
import type { UpdatePaymentMethodByIdRequestDTO } from "@erp/catalogs/common";
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { UpdatePaymentMethodByIdUseCase } from "../../../../application";
|
||||
import { paymentMethodsApiErrorMapper } from "../payment-methods-api-error-mapper";
|
||||
@ -16,11 +11,6 @@ export class UpdatePaymentMethodByIdController extends ExpressController {
|
||||
this.errorMapper = paymentMethodsApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
protected async executeImpl() {
|
||||
const companyId = this.getTenantId();
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type StartParams, validateRequest } from "@erp/core/api";
|
||||
import { type StartParams, forbidQueryFieldGuard, validateRequest } from "@erp/core/api";
|
||||
import { requireIdentityTenantMembership } from "@erp/identity/api";
|
||||
import { type NextFunction, type Request, type Response, Router } from "express";
|
||||
|
||||
@ -33,6 +33,7 @@ export const paymentMethodsRouter = (params: StartParams) => {
|
||||
// ----------------------------------------------
|
||||
|
||||
router.use(...requireIdentityTenantMembership(params));
|
||||
router.use(forbidQueryFieldGuard("companyId"));
|
||||
|
||||
// ----------------------------------------------
|
||||
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
import type { CreatePaymentTermRequestDTO } from "@erp/catalogs/common";
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { CreatePaymentTermUseCase } from "../../../../application/payment-terms";
|
||||
import { paymentTermsApiErrorMapper } from "../payment-terms-api-error-mapper";
|
||||
@ -14,12 +9,6 @@ export class CreatePaymentTermController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = paymentTermsApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { DeletePaymentTermByIdUseCase } from "../../../../application/payment-terms";
|
||||
import { paymentTermsApiErrorMapper } from "../payment-terms-api-error-mapper";
|
||||
@ -13,12 +8,6 @@ export class DeletePaymentTermByIdController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = paymentTermsApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { DisablePaymentTermByIdUseCase } from "../../../../application/payment-terms";
|
||||
import { paymentTermsApiErrorMapper } from "../payment-terms-api-error-mapper";
|
||||
@ -13,12 +8,6 @@ export class DisablePaymentTermByIdController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = paymentTermsApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { EnablePaymentTermByIdUseCase } from "../../../../application/payment-terms";
|
||||
import { paymentTermsApiErrorMapper } from "../payment-terms-api-error-mapper";
|
||||
@ -13,12 +8,6 @@ export class EnablePaymentTermByIdController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = paymentTermsApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { GetPaymentTermByIdUseCase } from "../../../../application/payment-terms";
|
||||
import { paymentTermsApiErrorMapper } from "../payment-terms-api-error-mapper";
|
||||
@ -13,12 +8,6 @@ export class GetPaymentTermByIdController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = paymentTermsApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
import { Criteria } from "@repo/rdx-criteria/server";
|
||||
|
||||
import type { ListPaymentTermsUseCase } from "../../../../application/payment-terms";
|
||||
@ -14,12 +9,6 @@ export class ListPaymentTermsController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = paymentTermsApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
private getCriteriaWithDefaultOrder() {
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
import type { UpdatePaymentTermByIdRequestDTO } from "@erp/catalogs/common";
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { UpdatePaymentTermByIdUseCase } from "../../../../application/payment-terms";
|
||||
import { paymentTermsApiErrorMapper } from "../payment-terms-api-error-mapper";
|
||||
@ -14,12 +9,6 @@ export class UpdatePaymentTermByIdController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = paymentTermsApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type StartParams, validateRequest } from "@erp/core/api";
|
||||
import { type StartParams, forbidQueryFieldGuard, validateRequest } from "@erp/core/api";
|
||||
import { requireIdentityTenantMembership } from "@erp/identity/api";
|
||||
import type { NextFunction, Request, Response } from "express";
|
||||
import { Router } from "express";
|
||||
@ -30,6 +30,7 @@ export const paymentTermsRouter = (params: StartParams) => {
|
||||
const router = Router({ mergeParams: true });
|
||||
|
||||
router.use(...requireIdentityTenantMembership(params));
|
||||
router.use(forbidQueryFieldGuard("companyId"));
|
||||
|
||||
router.get(
|
||||
"/",
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
import type { CreateTaxDefinitionRequestDTO } from "@erp/catalogs/common";
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { CreateTaxDefinitionUseCase } from "../../../../application/tax-definitions";
|
||||
import { taxDefinitionsApiErrorMapper } from "../tax-definitions-api-error-mapper";
|
||||
@ -14,12 +9,6 @@ export class CreateTaxDefinitionController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = taxDefinitionsApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { DeleteTaxDefinitionByIdUseCase } from "../../../../application/tax-definitions";
|
||||
import { taxDefinitionsApiErrorMapper } from "../tax-definitions-api-error-mapper";
|
||||
@ -13,12 +8,6 @@ export class DeleteTaxDefinitionByIdController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = taxDefinitionsApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { DisableTaxDefinitionByIdUseCase } from "../../../../application/tax-definitions";
|
||||
import { taxDefinitionsApiErrorMapper } from "../tax-definitions-api-error-mapper";
|
||||
@ -13,12 +8,6 @@ export class DisableTaxDefinitionByIdController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = taxDefinitionsApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { EnableTaxDefinitionByIdUseCase } from "../../../../application/tax-definitions";
|
||||
import { taxDefinitionsApiErrorMapper } from "../tax-definitions-api-error-mapper";
|
||||
@ -13,12 +8,6 @@ export class EnableTaxDefinitionByIdController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = taxDefinitionsApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { GetTaxDefinitionByIdUseCase } from "../../../../application/tax-definitions";
|
||||
import { taxDefinitionsApiErrorMapper } from "../tax-definitions-api-error-mapper";
|
||||
@ -13,12 +8,6 @@ export class GetTaxDefinitionByIdController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = taxDefinitionsApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
import { Criteria } from "@repo/rdx-criteria/server";
|
||||
|
||||
import type { ListTaxDefinitionsUseCase } from "../../../../application/tax-definitions";
|
||||
@ -14,12 +9,6 @@ export class ListTaxDefinitionsController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = taxDefinitionsApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
private getCriteriaWithDefaultOrder() {
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
import type { UpdateTaxDefinitionByIdRequestDTO } from "@erp/catalogs/common";
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { UpdateTaxDefinitionByIdUseCase } from "../../../../application/tax-definitions";
|
||||
import { taxDefinitionsApiErrorMapper } from "../tax-definitions-api-error-mapper";
|
||||
@ -14,12 +9,6 @@ export class UpdateTaxDefinitionByIdController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = taxDefinitionsApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type StartParams, validateRequest } from "@erp/core/api";
|
||||
import { type StartParams, forbidQueryFieldGuard, validateRequest } from "@erp/core/api";
|
||||
import { requireIdentityTenantMembership } from "@erp/identity/api";
|
||||
import type { NextFunction, Request, Response } from "express";
|
||||
import { Router } from "express";
|
||||
@ -30,6 +30,7 @@ export const taxDefinitionsRouter = (params: StartParams) => {
|
||||
const router = Router({ mergeParams: true });
|
||||
|
||||
router.use(...requireIdentityTenantMembership(params));
|
||||
router.use(forbidQueryFieldGuard("companyId"));
|
||||
|
||||
router.get(
|
||||
"/",
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
import type { CreateTaxRegimeRequestDTO } from "@erp/catalogs/common";
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { CreateTaxRegimeUseCase } from "../../../../application/tax-regimes";
|
||||
import { taxRegimesApiErrorMapper } from "../tax-regime-api-error-mapper";
|
||||
@ -14,12 +9,6 @@ export class CreateTaxRegimeController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = taxRegimesApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { DeleteTaxRegimeByIdUseCase } from "../../../../application";
|
||||
import { taxRegimesApiErrorMapper } from "../tax-regime-api-error-mapper";
|
||||
@ -13,12 +8,6 @@ export class DeleteTaxRegimeByIdController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = taxRegimesApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { DisableTaxRegimeByIdUseCase } from "../../../../application";
|
||||
import { taxRegimesApiErrorMapper } from "../tax-regime-api-error-mapper";
|
||||
@ -13,12 +8,6 @@ export class DisableTaxRegimeByIdController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = taxRegimesApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { EnableTaxRegimeByIdUseCase } from "../../../../application";
|
||||
import { taxRegimesApiErrorMapper } from "../tax-regime-api-error-mapper";
|
||||
@ -13,12 +8,6 @@ export class EnableTaxRegimeByIdController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = taxRegimesApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { GetTaxRegimeByIdUseCase } from "../../../../application";
|
||||
import { taxRegimesApiErrorMapper } from "../tax-regime-api-error-mapper";
|
||||
@ -13,12 +8,6 @@ export class GetTaxRegimeByIdController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = taxRegimesApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
import { Criteria } from "@repo/rdx-criteria/server";
|
||||
|
||||
import type { ListTaxRegimesUseCase } from "../../../../application";
|
||||
@ -14,12 +9,6 @@ export class ListTaxRegimesController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = taxRegimesApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
private getCriteriaWithDefaultOrder() {
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
import type { UpdateTaxRegimeByIdRequestDTO } from "@erp/catalogs/common";
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { UpdateTaxRegimeByIdUseCase } from "../../../../application";
|
||||
import { taxRegimesApiErrorMapper } from "../tax-regime-api-error-mapper";
|
||||
@ -14,12 +9,6 @@ export class UpdateTaxRegimeByIdController extends ExpressController {
|
||||
super();
|
||||
|
||||
this.errorMapper = taxRegimesApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type StartParams, validateRequest } from "@erp/core/api";
|
||||
import { type StartParams, forbidQueryFieldGuard, validateRequest } from "@erp/core/api";
|
||||
import { requireIdentityTenantMembership } from "@erp/identity/api";
|
||||
import type { NextFunction, Request, Response } from "express";
|
||||
import { Router } from "express";
|
||||
@ -30,6 +30,7 @@ export const taxRegimesRouter = (params: StartParams) => {
|
||||
const router = Router({ mergeParams: true });
|
||||
|
||||
router.use(...requireIdentityTenantMembership(params));
|
||||
router.use(forbidQueryFieldGuard("companyId"));
|
||||
|
||||
router.get(
|
||||
"/",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ExpressController, requireAuthenticatedGuard } from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { CreateCompanyRequestDTO } from "../../../../common";
|
||||
import type { CreateCompanyUseCase } from "../../../application";
|
||||
@ -8,7 +8,6 @@ export class CreateCompanyController extends ExpressController {
|
||||
public constructor(private readonly useCase: CreateCompanyUseCase) {
|
||||
super();
|
||||
this.errorMapper = companiesApiErrorMapper;
|
||||
this.registerGuards(requireAuthenticatedGuard());
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { DisableCompanyUseCase } from "../../../application";
|
||||
import { companiesApiErrorMapper } from "../companies-api-error-mapper";
|
||||
@ -11,13 +7,12 @@ export class DisableCompanyController extends ExpressController {
|
||||
public constructor(private readonly useCase: DisableCompanyUseCase) {
|
||||
super();
|
||||
this.errorMapper = companiesApiErrorMapper;
|
||||
this.registerGuards(requireAuthenticatedGuard(), requireCompanyContextGuard());
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
const tenantId = this.getTenantId();
|
||||
const { company_id } = this.req.params;
|
||||
if (!company_id || !tenantId) {
|
||||
if (!(company_id && tenantId)) {
|
||||
return this.invalidInputError("Company ID missing");
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { EnableCompanyUseCase } from "../../../application";
|
||||
import { companiesApiErrorMapper } from "../companies-api-error-mapper";
|
||||
@ -11,13 +7,12 @@ export class EnableCompanyController extends ExpressController {
|
||||
public constructor(private readonly useCase: EnableCompanyUseCase) {
|
||||
super();
|
||||
this.errorMapper = companiesApiErrorMapper;
|
||||
this.registerGuards(requireAuthenticatedGuard(), requireCompanyContextGuard());
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
const tenantId = this.getTenantId();
|
||||
const { company_id } = this.req.params;
|
||||
if (!company_id || !tenantId) {
|
||||
if (!(company_id && tenantId)) {
|
||||
return this.invalidInputError("Company ID missing");
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ExpressController, requireAuthenticatedGuard } from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { FindAvailableCompaniesForAccountUseCase } from "../../../application";
|
||||
import { companiesApiErrorMapper } from "../companies-api-error-mapper";
|
||||
@ -7,7 +7,6 @@ export class FindAvailableCompaniesController extends ExpressController {
|
||||
public constructor(private readonly useCase: FindAvailableCompaniesForAccountUseCase) {
|
||||
super();
|
||||
this.errorMapper = companiesApiErrorMapper;
|
||||
this.registerGuards(requireAuthenticatedGuard());
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { GetCompanyByIdUseCase } from "../../../application";
|
||||
import { companiesApiErrorMapper } from "../companies-api-error-mapper";
|
||||
@ -11,13 +7,12 @@ export class GetCompanyByIdController extends ExpressController {
|
||||
public constructor(private readonly useCase: GetCompanyByIdUseCase) {
|
||||
super();
|
||||
this.errorMapper = companiesApiErrorMapper;
|
||||
this.registerGuards(requireAuthenticatedGuard(), requireCompanyContextGuard());
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
const tenantId = this.getTenantId();
|
||||
const { company_id } = this.req.params;
|
||||
if (!company_id || !tenantId) {
|
||||
if (!(company_id && tenantId)) {
|
||||
return this.invalidInputError("Company ID missing");
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { GetCurrentCompanyUseCase } from "../../../application";
|
||||
import { companiesApiErrorMapper } from "../companies-api-error-mapper";
|
||||
@ -11,7 +7,6 @@ export class GetCurrentCompanyController extends ExpressController {
|
||||
public constructor(private readonly useCase: GetCurrentCompanyUseCase) {
|
||||
super();
|
||||
this.errorMapper = companiesApiErrorMapper;
|
||||
this.registerGuards(requireAuthenticatedGuard(), requireCompanyContextGuard());
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { UpdateCompanyRequestDTO } from "../../../../common";
|
||||
import type { UpdateCompanyUseCase } from "../../../application";
|
||||
@ -12,13 +8,12 @@ export class UpdateCompanyController extends ExpressController {
|
||||
public constructor(private readonly useCase: UpdateCompanyUseCase) {
|
||||
super();
|
||||
this.errorMapper = companiesApiErrorMapper;
|
||||
this.registerGuards(requireAuthenticatedGuard(), requireCompanyContextGuard());
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
const tenantId = this.getTenantId();
|
||||
const { company_id } = this.req.params;
|
||||
if (!company_id || !tenantId) {
|
||||
if (!(company_id && tenantId)) {
|
||||
return this.invalidInputError("Company ID missing");
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { UpdateCurrentCompanyRequestDTO } from "../../../../common";
|
||||
import type { UpdateCurrentCompanyUseCase } from "../../../application";
|
||||
@ -12,7 +8,6 @@ export class UpdateCurrentCompanyController extends ExpressController {
|
||||
public constructor(private readonly useCase: UpdateCurrentCompanyUseCase) {
|
||||
super();
|
||||
this.errorMapper = companiesApiErrorMapper;
|
||||
this.registerGuards(requireAuthenticatedGuard(), requireCompanyContextGuard());
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { Criteria } from "@repo/rdx-criteria/server";
|
||||
import type { NextFunction, Request, Response } from "express";
|
||||
import type { NextFunction, Request, RequestHandler, Response } from "express";
|
||||
|
||||
import {
|
||||
type ApiError,
|
||||
@ -7,7 +7,7 @@ import {
|
||||
UnauthorizedApiError,
|
||||
ValidationApiError,
|
||||
} from "./errors";
|
||||
import type { ExpressController } from "./express-controller";
|
||||
import { ExpressController } from "./express-controller";
|
||||
|
||||
export type GuardResultLike = { isFailure: boolean; error?: ApiError };
|
||||
|
||||
@ -53,12 +53,16 @@ export function requireCompanyContextGuard(): GuardFn {
|
||||
Impide que el cliente intente fijar el 'companyId' vía query (?companyId=...).
|
||||
La política de scoping real debe hacerse en repos/servicios; esto es defensa adicional.
|
||||
*/
|
||||
export function forbidQueryFieldGuard(field = "companyId"): GuardFn {
|
||||
return ({ req }: GuardContext) => {
|
||||
if (Object.hasOwn(req.query, field)) {
|
||||
return guardFail(new ValidationApiError(`Query param "${field}" is not allowed`));
|
||||
export function forbidQueryFieldGuard(field = "companyId"): RequestHandler {
|
||||
return (req, res, next) => {
|
||||
if (Object.hasOwn(req.query ?? {}, field)) {
|
||||
return ExpressController.errorResponse(
|
||||
new ValidationApiError(`Query param "${field}" is not allowed`),
|
||||
req,
|
||||
res
|
||||
);
|
||||
}
|
||||
return guardOk();
|
||||
return next();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { type StartParams, validateRequest } from "@erp/core/api";
|
||||
import { type StartParams, forbidQueryFieldGuard, validateRequest } from "@erp/core/api";
|
||||
import { requireIdentityTenant } from "@erp/identity/api";
|
||||
import { type NextFunction, type Request, type Response, Router } from "express";
|
||||
|
||||
import { UpdateCompanyVerifactuProfileRequestSchema } from "../../../../common";
|
||||
import type { CompanyVerifactuProfileInternalDeps } from "../di";
|
||||
|
||||
import {
|
||||
GetCompanyVerifactuProfileController,
|
||||
UpdateCompanyVerifactuProfileController,
|
||||
@ -19,6 +20,8 @@ export const companyVerifactuProfileRouter = (params: StartParams) => {
|
||||
const router = Router({ mergeParams: true });
|
||||
|
||||
router.use(...requireIdentityTenant(params));
|
||||
router.use(forbidQueryFieldGuard("companyId"));
|
||||
router.use(forbidQueryFieldGuard("company_id"));
|
||||
|
||||
router.get("/", (req: Request, res: Response, next: NextFunction) => {
|
||||
const controller = new GetCompanyVerifactuProfileController(
|
||||
|
||||
@ -1,21 +1,10 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { GetCompanyVerifactuProfileUseCase } from "../../../../application";
|
||||
|
||||
export class GetCompanyVerifactuProfileController extends ExpressController {
|
||||
public constructor(private readonly useCase: GetCompanyVerifactuProfileUseCase) {
|
||||
super();
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId"),
|
||||
forbidQueryFieldGuard("company_id")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { UpdateCompanyVerifactuProfileRequestDTO } from "../../../../../common";
|
||||
import type { UpdateCompanyVerifactuProfileUseCase } from "../../../../application";
|
||||
@ -11,12 +6,6 @@ import type { UpdateCompanyVerifactuProfileUseCase } from "../../../../applicati
|
||||
export class UpdateCompanyVerifactuProfileController extends ExpressController {
|
||||
public constructor(private readonly useCase: UpdateCompanyVerifactuProfileUseCase) {
|
||||
super();
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId"),
|
||||
forbidQueryFieldGuard("company_id")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { GetIssuedInvoiceByIdUseCase } from "../../../../application/issued-invoices/index.ts";
|
||||
import { issuedInvoicesApiErrorMapper } from "../issued-invoices-api-error-mapper.ts";
|
||||
@ -14,11 +9,6 @@ export class GetIssuedInvoiceByIdController extends ExpressController {
|
||||
this.errorMapper = issuedInvoicesApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
import { Criteria } from "@repo/rdx-criteria/server";
|
||||
|
||||
import { ListIssuedInvoicesResponseSchema } from "../../../../../common/index.ts";
|
||||
@ -16,11 +11,6 @@ export class ListIssuedInvoicesController extends ExpressController {
|
||||
this.errorMapper = issuedInvoicesApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
private getCriteriaWithDefaultOrder() {
|
||||
|
||||
@ -1,10 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
type RendererFormat,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { ReportIssueInvoiceByIdQueryRequestDTO } from "../../../../../common";
|
||||
import type { ReportIssuedInvoiceUseCase } from "../../../../application/index.ts";
|
||||
@ -16,11 +10,6 @@ export class ReportIssuedInvoiceController extends ExpressController {
|
||||
this.errorMapper = issuedInvoicesApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type StartParams, validateRequest } from "@erp/core/api";
|
||||
import { type StartParams, forbidQueryFieldGuard, validateRequest } from "@erp/core/api";
|
||||
import { requireIdentityTenant } from "@erp/identity/api";
|
||||
import { type NextFunction, type Request, type Response, Router } from "express";
|
||||
|
||||
@ -17,7 +17,7 @@ import {
|
||||
} from "./controllers";
|
||||
|
||||
export const issuedInvoicesRouter = (params: StartParams) => {
|
||||
const { app, config, getService, getInternal } = params;
|
||||
const { app, config, getInternal } = params;
|
||||
|
||||
const deps = getInternal<IssuedInvoicesInternalDeps>("customer-invoices", "issuedInvoices");
|
||||
|
||||
@ -26,6 +26,7 @@ export const issuedInvoicesRouter = (params: StartParams) => {
|
||||
// ----------------------------------------------
|
||||
|
||||
router.use(...requireIdentityTenant(params));
|
||||
router.use(forbidQueryFieldGuard("companyId"));
|
||||
|
||||
// ----------------------------------------------
|
||||
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { ArchiveProformaByIdUseCase } from "../../../../application";
|
||||
import { proformasApiErrorMapper } from "../proformas-api-error-mapper";
|
||||
@ -12,11 +7,6 @@ export class ArchiveProformaByIdController extends ExpressController {
|
||||
public constructor(private readonly useCase: ArchiveProformaByIdUseCase) {
|
||||
super();
|
||||
this.errorMapper = proformasApiErrorMapper;
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { ChangeStatusProformaByIdRequestDTO } from "../../../../../common/dto";
|
||||
import type { ChangeStatusProformaUseCase } from "../../../../application";
|
||||
@ -15,11 +10,6 @@ export class ChangeStatusProformaController extends ExpressController {
|
||||
this.errorMapper = proformasApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { CreateProformaRequestDTO } from "../../../../../common/dto/index.ts";
|
||||
import type { CreateProformaUseCase } from "../../../../application/index.ts";
|
||||
@ -15,11 +10,6 @@ export class CreateProformaController extends ExpressController {
|
||||
this.errorMapper = proformasApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { DeleteProformaByIdUseCase } from "../../../../application/index.ts";
|
||||
import { proformasApiErrorMapper } from "../proformas-api-error-mapper.ts";
|
||||
@ -14,11 +9,6 @@ export class DeleteProformaController extends ExpressController {
|
||||
this.errorMapper = proformasApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,14 +1,9 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import {
|
||||
type DuplicateProformaByIdParamsRequestDTO,
|
||||
DuplicateProformaByIdParamsRequestSchema,
|
||||
DuplicateProformaByIdResponseSchema,
|
||||
type DuplicateProformaByIdParamsRequestDTO,
|
||||
} from "../../../../../common";
|
||||
import type { DuplicateProformaByIdUseCase } from "../../../../application";
|
||||
import { proformasApiErrorMapper } from "../proformas-api-error-mapper";
|
||||
@ -17,12 +12,6 @@ export class DuplicateProformaByIdController extends ExpressController {
|
||||
public constructor(private readonly useCase: DuplicateProformaByIdUseCase) {
|
||||
super();
|
||||
this.errorMapper = proformasApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { GetProformaByIdUseCase } from "../../../../application/index.ts";
|
||||
import { proformasApiErrorMapper } from "../proformas-api-error-mapper.ts";
|
||||
@ -14,11 +9,6 @@ export class GetProformaByIdController extends ExpressController {
|
||||
this.errorMapper = proformasApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import { GetProformaIssuanceReadinessResponseSchema } from "../../../../../common";
|
||||
import type { GetProformaIssuanceReadinessUseCase } from "../../../../application";
|
||||
@ -13,12 +8,6 @@ export class GetProformaIssuanceReadinessController extends ExpressController {
|
||||
public constructor(private readonly useCase: GetProformaIssuanceReadinessUseCase) {
|
||||
super();
|
||||
this.errorMapper = proformasApiErrorMapper;
|
||||
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import { IssueProformaByIdResponseSchema } from "../../../../../common";
|
||||
import type { IssueProformaUseCase } from "../../../../application/proformas";
|
||||
@ -19,11 +14,6 @@ export class IssueProformaController extends ExpressController {
|
||||
this.errorMapper = proformasApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
import { Criteria } from "@repo/rdx-criteria/server";
|
||||
|
||||
import type { ListProformasUseCase, ProformaListScope } from "../../../../application/index.ts";
|
||||
@ -31,11 +26,6 @@ export class ListProformasController extends ExpressController {
|
||||
this.errorMapper = proformasApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
private getCriteriaWithDefaultOrder() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { PreviewProformaReportUseCase } from "../../../../application";
|
||||
import { proformasApiErrorMapper } from "../proformas-api-error-mapper.ts";
|
||||
@ -12,11 +7,6 @@ export class PreviewProformaReportController extends ExpressController {
|
||||
public constructor(private readonly useCase: PreviewProformaReportUseCase) {
|
||||
super();
|
||||
this.errorMapper = proformasApiErrorMapper;
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { ReportProformaPdfUseCase } from "../../../../application";
|
||||
import { proformasApiErrorMapper } from "../proformas-api-error-mapper.ts";
|
||||
@ -12,11 +7,6 @@ export class ReportProformaPdfController extends ExpressController {
|
||||
public constructor(private readonly useCase: ReportProformaPdfUseCase) {
|
||||
super();
|
||||
this.errorMapper = proformasApiErrorMapper;
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { UnarchiveProformaByIdUseCase } from "../../../../application";
|
||||
import { proformasApiErrorMapper } from "../proformas-api-error-mapper";
|
||||
@ -12,11 +7,6 @@ export class UnarchiveProformaByIdController extends ExpressController {
|
||||
public constructor(private readonly useCase: UnarchiveProformaByIdUseCase) {
|
||||
super();
|
||||
this.errorMapper = proformasApiErrorMapper;
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { UpdateProformaByIdRequestDTO } from "../../../../../common/dto";
|
||||
import type { UpdateProformaByIdUseCase } from "../../../../application";
|
||||
@ -15,11 +10,6 @@ export class UpdateProformaController extends ExpressController {
|
||||
this.errorMapper = proformasApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type StartParams, validateRequest } from "@erp/core/api";
|
||||
import { type StartParams, forbidQueryFieldGuard, validateRequest } from "@erp/core/api";
|
||||
import { requireIdentityTenant } from "@erp/identity/api";
|
||||
import { type NextFunction, type Request, type Response, Router } from "express";
|
||||
|
||||
@ -20,6 +20,7 @@ import {
|
||||
ChangeStatusProformaByIdParamsRequestSchema,
|
||||
ChangeStatusProformaByIdRequestSchema,
|
||||
CreateProformaRequestSchema,
|
||||
CreateRecurringProformaFromProformaRequestSchema,
|
||||
DeleteProformaByIdParamsRequestSchema,
|
||||
DuplicateProformaByIdParamsRequestSchema,
|
||||
GetProformaByIdRequestSchema,
|
||||
@ -32,7 +33,6 @@ import {
|
||||
UnarchiveProformaByIdParamsRequestSchema,
|
||||
UpdateProformaByIdParamsRequestSchema,
|
||||
UpdateProformaByIdRequestSchema,
|
||||
CreateRecurringProformaFromProformaRequestSchema,
|
||||
} from "../../../../common";
|
||||
import type { IIssuedInvoicePublicServices } from "../../../application";
|
||||
import { CreateRecurringProformaFromProformaController } from "../../recurring-proformas/express/controllers";
|
||||
@ -59,6 +59,7 @@ export const proformasRouter = (params: StartParams) => {
|
||||
// ----------------------------------------------
|
||||
|
||||
router.use(...requireIdentityTenant(params));
|
||||
router.use(forbidQueryFieldGuard("companyId"));
|
||||
|
||||
// ----------------------------------------------
|
||||
|
||||
@ -181,6 +182,7 @@ export const proformasRouter = (params: StartParams) => {
|
||||
router.post(
|
||||
"/:proforma_id/recurring-proforma",
|
||||
validateRequest(GetProformaByIdRequestSchema, "params"),
|
||||
forbidQueryFieldGuard("company_id"),
|
||||
validateRequest(CreateRecurringProformaFromProformaRequestSchema, "body"),
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const controller = new CreateRecurringProformaFromProformaController(
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
import { UniqueID } from "@repo/rdx-ddd";
|
||||
|
||||
import type { CreateRecurringProformaFromProformaRequestDTO } from "../../../../../common";
|
||||
@ -12,17 +7,9 @@ import { proformasApiErrorMapper } from "../../../proformas/express/proformas-ap
|
||||
import { parseDateOnly } from "../mappers/parse-date-only";
|
||||
|
||||
export class CreateRecurringProformaFromProformaController extends ExpressController {
|
||||
public constructor(
|
||||
private readonly fromProforma: RecurringProformaCommands["fromProforma"]
|
||||
) {
|
||||
public constructor(private readonly fromProforma: RecurringProformaCommands["fromProforma"]) {
|
||||
super();
|
||||
this.errorMapper = proformasApiErrorMapper;
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId"),
|
||||
forbidQueryFieldGuard("company_id")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
@ -37,8 +24,7 @@ export class CreateRecurringProformaFromProformaController extends ExpressContro
|
||||
if (startDate.isFailure) return this.invalidInputError(startDate.error.message);
|
||||
|
||||
const endDate = body.schedule.endDate;
|
||||
const parsedEndDate =
|
||||
typeof endDate === "string" ? parseDateOnly(endDate) : undefined;
|
||||
const parsedEndDate = typeof endDate === "string" ? parseDateOnly(endDate) : undefined;
|
||||
if (parsedEndDate?.isFailure) {
|
||||
return this.invalidInputError(parsedEndDate.error.message);
|
||||
}
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { CreateCustomerRequestDTO } from "../../../../common/dto";
|
||||
import type { CreateCustomerUseCase } from "../../../application";
|
||||
@ -13,13 +8,6 @@ export class CreateCustomerController extends ExpressController {
|
||||
public constructor(private readonly useCase: CreateCustomerUseCase) {
|
||||
super();
|
||||
this.errorMapper = customersApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { DeleteCustomerUseCase } from "../../../application";
|
||||
import { customersApiErrorMapper } from "../customer-api-error-mapper";
|
||||
@ -12,13 +7,6 @@ export class DeleteCustomerController extends ExpressController {
|
||||
public constructor(private readonly useCase: DeleteCustomerUseCase) {
|
||||
super();
|
||||
this.errorMapper = customersApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
async executeImpl(): Promise<any> {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
import type { GetCustomerByIdUseCase } from "@erp/customers/api/application";
|
||||
|
||||
import { customersApiErrorMapper } from "../customer-api-error-mapper";
|
||||
@ -12,13 +7,6 @@ export class GetCustomerController extends ExpressController {
|
||||
public constructor(private readonly useCase: GetCustomerByIdUseCase) {
|
||||
super();
|
||||
this.errorMapper = customersApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
import { Criteria } from "@repo/rdx-criteria/server";
|
||||
|
||||
import type { ListCustomersUseCase } from "../../../application";
|
||||
@ -13,13 +8,6 @@ export class ListCustomersController extends ExpressController {
|
||||
public constructor(private readonly useCase: ListCustomersUseCase) {
|
||||
super();
|
||||
this.errorMapper = customersApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
private getCriteriaWithDefaultOrder() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { UpdateCustomerByIdRequestDTO } from "../../../../common/dto";
|
||||
import type { UpdateCustomerUseCase } from "../../../application";
|
||||
@ -13,13 +8,6 @@ export class UpdateCustomerController extends ExpressController {
|
||||
public constructor(private readonly useCase: UpdateCustomerUseCase) {
|
||||
super();
|
||||
this.errorMapper = customersApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type StartParams, validateRequest } from "@erp/core/api";
|
||||
import { type StartParams, forbidQueryFieldGuard, validateRequest } from "@erp/core/api";
|
||||
import { requireIdentityTenant } from "@erp/identity/api";
|
||||
import { type NextFunction, type Request, type Response, Router } from "express";
|
||||
|
||||
@ -30,6 +30,7 @@ export const customersRouter = (params: StartParams) => {
|
||||
|
||||
// 🔐 Autenticación + Tenancy para TODO el router
|
||||
router.use(...requireIdentityTenant(params));
|
||||
router.use(forbidQueryFieldGuard("companyId"));
|
||||
|
||||
// ----------------------------------------------
|
||||
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { buildTransactionManager } from "@erp/core/api";
|
||||
import { ExpressController, type buildTransactionManager } from "@erp/core/api";
|
||||
|
||||
import type { AssignNextDocumentNumberRequestDTO } from "../../../../common";
|
||||
import type { AssignNextDocumentNumberUseCase } from "../../../application";
|
||||
@ -16,15 +11,11 @@ export class AssignNextDocumentNumberController extends ExpressController {
|
||||
) {
|
||||
super();
|
||||
this.errorMapper = documentSeriesApiErrorMapper;
|
||||
this.registerGuards(requireAuthenticatedGuard(), requireCompanyContextGuard());
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
const companyId = this.getTenantId();
|
||||
if (!companyId) {
|
||||
return this.forbiddenError("Tenant ID not found");
|
||||
}
|
||||
|
||||
if (!companyId) return this.forbiddenError("Tenant ID not found");
|
||||
const result = await this.transactionManager.complete((transaction: unknown) =>
|
||||
this.useCase.execute({
|
||||
companyId: companyId.toString(),
|
||||
@ -32,7 +23,6 @@ export class AssignNextDocumentNumberController extends ExpressController {
|
||||
transaction,
|
||||
})
|
||||
);
|
||||
|
||||
return result.match(
|
||||
(data) => this.ok(data),
|
||||
(error) => this.handleError(error)
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { CreateDocumentSeriesRequestDTO } from "../../../../common";
|
||||
import type { CreateDocumentSeriesUseCase } from "../../../application";
|
||||
@ -13,12 +8,6 @@ export class CreateDocumentSeriesController extends ExpressController {
|
||||
public constructor(private readonly useCase: CreateDocumentSeriesUseCase) {
|
||||
super();
|
||||
this.errorMapper = documentSeriesApiErrorMapper;
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId"),
|
||||
forbidQueryFieldGuard("company_id")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { DisableDocumentSeriesUseCase } from "../../../application";
|
||||
import { documentSeriesApiErrorMapper } from "../document-series-api-error-mapper";
|
||||
@ -12,12 +7,6 @@ export class DisableDocumentSeriesController extends ExpressController {
|
||||
public constructor(private readonly useCase: DisableDocumentSeriesUseCase) {
|
||||
super();
|
||||
this.errorMapper = documentSeriesApiErrorMapper;
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId"),
|
||||
forbidQueryFieldGuard("company_id")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { GetDocumentSeriesByIdUseCase } from "../../../application";
|
||||
import { documentSeriesApiErrorMapper } from "../document-series-api-error-mapper";
|
||||
@ -12,12 +7,6 @@ export class GetDocumentSeriesByIdController extends ExpressController {
|
||||
public constructor(private readonly useCase: GetDocumentSeriesByIdUseCase) {
|
||||
super();
|
||||
this.errorMapper = documentSeriesApiErrorMapper;
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId"),
|
||||
forbidQueryFieldGuard("company_id")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
import { Criteria } from "@repo/rdx-criteria/server";
|
||||
|
||||
import type { ListDocumentSeriesUseCase } from "../../../application";
|
||||
@ -13,12 +8,6 @@ export class ListDocumentSeriesController extends ExpressController {
|
||||
public constructor(private readonly useCase: ListDocumentSeriesUseCase) {
|
||||
super();
|
||||
this.errorMapper = documentSeriesApiErrorMapper;
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId"),
|
||||
forbidQueryFieldGuard("company_id")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
@ -28,7 +17,14 @@ export class ListDocumentSeriesController extends ExpressController {
|
||||
}
|
||||
|
||||
const { filters, pageNumber, pageSize, quickSearch } = this.criteria;
|
||||
const criteria = Criteria.fromPrimitives(filters.toPrimitives(), "code", "ASC", pageSize, pageNumber, quickSearch);
|
||||
const criteria = Criteria.fromPrimitives(
|
||||
filters.toPrimitives(),
|
||||
"code",
|
||||
"ASC",
|
||||
pageSize,
|
||||
pageNumber,
|
||||
quickSearch
|
||||
);
|
||||
|
||||
const result = await this.useCase.execute({
|
||||
companyId: companyId.toString(),
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
ExpressController,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { UpdateDocumentSeriesRequestDTO } from "../../../../common";
|
||||
import type { UpdateDocumentSeriesUseCase } from "../../../application";
|
||||
@ -13,12 +8,6 @@ export class UpdateDocumentSeriesController extends ExpressController {
|
||||
public constructor(private readonly useCase: UpdateDocumentSeriesUseCase) {
|
||||
super();
|
||||
this.errorMapper = documentSeriesApiErrorMapper;
|
||||
this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId"),
|
||||
forbidQueryFieldGuard("company_id")
|
||||
);
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,4 +1,9 @@
|
||||
import { type StartParams, buildTransactionManager, validateRequest } from "@erp/core/api";
|
||||
import {
|
||||
type StartParams,
|
||||
buildTransactionManager,
|
||||
forbidQueryFieldGuard,
|
||||
validateRequest,
|
||||
} from "@erp/core/api";
|
||||
import { requireIdentityTenant } from "@erp/identity/api";
|
||||
import { type NextFunction, type Request, type Response, Router } from "express";
|
||||
|
||||
@ -28,13 +33,20 @@ export const documentSeriesRouter = (params: StartParams) => {
|
||||
|
||||
router.use(...requireIdentityTenant(params));
|
||||
|
||||
router.get("/", (req: Request, res: Response, next: NextFunction) => {
|
||||
const controller = new ListDocumentSeriesController(deps.useCases.listDocumentSeries());
|
||||
return controller.execute(req, res, next);
|
||||
});
|
||||
router.get(
|
||||
"/",
|
||||
forbidQueryFieldGuard("companyId"),
|
||||
forbidQueryFieldGuard("company_id"),
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const controller = new ListDocumentSeriesController(deps.useCases.listDocumentSeries());
|
||||
return controller.execute(req, res, next);
|
||||
}
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/:id",
|
||||
forbidQueryFieldGuard("companyId"),
|
||||
forbidQueryFieldGuard("company_id"),
|
||||
validateRequest(GetDocumentSeriesByIdRequestSchema, "params"),
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const controller = new GetDocumentSeriesByIdController(deps.useCases.getDocumentSeriesById());
|
||||
@ -44,6 +56,8 @@ export const documentSeriesRouter = (params: StartParams) => {
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
forbidQueryFieldGuard("companyId"),
|
||||
forbidQueryFieldGuard("company_id"),
|
||||
validateRequest(CreateDocumentSeriesRequestSchema, "body"),
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const controller = new CreateDocumentSeriesController(deps.useCases.createDocumentSeries());
|
||||
|
||||
@ -9,13 +9,6 @@ export class CreateProformaFromFactugesController extends ExpressController {
|
||||
public constructor(private readonly useCase: CreateProformaFromFactugesUseCase) {
|
||||
super();
|
||||
this.errorMapper = factugesApiErrorMapper;
|
||||
|
||||
// 🔐 Reutiliza guards de auth/tenant y prohíbe 'companyId' en query
|
||||
/*this.registerGuards(
|
||||
requireAuthenticatedGuard(),
|
||||
requireCompanyContextGuard(),
|
||||
forbidQueryFieldGuard("companyId")
|
||||
);*/
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import { ExpressController, requireAuthenticatedGuard } from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
import type { UtcDateTime } from "@repo/rdx-ddd";
|
||||
|
||||
import type { ChangeAccountPasswordRequestDTO, ChangeAccountPasswordResponseDTO } from "../../../../common";
|
||||
import type {
|
||||
ChangeAccountPasswordRequestDTO,
|
||||
ChangeAccountPasswordResponseDTO,
|
||||
} from "../../../../common";
|
||||
import type { ChangeCurrentAccountPasswordUseCase } from "../../../application/accounts";
|
||||
import { identityAccountApiErrorMapper } from "../identity-account-api-error-mapper";
|
||||
|
||||
@ -12,7 +15,6 @@ export class ChangeCurrentAccountPasswordController extends ExpressController {
|
||||
) {
|
||||
super();
|
||||
this.errorMapper = identityAccountApiErrorMapper;
|
||||
this.registerGuards(requireAuthenticatedGuard());
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ExpressController, requireAuthenticatedGuard } from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { GetAccountProfileResponseDTO } from "../../../../common";
|
||||
import type { GetCurrentAccountUseCase } from "../../../application/accounts";
|
||||
@ -8,7 +8,6 @@ export class GetCurrentAccountController extends ExpressController {
|
||||
public constructor(private readonly useCase: GetCurrentAccountUseCase) {
|
||||
super();
|
||||
this.errorMapper = identityAccountApiErrorMapper;
|
||||
this.registerGuards(requireAuthenticatedGuard());
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ExpressController, requireAuthenticatedGuard } from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
|
||||
import type { CurrentSessionResponseDTO } from "../../../../common";
|
||||
import type { GetCurrentSessionUseCase } from "../../../application";
|
||||
@ -8,7 +8,6 @@ export class GetCurrentSessionController extends ExpressController {
|
||||
public constructor(private readonly useCase: GetCurrentSessionUseCase) {
|
||||
super();
|
||||
this.errorMapper = identityAuthApiErrorMapper;
|
||||
this.registerGuards(requireAuthenticatedGuard());
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ExpressController, requireAuthenticatedGuard } from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
import type { UtcDateTime } from "@repo/rdx-ddd";
|
||||
|
||||
import type { LogoutRequestDTO, LogoutResponseDTO } from "../../../../common";
|
||||
@ -12,7 +12,6 @@ export class LogoutController extends ExpressController {
|
||||
) {
|
||||
super();
|
||||
this.errorMapper = identityAuthApiErrorMapper;
|
||||
this.registerGuards(requireAuthenticatedGuard());
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import { ExpressController, requireAuthenticatedGuard } from "@erp/core/api";
|
||||
import { ExpressController } from "@erp/core/api";
|
||||
import type { UtcDateTime } from "@repo/rdx-ddd";
|
||||
|
||||
import type { UpdateAccountProfileRequestDTO, UpdateAccountProfileResponseDTO } from "../../../../common";
|
||||
import type {
|
||||
UpdateAccountProfileRequestDTO,
|
||||
UpdateAccountProfileResponseDTO,
|
||||
} from "../../../../common";
|
||||
import type { UpdateCurrentAccountUseCase } from "../../../application/accounts";
|
||||
import { identityAccountApiErrorMapper } from "../identity-account-api-error-mapper";
|
||||
|
||||
@ -12,7 +15,6 @@ export class UpdateCurrentAccountController extends ExpressController {
|
||||
) {
|
||||
super();
|
||||
this.errorMapper = identityAccountApiErrorMapper;
|
||||
this.registerGuards(requireAuthenticatedGuard());
|
||||
}
|
||||
|
||||
protected async executeImpl() {
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
import { ApplicationError } from "@repo/rdx-ddd";
|
||||
|
||||
/** La transición requiere un onboarding provisionado previamente para la cuenta. */
|
||||
export class InitialOnboardingNotProvisionedError extends ApplicationError {
|
||||
constructor() {
|
||||
super("Initial onboarding has not been provisioned.", "initial_onboarding_not_provisioned");
|
||||
this.name = "InitialOnboardingNotProvisionedError";
|
||||
}
|
||||
}
|
||||
@ -1 +1,5 @@
|
||||
export type IOnboardingPublicServices = Record<never, never>;
|
||||
import type { InitialOnboardingProvisioner } from "./initial-onboarding-provisioner";
|
||||
|
||||
export interface IOnboardingPublicServices {
|
||||
provisioner: InitialOnboardingProvisioner;
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import { UniqueID, UtcDateTime } from "@repo/rdx-ddd";
|
||||
import { Maybe, Result } from "@repo/rdx-utils";
|
||||
|
||||
import { InitialOnboarding } from "../../domain";
|
||||
import { InitialOnboardingNotProvisionedError } from "../errors/initial-onboarding-not-provisioned-error";
|
||||
import { CompleteInitialOnboardingInputMapper } from "../mappers/complete-initial-onboarding-input.mapper";
|
||||
|
||||
import { CompleteInitialOnboardingUseCase } from "./complete-initial-onboarding.use-case";
|
||||
@ -137,7 +138,10 @@ test("completed existente ignora payload nuevo", async () => {
|
||||
});
|
||||
test("ausente falla sin provisionar", async () => {
|
||||
const s = setup(undefined, true);
|
||||
assert.equal((await s.useCase.execute(input)).isFailure, true);
|
||||
const result = await s.useCase.execute(input);
|
||||
assert.equal(result.isFailure, true);
|
||||
assert.ok(result.error instanceof InitialOnboardingNotProvisionedError);
|
||||
assert.equal(result.error.code, "initial_onboarding_not_provisioned");
|
||||
assert.deepEqual(s.events, ["begin", "lock", "rollback"]);
|
||||
});
|
||||
for (const failure of ["read", "company", "admin", "save", "throw", "commit", "begin"]) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user