hcp 3.0.1 → 4.0.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: 4d401d8b087f9b1c05637aaad2f69c6f343ff3d8d50809c02d1b12f6d3fa2889
4
- data.tar.gz: e58432a01d9530029c912b92451c9e94101dd8de0982ca072fb540572d14cbfb
3
+ metadata.gz: 6452e662bc00ffd31b8fa21a935de8c1b4ef9d4779a87668ce4f6b4c4585b3b0
4
+ data.tar.gz: e9e8e77e53671ef7e822f2116d32a231ab6f71901de418bdd8643acd46232de5
5
5
  SHA512:
6
- metadata.gz: 9254b6d9c951494705a671b09484a8bef8586ca085e5a75b3101e66811179cdadd1239e6dd3598d80b9aa097bc0b6a9e7deb982351abc74c48de6f224a7320d8
7
- data.tar.gz: 0afdf26a35fa1a7c36e8862d272e39590b02f1d7462393bc972379c6a51a32d7f210c02ff9a0788ef0b973bb02259063d489503abc2bbab43ddfe487b1fe5fc1
6
+ metadata.gz: 4b03d7602db5e603bd0b0ba03c9031fb93f29bece4d5984a460f0fb4f79b5d7a740de189c4c0d281919650f39f5bf84e4019d8cc5988630114aaec5a59fdab45
7
+ data.tar.gz: 38068b949e79d8c8c26bbfe07f5c75546f4f11964a4ec9259eaab168df458423be6eac7575a070ed7cb91b0a529d3a11d22b6f2d2605decf7ea39d25847ad0c4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,56 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [4.0.0] - 2026-09-16
4
+
5
+ - [Breaking change] The vocabulary is `company` 3.0: `assigned_to` is `of`, so
6
+ `account.visits.of(technician)` is what `account.visits.assigned_to(technician)` was. The pin
7
+ is `~> 3.0`.
8
+
9
+ - [Feature] `account.windows`, the free time of the location: every stretch nobody is booked
10
+ for, read off `GET /company/schedule_availability/booking_windows`. Housecall Pro works it out
11
+ for itself -- it holds the hours the business keeps, the notice it needs, the padding it
12
+ leaves around a job and the calendar blocked out -- so this asks rather than reckons, and an
13
+ hour held is already taken off.
14
+
15
+ - [Feature] Housecall Pro answers a week as a row of short windows, each open or not. The open
16
+ ones that run together are joined back into the stretch they came from, so a window is as long
17
+ as the pro is free rather than as long as their booking page happens to offer. What to cut it
18
+ into is the caller's, where an offer is being made.
19
+
20
+ - [Note] `start_date`, `show_for_days` and `employee_ids` all narrow this list, so one person's
21
+ week is one request -- unlike `/events`, which accepts the same words and ignores them. An
22
+ account with nothing open answers a bare list rather than the usual object, and both shapes
23
+ are read.
24
+
25
+ ## [3.1.0] - 2026-09-16
26
+
27
+ - [Feature] `account.visits` covers the time blocked out around the work, which Housecall Pro
28
+ files as events: a hold, a day off, an hour that is simply not free. It occupies a pro
29
+ exactly as a stop does, and Jobber has answered with it all along, so a schedule read through
30
+ this gem now says the same thing as one read through `jbr`. Each reads as a `Hcp::Occurrence`
31
+ standing on no job and no lead, and a caller that wants what it had before asks `for_jobs`.
32
+
33
+ - [Feature] An event that repeats is stored by Housecall Pro once, as the hour it first takes
34
+ and the iCalendar rule it repeats by, so the hours after that are worked out here -- in the
35
+ time zone the event is kept in, so an hour that holds at ten in the morning still holds at
36
+ ten once the clocks have gone back rather than sliding to nine. `ice_cube` walks the rule;
37
+ it is a new runtime dependency, and the reason not to hand-roll one is that rule, DST and
38
+ month-end clamping together are where a hand-rolled walk quietly goes wrong.
39
+
40
+ - [Feature] An hour answers to the event's ID and the moment it starts -- `evt_1@2026-09-18T14:15:00Z`
41
+ -- there being one ID for the whole rule and nothing else to tell two of its hours apart.
42
+
43
+ - [Note] Housecall Pro narrows events by nothing. `/events` accepts `scheduled_start_min`,
44
+ `scheduled_start_max` and `employee_ids[]` and ignores all three, answering with the same
45
+ total every time, so a week is read by sweeping every page at 200 a page and keeping what the
46
+ week holds. On an account with 2,877 events that is 15 requests, which a nightly walk can
47
+ afford and a caller that only wants the stops of work should not pay: `for_jobs` and
48
+ `for_leads` now leave the sweep unasked.
49
+
50
+ - [Note] An event carries an address but no ID for it, and leaves it empty on all but a
51
+ handful, so `visit.location` is nil on blocked-out time -- as it is in Jobber, where an event
52
+ carries no property either.
53
+
3
54
  ## [3.0.1] - 2026-09-15
