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.
- checksums.yaml +7 -0
- data/Gemfile +10 -0
- data/README.md +316 -0
- data/bin/simulate +209 -0
- data/lib/clover_sandbox_simulator/configuration.rb +51 -0
- data/lib/clover_sandbox_simulator/data/restaurant/categories.json +39 -0
- data/lib/clover_sandbox_simulator/data/restaurant/discounts.json +39 -0
- data/lib/clover_sandbox_simulator/data/restaurant/items.json +238 -0
- data/lib/clover_sandbox_simulator/data/restaurant/modifiers.json +62 -0
- data/lib/clover_sandbox_simulator/data/restaurant/tenders.json +41 -0
- data/lib/clover_sandbox_simulator/generators/data_loader.rb +54 -0
- data/lib/clover_sandbox_simulator/generators/entity_generator.rb +164 -0
- data/lib/clover_sandbox_simulator/generators/order_generator.rb +540 -0
- data/lib/clover_sandbox_simulator/services/base_service.rb +111 -0
- data/lib/clover_sandbox_simulator/services/clover/customer_service.rb +82 -0
- data/lib/clover_sandbox_simulator/services/clover/discount_service.rb +58 -0
- data/lib/clover_sandbox_simulator/services/clover/employee_service.rb +82 -0
- data/lib/clover_sandbox_simulator/services/clover/inventory_service.rb +120 -0
- data/lib/clover_sandbox_simulator/services/clover/order_service.rb +170 -0
- data/lib/clover_sandbox_simulator/services/clover/payment_service.rb +123 -0
- data/lib/clover_sandbox_simulator/services/clover/services_manager.rb +49 -0
- data/lib/clover_sandbox_simulator/services/clover/tax_service.rb +53 -0
- data/lib/clover_sandbox_simulator/services/clover/tender_service.rb +117 -0
- data/lib/clover_sandbox_simulator.rb +43 -0
- metadata +195 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CloverSandboxSimulator
|
|
4
|
+
module Services
|
|
5
|
+
module Clover
|
|
6
|
+
# Manages Clover tax rates
|
|
7
|
+
class TaxService < BaseService
|
|
8
|
+
# Fetch all tax rates
|
|
9
|
+
def get_tax_rates
|
|
10
|
+
logger.info "Fetching tax rates..."
|
|
11
|
+
response = request(:get, endpoint("tax_rates"))
|
|
12
|
+
elements = response&.dig("elements") || []
|
|
13
|
+
logger.info "Found #{elements.size} tax rates"
|
|
14
|
+
elements
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Get default tax rate
|
|
18
|
+
def default_tax_rate
|
|
19
|
+
rates = get_tax_rates
|
|
20
|
+
# Find default rate or return first active one
|
|
21
|
+
rates.find { |r| r["isDefault"] == true } || rates.first
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Create a tax rate
|
|
25
|
+
def create_tax_rate(name:, rate:, is_default: false)
|
|
26
|
+
logger.info "Creating tax rate: #{name} (#{rate}%)"
|
|
27
|
+
|
|
28
|
+
# Rate is stored as basis points (8.25% = 825000)
|
|
29
|
+
rate_basis_points = (rate * 100_000).to_i
|
|
30
|
+
|
|
31
|
+
request(:post, endpoint("tax_rates"), payload: {
|
|
32
|
+
"name" => name,
|
|
33
|
+
"rate" => rate_basis_points,
|
|
34
|
+
"isDefault" => is_default,
|
|
35
|
+
"taxType" => "VAT_EXEMPT" # For US sales tax
|
|
36
|
+
})
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Delete a tax rate
|
|
40
|
+
def delete_tax_rate(tax_rate_id)
|
|
41
|
+
logger.info "Deleting tax rate: #{tax_rate_id}"
|
|
42
|
+
request(:delete, endpoint("tax_rates/#{tax_rate_id}"))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Calculate tax for an amount
|
|
46
|
+
def calculate_tax(subtotal, tax_rate = nil)
|
|
47
|
+
rate = tax_rate || config.tax_rate
|
|
48
|
+
(subtotal * rate / 100.0).round
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CloverSandboxSimulator
|
|
4
|
+
module Services
|
|
5
|
+
module Clover
|
|
6
|
+
# Manages Clover payment tenders (Cash, Gift Card, etc.)
|
|
7
|
+
class TenderService < BaseService
|
|
8
|
+
# Tenders that work reliably in Clover sandbox
|
|
9
|
+
# NOTE: Credit Card and Debit Card are BROKEN in sandbox - do not use!
|
|
10
|
+
SANDBOX_SAFE_TENDERS = %w[
|
|
11
|
+
com.clover.tender.cash
|
|
12
|
+
com.clover.tender.check
|
|
13
|
+
com.clover.tender.external_gift_card
|
|
14
|
+
com.clover.tender.external_payment
|
|
15
|
+
].freeze
|
|
16
|
+
|
|
17
|
+
# Fetch all tenders
|
|
18
|
+
def get_tenders
|
|
19
|
+
logger.info "Fetching tenders..."
|
|
20
|
+
response = request(:get, endpoint("tenders"))
|
|
21
|
+
elements = response&.dig("elements") || []
|
|
22
|
+
|
|
23
|
+
# Filter to enabled tenders only
|
|
24
|
+
enabled = elements.select { |t| t["enabled"] == true }
|
|
25
|
+
logger.info "Found #{enabled.size} enabled tenders"
|
|
26
|
+
enabled
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Get sandbox-safe tenders (excludes credit/debit cards)
|
|
30
|
+
def get_safe_tenders
|
|
31
|
+
tenders = get_tenders
|
|
32
|
+
|
|
33
|
+
safe = tenders.reject do |tender|
|
|
34
|
+
label = tender["label"]&.downcase || ""
|
|
35
|
+
label_key = tender["labelKey"]&.downcase || ""
|
|
36
|
+
|
|
37
|
+
# Exclude credit and debit cards - they're broken in sandbox
|
|
38
|
+
label.include?("credit") ||
|
|
39
|
+
label.include?("debit") ||
|
|
40
|
+
label_key.include?("credit") ||
|
|
41
|
+
label_key.include?("debit")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
logger.info "Found #{safe.size} sandbox-safe tenders"
|
|
45
|
+
safe
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Get a specific tender by label
|
|
49
|
+
def find_tender_by_label(label)
|
|
50
|
+
tenders = get_tenders
|
|
51
|
+
tenders.find { |t| t["label"]&.downcase == label.downcase }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Get cash tender
|
|
55
|
+
def cash_tender
|
|
56
|
+
find_tender_by_label("Cash") || get_safe_tenders.first
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Create a custom tender
|
|
60
|
+
def create_tender(label:, label_key: nil, enabled: true, opens_cash_drawer: false)
|
|
61
|
+
logger.info "Creating tender: #{label}"
|
|
62
|
+
|
|
63
|
+
payload = {
|
|
64
|
+
"label" => label,
|
|
65
|
+
"labelKey" => label_key || "com.clover.tender.#{label.downcase.gsub(/\s+/, '_')}",
|
|
66
|
+
"enabled" => enabled,
|
|
67
|
+
"opensCashDrawer" => opens_cash_drawer
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
request(:post, endpoint("tenders"), payload: payload)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Select random safe tenders for split payment
|
|
74
|
+
# Returns array of tender IDs with split percentages
|
|
75
|
+
def select_split_tenders(num_splits: nil)
|
|
76
|
+
tenders = get_safe_tenders
|
|
77
|
+
return [] if tenders.empty?
|
|
78
|
+
|
|
79
|
+
# Determine number of splits (1-3)
|
|
80
|
+
num_splits ||= rand(1..3)
|
|
81
|
+
num_splits = [num_splits, tenders.size].min
|
|
82
|
+
|
|
83
|
+
# Select random tenders
|
|
84
|
+
selected = tenders.sample(num_splits)
|
|
85
|
+
|
|
86
|
+
# Generate random split percentages that sum to 100
|
|
87
|
+
percentages = generate_split_percentages(num_splits)
|
|
88
|
+
|
|
89
|
+
selected.zip(percentages).map do |tender, percentage|
|
|
90
|
+
{ tender: tender, percentage: percentage }
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
def generate_split_percentages(count)
|
|
97
|
+
return [100] if count == 1
|
|
98
|
+
|
|
99
|
+
# Generate random split points
|
|
100
|
+
points = Array.new(count - 1) { rand(10..90) }.sort
|
|
101
|
+
|
|
102
|
+
# Calculate percentages from split points
|
|
103
|
+
percentages = []
|
|
104
|
+
prev = 0
|
|
105
|
+
points.each do |point|
|
|
106
|
+
percentages << (point - prev)
|
|
107
|
+
prev = point
|
|
108
|
+
end
|
|
109
|
+
percentages << (100 - prev)
|
|
110
|
+
|
|
111
|
+
# Ensure no percentage is less than 5%
|
|
112
|
+
percentages.map { |p| [p, 5].max }
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "zeitwerk"
|
|
4
|
+
require "logger"
|
|
5
|
+
require "json"
|
|
6
|
+
require "rest-client"
|
|
7
|
+
require "dotenv"
|
|
8
|
+
|
|
9
|
+
# Load environment variables
|
|
10
|
+
Dotenv.load
|
|
11
|
+
|
|
12
|
+
module CloverSandboxSimulator
|
|
13
|
+
class Error < StandardError; end
|
|
14
|
+
class ConfigurationError < Error; end
|
|
15
|
+
class ApiError < Error; end
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
attr_writer :configuration
|
|
19
|
+
|
|
20
|
+
def configuration
|
|
21
|
+
@configuration ||= Configuration.new
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def configure
|
|
25
|
+
yield(configuration)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def logger
|
|
29
|
+
configuration.logger
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def root
|
|
33
|
+
File.expand_path("..", __dir__)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Set up Zeitwerk autoloader
|
|
39
|
+
loader = Zeitwerk::Loader.for_gem
|
|
40
|
+
loader.setup
|
|
41
|
+
|
|
42
|
+
# Eager load in production
|
|
43
|
+
loader.eager_load if ENV["RACK_ENV"] == "production"
|
metadata
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: clover_sandbox_simulator
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- dan1d
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-01-31 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rest-client
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.1'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.1'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: dotenv
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '3.0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '3.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: thor
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '1.3'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '1.3'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: faker
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '3.2'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '3.2'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: zeitwerk
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '2.6'
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '2.6'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: rspec
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '3.12'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '3.12'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: rubocop
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - "~>"
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '1.50'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - "~>"
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '1.50'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: webmock
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - "~>"
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '3.18'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - "~>"
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '3.18'
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: vcr
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - "~>"
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '6.1'
|
|
132
|
+
type: :development
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - "~>"
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '6.1'
|
|
139
|
+
description: A Ruby gem for simulating Point of Sale operations in Clover sandbox
|
|
140
|
+
environments. Generates realistic restaurant orders, payments, and transaction data
|
|
141
|
+
for testing integrations with Clover's API.
|
|
142
|
+
email:
|
|
143
|
+
- dan1d@users.noreply.github.com
|
|
144
|
+
executables:
|
|
145
|
+
- simulate
|
|
146
|
+
extensions: []
|
|
147
|
+
extra_rdoc_files: []
|
|
148
|
+
files:
|
|
149
|
+
- Gemfile
|
|
150
|
+
- README.md
|
|
151
|
+
- bin/simulate
|
|
152
|
+
- lib/clover_sandbox_simulator.rb
|
|
153
|
+
- lib/clover_sandbox_simulator/configuration.rb
|
|
154
|
+
- lib/clover_sandbox_simulator/data/restaurant/categories.json
|
|
155
|
+
- lib/clover_sandbox_simulator/data/restaurant/discounts.json
|
|
156
|
+
- lib/clover_sandbox_simulator/data/restaurant/items.json
|
|
157
|
+
- lib/clover_sandbox_simulator/data/restaurant/modifiers.json
|
|
158
|
+
- lib/clover_sandbox_simulator/data/restaurant/tenders.json
|
|
159
|
+
- lib/clover_sandbox_simulator/generators/data_loader.rb
|
|
160
|
+
- lib/clover_sandbox_simulator/generators/entity_generator.rb
|
|
161
|
+
- lib/clover_sandbox_simulator/generators/order_generator.rb
|
|
162
|
+
- lib/clover_sandbox_simulator/services/base_service.rb
|
|
163
|
+
- lib/clover_sandbox_simulator/services/clover/customer_service.rb
|
|
164
|
+
- lib/clover_sandbox_simulator/services/clover/discount_service.rb
|
|
165
|
+
- lib/clover_sandbox_simulator/services/clover/employee_service.rb
|
|
166
|
+
- lib/clover_sandbox_simulator/services/clover/inventory_service.rb
|
|
167
|
+
- lib/clover_sandbox_simulator/services/clover/order_service.rb
|
|
168
|
+
- lib/clover_sandbox_simulator/services/clover/payment_service.rb
|
|
169
|
+
- lib/clover_sandbox_simulator/services/clover/services_manager.rb
|
|
170
|
+
- lib/clover_sandbox_simulator/services/clover/tax_service.rb
|
|
171
|
+
- lib/clover_sandbox_simulator/services/clover/tender_service.rb
|
|
172
|
+
homepage: https://github.com/dan1d/clover_sandbox_simulator
|
|
173
|
+
licenses:
|
|
174
|
+
- MIT
|
|
175
|
+
metadata: {}
|
|
176
|
+
post_install_message:
|
|
177
|
+
rdoc_options: []
|
|
178
|
+
require_paths:
|
|
179
|
+
- lib
|
|
180
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
181
|
+
requirements:
|
|
182
|
+
- - ">="
|
|
183
|
+
- !ruby/object:Gem::Version
|
|
184
|
+
version: 3.0.0
|
|
185
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
186
|
+
requirements:
|
|
187
|
+
- - ">="
|
|
188
|
+
- !ruby/object:Gem::Version
|
|
189
|
+
version: '0'
|
|
190
|
+
requirements: []
|
|
191
|
+
rubygems_version: 3.4.10
|
|
192
|
+
signing_key:
|
|
193
|
+
specification_version: 4
|
|
194
|
+
summary: Clover Sandbox Simulator for generating realistic restaurant data
|
|
195
|
+
test_files: []
|