hcp 1.4.0 → 2.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/.yardopts +0 -1
- data/CHANGELOG.md +40 -0
- data/CLAUDE.md +13 -9
- data/README.md +60 -142
- data/lib/hcp/account.rb +22 -0
- data/lib/hcp/answer.rb +3 -11
- data/lib/hcp/client.rb +41 -0
- data/lib/hcp/collections/jobs.rb +32 -0
- data/lib/hcp/collections/leads.rb +25 -0
- data/lib/hcp/collections/visits.rb +39 -0
- data/lib/hcp/error.rb +1 -1
- data/lib/hcp/errors/throttled.rb +4 -0
- data/lib/hcp/event.rb +1 -6
- data/lib/hcp/resources/business.rb +11 -0
- data/lib/hcp/resources/customer.rb +9 -45
- data/lib/hcp/resources/job.rb +54 -69
- data/lib/hcp/resources/lead.rb +26 -0
- data/lib/hcp/resources/line.rb +8 -0
- data/lib/hcp/resources/location.rb +7 -0
- data/lib/hcp/resources/quote.rb +28 -0
- data/lib/hcp/resources/visit.rb +21 -0
- data/lib/hcp/version.rb +3 -3
- data/lib/hcp.rb +19 -42
- metadata +27 -29
- data/lib/hcp/access.rb +0 -12
- data/lib/hcp/concerns/chainable.rb +0 -39
- data/lib/hcp/concerns/keyed.rb +0 -14
- data/lib/hcp/concerns/named.rb +0 -7
- data/lib/hcp/concerns/queryable.rb +0 -41
- data/lib/hcp/concerns/scheduled.rb +0 -16
- data/lib/hcp/concerns/statused.rb +0 -23
- data/lib/hcp/concerns/timestamped.rb +0 -10
- data/lib/hcp/errors/not_found.rb +0 -4
- data/lib/hcp/errors/too_many_requests.rb +0 -16
- data/lib/hcp/filter.rb +0 -36
- data/lib/hcp/key.rb +0 -24
- data/lib/hcp/lead/pipeline.rb +0 -36
- data/lib/hcp/lead.rb +0 -44
- data/lib/hcp/relation.rb +0 -76
- data/lib/hcp/request.rb +0 -31
- data/lib/hcp/resource.rb +0 -58
- data/lib/hcp/resources/address.rb +0 -24
- data/lib/hcp/resources/booking_window.rb +0 -52
- data/lib/hcp/resources/company.rb +0 -42
- data/lib/hcp/resources/employee.rb +0 -12
- data/lib/hcp/resources/estimate/option.rb +0 -26
- data/lib/hcp/resources/estimate.rb +0 -49
- data/lib/hcp/resources/job/appointment.rb +0 -22
- data/lib/hcp/resources/job/invoice.rb +0 -21
- data/lib/hcp/resources/line_item.rb +0 -20
- data/lib/hcp/resources/note.rb +0 -6
- data/lib/hcp/resources/schedule.rb +0 -21
|
@@ -1,52 +1,16 @@
|
|
|
1
1
|
module Hcp
|
|
2
2
|
# Whoever the work is for.
|
|
3
|
-
class Customer <
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
class Customer < Company::Customer
|
|
4
|
+
# The node keys Housecall Pro spells otherwise than the vocabulary.
|
|
5
|
+
def self.keys = { name: :first_name, surname: :last_name }
|
|
6
6
|
|
|
7
|
-
#
|
|
8
|
-
|
|
7
|
+
# The numbers Housecall Pro holds for a customer, the one they are reached on first.
|
|
8
|
+
NUMBERS = %i[mobile_number home_number work_number]
|
|
9
9
|
|
|
10
|
-
#
|
|
11
|
-
|
|
10
|
+
# @return [String, nil] first name, or the business's name where a person has none.
|
|
11
|
+
def name = super.presence || attribute(:company).presence
|
|
12
12
|
|
|
13
|
-
#
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
# What a customer brings back beside themselves where they are asked to.
|
|
17
|
-
EXPANDS = %i[attachments do_not_service]
|
|
18
|
-
|
|
19
|
-
# Where Housecall Pro keeps them.
|
|
20
|
-
def self.path = 'customers'
|
|
21
|
-
|
|
22
|
-
# What Housecall Pro calls a page of them.
|
|
23
|
-
def self.key = 'customers'
|
|
24
|
-
|
|
25
|
-
attribute :first_name
|
|
26
|
-
attribute :last_name
|
|
27
|
-
attribute :email
|
|
28
|
-
|
|
29
|
-
# @return [String, nil] the business the customer is, where they are one.
|
|
30
|
-
attribute :company
|
|
31
|
-
|
|
32
|
-
# @return [String, nil] where the customer came from.
|
|
33
|
-
attribute :lead_source
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
# Housecall Pro holds three numbers, and a caller wants whichever one there is.
|
|
37
|
-
# @return [String, nil] the number the customer is reached on first.
|
|
38
|
-
def phone = @node['mobile_number'] || @node['home_number'] || @node['work_number']
|
|
39
|
-
|
|
40
|
-
# @return [Symbol, nil] :homeowner, or whatever else Housecall Pro takes the customer for.
|
|
41
|
-
def kind = @node['kind']&.to_sym
|
|
42
|
-
|
|
43
|
-
# @return [Boolean] whether the customer agreed to hear from the pro.
|
|
44
|
-
def notifications_enabled? = @node['notifications_enabled']
|
|
45
|
-
|
|
46
|
-
# @return [Array<String>] what the customer is tagged with.
|
|
47
|
-
def tags = Array(@node['tags'])
|
|
48
|
-
|
|
49
|
-
# @return [Relation] every address the customer is billed or served at.
|
|
50
|
-
def addresses = nested Address, 'addresses'
|
|
13
|
+
# @return [String, nil] ten digits they are reached on, nil where none can be dialed.
|
|
14
|
+
def phone = NUMBERS.lazy.filter_map { |number| Company::Phone.from @node[number] }.first
|
|
51
15
|
end
|
|
52
16
|
end
|
data/lib/hcp/resources/job.rb
CHANGED
|
@@ -1,73 +1,58 @@
|
|
|
1
1
|
module Hcp
|
|
2
2
|
# Work a Housecall Pro user accepted: what it is, who it is for, and what it comes to.
|
|
3
|
-
class Job <
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
#
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
#
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
# @return [
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
# @return [Array<Note>] what the pros wrote on the job.
|
|
59
|
-
def notes = records Note, 'notes'
|
|
60
|
-
|
|
61
|
-
# @return [Array<Employee>] the pros the job is assigned to.
|
|
62
|
-
def assigned_employees = records Employee, 'assigned_employees'
|
|
63
|
-
|
|
64
|
-
# @return [Relation] the visits the job is booked for.
|
|
65
|
-
def appointments = nested Job::Appointment, 'appointments'
|
|
66
|
-
|
|
67
|
-
# @return [Relation] what the job is billed as.
|
|
68
|
-
def line_items = nested LineItem, 'line_items'
|
|
69
|
-
|
|
70
|
-
# @return [Relation] the invoices raised for the job.
|
|
71
|
-
def invoices = nested Job::Invoice, 'invoices'
|
|
3
|
+
class Job < Company::Job
|
|
4
|
+
# The node keys Housecall Pro spells otherwise than the vocabulary.
|
|
5
|
+
def self.keys = { amount: :total_amount }
|
|
6
|
+
|
|
7
|
+
# Housecall Pro nests when the work is booked and when it was done, and files the address
|
|
8
|
+
# and the customer side by side; the node is read flat, the way the vocabulary reads one.
|
|
9
|
+
# @param node [Hash] job as Housecall Pro answered it.
|
|
10
|
+
# @param client [Client] how to reach Housecall Pro for what the job holds elsewhere.
|
|
11
|
+
def initialize(node: {}, client:)
|
|
12
|
+
node = node.with_indifferent_access
|
|
13
|
+
super node: node.merge(scheduled_at: node.dig(:schedule, :scheduled_start),
|
|
14
|
+
completed_at: node.dig(:work_timestamps, :completed_at), location: location_from(node))
|
|
15
|
+
@client = client
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Housecall Pro files the notes on a job one by one, which read as a list, one to a line.
|
|
19
|
+
# @return [String, nil] every note's content, `- ` before each, nil where none was written.
|
|
20
|
+
def notes = Array(super).map { |note| "- #{note[:content]}" }.join("\n").presence
|
|
21
|
+
|
|
22
|
+
# Housecall Pro counts in cents, and a caller reads dollars.
|
|
23
|
+
# @return [BigDecimal, nil] what the job comes to.
|
|
24
|
+
def amount = (cents = super) && cents / 100
|
|
25
|
+
|
|
26
|
+
# Housecall Pro files a job's lines under their own endpoint rather than beside the job,
|
|
27
|
+
# so they are read once, on the first ask.
|
|
28
|
+
# @return [Array<Line>] lines the job is billed as.
|
|
29
|
+
def lines
|
|
30
|
+
@lines ||= @client.get("jobs/#{id}/line_items").fetch('data').map { Line.new node: it }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [Location, nil] where the work happens, nil where the job is booked nowhere.
|
|
34
|
+
def location = record Location, :location
|
|
35
|
+
|
|
36
|
+
# Housecall Pro nests the appointments under the schedule, and only where the list was
|
|
37
|
+
# asked to bring them.
|
|
38
|
+
# @return [Array<Visit>] stops the job is booked as, empty where none came back.
|
|
39
|
+
def visits = Array(@node.dig :schedule, :appointments).map { Visit.new node: it, job: self }
|
|
40
|
+
|
|
41
|
+
# @return [Boolean] whether the customer or the pro called the job off.
|
|
42
|
+
def canceled? = attribute(:work_status).to_s.end_with? 'canceled'
|
|
43
|
+
|
|
44
|
+
# Housecall Pro names the estimate option a job was created from beside the job, and the
|
|
45
|
+
# option is priced among the customer's estimates, so the quote takes the customer along.
|
|
46
|
+
# @return [Quote, nil] option the job was won with, nil where it was won without one.
|
|
47
|
+
def quote
|
|
48
|
+
option_id = @node[:original_estimate_id]
|
|
49
|
+
Quote.new node: { id: option_id, customer: @node[:customer] }, client: @client if option_id
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def location_from(node)
|
|
55
|
+
node[:address].merge customer: node[:customer] if node.dig(:address, :id).present?
|
|
56
|
+
end
|
|
72
57
|
end
|
|
73
58
|
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Hcp
|
|
2
|
+
# Somebody who asked a Housecall Pro user for work, and where they sit in the pipeline.
|
|
3
|
+
class Lead < Company::Lead
|
|
4
|
+
# @param node [Hash] lead as Housecall Pro answered it.
|
|
5
|
+
# @param client [Client] how to reach Housecall Pro as the lead's location.
|
|
6
|
+
def initialize(node: {}, client:)
|
|
7
|
+
super node: node
|
|
8
|
+
@client = client
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Moves the lead to the status going by this name. Housecall Pro moves a lead by status ID
|
|
12
|
+
# and names them only by their words, so the status is looked up first.
|
|
13
|
+
# @param status_name [String] status as the account names it, such as 'Won'.
|
|
14
|
+
def update(status_name:)
|
|
15
|
+
status = statuses.find { |each| each['name'] == status_name } || unknown(status_name)
|
|
16
|
+
@client.put 'pipeline/statuses',
|
|
17
|
+
resource_type: 'lead', resource_id: id, status_id: status['id']
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def statuses = @client.get('pipeline/statuses', resource_type: 'lead').fetch 'statuses'
|
|
23
|
+
|
|
24
|
+
def unknown(name) = raise Error, "Status #{name} not found for lead #{id}"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Hcp
|
|
2
|
+
# A price a Housecall Pro user sent, which Housecall Pro calls an estimate option: a job names
|
|
3
|
+
# the option it was created from, and an estimate holds one option or several.
|
|
4
|
+
class Quote < Company::Quote
|
|
5
|
+
# @param node [Hash] option as Housecall Pro named it beside a job: its ID, and the customer.
|
|
6
|
+
# @param client [Client] how to reach Housecall Pro for the rest of it.
|
|
7
|
+
def initialize(node: {}, client:)
|
|
8
|
+
super node: node
|
|
9
|
+
@client = client
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Housecall Pro reads an estimate by its own ID and not by an option's, so the option is
|
|
13
|
+
# found among the customer's estimates, read once on the first ask.
|
|
14
|
+
# @return [BigDecimal, nil] what the option comes to in dollars, nil where none is found.
|
|
15
|
+
def amount = option && BigDecimal(option['total_amount'].to_s) / 100
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def option = options.find { |each| each['id'] == id }
|
|
20
|
+
|
|
21
|
+
def options = estimates.flat_map { |estimate| estimate['options'] }
|
|
22
|
+
|
|
23
|
+
def estimates
|
|
24
|
+
@estimates ||= @client.get('estimates', customer_id: @node.dig(:customer, :id),
|
|
25
|
+
page_size: Jobs::PAGE).fetch 'estimates'
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Hcp
|
|
2
|
+
# One stop of a job, which Housecall Pro calls an appointment: it happens where the job does
|
|
3
|
+
# and says what the job says.
|
|
4
|
+
class Visit < Company::Visit
|
|
5
|
+
# The node keys Housecall Pro spells otherwise than the vocabulary.
|
|
6
|
+
def self.keys = { starts_at: :start_time, ends_at: :end_time }
|
|
7
|
+
|
|
8
|
+
# @param node [Hash] appointment as Housecall Pro answered it under the job's schedule.
|
|
9
|
+
# @param job [Job] job the appointment sits under.
|
|
10
|
+
def initialize(node: {}, job:)
|
|
11
|
+
super node: node
|
|
12
|
+
@job = job
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @return [Job] job the stop belongs to, which came with it.
|
|
16
|
+
attr_reader :job
|
|
17
|
+
|
|
18
|
+
# @return [String, nil] what the job is called: an appointment has no words of its own.
|
|
19
|
+
def description = @job.description
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/hcp/version.rb
CHANGED
data/lib/hcp.rb
CHANGED
|
@@ -1,60 +1,37 @@
|
|
|
1
1
|
require 'bigdecimal'
|
|
2
|
-
require 'date'
|
|
3
2
|
require 'json'
|
|
4
3
|
require 'net/http'
|
|
5
4
|
require 'time'
|
|
6
5
|
|
|
7
6
|
# Only the Active Support files whose methods are used, rather than the whole of it: a name
|
|
8
|
-
# Housecall Pro holds nothing for arrives as readily empty as null,
|
|
9
|
-
#
|
|
7
|
+
# Housecall Pro holds nothing for arrives as readily empty as null, and a query is written the
|
|
8
|
+
# way `to_query` writes one.
|
|
10
9
|
require 'active_support/core_ext/enumerable'
|
|
11
10
|
require 'active_support/core_ext/object/blank'
|
|
12
11
|
require 'active_support/core_ext/object/to_query'
|
|
13
|
-
|
|
12
|
+
|
|
13
|
+
# The vocabulary the account answers in.
|
|
14
|
+
require 'company'
|
|
14
15
|
|
|
15
16
|
require 'hcp/version'
|
|
16
17
|
require 'hcp/error'
|
|
17
|
-
require 'hcp/errors/
|
|
18
|
-
require 'hcp/errors/too_many_requests'
|
|
19
|
-
require 'hcp/key'
|
|
18
|
+
require 'hcp/errors/throttled'
|
|
20
19
|
|
|
21
|
-
# Answer before
|
|
20
|
+
# Answer before Client, which reads one, and Client before everything that talks through it.
|
|
22
21
|
require 'hcp/answer'
|
|
23
|
-
require 'hcp/
|
|
24
|
-
require 'hcp/
|
|
25
|
-
require 'hcp/
|
|
26
|
-
require 'hcp/
|
|
27
|
-
require 'hcp/concerns/queryable'
|
|
28
|
-
require 'hcp/resource'
|
|
29
|
-
|
|
30
|
-
# Every concern before the records including it, and Schedule before Scheduled, which reads one.
|
|
31
|
-
require 'hcp/concerns/named'
|
|
32
|
-
require 'hcp/concerns/timestamped'
|
|
33
|
-
require 'hcp/concerns/statused'
|
|
34
|
-
require 'hcp/resources/schedule'
|
|
35
|
-
require 'hcp/concerns/scheduled'
|
|
22
|
+
require 'hcp/client'
|
|
23
|
+
require 'hcp/resources/business'
|
|
24
|
+
require 'hcp/resources/lead'
|
|
25
|
+
require 'hcp/collections/leads'
|
|
36
26
|
|
|
37
|
-
|
|
38
|
-
require 'hcp/resources/booking_window'
|
|
39
|
-
require 'hcp/resources/company'
|
|
40
|
-
require 'hcp/resources/employee'
|
|
41
|
-
require 'hcp/resources/line_item'
|
|
42
|
-
require 'hcp/resources/note'
|
|
27
|
+
# Every record before the one that reads it beside itself, and the job before its list.
|
|
43
28
|
require 'hcp/resources/customer'
|
|
44
|
-
|
|
45
|
-
|
|
29
|
+
require 'hcp/resources/location'
|
|
30
|
+
require 'hcp/resources/line'
|
|
31
|
+
require 'hcp/resources/quote'
|
|
32
|
+
require 'hcp/resources/visit'
|
|
46
33
|
require 'hcp/resources/job'
|
|
47
|
-
require 'hcp/
|
|
48
|
-
require 'hcp/
|
|
49
|
-
|
|
50
|
-
require 'hcp/resources/estimate'
|
|
51
|
-
require 'hcp/resources/estimate/option'
|
|
52
|
-
|
|
53
|
-
# After Company, which is what a key opens.
|
|
54
|
-
require 'hcp/access'
|
|
55
|
-
|
|
56
|
-
require 'hcp/concerns/keyed'
|
|
57
|
-
require 'hcp/lead'
|
|
58
|
-
require 'hcp/lead/pipeline'
|
|
59
|
-
|
|
34
|
+
require 'hcp/collections/jobs'
|
|
35
|
+
require 'hcp/collections/visits'
|
|
36
|
+
require 'hcp/account'
|
|
60
37
|
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: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Claudio Baccigalupo
|
|
@@ -23,6 +23,20 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: company
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.0'
|
|
26
40
|
- !ruby/object:Gem::Dependency
|
|
27
41
|
name: minitest
|
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -120,39 +134,23 @@ files:
|
|
|
120
134
|
- LICENSE.txt
|
|
121
135
|
- README.md
|
|
122
136
|
- lib/hcp.rb
|
|
123
|
-
- lib/hcp/
|
|
137
|
+
- lib/hcp/account.rb
|
|
124
138
|
- lib/hcp/answer.rb
|
|
125
|
-
- lib/hcp/
|
|
126
|
-
- lib/hcp/
|
|
127
|
-
- lib/hcp/
|
|
128
|
-
- lib/hcp/
|
|
129
|
-
- lib/hcp/concerns/scheduled.rb
|
|
130
|
-
- lib/hcp/concerns/statused.rb
|
|
131
|
-
- lib/hcp/concerns/timestamped.rb
|
|
139
|
+
- lib/hcp/client.rb
|
|
140
|
+
- lib/hcp/collections/jobs.rb
|
|
141
|
+
- lib/hcp/collections/leads.rb
|
|
142
|
+
- lib/hcp/collections/visits.rb
|
|
132
143
|
- lib/hcp/error.rb
|
|
133
|
-
- lib/hcp/errors/
|
|
134
|
-
- lib/hcp/errors/too_many_requests.rb
|
|
144
|
+
- lib/hcp/errors/throttled.rb
|
|
135
145
|
- lib/hcp/event.rb
|
|
136
|
-
- lib/hcp/
|
|
137
|
-
- lib/hcp/key.rb
|
|
138
|
-
- lib/hcp/lead.rb
|
|
139
|
-
- lib/hcp/lead/pipeline.rb
|
|
140
|
-
- lib/hcp/relation.rb
|
|
141
|
-
- lib/hcp/request.rb
|
|
142
|
-
- lib/hcp/resource.rb
|
|
143
|
-
- lib/hcp/resources/address.rb
|
|
144
|
-
- lib/hcp/resources/booking_window.rb
|
|
145
|
-
- lib/hcp/resources/company.rb
|
|
146
|
+
- lib/hcp/resources/business.rb
|
|
146
147
|
- lib/hcp/resources/customer.rb
|
|
147
|
-
- lib/hcp/resources/employee.rb
|
|
148
|
-
- lib/hcp/resources/estimate.rb
|
|
149
|
-
- lib/hcp/resources/estimate/option.rb
|
|
150
148
|
- lib/hcp/resources/job.rb
|
|
151
|
-
- lib/hcp/resources/
|
|
152
|
-
- lib/hcp/resources/
|
|
153
|
-
- lib/hcp/resources/
|
|
154
|
-
- lib/hcp/resources/
|
|
155
|
-
- lib/hcp/resources/
|
|
149
|
+
- lib/hcp/resources/lead.rb
|
|
150
|
+
- lib/hcp/resources/line.rb
|
|
151
|
+
- lib/hcp/resources/location.rb
|
|
152
|
+
- lib/hcp/resources/quote.rb
|
|
153
|
+
- lib/hcp/resources/visit.rb
|
|
156
154
|
- lib/hcp/version.rb
|
|
157
155
|
homepage: https://github.com/claudiob/hcp
|
|
158
156
|
licenses:
|
data/lib/hcp/access.rb
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
module Hcp
|
|
2
|
-
# What a key opens: the account it belongs to, read as one of its locations.
|
|
3
|
-
class Access
|
|
4
|
-
# @param company_id [String, nil] the location to read as, where the account has several.
|
|
5
|
-
def initialize(company_id: nil)
|
|
6
|
-
@company_id = company_id
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
# @return [Company] the account the key belongs to, as the location it is read as.
|
|
10
|
-
def account = Company.current company_id: @company_id
|
|
11
|
-
end
|
|
12
|
-
end
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
module Hcp
|
|
2
|
-
# Extends a list with the chaining that narrows, orders and cuts it.
|
|
3
|
-
module Chainable
|
|
4
|
-
# A condition Housecall Pro does not take is one it ignores, answering the whole account
|
|
5
|
-
# rather than a page of it, so an unknown one is refused here where it can still be named.
|
|
6
|
-
# @return [Relation] the same list, narrowed by these conditions.
|
|
7
|
-
def where(**conditions)
|
|
8
|
-
only conditions.keys, @type.filters.keys
|
|
9
|
-
narrowed conditions: @conditions.merge(conditions)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
# @return [Relation] the same list, in this order.
|
|
13
|
-
def order(**sorts)
|
|
14
|
-
only sorts.keys, @type.sorts
|
|
15
|
-
narrowed sorts: sorts
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
# @return [Relation] the same list, stopping after this many records.
|
|
19
|
-
def limit(count) = narrowed limit: count
|
|
20
|
-
|
|
21
|
-
# @return [Relation] the same list, asking Housecall Pro for these beside each record.
|
|
22
|
-
def includes(*names)
|
|
23
|
-
only names, @type.expands
|
|
24
|
-
narrowed expands: @expands | names
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
private
|
|
28
|
-
|
|
29
|
-
def only(names, allowed)
|
|
30
|
-
unknown = names - allowed
|
|
31
|
-
raise Error, "#{unknown.first} is not one of: #{allowed.join ', '}" if unknown.any?
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def narrowed(conditions: @conditions, sorts: @sorts, limit: @limit, expands: @expands)
|
|
35
|
-
Relation.new type: @type, path: @path, company_id: @company_id, conditions: conditions,
|
|
36
|
-
sorts: sorts, limit: limit, expands: expands
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
data/lib/hcp/concerns/keyed.rb
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
module Hcp
|
|
2
|
-
# Extends a class handed its own key, as everything was before the key became global.
|
|
3
|
-
module Keyed
|
|
4
|
-
private
|
|
5
|
-
|
|
6
|
-
def headers
|
|
7
|
-
{
|
|
8
|
-
'Authorization' => "Token #{@key}",
|
|
9
|
-
'Content-Type' => 'application/json',
|
|
10
|
-
'X-Company-Id' => @company_id,
|
|
11
|
-
}.compact
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
data/lib/hcp/concerns/named.rb
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
module Hcp
|
|
2
|
-
# Extends a record Housecall Pro holds a person's names for.
|
|
3
|
-
module Named
|
|
4
|
-
# @return [String] what they are called, by whichever of their names Housecall Pro holds.
|
|
5
|
-
def name = [ @node['first_name'], @node['last_name'] ].compact_blank.join ' '
|
|
6
|
-
end
|
|
7
|
-
end
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
module Hcp
|
|
2
|
-
# Extends a resource with the entry points that read a list of it, or one record.
|
|
3
|
-
module Queryable
|
|
4
|
-
# @param company_id [String, nil] the location to read as, where the account has several.
|
|
5
|
-
# @return [Relation] every record of this kind the location holds.
|
|
6
|
-
def all(company_id: nil) = Relation.new type: self, company_id: company_id
|
|
7
|
-
|
|
8
|
-
# @return [Relation] the records matching these conditions.
|
|
9
|
-
def where(company_id: nil, **conditions) = all(company_id: company_id).where(**conditions)
|
|
10
|
-
|
|
11
|
-
# @return [Relation] the records in this order.
|
|
12
|
-
def order(company_id: nil, **sorts) = all(company_id: company_id).order(**sorts)
|
|
13
|
-
|
|
14
|
-
# @return [Relation] at most this many records.
|
|
15
|
-
def limit(count, company_id: nil) = all(company_id: company_id).limit(count)
|
|
16
|
-
|
|
17
|
-
# @return [Relation] the records, with these brought back beside each of them.
|
|
18
|
-
def includes(*names, company_id: nil) = all(company_id: company_id).includes(*names)
|
|
19
|
-
|
|
20
|
-
# @return [Integer] how many records of this kind the location holds.
|
|
21
|
-
def count(company_id: nil) = all(company_id: company_id).count
|
|
22
|
-
|
|
23
|
-
# @return [Resource, nil] the first record, or nil where the location holds none.
|
|
24
|
-
def first(company_id: nil) = all(company_id: company_id).first
|
|
25
|
-
|
|
26
|
-
# @return [Resource] the record Housecall Pro files under this ID.
|
|
27
|
-
def find(id, company_id: nil) = all(company_id: company_id).find(id)
|
|
28
|
-
|
|
29
|
-
# @return [Hash] what a list of these may be narrowed by.
|
|
30
|
-
def filters = self::FILTERS
|
|
31
|
-
|
|
32
|
-
# @return [Array<Symbol>] the conditions Housecall Pro takes more than one of.
|
|
33
|
-
def many = self::MANY
|
|
34
|
-
|
|
35
|
-
# @return [Array<Symbol>] what Housecall Pro will put a list of these in order of.
|
|
36
|
-
def sorts = self::SORTS
|
|
37
|
-
|
|
38
|
-
# @return [Array<Symbol>] what these bring back beside themselves where asked to.
|
|
39
|
-
def expands = self::EXPANDS
|
|
40
|
-
end
|
|
41
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
module Hcp
|
|
2
|
-
# Extends a record Housecall Pro books work for and stamps as the work happens.
|
|
3
|
-
module Scheduled
|
|
4
|
-
# @return [Schedule, nil] when the work is booked for.
|
|
5
|
-
def schedule = record Schedule, 'schedule'
|
|
6
|
-
|
|
7
|
-
# @return [Time, nil] when the pro said they were on their way.
|
|
8
|
-
def on_my_way_at = time 'work_timestamps', 'on_my_way_at'
|
|
9
|
-
|
|
10
|
-
# @return [Time, nil] when the work started.
|
|
11
|
-
def started_at = time 'work_timestamps', 'started_at'
|
|
12
|
-
|
|
13
|
-
# @return [Time, nil] when the work was finished.
|
|
14
|
-
def completed_at = time 'work_timestamps', 'completed_at'
|
|
15
|
-
end
|
|
16
|
-
end
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
module Hcp
|
|
2
|
-
# Extends a record Housecall Pro moves through a workflow.
|
|
3
|
-
module Statused
|
|
4
|
-
# Housecall Pro narrows a list by one set of words and answers with another, so a caller
|
|
5
|
-
# who filters by :in_progress reads :in_progress back rather than 'in progress'.
|
|
6
|
-
STATUSES = {
|
|
7
|
-
'needs scheduling' => :unscheduled,
|
|
8
|
-
'scheduled' => :scheduled,
|
|
9
|
-
'in progress' => :in_progress,
|
|
10
|
-
'complete rated' => :completed,
|
|
11
|
-
'complete unrated' => :completed,
|
|
12
|
-
'user canceled' => :canceled,
|
|
13
|
-
'pro canceled' => :canceled,
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
# @return [Symbol, nil] where Housecall Pro files the record in its own workflow.
|
|
17
|
-
def work_status = STATUSES[@node['work_status']]
|
|
18
|
-
|
|
19
|
-
# A rating is not a status, so it reads beside one rather than inside it.
|
|
20
|
-
# @return [Boolean] whether the customer rated the work.
|
|
21
|
-
def rated? = @node['work_status'] == 'complete rated'
|
|
22
|
-
end
|
|
23
|
-
end
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
module Hcp
|
|
2
|
-
# Extends a record Housecall Pro stamps with when it was opened and last changed.
|
|
3
|
-
module Timestamped
|
|
4
|
-
# @return [Time, nil] when the record was opened.
|
|
5
|
-
def created_at = time 'created_at'
|
|
6
|
-
|
|
7
|
-
# @return [Time, nil] when the record last changed.
|
|
8
|
-
def updated_at = time 'updated_at'
|
|
9
|
-
end
|
|
10
|
-
end
|
data/lib/hcp/errors/not_found.rb
DELETED