4
55
 
5
56
  - [Fix] `visit.anytime?` answers false on an estimate's slot rather than nil. Housecall Pro
data/README.md CHANGED
@@ -96,13 +96,24 @@ account.visits.upcoming(2.weeks).each do |visit|
96
96
  visit.id, visit.starts_at, visit.ends_at, visit.anytime?
97
97
  visit.description # => what the job is called, or nil: an estimate has no words of its own
98
98
  visit.location # => where the stop is, whatever it was booked for
99
- visit.job # => the Hcp::Job the stop belongs to, or nil where an estimate does
100
- visit.lead # => the Hcp::Estimate it belongs to, or nil where a job does
99
+ visit.job # => the Hcp::Job the stop belongs to, nil on an estimate or an event
100
+ visit.lead # => the Hcp::Estimate it belongs to, nil on a job or an event
101
101
  visit.technicians # => the Hcp::Technicians the stop is booked for
102
102
  end
103
103
  ```
104
104
 
105
- The two cost a list each, so a caller that wants one kind asks for it and spends one request:
105
+ Time blocked out around the work counts as booked too, and Housecall Pro files it as an event:
106
+ a hold, a day off, an hour that is simply not free. An event that repeats is stored once, as the
107
+ hour it first takes and the iCalendar rule it repeats by, so the hours after that are worked out
108
+ here rather than read -- in the time zone the event is kept in, so an hour that holds at ten in
109
+ the morning still holds at ten once the clocks have gone back. Each one reads as a visit
110
+ standing on no job and no lead, and answers to the event's ID and the moment it starts, there
111
+ being one ID for the whole rule.
112
+
113
+ Housecall Pro narrows events by nothing -- it accepts a window and a crew on `/events` and
114
+ ignores both -- so a week of them is read by sweeping every page and keeping what the week
115
+ holds. That is the one expensive part of the list, and the one way to be spared it is to ask
116
+ for the stops of work alone:
106
117
 
107
118
  ```ruby
108
119
  account.visits.upcoming(2.weeks).for_jobs # => only the appointments, one request
@@ -136,7 +147,7 @@ One technician's week is the visits in it narrowed to them, which is how a sched
136
147
 
137
148
  ```ruby
138
149
  monday = Date.today.beginning_of_week.in_time_zone
139
- account.visits.between(monday, monday + 1.week).assigned_to(technician).each do |visit|
150
+ account.visits.between(monday, monday + 1.week).of(technician).each do |visit|
140
151
  visit.starts_at, visit.ends_at, visit.job.location.street
141
152
  end
142
153
  ```
@@ -148,6 +159,24 @@ dispatched to some of its crew or to none of it, so a stop dispatched to nobody
148
159
  crew's, and the stops the technician is not on are let go once the jobs arrive. Asking for the
149
160
  week and asking for the technician narrow the same list, in either order.
150
161
 
