diff --git a/src/entity/blood-bank/blood_dispatch.ts b/src/entity/blood-bank/blood_dispatch.ts new file mode 100644 index 0000000..9f3f6e1 --- /dev/null +++ b/src/entity/blood-bank/blood_dispatch.ts @@ -0,0 +1,65 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, + DeleteDateColumn, + ManyToOne, + JoinColumn, + Index, +} from "typeorm"; +import { BloodRequest } from "./blood_request"; +import { BloodUnit } from "./blood_unit"; +import { Room } from "../room"; +import { User } from "../users"; + +@Entity({ schema: "blood_bank", name: "blood_dispatch" }) +@Index(["dispatch_no"], { unique: true }) +export class BloodDispatch { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ nullable: false, length: 64, unique: true }) + dispatch_no: string; + + @ManyToOne(() => BloodRequest, (req) => req.dispatches, { onDelete: "NO ACTION" }) + @JoinColumn() + blood_request: BloodRequest; + + @ManyToOne(() => BloodUnit, { onDelete: "NO ACTION" }) + @JoinColumn() + blood_unit: BloodUnit; + + @ManyToOne(() => Room, { onDelete: "NO ACTION", nullable: true }) + @JoinColumn() + destination_room: Room; + + @ManyToOne(() => User, { onDelete: "NO ACTION", nullable: true }) + @JoinColumn() + dispatched_by: User; + + @Column({ type: "timestamp", nullable: true }) + dispatched_at: Date; + + @Column({ nullable: false, type: "boolean", default: false }) + completed: boolean; + + @Column({ nullable: true, type: "text" }) + transfusion_notes: string; + + @Column({ type: "timestamp", nullable: true }) + transfusion_at: Date; + + @Column() + @CreateDateColumn() + created_at: Date; + + @Column({ nullable: true }) + @UpdateDateColumn() + updated_at: Date; + + @Column({ nullable: true }) + @DeleteDateColumn() + deleted_at: Date; +} diff --git a/src/entity/blood-bank/blood_donor.ts b/src/entity/blood-bank/blood_donor.ts new file mode 100644 index 0000000..6365fc0 --- /dev/null +++ b/src/entity/blood-bank/blood_donor.ts @@ -0,0 +1,58 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, + DeleteDateColumn, + OneToMany, +} from "typeorm"; +import { BloodUnit } from "./blood_unit"; + +@Entity({ schema: "blood_bank", name: "blood_donor" }) +export class BloodDonor { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ nullable: false, length: 64, unique: true }) + donor_no: string; + + @Column({ nullable: true, length: 256 }) + full_name: string; + + @Column({ type: "date", nullable: true }) + birth_date: Date; + + @Column({ nullable: true, length: 32 }) + gender: string; + + @Column({ nullable: true, length: 8 }) + blood_type: string; + + @Column({ nullable: true, length: 8 }) + rh: string; + + @Column({ nullable: true, length: 32 }) + phone: string; + + @Column({ nullable: true, type: "text" }) + address: string; + + @Column({ nullable: false, length: 32, default: "active" }) + status: string; + + @OneToMany(() => BloodUnit, (unit) => unit.donor) + blood_units: BloodUnit[]; + + @Column() + @CreateDateColumn() + created_at: Date; + + @Column({ nullable: true }) + @UpdateDateColumn() + updated_at: Date; + + @Column({ nullable: true }) + @DeleteDateColumn() + deleted_at: Date; +} diff --git a/src/entity/blood-bank/blood_request.ts b/src/entity/blood-bank/blood_request.ts new file mode 100644 index 0000000..ada6b1d --- /dev/null +++ b/src/entity/blood-bank/blood_request.ts @@ -0,0 +1,81 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, + DeleteDateColumn, + ManyToOne, + JoinColumn, + OneToMany, + Index, +} from "typeorm"; +import { EmrPatient } from "../emr/emr_patient"; +import { EmrRegistration } from "../emr/emr_registration"; +import { Room } from "../room"; +import { User } from "../users"; +import { BloodRequestStatus } from "../../types/blood_request_status"; +import { BloodRequestItem } from "./blood_request_item"; +import { CrossMatch } from "./cross_match"; +import { BloodDispatch } from "./blood_dispatch"; + +@Entity({ schema: "blood_bank", name: "blood_request" }) +@Index(["request_no"], { unique: true }) +export class BloodRequest { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ nullable: false, length: 64, unique: true }) + request_no: string; + + @Column({ + type: "enum", + enum: BloodRequestStatus, + nullable: false, + default: BloodRequestStatus.WAITING, + }) + status: BloodRequestStatus; + + @ManyToOne(() => EmrPatient, { onDelete: "NO ACTION" }) + @JoinColumn() + patient: EmrPatient; + + @ManyToOne(() => EmrRegistration, { onDelete: "NO ACTION", nullable: true }) + @JoinColumn() + registration: EmrRegistration; + + @ManyToOne(() => Room, { onDelete: "NO ACTION", nullable: true }) + @JoinColumn() + requesting_room: Room; + + @ManyToOne(() => User, { onDelete: "NO ACTION", nullable: true }) + @JoinColumn() + doctor: User; + + @Column({ nullable: false, length: 32, default: "normal" }) + priority: string; + + @Column({ nullable: true, type: "text" }) + notes: string; + + @OneToMany(() => BloodRequestItem, (item) => item.blood_request, { cascade: true }) + request_items: BloodRequestItem[]; + + @OneToMany(() => CrossMatch, (cm) => cm.blood_request) + cross_matches: CrossMatch[]; + + @OneToMany(() => BloodDispatch, (d) => d.blood_request) + dispatches: BloodDispatch[]; + + @Column() + @CreateDateColumn() + created_at: Date; + + @Column({ nullable: true }) + @UpdateDateColumn() + updated_at: Date; + + @Column({ nullable: true }) + @DeleteDateColumn() + deleted_at: Date; +} diff --git a/src/entity/blood-bank/blood_request_item.ts b/src/entity/blood-bank/blood_request_item.ts new file mode 100644 index 0000000..e2a0d6e --- /dev/null +++ b/src/entity/blood-bank/blood_request_item.ts @@ -0,0 +1,50 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, + DeleteDateColumn, + ManyToOne, + JoinColumn, +} from "typeorm"; +import { BloodRequest } from "./blood_request"; +import { BloodComponentType } from "../../types/blood_component_type"; + +@Entity({ schema: "blood_bank", name: "blood_request_item" }) +export class BloodRequestItem { + @PrimaryGeneratedColumn("uuid") + id: string; + + @ManyToOne(() => BloodRequest, (req) => req.request_items, { onDelete: "CASCADE" }) + @JoinColumn() + blood_request: BloodRequest; + + @Column({ + type: "enum", + enum: BloodComponentType, + nullable: false, + }) + component_type: BloodComponentType; + + @Column({ nullable: true, length: 8 }) + abo: string; + + @Column({ nullable: true, length: 8 }) + rh: string; + + @Column({ nullable: false, type: "int", default: 1 }) + quantity: number; + + @Column() + @CreateDateColumn() + created_at: Date; + + @Column({ nullable: true }) + @UpdateDateColumn() + updated_at: Date; + + @Column({ nullable: true }) + @DeleteDateColumn() + deleted_at: Date; +} diff --git a/src/entity/blood-bank/blood_stock.ts b/src/entity/blood-bank/blood_stock.ts new file mode 100644 index 0000000..89391e0 --- /dev/null +++ b/src/entity/blood-bank/blood_stock.ts @@ -0,0 +1,44 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, + DeleteDateColumn, + ManyToOne, + JoinColumn, +} from "typeorm"; +import { BloodUnit } from "./blood_unit"; +import { Room } from "../room"; + +@Entity({ schema: "blood_bank", name: "blood_stock" }) +export class BloodStock { + @PrimaryGeneratedColumn("uuid") + id: string; + + @ManyToOne(() => BloodUnit, { onDelete: "NO ACTION" }) + @JoinColumn() + blood_unit: BloodUnit; + + @ManyToOne(() => Room, { onDelete: "NO ACTION", nullable: true }) + @JoinColumn() + room: Room | null; + + @Column({ nullable: false, type: "int", default: 1 }) + quantity: number; + + @Column({ nullable: true, length: 64 }) + batch: string; + + @Column() + @CreateDateColumn() + created_at: Date; + + @Column({ nullable: true }) + @UpdateDateColumn() + updated_at: Date; + + @Column({ nullable: true }) + @DeleteDateColumn() + deleted_at: Date; +} diff --git a/src/entity/blood-bank/blood_stock_movement.ts b/src/entity/blood-bank/blood_stock_movement.ts new file mode 100644 index 0000000..3735719 --- /dev/null +++ b/src/entity/blood-bank/blood_stock_movement.ts @@ -0,0 +1,51 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + ManyToOne, + JoinColumn, +} from "typeorm"; +import { BloodUnit } from "./blood_unit"; +import { Room } from "../room"; +import { User } from "../users"; +import { BloodStockMovementType } from "../../types/blood_stock_movement_type"; + +@Entity({ schema: "blood_bank", name: "blood_stock_movement" }) +export class BloodStockMovement { + @PrimaryGeneratedColumn("uuid") + id: string; + + @ManyToOne(() => BloodUnit, { onDelete: "NO ACTION" }) + @JoinColumn() + blood_unit: BloodUnit; + + @ManyToOne(() => Room, { onDelete: "NO ACTION", nullable: true }) + @JoinColumn() + from_room: Room; + + @ManyToOne(() => Room, { onDelete: "NO ACTION", nullable: true }) + @JoinColumn() + to_room: Room; + + @Column({ nullable: false, type: "int", default: 1 }) + quantity: number; + + @Column({ + type: "enum", + enum: BloodStockMovementType, + nullable: false, + }) + movement_type: BloodStockMovementType; + + @Column({ nullable: true, type: "text" }) + notes: string; + + @ManyToOne(() => User, { onDelete: "NO ACTION", nullable: true }) + @JoinColumn() + performed_by: User; + + @Column() + @CreateDateColumn() + created_at: Date; +} diff --git a/src/entity/blood-bank/blood_unit.ts b/src/entity/blood-bank/blood_unit.ts new file mode 100644 index 0000000..78c2c0f --- /dev/null +++ b/src/entity/blood-bank/blood_unit.ts @@ -0,0 +1,82 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, + DeleteDateColumn, + ManyToOne, + JoinColumn, + OneToMany, + Index, +} from "typeorm"; +import { BloodDonor } from "./blood_donor"; +import { BloodUnitStatus } from "../../types/blood_unit_status"; +import { BloodComponentType } from "../../types/blood_component_type"; + +@Entity({ schema: "blood_bank", name: "blood_unit" }) +@Index(["unit_number"], { unique: true }) +export class BloodUnit { + @PrimaryGeneratedColumn("uuid") + id: string; + + @Column({ nullable: false, length: 64, unique: true }) + unit_number: string; + + @Column({ + type: "enum", + enum: BloodComponentType, + nullable: false, + default: BloodComponentType.WHOLE_BLOOD, + }) + component_type: BloodComponentType; + + @Column({ nullable: false, length: 8 }) + abo: string; + + @Column({ nullable: false, length: 8 }) + rh: string; + + @Column({ + type: "enum", + enum: BloodUnitStatus, + nullable: false, + default: BloodUnitStatus.QUARANTINE, + }) + status: BloodUnitStatus; + + @Column({ type: "date", nullable: false }) + collection_date: Date; + + @Column({ type: "date", nullable: false }) + exp_date: Date; + + @Column({ nullable: true, length: 64 }) + storage_temp: string; + + @Column({ nullable: true, length: 64 }) + volume_ml: string; + + @ManyToOne(() => BloodDonor, (donor) => donor.blood_units, { onDelete: "NO ACTION", nullable: true }) + @JoinColumn() + donor: BloodDonor; + + @ManyToOne(() => BloodUnit, { onDelete: "NO ACTION", nullable: true }) + @JoinColumn() + parent_unit: BloodUnit; + + @OneToMany(() => BloodUnit, (unit) => unit.parent_unit) + child_units: BloodUnit[]; + + @Column() + @CreateDateColumn() + created_at: Date; + + @Column({ nullable: true }) + @UpdateDateColumn() + updated_at: Date; + + @Column({ nullable: true }) + @DeleteDateColumn() + deleted_at: Date; +} diff --git a/src/entity/blood-bank/blood_waste_log.ts b/src/entity/blood-bank/blood_waste_log.ts new file mode 100644 index 0000000..5fccbad --- /dev/null +++ b/src/entity/blood-bank/blood_waste_log.ts @@ -0,0 +1,44 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, + DeleteDateColumn, + ManyToOne, + JoinColumn, +} from "typeorm"; +import { BloodUnit } from "./blood_unit"; +import { User } from "../users"; + +@Entity({ schema: "blood_bank", name: "blood_waste_log" }) +export class BloodWasteLog { + @PrimaryGeneratedColumn("uuid") + id: string; + + @ManyToOne(() => BloodUnit, { onDelete: "NO ACTION" }) + @JoinColumn() + blood_unit: BloodUnit; + + @Column({ nullable: false, type: "text" }) + reason: string; + + @ManyToOne(() => User, { onDelete: "NO ACTION", nullable: true }) + @JoinColumn() + discarded_by: User; + + @Column({ type: "timestamp", nullable: false }) + discarded_at: Date; + + @Column() + @CreateDateColumn() + created_at: Date; + + @Column({ nullable: true }) + @UpdateDateColumn() + updated_at: Date; + + @Column({ nullable: true }) + @DeleteDateColumn() + deleted_at: Date; +} diff --git a/src/entity/blood-bank/cross_match.ts b/src/entity/blood-bank/cross_match.ts new file mode 100644 index 0000000..260add0 --- /dev/null +++ b/src/entity/blood-bank/cross_match.ts @@ -0,0 +1,58 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, + DeleteDateColumn, + ManyToOne, + JoinColumn, +} from "typeorm"; +import { BloodRequest } from "./blood_request"; +import { BloodUnit } from "./blood_unit"; +import { User } from "../users"; + +@Entity({ schema: "blood_bank", name: "cross_match" }) +export class CrossMatch { + @PrimaryGeneratedColumn("uuid") + id: string; + + @ManyToOne(() => BloodRequest, (req) => req.cross_matches, { onDelete: "CASCADE" }) + @JoinColumn() + blood_request: BloodRequest; + + @ManyToOne(() => BloodUnit, { onDelete: "NO ACTION" }) + @JoinColumn() + blood_unit: BloodUnit; + + @Column({ nullable: false, length: 32 }) + result: string; + + @Column({ nullable: false, type: "boolean", default: false }) + compatibility: boolean; + + @Column({ nullable: true, type: "text" }) + notes: string; + + @ManyToOne(() => User, { onDelete: "NO ACTION", nullable: true }) + @JoinColumn() + performed_by: User; + + @Column({ type: "timestamp", nullable: true }) + performed_at: Date; + + @Column({ nullable: false, type: "boolean", default: false }) + verified: boolean; + + @Column() + @CreateDateColumn() + created_at: Date; + + @Column({ nullable: true }) + @UpdateDateColumn() + updated_at: Date; + + @Column({ nullable: true }) + @DeleteDateColumn() + deleted_at: Date; +} diff --git a/src/index.ts b/src/index.ts index f41acf1..88437b0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,4 +5,5 @@ export * from "./schema/finances"; export * from "./schema/procurement"; export * from "./schema/pharmacy"; export * from "./schema/pharmacy_logistic"; -export * from "./schema/cashier"; \ No newline at end of file +export * from "./schema/cashier"; +export * from "./schema/blood_bank"; \ No newline at end of file diff --git a/src/schema/blood_bank.ts b/src/schema/blood_bank.ts new file mode 100644 index 0000000..aaf533d --- /dev/null +++ b/src/schema/blood_bank.ts @@ -0,0 +1,13 @@ +export * from "../types/blood_unit_status"; +export * from "../types/blood_request_status"; +export * from "../types/blood_component_type"; +export * from "../types/blood_stock_movement_type"; +export * from "../entity/blood-bank/blood_donor"; +export * from "../entity/blood-bank/blood_unit"; +export * from "../entity/blood-bank/blood_request"; +export * from "../entity/blood-bank/blood_request_item"; +export * from "../entity/blood-bank/cross_match"; +export * from "../entity/blood-bank/blood_dispatch"; +export * from "../entity/blood-bank/blood_waste_log"; +export * from "../entity/blood-bank/blood_stock"; +export * from "../entity/blood-bank/blood_stock_movement"; diff --git a/src/types/blood_component_type.ts b/src/types/blood_component_type.ts new file mode 100644 index 0000000..be5e50e --- /dev/null +++ b/src/types/blood_component_type.ts @@ -0,0 +1,9 @@ +/** + * Blood component types. + */ +export enum BloodComponentType { + WHOLE_BLOOD = "whole_blood", + RED_CELLS = "red_cells", + PLATELETS = "platelets", + PLASMA = "plasma", +} diff --git a/src/types/blood_request_status.ts b/src/types/blood_request_status.ts new file mode 100644 index 0000000..501baa4 --- /dev/null +++ b/src/types/blood_request_status.ts @@ -0,0 +1,12 @@ +/** + * Workflow status for a blood request. + */ +export enum BloodRequestStatus { + WAITING = "waiting", + ORDERED = "ordered", + CROSSMATCHED = "crossmatched", + VERIFIED = "verified", + ISSUED = "issued", + COMPLETED = "completed", + CANCELED = "canceled", +} diff --git a/src/types/blood_stock_movement_type.ts b/src/types/blood_stock_movement_type.ts new file mode 100644 index 0000000..322fa2e --- /dev/null +++ b/src/types/blood_stock_movement_type.ts @@ -0,0 +1,9 @@ +/** + * Blood stock movement types. + */ +export enum BloodStockMovementType { + RECEIVE = "receive", + TRANSFER = "transfer", + ISSUE = "issue", + DISCARD = "discard", +} diff --git a/src/types/blood_unit_status.ts b/src/types/blood_unit_status.ts new file mode 100644 index 0000000..f86fad0 --- /dev/null +++ b/src/types/blood_unit_status.ts @@ -0,0 +1,14 @@ +/** + * Workflow status for a blood unit. + */ +export enum BloodUnitStatus { + COLLECTED = "collected", + QUARANTINE = "quarantine", + SCREENED = "screened", + AVAILABLE = "available", + RESERVED = "reserved", + ISSUED = "issued", + TRANSFUSED = "transfused", + DISCARDED = "discarded", + EXPIRED = "expired", +}