@happyvertical/smrt-human-resources 0.0.0-stage → 0.53.6
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/AGENTS.md +297 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +291 -2
- package/dist/__smrt-register__.d.ts +2 -0
- package/dist/__smrt-register__.d.ts.map +1 -0
- package/dist/dates.d.ts +25 -0
- package/dist/dates.d.ts.map +1 -0
- package/dist/employment/index.d.ts +3 -0
- package/dist/employment/index.d.ts.map +1 -0
- package/dist/employment/models.d.ts +73 -0
- package/dist/employment/models.d.ts.map +1 -0
- package/dist/employment/queries.d.ts +12 -0
- package/dist/employment/queries.d.ts.map +1 -0
- package/dist/employment/service.d.ts +277 -0
- package/dist/employment/service.d.ts.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2068 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.json +2424 -0
- package/dist/qualifications/index.d.ts +3 -0
- package/dist/qualifications/index.d.ts.map +1 -0
- package/dist/qualifications/internal.d.ts +24 -0
- package/dist/qualifications/internal.d.ts.map +1 -0
- package/dist/qualifications/models.d.ts +77 -0
- package/dist/qualifications/models.d.ts.map +1 -0
- package/dist/qualifications/service.d.ts +381 -0
- package/dist/qualifications/service.d.ts.map +1 -0
- package/dist/service-base.d.ts +47 -0
- package/dist/service-base.d.ts.map +1 -0
- package/dist/smrt-knowledge.json +902 -0
- package/dist/svelte/components/EmployeeForm.svelte +369 -0
- package/dist/svelte/components/EmployeeForm.svelte.d.ts +28 -0
- package/dist/svelte/components/EmployeeForm.svelte.d.ts.map +1 -0
- package/dist/svelte/components/EmployeeList.svelte +158 -0
- package/dist/svelte/components/EmployeeList.svelte.d.ts +13 -0
- package/dist/svelte/components/EmployeeList.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ExpiringQualificationsList.svelte +126 -0
- package/dist/svelte/components/ExpiringQualificationsList.svelte.d.ts +13 -0
- package/dist/svelte/components/ExpiringQualificationsList.svelte.d.ts.map +1 -0
- package/dist/svelte/components/PersonQualifications.svelte +156 -0
- package/dist/svelte/components/PersonQualifications.svelte.d.ts +15 -0
- package/dist/svelte/components/PersonQualifications.svelte.d.ts.map +1 -0
- package/dist/svelte/i18n.d.ts +67 -0
- package/dist/svelte/i18n.d.ts.map +1 -0
- package/dist/svelte/i18n.js +67 -0
- package/dist/svelte/index.d.ts +23 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +25 -0
- package/dist/svelte/types.d.ts +225 -0
- package/dist/svelte/types.d.ts.map +1 -0
- package/dist/svelte/types.js +257 -0
- package/dist/types.d.ts +119 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +23 -0
- package/dist/types.js.map +1 -0
- package/dist/ui.d.ts +17 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +65 -0
- package/dist/ui.js.map +1 -0
- package/dist/write.d.ts +22 -0
- package/dist/write.d.ts.map +1 -0
- package/package.json +86 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/qualifications/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { DatabaseInterface } from '@happyvertical/sql';
|
|
2
|
+
import type { HrEventQueue } from '../service-base.js';
|
|
3
|
+
import type { HrActor, IsoDate } from '../types.js';
|
|
4
|
+
import { type HeldQualification } from './models.js';
|
|
5
|
+
/** Reason recorded when an employment-scoped qualification ends with its employment. */
|
|
6
|
+
export declare const EMPLOYMENT_ENDED_REASON = "employment-ended";
|
|
7
|
+
/**
|
|
8
|
+
* End every qualification that belongs to an employment, from `effectiveOn`
|
|
9
|
+
* (the day after the last day employed). Called by EmploymentService inside
|
|
10
|
+
* the transaction that ends the employment, so the two never disagree.
|
|
11
|
+
* Person-scoped qualifications are untouched.
|
|
12
|
+
*
|
|
13
|
+
* The decision is made per renewal chain, from the chain's dated history and
|
|
14
|
+
* not from any stored status: the latest row of a chain gets the
|
|
15
|
+
* `employment-ended` revocation, and one event, unless a revocation effective
|
|
16
|
+
* on or before `effectiveOn` is already recorded anywhere in the chain. The
|
|
17
|
+
* rows that latest row renewed are not written: the dated replay cuts them
|
|
18
|
+
* off through the chain. A latest row whose stored status is already
|
|
19
|
+
* `revoked` (a later-dated revocation was recorded) is not saved again; only
|
|
20
|
+
* the change row is appended, and the replay treats the earliest revocation
|
|
21
|
+
* as in force. Returns the latest rows that were cut off.
|
|
22
|
+
*/
|
|
23
|
+
export declare function revokeEmploymentQualifications(db: DatabaseInterface, actor: Readonly<HrActor>, employmentId: string, effectiveOn: IsoDate, queue: HrEventQueue): Promise<HeldQualification[]>;
|
|
24
|
+
//# sourceMappingURL=internal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../../src/qualifications/internal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEpD,OAAO,EACL,KAAK,iBAAiB,EAIvB,MAAM,aAAa,CAAC;AAErB,wFAAwF;AACxF,eAAO,MAAM,uBAAuB,qBAAqB,CAAC;AAE1D;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,8BAA8B,CAClD,EAAE,EAAE,iBAAiB,EACrB,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,EACxB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,OAAO,EACpB,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,iBAAiB,EAAE,CAAC,CA4D9B"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { SmrtCollection, SmrtObject } from '@happyvertical/smrt-core';
|
|
2
|
+
import { type HeldQualificationChangeKind, type HeldQualificationStatus, type IsoDate, type QualificationKind, type QualificationScope } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* A qualification definition: a managed list per tenant. Other packages gate
|
|
5
|
+
* work by referencing its id (`@happyvertical/smrt-human-resources:Qualification`).
|
|
6
|
+
* Not a permission: who may use the software stays with smrt-users.
|
|
7
|
+
*/
|
|
8
|
+
export declare class Qualification extends SmrtObject {
|
|
9
|
+
tenantId: string;
|
|
10
|
+
/** Lowercase kebab-case, unique per tenant: `first-aid`. */
|
|
11
|
+
key: string;
|
|
12
|
+
name: string;
|
|
13
|
+
kind: QualificationKind;
|
|
14
|
+
issuingBody: string;
|
|
15
|
+
/** Whether held qualifications of this kind expire. */
|
|
16
|
+
expires: boolean;
|
|
17
|
+
/** Default validity in months, applied when a grant gives no expiry. */
|
|
18
|
+
validityMonths: number | null;
|
|
19
|
+
/** Whether a held qualification belongs to the person or the employment. */
|
|
20
|
+
scope: QualificationScope;
|
|
21
|
+
isActive: boolean;
|
|
22
|
+
save(): Promise<this>;
|
|
23
|
+
delete(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A person holds a qualification. `employmentId` is set when the definition's
|
|
27
|
+
* scope is `employment`; such a row is revoked when that employment ends.
|
|
28
|
+
* A renewal is a new row pointing at the one it renews, so past dates still
|
|
29
|
+
* answer correctly. Expiry on a date is computed from `expiresOn`; the stored
|
|
30
|
+
* `expired` status is set by the service sweep. The stored status records the
|
|
31
|
+
* last change made, not standing on a date, and `revoked` is final: a row
|
|
32
|
+
* stored `revoked` is saved again only to record a verification.
|
|
33
|
+
*/
|
|
34
|
+
export declare class HeldQualification extends SmrtObject {
|
|
35
|
+
tenantId: string;
|
|
36
|
+
qualificationId: string;
|
|
37
|
+
profileId: string;
|
|
38
|
+
employmentId: string | null;
|
|
39
|
+
issuedOn: IsoDate;
|
|
40
|
+
expiresOn: IsoDate | null;
|
|
41
|
+
certificateNumber: string;
|
|
42
|
+
status: HeldQualificationStatus;
|
|
43
|
+
verifiedByProfileId: string | null;
|
|
44
|
+
verifiedAt: Date | null;
|
|
45
|
+
documentAssetId: string | null;
|
|
46
|
+
renewalOfId: string | null;
|
|
47
|
+
save(): Promise<this>;
|
|
48
|
+
delete(): Promise<void>;
|
|
49
|
+
}
|
|
50
|
+
/** Append-only dated change to a held qualification, attributed to an actor. */
|
|
51
|
+
export declare class HeldQualificationChange extends SmrtObject {
|
|
52
|
+
tenantId: string;
|
|
53
|
+
heldQualificationId: string;
|
|
54
|
+
kind: HeldQualificationChangeKind;
|
|
55
|
+
effectiveOn: IsoDate;
|
|
56
|
+
reason: string;
|
|
57
|
+
actorProfileId: string;
|
|
58
|
+
save(): Promise<this>;
|
|
59
|
+
delete(): Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Scoped definition collection; mutations must use QualificationService.
|
|
63
|
+
* Collections are registered too, so each restates the closed surface: an
|
|
64
|
+
* undecorated one would publish its methods as generated tools.
|
|
65
|
+
*/
|
|
66
|
+
export declare class QualificationCollection extends SmrtCollection<Qualification> {
|
|
67
|
+
static readonly _itemClass: typeof Qualification;
|
|
68
|
+
}
|
|
69
|
+
/** Scoped held-qualification collection; closed like its model. */
|
|
70
|
+
export declare class HeldQualificationCollection extends SmrtCollection<HeldQualification> {
|
|
71
|
+
static readonly _itemClass: typeof HeldQualification;
|
|
72
|
+
}
|
|
73
|
+
/** Scoped append-only change collection; closed like its model. */
|
|
74
|
+
export declare class HeldQualificationChangeCollection extends SmrtCollection<HeldQualificationChange> {
|
|
75
|
+
static readonly _itemClass: typeof HeldQualificationChange;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=models.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/qualifications/models.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,cAAc,EACd,UAAU,EAEX,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,KAAK,2BAA2B,EAChC,KAAK,uBAAuB,EAE5B,KAAK,OAAO,EACZ,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACxB,MAAM,aAAa,CAAC;AAGrB;;;;GAIG;AACH,qBASa,aAAc,SAAQ,UAAU;IAC/B,QAAQ,SAAM;IAC1B,4DAA4D;IAC5D,GAAG,SAAM;IACT,IAAI,SAAM;IACe,IAAI,EAAE,iBAAiB,CAAmB;IACnE,WAAW,SAAM;IACjB,uDAAuD;IACvD,OAAO,UAAS;IAChB,wEAAwE;IAExE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAQ;IACrC,4EAA4E;IACnD,KAAK,EAAE,kBAAkB,CAAY;IAC9D,QAAQ,UAAQ;IAED,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAYrB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAMvC;AAmBD;;;;;;;;GAQG;AACH,qBAkBa,iBAAkB,SAAQ,UAAU;IACnC,QAAQ,SAAM;IAC2B,eAAe,SAAM;IAI1E,SAAS,SAAM;IAEf,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IACV,QAAQ,EAAE,OAAO,CAAM;IACP,SAAS,EAAE,OAAO,GAAG,IAAI,CAAQ;IAC1E,iBAAiB,SAAM;IACE,MAAM,EAAE,uBAAuB,CAAW;IAKnE,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC1C,UAAU,EAAE,IAAI,GAAG,IAAI,CAAQ;IAE/B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEtC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA+BrB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAMvC;AAED,gFAAgF;AAChF,qBAca,uBAAwB,SAAQ,UAAU;IACzC,QAAQ,SAAM;IAE1B,mBAAmB,SAAM;IACA,IAAI,EAAE,2BAA2B,CAAa;IAC9C,WAAW,EAAE,OAAO,CAAM;IACnD,MAAM,SAAM;IAIZ,cAAc,SAAM;IAEL,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAUrB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAMvC;AAED;;;;GAIG;AACH,qBACa,uBAAwB,SAAQ,cAAc,CAAC,aAAa,CAAC;IACxE,MAAM,CAAC,QAAQ,CAAC,UAAU,uBAAiB;CAC5C;AACD,mEAAmE;AACnE,qBACa,2BAA4B,SAAQ,cAAc,CAAC,iBAAiB,CAAC;IAChF,MAAM,CAAC,QAAQ,CAAC,UAAU,2BAAqB;CAChD;AACD,mEAAmE;AACnE,qBACa,iCAAkC,SAAQ,cAAc,CAAC,uBAAuB,CAAC;IAC5F,MAAM,CAAC,QAAQ,CAAC,UAAU,iCAA2B;CACtD"}
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import { HrService } from '../service-base.js';
|
|
2
|
+
import { type HeldQualificationStatus, type IsoDate, type QualificationCheck, type QualificationKind, type QualificationScope } from '../types.js';
|
|
3
|
+
import { HeldQualification, HeldQualificationChange, Qualification } from './models.js';
|
|
4
|
+
/** Input for {@link QualificationService.define} and {@link QualificationService.seed}. */
|
|
5
|
+
export interface DefineQualificationInput {
|
|
6
|
+
/** Lowercase kebab-case, unique per tenant: `first-aid`. */
|
|
7
|
+
key: string;
|
|
8
|
+
/** Display name. */
|
|
9
|
+
name: string;
|
|
10
|
+
/** What the qualification is. */
|
|
11
|
+
kind: QualificationKind;
|
|
12
|
+
/** Who issues it; free text. */
|
|
13
|
+
issuingBody?: string;
|
|
14
|
+
/** Whether held qualifications of this kind expire. Defaults to false. */
|
|
15
|
+
expires?: boolean;
|
|
16
|
+
/** Default validity in months; only meaningful when `expires` is true. */
|
|
17
|
+
validityMonths?: number | null;
|
|
18
|
+
/** Defaults to `employment` for kind `authorization`, else `person`. */
|
|
19
|
+
scope?: QualificationScope;
|
|
20
|
+
}
|
|
21
|
+
/** Input for {@link QualificationService.update}. Key and scope never change. */
|
|
22
|
+
export interface UpdateQualificationInput {
|
|
23
|
+
/** New display name. */
|
|
24
|
+
name?: string;
|
|
25
|
+
/** New issuing body. */
|
|
26
|
+
issuingBody?: string;
|
|
27
|
+
/** Whether new grants expire; turning it off clears `validityMonths`. */
|
|
28
|
+
expires?: boolean;
|
|
29
|
+
/** New default validity in months, or null for none. */
|
|
30
|
+
validityMonths?: number | null;
|
|
31
|
+
/** Deactivate (or reactivate) the definition; inactive ones cannot be granted. */
|
|
32
|
+
isActive?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/** Options for {@link QualificationService.list}. */
|
|
35
|
+
export interface ListQualificationsOptions {
|
|
36
|
+
/** Include deactivated definitions. Defaults to false. */
|
|
37
|
+
includeInactive?: boolean;
|
|
38
|
+
}
|
|
39
|
+
/** Input for {@link QualificationService.grant}. */
|
|
40
|
+
export interface GrantQualificationInput {
|
|
41
|
+
/** The definition being granted. */
|
|
42
|
+
qualificationId: string;
|
|
43
|
+
/** The person receiving it. */
|
|
44
|
+
profileId: string;
|
|
45
|
+
/** First day it is valid. */
|
|
46
|
+
issuedOn: IsoDate;
|
|
47
|
+
/** Last day it is valid (inclusive). Defaults from the definition's validity. */
|
|
48
|
+
expiresOn?: IsoDate | null;
|
|
49
|
+
/** Certificate or ticket number; free text. */
|
|
50
|
+
certificateNumber?: string;
|
|
51
|
+
/** Optional `smrt-assets:Asset` id of the scanned document. */
|
|
52
|
+
documentAssetId?: string | null;
|
|
53
|
+
/** Record the actor as having verified the document, now. */
|
|
54
|
+
verified?: boolean;
|
|
55
|
+
/** For an employment-scoped definition; resolved from the profile when omitted. */
|
|
56
|
+
employmentId?: string | null;
|
|
57
|
+
}
|
|
58
|
+
/** Input for {@link QualificationService.renew}. */
|
|
59
|
+
export interface RenewQualificationInput {
|
|
60
|
+
/** First day the renewal is valid. */
|
|
61
|
+
issuedOn: IsoDate;
|
|
62
|
+
/** Last day it is valid (inclusive). Defaults from the definition's validity. */
|
|
63
|
+
expiresOn?: IsoDate | null;
|
|
64
|
+
/** Certificate or ticket number of the renewal. */
|
|
65
|
+
certificateNumber?: string;
|
|
66
|
+
/** Optional `smrt-assets:Asset` id of the renewal's document. */
|
|
67
|
+
documentAssetId?: string | null;
|
|
68
|
+
/** Record the actor as having verified the document, now. */
|
|
69
|
+
verified?: boolean;
|
|
70
|
+
}
|
|
71
|
+
/** Input for suspending or revoking a held qualification. */
|
|
72
|
+
export interface HeldQualificationReasonInput {
|
|
73
|
+
/** The calendar date the change takes effect (that day included). */
|
|
74
|
+
effectiveOn: IsoDate;
|
|
75
|
+
/** Why; required. */
|
|
76
|
+
reason: string;
|
|
77
|
+
}
|
|
78
|
+
/** Input for {@link QualificationService.reinstate}. */
|
|
79
|
+
export interface ReinstateQualificationInput {
|
|
80
|
+
/** The calendar date the qualification is good again (that day included). */
|
|
81
|
+
effectiveOn: IsoDate;
|
|
82
|
+
/** Optional note. */
|
|
83
|
+
reason?: string;
|
|
84
|
+
}
|
|
85
|
+
/** Input for {@link QualificationService.verify}. */
|
|
86
|
+
export interface VerifyQualificationInput {
|
|
87
|
+
/**
|
|
88
|
+
* `smrt-assets:Asset` id of the document to attach or replace; null removes
|
|
89
|
+
* it, and omitting it keeps the current one.
|
|
90
|
+
*/
|
|
91
|
+
documentAssetId?: string | null;
|
|
92
|
+
}
|
|
93
|
+
/** Options for {@link QualificationService.holders}. */
|
|
94
|
+
export interface QualificationHoldersOptions {
|
|
95
|
+
/** Only people with an employment term covering the date. */
|
|
96
|
+
employedOnly?: boolean;
|
|
97
|
+
}
|
|
98
|
+
/** Options for {@link QualificationService.sweepExpired}. */
|
|
99
|
+
export interface SweepExpiredOptions {
|
|
100
|
+
/**
|
|
101
|
+
* Only look at qualifications whose last day is on or after this date.
|
|
102
|
+
* Bounds the work of a scheduled sweep; omit it to look at all history.
|
|
103
|
+
*/
|
|
104
|
+
since?: IsoDate | null;
|
|
105
|
+
}
|
|
106
|
+
/** Options for {@link QualificationService.expiringWithin}. */
|
|
107
|
+
export interface ExpiringQualificationsOptions {
|
|
108
|
+
/** Only people with an employment term covering `today`. */
|
|
109
|
+
employedOnly?: boolean;
|
|
110
|
+
}
|
|
111
|
+
/** Status of a held qualification computed for one calendar date. */
|
|
112
|
+
export type HeldQualificationStatusOn = HeldQualificationStatus | 'not-yet-issued';
|
|
113
|
+
/** One row of {@link QualificationService.listForProfile}. */
|
|
114
|
+
export interface ProfileQualification {
|
|
115
|
+
/** What the person holds. */
|
|
116
|
+
held: HeldQualification;
|
|
117
|
+
/** Its definition. */
|
|
118
|
+
qualification: Qualification;
|
|
119
|
+
/** Status computed for the requested date. */
|
|
120
|
+
status: HeldQualificationStatusOn;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* A small, generic starter list an application may pass to
|
|
124
|
+
* {@link QualificationService.seed}. Illustrative only: validity periods and
|
|
125
|
+
* what a restriction means are the application's and its jurisdiction's call.
|
|
126
|
+
*/
|
|
127
|
+
export declare const SUGGESTED_QUALIFICATIONS: readonly DefineQualificationInput[];
|
|
128
|
+
/**
|
|
129
|
+
* The only writer of qualification definitions and held qualifications, and
|
|
130
|
+
* the gate ({@link QualificationService.check}) applications call before
|
|
131
|
+
* letting a person do qualified work. Constructed from a trusted
|
|
132
|
+
* tenant/profile actor the application has already authorized.
|
|
133
|
+
*
|
|
134
|
+
* Whether a qualification is good on a date is computed from `issuedOn`,
|
|
135
|
+
* `expiresOn` and the dated change history, so past dates keep answering
|
|
136
|
+
* correctly and expiry needs no scheduler. The stored status records the
|
|
137
|
+
* last change made (a revocation or suspension stores at once, even when it
|
|
138
|
+
* takes effect later), so every rule that asks about standing replays the
|
|
139
|
+
* dated history instead, including the rules that decide whether a
|
|
140
|
+
* qualification may be suspended, reinstated, revoked or renewed. A stored
|
|
141
|
+
* `revoked` is final: later changes to such a row are change rows only. The
|
|
142
|
+
* stored `expired` status is written by
|
|
143
|
+
* {@link QualificationService.sweepExpired}, and by a reinstatement of a row
|
|
144
|
+
* whose lapse the sweep has recorded.
|
|
145
|
+
*/
|
|
146
|
+
export declare class QualificationService extends HrService {
|
|
147
|
+
/** Add a qualification definition; its key is unique per tenant. */
|
|
148
|
+
define(input: DefineQualificationInput): Promise<Qualification>;
|
|
149
|
+
/** Change a definition's descriptive fields; key and scope are immutable. */
|
|
150
|
+
update(qualificationId: string, input: UpdateQualificationInput): Promise<Qualification>;
|
|
151
|
+
/**
|
|
152
|
+
* Create the definitions whose key is missing and leave existing rows
|
|
153
|
+
* untouched. Returns one definition per input, in input order.
|
|
154
|
+
*/
|
|
155
|
+
seed(definitions: readonly DefineQualificationInput[]): Promise<Qualification[]>;
|
|
156
|
+
/** The tenant's definitions ordered by key; active ones unless asked. */
|
|
157
|
+
list(options?: ListQualificationsOptions): Promise<Qualification[]>;
|
|
158
|
+
/**
|
|
159
|
+
* One definition by id.
|
|
160
|
+
*
|
|
161
|
+
* @throws HrError `HR_NOT_FOUND` outside the actor's tenant, `HR_INVALID` for a blank id
|
|
162
|
+
*/
|
|
163
|
+
get(qualificationId: string): Promise<Qualification>;
|
|
164
|
+
/** One definition by key, or null. */
|
|
165
|
+
findByKey(key: string): Promise<Qualification | null>;
|
|
166
|
+
/**
|
|
167
|
+
* Record that a person holds a qualification from `issuedOn`.
|
|
168
|
+
*
|
|
169
|
+
* A person has one live grant of a qualification at a time, decided by
|
|
170
|
+
* date: the grant is refused unless everything they already hold of it is
|
|
171
|
+
* revoked effective on or before `issuedOn`. A lapsed ticket (swept or not)
|
|
172
|
+
* is renewed, not granted again, and a revocation scheduled for a later
|
|
173
|
+
* date does not free an earlier issue date.
|
|
174
|
+
*
|
|
175
|
+
* An employment-scoped grant dated inside a term whose end is already
|
|
176
|
+
* recorded ends with that employment: the `employment-ended` revocation,
|
|
177
|
+
* effective the day after the term's last day, is recorded in the same
|
|
178
|
+
* transaction and the row is stored `revoked`.
|
|
179
|
+
*
|
|
180
|
+
* @throws HrError `HR_ALREADY_HELD` unless every earlier grant is revoked effective on or before `issuedOn` (renew the existing one instead), `HR_QUALIFICATION_SCOPE`, `HR_INVALID`, `HR_NOT_FOUND`
|
|
181
|
+
*/
|
|
182
|
+
grant(input: GrantQualificationInput): Promise<HeldQualification>;
|
|
183
|
+
/**
|
|
184
|
+
* Renew a held qualification: a new row pointing at the old one. The old row
|
|
185
|
+
* is not modified, so checks on past dates still answer from it. Only the
|
|
186
|
+
* latest row of a chain can be renewed.
|
|
187
|
+
*
|
|
188
|
+
* The renewal is validated against the old row's dated history, not its
|
|
189
|
+
* stored status: `issuedOn` cannot be earlier than the row's latest
|
|
190
|
+
* suspension or reinstatement (the renewal would otherwise answer for dates
|
|
191
|
+
* the history says were suspended), the row must not be suspended on
|
|
192
|
+
* `issuedOn`, and it must not be revoked on or before `issuedOn`.
|
|
193
|
+
*
|
|
194
|
+
* A revocation recorded for a later date (a scheduled one, or the end of an
|
|
195
|
+
* employment whose last day is ahead) does not stop a renewal: the
|
|
196
|
+
* qualification is still in force, so a ticket that lapses first can be
|
|
197
|
+
* renewed. The renewal carries the cutoff forward. It is stored `revoked`
|
|
198
|
+
* with a revocation at the earlier of the old row's pending revocation
|
|
199
|
+
* (with its reason) and, for an employment-scoped one inside a term whose
|
|
200
|
+
* end is recorded, the day after the last day employed
|
|
201
|
+
* (`employment-ended`); `held-qualification.renewed` and
|
|
202
|
+
* `held-qualification.revoked` are both raised.
|
|
203
|
+
*
|
|
204
|
+
* @throws HrError `HR_STATUS_TRANSITION` when suspended on `issuedOn` (reinstate it first); `HR_ALREADY_HELD` when the person holds another grant outside this chain that is not revoked on or before `issuedOn` and starts before the day this renewal is cut off; `HR_INVALID` for a row revoked on or before `issuedOn`, an already renewed row, an `issuedOn` before the row's own or before its latest suspension or reinstatement, or an expiry earlier than the row's own (revoke it and grant a new one instead); `HR_NOT_FOUND`
|
|
205
|
+
*/
|
|
206
|
+
renew(heldQualificationId: string, input: RenewQualificationInput): Promise<HeldQualification>;
|
|
207
|
+
/**
|
|
208
|
+
* Suspend a qualification from `effectiveOn` until it is reinstated. Act on
|
|
209
|
+
* the latest row of a renewal chain: the whole chain shares its standing,
|
|
210
|
+
* so the rows it renewed stop passing checks too.
|
|
211
|
+
*
|
|
212
|
+
* Whether it can be suspended is decided from the chain's dated history as
|
|
213
|
+
* of `effectiveOn`, never from the stored status: it must be neither
|
|
214
|
+
* suspended nor revoked on that day. A revocation recorded for a later date
|
|
215
|
+
* (scheduled, or the end of an employment whose last day is ahead) does not
|
|
216
|
+
* prevent it, and neither does a stored `expired`.
|
|
217
|
+
*
|
|
218
|
+
* `effectiveOn` may be any date from the chain's first issue date onward,
|
|
219
|
+
* not before the chain's latest suspension or reinstatement. It may precede
|
|
220
|
+
* the latest row's own issue date: with a renewal recorded ahead of time,
|
|
221
|
+
* that is how the ticket in force today is suspended, and the renewal is
|
|
222
|
+
* then suspended from its issue date until reinstated.
|
|
223
|
+
*
|
|
224
|
+
* The stored status becomes `suspended`, except on a row already stored
|
|
225
|
+
* `revoked`, which keeps it: only the change row is added.
|
|
226
|
+
*
|
|
227
|
+
* @throws HrError `HR_INVALID` for a row that was renewed, a date outside that range or a bad input; `HR_STATUS_TRANSITION` when suspended or revoked on `effectiveOn`; `HR_NOT_FOUND`
|
|
228
|
+
*/
|
|
229
|
+
suspend(heldQualificationId: string, input: HeldQualificationReasonInput): Promise<HeldQualification>;
|
|
230
|
+
/**
|
|
231
|
+
* Lift a suspension from `effectiveOn`, which follows the same date rule as
|
|
232
|
+
* {@link suspend}. The chain must be suspended on that day by its dated
|
|
233
|
+
* history, and not yet revoked. The stored status becomes `valid`, or
|
|
234
|
+
* `expired` when the sweep has recorded the row's lapse; a stored `revoked`
|
|
235
|
+
* stays.
|
|
236
|
+
*
|
|
237
|
+
* @throws HrError `HR_INVALID` for a row that was renewed, a date before the suspension or a bad input; `HR_STATUS_TRANSITION` unless suspended on `effectiveOn`; `HR_NOT_FOUND`
|
|
238
|
+
*/
|
|
239
|
+
reinstate(heldQualificationId: string, input: ReinstateQualificationInput): Promise<HeldQualification>;
|
|
240
|
+
/**
|
|
241
|
+
* Revoke a qualification from `effectiveOn`. Terminal: grant a new one
|
|
242
|
+
* dated on or after it. Act on the latest row of a renewal chain: the rows
|
|
243
|
+
* it renewed stop passing checks from the same date; dated before a renewal
|
|
244
|
+
* recorded ahead of time, the renewal is revoked from its issue date.
|
|
245
|
+
*
|
|
246
|
+
* `effectiveOn` may be any date from the chain's first issue date onward,
|
|
247
|
+
* whatever else is recorded: an urgent revocation may be dated before a
|
|
248
|
+
* suspension recorded ahead of time, or before a revocation that has not
|
|
249
|
+
* taken effect yet (a scheduled one, or the end of an employment whose last
|
|
250
|
+
* day is ahead). The earliest revocation is the one in force. It is refused
|
|
251
|
+
* only when the chain is already revoked on or before `effectiveOn`.
|
|
252
|
+
*
|
|
253
|
+
* The stored status becomes `revoked` at once, meaning a revocation has
|
|
254
|
+
* been recorded; until `effectiveOn` the qualification still passes
|
|
255
|
+
* {@link check}. A row already stored `revoked` is not saved again: the
|
|
256
|
+
* earlier revocation is one more change row, and the event carries its
|
|
257
|
+
* date.
|
|
258
|
+
*
|
|
259
|
+
* @throws HrError `HR_INVALID` for a row that was renewed, a date before the chain was issued or a bad input; `HR_STATUS_TRANSITION` when already revoked on or before `effectiveOn`; `HR_NOT_FOUND`
|
|
260
|
+
*/
|
|
261
|
+
revoke(heldQualificationId: string, input: HeldQualificationReasonInput): Promise<HeldQualification>;
|
|
262
|
+
/**
|
|
263
|
+
* Record that the actor has verified a held qualification's document, now.
|
|
264
|
+
* Optionally attaches, replaces or removes the document. The status and the
|
|
265
|
+
* dated history do not change, and no event is raised. Verification is not
|
|
266
|
+
* a status, so it works whatever the standing, including on a row stored
|
|
267
|
+
* `revoked` (which may still be in force until its revocation takes effect).
|
|
268
|
+
*
|
|
269
|
+
* @throws HrError `HR_INVALID`, `HR_NOT_FOUND`
|
|
270
|
+
*/
|
|
271
|
+
verify(heldQualificationId: string, input?: VerifyQualificationInput): Promise<HeldQualification>;
|
|
272
|
+
/**
|
|
273
|
+
* Record the lapse of every qualification whose last day is before `today`
|
|
274
|
+
* and that was still in force on it: an `expired` change dated the day
|
|
275
|
+
* after `expiresOn`. The application calls this from its own scheduler;
|
|
276
|
+
* checks never depend on it. Idempotent: a row is recorded once.
|
|
277
|
+
*
|
|
278
|
+
* Which rows lapsed is decided by date, not by the stored status, so a
|
|
279
|
+
* ticket that runs out while a suspension is in force, or while a
|
|
280
|
+
* revocation is recorded for a later day (a notice period), is recorded
|
|
281
|
+
* too. A row revoked on or before the day after `expiresOn` did not lapse:
|
|
282
|
+
* it was revoked. The stored status becomes `expired` only on a row stored
|
|
283
|
+
* `valid`; a stored `suspended` or `revoked` stays as it is.
|
|
284
|
+
*
|
|
285
|
+
* Every run re-reads the lapsed rows stored `suspended` or `revoked`, a set
|
|
286
|
+
* that only grows with a tenant's history. Pass `since` (a day on or before
|
|
287
|
+
* the last day the previous run covered) to look only at rows whose last
|
|
288
|
+
* day is on or after it.
|
|
289
|
+
*
|
|
290
|
+
* Emits `held-qualification.expired`, and returns the row, only for
|
|
291
|
+
* qualifications that were not renewed.
|
|
292
|
+
*
|
|
293
|
+
* @throws HrError `HR_INVALID` for a bad `today` or `since`
|
|
294
|
+
*/
|
|
295
|
+
sweepExpired(today: IsoDate, options?: SweepExpiredOptions): Promise<HeldQualification[]>;
|
|
296
|
+
/**
|
|
297
|
+
* Whether a person holds a qualification on a calendar date. Computed from
|
|
298
|
+
* issue and expiry dates and the dated change history, never from the stored
|
|
299
|
+
* status alone, so it is correct for past dates and without the sweep. A
|
|
300
|
+
* renewal chain shares its standing: while a renewal is revoked or
|
|
301
|
+
* suspended, the row it renewed does not pass either. A blank profile or
|
|
302
|
+
* qualification id is `not-held`.
|
|
303
|
+
*/
|
|
304
|
+
check(profileId: string, qualificationId: string, on: IsoDate): Promise<QualificationCheck>;
|
|
305
|
+
/**
|
|
306
|
+
* One held qualification per person who passes {@link check} on `on`,
|
|
307
|
+
* ordered by profile id.
|
|
308
|
+
*/
|
|
309
|
+
holders(qualificationId: string, on: IsoDate, options?: QualificationHoldersOptions): Promise<HeldQualification[]>;
|
|
310
|
+
/**
|
|
311
|
+
* Qualifications good on `today` (issued, and not suspended or revoked by
|
|
312
|
+
* the dated history, exactly as {@link check} decides) whose last day falls
|
|
313
|
+
* in `[today, today + days]`, soonest first. The stored status plays no
|
|
314
|
+
* part. A qualification that was already renewed is left out, and
|
|
315
|
+
* `employedOnly` keeps only people with an employment term covering `today`.
|
|
316
|
+
*/
|
|
317
|
+
expiringWithin(days: number, today: IsoDate, options?: ExpiringQualificationsOptions): Promise<HeldQualification[]>;
|
|
318
|
+
/**
|
|
319
|
+
* Everything a person holds (renewed rows are replaced by their renewal),
|
|
320
|
+
* each with its definition and its status computed for `on`. The status
|
|
321
|
+
* agrees with {@link check}: it is `valid` when the row, or a row it renews,
|
|
322
|
+
* is good on `on`, and otherwise the status of the latest row of the chain
|
|
323
|
+
* issued by `on`. Ordered by qualification key, then issue date.
|
|
324
|
+
*
|
|
325
|
+
* @throws HrError `HR_INVALID` for a blank profile id or a bad date
|
|
326
|
+
*/
|
|
327
|
+
listForProfile(profileId: string, on: IsoDate): Promise<ProfileQualification[]>;
|
|
328
|
+
/**
|
|
329
|
+
* The dated changes of one held qualification, oldest first.
|
|
330
|
+
*
|
|
331
|
+
* @throws HrError `HR_NOT_FOUND` outside the actor's tenant, `HR_INVALID` for a blank id
|
|
332
|
+
*/
|
|
333
|
+
history(heldQualificationId: string): Promise<HeldQualificationChange[]>;
|
|
334
|
+
private byKey;
|
|
335
|
+
private definition;
|
|
336
|
+
private grantable;
|
|
337
|
+
private insertDefinition;
|
|
338
|
+
private held;
|
|
339
|
+
/** Held rows in the actor's tenant matching `where`. */
|
|
340
|
+
private rows;
|
|
341
|
+
private rowsById;
|
|
342
|
+
/**
|
|
343
|
+
* The chains among `rows` (everything one person holds of one
|
|
344
|
+
* qualification) that are not revoked effective on or before `on`, each
|
|
345
|
+
* represented by its latest row and the date its revocation takes effect,
|
|
346
|
+
* if one is recorded. The chain whose latest row is `exceptId` is skipped.
|
|
347
|
+
* Decided from the dated history: a revocation stored now but effective
|
|
348
|
+
* later still counts as held.
|
|
349
|
+
*/
|
|
350
|
+
private liveChains;
|
|
351
|
+
/** Ids, among `rows`, that a later row renews. */
|
|
352
|
+
private supersededIds;
|
|
353
|
+
/** Change rows per held qualification, oldest first. */
|
|
354
|
+
private changesByHeld;
|
|
355
|
+
/**
|
|
356
|
+
* The employment an employment-scoped grant belongs to (null for a
|
|
357
|
+
* person-scoped one) and, when the term covering `issuedOn` already has its
|
|
358
|
+
* end recorded, that term's last day. The person must be employed on
|
|
359
|
+
* `issuedOn`.
|
|
360
|
+
*/
|
|
361
|
+
private employmentFor;
|
|
362
|
+
/**
|
|
363
|
+
* Insert a granted or renewed row with its change and event. With a
|
|
364
|
+
* `revocation` (the end of an employment term that is already closed, or a
|
|
365
|
+
* pending revocation a renewal carries forward) the row starts out cut off,
|
|
366
|
+
* exactly as `EmploymentService.end()` leaves the rows that existed when
|
|
367
|
+
* the end was recorded: stored `revoked`, a revocation on that date, and
|
|
368
|
+
* the revoked event.
|
|
369
|
+
*/
|
|
370
|
+
private insertHeld;
|
|
371
|
+
/**
|
|
372
|
+
* Suspend, reinstate or revoke the latest row of a chain. The change is
|
|
373
|
+
* validated against the chain's dated history as of `effectiveOn`; the
|
|
374
|
+
* stored status is not consulted, except that a row stored `revoked` is
|
|
375
|
+
* never saved again and gets the change row only.
|
|
376
|
+
*/
|
|
377
|
+
private transition;
|
|
378
|
+
private recordChange;
|
|
379
|
+
private announce;
|
|
380
|
+
}
|
|
381
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/qualifications/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type DatabaseInterface } from '@happyvertical/sql';
|
|
2
|
+
import { type HrActor, type HrEvent, type HrServiceOptions } from './types.js';
|
|
3
|
+
/** Collects events inside a transaction; they are delivered after it commits. */
|
|
4
|
+
export type HrEventQueue = (event: HrEvent) => void;
|
|
5
|
+
/**
|
|
6
|
+
* Shared plumbing for the HR services: a trusted tenant/profile actor, scoped
|
|
7
|
+
* reads, serialized transactional writes, and after-commit event delivery.
|
|
8
|
+
* Applications authorize the actor before constructing a service. Reads and
|
|
9
|
+
* writes reject conflicting ambient tenancy.
|
|
10
|
+
*
|
|
11
|
+
* Construct a service with the root database handle. A mutation begins and
|
|
12
|
+
* commits its own transaction with `beginTransaction()` and then delivers its
|
|
13
|
+
* events; on a handle that is already inside a transaction (the `tx` of
|
|
14
|
+
* `db.transaction()`, or a `beginTransaction()` handle) the commit would
|
|
15
|
+
* belong to the caller, so a mutation is refused with
|
|
16
|
+
* `HR_TRANSACTION_UNSUPPORTED` before anything is written. Such a handle
|
|
17
|
+
* either has no `beginTransaction` or refuses it with the SDK's
|
|
18
|
+
* `NestedTransactionError`; the service never opens a nested (savepoint)
|
|
19
|
+
* transaction. Reads work on any handle.
|
|
20
|
+
*/
|
|
21
|
+
export declare abstract class HrService {
|
|
22
|
+
protected readonly db: DatabaseInterface;
|
|
23
|
+
protected readonly actor: Readonly<HrActor>;
|
|
24
|
+
private readonly onEvent;
|
|
25
|
+
constructor(db: DatabaseInterface, actor: HrActor, options?: HrServiceOptions);
|
|
26
|
+
/** Normalize an id; PostgreSQL stores identities as UUIDs. */
|
|
27
|
+
protected identity(value: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* A required id from caller input, trimmed and normalized.
|
|
30
|
+
*
|
|
31
|
+
* @throws HrError `HR_INVALID` when it is not text or is blank
|
|
32
|
+
*/
|
|
33
|
+
protected id(fieldName: string, value: unknown): string;
|
|
34
|
+
/** An optional id from caller input: null or undefined is null. */
|
|
35
|
+
protected optionalId(fieldName: string, value: unknown): string | null;
|
|
36
|
+
/** Run a read inside the actor's tenant. */
|
|
37
|
+
protected scope<T>(run: () => Promise<T>): Promise<T>;
|
|
38
|
+
/**
|
|
39
|
+
* Run a mutation in one transaction, serialized per tenant, then deliver the
|
|
40
|
+
* events it queued. Nothing is delivered when the transaction fails.
|
|
41
|
+
*
|
|
42
|
+
* @throws HrError `HR_TRANSACTION_UNSUPPORTED` when the service was constructed with a handle that is already inside a transaction
|
|
43
|
+
*/
|
|
44
|
+
protected transact<T>(run: (db: DatabaseInterface, queue: HrEventQueue) => Promise<T>): Promise<T>;
|
|
45
|
+
private emit;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=service-base.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-base.d.ts","sourceRoot":"","sources":["../src/service-base.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,KAAK,iBAAiB,EAGvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,KAAK,OAAO,EAEZ,KAAK,OAAO,EACZ,KAAK,gBAAgB,EACtB,MAAM,YAAY,CAAC;AAIpB,iFAAiF;AACjF,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;AAEpD;;;;;;;;;;;;;;;GAeG;AACH,8BAAsB,SAAS;IAK3B,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,iBAAiB;IAJ1C,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;gBAGjC,EAAE,EAAE,iBAAiB,EACxC,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,gBAAqB;IAchC,8DAA8D;IAC9D,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAWzC;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM;IAMvD,mEAAmE;IACnE,SAAS,CAAC,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI;IAMtE,4CAA4C;IAC5C,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAUrD;;;;;OAKG;cACa,QAAQ,CAAC,CAAC,EACxB,GAAG,EAAE,CAAC,EAAE,EAAE,iBAAiB,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,CAAC,CAAC,GAC9D,OAAO,CAAC,CAAC,CAAC;YAgDC,IAAI;CAWnB"}
|