- State entity and service with CRUD operations - CurrencyRate entity and service with conversion support - UoM conversion methods (convertQuantity, getConversionTable) - New API endpoints for states, currency rates, UoM conversions - Updated controller and routes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
149 lines
3.9 KiB
TypeScript
149 lines
3.9 KiB
TypeScript
import { Repository } from 'typeorm';
|
|
import { AppDataSource } from '../../config/typeorm.js';
|
|
import { State } from './entities/state.entity.js';
|
|
import { NotFoundError } from '../../shared/errors/index.js';
|
|
import { logger } from '../../shared/utils/logger.js';
|
|
|
|
export interface CreateStateDto {
|
|
countryId: string;
|
|
code: string;
|
|
name: string;
|
|
timezone?: string;
|
|
isActive?: boolean;
|
|
}
|
|
|
|
export interface UpdateStateDto {
|
|
name?: string;
|
|
timezone?: string;
|
|
isActive?: boolean;
|
|
}
|
|
|
|
export interface StateFilter {
|
|
countryId?: string;
|
|
countryCode?: string;
|
|
isActive?: boolean;
|
|
}
|
|
|
|
class StatesService {
|
|
private repository: Repository<State>;
|
|
|
|
constructor() {
|
|
this.repository = AppDataSource.getRepository(State);
|
|
}
|
|
|
|
async findAll(filter: StateFilter = {}): Promise<State[]> {
|
|
logger.debug('Finding all states', { filter });
|
|
|
|
const query = this.repository
|
|
.createQueryBuilder('state')
|
|
.leftJoinAndSelect('state.country', 'country');
|
|
|
|
if (filter.countryId) {
|
|
query.andWhere('state.countryId = :countryId', { countryId: filter.countryId });
|
|
}
|
|
|
|
if (filter.countryCode) {
|
|
query.andWhere('country.code = :countryCode', { countryCode: filter.countryCode.toUpperCase() });
|
|
}
|
|
|
|
if (filter.isActive !== undefined) {
|
|
query.andWhere('state.isActive = :isActive', { isActive: filter.isActive });
|
|
}
|
|
|
|
query.orderBy('state.name', 'ASC');
|
|
|
|
return query.getMany();
|
|
}
|
|
|
|
async findById(id: string): Promise<State> {
|
|
logger.debug('Finding state by id', { id });
|
|
|
|
const state = await this.repository.findOne({
|
|
where: { id },
|
|
relations: ['country'],
|
|
});
|
|
|
|
if (!state) {
|
|
throw new NotFoundError('Estado no encontrado');
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
async findByCode(countryId: string, code: string): Promise<State | null> {
|
|
logger.debug('Finding state by code', { countryId, code });
|
|
|
|
return this.repository.findOne({
|
|
where: { countryId, code: code.toUpperCase() },
|
|
relations: ['country'],
|
|
});
|
|
}
|
|
|
|
async findByCountry(countryId: string): Promise<State[]> {
|
|
logger.debug('Finding states by country', { countryId });
|
|
|
|
return this.repository.find({
|
|
where: { countryId, isActive: true },
|
|
relations: ['country'],
|
|
order: { name: 'ASC' },
|
|
});
|
|
}
|
|
|
|
async findByCountryCode(countryCode: string): Promise<State[]> {
|
|
logger.debug('Finding states by country code', { countryCode });
|
|
|
|
return this.repository
|
|
.createQueryBuilder('state')
|
|
.leftJoinAndSelect('state.country', 'country')
|
|
.where('country.code = :countryCode', { countryCode: countryCode.toUpperCase() })
|
|
.andWhere('state.isActive = :isActive', { isActive: true })
|
|
.orderBy('state.name', 'ASC')
|
|
.getMany();
|
|
}
|
|
|
|
async create(dto: CreateStateDto): Promise<State> {
|
|
logger.info('Creating state', { dto });
|
|
|
|
// Check if state already exists for this country
|
|
const existing = await this.findByCode(dto.countryId, dto.code);
|
|
if (existing) {
|
|
throw new Error(`Estado con código ${dto.code} ya existe para este país`);
|
|
}
|
|
|
|
const state = this.repository.create({
|
|
...dto,
|
|
code: dto.code.toUpperCase(),
|
|
isActive: dto.isActive ?? true,
|
|
});
|
|
|
|
return this.repository.save(state);
|
|
}
|
|
|
|
async update(id: string, dto: UpdateStateDto): Promise<State> {
|
|
logger.info('Updating state', { id, dto });
|
|
|
|
const state = await this.findById(id);
|
|
Object.assign(state, dto);
|
|
|
|
return this.repository.save(state);
|
|
}
|
|
|
|
async delete(id: string): Promise<void> {
|
|
logger.info('Deleting state', { id });
|
|
|
|
const state = await this.findById(id);
|
|
await this.repository.remove(state);
|
|
}
|
|
|
|
async setActive(id: string, isActive: boolean): Promise<State> {
|
|
logger.info('Setting state active status', { id, isActive });
|
|
|
|
const state = await this.findById(id);
|
|
state.isActive = isActive;
|
|
|
|
return this.repository.save(state);
|
|
}
|
|
}
|
|
|
|
export const statesService = new StatesService();
|