convert.rs 3.0 KB

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