Skip to main content

Ciudades API

The Ciudades (Cities) API provides read-only access to cities used throughout the POS system for geographic location management. Cities are essential for supplier registration, customer addresses, and location-based reporting.

Endpoints Overview

MethodEndpointAuthenticationDescription
GET/api/pos/ciudadRequiredGet all cities
The Ciudades API is read-only. City management is typically handled through database administration tools or dedicated admin interfaces.

GET /api/pos/ciudad

Retrieve a complete list of all cities in the system, ordered alphabetically by city name.

Authentication

Authorization
string
required
Bearer token received from the login endpointExample: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Response

Returns an array of city objects, each containing:
ct_codigo
string
Unique city code identifier
ct_descripcion
string
City name/description

Example Request

curl -X GET http://localhost:3000/api/pos/ciudad \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response Examples

[
  {
    "ct_codigo": "CT001",
    "ct_descripcion": "Ambato"
  },
  {
    "ct_codigo": "CT002",
    "ct_descripcion": "Cuenca"
  },
  {
    "ct_codigo": "CT003",
    "ct_descripcion": "Guayaquil"
  },
  {
    "ct_codigo": "CT004",
    "ct_descripcion": "Ibarra"
  },
  {
    "ct_codigo": "CT005",
    "ct_descripcion": "Loja"
  },
  {
    "ct_codigo": "CT006",
    "ct_descripcion": "Machala"
  },
  {
    "ct_codigo": "CT007",
    "ct_descripcion": "Manta"
  },
  {
    "ct_codigo": "CT008",
    "ct_descripcion": "Portoviejo"
  },
  {
    "ct_codigo": "CT009",
    "ct_descripcion": "Quito"
  },
  {
    "ct_codigo": "CT010",
    "ct_descripcion": "Riobamba"
  },
  {
    "ct_codigo": "CT011",
    "ct_descripcion": "Santo Domingo"
  }
]
Results are ordered alphabetically by ct_descripcion for easy reference in dropdown menus and selection interfaces.
{
  "message": "Token inválido o expirado"
}
This error occurs when:
  • No Authorization header is provided
  • The token is invalid or malformed
  • The token has expired
{
  "message": "Error al listar ciudades"
}

Usage Examples

JavaScript - Populate City Dropdown

async function loadCities() {
  try {
    const response = await fetch('http://localhost:3000/api/pos/ciudad', {
      headers: {
        'Authorization': `Bearer ${token}`
      }
    });
    
    const cities = await response.json();
    
    const select = document.getElementById('citySelect');
    
    // Add default option
    const defaultOption = document.createElement('option');
    defaultOption.value = '';
    defaultOption.textContent = 'Seleccione una ciudad';
    select.appendChild(defaultOption);
    
    // Add city options
    cities.forEach(city => {
      const option = document.createElement('option');
      option.value = city.ct_codigo;
      option.textContent = city.ct_descripcion;
      select.appendChild(option);
    });
  } catch (error) {
    console.error('Error loading cities:', error);
  }
}

Python - Create City Lookup

import requests

def get_cities_lookup(token):
    """Get cities as a dictionary for quick lookups"""
    url = "http://localhost:3000/api/pos/ciudad"
    headers = {"Authorization": f"Bearer {token}"}
    
    response = requests.get(url, headers=headers)
    cities = response.json()
    
    # Create a dictionary for quick lookups
    cities_dict = {
        city['ct_codigo']: city['ct_descripcion']
        for city in cities
    }
    
    return cities_dict

# Usage
cities = get_cities_lookup(token)
print(cities['CT009'])  # Output: "Quito"
print(cities['CT003'])  # Output: "Guayaquil"

React - Cities Hook with Caching

import { useState, useEffect } from 'react';

function useCities(token) {
  const [cities, setCities] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchCities() {
      // Check cache first
      const cached = localStorage.getItem('cities');
      const cacheTime = localStorage.getItem('cities_cache_time');
      const now = Date.now();
      const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours

      if (cached && cacheTime && (now - parseInt(cacheTime)) < CACHE_DURATION) {
        setCities(JSON.parse(cached));
        setLoading(false);
        return;
      }

      try {
        const response = await fetch('http://localhost:3000/api/pos/ciudad', {
          headers: {
            'Authorization': `Bearer ${token}`
          }
        });
        
        if (!response.ok) {
          throw new Error('Failed to fetch cities');
        }
        
        const data = await response.json();
        setCities(data);
        
        // Cache the results
        localStorage.setItem('cities', JSON.stringify(data));
        localStorage.setItem('cities_cache_time', now.toString());
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }

    fetchCities();
  }, [token]);

  const getCityName = (codigo) => {
    const city = cities.find(c => c.ct_codigo === codigo);
    return city ? city.ct_descripcion : codigo;
  };

  return { cities, loading, error, getCityName };
}