162
+ The other half of that week is the hours they are *not* out, which Housecall Pro works out for
163
+ itself and answers whole:
164
+
165
+ ```ruby
166
+ account.windows.between(monday, monday + 1.week).of(technician).each do |window|
167
+ window.starts_at, window.ends_at
168
+ end
169
+ ```
170
+
171
+ It holds the hours the business keeps, the notice it needs, the padding it leaves around a job
172
+ and the time blocked out on the calendar, so this asks rather than reckons and the holds are
173
+ already taken off. It answers a week as a row of short windows, each open or not; the open ones
174
+ that run together are one stretch and are joined back into it, so a window is as long as the pro
175
+ is free rather than as long as their booking page happens to offer.
176
+
177
+ `start_date`, `show_for_days` and `employee_ids` all narrow, so a week for one person is the one
178
+ request -- unlike `/events`, which accepts the same words and ignores them.
179
+
151
180
  What Housecall Pro schedules elsewhere is still not here: time blocked out on the calendar is
152
181
  filed under `/events`, which takes no date and no employee to narrow by, so a week of it cannot
153
182
  be asked for -- only paged in full.
data/lib/hcp/account.rb CHANGED
@@ -16,6 +16,9 @@ module Hcp
16
16
  # @return [Visits] visits of the location, read off its jobs.
17
17
  def visits = Visits.new client: @client
18
18
 
19
+ # @return [Windows] free time of the location, as Housecall Pro works it out.
20
+ def windows = Windows.new client: @client
21
+
19
22
  # @return [Technicians] employees of the location, walked a page at a time.
20
23
  def technicians = Technicians.new client: @client
21
24
 
@@ -0,0 +1,72 @@
1
+ module Hcp
2
+ # The blocked-out time of one location: the hours Housecall Pro files as events rather than as
3
+ # work. It narrows events by nothing -- neither a window nor a crew, both of which it accepts
4
+ # and ignores -- so the only way to read a week of them is to sweep every page and keep what
5
+ # the week holds. An event that repeats is stored once, as the hour it first takes and the
6
+ # iCalendar rule it repeats by, so every hour after that is worked out here rather than read.
7
+ class Occurrences < Company::Collection
8
+ # How far an open end reaches. A rule that repeats forever has no last hour to walk to, so a
9
+ # caller who named no end gets a year of them, which is a schedule rather than an eternity.
10
+ HORIZON = 1.year
11
+
12
+ # @param client [Client] how to reach Housecall Pro as the location.
13
+ # @param from [Time, nil] the moment the window opens, or nothing for from now on.
14
+ # @param to [Time, nil] the moment the window closes, or nothing for a year of it.
15
+ def initialize(client:, from: nil, to: nil)
16
+ @client = client
17
+ @from = from
18
+ @to = to
19
+ end
20
+
21
+ # @param from [Time, nil] the moment the window opens, or nothing for from now on.
22
+ # @param to [Time, nil] the moment the window closes, or nothing for a year of it.
23
+ # @return [Occurrences] the same list, narrowed to the hours taken between the two.
24
+ def between(from, to) = self.class.new(client: @client, from: from, to: to)
25
+
26
+ # Nothing is read until the walk starts, and a page only once the one before it runs out.
27
+ # @yield [Occurrence] each stretch blocked out in the window, event by event.
28
+ def each
29
+ events { |event| hours_of(event).each { |node| yield Occurrence.new node: node } }
30
+ end
31
+
32
+ private
33
+
34
+ def events
35
+ (1..).each do |page|
36
+ body = @client.get 'events', page: page, page_size: Jobs::PAGE
37
+ body.fetch('events').each { |node| yield node }
38
+ break if page >= body.fetch('total_pages')
39
+ end
40
+ end
41
+
42
+ def hours_of(event)
43
+ schedule = event.fetch 'schedule'
44
+ first = Time.iso8601 schedule.fetch('start_time')
45
+ length = Time.iso8601(schedule.fetch('end_time')) - first
46
+ starts(event['recurrence_rule'], first, schedule['time_zone']).map do |moment|
47
+ event.merge 'id' => "#{event['id']}@#{moment.utc.iso8601}",
48
+ 'start_time' => moment.utc.iso8601, 'end_time' => (moment + length).utc.iso8601
49
+ end
50
+ end
51
+
52
+ # An hour is repeated in the time zone the event is kept in rather than in UTC, so a rule
53
+ # that says ten in the morning still says ten once the clocks have gone back.
54
+ def starts(rule, first, zone)
55
+ return [ first ].select { |moment| window.cover? moment } if rule.blank?
56
+
57
+ repeated rule, first.in_time_zone(zone || 'UTC')
58
+ end
59
+
60
+ def repeated(rule, first)
61
+ schedule = IceCube::Schedule.new first
62
+ schedule.add_recurrence_rule IceCube::Rule.from_ical(rule)
63
+ schedule.occurrences_between opens, closes
64
+ end
65
+
66
+ def window = opens..closes
67
+
68
+ def opens = @from || Time.now
69
+
70
+ def closes = @to || opens + HORIZON
71
+ end
72
+ end
@@ -9,13 +9,16 @@ module Hcp
9
9
  # @param technician [Company::Technician, nil] whose work to ask for, or nothing for all.
