[PROJECTS] feat: Add Timesheet entity and DTOs

Add TypeORM entity and DTOs for Projects timesheets feature:
- TimesheetEntity with all fields matching DDL schema
- Extended DTOs for bulk operations (submit, approve, reject)
- Summary types for reporting (by project, user, task)
- Updated module exports to include new files

Complements existing timesheets.service.ts implementation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rckrdmrd 2026-01-20 04:28:19 -06:00
parent b25afada28
commit 24b6ba9b38
6 changed files with 229 additions and 0 deletions

View File

@ -0,0 +1,83 @@
// Note: Basic CreateTimesheetDto, UpdateTimesheetDto, TimesheetFilters are defined in timesheets.service.ts
// This file contains extended DTOs for additional functionality
/**
* Respuesta de timesheet con datos relacionados
*/
export interface TimesheetResponse {
id: string;
tenant_id: string;
company_id: string;
project_id: string;
project_name?: string;
task_id: string | null;
task_name?: string;
user_id: string;
user_name?: string;
date: Date;
hours: number;
description: string | null;
billable: boolean;
invoiced: boolean;
invoice_id: string | null;
status: 'draft' | 'submitted' | 'approved' | 'rejected';
approved_by: string | null;
approved_at: Date | null;
created_at: Date;
created_by: string | null;
updated_at: Date | null;
}
/**
* Resumen de horas por proyecto
*/
export interface TimesheetSummaryByProject {
project_id: string;
project_name: string;
total_hours: number;
billable_hours: number;
non_billable_hours: number;
invoiced_hours: number;
pending_hours: number;
}
/**
* Resumen de horas por usuario
*/
export interface TimesheetSummaryByUser {
user_id: string;
user_name: string;
total_hours: number;
billable_hours: number;
approved_hours: number;
pending_approval: number;
}
/**
* Resumen de horas por tarea
*/
export interface TimesheetSummaryByTask {
task_id: string;
task_name: string;
project_id: string;
project_name: string;
estimated_hours: number;
spent_hours: number;
remaining_hours: number;
progress_percentage: number;
}
/**
* Resumen general de timesheets
*/
export interface TimesheetSummary {
total_hours: number;
billable_hours: number;
non_billable_hours: number;
invoiced_hours: number;
approved_hours: number;
pending_approval_hours: number;
by_project?: TimesheetSummaryByProject[];
by_user?: TimesheetSummaryByUser[];
by_task?: TimesheetSummaryByTask[];
}

View File

@ -0,0 +1,2 @@
export * from './create-timesheet.dto.js';
export * from './update-timesheet.dto.js';

View File

@ -0,0 +1,42 @@
// Note: Basic UpdateTimesheetDto is defined in timesheets.service.ts
// This file contains extended DTOs for additional functionality
/**
* DTO para enviar un timesheet a aprobacion
*/
export interface SubmitTimesheetDto {
/** IDs de los timesheets a enviar */
timesheet_ids: string[];
}
/**
* DTO para aprobar/rechazar timesheets
*/
export interface ApproveTimesheetDto {
/** IDs de los timesheets a aprobar/rechazar */
timesheet_ids: string[];
/** Comentario opcional del aprobador */
comment?: string;
}
/**
* DTO para marcar timesheets como facturados
*/
export interface MarkInvoicedDto {
/** IDs de los timesheets a marcar */
timesheet_ids: string[];
/** ID de la factura */
invoice_id: string;
}
/**
* DTO para accion masiva sobre timesheets
*/
export interface BulkTimesheetActionDto {
/** IDs de los timesheets */
timesheet_ids: string[];
/** Accion a realizar */
action: 'submit' | 'approve' | 'reject' | 'delete';
/** Comentario opcional */
comment?: string;
}

View File

@ -0,0 +1 @@
export * from './timesheet.entity.js';

View File

@ -0,0 +1,95 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
Index,
ManyToOne,
JoinColumn,
} from 'typeorm';
export enum TimesheetStatus {
DRAFT = 'draft',
SUBMITTED = 'submitted',
APPROVED = 'approved',
REJECTED = 'rejected',
}
@Entity({ schema: 'projects', name: 'timesheets' })
@Index('idx_timesheets_tenant', ['tenantId'])
@Index('idx_timesheets_company', ['companyId'])
@Index('idx_timesheets_project', ['projectId'])
@Index('idx_timesheets_task', ['taskId'])
@Index('idx_timesheets_user', ['userId'])
@Index('idx_timesheets_user_date', ['userId', 'date'])
@Index('idx_timesheets_date', ['date'])
@Index('idx_timesheets_status', ['status'])
export class TimesheetEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ type: 'uuid', nullable: false, name: 'tenant_id' })
tenantId: string;
@Column({ type: 'uuid', nullable: false, name: 'company_id' })
companyId: string;
@Column({ type: 'uuid', nullable: false, name: 'project_id' })
projectId: string;
@Column({ type: 'uuid', nullable: true, name: 'task_id' })
taskId: string | null;
@Column({ type: 'uuid', nullable: false, name: 'user_id' })
userId: string;
@Column({ type: 'date', nullable: false })
date: Date;
@Column({ type: 'decimal', precision: 5, scale: 2, nullable: false })
hours: number;
@Column({ type: 'text', nullable: true })
description: string | null;
@Column({ type: 'boolean', default: true, nullable: false })
billable: boolean;
@Column({ type: 'boolean', default: false, nullable: false })
invoiced: boolean;
@Column({ type: 'uuid', nullable: true, name: 'invoice_id' })
invoiceId: string | null;
@Column({
type: 'enum',
enum: TimesheetStatus,
default: TimesheetStatus.DRAFT,
nullable: false,
})
status: TimesheetStatus;
@Column({ type: 'uuid', nullable: true, name: 'approved_by' })
approvedBy: string | null;
@Column({ type: 'timestamp', nullable: true, name: 'approved_at' })
approvedAt: Date | null;
// Audit fields
@CreateDateColumn({ name: 'created_at', type: 'timestamp' })
createdAt: Date;
@Column({ type: 'uuid', nullable: true, name: 'created_by' })
createdBy: string | null;
@UpdateDateColumn({
name: 'updated_at',
type: 'timestamp',
nullable: true,
})
updatedAt: Date | null;
@Column({ type: 'uuid', nullable: true, name: 'updated_by' })
updatedBy: string | null;
}

View File

@ -5,3 +5,9 @@ export * from './billing.service.js';
export * from './hr-integration.service.js';
export * from './projects.controller.js';
export { default as projectsRoutes } from './projects.routes.js';
// Entities
export * from './entities/index.js';
// DTOs
export * from './dto/index.js';