[SPRINT-0] fix: Resolve all 72 TypeScript compilation errors

- billing-usage: Remove unused imports, fix UpdateCouponDto access
- biometrics: Remove unused imports, fix duplicate exports in index.ts
- invoices: Fix undefined vs null type mismatches with ?? null
- partners: Fix duplicate ServiceContext exports, type mismatches
- projects: Remove unused imports
- shared/middleware: Fix TokenPayload type access

Build now passes: npm run build = 0 errors

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Adrian Flores Cortes 2026-02-03 02:31:34 -06:00
parent 450b13edff
commit caf9d4f0cb
24 changed files with 78 additions and 74 deletions

View File

@ -43,7 +43,7 @@ router.get('/', async (req: Request, res: Response, next: NextFunction) => {
* GET /api/v1/billing/coupons/valid
* List valid coupons
*/
router.get('/valid', async (req: Request, res: Response, next: NextFunction) => {
router.get('/valid', async (_req: Request, res: Response, next: NextFunction) => {
try {
const coupons = await couponService.findValidCoupons();
@ -61,7 +61,7 @@ router.get('/valid', async (req: Request, res: Response, next: NextFunction) =>
* GET /api/v1/billing/coupons/stats
* Get overall coupon statistics
*/
router.get('/stats', async (req: Request, res: Response, next: NextFunction) => {
router.get('/stats', async (_req: Request, res: Response, next: NextFunction) => {
try {
const stats = await couponService.getOverallStats();

View File

@ -47,7 +47,7 @@ router.get('/', async (req: Request, res: Response, next: NextFunction) => {
* GET /api/v1/billing/plans/public
* List public subscription plans (for pricing page)
*/
router.get('/public', async (req: Request, res: Response, next: NextFunction) => {
router.get('/public', async (_req: Request, res: Response, next: NextFunction) => {
try {
const plans = await planService.findPublicPlans();

View File

@ -44,7 +44,7 @@ router.get('/', async (req: Request, res: Response, next: NextFunction) => {
* GET /api/v1/billing/subscription/stats
* Get subscription statistics (admin only)
*/
router.get('/stats', async (req: Request, res: Response, next: NextFunction) => {
router.get('/stats', async (_req: Request, res: Response, next: NextFunction) => {
try {
const stats = await subscriptionService.getSubscriptionStats();

View File

@ -9,7 +9,6 @@
import { Router, Request, Response, NextFunction } from 'express';
import {
UsageTrackingService,
CreateUsageTrackingDto,
UpdateUsageTrackingDto,
} from '../services/usage-tracking.service';
import {

View File

@ -199,7 +199,7 @@ export class BillingCalculationService {
}
private async checkUsageLimit(
ctx: ServiceContext,
_ctx: ServiceContext,
limit: PlanLimit,
usage: UsageTracking
): Promise<UsageLimitCheck> {

View File

@ -5,7 +5,7 @@
* @module BillingUsage
*/
import { Repository, FindOptionsWhere, LessThanOrEqual, MoreThanOrEqual, In } from 'typeorm';
import { Repository, FindOptionsWhere, LessThanOrEqual, MoreThanOrEqual } from 'typeorm';
import { AppDataSource } from '../../../shared/database/typeorm.config';
import { Coupon, DiscountType, DurationPeriod } from '../entities/coupon.entity';
import { CouponRedemption } from '../entities/coupon-redemption.entity';
@ -127,9 +127,7 @@ export class CouponService {
return null;
}
if (data.code) {
data.code = data.code.toUpperCase();
}
// Note: code is not part of UpdateCouponDto - codes are immutable after creation
Object.assign(coupon, data);
return this.couponRepository.save(coupon);

View File

@ -5,7 +5,7 @@
* @module BillingUsage
*/
import { Repository, FindOptionsWhere, Between, MoreThanOrEqual, LessThanOrEqual } from 'typeorm';
import { Repository, Between, MoreThanOrEqual, LessThanOrEqual } from 'typeorm';
import { AppDataSource } from '../../../shared/database/typeorm.config';
import { UsageTracking } from '../entities/usage-tracking.entity';
import { UsageEvent, EventCategory } from '../entities/usage-event.entity';
@ -92,7 +92,6 @@ export class UsageTrackingService {
ctx: ServiceContext,
months: number = 12
): Promise<UsageTracking[]> {
const endDate = new Date();
const startDate = new Date();
startDate.setMonth(startDate.getMonth() - months);

View File

@ -7,10 +7,6 @@
*/
import { DataSource } from 'typeorm';
import { Device } from '../entities/device.entity';
import { DeviceSession } from '../entities/device-session.entity';
import { DeviceActivityLog } from '../entities/device-activity-log.entity';
import { BiometricCredential } from '../entities/biometric-credential.entity';
import { DeviceService } from './device.service';
import { BiometricCredentialService } from './biometric-credential.service';
@ -111,10 +107,8 @@ export interface BulkSyncResultDto {
export class BiometricSyncService {
private deviceService: DeviceService;
private credentialService: BiometricCredentialService;
private dataSource: DataSource;
constructor(dataSource: DataSource) {
this.dataSource = dataSource;
this.deviceService = new DeviceService(dataSource);
this.credentialService = new BiometricCredentialService(dataSource);
}
@ -251,7 +245,7 @@ export class BiometricSyncService {
/**
* Get pending actions for device
*/
private async getPendingActions(tenantId: string, deviceId: string): Promise<PendingActionDto[]> {
private async getPendingActions(_tenantId: string, _deviceId: string): Promise<PendingActionDto[]> {
// This would typically check a pending_actions table
// For now, return empty array
return [];
@ -325,7 +319,7 @@ export class BiometricSyncService {
/**
* Process individual attendance record
*/
private async processAttendanceRecord(tenantId: string, record: AttendanceRecordDto): Promise<void> {
private async processAttendanceRecord(_tenantId: string, record: AttendanceRecordDto): Promise<void> {
// This would typically insert into hr.attendance_records table
// For now, just validate the record
if (!record.employeeId) {
@ -439,7 +433,7 @@ export class BiometricSyncService {
async validateOfflineAuth(
deviceId: string,
token: string,
signature: string
_signature: string
): Promise<{
valid: boolean;
reason?: string;

View File

@ -3,6 +3,20 @@
* ERP Construccion - Modulo Biometrics
*/
export * from './device.service';
export * from './biometric-credential.service';
// Export device service (includes PaginatedResult and PaginationOptions)
export {
DeviceService,
PaginatedResult,
PaginationOptions,
} from './device.service';
// Export credential service (excluding duplicate interfaces)
export {
BiometricCredentialService,
RegisterCredentialDto,
UpdateCredentialDto,
VerifyCredentialDto,
} from './biometric-credential.service';
// Export sync service
export * from './biometric-sync.service';

View File

@ -111,7 +111,7 @@ export class InvoiceService {
private repository: Repository<Invoice>;
private itemRepository: Repository<InvoiceItem>;
constructor(private readonly dataSource: DataSource) {
constructor(dataSource: DataSource) {
this.repository = dataSource.getRepository(Invoice);
this.itemRepository = dataSource.getRepository(InvoiceItem);
}
@ -528,7 +528,7 @@ export class InvoiceService {
invoice.amountPaid = newAmountPaid;
invoice.paidAmount = newAmountPaid;
invoice.updatedBy = ctx.userId;
invoice.updatedBy = ctx.userId ?? null;
if (newAmountPaid >= total) {
invoice.status = 'paid';
@ -563,8 +563,8 @@ export class InvoiceService {
invoice.paidAmount = invoice.total;
invoice.paidAt = new Date();
invoice.paymentDate = new Date();
invoice.paymentReference = paymentReference || invoice.paymentReference;
invoice.updatedBy = ctx.userId;
invoice.paymentReference = paymentReference ?? invoice.paymentReference;
invoice.updatedBy = ctx.userId ?? null;
return this.repository.save(invoice);
}
@ -584,7 +584,7 @@ export class InvoiceService {
invoice.status = 'voided';
invoice.internalNotes = `${invoice.internalNotes || ''}\n[ANULADA]: ${reason || 'Sin motivo especificado'}`;
invoice.updatedBy = ctx.userId;
invoice.updatedBy = ctx.userId ?? null;
return this.repository.save(invoice);
}
@ -604,7 +604,7 @@ export class InvoiceService {
invoice.status = 'cancelled';
invoice.internalNotes = `${invoice.internalNotes || ''}\n[CANCELADA]: ${reason || 'Sin motivo especificado'}`;
invoice.updatedBy = ctx.userId;
invoice.updatedBy = ctx.userId ?? null;
return this.repository.save(invoice);
}
@ -657,7 +657,7 @@ export class InvoiceService {
// Mark original as refunded
originalInvoice.status = 'refunded';
originalInvoice.updatedBy = ctx.userId;
originalInvoice.updatedBy = ctx.userId ?? null;
await this.repository.save(originalInvoice);
return creditNote;
@ -898,7 +898,7 @@ export class InvoiceService {
}
invoice.status = newStatus;
invoice.updatedBy = ctx.userId;
invoice.updatedBy = ctx.userId ?? null;
return this.repository.save(invoice);
}

View File

@ -65,7 +65,7 @@ export class PaymentService {
private allocationRepository: Repository<PaymentAllocation>;
private invoiceRepository: Repository<Invoice>;
constructor(private readonly dataSource: DataSource) {
constructor(dataSource: DataSource) {
this.repository = dataSource.getRepository(Payment);
this.allocationRepository = dataSource.getRepository(PaymentAllocation);
this.invoiceRepository = dataSource.getRepository(Invoice);
@ -344,7 +344,7 @@ export class PaymentService {
invoice.status = 'partial';
}
invoice.updatedBy = ctx.userId;
invoice.updatedBy = ctx.userId ?? null;
await this.invoiceRepository.save(invoice);
}
@ -384,7 +384,7 @@ export class PaymentService {
}
}
invoice.updatedBy = ctx.userId;
invoice.updatedBy = ctx.userId ?? null;
await this.invoiceRepository.save(invoice);
await this.allocationRepository.remove(allocation);

View File

@ -101,7 +101,7 @@ export function createPartnerTaxInfoController(dataSource: DataSource): Router {
* GET /api/v1/partners/catalogs/sat-regimes
* Lista regimenes fiscales SAT
*/
router.get('/catalogs/sat-regimes', async (req: Request, res: Response, next: NextFunction) => {
router.get('/catalogs/sat-regimes', async (_req: Request, res: Response, next: NextFunction) => {
try {
const regimes = taxInfoService.getSatRegimes();
const formatted = Object.entries(regimes).map(([code, name]) => ({
@ -118,7 +118,7 @@ export function createPartnerTaxInfoController(dataSource: DataSource): Router {
* GET /api/v1/partners/catalogs/cfdi-uses
* Lista usos de CFDI
*/
router.get('/catalogs/cfdi-uses', async (req: Request, res: Response, next: NextFunction) => {
router.get('/catalogs/cfdi-uses', async (_req: Request, res: Response, next: NextFunction) => {
try {
const uses = taxInfoService.getCfdiUses();
const formatted = Object.entries(uses).map(([code, name]) => ({

View File

@ -70,7 +70,7 @@ export class PartnerBankAccount {
isVerified: boolean;
@Column({ name: 'verified_at', type: 'timestamptz', nullable: true })
verifiedAt: Date;
verifiedAt: Date | null;
// Notas
@Column({ type: 'text', nullable: true })

View File

@ -66,10 +66,10 @@ export class PartnerTaxInfo {
isVerified: boolean;
@Column({ name: 'verified_at', type: 'timestamptz', nullable: true })
verifiedAt: Date;
verifiedAt: Date | null;
@Column({ name: 'verification_source', type: 'varchar', length: 50, nullable: true })
verificationSource: string; // SAT, MANUAL, API
verificationSource: string | null; // SAT, MANUAL, API
// Metadata
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })

View File

@ -3,9 +3,10 @@
* @module Partners
*/
export * from './partner.service';
export * from './partner-address.service';
export * from './partner-contact.service';
export * from './partner-bank-account.service';
export * from './partner-tax-info.service';
export * from './partner-segment.service';
// Export ServiceContext only from partner.service (it's the same in all files)
export { ServiceContext, PartnerService, PaginatedResult, CreatePartnerDto, UpdatePartnerDto, PartnerFilters } from './partner.service';
export { PartnerAddressService, CreatePartnerAddressDto, UpdatePartnerAddressDto, AddressType } from './partner-address.service';
export { PartnerContactService, CreatePartnerContactDto, UpdatePartnerContactDto, PartnerContactFilters } from './partner-contact.service';
export { PartnerBankAccountService, CreatePartnerBankAccountDto, UpdatePartnerBankAccountDto, AccountType } from './partner-bank-account.service';
export { PartnerTaxInfoService, CreatePartnerTaxInfoDto, UpdatePartnerTaxInfoDto, VerifyTaxInfoDto, SAT_REGIMES, CFDI_USES } from './partner-tax-info.service';
export { PartnerSegmentService, CreatePartnerSegmentDto, UpdatePartnerSegmentDto, PartnerSegmentFilters, SegmentType } from './partner-segment.service';

View File

@ -5,7 +5,7 @@
* @module Partners
*/
import { DataSource, Repository, FindOptionsWhere } from 'typeorm';
import { DataSource, Repository } from 'typeorm';
import { PartnerAddress } from '../entities';
/**
@ -117,7 +117,7 @@ export class PartnerAddressService {
}
async create(
ctx: ServiceContext,
_ctx: ServiceContext,
dto: CreatePartnerAddressDto
): Promise<PartnerAddress> {
// If this is the first address or marked as default, update others
@ -141,7 +141,7 @@ export class PartnerAddressService {
}
async update(
ctx: ServiceContext,
_ctx: ServiceContext,
id: string,
dto: UpdatePartnerAddressDto
): Promise<PartnerAddress | null> {

View File

@ -5,7 +5,7 @@
* @module Partners
*/
import { DataSource, Repository, FindOptionsWhere } from 'typeorm';
import { DataSource, Repository } from 'typeorm';
import { PartnerBankAccount } from '../entities';
/**
@ -89,7 +89,7 @@ export class PartnerBankAccountService {
}
async create(
ctx: ServiceContext,
_ctx: ServiceContext,
dto: CreatePartnerBankAccountDto
): Promise<PartnerBankAccount> {
// Validate CLABE format for Mexican banks
@ -125,7 +125,7 @@ export class PartnerBankAccountService {
}
async update(
ctx: ServiceContext,
_ctx: ServiceContext,
id: string,
dto: UpdatePartnerBankAccountDto
): Promise<PartnerBankAccount | null> {
@ -169,7 +169,7 @@ export class PartnerBankAccountService {
return this.repository.save(account);
}
async verify(ctx: ServiceContext, id: string): Promise<PartnerBankAccount | null> {
async verify(_ctx: ServiceContext, id: string): Promise<PartnerBankAccount | null> {
const account = await this.findById(id);
if (!account) {
return null;
@ -180,7 +180,7 @@ export class PartnerBankAccountService {
return this.repository.save(account);
}
async unverify(ctx: ServiceContext, id: string): Promise<PartnerBankAccount | null> {
async unverify(_ctx: ServiceContext, id: string): Promise<PartnerBankAccount | null> {
const account = await this.findById(id);
if (!account) {
return null;

View File

@ -5,7 +5,7 @@
* @module Partners
*/
import { DataSource, Repository, FindOptionsWhere } from 'typeorm';
import { DataSource, Repository } from 'typeorm';
import { PartnerContact } from '../entities';
/**
@ -142,7 +142,7 @@ export class PartnerContactService {
}
async create(
ctx: ServiceContext,
_ctx: ServiceContext,
dto: CreatePartnerContactDto
): Promise<PartnerContact> {
// If this is primary, clear other primary contacts
@ -164,7 +164,7 @@ export class PartnerContactService {
}
async update(
ctx: ServiceContext,
_ctx: ServiceContext,
id: string,
dto: UpdatePartnerContactDto
): Promise<PartnerContact | null> {

View File

@ -5,7 +5,7 @@
* @module Partners
*/
import { DataSource, Repository, FindOptionsWhere } from 'typeorm';
import { DataSource, Repository } from 'typeorm';
import { PartnerSegment } from '../entities';
/**
@ -184,7 +184,7 @@ export class PartnerSegmentService {
}
segment.isActive = true;
segment.updatedBy = ctx.userId;
segment.updatedBy = ctx.userId ?? '';
return this.repository.save(segment);
}
@ -196,7 +196,7 @@ export class PartnerSegmentService {
}
segment.isActive = false;
segment.updatedBy = ctx.userId;
segment.updatedBy = ctx.userId ?? '';
return this.repository.save(segment);
}
@ -229,7 +229,7 @@ export class PartnerSegmentService {
const segment = segmentMap.get(id);
if (segment) {
segment.sortOrder = index;
segment.updatedBy = ctx.userId;
segment.updatedBy = ctx.userId ?? '';
return this.repository.save(segment);
}
return null;

View File

@ -5,7 +5,7 @@
* @module Partners
*/
import { DataSource, Repository, FindOptionsWhere } from 'typeorm';
import { DataSource, Repository } from 'typeorm';
import { PartnerTaxInfo } from '../entities';
/**
@ -118,7 +118,7 @@ export class PartnerTaxInfoService {
}
async create(
ctx: ServiceContext,
_ctx: ServiceContext,
dto: CreatePartnerTaxInfoDto
): Promise<PartnerTaxInfo> {
// Check if tax info already exists for partner
@ -151,7 +151,7 @@ export class PartnerTaxInfoService {
}
async update(
ctx: ServiceContext,
_ctx: ServiceContext,
id: string,
dto: UpdatePartnerTaxInfoDto
): Promise<PartnerTaxInfo | null> {
@ -202,7 +202,7 @@ export class PartnerTaxInfoService {
}
async verify(
ctx: ServiceContext,
_ctx: ServiceContext,
id: string,
dto: VerifyTaxInfoDto
): Promise<PartnerTaxInfo | null> {
@ -218,7 +218,7 @@ export class PartnerTaxInfoService {
return this.repository.save(taxInfo);
}
async unverify(ctx: ServiceContext, id: string): Promise<PartnerTaxInfo | null> {
async unverify(_ctx: ServiceContext, id: string): Promise<PartnerTaxInfo | null> {
const taxInfo = await this.findById(id);
if (!taxInfo) {
return null;

View File

@ -5,7 +5,7 @@
* @module Partners
*/
import { DataSource, Repository, FindOptionsWhere, ILike } from 'typeorm';
import { DataSource, Repository, FindOptionsWhere } from 'typeorm';
import { Partner, PartnerType } from '../entities';
/**
@ -275,7 +275,7 @@ export class PartnerService {
}
partner.currentBalance = Number(partner.currentBalance) + balanceChange;
partner.updatedBy = ctx.userId;
partner.updatedBy = ctx.userId ?? '';
return this.repository.save(partner);
}
@ -287,7 +287,7 @@ export class PartnerService {
}
partner.isVerified = true;
partner.updatedBy = ctx.userId;
partner.updatedBy = ctx.userId ?? '';
return this.repository.save(partner);
}
@ -299,7 +299,7 @@ export class PartnerService {
}
partner.isActive = true;
partner.updatedBy = ctx.userId;
partner.updatedBy = ctx.userId ?? '';
return this.repository.save(partner);
}
@ -311,7 +311,7 @@ export class PartnerService {
}
partner.isActive = false;
partner.updatedBy = ctx.userId;
partner.updatedBy = ctx.userId ?? '';
return this.repository.save(partner);
}

View File

@ -25,10 +25,9 @@ import {
ServiceContext,
} from '../services';
import { ProjectStatus, ProjectPrivacy } from '../entities/project.entity';
import { ProjectStatus } from '../entities/project.entity';
import { TaskStatus, TaskPriority } from '../entities/task.entity';
import { TimesheetStatus } from '../entities/timesheet.entity';
import { MilestoneStatus } from '../entities/milestone.entity';
/**
* Helper to get context from request

View File

@ -7,7 +7,7 @@
* @module Projects
*/
import { DataSource, Repository, IsNull } from 'typeorm';
import { DataSource, Repository } from 'typeorm';
import { ProjectStageEntity } from '../entities/project-stage.entity';
import { ServiceContext, PaginatedResult } from './project.service';

View File

@ -256,7 +256,7 @@ export function requirePermOrOwner(code: string, ownerField: string = 'userId')
throw new UnauthorizedError('Usuario no autenticado');
}
const userId = req.user.sub || req.user.userId;
const userId = req.user.sub || (req.user as any).userId;
const roles = req.user.roles || [];
const resourceOwnerId = req.params[ownerField] || req.body?.[ownerField];