Class: SeatLayer::Inventory

Inherits:
Resource show all
Defined in:
lib/seatlayer/inventory.rb

Overview

Holds, booking, blocking and availability.

Two complete flows, both first-class:

browser holds → retrieve_hold for authoritative pricing → charge → book(hold_id:)
backend books labels directly — box office, phone sales, comps

Never price from what the browser tells you. retrieve_hold is the authoritative answer, which is why it is a separate call.

Constant Summary

Constants inherited from Resource

Resource::UNSET

Instance Method Summary collapse

Methods inherited from Resource

#initialize

Constructor Details

This class inherits a constructor from SeatLayer::Resource

Instance Method Details

#block(event_key, labels:, release_at: nil) ⇒ Object

Hold inventory back from sale (house seats, production holds).



106
107
108
109
# File 'lib/seatlayer/inventory.rb', line 106

def block(event_key, labels:, release_at: nil)
  @client.post(path(event_key, "/block"),
               compact({ "labels" => labels, "releaseAt" => release_at }))
end

#book(event_key, hold_id: nil, labels: nil, booking_ref: nil, channel_ids: nil, ignore_channel_restrictions: nil, reason: nil, idempotency_key: nil) ⇒ Object



83
84
85
86
87
88
89
90
91
# File 'lib/seatlayer/inventory.rb', line 83

def book(event_key, hold_id: nil, labels: nil, booking_ref: nil, channel_ids: nil,
         ignore_channel_restrictions: nil, reason: nil, idempotency_key: nil)
  body = compact({ "holdId" => hold_id, "labels" => labels,
                   "bookingRef" => normalise_booking_ref(booking_ref),
                   "channelIds" => channel_ids,
                   "ignoreChannelRestrictions" => ignore_channel_restrictions,
                   "reason" => reason })
  @client.post(path(event_key, "/book"), body, idempotency_key: idempotency_key)
end

#book_best_available(event_key, qty:, booking_ref:, category_key: nil, zone_id: nil, channel_ids: nil, ignore_channel_restrictions: nil, reason: nil, idempotency_key: nil) ⇒ Object

Pick and book in one call — the box-office shape.

Prefer this over hold-then-book when payment is already taken: a failure between two calls would strand inventory until the TTL expired.



47
48
49
50
51
52
53
54
55
56
# File 'lib/seatlayer/inventory.rb', line 47

def book_best_available(event_key, qty:, booking_ref:, category_key: nil,
                        zone_id: nil, channel_ids: nil, ignore_channel_restrictions: nil,
                        reason: nil, idempotency_key: nil)
  body = compact({ "qty" => qty, "bookingRef" => normalise_booking_ref(booking_ref),
                   "categoryKey" => category_key, "zoneId" => zone_id,
                   "channelIds" => channel_ids,
                   "ignoreChannelRestrictions" => ignore_channel_restrictions,
                   "reason" => reason })
  @client.post(path(event_key, "/best-available-book"), body, idempotency_key: idempotency_key)
end

#box_office_book(event_key, labels:, booking_ref:, idempotency_key: nil) ⇒ Object



93
94
95
96
97
# File 'lib/seatlayer/inventory.rb', line 93

def box_office_book(event_key, labels:, booking_ref:, idempotency_key: nil)
  @client.post(path(event_key, "/box-book"),
               { "labels" => labels, "bookingRef" => normalise_booking_ref(booking_ref) },
               idempotency_key: idempotency_key)
end

#extend_hold(event_key, hold_id, ttl_ms: nil, channel_ids: nil, ignore_channel_restrictions: nil, reason: nil) ⇒ Object

Push an active hold's expiry out by a fresh window before it lapses.

Use this rather than release-and-re-hold when an order takes longer than the checkout window — invoiced sales, a phone order on hold. Releasing first hands the seats to whoever is racing for them in between. A hold that is gone, expired, or at its renewal cap answers 409 cannot_extend.



65
66
67
68
69
70
71
72
# File 'lib/seatlayer/inventory.rb', line 65

def extend_hold(event_key, hold_id, ttl_ms: nil, channel_ids: nil,
                ignore_channel_restrictions: nil, reason: nil)
  body = compact({ "holdId" => hold_id, "ttlMs" => ttl_ms,
                   "channelIds" => channel_ids,
                   "ignoreChannelRestrictions" => ignore_channel_restrictions,
                   "reason" => reason })
  @client.post(path(event_key, "/extend"), body)
end

#hold(event_key, labels: nil, selections: nil, ttl_ms: nil, replace_hold_id: nil, channel_ids: nil, ignore_channel_restrictions: nil, reason: nil, idempotency_key: nil) ⇒ Object

