Balance assertions view using libdrcr
This commit is contained in:
parent
bbcb3cee6f
commit
ffef2d16dc
@ -94,6 +94,7 @@ pub fn run() {
|
|||||||
libdrcr_bridge::get_balance_sheet,
|
libdrcr_bridge::get_balance_sheet,
|
||||||
libdrcr_bridge::get_income_statement,
|
libdrcr_bridge::get_income_statement,
|
||||||
libdrcr_bridge::get_trial_balance,
|
libdrcr_bridge::get_trial_balance,
|
||||||
|
libdrcr_bridge::get_validated_balance_assertions,
|
||||||
sql::sql_transaction_begin,
|
sql::sql_transaction_begin,
|
||||||
sql::sql_transaction_execute,
|
sql::sql_transaction_execute,
|
||||||
sql::sql_transaction_select,
|
sql::sql_transaction_select,
|
||||||
|
@ -16,19 +16,22 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
use libdrcr::db::DbConnection;
|
use libdrcr::db::DbConnection;
|
||||||
|
use libdrcr::model::assertions::BalanceAssertion;
|
||||||
use libdrcr::reporting::builders::register_dynamic_builders;
|
use libdrcr::reporting::builders::register_dynamic_builders;
|
||||||
use libdrcr::reporting::dynamic_report::DynamicReport;
|
use libdrcr::reporting::dynamic_report::DynamicReport;
|
||||||
use libdrcr::reporting::generate_report;
|
use libdrcr::reporting::generate_report;
|
||||||
use libdrcr::reporting::steps::register_lookup_fns;
|
use libdrcr::reporting::steps::register_lookup_fns;
|
||||||
use libdrcr::reporting::types::{
|
use libdrcr::reporting::types::{
|
||||||
DateArgs, DateStartDateEndArgs, MultipleDateArgs, MultipleDateStartDateEndArgs,
|
BalancesAt, DateArgs, DateStartDateEndArgs, MultipleDateArgs, MultipleDateStartDateEndArgs,
|
||||||
ReportingContext, ReportingProduct, ReportingProductId, ReportingProductKind, Transactions,
|
ReportingContext, ReportingProduct, ReportingProductId, ReportingProductKind, Transactions,
|
||||||
VoidArgs,
|
VoidArgs,
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use tauri::State;
|
use tauri::State;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
@ -163,3 +166,88 @@ pub(crate) async fn get_trial_balance(
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.to_json())
|
.to_json())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
struct ValidatedBalanceAssertion {
|
||||||
|
#[serde(flatten)]
|
||||||
|
assertion: BalanceAssertion,
|
||||||
|
is_valid: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub(crate) async fn get_validated_balance_assertions(
|
||||||
|
state: State<'_, Mutex<AppState>>,
|
||||||
|
) -> Result<String, ()> {
|
||||||
|
let state = state.lock().await;
|
||||||
|
let db_filename = state.db_filename.clone().unwrap();
|
||||||
|
|
||||||
|
// Connect to database
|
||||||
|
let db_connection =
|
||||||
|
DbConnection::new(format!("sqlite:{}", db_filename.as_str()).as_str()).await;
|
||||||
|
|
||||||
|
let reporting_commodity = db_connection.metadata().reporting_commodity.clone(); // Needed later
|
||||||
|
|
||||||
|
// First get balance assertions from database
|
||||||
|
let balance_assertions = db_connection.get_balance_assertions().await;
|
||||||
|
|
||||||
|
// Get dates of balance assertions
|
||||||
|
let dates = balance_assertions
|
||||||
|
.iter()
|
||||||
|
.map(|b| b.dt)
|
||||||
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
|
// Initialise ReportingContext
|
||||||
|
let eofy_date = db_connection.metadata().eofy_date;
|
||||||
|
let mut context = ReportingContext::new(db_connection, eofy_date, "$".to_string());
|
||||||
|
register_lookup_fns(&mut context);
|
||||||
|
register_dynamic_builders(&mut context);
|
||||||
|
|
||||||
|
// Get report targets
|
||||||
|
let mut targets = vec![ReportingProductId {
|
||||||
|
name: "CalculateIncomeTax",
|
||||||
|
kind: ReportingProductKind::Transactions,
|
||||||
|
args: Box::new(VoidArgs {}),
|
||||||
|
}];
|
||||||
|
for dt in dates {
|
||||||
|
// Request ordinary transaction balances at each balance assertion date
|
||||||
|
targets.push(ReportingProductId {
|
||||||
|
name: "CombineOrdinaryTransactions",
|
||||||
|
kind: ReportingProductKind::BalancesAt,
|
||||||
|
args: Box::new(DateArgs { date: dt.date() }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run report
|
||||||
|
let products = generate_report(targets, Arc::new(context)).await.unwrap();
|
||||||
|
|
||||||
|
// Validate each balance assertion
|
||||||
|
let mut validated_assertions = Vec::new();
|
||||||
|
for balance_assertion in balance_assertions {
|
||||||
|
let balances_at_date = products
|
||||||
|
.get_or_err(&ReportingProductId {
|
||||||
|
name: "CombineOrdinaryTransactions",
|
||||||
|
kind: ReportingProductKind::BalancesAt,
|
||||||
|
args: Box::new(DateArgs {
|
||||||
|
date: balance_assertion.dt.date(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
.downcast_ref::<BalancesAt>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let account_balance = *balances_at_date
|
||||||
|
.balances
|
||||||
|
.get(&balance_assertion.account)
|
||||||
|
.unwrap_or(&0);
|
||||||
|
|
||||||
|
let is_valid = balance_assertion.quantity == account_balance
|
||||||
|
&& balance_assertion.commodity == reporting_commodity;
|
||||||
|
|
||||||
|
validated_assertions.push(ValidatedBalanceAssertion {
|
||||||
|
assertion: balance_assertion,
|
||||||
|
is_valid,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(serde_json::to_string(&validated_assertions).unwrap())
|
||||||
|
}
|
||||||
|
@ -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) 2022-2025 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
|
||||||
@ -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.isValid" />
|
<CheckIcon class="w-4 h-4" v-if="assertion.is_valid" />
|
||||||
<XMarkIcon class="w-4 h-4 text-red-500" v-if="!assertion.isValid" />
|
<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);">
|
||||||
@ -63,14 +63,12 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
import { ref } from 'vue';
|
|
||||||
|
|
||||||
import { db } from '../db.ts';
|
|
||||||
import { pp } from '../display.ts';
|
|
||||||
import { ReportingStage, ReportingWorkflow } from '../reporting.ts';
|
|
||||||
import { CheckIcon, PencilIcon, XMarkIcon } from '@heroicons/vue/24/outline';
|
import { CheckIcon, PencilIcon, XMarkIcon } from '@heroicons/vue/24/outline';
|
||||||
import { PlusIcon } from '@heroicons/vue/16/solid';
|
import { PlusIcon } from '@heroicons/vue/16/solid';
|
||||||
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import { pp } from '../display.ts';
|
||||||
|
|
||||||
const balanceAssertions = ref([] as ValidatedBalanceAssertion[]);
|
const balanceAssertions = ref([] as ValidatedBalanceAssertion[]);
|
||||||
|
|
||||||
@ -81,42 +79,11 @@
|
|||||||
account: string,
|
account: string,
|
||||||
quantity: number,
|
quantity: number,
|
||||||
commodity: string,
|
commodity: string,
|
||||||
isValid: boolean,
|
is_valid: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
const session = await db.load();
|
balanceAssertions.value = JSON.parse(await invoke('get_validated_balance_assertions'));
|
||||||
|
|
||||||
const rawBalanceAssertions: any[] = await session.select(
|
|
||||||
`SELECT *
|
|
||||||
FROM balance_assertions
|
|
||||||
ORDER BY dt DESC, id DESC`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Get transactions
|
|
||||||
const reportingWorkflow = new ReportingWorkflow();
|
|
||||||
await reportingWorkflow.generate(session);
|
|
||||||
const transactions = reportingWorkflow.getTransactionsAtStage(ReportingStage.OrdinaryAPITransactions);
|
|
||||||
|
|
||||||
for (const balanceAssertion of rawBalanceAssertions) {
|
|
||||||
// Check assertion status
|
|
||||||
const balanceAssertionDt = dayjs(balanceAssertion.dt);
|
|
||||||
|
|
||||||
let accountBalance = 0;
|
|
||||||
for (const transaction of transactions) {
|
|
||||||
if (dayjs(transaction.dt) <= balanceAssertionDt) {
|
|
||||||
for (const posting of transaction.postings) {
|
|
||||||
if (posting.account === balanceAssertion.account) {
|
|
||||||
accountBalance += posting.quantity_ascost!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
balanceAssertion.isValid = balanceAssertion.quantity === accountBalance && balanceAssertion.commodity === db.metadata.reporting_commodity;
|
|
||||||
}
|
|
||||||
|
|
||||||
balanceAssertions.value = rawBalanceAssertions as ValidatedBalanceAssertion[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
load();
|
load();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user