revel_sandbox_simulator 0.1.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/LICENSE +21 -0
- data/README.md +305 -0
- data/bin/simulate +187 -0
- data/lib/revel_sandbox_simulator/configuration.rb +109 -0
- data/lib/revel_sandbox_simulator/data/bar_nightclub/categories.json +7 -0
- data/lib/revel_sandbox_simulator/data/bar_nightclub/items.json +27 -0
- data/lib/revel_sandbox_simulator/data/bar_nightclub/tenders.json +7 -0
- data/lib/revel_sandbox_simulator/data/cafe_bakery/categories.json +7 -0
- data/lib/revel_sandbox_simulator/data/cafe_bakery/items.json +27 -0
- data/lib/revel_sandbox_simulator/data/cafe_bakery/tenders.json +7 -0
- data/lib/revel_sandbox_simulator/data/restaurant/categories.json +7 -0
- data/lib/revel_sandbox_simulator/data/restaurant/items.json +27 -0
- data/lib/revel_sandbox_simulator/data/restaurant/tenders.json +7 -0
- data/lib/revel_sandbox_simulator/data/retail_general/categories.json +7 -0
- data/lib/revel_sandbox_simulator/data/retail_general/items.json +27 -0
- data/lib/revel_sandbox_simulator/data/retail_general/tenders.json +7 -0
- data/lib/revel_sandbox_simulator/database.rb +92 -0
- data/lib/revel_sandbox_simulator/db/factories/api_requests.rb +15 -0
- data/lib/revel_sandbox_simulator/db/factories/business_types.rb +34 -0
- data/lib/revel_sandbox_simulator/db/factories/categories.rb +10 -0
- data/lib/revel_sandbox_simulator/db/factories/items.rb +12 -0
- data/lib/revel_sandbox_simulator/db/factories/simulated_orders.rb +25 -0
- data/lib/revel_sandbox_simulator/db/factories/simulated_payments.rb +14 -0
- data/lib/revel_sandbox_simulator/db/migrate/20260313000001_enable_pgcrypto.rb +7 -0
- data/lib/revel_sandbox_simulator/db/migrate/20260313000002_create_business_types.rb +16 -0
- data/lib/revel_sandbox_simulator/db/migrate/20260313000003_create_categories.rb +16 -0
- data/lib/revel_sandbox_simulator/db/migrate/20260313000004_create_items.rb +19 -0
- data/lib/revel_sandbox_simulator/db/migrate/20260313000005_create_simulated_orders.rb +27 -0
- data/lib/revel_sandbox_simulator/db/migrate/20260313000006_create_simulated_payments.rb +22 -0
- data/lib/revel_sandbox_simulator/db/migrate/20260313000007_create_api_requests.rb +22 -0
- data/lib/revel_sandbox_simulator/db/migrate/20260313000008_create_daily_summaries.rb +21 -0
- data/lib/revel_sandbox_simulator/generators/data_loader.rb +80 -0
- data/lib/revel_sandbox_simulator/generators/entity_generator.rb +61 -0
- data/lib/revel_sandbox_simulator/generators/order_generator.rb +260 -0
- data/lib/revel_sandbox_simulator/models/api_request.rb +11 -0
- data/lib/revel_sandbox_simulator/models/business_type.rb +14 -0
- data/lib/revel_sandbox_simulator/models/category.rb +15 -0
- data/lib/revel_sandbox_simulator/models/daily_summary.rb +53 -0
- data/lib/revel_sandbox_simulator/models/item.rb +18 -0
- data/lib/revel_sandbox_simulator/models/record.rb +9 -0
- data/lib/revel_sandbox_simulator/models/simulated_order.rb +20 -0
- data/lib/revel_sandbox_simulator/models/simulated_payment.rb +16 -0
- data/lib/revel_sandbox_simulator/seeder.rb +115 -0
- data/lib/revel_sandbox_simulator/services/base_service.rb +143 -0
- data/lib/revel_sandbox_simulator/services/revel/customer_service.rb +53 -0
- data/lib/revel_sandbox_simulator/services/revel/establishment_service.rb +19 -0
- data/lib/revel_sandbox_simulator/services/revel/order_service.rb +77 -0
- data/lib/revel_sandbox_simulator/services/revel/payment_service.rb +52 -0
- data/lib/revel_sandbox_simulator/services/revel/product_service.rb +87 -0
- data/lib/revel_sandbox_simulator/services/revel/services_manager.rb +45 -0
- data/lib/revel_sandbox_simulator/version.rb +5 -0
- data/lib/revel_sandbox_simulator.rb +38 -0
- metadata +335 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RevelSandboxSimulator
|
|
4
|
+
module Services
|
|
5
|
+
module Revel
|
|
6
|
+
class CustomerService < BaseService
|
|
7
|
+
CUSTOMER_PATH = "resources/Customer/"
|
|
8
|
+
|
|
9
|
+
def fetch_customers
|
|
10
|
+
fetch_all_pages(CUSTOMER_PATH)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get_customer(id)
|
|
14
|
+
request(:get, "#{CUSTOMER_PATH}#{id}/", resource_type: "Customer", resource_id: id)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def create_customer(first_name:, last_name:, email: nil, phone: nil)
|
|
18
|
+
payload = {
|
|
19
|
+
first_name: first_name,
|
|
20
|
+
last_name: last_name,
|
|
21
|
+
email: email,
|
|
22
|
+
phone_number: phone
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
request(:post, CUSTOMER_PATH, payload: payload.compact, resource_type: "Customer")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def find_customer_by_email(email)
|
|
29
|
+
result = request(:get, CUSTOMER_PATH, params: { email: email, limit: 1 })
|
|
30
|
+
objects = result["objects"] || []
|
|
31
|
+
objects.first
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def ensure_customers(count: 20)
|
|
35
|
+
existing = fetch_customers
|
|
36
|
+
needed = count - existing.size
|
|
37
|
+
return existing if needed <= 0
|
|
38
|
+
|
|
39
|
+
created = needed.times.map do |i|
|
|
40
|
+
create_customer(
|
|
41
|
+
first_name: Faker::Name.first_name,
|
|
42
|
+
last_name: Faker::Name.last_name,
|
|
43
|
+
email: "customer#{existing.size + i + 1}@example.com",
|
|
44
|
+
phone: Faker::PhoneNumber.phone_number
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
existing + created
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RevelSandboxSimulator
|
|
4
|
+
module Services
|
|
5
|
+
module Revel
|
|
6
|
+
class EstablishmentService < BaseService
|
|
7
|
+
ESTABLISHMENT_PATH = "enterprise/Establishment/"
|
|
8
|
+
|
|
9
|
+
def fetch_establishments
|
|
10
|
+
fetch_all_pages(ESTABLISHMENT_PATH)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get_establishment(id)
|
|
14
|
+
request(:get, "#{ESTABLISHMENT_PATH}#{id}/", resource_type: "Establishment", resource_id: id)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RevelSandboxSimulator
|
|
4
|
+
module Services
|
|
5
|
+
module Revel
|
|
6
|
+
class OrderService < BaseService
|
|
7
|
+
ORDER_PATH = "resources/Order/"
|
|
8
|
+
ORDER_ITEM_PATH = "resources/OrderItem/"
|
|
9
|
+
|
|
10
|
+
DINING_OPTIONS = {
|
|
11
|
+
dine_in: 0,
|
|
12
|
+
takeout: 1,
|
|
13
|
+
delivery: 2
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
def fetch_orders(params: {})
|
|
17
|
+
fetch_all_pages(ORDER_PATH, params: params)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def get_order(id)
|
|
21
|
+
request(:get, "#{ORDER_PATH}#{id}/", resource_type: "Order", resource_id: id)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def create_order(items:, dining_option: :dine_in, discount_amount: 0, establishment_id: nil)
|
|
25
|
+
est_id = establishment_id || config.establishment_id
|
|
26
|
+
payload = {
|
|
27
|
+
establishment: "/enterprise/Establishment/#{est_id}/",
|
|
28
|
+
dining_option: DINING_OPTIONS.fetch(dining_option.to_sym, 0),
|
|
29
|
+
discount: format("%.2f", discount_amount / 100.0),
|
|
30
|
+
orderInfo_id: SecureRandom.uuid
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
result = request(:post, ORDER_PATH, payload: payload, resource_type: "Order")
|
|
34
|
+
order_id = extract_id(result)
|
|
35
|
+
|
|
36
|
+
items.each do |item|
|
|
37
|
+
create_order_item(order_id: order_id, product_id: item[:product_id],
|
|
38
|
+
quantity: item.fetch(:quantity, 1), unit_price: item[:unit_price])
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
result.merge("id" => order_id, "items_created" => items.size)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def create_order_item(order_id:, product_id:, quantity: 1, unit_price: nil)
|
|
45
|
+
payload = {
|
|
46
|
+
order: "/resources/Order/#{order_id}/",
|
|
47
|
+
product: "/resources/Product/#{product_id}/",
|
|
48
|
+
quantity: quantity
|
|
49
|
+
}
|
|
50
|
+
payload[:price] = format("%.2f", unit_price / 100.0) if unit_price
|
|
51
|
+
|
|
52
|
+
request(:post, ORDER_ITEM_PATH, payload: payload, resource_type: "OrderItem")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def fetch_orders_by_date(start_date:, end_date: nil)
|
|
56
|
+
end_date ||= start_date
|
|
57
|
+
params = {
|
|
58
|
+
created_date__gte: start_date.to_s,
|
|
59
|
+
created_date__lte: "#{end_date}T23:59:59"
|
|
60
|
+
}
|
|
61
|
+
fetch_orders(params: params)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def extract_id(result)
|
|
67
|
+
return result["id"] if result["id"]
|
|
68
|
+
|
|
69
|
+
uri = result["resource_uri"]
|
|
70
|
+
return nil unless uri
|
|
71
|
+
|
|
72
|
+
uri.split("/").reject(&:empty?).last
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RevelSandboxSimulator
|
|
4
|
+
module Services
|
|
5
|
+
module Revel
|
|
6
|
+
class PaymentService < BaseService
|
|
7
|
+
PAYMENT_PATH = "resources/Payment/"
|
|
8
|
+
|
|
9
|
+
PAYMENT_TYPES = {
|
|
10
|
+
cash: 0,
|
|
11
|
+
credit_card: 1,
|
|
12
|
+
debit_card: 2,
|
|
13
|
+
gift_card: 3,
|
|
14
|
+
check: 4,
|
|
15
|
+
mobile_pay: 5
|
|
16
|
+
}.freeze
|
|
17
|
+
|
|
18
|
+
def fetch_payments(params: {})
|
|
19
|
+
fetch_all_pages(PAYMENT_PATH, params: params)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def get_payment(id)
|
|
23
|
+
request(:get, "#{PAYMENT_PATH}#{id}/", resource_type: "Payment", resource_id: id)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def create_payment(order_id:, amount:, payment_type: :credit_card, tip_amount: 0)
|
|
27
|
+
payload = {
|
|
28
|
+
order: "/resources/Order/#{order_id}/",
|
|
29
|
+
amount: format("%.2f", amount / 100.0),
|
|
30
|
+
tip: format("%.2f", tip_amount / 100.0),
|
|
31
|
+
type: PAYMENT_TYPES.fetch(payment_type.to_sym, 1)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
request(:post, PAYMENT_PATH, payload: payload, resource_type: "Payment")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def create_cash_payment(order_id:, amount:, tip_amount: 0)
|
|
38
|
+
create_payment(order_id: order_id, amount: amount, payment_type: :cash, tip_amount: tip_amount)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def refund_payment(payment_id:, amount:, reason: nil)
|
|
42
|
+
payload = {
|
|
43
|
+
amount: format("%.2f", -(amount / 100.0).abs),
|
|
44
|
+
reason: reason || "Refund"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
request(:post, PAYMENT_PATH, payload: payload, resource_type: "Payment", resource_id: payment_id)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RevelSandboxSimulator
|
|
4
|
+
module Services
|
|
5
|
+
module Revel
|
|
6
|
+
class ProductService < BaseService
|
|
7
|
+
CATEGORY_PATH = "products/ProductCategory/"
|
|
8
|
+
PRODUCT_PATH = "resources/Product/"
|
|
9
|
+
|
|
10
|
+
def fetch_categories
|
|
11
|
+
fetch_all_pages(CATEGORY_PATH)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get_category(id)
|
|
15
|
+
request(:get, "#{CATEGORY_PATH}#{id}/", resource_type: "ProductCategory", resource_id: id)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def create_category(name:, description: nil, sort_order: 0, parent_id: nil)
|
|
19
|
+
payload = {
|
|
20
|
+
name: name,
|
|
21
|
+
description: description,
|
|
22
|
+
sorting: sort_order
|
|
23
|
+
}
|
|
24
|
+
payload[:parent] = parent_id if parent_id
|
|
25
|
+
|
|
26
|
+
request(:post, CATEGORY_PATH, payload: payload, resource_type: "ProductCategory")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def update_category(id, attributes)
|
|
30
|
+
request(:patch, "#{CATEGORY_PATH}#{id}/", payload: attributes, resource_type: "ProductCategory", resource_id: id)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def find_category_by_name(name)
|
|
34
|
+
result = request(:get, CATEGORY_PATH, params: { name: name, limit: 1 })
|
|
35
|
+
objects = result["objects"] || []
|
|
36
|
+
objects.first
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def find_or_create_category(name:, description: nil, sort_order: 0)
|
|
40
|
+
existing = find_category_by_name(name)
|
|
41
|
+
return existing if existing
|
|
42
|
+
|
|
43
|
+
create_category(name: name, description: description, sort_order: sort_order)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def fetch_products
|
|
47
|
+
fetch_all_pages(PRODUCT_PATH)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def get_product(id)
|
|
51
|
+
request(:get, "#{PRODUCT_PATH}#{id}/", resource_type: "Product", resource_id: id)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def create_product(name:, price:, category_id: nil, sku: nil, barcode: nil, description: nil, cost_price: 0)
|
|
55
|
+
payload = {
|
|
56
|
+
name: name,
|
|
57
|
+
original_price: format("%.2f", price / 100.0),
|
|
58
|
+
cost: format("%.2f", cost_price / 100.0),
|
|
59
|
+
sku: sku,
|
|
60
|
+
barcode: barcode,
|
|
61
|
+
description: description
|
|
62
|
+
}
|
|
63
|
+
payload[:categories] = ["/products/ProductCategory/#{category_id}/"] if category_id
|
|
64
|
+
|
|
65
|
+
request(:post, PRODUCT_PATH, payload: payload.compact, resource_type: "Product")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def update_product(id, attributes)
|
|
69
|
+
request(:patch, "#{PRODUCT_PATH}#{id}/", payload: attributes, resource_type: "Product", resource_id: id)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def find_product_by_name(name)
|
|
73
|
+
result = request(:get, PRODUCT_PATH, params: { name: name, limit: 1 })
|
|
74
|
+
objects = result["objects"] || []
|
|
75
|
+
objects.first
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def find_or_create_product(name:, price:, category_id: nil, sku: nil)
|
|
79
|
+
existing = find_product_by_name(name)
|
|
80
|
+
return existing if existing
|
|
81
|
+
|
|
82
|
+
create_product(name: name, price: price, category_id: category_id, sku: sku)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RevelSandboxSimulator
|
|
4
|
+
module Services
|
|
5
|
+
module Revel
|
|
6
|
+
class ServicesManager
|
|
7
|
+
attr_reader :config
|
|
8
|
+
|
|
9
|
+
def initialize(config: nil)
|
|
10
|
+
@config = config || RevelSandboxSimulator.configuration
|
|
11
|
+
@mutex = Mutex.new
|
|
12
|
+
@services = {}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def product
|
|
16
|
+
thread_safe_service(:product) { ProductService.new(config: config) }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def order
|
|
20
|
+
thread_safe_service(:order) { OrderService.new(config: config) }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def payment
|
|
24
|
+
thread_safe_service(:payment) { PaymentService.new(config: config) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def customer
|
|
28
|
+
thread_safe_service(:customer) { CustomerService.new(config: config) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def establishment
|
|
32
|
+
thread_safe_service(:establishment) { EstablishmentService.new(config: config) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def thread_safe_service(key)
|
|
38
|
+
@mutex.synchronize do
|
|
39
|
+
@services[key] ||= yield
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "zeitwerk"
|
|
4
|
+
require "dotenv"
|
|
5
|
+
|
|
6
|
+
module RevelSandboxSimulator
|
|
7
|
+
class Error < StandardError; end
|
|
8
|
+
class ConfigurationError < Error; end
|
|
9
|
+
class ApiError < Error; end
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
def configuration
|
|
13
|
+
@configuration ||= Configuration.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def configure
|
|
17
|
+
yield(configuration)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def reset_configuration!
|
|
21
|
+
@configuration = Configuration.new
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def root
|
|
25
|
+
File.expand_path("..", __dir__)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def logger
|
|
29
|
+
configuration.logger
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
loader = Zeitwerk::Loader.for_gem
|
|
35
|
+
loader.inflector.inflect("revel_sandbox_simulator" => "RevelSandboxSimulator")
|
|
36
|
+
loader.setup
|
|
37
|
+
|
|
38
|
+
Dotenv.load if File.exist?(".env")
|