seatlayer 0.7.0 → 0.9.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.
@@ -5,7 +5,36 @@ module SeatLayer
5
5
  #
6
6
  # Browser selection belongs in the distinct SeasonPicker. Give it only the
7
7
  # show-once scoped token returned by +create_season_buyer_access_session+.
8
+ #
9
+ # One thin method per public Season operation (90 of them), so the class is
10
+ # long by design rather than doing too much.
11
+ # rubocop:disable-next Metrics/ClassLength
8
12
  class Seasons < Resource
13
+ # The job voiding a cancelled date's tickets: "sbj_void_" and the cancel's
14
+ # "amendmentId". Cancelling a date starts it (a repeated cancel joins it);
15
+ # follow it with +retrieve_season_bulk_job+. Each Season ticket it voids is
16
+ # told by "season.ticket.voided" with reason "date_cancelled".
17
+ def self.date_cancel_void_job_id(amendment_id)
18
+ "sbj_void_#{amendment_id}"
19
+ end
20
+
21
+ # The most rows one +create_season_holder_import+ call takes for a package of
22
+ # +dates+ dates. Each row is fenced on every date: 100 up to 20 dates, then
23
+ # floor(2300 / (dates + 3)), 43 at 50. A call with more answers 422
24
+ # invalid_rows with "maxRows".
25
+ def self.holder_import_rows_per_call(dates)
26
+ (2300 / ([dates, 1].max + 3)).floor.clamp(1, 100)
27
+ end
28
+
29
+ # True for the 202 a season-seats season's date cancel (+create_season_amendment+)
30
+ # or past date (+mark_season_performance_past+) answers while the date is still
31
+ # leaving every season seat: "state" "finishing", "code" "block_date_leave_pending",
32
+ # with "eventKey", "shardsLeft" and "shardCount". It finishes by itself: send the
33
+ # same request again after Retry-After to read how it ended.
34
+ def self.date_leave_pending?(answer)
35
+ answer.is_a?(Hash) && answer["state"] == "finishing" && answer["code"] == "block_date_leave_pending"
36
+ end
37
+
9
38
  def list_seasons(workspace_id: nil, structure_state: nil, limit: nil, cursor: nil)
