Compare commits

..

47 Commits

Author SHA1 Message Date
0b3770fb95 Add 'libdrcr/' from commit '364c44d60a3cc0ee93ed52925aa5f1109636f93c'
git-subtree-dir: libdrcr
git-subtree-mainline: 4364af9b9a67a88a35da031a76d1a335af36c30b
git-subtree-split: 364c44d60a3cc0ee93ed52925aa5f1109636f93c
2025-05-28 00:37:35 +10:00
364c44d60a
Restore account links to dynamic reports 2025-05-28 00:33:54 +10:00
4364af9b9a
Account transactions view using libdrcr 2025-05-28 00:26:12 +10:00
20773c4640
Implement getting balance assertions from database 2025-05-28 00:25:47 +10:00
233c6d6aa9
Implement getting transactions from database 2025-05-27 23:48:40 +10:00
c9c3bc0d2c
Implement trial balance report 2025-05-27 22:19:36 +10:00
b938176b5f
Implement PostUnreconciledStatementLines 2025-05-27 18:26:13 +10:00
dfdd3b0924
Only calculate YTD figures in CurrentEarningsToEquity 2025-05-27 17:36:39 +10:00
930213c461
Fix Spacer being dropped when DynamicReport is calculated 2025-05-27 17:31:11 +10:00
1b67df61be
Execute reporting steps in parallel 2025-05-27 17:28:34 +10:00
53497e7593
Fix UpdateBalancesBetween using incorrect period 2025-05-27 17:28:22 +10:00
835af70bc7
Sanity check reporting products 2025-05-27 16:16:15 +10:00
706d26e54f
Make reporting API async 2025-05-27 16:02:28 +10:00
df8ec39e1e
Cache database metadata 2025-05-27 00:53:26 +10:00
5430c6713f
Implement IncomeStatement step 2025-05-27 00:21:30 +10:00
d44c2a1200
Refactor DynamicReport to use RefCell<DynamicReportEntry>
Allows calculations to refer to the results of previous calculations
Rather than the same cloned DynamicReport being passed to all calculations
2025-05-27 00:21:17 +10:00
42eaa015bd
Add ids for total rows in balance sheet 2025-05-26 23:07:18 +10:00
9bb9eaabaf
Fix multiple logic errors when reporting for not current year 2025-05-26 22:43:59 +10:00
faa53c625c
Implement JSON serialisation for DynamicReport 2025-05-25 01:23:35 +10:00
1dcb31df57
Update documentation 2025-05-25 01:20:37 +10:00
f3ad696168
Remove unused dependency from Cargo.toml 2025-05-25 01:20:30 +10:00
f76d2a5736
Implement formal BalanceSheet report 2025-05-24 21:07:18 +10:00
fed7def6f3
CurrentYearEarningsToEquity depends on AllTransactionsExceptEarningsToEquity 2025-05-24 14:32:01 +10:00
34fd8233cf
Refactor steps_for_targets to accept Vec<ReportingProductId> 2025-05-24 01:01:03 +10:00
8f1903e532
Refactor build_step_for_product into standalone function
Prepare for changing steps_for_targets to take list of products.
2025-05-24 00:51:24 +10:00
407974e440
Validate dynamic builder outputs 2025-05-24 00:43:40 +10:00
35d397f5c9
Add function to visualise dependency tree via graphviz 2025-05-24 00:40:27 +10:00
38014b7c91
Implement CurrentYearEarningsToEquity 2025-05-24 00:10:37 +10:00
4ba1317fce
Implement RetainedEarningsToEquity 2025-05-23 23:54:36 +10:00
9fe7bf22a6
Basic implementation of DBBalances 2025-05-22 00:26:29 +10:00
412b79ee45
Statically require single member for AllTransactionsExceptRetainedEarnings.product_kinds 2025-05-21 22:29:18 +10:00
4e94557370
Update documentation 2025-05-21 22:26:40 +10:00
798c7d3c07
Refactor update_balances_from_transactions 2025-05-21 21:53:35 +10:00
bfb41d8d15
Stub implementations for all steps 2025-05-21 21:48:57 +10:00
7f188db677
Refactor register_lookup_fns and register_dynamic_builders for readability 2025-05-21 20:22:06 +10:00
0f8e3e5d4a
Basic framework for executing reports 2025-05-21 20:15:18 +10:00
ae26b64d5e
Refactoring and documentation 2025-05-21 19:59:57 +10:00
37e9e19c5e
Rename Dependency.dependency to Dependency.product 2025-05-21 19:18:14 +10:00
58758b0cb3
Implement RetainedEarningsToEquity 2025-05-21 18:24:59 +10:00
349ecf3d76
Fix off by one error in BalancesAtToBalancesBetween 2025-05-21 18:24:29 +10:00
1e33074b4d
Refactor AllTransactionsIncludingRetainedEarnings 2025-05-21 18:20:19 +10:00
161acabb7d
Refactor CalculateIncomeTax 2025-05-21 18:10:08 +10:00
5a1b54f782
Implement UpdateBalancesAt 2025-05-21 17:58:42 +10:00
61ed6f82d7
Implement Display for ReportingStep 2025-05-21 17:16:25 +10:00
39617a54ac
Implement GenerateBalances dynamic builder 2025-05-21 17:11:20 +10:00
de890aeade
Refactor representation of ReportingStep args 2025-05-21 16:39:18 +10:00
0ee500af3e
Basic dependency resolution code 2025-05-21 00:39:54 +10:00
7 changed files with 12 additions and 59 deletions

