convert.rs 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  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::{Date, 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("Can't read file");
  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>(items: &[receipt::Item], cs: &mut CatStats, categorizer: F) -> Vec<Split>
  30. where
  31. F: Fn(&str, &mut CatStats) -> String,
  32. {
  33. let mut result: Vec<Split> = Vec::new();
  34. for i in items.iter() {
  35. let t = Split::new()
  36. .memo(i.name.as_str())
  37. .amount(-i.sum)
  38. .category(&categorizer(i.name.as_str(), cs))
  39. .build();
  40. result.push(t);
  41. }
  42. result
  43. }
  44. /// Generate QIF transaction from `splits`
  45. pub fn gen_trans<'a>(
  46. acc: &'a Account,
  47. date: Date<Utc>,
  48. sum: i64,
  49. memo: &str,
  50. splits: Vec<Split>,
  51. ) -> Result<Transaction<'a>, String> {
  52. let t = Transaction::new(acc)
  53. .date(date)
  54. .memo(memo)
  55. .splits(&splits)
  56. .build();
  57. match t {
  58. Ok(t) => {
  59. if t.sum() == -sum {
  60. Ok(t)
  61. } else {
  62. Err(format!(
  63. "Total sum is wrong. Expected: {} Got: {}",
  64. sum,
  65. t.sum()
  66. ))
  67. }
  68. }
  69. Err(e) => Err(e),
  70. }
  71. }
  72. /// Check if all items in `filename` do have a category assigned by `user`
  73. #[cfg(feature = "telegram")]
  74. pub fn non_cat_items(filename: &str, user: &User) -> Vec<String> {
  75. let file = read_file(filename);
  76. let mut result: Vec<String> = Vec::new();
  77. for i in &file.items {
  78. match get_top_category(i.name.as_str(), &user.catmap) {
  79. Some(_) => (),
  80. None => result.push(String::from(i.name.as_str())),
  81. }
  82. }
  83. result
  84. }
  85. /// Convert `filename` into a QIF transaction
  86. pub fn convert<'a, F>(
  87. filename: &'a str,
  88. memo: &str,
  89. user: &'a mut User,
  90. acc: &'a Account,
  91. categorizer: F,
  92. ) -> Result<Transaction<'a>, String>
  93. where
  94. F: Fn(&str, &mut CatStats) -> String,
  95. {
  96. let purchase = read_file(filename);
  97. let splits = gen_splits(&purchase.items, &mut user.catmap, categorizer);
  98. gen_trans(&acc, purchase.date(), purchase.total_sum(), memo, splits)
  99. }