Add user and employee management enhancements with database schema updates
- Introduced new columns `status`, `created_by`, and `updated_by` in the `users` table to track user status and ownership. - Updated the `employees` table to include a foreign key reference to the `users` table via `user_id`. - Created migration script `0012_users_primary.sql` to apply these changes to the database schema. - Enhanced the `EmployeesService` and `EmployeesRepository` to support user assignments and related data retrieval. - Updated DTOs and service methods to reflect the new user and employee relationships. - Added unit tests to validate the new functionality and ensure data integrity. - Modified existing controllers to accommodate the new fields and relationships in user and employee management.
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
parseCsvRecord,
|
||||
type EmployeePosition,
|
||||
} from './employee-fields';
|
||||
import { UsersService } from '../../users/users.service';
|
||||
import { EmployeesRepository } from './employees.repository';
|
||||
|
||||
export type ListEmployeesQuery = {
|
||||
@@ -28,6 +29,7 @@ export type ListEmployeesQuery = {
|
||||
readonly phone?: string;
|
||||
readonly position?: string;
|
||||
readonly status?: string;
|
||||
readonly userId?: string;
|
||||
readonly search?: string;
|
||||
readonly page?: number;
|
||||
readonly limit?: number;
|
||||
@@ -45,13 +47,17 @@ const VISIBLE_FIELDS = [
|
||||
'updatedAt',
|
||||
'createdBy',
|
||||
'updatedBy',
|
||||
'user',
|
||||
] as const;
|
||||
|
||||
const CSV_REQUIRED_HEADERS = ['code', 'name', 'phone', 'position'] as const;
|
||||
|
||||
@Injectable()
|
||||
export class EmployeesService {
|
||||
constructor(private readonly employeesRepository: EmployeesRepository) {}
|
||||
constructor(
|
||||
private readonly employeesRepository: EmployeesRepository,
|
||||
private readonly usersService: UsersService,
|
||||
) {}
|
||||
|
||||
async list(
|
||||
query: ListEmployeesQuery,
|
||||
@@ -63,6 +69,7 @@ export class EmployeesService {
|
||||
phone: query.phone,
|
||||
position: query.position,
|
||||
status: query.status,
|
||||
userId: query.userId,
|
||||
search: query.search,
|
||||
limit: page.limit,
|
||||
offset: page.offset,
|
||||
@@ -100,9 +107,10 @@ export class EmployeesService {
|
||||
position: string;
|
||||
status?: string;
|
||||
userId: string;
|
||||
assignedUserId?: string | null;
|
||||
}): Promise<ReturnType<EmployeesService['toListItem']>> {
|
||||
const created = await this.employeesRepository.create(
|
||||
this.toCreateInput(input),
|
||||
await this.toCreateInput(input),
|
||||
);
|
||||
return this.toListItem(created);
|
||||
}
|
||||
@@ -116,6 +124,7 @@ export class EmployeesService {
|
||||
position?: string;
|
||||
status?: unknown;
|
||||
userId: string;
|
||||
assignedUserId?: string | null;
|
||||
},
|
||||
): Promise<ReturnType<EmployeesService['toListItem']>> {
|
||||
if (input.status !== undefined) {
|
||||
@@ -131,6 +140,7 @@ export class EmployeesService {
|
||||
? this.assertPosition(input.position)
|
||||
: undefined,
|
||||
userId: input.userId,
|
||||
assignedUserId: await this.assertAssignedUserId(input.assignedUserId),
|
||||
};
|
||||
const updated = await this.employeesRepository.update(id, payload);
|
||||
return this.toListItem(updated);
|
||||
@@ -201,14 +211,16 @@ export class EmployeesService {
|
||||
const rowNum = filled[i].lineNo;
|
||||
try {
|
||||
const statusRaw = idx('status') >= 0 ? cols[idx('status')] : '';
|
||||
const assignedRaw = idx('userid') >= 0 ? cols[idx('userid')] : '';
|
||||
rows.push(
|
||||
this.toCreateInput({
|
||||
await this.toCreateInput({
|
||||
code: cols[idx('code')] ?? '',
|
||||
name: cols[idx('name')] ?? '',
|
||||
phone: cols[idx('phone')] ?? '',
|
||||
position: cols[idx('position')] ?? '',
|
||||
status: statusRaw || undefined,
|
||||
userId,
|
||||
assignedUserId: assignedRaw || undefined,
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
@@ -241,6 +253,7 @@ export class EmployeesService {
|
||||
updatedAt: employee.updatedAt.value,
|
||||
createdBy: employee.createdBy,
|
||||
updatedBy: employee.updatedBy,
|
||||
user: employee.user,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -248,14 +261,15 @@ export class EmployeesService {
|
||||
return VISIBLE_FIELDS;
|
||||
}
|
||||
|
||||
private toCreateInput(input: {
|
||||
private async toCreateInput(input: {
|
||||
code: string;
|
||||
name: string;
|
||||
phone: string;
|
||||
position: string;
|
||||
status?: string;
|
||||
userId: string;
|
||||
}): CreateEmployeeInput {
|
||||
assignedUserId?: string | null;
|
||||
}): Promise<CreateEmployeeInput> {
|
||||
return {
|
||||
code: this.assertCode(input.code),
|
||||
name: this.assertName(input.name),
|
||||
@@ -265,9 +279,26 @@ export class EmployeesService {
|
||||
? Status.create(input.status)
|
||||
: Status.create(Status.DEFAULT),
|
||||
userId: input.userId,
|
||||
assignedUserId: await this.assertAssignedUserId(input.assignedUserId),
|
||||
};
|
||||
}
|
||||
|
||||
private async assertAssignedUserId(
|
||||
userId?: string | null,
|
||||
): Promise<string | null | undefined> {
|
||||
if (userId === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (userId === null || userId === '') {
|
||||
return null;
|
||||
}
|
||||
const user = await this.usersService.findById(userId);
|
||||
if (!user) {
|
||||
throw new NotFoundException('User not found');
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
private assertName(raw: string): string {
|
||||
const name = raw.trim();
|
||||
if (!isValidEmployeeName(name)) {
|
||||
|
||||
Reference in New Issue
Block a user