@naisys/erp 3.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/bin/naisys-erp +2 -0
  2. package/client-dist/android-chrome-192x192.png +0 -0
  3. package/client-dist/android-chrome-512x512.png +0 -0
  4. package/client-dist/apple-touch-icon.png +0 -0
  5. package/client-dist/assets/index-45dVo30p.css +1 -0
  6. package/client-dist/assets/index-C9uuPHLH.js +168 -0
  7. package/client-dist/assets/naisys-logo-CzoPnn5I.webp +0 -0
  8. package/client-dist/favicon.ico +0 -0
  9. package/client-dist/index.html +42 -0
  10. package/client-dist/site.webmanifest +22 -0
  11. package/dist/api-reference.js +101 -0
  12. package/dist/audit.js +14 -0
  13. package/dist/auth-middleware.js +203 -0
  14. package/dist/dbConfig.js +10 -0
  15. package/dist/erpDb.js +34 -0
  16. package/dist/erpServer.js +321 -0
  17. package/dist/error-handler.js +17 -0
  18. package/dist/generated/prisma/client.js +35 -0
  19. package/dist/generated/prisma/commonInputTypes.js +11 -0
  20. package/dist/generated/prisma/enums.js +60 -0
  21. package/dist/generated/prisma/internal/class.js +50 -0
  22. package/dist/generated/prisma/internal/prismaNamespace.js +366 -0
  23. package/dist/generated/prisma/models/Attachment.js +2 -0
  24. package/dist/generated/prisma/models/AuditLog.js +2 -0
  25. package/dist/generated/prisma/models/Field.js +2 -0
  26. package/dist/generated/prisma/models/FieldAttachment.js +2 -0
  27. package/dist/generated/prisma/models/FieldRecord.js +2 -0
  28. package/dist/generated/prisma/models/FieldSet.js +2 -0
  29. package/dist/generated/prisma/models/FieldValue.js +2 -0
  30. package/dist/generated/prisma/models/Item.js +2 -0
  31. package/dist/generated/prisma/models/ItemInstance.js +2 -0
  32. package/dist/generated/prisma/models/LaborTicket.js +2 -0
  33. package/dist/generated/prisma/models/Operation.js +2 -0
  34. package/dist/generated/prisma/models/OperationDependency.js +2 -0
  35. package/dist/generated/prisma/models/OperationFieldRef.js +2 -0
  36. package/dist/generated/prisma/models/OperationRun.js +2 -0
  37. package/dist/generated/prisma/models/OperationRunComment.js +2 -0
  38. package/dist/generated/prisma/models/Order.js +2 -0
  39. package/dist/generated/prisma/models/OrderRevision.js +2 -0
  40. package/dist/generated/prisma/models/OrderRun.js +2 -0
  41. package/dist/generated/prisma/models/SchemaVersion.js +2 -0
  42. package/dist/generated/prisma/models/Session.js +2 -0
  43. package/dist/generated/prisma/models/Step.js +2 -0
  44. package/dist/generated/prisma/models/StepRun.js +2 -0
  45. package/dist/generated/prisma/models/User.js +2 -0
  46. package/dist/generated/prisma/models/UserPermission.js +2 -0
  47. package/dist/generated/prisma/models/WorkCenter.js +2 -0
  48. package/dist/generated/prisma/models/WorkCenterUser.js +2 -0
  49. package/dist/generated/prisma/models.js +2 -0
  50. package/dist/hateoas.js +61 -0
  51. package/dist/route-helpers.js +220 -0
  52. package/dist/routes/admin.js +147 -0
  53. package/dist/routes/audit.js +36 -0
  54. package/dist/routes/auth.js +112 -0
  55. package/dist/routes/dispatch.js +174 -0
  56. package/dist/routes/inventory.js +70 -0
  57. package/dist/routes/item-fields.js +220 -0
  58. package/dist/routes/item-instances.js +426 -0
  59. package/dist/routes/items.js +252 -0
  60. package/dist/routes/labor-tickets.js +268 -0
  61. package/dist/routes/operation-dependencies.js +170 -0
  62. package/dist/routes/operation-field-refs.js +263 -0
  63. package/dist/routes/operation-run-comments.js +108 -0
  64. package/dist/routes/operation-run-transitions.js +249 -0
  65. package/dist/routes/operation-runs.js +299 -0
  66. package/dist/routes/operations.js +283 -0
  67. package/dist/routes/order-revision-transitions.js +86 -0
  68. package/dist/routes/order-revisions.js +327 -0
  69. package/dist/routes/order-run-transitions.js +215 -0
  70. package/dist/routes/order-runs.js +335 -0
  71. package/dist/routes/orders.js +262 -0
  72. package/dist/routes/root.js +123 -0
  73. package/dist/routes/schemas.js +31 -0
  74. package/dist/routes/step-field-attachments.js +231 -0
  75. package/dist/routes/step-fields.js +315 -0
  76. package/dist/routes/step-run-fields.js +438 -0
  77. package/dist/routes/step-run-transitions.js +113 -0
  78. package/dist/routes/step-runs.js +324 -0
  79. package/dist/routes/steps.js +283 -0
  80. package/dist/routes/user-permissions.js +100 -0
  81. package/dist/routes/users.js +381 -0
  82. package/dist/routes/work-centers.js +280 -0
  83. package/dist/schema-registry.js +45 -0
  84. package/dist/services/attachment-service.js +118 -0
  85. package/dist/services/field-ref-service.js +74 -0
  86. package/dist/services/field-service.js +114 -0
  87. package/dist/services/field-value-service.js +256 -0
  88. package/dist/services/item-instance-service.js +155 -0
  89. package/dist/services/item-service.js +56 -0
  90. package/dist/services/labor-ticket-service.js +148 -0
  91. package/dist/services/log-file-service.js +11 -0
  92. package/dist/services/operation-dependency-service.js +30 -0
  93. package/dist/services/operation-run-comment-service.js +26 -0
  94. package/dist/services/operation-run-service.js +347 -0
  95. package/dist/services/operation-service.js +132 -0
  96. package/dist/services/order-revision-service.js +264 -0
  97. package/dist/services/order-run-service.js +356 -0
  98. package/dist/services/order-service.js +68 -0
  99. package/dist/services/revision-diff-service.js +194 -0
  100. package/dist/services/step-run-service.js +106 -0
  101. package/dist/services/step-service.js +89 -0
  102. package/dist/services/user-service.js +132 -0
  103. package/dist/services/work-center-service.js +106 -0
  104. package/dist/supervisorAuth.js +16 -0
  105. package/dist/userService.js +118 -0
  106. package/package.json +75 -0
  107. package/prisma/migrations/20260212170352_init/migration.sql +125 -0
  108. package/prisma/migrations/20260308000000_multi_session/migration.sql +23 -0
  109. package/prisma/migrations/20260309000000_add_user_api_key/migration.sql +5 -0
  110. package/prisma/migrations/20260309010000_add_plan_operations/migration.sql +21 -0
  111. package/prisma/migrations/20260309020000_rename_exec_orders_to_order_runs/migration.sql +13 -0
  112. package/prisma/migrations/20260310000000_rename_plan_order_revs_to_order_revisions/migration.sql +22 -0
  113. package/prisma/migrations/20260310100000_rename_plan_orders_to_orders/migration.sql +23 -0
  114. package/prisma/migrations/20260310200000_rename_order_no_to_run_no/migration.sql +3 -0
  115. package/prisma/migrations/20260312000000_add_user_permissions/migration.sql +16 -0
  116. package/prisma/migrations/20260313000000_rename_plan_operations_to_operations/migration.sql +2 -0
  117. package/prisma/migrations/20260313100000_add_steps/migration.sql +20 -0
  118. package/prisma/migrations/20260314000000_add_step_fields/migration.sql +22 -0
  119. package/prisma/migrations/20260315000000_add_operation_runs/migration.sql +24 -0
  120. package/prisma/migrations/20260315100000_add_step_runs/migration.sql +40 -0
  121. package/prisma/migrations/20260316000000_drop_order_name/migration.sql +12 -0
  122. package/prisma/migrations/20260317000000_add_attachments/migration.sql +28 -0
  123. package/prisma/migrations/20260317000000_add_items/migration.sql +21 -0
  124. package/prisma/migrations/20260317100000_add_order_item_id/migration.sql +8 -0
  125. package/prisma/migrations/20260318000000_add_labor_tickets/migration.sql +27 -0
  126. package/prisma/migrations/20260319000000_add_operation_dependencies/migration.sql +17 -0
  127. package/prisma/migrations/20260320000000_step_field_is_array/migration.sql +5 -0
  128. package/prisma/migrations/20260320100000_rename_is_array_to_multi_value/migration.sql +2 -0
  129. package/prisma/migrations/20260320200000_add_field_types/migration.sql +2 -0
  130. package/prisma/migrations/20260321000000_add_multi_set/migration.sql +13 -0
  131. package/prisma/migrations/20260322000000_add_field_sets/migration.sql +90 -0
  132. package/prisma/migrations/20260323000000_add_item_instances/migration.sql +18 -0
  133. package/prisma/migrations/20260324000000_add_field_records/migration.sql +77 -0
  134. package/prisma/migrations/20260325000000_add_run_costs/migration.sql +3 -0
  135. package/prisma/migrations/20260326000000_add_comments/migration.sql +16 -0
  136. package/prisma/migrations/20260327000000_move_assigned_to_op_run/migration.sql +3 -0
  137. package/prisma/migrations/20260328000000_add_step_completion_note/migration.sql +2 -0
  138. package/prisma/migrations/20260328000000_add_step_title/migration.sql +2 -0
  139. package/prisma/migrations/20260329000000_rename_notes_to_release_note/migration.sql +2 -0
  140. package/prisma/migrations/20260329000000_simplify_order_run_dates/migration.sql +5 -0
  141. package/prisma/migrations/20260330000000_add_operation_run_completion_note/migration.sql +2 -0
  142. package/prisma/migrations/20260331000000_add_work_centers/migration.sql +30 -0
  143. package/prisma/migrations/20260401000000_fix_field_values_column_shift/migration.sql +26 -0
  144. package/prisma/migrations/20260402000000_rename_completion_note_to_status_note/migration.sql +5 -0
  145. package/prisma/migrations/20260403000000_add_operation_field_refs/migration.sql +16 -0
  146. package/prisma/migrations/20260404000000_rename_multi_value_to_is_array/migration.sql +2 -0
  147. package/prisma/migrations/20260404100000_add_attachment_public_id/migration.sql +8 -0
  148. package/prisma/migrations/migration_lock.toml +3 -0
  149. package/prisma/schema.prisma +595 -0
  150. package/prisma.config.ts +18 -0
