mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
66 lines
2.4 KiB
TypeScript
66 lines
2.4 KiB
TypeScript
import { SoftDeleteAudit } from "@tria-plc/api-common/modules/typeorm/audit.entity";
|
|
import {
|
|
Check,
|
|
Column,
|
|
Entity,
|
|
JoinColumn,
|
|
ManyToOne,
|
|
PrimaryGeneratedColumn,
|
|
Unique,
|
|
} from "typeorm";
|
|
|
|
import { JobTitle } from "../../job-titles/entities/job-title.entity";
|
|
|
|
/**
|
|
* The headcount / vacancy layer over `iam.positions`.
|
|
*
|
|
* Stores NOTHING IAM already holds — not the unit, not the organization. Both
|
|
* come from `iam.positions` and are read by joining. A copy here would be a
|
|
* second answer that goes stale the moment a position is moved.
|
|
*
|
|
* It EXTENDS a position, never replaces it: `positionId` is a soft reference to
|
|
* the IAM slot, unique here so a position cannot acquire two competing headcount
|
|
* records. IAM keeps owning who holds the position; HR adds what it is graded as,
|
|
* how many bodies it is budgeted for, and whether it is open for recruitment
|
|
* (which 3.5 reads when creating a job opening).
|
|
*/
|
|
@Entity({ schema: "hr", name: "job_positions" })
|
|
@Unique("uq_job_positions_position_id", ["positionId"])
|
|
@Check("ck_job_positions_budgeted_count", `"budgeted_count" >= 0`)
|
|
@Check("ck_job_positions_current_count", `"current_count" >= 0`)
|
|
export class JobPosition extends SoftDeleteAudit {
|
|
@PrimaryGeneratedColumn("uuid")
|
|
id!: string;
|
|
|
|
/** Soft reference → `iam.positions.id`. */
|
|
@Column({ type: "uuid", name: "position_id" })
|
|
positionId!: string;
|
|
|
|
@Column({ type: "uuid", name: "job_title_id", nullable: true })
|
|
jobTitleId?: string | null;
|
|
|
|
@ManyToOne(() => JobTitle, { nullable: true, onDelete: "SET NULL" })
|
|
@JoinColumn({ name: "job_title_id" })
|
|
jobTitle?: JobTitle | null;
|
|
|
|
@Column({ type: "int", name: "budgeted_count", default: 1 })
|
|
budgetedCount!: number;
|
|
|
|
/**
|
|
* Cache of the current holder count in IAM. Refreshed from
|
|
* `IamDirectoryService.countCurrentPositionHolders` whenever the position is
|
|
* read or written through HR — IAM stays the source of truth, this column just
|
|
* makes "which posts are under-filled" answerable in one query.
|
|
*/
|
|
@Column({ type: "int", name: "current_count", default: 0 })
|
|
currentCount!: number;
|
|
|
|
/** Open for recruitment. Independent of vacancy: a post can be under-filled
|
|
* and still frozen, which is exactly what a hiring freeze is. */
|
|
@Column({ type: "boolean", name: "is_open", default: false })
|
|
isOpen!: boolean;
|
|
|
|
@Column({ type: "uuid", name: "created_by", nullable: true })
|
|
createdBy?: string | null;
|
|
}
|