choiceqr 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.
@@ -0,0 +1,84 @@
1
+ module ChoiceQR
2
+ # Generic response object representing any API entity (dish, order, area, …).
3
+ #
4
+ # All keys are snake_case symbols. Attribute access is available via:
5
+ # - Dot notation: resource.total_price
6
+ # - Hash notation: resource[:total_price]
7
+ # - Plain hash: resource.to_h
8
+ #
9
+ # Unlike the top level, ChoiceQR schemas are deeply nested (a dish carries
10
+ # menu options, which carry option list items; an order carries items,
11
+ # which carry modifiers, etc.) and the API has no `include`-style flag to
12
+ # opt in or out of them. So every nested Hash — at any depth — is wrapped
13
+ # as a Resource too, and every nested Array of Hashes becomes an Array of
14
+ # Resource, giving dot access all the way down.
15
+ class Resource
16
+ # +attributes+ is nil for a 200/201 response with an empty body, which
17
+ # the API returns for a handful of endpoints.
18
+ def initialize(attributes)
19
+ # Only this level's keys need converting: #wrap recurses into
20
+ # Resource.new for every nested Hash, which converts its own keys in
21
+ # turn. Pre-converting the whole tree here (e.g. via
22
+ # KeyTransformer.to_snake) would redo that work once per ancestor for
23
+ # every nested node.
24
+ @attributes = (attributes || {}).transform_keys { |k| KeyTransformer.snake_key(k) }
25
+ .transform_values { |v| self.class.wrap(v) }
26
+ end
27
+
28
+ # Wraps a single value: Hash → Resource, Array → Array of wrapped values,
29
+ # anything else is returned unchanged.
30
+ def self.wrap(value)
31
+ case value
32
+ when Hash then new(value)
33
+ when Array then value.map { |v| wrap(v) }
34
+ else value
35
+ end
36
+ end
37
+
38
+ # Hash-style access with either symbol or string key.
39
+ def [](key)
40
+ @attributes[KeyTransformer.snake_key(key)]
41
+ end
42
+
43
+ # Returns a plain snake_case-keyed hash (deep copy, nested Resources
44
+ # unwrapped back to Hash).
45
+ def to_h
46
+ deep_dup(@attributes)
47
+ end
48
+
49
+ def inspect
50
+ "#<#{self.class.name} #{@attributes.inspect}>"
51
+ end
52
+
53
+ def to_s
54
+ inspect
55
+ end
56
+
57
+ def ==(other)
58
+ other.is_a?(Resource) && other.to_h == to_h
59
+ end
60
+
61
+ def respond_to_missing?(name, include_private = false)
62
+ @attributes.key?(name) || super
63
+ end
64
+
65
+ def method_missing(name, *args)
66
+ if @attributes.key?(name)
67
+ @attributes[name]
68
+ else
69
+ super
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def deep_dup(obj)
76
+ case obj
77
+ when Resource then obj.to_h
78
+ when Hash then obj.transform_values { |v| deep_dup(v) }
79
+ when Array then obj.map { |v| deep_dup(v) }
80
+ else obj
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,37 @@
1
+ module ChoiceQR
2
+ module Resources
3
+ # Ordering areas (bar, hall, terrace, takeaway, delivery, digital menu).
4
+ #
5
+ # client.areas.list
6
+ # client.areas.get_by_type("takeaway")
7
+ # client.areas.create(name: "Terrace", payment_methods: { cash: true, card: true })
8
+ # client.areas.delete(id)
9
+ class Areas < Base
10
+ def list(language: nil)
11
+ fetch_list("location/#{lang(language)}/areas/list")
12
+ end
13
+
14
+ def get(id, language: nil)
15
+ fetch_one("location/#{lang(language)}/areas/#{id}")
16
+ end
17
+
18
+ # +type+ is one of: takeaway, delivery, simple, digitalMenu
19
+ def get_by_type(type, language: nil)
20
+ fetch_one("location/#{lang(language)}/areas/by-type/#{type}")
21
+ end
22
+
23
+ def create(language: nil, **attributes)
24
+ post_create("location/#{lang(language)}/areas", attributes)
25
+ end
26
+
27
+ # PUT — full replace. Returns true on success.
28
+ def update(id, language: nil, **attributes)
29
+ mutate(:put, "location/#{lang(language)}/areas/#{id}", body: attributes)
30
+ end
31
+
32
+ def delete(id, language: nil)
33
+ destroy("location/#{lang(language)}/areas/#{id}")
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,69 @@
1
+ module ChoiceQR
2
+ module Resources
3
+ # Shared plumbing for the per-entity resource wrapper classes reached via
4
+ # the client accessors (client.sections, client.dishes, client.orders, …).
5
+ #
6
+ # ChoiceQR's endpoints are not path-uniform the way Dotypos's are (list
7
+ # scoping, nesting, and available actions differ per resource type), so
8
+ # unlike a single generic ResourceCollection, each entity gets its own
9
+ # small class here that knows its own paths.
10
+ class Base
11
+ def initialize(client)
12
+ @client = client
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :client
18
+
19
+ # Resolves the :language path segment: an explicit override, or the
20
+ # client's configured default_language.
21
+ def lang(language)
22
+ (language || client.default_language).to_s
23
+ end
24
+
25
+ def fetch_one(path, params: {})
26
+ response = client.request(:get, path, params: to_query(params))
27
+ Resource.new(response.fetch(:body))
28
+ end
29
+
30
+ def fetch_list(path, params: {})
31
+ response = client.request(:get, path, params: to_query(params))
32
+ Array(response.fetch(:body)).map { |item| Resource.new(item) }
33
+ end
34
+
35
+ def post_create(path, attributes, params: {})
36
+ response = client.request(:post, path, body: KeyTransformer.to_camel(attributes), params: to_query(params))
37
+ Resource.new(response.fetch(:body))
38
+ end
39
+
40
+ # PUT/PATCH/POST calls that return 204 No Content on success.
41
+ def mutate(method, path, body: nil, params: {})
42
+ client.request(method, path, body: body && KeyTransformer.to_camel(body), params: to_query(params))
43
+ true
44
+ end
45
+
46
+ def destroy(path)
47
+ client.request(:delete, path)
48
+ true
49
+ end
50
+
51
+ # Query params use camelCase names too (e.g. includeApproved, perPage),
52
+ # same as request bodies.
53
+ def to_query(params)
54
+ KeyTransformer.to_camel(params.compact)
55
+ end
56
+
57
+ def format_time(value)
58
+ return nil if value.nil?
59
+ return value.to_s unless value.respond_to?(:iso8601)
60
+
61
+ # Time/DateTime#iso8601 take an optional fractional-digits argument;
62
+ # Date#iso8601 (no time component) takes none. Check arity instead of
63
+ # is_a?(Date) so any iso8601-compatible object works, not just those
64
+ # two stdlib classes.
65
+ value.method(:iso8601).arity.zero? ? value.iso8601 : value.iso8601(3)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,32 @@
1
+ module ChoiceQR
2
+ module Resources
3
+ # Table bookings/reservations.
4
+ #
5
+ # client.bookings.list(from: Time.now, till: Time.now + 86_400 * 7)
6
+ # client.bookings.get(id)
7
+ # client.bookings.confirm(id, location_points: [point_id])
8
+ # client.bookings.cancel(id, cancel_reason: "Table no longer available")
9
+ class Bookings < Base
10
+ # Rate limit: 1 request / 5 seconds.
11
+ def list(from: nil, till: nil, period_field: nil, page: nil, per_page: nil)
12
+ fetch_list("bookings/list", params: {
13
+ from: format_time(from), till: format_time(till),
14
+ period_field: period_field, page: page, per_page: per_page
15
+ })
16
+ end
17
+
18
+ def get(id)
19
+ fetch_one("bookings/#{id}")
20
+ end
21
+
22
+ # Only allowed while the booking status is created.
23
+ def confirm(id, location_points: nil)
24
+ mutate(:put, "bookings/#{id}/confirm", body: { location_points: location_points }.compact)
25
+ end
26
+
27
+ def cancel(id, cancel_reason:)
28
+ mutate(:put, "bookings/#{id}/cancel", body: { cancel_reason: cancel_reason })
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,39 @@
1
+ module ChoiceQR
2
+ module Resources
3
+ # Groupings of dishes within a section.
4
+ #
5
+ # client.categories.list(section_id)
6
+ # client.categories.create(name: "Hot", section: section_id)
7
+ # client.categories.update(id, name: "Cold")
8
+ # client.categories.set_position(section_id, [id1, id2])
9
+ # client.categories.delete(id)
10
+ class Categories < Base
11
+ def list(section_id, language: nil)
12
+ fetch_list("menu/#{lang(language)}/categories/list/#{section_id}")
13
+ end
14
+
15
+ def get(id, language: nil)
16
+ fetch_one("menu/#{lang(language)}/categories/#{id}")
17
+ end
18
+
19
+ def create(language: nil, **attributes)
20
+ post_create("menu/#{lang(language)}/categories", attributes)
21
+ end
22
+
23
+ # PUT — full replace. Returns true on success.
24
+ def update(id, language: nil, **attributes)
25
+ mutate(:put, "menu/#{lang(language)}/categories/#{id}", body: attributes)
26
+ end
27
+
28
+ # Reorders categories within +section_id+. +ids+ is the full, ordered
29
+ # array of category IDs.
30
+ def set_position(section_id, ids, language: nil)
31
+ mutate(:post, "menu/#{lang(language)}/categories/#{section_id}/position/bulk", body: ids)
32
+ end
33
+
34
+ def delete(id, language: nil)
35
+ destroy("menu/#{lang(language)}/categories/#{id}")
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,18 @@
1
+ module ChoiceQR
2
+ module Resources
3
+ # Cutlery modal configuration (single settings object, no id).
4
+ #
5
+ # client.cutlery.get
6
+ # client.cutlery.update(show: true, required_cutlery: false)
7
+ class Cutlery < Base
8
+ def get(language: nil)
9
+ fetch_one("menu/#{lang(language)}/cutlery")
10
+ end
11
+
12
+ # PUT — returns true on success.
13
+ def update(language: nil, **attributes)
14
+ mutate(:put, "menu/#{lang(language)}/cutlery", body: attributes)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ module ChoiceQR
2
+ module Resources
3
+ # Personal (custom) dish labels, e.g. "Chef's pick".
4
+ #
5
+ # client.dish_labels.list # => Array<Resource>
6
+ class DishLabels < Base
7
+ def list(language: nil)
8
+ fetch_list("menu/#{lang(language)}/dish-labels/list")
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,48 @@
1
+ module ChoiceQR
2
+ module Resources
3
+ # Customizable dish options (single/multiple choice modifiers), shared
4
+ # across the dishes they are attached to.
5
+ #
6
+ # client.dish_options.list(section_id)
7
+ # client.dish_options.create(name: "Size", type: "single", section: section_id)
8
+ # client.dish_options.attach(option_id, dish_id)
9
+ # client.dish_options.detach(option_id, dish_id)
10
+ # client.dish_options.delete(id)
11
+ class DishOptions < Base
12
+ def list(section_id, language: nil)
13
+ fetch_list("menu/#{lang(language)}/options/list/#{section_id}")
14
+ end
15
+
16
+ def get(id, language: nil)
17
+ fetch_one("menu/#{lang(language)}/options/#{id}")
18
+ end
19
+
20
+ def create(language: nil, **attributes)
21
+ post_create("menu/#{lang(language)}/options", attributes)
22
+ end
23
+
24
+ # PUT — full replace. Returns true on success.
25
+ def update(id, language: nil, **attributes)
26
+ mutate(:put, "menu/#{lang(language)}/options/#{id}", body: attributes)
27
+ end
28
+
29
+ # Reorders options within +section_id+. +ids+ is the full, ordered
30
+ # array of option IDs.
31
+ def set_position(section_id, ids, language: nil)
32
+ mutate(:post, "menu/#{lang(language)}/options/#{section_id}/position/bulk", body: ids)
33
+ end
34
+
35
+ def delete(id, language: nil)
36
+ destroy("menu/#{lang(language)}/options/#{id}")
37
+ end
38
+
39
+ def attach(id, dish_id, language: nil)
40
+ mutate(:put, "menu/#{lang(language)}/options/#{id}/attach", body: { dish: dish_id })
41
+ end
42
+
43
+ def detach(id, dish_id, language: nil)
44
+ mutate(:put, "menu/#{lang(language)}/options/#{id}/detach", body: { dish: dish_id })
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,58 @@
1
+ module ChoiceQR
2
+ module Resources
3
+ # Menu items.
4
+ #
5
+ # client.dishes.list(category_id)
6
+ # client.dishes.find_by_pos_id("b9e8db61")
7
+ # client.dishes.create(name: "Cappuccino", category: category_id, price: 420)
8
+ # client.dishes.update(id, name: "Double Espresso", price: 450)
9
+ # client.dishes.patch(id, active: false)
10
+ # client.dishes.update_areas(id, takeaway: true, delivery: false)
11
+ # client.dishes.set_position(category_id, [id1, id2])
12
+ # client.dishes.delete(id)
13
+ class Dishes < Base
14
+ def list(category_id, language: nil)
15
+ fetch_list("menu/#{lang(language)}/dishes/list/#{category_id}")
16
+ end
17
+
18
+ # Looks a dish up by the posID assigned in your own POS system.
19
+ def find_by_pos_id(pos_id, language: nil)
20
+ fetch_one("menu/#{lang(language)}/dishes", params: { pos_id: pos_id })
21
+ end
22
+
23
+ def get(id, language: nil)
24
+ fetch_one("menu/#{lang(language)}/dishes/#{id}")
25
+ end
26
+
27
+ def create(language: nil, **attributes)
28
+ post_create("menu/#{lang(language)}/dishes", attributes)
29
+ end
30
+
31
+ # PUT — full replace. Returns true on success.
32
+ def update(id, language: nil, **attributes)
33
+ mutate(:put, "menu/#{lang(language)}/dishes/#{id}", body: attributes)
34
+ end
35
+
36
+ # PATCH — partial update of only the given fields.
37
+ def patch(id, language: nil, **attributes)
38
+ mutate(:patch, "menu/#{lang(language)}/dishes/#{id}", body: attributes)
39
+ end
40
+
41
+ # Updates which areas (takeaway/delivery/dine-in/digital menu) the dish
42
+ # is available in.
43
+ def update_areas(id, language: nil, **areas)
44
+ mutate(:put, "menu/#{lang(language)}/dishes/#{id}/areas", body: areas)
45
+ end
46
+
47
+ # Reorders dishes within +category_id+. +ids+ is the full, ordered
48
+ # array of dish IDs.
49
+ def set_position(category_id, ids, language: nil)
50
+ mutate(:post, "menu/#{lang(language)}/dishes/#{category_id}/position/bulk", body: ids)
51
+ end
52
+
53
+ def delete(id, language: nil)
54
+ destroy("menu/#{lang(language)}/dishes/#{id}")
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,30 @@
1
+ module ChoiceQR
2
+ module Resources
3
+ # Customer feedback for orders and the restaurant in general.
4
+ #
5
+ # client.feedbacks.list(type: "ORDER")
6
+ # client.feedbacks.get(id)
7
+ # client.feedbacks.create(
8
+ # ref_id: order_id,
9
+ # feedback: { type: "ORDER", rate_serve: 5, rate_dish: 4, language: "en" },
10
+ # customer: { name: "John Doe", phone: "+380501234567" }
11
+ # )
12
+ class Feedbacks < Base
13
+ def list(from: nil, to: nil, limit: nil, offset: nil, type: nil, sort: nil)
14
+ fetch_list("feedbacks", params: {
15
+ from: format_time(from), to: format_time(to),
16
+ limit: limit, offset: offset, type: type, sort: sort
17
+ })
18
+ end
19
+
20
+ def get(id)
21
+ fetch_one("feedbacks/#{id}")
22
+ end
23
+
24
+ # Rate limit: 1 request / 10 seconds.
25
+ def create(**attributes)
26
+ post_create("feedbacks", attributes)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,56 @@
1
+ module ChoiceQR
2
+ module Resources
3
+ # Whole-menu operations: fetching the full client menu in one call,
4
+ # bulk import/replace, availability sync, and marketplace data sync.
5
+ #
6
+ # client.full_menu.list
7
+ # client.full_menu.import(sections: [...], categories: [...], dishes: [...])
8
+ # client.full_menu.patch_dishes(dishes: [...])
9
+ # client.full_menu.sync_availability(dishes: [{ pos_id: "525", active: false }])
10
+ # client.full_menu.sync_chain_availability(sections: [...])
11
+ # client.full_menu.sync_marketplace_data(dishes: [{ pos_id: "1", data: { WOLT: { price: 1000 } } }])
12
+ # client.full_menu.marketplace_sync_status(sync_id)
13
+ class FullMenu < Base
14
+ def list(language: nil)
15
+ fetch_one("menu/#{lang(language)}/full/list")
16
+ end
17
+
18
+ # Full replace of the menu (sections:, categories:, dishes:, dish_options:).
19
+ # By default, entities missing from the payload are deleted; pass
20
+ # preserve_missing_items: true to keep them (marked inactive) instead.
21
+ def import(language: nil, preserve_missing_items: nil, **payload)
22
+ mutate(:post, "menu/#{lang(language)}/full", body: payload,
23
+ params: { preserve_missing_items: preserve_missing_items })
24
+ end
25
+
26
+ # Partial update of only dish data (dishes:), matched by posID.
27
+ def patch_dishes(language: nil, **payload)
28
+ mutate(:patch, "menu/#{lang(language)}/full/dishes", body: payload)
29
+ end
30
+
31
+ # Bulk-updates active/attribute state for sections:, categories:,
32
+ # dishes:, and dish_options:, matched by posID. At least one of these
33
+ # keys must be present.
34
+ def sync_availability(language: nil, skip_missing: nil, **payload)
35
+ mutate(:post, "menu/#{lang(language)}/full/availability", body: payload,
36
+ params: { skip_missing: skip_missing })
37
+ end
38
+
39
+ # Same as #sync_availability, but per chain branch.
40
+ def sync_chain_availability(language: nil, **payload)
41
+ mutate(:post, "menu/#{lang(language)}/full/chain/availability", body: payload)
42
+ end
43
+
44
+ # Kicks off an async sync of marketplace-specific price/name overrides.
45
+ # Returns a Resource with the sync job +id+; poll it via
46
+ # #marketplace_sync_status.
47
+ def sync_marketplace_data(dishes:, language: nil)
48
+ post_create("menu/#{lang(language)}/full/marketplace/data", { dishes: dishes })
49
+ end
50
+
51
+ def marketplace_sync_status(sync_id, language: nil)
52
+ fetch_one("menu/#{lang(language)}/full/marketplace/data/status/#{sync_id}")
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,31 @@
1
+ module ChoiceQR
2
+ module Resources
3
+ # Individual points within an area (tables, kiosks, rooms).
4
+ #
5
+ # client.location_points.list(area_id)
6
+ # client.location_points.create(name: "Table 4", area: area_id)
7
+ # client.location_points.delete(id)
8
+ class LocationPoints < Base
9
+ def list(area_id, language: nil)
10
+ fetch_list("location/#{lang(language)}/points/list/#{area_id}")
11
+ end
12
+
13
+ def get(id, language: nil)
14
+ fetch_one("location/#{lang(language)}/points/#{id}")
15
+ end
16
+
17
+ def create(language: nil, **attributes)
18
+ post_create("location/#{lang(language)}/points", attributes)
19
+ end
20
+
21
+ # PUT — full replace. Returns true on success.
22
+ def update(id, language: nil, **attributes)
23
+ mutate(:put, "location/#{lang(language)}/points/#{id}", body: attributes)
24
+ end
25
+
26
+ def delete(id, language: nil)
27
+ destroy("location/#{lang(language)}/points/#{id}")
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,59 @@
1
+ module ChoiceQR
2
+ module Resources
3
+ # Customer orders (delivery, takeaway, or table).
4
+ #
5
+ # client.orders.list(since: Time.now - 3600)
6
+ # client.orders.list_archive(from: Time.now - 86_400 * 30, till: Time.now)
7
+ # client.orders.get(id)
8
+ # client.orders.get_by_guid(guid)
9
+ # client.orders.update_delivery(id, delivery_status: "processing")
10
+ # client.orders.cancel(id, reason: "Out of stock")
11
+ # client.orders.close(id)
12
+ class Orders < Base
13
+ def list(since: nil, include_approved: nil, branches: nil, page: nil, per_page: nil)
14
+ fetch_list("orders/list", params: {
15
+ since: format_time(since), include_approved: include_approved,
16
+ branches: format_branches(branches), page: page, per_page: per_page
17
+ })
18
+ end
19
+
20
+ # Rate limit: 1 request / 5 seconds.
21
+ def list_archive(from:, till:, branches: nil, page: nil, per_page: nil)
22
+ fetch_list("orders/list/archive", params: {
23
+ from: format_time(from), till: format_time(till),
24
+ branches: format_branches(branches), page: page, per_page: per_page
25
+ })
26
+ end
27
+
28
+ def get(id)
29
+ fetch_one("orders/#{id}")
30
+ end
31
+
32
+ def get_by_guid(guid)
33
+ fetch_one("orders/guid/#{guid}")
34
+ end
35
+
36
+ # Only allowed while the order status is approved or delivery.
37
+ def update_delivery(id, **attributes)
38
+ mutate(:put, "orders/#{id}/delivery", body: attributes)
39
+ end
40
+
41
+ # Only allowed while the order status is waiting_for_approve, approved,
42
+ # or delivery.
43
+ def cancel(id, reason:)
44
+ mutate(:put, "orders/#{id}/cancel", body: { reason: reason })
45
+ end
46
+
47
+ # Only allowed while the order status is approved or delivery.
48
+ def close(id)
49
+ mutate(:put, "orders/#{id}/close")
50
+ end
51
+
52
+ private
53
+
54
+ def format_branches(branches)
55
+ Array(branches).join(",") if branches
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,36 @@
1
+ module ChoiceQR
2
+ module Resources
3
+ # Menu packages (bundles of categories sold together).
4
+ #
5
+ # client.pack.list
6
+ # client.pack.create(name: "Lunch set", price: 1000, categories: [{ _id: category_id }])
7
+ # client.pack.delete(id)
8
+ #
9
+ # Note: entries in the +categories+ payload reference an existing
10
+ # category by its Mongo id. Use the literal key +:_id+ (or the string
11
+ # +"_id"+) — the leading underscore is preserved by the key transformer,
12
+ # unlike the top-level +id+ read from responses.
13
+ class Pack < Base
14
+ def list(language: nil)
15
+ fetch_list("menu/#{lang(language)}/pack/list")
16
+ end
17
+
18
+ def get(id, language: nil)
19
+ fetch_one("menu/#{lang(language)}/pack/#{id}")
20
+ end
21
+
22
+ def create(language: nil, **attributes)
23
+ post_create("menu/#{lang(language)}/pack", attributes)
24
+ end
25
+
26
+ # PUT — full replace. Returns true on success.
27
+ def update(id, language: nil, **attributes)
28
+ mutate(:put, "menu/#{lang(language)}/pack/#{id}", body: attributes)
29
+ end
30
+
31
+ def delete(id, language: nil)
32
+ destroy("menu/#{lang(language)}/pack/#{id}")
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,13 @@
1
+ module ChoiceQR
2
+ module Resources
3
+ # Information about the place (restaurant, cafe) the client's token
4
+ # belongs to.
5
+ #
6
+ # client.place.get # => Resource
7
+ class Place < Base
8
+ def get
9
+ fetch_one("place")
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module ChoiceQR
2
+ module Resources
3
+ # The customizable message shown at the top of a menu section.
4
+ #
5
+ # client.section_info.get(section_id) # => Resource
6
+ class SectionInfo < Base
7
+ def get(section_id, language: nil)
8
+ fetch_one("menu/#{lang(language)}/section-info/#{section_id}")
9
+ end
10
+ end
11
+ end
12
+ end