10
10
  # @param jobs [Boolean] whether to read the stops of jobs.
11
11
  # @param leads [Boolean] whether to read the stops of estimates.
12
- def initialize(client:, from: nil, to: nil, technician: nil, jobs: true, leads: true)
12
+ # @param events [Boolean] whether to read the hours blocked out around them.
13
+ def initialize(client:, from: nil, to: nil, technician: nil, jobs: true, leads: true,
14
+ events: true)
13
15
  @client = client
14
16
  @from = from
15
17
  @to = to
16
18
  @technician = technician
17
19
  @jobs = jobs
18
20
  @leads = leads
21
+ @events = events
19
22
  end
20
23
 
21
24
  # @param from [Time, nil] the moment the window opens, or nothing for every visit there was.
@@ -28,34 +31,38 @@ module Hcp
28
31
  # to it, so the stops they are not on are let go once the work comes back.
29
32
  # @param technician [Company::Technician] whoever the work is booked for.
30
33
  # @return [Company::Selection] the same list, narrowed to the stops they are booked for.
31
- def assigned_to(technician)
34
+ def of(technician)
32
35
  theirs = with technician: technician
33
36
  Company::Selection.new(collection: theirs) do |visit|
34
37
  visit.technicians.any? { |each| each.id == technician.id }
35
38
  end
36
39
  end
37
40
 
38
- # @return [Visits] the same list, read off the jobs alone: one request rather than two.
39
- def for_jobs = with leads: false
41
+ # @return [Visits] the same list, read off the jobs alone: one request rather than many.
42
+ def for_jobs = with(leads: false, events: false)
40
43
 
41
- # @return [Visits] the same list, read off the estimates alone: one request rather than two.
42
- def for_leads = with jobs: false
44
+ # @return [Visits] the same list, read off the estimates alone: one request rather than many.
45
+ def for_leads = with(jobs: false, events: false)
43
46
 
44
47
  # Work booked across the window carries every stop in it, so each list is read once and
45
48
  # what was called off keeps its stops to itself.
46
- # @yield [Visit] each visit in the window, the jobs' stops before the estimates'.
49
+ # @yield [Company::Visit] each visit in the window, the work's stops before the hours
50
+ # blocked out around them.
47
51
  def each(&)
48
52
  jobs.each { |job| stops job, & } if @jobs
49
53
  estimates.each { |estimate| stops estimate, & } if @leads
54
+ occurrences.each(&) if @events
50
55
  end
51
56
 
52
57
  private
53
58
 
54
59
  def with(**changed)
55
60
  self.class.new(**{ client: @client, from: @from, to: @to, technician: @technician,
56
- jobs: @jobs, leads: @leads }.merge(changed))
61
+ jobs: @jobs, leads: @leads, events: @events, }.merge(changed))
57
62
  end
58
63
 
64
+ def occurrences = Occurrences.new(client: @client, from: @from, to: @to)
65
+
59
66
  def stops(work)
60
67
  return if work.canceled?
61
68
 
