251123:0200 T6.1 to DO
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, DataSource } from 'typeorm';
|
||||
|
||||
import { Circulation } from './entities/circulation.entity';
|
||||
import { CirculationRouting } from './entities/circulation-routing.entity';
|
||||
import { User } from '../user/entities/user.entity';
|
||||
import { CreateCirculationDto } from './dto/create-circulation.dto'; // ต้องสร้าง DTO นี้
|
||||
|
||||
@Injectable()
|
||||
export class CirculationService {
|
||||
constructor(
|
||||
@InjectRepository(Circulation)
|
||||
private circulationRepo: Repository<Circulation>,
|
||||
@InjectRepository(CirculationRouting)
|
||||
private routingRepo: Repository<CirculationRouting>,
|
||||
private dataSource: DataSource,
|
||||
) {}
|
||||
|
||||
async create(createDto: CreateCirculationDto, user: User) {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
// 1. Create Master Circulation
|
||||
// TODO: Generate Circulation No. logic here (Simple format)
|
||||
const circulationNo = `CIR-${Date.now()}`;
|
||||
|
||||
const circulation = queryRunner.manager.create(Circulation, {
|
||||
organizationId: user.primaryOrganizationId,
|
||||
correspondenceId: createDto.correspondenceId,
|
||||
circulationNo: circulationNo,
|
||||
subject: createDto.subject,
|
||||
statusCode: 'OPEN',
|
||||
createdByUserId: user.user_id,
|
||||
});
|
||||
const savedCirculation = await queryRunner.manager.save(circulation);
|
||||
|
||||
// 2. Create Routings (Assignees)
|
||||
if (createDto.assigneeIds && createDto.assigneeIds.length > 0) {
|
||||
const routings = createDto.assigneeIds.map((userId, index) =>
|
||||
queryRunner.manager.create(CirculationRouting, {
|
||||
circulationId: savedCirculation.id,
|
||||
stepNumber: index + 1,
|
||||
organizationId: user.primaryOrganizationId, // Internal routing
|
||||
assignedTo: userId,
|
||||
status: 'PENDING',
|
||||
}),
|
||||
);
|
||||
await queryRunner.manager.save(routings);
|
||||
}
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
return savedCirculation;
|
||||
} catch (err) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw err;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
async findOne(id: number) {
|
||||
const circulation = await this.circulationRepo.findOne({
|
||||
where: { id },
|
||||
relations: ['routings', 'routings.assignee', 'correspondence'],
|
||||
});
|
||||
if (!circulation) throw new NotFoundException('Circulation not found');
|
||||
return circulation;
|
||||
}
|
||||
|
||||
// Method update status (Complete task)
|
||||
async updateRoutingStatus(
|
||||
routingId: number,
|
||||
status: string,
|
||||
comments: string,
|
||||
user: User,
|
||||
) {
|
||||
// Logic to update routing status
|
||||
// and Check if all routings are completed -> Close Circulation
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import {
|
||||
IsInt,
|
||||
IsString,
|
||||
IsNotEmpty,
|
||||
IsArray,
|
||||
IsOptional,
|
||||
} from 'class-validator';
|
||||
|
||||
export class CreateCirculationDto {
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
correspondenceId!: number; // เอกสารต้นเรื่องที่จะเวียน
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
subject!: string; // หัวข้อเรื่อง (Subject)
|
||||
|
||||
@IsArray()
|
||||
@IsInt({ each: true })
|
||||
@IsNotEmpty()
|
||||
assigneeIds!: number[]; // รายชื่อ User ID ที่ต้องการส่งให้ (ผู้รับผิดชอบ)
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
remarks?: string; // หมายเหตุเพิ่มเติม (ถ้ามี)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import {
|
||||
IsInt,
|
||||
IsString,
|
||||
IsOptional,
|
||||
IsArray,
|
||||
IsNotEmpty,
|
||||
IsEnum,
|
||||
} from 'class-validator';
|
||||
|
||||
export enum TransmittalPurpose {
|
||||
FOR_APPROVAL = 'FOR_APPROVAL',
|
||||
FOR_INFORMATION = 'FOR_INFORMATION',
|
||||
FOR_REVIEW = 'FOR_REVIEW',
|
||||
OTHER = 'OTHER',
|
||||
}
|
||||
|
||||
export class CreateTransmittalDto {
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
projectId!: number; // จำเป็นสำหรับการออกเลขที่เอกสาร
|
||||
|
||||
@IsEnum(TransmittalPurpose)
|
||||
@IsOptional()
|
||||
purpose?: TransmittalPurpose; // วัตถุประสงค์
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
remarks?: string; // หมายเหตุ
|
||||
|
||||
@IsArray()
|
||||
@IsInt({ each: true })
|
||||
@IsNotEmpty()
|
||||
itemIds!: number[]; // ID ของเอกสาร (Correspondence IDs) ที่จะแนบไปใน Transmittal
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { Circulation } from './circulation.entity';
|
||||
import { Organization } from '../../project/entities/organization.entity';
|
||||
import { User } from '../../user/entities/user.entity';
|
||||
|
||||
@Entity('circulation_routings')
|
||||
export class CirculationRouting {
|
||||
@PrimaryGeneratedColumn()
|
||||
id!: number;
|
||||
|
||||
@Column({ name: 'circulation_id' })
|
||||
circulationId!: number;
|
||||
|
||||
@Column({ name: 'step_number' })
|
||||
stepNumber!: number;
|
||||
|
||||
@Column({ name: 'organization_id' })
|
||||
organizationId!: number;
|
||||
|
||||
@Column({ name: 'assigned_to', nullable: true })
|
||||
assignedTo?: number;
|
||||
|
||||
@Column({
|
||||
type: 'enum',
|
||||
enum: ['PENDING', 'IN_PROGRESS', 'COMPLETED', 'REJECTED'],
|
||||
default: 'PENDING',
|
||||
})
|
||||
status!: string;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
comments?: string;
|
||||
|
||||
@Column({ name: 'completed_at', type: 'datetime', nullable: true })
|
||||
completedAt?: Date;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt!: Date;
|
||||
|
||||
@UpdateDateColumn({ name: 'updated_at' })
|
||||
updatedAt!: Date;
|
||||
|
||||
// Relations
|
||||
@ManyToOne(() => Circulation, (c) => c.routings, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'circulation_id' })
|
||||
circulation!: Circulation;
|
||||
|
||||
@ManyToOne(() => Organization)
|
||||
@JoinColumn({ name: 'organization_id' })
|
||||
organization!: Organization;
|
||||
|
||||
@ManyToOne(() => User)
|
||||
@JoinColumn({ name: 'assigned_to' })
|
||||
assignee?: User;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
|
||||
|
||||
@Entity('circulation_status_codes')
|
||||
export class CirculationStatusCode {
|
||||
@PrimaryGeneratedColumn()
|
||||
id!: number;
|
||||
|
||||
@Column({ length: 20, unique: true })
|
||||
code!: string;
|
||||
|
||||
@Column({ length: 50 })
|
||||
description!: string;
|
||||
|
||||
@Column({ name: 'sort_order', default: 0 })
|
||||
sortOrder!: number;
|
||||
|
||||
@Column({ name: 'is_active', default: true })
|
||||
isActive!: boolean;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
OneToMany,
|
||||
} from 'typeorm';
|
||||
import { Correspondence } from '../../correspondence/entities/correspondence.entity';
|
||||
import { Organization } from '../../project/entities/organization.entity';
|
||||
import { User } from '../../user/entities/user.entity';
|
||||
import { CirculationStatusCode } from './circulation-status-code.entity';
|
||||
import { CirculationRouting } from './circulation-routing.entity';
|
||||
|
||||
@Entity('circulations')
|
||||
export class Circulation {
|
||||
@PrimaryGeneratedColumn()
|
||||
id!: number;
|
||||
|
||||
@Column({ name: 'correspondence_id', nullable: true })
|
||||
correspondenceId?: number;
|
||||
|
||||
@Column({ name: 'organization_id' })
|
||||
organizationId!: number;
|
||||
|
||||
@Column({ name: 'circulation_no', length: 100 })
|
||||
circulationNo!: string;
|
||||
|
||||
@Column({ name: 'circulation_subject', length: 500 })
|
||||
subject!: string;
|
||||
|
||||
@Column({ name: 'circulation_status_code' })
|
||||
statusCode!: string;
|
||||
|
||||
@Column({ name: 'created_by_user_id' })
|
||||
createdByUserId!: number;
|
||||
|
||||
@Column({ name: 'submitted_at', type: 'timestamp', nullable: true })
|
||||
submittedAt?: Date;
|
||||
|
||||
@Column({ name: 'closed_at', type: 'timestamp', nullable: true })
|
||||
closedAt?: Date;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt!: Date;
|
||||
|
||||
@UpdateDateColumn({ name: 'updated_at' })
|
||||
updatedAt!: Date;
|
||||
|
||||
// Relations
|
||||
@ManyToOne(() => Correspondence)
|
||||
@JoinColumn({ name: 'correspondence_id' })
|
||||
correspondence?: Correspondence;
|
||||
|
||||
@ManyToOne(() => Organization)
|
||||
@JoinColumn({ name: 'organization_id' })
|
||||
organization!: Organization;
|
||||
|
||||
@ManyToOne(() => CirculationStatusCode)
|
||||
@JoinColumn({ name: 'circulation_status_code', referencedColumnName: 'code' })
|
||||
status!: CirculationStatusCode;
|
||||
|
||||
@ManyToOne(() => User)
|
||||
@JoinColumn({ name: 'created_by_user_id' })
|
||||
creator!: User;
|
||||
|
||||
@OneToMany(() => CirculationRouting, (routing) => routing.circulation, {
|
||||
cascade: true,
|
||||
})
|
||||
routings!: CirculationRouting[];
|
||||
}
|
||||
Reference in New Issue
Block a user