Remove project device mirror fields
This commit is contained in:
@@ -16,8 +16,9 @@ and requires both `PRAGMA integrity_check` and `PRAGMA foreign_key_check` to pas
|
||||
Database backups are operational recovery files and remain separate from the future
|
||||
user-visible project version history.
|
||||
|
||||
2. Apply pending schema migrations (includes `0008_circuit_first_model` and the additive
|
||||
`0009_project_device_circuit_fields` transition)
|
||||
2. Apply pending schema migrations (includes `0008_circuit_first_model`, the additive
|
||||
`0009_project_device_circuit_fields` transition and the post-cutover
|
||||
`0011_project_device_canonical_fields` cleanup)
|
||||
|
||||
```bash
|
||||
npm run db:migrate
|
||||
|
||||
@@ -344,6 +344,7 @@ Implemented foundation:
|
||||
- circuit and device-row persistence value mapping is separated from the general repositories
|
||||
- the legacy consumer UI and application read/write endpoints are removed after verified data cutover
|
||||
- retained legacy rows are accessible only through explicit database upgrade tooling
|
||||
- project devices no longer persist duplicate legacy power, phase, cosPhi or remark fields
|
||||
|
||||
## Phase 12: Project Revisions and Persistent Undo / Redo
|
||||
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@
|
||||
"build:api": "tsc -p tsconfig.json",
|
||||
"build:web": "next build",
|
||||
"start": "node dist/server/index.js",
|
||||
"test": "tsx --test tests/project-device-schema.test.ts tests/project-device-placement.service.test.ts tests/project-device-sync.service.test.ts tests/legacy-consumer-migration-planner.test.ts tests/legacy-consumer-migration.repository.test.ts tests/circuit-numbering.service.test.ts tests/circuit-write.rules.test.ts tests/circuit-power-calculation.test.ts tests/circuit-tree.controller.test.ts tests/circuit-grid-insertion.test.ts tests/circuit-grid-safety.test.ts tests/circuit-grid-model.test.ts tests/circuit-grid-projection.test.ts tests/distribution-board.repository.test.ts tests/circuit-device-row-transaction.repository.test.ts tests/project-device-row-sync.repository.test.ts tests/circuit-section-transaction.repository.test.ts tests/database-backup.test.ts",
|
||||
"test:watch": "tsx --watch --test tests/project-device-schema.test.ts tests/project-device-placement.service.test.ts tests/project-device-sync.service.test.ts tests/legacy-consumer-migration-planner.test.ts tests/legacy-consumer-migration.repository.test.ts tests/circuit-numbering.service.test.ts tests/circuit-write.rules.test.ts tests/circuit-power-calculation.test.ts tests/circuit-tree.controller.test.ts tests/circuit-grid-insertion.test.ts tests/circuit-grid-safety.test.ts tests/circuit-grid-model.test.ts tests/circuit-grid-projection.test.ts tests/distribution-board.repository.test.ts tests/circuit-device-row-transaction.repository.test.ts tests/project-device-row-sync.repository.test.ts tests/circuit-section-transaction.repository.test.ts tests/database-backup.test.ts",
|
||||
"test": "tsx --test tests/project-device-schema.test.ts tests/project-device-schema-migration.test.ts tests/project-device-placement.service.test.ts tests/project-device-sync.service.test.ts tests/legacy-consumer-migration-planner.test.ts tests/legacy-consumer-migration.repository.test.ts tests/circuit-numbering.service.test.ts tests/circuit-write.rules.test.ts tests/circuit-power-calculation.test.ts tests/circuit-tree.controller.test.ts tests/circuit-grid-insertion.test.ts tests/circuit-grid-safety.test.ts tests/circuit-grid-model.test.ts tests/circuit-grid-projection.test.ts tests/distribution-board.repository.test.ts tests/circuit-device-row-transaction.repository.test.ts tests/project-device-row-sync.repository.test.ts tests/circuit-section-transaction.repository.test.ts tests/database-backup.test.ts",
|
||||
"test:watch": "tsx --watch --test tests/project-device-schema.test.ts tests/project-device-schema-migration.test.ts tests/project-device-placement.service.test.ts tests/project-device-sync.service.test.ts tests/legacy-consumer-migration-planner.test.ts tests/legacy-consumer-migration.repository.test.ts tests/circuit-numbering.service.test.ts tests/circuit-write.rules.test.ts tests/circuit-power-calculation.test.ts tests/circuit-tree.controller.test.ts tests/circuit-grid-insertion.test.ts tests/circuit-grid-safety.test.ts tests/circuit-grid-model.test.ts tests/circuit-grid-projection.test.ts tests/distribution-board.repository.test.ts tests/circuit-device-row-transaction.repository.test.ts tests/project-device-row-sync.repository.test.ts tests/circuit-section-transaction.repository.test.ts tests/database-backup.test.ts",
|
||||
"db:generate": "drizzle-kit generate",
|
||||
"db:migrate": "drizzle-kit migrate",
|
||||
"db:backup": "tsx scripts/db-backup.ts",
|
||||
|
||||
@@ -33,6 +33,16 @@ const projectDeviceColumns = new Set(
|
||||
const missingProjectDeviceColumns = requiredProjectDeviceColumns.filter(
|
||||
(name) => !projectDeviceColumns.has(name)
|
||||
);
|
||||
const removedProjectDeviceColumns = [
|
||||
"installed_power_per_unit_kw",
|
||||
"demand_factor",
|
||||
"phase_count",
|
||||
"power_factor",
|
||||
"note",
|
||||
];
|
||||
const remainingRemovedProjectDeviceColumns = removedProjectDeviceColumns.filter(
|
||||
(name) => projectDeviceColumns.has(name)
|
||||
);
|
||||
const requiredCircuitColumns = ["control_requirement"];
|
||||
const circuitColumns = new Set(
|
||||
db.prepare("PRAGMA table_info(circuits)").all().map((column) => column.name)
|
||||
@@ -55,6 +65,14 @@ if (missingProjectDeviceColumns.length > 0) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (remainingRemovedProjectDeviceColumns.length > 0) {
|
||||
console.error(
|
||||
"Transitional project-device columns still present:",
|
||||
remainingRemovedProjectDeviceColumns.join(", ")
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (missingCircuitColumns.length > 0) {
|
||||
console.error("Missing circuit columns:", missingCircuitColumns.join(", "));
|
||||
process.exit(1);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
ALTER TABLE `project_devices` DROP COLUMN `installed_power_per_unit_kw`;
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE `project_devices` DROP COLUMN `demand_factor`;
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE `project_devices` DROP COLUMN `phase_count`;
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE `project_devices` DROP COLUMN `power_factor`;
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE `project_devices` DROP COLUMN `note`;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -78,6 +78,13 @@
|
||||
"when": 1784746800000,
|
||||
"tag": "0010_circuit_control_requirement",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 11,
|
||||
"version": "6",
|
||||
"when": 1784832541202,
|
||||
"tag": "0011_project_device_canonical_fields",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -37,12 +37,7 @@ export class ProjectDeviceRepository {
|
||||
simultaneityFactor: input.simultaneityFactor,
|
||||
cosPhi: input.cosPhi ?? null,
|
||||
remark: input.remark ?? null,
|
||||
installedPowerPerUnitKw: input.powerPerUnit,
|
||||
demandFactor: input.simultaneityFactor,
|
||||
voltageV: input.voltageV ?? null,
|
||||
phaseCount: input.phaseType === "three_phase" ? 3 : 1,
|
||||
powerFactor: input.cosPhi ?? null,
|
||||
note: input.remark ?? null,
|
||||
});
|
||||
const created = await this.findById(projectId, id);
|
||||
if (!created) {
|
||||
@@ -77,12 +72,7 @@ export class ProjectDeviceRepository {
|
||||
simultaneityFactor: input.simultaneityFactor,
|
||||
cosPhi: input.cosPhi ?? null,
|
||||
remark: input.remark ?? null,
|
||||
installedPowerPerUnitKw: input.powerPerUnit,
|
||||
demandFactor: input.simultaneityFactor,
|
||||
voltageV: input.voltageV ?? null,
|
||||
phaseCount: input.phaseType === "three_phase" ? 3 : 1,
|
||||
powerFactor: input.cosPhi ?? null,
|
||||
note: input.remark ?? null,
|
||||
})
|
||||
.where(
|
||||
and(eq(projectDevices.id, projectDeviceId), eq(projectDevices.projectId, projectId))
|
||||
|
||||
@@ -17,11 +17,5 @@ export const projectDevices = sqliteTable("project_devices", {
|
||||
simultaneityFactor: real("simultaneity_factor").notNull().default(1),
|
||||
cosPhi: real("cos_phi"),
|
||||
remark: text("remark"),
|
||||
// Transitional mirror columns retained until a dedicated schema cleanup migration removes them.
|
||||
installedPowerPerUnitKw: real("installed_power_per_unit_kw").notNull(),
|
||||
demandFactor: real("demand_factor").notNull(),
|
||||
voltageV: real("voltage_v"),
|
||||
phaseCount: integer("phase_count"),
|
||||
powerFactor: real("power_factor"),
|
||||
note: text("note"),
|
||||
});
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { describe, it } from "node:test";
|
||||
import Database from "better-sqlite3";
|
||||
|
||||
describe("project device canonical-field migration", () => {
|
||||
it("preserves canonical values while removing transitional mirror columns", () => {
|
||||
const sqlite = new Database(":memory:");
|
||||
try {
|
||||
sqlite.exec(`
|
||||
CREATE TABLE project_devices (
|
||||
id text PRIMARY KEY NOT NULL,
|
||||
power_per_unit real NOT NULL,
|
||||
simultaneity_factor real NOT NULL,
|
||||
phase_type text NOT NULL,
|
||||
cos_phi real,
|
||||
remark text,
|
||||
voltage_v real,
|
||||
installed_power_per_unit_kw real NOT NULL,
|
||||
demand_factor real NOT NULL,
|
||||
phase_count integer,
|
||||
power_factor real,
|
||||
note text
|
||||
);
|
||||
INSERT INTO project_devices VALUES (
|
||||
'device-1',
|
||||
0.25,
|
||||
0.8,
|
||||
'three_phase',
|
||||
0.95,
|
||||
'Canonical remark',
|
||||
400,
|
||||
0.25,
|
||||
0.8,
|
||||
3,
|
||||
0.95,
|
||||
'Canonical remark'
|
||||
);
|
||||
`);
|
||||
|
||||
const migrationSql = fs.readFileSync(
|
||||
path.resolve(
|
||||
"src",
|
||||
"db",
|
||||
"migrations",
|
||||
"0011_project_device_canonical_fields.sql"
|
||||
),
|
||||
"utf8"
|
||||
);
|
||||
for (const statement of migrationSql.split("--> statement-breakpoint")) {
|
||||
if (statement.trim()) {
|
||||
sqlite.exec(statement);
|
||||
}
|
||||
}
|
||||
|
||||
const columns = (
|
||||
sqlite.prepare("PRAGMA table_info(project_devices)").all() as Array<{
|
||||
name: string;
|
||||
}>
|
||||
).map((column) => column.name);
|
||||
assert.deepEqual(columns, [
|
||||
"id",
|
||||
"power_per_unit",
|
||||
"simultaneity_factor",
|
||||
"phase_type",
|
||||
"cos_phi",
|
||||
"remark",
|
||||
"voltage_v",
|
||||
]);
|
||||
assert.deepEqual(
|
||||
sqlite
|
||||
.prepare(
|
||||
`SELECT
|
||||
power_per_unit AS powerPerUnit,
|
||||
simultaneity_factor AS simultaneityFactor,
|
||||
phase_type AS phaseType,
|
||||
cos_phi AS cosPhi,
|
||||
remark,
|
||||
voltage_v AS voltageV
|
||||
FROM project_devices
|
||||
WHERE id = ?`
|
||||
)
|
||||
.get("device-1"),
|
||||
{
|
||||
powerPerUnit: 0.25,
|
||||
simultaneityFactor: 0.8,
|
||||
phaseType: "three_phase",
|
||||
cosPhi: 0.95,
|
||||
remark: "Canonical remark",
|
||||
voltageV: 400,
|
||||
}
|
||||
);
|
||||
} finally {
|
||||
sqlite.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user