collector-ruby 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +4 -0
  3. data/.gitignore +19 -0
  4. data/.rspec +2 -0
  5. data/Gemfile +7 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +170 -0
  8. data/Rakefile +6 -0
  9. data/collector-ruby.gemspec +33 -0
  10. data/collector_browser.rb +93 -0
  11. data/lib/collector.rb +25 -0
  12. data/lib/collector/activate_invoice_request.rb +45 -0
  13. data/lib/collector/address.rb +28 -0
  14. data/lib/collector/adjust_invoice_request.rb +28 -0
  15. data/lib/collector/article_list_item.rb +28 -0
  16. data/lib/collector/base_model.rb +116 -0
  17. data/lib/collector/cancel_invoice_request.rb +22 -0
  18. data/lib/collector/client.rb +133 -0
  19. data/lib/collector/get_address_request.rb +22 -0
  20. data/lib/collector/invoice_request.rb +63 -0
  21. data/lib/collector/invoice_response.rb +27 -0
  22. data/lib/collector/invoice_row.rb +18 -0
  23. data/lib/collector/replace_invoice_request.rb +31 -0
  24. data/lib/collector/user.rb +25 -0
  25. data/lib/collector/version.rb +3 -0
  26. data/spec/address_spec.rb +45 -0
  27. data/spec/article_list_item_spec.rb +33 -0
  28. data/spec/base_model_spec.rb +42 -0
  29. data/spec/helpers/sandbox_objects_helper.rb +83 -0
  30. data/spec/invoice_request_spec.rb +27 -0
  31. data/spec/invoice_response_spec.rb +29 -0
  32. data/spec/invoice_row_spec.rb +29 -0
  33. data/spec/operations/activate_invoice_spec.rb +82 -0
  34. data/spec/operations/add_invoice_spec.rb +130 -0
  35. data/spec/operations/adjust_invoice_spec.rb +54 -0
  36. data/spec/operations/cancel_invoice_spec.rb +33 -0
  37. data/spec/operations/get_address_spec.rb +34 -0
  38. data/spec/operations/replace_invoice_spec.rb +45 -0
  39. data/spec/spec_helper.rb +46 -0
  40. data/spec/user_spec.rb +40 -0
  41. data/usage_example.rb +39 -0
  42. metadata +253 -0