10
39
  @client.get("/v1/seasons", compact({ "workspaceId" => workspace_id,
11
40
  "structureState" => structure_state,
@@ -18,10 +47,18 @@ module SeatLayer
18
47
  selection(event_keys, source_performance_group_keys))
19
48
  end
20
49
 
50
+ # Create a draft Season. +single_tickets+ true makes each date also sell its own
51
+ # single tickets once the Season is on sale; the default is Season only.
52
+ # +inventory_mode+ "block" turns season seats on for this Season; the default is
53
+ # "per_hold", and anything else is refused with 422 invalid_inventory_mode. Every
54
+ # Season answer carries "inventoryMode": how the season's seats are decided, "block"
55
+ # once season seats are turned on for it (inventoryMode "block" on create or update)
56
+ # or it sells from its block, else "per_hold".
21
57
  def create_season(name:, event_keys: nil, source_performance_group_keys: nil,
22
- edition: UNSET, idempotency_key: nil)
58
+ edition: UNSET, idempotency_key: nil, single_tickets: nil, inventory_mode: nil)
23
59
  body = selection(event_keys, source_performance_group_keys).merge("name" => name)
24
60
  body.merge!(supplied({ "edition" => edition }))
61
+ body.merge!(compact({ "singleTickets" => single_tickets, "inventoryMode" => inventory_mode }))
25
62
  @client.post("/v1/seasons", body, idempotency_key: idempotency_key,
26
63
  retry_policy: :header_replay)
27
64
  end
@@ -30,10 +67,33 @@ module SeatLayer
30
67
  @client.get(path(season_key))
31
68
  end
32
69
 
33
- def update_season(season_key, expected_revision:, name: nil, edition: UNSET,
34
- idempotency_key: nil)
35
- body = compact({ "expectedRevision" => expected_revision, "name" => name })
36
- body.merge!(supplied({ "edition" => edition }))
70
+ # Rename a draft, or set how the Season sells. +checkout_mode+ ("seatlayer" or
71
+ # "server") cannot go back to undecided and is fixed once a live booking exists.
72
+ # The cutoffs are hours (0 to 720) before a date starts; pass +nil+ to send an
73
+ # explicit JSON null, which means the club default (Settings › Ticket site), or
74
+ # until the date starts when the club has none. +audience+ "public" lists
75
+ # the Season on your SeatLayer website ("embed_only" does not); an ended Season
76
+ # cannot become public (409 sales_ended). +single_tickets+ turned on while the Season
77
+ # is on sale opens each upcoming date's own sales through its readiness checks; turned
78
+ # off, they stop. The answer's "singleTicketDates" says which dates opened or stopped,
79
+ # and carries "jobRefused" when its season_singles job could not start: the change
80
+ # still stands, and sending +single_tickets+ true again opens any date still not open.
81
+ # A retry with the same +expected_revision+ and +idempotency_key+ is not applied twice.
82
+ # +inventory_mode+ "block" turns season seats on for this Season; "per_hold" turns them
83
+ # off again, refused with 409 season_block_exists once seats are set aside. It does not
84
+ # change the Season's "revision". Refused with 409 season_block_packages (more than one
85
+ # package), 409 season_block_on_sale (sales already opened without season seats),
86
+ # 409 season_not_draft_or_active, or 422 invalid_inventory_mode.
87
+ def update_season( # rubocop:disable Metrics/ParameterLists
88
+ season_key, expected_revision:, name: nil, edition: UNSET, checkout_mode: nil,
89
+ transfer_cutoff_hours: UNSET, return_cutoff_hours: UNSET, audience: nil, idempotency_key: nil,
90
+ single_tickets: nil, inventory_mode: nil
91
+ )
92
+ body = compact({ "expectedRevision" => expected_revision, "name" => name,
93
+ "checkoutMode" => checkout_mode, "audience" => audience,
94
+ "singleTickets" => single_tickets, "inventoryMode" => inventory_mode })
95
+ body.merge!(supplied({ "edition" => edition, "transferCutoffHours" => transfer_cutoff_hours,
96
+ "returnCutoffHours" => return_cutoff_hours }))
37
97
  @client.mutation_with_header_replay(
38
98
  "PATCH", path(season_key), body, idempotency_key: idempotency_key
39
99
  )
@@ -82,6 +142,11 @@ module SeatLayer
82
142
  { "expectedRevision" => expected_revision })
83
143
  end
84
144
 
145
+ # Open sales. The answer's "warnings" say what is advised but not required, such
146
+ # as "checkout_test_missing" until a checkout test or rehearsal has passed.
147
+ # Refused with 409 plan_not_published or checkout_mode_not_chosen until a package
148
+ # is published and a checkout mode is chosen, and with 409 block_not_ready when
149
+ # season seats are turned on (inventory_mode "block") but the seats are not set aside yet.
85
150
  def open_season_sales(season_key, expected_revision:)
86
151
  sales(season_key, "open", expected_revision)
87
152
  end
@@ -147,6 +212,11 @@ module SeatLayer
147
212
  @client.post(path(season_key, "/buyer-rehearsals/validate"))
148
213
  end
149
214
 
215
+ # Import incumbent holders. Each row hash takes "rowId", "holderRef",
216
+ # "priorPlanActivationId", "priorContractRef" and "labels", plus optional
217
+ # "existingBookingRef", "holderEmail" and "holderName". A call takes at most
218
+ # +Seasons.holder_import_rows_per_call(dates)+ rows (100 up to 20 dates, 43 at 50);
219
+ # more answers 422 invalid_rows with "maxRows" on the error's +body+.
150
220
  def create_season_holder_import(season_key, successor_plan_activation_id:, rows:,
151
221
  dry_run: UNSET, idempotency_key: nil)
152
222
  body = { "successorPlanActivationId" => successor_plan_activation_id, "rows" => rows }
@@ -159,6 +229,10 @@ module SeatLayer
159
229
  @client.get(path(season_key, "/imports/#{encode(import_id)}"))
160
230
  end
161
231
 
232
+ # Answers 201 {"offers"} for 100 contracts or fewer. Above 100 it answers 202
233
+ # {"job", "batchId"}: a renewal_run job makes the offers. Follow it with
234
+ # +retrieve_season_bulk_job+, then page the offers with +list_season_renewal_offers+.
235
+ # Retried with one Idempotency-Key, which answers the same job.
162
236
  def create_season_renewal_offers(season_key, deadline_at:,
163
237
  successor_plan_activation_id: nil, contract_ids: nil,
164
238
  idempotency_key: nil)
