badbill 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CONTRIBUTING.md +34 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +51 -0
- data/LICENSE +21 -0
- data/README.md +92 -0
- data/Rakefile +85 -0
- data/lib/badbill/base_resource.rb +118 -0
- data/lib/badbill/client.rb +32 -0
- data/lib/badbill/forward_methods.rb +30 -0
- data/lib/badbill/invoice.rb +106 -0
- data/lib/badbill/resource.rb +34 -0
- data/lib/badbill.rb +152 -0
- data/spec/badbill/base_resource_spec.rb +12 -0
- data/spec/badbill/client_spec.rb +98 -0
- data/spec/badbill/invoice_spec.rb +228 -0
- data/spec/badbill_spec.rb +89 -0
- data/spec/helper.rb +24 -0
- metadata +161 -0
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# How to contribute
|
2
|
+
|
3
|
+
This API client is implemented for my own use, so I will only work on the parts I need for myself.
|
4
|
+
Additional improvements, comments on code and implementation and bug fixes are very welcome.
|
5
|
+
|
6
|
+
## Report bugs, improvements or feature requests
|
7
|
+
|
8
|
+
* Submit a ticket for your issue, assuming one does not already exist.
|
9
|
+
* Clearly describe the issue including steps to reproduce when it is a bug.
|
10
|
+
* Make sure you fill in the earliest version that you know has the issue.
|
11
|
+
|
12
|
+
## Making Changes
|
13
|
+
|
14
|
+
* Fork the repository on GitHub.
|
15
|
+
* Create a topic branch from where you want to base your work.
|
16
|
+
* Make commits of logical units.
|
17
|
+
* Check for unnecessary whitespace with `git diff --check` before committing.
|
18
|
+
* Make sure your commit messages are in the proper format.
|
19
|
+
* Run _all_ the tests to assure nothing else was accidentally broken.
|
20
|
+
* Include tests for new features and bug fixes (if you don't know how to do that, feel free to ask, I will help).
|
21
|
+
|
22
|
+
|
23
|
+
## Submitting Changes
|
24
|
+
|
25
|
+
* Push your changes to a topic branch in your fork of the repository.
|
26
|
+
* Submit a pull request to the repository.
|
27
|
+
* Wait for my response.
|
28
|
+
|
29
|
+
# Contact
|
30
|
+
|
31
|
+
Feel free to contact me with any issues or questions.
|
32
|
+
|
33
|
+
* Mail: badboy@archlinux.us
|
34
|
+
* Twitter: [@badboy_](https://twitter.com/badboy_)
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
badbill (0.0.1)
|
5
|
+
faraday
|
6
|
+
faraday_middleware
|
7
|
+
hashie
|
8
|
+
yajl-ruby
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: https://rubygems.org/
|
12
|
+
specs:
|
13
|
+
addressable (2.3.2)
|
14
|
+
crack (0.3.1)
|
15
|
+
diff-lcs (1.1.3)
|
16
|
+
faraday (0.8.4)
|
17
|
+
multipart-post (~> 1.1)
|
18
|
+
faraday_middleware (0.8.8)
|
19
|
+
faraday (>= 0.7.4, < 0.9)
|
20
|
+
hashie (1.2.0)
|
21
|
+
multi_json (1.3.6)
|
22
|
+
multipart-post (1.1.5)
|
23
|
+
redcarpet (2.1.1)
|
24
|
+
rspec (2.11.0)
|
25
|
+
rspec-core (~> 2.11.0)
|
26
|
+
rspec-expectations (~> 2.11.0)
|
27
|
+
rspec-mocks (~> 2.11.0)
|
28
|
+
rspec-core (2.11.1)
|
29
|
+
rspec-expectations (2.11.3)
|
30
|
+
diff-lcs (~> 1.1.3)
|
31
|
+
rspec-mocks (2.11.3)
|
32
|
+
simplecov (0.6.4)
|
33
|
+
multi_json (~> 1.0)
|
34
|
+
simplecov-html (~> 0.5.3)
|
35
|
+
simplecov-html (0.5.3)
|
36
|
+
webmock (1.8.10)
|
37
|
+
addressable (>= 2.2.7)
|
38
|
+
crack (>= 0.1.7)
|
39
|
+
yajl-ruby (1.1.0)
|
40
|
+
yard (0.8.2.1)
|
41
|
+
|
42
|
+
PLATFORMS
|
43
|
+
ruby
|
44
|
+
|
45
|
+
DEPENDENCIES
|
46
|
+
badbill!
|
47
|
+
redcarpet
|
48
|
+
rspec
|
49
|
+
simplecov
|
50
|
+
webmock
|
51
|
+
yard
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2012 Jan-Erik Rediger <http://fnordig.de/about/>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person ob-
|
4
|
+
taining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without restric-
|
6
|
+
tion, including without limitation the rights to use, copy, modi-
|
7
|
+
fy, merge, publish, distribute, sublicense, and/or sell copies of
|
8
|
+
the Software, and to permit persons to whom the Software is fur-
|
9
|
+
nished to do so, subject to the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
16
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONIN-
|
17
|
+
FRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
19
|
+
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
BadBill - Billomat API Client
|
2
|
+
===================
|
3
|
+
|
4
|
+
Simple but working API client for the [Billomat API][apidocu].
|
5
|
+
|
6
|
+
See the [API documentation][apidocu] for full documentation of all resources.
|
7
|
+
|
8
|
+
## Features
|
9
|
+
|
10
|
+
* Fast and easy access to all resources the API provides
|
11
|
+
(not all resources are Ruby classes, yet)
|
12
|
+
* Full documentation.
|
13
|
+
* Test coverage as best as I can.
|
14
|
+
* Production-ready (it's for a job project).
|
15
|
+
|
16
|
+
## What's working right now
|
17
|
+
|
18
|
+
The basic BadBill class allows access to all resources. It includes no syntactic sugar to work with, instead it just returns the data as a hash. This works for basic usage.
|
19
|
+
|
20
|
+
The following resources are currently implemented as its own class:
|
21
|
+
|
22
|
+
* [clients](http://www.billomat.com/en/api/invoices/) (`BadBill::Client`)
|
23
|
+
* [invoices](http://www.billomat.com/en/api/invoices/) (`BadBill::Invoices`)
|
24
|
+
|
25
|
+
These are the two I need right now.
|
26
|
+
Implementing new resources is easy. Pull Requests for others are welcome.
|
27
|
+
|
28
|
+
## Requirements
|
29
|
+
|
30
|
+
* [yajl-ruby](https://github.com/brianmario/yajl-ruby)
|
31
|
+
* [faraday](https://github.com/technoweenie/faraday)
|
32
|
+
* [faraday_middleware](https://github.com/pengwynn/faraday_middleware)
|
33
|
+
* [hashie](https://github.com/intridea/hashie)
|
34
|
+
|
35
|
+
## Installation
|
36
|
+
|
37
|
+
BadBill is just a `gem install badbill` away. Get an API key for the Billomat API on your profile page.
|
38
|
+
|
39
|
+
## Examples
|
40
|
+
|
41
|
+
### Basic Usage
|
42
|
+
|
43
|
+
bill = BadBill.new "billo", "18e40e14"
|
44
|
+
# => #<BadBill:0x00000001319d30 ...>
|
45
|
+
bill.get 'settings'
|
46
|
+
# => {"settings"=>
|
47
|
+
# {"invoice_intro"=>"",
|
48
|
+
# "invoice_note"=>"",
|
49
|
+
# ...}}
|
50
|
+
bill.put 'settings', settings: { invoice_intro: "Intro" }
|
51
|
+
# => {"settings"=>
|
52
|
+
# {"invoice_intro"=>"Intro",
|
53
|
+
# "invoice_note"=>"",
|
54
|
+
# ...}}
|
55
|
+
|
56
|
+
### Using defined resource classes
|
57
|
+
|
58
|
+
BadBill.new "billo", "18e40e14"
|
59
|
+
|
60
|
+
BadBill::Invoice.all
|
61
|
+
# => [#<BadBill::Invoice:0x000000024caf98 @id="1" @data={...}>], ...]
|
62
|
+
|
63
|
+
invoice = BadBill::Invoice.find(1)
|
64
|
+
invoice.pdf
|
65
|
+
# => {"id"=>"1",
|
66
|
+
# "created"=>"2012-09-17T13:58:42+02:00",
|
67
|
+
# "invoice_id"=>"322791",
|
68
|
+
# "filename"=>"Invoice 322791.pdf",
|
69
|
+
# "mimetype"=>"application/pdf",
|
70
|
+
# "filesize"=>"90811",
|
71
|
+
# "base64file"=>"JVBERi0xLjM..."}
|
72
|
+
invoice.delete
|
73
|
+
# => true
|
74
|
+
|
75
|
+
BadBill::Invoice.create client_id: 1, date: "2012-09-18", note: "Thank you for your order", ...
|
76
|
+
|
77
|
+
## Documentation
|
78
|
+
|
79
|
+
Documentation is online at [rubydoc.info](http://rubydoc.info/github/badboy/badbill/master/frames).
|
80
|
+
|
81
|
+
Generate locale documentation with `rake doc` ([yard](http://yardoc.org/) required).
|
82
|
+
Required Parameters and possible values won't be documentated here. See the [API documentation][apidocu] for that.
|
83
|
+
|
84
|
+
## Contribute
|
85
|
+
|
86
|
+
See [CONTRIBUTING.md](/badboy/badbill/blob/master/CONTRIBUTING.md) for info.
|
87
|
+
|
88
|
+
## License
|
89
|
+
|
90
|
+
See [LICENSE](/badboy/badbill/blob/master/LICENSE) for info.
|
91
|
+
|
92
|
+
[apidocu]: http://www.billomat.com/en/api/
|
data/Rakefile
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
5
|
+
|
6
|
+
|
7
|
+
require 'yard'
|
8
|
+
YARD::Rake::YardocTask.new do |t|
|
9
|
+
t.files = ['lib/**/*.rb']
|
10
|
+
end
|
11
|
+
|
12
|
+
task :default => :spec
|
13
|
+
task :doc => :yard
|
14
|
+
|
15
|
+
## helper functions
|
16
|
+
|
17
|
+
def name
|
18
|
+
@name ||= Dir['*.gemspec'].first.split('.').first
|
19
|
+
end
|
20
|
+
|
21
|
+
def version
|
22
|
+
line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
|
23
|
+
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
|
24
|
+
end
|
25
|
+
|
26
|
+
def gemspec_file
|
27
|
+
"#{name}.gemspec"
|
28
|
+
end
|
29
|
+
|
30
|
+
def gem_file
|
31
|
+
"#{name}-#{version}.gem"
|
32
|
+
end
|
33
|
+
|
34
|
+
def replace_header(head, header_name)
|
35
|
+
head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "Open an irb session preloaded with this library"
|
39
|
+
task :console do
|
40
|
+
sh "irb -rubygems -r ./lib/#{name}.rb"
|
41
|
+
end
|
42
|
+
|
43
|
+
## release management tasks
|
44
|
+
|
45
|
+
desc "Commit, create tag v#{version} and build and push #{gem_file} to Rubygems"
|
46
|
+
task :release => :build do
|
47
|
+
sh "git commit --allow-empty -a -m 'Release #{version}'"
|
48
|
+
sh "git tag v#{version}"
|
49
|
+
sh "git push origin"
|
50
|
+
sh "git push origin v#{version}"
|
51
|
+
sh "gem push pkg/#{gem_file}"
|
52
|
+
end
|
53
|
+
|
54
|
+
desc "Build #{gem_file} into the pkg directory"
|
55
|
+
task :build => :gemspec do
|
56
|
+
FileUtils.mkdir_p 'pkg'
|
57
|
+
sh "gem build #{gemspec_file}"
|
58
|
+
sh "mv #{gem_file} pkg"
|
59
|
+
end
|
60
|
+
|
61
|
+
desc "Generate #{gemspec_file}"
|
62
|
+
task :gemspec do
|
63
|
+
# read spec file and split out manifest section
|
64
|
+
spec = File.read(gemspec_file)
|
65
|
+
head, manifest, tail = spec.split(" # = MANIFEST =\n")
|
66
|
+
|
67
|
+
# replace name version and date
|
68
|
+
replace_header(head, :name)
|
69
|
+
replace_header(head, :version)
|
70
|
+
|
71
|
+
# determine file list from git ls-files
|
72
|
+
files = `git ls-files`.
|
73
|
+
split("\n").
|
74
|
+
sort.
|
75
|
+
reject { |file| file =~ /^\./ }.
|
76
|
+
reject { |file| file =~ /^(doc|coverage|pkg)/ }.
|
77
|
+
map { |file| " ./#{file}" }.
|
78
|
+
join("\n")
|
79
|
+
|
80
|
+
# piece file back together and write
|
81
|
+
manifest = " s.files = %w[\n#{files}\n ]\n"
|
82
|
+
spec = [head, manifest, tail].join(" # = MANIFEST =\n")
|
83
|
+
File.open(gemspec_file, 'w') { |io| io.write(spec) }
|
84
|
+
puts "Updated #{gemspec_file}"
|
85
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class BadBill
|
4
|
+
class BaseResource
|
5
|
+
include BadBill::Resource
|
6
|
+
extend BadBill::Resource
|
7
|
+
|
8
|
+
include BadBill::ForwardMethods
|
9
|
+
|
10
|
+
# ID of the resource.
|
11
|
+
attr_reader :id
|
12
|
+
# All data in a Hash.
|
13
|
+
attr_accessor :data
|
14
|
+
|
15
|
+
# Public: Create new resource object.
|
16
|
+
#
|
17
|
+
# @param [String,Integer] id The ID of the resource.
|
18
|
+
# @param [Hashie::Mash] data Resource data.
|
19
|
+
#
|
20
|
+
# All resources have an ID and data. Methods are proxied to the data object.
|
21
|
+
#
|
22
|
+
# @return [Resource] A new resource.
|
23
|
+
def initialize id, data
|
24
|
+
@id = id
|
25
|
+
@data = data
|
26
|
+
end
|
27
|
+
|
28
|
+
# Get all resources of this type.
|
29
|
+
#
|
30
|
+
# @param [Hash] filter An Hash of filter parameters,
|
31
|
+
# see the online documentation for allowed values.
|
32
|
+
#
|
33
|
+
# @return [Array<Resource>] All found resources.
|
34
|
+
def self.all filter={}
|
35
|
+
all = get resource_name, filter
|
36
|
+
all.__send__(resource_name).__send__(resource_name_singular).map do |res|
|
37
|
+
new res.id, res
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Get the resource with the given ID.
|
42
|
+
#
|
43
|
+
# @param [String,Integer] id ID of the resource.
|
44
|
+
#
|
45
|
+
# @return [Resource] New resource with id and data set.
|
46
|
+
def self.find id
|
47
|
+
c = get resource_name, id
|
48
|
+
new id, c.__send__(resource_name_singular)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Create a new resource with the given parameters.
|
52
|
+
#
|
53
|
+
# @param [Hash] params A Hash-like object of data.
|
54
|
+
# See the online documentation for allowed values.
|
55
|
+
#
|
56
|
+
# @return [Resource] New resource with id and data set.
|
57
|
+
def self.create params
|
58
|
+
res = post(resource_name, {resource_name_singular => params})
|
59
|
+
res_data = res.__send__(resource_name_singular)
|
60
|
+
new res_data.id, res_data
|
61
|
+
end
|
62
|
+
|
63
|
+
# Save any changed data.
|
64
|
+
#
|
65
|
+
# @return [Boolean] True if successfull, false otherwise.
|
66
|
+
def save
|
67
|
+
@data.id = id
|
68
|
+
resp = put resource_name, id, {resource_name_singular => @data}
|
69
|
+
!resp
|
70
|
+
end
|
71
|
+
|
72
|
+
# Delete this resource.
|
73
|
+
#
|
74
|
+
# @return [Boolean] True if successfull, false otherwise.
|
75
|
+
def delete
|
76
|
+
# Hack: We can't call #delete here, because we ARE #delete
|
77
|
+
resp = call resource_name, id, nil, :delete
|
78
|
+
!resp
|
79
|
+
end
|
80
|
+
|
81
|
+
# Protect overriding of the ID.
|
82
|
+
#
|
83
|
+
# @raise [NoMethodError] because the ID may not be overwritten.
|
84
|
+
def id= *args
|
85
|
+
raise NoMethodError, "undefined method `id=' for '#{self.inspect.sub(/ .+/, '>')}`"
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def resource_name_singular
|
91
|
+
self.class.resource_name_singular
|
92
|
+
end
|
93
|
+
|
94
|
+
def resource_name
|
95
|
+
self.class.resource_name
|
96
|
+
end
|
97
|
+
|
98
|
+
# The singular resource name for use with the API.
|
99
|
+
#
|
100
|
+
# By default this is just the Class name, but it may be overridden.
|
101
|
+
# Make sure to overwrite resource_name, too.
|
102
|
+
#
|
103
|
+
# @return [String] The singular resource name.
|
104
|
+
def self.resource_name_singular
|
105
|
+
@resource_name_singular ||= self.name.split(/::/).last.downcase
|
106
|
+
end
|
107
|
+
|
108
|
+
# The resource name (plural) for use with the API.
|
109
|
+
#
|
110
|
+
# By default this is just the `resource_name_singular` with an appended 's'
|
111
|
+
# See #resource_name_singular
|
112
|
+
#
|
113
|
+
# @return [String] The resource name.
|
114
|
+
def self.resource_name
|
115
|
+
@resource_name ||= "#{resource_name_singular}s"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class BadBill
|
4
|
+
# The clients resource handles all clients.
|
5
|
+
#
|
6
|
+
# See http://www.billomat.com/en/api/clients/
|
7
|
+
class Client < BaseResource
|
8
|
+
attr_writer :myself
|
9
|
+
|
10
|
+
def initialize id, data
|
11
|
+
super
|
12
|
+
@myself = false
|
13
|
+
end
|
14
|
+
|
15
|
+
# Fetch information about yourself.
|
16
|
+
#
|
17
|
+
# @return [Client] A new resource.
|
18
|
+
def self.myself
|
19
|
+
c = get 'clients', 'myself'
|
20
|
+
client = new c.client.id, c.client
|
21
|
+
client.myself = true
|
22
|
+
client
|
23
|
+
end
|
24
|
+
|
25
|
+
# Indicates wether this resource is yourself or not.
|
26
|
+
#
|
27
|
+
# @return [Boolean] Wether or not this resource is yourself.
|
28
|
+
def myself?
|
29
|
+
@myself
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class BadBill
|
4
|
+
# Forward all methods to an underlying object called data.
|
5
|
+
#
|
6
|
+
# This acts like a proxy object.
|
7
|
+
module ForwardMethods
|
8
|
+
# Respond to method_missing to send down the line.
|
9
|
+
#
|
10
|
+
# As Hashie::Mash#method_missing does not respond with true to an
|
11
|
+
# assignment request, we only check for the method name without the equal sign.
|
12
|
+
def method_missing(method_name, *arguments, &block)
|
13
|
+
if data.respond_to?(method_name.to_s.sub(/=$/, ''))
|
14
|
+
data.send(method_name, *arguments, &block)
|
15
|
+
else
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Proxy respond_to? to the underlying object if needed.
|
21
|
+
#
|
22
|
+
# If playing with method_missing define respond_to? too.
|
23
|
+
# See http://robots.thoughtbot.com/post/28335346416/always-define-respond-to-missing-when-overriding
|
24
|
+
#
|
25
|
+
# @return [Boolean] True if the class itself or the proxied object responds to the given method.
|
26
|
+
def respond_to?(method_name, include_private = false)
|
27
|
+
super || data.respond_to?(method_name, include_private)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
class BadBill
|
6
|
+
# The resource handles all invoices.
|
7
|
+
#
|
8
|
+
# See http://www.billomat.com/en/api/invoices
|
9
|
+
class Invoice < BaseResource
|
10
|
+
# Get the PDF invoice.
|
11
|
+
#
|
12
|
+
#
|
13
|
+
# @return [Hashie::Mash] Hash containing the ID, filesize, base64file and
|
14
|
+
# other parameters.
|
15
|
+
# See http://www.billomat.com/en/api/invoices for
|
16
|
+
# all parameters.
|
17
|
+
def pdf
|
18
|
+
resp = get resource_name, "#{id}/pdf"
|
19
|
+
resp.pdf
|
20
|
+
end
|
21
|
+
|
22
|
+
# Closes a statement in the draft status (DRAFT). Here, the
|
23
|
+
# status of open (OPEN) or overdue (OVERDUE) is set and a PDF is
|
24
|
+
# generated and stored in the file system.
|
25
|
+
#
|
26
|
+
# @param [String,Integer] template_id ID of the Template used to create pdf,
|
27
|
+
# If not set, default template is used.
|
28
|
+
#
|
29
|
+
# @return [Boolean] Wether or not the call was successfull.
|
30
|
+
def complete template_id=nil
|
31
|
+
data = { complete: {} }
|
32
|
+
data[:complete] = { template_id: template_id } if template_id
|
33
|
+
resp = put resource_name, "#{id}/complete", data
|
34
|
+
|
35
|
+
!resp
|
36
|
+
end
|
37
|
+
|
38
|
+
# Cancel an invoice.
|
39
|
+
#
|
40
|
+
# @return [Boolean] Wether or not the call was successfull.
|
41
|
+
def cancel
|
42
|
+
resp = put resource_name, "#{id}/cancel"
|
43
|
+
!resp
|
44
|
+
end
|
45
|
+
|
46
|
+
# Sends an invoice by email.
|
47
|
+
#
|
48
|
+
# @param [String] to Recipient of the email.
|
49
|
+
# cc and bcc can be set in the `more` Hash.
|
50
|
+
# @param [String] from Sender email address.
|
51
|
+
# @param [String] subject Subject for the email.
|
52
|
+
# @param [String] body Body for the email.
|
53
|
+
# @param [Hash] more Hash-like object including more options.
|
54
|
+
# See online documentation for all allowed parameters.
|
55
|
+
#
|
56
|
+
# from, subject or body can be replaced by more.
|
57
|
+
#
|
58
|
+
# @return [Boolean] Wether or not the call was successfull.
|
59
|
+
def email to, from=nil, subject=nil, body=nil, more={}
|
60
|
+
data = { recipients: {} }
|
61
|
+
|
62
|
+
if more.empty? && from.kind_of?(Hash)
|
63
|
+
more = from
|
64
|
+
from = nil
|
65
|
+
end
|
66
|
+
|
67
|
+
if more.empty? && subject.kind_of?(Hash)
|
68
|
+
more = subject
|
69
|
+
subject = nil
|
70
|
+
end
|
71
|
+
|
72
|
+
if more.empty? && body.kind_of?(Hash)
|
73
|
+
more = body
|
74
|
+
body = nil
|
75
|
+
end
|
76
|
+
|
77
|
+
data[:from] = from if from
|
78
|
+
data[:subject] = subject if subject
|
79
|
+
data[:body] = body if body
|
80
|
+
|
81
|
+
data.merge! more
|
82
|
+
data[:recipients][:to] = to
|
83
|
+
|
84
|
+
resp = post resource_name, "#{id}/email", email: data
|
85
|
+
!resp
|
86
|
+
end
|
87
|
+
|
88
|
+
# Uploads a digital signature for a given invoice.
|
89
|
+
#
|
90
|
+
# The status of the invoice may not be DRAFT.
|
91
|
+
#
|
92
|
+
# @param [String,#read] file file name or a object responding to #read.
|
93
|
+
# This will be base64-encoded before send.
|
94
|
+
#
|
95
|
+
# @return [Boolean] false if status is DRAFT or another error occured. true if everything was successfull.
|
96
|
+
def upload_signature file
|
97
|
+
return false if data.status == 'DRAFT'
|
98
|
+
|
99
|
+
file = File.open(file, 'r') unless file.respond_to?(:read)
|
100
|
+
|
101
|
+
base64 = Base64.encode64 file.read
|
102
|
+
put resource_name, "#{id}/upload-signature", { signature: { base64file: base64 } }
|
103
|
+
true
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class BadBill
|
4
|
+
# Forward requests to the underlying connection object.
|
5
|
+
#
|
6
|
+
# This module is included in BadBill::BaseResource.
|
7
|
+
module Resource
|
8
|
+
# @param (see BadBill#get)
|
9
|
+
def get resource, id='', options=nil
|
10
|
+
call resource, id, options, :get
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param (see BadBill#post)
|
14
|
+
def post resource, id='', options=nil
|
15
|
+
call resource, id, options, :post
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param (see BadBill#put)
|
19
|
+
def put resource, id='', options=nil
|
20
|
+
call resource, id, options, :put
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param (see BadBill#delete)
|
24
|
+
def delete resource, id='', options=nil
|
25
|
+
call resource, id, options, :delete
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param (see BadBill#call)
|
29
|
+
def call resource, id='', options=nil, method=:get
|
30
|
+
raise BadBill::NoConnection, "No connection. Use BadBill.new first." if BadBill.connection.nil?
|
31
|
+
BadBill.connection.call resource, id, options, method
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|