1#![allow(dead_code)]
3#![allow(unused_parens)]
4#![allow(unused_variables)]
5
6use datastore::Datastore;
7use datastore::UserGroup;
8use datastore::Itemable;
9use datastore::Userable;
10
11use std::cmp;
12use config::StaticConfig;
13
14use left_threaded_avl_tree::AVLTree;
15use left_threaded_avl_tree::ScoredIdTreeMock;
16
17use std::collections::HashSet;
18use std::collections::HashMap;
19use std::iter::FromIterator;
20use serde_json;
21use std;
22use serde_json::Error;
23use datastore;
24use datastore::*;
25use suffix_rs::*;
26use datastore::PurchaseFunctions;
27
28
29pub trait Event {
30 fn can_be_applied(&self, store: &Datastore) -> bool;
31 fn apply(&self, store: &mut Datastore, config: &StaticConfig) -> bool;
32}
33
34pub fn default_true() -> bool {
35 true
36}
37
38#[derive(Serialize, Deserialize, Debug, PartialEq)]
39pub enum BLEvents {
40 CreateItem {
41 itemname: String,
42 price_cents: u32,
43 category: Option<String>,
44 },
45 CreateUser { username: String },
46 UpdateUser { user_id: u32, username: String, is_billed: bool, is_highlighted: bool, external_user_id: Option<String>,
47
48 #[serde(default = "default_true")]
49 is_sepa: bool,},
50 UpdateItem {
51 item_id: u32,
52 itemname: String,
53 price_cents: u32,
54 category: Option<String>,},
55 DeleteItem { item_id: u32 },
56 DeleteUser { user_id: u32 },
57 MakeSimplePurchase {
58 user_id: u32,
59 item_id: u32,
60 timestamp: i64,
61 },
62 MakeShoppingCartPurchase {
63 user_id: u32,
64 specials : Vec<String>,
65 item_ids : Vec<u32>,
66 timestamp: i64,
67 },
68 MakeSpecialPurchase {
69 user_id: u32,
70 special_name: String,
71 timestamp: i64,
72 },
73 MakeFreeForAllPurchase {
74 ffa_id: u64,
75 item_id: u32,
76 timestamp: i64,
77 },
78 CreateFreeForAll {
79 allowed_categories : Vec<String>,
80 allowed_drinks : Vec<u32>,
81 allowed_number_total : u16,
82 text_message : String,
83 created_timestamp : i64,
84 donor : u32,
85 },
86 CreateFreeCount {
87 allowed_categories : Vec<String>,
88 allowed_drinks : Vec<u32>,
89 allowed_number_total : u16,
90 text_message : String,
91 created_timestamp : i64,
92 donor : u32,
93 recipient : u32,
94 },
95 CreateFreeBudget {
96 cents_worth_total : u64,
97 text_message : String,
98 created_timestamp : i64,
99 donor : u32,
100 recipient : u32,
101 },
102 UndoPurchase { unique_id: u64 },
103 CreateBill {
104 timestamp_from: i64,
105 timestamp_to: i64,
106 user_ids: UserGroup,
107 comment: String,
108 },
109 FinalizeBill {
110 timestamp_from: i64, timestamp_to: i64,
112 },
113 ExportBill {
114 timestamp_from: i64, timestamp_to: i64,
116 },
117 DeleteUnfinishedBill {
118 timestamp_from: i64, timestamp_to: i64,
120 },
121 SetPriceForSpecial {
122 unique_id: u64,
123 price: u32,
124 },
125 UpdateBill {
126 timestamp_from: i64, timestamp_to: i64,
128 comment: String,
129 users: UserGroup,
130 users_that_will_not_be_billed: HashSet<u32>,
131 },
132}
133
134fn hashset(data: &[u32]) -> HashSet<u32> {
135 let r = HashSet::from_iter(data.iter().cloned());
136 return r;
137}
138
139impl Event for BLEvents {
140 fn can_be_applied(&self, store: &Datastore) -> bool {
141 return match self {
142 &BLEvents::CreateItem {
143 ref itemname,
144 price_cents,
145 ref category,
146 } => true,
147 &BLEvents::CreateUser { ref username } => true,
148 &BLEvents::CreateBill {
149 ref timestamp_from,
150 ref timestamp_to,
151 ref user_ids,
152 ref comment,
153 } => {
154 !store.purchases.is_empty() &&
155 !store.bills.iter().any(|b| b.timestamp_to == *timestamp_to && b.timestamp_from == *timestamp_from)
156 },
157 &BLEvents::UpdateItem { ref item_id,
158 ref itemname,
159 ref price_cents,
160 ref category } => store.has_item(*item_id),
161 &BLEvents::UpdateUser { ref user_id, ref username, ref is_billed, ref is_highlighted, ref external_user_id, ref is_sepa } => store.has_user(*user_id),
162 &BLEvents::DeleteItem { item_id } => store.has_item(item_id),
163 &BLEvents::DeleteUser { user_id } => store.has_user(user_id),
164 &BLEvents::MakeSimplePurchase {
165 user_id,
166 item_id,
167 timestamp,
168 } => store.has_item(item_id) && store.has_user(user_id),
169 &BLEvents::MakeSpecialPurchase { ref user_id, ref special_name, ref timestamp } => store.has_user(*user_id),
170 &BLEvents::MakeShoppingCartPurchase { ref user_id, ref specials, ref item_ids, ref timestamp } => {
171
172 let mut v : Vec<BLEvents> = Vec::new();
173 for x in item_ids {
174 v.push(BLEvents::MakeSimplePurchase {user_id: *user_id, item_id: *x, timestamp: *timestamp});
175 }
176 for x in specials {
177 v.push(BLEvents::MakeSpecialPurchase {user_id: *user_id, special_name: x.to_string(), timestamp: *timestamp});
178 }
179
180 let mut result = true;
181 for x in v {
182 result = result & x.can_be_applied(store);
183 }
184 return result;
185 }
186 &BLEvents::MakeFreeForAllPurchase { ffa_id, item_id, timestamp } => {
187 let mut b = false;
188 let x : Option<&Freeby> = store.open_ffa.iter().find(|x|x.get_id() == ffa_id);
189 let item: &Item = store.items.get(&item_id).unwrap();
190 match x {
191 Some(ffa) => {return ffa.allows(item);},
192 None => {return false;},
193 }
194 },
195 &BLEvents::CreateFreeForAll { ref allowed_categories, ref allowed_drinks, ref allowed_number_total, ref text_message, ref created_timestamp, ref donor } =>
196 {
197 return true;
198 },
199 &BLEvents::CreateFreeCount { ref allowed_categories, ref allowed_drinks, ref allowed_number_total, ref text_message, ref created_timestamp, ref donor, ref recipient } => (store.has_user(*donor) && store.has_user(*recipient)),
200 &BLEvents::CreateFreeBudget { ref cents_worth_total, ref text_message, ref created_timestamp, ref donor, ref recipient } => (store.has_user(*donor) && store.has_user(*recipient)),
201 &BLEvents::UndoPurchase { unique_id } => store.get_purchase(unique_id).is_some(),
202 &BLEvents::FinalizeBill { timestamp_from, timestamp_to } => {
203 match store.get_bill(timestamp_from, timestamp_to) {
205 Some(b) => {
206 println!("Bill for finalization is found, with is_created() = {}, store.get_un_set_users_to_bill(timestamp_from, timestamp_to).is_empty() = {}, and store.get_unpriced_specials_to_bill(timestamp_from, timestamp_to).is_empty() = {}\nstore.get_un_set_users_to_bill(timestamp_from, timestamp_to) = {:?}", b.bill_state.is_created() , store.get_un_set_users_to_bill(timestamp_from, timestamp_to).is_empty() , store.get_unpriced_specials_to_bill(timestamp_from, timestamp_to).is_empty(), store.get_un_set_users_to_bill(timestamp_from, timestamp_to) );
207 return b.bill_state.is_created() && store.get_un_set_users_to_bill(timestamp_from, timestamp_to).is_empty() && store.get_unpriced_specials_to_bill(timestamp_from, timestamp_to).is_empty();},
208 None => {return false;},
209 }
210 },
211 &BLEvents::UpdateBill { ref timestamp_from, ref timestamp_to, ref comment, ref users, ref users_that_will_not_be_billed } => {
212 match store.get_bill(*timestamp_from, *timestamp_to) {
213 Some(b) => {return b.bill_state.is_created();},
214 None => {return false;},
215 }
216 },
217 &BLEvents::ExportBill { timestamp_from, timestamp_to } => {
218 match store.get_bill(timestamp_from, timestamp_to) {
219 Some(b) => {return b.bill_state.is_finalized();},
220 None => {return false;},
221 }
222 },
223 &BLEvents::DeleteUnfinishedBill { timestamp_from, timestamp_to } => {
224 match store.get_bill(timestamp_from, timestamp_to) {
225 Some(b) => {
226 return b.bill_state.is_created();
227 },
228 None => {return false;},
229 }
230 },
231 &BLEvents::SetPriceForSpecial { unique_id, price } => store.get_purchase(unique_id).is_some(),
232 };
233 }
234
235 fn apply(&self, store: &mut Datastore, config: &StaticConfig) -> bool {
236 return match self {
237 &BLEvents::CreateItem {
238 ref itemname,
239 ref price_cents,
240 ref category,
241 } => {
242 let id = store.item_id_counter;
243 for cat in category.iter() {
244 store.categories.insert(cat.to_string());
245 }
246 store.items.insert(
247 id,
248 datastore::Item {
249 name: itemname.to_string(),
250 item_id: id,
251 cost_cents: *price_cents,
252 category: category.clone(),
253 deleted: false,
254 },
255 );
256 store.item_id_counter = id + 1u32;
257
258 for (_, value) in &mut store.drink_scores_per_user {
259 (*value).insert(id);
260 }
261
262
263
264 {
265 let mut items_vec: Vec<datastore::Item> = vec![];
266
267 for (_, v) in &store.items {
268 let copy : datastore::Item = (v.clone());
269 items_vec.push(copy);
270 }
271
272 store.items_suffix_tree = MockKDTree::build(&items_vec, false);
273 }
274
275
276 true
277 }
278 &BLEvents::CreateUser { ref username } => {
279 let id = store.user_id_counter;
280 store.users.insert(
281 id,
282 datastore::User {
283 username: username.to_string(),
284 external_user_id: None,
285 user_id: id,
286 is_billed: true,
287 is_sepa: true,
288 highlight_in_ui: false,
289 deleted: false,
290 },
291 );
292 store.user_id_counter = id + 1u32;
293
294 let mut score_tree = ScoredIdTreeMock::default();
296 for (_key, _) in &store.items {
297 let _ = score_tree.insert(*_key);
298 }
299
300 store.drink_scores_per_user.insert(id, score_tree);
301 store.top_drinks_per_user.insert(id, HashSet::new());
302
303 store.top_user_scores.insert(id);
305 store.top_users = hashset(
306 store
307 .top_user_scores
308 .extract_top(config.users_in_top_users)
309 .as_slice(),
310 );
311
312 {
313 let mut users_vec: Vec<datastore::User> = vec![];
314
315 for (_, v) in &store.users {
316 users_vec.push(v.clone());
317 }
318
319 store.users_suffix_tree = MockKDTree::build(&users_vec, false);
320 }
321
322
323 true
324 },
325 &BLEvents::UpdateItem { ref item_id,
326 ref itemname,
327 ref price_cents,
328 ref category } => {
329 let mut e = store.items.get_mut(item_id).unwrap();
330 e.name = itemname.to_string();
331 e.cost_cents = *price_cents;
332 e.category = category.clone();
333
334 true
335 },
336 &BLEvents::UpdateUser { ref user_id, ref username, ref is_billed, ref is_highlighted, ref external_user_id, ref is_sepa } => {
337 let mut e = store.users.get_mut(user_id).unwrap();
338 e.username = username.to_string();
339 e.is_billed = *is_billed;
340 e.highlight_in_ui = *is_highlighted;
341 e.external_user_id = external_user_id.clone();
342
343 if *is_highlighted {
345 let _ = store.highlighted_users.insert(*user_id);
346 } else {
347 let _ = store.highlighted_users.remove(user_id);
348 }
349
350 true
351 },
352 &BLEvents::CreateBill {
353 ref timestamp_from,
354 ref timestamp_to,
355 ref user_ids,
356 ref comment,
357 } => {
358 store.bills.push(datastore::Bill {
359 timestamp_from: *timestamp_from,
360 timestamp_to: *timestamp_to,
361 users: user_ids.clone(),
362 bill_state: datastore::BillState::Created,
363 users_that_will_not_be_billed: HashSet::new(),
364 comment: comment.to_string(),
365 finalized_data: datastore::ExportableBillData {
366 all_users: HashMap::new(),
367 all_items: HashMap::new(),
368 user_consumption: HashMap::new(),
369 },
370 });
371 true
372 }
373 &BLEvents::DeleteItem { item_id } => {
374 let _ = store.items.get_mut(&item_id).map(|it|{it.deleted = true});
375 let v = store.items.get(&item_id);
376 match v {
377 None => (),
378 Some(item) => {
379 match item.category {
381 None => (),
382 Some(ref category) => {
383 if !store.categories.iter().any(|x| x.eq(category)) {
384 let _ = store.categories.remove(&category.clone());
385 }
386 }
387 }
388 for (_key, mut value) in &mut store.drink_scores_per_user {
390 value.remove(item_id);
391 }
392
393 for (_key, value) in &mut store.top_drinks_per_user {
394 match store.drink_scores_per_user.get(&_key) {
395 None => (),
396 Some(drinkscore) => if value.contains(&item_id) {
397 *value = hashset(
398 drinkscore
399 .extract_top(config.top_drinks_per_user as usize)
400 .as_slice(),
401 );
402 },
403 }
404 }
405 }
406 }
407
408 {
409 let mut items_vec: Vec<datastore::Item> = vec![];
410
411 for (_, v) in &store.items {
412 if !v.deleted {
413 items_vec.push(v.clone());
414 }
415 }
416
417 store.items_suffix_tree = MockKDTree::build(&items_vec, false);
418 }
419 true
420 }
421 &BLEvents::DeleteUser { user_id } => {
422
423
424 match store.users.get(&user_id) {
426 Some(x) => {
427 if x.highlight_in_ui {
428 let _ = store.highlighted_users.remove(&user_id);
429 }
430 },
431 None => (),
432 }
433
434 let _ = store.users.get_mut(&user_id).map(|it|it.deleted = true);
436
437 let _ = store.drink_scores_per_user.remove(&user_id);
439
440 let _ = store.top_drinks_per_user.remove(&user_id);
442
443 let _ = store.top_user_scores.remove(user_id);
445
446
447
448 {
449 let mut users_vec: Vec<datastore::User> = vec![];
450
451 for (_, v) in &store.users {
452 if !v.deleted {
453 users_vec.push(v.clone());
454 }
455 }
456
457 store.users_suffix_tree = MockKDTree::build(&users_vec, false);
458 }
459
460 if store.top_users.remove(&user_id) {
462 store.top_users = hashset(
463 store
464 .top_user_scores
465 .extract_top(config.users_in_top_users as usize)
466 .as_slice(),
467 );
468 true
469 } else {
470 false
471 }
472 }
473 &BLEvents::MakeSimplePurchase {
476 user_id,
477 item_id,
478 timestamp,
479 } => {
480 let idx: u64 = store.purchase_count + 1;
481 store.purchase_count = idx;
482
483 store.purchases.push(datastore::Purchase::SimplePurchase {
485 unique_id: idx,
486 timestamp_epoch_millis: timestamp,
487 item_id: item_id,
488 consumer_id: user_id,
489 });
490
491 let was_in_before = store.top_users.contains(&user_id);
492
493 if let Some(ref mut drinkscore) = store.drink_scores_per_user.get_mut(&user_id) {
495 drinkscore.increment_by_one(item_id);
496 if let Some(topitems) = store.top_drinks_per_user.get_mut(&user_id) {
498 if !(topitems.contains(&item_id)) {
499 *topitems = hashset(
500 drinkscore
501 .extract_top(config.top_drinks_per_user)
502 .as_slice(),
503 );
504 }
505 }
506 }
507
508 store.top_user_scores.increment_by_one(user_id);
510
511 if !(store.top_users.contains(&user_id)) {
513 println!("not in top users for userid = {}", user_id);
514 store.top_users = hashset(
515 store
516 .top_user_scores
517 .extract_top(config.users_in_top_users)
518 .as_slice(),
519 );
520 } else {
521
522 }
523
524 let alt_hashmap_1 = HashMap::new();
526 let username = store.users.get(&user_id).unwrap().username.to_string();
527 let itemname = store.items.get(&item_id).unwrap().name.to_string();
528 let user_key = (user_id, store.users.get(&user_id).unwrap().username.to_string());
529 let item_key = (item_id, store.items.get(&item_id).unwrap().name.to_string());
530 let mut old_cost_map = store
531 .balance_cost_per_user
532 .remove(&user_key)
533 .unwrap_or(alt_hashmap_1);
534 let old_cost_value = *old_cost_map.get(&item_key).unwrap_or(&0);
535 old_cost_map.insert(
536 item_key,
537 old_cost_value
538 + store
539 .items
540 .get(&item_id)
541 .map(|item| item.cost_cents)
542 .unwrap_or(0),
543 );
544 store.balance_cost_per_user.insert(user_key, old_cost_map);
545
546 let alt_hashmap_2 = HashMap::new();
548 let user_key2 : (u32, String) = (user_id, username.to_string());
549 let user_key3 : (u32, String) = (user_id, username.to_string());
550 let item_key2 : (u32, String) = (item_id, itemname.to_string());
551 let item_key3 : (u32, String) = (item_id, itemname.to_string());
552 let mut old_count_map = store
553 .balance_count_per_user
554 .remove(&user_key2)
555 .unwrap_or(alt_hashmap_2);
556 let old_count_value = *old_count_map.get(&item_key2).unwrap_or(&0);
557 old_count_map.insert(item_key3, old_count_value + 1);
558 store.balance_count_per_user.insert(user_key3, old_count_map);
559
560
561 let is_in_now = store.top_users.contains(&user_id);
562
563
564
565 ((!was_in_before) & (is_in_now))
566 }
567 &BLEvents::MakeSpecialPurchase { ref user_id, ref special_name, ref timestamp } => {
568
569 let idx: u64 = store.purchase_count + 1;
570 store.purchase_count = idx;
571
572
573 store.purchases.push(datastore::Purchase::SpecialPurchase {
574 unique_id: idx,
575 timestamp_epoch_millis: *timestamp,
576 special_name: special_name.to_string(),
577 specialcost: None,
578 consumer_id: *user_id,
579 });
580
581 true
582 },
583 &BLEvents::MakeShoppingCartPurchase { ref user_id, ref specials, ref item_ids, ref timestamp } => {
584 let mut v : Vec<BLEvents> = Vec::new();
585 for x in item_ids {
586 v.push(BLEvents::MakeSimplePurchase {user_id: *user_id, item_id: *x, timestamp: *timestamp});
587 }
588 for x in specials {
589 v.push(BLEvents::MakeSpecialPurchase {user_id: *user_id, special_name: x.to_string(), timestamp: *timestamp});
590 }
591
592 let mut result = true;
593 for x in v {
594 result = result & x.apply(store, config);
595 }
596 return result;
597 },
598
599 &BLEvents::MakeFreeForAllPurchase { ffa_id, item_id, timestamp } => {
600
601 let idx: u64 = store.purchase_count + 1;
603 store.purchase_count = idx;
604
605
606 let index = store.open_ffa.iter().position(|x| x.get_id() == ffa_id).unwrap();
607 let mut freeby : Freeby = store.open_ffa.remove(index);
608
609 let user_id: u32 = freeby.get_donor();
610
611 {
612 store.purchases.push(datastore::Purchase::FFAPurchase {
614 unique_id: idx,
615 timestamp_epoch_millis: timestamp,
616 item_id: item_id,
617 freeby_id: freeby.get_id(),
618 donor: freeby.get_donor(),
619 });
620 }
621
622 {
623
624 println!("Freeby was : {:?}", freeby);
625 freeby.decrement();
627 println!("Freeby is : {:?}", freeby);
628 }
629
630 if freeby.left() == 0 {
632 let pos = store.used_up_freebies.binary_search_by(|f|f.get_id().cmp(&freeby.get_id())).unwrap_or_else(|e| e);
634 store.used_up_freebies.insert(pos, freeby);
635 } else {
636 store.open_ffa.insert(index, freeby);
637 }
638
639 {
642 let alt_hashmap_1 = HashMap::new();
644 let username = store.users.get(&user_id).unwrap().username.to_string();
645 let itemname = store.items.get(&item_id).unwrap().name.to_string();
646 let user_key = (user_id, store.users.get(&user_id).unwrap().username.to_string());
647 let item_key = (item_id, store.items.get(&item_id).unwrap().name.to_string());
648 let mut old_cost_map = store
649 .balance_cost_per_user
650 .remove(&user_key)
651 .unwrap_or(alt_hashmap_1);
652 let old_cost_value = *old_cost_map.get(&item_key).unwrap_or(&0);
653 old_cost_map.insert(
654 item_key,
655 old_cost_value
656 + store
657 .items
658 .get(&item_id)
659 .map(|item| item.cost_cents)
660 .unwrap_or(0),
661 );
662 store.balance_cost_per_user.insert(user_key, old_cost_map);
663
664 let alt_hashmap_2 = HashMap::new();
666 let user_key2 : (u32, String) = (user_id, username.to_string());
667 let user_key3 : (u32, String) = (user_id, username.to_string());
668 let item_key2 : (u32, String) = (item_id, itemname.to_string());
669 let item_key3 : (u32, String) = (item_id, itemname.to_string());
670 let mut old_count_map = store
671 .balance_count_per_user
672 .remove(&user_key2)
673 .unwrap_or(alt_hashmap_2);
674 let old_count_value = *old_count_map.get(&item_key2).unwrap_or(&0);
675 old_count_map.insert(item_key3, old_count_value + 1);
676 store.balance_count_per_user.insert(user_key3, old_count_map);
677 }
678
679 true
680
681 },
758 &BLEvents::CreateFreeForAll { ref allowed_categories, ref allowed_drinks, ref allowed_number_total, ref text_message, ref created_timestamp, ref donor } => {
759 let id = {
760 let x = store.freeby_id_counter + 1;
761 store.freeby_id_counter = x;
762 x
763 };
764
765 store.open_ffa.push(Freeby::FFA{
766 id: id,
767 allowed_categories: allowed_categories.to_vec(),
768 allowed_drinks: allowed_drinks.to_vec(),
769 allowed_number_total: *allowed_number_total,
770 allowed_number_used : 0,
771 text_message : text_message.to_string(),
772 created_timestamp : *created_timestamp,
773 donor: *donor,
774 });
775
776
777 true
778 },
779 &BLEvents::ExportBill { timestamp_from, timestamp_to } => {
780 let b: &mut Bill = store.get_mut_bill(timestamp_from, timestamp_to).unwrap();
781 b.bill_state = datastore::BillState::ExportedAtLeastOnce;
782 true
783 },
784 &BLEvents::CreateFreeCount { ref allowed_categories, ref allowed_drinks, ref allowed_number_total, ref text_message, ref created_timestamp, ref donor, ref recipient } => {
785 let id = {
786 let x = store.freeby_id_counter + 1;
787 store.freeby_id_counter = x;
788 x
789 };
790 if !store.open_freebies.contains_key(recipient) {
791 store.open_freebies.insert(*recipient, vec![]);
792 }
793 store.open_freebies.get_mut(recipient).unwrap().push( datastore::Freeby::Classic {
794 id: id,
795 allowed_categories: allowed_categories.to_vec(),
796 allowed_drinks: allowed_drinks.to_vec(),
797 allowed_number_total: *allowed_number_total,
798 allowed_number_used: 0,
799 text_message: text_message.to_string(),
800 created_timestamp: *created_timestamp,
801 donor: *donor,
802 recipient: *recipient,
803 });
804
805 true
806 },
807 &BLEvents::CreateFreeBudget { ref cents_worth_total, ref text_message, ref created_timestamp, ref donor, ref recipient } => {
808 let id = {
809 let x = store.freeby_id_counter + 1;
810 store.freeby_id_counter = x;
811 x
812 };
813 if !store.open_freebies.contains_key(recipient) {
814 store.open_freebies.insert(*recipient, vec![]);
815 }
816 store.open_freebies.get_mut(recipient).unwrap().push( datastore::Freeby::Transfer {
817 id: id,
818 cents_worth_total: *cents_worth_total,
819 cents_worth_used: 0,
820 text_message: text_message.to_string(),
821 created_timestamp: *created_timestamp,
822 donor: *donor,
823 recipient: *recipient,
824 });
825
826 true
827 },
828 &BLEvents::UndoPurchase { unique_id } => {
829 let index = store
831 .purchases
832 .iter()
833 .position(|x| x.has_unique_id(unique_id))
834 .unwrap();
835 let old_size = store.purchases.len();
836 let element = store.purchases.remove(index);
837
838 let item_key1 : (u32, String) = (*element.get_item_id(), store.items.get(&element.get_item_id()).unwrap().name.to_string());
839 let item_key2 : (u32, String) = (*element.get_item_id(), store.items.get(&element.get_item_id()).unwrap().name.to_string());
840 let user_key : (u32, String) = (*element.get_user_id(), store.users.get(&element.get_user_id()).unwrap().username.to_string());
841
842 let oldcost = *store
844 .balance_cost_per_user
845 .get(&user_key)
846 .unwrap()
847 .get(&item_key1)
848 .unwrap();
849 let oldcount = *store
850 .balance_count_per_user
851 .get(&user_key)
852 .unwrap()
853 .get(&item_key1)
854 .unwrap();
855
856
857 store
858 .balance_cost_per_user
859 .get_mut(&user_key)
860 .unwrap()
861 .insert(item_key1, oldcost - 1);
862 store
863 .balance_count_per_user
864 .get_mut(&user_key)
865 .unwrap()
866 .insert(item_key2, oldcount - 1);
867
868
869 old_size == index + 1
870 },
871 &BLEvents::FinalizeBill { timestamp_from, timestamp_to } => {
873
874
875 let bill_idx: usize = store.get_bill_index(timestamp_from, timestamp_to).unwrap();
876
877 {
878 store.bills.get_mut(bill_idx).unwrap().bill_state = BillState::Finalized;
879 }
880
881 let bill_cpy: Bill = store.bills[bill_idx].clone();
882
883
884 let purchase_indices = store.get_purchase_indices_to_bill(&bill_cpy);
885
886 {
888 let filtered_purs : Vec<Purchase> = purchase_indices.iter().map(|idx| store.purchases[*idx].clone()).collect();
889 for purchase in filtered_purs {
890
891 let user : User = store.users[purchase.get_user_id()].clone();
892
893 match purchase {
894 Purchase::SpecialPurchase{
895 unique_id,
896 timestamp_epoch_millis,
897 special_name,
898 specialcost,
899 consumer_id,
900 } => {
901 let day_idx : usize = bill_cpy.get_day_index(timestamp_epoch_millis);
902 let mut bill: &mut Bill = store.bills.get_mut(bill_idx).unwrap();
903 if !bill.finalized_data.all_users.contains_key(&consumer_id) {
904 bill.finalized_data.all_users.insert(consumer_id, user.clone());
905 bill.finalized_data.user_consumption.insert(consumer_id, BillUserInstance {
906 user_id: consumer_id,
907 per_day: HashMap::new(),
908 });
909 }
910
911 if !bill.finalized_data.user_consumption.get_mut(&consumer_id).unwrap().per_day.contains_key(&day_idx) {
912 bill.finalized_data.user_consumption.get_mut(&consumer_id).unwrap().per_day.insert(day_idx, BillUserDayInstance {
913 personally_consumed: HashMap::new(),
914 specials_consumed: Vec::new(),
915 ffa_giveouts: HashMap::new(),
916 giveouts_to_user_id: HashMap::new(),
917 });
918 }
919 bill.finalized_data.user_consumption.get_mut(&consumer_id).unwrap().per_day.get_mut(&day_idx).unwrap().specials_consumed.push(PricedSpecial {
920 purchase_id: unique_id,
921 price: specialcost.unwrap(),
922 name: special_name.to_string(),
923 });
924 },
925 Purchase::SimplePurchase {
926 unique_id,
927 timestamp_epoch_millis,
928 item_id,
929 consumer_id,
930 } => {
931 let day_idx : usize = bill_cpy.get_day_index(timestamp_epoch_millis);
932 let item: Item = store.items.get(&item_id).unwrap().clone();
933 let count_freeby_idx = store.get_count_freeby_id_useable_for(consumer_id, item_id);
934 let budget_freeby_idx = store.get_budget_freeby_id_useable_for(consumer_id);
935
936
937 let mut bill: &mut Bill = store.bills.get_mut(bill_idx).unwrap();
938 if !bill.finalized_data.all_users.contains_key(&consumer_id) {
939 bill.finalized_data.all_users.insert(consumer_id, user.clone());
940 bill.finalized_data.user_consumption.insert(consumer_id, BillUserInstance {
941 user_id: consumer_id,
942 per_day: HashMap::new(),
943 });
944 }
945
946 if !bill.finalized_data.all_items.contains_key(&item_id) {
947 bill.finalized_data.all_items.insert(item_id, item.clone());
948 }
949
950 match count_freeby_idx {
952 Some(cidx) => {
953 let old_count: u16 = store.open_freebies.get(&consumer_id).unwrap().get(cidx).unwrap().left();
955 let donor_id: u32 = store.open_freebies.get(&consumer_id).unwrap().get(cidx).unwrap().get_donor();
956 let donor: User = store.users.get(&donor_id).unwrap().clone();
957
958
959 {
960 if !bill.finalized_data.all_users.contains_key(&donor_id) {
961 bill.finalized_data.all_users.insert(donor_id, donor.clone());
962 bill.finalized_data.user_consumption.insert(donor_id, BillUserInstance {
963 user_id: donor_id,
964 per_day: HashMap::new(),
965 });
966 }
967
968 if !bill.finalized_data.user_consumption.get_mut(&donor_id).unwrap().per_day.contains_key(&day_idx) {
969 bill.finalized_data.user_consumption.get_mut(&donor_id).unwrap().per_day.insert(day_idx, BillUserDayInstance {
970 personally_consumed: HashMap::new(),
971 specials_consumed: Vec::new(),
972 ffa_giveouts: HashMap::new(),
973 giveouts_to_user_id: HashMap::new(),
974 });
975 }
976
977 *bill.finalized_data.user_consumption.get_mut(&donor_id).unwrap().per_day.get_mut(&day_idx).unwrap().giveouts_to_user_id.entry(consumer_id).or_insert(PaidFor {
978 recipient_id: consumer_id,
979 count_giveouts_used: HashMap::new(),
980 budget_given: 0,
981 budget_gotten: 0,
982 }).count_giveouts_used.entry(item_id).or_insert(0) += 1;
983
984
985 }
986
987 {
988 store.open_freebies.get_mut(&consumer_id).unwrap().get_mut(cidx).unwrap().decrement();
989 }
990
991 if old_count <= 1 {
993 let freeby = store.open_freebies.get_mut(&consumer_id).unwrap().remove(cidx);
994 let pos = store.used_up_freebies.binary_search_by(|f|f.get_id().cmp(&freeby.get_id())).unwrap_or_else(|e| e);
995 store.used_up_freebies.insert(pos, freeby);
996 }
997 },
998 None => {
999 *bill.finalized_data.user_consumption.get_mut(&consumer_id).unwrap().per_day.entry(day_idx).or_insert(BillUserDayInstance {
1001 personally_consumed: HashMap::new(),
1002 specials_consumed: Vec::new(),
1003 ffa_giveouts: HashMap::new(),
1004 giveouts_to_user_id: HashMap::new(),
1005 }).personally_consumed.entry(item_id).or_insert(0u32) += 1;
1006
1007 match budget_freeby_idx {
1009 Some(bidx) => {
1010 let max_budget : u64 = store.open_freebies.get(&consumer_id).unwrap().get(bidx).unwrap().get_budget_cents_left();
1011 let donor_id: u32 = store.open_freebies.get(&consumer_id).unwrap().get(bidx).unwrap().get_donor();
1012 let donor: User = store.users.get(&donor_id).unwrap().clone();
1013 let item_cost : u64 = item.cost_cents as u64;
1014 let taken_budget : u64 = cmp::min(max_budget, cmp::max(0u64, item_cost));
1015 let used_up : bool = max_budget <= item_cost;
1016
1017
1018
1019 {
1021 store.open_freebies.get_mut(&consumer_id).unwrap().get_mut(bidx).unwrap().remove_budget_by(taken_budget);
1022 }
1023
1024 {
1025 if !bill.finalized_data.user_consumption.contains_key(&consumer_id) {
1027 bill.finalized_data.user_consumption.insert(consumer_id, BillUserInstance {
1028 user_id: consumer_id,
1029 per_day: HashMap::new(),
1030 });
1031 }
1032 if !bill.finalized_data.user_consumption.get(&consumer_id).unwrap().per_day.contains_key(&day_idx) {
1033 bill.finalized_data.user_consumption.get_mut(&consumer_id).unwrap().per_day.insert(day_idx, BillUserDayInstance {
1034 personally_consumed: HashMap::new(),
1035 specials_consumed: Vec::new(),
1036 ffa_giveouts: HashMap::new(),
1037 giveouts_to_user_id: HashMap::new(),
1038 });
1039 }
1040
1041 if !bill.finalized_data.user_consumption.get(&consumer_id).unwrap().per_day.get(&day_idx).unwrap().giveouts_to_user_id.contains_key(&donor_id) {
1042 bill.finalized_data.user_consumption.get_mut(&consumer_id).unwrap().per_day.get_mut(&day_idx).unwrap().giveouts_to_user_id.insert(donor_id, PaidFor {
1043 recipient_id: donor_id,
1044 count_giveouts_used: HashMap::new(),
1045 budget_given: 0,
1046 budget_gotten: 0,
1047 });
1048 }
1049
1050 bill.finalized_data.user_consumption.get_mut(&consumer_id).unwrap().per_day.get_mut(&day_idx).unwrap().giveouts_to_user_id.entry(donor_id).or_insert(PaidFor {
1051 recipient_id: donor_id,
1052 count_giveouts_used: HashMap::new(),
1053 budget_given: 0,
1054 budget_gotten: 0,
1055 }).budget_gotten += taken_budget;
1056
1057 }
1058
1059 {
1060 if !bill.finalized_data.user_consumption.contains_key(&donor_id) {
1063 bill.finalized_data.user_consumption.insert(donor_id, BillUserInstance {
1064 user_id: donor_id,
1065 per_day: HashMap::new(),
1066 });
1067 }
1068 if !bill.finalized_data.user_consumption.get(&donor_id).unwrap().per_day.contains_key(&day_idx) {
1069 bill.finalized_data.user_consumption.get_mut(&donor_id).unwrap().per_day.insert(day_idx, BillUserDayInstance {
1070 personally_consumed: HashMap::new(),
1071 specials_consumed: Vec::new(),
1072 ffa_giveouts: HashMap::new(),
1073 giveouts_to_user_id: HashMap::new(),
1074 });
1075 }
1076
1077 if !bill.finalized_data.user_consumption.get(&donor_id).unwrap().per_day.get(&day_idx).unwrap().giveouts_to_user_id.contains_key(&consumer_id) {
1078 bill.finalized_data.user_consumption.get_mut(&donor_id).unwrap().per_day.get_mut(&day_idx).unwrap().giveouts_to_user_id.insert(consumer_id, PaidFor {
1079 recipient_id: consumer_id,
1080 count_giveouts_used: HashMap::new(),
1081 budget_given: 0,
1082 budget_gotten: 0,
1083 });
1084 }
1085
1086 bill.finalized_data.user_consumption.get_mut(&donor_id).unwrap().per_day.get_mut(&day_idx).unwrap().giveouts_to_user_id.entry(consumer_id).or_insert(PaidFor {
1087 recipient_id: consumer_id,
1088 count_giveouts_used: HashMap::new(),
1089 budget_given: 0,
1090 budget_gotten: 0,
1091 }).budget_given += taken_budget;
1092 }
1093
1094 if used_up {
1096 let freeby = store.open_freebies.get_mut(&consumer_id).unwrap().remove(bidx);
1097 let pos = store.used_up_freebies.binary_search_by(|f|f.get_id().cmp(&freeby.get_id())).unwrap_or_else(|e| e);
1098 store.used_up_freebies.insert(pos, freeby);
1099 }
1100 },
1101 None => (),
1102 }
1103 },
1104 }
1105 },
1106 Purchase::FFAPurchase {
1107 unique_id,
1108 timestamp_epoch_millis,
1109 item_id,
1110 freeby_id,
1111 donor,
1112 } => {
1113 let day_idx : usize = bill_cpy.get_day_index(timestamp_epoch_millis);
1114 let item: Item = store.items.get(&item_id).unwrap().clone();
1115
1116
1117 let mut bill: &mut Bill = store.bills.get_mut(bill_idx).unwrap();
1118 if !bill.finalized_data.all_users.contains_key(&donor) {
1119 bill.finalized_data.all_users.insert(donor, user.clone());
1120 bill.finalized_data.user_consumption.insert(donor, BillUserInstance {
1121 user_id: donor,
1122 per_day: HashMap::new(),
1123 });
1124 }
1125
1126 if !bill.finalized_data.all_items.contains_key(&item_id) {
1127 bill.finalized_data.all_items.insert(item_id, item.clone());
1128 }
1129
1130
1131 if !bill.finalized_data.user_consumption[&donor].per_day.contains_key(&day_idx) {
1132 bill.finalized_data.user_consumption.get_mut(&donor).unwrap().per_day.insert(day_idx, BillUserDayInstance {
1133 personally_consumed: HashMap::new(),
1134 specials_consumed: Vec::new(),
1135 ffa_giveouts: HashMap::new(),
1136 giveouts_to_user_id: HashMap::new(),
1137 });
1138 }
1139
1140 *bill.finalized_data.user_consumption.get_mut(&donor).unwrap().per_day.get_mut(&day_idx).unwrap().ffa_giveouts.entry(item_id).or_insert(0u32) += 1;
1141 },
1142 }
1143
1144 }
1145 }
1146
1147 {
1151 store.remove_purchases_indices(purchase_indices);
1152 }
1153 true
1154
1155 },
1157 &BLEvents::DeleteUnfinishedBill { timestamp_from, timestamp_to } => {
1158 let idx_opt : Option<usize> = store.bills.iter().position(|b|b.timestamp_to == timestamp_to && b.timestamp_from == timestamp_from);
1159 match idx_opt {
1160 Some(idx) => {
1161 let _ = store.bills.remove(idx);
1162 return true;
1163 }
1164 None => {
1165 return false;
1166 },
1167 }
1168 },
1169 &BLEvents::SetPriceForSpecial { unique_id, price } => {
1170 let x = store.get_purchase_mut(unique_id).unwrap();
1171
1172 match x {
1173 &mut Purchase::SpecialPurchase{
1174 ref unique_id,
1175 ref timestamp_epoch_millis,
1176 ref special_name,
1177 ref mut specialcost,
1178 ref consumer_id,
1179 } => {
1180 *specialcost = Some(price);
1181 return true;
1182 },
1183 _ => return false,
1184 }
1185 },
1186 &BLEvents::UpdateBill { ref timestamp_from, ref timestamp_to, ref comment, ref users, ref users_that_will_not_be_billed } => {
1187 match store.get_mut_bill(*timestamp_from, *timestamp_to) {
1188 Some(b) => {
1189 b.comment = comment.to_string();
1190 b.users = users.clone();
1191 b.users_that_will_not_be_billed = users_that_will_not_be_billed.clone();
1192 return true;
1193 },
1194 None => {return false;},
1195 }
1196 },
1197 };
1198 }
1199}
1200
1201
1202#[cfg(test)]
1203mod tests {
1204 use rustix_event_shop::BLEvents;
1205 use serde_json;
1206 use std;
1207
1208
1209 #[test]
1210 fn hashset_test() {
1211 let mut hashset = std::collections::HashSet::new();
1212
1213 let str_1 = "Hello World".to_string();
1214
1215 hashset.insert(str_1);
1216
1217 let str_2_a = "Hello".to_string();
1218 let str_2_b = " World".to_string();
1219 let str_2 = str_2_a + &str_2_b;
1220
1221 assert!(hashset.remove(&str_2))
1222 }
1223
1224
1225 #[test]
1226 fn events_serialize_and_deserialize_raw() {
1227 let v = vec![
1228 BLEvents::CreateItem {
1229 itemname: "beer".to_string(),
1230 price_cents: 95u32,
1231 category: None,
1232 },
1233 BLEvents::CreateItem {
1234 itemname: "beer 2".to_string(),
1235 price_cents: 95u32,
1236 category: None,
1237 },
1238 BLEvents::DeleteItem { item_id: 2u32 },
1239 BLEvents::CreateUser {
1240 username: "klaus".to_string(),
1241 },
1242 BLEvents::MakeSimplePurchase {
1243 item_id: 1u32,
1244 user_id: 1u32,
1245 timestamp: 123456789i64,
1246 },
1247 ];
1248
1249 let json = serde_json::to_string(&v).unwrap();
1251 let reparsed_content: Vec<BLEvents> = serde_json::from_str(&json).unwrap();
1252 assert_eq!(reparsed_content, v);
1253 }
1254
1255 #[test]
1256 fn events_serialize_and_deserialize_packed() {
1257 let v = vec![
1258 BLEvents::CreateItem {
1259 itemname: "beer".to_string(),
1260 price_cents: 95u32,
1261 category: None,
1262 },
1263 BLEvents::CreateItem {
1264 itemname: "beer 2".to_string(),
1265 price_cents: 95u32,
1266 category: None,
1267 },
1268 BLEvents::DeleteItem { item_id: 2u32 },
1269 BLEvents::CreateUser {
1270 username: "klaus".to_string(),
1271 },
1272 BLEvents::MakeSimplePurchase {
1273 item_id: 1u32,
1274 user_id: 1u32,
1275 timestamp: 123456789i64,
1276 },
1277 ];
1278
1279 let json_bytes = serde_json::to_string(&v).unwrap().as_bytes().to_vec();
1281 let reparsed_content: Vec<BLEvents> =
1282 serde_json::from_str(std::str::from_utf8(json_bytes.as_ref()).unwrap()).unwrap();
1283 assert_eq!(reparsed_content, v);
1284 }
1285}