wiq-cli 0.3.0 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bffdea54163c5b95a5cab8b219d3e5cb3a898a059d7edce13afa8ae02928b27a
4
- data.tar.gz: 0f9e49529c6bc9fb37b75761e581d1de90a186107242a6c3092fcca55687f9ba
3
+ metadata.gz: eb99112754a10af0b80b20d71425f95e864fb28a3db78a1c3d0e7d75340ebf8f
4
+ data.tar.gz: ee12d73a7e5053dd48540c73c35e4327bb3f4456998f3182725b2a1d40a99403
5
5
  SHA512:
6
- metadata.gz: 57f4ccba1f18ca17207739d051e94fcfb1b52b712c2d87e97fc64951acd4041d37a69f6d89d262dfde73cc44d278549d6ac9af02b67a572e3dd549dd2c58cd95
7
- data.tar.gz: a317f13e135ab5b7a20f24b7f0c8a6640cbf84afc6a0a3e8d38d9d6483a537c302289f479b1cac0d454ca9c10485ebbcf453804decf4cdf02dbd73b822b0b933
6
+ metadata.gz: 92e5c6842312a60ad606f711b5c6893c2a2c761e181b0c999dcf0fcb6bb1e92f8cc3b25dadd6c365656b91f7b242666b43131e5680ec440036c67868f04eb794
7
+ data.tar.gz: a4e06d43acf373996c31593b6a736555ab9b5aa71a4d7c0baf606f8dc76fa090f3ae7519820ec04ed6ca1167375b977261d12312bca2fe07b70de86f902430e6
data/docs/deferred.md CHANGED
@@ -68,9 +68,16 @@ Last updated: 2026-05-12 (after L1 ships).
68
68
  --all flag, base payload kept tight. Filters translate to Ransack
69
69
  (--first-name, --last-name, --weight-class, --academic-class, --age,
70
70
  --roster, --profile-type) + legacy free-text --query. --expand opts
71
- into rosters / registration_answers per row. Multi-roster
71
+ into rosters / registration_answers / notification_preferences
72
+ (coach-only, Aug 2026 — see wiq_api_notes.md) per row. Multi-roster
72
73
  intersection deferred (API supports it; CLI surface didn't justify
73
74
  the complexity for v1). `wiq wrestlers show <id>` paired.
