collector-ruby 0.1.1

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.
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