Skip to content

Commit

Permalink
Set child transactions sort_order
Browse files Browse the repository at this point in the history
  • Loading branch information
joel-jeremy committed Dec 6, 2023
1 parent d798e82 commit 50e63ea
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 19 deletions.
39 changes: 31 additions & 8 deletions packages/loot-core/src/server/accounts/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ async function getTransactionsByIds(
);
}

type BatchedTransaction = {
id: string;
account?: string;
category?: string;
payee?: string;
schedule?: string;
} & Omit<
TransactionEntity,
'id' | 'account' | 'category' | 'payee' | 'schedule'
>;

export async function batchUpdateTransactions({
added,
deleted,
Expand All @@ -42,14 +53,9 @@ export async function batchUpdateTransactions({
detectOrphanPayees = true,
runTransfers = true,
}: {
added?: Array<{ id: string; payee: unknown; category: unknown }>;
deleted?: Array<{ id: string; payee: unknown }>;
updated?: Array<{
id: string;
payee?: unknown;
account?: unknown;
category?: unknown;
}>;
added?: BatchedTransaction[];
deleted?: BatchedTransaction[];
updated?: BatchedTransaction[];
learnCategories?: boolean;
detectOrphanPayees?: boolean;
runTransfers?: boolean;
Expand Down Expand Up @@ -80,6 +86,7 @@ export async function batchUpdateTransactions({
// and makes bulk updates much faster
await batchMessages(async () => {
if (added) {
added = assignChildSortOrders(added);
addedIds = await Promise.all(
added.map(async t => db.insertTransaction(t)),
);
Expand Down Expand Up @@ -184,3 +191,19 @@ export async function batchUpdateTransactions({
updated: runTransfers ? transfersUpdated : resultUpdated,
};
}

function assignChildSortOrders(transactions: BatchedTransaction[]) {
const childSortOrderPerParent = Object.fromEntries(
transactions.map(t => [t.parent_id, 0]),
);

function nextChildSortOrder(parentId) {
return ++childSortOrderPerParent[parentId];
}

// Set relative sort order of children
return transactions.map(t => ({
...t,
sort_order: t.parent_id ? -nextChildSortOrder(t.parent_id) : undefined,
}));
}
45 changes: 39 additions & 6 deletions packages/loot-core/src/server/db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ import {
} from '../models';
import { sendMessages, batchMessages } from '../sync';

import { shoveSortOrders, SORT_INCREMENT } from './sort';
import {
shoveSortOrders,
SORT_INCREMENT,
TRANSACTION_SORT_INCREMENT,
} from './sort';

export { toDateRepr, fromDateRepr } from '../models';

Expand Down Expand Up @@ -628,15 +632,22 @@ export async function getTransactions(accountId) {

export async function insertTransaction(transaction) {
const lastTransaction = await first(
`SELECT sort_order FROM v_transactions WHERE date = ? ORDER BY sort_order DESC LIMIT 1`,
'SELECT sort_order FROM v_transactions ' +
'WHERE date = ? AND is_child <> 1 AND parent_id IS NULL ' +
'ORDER BY sort_order DESC LIMIT 1',
[transaction.date.replace(/-/g, '')], // Remove hyphens
);
const sort_order =
(lastTransaction ? lastTransaction.sort_order : 0) + SORT_INCREMENT;

// Child transactions have relative sort orders.
const isChild = transaction.is_child || transaction.parent_id;
const sortIncrement =
TRANSACTION_SORT_INCREMENT +
(isChild && transaction.sort_order ? transaction.sort_order : 0);

transaction = {
...transaction,
sort_order: sort_order,
sort_order:
(lastTransaction ? lastTransaction.sort_order : 0) + sortIncrement,
};

return insertWithSchema('transactions', transaction);
Expand All @@ -653,11 +664,33 @@ export async function moveTransaction(id, accountId, targetId) {
[accountId, id],
);

const { updates, sort_order } = shoveSortOrders(transactions, targetId);
const { updates, sort_order } = shoveSortOrders(
transactions,
targetId,
TRANSACTION_SORT_INCREMENT,
);

for (let info of updates) {
await update('transactions', info);
moveSubtransactions(info.id, info.sort_order);
}
await update('transactions', { id, sort_order });
moveSubtransactions(id, sort_order);
}

async function moveSubtransactions(parentId, parentSortOrder) {
const subtransactions = await all(
'SELECT id FROM v_transactions WHERE parent_id = ? ORDER BY sort_order DESC',
[parentId],
);

for (let [index, sub] of subtransactions.entries()) {
const newIndex = index + 1;
await update('transactions', {
id: sub.id,
sort_order: parentSortOrder - newIndex,
});
}
}

export async function deleteTransaction(transaction) {
Expand Down
15 changes: 10 additions & 5 deletions packages/loot-core/src/server/db/sort.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const SORT_INCREMENT = 16384;
export const TRANSACTION_SORT_INCREMENT = 1024;

function midpoint(items, to) {
const below = items[to - 1];
Expand All @@ -13,7 +14,11 @@ function midpoint(items, to) {
}
}

export function shoveSortOrders(items, targetId?: string) {
export function shoveSortOrders(
items,
targetId?: string,
sortIncrement = SORT_INCREMENT,
) {
const to = items.findIndex(item => item.id === targetId);
const target = items[to];
const before = items[to - 1];
Expand All @@ -24,17 +29,17 @@ export function shoveSortOrders(items, targetId?: string) {
let order;
if (items.length > 0) {
// Add a new increment to whatever is the latest sort order
order = items[items.length - 1].sort_order + SORT_INCREMENT;
order = items[items.length - 1].sort_order + sortIncrement;
} else {
// If no items exist, the default is to use the first increment
order = SORT_INCREMENT;
order = sortIncrement;
}

return { updates, sort_order: order };
} else {
if (target.sort_order - (before ? before.sort_order : 0) <= 2) {
let next = to;
let order = Math.floor(items[next].sort_order) + SORT_INCREMENT;
let order = Math.floor(items[next].sort_order) + sortIncrement;
while (next < items.length) {
// No need to update it if it's already greater than the current
// order. This can happen because there may already be large
Expand All @@ -46,7 +51,7 @@ export function shoveSortOrders(items, targetId?: string) {
updates.push({ id: items[next].id, sort_order: order });

next++;
order += SORT_INCREMENT;
order += sortIncrement;
}
}

Expand Down

0 comments on commit 50e63ea

Please sign in to comment.