hcp 3.1.0 → 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: 3c2323d80d7ca73d0e62b6cef443bd2e40b803495df3ed0ad0d05fac2b5eb186
4
- data.tar.gz: d908b99bcf637800fbc1bf0bc17bb777d06338e1ca51ae999981405ae717a59b
3
+ metadata.gz: 6452e662bc00ffd31b8fa21a935de8c1b4ef9d4779a87668ce4f6b4c4585b3b0
4
+ data.tar.gz: e9e8e77e53671ef7e822f2116d32a231ab6f71901de418bdd8643acd46232de5
5
5
  SHA512:
6
- metadata.gz: a93a05b7b4b3b0785661ab9104ac0a0ec88a4d18e43331fd49414c4ad8388e379806a01425250d5e862c2015b76101747c0a4d2e139d67b9ee139a7871d078fe
7
- data.tar.gz: '0759b4af994c2ef83c40abc10b7730b9e49350d3ceb8a74fa7f9be43cf373094b0056bfd35644b69a77bb04328176d9bd3994cadba1dabcc4f01abbf1054ff7f'
6
+ metadata.gz: 4b03d7602db5e603bd0b0ba03c9031fb93f29bece4d5984a460f0fb4f79b5d7a740de189c4c0d281919650f39f5bf84e4019d8cc5988630114aaec5a59fdab45
7
+ data.tar.gz: 38068b949e79d8c8c26bbfe07f5c75546f4f11964a4ec9259eaab168df458423be6eac7575a070ed7cb91b0a529d3a11d22b6f2d2605decf7ea39d25847ad0c4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
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
+
3
25
  ## [3.1.0] - 2026-09-16
4
26
 
5
27
  - [Feature] `account.visits` covers the time blocked out around the work, which Housecall Pro
data/README.md CHANGED
@@ -147,7 +147,7 @@ One technician's week is the visits in it narrowed to them, which is how a sched
147
147
 
148
148
  ```ruby
149
149
  monday = Date.today.beginning_of_week.in_time_zone
150
- 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|
151
151
  visit.starts_at, visit.ends_at, visit.job.location.street
152
152
  end
153
153
  ```
@@ -159,6 +159,24 @@ dispatched to some of its crew or to none of it, so a stop dispatched to nobody
159
159
  crew's, and the stops the technician is not on are let go once the jobs arrive. Asking for the
160
160
  week and asking for the technician narrow the same list, in either order.
161
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
+
162
180
  What Housecall Pro schedules elsewhere is still not here: time blocked out on the calendar is
163
181
  filed under `/events`, which takes no date and no employee to narrow by, so a week of it cannot
164
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
 
@@ -31,7 +31,7 @@ module Hcp
31
31
  # to it, so the stops they are not on are let go once the work comes back.
32
32
  # @param technician [Company::Technician] whoever the work is booked for.
33
33
  # @return [Company::Selection] the same list, narrowed to the stops they are booked for.
34
- def assigned_to(technician)
34
+ def of(technician)
35
35
  theirs = with technician: technician
36
36
  Company::Selection.new(collection: theirs) do |visit|
37
37
  visit.technicians.any? { |each| each.id == technician.id }
@@ -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
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.1.0'
4
+ VERSION = '4.0.0'
5
5
  end
data/lib/hcp.rb CHANGED
@@ -45,6 +45,7 @@ require 'hcp/collections/jobs'
45
45
  require 'hcp/collections/estimates'
46
46
  require 'hcp/collections/occurrences'
47
47
  require 'hcp/collections/visits'
48
+ require 'hcp/collections/windows'
48
49
  require 'hcp/collections/technicians'
49
50
  require 'hcp/account'
50
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.1.0
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Claudio Baccigalupo
@@ -29,14 +29,14 @@ 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
40
  - !ruby/object:Gem::Dependency
41
41
  name: ice_cube
42
42
  requirement: !ruby/object:Gem::Requirement
@@ -157,6 +157,7 @@ files:
157
157
  - lib/hcp/collections/occurrences.rb
158
158
  - lib/hcp/collections/technicians.rb
159
159
  - lib/hcp/collections/visits.rb
160
+ - lib/hcp/collections/windows.rb
160
161
  - lib/hcp/error.rb
161
162
  - lib/hcp/errors/throttled.rb
162
163
  - lib/hcp/event.rb