@@ -168,8 +242,10 @@ module SeatLayer
168
242
  idempotency_key: idempotency_key, retry_policy: :header_replay)
169
243
  end
170
244
 
171
- def list_season_renewal_offers(season_key)
172
- @client.get(path(season_key, "/renewal-offers"))
245
+ # One page of renewal offers, newest first. Pass +cursor+ from the previous
246
+ # page's +nextCursor+ (nil on the last page); +limit+ is 1 to 100 (default 100).
247
+ def list_season_renewal_offers(season_key, cursor: nil, limit: nil)
248
+ @client.get(path(season_key, "/renewal-offers"), compact({ "cursor" => cursor, "limit" => limit }))
173
249
  end
174
250
 
175
251
  def retrieve_season_renewal_offer(season_key, offer_id)
@@ -192,8 +268,12 @@ module SeatLayer
192
268
  "bookingRef" => booking_ref, "planActivationId" => plan_activation_id })
193
269
  end
194
270
 
195
- def decline_season_renewal_offer(season_key, offer_id)
196
- @client.post(path(season_key, "/renewal-offers/#{encode(offer_id)}/decline"), {})
271
+ # Decline a renewal offer and release its seats at once. +reason+ (1 to 200
272
+ # characters) is kept on the offer as +declineReason+; a repeated decline
273
+ # keeps the first reason.
274
+ def decline_season_renewal_offer(season_key, offer_id, reason: nil)
275
+ @client.post(path(season_key, "/renewal-offers/#{encode(offer_id)}/decline"),
276
+ compact({ "reason" => reason }))
197
277
  end
198
278
 
199
279
  def release_season_renewal_offer(season_key, offer_id)
@@ -204,11 +284,23 @@ module SeatLayer
204
284
  @client.get(path(season_key, "/occurrences"))
205
285
  end
206
286
 
287
+ # Amend one date. +kind+ is "reschedule", "replace", "cancel_exception",
288
+ # "postpone" (no new time yet) or "cancel" (the date will not happen). With
289
+ # "cancel" only, +refund+ ("none" or "per_date") refunds each SeatLayer order's
290
+ # share of the date as a job, and +reason+ is kept on it. A whole-order refund
291
+ # is the order refund. A "cancel" also voids every live ticket for the date as a
292
+ # date_cancel_void job, +Seasons.date_cancel_void_job_id(amendment_id)+ (the answer
293
+ # does not name it); follow it with +retrieve_season_bulk_job+. A cancelled date
294
+ # can't be moved ("reschedule"), postponed or replaced: 409 performance_cancelled.
295
+ # On a season-seats season, a "cancel" first takes the date off every season seat:
296
+ # while that finishes the answer is a 202 (check with +Seasons.date_leave_pending?+);
297
+ # send the same request again to read the amendment.
207
298
  def create_season_amendment(season_key, event_key:, kind:, starts_at: nil, name: nil,
208
- idempotency_key: nil)
299
+ refund: nil, reason: nil, idempotency_key: nil)
209
300
  @client.post(path(season_key, "/amendments"),
210
301
  compact({ "eventKey" => event_key, "kind" => kind,
211
- "startsAt" => starts_at, "name" => name }),
302
+ "startsAt" => starts_at, "name" => name,
303
+ "refund" => refund, "reason" => reason }),
212
304
  idempotency_key: idempotency_key, retry_policy: :header_replay)
213
305
  end
214
306
 
@@ -249,6 +341,335 @@ module SeatLayer
249
341
  @client.get(path(season_key, "/export"))
250
342
  end
251
343
 
