metis-desk/modules/companies/src/api/application/folder-mapping-account-policy.ts
David fe6ec361b7 feat(companies): vincular identidades locales a activos y añadir cuenta de acceso a FolderMapping
- Asociar entornos locales con activos y conservar su trazabilidad.
- Registrar la cuenta de acceso del mapeo con historial de cambios.
- Calcular cobertura documental específica sin alterar la cobertura general.
- Añadir migraciones, validaciones, endpoints protegidos, pruebas y documentación.
2026-10-21 11:25:23 +02:00

114 lines
3.7 KiB
TypeScript

import { projectAccess } from "../domain/access/access-projection";
import type { FolderMappingValues } from "../domain/value-objects/folder-mapping-values.vo";
import type { FolderContext } from "./folder-mapping.repository";
import { folderAvailable, folderReferences } from "./folder-mapping-policy";
/** Una cuenta compartida requiere también asociación documental con la persona del perfil. */
export function folderAccountUsable(values: FolderMappingValues, context: FolderContext) {
const id = values.accessAccountId.getOrUndefined()?.toPrimitive();
const profile = context.profiles.find((item) => item.id.equals(values.profileId));
const account = context.items.find((item) => item.id.toPrimitive() === id)?.values.data;
if (
!profile ||
account?.kind !== "account" ||
!account.active ||
(account.expiresOn !== null && account.expiresOn < context.today)
) {
return false;
}
const catalogActive = [account.systemId, account.environmentId].every(
(key) =>
key === null ||
context.items.some(
(item) =>
item.id.toPrimitive() === key && "active" in item.values.data && item.values.data.active
)
);
return (
catalogActive &&
context.items.some((item) => {
const data = item.values.data;
return (
data.kind === "account_person" &&
data.accountId === id &&
data.personId === profile.values.personId.toPrimitive() &&
data.startDate <= context.today &&
data.endDate === null &&
(data.configuration === "pending" ||
data.configuration === "configured" ||
data.configuration === null)
);
})
);
}
/** Restringe Coverage a vías cuya relación account_person identifica exactamente la cuenta elegida. */
export function folderAccountCoverage(
values: FolderMappingValues,
context: FolderContext
): "unknown" | "covered" | "not-covered" {
const accountId = values.accessAccountId.getOrUndefined()?.toPrimitive();
if (!accountId) {
return "unknown";
}
if (!(folderAvailable(values, context) && folderAccountUsable(values, context))) {
return "not-covered";
}
const references = folderReferences(values, context);
if (!references) {
return "not-covered";
}
const accountPeople = new Set(
context.items
.filter(
(item) =>
item.values.data.kind === "account_person" &&
item.values.data.accountId === accountId &&
item.values.data.personId === references.need.personId
)
.map((item) => item.id.toPrimitive())
);
const current = context.items.filter((item) => {
const data = item.values.data;
if ("startDate" in data && data.startDate > context.today) {
return false;
}
if (data.kind !== "coverage") {
return true;
}
if (!accountPeople.has(data.accountPersonId) || data.needId !== values.needId.toPrimitive()) {
return false;
}
const grant = context.items.find((entry) => entry.id.toPrimitive() === data.grantId)?.values
.data;
if (grant?.kind !== "grant" || grant.resourceId !== references.need.resourceId) {
return false;
}
if (grant.accountId !== null) {
return grant.accountId === accountId && data.membershipId === null;
}
const membership = context.items.find((entry) => entry.id.toPrimitive() === data.membershipId)
?.values.data;
return (
membership?.kind === "membership" &&
membership.accountId === accountId &&
membership.groupId === grant.groupId
);
});
return projectAccess(current, context.people, context.today).coverage.some(
(item) => item.needId === values.needId.toPrimitive() && item.covered
)
? "covered"
: "not-covered";
}