Tipos de toma backend logic

This commit is contained in:
2026-02-02 17:37:10 -06:00
parent e06941fd02
commit 6c5323906d
8 changed files with 522 additions and 6 deletions

View File

@@ -0,0 +1,185 @@
import { Request, Response } from 'express';
import * as meterTypeService from '../services/meterType.service';
/**
* GET /meter-types
* Get all active meter types
*/
export async function getAll(req: Request, res: Response): Promise<void> {
try {
const meterTypes = await meterTypeService.getAll();
res.status(200).json({
success: true,
data: meterTypes,
});
} catch (error) {
console.error('Error fetching meter types:', error);
res.status(500).json({
success: false,
error: 'Failed to fetch meter types',
});
}
}
/**
* GET /meter-types/:id
* Get a single meter type by ID
*/
export async function getById(req: Request, res: Response): Promise<void> {
try {
const { id } = req.params;
const meterType = await meterTypeService.getById(id);
if (!meterType) {
res.status(404).json({
success: false,
error: 'Meter type not found',
});
return;
}
res.status(200).json({
success: true,
data: meterType,
});
} catch (error) {
console.error('Error fetching meter type:', error);
res.status(500).json({
success: false,
error: 'Failed to fetch meter type',
});
}
}
/**
* GET /meter-types/code/:code
* Get a meter type by code
*/
export async function getByCode(req: Request, res: Response): Promise<void> {
try {
const { code } = req.params;
const meterType = await meterTypeService.getByCode(code);
if (!meterType) {
res.status(404).json({
success: false,
error: 'Meter type not found',
});
return;
}
res.status(200).json({
success: true,
data: meterType,
});
} catch (error) {
console.error('Error fetching meter type:', error);
res.status(500).json({
success: false,
error: 'Failed to fetch meter type',
});
}
}
/**
* POST /meter-types
* Create a new meter type (ADMIN only)
*/
export async function create(req: Request, res: Response): Promise<void> {
try {
const { name, code, description } = req.body;
if (!name || !code) {
res.status(400).json({
success: false,
error: 'Name and code are required',
});
return;
}
const meterType = await meterTypeService.create({
name,
code,
description,
});
res.status(201).json({
success: true,
data: meterType,
});
} catch (error) {
console.error('Error creating meter type:', error);
res.status(500).json({
success: false,
error: 'Failed to create meter type',
});
}
}
/**
* PUT /meter-types/:id
* Update a meter type (ADMIN only)
*/
export async function update(req: Request, res: Response): Promise<void> {
try {
const { id } = req.params;
const { name, code, description, is_active } = req.body;
const meterType = await meterTypeService.update(id, {
name,
code,
description,
is_active,
});
if (!meterType) {
res.status(404).json({
success: false,
error: 'Meter type not found',
});
return;
}
res.status(200).json({
success: true,
data: meterType,
});
} catch (error) {
console.error('Error updating meter type:', error);
res.status(500).json({
success: false,
error: 'Failed to update meter type',
});
}
}
/**
* DELETE /meter-types/:id
* Soft delete a meter type (ADMIN only)
*/
export async function remove(req: Request, res: Response): Promise<void> {
try {
const { id } = req.params;
const success = await meterTypeService.remove(id);
if (!success) {
res.status(404).json({
success: false,
error: 'Meter type not found',
});
return;
}
res.status(200).json({
success: true,
message: 'Meter type deleted successfully',
});
} catch (error) {
console.error('Error deleting meter type:', error);
res.status(500).json({
success: false,
error: 'Failed to delete meter type',
});
}
}

View File

