convert.rs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. #[cfg(feature = "telegram")]
  2. use crate::categories::get_top_category;
  3. use crate::categories::CatStats;
  4. use crate::receipt;
  5. use crate::user::User;
  6. use chrono::{DateTime, Utc};
  7. use qif_generator::{account::Account, split::Split, transaction::Transaction};
  8. use std::fs;
  9. /// Read json file with receipt and convert it into `receipt::Purchase`
  10. pub fn read_file(f: &str) -> receipt::Purchase {
  11. let json = fs::read_to_string(f).expect(format!("Can't read file {}", f).as_str());
  12. receipt::parse_purchase(&json)
  13. }
  14. #[cfg(test)]
  15. mod tests {
  16. use super::*;
  17. use std::path::PathBuf;
  18. #[test]
  19. fn test_read_receipt() {
  20. let mut p = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
  21. p.push("tests/resources/test.json");
  22. let full_path = p.to_string_lossy();
  23. let result = read_file(&full_path).items;
  24. assert_eq!(result[0].name, "СИДР 0.5 MAGNERS APP");
  25. assert_eq!(result[0].sum, 17713);
  26. }
  27. }
  28. /// Generate set of QIF Splits from a Purchase items
  29. pub fn gen_splits<F, C>(
  30. items: &[receipt::Item],
  31. cs: &mut CatStats,
  32. accounts: &[String],
  33. filter: F,
  34. categorizer: C,
  35. ) -> Vec<Split>
  36. where
  37. C: Fn(&str, &mut CatStats, &[String]) -> String,
  38. F: Fn(&str) -> &str,
  39. {
  40. let mut result: Vec<Split> = Vec::new();
  41. for i in items.iter() {
  42. let t = Split::new()
  43. .memo(filter(i.name.as_str()))
  44. .amount(-i.sum)
  45. .category(&categorizer(i.name.as_str(), cs, accounts))
  46. .build();
  47. result.push(t);
  48. }
  49. result
  50. }
  51. /// Generate QIF transaction from `splits`
  52. pub fn gen_trans<'a>(
  53. acc: &'a Account,
  54. date: DateTime<Utc>,
  55. sum: i64,
  56. memo: &str,
  57. splits: &[Split],
  58. ) -> Result<Transaction<'a>, String> {
  59. let t = Transaction::new(acc)
  60. .date(date)
  61. .memo(memo)
  62. .splits(splits)
  63. .build();
  64. match t {
  65. Ok(t) => {
  66. if t.sum() == -sum {
  67. Ok(t)
  68. } else {
  69. Err(format!(
  70. "Total sum is wrong. Expected: {} Got: {}",
  71. sum,
  72. t.sum()
  73. ))
  74. }
  75. }
  76. Err(e) => Err(e),
  77. }
  78. }
  79. /// Check if all items in `filename` do have a category assigned by `user`
  80. #[cfg(feature = "telegram")]
  81. pub fn non_cat_items(filename: &str, user: &User) -> Vec<String> {
  82. let file = read_file(filename);
  83. let mut result: Vec<String> = Vec::new();
  84. for i in file.items {
  85. match get_top_category(i.name.as_str(), &user.catmap) {
  86. Some(_) => (),
  87. None => result.push(String::from(i.name.as_str())),
  88. }
  89. }
  90. result
  91. }
  92. /// Convert `filename` into a QIF transaction
  93. pub fn convert<'a, F, C>(
  94. filename: &'a str,
  95. memo: &str,
  96. user: &'a mut User,
  97. acc: &'a Account,
  98. filter: F,
  99. categorizer: C,
  100. ) -> Result<Transaction<'a>, String>
  101. where
  102. F: Fn(&str) -> &str,
  103. C: Fn(&str, &mut CatStats, &[String]) -> String,
  104. {
  105. let purchase = &read_file(filename);
  106. let splits = &gen_splits(
  107. &purchase.items,
  108. &mut user.catmap,
  109. &user.accounts,
  110. &filter,
  111. &categorizer,
  112. );
  113. gen_trans(acc, purchase.date(), purchase.total_sum(), memo, splits)
  114. }