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 +4 -4
- data/CHANGELOG.md +56 -0
- data/README.md +108 -2
- data/Rakefile +2 -4
- data/lib/vng/asset.rb +48 -0
- data/lib/vng/availability.rb +37 -0
- data/lib/vng/breed.rb +35 -0
- data/lib/vng/case.rb +45 -0
- data/lib/vng/config.rb +45 -0
- data/lib/vng/configuration.rb +51 -0
- data/lib/vng/connection_error.rb +5 -0
- data/lib/vng/contact.rb +52 -0
- data/lib/vng/error.rb +5 -0
- data/lib/vng/franchise.rb +43 -0
- data/lib/vng/lead.rb +49 -0
- data/lib/vng/location.rb +55 -0
- data/lib/vng/lock.rb +39 -0
- data/lib/vng/price_item.rb +45 -0
- data/lib/vng/resource.rb +40 -0
- data/lib/vng/security_token.rb +51 -0
- data/lib/vng/service_type.rb +47 -0
- data/lib/vng/version.rb +1 -3
- data/lib/vng/work_order.rb +70 -0
- data/lib/vng/zip.rb +29 -0
- data/lib/vng.rb +22 -7
- metadata +38 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de1ec03096db648933db7f9886eaa72b6c4af73e614e475bc0ad26126f35015e
|
4
|
+
data.tar.gz: 0adb9f129d030b78ec726f6a2a1cedd61ebc37c88a88df524aca23ff4d2583ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
1
|
+
Vng - a Ruby client for the Vonigo API
|
2
|
+
======================================================
|
2
3
|
|
3
|
-
|
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
|
+
[](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
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
|
data/lib/vng/contact.rb
ADDED
@@ -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,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
|
data/lib/vng/location.rb
ADDED
@@ -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
|
data/lib/vng/resource.rb
ADDED
@@ -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
@@ -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
|
-
|
1
|
+
require 'date'
|
2
|
+
require 'digest/md5'
|
3
|
+
require 'json'
|
4
|
+
require 'net/http'
|
5
|
+
require 'uri'
|
2
6
|
|
3
|
-
require_relative
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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.
|
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-
|
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
|
-
|
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://
|
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:
|