diff --git a/packages/loot-core/src/shared/transactions.ts b/packages/loot-core/src/shared/transactions.ts index 532047a223c..6142b1cc09a 100644 --- a/packages/loot-core/src/shared/transactions.ts +++ b/packages/loot-core/src/shared/transactions.ts @@ -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 | 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( + 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[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, diff --git a/packages/loot-core/src/types/models/transaction.d.ts b/packages/loot-core/src/types/models/transaction.d.ts index 07a64a23175..bc145b52440 100644 --- a/packages/loot-core/src/types/models/transaction.d.ts +++ b/packages/loot-core/src/types/models/transaction.d.ts @@ -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; diff --git a/upcoming-release-notes/2388.md b/upcoming-release-notes/2388.md new file mode 100644 index 00000000000..a98f85df35f --- /dev/null +++ b/upcoming-release-notes/2388.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [twk3] +--- + +Update shared transaction module to strict typescript.