@@ -4,6 +4,7 @@ import { Router } from 'express';
import authRoutes from './auth.routes';
import projectRoutes from './project.routes';
import meterRoutes from './meter.routes';
import meterTypeRoutes from './meterType.routes';
import concentratorRoutes from './concentrator.routes';
import gatewayRoutes from './gateway.routes';
import deviceRoutes from './device.routes';
@@ -52,6 +53,17 @@ router.use('/projects', projectRoutes);
*/
router.use('/meters', meterRoutes);
/**
* Meter Type routes:
* - GET /meter-types - List all meter types
* - GET /meter-types/:id - Get meter type by ID
* - GET /meter-types/code/:code - Get meter type by code
* - POST /meter-types - Create meter type (admin only)
* - PUT /meter-types/:id - Update meter type (admin only)
* - DELETE /meter-types/:id - Delete meter type (admin only)
*/
router.use('/meter-types', meterTypeRoutes);
/**
* Concentrator routes:
* - GET /concentrators - List all concentrators

View File

@@ -0,0 +1,64 @@
import { Router } from 'express';
import * as meterTypeController from '../controllers/meterType.controller';
import { authenticateToken, requireRole } from '../middleware/auth.middleware';
const router = Router();
/**
* @route GET /api/meter-types
* @desc Get all active meter types
* @access Private (any authenticated user)
*/
router.get('/', authenticateToken, meterTypeController.getAll);
/**
* @route GET /api/meter-types/code/:code
* @desc Get a meter type by code
* @access Private (any authenticated user)
*/
router.get('/code/:code', authenticateToken, meterTypeController.getByCode);
/**
* @route GET /api/meter-types/:id
* @desc Get a single meter type by ID
* @access Private (any authenticated user)
*/
router.get('/:id', authenticateToken, meterTypeController.getById);
/**
* @route POST /api/meter-types
* @desc Create a new meter type
* @access Private (ADMIN only)
*/
router.post(
'/',
authenticateToken,
requireRole('ADMIN'),
meterTypeController.create
);
/**
* @route PUT /api/meter-types/:id
* @desc Update a meter type
* @access Private (ADMIN only)
*/
router.put(
'/:id',
authenticateToken,
requireRole('ADMIN'),
meterTypeController.update
);
/**
* @route DELETE /api/meter-types/:id
* @desc Soft delete a meter type
* @access Private (ADMIN only)
*/
router.delete(
'/:id',
authenticateToken,
requireRole('ADMIN'),
meterTypeController.remove
);
export default router;

View File

@@ -0,0 +1,142 @@
import { query } from '../config/database';
import { MeterType } from '../types';
/**
* Get all meter types
* @returns Promise resolving to array of meter types
*/
export async function getAll(): Promise<MeterType[]> {
const result = await query<MeterType>(
`SELECT id, name, code, description, is_active, created_at, updated_at
FROM meter_types
WHERE is_active = true
ORDER BY code ASC`
);
return result.rows;
}
/**
* Get a single meter type by ID
* @param id - Meter type ID
* @returns Promise resolving to meter type or null if not found
*/
export async function getById(id: string): Promise<MeterType | null> {
const result = await query<MeterType>(
`SELECT id, name, code, description, is_active, created_at, updated_at
FROM meter_types
WHERE id = $1`,
[id]
);
return result.rows[0] || null;
}
/**
* Get a meter type by code
* @param code - Meter type code (LORA, LORAWAN, GRANDES)
* @returns Promise resolving to meter type or null if not found
*/
export async function getByCode(code: string): Promise<MeterType | null> {
const result = await query<MeterType>(
`SELECT id, name, code, description, is_active, created_at, updated_at
FROM meter_types
WHERE code = $1 AND is_active = true`,
[code]
);
return result.rows[0] || null;
}
/**
* Create a new meter type
* @param data - Meter type data
* @returns Promise resolving to created meter type
*/
export async function create(data: {
name: string;
code: string;
description?: string | null;
}): Promise<MeterType> {
const result = await query<MeterType>(
`INSERT INTO meter_types (name, code, description)
VALUES ($1, $2, $3)
RETURNING id, name, code, description, is_active, created_at, updated_at`,
[data.name, data.code, data.description || null]
);
return result.rows[0];
}
/**
* Update a meter type
* @param id - Meter type ID
* @param data - Fields to update
* @returns Promise resolving to updated meter type or null if not found
*/
export async function update(
id: string,
data: {
name?: string;
code?: string;
description?: string | null;
is_active?: boolean;
}
): Promise<MeterType | null> {
const updates: string[] = [];
const params: unknown[] = [];
let paramIndex = 1;
if (data.name !== undefined) {
updates.push(`name = $${paramIndex}`);
params.push(data.name);
paramIndex++;
}
if (data.code !== undefined) {
updates.push(`code = $${paramIndex}`);
params.push(data.code);
paramIndex++;
}
if (data.description !== undefined) {
updates.push(`description = $${paramIndex}`);
params.push(data.description);
paramIndex++;
}
if (data.is_active !== undefined) {
updates.push(`is_active = $${paramIndex}`);
params.push(data.is_active);
paramIndex++;
}
if (updates.length === 0) {
return getById(id);
}
params.push(id);
const result = await query<MeterType>(
`UPDATE meter_types
SET ${updates.join(', ')}
WHERE id = $${paramIndex}
RETURNING id, name, code, description, is_active, created_at, updated_at`,
params
);
return result.rows[0] || null;
}
/**
* Delete a meter type (soft delete by setting is_active to false)
* @param id - Meter type ID
* @returns Promise resolving to boolean indicating success
*/
export async function remove(id: string): Promise<boolean> {
const result = await query(
`UPDATE meter_types SET is_active = false WHERE id = $1`,
[id]
);
return result.rowCount ? result.rowCount > 0 : false;
}