@@ -0,0 +1,118 @@
1
+ import { SUPER_ADMIN_USERNAME } from "@naisys/common";
2
+ import { ensureSuperAdmin } from "@naisys/supervisor-database";
3
+ import bcrypt from "bcryptjs";
4
+ import { randomBytes, randomUUID } from "crypto";
5
+ import readline from "readline/promises";
6
+ import erpDb from "./erpDb.js";
7
+ const SALT_ROUNDS = 10;
8
+ /**
9
+ * Ensure a superadmin user exists in the local ERP database.
10
+ * For standalone mode (no supervisor auth).
11
+ */
12
+ export async function ensureLocalSuperAdmin() {
13
+ const existing = await erpDb.user.findUnique({
14
+ where: { username: SUPER_ADMIN_USERNAME },
15
+ });
16
+ if (existing) {
17
+ // Ensure superadmin has erp_admin permission
18
+ await ensureErpAdminPermission(existing.id);
19
+ }
20
+ else {
21
+ const password = randomUUID().slice(0, 8);
22
+ const hash = await bcrypt.hash(password, SALT_ROUNDS);
23
+ const user = await erpDb.user.create({
24
+ data: {
25
+ uuid: randomUUID(),
26
+ username: SUPER_ADMIN_USERNAME,
27
+ passwordHash: hash,
28
+ apiKey: randomBytes(32).toString("hex"),
29
+ },
30
+ });
31
+ await ensureErpAdminPermission(user.id);
32
+ console.log(`\n ${SUPER_ADMIN_USERNAME} user created. Password: ${password}`);
33
+ console.log(` Change it via --reset-password\n`);
34
+ }
35
+ // Warn if agent users exist without supervisor auth
36
+ const agentCount = await erpDb.user.count({ where: { isAgent: true } });
37
+ if (agentCount > 0) {
38
+ console.warn(`[ERP] Warning: ${agentCount} agent user(s) found but supervisor auth is disabled. ` +
39
+ `Agent API key lookups and authentication will not work. ` +
40
+ `Start with --supervisor-auth to enable.`);
41
+ }
42
+ }
43
+ /**
44
+ * Sync superadmin from supervisor into ERP DB and ensure permissions.
45
+ * For supervisor auth mode.
46
+ */
47
+ export async function ensureSupervisorSuperAdmin() {
48
+ const result = await ensureSuperAdmin();
49
+ await erpDb.user.upsert({
50
+ where: { uuid: result.user.uuid },
51
+ create: {
52
+ uuid: result.user.uuid,
53
+ username: result.user.username,
54
+ passwordHash: result.user.passwordHash,
55
+ apiKey: result.user.apiKey,
56
+ },
57
+ update: {
58
+ username: result.user.username,
59
+ passwordHash: result.user.passwordHash,
60
+ apiKey: result.user.apiKey,
61
+ },
62
+ });
63
+ const localSuperAdmin = await erpDb.user.findUnique({
64
+ where: { uuid: result.user.uuid },
65
+ });
66
+ if (localSuperAdmin) {
67
+ await ensureErpAdminPermission(localSuperAdmin.id);
68
+ }
69
+ if (result.created) {
70
+ console.log(`[ERP] ${SUPER_ADMIN_USERNAME} user created. Password: ${result.generatedPassword}`);
71
+ }
72
+ }
73
+ /**
74
+ * Ensure a user has the erp_admin permission.
75
+ */
76
+ export async function ensureErpAdminPermission(userId) {
77
+ const existing = await erpDb.userPermission.findUnique({
78
+ where: { userId_permission: { userId, permission: "erp_admin" } },
79
+ });
80
+ if (!existing) {
81
+ await erpDb.userPermission.create({
82
+ data: { userId, permission: "erp_admin" },
83
+ });
84
+ }
85
+ }
86
+ /**
87
+ * Interactive CLI to reset a local user's password.
88
+ * For standalone mode (no supervisor auth).
89
+ */
90
+ export async function resetLocalPassword() {
91
+ const rl = readline.createInterface({
92
+ input: process.stdin,
93
+ output: process.stdout,
94
+ });
95
+ try {
96
+ const username = await rl.question("Username: ");
97
+ const user = await erpDb.user.findUnique({ where: { username } });
98
+ if (!user) {
99
+ console.error(`User '${username}' not found.`);
100
+ process.exit(1);
101
+ }
102
+ const password = await rl.question("New password: ");
103
+ if (password.length < 6) {
104
+ console.error("Password must be at least 6 characters.");
105
+ process.exit(1);
106
+ }
107
+ const hash = await bcrypt.hash(password, SALT_ROUNDS);
108
+ await erpDb.user.update({
109
+ where: { id: user.id },
110
+ data: { passwordHash: hash },
111
+ });
112
+ console.log(`Password reset for '${username}'.`);
113
+ }
114
+ finally {
115
+ rl.close();
116
+ }
117
+ }
118
+ //# sourceMappingURL=userService.js.map
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@naisys/erp",
3
+ "version": "3.0.0-beta.10",
4
+ "description": "NAISYS ERP - Web UI for AI-driven order and work management",
5
+ "type": "module",
6
+ "main": "dist/erpServer.js",
7
+ "bin": {
8
+ "naisys-erp": "bin/naisys-erp"
9
+ },
10
+ "exports": {
11
+ ".": "./dist/erpServer.js"
12
+ },
13
+ "scripts": {
14
+ "clean": "rimraf dist client-dist .test-naisys",
15
+ "dev": "tsx watch src/erpServer.ts",
16
+ "build": "prisma generate && tsc",
17
+ "bundle": "npx shx rm -rf client-dist && npx shx cp -r ../client/dist client-dist",
18
+ "start": "node dist/erpServer.js",
19
+ "start:reset-password": "node dist/erpServer.js --reset-password",
20
+ "type-check": "tsc --noEmit",
21
+ "npm:publish:dryrun": "npm publish --dry-run",
22
+ "npm:publish": "npm publish --access public",
23
+ "db:generate": "prisma generate",
24
+ "db:migrate": "prisma migrate dev",
25
+ "db:push": "prisma db push",
26
+ "test": "vitest run && npx playwright test"
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "client-dist",
31
+ "bin",
32
+ "prisma.config.ts",
33
+ "prisma/schema.prisma",
34
+ "prisma/migrations",
35
+ "!dist/**/*.map",
36
+ "!dist/**/*.d.ts",
37
+ "!dist/**/*.d.ts.map",
38
+ "!dist/**/tests",
39
+ "!dist/**/*.test.*"
40
+ ],
41
+ "dependencies": {
42
+ "@fastify/cookie": "^11.0.2",
43
+ "@fastify/cors": "^11.2.0",
44
+ "@fastify/multipart": "^9.4.0",
45
+ "@fastify/rate-limit": "^10.3.0",
46
+ "@fastify/static": "^9.0.0",
47
+ "@fastify/swagger": "^9.7.0",
48
+ "@naisys/erp-shared": "3.0.0-beta.10",
49
+ "@naisys/common": "3.0.0-beta.10",
50
+ "@naisys/common-node": "3.0.0-beta.10",
51
+ "@naisys/hub-database": "3.0.0-beta.10",
52
+ "@naisys/supervisor-database": "3.0.0-beta.10",
53
+ "@prisma/adapter-better-sqlite3": "^7.5.0",
54
+ "@prisma/client": "^7.5.0",
55
+ "@scalar/fastify-api-reference": "^1.48.7",
56
+ "bcryptjs": "^3.0.2",
57
+ "dotenv": "^17.3.1",
58
+ "fastify": "^5.8.2",
59
+ "fastify-plugin": "^5.1.0",
60
+ "fastify-type-provider-zod": "^6.1.0",
61
+ "zod": "^4.3.6"
62
+ },
63
+ "devDependencies": {
64
+ "@playwright/test": "^1.58.2",
65
+ "@types/better-sqlite3": "^7.6.13",
66
+ "@types/node": "^25.5.0",
67
+ "prisma": "^7.5.0",
68
+ "tsx": "^4.21.0",
69
+ "typescript": "^5.9.3",
70
+ "vitest": "^4.1.0"
71
+ },
72
+ "engines": {
73
+ "node": ">=22.0.0"
74
+ }
75
+ }
@@ -0,0 +1,125 @@
1
+ -- CreateTable
2
+ CREATE TABLE "plan_orders" (
3
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4
+ "key" TEXT NOT NULL,
5
+ "name" TEXT NOT NULL,
6
+ "description" TEXT NOT NULL DEFAULT '',
7
+ "status" TEXT NOT NULL DEFAULT 'active',
8
+ "created_by" INTEGER NOT NULL,
9
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
10
+ "updated_by" INTEGER NOT NULL,
11
+ "updated_at" DATETIME NOT NULL,
12
+ CONSTRAINT "plan_orders_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
13
+ CONSTRAINT "plan_orders_updated_by_fkey" FOREIGN KEY ("updated_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
14
+ );
15
+
16
+ -- CreateTable
17
+ CREATE TABLE "plan_order_revs" (
18
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
19
+ "plan_order_id" INTEGER NOT NULL,
20
+ "rev_no" INTEGER NOT NULL,
21
+ "status" TEXT NOT NULL DEFAULT 'draft',
22
+ "notes" TEXT,
23
+ "change_summary" TEXT,
24
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
25
+ "created_by" INTEGER NOT NULL,
26
+ "updated_at" DATETIME NOT NULL,
27
+ "updated_by" INTEGER NOT NULL,
28
+ CONSTRAINT "plan_order_revs_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
29
+ CONSTRAINT "plan_order_revs_updated_by_fkey" FOREIGN KEY ("updated_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
30
+ CONSTRAINT "plan_order_revs_plan_order_id_fkey" FOREIGN KEY ("plan_order_id") REFERENCES "plan_orders" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
31
+ );
32
+
33
+ -- CreateTable
34
+ CREATE TABLE "exec_orders" (
35
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
36
+ "order_no" INTEGER NOT NULL,
37
+ "plan_order_id" INTEGER NOT NULL,
38
+ "plan_order_rev_id" INTEGER NOT NULL,
39
+ "status" TEXT NOT NULL DEFAULT 'released',
40
+ "priority" TEXT NOT NULL DEFAULT 'medium',
41
+ "scheduled_start_at" DATETIME,
42
+ "due_at" DATETIME,
43
+ "released_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
44
+ "assigned_to" TEXT,
45
+ "notes" TEXT,
46
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
47
+ "created_by" INTEGER NOT NULL,
48
+ "updated_at" DATETIME NOT NULL,
49
+ "updated_by" INTEGER NOT NULL,
50
+ CONSTRAINT "exec_orders_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
51
+ CONSTRAINT "exec_orders_updated_by_fkey" FOREIGN KEY ("updated_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
52
+ CONSTRAINT "exec_orders_plan_order_id_fkey" FOREIGN KEY ("plan_order_id") REFERENCES "plan_orders" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
53
+ CONSTRAINT "exec_orders_plan_order_rev_id_fkey" FOREIGN KEY ("plan_order_rev_id") REFERENCES "plan_order_revs" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
54
+ );
55
+
56
+ -- CreateTable
57
+ CREATE TABLE "users" (
58
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
59
+ "uuid" TEXT NOT NULL,
60
+ "username" TEXT NOT NULL,
61
+ "password_hash" TEXT NOT NULL,
62
+ "is_agent" INTEGER NOT NULL DEFAULT 0,
63
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
64
+ "updated_at" DATETIME NOT NULL,
65
+ "deleted_at" DATETIME,
66
+ "session_token_hash" TEXT,
67
+ "session_expires_at" DATETIME
68
+ );
69
+
70
+ -- CreateTable
71
+ CREATE TABLE "schema_version" (
72
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT DEFAULT 1,
73
+ "version" INTEGER NOT NULL,
74
+ "updated" TEXT NOT NULL
75
+ );
76
+
77
+ -- CreateTable
78
+ CREATE TABLE "audit_log" (
79
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
80
+ "entity_type" TEXT NOT NULL,
81
+ "entity_id" INTEGER NOT NULL,
82
+ "action" TEXT NOT NULL,
83
+ "field" TEXT NOT NULL,
84
+ "old_value" TEXT,
85
+ "new_value" TEXT,
86
+ "user_id" INTEGER NOT NULL,
87
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
88
+ CONSTRAINT "audit_log_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
89
+ );
90
+
91
+ -- CreateIndex
92
+ CREATE UNIQUE INDEX "plan_orders_key_key" ON "plan_orders"("key");
93
+
94
+ -- CreateIndex
95
+ CREATE INDEX "plan_orders_status_idx" ON "plan_orders"("status");
96
+
97
+ -- CreateIndex
98
+ CREATE INDEX "plan_orders_created_at_idx" ON "plan_orders"("created_at");
99
+
100
+ -- CreateIndex
101
+ CREATE UNIQUE INDEX "plan_order_revs_plan_order_id_rev_no_key" ON "plan_order_revs"("plan_order_id", "rev_no");
102
+
103
+ -- CreateIndex
104
+ CREATE INDEX "exec_orders_status_idx" ON "exec_orders"("status");
105
+
106
+ -- CreateIndex
107
+ CREATE INDEX "exec_orders_priority_idx" ON "exec_orders"("priority");
108
+
109
+ -- CreateIndex
110
+ CREATE INDEX "exec_orders_created_at_idx" ON "exec_orders"("created_at");
111
+
112
+ -- CreateIndex
113
+ CREATE INDEX "exec_orders_plan_order_rev_id_idx" ON "exec_orders"("plan_order_rev_id");
114
+
115
+ -- CreateIndex
116
+ CREATE UNIQUE INDEX "exec_orders_plan_order_id_order_no_key" ON "exec_orders"("plan_order_id", "order_no");
117
+
118
+ -- CreateIndex
119
+ CREATE UNIQUE INDEX "users_uuid_key" ON "users"("uuid");
120
+
121
+ -- CreateIndex
122
+ CREATE UNIQUE INDEX "users_username_key" ON "users"("username");
123
+
124
+ -- CreateIndex
125
+ CREATE INDEX "audit_log_entity_type_entity_id_idx" ON "audit_log"("entity_type", "entity_id");
@@ -0,0 +1,23 @@
1
+ -- CreateTable
2
+ CREATE TABLE "sessions" (
3
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4
+ "user_id" INTEGER NOT NULL,
5
+ "token_hash" TEXT NOT NULL,
6
+ "expires_at" DATETIME NOT NULL,
7
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
8
+ CONSTRAINT "sessions_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE
9
+ );
10
+
11
+ -- CreateIndex
12
+ CREATE UNIQUE INDEX "sessions_token_hash_key" ON "sessions"("token_hash");
13
+
14
+ -- Migrate existing sessions
15
+ INSERT INTO "sessions" ("user_id", "token_hash", "expires_at", "created_at")
16
+ SELECT "id", "session_token_hash", "session_expires_at", CURRENT_TIMESTAMP
17
+ FROM "users"
18
+ WHERE "session_token_hash" IS NOT NULL AND "session_expires_at" IS NOT NULL
19
+ AND "session_token_hash" != '!sso';
20
+
21
+ -- DropColumns
22
+ ALTER TABLE "users" DROP COLUMN "session_token_hash";
23
+ ALTER TABLE "users" DROP COLUMN "session_expires_at";
@@ -0,0 +1,5 @@
1
+ -- AlterTable
2
+ ALTER TABLE "users" ADD COLUMN "api_key" TEXT;
3
+
4
+ -- CreateIndex
5
+ CREATE UNIQUE INDEX "users_api_key_key" ON "users"("api_key");
@@ -0,0 +1,21 @@
1
+ -- CreateTable
2
+ CREATE TABLE "plan_operations" (
3
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4
+ "plan_order_rev_id" INTEGER NOT NULL,
5
+ "seq_no" INTEGER NOT NULL,
6
+ "title" TEXT NOT NULL,
7
+ "description" TEXT NOT NULL DEFAULT '',
8
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
9
+ "created_by" INTEGER NOT NULL,
10
+ "updated_at" DATETIME NOT NULL,
11
+ "updated_by" INTEGER NOT NULL,
12
+ CONSTRAINT "plan_operations_plan_order_rev_id_fkey" FOREIGN KEY ("plan_order_rev_id") REFERENCES "plan_order_revs" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
13
+ CONSTRAINT "plan_operations_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
14
+ CONSTRAINT "plan_operations_updated_by_fkey" FOREIGN KEY ("updated_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
15
+ );
16
+
17
+ -- CreateIndex
18
+ CREATE UNIQUE INDEX "plan_operations_plan_order_rev_id_seq_no_key" ON "plan_operations"("plan_order_rev_id", "seq_no");
19
+
20
+ -- UpdateVersion
21
+ UPDATE "schema_version" SET "version" = 5, "updated" = datetime('now');
@@ -0,0 +1,13 @@
1
+ ALTER TABLE "exec_orders" RENAME TO "order_runs";
2
+ DROP INDEX "exec_orders_status_idx";
3
+ DROP INDEX "exec_orders_priority_idx";
4
+ DROP INDEX "exec_orders_created_at_idx";
5
+ DROP INDEX "exec_orders_plan_order_rev_id_idx";
6
+ DROP INDEX "exec_orders_plan_order_id_order_no_key";
7
+ CREATE INDEX "order_runs_status_idx" ON "order_runs"("status");
8
+ CREATE INDEX "order_runs_priority_idx" ON "order_runs"("priority");
9
+ CREATE INDEX "order_runs_created_at_idx" ON "order_runs"("created_at");
10
+ CREATE INDEX "order_runs_plan_order_rev_id_idx" ON "order_runs"("plan_order_rev_id");
11
+ CREATE UNIQUE INDEX "order_runs_plan_order_id_order_no_key" ON "order_runs"("plan_order_id", "order_no");
12
+ UPDATE "audit_log" SET "entity_type" = 'OrderRun' WHERE "entity_type" = 'ExecOrder';
13
+ UPDATE "schema_version" SET "version" = 6, "updated" = datetime('now');
@@ -0,0 +1,22 @@
1
+ -- Rename table
2
+ ALTER TABLE "plan_order_revs" RENAME TO "order_revisions";
3
+
4
+ -- Recreate unique index with new table name
5
+ DROP INDEX "plan_order_revs_plan_order_id_rev_no_key";
6
+ CREATE UNIQUE INDEX "order_revisions_plan_order_id_rev_no_key" ON "order_revisions"("plan_order_id", "rev_no");
7
+
8
+ -- Rename FK column on order_runs
9
+ ALTER TABLE "order_runs" RENAME COLUMN "plan_order_rev_id" TO "order_rev_id";
10
+ DROP INDEX "order_runs_plan_order_rev_id_idx";
11
+ CREATE INDEX "order_runs_order_rev_id_idx" ON "order_runs"("order_rev_id");
12
+
13
+ -- Rename FK column on plan_operations
14
+ ALTER TABLE "plan_operations" RENAME COLUMN "plan_order_rev_id" TO "order_rev_id";
15
+ DROP INDEX "plan_operations_plan_order_rev_id_seq_no_key";
16
+ CREATE UNIQUE INDEX "plan_operations_order_rev_id_seq_no_key" ON "plan_operations"("order_rev_id", "seq_no");
17
+
18
+ -- Update audit entity type
19
+ UPDATE "audit_log" SET "entity_type" = 'OrderRevision' WHERE "entity_type" = 'PlanningOrderRevision';
20
+
21
+ -- Bump schema version
22
+ UPDATE "schema_version" SET "version" = 7, "updated" = datetime('now');
@@ -0,0 +1,23 @@
1
+ -- Rename plan_orders table
2
+ ALTER TABLE "plan_orders" RENAME TO "orders";
3
+
4
+ -- Recreate indexes with new table name
5
+ DROP INDEX "plan_orders_key_key";
6
+ CREATE UNIQUE INDEX "orders_key_key" ON "orders"("key");
7
+ DROP INDEX "plan_orders_status_idx";
8
+ CREATE INDEX "orders_status_idx" ON "orders"("status");
9
+ DROP INDEX "plan_orders_created_at_idx";
10
+ CREATE INDEX "orders_created_at_idx" ON "orders"("created_at");
11
+
12
+ -- Rename FK column on order_revisions
13
+ ALTER TABLE "order_revisions" RENAME COLUMN "plan_order_id" TO "order_id";
14
+ DROP INDEX "order_revisions_plan_order_id_rev_no_key";
15
+ CREATE UNIQUE INDEX "order_revisions_order_id_rev_no_key" ON "order_revisions"("order_id", "rev_no");
16
+
17
+ -- Rename FK column on order_runs
18
+ ALTER TABLE "order_runs" RENAME COLUMN "plan_order_id" TO "order_id";
19
+ DROP INDEX "order_runs_plan_order_id_order_no_key";
20
+ CREATE UNIQUE INDEX "order_runs_order_id_order_no_key" ON "order_runs"("order_id", "order_no");
21
+
22
+ -- Bump schema version
23
+ UPDATE "schema_version" SET "version" = 8, "updated" = datetime('now');
@@ -0,0 +1,3 @@
1
+ ALTER TABLE "order_runs" RENAME COLUMN "order_no" TO "run_no";
2
+ DROP INDEX "order_runs_order_id_order_no_key";
3
+ CREATE UNIQUE INDEX "order_runs_order_id_run_no_key" ON "order_runs"("order_id", "run_no");
@@ -0,0 +1,16 @@
1
+ -- CreateTable
2
+ CREATE TABLE "user_permissions" (
3
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4
+ "user_id" INTEGER NOT NULL,
5
+ "permission" TEXT NOT NULL,
6
+ "granted_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
7
+ "granted_by" INTEGER,
8
+ CONSTRAINT "user_permissions_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
9
+ CONSTRAINT "user_permissions_granted_by_fkey" FOREIGN KEY ("granted_by") REFERENCES "users" ("id") ON DELETE SET NULL ON UPDATE CASCADE
10
+ );
11
+
12
+ -- CreateIndex
13
+ CREATE UNIQUE INDEX "user_permissions_user_id_permission_key" ON "user_permissions"("user_id", "permission");
14
+
15
+ -- UpdateSchemaVersion
16
+ UPDATE "schema_version" SET "version" = 10, "updated" = datetime('now');
@@ -0,0 +1,2 @@
1
+ -- Rename plan_operations table to operations
2
+ ALTER TABLE "plan_operations" RENAME TO "operations";
@@ -0,0 +1,20 @@
1
+ -- CreateTable
2
+ CREATE TABLE "steps" (
3
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4
+ "operation_id" INTEGER NOT NULL,
5
+ "seq_no" INTEGER NOT NULL,
6
+ "instructions" TEXT NOT NULL DEFAULT '',
7
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
8
+ "created_by" INTEGER NOT NULL,
9
+ "updated_at" DATETIME NOT NULL,
10
+ "updated_by" INTEGER NOT NULL,
11
+ CONSTRAINT "steps_operation_id_fkey" FOREIGN KEY ("operation_id") REFERENCES "operations" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
12
+ CONSTRAINT "steps_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
13
+ CONSTRAINT "steps_updated_by_fkey" FOREIGN KEY ("updated_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
14
+ );
15
+
16
+ -- CreateIndex
17
+ CREATE UNIQUE INDEX "steps_operation_id_seq_no_key" ON "steps"("operation_id", "seq_no");
18
+
19
+ -- UpdateSchemaVersion
20
+ UPDATE "schema_version" SET "version" = 12, "updated" = datetime('now');
@@ -0,0 +1,22 @@
1
+ -- CreateTable
2
+ CREATE TABLE "step_fields" (
3
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4
+ "step_id" INTEGER NOT NULL,
5
+ "seq_no" INTEGER NOT NULL,
6
+ "label" TEXT NOT NULL,
7
+ "type" TEXT NOT NULL DEFAULT 'string',
8
+ "required" BOOLEAN NOT NULL DEFAULT 0,
9
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
10
+ "created_by" INTEGER NOT NULL,
11
+ "updated_at" DATETIME NOT NULL,
12
+ "updated_by" INTEGER NOT NULL,
13
+ CONSTRAINT "step_fields_step_id_fkey" FOREIGN KEY ("step_id") REFERENCES "steps" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
14
+ CONSTRAINT "step_fields_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
15
+ CONSTRAINT "step_fields_updated_by_fkey" FOREIGN KEY ("updated_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
16
+ );
17
+
18
+ -- CreateIndex
19
+ CREATE UNIQUE INDEX "step_fields_step_id_seq_no_key" ON "step_fields"("step_id", "seq_no");
20
+
21
+ -- UpdateSchemaVersion
22
+ UPDATE "schema_version" SET "version" = 13, "updated" = datetime('now');
@@ -0,0 +1,24 @@
1
+ -- CreateTable
2
+ CREATE TABLE "operation_runs" (
3
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4
+ "order_run_id" INTEGER NOT NULL,
5
+ "operation_id" INTEGER NOT NULL,
6
+ "status" TEXT NOT NULL DEFAULT 'pending',
7
+ "completed_at" DATETIME,
8
+ "notes" TEXT,
9
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
10
+ "created_by" INTEGER NOT NULL,
11
+ "updated_at" DATETIME NOT NULL,
12
+ "updated_by" INTEGER NOT NULL,
13
+ CONSTRAINT "operation_runs_order_run_id_fkey" FOREIGN KEY ("order_run_id") REFERENCES "order_runs" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
14
+ CONSTRAINT "operation_runs_operation_id_fkey" FOREIGN KEY ("operation_id") REFERENCES "operations" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
15
+ CONSTRAINT "operation_runs_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
16
+ CONSTRAINT "operation_runs_updated_by_fkey" FOREIGN KEY ("updated_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
17
+ );
18
+
19
+ -- CreateIndex
20
+ CREATE UNIQUE INDEX "operation_runs_order_run_id_operation_id_key" ON "operation_runs"("order_run_id", "operation_id");
21
+ CREATE INDEX "operation_runs_status_idx" ON "operation_runs"("status");
22
+
23
+ -- UpdateSchemaVersion
24
+ UPDATE "schema_version" SET "version" = 14, "updated" = datetime('now');
@@ -0,0 +1,40 @@
1
+ -- CreateTable
2
+ CREATE TABLE "step_runs" (
3
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4
+ "operation_run_id" INTEGER NOT NULL,
5
+ "step_id" INTEGER NOT NULL,
6
+ "completed" BOOLEAN NOT NULL DEFAULT 0,
7
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
8
+ "created_by" INTEGER NOT NULL,
9
+ "updated_at" DATETIME NOT NULL,
10
+ "updated_by" INTEGER NOT NULL,
11
+ CONSTRAINT "step_runs_operation_run_id_fkey" FOREIGN KEY ("operation_run_id") REFERENCES "operation_runs" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
12
+ CONSTRAINT "step_runs_step_id_fkey" FOREIGN KEY ("step_id") REFERENCES "steps" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
13
+ CONSTRAINT "step_runs_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
14
+ CONSTRAINT "step_runs_updated_by_fkey" FOREIGN KEY ("updated_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
15
+ );
16
+
17
+ -- CreateIndex
18
+ CREATE UNIQUE INDEX "step_runs_operation_run_id_step_id_key" ON "step_runs"("operation_run_id", "step_id");
19
+
20
+ -- CreateTable
21
+ CREATE TABLE "step_field_values" (
22
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
23
+ "step_run_id" INTEGER NOT NULL,
24
+ "step_field_id" INTEGER NOT NULL,
25
+ "value" TEXT NOT NULL DEFAULT '',
26
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
27
+ "created_by" INTEGER NOT NULL,
28
+ "updated_at" DATETIME NOT NULL,
29
+ "updated_by" INTEGER NOT NULL,
30
+ CONSTRAINT "step_field_values_step_run_id_fkey" FOREIGN KEY ("step_run_id") REFERENCES "step_runs" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
31
+ CONSTRAINT "step_field_values_step_field_id_fkey" FOREIGN KEY ("step_field_id") REFERENCES "step_fields" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
32
+ CONSTRAINT "step_field_values_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
33
+ CONSTRAINT "step_field_values_updated_by_fkey" FOREIGN KEY ("updated_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
34
+ );
35
+
36
+ -- CreateIndex
37
+ CREATE UNIQUE INDEX "step_field_values_step_run_id_step_field_id_key" ON "step_field_values"("step_run_id", "step_field_id");
38
+
39
+ -- UpdateSchemaVersion
40
+ UPDATE "schema_version" SET "version" = 15, "updated" = datetime('now');
@@ -0,0 +1,12 @@
1
+ -- Drop the "name" column from the orders table.
2
+ ALTER TABLE "orders" DROP COLUMN "name";
3
+
4
+ -- Rename "notes" to "description" in order_revisions and make it non-nullable.
5
+ ALTER TABLE "order_revisions" RENAME COLUMN "notes" TO "description";
6
+ UPDATE "order_revisions" SET "description" = '' WHERE "description" IS NULL;
7
+
8
+ -- Drop the "released_at" column from the order_runs table.
9
+ ALTER TABLE "order_runs" DROP COLUMN "released_at";
10
+
11
+ -- Rename "notes" to "feedback" on operation_runs.
12
+ ALTER TABLE "operation_runs" RENAME COLUMN "notes" TO "feedback";
@@ -0,0 +1,28 @@
1
+ -- Create attachments table
2
+ CREATE TABLE "attachments" (
3
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4
+ "filepath" TEXT NOT NULL,
5
+ "filename" TEXT NOT NULL,
6
+ "file_size" INTEGER NOT NULL,
7
+ "file_hash" TEXT NOT NULL,
8
+ "uploaded_by" INTEGER NOT NULL,
9
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
10
+ CONSTRAINT "attachments_uploaded_by_fkey" FOREIGN KEY ("uploaded_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
11
+ );
12
+
13
+ CREATE INDEX "attachments_file_hash_idx" ON "attachments" ("file_hash");
14
+
15
+ -- Create step_field_attachments junction table
16
+ CREATE TABLE "step_field_attachments" (
17
+ "step_field_value_id" INTEGER NOT NULL,
18
+ "attachment_id" INTEGER NOT NULL,
19
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
20
+ CONSTRAINT "step_field_attachments_step_field_value_id_fkey" FOREIGN KEY ("step_field_value_id") REFERENCES "step_field_values" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
21
+ CONSTRAINT "step_field_attachments_attachment_id_fkey" FOREIGN KEY ("attachment_id") REFERENCES "attachments" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
22
+ PRIMARY KEY ("step_field_value_id", "attachment_id")
23
+ );
24
+
25
+ -- No DDL needed for new StepFieldType value 'attachment': SQLite stores enums as TEXT.
26
+
27
+ -- Bump schema version
28
+ UPDATE "schema_version" SET "version" = 26, "updated" = datetime('now');
@@ -0,0 +1,21 @@
1
+ -- CreateTable
2
+ CREATE TABLE "items" (
3
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4
+ "key" TEXT NOT NULL,
5
+ "description" TEXT NOT NULL DEFAULT '',
6
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
7
+ "created_by" INTEGER NOT NULL,
8
+ "updated_at" DATETIME NOT NULL,
9
+ "updated_by" INTEGER NOT NULL,
10
+ CONSTRAINT "items_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
11
+ CONSTRAINT "items_updated_by_fkey" FOREIGN KEY ("updated_by") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
12
+ );
13
+
14
+ -- CreateIndex
15
+ CREATE UNIQUE INDEX "items_key_key" ON "items"("key");
16
+
17
+ -- CreateIndex
18
+ CREATE INDEX "items_created_at_idx" ON "items"("created_at");
19
+
20
+ -- UpdateSchemaVersion
21
+ UPDATE "schema_version" SET "version" = 17, "updated" = datetime('now');
@@ -0,0 +1,8 @@
1
+ -- Add item_id column to orders
2
+ ALTER TABLE "orders" ADD COLUMN "item_id" INTEGER REFERENCES "items"("id") ON DELETE RESTRICT;
3
+
4
+ -- CreateIndex
5
+ CREATE INDEX "orders_item_id_idx" ON "orders"("item_id");
6
+
7
+ -- UpdateSchemaVersion
8
+ UPDATE "schema_version" SET "version" = 18, "updated" = datetime('now');