75
+ - ~~**`wiq parents list/show`**~~ SHIPPED narrow (Aug 2026, alongside
76
+ the `expand_notification_preferences` param from wrestling PR #2460).
77
+ Filters: --query (legacy name search), --first-name / --last-name
78
+ (Ransack). --expand notification_preferences (coach-only). Slim
79
+ payload by design (id, user_id, type, names); no wrestler refs —
80
+ family linkage stays on the wrestlers side.
74
81
  - **`wiq url parse <url>`** — extract team/roster/event IDs from WIQ web
75
82
  URLs. Initial-plan item; useful for agents pasted URLs by humans.
76
83
  - **`wiq paid_sessions create/update`**, **`wiq rosters create/update`**,
@@ -390,6 +390,60 @@ them. PATs minted by parents/wrestlers will 403 here.
390
390
  shape (`category`, `subcategory`, `detail_category`, `total_net_amount`)
391
391
  — special-case it in the formatter.
392
392
 
393
+ ## Wrestlers + parents: `expand_notification_preferences`
394
+
395
+ Shipped Aug 2026 (wrestling PR #2460). All four endpoints accept
396
+ `expand_notification_preferences=true`:
397
+
398
+ - `GET /api/v1/wrestlers` / `GET /api/v1/wrestlers/:id`
399
+ - `GET /api/v1/parents` / `GET /api/v1/parents/:id`
400
+
401
+ When set, each profile gains two fields, mirroring the "Notified via"
402
+ section on the web profile pages:
403
+
404
+ ```json
405
+ {
406
+ "wiq_app_installed": true,
407
+ "notification_preferences": {
408
+ "email": true, "sms": false, "push": true, "push_user_pref": true
409
+ }
410
+ }
411
+ ```
412
+
413
+ Things to know:
414
+
415
+ - **Coach-gated, silently.** `expand_notification_preferences?` in
416
+ `base_controller.rb` requires `current_profile.coach?`. A parent- or
417
+ wrestler-bound PAT gets a normal 200 with the fields simply absent —
418
+ no error. Don't diagnose missing fields as a bug; check the PAT's
419
+ profile type first (`wiq auth status`).
420
+ - **Nullable prefs.** `notification_preference` is a `has_one` that may
421
+ not exist; each key can be `null` (rendered via `np&.email` etc.).
422
+ Treat `null` as "no explicit preference recorded", not "off".
423
+ - The controller eager-loads `:notification_preference` only when the
424
+ flag is set, so the index cost is opt-in.
425
+
426
+ CLI exposure: `wiq wrestlers list|show --expand notification_preferences`
427
+ (composes with `rosters`, `registration_answers` in the same CSV) and
428
+ `wiq parents list|show --expand notification_preferences`.
429
+
430
+ While we're here, the parents endpoints themselves
431
+ (`app/controllers/api/v1/parents_controller.rb`):
432
+
433
+ - Index scope is `team.parent_profiles.teammates` ordered by first
434
+ name — guest parents (camp signups) are excluded. Policy scope:
435
+ coaches see the whole team (roster-restricted coaches see only their
436
+ allowed parents), wrestlers see their own guardians, parents see
437
+ nobody.
438
+ - Filters: legacy `?query=` name search, plus Ransack limited to
439
+ `id, first_name, last_name` (`ParentProfile.ransackable_attributes`).
440
+ - The jbuilder payload is slim: `id, user_id, type, first_name,
441
+ last_name, full_name` (+ the expand fields above). No wrestler refs —
442
+ walk the family from the wrestler side (`wiq wrestlers show <id>`
443
+ embeds parent refs).
444
+ - `show` uses `ParentProfile.with_deleted` — soft-deleted parents still
445
+ resolve by id.
446
+
393
447
  ## Check-ins (attendance)
394
448
 
395
449
  Two index endpoints + create/update on the event-scoped path.
data/lib/wiq/cli.rb CHANGED
@@ -83,10 +83,16 @@ module Wiq
83
83
  desc "wrestlers SUBCOMMAND", "Search wrestlers — narrow ID-discovery surface (list, show)"
84
84
  subcommand "wrestlers", Wiq::Commands::Wrestlers
85
85
 
86
+ desc "parents SUBCOMMAND", "Search parents — narrow ID-discovery surface (list, show)"
87
+ subcommand "parents", Wiq::Commands::Parents
88
+
86
89
  desc "charges SUBCOMMAND", "Payment history — list charges by family, status, type, date (admin only)"
87
90
  subcommand "charges", Wiq::Commands::Charges
88
91
 
89
92
  desc "billing_profiles SUBCOMMAND", "Look up a parent or coach's billing profile (admin only)"
90
93
  subcommand "billing_profiles", Wiq::Commands::BillingProfiles
94
+
95
+ desc "payouts SUBCOMMAND", "Bank payouts / deposits — list and show (admin only)"
96
+ subcommand "payouts", Wiq::Commands::Payouts
91
97
  end
92
98
  end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wiq
4
+ module Commands
5
+ class Parents < Base
6
+ DEFAULT_PER_PAGE = 20
7
+
8
+ desc "list", "Search parents — narrow ID-discovery surface"
9
+ long_desc <<~DESC
10
+ Narrow listing over the team's parent profiles (teammates only —
11
+ guest parents from camp signups are excluded server-side). Rows
12
+ are slim: id, user_id, type, first_name, last_name, full_name.
13
+ Sorted by first name. Default page size is 20; there's no --all
14
+ flag.
15
+
16
+ Filters:
17
+ --query free-text name search (legacy `?query=`)
18
+ --first-name q[first_name_cont]
19
+ --last-name q[last_name_cont]
20
+
21
+ --expand opts into a wider payload:
22
+ notification_preferences Adds wiq_app_installed + a
23
+ notification_preferences object
24
+ (email, sms, push, push_user_pref)
25
+ per parent — "which parents can we
26
+ reach, and how?". Coach PATs only;
27
+ the server silently omits it for
28
+ parent/wrestler tokens.
29
+
30
+ The parent → wrestler linkage is NOT in this payload. To see a
31
+ family's kids, go the other direction: `wiq wrestlers show <id>`
32
+ embeds parent refs, or `wiq wrestlers list --query <last name>`.
33
+ DESC
34
+ method_option :query, type: :string, desc: "Free-text name search"
35
+ method_option :first_name, type: :string, desc: "First name (contains)"
36
+ method_option :last_name, type: :string, desc: "Last name (contains)"
37
+ method_option :expand, type: :string,
38
+ desc: "CSV: notification_preferences"
39
+ method_option :per_page, type: :numeric, default: DEFAULT_PER_PAGE,
40
+ desc: "Page size (default 20; narrow surface)"
41
+ def list
42
+ params = build_list_params
43
+
44
+ records, total = fetch_index("/api/v1/parents", params, key: "parent_profiles")
45
+ render_index(
46
+ records, total: total,
47
+ summary: "Listed #{records.size} parents#{summary_filters_suffix}.",
48
+ breadcrumbs: [
49
+ { "cmd" => "wiq parents show <id>", "description" => "Drill into one parent" },
50
+ { "cmd" => "wiq billing_profiles show <id> --profile-type ParentProfile",
51
+ "description" => "Billing profile for a parent (admin only)" },
52
+ { "cmd" => "wiq wrestlers list --last-name <name>",
53
+ "description" => "Find the family's wrestlers (parent refs embedded)" }
54
+ ]
55
+ )
56
+ end
57
+
58
+ desc "show ID", "Fetch a single parent profile"
59
+ long_desc <<~DESC
60
+ Single-parent drill-down. Same slim payload as the index (id,
61
+ user_id, type, names); soft-deleted parents still resolve here.
62
+
63
+ Pass --expand for additional sections:
64
+ notification_preferences wiq_app_installed + notification_preferences
65
+ (email, sms, push, push_user_pref) —
66
+ "can this parent be reached, and how?"
67
+ Coach PATs only; silently omitted for
68
+ parent/wrestler tokens.
69
+ DESC
70
+ method_option :expand, type: :string,
71
+ desc: "CSV: notification_preferences"
72
+ def show(id)
73
+ params = {}
74
+ params["expand_notification_preferences"] = true if expand_includes?("notification_preferences")
75
+ parent = client.get("/api/v1/parents/#{id}", params)
76
+ render(parent,
77
+ summary: "Parent #{parent["id"]} — #{parent["full_name"]}",
78
+ breadcrumbs: [
79
+ { "cmd" => "wiq billing_profiles show #{parent["id"]} --profile-type ParentProfile",
80
+ "description" => "Billing profile for this parent (admin only)" },
81
+ { "cmd" => "wiq wrestlers list --last-name #{parent["last_name"]}",
82
+ "description" => "Likely wrestlers for this family" }
83
+ ])
84
+ end
85
+
86
+ no_commands do
87
+ # Extracted so the spec can verify the option → query-param mapping
88
+ # without round-tripping a real Faraday connection.
89
+ def build_list_params
90
+ params = { "per_page" => options[:per_page] || DEFAULT_PER_PAGE }
91
+ params["query"] = options[:query] if options[:query]
92
+ params["q[first_name_cont]"] = options[:first_name] if options[:first_name]
93
+ params["q[last_name_cont]"] = options[:last_name] if options[:last_name]
94
+ params["expand_notification_preferences"] = true if expand_includes?("notification_preferences")
95
+ params
96
+ end
97
+
98
+ def expand_includes?(part)
99
+ return false unless options[:expand]
100
+
101
+ options[:expand].split(",").map(&:strip).include?(part)
102
+ end
103
+
104
+ def summary_filters_suffix
105
+ bits = []
106
+ bits << "matching #{options[:query].inspect}" if options[:query]
107
+ bits << "first name ~ #{options[:first_name].inspect}" if options[:first_name]
108
+ bits << "last name ~ #{options[:last_name].inspect}" if options[:last_name]
109
+ bits.empty? ? "" : " (#{bits.join(", ")})"
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wiq
4
+ module Commands
5
+ class Payouts < Base
6
+ # Payout.status is a plain text column mirroring the billing
7
+ # partner's normalized status — no integer-enum translation needed
8
+ # (unlike Charge.status). Common values below; the column is not
9
+ # constrained server-side, so --status stays free-form.
10
+ COMMON_STATUSES = %w[paid in_transit scheduled].freeze
11
+ BILLING_PARTNERS = %w[stripe justifi].freeze
12
+ DEFAULT_PER_PAGE = 25
13
+
14
+ desc "list", "List bank payouts / deposits (finance: admin coach only)"
15
+ long_desc <<~DESC
16
+ Returns payouts — the batched deposits Stripe or Justifi (WIQ
17
+ Payments) sends to the club's bank account. Useful for "when does
18
+ our money arrive?", "what landed in the bank last week?", and
19
+ reconciling a bank statement line against WIQ charges.
20
+
21
+ Auth: admin-only (Pundit scope filters non-admin coach PATs to
22
+ empty results; parent/wrestler PATs return 403).
23
+
24
+ Filters translate to Ransack server-side (allowlist: deposits_at,
25
+ status, billing_partner, amount, created_at):
26
+ --status <str> q[status_eq]. Common values: paid,
27
+ in_transit, scheduled. Free-form —
28
+ the column mirrors the billing
29
+ partner's status strings.
30
+ --billing-partner <str> q[billing_partner_eq] — stripe | justifi
31
+ (justifi is branded "WIQ Payments").
32
+ --since <YYYY-MM-DD> q[deposits_at_gteq]
33
+ --until <YYYY-MM-DD> q[deposits_at_lteq]
34
+
35
+ --since/--until filter on deposits_at (when the money reaches the
36
+ bank), not created_at — that's the date a treasurer reconciling a
37
+ bank statement cares about. Rows come back newest-deposit first.
38
+
39
+ Money fields are integer cents. Each row: amount (net deposit),
40
+ payments_count/payments_total, refunds_count/refunds_total,
41
+ fees_total, status, deposits_at, billing_partner, description,
42
+ and destination (bank name + last4). Payouts accrue slowly (a
43
+ few per week), so default page size is #{DEFAULT_PER_PAGE}; pass
44
+ --all with --since to bound an exhaustive scan.
45
+
46
+ To see which individual charges landed in a payout, go through
47
+ the charges side: each `wiq charges list` row embeds its payout,
48
+ so filter charges by date window and group on payout.id.
49
+ DESC
50
+ method_option :status, type: :string,
51
+ desc: "Filter by status (common: #{COMMON_STATUSES.join(", ")})"
52
+ method_option :billing_partner, type: :string, enum: BILLING_PARTNERS,
53
+ desc: "Filter by billing partner (stripe | justifi)"
54
+ method_option :since, type: :string, desc: "Earliest deposits_at (YYYY-MM-DD)"
55
+ method_option :until, type: :string, desc: "Latest deposits_at (YYYY-MM-DD)"
56
+ method_option :per_page, type: :numeric, default: DEFAULT_PER_PAGE,
57
+ desc: "Page size (default #{DEFAULT_PER_PAGE})"
58
+ method_option :all, type: :boolean, default: false,
59
+ desc: "Follow pagination until exhausted"
60
+ def list
61
+ params = build_list_params
62
+ records, total = fetch_index("/api/v1/payouts", params, key: "payouts")
63
+ render_index(
64
+ records, total: total,
65
+ summary: "Listed #{records.size} payouts#{summary_filters_suffix}.",
66
+ breadcrumbs: [
67
+ { "cmd" => "wiq payouts show <id>",
68
+ "description" => "Inspect a single payout" },
69
+ { "cmd" => "wiq charges list --since <date> --until <date> --all",
70
+ "description" => "Charges in a window — each row embeds its payout for reconciliation" }
71
+ ]
72
+ )
73
+ end
74
+
75
+ desc "show ID", "Fetch a single payout"
76
+ long_desc <<~DESC
77
+ Full payout payload: amount, currency, status, deposits_at,
78
+ payments_count/payments_total, refunds_count/refunds_total,
79
+ fees_total, billing_partner (+ billing_partner_id, the Stripe/
80
+ Justifi payout id like po_...), delivery_method, description, and
81
+ destination bank account (holder name, bank name, last4,
82
+ account type). Money fields are integer cents.
83
+
84
+ Auth: admin-only; a payout outside the PAT's team returns 403.
85
+ DESC
86
+ def show(id)
87
+ payout = client.get("/api/v1/payouts/#{id}")
88
+ render(payout,
89
+ summary: "Payout #{payout["id"]} — #{payout["status"]}, " \
90
+ "#{format_cents(payout["amount"])} deposited #{payout["deposits_at"]}",
91
+ breadcrumbs: [
92
+ { "cmd" => "wiq charges list --since <date> --until <date> --all",
93
+ "description" => "Find this payout's charges (rows embed payout.id)" }
94
+ ])
95
+ end
96
+
97
+ no_commands do
98
+ def build_list_params
99
+ params = { "per_page" => options[:per_page] || DEFAULT_PER_PAGE }
100
+ params["q[status_eq]"] = options[:status] if options[:status]
101
+ params["q[billing_partner_eq]"] = options[:billing_partner] if options[:billing_partner]
102
+ params["q[deposits_at_gteq]"] = options[:since] if options[:since]
103
+ params["q[deposits_at_lteq]"] = options[:until] if options[:until]
104
+ params
105
+ end
106
+
107
+ def summary_filters_suffix
108
+ bits = []
109
+ bits << "status=#{options[:status]}" if options[:status]
110
+ bits << "billing_partner=#{options[:billing_partner]}" if options[:billing_partner]
111
+ bits << "since=#{options[:since]}" if options[:since]
112
+ bits << "until=#{options[:until]}" if options[:until]
113
+ bits.empty? ? "" : " (#{bits.join(", ")})"
114
+ end
115
+
116
+ def format_cents(cents)
117
+ return "?" unless cents.is_a?(Numeric)
118
+
119
+ format("$%.2f", cents / 100.0)
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -35,8 +35,14 @@ module Wiq
35
35
  client-side.
36
36
 
37
37
  --expand opts into a wider payload:
38
- rosters Adds full roster details per wrestler
39
- registration_answers Adds intake-form answers per wrestler
38
+ rosters Adds full roster details per wrestler
39
+ registration_answers Adds intake-form answers per wrestler
40
+ notification_preferences Adds wiq_app_installed + a
41
+ notification_preferences object
42
+ (email, sms, push, push_user_pref)
43
+ per wrestler. Coach PATs only — the
44
+ server silently omits it for
45
+ parent/wrestler tokens.
40
46
 
41
47
  Base payload is already wider than most index endpoints (parents,
42
48
  coach_guardians, profile_photos, basic roster refs all render by
@@ -58,7 +64,7 @@ module Wiq
58
64
  desc: "Default 'teammate' matches the WIQ web UI default; " \
59
65
  "'all' removes the filter entirely"
60
66
  method_option :expand, type: :string,
61
- desc: "CSV: rosters, registration_answers"
67
+ desc: "CSV: rosters, registration_answers, notification_preferences"
62
68
  method_option :per_page, type: :numeric, default: DEFAULT_PER_PAGE,
63
69
  desc: "Page size (default 20; narrow surface)"
64
70
  def list
@@ -84,15 +90,21 @@ module Wiq
84
90
  rosters refs).
85
91
 
86
92
  Pass --expand for additional sections:
87
- rosters Full roster details with tags
88
- registration_answers Intake-form answers
93
+ rosters Full roster details with tags
94
+ registration_answers Intake-form answers
95
+ notification_preferences wiq_app_installed + notification_preferences
96
+ (email, sms, push, push_user_pref) —
97
+ "can this family be reached, and how?"
98
+ Coach PATs only; silently omitted for
99
+ parent/wrestler tokens.
89
100
  DESC
90
101
  method_option :expand, type: :string,
91
- desc: "CSV: rosters, registration_answers"
102
+ desc: "CSV: rosters, registration_answers, notification_preferences"
92
103
  def show(id)
93
104
  params = {}
94
105
  params["expand_rosters"] = true if expand_includes?("rosters")
95
106
  params["expand_registration_answers"] = true if expand_includes?("registration_answers")
107
+ params["expand_notification_preferences"] = true if expand_includes?("notification_preferences")
96
108
  wrestler = client.get("/api/v1/wrestlers/#{id}", params)
97
109
  render(wrestler,
98
110
  summary: "Wrestler #{wrestler["id"]} — #{wrestler["full_name"] || wrestler["display_name"]}",
@@ -120,6 +132,7 @@ module Wiq
120
132
  params["q[profile_type_eq]"] = options[:profile_type] if profile_type_filter?
121
133
  params["expand_rosters"] = true if expand_includes?("rosters")
122
134
  params["expand_registration_answers"] = true if expand_includes?("registration_answers")
135
+ params["expand_notification_preferences"] = true if expand_includes?("notification_preferences")
123
136
  params
124
137
  end
125
138
 
data/lib/wiq/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Wiq
4
- VERSION = "0.3.0"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/wiq.rb CHANGED
@@ -26,6 +26,8 @@ require "wiq/commands/prospect_families"
26
26
  require "wiq/commands/workflows"
27
27
  require "wiq/commands/setup"
28
28
  require "wiq/commands/wrestlers"
29
+ require "wiq/commands/parents"
29
30
  require "wiq/commands/charges"
30
31
  require "wiq/commands/billing_profiles"
32
+ require "wiq/commands/payouts"
31
33
  require "wiq/cli"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: wiq
3
- description: Use this skill when the user asks about their WrestlingIQ data — rosters, attendance, check-ins, paid sessions and registrations, the prospects/leads pipeline, financial metrics, reports, USAW/AAU memberships, fundraising, online store orders, or per-location/site breakdowns for multi-gym clubs. The `wiq` CLI provides read-only access to /api/v1 via personal access tokens. Recognize phrasings like "how is our pipeline?", "who came to practice this week?", "show me the roster", "what's our MRR?", "which kids need USAW renewal?", "how is the Eastside gym doing?", or anything that maps to a wrestling club's admin workflows.
3
+ description: Use this skill when the user asks about their WrestlingIQ data — rosters, attendance, check-ins, paid sessions and registrations, the prospects/leads pipeline, financial metrics, payouts/bank deposits, reports, USAW/AAU memberships, fundraising, online store orders, or per-location/site breakdowns for multi-gym clubs. The `wiq` CLI provides read-only access to /api/v1 via personal access tokens. Recognize phrasings like "how is our pipeline?", "who came to practice this week?", "show me the roster", "what's our MRR?", "which kids need USAW renewal?", "how is the Eastside gym doing?", or anything that maps to a wrestling club's admin workflows.
4
4
  ---
5
5
 
6
6
  # WrestlingIQ CLI Skill
@@ -284,6 +284,25 @@ charge for the same `(billing_profile_id, chargeable_id,
284
284
  chargeable_type)` tuple exists AFTER the failure's `created_at`. The
285
285
  canonical pattern is in `wiq workflows show failed-payments-recent`.
286
286
 
287
+ ### Payouts (bank deposits)
288
+
289
+ For "when does our money hit the bank?" / "what deposited last week?" /
290
+ bank-statement reconciliation — use the payouts surface (admin PAT only):
291
+
292
+ ```bash
293
+ wiq payouts list --since 2026-08-01 --until 2026-08-18 # deposits in a window
294
+ wiq payouts list --status in_transit # money on the way
295
+ wiq payouts show <id> # one deposit's detail
296
+ ```
297
+
298
+ `--since/--until` filter on `deposits_at` (bank arrival date), not
299
+ `created_at`. `Payout.status` is a plain string column (paid,
300
+ in_transit, scheduled, ...) — no integer-enum translation, unlike
301
+ charges. All
302
+ money fields are integer cents. To see which charges make up a payout,
303
+ go through charges: each `wiq charges list` row embeds its `payout`,
304
+ so pull charges for the date window and group by `payout.id`.
305
+
287
306
  To go from a wrestler name to a billing_profile_id:
288
307
 
289
308
  ```bash
@@ -343,6 +362,25 @@ For exhaustive exports go through `wiq reports run RosterReport`
343
362
  `profile_type=teammate` (matching the WIQ web UI default); pass
344
363
  `--profile-type alumnus|guest|all` to widen.
345
364
 
365
+ For "can this family be reached, and how?" questions, add
366
+ `--expand notification_preferences` to `wrestlers list|show` or
367
+ `parents list|show`. Each row gains `wiq_app_installed` plus a
368
+ `notification_preferences` object (`email`, `sms`, `push`,
369
+ `push_user_pref`; `null` means no explicit preference recorded). Coach
370
+ PATs only — with a parent/wrestler token the server returns 200 with
371
+ the fields silently absent, so a missing field means "check the
372
+ token's profile type", not "API bug".
373
+
374
+ ### Parents
375
+
376
+ `wiq parents list [--query <name>] [--first-name X] [--last-name Y]`
377
+ and `wiq parents show <id>` — slim payload (id, user_id, names) over
378
+ the team's teammate parents. There are no wrestler refs on a parent
379
+ row; to walk a family, start from the wrestler side
380
+ (`wiq wrestlers show <id>` embeds parent refs). Main uses: parent-first
381
+ ID discovery for `wiq billing_profiles show <id> --profile-type
382
+ ParentProfile`, and the notification-reachability expand above.
383
+
346
384
  ## Locations (multi-site clubs)
347
385
 
348
386
  WIQ has a structured Location model (name + street address, per team).
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wiq-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - WrestlingIQ
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-02 00:00:00.000000000 Z
11
+ date: 2026-08-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -93,6 +93,8 @@ files:
93
93
  - lib/wiq/commands/locations.rb
94
94
  - lib/wiq/commands/metrics.rb
95
95
  - lib/wiq/commands/paid_sessions.rb
96
+ - lib/wiq/commands/parents.rb
97
+ - lib/wiq/commands/payouts.rb
96
98
  - lib/wiq/commands/prospect_families.rb
97
99
  - lib/wiq/commands/prospects.rb
98
100
  - lib/wiq/commands/registrations.rb