@@ -0,0 +1,27 @@
1
+ require 'representable/hash'
2
+
3
+ module Collector
4
+ class InvoiceResponse < BaseModel
5
+ swallow_unsupported_attributes # Accept unknown attributes returned from service
6
+
7
+ attributes_opt :available_reservation_amount, :correlation_id, :due_date,
8
+ :invoice_no, :invoice_status, :invoice_url,
9
+ :lowest_amount_to_pay, :payment_reference, :total_amount,
10
+ :new_invoice_no
11
+ end
12
+
13
+ class InvoiceResponseRepresenter < Representable::Decorator
14
+ include Representable::Hash
15
+
16
+ property :available_reservation_amount
17
+ property :correlation_id
18
+ property :due_date
19
+ property :invoice_no
20
+ property :invoice_status
21
+ property :invoice_url
22
+ property :lowest_amount_to_pay
23
+ property :new_invoice_no
24
+ property :payment_reference
25
+ property :total_amount
26
+ end
27
+ end
@@ -0,0 +1,18 @@
1
+ require 'representable/hash'
2
+
3
+ module Collector
4
+ class InvoiceRow < BaseModel
5
+ attributes :article_id, :description, :quantity, :unit_price, :vat
6
+ end
7
+
8
+ class InvoiceRowRepresenter < Representable::Decorator
9
+ include Representable::Hash
10
+
11
+ property :article_id, as: "ArticleId"
12
+ property :description, as: "Description"
13
+ property :quantity, as: "Quantity"
14
+ property :unit_price, as: "UnitPrice"
15
+ property :vat, as: "VAT"
16
+ end
17
+
18
+ end
@@ -0,0 +1,31 @@
1
+ require 'representable/hash'
2
+ require 'representable/hash/collection'
3
+
4
+ module Collector
5
+ class ReplaceInvoiceRequest < BaseModel
6
+
7
+ attributes :invoice_no, :invoice_rows,
8
+ :store_id, :country_code
9
+ attributes_opt :correlation_id
10
+ end
11
+
12
+ class ReplaceInvoiceRequestRepresenter < Representable::Decorator
13
+ include Representable::Hash
14
+
15
+ self.representation_wrap = "ReplaceInvoiceRequest"
16
+
17
+ property :correlation_id, as: "CorrelationId"
18
+ property :country_code, as: "CountryCode"
19
+ property :invoice_no, as: "InvoiceNo"
20
+ property :invoice_rows,
21
+ writer: lambda {|doc, args|
22
+ doc["InvoiceRows"] = InvoiceRowListRepresenter.new(invoice_rows).to_hash
23
+ },
24
+ reader: lambda {|doc, args|
25
+ rows = doc["InvoiceRows"]["InvoiceRow"]
26
+ self.invoice_rows = rows.map{|row| InvoiceRowRepresenter.new(InvoiceRow.new).from_hash(row) }
27
+ }
28
+ property :store_id, as: "StoreId"
29
+ end
30
+
31
+ end
@@ -0,0 +1,25 @@
1
+ require 'representable/hash'
2
+
3
+ module Collector
4
+ class User < BaseModel
5
+ swallow_unsupported_attributes
6
+
7
+ attributes :first_name, :last_name, :reg_no, :addresses
8
+ end
9
+
10
+ class UserRepresenter < Representable::Decorator
11
+ include Representable::Hash
12
+
13
+ property :first_name, as: :firstname
14
+ property :last_name, as: :lastname
15
+ property :reg_no
16
+ property :addresses,
17
+ reader: lambda {|doc, args|
18
+ addresses = doc[:addresses]
19
+ doc[:addresses] = [addresses] unless addresses.kind_of? Array
20
+ self.addresses = addresses.map{|key, adr|
21
+ key == "base_address" ? Address.new(adr) : nil
22
+ }.compact
23
+ }
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module Collector
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe Collector::Address do
4
+ let (:address_hash) {
5
+ { address1: "Storgatan",
6
+ city: "Njutånger",
7
+ country_code: "SE",
8
+ postal_code: "90737",
9
+ first_name: "Svenne",
10
+ last_name: "Banan"}
11
+ }
12
+ it "can be constructed from a hash" do
13
+ address = Collector::Address.new( address_hash )
14
+ address.address1.should eq "Storgatan"
15
+ address.city.should eq "Njutånger"
16
+ address.country_code.should eq "SE"
17
+ address.postal_code.should eq "90737"
18
+ address.first_name.should eq "Svenne"
19
+ address.last_name.should eq "Banan"
20
+ end
21
+
22
+ it "converts to hash and back without losing information" do
23
+ obj = Collector::Address.new( address_hash )
24
+ hash = Collector::AddressRepresenter.new(obj).to_hash
25
+ obj2 = Collector::Address.new
26
+ Collector::AddressRepresenter.new(obj2).from_hash(hash)
27
+ obj2.should eq obj
28
+ end
29
+
30
+ it "reports that all required attributes are present if they are" do
31
+ full_address = Collector::Address.new( address_hash )
32
+ full_address.should have_required_attributes
33
+ full_address.missing_attributes.should be_empty
34
+ end
35
+
36
+ it "reports missing attributes" do
37
+ incomplete = address_hash.dup
38
+ incomplete.delete(:address1)
39
+ incomplete.delete(:postal_code)
40
+ incomplete_address = Collector::Address.new( incomplete )
41
+ incomplete_address.should_not have_required_attributes
42
+ incomplete_address.missing_attributes.should eq [:address1, :postal_code]
43
+ end
44
+
45
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Collector::ArticleListItem do
4
+ let (:article_list_item_hash) {
5
+ { "article_id" => "123",
6
+ "description" => "A fine piece",
7
+ "quantity" => "23",
8
+ }
9
+ }
10
+ it "can be constructed from a hash" do
11
+ aticle = Collector::ArticleListItem.new( article_list_item_hash )
12
+ aticle.article_id.should eq "123"
13
+ aticle.description.should eq "A fine piece"
14
+ aticle.quantity.should eq "23"
15
+ end
16
+
17
+ it "can be constructed from an InvoiceRow instance" do
18
+ invoice_row = Collector::InvoiceRow.new(sandbox_invoice_row_hash)
19
+ article = Collector::ArticleListItem.new( invoice_row )
20
+ article.article_id.should eq "12"
21
+ article.description.should eq "A wonderful thing"
22
+ article.quantity.should eq "2"
23
+ end
24
+
25
+ it "converts to hash and back without losing information" do
26
+ obj = Collector::ArticleListItem.new( article_list_item_hash )
27
+ hash = Collector::ArticleListItemRepresenter.new(obj).to_hash
28
+ obj2 = Collector::ArticleListItem.new
29
+ Collector::ArticleListItemRepresenter.new(obj2).from_hash(hash)
30
+ obj2.should eq obj
31
+ end
32
+
33
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ class Model < Collector::BaseModel
4
+ attributes :required1, :required2
5
+ attributes_opt :optional1, :optional2
6
+ end
7
+
8
+ class NestedModel < Collector::BaseModel
9
+ attribute :required
10
+ attribute_opt :optional
11
+ end
12
+
13
+ class TolerantModel < Collector::BaseModel
14
+ swallow_unsupported_attributes
15
+ end
16
+
17
+ describe Collector::BaseModel do
18
+ it "raises an error when initialized with undeclared attributes" do
19
+ expect{ Model.new(required1: true,
20
+ required2: true,
21
+ unsupported: true)}.to raise_error ArgumentError
22
+ end
23
+ it "does not raise an error for undeclared attributes if set to swallow those" do
24
+ expect{ TolerantModel.new(humbug: true) }.not_to raise_error
25
+ end
26
+ it "reports presence of required attributes" do
27
+ obj = Model.new(required1: true, required2: true)
28
+ obj.should have_required_attributes
29
+ obj.missing_attributes.should be_empty
30
+ end
31
+ it "reports absence of required attributes" do
32
+ obj = Model.new(required1: true, optional1: true, optional2: true)
33
+ obj.should_not have_required_attributes
34
+ obj.missing_attributes.should eq [:required2]
35
+ end
36
+ it "reports absence of required attributes in nested objects" do
37
+ nested = NestedModel.new(optional: true)
38
+ obj = Model.new(required1: true, required2: nested, optional1: nested)
39
+ obj.should_not have_required_attributes
40
+ obj.missing_attributes.should eq [{required2: [:required]}, {optional1: [:required]}]
41
+ end
42
+ end
@@ -0,0 +1,83 @@
1
+ # encoding: utf-8
2
+
3
+ # Hashes
4
+ def sandbox_user_address_hash
5
+ { address1: "GATUADRESSAKT211",
6
+ address2: "Not required",
7
+ city: "UMEÅ",
8
+ country_code: "SE",
9
+ postal_code: "90737",
10
+ first_name: "FÖRNAMNAKT211",
11
+ last_name: "EFTERNAMNAKT211" }
12
+ end
13
+
14
+ def sandbox_user_address
15
+ Collector::Address.new(sandbox_user_address_hash )
16
+ end
17
+
18
+ def sandbox_invoice_row_hash(opts = {})
19
+ { article_id: "12",
20
+ description: "A wonderful thing",
21
+ quantity: "2",
22
+ unit_price: "12.0",
23
+ vat: "2.0"
24
+ }.merge(opts)
25
+ end
26
+
27
+ def sandbox_invoice_row(opts = {})
28
+ Collector::InvoiceRow.new(sandbox_invoice_row_hash(opts))
29
+ end
30
+
31
+ def sandbox_invoice_request_hash
32
+ { activation_option: 0,
33
+ country_code: "SE",
34
+ currency: "SEK",
35
+ delivery_address: sandbox_user_address,
36
+ invoice_address: sandbox_user_address,
37
+ invoice_delivery_method: 1,
38
+ invoice_rows: [sandbox_invoice_row],
39
+ invoice_type: 0,
40
+ order_date: DateTime.now,
41
+ reg_no: "1602079954",
42
+ store_id: "355" }
43
+ end
44
+
45
+ def sandbox_invoice_request
46
+ Collector::InvoiceRequest.new(sandbox_invoice_request_hash)
47
+ end
48
+
49
+ def full_address_hash
50
+ sandbox_user_address_hash.merge({
51
+ address2: "not required",
52
+ co_address: "not required",
53
+ cell_phone_number: "not required",
54
+ # company_name: "not required", # Can't test. Sandbox user is a "private customer"
55
+ email: "not@required.se",
56
+ phone_number: "not required"
57
+ })
58
+ end
59
+
60
+ def full_sandbox_address
61
+ Collector::Address.new(full_address_hash)
62
+ end
63
+
64
+ def full_invoice_request_hash
65
+ sandbox_invoice_request_hash.merge({
66
+ correlation_id: "test corr id",
67
+ cost_center: "not required",
68
+ credit_time: 10,
69
+ customer_no: "not required",
70
+ delivery_address: full_sandbox_address,
71
+ gender: 0,
72
+ invoice_address: full_sandbox_address,
73
+ order_no: "not required",
74
+ # product_code: "", # Can't test, defined by Collector, requires agreement
75
+ purchase_type: 0,
76
+ reference: "not required",
77
+ sales_person: "not required"
78
+ })
79
+ end
80
+
81
+ def full_sandbox_invoice_request
82
+ Collector::InvoiceRequest.new(full_invoice_request_hash)
83
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Collector::InvoiceRequest do
4
+ let (:invoice_req_hash) { sandbox_invoice_request_hash }
5
+
6
+ it "can be constructed from a hash" do
7
+ invoice_req = Collector::InvoiceRequest.new( invoice_req_hash )
8
+ invoice_req_hash.each do |key, val|
9
+ invoice_req.send(key).should eq val
10
+ end
11
+ end
12
+
13
+ it "compares objects" do
14
+ invoice_req1 = Collector::InvoiceRequest.new( invoice_req_hash )
15
+ invoice_req2 = Collector::InvoiceRequest.new( invoice_req_hash )
16
+ invoice_req1.should eq invoice_req2
17
+ end
18
+
19
+ it "converts to hash and back without losing information" do
20
+ obj = sandbox_invoice_request
21
+ hash = Collector::InvoiceRequestRepresenter.new(obj).to_hash
22
+ obj2 = Collector::InvoiceRequest.new
23
+ Collector::InvoiceRequestRepresenter.new(obj2).from_hash(hash)
24
+ obj2.should eq obj
25
+ end
26
+
27
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Collector::InvoiceResponse do
4
+ let (:raw_invoice_reponse_hash) {
5
+ {:add_invoice_response_v31=>{:available_reservation_amount=>"0",
6
+ :correlation_id=>nil,
7
+ :due_date=>nil,
8
+ :invoice_no=>"125423",
9
+ :invoice_status=>"1",
10
+ :invoice_url=>nil,
11
+ :lowest_amount_to_pay=>nil,
12
+ :payment_reference=>nil,
13
+ :total_amount=>nil,
14
+ :@xmlns=>"http://schemas.ecommerce.collector.se/v30/InvoiceService"}
15
+ }
16
+ }
17
+ it "can be constructed from a hash" do
18
+ inv_resp = Collector::InvoiceResponse.new( raw_invoice_reponse_hash[:add_invoice_response_v31] )
19
+ inv_resp.available_reservation_amount.should eq "0"
20
+ inv_resp.correlation_id.should eq nil
21
+ inv_resp.due_date.should eq nil
22
+ inv_resp.invoice_no.should eq "125423"
23
+ inv_resp.invoice_status.should eq "1"
24
+ inv_resp.lowest_amount_to_pay.should eq nil
25
+ inv_resp.payment_reference.should eq nil
26
+ inv_resp.total_amount.should eq nil
27
+ end
28
+
29
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Collector::InvoiceRow do
4
+ let (:invoice_row_hash) {
5
+ { "article_id" => "123",
6
+ "description" => "A fine piece",
7
+ "quantity" => "23",
8
+ "unit_price" => "12.50",
9
+ "vat" => "3.40"
10
+ }
11
+ }
12
+ it "can be constructed from a hash" do
13
+ invoice_row = Collector::InvoiceRow.new( invoice_row_hash )
14
+ invoice_row.article_id.should eq "123"
15
+ invoice_row.description.should eq "A fine piece"
16
+ invoice_row.quantity.should eq "23"
17
+ invoice_row.unit_price.should eq "12.50"
18
+ invoice_row.vat.should eq "3.40"
19
+ end
20
+
21
+ it "converts to hash and back without losing information" do
22
+ obj = Collector::InvoiceRow.new( invoice_row_hash )
23
+ hash = Collector::InvoiceRowRepresenter.new(obj).to_hash
24
+ obj2 = Collector::InvoiceRow.new
25
+ Collector::InvoiceRowRepresenter.new(obj2).from_hash(hash)
26
+ obj2.should eq obj
27
+ end
28
+
29
+ end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+ require 'vcr'
3
+
4
+ describe "Collector::Client" do
5
+ # TODO: Should be a shared example, but hard to make it fit
6
+ def spec_correct_response(response)
7
+ response.should be_kind_of Collector::InvoiceResponse
8
+ response.payment_reference.should_not be_nil
9
+ # TODO: What does this depend on?
10
+ response.lowest_amount_to_pay.should eq "50.00"
11
+ expected_due_date = (Time.now + 180*24*60*60).to_f
12
+ response.due_date.to_time.to_f.should be_within(24*60*60).of(expected_due_date)
13
+ response.invoice_url.should match %r(https://commerce.collector.se/testportal/PdfInvoice\?pnr=(\d+)\&invnr=#{@invoice_no})
14
+ end
15
+
16
+ before :each do
17
+ @client = collector_client
18
+ end
19
+
20
+ context "#activate_invoice" do
21
+
22
+ it "performs an ActivateInvoice request" do
23
+ VCR.use_cassette('activate_invoice') do
24
+ @invoice_no = add_original_invoice
25
+ response = @client.activate_invoice(invoice_no: @invoice_no,
26
+ store_id: "355",
27
+ country_code: "SE",
28
+ correlation_id: "testing_activate_invoice")
29
+ spec_correct_response(response)
30
+ response.correlation_id.should eq "testing_activate_invoice"
31
+ total_price = [product1, product2, product3].reduce(0){|memo, p| memo += (p.unit_price * p.quantity) }
32
+ response.total_amount.to_f.should eq total_price
33
+ end
34
+ end
35
+ end # #activate_invoice
36
+ context "#part_activate_invoice" do
37
+ def part_activate
38
+ @product = product2
39
+ @item = Collector::ArticleListItem.new(@product)
40
+ @response = @client.part_activate_invoice(
41
+ invoice_no: @invoice_no,
42
+ store_id: "355",
43
+ country_code: "SE",
44
+ correlation_id: "testing_part_activate_invoice",
45
+ article_list: [@item])
46
+ end
47
+ it "requires the article_list parameter" do
48
+ VCR.use_cassette('part_activate_invoice', :record => :new_episodes) { @invoice_no = add_original_invoice }
49
+ expect {
50
+ @client.part_activate_invoice(invoice_no: @invoice_no, store_id: "355", country_code: "SE")
51
+ }.to raise_error ArgumentError
52
+ end
53
+ it "performs a PartActivateInvoice request" do
54
+ VCR.use_cassette('part_activate_invoice', :record => :new_episodes) do
55
+ @invoice_no = add_original_invoice
56
+ part_activate
57
+ spec_correct_response(@response)
58
+ @response.correlation_id.should eq "testing_part_activate_invoice"
59
+ @response.total_amount.to_f.should eq (@product.unit_price * @item.quantity)
60
+ end
61
+ end
62
+ it "returns the new invoice number" do
63
+ VCR.use_cassette('part_activate_invoice', :record => :new_episodes) do
64
+ @invoice_no = add_original_invoice
65
+ part_activate
66
+ @response.new_invoice_no.should_not be_nil
67
+ end
68
+ VCR.use_cassette('activate_remainder_invoice') do
69
+ @invoice_no = @response.new_invoice_no
70
+ new_resp = @client.activate_invoice(invoice_no: @invoice_no,
71
+ store_id: "355",
72
+ country_code: "SE",
73
+ correlation_id: "testing_remainder_activate_invoice")
74
+ spec_correct_response(new_resp)
75
+ new_resp.correlation_id.should eq "testing_remainder_activate_invoice"
76
+ total_price = [product1, product2, product3].reduce(0){|memo, p| memo += (p.unit_price * p.quantity) }
77
+ activated_amount = @product.unit_price * @item.quantity
78
+ new_resp.total_amount.to_f.should eq total_price - activated_amount
79
+ end
80
+ end
81
+ end # #part_activate_invoice
82
+ end