mirror of
https://github.com/actualbudget/actual.git
synced 2026-03-11 12:43:09 -05:00
Update shared transaction module to strict typescript (#2388)
* Update shared transaction module to strict typescript
This commit is contained in:
@@ -1,20 +1,27 @@
|
||||
// @ts-strict-ignore
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { type TransactionEntity } from '../types/models';
|
||||
import {
|
||||
type TransactionEntity,
|
||||
type NewTransactionEntity,
|
||||
} from '../types/models';
|
||||
|
||||
import { last, diffItems, applyChanges } from './util';
|
||||
|
||||
export function isPreviewId(id) {
|
||||
interface TransactionEntityWithError extends TransactionEntity {
|
||||
error: ReturnType<typeof SplitTransactionError> | null;
|
||||
_deleted?: boolean;
|
||||
}
|
||||
|
||||
export function isPreviewId(id: string) {
|
||||
return id.indexOf('preview/') !== -1;
|
||||
}
|
||||
|
||||
// The amount might be null when adding a new transaction
|
||||
function num(n) {
|
||||
function num(n: number | null | undefined) {
|
||||
return typeof n === 'number' ? n : 0;
|
||||
}
|
||||
|
||||
function SplitTransactionError(total, parent) {
|
||||
function SplitTransactionError(total: number, parent: TransactionEntity) {
|
||||
const difference = num(parent.amount) - total;
|
||||
|
||||
return {
|
||||
@@ -24,14 +31,22 @@ function SplitTransactionError(total, parent) {
|
||||
};
|
||||
}
|
||||
|
||||
export function makeChild(parent, data) {
|
||||
type GenericTransactionEntity =
|
||||
| NewTransactionEntity
|
||||
| TransactionEntity
|
||||
| TransactionEntityWithError;
|
||||
|
||||
export function makeChild<T extends GenericTransactionEntity>(
|
||||
parent: T,
|
||||
data: object,
|
||||
) {
|
||||
const prefix = parent.id === 'temp' ? 'temp' : '';
|
||||
|
||||
return {
|
||||
amount: 0,
|
||||
...data,
|
||||
payee: data.payee || parent.payee,
|
||||
id: data.id ? data.id : prefix + uuidv4(),
|
||||
payee: 'payee' in data ? data.payee : parent.payee,
|
||||
id: 'id' in data ? data.id : prefix + uuidv4(),
|
||||
account: parent.account,
|
||||
date: parent.date,
|
||||
cleared: parent.cleared != null ? parent.cleared : null,
|
||||
@@ -42,13 +57,13 @@ export function makeChild(parent, data) {
|
||||
is_child: true,
|
||||
parent_id: parent.id,
|
||||
error: null,
|
||||
};
|
||||
} as unknown as T;
|
||||
}
|
||||
|
||||
export function recalculateSplit(trans) {
|
||||
export function recalculateSplit(trans: TransactionEntity) {
|
||||
// Calculate the new total of split transactions and make sure
|
||||
// that it equals the parent amount
|
||||
const total = trans.subtransactions.reduce(
|
||||
const total = (trans.subtransactions || []).reduce(
|
||||
(acc, t) => acc + num(t.amount),
|
||||
0,
|
||||
);
|
||||
@@ -56,10 +71,10 @@ export function recalculateSplit(trans) {
|
||||
...trans,
|
||||
error:
|
||||
total === num(trans.amount) ? null : SplitTransactionError(total, trans),
|
||||
};
|
||||
} as TransactionEntityWithError;
|
||||
}
|
||||
|
||||
function findParentIndex(transactions, idx) {
|
||||
function findParentIndex(transactions: TransactionEntity[], idx: number) {
|
||||
// This relies on transactions being sorted in a way where parents
|
||||
// are always before children, which is enforced in the db layer.
|
||||
// Walk backwards and find the last parent;
|
||||
@@ -73,7 +88,7 @@ function findParentIndex(transactions, idx) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function getSplit(transactions, parentIndex) {
|
||||
function getSplit(transactions: TransactionEntity[], parentIndex: number) {
|
||||
const split = [transactions[parentIndex]];
|
||||
let curr = parentIndex + 1;
|
||||
while (curr < transactions.length && transactions[curr].is_child) {
|
||||
@@ -97,8 +112,8 @@ export function ungroupTransactions(transactions: TransactionEntity[]) {
|
||||
}, []);
|
||||
}
|
||||
|
||||
export function groupTransaction(split) {
|
||||
return { ...split[0], subtransactions: split.slice(1) };
|
||||
export function groupTransaction(split: TransactionEntity[]) {
|
||||
return { ...split[0], subtransactions: split.slice(1) } as TransactionEntity;
|
||||
}
|
||||
|
||||
export function ungroupTransaction(split: TransactionEntity | null) {
|
||||
@@ -113,14 +128,19 @@ export function applyTransactionDiff(
|
||||
diff: Parameters<typeof applyChanges>[0],
|
||||
) {
|
||||
return groupTransaction(
|
||||
applyChanges(diff, ungroupTransaction(groupedTrans) || []),
|
||||
applyChanges(
|
||||
diff,
|
||||
ungroupTransaction(groupedTrans) || [],
|
||||
) as TransactionEntity[],
|
||||
);
|
||||
}
|
||||
|
||||
function replaceTransactions(
|
||||
transactions: TransactionEntity[],
|
||||
id: string,
|
||||
func: (transaction: TransactionEntity) => TransactionEntity,
|
||||
func: (
|
||||
transaction: TransactionEntity,
|
||||
) => TransactionEntity | TransactionEntityWithError | null,
|
||||
) {
|
||||
const idx = transactions.findIndex(t => t.id === id);
|
||||
const trans = transactions[idx];
|
||||
@@ -138,9 +158,7 @@ function replaceTransactions(
|
||||
}
|
||||
|
||||
const split = getSplit(transactions, parentIndex);
|
||||
let grouped: TransactionEntity | { id: string; _deleted: boolean } = func(
|
||||
groupTransaction(split),
|
||||
);
|
||||
let grouped = func(groupTransaction(split));
|
||||
const newSplit = ungroupTransaction(grouped);
|
||||
|
||||
let diff;
|
||||
@@ -148,7 +166,7 @@ function replaceTransactions(
|
||||
// If everything was deleted, just delete the parent which will
|
||||
// delete everything
|
||||
diff = { deleted: [{ id: split[0].id }], updated: [] };
|
||||
grouped = { id: split[0].id, _deleted: true };
|
||||
grouped = { ...split[0], _deleted: true };
|
||||
transactionsCopy.splice(parentIndex, split.length);
|
||||
} else {
|
||||
diff = diffItems(split, newSplit);
|
||||
@@ -166,7 +184,10 @@ function replaceTransactions(
|
||||
|
||||
return {
|
||||
data: transactionsCopy,
|
||||
newTransaction: grouped || { id: trans.id, _deleted: true },
|
||||
newTransaction: grouped || {
|
||||
...trans,
|
||||
_deleted: true,
|
||||
},
|
||||
diff: diffItems([trans], newTrans),
|
||||
};
|
||||
}
|
||||
@@ -233,10 +254,10 @@ export function deleteTransaction(
|
||||
} else if (trans.subtransactions?.length === 1) {
|
||||
return {
|
||||
...trans,
|
||||
subtransactions: null,
|
||||
subtransactions: undefined,
|
||||
is_parent: false,
|
||||
error: null,
|
||||
};
|
||||
} as TransactionEntityWithError;
|
||||
} else {
|
||||
const sub = trans.subtransactions?.filter(t => t.id !== id);
|
||||
return recalculateSplit({ ...trans, subtransactions: sub });
|
||||
@@ -261,14 +282,12 @@ export function splitTransaction(
|
||||
is_parent: true,
|
||||
error: num(trans.amount) === 0 ? null : SplitTransactionError(0, trans),
|
||||
subtransactions: [makeChild(trans, { amount: 0, sort_order: -1 })],
|
||||
};
|
||||
} as TransactionEntityWithError;
|
||||
});
|
||||
}
|
||||
|
||||
export function realizeTempTransactions(transactions) {
|
||||
let parent = transactions.find(t => !t.is_child);
|
||||
parent = { ...parent, id: uuidv4() };
|
||||
|
||||
export function realizeTempTransactions(transactions: TransactionEntity[]) {
|
||||
const parent = { ...transactions.find(t => !t.is_child), id: uuidv4() };
|
||||
const children = transactions.filter(t => t.is_child);
|
||||
return [
|
||||
parent,
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { PayeeEntity } from './payee';
|
||||
import type { ScheduleEntity } from './schedule';
|
||||
|
||||
export interface NewTransactionEntity {
|
||||
id?: string;
|
||||
is_parent?: boolean;
|
||||
is_child?: boolean;
|
||||
parent_id?: string;
|
||||
|
||||
6
upcoming-release-notes/2388.md
Normal file
6
upcoming-release-notes/2388.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Maintenance
|
||||
authors: [twk3]
|
||||
---
|
||||
|
||||
Update shared transaction module to strict typescript.
|
||||
Reference in New Issue
Block a user