feat(fiscal): Add fiscal module with catalogs (MGN-005)
- Add fiscal entities: - TaxCategory, FiscalRegime, CfdiUse - PaymentMethod, PaymentType, WithholdingType - Add fiscal services with filtering capabilities - Add fiscal controller with REST endpoints - Add fiscal routes at /api/v1/fiscal - Register fiscal routes in app.ts Endpoints: - GET /fiscal/tax-categories - GET /fiscal/fiscal-regimes - GET /fiscal/cfdi-uses - GET /fiscal/payment-methods - GET /fiscal/payment-types - GET /fiscal/withholding-types Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
6b7ea745d8
commit
5fa451e09f
@ -27,6 +27,7 @@ import reportsRoutes from './modules/reports/reports.routes.js';
|
|||||||
import invoicesRoutes from './modules/invoices/invoices.routes.js';
|
import invoicesRoutes from './modules/invoices/invoices.routes.js';
|
||||||
import productsRoutes from './modules/products/products.routes.js';
|
import productsRoutes from './modules/products/products.routes.js';
|
||||||
import warehousesRoutes from './modules/warehouses/warehouses.routes.js';
|
import warehousesRoutes from './modules/warehouses/warehouses.routes.js';
|
||||||
|
import fiscalRoutes from './modules/fiscal/fiscal.routes.js';
|
||||||
|
|
||||||
const app: Application = express();
|
const app: Application = express();
|
||||||
|
|
||||||
@ -79,6 +80,7 @@ app.use(`${apiPrefix}/reports`, reportsRoutes);
|
|||||||
app.use(`${apiPrefix}/invoices`, invoicesRoutes);
|
app.use(`${apiPrefix}/invoices`, invoicesRoutes);
|
||||||
app.use(`${apiPrefix}/products`, productsRoutes);
|
app.use(`${apiPrefix}/products`, productsRoutes);
|
||||||
app.use(`${apiPrefix}/warehouses`, warehousesRoutes);
|
app.use(`${apiPrefix}/warehouses`, warehousesRoutes);
|
||||||
|
app.use(`${apiPrefix}/fiscal`, fiscalRoutes);
|
||||||
|
|
||||||
// 404 handler
|
// 404 handler
|
||||||
app.use((_req: Request, res: Response) => {
|
app.use((_req: Request, res: Response) => {
|
||||||
|
|||||||
47
src/modules/fiscal/entities/cfdi-use.entity.ts
Normal file
47
src/modules/fiscal/entities/cfdi-use.entity.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import {
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
Index,
|
||||||
|
} from 'typeorm';
|
||||||
|
import { PersonType } from './fiscal-regime.entity.js';
|
||||||
|
|
||||||
|
@Entity({ schema: 'fiscal', name: 'cfdi_uses' })
|
||||||
|
@Index('idx_cfdi_uses_code', ['code'], { unique: true })
|
||||||
|
@Index('idx_cfdi_uses_applies', ['appliesTo'])
|
||||||
|
export class CfdiUse {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: 10, nullable: false, unique: true })
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: 255, nullable: false })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column({ type: 'text', nullable: true })
|
||||||
|
description: string | null;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'enum',
|
||||||
|
enum: PersonType,
|
||||||
|
nullable: false,
|
||||||
|
default: PersonType.BOTH,
|
||||||
|
name: 'applies_to',
|
||||||
|
})
|
||||||
|
appliesTo: PersonType;
|
||||||
|
|
||||||
|
@Column({ type: 'simple-array', nullable: true, name: 'allowed_regimes' })
|
||||||
|
allowedRegimes: string[] | null;
|
||||||
|
|
||||||
|
@Column({ type: 'boolean', default: true, nullable: false, name: 'is_active' })
|
||||||
|
isActive: boolean;
|
||||||
|
|
||||||
|
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
49
src/modules/fiscal/entities/fiscal-regime.entity.ts
Normal file
49
src/modules/fiscal/entities/fiscal-regime.entity.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import {
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
Index,
|
||||||
|
} from 'typeorm';
|
||||||
|
|
||||||
|
export enum PersonType {
|
||||||
|
NATURAL = 'natural', // Persona fisica
|
||||||
|
LEGAL = 'legal', // Persona moral
|
||||||
|
BOTH = 'both',
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity({ schema: 'fiscal', name: 'fiscal_regimes' })
|
||||||
|
@Index('idx_fiscal_regimes_code', ['code'], { unique: true })
|
||||||
|
@Index('idx_fiscal_regimes_applies', ['appliesTo'])
|
||||||
|
export class FiscalRegime {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: 10, nullable: false, unique: true })
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: 255, nullable: false })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column({ type: 'text', nullable: true })
|
||||||
|
description: string | null;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'enum',
|
||||||
|
enum: PersonType,
|
||||||
|
nullable: false,
|
||||||
|
default: PersonType.BOTH,
|
||||||
|
name: 'applies_to',
|
||||||
|
})
|
||||||
|
appliesTo: PersonType;
|
||||||
|
|
||||||
|
@Column({ type: 'boolean', default: true, nullable: false, name: 'is_active' })
|
||||||
|
isActive: boolean;
|
||||||
|
|
||||||
|
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
6
src/modules/fiscal/entities/index.ts
Normal file
6
src/modules/fiscal/entities/index.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export * from './tax-category.entity.js';
|
||||||
|
export * from './fiscal-regime.entity.js';
|
||||||
|
export * from './cfdi-use.entity.js';
|
||||||
|
export * from './payment-method.entity.js';
|
||||||
|
export * from './payment-type.entity.js';
|
||||||
|
export * from './withholding-type.entity.js';
|
||||||
36
src/modules/fiscal/entities/payment-method.entity.ts
Normal file
36
src/modules/fiscal/entities/payment-method.entity.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import {
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
Index,
|
||||||
|
} from 'typeorm';
|
||||||
|
|
||||||
|
@Entity({ schema: 'fiscal', name: 'payment_methods' })
|
||||||
|
@Index('idx_payment_methods_code', ['code'], { unique: true })
|
||||||
|
export class PaymentMethod {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: 10, nullable: false, unique: true })
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: 100, nullable: false })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column({ type: 'text', nullable: true })
|
||||||
|
description: string | null;
|
||||||
|
|
||||||
|
@Column({ type: 'boolean', default: false, nullable: false, name: 'requires_bank_info' })
|
||||||
|
requiresBankInfo: boolean;
|
||||||
|
|
||||||
|
@Column({ type: 'boolean', default: true, nullable: false, name: 'is_active' })
|
||||||
|
isActive: boolean;
|
||||||
|
|
||||||
|
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
33
src/modules/fiscal/entities/payment-type.entity.ts
Normal file
33
src/modules/fiscal/entities/payment-type.entity.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import {
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
Index,
|
||||||
|
} from 'typeorm';
|
||||||
|
|
||||||
|
@Entity({ schema: 'fiscal', name: 'payment_types' })
|
||||||
|
@Index('idx_payment_types_code', ['code'], { unique: true })
|
||||||
|
export class PaymentType {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: 10, nullable: false, unique: true })
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: 100, nullable: false })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column({ type: 'text', nullable: true })
|
||||||
|
description: string | null;
|
||||||
|
|
||||||
|
@Column({ type: 'boolean', default: true, nullable: false, name: 'is_active' })
|
||||||
|
isActive: boolean;
|
||||||
|
|
||||||
|
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
52
src/modules/fiscal/entities/tax-category.entity.ts
Normal file
52
src/modules/fiscal/entities/tax-category.entity.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import {
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
Index,
|
||||||
|
} from 'typeorm';
|
||||||
|
|
||||||
|
export enum TaxNature {
|
||||||
|
TAX = 'tax',
|
||||||
|
WITHHOLDING = 'withholding',
|
||||||
|
BOTH = 'both',
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity({ schema: 'fiscal', name: 'tax_categories' })
|
||||||
|
@Index('idx_tax_categories_code', ['code'], { unique: true })
|
||||||
|
@Index('idx_tax_categories_sat', ['satCode'])
|
||||||
|
export class TaxCategory {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: 20, nullable: false, unique: true })
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: 100, nullable: false })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column({ type: 'text', nullable: true })
|
||||||
|
description: string | null;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'enum',
|
||||||
|
enum: TaxNature,
|
||||||
|
nullable: false,
|
||||||
|
default: TaxNature.TAX,
|
||||||
|
name: 'tax_nature',
|
||||||
|
})
|
||||||
|
taxNature: TaxNature;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: 10, nullable: true, name: 'sat_code' })
|
||||||
|
satCode: string | null;
|
||||||
|
|
||||||
|
@Column({ type: 'boolean', default: true, nullable: false, name: 'is_active' })
|
||||||
|
isActive: boolean;
|
||||||
|
|
||||||
|
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
54
src/modules/fiscal/entities/withholding-type.entity.ts
Normal file
54
src/modules/fiscal/entities/withholding-type.entity.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import {
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
Index,
|
||||||
|
ManyToOne,
|
||||||
|
JoinColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
import { TaxCategory } from './tax-category.entity.js';
|
||||||
|
|
||||||
|
@Entity({ schema: 'fiscal', name: 'withholding_types' })
|
||||||
|
@Index('idx_withholding_types_code', ['code'], { unique: true })
|
||||||
|
@Index('idx_withholding_types_category', ['taxCategoryId'])
|
||||||
|
export class WithholdingType {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: 20, nullable: false, unique: true })
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: 100, nullable: false })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column({ type: 'text', nullable: true })
|
||||||
|
description: string | null;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'decimal',
|
||||||
|
precision: 5,
|
||||||
|
scale: 2,
|
||||||
|
nullable: false,
|
||||||
|
default: 0,
|
||||||
|
name: 'default_rate',
|
||||||
|
})
|
||||||
|
defaultRate: number;
|
||||||
|
|
||||||
|
@Column({ type: 'uuid', nullable: true, name: 'tax_category_id' })
|
||||||
|
taxCategoryId: string | null;
|
||||||
|
|
||||||
|
@ManyToOne(() => TaxCategory, { nullable: true })
|
||||||
|
@JoinColumn({ name: 'tax_category_id' })
|
||||||
|
taxCategory: TaxCategory | null;
|
||||||
|
|
||||||
|
@Column({ type: 'boolean', default: true, nullable: false, name: 'is_active' })
|
||||||
|
isActive: boolean;
|
||||||
|
|
||||||
|
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
426
src/modules/fiscal/fiscal-catalogs.service.ts
Normal file
426
src/modules/fiscal/fiscal-catalogs.service.ts
Normal file
@ -0,0 +1,426 @@
|
|||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { AppDataSource } from '../../config/typeorm.js';
|
||||||
|
import {
|
||||||
|
TaxCategory,
|
||||||
|
FiscalRegime,
|
||||||
|
CfdiUse,
|
||||||
|
PaymentMethod,
|
||||||
|
PaymentType,
|
||||||
|
WithholdingType,
|
||||||
|
PersonType,
|
||||||
|
} from './entities/index.js';
|
||||||
|
import { NotFoundError } from '../../shared/errors/index.js';
|
||||||
|
import { logger } from '../../shared/utils/logger.js';
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// TAX CATEGORIES SERVICE
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
export interface TaxCategoryFilter {
|
||||||
|
taxNature?: string;
|
||||||
|
active?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TaxCategoriesService {
|
||||||
|
private repository: Repository<TaxCategory>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.repository = AppDataSource.getRepository(TaxCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAll(filter: TaxCategoryFilter = {}): Promise<TaxCategory[]> {
|
||||||
|
logger.debug('Finding all tax categories', { filter });
|
||||||
|
|
||||||
|
const qb = this.repository.createQueryBuilder('tc');
|
||||||
|
|
||||||
|
if (filter.taxNature) {
|
||||||
|
qb.andWhere('tc.tax_nature = :taxNature', { taxNature: filter.taxNature });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.active !== undefined) {
|
||||||
|
qb.andWhere('tc.is_active = :active', { active: filter.active });
|
||||||
|
}
|
||||||
|
|
||||||
|
qb.orderBy('tc.name', 'ASC');
|
||||||
|
|
||||||
|
return qb.getMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
async findById(id: string): Promise<TaxCategory> {
|
||||||
|
logger.debug('Finding tax category by id', { id });
|
||||||
|
|
||||||
|
const category = await this.repository.findOne({ where: { id } });
|
||||||
|
|
||||||
|
if (!category) {
|
||||||
|
throw new NotFoundError('Categoría de impuesto no encontrada');
|
||||||
|
}
|
||||||
|
|
||||||
|
return category;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findByCode(code: string): Promise<TaxCategory | null> {
|
||||||
|
logger.debug('Finding tax category by code', { code });
|
||||||
|
|
||||||
|
return this.repository.findOne({
|
||||||
|
where: { code: code.toUpperCase() },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async findBySatCode(satCode: string): Promise<TaxCategory | null> {
|
||||||
|
logger.debug('Finding tax category by SAT code', { satCode });
|
||||||
|
|
||||||
|
return this.repository.findOne({
|
||||||
|
where: { satCode },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// FISCAL REGIMES SERVICE
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
export interface FiscalRegimeFilter {
|
||||||
|
appliesTo?: PersonType;
|
||||||
|
active?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class FiscalRegimesService {
|
||||||
|
private repository: Repository<FiscalRegime>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.repository = AppDataSource.getRepository(FiscalRegime);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAll(filter: FiscalRegimeFilter = {}): Promise<FiscalRegime[]> {
|
||||||
|
logger.debug('Finding all fiscal regimes', { filter });
|
||||||
|
|
||||||
|
const qb = this.repository.createQueryBuilder('fr');
|
||||||
|
|
||||||
|
if (filter.appliesTo) {
|
||||||
|
qb.andWhere('(fr.applies_to = :appliesTo OR fr.applies_to = :both)', {
|
||||||
|
appliesTo: filter.appliesTo,
|
||||||
|
both: PersonType.BOTH,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.active !== undefined) {
|
||||||
|
qb.andWhere('fr.is_active = :active', { active: filter.active });
|
||||||
|
}
|
||||||
|
|
||||||
|
qb.orderBy('fr.code', 'ASC');
|
||||||
|
|
||||||
|
return qb.getMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
async findById(id: string): Promise<FiscalRegime> {
|
||||||
|
logger.debug('Finding fiscal regime by id', { id });
|
||||||
|
|
||||||
|
const regime = await this.repository.findOne({ where: { id } });
|
||||||
|
|
||||||
|
if (!regime) {
|
||||||
|
throw new NotFoundError('Régimen fiscal no encontrado');
|
||||||
|
}
|
||||||
|
|
||||||
|
return regime;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findByCode(code: string): Promise<FiscalRegime | null> {
|
||||||
|
logger.debug('Finding fiscal regime by code', { code });
|
||||||
|
|
||||||
|
return this.repository.findOne({
|
||||||
|
where: { code },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async findForPersonType(personType: PersonType): Promise<FiscalRegime[]> {
|
||||||
|
logger.debug('Finding fiscal regimes for person type', { personType });
|
||||||
|
|
||||||
|
return this.repository
|
||||||
|
.createQueryBuilder('fr')
|
||||||
|
.where('fr.applies_to = :personType OR fr.applies_to = :both', {
|
||||||
|
personType,
|
||||||
|
both: PersonType.BOTH,
|
||||||
|
})
|
||||||
|
.andWhere('fr.is_active = true')
|
||||||
|
.orderBy('fr.code', 'ASC')
|
||||||
|
.getMany();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// CFDI USES SERVICE
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
export interface CfdiUseFilter {
|
||||||
|
appliesTo?: PersonType;
|
||||||
|
active?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CfdiUsesService {
|
||||||
|
private repository: Repository<CfdiUse>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.repository = AppDataSource.getRepository(CfdiUse);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAll(filter: CfdiUseFilter = {}): Promise<CfdiUse[]> {
|
||||||
|
logger.debug('Finding all CFDI uses', { filter });
|
||||||
|
|
||||||
|
const qb = this.repository.createQueryBuilder('cu');
|
||||||
|
|
||||||
|
if (filter.appliesTo) {
|
||||||
|
qb.andWhere('(cu.applies_to = :appliesTo OR cu.applies_to = :both)', {
|
||||||
|
appliesTo: filter.appliesTo,
|
||||||
|
both: PersonType.BOTH,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.active !== undefined) {
|
||||||
|
qb.andWhere('cu.is_active = :active', { active: filter.active });
|
||||||
|
}
|
||||||
|
|
||||||
|
qb.orderBy('cu.code', 'ASC');
|
||||||
|
|
||||||
|
return qb.getMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
async findById(id: string): Promise<CfdiUse> {
|
||||||
|
logger.debug('Finding CFDI use by id', { id });
|
||||||
|
|
||||||
|
const cfdiUse = await this.repository.findOne({ where: { id } });
|
||||||
|
|
||||||
|
if (!cfdiUse) {
|
||||||
|
throw new NotFoundError('Uso de CFDI no encontrado');
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfdiUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findByCode(code: string): Promise<CfdiUse | null> {
|
||||||
|
logger.debug('Finding CFDI use by code', { code });
|
||||||
|
|
||||||
|
return this.repository.findOne({
|
||||||
|
where: { code: code.toUpperCase() },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async findForPersonType(personType: PersonType): Promise<CfdiUse[]> {
|
||||||
|
logger.debug('Finding CFDI uses for person type', { personType });
|
||||||
|
|
||||||
|
return this.repository
|
||||||
|
.createQueryBuilder('cu')
|
||||||
|
.where('cu.applies_to = :personType OR cu.applies_to = :both', {
|
||||||
|
personType,
|
||||||
|
both: PersonType.BOTH,
|
||||||
|
})
|
||||||
|
.andWhere('cu.is_active = true')
|
||||||
|
.orderBy('cu.code', 'ASC')
|
||||||
|
.getMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
async findForRegime(regimeCode: string): Promise<CfdiUse[]> {
|
||||||
|
logger.debug('Finding CFDI uses for regime', { regimeCode });
|
||||||
|
|
||||||
|
// Get all active CFDI uses and filter by allowed regimes
|
||||||
|
const all = await this.repository
|
||||||
|
.createQueryBuilder('cu')
|
||||||
|
.where('cu.is_active = true')
|
||||||
|
.orderBy('cu.code', 'ASC')
|
||||||
|
.getMany();
|
||||||
|
|
||||||
|
return all.filter(
|
||||||
|
(cu) => !cu.allowedRegimes || cu.allowedRegimes.length === 0 || cu.allowedRegimes.includes(regimeCode)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// PAYMENT METHODS SERVICE
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
export interface PaymentMethodFilter {
|
||||||
|
requiresBankInfo?: boolean;
|
||||||
|
active?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PaymentMethodsService {
|
||||||
|
private repository: Repository<PaymentMethod>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.repository = AppDataSource.getRepository(PaymentMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAll(filter: PaymentMethodFilter = {}): Promise<PaymentMethod[]> {
|
||||||
|
logger.debug('Finding all payment methods', { filter });
|
||||||
|
|
||||||
|
const qb = this.repository.createQueryBuilder('pm');
|
||||||
|
|
||||||
|
if (filter.requiresBankInfo !== undefined) {
|
||||||
|
qb.andWhere('pm.requires_bank_info = :requiresBankInfo', {
|
||||||
|
requiresBankInfo: filter.requiresBankInfo,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.active !== undefined) {
|
||||||
|
qb.andWhere('pm.is_active = :active', { active: filter.active });
|
||||||
|
}
|
||||||
|
|
||||||
|
qb.orderBy('pm.code', 'ASC');
|
||||||
|
|
||||||
|
return qb.getMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
async findById(id: string): Promise<PaymentMethod> {
|
||||||
|
logger.debug('Finding payment method by id', { id });
|
||||||
|
|
||||||
|
const method = await this.repository.findOne({ where: { id } });
|
||||||
|
|
||||||
|
if (!method) {
|
||||||
|
throw new NotFoundError('Forma de pago no encontrada');
|
||||||
|
}
|
||||||
|
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findByCode(code: string): Promise<PaymentMethod | null> {
|
||||||
|
logger.debug('Finding payment method by code', { code });
|
||||||
|
|
||||||
|
return this.repository.findOne({
|
||||||
|
where: { code },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// PAYMENT TYPES SERVICE
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
export interface PaymentTypeFilter {
|
||||||
|
active?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PaymentTypesService {
|
||||||
|
private repository: Repository<PaymentType>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.repository = AppDataSource.getRepository(PaymentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAll(filter: PaymentTypeFilter = {}): Promise<PaymentType[]> {
|
||||||
|
logger.debug('Finding all payment types', { filter });
|
||||||
|
|
||||||
|
const qb = this.repository.createQueryBuilder('pt');
|
||||||
|
|
||||||
|
if (filter.active !== undefined) {
|
||||||
|
qb.andWhere('pt.is_active = :active', { active: filter.active });
|
||||||
|
}
|
||||||
|
|
||||||
|
qb.orderBy('pt.code', 'ASC');
|
||||||
|
|
||||||
|
return qb.getMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
async findById(id: string): Promise<PaymentType> {
|
||||||
|
logger.debug('Finding payment type by id', { id });
|
||||||
|
|
||||||
|
const type = await this.repository.findOne({ where: { id } });
|
||||||
|
|
||||||
|
if (!type) {
|
||||||
|
throw new NotFoundError('Método de pago no encontrado');
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findByCode(code: string): Promise<PaymentType | null> {
|
||||||
|
logger.debug('Finding payment type by code', { code });
|
||||||
|
|
||||||
|
return this.repository.findOne({
|
||||||
|
where: { code: code.toUpperCase() },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// WITHHOLDING TYPES SERVICE
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
export interface WithholdingTypeFilter {
|
||||||
|
taxCategoryId?: string;
|
||||||
|
active?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class WithholdingTypesService {
|
||||||
|
private repository: Repository<WithholdingType>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.repository = AppDataSource.getRepository(WithholdingType);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAll(filter: WithholdingTypeFilter = {}): Promise<WithholdingType[]> {
|
||||||
|
logger.debug('Finding all withholding types', { filter });
|
||||||
|
|
||||||
|
const qb = this.repository
|
||||||
|
.createQueryBuilder('wt')
|
||||||
|
.leftJoinAndSelect('wt.taxCategory', 'taxCategory');
|
||||||
|
|
||||||
|
if (filter.taxCategoryId) {
|
||||||
|
qb.andWhere('wt.tax_category_id = :taxCategoryId', {
|
||||||
|
taxCategoryId: filter.taxCategoryId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.active !== undefined) {
|
||||||
|
qb.andWhere('wt.is_active = :active', { active: filter.active });
|
||||||
|
}
|
||||||
|
|
||||||
|
qb.orderBy('wt.code', 'ASC');
|
||||||
|
|
||||||
|
return qb.getMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
async findById(id: string): Promise<WithholdingType> {
|
||||||
|
logger.debug('Finding withholding type by id', { id });
|
||||||
|
|
||||||
|
const type = await this.repository.findOne({
|
||||||
|
where: { id },
|
||||||
|
relations: ['taxCategory'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!type) {
|
||||||
|
throw new NotFoundError('Tipo de retención no encontrado');
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findByCode(code: string): Promise<WithholdingType | null> {
|
||||||
|
logger.debug('Finding withholding type by code', { code });
|
||||||
|
|
||||||
|
return this.repository.findOne({
|
||||||
|
where: { code },
|
||||||
|
relations: ['taxCategory'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async findByTaxCategory(taxCategoryId: string): Promise<WithholdingType[]> {
|
||||||
|
logger.debug('Finding withholding types by tax category', { taxCategoryId });
|
||||||
|
|
||||||
|
return this.repository.find({
|
||||||
|
where: { taxCategoryId, isActive: true },
|
||||||
|
relations: ['taxCategory'],
|
||||||
|
order: { code: 'ASC' },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// SERVICE EXPORTS
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
export const taxCategoriesService = new TaxCategoriesService();
|
||||||
|
export const fiscalRegimesService = new FiscalRegimesService();
|
||||||
|
export const cfdiUsesService = new CfdiUsesService();
|
||||||
|
export const paymentMethodsService = new PaymentMethodsService();
|
||||||
|
export const paymentTypesService = new PaymentTypesService();
|
||||||
|
export const withholdingTypesService = new WithholdingTypesService();
|
||||||
281
src/modules/fiscal/fiscal.controller.ts
Normal file
281
src/modules/fiscal/fiscal.controller.ts
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
import { Response, NextFunction } from 'express';
|
||||||
|
import {
|
||||||
|
taxCategoriesService,
|
||||||
|
fiscalRegimesService,
|
||||||
|
cfdiUsesService,
|
||||||
|
paymentMethodsService,
|
||||||
|
paymentTypesService,
|
||||||
|
withholdingTypesService,
|
||||||
|
} from './fiscal-catalogs.service.js';
|
||||||
|
import { PersonType } from './entities/fiscal-regime.entity.js';
|
||||||
|
import { AuthenticatedRequest } from '../../shared/middleware/auth.middleware.js';
|
||||||
|
|
||||||
|
class FiscalController {
|
||||||
|
// ========== TAX CATEGORIES ==========
|
||||||
|
async getTaxCategories(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const filter = {
|
||||||
|
taxNature: req.query.tax_nature as string | undefined,
|
||||||
|
active: req.query.active === 'true' ? true : undefined,
|
||||||
|
};
|
||||||
|
const categories = await taxCategoriesService.findAll(filter);
|
||||||
|
res.json({ success: true, data: categories });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTaxCategory(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const category = await taxCategoriesService.findById(req.params.id);
|
||||||
|
res.json({ success: true, data: category });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTaxCategoryByCode(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const category = await taxCategoriesService.findByCode(req.params.code);
|
||||||
|
if (!category) {
|
||||||
|
res.status(404).json({ success: false, message: 'Categoría de impuesto no encontrada' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.json({ success: true, data: category });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTaxCategoryBySatCode(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const category = await taxCategoriesService.findBySatCode(req.params.satCode);
|
||||||
|
if (!category) {
|
||||||
|
res.status(404).json({ success: false, message: 'Categoría de impuesto no encontrada' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.json({ success: true, data: category });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== FISCAL REGIMES ==========
|
||||||
|
async getFiscalRegimes(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const filter = {
|
||||||
|
appliesTo: req.query.applies_to as PersonType | undefined,
|
||||||
|
active: req.query.active === 'true' ? true : undefined,
|
||||||
|
};
|
||||||
|
const regimes = await fiscalRegimesService.findAll(filter);
|
||||||
|
res.json({ success: true, data: regimes });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFiscalRegime(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const regime = await fiscalRegimesService.findById(req.params.id);
|
||||||
|
res.json({ success: true, data: regime });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFiscalRegimeByCode(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const regime = await fiscalRegimesService.findByCode(req.params.code);
|
||||||
|
if (!regime) {
|
||||||
|
res.status(404).json({ success: false, message: 'Régimen fiscal no encontrado' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.json({ success: true, data: regime });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFiscalRegimesForPersonType(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const personType = req.params.personType as PersonType;
|
||||||
|
const regimes = await fiscalRegimesService.findForPersonType(personType);
|
||||||
|
res.json({ success: true, data: regimes });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== CFDI USES ==========
|
||||||
|
async getCfdiUses(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const filter = {
|
||||||
|
appliesTo: req.query.applies_to as PersonType | undefined,
|
||||||
|
active: req.query.active === 'true' ? true : undefined,
|
||||||
|
};
|
||||||
|
const uses = await cfdiUsesService.findAll(filter);
|
||||||
|
res.json({ success: true, data: uses });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCfdiUse(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const use = await cfdiUsesService.findById(req.params.id);
|
||||||
|
res.json({ success: true, data: use });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCfdiUseByCode(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const use = await cfdiUsesService.findByCode(req.params.code);
|
||||||
|
if (!use) {
|
||||||
|
res.status(404).json({ success: false, message: 'Uso de CFDI no encontrado' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.json({ success: true, data: use });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCfdiUsesForPersonType(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const personType = req.params.personType as PersonType;
|
||||||
|
const uses = await cfdiUsesService.findForPersonType(personType);
|
||||||
|
res.json({ success: true, data: uses });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCfdiUsesForRegime(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const uses = await cfdiUsesService.findForRegime(req.params.regimeCode);
|
||||||
|
res.json({ success: true, data: uses });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== PAYMENT METHODS ==========
|
||||||
|
async getPaymentMethods(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const filter = {
|
||||||
|
requiresBankInfo: req.query.requires_bank_info === 'true' ? true : undefined,
|
||||||
|
active: req.query.active === 'true' ? true : undefined,
|
||||||
|
};
|
||||||
|
const methods = await paymentMethodsService.findAll(filter);
|
||||||
|
res.json({ success: true, data: methods });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPaymentMethod(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const method = await paymentMethodsService.findById(req.params.id);
|
||||||
|
res.json({ success: true, data: method });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPaymentMethodByCode(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const method = await paymentMethodsService.findByCode(req.params.code);
|
||||||
|
if (!method) {
|
||||||
|
res.status(404).json({ success: false, message: 'Forma de pago no encontrada' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.json({ success: true, data: method });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== PAYMENT TYPES ==========
|
||||||
|
async getPaymentTypes(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const filter = {
|
||||||
|
active: req.query.active === 'true' ? true : undefined,
|
||||||
|
};
|
||||||
|
const types = await paymentTypesService.findAll(filter);
|
||||||
|
res.json({ success: true, data: types });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPaymentType(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const type = await paymentTypesService.findById(req.params.id);
|
||||||
|
res.json({ success: true, data: type });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPaymentTypeByCode(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const type = await paymentTypesService.findByCode(req.params.code);
|
||||||
|
if (!type) {
|
||||||
|
res.status(404).json({ success: false, message: 'Método de pago no encontrado' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.json({ success: true, data: type });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== WITHHOLDING TYPES ==========
|
||||||
|
async getWithholdingTypes(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const filter = {
|
||||||
|
taxCategoryId: req.query.tax_category_id as string | undefined,
|
||||||
|
active: req.query.active === 'true' ? true : undefined,
|
||||||
|
};
|
||||||
|
const types = await withholdingTypesService.findAll(filter);
|
||||||
|
res.json({ success: true, data: types });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getWithholdingType(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const type = await withholdingTypesService.findById(req.params.id);
|
||||||
|
res.json({ success: true, data: type });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getWithholdingTypeByCode(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const type = await withholdingTypesService.findByCode(req.params.code);
|
||||||
|
if (!type) {
|
||||||
|
res.status(404).json({ success: false, message: 'Tipo de retención no encontrado' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.json({ success: true, data: type });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getWithholdingTypesByCategory(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
|
||||||
|
try {
|
||||||
|
const types = await withholdingTypesService.findByTaxCategory(req.params.categoryId);
|
||||||
|
res.json({ success: true, data: types });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fiscalController = new FiscalController();
|
||||||
45
src/modules/fiscal/fiscal.routes.ts
Normal file
45
src/modules/fiscal/fiscal.routes.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import { fiscalController } from './fiscal.controller.js';
|
||||||
|
import { authenticate } from '../../shared/middleware/auth.middleware.js';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
// All routes require authentication
|
||||||
|
router.use(authenticate);
|
||||||
|
|
||||||
|
// ========== TAX CATEGORIES ==========
|
||||||
|
router.get('/tax-categories', (req, res, next) => fiscalController.getTaxCategories(req, res, next));
|
||||||
|
router.get('/tax-categories/by-code/:code', (req, res, next) => fiscalController.getTaxCategoryByCode(req, res, next));
|
||||||
|
router.get('/tax-categories/by-sat-code/:satCode', (req, res, next) => fiscalController.getTaxCategoryBySatCode(req, res, next));
|
||||||
|
router.get('/tax-categories/:id', (req, res, next) => fiscalController.getTaxCategory(req, res, next));
|
||||||
|
|
||||||
|
// ========== FISCAL REGIMES ==========
|
||||||
|
router.get('/fiscal-regimes', (req, res, next) => fiscalController.getFiscalRegimes(req, res, next));
|
||||||
|
router.get('/fiscal-regimes/by-code/:code', (req, res, next) => fiscalController.getFiscalRegimeByCode(req, res, next));
|
||||||
|
router.get('/fiscal-regimes/person-type/:personType', (req, res, next) => fiscalController.getFiscalRegimesForPersonType(req, res, next));
|
||||||
|
router.get('/fiscal-regimes/:id', (req, res, next) => fiscalController.getFiscalRegime(req, res, next));
|
||||||
|
|
||||||
|
// ========== CFDI USES ==========
|
||||||
|
router.get('/cfdi-uses', (req, res, next) => fiscalController.getCfdiUses(req, res, next));
|
||||||
|
router.get('/cfdi-uses/by-code/:code', (req, res, next) => fiscalController.getCfdiUseByCode(req, res, next));
|
||||||
|
router.get('/cfdi-uses/person-type/:personType', (req, res, next) => fiscalController.getCfdiUsesForPersonType(req, res, next));
|
||||||
|
router.get('/cfdi-uses/regime/:regimeCode', (req, res, next) => fiscalController.getCfdiUsesForRegime(req, res, next));
|
||||||
|
router.get('/cfdi-uses/:id', (req, res, next) => fiscalController.getCfdiUse(req, res, next));
|
||||||
|
|
||||||
|
// ========== PAYMENT METHODS (SAT Forms of Payment) ==========
|
||||||
|
router.get('/payment-methods', (req, res, next) => fiscalController.getPaymentMethods(req, res, next));
|
||||||
|
router.get('/payment-methods/by-code/:code', (req, res, next) => fiscalController.getPaymentMethodByCode(req, res, next));
|
||||||
|
router.get('/payment-methods/:id', (req, res, next) => fiscalController.getPaymentMethod(req, res, next));
|
||||||
|
|
||||||
|
// ========== PAYMENT TYPES (SAT Payment Methods - PUE/PPD) ==========
|
||||||
|
router.get('/payment-types', (req, res, next) => fiscalController.getPaymentTypes(req, res, next));
|
||||||
|
router.get('/payment-types/by-code/:code', (req, res, next) => fiscalController.getPaymentTypeByCode(req, res, next));
|
||||||
|
router.get('/payment-types/:id', (req, res, next) => fiscalController.getPaymentType(req, res, next));
|
||||||
|
|
||||||
|
// ========== WITHHOLDING TYPES ==========
|
||||||
|
router.get('/withholding-types', (req, res, next) => fiscalController.getWithholdingTypes(req, res, next));
|
||||||
|
router.get('/withholding-types/by-code/:code', (req, res, next) => fiscalController.getWithholdingTypeByCode(req, res, next));
|
||||||
|
router.get('/withholding-types/by-category/:categoryId', (req, res, next) => fiscalController.getWithholdingTypesByCategory(req, res, next));
|
||||||
|
router.get('/withholding-types/:id', (req, res, next) => fiscalController.getWithholdingType(req, res, next));
|
||||||
|
|
||||||
|
export default router;
|
||||||
4
src/modules/fiscal/index.ts
Normal file
4
src/modules/fiscal/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export * from './entities/index.js';
|
||||||
|
export * from './fiscal-catalogs.service.js';
|
||||||
|
export { fiscalController } from './fiscal.controller.js';
|
||||||
|
export { default as fiscalRoutes } from './fiscal.routes.js';
|
||||||
Loading…
Reference in New Issue
Block a user