servicetrade 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 82dc5b71eb88760c25b84d5341b0db78a94245eca3e78e3ecec623b263eb8bb2
4
+ data.tar.gz: 589dd883ba165436c59fe3b1cb562e4ed2263bd36061977c290a12743b2e76b5
5
+ SHA512:
6
+ metadata.gz: ba667f7a59253c9bc20ba7bc6f97350d2e0b444eed0e39a9825e9b63a4bae78f1fb0b540dcc681455e5c177c9550dc09707d702672fc253d6e480ca172e4e4c8
7
+ data.tar.gz: 1de099c1400de275c0a0bd15a4de7a72bc1c6629d72649782bfbc4bf8c6af33f0a8d7045148fd55c03466cec1f67d7adbad97c9366464aaebea9e493fc0c385f
data/CHANGELOG.md ADDED
File without changes
data/LICENSE.md ADDED
File without changes
data/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # ServiceTrade Ruby
2
+
3
+ A Ruby client library for the ServiceTrade API.
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/servicetrade.svg)](https://badge.fury.io/rb/servicetrade)
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'servicetrade'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install servicetrade
22
+
23
+ ## Configuration
24
+
25
+ Configure the client with your ServiceTrade credentials:
26
+
27
+ ```ruby
28
+ ServiceTrade.configure do |config|
29
+ config.username = 'your_username'
30
+ config.password = 'your_password'
31
+ config.api_version = '1' # Optional, defaults to '1'
32
+ config.timeout = 30 # Optional, defaults to 30 seconds
33
+ config.open_timeout = 10 # Optional, defaults to 10 seconds
34
+ end
35
+ ```
36
+
37
+ ## Authentication
38
+
39
+ The gem automatically handles authentication with the ServiceTrade API using your username and password. Session management is handled internally.
40
+
41
+ ## Usage
42
+
43
+ ### Jobs
44
+
45
+ ```ruby
46
+ # List all jobs
47
+ jobs = ServiceTrade::Job.list
48
+
49
+ # Create a new job
50
+ job = ServiceTrade::Job.create(
51
+ name: 'Service Call',
52
+ customer_id: 123,
53
+ location_id: 456,
54
+ scheduled_date: '2023-12-01'
55
+ )
56
+
57
+ # Update a job
58
+ ServiceTrade::Job.update(job_id, {
59
+ status: 'completed',
60
+ completed_date: Time.now.iso8601
61
+ })
62
+
63
+ # Delete a job
64
+ ServiceTrade::Job.delete(job_id)
65
+ ```
66
+
67
+ ### Appointments
68
+
69
+ ```ruby
70
+ # List appointments
71
+ appointments = ServiceTrade::Appointment.list
72
+
73
+ # Create an appointment
74
+ appointment = ServiceTrade::Appointment.create(
75
+ job_id: 123,
76
+ start_time: '2023-12-01T09:00:00Z',
77
+ end_time: '2023-12-01T10:00:00Z'
78
+ )
79
+ ```
80
+
81
+ ### Locations
82
+
83
+ ```ruby
84
+ # List locations
85
+ locations = ServiceTrade::Location.list
86
+
87
+ # Create a location
88
+ location = ServiceTrade::Location.create(
89
+ name: 'Customer Site',
90
+ address: '123 Main St',
91
+ city: 'Anytown',
92
+ state: 'CA',
93
+ zip: '12345'
94
+ )
95
+ ```
96
+
97
+ ## Error Handling
98
+
99
+ The gem provides specific error classes for different types of API errors:
100
+
101
+ ```ruby
102
+ begin
103
+ job = ServiceTrade::Job.create(invalid_data)
104
+ rescue ServiceTrade::AuthenticationError => e
105
+ # Handle authentication errors (401)
106
+ puts "Authentication failed: #{e.message}"
107
+ rescue ServiceTrade::AuthorizationError => e
108
+ # Handle authorization errors (403)
109
+ puts "Not authorized: #{e.message}"
110
+ rescue ServiceTrade::NotFoundError => e
111
+ # Handle not found errors (404)
112
+ puts "Resource not found: #{e.message}"
113
+ rescue ServiceTrade::ApiError => e
114
+ # Handle other API errors
115
+ puts "API error: #{e.message}"
116
+ end
117
+ ```
118
+
119
+ ## Available Resources
120
+
121
+ - **Job** - Create, read, update, and delete jobs
122
+ - **Appointment** - Manage appointments associated with jobs
123
+ - **Location** - Manage customer locations
124
+
125
+ Each resource supports standard CRUD operations where applicable:
126
+ - `list` - Retrieve a list of resources
127
+ - `create(params)` - Create a new resource
128
+ - `update(id, params)` - Update an existing resource
129
+ - `delete(id)` - Delete a resource
130
+
131
+ ## Development
132
+
133
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
134
+
135
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
136
+
137
+ ## Contributing
138
+
139
+ Bug reports and pull requests are welcome on GitHub at https://github.com/destilado/servicetrade-ruby.
140
+
141
+ ## License
142
+
143
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ServiceTrade
4
+ module ApiOperations
5
+ module Create
6
+ def create(params = {})
7
+ response = ServiceTrade.client.request(:post, resource_url, params)
8
+ new(response['data'])
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ServiceTrade
4
+ module ApiOperations
5
+ module Delete
6
+ def delete(id)
7
+ ServiceTrade.client.request(:delete, "#{resource_url}/#{id}")
8
+ true
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,67 @@
1
+ # lib/servicetrade/api_operations/list.rb
2
+ module ServiceTrade
3
+ module ApiOperations
4
+ module List
5
+ def list(filters = {}, page: 1, per_page: 100)
6
+ params = filters.merge({
7
+ page: page,
8
+ per_page: per_page
9
+ })
10
+
11
+ response = ServiceTrade.client.request(:get, resource_url, params)
12
+ ListResponse.new(response, self)
13
+ end
14
+
15
+ # Iterator method for automatically handling pagination
16
+ def all(filters = {}, per_page: 100, &block)
17
+ if block_given?
18
+ page = 1
19
+ loop do
20
+ response = list(filters, page: page, per_page: per_page)
21
+ response.data.each(&block)
22
+ break unless response.has_more?
23
+ page += 1
24
+ end
25
+ else
26
+ Enumerator.new do |yielder|
27
+ page = 1
28
+ loop do
29
+ response = list(filters, page: page, per_page: per_page)
30
+ response.data.each { |item| yielder << item }
31
+ break unless response.has_more?
32
+ page += 1
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ class ListResponse
41
+ attr_reader :data, :total_count, :page, :per_page
42
+
43
+ def initialize(response, resource_class)
44
+ # Handle ServiceTrade API response format
45
+ data_key = case resource_class.name.split('::').last.downcase
46
+ when 'job'
47
+ 'jobs'
48
+ when 'appointment'
49
+ 'appointments'
50
+ when 'location'
51
+ 'locations'
52
+ else
53
+ 'data'
54
+ end
55
+
56
+ items = response.dig('data', data_key) || response['data'] || []
57
+ @data = items.map { |item| resource_class.new(item) }
58
+ @total_count = response.dig('data', 'total') || response['total'] || items.length
59
+ @page = response.dig('data', 'page') || response['page'] || 1
60
+ @per_page = response.dig('data', 'per_page') || response['per_page'] || items.length
61
+ end
62
+
63
+ def has_more?
64
+ (@page * @per_page) < @total_count
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ServiceTrade
4
+ module ApiOperations
5
+ module Update
6
+ def update(id, params = {})
7
+ response = ServiceTrade.client.request(:put, "#{resource_url}/#{id}", params)
8
+ new(response['data'])
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,42 @@
1
+ module ServiceTrade
2
+ class Auth
3
+ attr_reader :session_id
4
+
5
+ def initialize
6
+ @session_id = nil
7
+ @last_auth_time = nil
8
+ end
9
+
10
+ def authenticate
11
+ response = Client.new.request(
12
+ :post,
13
+ 'auth',
14
+ {
15
+ username: ServiceTrade.configuration.username,
16
+ password: ServiceTrade.configuration.password
17
+ },
18
+ skip_auth: true
19
+ )
20
+
21
+ @session_id = response['sessionId']
22
+ @last_auth_time = Time.now
23
+
24
+ @session_id
25
+ end
26
+
27
+ def session_id
28
+ if @session_id.nil? || session_expired?
29
+ authenticate
30
+ end
31
+ @session_id
32
+ end
33
+
34
+ private
35
+
36
+ def session_expired?
37
+ return true if @last_auth_time.nil?
38
+ # ServiceTrade sessions typically expire after 24 hours
39
+ Time.now - @last_auth_time > 86400 # 24 hours in seconds
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,72 @@
1
+ module ServiceTrade
2
+ class Client
3
+ attr_reader :api_base
4
+
5
+ def initialize
6
+ @api_base = ServiceTrade.api_base
7
+ end
8
+
9
+ def request(method, path, params = {}, headers = {}, skip_auth: false)
10
+ uri = URI.parse("#{api_base}/#{path}")
11
+
12
+ # Set up the request
13
+ klass = case method
14
+ when :get
15
+ Net::HTTP::Get
16
+ when :post
17
+ Net::HTTP::Post
18
+ when :put
19
+ Net::HTTP::Put
20
+ when :delete
21
+ Net::HTTP::Delete
22
+ else
23
+ raise ArgumentError, "Unknown HTTP method: #{method}"
24
+ end
25
+
26
+ request = klass.new(uri)
27
+
28
+ # Add headers
29
+ request['Content-Type'] = 'application/json'
30
+ request['Accept'] = 'application/json'
31
+ unless skip_auth
32
+ request['X-Session-Id'] = ServiceTrade.auth.session_id
33
+ end
34
+ headers.each { |key, value| request[key] = value }
35
+
36
+ # Add parameters
37
+ if [:post, :put].include?(method)
38
+ request.body = params.to_json
39
+ elsif method == :get && !params.empty?
40
+ uri.query = URI.encode_www_form(params)
41
+ end
42
+
43
+ # Make the request
44
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
45
+ http.open_timeout = ServiceTrade.configuration.open_timeout
46
+ http.read_timeout = ServiceTrade.configuration.timeout
47
+ http.request(request)
48
+ end
49
+
50
+ handle_response(response)
51
+ end
52
+
53
+ private
54
+
55
+ def handle_response(response)
56
+ case response.code.to_i
57
+ when 200..299
58
+ # Handle 204 No Content responses
59
+ return {} if response.body.nil? || response.body.strip.empty?
60
+ JSON.parse(response.body)
61
+ when 401
62
+ raise ServiceTrade::AuthenticationError, "Invalid credentials or expired session"
63
+ when 403
64
+ raise ServiceTrade::AuthorizationError, "Not authorized to perform this action"
65
+ when 404
66
+ raise ServiceTrade::NotFoundError, "Resource not found"
67
+ else
68
+ raise ServiceTrade::ApiError, "API error (#{response.code}): #{response.body}"
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,11 @@
1
+ module ServiceTrade
2
+ class Configuration
3
+ attr_accessor :username, :password, :api_version, :timeout, :open_timeout
4
+
5
+ def initialize
6
+ @api_version = '1'
7
+ @timeout = 30
8
+ @open_timeout = 10
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module ServiceTrade
2
+ class Error < StandardError; end
3
+ class ApiError < Error; end
4
+ class AuthenticationError < Error; end
5
+ class AuthorizationError < Error; end
6
+ class NotFoundError < Error; end
7
+ end
@@ -0,0 +1,14 @@
1
+ module ServiceTrade
2
+ class Appointment
3
+ extend ServiceTrade::ApiOperations::Create
4
+ extend ServiceTrade::ApiOperations::List
5
+ extend ServiceTrade::ApiOperations::Update
6
+ extend ServiceTrade::ApiOperations::Delete
7
+
8
+ OBJECT_NAME = 'appointments'
9
+
10
+ def self.resource_url
11
+ OBJECT_NAME
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+ module ServiceTrade
2
+ class BaseResource
3
+ attr_reader :attributes
4
+
5
+ def initialize(attributes = {})
6
+ @attributes = attributes
7
+ set_attributes(attributes)
8
+ end
9
+
10
+ private
11
+
12
+ def set_attributes(attributes)
13
+ attributes.each do |key, value|
14
+ # Convert camelCase to snake_case for Ruby conventions
15
+ snake_key = camel_to_snake(key.to_s)
16
+
17
+ # Only set attributes that have corresponding reader methods
18
+ if self.respond_to?(snake_key)
19
+ instance_variable_set("@#{snake_key}", value)
20
+ end
21
+ end
22
+ end
23
+
24
+ def camel_to_snake(str)
25
+ str.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
26
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
27
+ .downcase
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,149 @@
1
+ module ServiceTrade
2
+ class Job < BaseResource
3
+ extend ServiceTrade::ApiOperations::Create
4
+ extend ServiceTrade::ApiOperations::List
5
+ extend ServiceTrade::ApiOperations::Update
6
+ extend ServiceTrade::ApiOperations::Delete
7
+
8
+ OBJECT_NAME = 'job'.freeze
9
+
10
+ # Core job attributes
11
+ attr_reader :id, :uri, :name, :custom_name, :type, :job_type_weight,
12
+ :status, :display_status, :substatus, :display_substatus,
13
+ :number, :ref_number, :customer_po, :visibility, :section_visibilities,
14
+ :description, :scheduled_date, :estimated_price, :latest_clock_in,
15
+ :ivr_open, :ivr_activity, :service_line, :due_by, :due_after,
16
+ :completed_on, :percent_complete, :is_project, :budgeted,
17
+ :created, :updated
18
+
19
+ # Related objects
20
+ attr_reader :vendor, :customer, :location, :owner, :sales, :primary_contact,
21
+ :current_appointment, :assigned_office, :offices, :tags,
22
+ :external_ids, :terms, :contract, :project, :notes,
23
+ :service_requests, :scheduling_comments
24
+
25
+ # Service link visibility
26
+ attr_reader :service_link_attachment_visibility, :service_link_comment_visibility,
27
+ :service_link_attachment_category_visibility
28
+
29
+ # Deprecated fields (kept for backwards compatibility)
30
+ attr_reader :deficiencies_found, :other_trade_deficiencies_found, :red_tags_found
31
+
32
+ def self.resource_url
33
+ OBJECT_NAME
34
+ end
35
+
36
+ # Find a specific job by ID
37
+ def self.find(id)
38
+ response = ServiceTrade.client.request(:get, "#{resource_url}/#{id}")
39
+ new(response['data'])
40
+ end
41
+
42
+ # Enhanced list method with comprehensive filtering
43
+ def self.list(filters = {})
44
+ # Set default status to 'scheduled' if not provided and no job number is specified
45
+ unless filters.key?(:status) || filters.key?('status') || filters.key?(:number) || filters.key?('number')
46
+ filters[:status] = 'scheduled'
47
+ end
48
+
49
+ response = ServiceTrade.client.request(:get, resource_url, filters)
50
+
51
+ # Handle the nested response structure from ServiceTrade API
52
+ jobs_data = response.dig('data', 'jobs') || response['data'] || []
53
+ jobs_data.map { |job_data| new(job_data) }
54
+ end
55
+
56
+ # Create a new job
57
+ def self.create(params = {})
58
+ response = ServiceTrade.client.request(:post, resource_url, params)
59
+ new(response['data'])
60
+ end
61
+
62
+ # Update an existing job
63
+ def self.update(id, params = {})
64
+ response = ServiceTrade.client.request(:put, "#{resource_url}/#{id}", params)
65
+ new(response['data'])
66
+ end
67
+
68
+ # Update this job instance
69
+ def update(params = {})
70
+ self.class.update(id, params)
71
+ end
72
+
73
+ # Delete a job
74
+ def self.delete(id)
75
+ ServiceTrade.client.request(:delete, "#{resource_url}/#{id}")
76
+ true
77
+ end
78
+
79
+ # Delete this job instance
80
+ def delete
81
+ self.class.delete(id)
82
+ end
83
+
84
+ # Add task responses to a job
85
+ def add_task_responses(task_responses)
86
+ params = { task_responses: task_responses }
87
+ response = ServiceTrade.client.request(:post, "#{self.class.resource_url}/#{id}/taskresponses", params)
88
+ response['data']
89
+ end
90
+
91
+ # Convenience methods for common job filtering
92
+ def self.by_status(status)
93
+ list(status: status)
94
+ end
95
+
96
+ def self.by_customer(customer_id)
97
+ list(customer_id: customer_id)
98
+ end
99
+
100
+ def self.by_vendor(vendor_id)
101
+ list(vendor_id: vendor_id)
102
+ end
103
+
104
+ def self.by_location(location_id)
105
+ list(location_id: location_id)
106
+ end
107
+
108
+ def self.by_owner(owner_id)
109
+ list(owner_id: owner_id)
110
+ end
111
+
112
+ def self.around_location(lat, lon, radius)
113
+ list(lat: lat, lon: lon, radius: radius)
114
+ end
115
+
116
+ def self.due_between(start_timestamp, end_timestamp)
117
+ list(due_by_begin: start_timestamp, due_by_end: end_timestamp)
118
+ end
119
+
120
+ def self.completed_between(start_timestamp, end_timestamp)
121
+ list(completed_on_begin: start_timestamp, completed_on_end: end_timestamp)
122
+ end
123
+
124
+ # Check if job is completed
125
+ def completed?
126
+ status == 'completed'
127
+ end
128
+
129
+ # Check if job is canceled
130
+ def canceled?
131
+ status == 'canceled'
132
+ end
133
+
134
+ # Check if job is scheduled
135
+ def scheduled?
136
+ status == 'scheduled'
137
+ end
138
+
139
+ # Check if job is invoiced
140
+ def invoiced?
141
+ status == 'invoiced'
142
+ end
143
+
144
+ # Check if job has IVR activity open
145
+ def ivr_open?
146
+ ivr_open == true
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,218 @@
1
+ module ServiceTrade
2
+ class Location < BaseResource
3
+ extend ServiceTrade::ApiOperations::Create
4
+ extend ServiceTrade::ApiOperations::List
5
+ extend ServiceTrade::ApiOperations::Update
6
+ extend ServiceTrade::ApiOperations::Delete
7
+
8
+ OBJECT_NAME = 'location'.freeze
9
+
10
+ # Core location attributes
11
+ attr_reader :id, :uri, :name, :ref_number, :lat, :lon, :geocode_quality,
12
+ :distance, :phone_number, :email, :general_manager, :status,
13
+ :taxable, :store_number, :created, :updated
14
+
15
+ # Address components
16
+ attr_reader :address, :address_street, :address_city, :address_state, :address_postal_code
17
+
18
+ # Related objects
19
+ attr_reader :company, :brand, :primary_contact, :offices, :tags, :tax_group,
20
+ :external_ids, :remit_to_address, :remit_to_source
21
+
22
+ # Deprecated fields (kept for backwards compatibility)
23
+ attr_reader :legacy_id
24
+
25
+ def self.resource_url
26
+ OBJECT_NAME
27
+ end
28
+
29
+ # Find a specific location by ID
30
+ def self.find(id)
31
+ response = ServiceTrade.client.request(:get, "#{resource_url}/#{id}")
32
+ new(response['data'])
33
+ end
34
+
35
+ # Enhanced list method with comprehensive filtering
36
+ def self.list(filters = {})
37
+ # Set default isCustomer to true if not specified
38
+ unless filters.key?(:is_customer) || filters.key?('isCustomer')
39
+ filters[:is_customer] = true
40
+ end
41
+
42
+ response = ServiceTrade.client.request(:get, resource_url, filters)
43
+
44
+ # Handle the nested response structure from ServiceTrade API
45
+ locations_data = response.dig('data', 'locations') || response['data'] || []
46
+ locations_data.map { |location_data| new(location_data) }
47
+ end
48
+
49
+ # Create a new location
50
+ def self.create(params = {})
51
+ response = ServiceTrade.client.request(:post, resource_url, params)
52
+ new(response['data'])
53
+ end
54
+
55
+ # Update an existing location
56
+ def self.update(id, params = {})
57
+ response = ServiceTrade.client.request(:put, "#{resource_url}/#{id}", params)
58
+ new(response['data'])
59
+ end
60
+
61
+ # Update this location instance
62
+ def update(params = {})
63
+ self.class.update(id, params)
64
+ end
65
+
66
+ # Delete a location
67
+ def self.delete(id)
68
+ ServiceTrade.client.request(:delete, "#{resource_url}/#{id}")
69
+ true
70
+ end
71
+
72
+ # Delete this location instance
73
+ def delete
74
+ self.class.delete(id)
75
+ end
76
+
77
+ # Merge this location into another location
78
+ def merge(replacement_id)
79
+ params = { replacement_id: replacement_id }
80
+ response = ServiceTrade.client.request(:post, "#{self.class.resource_url}/#{id}/merge", params)
81
+ self.class.new(response['data'])
82
+ end
83
+
84
+ # Get assets at this location
85
+ def assets
86
+ response = ServiceTrade.client.request(:get, "#{self.class.resource_url}/#{id}/asset")
87
+ assets_data = response.dig('data', 'assets') || []
88
+ # For now, return raw data - could create Asset resource class later
89
+ assets_data
90
+ end
91
+
92
+ # Get comments for this location
93
+ def comments
94
+ response = ServiceTrade.client.request(:get, "#{self.class.resource_url}/#{id}/comment")
95
+ comments_data = response.dig('data', 'comments') || []
96
+ # For now, return raw data - could create Comment resource class later
97
+ comments_data
98
+ end
99
+
100
+ # Create a comment for this location
101
+ def create_comment(params = {})
102
+ response = ServiceTrade.client.request(:post, "#{self.class.resource_url}/#{id}/comment", params)
103
+ response['data']
104
+ end
105
+
106
+ # Convenience methods for common location filtering
107
+ def self.by_name(name)
108
+ list(name: name)
109
+ end
110
+
111
+ def self.by_company(company_id)
112
+ list(company_id: company_id)
113
+ end
114
+
115
+ def self.by_ref_number(ref_number)
116
+ list(ref_number: ref_number)
117
+ end
118
+
119
+ def self.by_status(status)
120
+ list(status: status)
121
+ end
122
+
123
+ def self.by_region(region_ids)
124
+ region_ids = Array(region_ids).join(',') if region_ids.is_a?(Array)
125
+ list(region_ids: region_ids)
126
+ end
127
+
128
+ def self.by_office(office_ids)
129
+ office_ids = Array(office_ids).join(',') if office_ids.is_a?(Array)
130
+ list(office_ids: office_ids)
131
+ end
132
+
133
+ def self.customers_only
134
+ list(is_customer: true)
135
+ end
136
+
137
+ def self.vendors_only
138
+ list(is_vendor: true)
139
+ end
140
+
141
+ def self.active_locations
142
+ list(status: 'active')
143
+ end
144
+
145
+ def self.inactive_locations
146
+ list(status: 'inactive')
147
+ end
148
+
149
+ def self.updated_after(timestamp)
150
+ list(updated_after: timestamp)
151
+ end
152
+
153
+ def self.updated_before(timestamp)
154
+ list(updated_before: timestamp)
155
+ end
156
+
157
+ def self.created_after(timestamp)
158
+ list(created_after: timestamp)
159
+ end
160
+
161
+ def self.created_before(timestamp)
162
+ list(created_before: timestamp)
163
+ end
164
+
165
+ def self.with_tags(tags)
166
+ tags = Array(tags).join(',') if tags.is_a?(Array)
167
+ list(tag: tags)
168
+ end
169
+
170
+ # Status check methods
171
+ def active?
172
+ status == 'active'
173
+ end
174
+
175
+ def inactive?
176
+ status == 'inactive'
177
+ end
178
+
179
+ def pending?
180
+ status == 'pending'
181
+ end
182
+
183
+ def on_hold?
184
+ status == 'on_hold'
185
+ end
186
+
187
+ # Check if location is taxable
188
+ def taxable?
189
+ taxable == true
190
+ end
191
+
192
+ # Get the full address as a string
193
+ def full_address
194
+ parts = []
195
+
196
+ if address
197
+ parts = [
198
+ address['street'],
199
+ address['city'],
200
+ address['state'],
201
+ address['postalCode']
202
+ ]
203
+ else
204
+ parts = [
205
+ address_street,
206
+ address_city,
207
+ address_state,
208
+ address_postal_code
209
+ ]
210
+ end
211
+
212
+ parts = parts.compact
213
+ return nil if parts.empty?
214
+
215
+ parts.join(', ')
216
+ end
217
+ end
218
+ end
File without changes
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ServiceTrade
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "servicetrade/version"
4
+ require_relative "servicetrade/configuration"
5
+ require_relative "servicetrade/client"
6
+ require_relative "servicetrade/auth"
7
+ require_relative "servicetrade/errors"
8
+
9
+ # API Operations
10
+ require_relative "servicetrade/api_operations/create"
11
+ require_relative "servicetrade/api_operations/list"
12
+ require_relative "servicetrade/api_operations/update"
13
+ require_relative "servicetrade/api_operations/delete"
14
+
15
+ # Resources
16
+ require_relative "servicetrade/resources/base_resource"
17
+ require_relative "servicetrade/resources/job"
18
+ require_relative "servicetrade/resources/appointment"
19
+ require_relative "servicetrade/resources/location"
20
+
21
+ require "net/http"
22
+ require "json"
23
+
24
+ module ServiceTrade
25
+ class << self
26
+ attr_accessor :configuration, :auth_instance, :client_instance
27
+ end
28
+
29
+ def self.configure
30
+ self.configuration ||= Configuration.new
31
+ yield(configuration) if block_given?
32
+ end
33
+
34
+ def self.auth
35
+ @auth_instance ||= Auth.new
36
+ end
37
+
38
+ def self.client
39
+ @client_instance ||= Client.new
40
+ end
41
+
42
+ def self.api_base
43
+ "https://api.servicetrade.com/api"
44
+ end
45
+
46
+ # Reset all instances (useful for testing)
47
+ def self.reset!
48
+ @configuration = nil
49
+ @auth_instance = nil
50
+ @client_instance = nil
51
+ end
52
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: servicetrade
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Bryce Holcomb
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2025-09-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rake
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '13.0'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '13.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rspec
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: stringio
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: 3.1.2
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 3.1.2
54
+ description: A Ruby library for interacting with the ServiceTrade API
55
+ email:
56
+ - bryce@destilado.tech
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - CHANGELOG.md
62
+ - LICENSE.md
63
+ - README.md
64
+ - lib/servicetrade.rb
65
+ - lib/servicetrade/api_operations/create.rb
66
+ - lib/servicetrade/api_operations/delete.rb
67
+ - lib/servicetrade/api_operations/list.rb
68
+ - lib/servicetrade/api_operations/update.rb
69
+ - lib/servicetrade/auth.rb
70
+ - lib/servicetrade/client.rb
71
+ - lib/servicetrade/configuration.rb
72
+ - lib/servicetrade/errors.rb
73
+ - lib/servicetrade/resources/appointment.rb
74
+ - lib/servicetrade/resources/base_resource.rb
75
+ - lib/servicetrade/resources/job.rb
76
+ - lib/servicetrade/resources/location.rb
77
+ - lib/servicetrade/util.rb
78
+ - lib/servicetrade/version.rb
79
+ homepage: https://github.com/destilado/servicetrade-ruby
80
+ licenses:
81
+ - MIT
82
+ metadata: {}
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: 2.6.0
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubygems_version: 3.6.2
98
+ specification_version: 4
99
+ summary: Ruby client for the ServiceTrade API
100
+ test_files: []