// Usage in component
function SupplierForm() {
  const token = localStorage.getItem('token');
  const { cities, loading, error } = useCities(token);

  if (loading) return <div>Cargando ciudades...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <form>
      <label htmlFor="city">Ciudad:</label>
      <select id="city" name="ct_codigo" required>
        <option value="">Seleccione una ciudad</option>
        {cities.map(city => (
          <option key={city.ct_codigo} value={city.ct_codigo}>
            {city.ct_descripcion}
          </option>
        ))}
      </select>
    </form>
  );
}

// Display component
function SupplierDetails({ supplier }) {
  const { getCityName } = useCities(token);
  
  return (
    <div>
      <h3>{supplier.prv_razonsocial}</h3>
      <p>Ciudad: {getCityName(supplier.ct_codigo)}</p>
    </div>
  );
}

TypeScript - Type-Safe Cities Service

interface Ciudad {
  ct_codigo: string;
  ct_descripcion: string;
}

class CitiesService {
  private static cities: Ciudad[] = [];
  private static lastFetch: number = 0;
  private static CACHE_DURATION = 86400000; // 24 hours

  static async getCities(token: string): Promise<Ciudad[]> {
    const now = Date.now();
    
    // Return cached cities if still fresh
    if (this.cities.length > 0 && now - this.lastFetch < this.CACHE_DURATION) {
      return this.cities;
    }

    const response = await fetch('http://localhost:3000/api/pos/ciudad', {
      headers: {
        'Authorization': `Bearer ${token}`
      }
    });

    if (!response.ok) {
      throw new Error('Failed to fetch cities');
    }

    this.cities = await response.json();
    this.lastFetch = now;
    return this.cities;
  }

  static findByCode(codigo: string): Ciudad | undefined {
    return this.cities.find(c => c.ct_codigo === codigo);
  }

  static findByName(name: string): Ciudad | undefined {
    const normalizedName = name.toLowerCase().trim();
    return this.cities.find(c => 
      c.ct_descripcion.toLowerCase() === normalizedName
    );
  }

  static searchByName(query: string): Ciudad[] {
    const normalizedQuery = query.toLowerCase().trim();
    return this.cities.filter(c => 
      c.ct_descripcion.toLowerCase().includes(normalizedQuery)
    );
  }
}

// Usage
const cities = await CitiesService.getCities(token);
const quito = CitiesService.findByName('Quito');
const searchResults = CitiesService.searchByName('gua'); // ["Guayaquil"]

Vue.js - Cities Composable

import { ref, onMounted } from 'vue';

export function useCities(token) {
  const cities = ref([]);
  const loading = ref(true);
  const error = ref(null);

  const fetchCities = async () => {
    try {
      const response = await fetch('http://localhost:3000/api/pos/ciudad', {
        headers: {
          'Authorization': `Bearer ${token}`
        }
      });

      if (!response.ok) {
        throw new Error('Failed to fetch cities');
      }

      cities.value = await response.json();
    } catch (err) {
      error.value = err.message;
    } finally {
      loading.value = false;
    }
  };

  const getCityName = (codigo) => {
    const city = cities.value.find(c => c.ct_codigo === codigo);
    return city ? city.ct_descripcion : codigo;
  };

  onMounted(fetchCities);

  return {
    cities,
    loading,
    error,
    getCityName
  };
}

Error Codes

Status CodeDescription
200Request successful
401Unauthorized - invalid or missing token
500Internal server error

Data Model

City Object Structure

interface Ciudad {
  ct_codigo: string;      // Primary key, unique identifier
  ct_descripcion: string; // City name
}

Database Schema

The cities are stored in the ciudad table with the following structure:
ColumnTypeDescription
ct_codigoVARCHAR(10)Primary key, city code
ct_descripcionVARCHAR(100)City name
Unlike other master data tables (categories, units), the ciudad table does not include a status field (estado). All cities returned are considered active.

Common Use Cases

Supplier Management

Cities are primarily used in supplier registration and management:
  • Supplier Registration: Select supplier’s city location
  • Supplier Filtering: Filter suppliers by city
  • Geographic Reports: Generate supplier distribution reports
  • Logistics: Plan delivery routes based on supplier locations

Customer Management

  • Customer Addresses: Store customer location information
  • Delivery Zones: Define delivery areas by city
  • Sales Analysis: Analyze sales patterns by geographic region
  • Regional Pricing: Apply city-specific pricing if needed

Reporting and Analytics

  • Geographic Distribution: View business presence across cities
  • Regional Performance: Compare sales/purchases by city
  • Logistics Optimization: Plan routes and deliveries
  • Market Analysis: Identify expansion opportunities