@@ -0,0 +1,74 @@
1
+ module Hcp
2
+ # The free time of one location, which Housecall Pro works out for itself: it holds the hours
3
+ # the business keeps, the notice it needs and the padding it leaves around a job, and answers
4
+ # which stretches of a week are still open. So this asks rather than reckons, and the hours
5
+ # blocked out on the calendar are already taken off.
6
+ class Windows < Company::Windows
7
+ # How far ahead to ask where nothing named an end. Housecall Pro's own default, and a week
8
+ # is what a schedule is read by.
9
+ DAYS = 7
10
+
11
+ # @param client [Client] how to reach Housecall Pro as the location.
12
+ # @param technician [Company::Technician, nil] whose free time to ask for, or nothing for
13
+ # whether anybody at all is free.
14
+ # @param from [Time, nil] the moment the window opens, or nothing for the first day open.
15
+ # @param to [Time, nil] the moment the window closes, or nothing for a week of it.
16
+ def initialize(client:, technician: nil, from: nil, to: nil)
17
+ @client = client
18
+ @technician = technician
19
+ @from = from
20
+ @to = to
21
+ end
22
+
23
+ # @param from [Time, nil] the moment the window opens, or nothing for the first day open.
24
+ # @param to [Time, nil] the moment the window closes, or nothing for a week of it.
25
+ # @return [Windows] the same list, narrowed to the free time between the two.
26
+ def between(from, to) = with(from: from, to: to)
27
+
28
+ # Housecall Pro narrows free time by who it is free for, so the technician joins the window
29
+ # in the one request and nobody else's hours are answered or paid for.
30
+ # @param technician [Company::Technician] whose free time to answer.
31
+ # @return [Windows] the same list, as that technician's alone.
32
+ def of(technician) = with(technician: technician)
33
+
34
+ # Housecall Pro answers a week as a row of short windows, each open or not. Consecutive open
35
+ # ones are one stretch of free time and are joined back into it, so what comes out is as long
36
+ # as the pro is actually free rather than as long as their booking page happens to offer.
37
+ # @yield [Company::Window] each stretch nobody is booked for, earliest first.
38
+ def each
39
+ joined(open).each do |from, to|
40
+ yield Company::Window.new node: { starts_at: from, ends_at: to }
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def open = answered.select { it['available'] }.map { [ it['start_time'], it['end_time'] ] }
47
+
48
+ def joined(windows)
49
+ windows.each_with_object [] do |(from, to), stretches|
50
+ carries_on = stretches.last && stretches.last[1] == from
51
+ carries_on ? stretches.last[1] = to : stretches << [ from, to ]
52
+ end
53
+ end
54
+
55
+ # An account with nothing open answers a bare list rather than the usual object, so the
56
+ # windows are taken out of whichever shape came back.
57
+ def answered
58
+ body = @client.get 'company/schedule_availability/booking_windows', params
59
+ body.is_a?(Array) ? body : body.fetch('booking_windows', [])
60
+ end
61
+
62
+ def params
63
+ { start_date: @from&.utc&.strftime('%Y-%m-%dT%H:%M:%S'), show_for_days: days,
64
+ employee_ids: (@technician && [ @technician.id ]), }.compact
65
+ end
66
+
67
+ def days = (@to && @from) ? ((@to - @from) / 1.day).ceil : DAYS
68
+
69
+ def with(**changed)
70
+ self.class.new(**{ client: @client, technician: @technician, from: @from,
71
+ to: @to, }.merge(changed))
72
+ end
73
+ end
74
+ end
@@ -37,7 +37,7 @@ module Hcp
37
37
 
38
38
  def slot
39
39
  { id: id, anytime: false, start_time: @node.dig(:schedule, :scheduled_start),
40
- end_time: @node.dig(:schedule, :scheduled_end) }
40
+ end_time: @node.dig(:schedule, :scheduled_end), }
41
41
  end
42
42
  end
43
43
  end
