receipt.rs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. use chrono::{Date, Utc};
  2. use serde::Deserialize;
  3. use std::fmt;
  4. pub struct Purchase {
  5. sum: i64,
  6. date: Date<Utc>,
  7. pub items: Vec<Item>,
  8. }
  9. impl Purchase {
  10. pub fn total_sum(self) -> i64 {
  11. self.sum
  12. }
  13. pub fn date(&self) -> Date<Utc> {
  14. self.date
  15. }
  16. }
  17. #[derive(Deserialize, Debug, Clone)]
  18. pub struct Item {
  19. pub name: String,
  20. pub sum: i64,
  21. }
  22. impl fmt::Display for Item {
  23. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  24. write!(f, "{}:{}", self.name, self.sum)
  25. }
  26. }
  27. #[allow(dead_code)]
  28. #[allow(non_snake_case)]
  29. #[derive(Deserialize, Debug, Clone)]
  30. struct Receipt {
  31. totalSum: i64,
  32. pub items: Vec<Item>,
  33. }
  34. #[allow(dead_code)]
  35. #[derive(Deserialize)]
  36. struct Document {
  37. receipt: Receipt,
  38. }
  39. #[allow(dead_code)]
  40. #[derive(Deserialize)]
  41. struct Ticket {
  42. document: Document,
  43. }
  44. #[allow(dead_code)]
  45. #[derive(Deserialize)]
  46. struct Query {
  47. sum: i64,
  48. #[serde(with = "custom_date_format")]
  49. date: Date<Utc>,
  50. }
  51. mod custom_date_format {
  52. use chrono::{Date, TimeZone, Utc};
  53. use serde::{self, Deserialize, Deserializer};
  54. /// The format seems alike to RFC3339 but is not compliant
  55. const FORMAT: &str = "%Y-%m-%dT%H:%M";
  56. /// Custom deserializer for format in our json
  57. pub fn deserialize<'de, D>(deserializer: D) -> Result<Date<Utc>, D::Error>
  58. where
  59. D: Deserializer<'de>,
  60. {
  61. let s = String::deserialize(deserializer)?;
  62. let dt = Utc
  63. .datetime_from_str(&s, FORMAT)
  64. .map_err(serde::de::Error::custom);
  65. match dt {
  66. Ok(date) => Ok(date.date()),
  67. Err(e) => Err(e),
  68. }
  69. }
  70. }
  71. #[allow(dead_code)]
  72. #[derive(Deserialize)]
  73. struct Input {
  74. query: Query,
  75. ticket: Ticket,
  76. }
  77. pub fn parse_purchase(line: &str) -> Purchase {
  78. // TODO: Check if several receipts are possible
  79. let receipt: Vec<Input> = serde_json::from_str(line).unwrap();
  80. Purchase {
  81. sum: receipt[0].query.sum,
  82. date: receipt[0].query.date,
  83. items: receipt[0].ticket.document.receipt.items.clone(),
  84. }
  85. }
  86. #[cfg(test)]
  87. mod receipt {
  88. use super::*;
  89. use chrono::Datelike;
  90. #[test]
  91. fn item() {
  92. let line = String::from(
  93. r#"
  94. {
  95. "quantity" : 1,
  96. "ndsRate" : 1,
  97. "price" : 5549,
  98. "calculationSubjectSign" : 1,
  99. "calculationTypeSign" : 4,
  100. "name" : "ХРЕН РУССКИЙ 170Г",
  101. "sum" : 5549
  102. }
  103. "#,
  104. );
  105. let testit: Item = serde_json::from_str(&line).unwrap();
  106. assert_eq!(testit.name, "ХРЕН РУССКИЙ 170Г");
  107. assert_eq!(testit.sum, 5549);
  108. }
  109. #[test]
  110. fn query() {
  111. let line = String::from(
  112. r#"
  113. {
  114. "date": "2021-03-24T20:02",
  115. "documentId": 28230,
  116. "fsId": "9282440300829284",
  117. "fiscalSign": "1706439950",
  118. "operationType": 1,
  119. "sum": 267800
  120. }
  121. "#,
  122. );
  123. let testit: Query = serde_json::from_str(&line).unwrap();
  124. assert_eq!(testit.sum, 267800);
  125. assert_eq!(testit.date.day(), 24);
  126. assert_eq!(testit.date.month(), 3);
  127. assert_eq!(testit.date.year(), 2021);
  128. }
  129. #[test]
  130. fn receipt() {
  131. let line = String::from(
  132. r#"
  133. {
  134. "totalSum" : 548702,
  135. "userInn" : "7703270067",
  136. "operator" : "Теpминал 24",
  137. "items" : [
  138. {
  139. "quantity" : 1,
  140. "ndsRate" : 1,
  141. "price" : 5549,
  142. "calculationSubjectSign" : 1,
  143. "calculationTypeSign" : 4,
  144. "name" : "ХРЕН РУССКИЙ 170Г",
  145. "sum" : 5549
  146. },
  147. {
  148. "quantity" : 1,
  149. "ndsRate" : 1,
  150. "price" : 20599,
  151. "calculationSubjectSign" : 1,
  152. "calculationTypeSign" : 4,
  153. "name" : "СОУС ОСТР.380Г КИНТО",
  154. "sum" : 20599
  155. }
  156. ]
  157. }
  158. "#,
  159. );
  160. let testit: Receipt = serde_json::from_str(&line).unwrap();
  161. assert_eq!(testit.totalSum, 548702);
  162. assert_eq!(testit.items.len(), 2);
  163. assert_eq!(testit.items[0].sum, 5549);
  164. assert_eq!(testit.items[1].sum, 20599);
  165. }
  166. #[test]
  167. fn input() {
  168. let line = String::from(
  169. r#"
  170. {
  171. "query": {
  172. "date": "2021-03-24T20:02",
  173. "documentId": 28230,
  174. "fsId": "9282440300829284",
  175. "fiscalSign": "1706439950",
  176. "operationType": 1,
  177. "sum": 267800
  178. },
  179. "ticket": {
  180. "document" : {
  181. "receipt" : {
  182. "totalSum" : 548702,
  183. "userInn" : "7703270067",
  184. "operator" : "Теpминал 24",
  185. "items" : [
  186. {
  187. "quantity" : 1,
  188. "ndsRate" : 1,
  189. "price" : 5549,
  190. "calculationSubjectSign" : 1,
  191. "calculationTypeSign" : 4,
  192. "name" : "ХРЕН РУССКИЙ 170Г",
  193. "sum" : 5549
  194. },
  195. {
  196. "quantity" : 1,
  197. "ndsRate" : 1,
  198. "price" : 20599,
  199. "calculationSubjectSign" : 1,
  200. "calculationTypeSign" : 4,
  201. "name" : "СОУС ОСТР.380Г КИНТО",
  202. "sum" : 20599
  203. }
  204. ]
  205. }
  206. }
  207. }
  208. }
  209. "#,
  210. );
  211. let testit: Input = serde_json::from_str(&line).unwrap();
  212. assert_eq!(testit.ticket.document.receipt.totalSum, 548702);
  213. assert_eq!(testit.ticket.document.receipt.items.len(), 2);
  214. assert_eq!(testit.ticket.document.receipt.items[0].sum, 5549);
  215. assert_eq!(testit.ticket.document.receipt.items[1].sum, 20599);
  216. assert_eq!(testit.query.date.day(), 24);
  217. assert_eq!(testit.query.date.month(), 3);
  218. assert_eq!(testit.query.date.year(), 2021);
  219. }
  220. #[test]
  221. fn display() {
  222. let it = Item {
  223. name: "test".to_string(),
  224. sum: 1000,
  225. };
  226. let line = format!("{}", it.to_string());
  227. assert_eq!(line, "test:1000");
  228. }
  229. }