Tipos de toma backend logic
This commit is contained in:
81
water-api/sql/create_meter_types.sql
Normal file
81
water-api/sql/create_meter_types.sql
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
-- ============================================================================
|
||||||
|
-- Create meter_types table and add relationship to projects
|
||||||
|
-- Meter types: LoRa, LoRaWAN, Grandes Consumidores
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Create meter_types table
|
||||||
|
CREATE TABLE IF NOT EXISTS meter_types (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
name VARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
code VARCHAR(20) NOT NULL UNIQUE,
|
||||||
|
description TEXT,
|
||||||
|
is_active BOOLEAN DEFAULT true,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Insert default meter types
|
||||||
|
INSERT INTO meter_types (name, code, description) VALUES
|
||||||
|
('LoRa', 'LORA', 'Medidores con tecnología LoRa'),
|
||||||
|
('LoRaWAN', 'LORAWAN', 'Medidores con tecnología LoRaWAN'),
|
||||||
|
('Grandes Consumidores', 'GRANDES', 'Medidores para grandes consumidores')
|
||||||
|
ON CONFLICT (code) DO NOTHING;
|
||||||
|
|
||||||
|
-- Add meter_type_id column to projects table
|
||||||
|
ALTER TABLE projects
|
||||||
|
ADD COLUMN IF NOT EXISTS meter_type_id UUID REFERENCES meter_types(id) ON DELETE SET NULL;
|
||||||
|
|
||||||
|
-- Add index for better query performance
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_projects_meter_type_id ON projects(meter_type_id);
|
||||||
|
|
||||||
|
-- Add comment
|
||||||
|
COMMENT ON TABLE meter_types IS 'Catalog of meter types (LoRa, LoRaWAN, Grandes Consumidores)';
|
||||||
|
COMMENT ON COLUMN projects.meter_type_id IS 'Default meter type for this project';
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- Helper function to get meter type by code
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION get_meter_type_id(type_code VARCHAR)
|
||||||
|
RETURNS UUID AS $$
|
||||||
|
DECLARE
|
||||||
|
type_id UUID;
|
||||||
|
BEGIN
|
||||||
|
SELECT id INTO type_id FROM meter_types WHERE code = type_code AND is_active = true;
|
||||||
|
RETURN type_id;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- Update trigger for updated_at
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION update_meter_types_updated_at()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE TRIGGER trigger_meter_types_updated_at
|
||||||
|
BEFORE UPDATE ON meter_types
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION update_meter_types_updated_at();
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- Verify the changes
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Show meter types
|
||||||
|
SELECT id, name, code, description, is_active FROM meter_types ORDER BY code;
|
||||||
|
|
||||||
|
-- Show projects table structure
|
||||||
|
SELECT
|
||||||
|
column_name,
|
||||||
|
data_type,
|
||||||
|
is_nullable,
|
||||||
|
column_default
|
||||||
|
FROM information_schema.columns
|
||||||
|
WHERE table_name = 'projects'
|
||||||
|
AND column_name = 'meter_type_id';
|
||||||
185
water-api/src/controllers/meterType.controller.ts
Normal file
185
water-api/src/controllers/meterType.controller.ts
Normal 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',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import { Router } from 'express';
|
|||||||
import authRoutes from './auth.routes';
|
import authRoutes from './auth.routes';
|
||||||
import projectRoutes from './project.routes';
|
import projectRoutes from './project.routes';
|
||||||
import meterRoutes from './meter.routes';
|
import meterRoutes from './meter.routes';
|
||||||
|
import meterTypeRoutes from './meterType.routes';
|
||||||
import concentratorRoutes from './concentrator.routes';
|
import concentratorRoutes from './concentrator.routes';
|
||||||
import gatewayRoutes from './gateway.routes';
|
import gatewayRoutes from './gateway.routes';
|
||||||
import deviceRoutes from './device.routes';
|
import deviceRoutes from './device.routes';
|
||||||
@@ -52,6 +53,17 @@ router.use('/projects', projectRoutes);
|
|||||||
*/
|
*/
|
||||||
router.use('/meters', meterRoutes);
|
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:
|
* Concentrator routes:
|
||||||
* - GET /concentrators - List all concentrators
|
* - GET /concentrators - List all concentrators
|
||||||
|
|||||||
64
water-api/src/routes/meterType.routes.ts
Normal file
64
water-api/src/routes/meterType.routes.ts
Normal 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;
|
||||||
142
water-api/src/services/meterType.service.ts
Normal file
142
water-api/src/services/meterType.service.ts
Normal 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;
|
||||||
|
}
|
||||||
@@ -103,7 +103,7 @@ export async function getAll(
|
|||||||
|
|
||||||
// Get paginated data
|
// Get paginated data
|
||||||
const dataQuery = `
|
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
|
FROM projects
|
||||||
${whereClause}
|
${whereClause}
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
@@ -131,7 +131,7 @@ export async function getAll(
|
|||||||
*/
|
*/
|
||||||
export async function getById(id: string): Promise<Project | null> {
|
export async function getById(id: string): Promise<Project | null> {
|
||||||
const result = await query<Project>(
|
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
|
FROM projects
|
||||||
WHERE id = $1`,
|
WHERE id = $1`,
|
||||||
[id]
|
[id]
|
||||||
@@ -148,15 +148,16 @@ export async function getById(id: string): Promise<Project | null> {
|
|||||||
*/
|
*/
|
||||||
export async function create(data: CreateProjectInput, userId: string): Promise<Project> {
|
export async function create(data: CreateProjectInput, userId: string): Promise<Project> {
|
||||||
const result = await query<Project>(
|
const result = await query<Project>(
|
||||||
`INSERT INTO projects (name, description, area_name, location, status, created_by)
|
`INSERT INTO projects (name, description, area_name, location, status, meter_type_id, created_by)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6)
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
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`,
|
||||||
[
|
[
|
||||||
data.name,
|
data.name,
|
||||||
data.description || null,
|
data.description || null,
|
||||||
data.area_name || null,
|
data.area_name || null,
|
||||||
data.location || null,
|
data.location || null,
|
||||||
data.status || 'ACTIVE',
|
data.status || 'ACTIVE',
|
||||||
|
data.meter_type_id || null,
|
||||||
userId,
|
userId,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@@ -206,6 +207,12 @@ export async function update(id: string, data: UpdateProjectInput): Promise<Proj
|
|||||||
paramIndex++;
|
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
|
// Always update the updated_at timestamp
|
||||||
updates.push(`updated_at = NOW()`);
|
updates.push(`updated_at = NOW()`);
|
||||||
|
|
||||||
@@ -220,7 +227,7 @@ export async function update(id: string, data: UpdateProjectInput): Promise<Proj
|
|||||||
`UPDATE projects
|
`UPDATE projects
|
||||||
SET ${updates.join(', ')}
|
SET ${updates.join(', ')}
|
||||||
WHERE id = $${paramIndex}
|
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
|
params
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,20 @@ export interface TokenPair {
|
|||||||
refreshToken: string;
|
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
|
// Project Types
|
||||||
// ============================================
|
// ============================================
|
||||||
@@ -81,6 +95,7 @@ export interface Project {
|
|||||||
location: string | null;
|
location: string | null;
|
||||||
latitude: number | null;
|
latitude: number | null;
|
||||||
longitude: number | null;
|
longitude: number | null;
|
||||||
|
meter_type_id: string | null;
|
||||||
is_active: boolean;
|
is_active: boolean;
|
||||||
created_by: number;
|
created_by: number;
|
||||||
created_at: Date;
|
created_at: Date;
|
||||||
|
|||||||
@@ -44,6 +44,11 @@ export const createProjectSchema = z.object({
|
|||||||
.enum([ProjectStatus.ACTIVE, ProjectStatus.INACTIVE, ProjectStatus.COMPLETED])
|
.enum([ProjectStatus.ACTIVE, ProjectStatus.INACTIVE, ProjectStatus.COMPLETED])
|
||||||
.default(ProjectStatus.ACTIVE)
|
.default(ProjectStatus.ACTIVE)
|
||||||
.optional(),
|
.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
|
status: z
|
||||||
.enum([ProjectStatus.ACTIVE, ProjectStatus.INACTIVE, ProjectStatus.COMPLETED])
|
.enum([ProjectStatus.ACTIVE, ProjectStatus.INACTIVE, ProjectStatus.COMPLETED])
|
||||||
.optional(),
|
.optional(),
|
||||||
|
meter_type_id: z
|
||||||
|
.string()
|
||||||
|
.uuid('Meter type ID must be a valid UUID')
|
||||||
|
.optional()
|
||||||
|
.nullable(),
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user