@rytass/bpm-core-nestjs-module 0.13.1 → 0.13.2
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.
- package/CHANGELOG.md +11 -0
- package/package.json +2 -2
- package/src/lib/migrations/0000000022000-org-unit-code-active-unique.d.ts +26 -0
- package/src/lib/migrations/0000000022000-org-unit-code-active-unique.js +80 -0
- package/src/lib/migrations/0000000022000-org-unit-code-active-unique.js.map +1 -0
- package/src/lib/migrations/index.d.ts +1 -0
- package/src/lib/migrations/index.js +3 -0
- package/src/lib/migrations/index.js.map +1 -1
- package/src/lib/organization/org-unit.entity.d.ts +9 -0
- package/src/lib/organization/org-unit.entity.js +14 -1
- package/src/lib/organization/org-unit.entity.js.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
## 0.13.2 (2026-09-04)
|
|
2
|
+
|
|
3
|
+
### 🩹 Fixes
|
|
4
|
+
|
|
5
|
+
- **organization:** stop a deleted org unit from burning its code forever ([0f6897f](https://github.com/Rytass/BPMCore/commit/0f6897f))
|
|
6
|
+
|
|
7
|
+
### ❤️ Thank You
|
|
8
|
+
|
|
9
|
+
- Chia Yu Pai @fantasywind
|
|
10
|
+
- Claude Opus 5
|
|
11
|
+
|
|
1
12
|
## 0.13.1 (2026-09-04)
|
|
2
13
|
|
|
3
14
|
### 🩹 Fixes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rytass/bpm-core-nestjs-module",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.2",
|
|
4
4
|
"description": "Embeddable NestJS BPM approval workflow module: workflow engine, approval templates, forms, organization/member contracts, attachments, signatures, notifications, delegation, and TypeORM migrations.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bpm",
|
|
@@ -155,7 +155,7 @@
|
|
|
155
155
|
"@nestjs/core": ">=10.0.0",
|
|
156
156
|
"@nestjs/graphql": ">=12.0.0",
|
|
157
157
|
"@nestjs/typeorm": ">=10.0.0",
|
|
158
|
-
"@rytass/bpm-core-shared": "^0.13.
|
|
158
|
+
"@rytass/bpm-core-shared": "^0.13.2",
|
|
159
159
|
"@rytass/secret-adapter-vault-nestjs": ">=0.4.5",
|
|
160
160
|
"express": ">=4.0.0",
|
|
161
161
|
"graphql": ">=16.0.0",
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from 'typeorm';
|
|
2
|
+
/**
|
|
3
|
+
* Scopes the org unit code uniqueness to rows that are not soft-deleted.
|
|
4
|
+
*
|
|
5
|
+
* `org_units.code` was created as a plain `UNIQUE` column while
|
|
6
|
+
* `deleteOrgUnit` only soft-deletes and `assertOrgUnitCodeAvailable` looks for
|
|
7
|
+
* a clash among rows with `deleted_at IS NULL`. The two disagreed: the service
|
|
8
|
+
* cleared a recycled code, and the insert was then rejected by the database
|
|
9
|
+
* with a raw `duplicate key value violates unique constraint
|
|
10
|
+
* "org_units_code_key"` — naming a row no query can return, because it is soft
|
|
11
|
+
* deleted. Every deleted unit permanently consumed its code, which broke the
|
|
12
|
+
* ordinary "close a department and recreate it under the same code" operation.
|
|
13
|
+
*
|
|
14
|
+
* The partial index makes the schema agree with the service. Nothing about
|
|
15
|
+
* `assertOrgUnitCodeAvailable` changes; it was already the intended contract.
|
|
16
|
+
*/
|
|
17
|
+
export declare class OrgUnitCodeActiveUnique0000000022000 implements MigrationInterface {
|
|
18
|
+
name: string;
|
|
19
|
+
up(queryRunner: QueryRunner): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Restoring the global constraint fails if any code has been reused since
|
|
22
|
+
* `up()` ran — which is precisely the operation this migration exists to
|
|
23
|
+
* allow. Free the duplicate codes first if you genuinely need to go back.
|
|
24
|
+
*/
|
|
25
|
+
down(queryRunner: QueryRunner): Promise<void>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OrgUnitCodeActiveUnique0000000022000 = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Scopes the org unit code uniqueness to rows that are not soft-deleted.
|
|
6
|
+
*
|
|
7
|
+
* `org_units.code` was created as a plain `UNIQUE` column while
|
|
8
|
+
* `deleteOrgUnit` only soft-deletes and `assertOrgUnitCodeAvailable` looks for
|
|
9
|
+
* a clash among rows with `deleted_at IS NULL`. The two disagreed: the service
|
|
10
|
+
* cleared a recycled code, and the insert was then rejected by the database
|
|
11
|
+
* with a raw `duplicate key value violates unique constraint
|
|
12
|
+
* "org_units_code_key"` — naming a row no query can return, because it is soft
|
|
13
|
+
* deleted. Every deleted unit permanently consumed its code, which broke the
|
|
14
|
+
* ordinary "close a department and recreate it under the same code" operation.
|
|
15
|
+
*
|
|
16
|
+
* The partial index makes the schema agree with the service. Nothing about
|
|
17
|
+
* `assertOrgUnitCodeAvailable` changes; it was already the intended contract.
|
|
18
|
+
*/
|
|
19
|
+
class OrgUnitCodeActiveUnique0000000022000 {
|
|
20
|
+
constructor() {
|
|
21
|
+
this.name = 'OrgUnitCodeActiveUnique0000000022000';
|
|
22
|
+
}
|
|
23
|
+
async up(queryRunner) {
|
|
24
|
+
// The constraint is auto-named `org_units_code_key` by PostgreSQL, but it
|
|
25
|
+
// is looked up by shape rather than by that name so a database created
|
|
26
|
+
// through any other path still converges here.
|
|
27
|
+
await queryRunner.query(`
|
|
28
|
+
DO $$
|
|
29
|
+
DECLARE
|
|
30
|
+
target regclass := to_regclass('org_units');
|
|
31
|
+
existing_constraint text;
|
|
32
|
+
BEGIN
|
|
33
|
+
IF target IS NULL THEN
|
|
34
|
+
RETURN;
|
|
35
|
+
END IF;
|
|
36
|
+
|
|
37
|
+
SELECT con.conname INTO existing_constraint
|
|
38
|
+
FROM pg_constraint con
|
|
39
|
+
WHERE con.conrelid = target
|
|
40
|
+
AND con.contype = 'u'
|
|
41
|
+
AND con.conkey = ARRAY[(
|
|
42
|
+
SELECT att.attnum
|
|
43
|
+
FROM pg_attribute att
|
|
44
|
+
WHERE att.attrelid = target
|
|
45
|
+
AND att.attname = 'code'
|
|
46
|
+
AND NOT att.attisdropped
|
|
47
|
+
)]::smallint[];
|
|
48
|
+
|
|
49
|
+
IF existing_constraint IS NOT NULL THEN
|
|
50
|
+
EXECUTE format(
|
|
51
|
+
'ALTER TABLE %s DROP CONSTRAINT %I', target::text, existing_constraint
|
|
52
|
+
);
|
|
53
|
+
END IF;
|
|
54
|
+
END $$
|
|
55
|
+
`);
|
|
56
|
+
// Safe on an existing database: the constraint just dropped already
|
|
57
|
+
// guaranteed global uniqueness, so the live rows cannot collide.
|
|
58
|
+
await queryRunner.query(`
|
|
59
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "org_units_code_active_key"
|
|
60
|
+
ON "org_units" ("code")
|
|
61
|
+
WHERE "deleted_at" IS NULL
|
|
62
|
+
`);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Restoring the global constraint fails if any code has been reused since
|
|
66
|
+
* `up()` ran — which is precisely the operation this migration exists to
|
|
67
|
+
* allow. Free the duplicate codes first if you genuinely need to go back.
|
|
68
|
+
*/
|
|
69
|
+
async down(queryRunner) {
|
|
70
|
+
await queryRunner.query(`
|
|
71
|
+
DROP INDEX IF EXISTS "org_units_code_active_key"
|
|
72
|
+
`);
|
|
73
|
+
await queryRunner.query(`
|
|
74
|
+
ALTER TABLE "org_units"
|
|
75
|
+
ADD CONSTRAINT "org_units_code_key" UNIQUE ("code")
|
|
76
|
+
`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
exports.OrgUnitCodeActiveUnique0000000022000 = OrgUnitCodeActiveUnique0000000022000;
|
|
80
|
+
//# sourceMappingURL=0000000022000-org-unit-code-active-unique.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"0000000022000-org-unit-code-active-unique.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/migrations/0000000022000-org-unit-code-active-unique.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;GAcG;AACH,MAAa,oCAAoC;IAAjD;QACE,SAAI,GAAG,sCAAsC,CAAC;IA4DhD,CAAC;IA1DC,KAAK,CAAC,EAAE,CAAC,WAAwB;QAC/B,0EAA0E;QAC1E,uEAAuE;QACvE,+CAA+C;QAC/C,MAAM,WAAW,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA4BvB,CAAC,CAAC;QAEH,oEAAoE;QACpE,iEAAiE;QACjE,MAAM,WAAW,CAAC,KAAK,CAAC;;;;KAIvB,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,IAAI,CAAC,WAAwB;QACjC,MAAM,WAAW,CAAC,KAAK,CAAC;;KAEvB,CAAC,CAAC;QAEH,MAAM,WAAW,CAAC,KAAK,CAAC;;;KAGvB,CAAC,CAAC;IACL,CAAC;CACF;AA7DD,oFA6DC"}
|
|
@@ -22,3 +22,4 @@ export * from './0000000018000-notification-archive';
|
|
|
22
22
|
export * from './0000000019000-approval-template-activation';
|
|
23
23
|
export * from './0000000020000-form-data-option-snapshots';
|
|
24
24
|
export * from './0000000021000-notification-silenced';
|
|
25
|
+
export * from './0000000022000-org-unit-code-active-unique';
|
|
@@ -24,6 +24,7 @@ const _0000000018000_notification_archive_1 = require("./0000000018000-notificat
|
|
|
24
24
|
const _0000000019000_approval_template_activation_1 = require("./0000000019000-approval-template-activation");
|
|
25
25
|
const _0000000020000_form_data_option_snapshots_1 = require("./0000000020000-form-data-option-snapshots");
|
|
26
26
|
const _0000000021000_notification_silenced_1 = require("./0000000021000-notification-silenced");
|
|
27
|
+
const _0000000022000_org_unit_code_active_unique_1 = require("./0000000022000-org-unit-code-active-unique");
|
|
27
28
|
exports.BPM_CORE_MIGRATIONS = [
|
|
28
29
|
_0000000000001_enable_postgres_extensions_1.EnablePostgresExtensions0000000000001,
|
|
29
30
|
_0000000001000_identity_organization_foundation_1.IdentityOrganizationFoundation0000000001000,
|
|
@@ -47,6 +48,7 @@ exports.BPM_CORE_MIGRATIONS = [
|
|
|
47
48
|
_0000000019000_approval_template_activation_1.ApprovalTemplateActivation0000000019000,
|
|
48
49
|
_0000000020000_form_data_option_snapshots_1.FormDataOptionSnapshots0000000020000,
|
|
49
50
|
_0000000021000_notification_silenced_1.NotificationSilenced0000000021000,
|
|
51
|
+
_0000000022000_org_unit_code_active_unique_1.OrgUnitCodeActiveUnique0000000022000,
|
|
50
52
|
];
|
|
51
53
|
tslib_1.__exportStar(require("./0000000000001-enable-postgres-extensions"), exports);
|
|
52
54
|
tslib_1.__exportStar(require("./0000000001000-identity-organization-foundation"), exports);
|
|
@@ -70,4 +72,5 @@ tslib_1.__exportStar(require("./0000000018000-notification-archive"), exports);
|
|
|
70
72
|
tslib_1.__exportStar(require("./0000000019000-approval-template-activation"), exports);
|
|
71
73
|
tslib_1.__exportStar(require("./0000000020000-form-data-option-snapshots"), exports);
|
|
72
74
|
tslib_1.__exportStar(require("./0000000021000-notification-silenced"), exports);
|
|
75
|
+
tslib_1.__exportStar(require("./0000000022000-org-unit-code-active-unique"), exports);
|
|
73
76
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/migrations/index.ts"],"names":[],"mappings":";;;;AACA,0GAAmG;AACnG,sHAA+G;AAC/G,oGAA6F;AAC7F,8GAAuG;AACvG,0GAAmG;AACnG,sFAAgF;AAChF,wFAAkF;AAClF,kGAA4F;AAC5F,8GAAuG;AACvG,oFAA8E;AAC9E,4GAAqG;AACrG,sHAA8G;AAC9G,8GAAuG;AACvG,kGAA2F;AAC3F,oGAA8F;AAC9F,kIAA0H;AAC1H,8GAAsG;AACtG,sFAAgF;AAChF,8FAAwF;AACxF,8GAAuG;AACvG,0GAAkG;AAClG,gGAA0F;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/migrations/index.ts"],"names":[],"mappings":";;;;AACA,0GAAmG;AACnG,sHAA+G;AAC/G,oGAA6F;AAC7F,8GAAuG;AACvG,0GAAmG;AACnG,sFAAgF;AAChF,wFAAkF;AAClF,kGAA4F;AAC5F,8GAAuG;AACvG,oFAA8E;AAC9E,4GAAqG;AACrG,sHAA8G;AAC9G,8GAAuG;AACvG,kGAA2F;AAC3F,oGAA8F;AAC9F,kIAA0H;AAC1H,8GAAsG;AACtG,sFAAgF;AAChF,8FAAwF;AACxF,8GAAuG;AACvG,0GAAkG;AAClG,gGAA0F;AAC1F,4GAAmG;AAEtF,QAAA,mBAAmB,GAA8C;IAC5E,iFAAqC;IACrC,6FAA2C;IAC3C,2EAAkC;IAClC,qFAAuC;IACvC,iFAAqC;IACrC,8DAA4B;IAC5B,gEAA6B;IAC7B,0EAAkC;IAClC,qFAAuC;IACvC,4DAA2B;IAC3B,mFAAsC;IACtC,4FAA0C;IAC1C,qFAAuC;IACvC,yEAAiC;IACjC,4EAAmC;IACnC,wGAAgD;IAChD,oFAAsC;IACtC,8DAA4B;IAC5B,sEAAgC;IAChC,qFAAuC;IACvC,gFAAoC;IACpC,wEAAiC;IACjC,iFAAoC;CACrC,CAAC;AAEF,qFAA2D;AAC3D,2FAAiE;AACjE,kFAAwD;AACxD,uFAA6D;AAC7D,qFAA2D;AAC3D,2EAAiD;AACjD,4EAAkD;AAClD,iFAAuD;AACvD,uFAA6D;AAC7D,0EAAgD;AAChD,sFAA4D;AAC5D,2FAAiE;AACjE,uFAA6D;AAC7D,iFAAuD;AACvD,kFAAwD;AACxD,iGAAuE;AACvE,uFAA6D;AAC7D,2EAAiD;AACjD,+EAAqD;AACrD,uFAA6D;AAC7D,qFAA2D;AAC3D,gFAAsD;AACtD,sFAA4D"}
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { OrgUnitTypeEnum } from './organization.enums';
|
|
2
|
+
/**
|
|
3
|
+
* Codes are unique among *live* units only.
|
|
4
|
+
*
|
|
5
|
+
* A plain `unique: true` on `code` would outlive the soft delete and burn the
|
|
6
|
+
* code forever, while `assertOrgUnitCodeAvailable` — which ignores
|
|
7
|
+
* soft-deleted rows — would keep clearing it and let the insert fail in the
|
|
8
|
+
* driver instead. Keep this partial index in step with migration
|
|
9
|
+
* `OrgUnitCodeActiveUnique0000000022000`.
|
|
10
|
+
*/
|
|
2
11
|
export declare class OrgUnitEntity {
|
|
3
12
|
id: string;
|
|
4
13
|
parentId: string | null;
|
|
@@ -5,6 +5,15 @@ const tslib_1 = require("tslib");
|
|
|
5
5
|
const graphql_1 = require("@nestjs/graphql");
|
|
6
6
|
const typeorm_1 = require("typeorm");
|
|
7
7
|
const organization_enums_1 = require("./organization.enums");
|
|
8
|
+
/**
|
|
9
|
+
* Codes are unique among *live* units only.
|
|
10
|
+
*
|
|
11
|
+
* A plain `unique: true` on `code` would outlive the soft delete and burn the
|
|
12
|
+
* code forever, while `assertOrgUnitCodeAvailable` — which ignores
|
|
13
|
+
* soft-deleted rows — would keep clearing it and let the insert fail in the
|
|
14
|
+
* driver instead. Keep this partial index in step with migration
|
|
15
|
+
* `OrgUnitCodeActiveUnique0000000022000`.
|
|
16
|
+
*/
|
|
8
17
|
let OrgUnitEntity = class OrgUnitEntity {
|
|
9
18
|
};
|
|
10
19
|
exports.OrgUnitEntity = OrgUnitEntity;
|
|
@@ -19,7 +28,7 @@ tslib_1.__decorate([
|
|
|
19
28
|
tslib_1.__metadata("design:type", Object)
|
|
20
29
|
], OrgUnitEntity.prototype, "parentId", void 0);
|
|
21
30
|
tslib_1.__decorate([
|
|
22
|
-
(0, typeorm_1.Column)('text'
|
|
31
|
+
(0, typeorm_1.Column)('text'),
|
|
23
32
|
(0, graphql_1.Field)(),
|
|
24
33
|
tslib_1.__metadata("design:type", String)
|
|
25
34
|
], OrgUnitEntity.prototype, "code", void 0);
|
|
@@ -58,6 +67,10 @@ tslib_1.__decorate([
|
|
|
58
67
|
tslib_1.__metadata("design:type", Date)
|
|
59
68
|
], OrgUnitEntity.prototype, "updatedAt", void 0);
|
|
60
69
|
exports.OrgUnitEntity = OrgUnitEntity = tslib_1.__decorate([
|
|
70
|
+
(0, typeorm_1.Index)('org_units_code_active_key', ['code'], {
|
|
71
|
+
unique: true,
|
|
72
|
+
where: '"deleted_at" IS NULL',
|
|
73
|
+
}),
|
|
61
74
|
(0, typeorm_1.Entity)('org_units'),
|
|
62
75
|
(0, graphql_1.ObjectType)('OrgUnit')
|
|
63
76
|
], OrgUnitEntity);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"org-unit.entity.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/organization/org-unit.entity.ts"],"names":[],"mappings":";;;;AAAA,6CAAwD;AACxD,
|
|
1
|
+
{"version":3,"file":"org-unit.entity.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/organization/org-unit.entity.ts"],"names":[],"mappings":";;;;AAAA,6CAAwD;AACxD,qCAQiB;AACjB,6DAAuD;AAEvD;;;;;;;;GAQG;AAOI,IAAM,aAAa,GAAnB,MAAM,aAAa;CAuCzB,CAAA;AAvCY,sCAAa;AAGxB;IAFC,IAAA,gCAAsB,EAAC,MAAM,CAAC;IAC9B,IAAA,eAAK,EAAC,GAAG,EAAE,CAAC,YAAE,CAAC;;yCACJ;AAIZ;IAFC,IAAA,gBAAM,EAAC,MAAM,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACrD,IAAA,eAAK,EAAC,GAAG,EAAE,CAAC,YAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;+CACX;AAIzB;IAFC,IAAA,gBAAM,EAAC,MAAM,CAAC;IACd,IAAA,eAAK,GAAE;;2CACM;AAId;IAFC,IAAA,gBAAM,EAAC,MAAM,CAAC;IACd,IAAA,eAAK,GAAE;;2CACM;AAId;IAFC,IAAA,gBAAM,EAAC,MAAM,CAAC;IACd,IAAA,eAAK,EAAC,GAAG,EAAE,CAAC,oCAAe,CAAC;;2CACN;AAIvB;IAFC,IAAA,gBAAM,EAAC,OAAO,CAAC;IACf,IAAA,eAAK,GAAE;;2CACM;AAGd;IADC,IAAA,gBAAM,EAAC,OAAO,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;;+CACY;AAI7C;IAFC,IAAA,0BAAgB,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;IAC7D,IAAA,eAAK,EAAC,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;gDACd;AAIxB;IAFC,IAAA,0BAAgB,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;IAC7D,IAAA,eAAK,GAAE;sCACI,IAAI;gDAAC;AAIjB;IAFC,IAAA,0BAAgB,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;IAC7D,IAAA,eAAK,GAAE;sCACI,IAAI;gDAAC;wBAtCN,aAAa;IANzB,IAAA,eAAK,EAAC,2BAA2B,EAAE,CAAC,MAAM,CAAC,EAAE;QAC5C,MAAM,EAAE,IAAI;QACZ,KAAK,EAAE,sBAAsB;KAC9B,CAAC;IACD,IAAA,gBAAM,EAAC,WAAW,CAAC;IACnB,IAAA,oBAAU,EAAC,SAAS,CAAC;GACT,aAAa,CAuCzB"}
|