BillTracker/services/amountSuggestionService.js

58 lines
1.6 KiB
JavaScript

'use strict';
const { accountingActiveSql } = require('./paymentAccountingService');
const { fromCents } = require('../utils/money');
/**
* Computes a suggested expected amount for a bill based on the rolling median
* of the last 6 months of actual data. Prefers monthly_bill_state.actual_amount
* (user-corrected values) over raw payment sums.
*/
function computeAmountSuggestion(db, billId, year, month) {
const amounts = [];
let y = year;
let m = month;
for (let i = 0; i < 6; i++) {
m -= 1;
if (m === 0) { m = 12; y -= 1; }
const mbs = db.prepare(
'SELECT actual_amount FROM monthly_bill_state WHERE bill_id = ? AND year = ? AND month = ?'
).get(billId, y, m);
if (mbs?.actual_amount != null) {
amounts.push(mbs.actual_amount);
continue;
}
const result = db.prepare(`
SELECT COALESCE(SUM(amount), 0) AS total
FROM payments
WHERE bill_id = ?
AND deleted_at IS NULL
AND ${accountingActiveSql()}
AND strftime('%Y', paid_date) = ?
AND strftime('%m', paid_date) = ?
`).get(billId, String(y), String(m).padStart(2, '0'));
if (result.total > 0) amounts.push(result.total);
}
if (amounts.length === 0) return null;
const sorted = [...amounts].sort((a, b) => a - b);
const mid = Math.floor(sorted.length / 2);
const median = sorted.length % 2 === 0
? (sorted[mid - 1] + sorted[mid]) / 2
: sorted[mid];
return {
suggestion: fromCents(Math.round(median)),
months_used: amounts.length,
confidence: amounts.length >= 3 ? 'high' : 'low',
};
}
module.exports = { computeAmountSuggestion };