Integration Points

The Ciudades API integrates with several POS modules:
Related Modules:
  • Proveedores: Suppliers are assigned cities through ct_codigo
  • Clientes: Customers may be assigned cities for location tracking
  • Reporting: Geographic reports use city data
  • Logistics: Delivery and route planning based on cities

Foreign Key References

Cities are referenced by:
  • proveedor.ct_codigo - Supplier location
  • cliente.ct_codigo - Customer location (if applicable)
  • Other location-based tables

Best Practices

Performance Optimization:
  • Cache cities data aggressively - it changes very rarely
  • Load once on application startup
  • Store in local storage or application state
  • Consider a cache TTL of 24-48 hours or even longer
  • Cities are perfect candidates for static data bundling
UI/UX Recommendations:
  • Provide autocomplete/search functionality for city selection
  • Sort alphabetically for easy browsing
  • Consider grouping by province/region for larger countries
  • Display city names in local language and script
  • Support keyboard navigation in dropdown menus
Data Integrity:
  • Validate ct_codigo before creating suppliers or customers
  • Handle missing city references gracefully in displays
  • Don’t hardcode city codes - always fetch from API
  • Maintain referential integrity - don’t delete cities with active references

Geographic Expansion

If your business expands to new cities:
1

Add City to Database

Insert new city into the ciudad table with unique ct_codigo and ct_descripcion
2

Clear Client Caches

Ensure client applications refresh their cached city data
3

Update Related Systems

Update delivery zones, pricing tables, and other location-based configurations
4

Test Integration

Verify that suppliers and customers can be created with the new city

Ecuador Cities Reference

The system typically includes major Ecuadorian cities:

Coastal Region (Costa)

CodeCityProvince
CT003GuayaquilGuayas
CT007MantaManabí
CT008PortoviejoManabí
CT006MachalaEl Oro

Highland Region (Sierra)

CodeCityProvince
CT009QuitoPichincha
CT002CuencaAzuay
CT001AmbatoTungurahua
CT010RiobambaChimborazo
CT004IbarraImbabura
CT005LojaLoja

Other Regions

CodeCityProvince
CT011Santo DomingoSanto Domingo de los Tsáchilas
Your installation may include additional cities based on your business needs and geographic coverage.

Implementation Notes

Controller Implementation

The city controller is implemented in pos.ciudad.controller.js:
const CiudadController = {
    async list(req, res) {
        const pool = connectFromJWT(req);
        try {
            const data = await CiudadModel.getAllQuery(pool);
            res.json(data);
        } catch (error) {
            console.error('ERROR CIUDADES:', error);
            res.status(500).json({ message: 'Error al listar ciudades' });
        } finally {
            await pool.end();
        }
    }
};

Model Implementation

The city model in ciudad.model.js executes a simple query:
const getAllQuery = async (pool) => {
  const query = 'SELECT ct_codigo, ct_descripcion FROM ciudad ORDER BY ct_descripcion';
  const result = await pool.query(query);
  return result.rows;
};

Connection Management

Like all POS endpoints, cities use JWT-based connection pooling:
  1. JWT token is verified by verifyToken middleware
  2. Database credentials are extracted from the token payload
  3. A dedicated connection pool is created with user credentials
  4. Query is executed with proper permission scope
  5. Connection pool is closed in the finally block
This ensures proper row-level security and audit trailing at the database level.

Extending the Cities System

For more complex geographic requirements, consider:

Hierarchical Locations

Add province/region hierarchy:
ALTER TABLE ciudad ADD COLUMN provincia VARCHAR(50);
ALTER TABLE ciudad ADD COLUMN region VARCHAR(50);

Coordinates

Add GPS coordinates for mapping:
ALTER TABLE ciudad ADD COLUMN latitud DECIMAL(10, 8);
ALTER TABLE ciudad ADD COLUMN longitud DECIMAL(11, 8);

Delivery Zones

Create delivery zone mappings:
CREATE TABLE zona_entrega (
  zona_codigo VARCHAR(10) PRIMARY KEY,
  zona_descripcion VARCHAR(100),
  ct_codigo VARCHAR(10) REFERENCES ciudad(ct_codigo)
);

API Response Optimization

For very large city lists, consider implementing:
  1. Pagination: Add page/limit parameters
  2. Search: Add query parameter for filtering
  3. Partial Loading: Load cities on-demand as user types
  4. Regionalization: Filter by province/region
  5. Compression: Enable gzip compression for responses
Example enhanced endpoint:
GET /api/pos/ciudad?q=qui&region=sierra&limit=20
These enhancements maintain backward compatibility while supporting advanced use cases.

Build docs developers (and LLMs) love