Refactor inventory health calculations and enhance account mismatch detection

- Renamed and refactored `ledgerBalanceInCurrency` to `ledgerRowsInAccountCurrency` for clarity.
- Introduced `accountHasApiLedgerMismatch` function to streamline balance mismatch checks between API and ledger.
- Updated `computeInventoryHealth` and `getBalanceMismatchAccountIds` to utilize the new mismatch detection logic.
- Enhanced user feedback in `AccountsPage` regarding balance discrepancies and ledger activity.
This commit is contained in:
Denozordec
2026-03-20 23:16:49 +07:00
parent 196c5be532
commit 93cb6422d5
2 changed files with 30 additions and 25 deletions
+27 -24
View File
@@ -2,12 +2,15 @@ import { getPaidUntilDate } from './paid-until'
const STALE_SYNC_HOURS = 48
function ledgerBalanceInCurrency(account, balanceLedger) {
function ledgerRowsInAccountCurrency(account, balanceLedger) {
const cur = (account.balance_currency || account.currency || '').trim()
const rows = balanceLedger.filter((row) => row.providerAccountId === account.id)
const filtered = cur
? rows.filter((row) => !row.currency || row.currency === cur)
: rows
if (!cur) return rows
return rows.filter((row) => !row.currency || row.currency === cur)
}
function ledgerBalanceInCurrency(account, balanceLedger) {
const filtered = ledgerRowsInAccountCurrency(account, balanceLedger)
const credits = filtered
.filter((row) => row.direction === 'credit')
.reduce((acc, row) => acc + Number(row.amount || 0), 0)
@@ -17,6 +20,23 @@ function ledgerBalanceInCurrency(account, balanceLedger) {
return credits - debits
}
/**
* Сравниваем баланс из API с суммой по balance_ledger.
* Если в ledger нет ни одной строки по аккаунту (в валюте баланса) — не считаем расхождением:
* пользователь видит только API, а «0 из ledger» — не противоречие, а отсутствие учёта.
*/
export function accountHasApiLedgerMismatch(account, balanceLedger) {
if (account.balance_api == null || !Number.isFinite(Number(account.balance_api))) return false
const rows = ledgerRowsInAccountCurrency(account, balanceLedger)
if (rows.length === 0) return false
const ledger = ledgerBalanceInCurrency(account, balanceLedger)
if (!Number.isFinite(ledger)) return false
const api = Number(account.balance_api)
const diff = Math.abs(api - ledger)
const tol = Math.max(10, Math.abs(api) * 0.05)
return diff > tol
}
export function lastOkSyncFinishedAt(accountId, syncLog) {
const rows = (syncLog || []).filter(
(r) => r.accountId === accountId && r.status === 'ok' && r.finishedAt,
@@ -108,21 +128,14 @@ export function computeInventoryHealth(input) {
})
}
const mismatchAccounts = providerAccounts.filter((a) => {
if (a.balance_api == null || !Number.isFinite(Number(a.balance_api))) return false
const ledger = ledgerBalanceInCurrency(a, balanceLedger)
if (!Number.isFinite(ledger)) return false
const api = Number(a.balance_api)
const diff = Math.abs(api - ledger)
const tol = Math.max(10, Math.abs(api) * 0.05)
return diff > tol
})
const mismatchAccounts = providerAccounts.filter((a) => accountHasApiLedgerMismatch(a, balanceLedger))
if (mismatchAccounts.length) {
issues.push({
key: 'balance-mismatch',
title: 'Баланс API и ledger расходятся',
count: mismatchAccounts.length,
to: '/accounts?health=balance-mismatch',
hint: 'Считается только если в журнале «Баланс и списания» есть движения по аккаунту',
})
}
@@ -152,17 +165,7 @@ export function getStaleSyncAccountIds(providerAccounts, syncLog, now = new Date
* @param {object[]} balanceLedger
*/
export function getBalanceMismatchAccountIds(providerAccounts, balanceLedger) {
return providerAccounts
.filter((a) => {
if (a.balance_api == null || !Number.isFinite(Number(a.balance_api))) return false
const ledger = ledgerBalanceInCurrency(a, balanceLedger)
if (!Number.isFinite(ledger)) return false
const api = Number(a.balance_api)
const diff = Math.abs(api - ledger)
const tol = Math.max(10, Math.abs(api) * 0.05)
return diff > tol
})
.map((a) => a.id)
return providerAccounts.filter((a) => accountHasApiLedgerMismatch(a, balanceLedger)).map((a) => a.id)
}
/**
+3 -1
View File
@@ -262,7 +262,9 @@ export function AccountsPage({ db, actions, settings, ratesData }) {
highlightAccountIds?.size ? (
<div className="alert alert-warning d-flex align-items-center justify-content-between flex-wrap gap-2">
<span>
Подсвечены аккаунты, где баланс API заметно расходится с суммой по ledger (та же валюта).
В колонке «Баланс» для BILLmanager показывается сумма из API (в двух строках с конвертацией и в валюте аккаунта).
Здесь же сверяется с <strong>журналом на странице «Баланс и списания»</strong> (те же движения в валюте баланса).
Подсветка только если в журнале уже есть записи по аккаунту, но итог заметно не совпадает с API.
</span>
<Link to="/accounts" className="btn btn-sm btn-outline-secondary">
Сбросить фильтр