219 lines
6.3 KiB
TypeScript
219 lines
6.3 KiB
TypeScript
import type { UniqueID } from "@repo/rdx-ddd";
|
|
import { DomainEntity } from "@repo/rdx-ddd";
|
|
import { Maybe, Result } from "@repo/rdx-utils";
|
|
|
|
import { ResourceCatalogError } from "./resource-definition";
|
|
import { TemplateSection } from "./template-section.entity";
|
|
import { ResourceDefinitionValues } from "./value-objects/resource-definition-values.vo";
|
|
|
|
export class TemplateField extends DomainEntity<{ fieldId: UniqueID; required: boolean }> {
|
|
static create(fieldId: UniqueID, required: boolean, id?: UniqueID) {
|
|
return new TemplateField({ fieldId, required }, id);
|
|
}
|
|
|
|
static rehydrate(fieldId: UniqueID, required: boolean, id: UniqueID) {
|
|
return new TemplateField({ fieldId, required }, id);
|
|
}
|
|
|
|
get fieldId() {
|
|
return this.props.fieldId;
|
|
}
|
|
|
|
get required() {
|
|
return this.props.required;
|
|
}
|
|
}
|
|
|
|
type DefinitionProps = {
|
|
companyId: Maybe<UniqueID>;
|
|
stockKey: Maybe<string>;
|
|
values: ResourceDefinitionValues;
|
|
active: boolean;
|
|
bindings: readonly TemplateField[];
|
|
};
|
|
|
|
export class ResourceDefinition extends DomainEntity<DefinitionProps> {
|
|
private constructor(props: DefinitionProps, id?: UniqueID) {
|
|
super({ ...props, bindings: Object.freeze([...props.bindings]) }, id);
|
|
}
|
|
|
|
static create(
|
|
companyId: Maybe<UniqueID>,
|
|
values: ResourceDefinitionValues,
|
|
active: boolean,
|
|
stockKey = Maybe.none<string>()
|
|
) {
|
|
const bindings = values.fieldIds.map((id, index) =>
|
|
TemplateField.create(
|
|
id,
|
|
values.data.kind === "template" && (values.data.fields[index]?.required ?? false)
|
|
)
|
|
);
|
|
|
|
return new ResourceDefinition({ companyId, stockKey, values, active, bindings });
|
|
}
|
|
|
|
static rehydrate(props: DefinitionProps, id: UniqueID) {
|
|
return new ResourceDefinition(props, id);
|
|
}
|
|
|
|
get companyId() {
|
|
return this.props.companyId;
|
|
}
|
|
|
|
get stockKey() {
|
|
return this.props.stockKey;
|
|
}
|
|
|
|
get values() {
|
|
return this.props.values;
|
|
}
|
|
|
|
get active() {
|
|
return this.props.active;
|
|
}
|
|
|
|
get sections() {
|
|
return this.values.data.kind === "template"
|
|
? (this.values.data.sections ?? []).map((section) => TemplateSection.rehydrate(section))
|
|
: [];
|
|
}
|
|
|
|
get bindings() {
|
|
return this.props.bindings;
|
|
}
|
|
|
|
update(values: ResourceDefinitionValues): Result<ResourceDefinition, ResourceCatalogError> {
|
|
const previous = this.values.data;
|
|
const next = values.data;
|
|
|
|
if (
|
|
previous.kind !== next.kind ||
|
|
(previous.kind === "template" && next.kind === "template" && previous.usage !== next.usage) ||
|
|
(previous.kind === "field" &&
|
|
next.kind === "field" &&
|
|
(previous.type !== next.type ||
|
|
previous.choices.some((choice) => !next.choices.includes(choice))))
|
|
) {
|
|
return Result.fail(new ResourceCatalogError("FIELD_TYPE_FIXED"));
|
|
}
|
|
if (this.values.equals(values)) {
|
|
return Result.ok(this);
|
|
}
|
|
const bindings = values.fieldIds.map((fieldId, index) =>
|
|
TemplateField.create(
|
|
fieldId,
|
|
next.kind === "template" && (next.fields[index]?.required ?? false),
|
|
this.bindings.find((field) => field.fieldId.equals(fieldId))?.id
|
|
)
|
|
);
|
|
|
|
return Result.ok(new ResourceDefinition({ ...this.props, values, bindings }, this.id));
|
|
}
|
|
|
|
/** Ampliación idempotente de presentación de serie; conserva datos y disponibilidad. */
|
|
initializeSections(source: ResourceDefinitionValues) {
|
|
const previous = this.values.data;
|
|
const next = source.data;
|
|
|
|
if (
|
|
!this.stockKey.getOrUndefined() ||
|
|
previous.kind !== "template" ||
|
|
next.kind !== "template" ||
|
|
previous.sections !== undefined ||
|
|
!next.sections
|
|
) {
|
|
return this;
|
|
}
|
|
// Solo asociar secciones a campos ya presentes, sin cambiar orden ni obligatoriedad.
|
|
const known = new Set(previous.fields.map((field) => field.fieldId));
|
|
const sections = next.sections.filter((section) =>
|
|
next.fields.some((field) => field.sectionId === section.id && known.has(field.fieldId))
|
|
);
|
|
const sectionIds = new Set(sections.map((section) => section.id));
|
|
const fields = previous.fields.map((field) => {
|
|
const sectionId = next.fields.find((entry) => entry.fieldId === field.fieldId)?.sectionId;
|
|
|
|
return { ...field, ...(sectionId && sectionIds.has(sectionId) ? { sectionId } : {}) };
|
|
});
|
|
const values = ResourceDefinitionValues.create({ ...previous, sections, fields });
|
|
|
|
if (!values.isSuccess) {
|
|
throw new Error("Secciones iniciales de plantilla inconsistentes.");
|
|
}
|
|
|
|
return new ResourceDefinition({ ...this.props, values: values.data }, this.id);
|
|
}
|
|
|
|
/** Completa la ayuda de un campo de serie solo cuando todavía no fue definida. */
|
|
initializeDescription(source: ResourceDefinitionValues) {
|
|
const previous = this.values.data;
|
|
const next = source.data;
|
|
|
|
if (
|
|
!this.stockKey.getOrUndefined() ||
|
|
previous.kind !== "field" ||
|
|
next.kind !== "field" ||
|
|
!next.description ||
|
|
(previous.description && !next.description.startsWith(`${previous.description} Ejemplo:`))
|
|
) {
|
|
return this;
|
|
}
|
|
const values = ResourceDefinitionValues.create({ ...previous, description: next.description });
|
|
|
|
if (!values.isSuccess) {
|
|
throw new Error("Texto de ayuda inicial de recurso inconsistente.");
|
|
}
|
|
|
|
return new ResourceDefinition({ ...this.props, values: values.data }, this.id);
|
|
}
|
|
|
|
setActive(active: boolean) {
|
|
return active === this.active
|
|
? this
|
|
: new ResourceDefinition({ ...this.props, active }, this.id);
|
|
}
|
|
}
|
|
|
|
export class CatalogAvailability extends DomainEntity<{
|
|
companyId: UniqueID;
|
|
definitionId: UniqueID;
|
|
active: Maybe<boolean>;
|
|
}> {
|
|
static create(
|
|
companyId: UniqueID,
|
|
definitionId: UniqueID,
|
|
active: Maybe<boolean>,
|
|
id?: UniqueID
|
|
) {
|
|
return new CatalogAvailability({ companyId, definitionId, active }, id);
|
|
}
|
|
|
|
static rehydrate(
|
|
companyId: UniqueID,
|
|
definitionId: UniqueID,
|
|
active: Maybe<boolean>,
|
|
id: UniqueID
|
|
) {
|
|
return new CatalogAvailability({ companyId, definitionId, active }, id);
|
|
}
|
|
|
|
get companyId() {
|
|
return this.props.companyId;
|
|
}
|
|
|
|
get definitionId() {
|
|
return this.props.definitionId;
|
|
}
|
|
|
|
get active() {
|
|
return this.props.active;
|
|
}
|
|
|
|
update(active: Maybe<boolean>) {
|
|
return active.getOrUndefined() === this.active.getOrUndefined()
|
|
? this
|
|
: CatalogAvailability.create(this.companyId, this.definitionId, active, this.id);
|
|
}
|
|
}
|