@@ -0,0 +1,21 @@
1
+ module Hcp
2
+ # One stretch of blocked-out time: what a Housecall Pro event takes, once where the event
3
+ # happens once and once for every hour its rule lands on where it repeats. Housecall Pro files
4
+ # one ID for the whole rule rather than one per hour, so an hour answers to the event's ID and
5
+ # the moment it starts, which is the one thing telling two hours of the same event apart.
6
+ class Occurrence < Company::Visit
7
+ # The node keys Housecall Pro spells otherwise than the vocabulary.
8
+ def self.keys
9
+ { description: :name, anytime: :all_day, starts_at: :start_time, ends_at: :end_time }
10
+ end
11
+
12
+ # Housecall Pro files an address on an event but no ID for it, and leaves it empty on all
13
+ # but a handful, so there is nothing to file a place under: blocked-out time stands nowhere,
14
+ # as it does in Jobber, where an event carries no property either.
15
+ # @return [nil] nothing, so an hour blocked out is booked at no address.
16
+ def location = nil
17
+
18
+ # @return [Array<Technician>] whoever the hour is blocked out for.
19
+ def technicians = records Technician, :assigned_employees
20
+ end
21
+ end
data/lib/hcp/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # The Housecall Pro API, answered in the vocabulary of the company gem.
2
2
  module Hcp
3
3
  # The version of this gem, as RubyGems knows it.
4
- VERSION = '3.0.1'
4
+ VERSION = '4.0.0'
5
5
  end
data/lib/hcp.rb CHANGED
@@ -7,8 +7,15 @@ require 'time'
7
7
  # Housecall Pro holds nothing for arrives as readily empty as null, and a query is written the
8
8
  # way `to_query` writes one.
9
9
  require 'active_support/core_ext/enumerable'
10
+ require 'active_support/core_ext/integer/time'
11
+ require 'active_support/core_ext/numeric/time'
10
12
  require 'active_support/core_ext/object/blank'
11
13
  require 'active_support/core_ext/object/to_query'
14
+ require 'active_support/core_ext/time/zones'
15
+
16
+ # An event repeats by an iCalendar rule, which is walked rather than parsed here: an hour that
17
+ # holds at ten in the morning either side of the clocks going back is not worth rewriting.
18
+ require 'ice_cube'
12
19
 
13
20
  # The vocabulary the account answers in.
14
21
  require 'company'
@@ -31,11 +38,14 @@ require 'hcp/resources/location'
31
38
  require 'hcp/resources/line'
32
39
  require 'hcp/resources/quote'
33
40
  require 'hcp/resources/visit'
41
+ require 'hcp/resources/occurrence'
34
42
  require 'hcp/resources/job'
35
43
  require 'hcp/resources/estimate'
36
44
  require 'hcp/collections/jobs'
37
45
  require 'hcp/collections/estimates'
46
+ require 'hcp/collections/occurrences'
38
47
  require 'hcp/collections/visits'
48
+ require 'hcp/collections/windows'
39
49
  require 'hcp/collections/technicians'
40
50
  require 'hcp/account'
41
51
  require 'hcp/event'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.1
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Claudio Baccigalupo
@@ -29,14 +29,28 @@ dependencies:
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: '2.1'
32
+ version: '3.0'
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '2.1'
39
+ version: '3.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: ice_cube
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
40
54
  - !ruby/object:Gem::Dependency
41
55
  name: minitest
42
56
  requirement: !ruby/object:Gem::Requirement
@@ -140,8 +154,10 @@ files:
140
154
  - lib/hcp/collections/estimates.rb
141
155
  - lib/hcp/collections/jobs.rb
142
156
  - lib/hcp/collections/leads.rb
157
+ - lib/hcp/collections/occurrences.rb
143
158
  - lib/hcp/collections/technicians.rb
144
159
  - lib/hcp/collections/visits.rb
160
+ - lib/hcp/collections/windows.rb
145
161
  - lib/hcp/error.rb
146
162
  - lib/hcp/errors/throttled.rb
147
163
  - lib/hcp/event.rb
@@ -152,6 +168,7 @@ files:
152
168
  - lib/hcp/resources/lead.rb
153
169
  - lib/hcp/resources/line.rb
154
170
  - lib/hcp/resources/location.rb
171
+ - lib/hcp/resources/occurrence.rb
155
172
  - lib/hcp/resources/quote.rb
156
173
  - lib/hcp/resources/technician.rb
157
174
  - lib/hcp/resources/visit.rb