vng 0.1.0 → 0.1.14

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: 5db1598d18b48ee1c31a7e367c907bc8ba6c224623dc0590d573889812f27322
4
- data.tar.gz: 3384c1b42509e05a31c85b5335792c9796a9e20bb4985a0ecb0879d7c8f031c5
3
+ metadata.gz: de1ec03096db648933db7f9886eaa72b6c4af73e614e475bc0ad26126f35015e
4
+ data.tar.gz: 0adb9f129d030b78ec726f6a2a1cedd61ebc37c88a88df524aca23ff4d2583ca
5
5
  SHA512:
6
- metadata.gz: c9b6bdd93b8627c1512915dd353a08485047356a7cb7835c7a55dc587ce6eb27193fb4404dad08918e5e291cf22cd1380acf6544279a198eb593305232501f80
7
- data.tar.gz: 115ff16da1a5d29c4fb3a39404da2123332021df2d8bc1b809ce42126483010c4215c1030d1361120bea9943ee0c4e96eca8f0175f3b09e3b95f001d73bc8260
6
+ metadata.gz: ce83ddaea6c34121333fdaf15b5600b2486e51c2803b82a71826ca464b7c18f79f1c5d39e0837a0483ecda73512abae325fda7aea0b6ec7b4f8fac3ebcc201c4
7
+ data.tar.gz: 7efd6a2bc16eae79b5ecde44d85ec4ce4de4366a9c735bebc99350d1c743ccc4a6320d47a834e84a92205524761222d07e43a02b06878ff413ca5ff8219e7962
data/CHANGELOG.md CHANGED
@@ -1,5 +1,61 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.14] - 2024-11-17
4
+
5
+ - Adds Vng::Error
6
+
7
+ ## [0.1.13] - 2024-11-15
8
+
9
+ - Enable Vng.configuration.security_token
10
+
11
+ ## [0.1.12] - 2024-11-15
12
+
13
+ - Adds ServiceType.where(zip:)
14
+
15
+ ## [0.1.11] - 2024-11-15
16
+
17
+ - Adds Zip
18
+
19
+ ## [0.1.10] - 2024-11-14
20
+
21
+ - Adds Service Type
22
+
23
+ ## [0.1.9] - 2024-11-13
24
+
25
+ - Adds Franchise
26
+
27
+ ## [0.1.8] - 2024-11-13
28
+
29
+ - Adds Security Token
30
+
31
+ ## [0.1.7] - 2024-11-12
32
+
33
+ - Adds Case
34
+
35
+ ## [0.1.6] - 2024-11-12
36
+
37
+ - Adds WorkOrder
38
+
39
+ ## [0.1.5] - 2024-11-11
40
+
41
+ - Adds Availability
42
+
43
+ ## [0.1.4] - 2024-11-11
44
+
45
+ - Adds Price Item
46
+
47
+ ## [0.1.3] - 2024-11-10
48
+
49
+ - Adds Location
50
+
51
+ ## [0.1.2] - 2024-11-10
52
+
53
+ - Adds Breed
54
+
55
+ ## [0.1.1] - 2024-11-10
56
+
57
+ - Adds Lead.create, Lead#destroy, Contact.create, Contact#destroy
58
+
3
59
  ## [0.1.0] - 2024-11-10
4
60
 
5
61
  - Initial release
data/README.md CHANGED
@@ -1,3 +1,109 @@
1
- # Vng
1
+ Vng - a Ruby client for the Vonigo API
2
+ ======================================================
2
3
 
