refactor(types): type-check batch 2 — 7 medium routes + 2 real fixes
Dropped @ts-nocheck from admin/payments/monthly-starting-amounts/ subscriptions/authOidc/calendar/export (~113 errors). Mostly mechanical (catch bindings, params, never[] arrays), but the checker earned its keep: - types/http.d.ts: added the 4-arg res.download(path, name, options, cb) overload — the v0.42.0 root-option download fix (admin backup + user-db export) didn't type-check against the old 3-arg-only signature. - routes/calendar.cts: deleted dead clampDay() (zero callers; was also a standing lint warning). @ts-nocheck server count: 21 -> 14. Server suite 252/252. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
345a159288
commit
6c915a62c8
|
|
@ -1,4 +1,3 @@
|
|||
// @ts-nocheck — route controller converted to .cts; handler req/res typed, full type-check deferred (thin glue over typed services).
|
||||
import type { Req, Res, Next } from '../types/http';
|
||||
const express = require('express');
|
||||
const { log } = require('../utils/logger.cts');
|
||||
|
|
@ -44,7 +43,7 @@ const { backupOperationLimiter } = require('../middleware/rateLimiter.cts');
|
|||
// All routes mounted at /api/admin (requireAuth + requireAdmin applied at server level)
|
||||
|
||||
// Returns a validated positive integer from req.params.id, or null.
|
||||
function parseUserId(params) {
|
||||
function parseUserId(params: any) {
|
||||
const n = parseInt(params.id, 10);
|
||||
return Number.isInteger(n) && n > 0 ? n : null;
|
||||
}
|
||||
|
|
@ -52,7 +51,7 @@ function parseUserId(params) {
|
|||
// Backup-ops error path. Kept res-based (not throw-pattern) deliberately: the
|
||||
// download route can fail mid-stream after headers are sent, where a throw
|
||||
// can't produce a response. Masks 500s; 4xx service messages pass through.
|
||||
function sendError(res, err) {
|
||||
function sendError(res: Res, err: any) {
|
||||
const status = err.status || 500;
|
||||
res.status(status).json({ error: status === 500 ? 'Backup operation failed' : err.message });
|
||||
}
|
||||
|
|
@ -79,7 +78,7 @@ router.post('/backups', backupOperationLimiter, async (req: Req, res: Res) => {
|
|||
try {
|
||||
const backup = await createBackup();
|
||||
res.status(201).json(backup);
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
sendError(res, err);
|
||||
}
|
||||
});
|
||||
|
|
@ -88,7 +87,7 @@ router.post('/backups', backupOperationLimiter, async (req: Req, res: Res) => {
|
|||
router.get('/backups', backupOperationLimiter, (req: Req, res: Res) => {
|
||||
try {
|
||||
res.json({ backups: listBackups() });
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
sendError(res, err);
|
||||
}
|
||||
});
|
||||
|
|
@ -97,7 +96,7 @@ router.get('/backups', backupOperationLimiter, (req: Req, res: Res) => {
|
|||
router.get('/backups/settings', backupOperationLimiter, (req: Req, res: Res) => {
|
||||
try {
|
||||
res.json(getScheduleStatus());
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
sendError(res, err);
|
||||
}
|
||||
});
|
||||
|
|
@ -106,7 +105,7 @@ router.get('/backups/settings', backupOperationLimiter, (req: Req, res: Res) =>
|
|||
router.put('/backups/settings', backupOperationLimiter, (req: Req, res: Res) => {
|
||||
try {
|
||||
res.json(saveBackupScheduleSettings(req.body));
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
sendError(res, err);
|
||||
}
|
||||
});
|
||||
|
|
@ -115,7 +114,7 @@ router.put('/backups/settings', backupOperationLimiter, (req: Req, res: Res) =>
|
|||
router.post('/backups/run-scheduled-now', backupOperationLimiter, async (req: Req, res: Res) => {
|
||||
try {
|
||||
res.status(201).json(await runScheduledBackupNow());
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
sendError(res, err);
|
||||
}
|
||||
});
|
||||
|
|
@ -137,7 +136,7 @@ router.post(
|
|||
expectedChecksum: expectedChecksum ? String(expectedChecksum).trim() : undefined,
|
||||
});
|
||||
res.status(201).json(backup);
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
sendError(res, err);
|
||||
}
|
||||
},
|
||||
|
|
@ -159,7 +158,7 @@ router.get('/backups/:id/download', (req: Req, res: Res) => {
|
|||
if (err && !res.headersSent) sendError(res, err);
|
||||
},
|
||||
);
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
sendError(res, err);
|
||||
}
|
||||
});
|
||||
|
|
@ -168,7 +167,7 @@ router.get('/backups/:id/download', (req: Req, res: Res) => {
|
|||
router.post('/backups/:id/restore', backupOperationLimiter, async (req: Req, res: Res) => {
|
||||
try {
|
||||
res.json(await restoreBackup(req.params.id));
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
sendError(res, err);
|
||||
}
|
||||
});
|
||||
|
|
@ -177,7 +176,7 @@ router.post('/backups/:id/restore', backupOperationLimiter, async (req: Req, res
|
|||
router.delete('/backups/:id', backupOperationLimiter, (req: Req, res: Res) => {
|
||||
try {
|
||||
res.json(deleteBackup(req.params.id));
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
sendError(res, err);
|
||||
}
|
||||
});
|
||||
|
|
@ -428,7 +427,7 @@ router.delete('/users/:id', (req: Req, res: Res) => {
|
|||
router.get('/cleanup', (req: Req, res: Res) => {
|
||||
try {
|
||||
res.json(getCleanupStatus());
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
sendError(res, err);
|
||||
}
|
||||
});
|
||||
|
|
@ -444,7 +443,7 @@ router.get('/cleanup', (req: Req, res: Res) => {
|
|||
router.put('/cleanup', (req: Req, res: Res) => {
|
||||
try {
|
||||
res.json(applyCleanupSettings(req.body));
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
sendError(res, err);
|
||||
}
|
||||
});
|
||||
|
|
@ -455,7 +454,7 @@ router.post('/cleanup/run', backupOperationLimiter, async (req: Req, res: Res) =
|
|||
try {
|
||||
const result = await runAllCleanup();
|
||||
res.json(result);
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
sendError(res, err);
|
||||
}
|
||||
});
|
||||
|
|
@ -489,7 +488,7 @@ router.post('/auth-mode/oidc-test', async (req: Req, res: Res) => {
|
|||
router.put('/auth-mode', (req: Req, res: Res) => {
|
||||
try {
|
||||
res.json(applyAuthModeSettings(req.body || {}));
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
res
|
||||
.status(err.status || 500)
|
||||
.json({ error: err.status ? err.message : 'Failed to update authentication settings' });
|
||||
|
|
@ -538,7 +537,7 @@ router.post('/migrations/rollback', async (req: Req, res: Res) => {
|
|||
user_agent: req.get('user-agent'),
|
||||
});
|
||||
res.json({ success: true, ...result });
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
logAudit({
|
||||
user_id: req.user.id,
|
||||
action: 'migration.rollback.failure',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// @ts-nocheck — route controller converted to .cts; handler req/res typed, full type-check deferred (thin glue over typed services).
|
||||
import type { Req, Res, Next } from '../types/http';
|
||||
('use strict');
|
||||
|
||||
|
|
@ -47,11 +46,11 @@ router.get('/login', async (req: Req, res: Res) => {
|
|||
const state = createLoginState(redirectTo);
|
||||
const authUrl = await buildAuthorizationUrl(config, state);
|
||||
res.redirect(authUrl);
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
const msg = err.message || String(err);
|
||||
log.error('[oidc] Login initiation error:', msg || '(no message)');
|
||||
if (err.errors) {
|
||||
err.errors.forEach((e, i) => log.error(` [${i}]`, e.message || String(e)));
|
||||
err.errors.forEach((e: any, i: number) => log.error(` [${i}]`, e.message || String(e)));
|
||||
}
|
||||
if (err.cause) log.error(' cause:', err.cause.message || String(err.cause));
|
||||
res.status(502).json({ error: 'Failed to reach the identity provider. Please try again.' });
|
||||
|
|
@ -111,12 +110,12 @@ router.get('/callback', async (req: Req, res: Res) => {
|
|||
recordLogin(user.id, req.ip, req.get('user-agent'), session.sessionId);
|
||||
res.cookie(COOKIE_NAME, session.sessionId, cookieOpts(req));
|
||||
res.redirect(savedState.redirect_to || '/');
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
// Log message only — never log tokens, codes, or ID token contents
|
||||
const msg = err.message || String(err);
|
||||
log.error('[oidc] Callback error:', msg || '(no message)');
|
||||
if (err.errors) {
|
||||
err.errors.forEach((e, i) => log.error(` [${i}]`, e.message || String(e)));
|
||||
err.errors.forEach((e: any, i: number) => log.error(` [${i}]`, e.message || String(e)));
|
||||
}
|
||||
if (err.cause) log.error(' cause:', err.cause.message || String(err.cause));
|
||||
const errCode = err.status === 403 ? 'access_denied' : 'authentication_failed';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// @ts-nocheck — route controller converted to .cts; handler req/res typed, full type-check deferred (thin glue over typed services).
|
||||
import type { Req, Res, Next } from '../types/http';
|
||||
const express = require('express');
|
||||
const { ValidationError } = require('../utils/apiError.cts');
|
||||
|
|
@ -18,21 +17,16 @@ const {
|
|||
const { localDateString } = require('../utils/dates.mts');
|
||||
const { roundMoney, sumMoney, fromCents } = require('../utils/money.mts');
|
||||
|
||||
function clampDay(year, month, day) {
|
||||
const daysInMonth = new Date(year, month, 0).getDate();
|
||||
return Math.min(Math.max(parseInt(day || 1, 10), 1), daysInMonth);
|
||||
}
|
||||
|
||||
function toDateString(year, month, day) {
|
||||
function toDateString(year: number, month: number, day: number) {
|
||||
return `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
function emptyDay(year, month, day) {
|
||||
function emptyDay(year: number, month: number, day: number) {
|
||||
return {
|
||||
date: toDateString(year, month, day),
|
||||
day,
|
||||
bills_due: [],
|
||||
payments: [],
|
||||
bills_due: [] as any[],
|
||||
payments: [] as any[],
|
||||
status_summary: {
|
||||
due_count: 0,
|
||||
paid_count: 0,
|
||||
|
|
@ -44,7 +38,7 @@ function emptyDay(year, month, day) {
|
|||
};
|
||||
}
|
||||
|
||||
function tokenPayload(req, tokenRow) {
|
||||
function tokenPayload(req: Req, tokenRow: any) {
|
||||
if (!tokenRow) {
|
||||
return {
|
||||
active: false,
|
||||
|
|
@ -189,7 +183,7 @@ router.get('/', (req: Req, res: Res) => {
|
|||
}
|
||||
|
||||
const calendarBills = bills
|
||||
.map((bill) => {
|
||||
.map((bill: any) => {
|
||||
const billRange = getCycleRange(year, month, bill);
|
||||
if (!billRange) return null;
|
||||
|
||||
|
|
@ -237,9 +231,9 @@ router.get('/', (req: Req, res: Res) => {
|
|||
if (!bill.is_skipped) day.status_summary.total_due += bill.effective_amount || 0;
|
||||
}
|
||||
|
||||
const activeBills = calendarBills.filter((bill) => !bill.is_skipped);
|
||||
const expectedTotal = sumMoney(activeBills, (bill) => bill.effective_amount || 0);
|
||||
const paidTotal = sumMoney(activeBills, (bill) => bill.paid_amount || 0);
|
||||
const activeBills = calendarBills.filter((bill: any) => !bill.is_skipped);
|
||||
const expectedTotal = sumMoney(activeBills, (bill: any) => bill.effective_amount || 0);
|
||||
const paidTotal = sumMoney(activeBills, (bill: any) => bill.paid_amount || 0);
|
||||
const remainingTotal = Math.max(0, roundMoney(expectedTotal - paidTotal));
|
||||
const paidPercent =
|
||||
expectedTotal > 0 ? Math.min(100, Math.round((paidTotal / expectedTotal) * 100)) : 0;
|
||||
|
|
@ -261,10 +255,11 @@ router.get('/', (req: Req, res: Res) => {
|
|||
remaining_total: remainingTotal,
|
||||
paid_percent: paidPercent,
|
||||
bill_count: activeBills.length,
|
||||
paid_count: activeBills.filter((bill) => bill.is_paid).length,
|
||||
skipped_count: calendarBills.filter((bill) => bill.is_skipped).length,
|
||||
missed_count: activeBills.filter((bill) => bill.status === 'late' || bill.status === 'missed')
|
||||
.length,
|
||||
paid_count: activeBills.filter((bill: any) => bill.is_paid).length,
|
||||
skipped_count: calendarBills.filter((bill: any) => bill.is_skipped).length,
|
||||
missed_count: activeBills.filter(
|
||||
(bill: any) => bill.status === 'late' || bill.status === 'missed',
|
||||
).length,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// @ts-nocheck — route controller converted to .cts; handler req/res typed, full type-check deferred (thin glue over typed services).
|
||||
import type { Req, Res, Next } from '../types/http';
|
||||
const express = require('express');
|
||||
const { ValidationError } = require('../utils/apiError.cts');
|
||||
|
|
@ -16,7 +15,7 @@ router.get('/', (req: Req, res: Res) => {
|
|||
const db = getDb();
|
||||
const format = (req.query.format || 'csv').toLowerCase();
|
||||
const { from, to } = req.query;
|
||||
const isDate = (s) => typeof s === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(s);
|
||||
const isDate = (s: any) => typeof s === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(s);
|
||||
|
||||
// Filter by an explicit date range (both bounds required) or by a single year.
|
||||
let where, whereParams, label;
|
||||
|
|
@ -71,14 +70,14 @@ router.get('/', (req: Req, res: Res) => {
|
|||
|
||||
if (format === 'csv') {
|
||||
const header = 'Date,Bill,Category,Expected,Paid,Method,Notes,Actual Amount,Monthly Notes\n';
|
||||
const escCsv = (v) => {
|
||||
const escCsv = (v: any) => {
|
||||
if (v == null) return '';
|
||||
const s = String(v);
|
||||
return /[,"\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
|
||||
};
|
||||
|
||||
const body = rows
|
||||
.map((r) => {
|
||||
.map((r: any) => {
|
||||
const paidMonth = parseInt(r.paid_date.slice(5, 7), 10);
|
||||
const paidYear = parseInt(r.paid_date.slice(0, 4), 10);
|
||||
const mbs = mbsStmt.get(r.bill_id, paidYear, paidMonth);
|
||||
|
|
@ -102,7 +101,7 @@ router.get('/', (req: Req, res: Res) => {
|
|||
}
|
||||
|
||||
// Fallback: JSON — enrich each row with monthly_bill_state overrides
|
||||
const enriched = rows.map((r) => {
|
||||
const enriched = rows.map((r: any) => {
|
||||
const paidMonth = parseInt(r.paid_date.slice(5, 7), 10);
|
||||
const paidYear = parseInt(r.paid_date.slice(0, 4), 10);
|
||||
const mbs = mbsStmt.get(r.bill_id, paidYear, paidMonth);
|
||||
|
|
@ -117,7 +116,7 @@ router.get('/', (req: Req, res: Res) => {
|
|||
res.json({ range: label, count: enriched.length, payments: enriched });
|
||||
});
|
||||
|
||||
function getUserExportData(userId) {
|
||||
function getUserExportData(userId: any) {
|
||||
const db = getDb();
|
||||
const categories = db
|
||||
.prepare(
|
||||
|
|
@ -136,7 +135,7 @@ function getUserExportData(userId) {
|
|||
`,
|
||||
)
|
||||
.all(userId)
|
||||
.map((b) => ({ ...b, expected_amount: fromCents(b.expected_amount) }));
|
||||
.map((b: any) => ({ ...b, expected_amount: fromCents(b.expected_amount) }));
|
||||
const payments = db
|
||||
.prepare(
|
||||
`
|
||||
|
|
@ -150,7 +149,7 @@ function getUserExportData(userId) {
|
|||
`,
|
||||
)
|
||||
.all(userId)
|
||||
.map((p) => ({ ...p, amount: fromCents(p.amount) }));
|
||||
.map((p: any) => ({ ...p, amount: fromCents(p.amount) }));
|
||||
const monthlyState = db
|
||||
.prepare(
|
||||
`
|
||||
|
|
@ -162,7 +161,7 @@ function getUserExportData(userId) {
|
|||
`,
|
||||
)
|
||||
.all(userId)
|
||||
.map((m) => ({ ...m, actual_amount: fromCents(m.actual_amount) }));
|
||||
.map((m: any) => ({ ...m, actual_amount: fromCents(m.actual_amount) }));
|
||||
const monthlyStartingAmounts = db
|
||||
.prepare(
|
||||
`
|
||||
|
|
@ -173,7 +172,7 @@ function getUserExportData(userId) {
|
|||
`,
|
||||
)
|
||||
.all(userId)
|
||||
.map((r) => ({
|
||||
.map((r: any) => ({
|
||||
...r,
|
||||
first_amount: fromCents(r.first_amount),
|
||||
fifteenth_amount: fromCents(r.fifteenth_amount),
|
||||
|
|
@ -190,13 +189,15 @@ function getUserExportData(userId) {
|
|||
)
|
||||
.all(userId);
|
||||
const notes = [
|
||||
...bills.filter((b) => b.notes).map((b) => ({ type: 'bill', bill_id: b.id, notes: b.notes })),
|
||||
...bills
|
||||
.filter((b: any) => b.notes)
|
||||
.map((b: any) => ({ type: 'bill', bill_id: b.id, notes: b.notes })),
|
||||
...payments
|
||||
.filter((p) => p.notes)
|
||||
.map((p) => ({ type: 'payment', payment_id: p.id, bill_id: p.bill_id, notes: p.notes })),
|
||||
.filter((p: any) => p.notes)
|
||||
.map((p: any) => ({ type: 'payment', payment_id: p.id, bill_id: p.bill_id, notes: p.notes })),
|
||||
...monthlyState
|
||||
.filter((m) => m.notes)
|
||||
.map((m) => ({
|
||||
.filter((m: any) => m.notes)
|
||||
.map((m: any) => ({
|
||||
type: 'monthly_state',
|
||||
monthly_state_id: m.id,
|
||||
bill_id: m.bill_id,
|
||||
|
|
@ -274,7 +275,7 @@ router.get('/user-excel', (req: Req, res: Res) => {
|
|||
|
||||
// Build the SQLite user-DB export to a temp file and return its path. Extracted
|
||||
// so the export→import round-trip can be regression-tested (exportImportRoundTrip).
|
||||
function buildUserDbExportFile(userId) {
|
||||
function buildUserDbExportFile(userId: any) {
|
||||
const data = getUserExportData(userId);
|
||||
const file = path.join(os.tmpdir(), `bill-tracker-user-${userId}-${Date.now()}.sqlite`);
|
||||
const out = new Database(file);
|
||||
|
|
@ -291,14 +292,14 @@ function buildUserDbExportFile(userId) {
|
|||
`);
|
||||
const meta = out.prepare('INSERT INTO export_metadata (key, value) VALUES (?, ?)');
|
||||
meta.run('metadata_json', JSON.stringify(data.metadata));
|
||||
const insertRows = (table, rows) => {
|
||||
const insertRows = (table: any, rows: any) => {
|
||||
if (!rows.length) return;
|
||||
const cols = Object.keys(rows[0]);
|
||||
const stmt = out.prepare(
|
||||
`INSERT INTO ${table} (${cols.join(',')}) VALUES (${cols.map(() => '?').join(',')})`,
|
||||
);
|
||||
const tx = out.transaction((items) =>
|
||||
items.forEach((row) => stmt.run(cols.map((c) => row[c]))),
|
||||
const tx = out.transaction((items: any) =>
|
||||
items.forEach((row: any) => stmt.run(cols.map((c: any) => row[c]))),
|
||||
);
|
||||
tx(rows);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// @ts-nocheck — route controller converted to .cts; handler req/res typed, full type-check deferred (thin glue over typed services).
|
||||
import type { Req, Res, Next } from '../types/http';
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
|
@ -8,7 +7,7 @@ const { accountingActiveSql } = require('../services/paymentAccountingService.ct
|
|||
const { toCents, fromCents } = require('../utils/money.mts');
|
||||
const { ValidationError } = require('../utils/apiError.cts');
|
||||
|
||||
function parseYearMonth(source) {
|
||||
function parseYearMonth(source: any) {
|
||||
const now = new Date();
|
||||
const year = parseInt(source.year || now.getFullYear(), 10);
|
||||
const month = parseInt(source.month || now.getMonth() + 1, 10);
|
||||
|
|
@ -23,12 +22,12 @@ function parseYearMonth(source) {
|
|||
return { year, month };
|
||||
}
|
||||
|
||||
function money(value) {
|
||||
function money(value: any) {
|
||||
const n = Number(value);
|
||||
return Number.isFinite(n) ? Math.round(n * 100) / 100 : 0;
|
||||
}
|
||||
|
||||
function getStartingAmounts(db, userId, year, month) {
|
||||
function getStartingAmounts(db: any, userId: any, year: any, month: any) {
|
||||
const row = db
|
||||
.prepare(
|
||||
`
|
||||
|
|
@ -46,7 +45,7 @@ function getStartingAmounts(db, userId, year, month) {
|
|||
};
|
||||
}
|
||||
|
||||
function calculatePaidDeductions(db, userId, year, month) {
|
||||
function calculatePaidDeductions(db: any, userId: any, year: any, month: any) {
|
||||
const { start, end } = getCycleRange(year, month);
|
||||
|
||||
// Paid from first bucket: bills with due_day 1-14
|
||||
|
|
@ -119,7 +118,7 @@ function calculatePaidDeductions(db, userId, year, month) {
|
|||
};
|
||||
}
|
||||
|
||||
function buildStartingAmountsResponse(db, userId, year, month) {
|
||||
function buildStartingAmountsResponse(db: any, userId: any, year: any, month: any) {
|
||||
const amounts = getStartingAmounts(db, userId, year, month);
|
||||
const paid = calculatePaidDeductions(db, userId, year, month);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// @ts-nocheck — route controller converted to .cts; handler req/res typed, full type-check deferred (thin glue over typed services).
|
||||
import type { Req, Res, Next } from '../types/http';
|
||||
const express = require('express');
|
||||
const { ValidationError, NotFoundError, ConflictError } = require('../utils/apiError.cts');
|
||||
|
|
@ -22,7 +21,7 @@ const { fromCents } = require('../utils/money.mts');
|
|||
const SQL_NOT_DELETED = 'deleted_at IS NULL';
|
||||
const TRANSACTION_MATCH_SOURCE = 'transaction_match';
|
||||
|
||||
function isTransactionLinkedPayment(payment) {
|
||||
function isTransactionLinkedPayment(payment: any) {
|
||||
return payment?.payment_source === TRANSACTION_MATCH_SOURCE || payment?.transaction_id != null;
|
||||
}
|
||||
|
||||
|
|
@ -34,7 +33,7 @@ function rejectTransactionLinkedPayment() {
|
|||
);
|
||||
}
|
||||
|
||||
function parseYearMonth(body) {
|
||||
function parseYearMonth(body: any) {
|
||||
const year = parseInt(body.year, 10);
|
||||
const month = parseInt(body.month, 10);
|
||||
if (!Number.isInteger(year) || year < 2000 || year > 2100) {
|
||||
|
|
@ -46,7 +45,7 @@ function parseYearMonth(body) {
|
|||
return { year, month };
|
||||
}
|
||||
|
||||
function getAutopaySuggestionContext(db, userId, billId, year, month) {
|
||||
function getAutopaySuggestionContext(db: any, userId: any, billId: any, year: any, month: any) {
|
||||
const bill = db
|
||||
.prepare(
|
||||
`
|
||||
|
|
@ -543,9 +542,9 @@ router.post('/bulk', (req: Req, res: Res) => {
|
|||
AND p.${SQL_NOT_DELETED}`,
|
||||
);
|
||||
|
||||
const created = [];
|
||||
const skipped = [];
|
||||
const errors = [];
|
||||
const created: any[] = [];
|
||||
const skipped: any[] = [];
|
||||
const errors: any[] = [];
|
||||
|
||||
const runBulk = db.transaction(() => {
|
||||
for (const item of payments) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// @ts-nocheck — route controller converted to .cts; handler req/res typed, full type-check deferred (thin glue over typed services).
|
||||
import type { Req, Res, Next } from '../types/http';
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
|
@ -69,8 +68,8 @@ router.post('/recommendations/match-bill', (req: Req, res: Res) => {
|
|||
const billId = parseInt(req.body?.bill_id, 10);
|
||||
const rawIds = Array.isArray(req.body?.transaction_ids) ? req.body.transaction_ids : [];
|
||||
const txIds = rawIds
|
||||
.map((id) => parseInt(id, 10))
|
||||
.filter((n) => Number.isInteger(n) && n > 0)
|
||||
.map((id: any) => parseInt(id, 10))
|
||||
.filter((n: any) => Number.isInteger(n) && n > 0)
|
||||
.slice(0, 50);
|
||||
|
||||
if (!Number.isInteger(billId) || billId < 1) {
|
||||
|
|
@ -178,7 +177,7 @@ router.post('/recommendations/create', (req: Req, res: Res) => {
|
|||
metadata: { transaction_ids: req.body?.transaction_ids || [] },
|
||||
});
|
||||
res.status(201).json(created);
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
// Service validation errors are user-facing at 4xx (parity with the old
|
||||
// behavior: no-status service throws surfaced at 400, never as masked 5xx).
|
||||
throw new ApiError(
|
||||
|
|
@ -222,7 +221,7 @@ router.get('/catalog', (req: Req, res: Res) => {
|
|||
)
|
||||
.all(req.user.id);
|
||||
|
||||
const billByCatalogId = new Map(matchedBills.map((b) => [b.catalog_id, b]));
|
||||
const billByCatalogId = new Map<any, any>(matchedBills.map((b: any) => [b.catalog_id, b]));
|
||||
|
||||
// User's custom descriptors
|
||||
let userDescriptors = [];
|
||||
|
|
@ -240,7 +239,7 @@ router.get('/catalog', (req: Req, res: Res) => {
|
|||
userDescsByCatalogId.get(d.catalog_id).push({ id: d.id, descriptor: d.descriptor });
|
||||
}
|
||||
|
||||
const catalog = catalogEntries.map((entry) => {
|
||||
const catalog = catalogEntries.map((entry: any) => {
|
||||
const bill = billByCatalogId.get(entry.id) ?? null;
|
||||
return {
|
||||
...entry,
|
||||
|
|
|
|||
|
|
@ -39,6 +39,12 @@ export interface Res {
|
|||
type(t: string): Res;
|
||||
redirect(url: string): void;
|
||||
download(path: string, filename?: string, cb?: (err?: any) => void): void;
|
||||
download(
|
||||
path: string,
|
||||
filename: string,
|
||||
options: Record<string, any>,
|
||||
cb?: (err?: any) => void,
|
||||
): void;
|
||||
sendFile(path: string, options?: any, cb?: (err?: any) => void): void;
|
||||
attachment(filename?: string): Res;
|
||||
locals: any;
|
||||
|
|
|
|||
Loading…
Reference in New Issue