unit4-checkout 0.1.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +0 -2
- data/Gemfile.lock +1 -1
- data/lib/unit4/checkout/checkout.rb +70 -34
- data/lib/unit4/checkout/connection.rb +24 -0
- data/lib/unit4/checkout/exceptions.rb +16 -0
- data/lib/unit4/checkout/price_query.rb +16 -0
- data/lib/unit4/checkout/version.rb +1 -1
- data/lib/unit4/checkout.rb +3 -0
- metadata +10 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3539766a2698101358c85167d1f34d563461cdc23b24098439e1948c2e815fba
|
4
|
+
data.tar.gz: 40bc469e6c776039f1d2d424b73d9904e08546181af2f95a117fbd9b4e39d553
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7abf3c6c31eeaff5105c9f966d8751e14e94aeb585c31a7a6c3e42321797429ce6ae1e6a7fcd083e30801df9b4795144be22967bb5d759c81ca94af447ce146d
|
7
|
+
data.tar.gz: d5c3abb4a0ce0c7599fc5193d44d73560b7de229b1bdb8c9a1607ee71ab2dd569a860292487a9b05fac605082f950a0e6d001fc20a9e119940b03490c6e4a073
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,20 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_record"
|
4
|
-
require "erb"
|
5
|
-
|
6
3
|
class Checkout
|
7
|
-
attr_reader :
|
4
|
+
attr_reader :total
|
8
5
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
raise TypeError, "expected a Hash, got #{promotional_rules.class.name}" unless promotional_rules.is_a? Hash
|
6
|
+
def initialize(promotional_rules = {})
|
7
|
+
raise PromotionalRulesTypeError, promotional_rules.class.name unless promotional_rules.is_a? Hash
|
8
|
+
raise PromotionalRulesForbiddenKeys unless correct_keys?(promotional_rules)
|
13
9
|
|
14
10
|
@promotional_rules = promotional_rules
|
15
11
|
@total = 0
|
16
12
|
@basket = Basket.new
|
17
|
-
|
13
|
+
create_db_connection
|
14
|
+
end
|
15
|
+
|
16
|
+
def correct_keys?(promotional_rules)
|
17
|
+
(promotional_rules.keys - %i[product_discounts total_price_discount]).empty?
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_db_connection
|
21
|
+
Connection.new
|
18
22
|
end
|
19
23
|
|
20
24
|
# maybe facilitate scanning multiple items at once
|
@@ -23,46 +27,78 @@ class Checkout
|
|
23
27
|
# check for item id
|
24
28
|
add_to_basket(item)
|
25
29
|
calculate_total(item)
|
26
|
-
puts "#{item
|
30
|
+
puts "Item with ID '#{item}' has been added to the basket successfully!"
|
27
31
|
end
|
28
32
|
|
29
33
|
def calculate_total(item)
|
30
|
-
item_price =
|
31
|
-
|
32
|
-
|
34
|
+
item_price = item_price(item)
|
35
|
+
if @total_price_discount_applied_flag
|
36
|
+
item_discounted?(item) ? apply_item_and_total_discount(item, item_price) : apply_total_discount_on_item(item_price)
|
37
|
+
else
|
38
|
+
item_discounted?(item) ? apply_item_discount(item, item_price) : @total += item_price
|
39
|
+
apply_total_discount if total_price_discounted?
|
40
|
+
end
|
41
|
+
@total = @total.round(2)
|
42
|
+
end
|
43
|
+
|
44
|
+
def item_price(item)
|
45
|
+
# TODO: facilitate DB name different from products, i.e. ask gem user for db name???
|
46
|
+
PriceQuery.new(item).find_price
|
47
|
+
end
|
48
|
+
|
49
|
+
def apply_item_and_total_discount(item, item_price)
|
50
|
+
item_prom_rules = @promotional_rules[:product_discounts][item]
|
51
|
+
item_basket_count = @basket.items[item]
|
52
|
+
total_discount = (1 - @promotional_rules[:total_price_discount][:percent] / 100.00)
|
53
|
+
@total += item_and_total(item_basket_count, item_prom_rules, total_discount, item_price)
|
33
54
|
end
|
34
55
|
|
35
|
-
def
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
56
|
+
def item_and_total(item_basket_count, item_prom_rules, total_discount, item_price)
|
57
|
+
if item_basket_count == item_prom_rules[:count]
|
58
|
+
(item_basket_count * item_prom_rules[:price] - (item_basket_count - 1) * item_price) * total_discount
|
59
|
+
else
|
60
|
+
item_basket_count * item_prom_rules[:price] * total_discount
|
61
|
+
end
|
40
62
|
end
|
41
63
|
|
42
|
-
def
|
64
|
+
def apply_total_discount_on_item(item_price)
|
65
|
+
@total += item_price * (1 - @promotional_rules[:total_price_discount][:percent] / 100.00)
|
66
|
+
end
|
43
67
|
|
44
|
-
def
|
68
|
+
def item_discounted?(item)
|
69
|
+
return false unless item_in_discounts?(item)
|
45
70
|
|
46
|
-
|
47
|
-
results = ActiveRecord::Base.connection.exec_query(sql)
|
48
|
-
results if results.present?
|
71
|
+
@basket.items[item] >= @promotional_rules[:product_discounts][item][:count]
|
49
72
|
end
|
50
73
|
|
51
|
-
def
|
52
|
-
|
53
|
-
ActiveRecord::Base.establish_connection(adapter: db_config["adapter"], database: db_config["database"])
|
74
|
+
def item_in_discounts?(item)
|
75
|
+
@promotional_rules[:product_discounts] && @promotional_rules[:product_discounts][item]
|
54
76
|
end
|
55
77
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
78
|
+
def apply_item_discount(item, item_price)
|
79
|
+
item_prom_rules = @promotional_rules[:product_discounts][item]
|
80
|
+
item_basket_count = @basket.items[item]
|
81
|
+
@total += if item_basket_count == item_prom_rules[:count]
|
82
|
+
item_basket_count * item_prom_rules[:price] - (item_basket_count - 1) * item_price
|
83
|
+
else
|
84
|
+
item_prom_rules[:price]
|
85
|
+
end
|
63
86
|
end
|
64
87
|
|
65
|
-
|
88
|
+
def total_price_discounted?
|
89
|
+
return false unless total_price_in_discounts?
|
90
|
+
|
91
|
+
@total >= @promotional_rules[:total_price_discount][:price]
|
92
|
+
end
|
93
|
+
|
94
|
+
def total_price_in_discounts?
|
95
|
+
@promotional_rules[:total_price_discount] && @promotional_rules[:total_price_discount][:price]
|
96
|
+
end
|
97
|
+
|
98
|
+
def apply_total_discount
|
99
|
+
@total *= (1 - @promotional_rules[:total_price_discount][:percent] / 100.00)
|
100
|
+
@total_price_discount_applied_flag = true
|
101
|
+
end
|
66
102
|
|
67
103
|
def add_to_basket(item)
|
68
104
|
@basket.items[item] ? @basket.items[item] += 1 : @basket.items[item] = 1
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record"
|
4
|
+
require "erb"
|
5
|
+
|
6
|
+
class Connection
|
7
|
+
def initialize
|
8
|
+
db_config = setup_db_config
|
9
|
+
ActiveRecord::Base.establish_connection(adapter: db_config["adapter"], database: db_config["database"])
|
10
|
+
end
|
11
|
+
|
12
|
+
def setup_db_config
|
13
|
+
defined?(Rails) && defined?(Rails.env) ? rails_db_config : non_rails_db_config
|
14
|
+
end
|
15
|
+
|
16
|
+
def rails_db_config
|
17
|
+
Rails.application.config.database_configuration[Rails.env]
|
18
|
+
end
|
19
|
+
|
20
|
+
def non_rails_db_config
|
21
|
+
# TODO: use current database instead of development
|
22
|
+
YAML.safe_load(ERB.new(File.read("./config/database.yml")).result, aliases: true)["development"]
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class PromotionalRulesTypeError < TypeError
|
4
|
+
def initialize(class_name, msg = "expected a Hash, got ", exception_type = "custom")
|
5
|
+
msg += class_name
|
6
|
+
@exception_type = exception_type
|
7
|
+
super(msg)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class PromotionalRulesForbiddenKeys < TypeError
|
12
|
+
def initialize(msg = "expected Hash with optional keys 'product_discounts' and 'total_price_discount", exception_type = "custom")
|
13
|
+
@exception_type = exception_type
|
14
|
+
super(msg)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class PriceQuery
|
2
|
+
|
3
|
+
def initialize(item)
|
4
|
+
@sanitized_query = prepare_sql_statement(item)
|
5
|
+
end
|
6
|
+
|
7
|
+
def prepare_sql_statement(item)
|
8
|
+
# TODO: keep seen items in cache or variable !!!!!!!!!
|
9
|
+
ActiveRecord::Base.sanitize_sql_array(["SELECT 'products'.'price' FROM 'products' WHERE 'products'.'id' = ?", item])
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_price
|
13
|
+
results = ActiveRecord::Base.connection.exec_query(@sanitized_query)
|
14
|
+
results.rows.first.first if results.present?
|
15
|
+
end
|
16
|
+
end
|
data/lib/unit4/checkout.rb
CHANGED
@@ -3,6 +3,9 @@
|
|
3
3
|
require_relative "checkout/version"
|
4
4
|
require_relative "checkout/checkout"
|
5
5
|
require_relative "checkout/basket"
|
6
|
+
require_relative "checkout/connection"
|
7
|
+
require_relative "checkout/price_query"
|
8
|
+
require_relative "checkout/exceptions"
|
6
9
|
|
7
10
|
module Unit4
|
8
11
|
module Checkout
|
metadata
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unit4-checkout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Boyan Georgiev
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-07-
|
11
|
+
date: 2022-07-22 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description:
|
13
|
+
description: A Ruby gem that helps the user implement a checkout system by supplying
|
14
|
+
only the promotional rules
|
14
15
|
email:
|
15
16
|
- bbgeorgiev96@gmail.com
|
16
17
|
executables: []
|
@@ -26,10 +27,14 @@ files:
|
|
26
27
|
- lib/unit4/checkout.rb
|
27
28
|
- lib/unit4/checkout/basket.rb
|
28
29
|
- lib/unit4/checkout/checkout.rb
|
30
|
+
- lib/unit4/checkout/connection.rb
|
31
|
+
- lib/unit4/checkout/exceptions.rb
|
32
|
+
- lib/unit4/checkout/price_query.rb
|
29
33
|
- lib/unit4/checkout/version.rb
|
30
34
|
- sig/unit4/checkout.rbs
|
31
35
|
homepage: https://github.com/BoyanGeorgiev96/unit4-checkout
|
32
|
-
licenses:
|
36
|
+
licenses:
|
37
|
+
- MIT
|
33
38
|
metadata:
|
34
39
|
allowed_push_host: https://rubygems.org/
|
35
40
|
homepage_uri: https://github.com/BoyanGeorgiev96/unit4-checkout
|
@@ -52,5 +57,5 @@ requirements: []
|
|
52
57
|
rubygems_version: 3.3.3
|
53
58
|
signing_key:
|
54
59
|
specification_version: 4
|
55
|
-
summary:
|
60
|
+
summary: Checkout gem for Unit4 technical task
|
56
61
|
test_files: []
|