mirror of
https://github.com/actualbudget/actual.git
synced 2026-04-28 18:40:34 -05:00
♻️ (typescript) adding strict typings to utils.ts (#2023)
This commit is contained in:
committed by
GitHub
parent
0303292a28
commit
7174fccfe4
@@ -12,24 +12,26 @@ import q from '../shared/query';
|
||||
import type {
|
||||
CategoryGroupEntity,
|
||||
PayeeEntity,
|
||||
TransactionEntity,
|
||||
NewTransactionEntity,
|
||||
} from '../types/models';
|
||||
|
||||
import random from './random';
|
||||
|
||||
function pickRandom(list) {
|
||||
type MockPayeeEntity = PayeeEntity & { bill?: boolean };
|
||||
|
||||
function pickRandom<T>(list: T[]): T {
|
||||
return list[Math.floor(random() * list.length) % list.length];
|
||||
}
|
||||
|
||||
function number(start, end) {
|
||||
function number(start: number, end: number) {
|
||||
return start + (end - start) * random();
|
||||
}
|
||||
|
||||
function integer(start, end) {
|
||||
function integer(start: number, end: number) {
|
||||
return Math.round(number(start, end));
|
||||
}
|
||||
|
||||
function findMin(items, field) {
|
||||
function findMin<T, K extends keyof T>(items: T[], field: K) {
|
||||
let item = items[0];
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (items[i][field] < item[field]) {
|
||||
@@ -39,17 +41,20 @@ function findMin(items, field) {
|
||||
return item;
|
||||
}
|
||||
|
||||
function getStartingBalanceCat(categories) {
|
||||
function getStartingBalanceCat(categories: CategoryGroupEntity[]) {
|
||||
return categories.find(c => c.name === 'Starting Balances').id;
|
||||
}
|
||||
|
||||
function extractCommonThings(payees, groups) {
|
||||
function extractCommonThings(
|
||||
payees: MockPayeeEntity[],
|
||||
groups: CategoryGroupEntity[],
|
||||
) {
|
||||
const incomePayee = payees.find(p => p.name === 'Deposit');
|
||||
const expensePayees = payees.filter(
|
||||
p => p.name !== 'Deposit' && p.name !== 'Starting Balance',
|
||||
);
|
||||
const expenseGroup = groups.find(g => g.is_income === 0);
|
||||
const incomeGroup = groups.find(g => g.is_income === 1);
|
||||
const expenseGroup = groups.find(g => !g.is_income);
|
||||
const incomeGroup = groups.find(g => g.is_income);
|
||||
const categories = expenseGroup.categories.filter(
|
||||
c =>
|
||||
[
|
||||
@@ -73,7 +78,12 @@ function extractCommonThings(payees, groups) {
|
||||
};
|
||||
}
|
||||
|
||||
async function fillPrimaryChecking(handlers, account, payees, groups) {
|
||||
async function fillPrimaryChecking(
|
||||
handlers,
|
||||
account,
|
||||
payees: MockPayeeEntity[],
|
||||
groups: CategoryGroupEntity[],
|
||||
) {
|
||||
const {
|
||||
incomePayee,
|
||||
expensePayees,
|
||||
@@ -107,7 +117,7 @@ async function fillPrimaryChecking(handlers, account, payees, groups) {
|
||||
amount = integer(0, random() < 0.05 ? -8000 : -700);
|
||||
}
|
||||
|
||||
const transaction: TransactionEntity = {
|
||||
const transaction: NewTransactionEntity = {
|
||||
amount,
|
||||
payee: payee.id,
|
||||
account: account.id,
|
||||
@@ -129,7 +139,7 @@ async function fillPrimaryChecking(handlers, account, payees, groups) {
|
||||
amount: transaction.amount - a * 2,
|
||||
category: pick(),
|
||||
},
|
||||
] as TransactionEntity[];
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,7 +401,7 @@ async function fillOther(handlers, account, payees, groups) {
|
||||
const numTransactions = integer(3, 6);
|
||||
const category = incomeGroup.categories.find(c => c.name === 'Income');
|
||||
|
||||
const transactions: TransactionEntity[] = [
|
||||
const transactions: NewTransactionEntity[] = [
|
||||
{
|
||||
amount: integer(3250, 3700) * 100 * 100,
|
||||
payee: payees.find(p => p.name === 'Starting Balance').id,
|
||||
@@ -585,7 +595,7 @@ export async function createTestBudget(handlers) {
|
||||
}),
|
||||
);
|
||||
|
||||
const payees: Array<PayeeEntity & { bill?: boolean }> = [
|
||||
const payees: Array<MockPayeeEntity> = [
|
||||
{ name: 'Starting Balance' },
|
||||
{ name: 'Kroger' },
|
||||
{ name: 'Publix' },
|
||||
|
||||
@@ -11,7 +11,11 @@ import {
|
||||
getApproxNumberThreshold,
|
||||
} from '../../shared/rules';
|
||||
import { partitionByField, fastSetMerge } from '../../shared/util';
|
||||
import { type RuleActionEntity, type RuleEntity } from '../../types/models';
|
||||
import {
|
||||
type TransactionEntity,
|
||||
type RuleActionEntity,
|
||||
type RuleEntity,
|
||||
} from '../../types/models';
|
||||
import { schemaConfig } from '../aql';
|
||||
import * as db from '../db';
|
||||
import { getMappings } from '../db/mappings';
|
||||
@@ -682,7 +686,7 @@ export async function updateCategoryRules(transactions) {
|
||||
|
||||
// Also look 180 days in the future to get any future transactions
|
||||
// (this might change when we think about scheduled transactions)
|
||||
let register = await db.all(
|
||||
let register: TransactionEntity[] = await db.all(
|
||||
`SELECT t.* FROM v_transactions t
|
||||
LEFT JOIN accounts a ON a.id = t.account
|
||||
WHERE date >= ? AND date <= ? AND is_parent = 0 AND a.closed = 0
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { type TransactionEntity } from '../types/models';
|
||||
|
||||
import { last, diffItems, applyChanges } from './util';
|
||||
|
||||
export function isPreviewId(id) {
|
||||
@@ -80,8 +82,8 @@ function getSplit(transactions, parentIndex) {
|
||||
return split;
|
||||
}
|
||||
|
||||
export function ungroupTransactions(transactions) {
|
||||
const x = transactions.reduce((list, parent) => {
|
||||
export function ungroupTransactions(transactions: TransactionEntity[]) {
|
||||
return transactions.reduce<TransactionEntity[]>((list, parent) => {
|
||||
const { subtransactions, ...trans } = parent;
|
||||
const _subtransactions = subtransactions || [];
|
||||
|
||||
@@ -92,25 +94,31 @@ export function ungroupTransactions(transactions) {
|
||||
}
|
||||
return list;
|
||||
}, []);
|
||||
return x;
|
||||
}
|
||||
|
||||
function groupTransaction(split) {
|
||||
return { ...split[0], subtransactions: split.slice(1) };
|
||||
}
|
||||
|
||||
export function ungroupTransaction(split) {
|
||||
export function ungroupTransaction(split: TransactionEntity | null) {
|
||||
if (split == null) {
|
||||
return null;
|
||||
}
|
||||
return ungroupTransactions([split]);
|
||||
}
|
||||
|
||||
export function applyTransactionDiff(groupedTrans, diff) {
|
||||
export function applyTransactionDiff(
|
||||
groupedTrans: Parameters<typeof ungroupTransaction>[0],
|
||||
diff: Parameters<typeof applyChanges>[0],
|
||||
) {
|
||||
return groupTransaction(applyChanges(diff, ungroupTransaction(groupedTrans)));
|
||||
}
|
||||
|
||||
function replaceTransactions(transactions, id, func) {
|
||||
function replaceTransactions(
|
||||
transactions: TransactionEntity[],
|
||||
id: string,
|
||||
func: (transaction: TransactionEntity) => TransactionEntity,
|
||||
) {
|
||||
const idx = transactions.findIndex(t => t.id === id);
|
||||
const trans = transactions[idx];
|
||||
const transactionsCopy = [...transactions];
|
||||
@@ -127,7 +135,9 @@ function replaceTransactions(transactions, id, func) {
|
||||
}
|
||||
|
||||
const split = getSplit(transactions, parentIndex);
|
||||
let grouped = func(groupTransaction(split));
|
||||
let grouped: TransactionEntity | { id: string; _deleted: boolean } = func(
|
||||
groupTransaction(split),
|
||||
);
|
||||
const newSplit = ungroupTransaction(grouped);
|
||||
|
||||
let diff;
|
||||
@@ -159,7 +169,10 @@ function replaceTransactions(transactions, id, func) {
|
||||
}
|
||||
}
|
||||
|
||||
export function addSplitTransaction(transactions, id) {
|
||||
export function addSplitTransaction(
|
||||
transactions: TransactionEntity[],
|
||||
id: string,
|
||||
) {
|
||||
return replaceTransactions(transactions, id, trans => {
|
||||
if (!trans.is_parent) {
|
||||
return trans;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export function last(arr) {
|
||||
export function last<T>(arr: Array<T>) {
|
||||
return arr[arr.length - 1];
|
||||
}
|
||||
|
||||
@@ -33,7 +33,14 @@ export function hasFieldsChanged(obj1, obj2, fields) {
|
||||
return changed;
|
||||
}
|
||||
|
||||
export function applyChanges(changes, items) {
|
||||
export function applyChanges<T extends { id: string }>(
|
||||
changes: {
|
||||
added?: T[];
|
||||
updated?: T[];
|
||||
deleted?: T[];
|
||||
},
|
||||
items: T[],
|
||||
) {
|
||||
items = [...items];
|
||||
|
||||
if (changes.added) {
|
||||
@@ -64,7 +71,7 @@ export function applyChanges(changes, items) {
|
||||
return items;
|
||||
}
|
||||
|
||||
export function partitionByField(data, field) {
|
||||
export function partitionByField<T, K extends keyof T>(data: T[], field: K) {
|
||||
const res = new Map();
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const item = data[i];
|
||||
@@ -93,7 +100,7 @@ export function groupBy<T, K extends keyof T>(data: T[], field: K) {
|
||||
// `Map` is better, but we can't swap it out because `Map` has a
|
||||
// different API and we need to go through and update everywhere that
|
||||
// uses it.
|
||||
function _groupById(data) {
|
||||
function _groupById<T extends { id: string }>(data: T[]) {
|
||||
const res = new Map();
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const item = data[i];
|
||||
@@ -102,7 +109,7 @@ function _groupById(data) {
|
||||
return res;
|
||||
}
|
||||
|
||||
export function diffItems(items, newItems) {
|
||||
export function diffItems<T extends { id: string }>(items: T[], newItems: T[]) {
|
||||
const grouped = _groupById(items);
|
||||
const newGrouped = _groupById(newItems);
|
||||
const added = [];
|
||||
@@ -127,7 +134,7 @@ export function diffItems(items, newItems) {
|
||||
return { added, updated, deleted };
|
||||
}
|
||||
|
||||
export function groupById(data) {
|
||||
export function groupById<T extends { id: string }>(data: T[]) {
|
||||
const res = {};
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const item = data[i];
|
||||
@@ -168,7 +175,7 @@ export function getIn(map, keys) {
|
||||
return item;
|
||||
}
|
||||
|
||||
export function fastSetMerge(set1, set2) {
|
||||
export function fastSetMerge<T>(set1: Set<T>, set2: Set<T>) {
|
||||
const finalSet = new Set(set1);
|
||||
const iter = set2.values();
|
||||
let value = iter.next();
|
||||
@@ -214,7 +221,13 @@ export function setNumberFormat(config: typeof numberFormatConfig) {
|
||||
numberFormatConfig = config;
|
||||
}
|
||||
|
||||
export function getNumberFormat({ format, hideFraction } = numberFormatConfig) {
|
||||
export function getNumberFormat({
|
||||
format,
|
||||
hideFraction,
|
||||
}: {
|
||||
format: NumberFormats;
|
||||
hideFraction: boolean;
|
||||
} = numberFormatConfig) {
|
||||
let locale, regex, separator;
|
||||
|
||||
switch (format) {
|
||||
@@ -282,11 +295,14 @@ export function safeNumber(value: number) {
|
||||
return value;
|
||||
}
|
||||
|
||||
export function toRelaxedNumber(value) {
|
||||
export function toRelaxedNumber(value: string) {
|
||||
return integerToAmount(currencyToInteger(value) || 0);
|
||||
}
|
||||
|
||||
export function integerToCurrency(n, formatter = getNumberFormat().formatter) {
|
||||
export function integerToCurrency(
|
||||
n: number,
|
||||
formatter = getNumberFormat().formatter,
|
||||
) {
|
||||
return formatter.format(safeNumber(n) / 100);
|
||||
}
|
||||
|
||||
@@ -294,7 +310,7 @@ export function amountToCurrency(n) {
|
||||
return getNumberFormat().formatter.format(n);
|
||||
}
|
||||
|
||||
export function currencyToAmount(str) {
|
||||
export function currencyToAmount(str: string) {
|
||||
const amount = parseFloat(
|
||||
str
|
||||
.replace(getNumberFormat().regex, '')
|
||||
@@ -303,12 +319,12 @@ export function currencyToAmount(str) {
|
||||
return isNaN(amount) ? null : amount;
|
||||
}
|
||||
|
||||
export function currencyToInteger(str) {
|
||||
export function currencyToInteger(str: string) {
|
||||
const amount = currencyToAmount(str);
|
||||
return amount == null ? null : amountToInteger(amount);
|
||||
}
|
||||
|
||||
export function stringToInteger(str) {
|
||||
export function stringToInteger(str: string) {
|
||||
const amount = parseInt(str.replace(/[^-0-9.,]/g, ''));
|
||||
if (!isNaN(amount)) {
|
||||
return amount;
|
||||
@@ -316,7 +332,7 @@ export function stringToInteger(str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function amountToInteger(n) {
|
||||
export function amountToInteger(n: number) {
|
||||
return Math.round(n * 100);
|
||||
}
|
||||
|
||||
@@ -328,12 +344,12 @@ export function integerToAmount(n) {
|
||||
// financial files and we don't want to parse based on the user's
|
||||
// number format, because the user could be importing from many
|
||||
// currencies. We extract out the numbers and just ignore separators.
|
||||
export function looselyParseAmount(amount) {
|
||||
function safeNumber(v) {
|
||||
export function looselyParseAmount(amount: string) {
|
||||
function safeNumber(v: number): null | number {
|
||||
return isNaN(v) ? null : v;
|
||||
}
|
||||
|
||||
function extractNumbers(v) {
|
||||
function extractNumbers(v: string): string {
|
||||
return v.replace(/[^0-9-]/g, '');
|
||||
}
|
||||
|
||||
|
||||
@@ -3,15 +3,14 @@ import type { CategoryEntity } from './category';
|
||||
import type { PayeeEntity } from './payee';
|
||||
import type { ScheduleEntity } from './schedule';
|
||||
|
||||
export interface TransactionEntity {
|
||||
id?: string;
|
||||
export interface NewTransactionEntity {
|
||||
is_parent?: boolean;
|
||||
is_child?: boolean;
|
||||
parent_id?: string;
|
||||
account: AccountEntity;
|
||||
category?: CategoryEntity;
|
||||
account: string;
|
||||
category?: string;
|
||||
amount: number;
|
||||
payee?: PayeeEntity;
|
||||
payee?: string;
|
||||
notes?: string;
|
||||
date: string;
|
||||
imported_id?: string;
|
||||
@@ -22,6 +21,15 @@ export interface TransactionEntity {
|
||||
cleared?: boolean;
|
||||
reconciled?: boolean;
|
||||
tombstone?: boolean;
|
||||
schedule?: string;
|
||||
subtransactions?: Omit<NewTransactionEntity, 'account' | 'date'>[];
|
||||
}
|
||||
|
||||
export interface TransactionEntity extends NewTransactionEntity {
|
||||
id: string;
|
||||
account: AccountEntity;
|
||||
category?: CategoryEntity;
|
||||
payee?: PayeeEntity;
|
||||
schedule?: ScheduleEntity;
|
||||
subtransactions?: TransactionEntity[];
|
||||
}
|
||||
|
||||
6
upcoming-release-notes/2023.md
Normal file
6
upcoming-release-notes/2023.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Maintenance
|
||||
authors: [MatissJanis]
|
||||
---
|
||||
|
||||
Added more strict typings to `utils.ts` and some of its dependencies
|
||||
Reference in New Issue
Block a user