Fix monthly actuals: allow negative values and fix save/reconcile error
- Remove min={0} from NumberInput to allow negative actuals (refunds/corrections)
- Fix post() in journal-entries service: use id param directly instead of
RETURNING result which returns [rows, count] in TypeORM QueryRunner
- Handle negative amounts in saveActuals(): negative expense credits the
expense account, negative income debits the income account
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -140,11 +140,11 @@ export class JournalEntriesService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await this.tenant.query(
|
await this.tenant.query(
|
||||||
`UPDATE journal_entries SET is_posted = true, posted_by = $1, posted_at = NOW() WHERE id = $2 RETURNING *`,
|
`UPDATE journal_entries SET is_posted = true, posted_by = $1, posted_at = NOW() WHERE id = $2`,
|
||||||
[userId, id],
|
[userId, id],
|
||||||
);
|
);
|
||||||
return this.findOne(result[0].id);
|
return this.findOne(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async void(id: string, userId: string, reason: string) {
|
async void(id: string, userId: string, reason: string) {
|
||||||
|
|||||||
@@ -127,25 +127,28 @@ export class MonthlyActualsService {
|
|||||||
for (const line of filteredLines) {
|
for (const line of filteredLines) {
|
||||||
const acctType = accountTypeMap.get(line.accountId);
|
const acctType = accountTypeMap.get(line.accountId);
|
||||||
if (!acctType) continue;
|
if (!acctType) continue;
|
||||||
|
const abs = Math.abs(line.amount);
|
||||||
|
|
||||||
if (acctType === 'expense') {
|
if (acctType === 'expense') {
|
||||||
// Expense: debit expense account, credit cash
|
if (line.amount > 0) {
|
||||||
jeLines.push({
|
// Normal expense: debit expense, credit cash
|
||||||
accountId: line.accountId,
|
jeLines.push({ accountId: line.accountId, debit: abs, credit: 0, memo: `${monthLabel} actual` });
|
||||||
debit: Math.abs(line.amount),
|
totalCashCredit += abs;
|
||||||
credit: 0,
|
} else {
|
||||||
memo: `${monthLabel} actual`,
|
// Negative expense (refund/correction): credit expense, debit cash
|
||||||
});
|
jeLines.push({ accountId: line.accountId, debit: 0, credit: abs, memo: `${monthLabel} actual (correction)` });
|
||||||
totalCashCredit += Math.abs(line.amount);
|
totalCashDebit += abs;
|
||||||
|
}
|
||||||
} else if (acctType === 'income') {
|
} else if (acctType === 'income') {
|
||||||
// Income: credit income account, debit cash
|
if (line.amount > 0) {
|
||||||
jeLines.push({
|
// Normal income: credit income, debit cash
|
||||||
accountId: line.accountId,
|
jeLines.push({ accountId: line.accountId, debit: 0, credit: abs, memo: `${monthLabel} actual` });
|
||||||
debit: 0,
|
totalCashDebit += abs;
|
||||||
credit: Math.abs(line.amount),
|
} else {
|
||||||
memo: `${monthLabel} actual`,
|
// Negative income (correction): debit income, credit cash
|
||||||
});
|
jeLines.push({ accountId: line.accountId, debit: abs, credit: 0, memo: `${monthLabel} actual (correction)` });
|
||||||
totalCashDebit += Math.abs(line.amount);
|
totalCashCredit += abs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ export function MonthlyActualsPage() {
|
|||||||
size="xs"
|
size="xs"
|
||||||
hideControls
|
hideControls
|
||||||
decimalScale={2}
|
decimalScale={2}
|
||||||
min={0}
|
allowNegative
|
||||||
styles={{ input: { textAlign: 'right', fontFamily: 'monospace' } }}
|
styles={{ input: { textAlign: 'right', fontFamily: 'monospace' } }}
|
||||||
/>
|
/>
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
|
|||||||
Reference in New Issue
Block a user