jbr 2.2.0 → 2.4.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: c2e03e9b80223954e92497d75f84d2f6be78f23fadcbcfd7d82ba95e83ebacec
4
- data.tar.gz: aac7754bb691e5b45ec8284a289eceacfc9afc961cbb71e6ffedc2f92a5fd2a9
3
+ metadata.gz: 131ece92fb051e64674afcd8e0066a126c83cd4502b7ea570795e2f6a456aaf6
4
+ data.tar.gz: d56615c6a47fab3de06ecebf672ade7f355e2338e263ff23577faea1b3dd744d
5
5
  SHA512:
6
- metadata.gz: 1a962bbdc7d20e9549fb10d55a5c52ddb1de7ce7c92a9da20e61162f040a8b2da35c2c6daa545c4ef67e36b04eec0085fa03faddf10db6435674390c20ddacde
7
- data.tar.gz: e565f59d6f14284fd3919a048f770e060588be06850d225947b4552d88b1201ea7149afec5fe733f57d78afaa1809ea762f62377a7596729917381ecd7d65e7f
6
+ metadata.gz: 12e0d034ad5a76d89bf9d8ded9b84bd5790197e34a709404c551f9b75d9d697dcaec066db7f9bd05283bb17fabbee9a5b1239f29b5ed90bb025141f47b9db88a
7
+ data.tar.gz: 1c3b8eff188c1a4ade48f273c6ec575bfe57a9906da717b8fba10237e3722317f04fc1bacbb333b06b2df4753e3dcf3c5e5d203a2d970908136d55098f3009d9
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ ## [2.4.0] - 2026-08-08
2
+
3
+ - [Breaking change] A visit answers `property` -- the ID Jobber files the place under, beside
4
+ the address fields -- rather than `property_id` and `address` apart. It reads the way
5
+ `client` already did, so a caller unpacks both the same way
6
+
7
+ ## [2.3.0] - 2026-08-07
8
+
9
+ - [Breaking change] A client's phone is the number Jobber holds as reachable rather than
10
+ the string on the record: the primary before the rest, one that takes texts before one
11
+ that does not, and the first of those the North American plan recognizes, ten digits
12
+ without the country code. A client with no such number now answers nil
13
+
1
14
  ## [2.2.0] - 2026-08-07
2
15
 
3
16
  - [New] Carry the client a visit is for -- id, first name, last name, phone and email --
data/README.md CHANGED
@@ -98,11 +98,11 @@ visit = visits.first
98
98
  visit.id # => 'Z2lkOi8vS'
99
99
  visit.title # => 'Furnace tune-up'
100
100
  visit.job_id # => 'Z2lkOi8vS'
101
- visit.property_id # => 'Z2lkOi8vS'
101
+ visit.property # => { id: 'Z2lkOi8vS', street: '1 Main St', city: 'Raleigh',
102
+ # state: 'NC', zip: '27601', latitude: 35.77, longitude: -78.63 }
102
103
  visit.client # => { id: 'Z2lkOi8vS', first_name: 'Jane', last_name: 'Doe',
103
104
  # phone: '5553335555', email: 'jane@example.com' }
104
- visit.address # => { street: '1 Main St', city: 'Raleigh', state: 'NC', zip: '27601',
105
- # latitude: 35.77, longitude: -78.63 }
105
+ # phone is the reachable North American number, ten digits, or nil
106
106
  visit.starts_at # => 2026-08-09 14:00:00
107
107
  visit.ends_at # => 2026-08-09 16:00:00
108
108
  visit.all_day? # => false
@@ -174,7 +174,8 @@ Mock successfully fetching upcoming visits:
174
174
 
