jbr 5.0.0 → 7.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: a01ddd16fc41518ff9cc7d18b61bdc2c0d9e419959c01fc7dd822473de08bade
4
- data.tar.gz: 3d60fe2c06fd056d0e61d83bca1e40ff6f61954994a061e858a60422b6546e12
3
+ metadata.gz: d0f69e98e247c3db048083001987272c2005e55ff55b4e4e40911aeb1057993f
4
+ data.tar.gz: 18e53a6747f4ca42cf9cd2cb872d0b083a4ec18aa4b933c2825522dc8ea78c5b
5
5
  SHA512:
6
- metadata.gz: 71b4e3faacce7acb0ea6d5a3eab5f1beea5f4534a416a16ed663d434299551b94375e3522f0152e6627a22340a7c098eaf464fdcec0012688fa07b0def98c9e8
7
- data.tar.gz: 99828c97615dd6357dd58f167ec05338b6595cbcf5206cbad1088f9e3d3a95665f08698add77b3aeb854d4e10c9a664f8b77467ac064c680ecdd409bc763e96f
6
+ metadata.gz: dfd01ee3d0f0a5665c1daaae6ec66c51a22746fbd61b34d9e849f7f856e4c042cdc1eb7ce74b8aad1f67f68a817addbe14d2f17d20d46a023dbbb20ca52f8670
7
+ data.tar.gz: a5b628621b35d3343f99136a8228c808bd5d7df3dfe481de0e43a60944aa0ed6de659dab7af643f51db5307f565683814c5bed3b37dca071272ee47a992bd0c8
data/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [7.0.0] - 2026-09-17
4
+
5
+ - [Breaking change] The vocabulary is `company` 4.0: `of` takes the ID Jobber files a technician
6
+ under rather than the technician, so `account.visits.of(technician.id)` is what
7
+ `account.visits.of(technician)` was. It still lands in the one filter beside the window, and
8
+ it still needs no Users scope. The pin is `~> 4.0`.
9
+
10
+ - [Note] The ID is the whole of what `assignedTo` ever took, and asking for it plainly is what
11
+ stops a caller handing over a record of its own whose `id` is not the one Jobber knows.
12
+
13
+ ## [6.0.0] - 2026-09-16
14
+
15
+ - [Breaking change] The vocabulary is `company` 3.0: `assigned_to` is `of`, so
16
+ `account.visits.of(technician)` is what `account.visits.assigned_to(technician)` was. Jobber
17
+ still narrows it in the one filter, and it still needs no Users scope. The pin is `~> 3.0`.
18
+
19
+ - [Note] `account.windows` -- the free time of a business -- raises `NotImplementedError` here,
20
+ and will until Jobber exposes the hours a business keeps. It does not today: the schema was
21
+ enumerated looking, and there is no `BusinessHours`, `WorkingHours`, `Shift` or `Weekday` type
22
+ of any kind, while `OnlineBookingConfiguration` is four fields and none of them is an hour.
23
+
24
+ - [Note] Every setting around those hours is there, on `requestSettings`:
25
+ `intervalDurationMinutes` is the grain, `earliestAvailabilityMinutes` the notice,
26
+ `bufferDurationMinutes` the padding either side of a job, `efficientSchedulingType` and
27
+ `maxDriveTimeMinutes` whether travel counts. A caller can work free time out from those and
28
+ the visits. Read them off the form that books, not the one marked default, whose
29
+ `bookingType` may be `NONE`.
30
+
3
31
  ## [5.0.0] - 2026-09-15
4
32
 
5
33
  - [Feature] A scope the app was never granted no longer raises. Jobber answers `An object of
data/README.md CHANGED
@@ -115,7 +115,7 @@ failing. What each reader here needs:
115
115
  | --- | --- |
116
116
  | `account.jobs`, `job.lines` | Jobs |
117
117
  | `job.location`, `location.customer`, `visit.job` | Jobs and Clients |
118
- | `account.visits`, `assigned_to` | Scheduled Items |
118
+ | `account.visits`, `of` | Scheduled Items |
119
119
  | `account.technicians`, `visit.technicians`, `includes(:technicians)` | Users |
120
120
  | `account.quotes` | Quotes |
121
121
  | `account.invoices` | Invoices |
@@ -125,7 +125,7 @@ Jobber files a visit under **Scheduled Items**, which is one object covering vis
125
125
  assessments, tasks and calendar events -- so there is no scope to add for the kinds of booked
126
126
  time this gem does not read yet.
127
127
 
128
- `assigned_to` is the one worth knowing: narrowing to a technician needs no Users, because
128
+ `of` is the one worth knowing: narrowing to a technician needs no Users, because
129
129
  Jobber does the narrowing and no user is ever selected. Reading *who* is on a visit is what
130
130
  needs it.
131
131
 
@@ -325,17 +325,40 @@ One technician's week is the visits in it narrowed to them:
325
325
 
326
326
  ```ruby
327
327
  monday = Date.today.beginning_of_week.in_time_zone
328
- account.visits.between(monday, monday + 1.week).assigned_to(technician).each do |visit|
328
+ account.visits.between(monday, monday + 1.week).of(technician.id).each do |visit|
329
329
  visit.starts_at, visit.ends_at, visit.job.id
