169 lines
5.4 KiB
TypeScript
169 lines
5.4 KiB
TypeScript
import { and, eq, isNotNull } from "drizzle-orm";
|
|
import {
|
|
assertProjectSettingsUpdateProjectCommand,
|
|
createProjectSettingsUpdateProjectCommand,
|
|
type ProjectSettingsValues,
|
|
} from "../../domain/models/project-settings-project-command.model.js";
|
|
import type {
|
|
ExecuteProjectSettingsUpdateCommandInput,
|
|
ProjectSettingsProjectCommandStore,
|
|
} from "../../domain/ports/project-settings-project-command.store.js";
|
|
import type { AppDatabase } from "../database-context.js";
|
|
import { projects } from "../schema/projects.js";
|
|
import { distributionBoards } from "../schema/distribution-boards.js";
|
|
import { executeProjectCommandTransaction } from "./project-command-transaction.persistence.js";
|
|
import { updateAllDerivedProjectVoltages } from "./project-voltage.persistence.js";
|
|
|
|
export class ProjectSettingsProjectCommandRepository
|
|
implements ProjectSettingsProjectCommandStore
|
|
{
|
|
constructor(private readonly database: AppDatabase) {}
|
|
|
|
executeUpdate(input: ExecuteProjectSettingsUpdateCommandInput) {
|
|
assertProjectSettingsUpdateProjectCommand(input.command);
|
|
return executeProjectCommandTransaction(
|
|
this.database,
|
|
input,
|
|
(tx) => this.applyCommand(tx, input)
|
|
);
|
|
}
|
|
|
|
private applyCommand(
|
|
tx: AppDatabase,
|
|
input: ExecuteProjectSettingsUpdateCommandInput
|
|
) {
|
|
const current = tx
|
|
.select()
|
|
.from(projects)
|
|
.where(eq(projects.id, input.projectId))
|
|
.get();
|
|
if (!current) {
|
|
throw new Error("Project not found.");
|
|
}
|
|
const target: ProjectSettingsValues =
|
|
input.command.schemaVersion === 1
|
|
? {
|
|
...input.command.payload,
|
|
isPublicBuilding: current.isPublicBuilding,
|
|
}
|
|
: input.command.payload;
|
|
if (projectSettingsEqual(current, target)) {
|
|
throw new Error("Project settings did not change.");
|
|
}
|
|
|
|
const inverseValues: ProjectSettingsValues = {
|
|
name: current.name,
|
|
internalProjectNumber: current.internalProjectNumber,
|
|
externalProjectNumber: current.externalProjectNumber,
|
|
buildingOwner: current.buildingOwner,
|
|
description: current.description,
|
|
isPublicBuilding: current.isPublicBuilding,
|
|
singlePhaseVoltageV: current.singlePhaseVoltageV,
|
|
threePhaseVoltageV: current.threePhaseVoltageV,
|
|
enabledDistributionBoardSupplyTypes:
|
|
current.enabledDistributionBoardSupplyTypes,
|
|
};
|
|
const inverse =
|
|
input.command.schemaVersion === 1
|
|
? createBaselineProjectSettingsUpdateCommand(inverseValues)
|
|
: createProjectSettingsUpdateProjectCommand(inverseValues);
|
|
assertDisabledSupplyTypesAreUnused(tx, input.projectId, target);
|
|
const updated = tx
|
|
.update(projects)
|
|
.set(target)
|
|
.where(eq(projects.id, input.projectId))
|
|
.run();
|
|
if (updated.changes !== 1) {
|
|
throw new Error("Project changed before settings update.");
|
|
}
|
|
updateAllDerivedProjectVoltages(tx, input.projectId);
|
|
|
|
return inverse;
|
|
}
|
|
}
|
|
|
|
function createBaselineProjectSettingsUpdateCommand(
|
|
values: ProjectSettingsValues
|
|
) {
|
|
return {
|
|
schemaVersion: 1 as const,
|
|
type: "project.update-settings" as const,
|
|
payload: {
|
|
name: values.name,
|
|
internalProjectNumber: values.internalProjectNumber,
|
|
externalProjectNumber: values.externalProjectNumber,
|
|
buildingOwner: values.buildingOwner,
|
|
description: values.description,
|
|
singlePhaseVoltageV: values.singlePhaseVoltageV,
|
|
threePhaseVoltageV: values.threePhaseVoltageV,
|
|
enabledDistributionBoardSupplyTypes:
|
|
values.enabledDistributionBoardSupplyTypes,
|
|
},
|
|
};
|
|
}
|
|
|
|
function projectSettingsEqual(
|
|
current: ProjectSettingsValues,
|
|
target: ProjectSettingsValues
|
|
) {
|
|
return (
|
|
current.name === target.name &&
|
|
current.internalProjectNumber === target.internalProjectNumber &&
|
|
current.externalProjectNumber === target.externalProjectNumber &&
|
|
current.buildingOwner === target.buildingOwner &&
|
|
current.description === target.description &&
|
|
current.isPublicBuilding === target.isPublicBuilding &&
|
|
current.singlePhaseVoltageV === target.singlePhaseVoltageV &&
|
|
current.threePhaseVoltageV === target.threePhaseVoltageV
|
|
&& sameSupplyTypes(
|
|
current.enabledDistributionBoardSupplyTypes,
|
|
target.enabledDistributionBoardSupplyTypes
|
|
)
|
|
);
|
|
}
|
|
|
|
function sameSupplyTypes(
|
|
left: ProjectSettingsValues["enabledDistributionBoardSupplyTypes"],
|
|
right: ProjectSettingsValues["enabledDistributionBoardSupplyTypes"]
|
|
) {
|
|
return (
|
|
left.length === right.length &&
|
|
left.every((value, index) => value === right[index])
|
|
);
|
|
}
|
|
|
|
function assertDisabledSupplyTypesAreUnused(
|
|
database: AppDatabase,
|
|
projectId: string,
|
|
target: ProjectSettingsValues
|
|
) {
|
|
const used = database
|
|
.select({ supplyType: distributionBoards.supplyType })
|
|
.from(distributionBoards)
|
|
.where(
|
|
and(
|
|
eq(distributionBoards.projectId, projectId),
|
|
isNotNull(distributionBoards.supplyType)
|
|
)
|
|
)
|
|
.all();
|
|
const disabledUsedTypes = [
|
|
...new Set(
|
|
used
|
|
.map((entry) => entry.supplyType)
|
|
.filter(
|
|
(supplyType) =>
|
|
supplyType !== null &&
|
|
!target.enabledDistributionBoardSupplyTypes.some(
|
|
(enabled) => enabled === supplyType
|
|
)
|
|
)
|
|
),
|
|
];
|
|
if (disabledUsedTypes.length > 0) {
|
|
throw new Error(
|
|
`Verwendete Netzarten können nicht deaktiviert werden: ${disabledUsedTypes.join(", ")}.`
|
|
);
|
|
}
|
|
}
|