Fix multiple logic errors when reporting for not current year
This commit is contained in:
parent
3cfcdf6778
commit
f4c232ae35
16
src/main.rs
16
src/main.rs
@ -29,6 +29,8 @@ use libdrcr::reporting::types::{
|
||||
};
|
||||
|
||||
fn main() {
|
||||
const YEAR: i32 = 2023;
|
||||
|
||||
// Connect to database
|
||||
let db_connection = DbConnection::connect("sqlite:drcr_testing.db");
|
||||
|
||||
@ -55,7 +57,7 @@ fn main() {
|
||||
kind: ReportingProductKind::Generic,
|
||||
args: Box::new(MultipleDateArgs {
|
||||
dates: vec![DateArgs {
|
||||
date: NaiveDate::from_ymd_opt(2025, 6, 30).unwrap(),
|
||||
date: NaiveDate::from_ymd_opt(YEAR, 6, 30).unwrap(),
|
||||
}],
|
||||
}),
|
||||
},
|
||||
@ -77,8 +79,8 @@ fn main() {
|
||||
name: "AllTransactionsExceptEarningsToEquity",
|
||||
kind: ReportingProductKind::BalancesBetween,
|
||||
args: Box::new(DateStartDateEndArgs {
|
||||
date_start: NaiveDate::from_ymd_opt(2024, 7, 1).unwrap(),
|
||||
date_end: NaiveDate::from_ymd_opt(2025, 6, 30).unwrap(),
|
||||
date_start: NaiveDate::from_ymd_opt(YEAR - 1, 7, 1).unwrap(),
|
||||
date_end: NaiveDate::from_ymd_opt(YEAR, 6, 30).unwrap(),
|
||||
}),
|
||||
},
|
||||
];
|
||||
@ -89,8 +91,8 @@ fn main() {
|
||||
name: "AllTransactionsExceptEarningsToEquity",
|
||||
kind: ReportingProductKind::BalancesBetween,
|
||||
args: Box::new(DateStartDateEndArgs {
|
||||
date_start: NaiveDate::from_ymd_opt(2024, 7, 1).unwrap(),
|
||||
date_end: NaiveDate::from_ymd_opt(2025, 6, 30).unwrap(),
|
||||
date_start: NaiveDate::from_ymd_opt(YEAR - 1, 7, 1).unwrap(),
|
||||
date_end: NaiveDate::from_ymd_opt(YEAR, 6, 30).unwrap(),
|
||||
}),
|
||||
})
|
||||
.unwrap();
|
||||
@ -111,7 +113,7 @@ fn main() {
|
||||
kind: ReportingProductKind::Generic,
|
||||
args: Box::new(MultipleDateArgs {
|
||||
dates: vec![DateArgs {
|
||||
date: NaiveDate::from_ymd_opt(2025, 6, 30).unwrap(),
|
||||
date: NaiveDate::from_ymd_opt(YEAR, 6, 30).unwrap(),
|
||||
}],
|
||||
}),
|
||||
},
|
||||
@ -124,7 +126,7 @@ fn main() {
|
||||
kind: ReportingProductKind::Generic,
|
||||
args: Box::new(MultipleDateArgs {
|
||||
dates: vec![DateArgs {
|
||||
date: NaiveDate::from_ymd_opt(2025, 6, 30).unwrap(),
|
||||
date: NaiveDate::from_ymd_opt(YEAR, 6, 30).unwrap(),
|
||||
}],
|
||||
}),
|
||||
})
|
||||
|
@ -364,11 +364,15 @@ impl UpdateBalancesAt {
|
||||
fn can_build(
|
||||
name: &'static str,
|
||||
kind: ReportingProductKind,
|
||||
_args: &Box<dyn ReportingStepArgs>,
|
||||
args: &Box<dyn ReportingStepArgs>,
|
||||
steps: &Vec<Box<dyn ReportingStep>>,
|
||||
dependencies: &ReportingGraphDependencies,
|
||||
context: &ReportingContext,
|
||||
) -> bool {
|
||||
if !args.is::<DateArgs>() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for Transactions -> BalancesAt
|
||||
if kind == ReportingProductKind::BalancesAt {
|
||||
// Initially no need to check args
|
||||
@ -391,18 +395,13 @@ impl UpdateBalancesAt {
|
||||
&& dependencies_for_step[0].product.kind
|
||||
== ReportingProductKind::BalancesBetween
|
||||
{
|
||||
let date_end = dependencies_for_step[0]
|
||||
.product
|
||||
.args
|
||||
.downcast_ref::<DateStartDateEndArgs>()
|
||||
.unwrap()
|
||||
.date_end;
|
||||
|
||||
match has_step_or_can_build(
|
||||
&ReportingProductId {
|
||||
name: dependencies_for_step[0].product.name,
|
||||
kind: ReportingProductKind::BalancesAt,
|
||||
args: Box::new(DateArgs { date: date_end }),
|
||||
args: Box::new(DateArgs {
|
||||
date: args.downcast_ref::<DateArgs>().unwrap().date,
|
||||
}),
|
||||
},
|
||||
steps,
|
||||
dependencies,
|
||||
@ -477,6 +476,27 @@ impl ReportingStep for UpdateBalancesAt {
|
||||
args: parent_step.id().args.clone(),
|
||||
},
|
||||
);
|
||||
|
||||
// Look up the BalancesAt step
|
||||
let dependencies_for_step = dependencies.dependencies_for_step(&parent_step.id());
|
||||
let dependency = &dependencies_for_step[0].product; // Existence and uniqueness checked in can_build
|
||||
|
||||
if dependency.kind == ReportingProductKind::BalancesAt {
|
||||
// Directly depends on BalancesAt -> Transaction
|
||||
// Do not need to add extra dependencies
|
||||
} else {
|
||||
// As checked in can_build, must depend on BalancesBetween -> Transaction with a BalancesAt available
|
||||
dependencies.add_dependency(
|
||||
self.id(),
|
||||
ReportingProductId {
|
||||
name: dependency.name,
|
||||
kind: ReportingProductKind::BalancesAt,
|
||||
args: Box::new(DateArgs {
|
||||
date: self.args.date,
|
||||
}),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn execute(
|
||||
@ -522,17 +542,13 @@ impl ReportingStep for UpdateBalancesAt {
|
||||
.unwrap();
|
||||
} else {
|
||||
// As checked in can_build, must depend on BalancesBetween -> Transaction with a BalancesAt available
|
||||
let date_end = dependency
|
||||
.args
|
||||
.downcast_ref::<DateStartDateEndArgs>()
|
||||
.unwrap()
|
||||
.date_end;
|
||||
|
||||
opening_balances_at = products
|
||||
.get_or_err(&ReportingProductId {
|
||||
name: dependency.name,
|
||||
kind: ReportingProductKind::BalancesAt,
|
||||
args: Box::new(DateArgs { date: date_end }),
|
||||
args: Box::new(DateArgs {
|
||||
date: self.args.date,
|
||||
}),
|
||||
})?
|
||||
.downcast_ref()
|
||||
.unwrap();
|
||||
@ -542,7 +558,12 @@ impl ReportingStep for UpdateBalancesAt {
|
||||
let mut balances = BalancesAt {
|
||||
balances: opening_balances_at.balances.clone(),
|
||||
};
|
||||
update_balances_from_transactions(&mut balances.balances, transactions.iter());
|
||||
update_balances_from_transactions(
|
||||
&mut balances.balances,
|
||||
transactions
|
||||
.iter()
|
||||
.filter(|t| t.transaction.dt.date() <= self.args.date),
|
||||
);
|
||||
|
||||
// Store result
|
||||
products.insert(
|
||||
@ -706,7 +727,13 @@ impl ReportingStep for UpdateBalancesBetween {
|
||||
let mut balances = BalancesBetween {
|
||||
balances: opening_balances.clone(),
|
||||
};
|
||||
update_balances_from_transactions(&mut balances.balances, transactions.iter());
|
||||
update_balances_from_transactions(
|
||||
&mut balances.balances,
|
||||
transactions.iter().filter(|t| {
|
||||
t.transaction.dt.date() >= self.args.date_start
|
||||
&& t.transaction.dt.date() <= self.args.date_end
|
||||
}),
|
||||
);
|
||||
|
||||
// Store result
|
||||
products.insert(
|
||||
|
@ -28,7 +28,7 @@ use crate::reporting::types::{BalancesAt, DateStartDateEndArgs, ReportingProduct
|
||||
use crate::transaction::{
|
||||
update_balances_from_transactions, Posting, Transaction, TransactionWithPostings,
|
||||
};
|
||||
use crate::util::sofy_from_eofy;
|
||||
use crate::util::{get_eofy, sofy_from_eofy};
|
||||
use crate::QuantityInt;
|
||||
|
||||
use super::calculator::ReportingGraphDependencies;
|
||||
@ -37,9 +37,8 @@ use super::dynamic_report::{
|
||||
};
|
||||
use super::executor::ReportingExecutionError;
|
||||
use super::types::{
|
||||
BalancesBetween, DateArgs, MultipleDateArgs, ReportingContext, ReportingProduct,
|
||||
ReportingProductKind, ReportingProducts, ReportingStep, ReportingStepArgs, ReportingStepId,
|
||||
VoidArgs,
|
||||
BalancesBetween, DateArgs, MultipleDateArgs, ReportingContext, ReportingProductKind,
|
||||
ReportingProducts, ReportingStep, ReportingStepArgs, ReportingStepId, VoidArgs,
|
||||
};
|
||||
|
||||
/// Call [ReportingContext::register_lookup_fn] for all steps provided by this module
|
||||
@ -113,6 +112,15 @@ impl ReportingStep for AllTransactionsExceptEarningsToEquity {
|
||||
}
|
||||
}
|
||||
|
||||
fn requires(&self, _context: &ReportingContext) -> Vec<ReportingProductId> {
|
||||
// AllTransactionsExceptEarningsToEquity always depends on CombineOrdinaryTransactions at least
|
||||
vec![ReportingProductId {
|
||||
name: "CombineOrdinaryTransactions",
|
||||
kind: self.product_kinds[0],
|
||||
args: self.args.clone(),
|
||||
}]
|
||||
}
|
||||
|
||||
fn execute(
|
||||
&self,
|
||||
_context: &ReportingContext,
|
||||
@ -142,30 +150,11 @@ impl ReportingStep for AllTransactionsExceptEarningsToEquity {
|
||||
}
|
||||
}
|
||||
|
||||
// No dependencies?! - store empty result
|
||||
let product: Box<dyn ReportingProduct> = match self.product_kinds[0] {
|
||||
ReportingProductKind::Transactions => Box::new(Transactions {
|
||||
transactions: Vec::new(),
|
||||
}),
|
||||
ReportingProductKind::BalancesAt => Box::new(BalancesAt {
|
||||
balances: HashMap::new(),
|
||||
}),
|
||||
ReportingProductKind::BalancesBetween => Box::new(BalancesBetween {
|
||||
balances: HashMap::new(),
|
||||
}),
|
||||
ReportingProductKind::Generic => panic!("Requested AllTransactionsExceptEarningsToEquity.Generic but no available dependencies to provide it"),
|
||||
};
|
||||
|
||||
products.insert(
|
||||
ReportingProductId {
|
||||
name: self.id().name,
|
||||
kind: product_kind,
|
||||
args: self.args.clone(),
|
||||
},
|
||||
product,
|
||||
// No dependencies?! - this is likely a mistake
|
||||
panic!(
|
||||
"Requested {:?} but no available dependencies to provide it",
|
||||
self.product_kinds[0]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -675,7 +664,7 @@ impl ReportingStep for CombineOrdinaryTransactions {
|
||||
}
|
||||
}
|
||||
|
||||
/// Transfer current year balances in income and expense accounts to the current year earnings equity account
|
||||
/// Transfer year-to-date balances in income and expense accounts (as at the requested date) to the current year earnings equity account
|
||||
#[derive(Debug)]
|
||||
pub struct CurrentYearEarningsToEquity {
|
||||
pub args: DateArgs,
|
||||
@ -718,13 +707,15 @@ impl ReportingStep for CurrentYearEarningsToEquity {
|
||||
}
|
||||
|
||||
fn requires(&self, context: &ReportingContext) -> Vec<ReportingProductId> {
|
||||
let eofy_date = get_eofy(&self.args.date, &context.eofy_date);
|
||||
|
||||
// CurrentYearEarningsToEquity depends on AllTransactionsExceptEarningsToEquity
|
||||
vec![ReportingProductId {
|
||||
name: "AllTransactionsExceptEarningsToEquity",
|
||||
kind: ReportingProductKind::BalancesBetween,
|
||||
args: Box::new(DateStartDateEndArgs {
|
||||
date_start: sofy_from_eofy(context.eofy_date),
|
||||
date_end: context.eofy_date.clone(),
|
||||
date_start: sofy_from_eofy(eofy_date),
|
||||
date_end: eofy_date,
|
||||
}),
|
||||
}]
|
||||
}
|
||||
@ -736,14 +727,16 @@ impl ReportingStep for CurrentYearEarningsToEquity {
|
||||
_dependencies: &ReportingGraphDependencies,
|
||||
products: &mut ReportingProducts,
|
||||
) -> Result<(), ReportingExecutionError> {
|
||||
let eofy_date = get_eofy(&self.args.date, &context.eofy_date);
|
||||
|
||||
// Get balances for this financial year
|
||||
let balances = products
|
||||
.get_or_err(&ReportingProductId {
|
||||
name: "AllTransactionsExceptEarningsToEquity",
|
||||
kind: ReportingProductKind::BalancesBetween,
|
||||
args: Box::new(DateStartDateEndArgs {
|
||||
date_start: sofy_from_eofy(context.eofy_date),
|
||||
date_end: context.eofy_date.clone(),
|
||||
date_start: sofy_from_eofy(eofy_date),
|
||||
date_end: eofy_date,
|
||||
}),
|
||||
})?
|
||||
.downcast_ref::<BalancesBetween>()
|
||||
@ -767,7 +760,7 @@ impl ReportingStep for CurrentYearEarningsToEquity {
|
||||
transactions.transactions.push(TransactionWithPostings {
|
||||
transaction: Transaction {
|
||||
id: None,
|
||||
dt: context.eofy_date.and_hms_opt(0, 0, 0).unwrap(),
|
||||
dt: eofy_date.and_hms_opt(0, 0, 0).unwrap(),
|
||||
description: "Current year earnings".to_string(),
|
||||
},
|
||||
postings: vec![
|
||||
@ -985,15 +978,15 @@ impl ReportingStep for RetainedEarningsToEquity {
|
||||
}
|
||||
|
||||
fn requires(&self, context: &ReportingContext) -> Vec<ReportingProductId> {
|
||||
let eofy_date = get_eofy(&self.args.date, &context.eofy_date);
|
||||
let last_eofy_date = eofy_date.with_year(eofy_date.year() - 1).unwrap();
|
||||
|
||||
// RetainedEarningsToEquity depends on CombineOrdinaryTransactions for last financial year
|
||||
vec![ReportingProductId {
|
||||
name: "CombineOrdinaryTransactions",
|
||||
kind: ReportingProductKind::BalancesAt,
|
||||
args: Box::new(DateArgs {
|
||||
date: context
|
||||
.eofy_date
|
||||
.with_year(context.eofy_date.year() - 1)
|
||||
.unwrap(),
|
||||
date: last_eofy_date,
|
||||
}),
|
||||
}]
|
||||
}
|
||||
@ -1005,12 +998,10 @@ impl ReportingStep for RetainedEarningsToEquity {
|
||||
_dependencies: &ReportingGraphDependencies,
|
||||
products: &mut ReportingProducts,
|
||||
) -> Result<(), ReportingExecutionError> {
|
||||
// Get balances at end of last financial year
|
||||
let last_eofy_date = context
|
||||
.eofy_date
|
||||
.with_year(context.eofy_date.year() - 1)
|
||||
.unwrap();
|
||||
let eofy_date = get_eofy(&self.args.date, &context.eofy_date);
|
||||
let last_eofy_date = eofy_date.with_year(eofy_date.year() - 1).unwrap();
|
||||
|
||||
// Get balances at end of last financial year
|
||||
let balances_last_eofy = products
|
||||
.get_or_err(&ReportingProductId {
|
||||
name: "CombineOrdinaryTransactions",
|
||||
|
18
src/util.rs
18
src/util.rs
@ -18,9 +18,23 @@
|
||||
|
||||
use chrono::{Datelike, NaiveDate};
|
||||
|
||||
/// Return the end date of the current financial year for the given date
|
||||
pub fn get_eofy(date: &NaiveDate, eofy_date: &NaiveDate) -> NaiveDate {
|
||||
let date_eofy = eofy_date.with_year(date.year()).unwrap();
|
||||
if date_eofy >= *date {
|
||||
date_eofy
|
||||
} else {
|
||||
date_eofy.with_year(date_eofy.year() + 1).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the start date of the financial year, given the end date of the financial year
|
||||
pub fn sofy_from_eofy(date_eofy: NaiveDate) -> NaiveDate {
|
||||
date_eofy.with_year(date_eofy.year() - 1).unwrap().succ_opt().unwrap()
|
||||
pub fn sofy_from_eofy(eofy_date: NaiveDate) -> NaiveDate {
|
||||
eofy_date
|
||||
.with_year(eofy_date.year() - 1)
|
||||
.unwrap()
|
||||
.succ_opt()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Format the [NaiveDate] as a string
|
||||
|
Loading…
x
Reference in New Issue
Block a user