330
330
  end
331
331
  ```
332
332
 
333
- Jobber narrows a list of visits by who is on it, so `assigned_to` puts the technician into the
333
+ Jobber narrows a list of visits by who is on it, so `of` puts the technician into the
334
334
  same filter as the window: nobody else's visits are answered, paged or paid for, and the crew
335
335
  is not read at all unless `includes(:technicians)` asks. The two narrowings land in the one
336
336
  filter, so a caller may ask for the week and the technician in either order.
337
337
 
338
- `assigned_to` therefore needs no Users scope. Reading *who* is on a visit does.
338
+ `of` therefore needs no Users scope. Reading *who* is on a visit does.
339
+
340
+ ## What Jobber cannot say
341
+
342
+ `account.windows` -- the free time of a business, which the vocabulary names -- raises
343
+ `NotImplementedError` here, and will go on doing so until Jobber exposes the hours a business
344
+ keeps.
345
+
346
+ It does not today. The whole schema was enumerated looking for it: there is no `BusinessHours`,
347
+ `WorkingHours`, `OperatingHours`, `Shift`, `Weekday` or `DayOfWeek` type of any kind, and
348
+ `OnlineBookingConfiguration` is four fields -- whether bookings are on, the URL, the embed
349
+ script, the id. Jobber's own booking page must know the hours; the API does not say them.
350
+
351
+ What it does say is every setting around them, on `requestSettings`:
352
+ `intervalDurationMinutes` is the grain an offer is cut to, `earliestAvailabilityMinutes` the
353
+ notice the business needs, `bufferDurationMinutes` the padding left either side of a job, and
354
+ `efficientSchedulingType` with `maxDriveTimeMinutes` whether travel is counted as well. Read
355
+ those beside the visits and a caller can work free time out for itself -- which is what the
356
+ missing reader would have done. Take the settings from the form that books, not the one marked
357
+ default: the default form may be a request form, whose `bookingType` is `NONE`.
358
+
359
+ Raising is deliberate rather than answering with none. An empty week and a fully booked one are
360
+ the same shape, so a caller reading none as none would quietly stop offering the business
361
+ altogether, with nothing in a log and no page looking wrong.
339
362
 
340
363
  A visit is still not all Jobber schedules. A task, an event and the two kinds of reminder sit
341
364
  on the same calendar and are read past unread, because Jobber's filter takes one kind and not
data/lib/jbr/booking.rb CHANGED
@@ -40,7 +40,7 @@ module Jbr
40
40
 
41
41
  def schedule(starts_at, ends_at, technicians)
42
42
  { startAt: moment(starts_at), endAt: (moment(ends_at) if ends_at),
43
- teamMemberIdsToAssign: technicians.map(&:id) }.compact
43
+ teamMemberIdsToAssign: technicians.map(&:id), }.compact
44
44
  end
45
45
 
46
46
  def moment(at)
@@ -15,16 +15,16 @@ module Jbr
15
15
  def between(from, to)
16
16
  now = Time.now
17
17
  narrowed occursWithin: { startAt: (from || now - HORIZON).iso8601,
18
- endAt: (to || now + HORIZON).iso8601 },
18
+ endAt: (to || now + HORIZON).iso8601, },
19
19
  includeUnassigned: true, includeUnscheduled: false
20
20
  end
21
21
 
22
22
  # Jobber narrows a schedule by who is on it, so the technician joins the window in the one
23
23
  # filter and nobody else's work is answered, paged or paid for.
24
- # @param technician [Company::Technician] whoever the work is booked for.
24
+ # @param id [String] ID Jobber files whoever the work is booked for under.
25
25
  # @return [Visits] the same list, narrowed to what they are booked for.
26
26
  # @note Needs no Users scope: Jobber narrows, and no user is selected to do it.
27
- def assigned_to(technician) = narrowed(assignedTo: [ technician.id ])
27
+ def of(id) = narrowed(assignedTo: [ id ])
28
28
 
29
29
  # @return [Visits] the same list, narrowed to the stops of jobs, by Jobber rather than here.
30
30
  def for_jobs = narrowed(scheduleItemType: 'VISIT')
data/lib/jbr/scheduled.rb CHANGED
@@ -13,7 +13,7 @@ module Jbr
13
13
 
14
14
  def fields
15
15
  [ SHARED, kind('Visit', 'clientConfirmed job { id }'),
16
- kind('Assessment', 'clientConfirmed request { id }') ].join ' '
16
+ kind('Assessment', 'clientConfirmed request { id }'), ].join ' '
17
17
  end
18
18
 
19
19
  def kind(name, own) = "... on #{name} { #{[ own, located ].compact_blank.join ' '} }"
data/lib/jbr/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # A Ruby client for the Jobber API.
2
2
  module Jbr
3
3
  # The version of this gem.
4
- VERSION = '5.0.0'
4
+ VERSION = '7.0.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jbr
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0
4
+ version: 7.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: '4.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: '4.0'
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: minitest
42
42
  requirement: !ruby/object:Gem::Requirement