НДС в DI2: вкладка «Расчёты → НДС» переведена на декларационную витрину 1С
- Новый server/finance-di2/nds.routes.ts: GET /api/di2/nds/summary|quarters|input-balance|sync-status|contract-markers (только отображение nds_monthly/nds_input_balance/nds_sync_log/nds_contract_marker, без расчёта НДС) - VatTab переписан: KPI, динамика (месяц с накопительным итогом квартала / квартал), по ставкам, stacked по типам операций, доля авансов, резерв вычетов (19 счёт), баннер свежести, таблица «деньги есть — отгрузки нет» (RED_NO_SHIPMENT > 1 млн) - Старый GET /api/calculations/vat (вычисление 20/120 из платежей) удалён
This commit is contained in:
@@ -4465,94 +4465,6 @@ export async function registerRoutes(httpServer: Server, app: Express): Promise<
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/api/calculations/vat", async (req, res) => {
|
||||
try {
|
||||
const { dateFrom, dateTo } = req.query;
|
||||
const incParams: unknown[] = [];
|
||||
const expParams: unknown[] = [];
|
||||
const incConds: string[] = ["1=1"];
|
||||
const expConds: string[] = ["1=1"];
|
||||
|
||||
if (dateFrom) {
|
||||
incParams.push(dateFrom); incConds.push(`data >= $${incParams.length}`);
|
||||
expParams.push(dateFrom); expConds.push(`data >= $${expParams.length}`);
|
||||
}
|
||||
if (dateTo) {
|
||||
incParams.push(dateTo); incConds.push(`data <= $${incParams.length}`);
|
||||
expParams.push(dateTo); expConds.push(`data <= $${expParams.length}`);
|
||||
}
|
||||
|
||||
const incWhere = `WHERE ${incConds.join(" AND ")}`;
|
||||
const expWhere = `WHERE ${expConds.join(" AND ")}`;
|
||||
|
||||
const [incByRate, expByRate, incMonthly, expMonthly] = await Promise.all([
|
||||
cachedQuery(stableKey("vat-inc-rate", req.query), TTL_SHORT, () =>
|
||||
query(`SELECT COALESCE(stavkands, 'Без НДС') as rate, COUNT(*) as cnt, COALESCE(SUM(summa), 0) as total FROM ${getIncomeEffectiveView()} ${incWhere} GROUP BY COALESCE(stavkands, 'Без НДС') ORDER BY total DESC`, incParams)
|
||||
),
|
||||
cachedQuery(stableKey("vat-exp-rate", req.query), TTL_SHORT, () =>
|
||||
query(`SELECT COALESCE(stavkands, 'Без НДС') as rate, COUNT(*) as cnt, COALESCE(SUM(summadokumenta), 0) as total FROM ${getExpenseEffectiveView()} ${expWhere} GROUP BY COALESCE(stavkands, 'Без НДС') ORDER BY total DESC`, expParams)
|
||||
),
|
||||
cachedQuery(stableKey("vat-inc-monthly", req.query), TTL_SHORT, () =>
|
||||
query(`SELECT TO_CHAR(data, 'YYYY-MM') as period, COALESCE(stavkands, 'Без НДС') as rate, COALESCE(SUM(summa), 0) as total FROM ${getIncomeEffectiveView()} ${incWhere} GROUP BY TO_CHAR(data, 'YYYY-MM'), COALESCE(stavkands, 'Без НДС') ORDER BY period`, incParams)
|
||||
),
|
||||
cachedQuery(stableKey("vat-exp-monthly", req.query), TTL_SHORT, () =>
|
||||
query(`SELECT TO_CHAR(data, 'YYYY-MM') as period, COALESCE(stavkands, 'Без НДС') as rate, COALESCE(SUM(summadokumenta), 0) as total FROM ${getExpenseEffectiveView()} ${expWhere} GROUP BY TO_CHAR(data, 'YYYY-MM'), COALESCE(stavkands, 'Без НДС') ORDER BY period`, expParams)
|
||||
),
|
||||
]);
|
||||
|
||||
const parseRate = (r: string): number => {
|
||||
const m = r.match(/(\d+)/);
|
||||
return m ? Number(m[1]) / 100 : 0;
|
||||
};
|
||||
|
||||
const incomeByRate = incByRate.rows.map((r: any) => ({
|
||||
rate: r.rate,
|
||||
count: Number(r.cnt),
|
||||
total: Number(r.total),
|
||||
vat: Number(r.total) * parseRate(r.rate) / (1 + parseRate(r.rate)),
|
||||
}));
|
||||
|
||||
const expenseByRate = expByRate.rows.map((r: any) => ({
|
||||
rate: r.rate,
|
||||
count: Number(r.cnt),
|
||||
total: Number(r.total),
|
||||
vat: Number(r.total) * parseRate(r.rate) / (1 + parseRate(r.rate)),
|
||||
}));
|
||||
|
||||
const totalIncomeVat = incomeByRate.reduce((s: number, r: any) => s + r.vat, 0);
|
||||
const totalExpenseVat = expenseByRate.reduce((s: number, r: any) => s + r.vat, 0);
|
||||
|
||||
const periodsSet = new Set<string>();
|
||||
[...incMonthly.rows, ...expMonthly.rows].forEach((r: any) => periodsSet.add(r.period));
|
||||
const periods = [...periodsSet].sort();
|
||||
|
||||
const monthly = periods.map(p => {
|
||||
let incVat = 0;
|
||||
let expVat = 0;
|
||||
incMonthly.rows.filter((r: any) => r.period === p).forEach((r: any) => {
|
||||
const rate = parseRate(r.rate);
|
||||
incVat += Number(r.total) * rate / (1 + rate);
|
||||
});
|
||||
expMonthly.rows.filter((r: any) => r.period === p).forEach((r: any) => {
|
||||
const rate = parseRate(r.rate);
|
||||
expVat += Number(r.total) * rate / (1 + rate);
|
||||
});
|
||||
return { period: p, incomeVat: Math.round(incVat * 100) / 100, expenseVat: Math.round(expVat * 100) / 100, toPay: Math.round((incVat - expVat) * 100) / 100 };
|
||||
});
|
||||
|
||||
res.json({
|
||||
incomeByRate,
|
||||
expenseByRate,
|
||||
totalIncomeVat: Math.round(totalIncomeVat * 100) / 100,
|
||||
totalExpenseVat: Math.round(totalExpenseVat * 100) / 100,
|
||||
toPay: Math.round((totalIncomeVat - totalExpenseVat) * 100) / 100,
|
||||
monthly,
|
||||
});
|
||||
} catch (err) {
|
||||
handleApiError(res, err);
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/api/calculations/profit-tax", async (req, res) => {
|
||||
try {
|
||||
await ensureTreesReady();
|
||||
|
||||
Reference in New Issue
Block a user