/*
DrCr: Web-based double-entry bookkeeping framework
Copyright (C) 2022-2025 Lee Yingtong Li (RunasSudo)
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
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
use std::fmt::Display;
use crate::util::sofy_from_eofy;
use super::{
calculator::ReportingGraphDependencies, DateArgs, DateEofyArgs, DateStartDateEndArgs,
ReportingContext, ReportingProductId, ReportingProductKind, ReportingStep, ReportingStepArgs,
ReportingStepId,
};
pub fn register_lookup_fns(context: &mut ReportingContext) {
context.register_lookup_fn(
"AllTransactionsExceptRetainedEarnings",
&[ReportingProductKind::BalancesAt],
AllTransactionsExceptRetainedEarnings::takes_args,
|a| {
AllTransactionsExceptRetainedEarnings::from_args(&[ReportingProductKind::BalancesAt], a)
},
);
context.register_lookup_fn(
"AllTransactionsExceptRetainedEarnings",
&[ReportingProductKind::BalancesBetween],
AllTransactionsExceptRetainedEarnings::takes_args,
|a| {
AllTransactionsExceptRetainedEarnings::from_args(
&[ReportingProductKind::BalancesBetween],
a,
)
},
);
context.register_lookup_fn(
"AllTransactionsIncludingRetainedEarnings",
&[ReportingProductKind::BalancesAt],
AllTransactionsIncludingRetainedEarnings::takes_args,
|a| {
AllTransactionsIncludingRetainedEarnings::from_args(
&[ReportingProductKind::BalancesAt],
a,
)
},
);
context.register_lookup_fn(
"AllTransactionsIncludingRetainedEarnings",
&[ReportingProductKind::BalancesBetween],
AllTransactionsIncludingRetainedEarnings::takes_args,
|a| {
AllTransactionsIncludingRetainedEarnings::from_args(
&[ReportingProductKind::BalancesBetween],
a,
)
},
);
context.register_lookup_fn(
"CalculateIncomeTax",
&[ReportingProductKind::Transactions],
CalculateIncomeTax::takes_args,
CalculateIncomeTax::from_args,
);
context.register_lookup_fn(
"CombineOrdinaryTransactions",
&[ReportingProductKind::BalancesAt],
CombineOrdinaryTransactions::takes_args,
CombineOrdinaryTransactions::from_args,
);
context.register_lookup_fn(
"DBBalances",
&[ReportingProductKind::BalancesAt],
DBBalances::takes_args,
DBBalances::from_args,
);
context.register_lookup_fn(
"PostUnreconciledStatementLines",
&[ReportingProductKind::Transactions],
PostUnreconciledStatementLines::takes_args,
PostUnreconciledStatementLines::from_args,
);
}
#[derive(Debug)]
pub struct AllTransactionsExceptRetainedEarnings {
pub product_kinds: &'static [ReportingProductKind], // Must have single member
pub args: Box,
}
impl AllTransactionsExceptRetainedEarnings {
fn takes_args(_args: &Box) -> bool {
true
}
fn from_args(
product_kinds: &'static [ReportingProductKind],
args: Box,
) -> Box {
Box::new(AllTransactionsExceptRetainedEarnings {
product_kinds,
args,
})
}
}
impl Display for AllTransactionsExceptRetainedEarnings {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{}", self.id()))
}
}
impl ReportingStep for AllTransactionsExceptRetainedEarnings {
fn id(&self) -> ReportingStepId {
ReportingStepId {
name: "AllTransactionsExceptRetainedEarnings",
product_kinds: self.product_kinds,
args: self.args.clone(),
}
}
}
#[derive(Debug)]
pub struct AllTransactionsIncludingRetainedEarnings {
pub product_kinds: &'static [ReportingProductKind], // Must have single member
pub args: Box,
}
impl AllTransactionsIncludingRetainedEarnings {
fn takes_args(_args: &Box) -> bool {
true
}
fn from_args(
product_kinds: &'static [ReportingProductKind],
args: Box,
) -> Box {
Box::new(AllTransactionsIncludingRetainedEarnings {
product_kinds,
args,
})
}
}
impl Display for AllTransactionsIncludingRetainedEarnings {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{}", self.id()))
}
}
impl ReportingStep for AllTransactionsIncludingRetainedEarnings {
fn id(&self) -> ReportingStepId {
ReportingStepId {
name: "AllTransactionsIncludingRetainedEarnings",
product_kinds: self.product_kinds,
args: self.args.clone(),
}
}
fn requires(&self) -> Vec {
vec![ReportingProductId {
name: "AllTransactionsExceptRetainedEarnings",
kind: self.product_kinds[0],
args: self.args.clone(),
}]
}
}
#[derive(Debug)]
pub struct CalculateIncomeTax {
pub args: DateEofyArgs,
}
impl CalculateIncomeTax {
fn takes_args(args: &Box) -> bool {
args.is::()
}
fn from_args(args: Box) -> Box {
Box::new(CalculateIncomeTax {
args: *args.downcast().unwrap(),
})
}
}
impl Display for CalculateIncomeTax {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{}", self.id()))
}
}
impl ReportingStep for CalculateIncomeTax {
fn id(&self) -> ReportingStepId {
ReportingStepId {
name: "CalculateIncomeTax",
product_kinds: &[ReportingProductKind::Transactions],
args: Box::new(self.args.clone()),
}
}
fn requires(&self) -> Vec {
// CalculateIncomeTax depends on CombineOrdinaryTransactions
vec![ReportingProductId {
name: "CombineOrdinaryTransactions",
kind: ReportingProductKind::BalancesBetween,
args: Box::new(DateStartDateEndArgs {
date_start: sofy_from_eofy(self.args.date_eofy),
date_end: self.args.date_eofy.clone(),
}),
}]
}
fn after_init_graph(
&self,
steps: &Vec>,
dependencies: &mut ReportingGraphDependencies,
) {
for other in steps {
if let Some(other) = other.downcast_ref::() {
// AllTransactionsExceptRetainedEarnings (in applicable periods) depends on CalculateIncomeTax
if other.args.is::() {
let other_args = other.args.downcast_ref::().unwrap();
if other_args.date >= self.args.date_eofy {
dependencies.add_dependency(
other.id(),
ReportingProductId {
name: self.id().name,
kind: other.product_kinds[0],
args: other.id().args,
},
);
}
} else if other.args.is::() {
let other_args = other.args.downcast_ref::().unwrap();
if other_args.date_start <= self.args.date_eofy
&& other_args.date_end >= self.args.date_eofy
{
dependencies.add_dependency(
other.id(),
ReportingProductId {
name: self.id().name,
kind: other.product_kinds[0],
args: other.id().args,
},
);
}
} else {
unreachable!();
}
}
}
}
}
#[derive(Debug)]
pub struct CombineOrdinaryTransactions {
pub args: DateArgs,
}
impl CombineOrdinaryTransactions {
fn takes_args(args: &Box) -> bool {
args.is::()
}
fn from_args(args: Box) -> Box {
Box::new(CombineOrdinaryTransactions {
args: *args.downcast().unwrap(),
})
}
}
impl Display for CombineOrdinaryTransactions {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{}", self.id()))
}
}
impl ReportingStep for CombineOrdinaryTransactions {
fn id(&self) -> ReportingStepId {
ReportingStepId {
name: "CombineOrdinaryTransactions",
product_kinds: &[ReportingProductKind::BalancesAt],
args: Box::new(self.args.clone()),
}
}
fn requires(&self) -> Vec {
vec![
// CombineOrdinaryTransactions depends on DBBalances
ReportingProductId {
name: "DBBalances",
kind: ReportingProductKind::BalancesAt,
args: Box::new(self.args.clone()),
},
// CombineOrdinaryTransactions depends on PostUnreconciledStatementLines
ReportingProductId {
name: "PostUnreconciledStatementLines",
kind: ReportingProductKind::BalancesAt,
args: Box::new(self.args.clone()),
},
]
}
}
#[derive(Debug)]
pub struct DBBalances {
pub args: DateArgs,
}
impl DBBalances {
fn takes_args(args: &Box) -> bool {
args.is::()
}
fn from_args(args: Box) -> Box {
Box::new(DBBalances {
args: *args.downcast().unwrap(),
})
}
}
impl Display for DBBalances {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{}", self.id()))
}
}
impl ReportingStep for DBBalances {
fn id(&self) -> ReportingStepId {
ReportingStepId {
name: "DBBalances",
product_kinds: &[ReportingProductKind::BalancesAt],
args: Box::new(self.args.clone()),
}
}
}
#[derive(Debug)]
pub struct PostUnreconciledStatementLines {
pub args: DateArgs,
}
impl PostUnreconciledStatementLines {
fn takes_args(args: &Box) -> bool {
args.is::()
}
fn from_args(args: Box) -> Box {
Box::new(PostUnreconciledStatementLines {
args: *args.downcast().unwrap(),
})
}
}
impl Display for PostUnreconciledStatementLines {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{}", self.id()))
}
}
impl ReportingStep for PostUnreconciledStatementLines {
fn id(&self) -> ReportingStepId {
ReportingStepId {
name: "PostUnreconciledStatementLines",
product_kinds: &[ReportingProductKind::Transactions],
args: Box::new(self.args.clone()),
}
}
}