clover_sandbox_simulator 1.0.0

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.
Files changed (25) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +10 -0
  3. data/README.md +316 -0
  4. data/bin/simulate +209 -0
  5. data/lib/clover_sandbox_simulator/configuration.rb +51 -0
  6. data/lib/clover_sandbox_simulator/data/restaurant/categories.json +39 -0
  7. data/lib/clover_sandbox_simulator/data/restaurant/discounts.json +39 -0
  8. data/lib/clover_sandbox_simulator/data/restaurant/items.json +238 -0
  9. data/lib/clover_sandbox_simulator/data/restaurant/modifiers.json +62 -0
  10. data/lib/clover_sandbox_simulator/data/restaurant/tenders.json +41 -0
  11. data/lib/clover_sandbox_simulator/generators/data_loader.rb +54 -0
  12. data/lib/clover_sandbox_simulator/generators/entity_generator.rb +164 -0
  13. data/lib/clover_sandbox_simulator/generators/order_generator.rb +540 -0
  14. data/lib/clover_sandbox_simulator/services/base_service.rb +111 -0
  15. data/lib/clover_sandbox_simulator/services/clover/customer_service.rb +82 -0
  16. data/lib/clover_sandbox_simulator/services/clover/discount_service.rb +58 -0
  17. data/lib/clover_sandbox_simulator/services/clover/employee_service.rb +82 -0
  18. data/lib/clover_sandbox_simulator/services/clover/inventory_service.rb +120 -0
  19. data/lib/clover_sandbox_simulator/services/clover/order_service.rb +170 -0
  20. data/lib/clover_sandbox_simulator/services/clover/payment_service.rb +123 -0
  21. data/lib/clover_sandbox_simulator/services/clover/services_manager.rb +49 -0
  22. data/lib/clover_sandbox_simulator/services/clover/tax_service.rb +53 -0
  23. data/lib/clover_sandbox_simulator/services/clover/tender_service.rb +117 -0
  24. data/lib/clover_sandbox_simulator.rb +43 -0
  25. metadata +195 -0
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faker"
4
+
5
+ module CloverSandboxSimulator
6
+ module Services
7
+ module Clover
8
+ # Manages Clover customers
9
+ class CustomerService < BaseService
10
+ # Fetch all customers
11
+ def get_customers
12
+ logger.info "Fetching customers..."
13
+ response = request(:get, endpoint("customers"))
14
+ elements = response&.dig("elements") || []
15
+ logger.info "Found #{elements.size} customers"
16
+ elements
17
+ end
18
+
19
+ # Get a specific customer
20
+ def get_customer(customer_id)
21
+ request(:get, endpoint("customers/#{customer_id}"))
22
+ end
23
+
24
+ # Create a customer
25
+ def create_customer(first_name:, last_name:, email: nil, phone: nil)
26
+ logger.info "Creating customer: #{first_name} #{last_name}"
27
+
28
+ payload = {
29
+ "firstName" => first_name,
30
+ "lastName" => last_name
31
+ }
32
+ payload["emailAddresses"] = [{ "emailAddress" => email }] if email
33
+ payload["phoneNumbers"] = [{ "phoneNumber" => phone }] if phone
34
+
35
+ request(:post, endpoint("customers"), payload: payload)
36
+ end
37
+
38
+ # Create sample customers if needed
39
+ def ensure_customers(count: 10)
40
+ existing = get_customers
41
+ return existing if existing.size >= count
42
+
43
+ needed = count - existing.size
44
+ logger.info "Creating #{needed} sample customers..."
45
+
46
+ new_customers = []
47
+ needed.times do
48
+ first = Faker::Name.first_name
49
+ last = Faker::Name.last_name
50
+ # Use example.com domain - Clover rejects .test domains
51
+ # Remove special chars, collapse dots, strip leading/trailing dots
52
+ safe_first = first.downcase.gsub(/[^a-z0-9]/, "")
53
+ safe_last = last.downcase.gsub(/[^a-z0-9]/, "")
54
+ customer = create_customer(
55
+ first_name: first,
56
+ last_name: last,
57
+ email: "#{safe_first}.#{safe_last}@example.com",
58
+ phone: Faker::PhoneNumber.cell_phone
59
+ )
60
+ new_customers << customer if customer
61
+ end
62
+
63
+ existing + new_customers
64
+ end
65
+
66
+ # Get a random customer (70% chance of returning a customer, 30% anonymous)
67
+ def random_customer
68
+ return nil if rand < 0.3 # 30% anonymous orders
69
+
70
+ customers = get_customers
71
+ customers.sample
72
+ end
73
+
74
+ # Delete a customer
75
+ def delete_customer(customer_id)
76
+ logger.info "Deleting customer: #{customer_id}"
77
+ request(:delete, endpoint("customers/#{customer_id}"))
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CloverSandboxSimulator
4
+ module Services
5
+ module Clover
6
+ # Manages Clover discounts
7
+ class DiscountService < BaseService
8
+ # Fetch all discounts
9
+ def get_discounts
10
+ logger.info "Fetching discounts..."
11
+ response = request(:get, endpoint("discounts"))
12
+ elements = response&.dig("elements") || []
13
+ logger.info "Found #{elements.size} discounts"
14
+ elements
15
+ end
16
+
17
+ # Get a specific discount
18
+ def get_discount(discount_id)
19
+ request(:get, endpoint("discounts/#{discount_id}"))
20
+ end
21
+
22
+ # Create a fixed amount discount
23
+ def create_fixed_discount(name:, amount:)
24
+ logger.info "Creating fixed discount: #{name} ($#{amount / 100.0})"
25
+
26
+ request(:post, endpoint("discounts"), payload: {
27
+ "name" => name,
28
+ "amount" => -amount.abs # Clover expects negative for discounts
29
+ })
30
+ end
31
+
32
+ # Create a percentage discount
33
+ def create_percentage_discount(name:, percentage:)
34
+ logger.info "Creating percentage discount: #{name} (#{percentage}%)"
35
+
36
+ request(:post, endpoint("discounts"), payload: {
37
+ "name" => name,
38
+ "percentage" => percentage
39
+ })
40
+ end
41
+
42
+ # Delete a discount
43
+ def delete_discount(discount_id)
44
+ logger.info "Deleting discount: #{discount_id}"
45
+ request(:delete, endpoint("discounts/#{discount_id}"))
46
+ end
47
+
48
+ # Select a random discount (30% chance of returning nil)
49
+ def random_discount
50
+ return nil if rand < 0.7 # 70% chance of no discount
51
+
52
+ discounts = get_discounts
53
+ discounts.sample
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faker"
4
+
5
+ module CloverSandboxSimulator
6
+ module Services
7
+ module Clover
8
+ # Manages Clover employees
9
+ class EmployeeService < BaseService
10
+ # Note: OWNER and ADMIN roles may not be available in sandbox
11
+ ROLES = %w[MANAGER EMPLOYEE].freeze
12
+
13
+ # Fetch all employees
14
+ def get_employees
15
+ logger.info "Fetching employees..."
16
+ response = request(:get, endpoint("employees"))
17
+ elements = response&.dig("elements") || []
18
+ # Filter active employees
19
+ active = elements.select { |e| e["deleted"] != true }
20
+ logger.info "Found #{active.size} active employees"
21
+ active
22
+ end
23
+
24
+ # Get a specific employee
25
+ def get_employee(employee_id)
26
+ request(:get, endpoint("employees/#{employee_id}"))
27
+ end
28
+
29
+ # Create an employee
30
+ def create_employee(name:, email: nil, role: "EMPLOYEE", pin: nil)
31
+ logger.info "Creating employee: #{name}"
32
+
33
+ payload = {
34
+ "name" => name,
35
+ "role" => role
36
+ }
37
+ payload["email"] = email if email
38
+ payload["pin"] = pin if pin
39
+
40
+ request(:post, endpoint("employees"), payload: payload)
41
+ end
42
+
43
+ # Create sample employees if needed
44
+ def ensure_employees(count: 3)
45
+ existing = get_employees
46
+ return existing if existing.size >= count
47
+
48
+ needed = count - existing.size
49
+ logger.info "Creating #{needed} sample employees..."
50
+
51
+ new_employees = []
52
+ needed.times do
53
+ name = Faker::Name.name
54
+ # Use example.com domain - Clover rejects .test domains
55
+ # Remove special chars, collapse dots, strip leading/trailing dots
56
+ safe_name = name.downcase.gsub(/[^a-z0-9]/, ".").gsub(/\.+/, ".").gsub(/^\.|\.$/, "")
57
+ employee = create_employee(
58
+ name: name,
59
+ email: "#{safe_name}@example.com",
60
+ role: ROLES.sample
61
+ )
62
+ new_employees << employee if employee
63
+ end
64
+
65
+ existing + new_employees
66
+ end
67
+
68
+ # Get a random employee
69
+ def random_employee
70
+ employees = get_employees
71
+ employees.sample
72
+ end
73
+
74
+ # Delete an employee
75
+ def delete_employee(employee_id)
76
+ logger.info "Deleting employee: #{employee_id}"
77
+ request(:delete, endpoint("employees/#{employee_id}"))
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CloverSandboxSimulator
4
+ module Services
5
+ module Clover
6
+ # Manages Clover inventory: categories, items, modifiers
7
+ class InventoryService < BaseService
8
+ # Fetch all categories
9
+ def get_categories
10
+ logger.info "Fetching categories..."
11
+ response = request(:get, endpoint("categories"))
12
+ elements = response&.dig("elements") || []
13
+ logger.info "Found #{elements.size} categories"
14
+ elements
15
+ end
16
+
17
+ # Create a category
18
+ def create_category(name:, sort_order: nil)
19
+ logger.info "Creating category: #{name}"
20
+ payload = { "name" => name }
21
+ payload["sortOrder"] = sort_order if sort_order
22
+ request(:post, endpoint("categories"), payload: payload)
23
+ end
24
+
25
+ # Delete a category
26
+ def delete_category(category_id)
27
+ logger.info "Deleting category: #{category_id}"
28
+ request(:delete, endpoint("categories/#{category_id}"))
29
+ end
30
+
31
+ # Fetch all items
32
+ def get_items
33
+ logger.info "Fetching items..."
34
+ response = request(:get, endpoint("items"), params: { expand: "categories,modifierGroups" })
35
+ elements = response&.dig("elements") || []
36
+ # Filter out deleted items
37
+ active_items = elements.reject { |item| item["deleted"] == true }
38
+ logger.info "Found #{active_items.size} active items"
39
+ active_items
40
+ end
41
+
42
+ # Create an item
43
+ def create_item(name:, price:, category_id: nil, sku: nil, hidden: false)
44
+ logger.info "Creating item: #{name} ($#{price / 100.0})"
45
+
46
+ payload = {
47
+ "name" => name,
48
+ "price" => price,
49
+ "priceType" => "FIXED",
50
+ "hidden" => hidden,
51
+ "defaultTaxRates" => true
52
+ }
53
+ payload["sku"] = sku if sku
54
+
55
+ item = request(:post, endpoint("items"), payload: payload)
56
+
57
+ # Associate with category if provided
58
+ if item && category_id
59
+ associate_item_with_category(item["id"], category_id)
60
+ end
61
+
62
+ item
63
+ end
64
+
65
+ # Associate item with category
66
+ def associate_item_with_category(item_id, category_id)
67
+ logger.debug "Associating item #{item_id} with category #{category_id}"
68
+ request(:post, endpoint("category_items"), payload: {
69
+ "elements" => [{ "item" => { "id" => item_id }, "category" => { "id" => category_id } }]
70
+ })
71
+ end
72
+
73
+ # Delete an item
74
+ def delete_item(item_id)
75
+ logger.info "Deleting item: #{item_id}"
76
+ request(:delete, endpoint("items/#{item_id}"))
77
+ end
78
+
79
+ # Fetch all modifier groups
80
+ def get_modifier_groups
81
+ logger.info "Fetching modifier groups..."
82
+ response = request(:get, endpoint("modifier_groups"), params: { expand: "modifiers" })
83
+ elements = response&.dig("elements") || []
84
+ logger.info "Found #{elements.size} modifier groups"
85
+ elements
86
+ end
87
+
88
+ # Create a modifier group
89
+ def create_modifier_group(name:, min_required: 0, max_allowed: nil)
90
+ logger.info "Creating modifier group: #{name}"
91
+ payload = {
92
+ "name" => name,
93
+ "minRequired" => min_required
94
+ }
95
+ payload["maxAllowed"] = max_allowed if max_allowed
96
+ request(:post, endpoint("modifier_groups"), payload: payload)
97
+ end
98
+
99
+ # Create a modifier within a group
100
+ def create_modifier(modifier_group_id:, name:, price: 0)
101
+ logger.info "Creating modifier: #{name} in group #{modifier_group_id}"
102
+ request(:post, endpoint("modifier_groups/#{modifier_group_id}/modifiers"), payload: {
103
+ "name" => name,
104
+ "price" => price
105
+ })
106
+ end
107
+
108
+ # Delete all categories and items
109
+ def delete_all
110
+ logger.warn "Deleting all inventory..."
111
+
112
+ get_items.each { |item| delete_item(item["id"]) }
113
+ get_categories.each { |cat| delete_category(cat["id"]) }
114
+
115
+ logger.info "All inventory deleted"
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,170 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CloverSandboxSimulator
4
+ module Services
5
+ module Clover
6
+ # Manages Clover orders and line items
7
+ class OrderService < BaseService
8
+ DINING_OPTIONS = %w[HERE TO_GO DELIVERY].freeze
9
+
10
+ # Fetch all orders
11
+ def get_orders(limit: 50, offset: 0, filter: nil)
12
+ logger.info "Fetching orders..."
13
+ params = { limit: limit, offset: offset }
14
+ params[:filter] = filter if filter
15
+
16
+ response = request(:get, endpoint("orders"), params: params)
17
+ response&.dig("elements") || []
18
+ end
19
+
20
+ # Get a single order with expanded data
21
+ def get_order(order_id)
22
+ request(:get, endpoint("orders/#{order_id}"), params: {
23
+ expand: "lineItems,discounts,payments,customers"
24
+ })
25
+ end
26
+
27
+ # Create an order shell
28
+ def create_order(employee_id: nil, customer_id: nil)
29
+ logger.info "Creating order..."
30
+
31
+ payload = {}
32
+ payload["employee"] = { "id" => employee_id } if employee_id
33
+
34
+ order = request(:post, endpoint("orders"), payload: payload)
35
+
36
+ if order && customer_id
37
+ add_customer_to_order(order["id"], customer_id)
38
+ end
39
+
40
+ order
41
+ end
42
+
43
+ # Add line item to order
44
+ def add_line_item(order_id, item_id:, quantity: 1, note: nil)
45
+ logger.debug "Adding item #{item_id} to order #{order_id}"
46
+
47
+ payload = {
48
+ "item" => { "id" => item_id },
49
+ "quantity" => quantity
50
+ }
51
+ payload["note"] = note if note
52
+
53
+ request(:post, endpoint("orders/#{order_id}/line_items"), payload: payload)
54
+ end
55
+
56
+ # Batch add line items (adds one by one as Clover doesn't support bulk)
57
+ def add_line_items(order_id, items)
58
+ logger.info "Adding #{items.size} items to order #{order_id}"
59
+
60
+ line_items = []
61
+ items.each do |item|
62
+ line_item = add_line_item(
63
+ order_id,
64
+ item_id: item[:item_id],
65
+ quantity: item[:quantity] || 1,
66
+ note: item[:note]
67
+ )
68
+ line_items << line_item if line_item
69
+ end
70
+
71
+ line_items
72
+ end
73
+
74
+ # Apply discount to order
75
+ def apply_discount(order_id, discount_id:, calculated_amount: nil)
76
+ logger.info "Applying discount #{discount_id} to order #{order_id}"
77
+
78
+ # Fetch discount details
79
+ discount_service = DiscountService.new(config: config)
80
+ discount = discount_service.get_discount(discount_id)
81
+
82
+ return nil unless discount
83
+
84
+ payload = { "name" => discount["name"] }
85
+
86
+ if calculated_amount
87
+ payload["amount"] = -calculated_amount.abs
88
+ elsif discount["amount"]
89
+ payload["amount"] = discount["amount"]
90
+ elsif discount["percentage"]
91
+ payload["percentage"] = discount["percentage"].to_s
92
+ end
93
+
94
+ request(:post, endpoint("orders/#{order_id}/discounts"), payload: payload)
95
+ end
96
+
97
+ # Set dining option
98
+ def set_dining_option(order_id, option)
99
+ unless DINING_OPTIONS.include?(option)
100
+ raise ArgumentError, "Invalid dining option: #{option}. Must be one of #{DINING_OPTIONS.join(', ')}"
101
+ end
102
+
103
+ request(:post, endpoint("orders/#{order_id}"), payload: { "diningOption" => option })
104
+ end
105
+
106
+ # Add customer to order
107
+ def add_customer_to_order(order_id, customer_id)
108
+ logger.debug "Adding customer #{customer_id} to order #{order_id}"
109
+ request(:post, endpoint("orders/#{order_id}"), payload: {
110
+ "customers" => { "elements" => [{ "id" => customer_id }] }
111
+ })
112
+ end
113
+
114
+ # Update order total
115
+ def update_total(order_id, total)
116
+ logger.debug "Updating order #{order_id} total to #{total}"
117
+ request(:post, endpoint("orders/#{order_id}"), payload: { "total" => total })
118
+ end
119
+
120
+ # Update order state
121
+ def update_state(order_id, state)
122
+ logger.info "Updating order #{order_id} state to #{state}"
123
+ request(:post, endpoint("orders/#{order_id}"), payload: { "state" => state })
124
+ end
125
+
126
+ # Calculate order total from line items
127
+ def calculate_total(order_id)
128
+ order = get_order(order_id)
129
+ return 0 unless order && order["lineItems"]&.dig("elements")
130
+
131
+ total = 0
132
+
133
+ order["lineItems"]["elements"].each do |line_item|
134
+ price = line_item["price"] || 0
135
+ quantity = line_item["quantity"] || 1
136
+ item_total = price * quantity
137
+
138
+ # Add modification prices
139
+ if line_item["modifications"]&.dig("elements")
140
+ line_item["modifications"]["elements"].each do |mod|
141
+ item_total += (mod["price"] || 0)
142
+ end
143
+ end
144
+
145
+ total += item_total
146
+ end
147
+
148
+ # Subtract discounts
149
+ if order["discounts"]&.dig("elements")
150
+ order["discounts"]["elements"].each do |discount|
151
+ if discount["percentage"]
152
+ total -= (total * discount["percentage"] / 100.0).round
153
+ else
154
+ total -= (discount["amount"] || 0).abs
155
+ end
156
+ end
157
+ end
158
+
159
+ total
160
+ end
161
+
162
+ # Delete an order
163
+ def delete_order(order_id)
164
+ logger.info "Deleting order: #{order_id}"
165
+ request(:delete, endpoint("orders/#{order_id}"))
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CloverSandboxSimulator
4
+ module Services
5
+ module Clover
6
+ # Manages Clover payments
7
+ # NOTE: Credit/Debit card payments are BROKEN in Clover sandbox
8
+ # Use Cash, Check, Gift Card, External Payment, Store Credit instead
9
+ class PaymentService < BaseService
10
+ # Fetch all payments
11
+ def get_payments
12
+ logger.info "Fetching payments..."
13
+ response = request(:get, endpoint("payments"))
14
+ response&.dig("elements") || []
15
+ end
16
+
17
+ # Process a payment for an order
18
+ #
19
+ # @param order_id [String] The order ID
20
+ # @param amount [Integer] Subtotal amount in cents (before tip/tax)
21
+ # @param tender_id [String] The tender ID to use
22
+ # @param employee_id [String] The employee processing payment
23
+ # @param tip_amount [Integer] Tip amount in cents
24
+ # @param tax_amount [Integer] Tax amount in cents
25
+ # @return [Hash, nil] Payment response or nil on failure
26
+ def process_payment(order_id:, amount:, tender_id:, employee_id:, tip_amount: 0, tax_amount: 0)
27
+ logger.info "Processing payment for order #{order_id}: $#{amount / 100.0} + tip $#{tip_amount / 100.0} + tax $#{tax_amount / 100.0}"
28
+
29
+ payload = {
30
+ "order" => { "id" => order_id },
31
+ "tender" => { "id" => tender_id },
32
+ "employee" => { "id" => employee_id },
33
+ "offline" => false,
34
+ "amount" => amount,
35
+ "tipAmount" => tip_amount,
36
+ "taxAmount" => tax_amount,
37
+ "transactionSettings" => {
38
+ "disableCashBack" => false,
39
+ "cloverShouldHandleReceipts" => true,
40
+ "forcePinEntryOnSwipe" => false,
41
+ "disableRestartTransactionOnFailure" => false,
42
+ "allowOfflinePayment" => false,
43
+ "approveOfflinePaymentWithoutPrompt" => false,
44
+ "forceOfflinePayment" => false,
45
+ "disableReceiptSelection" => false,
46
+ "disableDuplicateCheck" => false,
47
+ "autoAcceptPaymentConfirmations" => false,
48
+ "autoAcceptSignature" => false,
49
+ "returnResultOnTransactionComplete" => false,
50
+ "disableCreditSurcharge" => false
51
+ }
52
+ }
53
+
54
+ response = request(:post, endpoint("orders/#{order_id}/payments"), payload: payload)
55
+
56
+ if response && response["id"]
57
+ logger.info "Payment successful: #{response["id"]}"
58
+ else
59
+ logger.error "Payment failed: #{response.inspect}"
60
+ end
61
+
62
+ response
63
+ end
64
+
65
+ # Process split payment across multiple tenders
66
+ #
67
+ # @param order_id [String] The order ID
68
+ # @param total_amount [Integer] Total amount including tax (before tip)
69
+ # @param tip_amount [Integer] Total tip amount
70
+ # @param tax_amount [Integer] Tax amount
71
+ # @param employee_id [String] Employee ID
72
+ # @param splits [Array<Hash>] Array of { tender:, percentage: } hashes
73
+ # @return [Array<Hash>] Array of payment responses
74
+ def process_split_payment(order_id:, total_amount:, tip_amount:, tax_amount:, employee_id:, splits:)
75
+ logger.info "Processing split payment for order #{order_id} across #{splits.size} tenders"
76
+
77
+ payments = []
78
+ remaining_amount = total_amount
79
+ remaining_tip = tip_amount
80
+
81
+ splits.each_with_index do |split, index|
82
+ tender = split[:tender]
83
+ percentage = split[:percentage]
84
+ is_last = (index == splits.size - 1)
85
+
86
+ # Calculate this payment's portion
87
+ if is_last
88
+ payment_amount = remaining_amount
89
+ payment_tip = remaining_tip
90
+ else
91
+ payment_amount = (total_amount * percentage / 100.0).round
92
+ payment_tip = (tip_amount * percentage / 100.0).round
93
+ remaining_amount -= payment_amount
94
+ remaining_tip -= payment_tip
95
+ end
96
+
97
+ # Tax only on first payment
98
+ payment_tax = index.zero? ? tax_amount : 0
99
+
100
+ payment = process_payment(
101
+ order_id: order_id,
102
+ amount: payment_amount,
103
+ tender_id: tender["id"],
104
+ employee_id: employee_id,
105
+ tip_amount: payment_tip,
106
+ tax_amount: payment_tax
107
+ )
108
+
109
+ payments << payment if payment
110
+ end
111
+
112
+ payments
113
+ end
114
+
115
+ # Generate a random tip amount (15-25% of subtotal)
116
+ def generate_tip(subtotal)
117
+ tip_percentage = rand(15..25)
118
+ (subtotal * tip_percentage / 100.0).round
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CloverSandboxSimulator
4
+ module Services
5
+ module Clover
6
+ # Central manager for all Clover services
7
+ # Provides lazy-loaded access to all service classes
8
+ class ServicesManager
9
+ attr_reader :config
10
+
11
+ def initialize(config: nil)
12
+ @config = config || CloverSandboxSimulator.configuration
13
+ end
14
+
15
+ def inventory
16
+ @inventory ||= InventoryService.new(config: config)
17
+ end
18
+
19
+ def tender
20
+ @tender ||= TenderService.new(config: config)
21
+ end
22
+
23
+ def tax
24
+ @tax ||= TaxService.new(config: config)
25
+ end
26
+
27
+ def discount
28
+ @discount ||= DiscountService.new(config: config)
29
+ end
30
+
31
+ def order
32
+ @order ||= OrderService.new(config: config)
33
+ end
34
+
35
+ def payment
36
+ @payment ||= PaymentService.new(config: config)
37
+ end
38
+
39
+ def employee
40
+ @employee ||= EmployeeService.new(config: config)
41
+ end
42
+
43
+ def customer
44
+ @customer ||= CustomerService.new(config: config)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end