175
175
  ```ruby
176
176
  Jbr.mock.visits = [ { id: 'visit-01', title: 'Furnace tune-up', job_id: 'job-01',
177
- property_id: 'property-01', client: { id: 'client-01', first_name: 'Jane' },
177
+ property: { id: 'property-01', street: '1 Main St' },
178
+ client: { id: 'client-01', first_name: 'Jane' },
178
179
  address: { street: '1 Main St', city: 'Raleigh', state: 'NC', zip: '27601' },
179
180
  starts_at: Date.tomorrow.noon, ends_at: Date.tomorrow.end_of_day,
180
181
  all_day: false, client_confirmed: true } ]
@@ -15,11 +15,9 @@ module Jbr
15
15
 
16
16
  def job_id = @node[:job_id]
17
17
 
18
- def property_id = @node[:property_id]
19
-
20
18
  def client = @node.fetch :client, {}
21
19
 
22
- def address = @node.fetch :address, {}
20
+ def property = @node.fetch :property, {}
23
21
 
24
22
  def all_day? = @node[:all_day]
25
23
 
data/lib/jbr/phone.rb ADDED
@@ -0,0 +1,23 @@
1
+ module Jbr
2
+ # The numbers on a client's file, and which of them the client is reached on.
3
+ class Phone
4
+ # What Jobber is asked for wherever a client is read: the number, and what ranks it.
5
+ SELECTION = 'phones { normalizedPhoneNumber smsAllowed primary }'
6
+
7
+ # Ten digits whose area code and exchange open with 2 through 9, behind the E.164 +1.
8
+ NORTH_AMERICAN = /\A\+1([2-9]\d{2}[2-9]\d{6})\z/
9
+
10
+ # The number to reach a client on, without its country code.
11
+ # @param phones [Array<Hash>, nil] the numbers as Jobber answered them.
12
+ # @return [String, nil] the ten digits to call, or nil where none can be dialed.
13
+ def self.from(phones)
14
+ # The index breaks a tie because sort_by does not: two equally ranked numbers would
15
+ # otherwise swap between runs, and the client would answer a different phone each time.
16
+ ranked = Array(phones).each_with_index.sort_by do |phone, index|
17
+ [ phone['primary'] ? 0 : 1, phone['smsAllowed'] ? 0 : 1, index ]
18
+ end
19
+ numbers = ranked.lazy.map { |phone, _| phone['normalizedPhoneNumber'].to_s }
20
+ numbers.filter_map { |number| number[NORTH_AMERICAN, 1] }.first
21
+ end
22
+ end
23
+ end
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 = '2.2.0'
4
+ VERSION = '2.4.0'
5
5
  end
data/lib/jbr/visit.rb CHANGED
@@ -2,9 +2,7 @@ module Jbr
2
2
  # One stop at a property: when the work on a job is scheduled to happen.
3
3
  class Visit < Resource
4
4
  # What Jobber calls each field of the client the work is for, against what a caller reads.
5
- CLIENT_FIELDS = { id: :id, firstName: :first_name, lastName: :last_name,
6
- phone: :phone, email: :email,
7
- }
5
+ CLIENT_FIELDS = { id: :id, firstName: :first_name, lastName: :last_name, email: :email }
8
6
 
9
7
  # The query that reads a page of visits starting after a moment, oldest first. Forty a
10
8
  # page, not a hundred: Jobber prices a query by its page size and refuses the wider one.
@@ -14,7 +12,7 @@ module Jbr
14
12
  nodes {
15
13
  id title startAt endAt allDay clientConfirmed
16
14
  job { id }
17
- client { #{CLIENT_FIELDS.keys.join ' '} }
15
+ client { #{CLIENT_FIELDS.keys.join ' '} #{Phone::SELECTION} }
18
16
  property { id address { #{Property::SELECTION} } }
19
17
  }
20
18
  pageInfo { hasNextPage endCursor }
@@ -53,18 +51,20 @@ module Jbr
53
51
  # @return [Boolean, nil] whether the client has confirmed the visit.
54
52
  def client_confirmed? = @node['clientConfirmed']
55
53
 
56
- # @return [String, nil] the ID of the property the work happens at.
57
- def property_id = @node.dig 'property', 'id'
58
-
59
54
  # Who the work is for, in the fields a client is created with.
60
55
  # @return [Hash] any of :id, :first_name, :last_name, :phone and :email.
61
56
  def client
62
- CLIENT_FIELDS.to_h { |jobber, ours| [ ours, @node.dig('client', jobber.to_s) ] }.compact
57
+ fields = CLIENT_FIELDS.to_h { |jobber, ours| [ ours, @node.dig('client', jobber.to_s) ] }
58
+ fields.merge(phone: Phone.from(@node.dig('client', 'phones'))).compact
63
59
  end
64
60
 
65
- # Where the work happens, in the fields {Property} carries.
66
- # @return [Hash] any of :street, :city, :state, :zip, :latitude and :longitude.
67
- def address = Property.fields_from @node.dig('property', 'address')
61
+ # Where the work happens, in the fields {Property} carries, under the ID Jobber files
62
+ # it by -- the shape {#client} answers in, so both read the same way.
63
+ # @return [Hash] any of :id, :street, :city, :state, :zip, :latitude and :longitude.
64
+ def property
65
+ fields = Property.fields_from @node.dig('property', 'address')
66
+ { id: @node.dig('property', 'id') }.merge fields
67
+ end
68
68
 
69
69
  # @return [Time, nil] the visit start time
70
70
  def starts_at
data/lib/jbr.rb CHANGED
@@ -14,7 +14,8 @@ require 'jbr/request'
14
14
  require 'jbr/oauth'
15
15
 
16
16
  require 'jbr/account'
17
- # Property comes before Client and Visit: their queries read its fields as they load.
17
+ # Phone and Property come before Client and Visit: their queries read these as they load.
18
+ require 'jbr/phone'
18
19
  require 'jbr/property'
19
20
  require 'jbr/client'
20
21
  require 'jbr/invoice'
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: 2.2.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Claudio Baccigalupo
@@ -111,6 +111,7 @@ files:
111
111
  - lib/jbr/mock/visit.rb
112
112
  - lib/jbr/mocking.rb
113
113
  - lib/jbr/oauth.rb
114
+ - lib/jbr/phone.rb
114
115
  - lib/jbr/property.rb
115
116
  - lib/jbr/quote.rb
116
117
  - lib/jbr/request.rb