344
+ # Dry-run or commit renewal rights derived from a previous Season's holders.
345
+ # A commit of more than 100 holders answers 202 {"job"}: a holder_import job
346
+ # brings them across, and its +summary+ has the same counts once it completes.
347
+ # Retried with one Idempotency-Key, which answers the same job.
348
+ def create_season_holder_import_from_season(season_key, source_season_id:,
349
+ successor_plan_activation_id:, dry_run: UNSET,
350
+ idempotency_key: nil)
351
+ body = { "sourceSeasonId" => source_season_id,
352
+ "successorPlanActivationId" => successor_plan_activation_id }
353
+ body.merge!(supplied({ "dryRun" => dry_run }))
354
+ @client.post(path(season_key, "/imports/from-season"), body,
355
+ idempotency_key: idempotency_key, retry_policy: :header_replay)
356
+ end
357
+
358
+ # List the Season's performances and package membership, in every lifecycle state.
359
+ def list_season_performances(season_key)
360
+ @client.get(path(season_key, "/performances"))
361
+ end
362
+
363
+ # Create performances inside a draft Season from a repeat rule. Pass +nil+
364
+ # for +venue+ or +duration_minutes+ to send an explicit JSON null.
365
+ # rubocop:disable-next Metrics/ParameterLists
366
+ def create_season_performances(season_key, title_pattern:, timezone:, repeat:,
367
+ first_starts_at: nil, count: nil, dates: nil, venue: UNSET,
368
+ duration_minutes: UNSET, region: nil,
369
+ attach_to_plan_keys: nil, idempotency_key: nil)
370
+ body = compact({ "titlePattern" => title_pattern, "firstStartsAt" => first_starts_at,
371
+ "timezone" => timezone, "repeat" => repeat, "count" => count,
372
+ "dates" => dates, "region" => region,
373
+ "attachToPlanKeys" => attach_to_plan_keys })
374
+ body.merge!(supplied({ "venue" => venue, "durationMinutes" => duration_minutes }))
375
+ @client.post(path(season_key, "/performances"), body,
376
+ idempotency_key: idempotency_key, retry_policy: :header_replay)
377
+ end
378
+
379
+ # Attach existing events to a draft Season as performances.
380
+ def attach_season_performances(season_key, event_keys:, attach_to_plan_keys: nil,
381
+ idempotency_key: nil)
382
+ body = compact({ "eventKeys" => event_keys, "attachToPlanKeys" => attach_to_plan_keys })
383
+ @client.post(path(season_key, "/performances/attach"), body,
384
+ idempotency_key: idempotency_key, retry_policy: :header_replay)
385
+ end
386
+
387
+ # Remove one performance from a draft Season.
388
+ def delete_season_performance(season_key, event_key, idempotency_key: nil)
389
+ @client.mutation_with_header_replay(
390
+ "DELETE", path(season_key, "/performances/#{encode(event_key)}"),
391
+ idempotency_key: idempotency_key
392
+ )
393
+ end
394
+
395
+ # Rename a draft package or change the performances it covers.
396
+ def update_season_plan(season_key, plan_key, name: nil, event_keys: nil, idempotency_key: nil)
397
+ @client.mutation_with_header_replay(
398
+ "PATCH", path(season_key, "/plans/#{encode(plan_key)}"),
399
+ compact({ "name" => name, "eventKeys" => event_keys }), idempotency_key: idempotency_key
400
+ )
401
+ end
402
+
403
+ # Delete a draft package. The API answers 204, so this returns an empty Hash.
404
+ def delete_season_plan(season_key, plan_key, idempotency_key: nil)
405
+ @client.mutation_with_header_replay(
406
+ "DELETE", path(season_key, "/plans/#{encode(plan_key)}"), idempotency_key: idempotency_key
407
+ )
408
+ end
409
+
410
+ # Publish a draft Season. Sends no body.
411
+ def publish_season(season_key, idempotency_key: nil)
412
+ @client.post(path(season_key, "/publish"), nil,
413
+ idempotency_key: idempotency_key, retry_policy: :header_replay)
414
+ end
415
+
416
+ # Run a test checkout against the Season.
417
+ def create_season_checkout_test(season_key, plan_key: nil, quantity: nil, idempotency_key: nil)
418
+ @client.post(path(season_key, "/checkout-tests"),
419
+ compact({ "planKey" => plan_key, "quantity" => quantity }),
420
+ idempotency_key: idempotency_key, retry_policy: :header_replay)
421
+ end
422
+
423
+ # List the Season's test checkouts.
424
+ def list_season_checkout_tests(season_key)
425
+ @client.get(path(season_key, "/checkout-tests"))
426
+ end
427
+
428
+ # Replace every price of one package. Each price hash takes "categoryKey" and
429
+ # "unitPrice" (minor units), plus an optional "priceTypeKey": "adult" (the
430
+ # default), "child", "concession", "senior" or "student".
431
+ def replace_season_package_prices(season_key, plan_key, prices:, currency: nil,
432
+ idempotency_key: nil)
433
+ @client.mutation_with_header_replay(
434
+ "PUT", path(season_key, "/plans/#{encode(plan_key)}/prices"),
435
+ compact({ "prices" => prices, "currency" => currency }), idempotency_key: idempotency_key
436
+ )
437
+ end
438
+
439
+ # List the prices of every package in the Season, each with its
440
+ # "priceTypeKey" and "priceTypeLabel".
441
+ def list_season_prices(season_key)
442
+ @client.get(path(season_key, "/prices"))
443
+ end
444
+
445
+ # Add one performance to a package.
446
+ def attach_season_plan_performance(season_key, plan_key, event_key:, idempotency_key: nil)
447
+ @client.post(path(season_key, "/plans/#{encode(plan_key)}/performances"),
448
+ { "eventKey" => event_key },
449
+ idempotency_key: idempotency_key, retry_policy: :header_replay)
450
+ end
451
+
452
+ # Remove one performance from a package.
453
+ def delete_season_plan_performance(season_key, plan_key, event_key, idempotency_key: nil)
454
+ @client.mutation_with_header_replay(
455
+ "DELETE", path(season_key, "/plans/#{encode(plan_key)}/performances/#{encode(event_key)}"),
456
+ idempotency_key: idempotency_key
457
+ )
458
+ end
459
+
460
+ # Mark a performance past so it stops blocking new Season sales. A cancelled date
461
+ # answers 409 performance_cancelled. On a season-seats season the date first leaves
462
+ # every season seat: while that finishes the answer is a 202 (check with
463
+ # +Seasons.date_leave_pending?+).
464
+ def mark_season_performance_past(season_key, event_key, force: nil, idempotency_key: nil)
465
+ @client.post(path(season_key, "/performances/#{encode(event_key)}/past"),
466
+ compact({ "force" => force }),
467
+ idempotency_key: idempotency_key, retry_policy: :header_replay)
468
+ end
469
+
470
+ # Return one Season occurrence allocation to ordinary Event inventory.
471
+ # Single-attempt: keep +action_id+ stable and retry it yourself if needed.
472
+ def return_season_occurrence_inventory(season_key, event_key, action_id:, booking_ref:,
473
+ plan_activation_id:, labels:, source:)
474
+ @client.post(path(season_key, "/occurrences/#{encode(event_key)}/returns"),
475
+ { "actionId" => action_id, "bookingRef" => booking_ref,
476
+ "planActivationId" => plan_activation_id, "labels" => labels,
477
+ "source" => source })
478
+ end
479
+
480
+ # Reclaim returned inventory for a Season occurrence. Single-attempt, like
481
+ # +return_season_occurrence_inventory+.
482
+ def reclaim_season_occurrence_inventory(season_key, event_key, action_id:, return_action_id:,
483
+ booking_ref:, plan_activation_id:, labels:, source:)
484
+ @client.post(path(season_key, "/occurrences/#{encode(event_key)}/reclaims"),
485
+ { "actionId" => action_id, "returnActionId" => return_action_id,
486
+ "bookingRef" => booking_ref, "planActivationId" => plan_activation_id,
487
+ "labels" => labels, "source" => source })
488
+ end
489
+
490
+ # One page of Season holders. +search+ is sent as the API's +q+ parameter.
491
+ def list_season_holders(season_key, search: nil, status: nil, cursor: nil, limit: nil)
492
+ @client.get(path(season_key, "/holders"),
493
+ compact({ "q" => search, "status" => status, "cursor" => cursor,
494
+ "limit" => limit }))
495
+ end
496
+
497
+ # Refund a Season order, in full or by +amount_minor+.
498
+ def refund_season_order(season_key, order_id, amount_minor: nil, idempotency_key: nil)
499
+ @client.post(path(season_key, "/orders/#{encode(order_id)}/refund"),
500
+ compact({ "amountMinor" => amount_minor }),
501
+ idempotency_key: idempotency_key, retry_policy: :header_replay)
502
+ end
503
+
504
+ # Cancel a Season order.
505
+ def cancel_season_order(season_key, order_id, reason:, note: nil, refund_confirmed: nil,
506
+ used_tickets_acknowledged: nil, notify_buyer: nil, idempotency_key: nil)
507
+ body = compact({ "reason" => reason, "note" => note, "refundConfirmed" => refund_confirmed,
508
+ "usedTicketsAcknowledged" => used_tickets_acknowledged,
509
+ "notifyBuyer" => notify_buyer })
510
+ @client.post(path(season_key, "/orders/#{encode(order_id)}/cancel"), body,
511
+ idempotency_key: idempotency_key, retry_policy: :header_replay)
512
+ end
513
+
514
+ # Cancel one date of a Season order.
515
+ def cancel_season_order_date(season_key, order_id, event_key, action_id: nil,
516
+ idempotency_key: nil)
517
+ @client.post(path(season_key, "/orders/#{encode(order_id)}/dates/#{encode(event_key)}/cancel"),
518
+ compact({ "actionId" => action_id }),
519
+ idempotency_key: idempotency_key, retry_policy: :header_replay)
520
+ end
521
+
522
+ # Send a Season order's tickets again. Single-attempt: never retried automatically.
523
+ def resend_season_order_tickets(season_key, order_id, email: nil)
524
+ @client.post(path(season_key, "/orders/#{encode(order_id)}/resend"),
525
+ compact({ "email" => email }))
526
+ end
527
+
528
+ # Read the state of hosted renewals for one page of offers, newest first. Pass
529
+ # +cursor+ from the previous page's +nextCursor+ (nil on the last page); +limit+
530
+ # is 1 to 500 (default 500).
531
+ def retrieve_season_hosted_renewals(season_key, cursor: nil, limit: nil)
532
+ @client.get(path(season_key, "/renewal-hosted"), compact({ "cursor" => cursor, "limit" => limit }))
533
+ end
534
+
535
+ # Replace a renewal offer's link. The new link is shown once, so this is
536
+ # deliberately single-attempt.
537
+ def rotate_season_renewal_offer_link(season_key, offer_id)
538
+ @client.post(path(season_key, "/renewal-offers/#{encode(offer_id)}/link"))
539
+ end
540
+
541
+ # Set the contact on a renewal offer. Pass +nil+ to send an explicit JSON null.
542
+ def update_season_renewal_offer_contact(season_key, offer_id, email: UNSET, name: UNSET)
543
+ @client.post(path(season_key, "/renewal-offers/#{encode(offer_id)}/contact"),
544
+ supplied({ "email" => email, "name" => name }))
545
+ end
546
+
547
+ # Send renewal notices, to every open offer or only to +offer_ids+ (at most
548
+ # 20,000; more is refused with 422 too_many_offer_ids).
549
+ # +batch_id+, instead of +offer_ids+: every open offer one renewal offer batch made.
550
+ def create_season_renewal_notices(season_key, offer_ids: nil, batch_id: nil)
551
+ @client.post(path(season_key, "/renewal-notices"),
552
+ compact({ "offerIds" => offer_ids, "batchId" => batch_id }))
553
+ end
554
+
555
+ # Send one renewal offer again. Single-attempt: never retried automatically.
556
+ def resend_season_renewal_offer(season_key, offer_id)
557
+ @client.post(path(season_key, "/renewal-offers/#{encode(offer_id)}/resend"))
558
+ end
559
+
560
+ # What renewing an offer costs, priced as the SeatLayer checkout will charge
561
+ # it. +problem+ is nil when SeatLayer checkout can take the renewal now.
562
+ def retrieve_season_renewal_quote(season_key, offer_id)
563
+ @client.get(path(season_key, "/renewal-offers/#{encode(offer_id)}/quote"))
564
+ end
565
+
566
+ # Record that the holder means to renew, for a renewal taken by phone or at
567
+ # the box office, so your own checkout can commit the offer. Single-attempt;
568
+ # a repeat answers the same offer.
569
+ def create_season_renewal_intent(season_key, offer_id)
570
+ @client.post(path(season_key, "/renewal-offers/#{encode(offer_id)}/intent"))
571
+ end
572
+
573
+ # Start a SeatLayer checkout that renews an offer, with no hosted page. Send
574
+ # the buyer to +payment.url+. Retried with one Idempotency-Key, which answers
575
+ # the same order.
576
+ def create_season_renewal_checkout(season_key, offer_id, return_url:, buyer_email: nil,
577
+ buyer_name: nil, provider: nil, idempotency_key: nil)
578
+ @client.post(path(season_key, "/renewal-offers/#{encode(offer_id)}/checkout"),
579
+ compact({ "returnUrl" => return_url, "buyerEmail" => buyer_email,
580
+ "buyerName" => buyer_name, "provider" => provider }),
581
+ idempotency_key: idempotency_key, retry_policy: :header_replay)
582
+ end
583
+
584
+ # Read the Season's sales report.
585
+ def retrieve_season_sales_report(season_key)
586
+ @client.get(path(season_key, "/reports/sales"))
587
+ end
588
+
589
+ # Read the Season's seats report.
590
+ def retrieve_season_seats_report(season_key)
591
+ @client.get(path(season_key, "/reports/seats"))
592
+ end
593
+
594
+ # Download the Season's holders as CSV. Returns the raw CSV text.
595
+ def export_season_holders_csv(season_key, search: nil, status: nil)
596
+ @client.get_text(path(season_key, "/reports/holders.csv"),
597
+ compact({ "q" => search, "status" => status }))
598
+ end
599
+
600
+ # Download every Season order SeatLayer took as CSV. Returns the raw CSV text.
601
+ def export_season_orders_csv(season_key)
602
+ @client.get_text(path(season_key, "/reports/orders.csv"))
603
+ end
604
+
605
+ # Read the Season's seat counts.
606
+ def retrieve_season_seat_counts(season_key)
607
+ @client.get(path(season_key, "/seat-counts"))
608
+ end
609
+
610
+ # Refund every SeatLayer order holding a cancelled date, as a job. +amount+ is
611
+ # "per_date" (the date's share of each order); "full" is refused (422
612
+ # date_refund_per_date_only), so refund a whole order with the order refund.
613
+ # Retried with one Idempotency-Key, which replays the same job.
614
+ def refund_season_performance(season_key, event_key, amount:, reason: nil, idempotency_key: nil)
615
+ @client.post(path(season_key, "/performances/#{encode(event_key)}/refunds"),
616
+ compact({ "amount" => amount, "reason" => reason }),
617
+ idempotency_key: idempotency_key, retry_policy: :header_replay)
618
+ end
619
+
620
+ # Release the seats of every lapsed renewal offer, as a job; an offer extended
621
+ # before its turn is skipped and keeps its seat. Follow it with
622
+ # +retrieve_season_bulk_job+. Retried with one Idempotency-Key, which replays the same job.
623
+ def release_season_lapsed_offers(season_key, idempotency_key: nil)
624
+ @client.post(path(season_key, "/renewal-offers/release-lapsed"), nil,
625
+ idempotency_key: idempotency_key, retry_policy: :header_replay)
626
+ end
627
+
628
+ # List the Season's bulk jobs, newest first (100 at most).
629
+ def list_season_bulk_jobs(season_key)
630
+ @client.get(path(season_key, "/jobs"))
631
+ end
632
+
633
+ # Read one bulk job with its counts and up to 500 failed items.
634
+ def retrieve_season_bulk_job(season_key, job_id)
635
+ @client.get(path(season_key, "/jobs/#{encode(job_id)}"))
636
+ end
637
+
638
+ # Set the Season's seats aside on every date: every seat its packages price, minus
639
+ # +keep_for_single_tickets+ ({ "sectionIds", "categoryKeys", "labels" }). Answers 202
640
+ # with "job" (a season_block_set_aside job to follow with +retrieve_season_bulk_job+)
641
+ # and "block". On an active Season block mode turns on by itself once every date has
642
+ # the seats (season.block.ready); on a draft, Publish turns it on. Season seats must be
643
+ # on for the Season (409 season_block_not_enabled otherwise). Retried with one
644
+ # Idempotency-Key, which replays the same answer.
645
+ def set_aside_season_block(season_key, expected_revision:, keep_for_single_tickets: nil,
646
+ idempotency_key: nil)
647
+ @client.post(path(season_key, "/block"),
648
+ compact({ "expectedRevision" => expected_revision,
649
+ "keepForSingleTickets" => keep_for_single_tickets }),
650
+ idempotency_key: idempotency_key, retry_policy: :header_replay)
651
+ end
652
+
653
+ # The Season's seats set aside on every date: "block" with its "mode" ("block" once
654
+ # on, else "per_hold"), "state", "counts" and each date's "state".
655
+ def retrieve_season_block(season_key)
656
+ @client.get(path(season_key, "/block"))
657
+ end
658
+
659
+ # Release season seats to single sale: every unsold seat (+scope+ { "all" => true }),
660
+ # or the sections, categories or seats named. +event_keys+ limits it to those dates
661
+ # (left out: every date still to come); +at+ (an ISO time) releases then instead of
662
+ # now. Answers 202 with "job" (a season_block_release job to follow with
663
+ # +retrieve_season_bulk_job+) and "block". season.block.released is sent once it has
664
+ # run on every date. Retried with one Idempotency-Key, which replays the same release.
665
+ def release_season_block(season_key, expected_revision:, scope:, event_keys: nil, at: nil,
666
+ reason: nil, idempotency_key: nil)
667
+ @client.post(path(season_key, "/block/release"),
668
+ compact({ "expectedRevision" => expected_revision, "scope" => scope,
669
+ "eventKeys" => event_keys, "at" => at, "reason" => reason }),
670
+ idempotency_key: idempotency_key, retry_policy: :header_replay)
671
+ end
672
+
252
673
  private