Explicit keywords keep allocation authority visible at every sale call. rubocop:disable Metrics/ParameterLists



16
17
18
19
20
21
22
23
24
25
# File 'lib/seatlayer/inventory.rb', line 16

def hold(event_key, labels: nil, selections: nil, ttl_ms: nil,
         replace_hold_id: nil, channel_ids: nil, ignore_channel_restrictions: nil,
         reason: nil, idempotency_key: nil)
  body = compact({ "labels" => labels, "selections" => selections,
                   "ttlMs" => ttl_ms, "replaceHoldId" => replace_hold_id,
                   "channelIds" => channel_ids,
                   "ignoreChannelRestrictions" => ignore_channel_restrictions,
                   "reason" => reason })
  @client.post(path(event_key, "/hold"), body, idempotency_key: idempotency_key)
end

#hold_best_available(event_key, qty:, category_key: nil, zone_id: nil, ttl_ms: nil, channel_ids: nil, ignore_channel_restrictions: nil, reason: nil, idempotency_key: nil) ⇒ Object

Ask us to pick the best free objects and hold them.

The picker is the one the buyer widget uses, so a phone order and a web order get the same answer for the same inventory. A qty above the server cap is clamped, not rejected.



32
33
34
35
36
37
38
39
40
41
# File 'lib/seatlayer/inventory.rb', line 32

def hold_best_available(event_key, qty:, category_key: nil, zone_id: nil,
                        ttl_ms: nil, channel_ids: nil, ignore_channel_restrictions: nil,
                        reason: nil, idempotency_key: nil)
  body = compact({ "qty" => qty, "categoryKey" => category_key,
                   "zoneId" => zone_id, "ttlMs" => ttl_ms,
                   "channelIds" => channel_ids,
                   "ignoreChannelRestrictions" => ignore_channel_restrictions,
                   "reason" => reason })
  @client.post(path(event_key, "/best-available"), body, idempotency_key: idempotency_key)
end

#list_bookings(event_key, query: nil, state: nil, limit: nil, cursor: nil) ⇒ Object

One page of inventory booking lifecycles, newest first.



128
129
130
131
# File 'lib/seatlayer/inventory.rb', line 128

def list_bookings(event_key, query: nil, state: nil, limit: nil, cursor: nil)
  @client.get(path(event_key, "/bookings"),
              compact({ "q" => query, "state" => state, "limit" => limit, "cursor" => cursor }))
end

#release(event_key, labels:, hold_id:) ⇒ Object



79
80
81
# File 'lib/seatlayer/inventory.rb', line 79

def release(event_key, labels:, hold_id:)
  @client.post(path(event_key, "/release"), { "labels" => labels, "holdId" => hold_id })
end

#retrieve_availability(event_key) ⇒ Object



119
120
121
# File 'lib/seatlayer/inventory.rb', line 119

def retrieve_availability(event_key)
  @client.get(path(event_key, "/availability"))
end

#retrieve_booking(event_key, booking_ref) ⇒ Object



133
134
135
# File 'lib/seatlayer/inventory.rb', line 133

def retrieve_booking(event_key, booking_ref)
  @client.get(path(event_key, "/bookings/#{encode(normalise_booking_ref(booking_ref))}"))
end

#retrieve_hold(event_key, hold_id) ⇒ Object

Authoritative items and prices. Charge from this, not the browser.



75
76
77
# File 'lib/seatlayer/inventory.rb', line 75

def retrieve_hold(event_key, hold_id)
  @client.get(path(event_key, "/holds/#{encode(hold_id)}"))
end

#unblock(event_key, labels:) ⇒ Object



111
112
113
# File 'lib/seatlayer/inventory.rb', line 111

def unblock(event_key, labels:)
  @client.post(path(event_key, "/unblock"), { "labels" => labels })
end

#unblock_all(event_key) ⇒ Object



115
116
117
# File 'lib/seatlayer/inventory.rb', line 115

def unblock_all(event_key)
  @client.post(path(event_key, "/unblock-all"))
end

#unbook(event_key, labels:, booking_ref:) ⇒ Object

Reverse a booking. Requires a key with cancel authority.



100
101
102
103
# File 'lib/seatlayer/inventory.rb', line 100

def unbook(event_key, labels:, booking_ref:)
  @client.post(path(event_key, "/unbook"),
               { "labels" => labels, "bookingRef" => normalise_booking_ref(booking_ref) })
end

#update_availability(event_key, fields) ⇒ Object



123
124
125
# File 'lib/seatlayer/inventory.rb', line 123

def update_availability(event_key, fields)
  @client.post(path(event_key, "/availability"), fields)
end