1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
use crate::broker_statement::BrokerStatement;
use crate::config::Config;
use crate::core::EmptyResult;
use crate::currency::converter::CurrencyConverter;
use crate::db;
use crate::localities::Jurisdiction;

pub use self::statement::TaxStatement;

mod dividends;
mod interest;
mod statement;
mod tax_agent;
mod trades;

pub fn generate_tax_statement(
    config: &Config, portfolio_name: &str, year: Option<i32>, tax_statement_path: Option<&str>
) -> EmptyResult {
    let country = config.get_tax_country();
    let portfolio = config.get_portfolio(portfolio_name)?;
    let broker = portfolio.broker.get_info(config, portfolio.plan.as_ref())?;

    let broker_statement = BrokerStatement::read(
        broker, &portfolio.statements, &portfolio.symbol_remapping, &portfolio.instrument_names,
        portfolio.get_tax_remapping()?, true)?;

    if let Some(year) = year {
        broker_statement.check_period_against_tax_year(year)?;
    }

    let mut tax_statement = match tax_statement_path {
        Some(path) => {
            let year = year.ok_or("Tax year must be specified when tax statement is specified")?;

            let statement = TaxStatement::read(path)?;
            if statement.year != year {
                return Err!("Tax statement year ({}) doesn't match the requested year {}",
                            statement.year, year);
            }

            Some(statement)
        },
        None => None,
    };

    let database = db::connect(&config.db_path)?;
    let converter = CurrencyConverter::new(database, None, true);

    let trades_tax = trades::process_income(
        &country, &portfolio, &broker_statement, year, tax_statement.as_mut(), &converter,
    ).map_err(|e| format!("Failed to process income from stock trading: {}", e))?;

    let dividends_tax = dividends::process_income(
        &country, &broker_statement, year, tax_statement.as_mut(), &converter,
    ).map_err(|e| format!("Failed to process dividend income: {}", e))?;

    let interest_tax = interest::process_income(
        &country, &broker_statement, year, tax_statement.as_mut(), &converter,
    ).map_err(|e| format!("Failed to process income from idle cash interest: {}", e))?;

    if broker_statement.broker.type_.jurisdiction() == Jurisdiction::Russia {
        if let Some(mut total_tax) = trades_tax {
            total_tax.add_assign(dividends_tax).unwrap();
            total_tax.add_assign(interest_tax).unwrap();
            tax_agent::process_tax_agent_withholdings(&broker_statement, year, total_tax);
        }
    }

    if let Some(ref tax_statement) = tax_statement {
        tax_statement.save()?;
    }

    Ok(())
}