BIN
libdrcr/drcr_testing.db Normal file

Binary file not shown.

View File

@ -199,7 +199,7 @@ impl DbConnection {
).map(|r: SqliteRow| StatementLine { ).map(|r: SqliteRow| StatementLine {
id: Some(r.get("id")), id: Some(r.get("id")),
source_account: r.get("source_account"), source_account: r.get("source_account"),
dt: NaiveDateTime::parse_from_str(r.get("dt"), "%Y-%m-%d %H:%M:%S.%6f").expect("Invalid statement_lines.dt"), dt: NaiveDateTime::parse_from_str(r.get("dt"), "%Y-%m-%d").expect("Invalid statement_lines.dt"),
description: r.get("description"), description: r.get("description"),
quantity: r.get("quantity"), quantity: r.get("quantity"),
balance: r.get("balance"), balance: r.get("balance"),

View File

@ -397,12 +397,7 @@ impl ReportingStep for GenerateBalances {
let mut balances = BalancesAt { let mut balances = BalancesAt {
balances: HashMap::new(), balances: HashMap::new(),
}; };
update_balances_from_transactions( update_balances_from_transactions(&mut balances.balances, transactions.iter());
&mut balances.balances,
transactions
.iter()
.filter(|t| t.transaction.dt.date() <= self.args.date),
);
// Store result // Store result
let mut result = ReportingProducts::new(); let mut result = ReportingProducts::new();

View File

@ -1,6 +1,6 @@
<!-- <!--
DrCr: Web-based double-entry bookkeeping framework DrCr: Web-based double-entry bookkeeping framework
Copyright (C) 2022-2025 Lee Yingtong Li (RunasSudo) Copyright (C) 20222025 Lee Yingtong Li (RunasSudo)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by it under the terms of the GNU Affero General Public License as published by
@ -62,7 +62,7 @@
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3"> <div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<span class="text-gray-500">{{ db.metadata.reporting_commodity }}</span> <span class="text-gray-500">{{ db.metadata.reporting_commodity }}</span>
</div> </div>
<input type="text" class="bordered-field pl-7" v-model="posting.amount_abs" @input="onAmountChange(posting)"> <input type="text" class="bordered-field pl-7" v-model="posting.amount_abs">
</div> </div>
</td> </td>
<td class="amount-cr py-1 pl-1"></td> <td class="amount-cr py-1 pl-1"></td>
@ -74,7 +74,7 @@
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3"> <div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<span class="text-gray-500">{{ db.metadata.reporting_commodity }}</span> <span class="text-gray-500">{{ db.metadata.reporting_commodity }}</span>
</div> </div>
<input type="text" class="bordered-field pl-7" v-model="posting.amount_abs" @input="onAmountChange(posting)"> <input type="text" class="bordered-field pl-7" v-model="posting.amount_abs">
</div> </div>
</td> </td>
</template> </template>
@ -312,15 +312,4 @@
await getCurrentWindow().close(); await getCurrentWindow().close();
} }
async function onAmountChange(posting: EditingPosting) {
// Synchronise the amounts if only two postings
if (transaction.postings.length == 2) {
for (const otherPosting of transaction.postings) {
if (otherPosting !== posting) {
otherPosting.amount_abs = posting.amount_abs;
}
}
}
}
</script> </script>

View File

@ -48,8 +48,8 @@
<td class="py-0.5 px-1 text-gray-900 text-end">{{ pp(Math.abs(assertion.quantity)) }}</td> <td class="py-0.5 px-1 text-gray-900 text-end">{{ pp(Math.abs(assertion.quantity)) }}</td>
<td class="py-0.5 pr-1 text-gray-900">{{ assertion.quantity >= 0 ? 'Dr' : 'Cr' }}</td> <td class="py-0.5 pr-1 text-gray-900">{{ assertion.quantity >= 0 ? 'Dr' : 'Cr' }}</td>
<td class="py-0.5 px-1 text-gray-900"> <td class="py-0.5 px-1 text-gray-900">
<CheckIcon class="w-4 h-4" v-if="assertion.is_valid === true" /> <CheckIcon class="w-4 h-4" v-if="assertion.is_valid" />
<XMarkIcon class="w-4 h-4 text-red-500" v-if="assertion.is_valid === false" /> <XMarkIcon class="w-4 h-4 text-red-500" v-if="!assertion.is_valid" />
</td> </td>
<td class="py-0.5 pl-1 text-gray-900 text-end"> <td class="py-0.5 pl-1 text-gray-900 text-end">
<a :href="'/balance-assertions/edit/' + assertion.id" class="text-gray-500 hover:text-gray-700" onclick="return openLinkInNewWindow(this);"> <a :href="'/balance-assertions/edit/' + assertion.id" class="text-gray-500 hover:text-gray-700" onclick="return openLinkInNewWindow(this);">
@ -68,7 +68,6 @@
import { invoke } from '@tauri-apps/api/core'; import { invoke } from '@tauri-apps/api/core';
import { ref } from 'vue'; import { ref } from 'vue';
import { db } from '../db.ts';
import { pp } from '../display.ts'; import { pp } from '../display.ts';
const balanceAssertions = ref([] as ValidatedBalanceAssertion[]); const balanceAssertions = ref([] as ValidatedBalanceAssertion[]);
@ -84,15 +83,6 @@
} }
async function load() { async function load() {
// Since validating the assertions takes a while, first load them from database
const session = await db.load();
balanceAssertions.value = await session.select(
`SELECT id, dt, description, account, quantity, commodity, NULL as is_valid
FROM balance_assertions
ORDER BY dt DESC, id DESC`
);
// Then validate them in the background
balanceAssertions.value = JSON.parse(await invoke('get_validated_balance_assertions')); balanceAssertions.value = JSON.parse(await invoke('get_validated_balance_assertions'));
} }