View File

@@ -103,7 +103,7 @@ export async function getAll(
// Get paginated data
const dataQuery = `
SELECT id, name, description, area_name, location, status, created_by, created_at, updated_at
SELECT id, name, description, area_name, location, status, meter_type_id, created_by, created_at, updated_at
FROM projects
${whereClause}
ORDER BY created_at DESC
@@ -131,7 +131,7 @@ export async function getAll(
*/
export async function getById(id: string): Promise<Project | null> {
const result = await query<Project>(
`SELECT id, name, description, area_name, location, status, created_by, created_at, updated_at
`SELECT id, name, description, area_name, location, status, meter_type_id, created_by, created_at, updated_at
FROM projects
WHERE id = $1`,
[id]
@@ -148,15 +148,16 @@ export async function getById(id: string): Promise<Project | null> {
*/
export async function create(data: CreateProjectInput, userId: string): Promise<Project> {
const result = await query<Project>(
`INSERT INTO projects (name, description, area_name, location, status, created_by)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id, name, description, area_name, location, status, created_by, created_at, updated_at`,
`INSERT INTO projects (name, description, area_name, location, status, meter_type_id, created_by)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING id, name, description, area_name, location, status, meter_type_id, created_by, created_at, updated_at`,
[
data.name,
data.description || null,
data.area_name || null,
data.location || null,
data.status || 'ACTIVE',
data.meter_type_id || null,
userId,
]
);
@@ -206,6 +207,12 @@ export async function update(id: string, data: UpdateProjectInput): Promise<Proj
paramIndex++;
}
if (data.meter_type_id !== undefined) {
updates.push(`meter_type_id = $${paramIndex}`);
params.push(data.meter_type_id);
paramIndex++;
}
// Always update the updated_at timestamp
updates.push(`updated_at = NOW()`);
@@ -220,7 +227,7 @@ export async function update(id: string, data: UpdateProjectInput): Promise<Proj
`UPDATE projects
SET ${updates.join(', ')}
WHERE id = $${paramIndex}
RETURNING id, name, description, area_name, location, status, created_by, created_at, updated_at`,
RETURNING id, name, description, area_name, location, status, meter_type_id, created_by, created_at, updated_at`,
params
);

View File

@@ -70,6 +70,20 @@ export interface TokenPair {
refreshToken: string;
}
// ============================================
// Meter Type Types
// ============================================
export interface MeterType {
id: string;
name: string;
code: string;
description: string | null;
is_active: boolean;
created_at: Date;
updated_at: Date;
}
// ============================================
// Project Types
// ============================================
@@ -81,6 +95,7 @@ export interface Project {
location: string | null;
latitude: number | null;
longitude: number | null;
meter_type_id: string | null;
is_active: boolean;
created_by: number;
created_at: Date;

View File

@@ -44,6 +44,11 @@ export const createProjectSchema = z.object({
.enum([ProjectStatus.ACTIVE, ProjectStatus.INACTIVE, ProjectStatus.COMPLETED])
.default(ProjectStatus.ACTIVE)
.optional(),
meter_type_id: z
.string()
.uuid('Meter type ID must be a valid UUID')
.optional()
.nullable(),
});
/**
@@ -74,6 +79,11 @@ export const updateProjectSchema = z.object({
status: z
.enum([ProjectStatus.ACTIVE, ProjectStatus.INACTIVE, ProjectStatus.COMPLETED])
.optional(),
meter_type_id: z
.string()
.uuid('Meter type ID must be a valid UUID')
.optional()
.nullable(),
});
/**