253
674
 
254
675
  def path(season_key, suffix = "")
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SeatLayer
4
- VERSION = "0.7.0"
4
+ VERSION = "0.9.0"
5
5
  end
data/lib/seatlayer.rb CHANGED
@@ -8,6 +8,9 @@ require_relative "seatlayer/inventory"
8
8
  require_relative "seatlayer/channels"
9
9
  require_relative "seatlayer/performance_groups"
10
10
  require_relative "seatlayer/seasons"
11
+ require_relative "seatlayer/holders"
12
+ require_relative "seatlayer/orders"
13
+ require_relative "seatlayer/event_series"
11
14
  require_relative "seatlayer/webhook"
12
15
 
13
16
  # Official Ruby server SDK for the SeatLayer reserved-seating API.
@@ -22,7 +25,7 @@ module SeatLayer
22
25
  # The SeatLayer client.
23
26
  class Client
24
27
  attr_reader :charts, :events, :inventory, :channels, :performance_groups, :sessions,
25
- :seasons, :webhooks, :workspaces, :templates
28
+ :seasons, :webhooks, :workspaces, :templates, :holders, :orders, :event_series
26
29
 
27
30
  def initialize(secret_key, base_url: HTTPClient::DEFAULT_BASE_URL,
28
31
  max_retries: HTTPClient::DEFAULT_MAX_RETRIES,
@@ -36,6 +39,9 @@ module SeatLayer
36
39
  @channels = Channels.new(@http)
37
40
  @performance_groups = PerformanceGroups.new(@http)
38
41
  @seasons = Seasons.new(@http)
42
+ @holders = Holders.new(@http)
43
+ @orders = Orders.new(@http)
44
+ @event_series = EventSeries.new(@http)
39
45
  @sessions = Sessions.new(@http)
40
46
  @templates = Templates.new(@http)
41
47
  @webhooks = Webhooks.new(@http)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: seatlayer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SeatLayer
@@ -24,8 +24,11 @@ files:
24
24
  - lib/seatlayer.rb
25
25
  - lib/seatlayer/channels.rb
26
26
  - lib/seatlayer/errors.rb
27
+ - lib/seatlayer/event_series.rb
28
+ - lib/seatlayer/holders.rb
27
29
  - lib/seatlayer/http_client.rb
28
30
  - lib/seatlayer/inventory.rb
31
+ - lib/seatlayer/orders.rb
29
32
  - lib/seatlayer/performance_groups.rb
30
33
  - lib/seatlayer/resources.rb
31
34
  - lib/seatlayer/seasons.rb
@@ -57,5 +60,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
57
60
  requirements: []
58
61
  rubygems_version: 3.6.9
59
62
  specification_version: 4
60
- summary: Official Ruby server SDK for the SeatLayer reserved-seating API.
63
+ summary: 'Official Ruby server SDK for the SeatLayer reserved-seating API: seating
64
+ charts, seat maps, holds and seat booking.'
61
65
  test_files: []