175 lines
5.3 KiB
JavaScript
175 lines
5.3 KiB
JavaScript
"use strict";
|
|
/**
|
|
* LoteService - Gestión de lotes/terrenos
|
|
*
|
|
* CRUD de lotes con soporte multi-tenant.
|
|
*
|
|
* @module Construction
|
|
*/
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.LoteService = void 0;
|
|
const typeorm_1 = require("typeorm");
|
|
class LoteService {
|
|
repository;
|
|
constructor(repository) {
|
|
this.repository = repository;
|
|
}
|
|
/**
|
|
* Listar lotes
|
|
*/
|
|
async findAll(options) {
|
|
const { tenantId, manzanaId, prototipoId, page = 1, limit = 20, search, status } = options;
|
|
const query = this.repository
|
|
.createQueryBuilder('l')
|
|
.where('l.tenant_id = :tenantId', { tenantId })
|
|
.andWhere('l.deleted_at IS NULL');
|
|
if (manzanaId) {
|
|
query.andWhere('l.manzana_id = :manzanaId', { manzanaId });
|
|
}
|
|
if (prototipoId) {
|
|
query.andWhere('l.prototipo_id = :prototipoId', { prototipoId });
|
|
}
|
|
if (search) {
|
|
query.andWhere('(l.code ILIKE :search OR l.official_number ILIKE :search)', { search: `%${search}%` });
|
|
}
|
|
if (status) {
|
|
query.andWhere('l.status = :status', { status });
|
|
}
|
|
const total = await query.getCount();
|
|
const items = await query
|
|
.leftJoinAndSelect('l.prototipo', 'prototipo')
|
|
.skip((page - 1) * limit)
|
|
.take(limit)
|
|
.orderBy('l.code', 'ASC')
|
|
.getMany();
|
|
return { items, total };
|
|
}
|
|
/**
|
|
* Obtener lote por ID
|
|
*/
|
|
async findById(id, tenantId) {
|
|
return this.repository.findOne({
|
|
where: { id, tenantId, deletedAt: (0, typeorm_1.IsNull)() },
|
|
relations: ['prototipo', 'manzana'],
|
|
});
|
|
}
|
|
/**
|
|
* Obtener lote por código dentro de una manzana
|
|
*/
|
|
async findByCode(code, manzanaId, tenantId) {
|
|
return this.repository.findOne({
|
|
where: { code, manzanaId, tenantId, deletedAt: (0, typeorm_1.IsNull)() },
|
|
});
|
|
}
|
|
/**
|
|
* Crear lote
|
|
*/
|
|
async create(tenantId, dto, createdBy) {
|
|
const existing = await this.findByCode(dto.code, dto.manzanaId, tenantId);
|
|
if (existing) {
|
|
throw new Error('Lot code already exists in this block');
|
|
}
|
|
return this.repository.save(this.repository.create({
|
|
tenantId,
|
|
...dto,
|
|
createdBy,
|
|
status: dto.status || 'available',
|
|
}));
|
|
}
|
|
/**
|
|
* Actualizar lote
|
|
*/
|
|
async update(id, tenantId, dto, updatedBy) {
|
|
const lote = await this.findById(id, tenantId);
|
|
if (!lote) {
|
|
throw new Error('Lot not found');
|
|
}
|
|
// Verificar código único si se está cambiando
|
|
if (dto.code && dto.code !== lote.code) {
|
|
const existing = await this.findByCode(dto.code, lote.manzanaId, tenantId);
|
|
if (existing) {
|
|
throw new Error('Lot code already exists in this block');
|
|
}
|
|
}
|
|
await this.repository.update(id, {
|
|
...dto,
|
|
updatedBy,
|
|
updatedAt: new Date(),
|
|
});
|
|
return this.findById(id, tenantId);
|
|
}
|
|
/**
|
|
* Eliminar lote (soft delete)
|
|
*/
|
|
async delete(id, tenantId, _deletedBy) {
|
|
const lote = await this.findById(id, tenantId);
|
|
if (!lote) {
|
|
throw new Error('Lot not found');
|
|
}
|
|
// Verificar que no esté vendido
|
|
if (lote.status === 'sold') {
|
|
throw new Error('Cannot delete a sold lot');
|
|
}
|
|
await this.repository.update(id, {
|
|
deletedAt: new Date(),
|
|
});
|
|
}
|
|
/**
|
|
* Obtener lotes por manzana
|
|
*/
|
|
async findByManzana(manzanaId, tenantId) {
|
|
return this.repository.find({
|
|
where: { manzanaId, tenantId, deletedAt: (0, typeorm_1.IsNull)() },
|
|
order: { code: 'ASC' },
|
|
relations: ['prototipo'],
|
|
});
|
|
}
|
|
/**
|
|
* Asignar prototipo a lote
|
|
*/
|
|
async assignPrototipo(id, tenantId, prototipoId, updatedBy) {
|
|
const lote = await this.findById(id, tenantId);
|
|
if (!lote) {
|
|
throw new Error('Lot not found');
|
|
}
|
|
await this.repository.update(id, {
|
|
prototipoId,
|
|
updatedBy,
|
|
updatedAt: new Date(),
|
|
});
|
|
return this.findById(id, tenantId);
|
|
}
|
|
/**
|
|
* Cambiar estado del lote
|
|
*/
|
|
async changeStatus(id, tenantId, status, updatedBy) {
|
|
const lote = await this.findById(id, tenantId);
|
|
if (!lote) {
|
|
throw new Error('Lot not found');
|
|
}
|
|
await this.repository.update(id, {
|
|
status,
|
|
updatedBy,
|
|
updatedAt: new Date(),
|
|
});
|
|
return this.findById(id, tenantId);
|
|
}
|
|
/**
|
|
* Obtener estadísticas de lotes por estado
|
|
*/
|
|
async getStatsByStatus(tenantId, manzanaId) {
|
|
const query = this.repository
|
|
.createQueryBuilder('l')
|
|
.select('l.status', 'status')
|
|
.addSelect('COUNT(*)', 'count')
|
|
.where('l.tenant_id = :tenantId', { tenantId })
|
|
.andWhere('l.deleted_at IS NULL')
|
|
.groupBy('l.status');
|
|
if (manzanaId) {
|
|
query.andWhere('l.manzana_id = :manzanaId', { manzanaId });
|
|
}
|
|
return query.getRawMany();
|
|
}
|
|
}
|
|
exports.LoteService = LoteService;
|
|
//# sourceMappingURL=lote.service.js.map
|