View File

@ -1,6 +1,6 @@
<!-- <!--
DrCr: Web-based double-entry bookkeeping framework DrCr: Web-based double-entry bookkeeping framework
Copyright (C) 2022-2025 Lee Yingtong Li (RunasSudo) Copyright (C) 20222025 Lee Yingtong Li (RunasSudo)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by it under the terms of the GNU Affero General Public License as published by
@ -63,7 +63,6 @@
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { PencilIcon, PlusIcon } from '@heroicons/vue/24/outline'; import { PencilIcon, PlusIcon } from '@heroicons/vue/24/outline';
import { invoke } from '@tauri-apps/api/core'; import { invoke } from '@tauri-apps/api/core';
import { UnlistenFn, listen } from '@tauri-apps/api/event';
import { onUnmounted, ref, watch } from 'vue'; import { onUnmounted, ref, watch } from 'vue';
import { Transaction } from '../db.ts'; import { Transaction } from '../db.ts';
@ -152,20 +151,9 @@
load(); load();
// Refresh transaction list when transaction updated
let unlistenTransactionUpdated: UnlistenFn | null = null;
(async () => {
// Cannot await at top level without <Suspense> therefore do this in an async function
unlistenTransactionUpdated = await listen('transaction-updated', async (_event) => { await load(); });
})();
onUnmounted(() => { onUnmounted(() => {
if (clusterize !== null) { if (clusterize !== null) {
clusterize.destroy(); clusterize.destroy();
} }
if (unlistenTransactionUpdated !== null) {
unlistenTransactionUpdated();
}
}); });
</script> </script>

View File

@ -1,6 +1,6 @@
<!-- <!--
DrCr: Web-based double-entry bookkeeping framework DrCr: Web-based double-entry bookkeeping framework
Copyright (C) 2022-2025 Lee Yingtong Li (RunasSudo) Copyright (C) 20222025 Lee Yingtong Li (RunasSudo)
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by it under the terms of the GNU Affero General Public License as published by
@ -60,10 +60,12 @@
<script setup lang="ts"> <script setup lang="ts">
import Clusterize from 'clusterize.js'; import Clusterize from 'clusterize.js';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { PencilIcon } from '@heroicons/vue/24/outline'; import { PencilIcon } from '@heroicons/vue/24/outline';
import { PlusIcon } from '@heroicons/vue/16/solid'; import { PlusIcon } from '@heroicons/vue/16/solid';
import { UnlistenFn, listen } from '@tauri-apps/api/event';
import { onUnmounted, ref, watch } from 'vue'; import { onUnmounted, ref, watch } from 'vue';
import { JoinedTransactionPosting, Transaction, db, joinedToTransactions } from '../db.ts'; import { JoinedTransactionPosting, Transaction, db, joinedToTransactions } from '../db.ts';
@ -155,20 +157,9 @@
load(); load();
// Refresh transaction list when transaction updated
let unlistenTransactionUpdated: UnlistenFn | null = null;
(async () => {
// Cannot await at top level without <Suspense> therefore do this in an async function
unlistenTransactionUpdated = await listen('transaction-updated', async (_event) => { await load(); });
})();
onUnmounted(() => { onUnmounted(() => {
if (clusterize !== null) { if (clusterize !== null) {
clusterize.destroy(); clusterize.destroy();
} }
if (unlistenTransactionUpdated !== null) {
unlistenTransactionUpdated();
}
}); });
</script> </script>