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 +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +19 -1
- data/lib/hcp/account.rb +3 -0
- data/lib/hcp/collections/visits.rb +1 -1
- data/lib/hcp/collections/windows.rb +74 -0
- data/lib/hcp/version.rb +1 -1
- data/lib/hcp.rb +1 -0
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6452e662bc00ffd31b8fa21a935de8c1b4ef9d4779a87668ce4f6b4c4585b3b0
|
|
4
|
+
data.tar.gz: e9e8e77e53671ef7e822f2116d32a231ab6f71901de418bdd8643acd46232de5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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).
|
|
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
|
|
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
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:
|
|
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: '
|
|
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: '
|
|
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
|