erp-construccion-backend-v2/src/modules/branches/services/branch-schedule.service.ts
Adrian Flores Cortes 8f8843cd10 [ERP-CONSTRUCCION] feat: Implement 6 core business modules
Branches:
- 5 services: branch, schedule, inventory-settings, payment-terminal, user-assignment
- Hierarchical management, schedules, terminals

Products:
- 6 services: category, product, price, supplier, attribute, variant
- Hierarchical categories, multi-pricing, variants

Projects:
- 6 services: project, task, timesheet, milestone, member, stage
- Kanban support, timesheet approval workflow

Sales:
- 2 services: quotation, sales-order
- Full sales workflow with quotation-to-order conversion

Invoices:
- 2 services: invoice, payment
- Complete invoicing with payment allocation

Notifications:
- 5 services: notification, preference, template, channel, in-app
- Multi-channel support (email, sms, push, whatsapp, in-app)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 00:24:00 -06:00

207 lines
5.4 KiB
TypeScript

/**
* BranchSchedule Service
* Servicio para gestión de horarios de sucursales
*
* @module Branches
*/
import { Repository, FindOptionsWhere } from 'typeorm';
import { AppDataSource } from '../../../shared/database/typeorm.config';
import { BranchSchedule, ScheduleType } from '../entities/branch-schedule.entity';
import { ServiceContext } from './branch.service';
export interface CreateBranchScheduleDto {
branchId: string;
name: string;
description?: string;
scheduleType?: ScheduleType;
dayOfWeek?: number;
specificDate?: Date;
openTime: string;
closeTime: string;
shifts?: Array<{ name: string; start: string; end: string }>;
isActive?: boolean;
}
export interface UpdateBranchScheduleDto {
name?: string;
description?: string;
scheduleType?: ScheduleType;
dayOfWeek?: number;
specificDate?: Date;
openTime?: string;
closeTime?: string;
shifts?: Array<{ name: string; start: string; end: string }>;
isActive?: boolean;
}
export interface BranchScheduleFilters {
branchId?: string;
scheduleType?: ScheduleType;
dayOfWeek?: number;
isActive?: boolean;
}
export class BranchScheduleService {
private repository: Repository<BranchSchedule>;
constructor() {
this.repository = AppDataSource.getRepository(BranchSchedule);
}
async findAll(_ctx: ServiceContext, filters?: BranchScheduleFilters): Promise<BranchSchedule[]> {
const where: FindOptionsWhere<BranchSchedule> = {};
if (filters?.branchId) {
where.branchId = filters.branchId;
}
if (filters?.scheduleType) {
where.scheduleType = filters.scheduleType;
}
if (filters?.dayOfWeek !== undefined) {
where.dayOfWeek = filters.dayOfWeek;
}
if (filters?.isActive !== undefined) {
where.isActive = filters.isActive;
}
return this.repository.find({
where,
relations: ['branch'],
order: { dayOfWeek: 'ASC', openTime: 'ASC' },
});
}
async findById(_ctx: ServiceContext, id: string): Promise<BranchSchedule | null> {
return this.repository.findOne({
where: { id },
relations: ['branch'],
});
}
async findByBranch(_ctx: ServiceContext, branchId: string): Promise<BranchSchedule[]> {
return this.repository.find({
where: { branchId, isActive: true },
order: { dayOfWeek: 'ASC', openTime: 'ASC' },
});
}
async findRegularSchedule(_ctx: ServiceContext, branchId: string): Promise<BranchSchedule[]> {
return this.repository.find({
where: { branchId, scheduleType: 'regular', isActive: true },
order: { dayOfWeek: 'ASC' },
});
}
async findHolidaySchedule(_ctx: ServiceContext, branchId: string): Promise<BranchSchedule[]> {
return this.repository.find({
where: { branchId, scheduleType: 'holiday', isActive: true },
order: { specificDate: 'ASC' },
});
}
async findScheduleForDate(
_ctx: ServiceContext,
branchId: string,
date: Date
): Promise<BranchSchedule | null> {
// First check for specific date (holiday or special)
const specificSchedule = await this.repository.findOne({
where: {
branchId,
specificDate: date,
isActive: true,
},
});
if (specificSchedule) {
return specificSchedule;
}
// Fall back to regular schedule for day of week
const dayOfWeek = date.getDay();
return this.repository.findOne({
where: {
branchId,
scheduleType: 'regular',
dayOfWeek,
isActive: true,
},
});
}
async create(_ctx: ServiceContext, data: CreateBranchScheduleDto): Promise<BranchSchedule> {
const schedule = this.repository.create(data);
return this.repository.save(schedule);
}
async update(
ctx: ServiceContext,
id: string,
data: UpdateBranchScheduleDto
): Promise<BranchSchedule | null> {
const schedule = await this.findById(ctx, id);
if (!schedule) {
return null;
}
Object.assign(schedule, data);
return this.repository.save(schedule);
}
async delete(_ctx: ServiceContext, id: string): Promise<boolean> {
const result = await this.repository.delete({ id });
return result.affected ? result.affected > 0 : false;
}
async createWeeklySchedule(
_ctx: ServiceContext,
branchId: string,
weeklyHours: Array<{
dayOfWeek: number;
openTime: string;
closeTime: string;
shifts?: Array<{ name: string; start: string; end: string }>;
}>
): Promise<BranchSchedule[]> {
// Delete existing regular schedules for this branch
await this.repository.delete({
branchId,
scheduleType: 'regular',
});
// Create new schedules
const schedules = weeklyHours.map(hours =>
this.repository.create({
branchId,
name: `Regular - Day ${hours.dayOfWeek}`,
scheduleType: 'regular',
dayOfWeek: hours.dayOfWeek,
openTime: hours.openTime,
closeTime: hours.closeTime,
shifts: hours.shifts || [],
isActive: true,
})
);
return this.repository.save(schedules);
}
async isBranchOpenAt(
ctx: ServiceContext,
branchId: string,
dateTime: Date
): Promise<boolean> {
const schedule = await this.findScheduleForDate(ctx, branchId, dateTime);
if (!schedule) {
return false;
}
const timeStr = dateTime.toTimeString().slice(0, 5); // HH:MM format
return timeStr >= schedule.openTime && timeStr <= schedule.closeTime;
}
}