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