blood bank

This commit is contained in:
Narul Hidayah
2026-05-24 23:07:08 +09:00
parent 29b37c5839
commit 46b7e26676
15 changed files with 592 additions and 1 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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";
export * from "./schema/cashier";
export * from "./schema/blood_bank";

13
src/schema/blood_bank.ts Normal file
View File

@ -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";

View File

@ -0,0 +1,9 @@
/**
* Blood component types.
*/
export enum BloodComponentType {
WHOLE_BLOOD = "whole_blood",
RED_CELLS = "red_cells",
PLATELETS = "platelets",
PLASMA = "plasma",
}

View File

@ -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",
}

View File

@ -0,0 +1,9 @@
/**
* Blood stock movement types.
*/
export enum BloodStockMovementType {
RECEIVE = "receive",
TRANSFER = "transfer",
ISSUE = "issue",
DISCARD = "discard",
}

View File

@ -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",
}