3
- A Ruby API Client for the Vonigo API
4
+ Vng helps you write apps that need to interact with Vonigo.
5
+
6
+ The **source code** is available on [GitHub](https://github.com/HouseAccountEng/vng) and the **documentation** on [RubyDoc](http://www.rubydoc.info/gems/vng/frames).
7
+
8
+ [![Code Climate](https://codeclimate.com/github/HouseAccountEng/vng.png)](https://codeclimate.com/github/HouseAccountEng/vng)
9
+
10
+ After [registering your app](#configuring-your-app), you can run commands like:
11
+
12
+
13
+ ```ruby
14
+ Vng::Zip.all
15
+ Vng::Franchise.all
16
+ Vng::ServiceType.where zip:
17
+ Vng::SecurityToken.create host:, usename:, password:
18
+ Vng::Lead.create email:, phone:, name: full_name
19
+ Vng::Contact.create first_name:, last_name:, email:, phone:, client_id:
20
+ Vng::Location.create address:, city:, zip:, state: state, client_id:
21
+ Vng::Breed.all
22
+ Vng::Asset.create name:, weight:, breed_option_id:, client_id:
23
+ Vng::PriceItem.where location_id:, asset_id:
24
+ Vng::Availability.where(location_id:, duration:, from_time:, to_time:
25
+ Vng::Lock.create date:, duration: location_id:
26
+ Vng::WorkOrder.create lock_id:, client_id:, contact_id:, location_id:, duration:, summary:, line_items:
27
+ Vng::Case.create client_id:, summary:, comments:
28
+ ```
29
+
30
+ The **full documentation** is available at [rubydoc.info](http://www.rubydoc.info/gems/vng/frames).
31
+
32
+ How to install
33
+ ==============
34
+
35
+ To install on your system, run
36
+
37
+ gem install vng
38
+
39
+ To use inside a bundled Ruby project, add this line to the Gemfile:
40
+
41
+ gem 'vng', '~> 0.1.13'
42
+
43
+ Since the gem follows [Semantic Versioning](http://semver.org),
44
+ indicating the full version in your Gemfile (~> *major*.*minor*.*patch*)
45
+ guarantees that your project won’t occur in any error when you `bundle update`
46
+ and a new version of Vng is released.
47
+
48
+ Configuring your app
49
+ ====================
50
+
51
+ In order to use Vng you must have credentials to the [Vonigo](https://www.vonigo.com/) API.
52
+
53
+ Add them to your code with the following snippet of code (replacing with your own credentials):
54
+
55
+ ```ruby
56
+ Vng.configure do |config|
57
+ config.host = 'subdomain.vonigo.com'
58
+ config.username = 'VonigoUser'
59
+ config.password = 'VonigoPassword'
60
+ end
61
+ ```
62
+
63
+ Configuring with environment variables
64
+ --------------------------------------
65
+
66
+ As an alternative to the approach above, you can configure your app with
67
+ variables. Setting the following environment variables:
68
+
69
+ ```bash
70
+ export VNG_HOST="subdomain.vonigo.com"
71
+ export VNG_USERNAME="VonigoUser"
72
+ export VNG_PASSWORD="VonigoPassword"
73
+ ```
74
+
75
+ is equivalent to the previous approach so pick the one you prefer.
76
+ If a variable is set in both places, then `Vng.configure` takes precedence.
77
+
78
+ How to test
79
+ ===========
80
+
81
+ To run tests:
82
+
83
+ ```bash
84
+ rspec
85
+ ```
86
+
87
+ How to release new versions
88
+ ===========================
89
+
90
+ If you are a manager of this project, remember to upgrade the [Vng gem](http://rubygems.org/gems/vng)
91
+ whenever a new feature is added or a bug gets fixed.
92
+ Make sure all the tests are passing and the code is 100% test-covered.
93
+ Document the changes in CHANGELOG.md and README.md, bump the version, then run:
94
+
95
+ rake release
96
+
97
+ Remember that the vng gem follows [Semantic Versioning](http://semver.org).
98
+ Any new release that is fully backward-compatible should bump the *patch* version (0.0.x).
99
+ Any new version that breaks compatibility should bump the *minor* version (0.x.0)
100
+
101
+ How to contribute
102
+ =================
103
+
104
+ Vng needs your support!
105
+ The goal of Vng is to provide a Ruby interface for all the methods exposed by the Vonigo API.
106
+
107
+ If you find that a method is missing, fork the project, add the missing code,
108
+ write the appropriate tests, then submit a pull request, and it will gladly
109
+ be merged!
data/Rakefile CHANGED
@@ -1,7 +1,5 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
5
3
 
6
4
  RSpec::Core::RakeTask.new(:spec)
7
5
 
data/lib/vng/asset.rb ADDED
@@ -0,0 +1,48 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo assets.
5
+ class Asset < Resource
6
+ PATH = '/api/v1/data/Assets/'
7
+
8
+ attr_reader :id
9
+
10
+ def initialize(id:)
11
+ @id = id
12
+ end
13
+
14
+ def self.create(name:, weight:, breed_option_id:, client_id:)
15
+ body = {
16
+ method: '3',
17
+ clientID: client_id,
18
+ Fields: [
19
+ {fieldID: 1013, fieldValue: name},
20
+ {fieldID: 1017, fieldValue: weight},
21
+ {fieldID: 1014, optionID: breed_option_id},
22
+ ]
23
+ }
24
+
25
+ data = request path: PATH, body: body
26
+
27
+ # curl = 'curl'.tap do |curl|
28
+ # curl << ' -X POST'
29
+ # request.each_header{|k, v| curl << %Q{ -H "#{k}: #{v}"}}
30
+ # curl << %Q{ -d '#{request.body}'} if request.body
31
+ # curl << %Q{ "#{uri.to_s}"}
32
+ # end
33
+ # puts curl
34
+
35
+ new id: data['Asset']['objectID']
36
+ end
37
+
38
+ def destroy
39
+ body = {
40
+ method: '4',
41
+ objectID: id,
42
+ }
43
+
44
+ data = self.class.request path: PATH, body: body
45
+ end
46
+ end
47
+ end
48
+
@@ -0,0 +1,37 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo availabilities.
5
+ class Availability < Resource
6
+ PATH = '/api/v1/resources/availability/'
7
+
8
+ attr_reader :route_id, :date, :minutes
9
+
10
+ def initialize(route_id:, date:, minutes:)
11
+ @route_id = route_id
12
+ @date = date
13
+ @minutes = minutes
14
+ end
15
+
16
+ def self.where(location_id:, duration:, from_time:, to_time:)
17
+ body = {
18
+ method: '0',
19
+ serviceTypeID: '14', # only return items of serviceType 'Pet Grooming'
20
+ locationID: location_id,
21
+ duration: [duration.to_i, 30].max, # or 'duration is not provided'
22
+ dateStart: from_time.to_i,
23
+ dateEnd: to_time.to_i,
24
+ }
25
+
26
+ data = request path: PATH, body: body
27
+
28
+ data['Availability'].map do |availability|
29
+ route_id = availability['routeID']
30
+ date = Date.strptime availability['dayID'], '%Y%m%d'
31
+ minutes = availability['startTime'].to_i
32
+
33
+ new route_id: route_id, date: date, minutes: minutes
34
+ end
35
+ end
36
+ end
37
+ end
data/lib/vng/breed.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo breeds.
5
+ class Breed < Resource
6
+ PATH = '/api/v1/resources/breeds/'
7
+
8
+ attr_reader :id, :name, :species, :option_id, :low_weight, :high_weight
9
+
10
+ def initialize(id:, name:, species:, option_id:, low_weight:, high_weight:)
11
+ @id = id
12
+ @name = name
13
+ @species = species
14
+ @option_id = option_id
15
+ @low_weight = low_weight
16
+ @high_weight = high_weight
17
+ end
18
+
19
+ # TODO: Needs pagination
20
+ def self.all
21
+ data = request path: PATH
22
+
23
+ data['Breeds'].map do |breed|
24
+ id = breed['breedID']
25
+ name = breed['breed']
26
+ species = breed['species']
27
+ option_id = breed['optionID']
28
+ low_weight = breed['breedLowWeight']
29
+ high_weight = breed['breedHighWeight']
30
+
31
+ new id: id, name: name, species: species, option_id: option_id, low_weight: low_weight, high_weight: high_weight
32
+ end
33
+ end
34
+ end
35
+ end
data/lib/vng/case.rb ADDED
@@ -0,0 +1,45 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo cases.
5
+ class Case < Resource
6
+ PATH = '/api/v1/data/Cases/'
7
+
8
+ attr_reader :id
9
+
10
+ def initialize(id:)
11
+ @id = id
12
+ end
13
+
14
+ def self.create(client_id:, summary:, comments:, phone:, email:, zip:)
15
+ body = {
16
+ method: '3',
17
+ clientID: client_id,
18
+ Fields: [
19
+ {fieldID: 219, optionID: 239}, # Status: open
20
+ {fieldID: 220, fieldValue: summary}, # Summary:
21
+ {fieldID: 230, fieldValue: comments}, # Comments:
22
+ {fieldID: 226, optionID: 227}, # Type: 'General request'
23
+ {fieldID: 227, optionID: 232}, # Preferred Contact Method: 'Phone'
24
+ {fieldID: 228, fieldValue: phone}, # Phone Me Back at:
25
+ {fieldID: 229, fieldValue: email}, # Email:
26
+ {fieldID: 1023, fieldValue: zip}, # Zip Code:
27
+ ]
28
+ }
29
+
30
+ data = request path: PATH, body: body
31
+
32
+ new id: data['Case']['objectID']
33
+ end
34
+
35
+ def destroy
36
+ body = {
37
+ method: '4',
38
+ objectID: id,
39
+ }
40
+
41
+ self.class.request path: PATH, body: body
42
+ end
43
+ end
44
+ end
45
+
data/lib/vng/config.rb ADDED
@@ -0,0 +1,45 @@
1
+ require 'vng/configuration'
2
+
3
+ # An object-oriented Ruby client for Voonigo.
4
+ # @see http://www.rubydoc.info/gems/vng/
5
+ module Vng
6
+ # Provides methods to read and write global configuration settings.
7
+ #
8
+ # A typical usage is to set the Security Token for the API calls.
9
+ #
10
+ # @example Set the Security Token for the API client:
11
+ # Vng.configure do |config|
12
+ # config.security_token = 'ABCDEFGHIJ1234567890'
13
+ # end
14
+ #
15
+ module Config
16
+ # Yields the global configuration to the given block.
17
+ #
18
+ # @example
19
+ # Vng.configure do |config|
20
+ # config.security_token = 'ABCDEFGHIJ1234567890'
21
+ # end
22
+ #
23
+ # @yield [Vng::Models::Configuration] The global configuration.
24
+ def configure
25
+ yield configuration if block_given?
26
+ end
27
+
28
+ # Returns the global {Vng::Models::Configuration} object.
29
+ #
30
+ # While this method _can_ be used to read and write configuration settings,
31
+ # it is easier to use {Vng::Config#configure} Vng.configure}.
32
+ #
33
+ # @example
34
+ # Vng.configuration.security_token = 'ABCDEFGHIJ1234567890'
35
+ #
36
+ # @return [Vng::Models::Configuration] The global configuration.
37
+ def configuration
38
+ @configuration ||= Vng::Configuration.new
39
+ end
40
+ end
41
+
42
+ # @note Config is the only module auto-loaded in the Vng module,
43
+ # in order to have a syntax as easy as Vng.configure
44
+ extend Config
45
+ end
@@ -0,0 +1,51 @@
1
+ module Vng
2
+ # Provides an object to store global configuration settings.
3
+ #
4
+ # This class is typically not used directly, but by calling
5
+ # {Vng::Config#configure Vng.configure}, which creates and updates a single
6
+ # instance of {Vng::Models::Configuration}.
7
+ #
8
+ # @example Set the Security Token for the API client:
9
+ # Vng.configure do |config|
10
+ # config.security_token = 'ABCDEFGHIJ1234567890'
11
+ # end
12
+ #
13
+ # @see Vng::Config for more examples.
14
+ #
15
+ # An alternative way to set global configuration settings is by storing
16
+ # them in the following environment variables:
17
+ #
18
+ # * +VNG_HOST+ to store the host for the Vonigo API
19
+ # * +VNG_USERNAME+ to store the username for the Vonigo API
20
+ # * +VNG_PASSWORD+ to store the password for the Vonigo API
21
+ #
22
+ # In case both methods are used together,
23
+ # {Vng::Config#configure Vng.configure} takes precedence.
24
+ #
25
+ # @example Set the API credentials
26
+ # ENV['VNG_HOST'] = 'subdomain.vonigo.com'
27
+ # ENV['VNG_USERNAME'] = 'VonigoUser'
28
+ # ENV['VNG_Password'] = 'VonigoPassword'
29
+ #
30
+ class Configuration
31
+ # @return [String] the Security Token for the API calls.
32
+ attr_accessor :security_token
33
+
34
+ # @return [String] the URI host for the API calls.
35
+ attr_accessor :host
36
+
37
+ # @return [String] the username for the API calls.
38
+ attr_accessor :username
39
+
40
+ # @return [String] the password for the API calls.
41
+ attr_accessor :password
42
+
43
+ # Initialize the global configuration settings, using the values of
44
+ # the specified following environment variables by default.
45
+ def initialize
46
+ @host = ENV['VNG_HOST']
47
+ @username = ENV['VNG_USERNAME']
48
+ @password = ENV['VNG_PASSWORD']
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,5 @@
1
+ module Vng
2
+ # A wrapper around StandardError.
3
+ class ConnectionError < StandardError
4
+ end
5
+ end
@@ -0,0 +1,52 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo contacts.
5
+ class Contact < Resource
6
+ PATH = '/api/v1/data/Contacts/'
7
+
8
+ attr_reader :id, :first_name, :last_name, :email, :phone
9
+
10
+ def initialize(id:, first_name:, last_name:, email:, phone:)
11
+ @id = id
12
+ @first_name = first_name
13
+ @last_name = last_name
14
+ @email = email
15
+ @phone = phone
16
+ end
17
+
18
+ def self.create(first_name:, last_name:, email:, phone:, client_id:)
19
+ body = {
20
+ method: '3',
21
+ clientID: client_id,
22
+ Fields: [
23
+ {fieldID: 127, fieldValue: first_name},
24
+ {fieldID: 128, fieldValue: last_name},
25
+ {fieldID: 97, fieldValue: email},
26
+ {fieldID: 96, fieldValue: phone},
27
+ ]
28
+ }
29
+
30
+ data = request path: PATH, body: body
31
+
32
+ id = data['Contact']['objectID']
33
+ first_name = data['Fields'].find{|field| field['fieldID'] == 127}['fieldValue']
34
+ last_name = data['Fields'].find{|field| field['fieldID'] == 128}['fieldValue']
35
+ email = data['Fields'].find{|field| field['fieldID'] == 97}['fieldValue']
36
+ phone = data['Fields'].find{|field| field['fieldID'] == 96}['fieldValue']
37
+
38
+ new id: id, first_name: first_name, last_name: last_name, email: email, phone: phone
39
+ end
40
+
41
+ # Data validation failed. [{"fieldID"=>0, "fieldName"=>nil, "errNo"=>-1408, "errMsg"=>"Primary contact cannot be deleted."}]
42
+ # def destroy
43
+ # body = {
44
+ # method: '4',
45
+ # objectID: id,
46
+ # }
47
+ #
48
+ # self.class.request path: PATH, body: body
49
+ # end
50
+ end
51
+ end
52
+
data/lib/vng/error.rb ADDED
@@ -0,0 +1,5 @@
1
+ module Vng
2
+ # A wrapper around StandardError.
3
+ class Error < StandardError
4
+ end
5
+ end
@@ -0,0 +1,43 @@
1
+ require 'vng/availability'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo franchises.
5
+ class Franchise < Resource
6
+ PATH = '/api/v1/resources/franchises/'
7
+
8
+ attr_reader :id, :name, :gmt_offset
9
+
10
+ def initialize(id:, name: nil, gmt_offset: nil)
11
+ @id = id
12
+ @name = name
13
+ @gmt_offset = gmt_offset
14
+ end
15
+
16
+
17
+ def self.find_by(zip:)
18
+ body = {
19
+ method: '1',
20
+ zip: zip,
21
+ }
22
+
23
+ data = request path: Vng::Availability::PATH, body: body
24
+
25
+ franchise_id = data['Ids']['franchiseID']
26
+ new(id: franchise_id) unless franchise_id == '0'
27
+ end
28
+
29
+ def self.all
30
+ data = request path: PATH
31
+
32
+ data['Franchises'].filter do |franchise|
33
+ franchise['isActive']
34
+ end.map do |franchise|
35
+ id = franchise['franchiseID']
36
+ name = franchise['franchiseName']
37
+ gmt_offset = franchise['gmtOffsetFranchise']
38
+
39
+ new id: id, name: name, gmt_offset: gmt_offset
40
+ end
41
+ end
42
+ end
43
+ end
data/lib/vng/lead.rb ADDED
@@ -0,0 +1,49 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo leads.
5
+ class Lead < Resource
6
+ PATH = '/api/v1/data/Leads/'
7
+
8
+ attr_reader :id, :name, :email, :phone
9
+
10
+ def initialize(id:, name:, email:, phone:)
11
+ @id = id
12
+ @name = name
13
+ @email = email
14
+ @phone = phone
15
+ end
16
+
17
+ def self.create(name:, email:, phone:)
18
+ body = {
19
+ method: '3',
20
+ Fields: [
21
+ {fieldID: 121, optionID: '59'},
22
+ {fieldID: 126, fieldValue: name},
23
+ {fieldID: 238, fieldValue: email},
24
+ {fieldID: 1024, fieldValue: phone},
25
+ ]
26
+ }
27
+
28
+ data = request path: PATH, body: body
29
+
30
+ id = data['Client']['objectID']
31
+ name = data['Fields'].find{|field| field['fieldID'] == 126}['fieldValue']
32
+ email = data['Fields'].find{|field| field['fieldID'] == 238}['fieldValue']
33
+ phone = data['Fields'].find{|field| field['fieldID'] == 1024}['fieldValue']
34
+
35
+ new id: id, name: name, email: email, phone: phone
36
+ end
37
+
38
+ # Data validation failed. [{"fieldID"=>0, "fieldName"=>nil, "errNo"=>-1201, "errMsg"=>"Lead ID does not exist."}]
39
+ # TODO: has become an account meanwhile!! so this doesn't work
40
+ # def destroy
41
+ # body = {
42
+ # method: '4',
43
+ # objectID: id,
44
+ # }
45
+ #
46
+ # self.class.request path: PATH, body: body
47
+ # end
48
+ end
49
+ end
@@ -0,0 +1,55 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo locations.
5
+ class Location < Resource
6
+ PATH = '/api/v1/data/Locations/'
7
+
8
+ # TODO: fetch from /system/objects/ method: 1, objectID: 20
9
+ STATES_OPTION_ID = {
10
+ AK: 9879, AL: 9878, AR: 9877, AZ: 9880, CA: 9883, CO: 9876, CT: 9875,
11
+ DC: 9874, DE: 9873, FL: 9872, GA: 9871, HI: 9870, IA: 9869, ID: 9868,
12
+ IL: 9867, IN: 9866, KS: 9865, KY: 9864, LA: 9863, MA: 9862, MD: 9861,
13
+ ME: 9860, MI: 9859, MN: 9858, MO: 9857, MS: 9856, MT: 9855, NC: 9854,
14
+ ND: 9853, NE: 9852, NH: 9851, NJ: 9850, NM: 9849, NV: 9829, NY: 9848,
15
+ OH: 9847, OK: 9846, OR: 9881, PA: 9845, RI: 9843, SC: 9841, SD: 9842,
16
+ TN: 9840, TX: 9839, UT: 9838, VA: 9837, VT: 9836, WA: 9882, WI: 9828,
17
+ WV: 9835, WY: 9834,
18
+ }
19
+
20
+ attr_reader :id
21
+
22
+ def initialize(id:)
23
+ @id = id
24
+ end
25
+
26
+ def self.create(address:, city:, zip:, state:, client_id:)
27
+ body = {
28
+ method: '3',
29
+ clientID: client_id,
30
+ Fields: [
31
+ {fieldID: 779, optionID: 9906}, # 'USA'
32
+ {fieldID: 778, optionID: STATES_OPTION_ID[state.to_sym]},
33
+ {fieldID: 776, fieldValue: city},
34
+ {fieldID: 773, fieldValue: address},
35
+ {fieldID: 775, fieldValue: zip},
36
+ ]
37
+ }
38
+
39
+ data = request path: PATH, body: body
40
+
41
+ new id: data['Location']['objectID']
42
+ end
43
+
44
+ # Data validation failed. [{"fieldID"=>0, "fieldName"=>"", "errNo"=>-1809, "errMsg"=>"Primary location cannot be deleted."}]
45
+ # def destroy
46
+ # body = {
47
+ # method: '4',
48
+ # objectID: id,
49
+ # }
50
+ #
51
+ # self.class.request path: PATH, body: body
52
+ # end
53
+ end
54
+ end
55
+
data/lib/vng/lock.rb ADDED
@@ -0,0 +1,39 @@
1
+ require 'vng/availability'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo locks.
5
+ class Lock < Resource
6
+ PATH = '/api/v1/resources/availability/'
7
+
8
+ attr_reader :id
9
+
10
+ def initialize(id:)
11
+ @id = id
12
+ end
13
+
14
+ def self.create(duration:, location_id:, date:, minutes:, route_id:)
15
+ body = {
16
+ method: '2',
17
+ serviceTypeID: '14', # only create items of serviceType 'Pet Grooming'
18
+ duration: duration.to_i,
19
+ locationID: location_id,
20
+ dayID: date.strftime('%Y%m%d'),
21
+ routeID: route_id,
22
+ startTime: minutes,
23
+ }
24
+
25
+ data = request path: Vng::Availability::PATH, body: body
26
+
27
+ new id: data['Ids']['lockID']
28
+ end
29
+
30
+ # def destroy
31
+ # body = {
32
+ # method: '4',
33
+ # lockID: id,
34
+ # }
35
+ #
36
+ # self.class.request path: PATH, body: body
37
+ # end
38
+ end
39
+ end
@@ -0,0 +1,45 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo price items.
5
+ class PriceItem < Resource
6
+ PATH = '/api/v1/data/priceLists/'
7
+
8
+ attr_reader :id, :price_item, :value, :tax_id, :duration_per_unit, :service_badge, :service_category
9
+
10
+ def initialize(id:, price_item:, value:, tax_id:, duration_per_unit:, service_badge:, service_category:)
11
+ @id = id
12
+ @price_item = price_item
13
+ @value = value
14
+ @tax_id = tax_id
15
+ @duration_per_unit = duration_per_unit
16
+ @service_badge = service_badge
17
+ @service_category = service_category
18
+ end
19
+
20
+ def self.where(location_id:, asset_id:)
21
+ body = {
22
+ method: '2',
23
+ serviceTypeID: '14', # only return items of serviceType 'Pet Grooming'
24
+ locationID: location_id,
25
+ assetID: asset_id,
26
+ }
27
+
28
+ data = request path: PATH, body: body
29
+
30
+ data['PriceItems'].filter do |body|
31
+ body['isOnline'] && body['isActive']
32
+ end.map do |body|
33
+ id = body['priceItemID']
34
+ price_item = body['priceItem']
35
+ value = body['value']
36
+ tax_id = body['taxID']
37
+ duration_per_unit = body['durationPerUnit']
38
+ service_badge = body['serviceBadge']
39
+ service_category = body['serviceCategory']
40
+
41
+ new id: id, price_item: price_item, value: value, tax_id: tax_id, duration_per_unit: duration_per_unit, service_badge: service_badge, service_category: service_category
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,40 @@
1
+ require 'vng/connection_error'
2
+ require 'vng/error'
3
+
4
+ module Vng
5
+ # Provides an abstract class for every Vonigo resource.
6
+ class Resource
7
+ private
8
+ def self.request(path:, body: {}, query: {}, include_security_token: true)
9
+ uri = URI::HTTPS.build host: host, path: path
10
+ uri.query = URI.encode_www_form(query) if query.any?
11
+
12
+ method = query.any? ? Net::HTTP::Get : Net::HTTP::Post
13
+ request = method.new uri.request_uri
14
+ request.initialize_http_header 'Content-Type' => 'application/json'
15
+
16
+ if query.none?
17
+ body = body.merge(securityToken: security_token) if include_security_token
18
+ request.body = body.to_json
19
+ end
20
+
21
+ response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
22
+ http.request request
23
+ end
24
+
25
+ JSON(response.body).tap do |data|
26
+ raise Vng::Error, "#{data['errMsg']} #{data['Errors']}" unless data['errNo'].zero?
27
+ end
28
+ rescue Errno::ECONNREFUSED, SocketError => e
29
+ raise Vng::ConnectionError, e.message
30
+ end
31
+
32
+ def self.host
33
+ Vng.configuration.host
34
+ end
35
+
36
+ def self.security_token
37
+ Vng.configuration.security_token
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,51 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo work security tokens.
5
+ class SecurityToken < Resource
6
+ PATH = '/api/v1/security/login/'
7
+
8
+ attr_reader :token
9
+
10
+ def initialize(token:)
11
+ @token = token
12
+ end
13
+
14
+ def self.create
15
+ body = {
16
+ app_version: '1',
17
+ company: 'Vonigo',
18
+ username: Vng.configuration.username,
19
+ password: Digest::MD5.hexdigest(Vng.configuration.password),
20
+ }
21
+
22
+ data = request path: PATH, body: body, include_security_token: false
23
+
24
+ new token: data['securityToken']
25
+ end
26
+
27
+ # TODO: Check if it's not the correct one already or catch
28
+ # Data validation failed. [{"fieldID"=>0, "fieldName"=>nil, "errNo"=>-5213, "errMsg"=>"Same franchise ID supplied."}]
29
+ def assign_to(franchise_id:)
30
+ body = {
31
+ securityToken: @token,
32
+ method: "2",
33
+ franchiseID: franchise_id,
34
+ }
35
+
36
+ self.class.request path: '/api/v1/security/session/', body: body, include_security_token: false
37
+ rescue Vng::Error => e
38
+ # TODO: improve: ignore if the token was already assigned to the franchise
39
+ raise unless e.message.include? 'Same franchise ID supplied'
40
+ end
41
+
42
+ def destroy
43
+ query = { securityToken: @token }
44
+ self.class.request path: '/api/v1/security/logout/', query: query
45
+ rescue Vng::Error => e
46
+ p "Vng:Error! #{e}"
47
+ # TODO: improve: ignore if the token was already destroyed
48
+ raise unless e.message.include?('Session expired') || e.message.include?('Session does not exist')
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,47 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo service types.
5
+ class ServiceType < Resource
6
+ PATH = '/api/v1/resources/serviceTypes/'
7
+
8
+ attr_reader :id, :type, :duration
9
+
10
+ def initialize(id:, type:, duration:)
11
+ @id = id
12
+ @type = type
13
+ @duration = duration
14
+ end
15
+
16
+ # TODO: Needs pagination
17
+ def self.all
18
+ data = request path: PATH
19
+
20
+ data['ServiceTypes'].map do |body|
21
+ id = body['serviceTypeID']
22
+ type = body['serviceType']
23
+ duration = body['duration']
24
+
25
+ new id: id, type: type, duration: duration
26
+ end
27
+ end
28
+
29
+ def self.where(zip:)
30
+ body = {
31
+ zip: zip,
32
+ }
33
+
34
+ data = request path: PATH, body: body
35
+
36
+ data.fetch('ServiceTypes', []).filter do |body|
37
+ body['isActive']
38
+ end.map do |body|
39
+ id = body['serviceTypeID']
40
+ type = body['serviceType']
41
+ duration = body['duration']
42
+
43
+ new id: id, type: type, duration: duration
44
+ end
45
+ end
46
+ end
47
+ end
data/lib/vng/version.rb CHANGED
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Vng
4
- VERSION = "0.1.0"
2
+ VERSION = '0.1.14'
5
3
  end
@@ -0,0 +1,70 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo work orders.
5
+ class WorkOrder < Resource
6
+ PATH = '/api/v1/data/WorkOrders/'
7
+
8
+ attr_reader :id
9
+
10
+ def initialize(id:)
11
+ @id = id
12
+ end
13
+
14
+ def self.create(lock_id:, client_id:, contact_id:, location_id:, duration:, summary:, line_items:)
15
+ body = {
16
+ method: '3',
17
+ serviceTypeID: '14', # only return items of serviceType 'Pet Grooming'
18
+ lockID: lock_id,
19
+ clientID: client_id,
20
+ contactID: contact_id,
21
+ locationID: location_id,
22
+ Fields: [
23
+ {fieldID: 200, fieldValue: summary},
24
+ {fieldID: 186, fieldValue: duration.to_i},
25
+ {fieldID: 201, optionID: '9537'} # label: Online Tentative
26
+ ],
27
+ Charges: charges_for(line_items)
28
+ }
29
+
30
+ data = request path: PATH, body: body
31
+
32
+ new id: data['WorkOrder']['objectID']
33
+ end
34
+
35
+ # TODO: This moves to "archived". actual cancelation is different
36
+ # def destroy
37
+ # body = {
38
+ # method: '8',
39
+ # objectID: id,
40
+ # }
41
+ #
42
+ # data = self.class.request path: PATH, body: body
43
+ # end
44
+
45
+ private
46
+
47
+ def self.charges_for(line_items)
48
+ line_items.map do |line_item|
49
+ {
50
+ priceItemID: line_item[:price_item_id],
51
+ taxID: line_item[:tax_id],
52
+ assetID: line_item[:asset_id],
53
+ Fields: charge_fields_for(line_item)
54
+ }
55
+ end
56
+ end
57
+
58
+ def self.charge_fields_for(line_item)
59
+ [
60
+ {fieldID: 9289, fieldValue: line_item[:description]},
61
+ {fieldID: 9287, fieldValue: line_item[:price]}, # Unit price
62
+ {fieldID: 8673, fieldValue: line_item[:price]}, # Price item
63
+ {fieldID: 813, fieldValue: line_item[:price]}, # Price
64
+ {fieldID: 9286, fieldValue: line_item[:price]}, # Subtotal
65
+ {fieldID: 9283, fieldValue: line_item[:price]}, # Total
66
+ {fieldID: 9288, fieldValue: 1}, # Qty
67
+ ]
68
+ end
69
+ end
70
+ end
data/lib/vng/zip.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'vng/resource'
2
+
3
+ module Vng
4
+ # Provides methods to interact with Vonigo ZIP codes.
5
+ class Zip < Resource
6
+ PATH = '/api/v1/resources/zips/'
7
+
8
+ attr_reader :zip, :state, :zone_name
9
+
10
+ def initialize(zip:, state:, zone_name:)
11
+ @zip = zip
12
+ @state = state
13
+ @zone_name = zone_name
14
+ end
15
+
16
+ # TODO: Needs pagination
17
+ def self.all
18
+ data = request path: PATH
19
+
20
+ data['Zips'].map do |body|
21
+ zip = body['zip']
22
+ state = body['state']
23
+ zone_name = body['zoneName']
24
+
25
+ new zip: zip, state: state, zone_name: zone_name
26
+ end
27
+ end
28
+ end
29
+ end
data/lib/vng.rb CHANGED
@@ -1,8 +1,23 @@
1
- # frozen_string_literal: true
1
+ require 'date'
2
+ require 'digest/md5'
3
+ require 'json'
4
+ require 'net/http'
5
+ require 'uri'
2
6
 
3
- require_relative "vng/version"
4
-
5
- module Vng
6
- class Error < StandardError; end
7
- # Your code goes here...
8
- end
7
+ require_relative 'vng/asset'
8
+ require_relative 'vng/availability'
9
+ require_relative 'vng/breed'
10
+ require_relative 'vng/case'
11
+ require_relative 'vng/config'
12
+ require_relative 'vng/connection_error'
13
+ require_relative 'vng/contact'
14
+ require_relative 'vng/franchise'
15
+ require_relative 'vng/lead'
16
+ require_relative 'vng/location'
17
+ require_relative 'vng/lock'
18
+ require_relative 'vng/price_item'
19
+ require_relative 'vng/security_token'
20
+ require_relative 'vng/service_type'
21
+ require_relative 'vng/version'
22
+ require_relative 'vng/work_order'
23
+ require_relative 'vng/zip'
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vng
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - claudiob
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-11-10 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2024-11-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: simplecov
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description: A Ruby client for the Vonigo API.
14
28
  email:
15
29
  - claudiob@users.noreply.github.com
@@ -23,12 +37,31 @@ files:
23
37
  - README.md
24
38
  - Rakefile
25
39
  - lib/vng.rb
40
+ - lib/vng/asset.rb
41
+ - lib/vng/availability.rb
42
+ - lib/vng/breed.rb
43
+ - lib/vng/case.rb
44
+ - lib/vng/config.rb
45
+ - lib/vng/configuration.rb
46
+ - lib/vng/connection_error.rb
47
+ - lib/vng/contact.rb
48
+ - lib/vng/error.rb
49
+ - lib/vng/franchise.rb
50
+ - lib/vng/lead.rb
51
+ - lib/vng/location.rb
52
+ - lib/vng/lock.rb
53
+ - lib/vng/price_item.rb
54
+ - lib/vng/resource.rb
55
+ - lib/vng/security_token.rb
56
+ - lib/vng/service_type.rb
26
57
  - lib/vng/version.rb
27
- homepage: https://github.com/HouseAccountEng/vng
58
+ - lib/vng/work_order.rb
59
+ - lib/vng/zip.rb
60
+ homepage: https://rubygems.org/gems/vng
28
61
  licenses:
29
62
  - MIT
30
63
  metadata:
31
- homepage_uri: https://github.com/HouseAccountEng/vng
64
+ homepage_uri: https://rubygems.org/gems/vng
32
65
  source_code_uri: https://github.com/HouseAccountEng/vng
33
66
  changelog_uri: https://github.com/HouseAccountEng/vng/blob/main/CHANGELOG.md
34
67
  post_install_message: