rustix_bl/
rustix_event_shop.rs

1// An attribute to hide warnings for unused code.
2#![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, //timestamps uniquely identify a bill
111        timestamp_to: i64,
112    },
113    ExportBill {
114        timestamp_from: i64, //timestamps uniquely identify a bill
115        timestamp_to: i64,
116    },
117    DeleteUnfinishedBill {
118        timestamp_from: i64, //timestamps uniquely identify a bill
119        timestamp_to: i64,
120    },
121    SetPriceForSpecial {
122        unique_id: u64,
123        price: u32,
124    },
125    UpdateBill {
126        timestamp_from: i64, //timestamps uniquely identify a bill
127        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                //check if all specials are set with price and all users are too
204                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                //add per user scores and top items:
295                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                //add to user scores and reextract:
304                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 highlight_in_ui changed, update store.highlighted_users
344                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                        //potentially remove category, if no one else is sharing that category
380                        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                        //remove from personal drink scores and possibly reextract top drinks
389                        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                //if highlight_in_ui, update store.highlighted_users
425                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                //remove from user hashmap
435                let _ = store.users.get_mut(&user_id).map(|it|it.deleted = true);
436
437                //remove user item score
438                let _ = store.drink_scores_per_user.remove(&user_id);
439
440                //remove user top items
441                let _ = store.top_drinks_per_user.remove(&user_id);
442
443                //remove from user score tree
444                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                //remove from top users and renew topusers if that is the case
461                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            //should return true if it was the most recent purchase
474
475            &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                // add purchase to vector
484                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                // increase item score for user
494                if let Some(ref mut drinkscore) = store.drink_scores_per_user.get_mut(&user_id) {
495                    drinkscore.increment_by_one(item_id);
496                    // if not in top items, potentially extract new set
497                    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                // increase user score
509                store.top_user_scores.increment_by_one(user_id);
510
511                // if not in top users, potentially extract new set
512                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                //increase cost map value
525                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                //increase count map value
547                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                //get new id
602                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                //add to purchase vector
613                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                    //decrease existing freeby
626                freeby.decrement();
627                    println!("Freeby is : {:?}", freeby);
628            }
629
630                //potentially move used up freeby to "old" stack
631                if freeby.left() == 0 {
632                    //add to new vec
633                    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                //add to cost / count map of donor
640
641                {
642                    //increase cost map value
643                    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                    //increase count map value
665                    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                /*
682
683
684                let was_in_before = store.top_users.contains(&user_id);
685
686                // increase item score for user
687                if let Some(ref mut drinkscore) = store.drink_scores_per_user.get_mut(&user_id) {
688                    drinkscore.increment_by_one(item_id);
689                    // if not in top items, potentially extract new set
690                    if let Some(topitems) = store.top_drinks_per_user.get_mut(&user_id) {
691                        if !(topitems.contains(&item_id)) {
692                            *topitems = hashset(
693                                drinkscore
694                                    .extract_top(config.top_drinks_per_user)
695                                    .as_slice(),
696                            );
697                        }
698                    }
699                }
700
701                // increase user score
702                store.top_user_scores.increment_by_one(user_id);
703
704                // if not in top users, potentially extract new set
705                if !(store.top_users.contains(&user_id)) {
706                    println!("not in top users for userid = {}", user_id);
707                    store.top_users = hashset(
708                        store
709                            .top_user_scores
710                            .extract_top(config.users_in_top_users)
711                            .as_slice(),
712                    );
713                } else {
714
715                }
716
717                //increase cost map value
718                let alt_hashmap_1 = HashMap::new();
719                let username = store.users.get(&user_id).unwrap().username.to_string();
720                let itemname = store.items.get(&item_id).unwrap().name.to_string();
721                let user_key = (user_id, store.users.get(&user_id).unwrap().username.to_string());
722                let item_key = (item_id, store.items.get(&item_id).unwrap().name.to_string());
723                let mut old_cost_map = store
724                    .balance_cost_per_user
725                    .remove(&user_key)
726                    .unwrap_or(alt_hashmap_1);
727                let old_cost_value = *old_cost_map.get(&item_key).unwrap_or(&0);
728                old_cost_map.insert(
729                    item_key,
730                    old_cost_value
731                        + store
732                        .items
733                        .get(&item_id)
734                        .map(|item| item.cost_cents)
735                        .unwrap_or(0),
736                );
737                store.balance_cost_per_user.insert(user_key, old_cost_map);
738
739                //increase count map value
740                let alt_hashmap_2 = HashMap::new();
741                let user_key2 : (u32, String) = (user_id, username.to_string());
742                let user_key3 : (u32, String) = (user_id, username.to_string());
743                let item_key2 : (u32, String) = (item_id, itemname.to_string());
744                let item_key3 : (u32, String) = (item_id, itemname.to_string());
745                let mut old_count_map = store
746                    .balance_count_per_user
747                    .remove(&user_key2)
748                    .unwrap_or(alt_hashmap_2);
749                let old_count_value = *old_count_map.get(&item_key2).unwrap_or(&0);
750                old_count_map.insert(item_key3, old_count_value + 1);
751                store.balance_count_per_user.insert(user_key3, old_count_map);
752
753
754                let is_in_now = store.top_users.contains(&user_id);
755
756                ((!was_in_before) & (is_in_now))*/
757            },
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                //remove purchase from list
830                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                //remove cost and count lists:
843                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            //removes purchases from global list and also recomputes counts
872            &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                //compute users and create copies
887                {
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                                //first, get a count giveout
951                                match count_freeby_idx {
952                                    Some(cidx) => {
953                                        //if count giveout was found, add purchase under donor as a PaidFor
954                                        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                                        //removed used up freeby
992                                        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                                        //add purchase under consumer
1000                                        *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                                        //if a count giveout does not exist, find a budget giveout, and add decrease / increase if possible
1008                                        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                                                //remove budget in freeby
1020                                                {
1021                                                    store.open_freebies.get_mut(&consumer_id).unwrap().get_mut(bidx).unwrap().remove_budget_by(taken_budget);
1022                                                }
1023
1024                                                {
1025                                                    //add budget (min(freeby.left, max(0, item.cost))) positive to recipient
1026                                                    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                                                    //add budget negative to donor
1061                                                    //add donor if he does not yet exist
1062                                                    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                                                //removed used up freeby
1095                                                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                //TODO: balance_cost_per_user also has to be reduced for each purchase
1148
1149                //remove purchases from purchases vec
1150                {
1151                    store.remove_purchases_indices(purchase_indices);
1152                }
1153                true
1154
1155                //TODO: Open question: how will purchase rank be recomputed? Currently kept
1156            },
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        // Serialize it to a JSON string.
1250        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        // Serialize it to a JSON string.
1280        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}