Improve ebase importer to handle currency conversions in sales

korrat
Jun 8, 2024, 4:46 PM
2AXMWXFHNL4UW2K4DBUGXA5FOS6N5F3EKWLLQGN7F3DC7BLF5TJAC

Dependencies

  • [2] 3VXVY2YU Use Self instead of typename
  • [3] 5XUAFMCK Enforce decimal precision for ebase importer
  • [4] RQIRG5BD Add english variant of decimal parser
  • [5] D6LJRTWX Add importers for ebase accounts
  • [6] UO34MAAG Refactor CSV-based Importers

Change contents

  • edit in importers/ebase/src/transactions.rs at line 10
    [4.317]
    [4.317]
    use beancount_types::Balance;
  • edit in importers/ebase/src/transactions.rs at line 27
    [4.781]
    [4.822]
    use tap::prelude::Tap;
  • edit in importers/ebase/src/transactions.rs at line 32
    [4.12061]
    [4.12061]
    pub mod opt {
    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<time::Date>, D::Error>
    where
    D: serde::Deserializer<'de>,
    {
    struct Visitor;
    impl<'de> serde::de::Visitor<'de> for Visitor {
    type Value = Option<time::Date>;
    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
    formatter.write_str("a date in dd.mm.yy format")
    }
    fn visit_none<E>(self) -> Result<Self::Value, E>
    where
    E: serde::de::Error,
    {
    Ok(None)
    }
    fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
    where
    D: serde::Deserializer<'de>,
    {
    super::deserialize(deserializer).map(Some)
    }
    }
    deserializer.deserialize_option(Visitor)
    }
    }
  • replacement in importers/ebase/src/transactions.rs at line 87
    [4.12895][4.12895:12967]()
    parsed.parse_items(v.as_bytes(), DATE_FORMAT).unwrap();
    [4.12895]
    [4.12967]
    parsed
    .parse_items(v.as_bytes(), DATE_FORMAT)
    .map_err(E::custom)?;
  • edit in importers/ebase/src/transactions.rs at line 282
    [4.7826][4.7826:7827](),[4.7827][4.13524:13648](),[4.13648][4.8058:8059](),[4.8058][4.8058:8059](),[4.8059][4.13649:13718](),[4.13718][4.8131:8132](),[4.8131][4.8131:8132](),[4.8132][4.13719:13808](),[4.13808][4.8422:8423](),[4.8422][4.8422:8423](),[4.8423][4.13809:13924]()
    let fund_currency = record.fund_currency;
    let share_price = Amount::new(record.share_price, fund_currency);
    let shares_amount = Amount::new(record.shares, record.isin);
    let payment_amount = Amount::new(record.payment_amount, record.payment_curreny);
    let investment_amount_payment =
    Amount::new(record.investment_amount, record.payment_curreny);
  • replacement in importers/ebase/src/transactions.rs at line 283
    [4.8649][4.13925:14156]()
    let investment_amount_fund = record.exchange_rate.map(|rate| {
    let mut amount = investment_amount_payment.amount * rate;
    amount.rescale(2);
    Amount::new(amount, fund_currency)
    });
    [4.8649]
    [4.8808]
    let mut directives = Vec::new();
  • edit in importers/ebase/src/transactions.rs at line 292
    [4.14436]
    [4.9125]
    TransactionKind::Distribution => {
    let distribution_amount =
    Amount::new(record.distribution_total, record.payment_currency);
    let balance_date = record.balance_date.unwrap();
    let main_account = self.config.account_template.render(&context);
    let balance_amount = Amount::new(record.balance_shares, record.isin);
    directives.push(Directive::from(Balance::new(
    balance_date,
    main_account,
    balance_amount,
    )));
    transaction.build_posting(
    self.config.reference_account.render(&context),
    |posting| {
    if record.payment_currency.as_ref() == "EUR" {
    posting.set_amount(distribution_amount);
    } else {
    let rate = record.exchange_rate_eur_payment.unwrap();
    let distribution_amount_eur = {
    let amount_eur =
    (distribution_amount.amount / rate).tap_mut(|amount| {
    amount.rescale(2);
    });
    Amount::new(amount_eur, Commodity::try_from("EUR").unwrap())
    };
    posting
    .set_amount(distribution_amount_eur)
    .set_price(Amount::new(rate, record.payment_currency));
    }
    },
    );
    transaction.build_posting(
    self.config.distributions_template.render(&context),
    |posting| {
    posting.set_amount(-distribution_amount);
    },
    );
    }
    TransactionKind::LumpSumTaxAdvance => {
    // No interesting information
    return Ok(vec![]);
    }
  • edit in importers/ebase/src/transactions.rs at line 342
    [4.9199]
    [4.9199]
    let fund_currency = record.fund_currency.unwrap();
    let share_price = Amount::new(record.share_price, fund_currency);
    let shares_amount = Amount::new(record.shares, record.isin);
    let payment_currency = record.payment_currency;
    let payment_amount = Amount::new(record.payment_amount, payment_currency);
    let investment_amount_payment =
    Amount::new(record.investment_amount, payment_currency);
    let investment_amount_fund = record.exchange_rate_payment_fund.map(|rate| {
    let mut amount = investment_amount_payment.amount * rate;
    amount.rescale(2);
    Amount::new(amount, fund_currency)
    });
  • replacement in importers/ebase/src/transactions.rs at line 367
    [4.14481][4.14481:14562]()
    let fees = Amount::new(record.fees, record.payment_curreny);
    [4.14481]
    [4.9696]
    let fees = Amount::new(record.fees, payment_currency);
  • replacement in importers/ebase/src/transactions.rs at line 391
    [4.10573][4.14563:14649]()
    let currency: &str = &record.payment_curreny;
    [4.10573]
    [4.10653]
    let currency: &str = &payment_currency;
  • edit in importers/ebase/src/transactions.rs at line 419
    [4.11931]
    [4.11931]
    let fund_currency = record.fund_currency.unwrap();
    let share_price = Amount::new(record.share_price, fund_currency);
    let shares_amount = Amount::new(record.shares, record.isin);
    let payment_currency = record.payment_currency;
    let sale_amount_payment = Amount::new(record.fees, payment_currency);
    let sale_amount_fund = record.exchange_rate_payment_fund.map(|rate| {
    let mut amount = sale_amount_payment.amount * rate;
    amount.rescale(2);
    Amount::new(amount, fund_currency)
    });
  • replacement in importers/ebase/src/transactions.rs at line 442
    [4.12421][4.14650:14748]()
    posting.set_amount(Amount::new(record.fees, record.payment_curreny));
    [4.12421]
    [4.12506]
    posting.set_amount(sale_amount_payment);
  • edit in importers/ebase/src/transactions.rs at line 449
    [4.12729]
    [4.12729]
    if let Some(sale_amount_fund) = sale_amount_fund {
    transaction
    .build_posting(
    self.config
    .currency_account
    .render(&CurrencyTemplateContext {
    currency: {
    let currency: &str = &payment_currency;
    <&Seg>::try_from(currency)
    .expect("commodities are valid segments")
    },
    }),
    |posting| {
    posting.set_amount(-sale_amount_payment);
    },
    )
    .build_posting(
    self.config
    .currency_account
    .render(&CurrencyTemplateContext {
    currency: {
    let currency: &str = &fund_currency;
    <&Seg>::try_from(currency)
    .expect("commodities are valid segments")
    },
    }),
    |posting| {
    posting.set_amount(sale_amount_fund);
    },
    );
    }
  • edit in importers/ebase/src/transactions.rs at line 484
    [4.12787]
    [4.12787]
    let fund_currency = record.fund_currency.unwrap();
    let share_price = Amount::new(record.share_price, fund_currency);
    let shares_amount = Amount::new(record.shares, record.isin);
    let payment_currency = record.payment_currency;
    let investment_amount_payment =
    Amount::new(record.investment_amount, payment_currency);
  • replacement in importers/ebase/src/transactions.rs at line 504
    [4.14793][4.14793:14874]()
    let fees = Amount::new(record.fees, record.payment_curreny);
    [4.14793]
    [4.13236]
    let fees = Amount::new(record.fees, payment_currency);
  • replacement in importers/ebase/src/transactions.rs at line 516
    [4.13688][4.13688:13761]()
    self.config.distributions_template.render(&context),
    [4.13688]
    [4.13761]
    self.config.reference_account.render(&context),
  • replacement in importers/ebase/src/transactions.rs at line 520
    [4.13866][4.14875:14944]()
    if record.payment_curreny != fund_currency {
    [4.13866]
    [4.13929]
    if payment_currency != fund_currency {
  • replacement in importers/ebase/src/transactions.rs at line 523
    [4.15000][4.15000:15047]()
    .exchange_rate
    [4.15000]
    [4.15047]
    .exchange_rate_payment_fund
  • edit in importers/ebase/src/transactions.rs at line 532
    [4.14359]
    [4.14359]
    let fund_currency = record.fund_currency.unwrap();
    let share_price = Amount::new(record.share_price, fund_currency);
    let shares_amount = Amount::new(record.shares, record.isin);
    let payment_currency = record.payment_currency;
    let investment_amount_payment =
    Amount::new(record.investment_amount, payment_currency);
  • replacement in importers/ebase/src/transactions.rs at line 552
    [4.15180][4.15180:15261]()
    let fees = Amount::new(record.fees, record.payment_curreny);
    [4.15180]
    [4.14808]
    let fees = Amount::new(record.fees, payment_currency);
  • replacement in importers/ebase/src/transactions.rs at line 568
    [4.15433][4.15262:15331]()
    if record.payment_curreny != fund_currency {
    [4.15433]
    [4.15496]
    if payment_currency != fund_currency {
  • replacement in importers/ebase/src/transactions.rs at line 571
    [4.15387][4.15387:15434]()
    .exchange_rate
    [4.15387]
    [4.15434]
    .exchange_rate_payment_fund
  • edit in importers/ebase/src/transactions.rs at line 580
    [4.15925]
    [4.15925]
    let fund_currency = record.fund_currency.unwrap();
    let share_price = Amount::new(record.share_price, fund_currency);
    let shares_amount = Amount::new(record.shares, record.isin);
    let payment_currency = record.payment_currency;
    let payment_amount = Amount::new(record.payment_amount, payment_currency);
    let investment_amount_payment =
    Amount::new(record.investment_amount, payment_currency);
    let investment_amount_fund = record.exchange_rate_payment_fund.map(|rate| {
    let mut amount = investment_amount_payment.amount * rate;
    amount.rescale(2);
    Amount::new(amount, fund_currency)
    });
  • replacement in importers/ebase/src/transactions.rs at line 606
    [4.15567][4.15567:15648]()
    let fees = Amount::new(record.fees, record.payment_curreny);
    [4.15567]
    [4.16374]
    let fees = Amount::new(record.fees, payment_currency);
  • replacement in importers/ebase/src/transactions.rs at line 630
    [4.17251][4.15649:15735]()
    let currency: &str = &record.payment_curreny;
    [4.17251]
    [4.17331]
    let currency: &str = &payment_currency;
  • replacement in importers/ebase/src/transactions.rs at line 657
    [4.18504][4.18504:18526](),[4.18526][4.15736:15833](),[4.15833][4.18654:18665](),[4.18654][4.18654:18665]()
    let price = {
    let date = record.price_date;
    Price::new(date, record.isin, share_price)
    };
    [4.18504]
    [4.18665]
    directives.push(Directive::from(transaction));
    if let Some(date) = record.price_date {
    let share_price = Amount::new(record.share_price, record.fund_currency.unwrap());
    directives.push(Directive::from(Price::new(date, record.isin, share_price)));
    }
  • replacement in importers/ebase/src/transactions.rs at line 663
    [4.18666][4.18666:18737]()
    Ok(vec![Directive::from(transaction), Directive::from(price)])
    [4.18666]
    [4.18737]
    Ok(directives)
  • edit in importers/ebase/src/transactions.rs at line 730
    [4.15902]
    [4.15902]
    #[serde(rename = "Bestandsdatum", with = "dmy_short::opt")]
    balance_date: Option<Date>,
    #[serde(
    rename = "Anteile zum Bestandsdatum",
    with = "decimal_parsers::german::serde::with_precision::<6>"
    )]
    balance_shares: Decimal,
  • edit in importers/ebase/src/transactions.rs at line 748
    [4.16116]
    [3.0]
    #[serde(rename = "Ertragswährung (EW)")]
    distribution_currency: Option<Commodity>,
  • edit in importers/ebase/src/transactions.rs at line 752
    [3.13]
    [3.13]
    rename = "Barausschüttung/Steuerliquidität in ZW",
    with = "decimal_parsers::german::serde::with_precision::<2>"
    )]
    distribution_total: Decimal,
    #[serde(
  • replacement in importers/ebase/src/transactions.rs at line 761
    [3.127][4.16200:16236](),[4.16200][4.16200:16236]()
    exchange_rate: Option<Decimal>,
    [3.127]
    [4.16236]
    exchange_rate_payment_fund: Option<Decimal>,
    #[serde(
    rename = "Devisenkurs (EUR/ZW)",
    with = "decimal_parsers::german::serde::opt::with_precision::<6>"
    )]
    exchange_rate_eur_payment: Option<Decimal>,
  • replacement in importers/ebase/src/transactions.rs at line 776
    [4.16415][4.16415:16445]()
    fund_currency: Commodity,
    [4.16415]
    [4.16445]
    fund_currency: Option<Commodity>,
  • replacement in importers/ebase/src/transactions.rs at line 797
    [4.16904][4.16904:16936]()
    payment_curreny: Commodity,
    [4.16904]
    [4.16936]
    payment_currency: Commodity,
  • replacement in importers/ebase/src/transactions.rs at line 799
    [4.16937][4.16937:17014]()
    #[serde(rename = "Kursdatum", with = "dmy_short")]
    price_date: Date,
    [4.16937]
    [4.17014]
    #[serde(rename = "Kursdatum", with = "dmy_short::opt")]
    price_date: Option<Date>,
  • edit in importers/ebase/src/transactions.rs at line 883
    [4.22441]
    [4.17406]
    #[serde(rename = "Fondsertrag (Ausschüttung)")]
    Distribution,
  • edit in importers/ebase/src/transactions.rs at line 887
    [4.22459]
    [4.17457]
    #[serde(rename = "Vorabpauschale")]
    LumpSumTaxAdvance,
  • edit in importers/ebase/src/transactions.rs at line 904
    [4.22657]
    [2.36]
    Self::Distribution => format!("Distribution of {fund}"),
  • edit in importers/ebase/src/transactions.rs at line 906
    [2.118]
    [2.118]
    Self::LumpSumTaxAdvance => format!("Lump-sum tax advance for {fund}"),
  • edit in importers/ebase/src/transactions.rs at line 923
    [4.23458]
    [4.23458]
    "Fondsertrag (Ausschüttung)" => Self::Distribution,
  • edit in importers/ebase/src/transactions.rs at line 927
    [4.23633]
    [4.23633]
    "Vorabpauschale" => Self::LumpSumTaxAdvance,