klarna-xmlrpc 0.2.0

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 (95) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +13 -0
  3. data/Gemfile +14 -0
  4. data/Guardfile +16 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.textile +139 -0
  7. data/Rakefile +22 -0
  8. data/TODO +30 -0
  9. data/examples/Gemfile +8 -0
  10. data/examples/config/initializer.rb +15 -0
  11. data/examples/console.rb +71 -0
  12. data/examples/public/images/klarna.png +0 -0
  13. data/examples/public/images/ruby.png +0 -0
  14. data/examples/views/_address.haml +22 -0
  15. data/examples/views/_articles.haml +21 -0
  16. data/examples/views/checkout_page_example.haml +2 -0
  17. data/examples/views/essential/add_transaction/_form.haml +32 -0
  18. data/examples/views/essential/add_transaction/result.haml +7 -0
  19. data/examples/views/essential/calculate_monthly_cost/_form.haml +16 -0
  20. data/examples/views/essential/calculate_monthly_cost/result.haml +7 -0
  21. data/examples/views/essential/get_addresses/_form.haml +9 -0
  22. data/examples/views/essential/get_addresses/result.haml +8 -0
  23. data/examples/views/index.haml +296 -0
  24. data/examples/views/layout.haml +48 -0
  25. data/examples/views/payment_terms_example.haml +102 -0
  26. data/examples/views/product_page_example.haml +2 -0
  27. data/examples/views/reservation/activate_reservation/_form.haml +54 -0
  28. data/examples/views/reservation/activate_reservation/result.haml +7 -0
  29. data/examples/views/reservation/cancel_reservation/_form.haml +8 -0
  30. data/examples/views/reservation/cancel_reservation/result.haml +7 -0
  31. data/examples/views/reservation/change_reservation/_form.haml +10 -0
  32. data/examples/views/reservation/change_reservation/result.haml +7 -0
  33. data/examples/views/reservation/reserve_amount/_form.haml +58 -0
  34. data/examples/views/reservation/reserve_amount/result.haml +7 -0
  35. data/examples/views/reservation/reserve_ocr_numbers/_form.haml +8 -0
  36. data/examples/views/reservation/reserve_ocr_numbers/result.haml +7 -0
  37. data/examples/views/reservation/split_reservation/_form.haml +14 -0
  38. data/examples/views/reservation/split_reservation/result.haml +7 -0
  39. data/examples/views/special/get_pclasses/_form.haml +8 -0
  40. data/examples/views/special/get_pclasses/result.haml +7 -0
  41. data/examples/views/special/invoice_address/_form.haml +8 -0
  42. data/examples/views/special/invoice_address/result.haml +7 -0
  43. data/examples/views/special/invoice_amount/_form.haml +8 -0
  44. data/examples/views/special/invoice_amount/result.haml +7 -0
  45. data/examples/views/special/is_invoice_paid/_form.haml +8 -0
  46. data/examples/views/special/is_invoice_paid/result.haml +7 -0
  47. data/examples/views/special/update_charge_amount/_form.haml +15 -0
  48. data/examples/views/special/update_charge_amount/result.haml +7 -0
  49. data/examples/views/special/update_goods_quantity/_form.haml +17 -0
  50. data/examples/views/special/update_goods_quantity/result.haml +7 -0
  51. data/examples/views/special/update_order_number/_form.haml +10 -0
  52. data/examples/views/special/update_order_number/result.haml +7 -0
  53. data/examples/views/useful/activate_invoice/_form.haml +9 -0
  54. data/examples/views/useful/activate_invoice/result.haml +7 -0
  55. data/examples/views/useful/credit_invoice/_form.haml +11 -0
  56. data/examples/views/useful/credit_invoice/result.haml +7 -0
  57. data/examples/views/useful/delete_invoice/_form.haml +9 -0
  58. data/examples/views/useful/delete_invoice/result.haml +7 -0
  59. data/examples/views/useful/email_invoice/_form.haml +9 -0
  60. data/examples/views/useful/email_invoice/result.haml +7 -0
  61. data/examples/views/useful/has_account/_form.haml +9 -0
  62. data/examples/views/useful/has_account/result.haml +7 -0
  63. data/examples/views/useful/return_amount/_form.haml +15 -0
  64. data/examples/views/useful/return_amount/result.haml +7 -0
  65. data/examples/views/useful/send_invoice/_form.haml +9 -0
  66. data/examples/views/useful/send_invoice/result.haml +7 -0
  67. data/examples/web.rb +349 -0
  68. data/klarna.gemspec +34 -0
  69. data/lib/klarna.rb +171 -0
  70. data/lib/klarna/api.rb +187 -0
  71. data/lib/klarna/api/client.rb +126 -0
  72. data/lib/klarna/api/constants.rb +647 -0
  73. data/lib/klarna/api/errors.rb +154 -0
  74. data/lib/klarna/api/methods.rb +16 -0
  75. data/lib/klarna/api/methods/cost_calculations.rb +134 -0
  76. data/lib/klarna/api/methods/invoicing.rb +304 -0
  77. data/lib/klarna/api/methods/reservation.rb +188 -0
  78. data/lib/klarna/api/methods/standard.rb +126 -0
  79. data/lib/klarna/version.rb +5 -0
  80. data/test/fixtures/api/companies.yml +97 -0
  81. data/test/fixtures/api/pclasses.yml +37 -0
  82. data/test/fixtures/api/persons.yml +144 -0
  83. data/test/fixtures/api/stores.yml +6 -0
  84. data/test/fixtures/klarna.yml +10 -0
  85. data/test/klarna/api/client_test.rb +288 -0
  86. data/test/klarna/api/errors_test.rb +34 -0
  87. data/test/klarna/api/methods/cost_calculations_test.rb +78 -0
  88. data/test/klarna/api/methods/invoicing_test.rb +409 -0
  89. data/test/klarna/api/methods/reservation_test.rb +66 -0
  90. data/test/klarna/api/methods/standard_test.rb +244 -0
  91. data/test/klarna/api_test.rb +184 -0
  92. data/test/klarna_test.rb +204 -0
  93. data/test/support/assertions_helper.rb +40 -0
  94. data/test/test_helper.rb +55 -0
  95. metadata +335 -0
@@ -0,0 +1,154 @@
1
+ # encoding: utf-8
2
+ require 'xmlrpc/client'
3
+
4
+ module Klarna
5
+ module API
6
+ module Errors
7
+
8
+ ERROR_CODES = {
9
+ 1103 => :estore_overrun,
10
+ 1104 => :estore_blacklisted,
11
+ 1999 => :misc_estore_error,
12
+ 2101 => :credit,
13
+ 2102 => :amount,
14
+ 2103 => :customer_credit_overrun,
15
+ 2104 => :blocked,
16
+ 2105 => :unpaid_bills,
17
+ 2106 => :customer_not_accepted,
18
+ 2107 => :customer_blacklisted,
19
+ 2201 => :pno,
20
+ 2202 => :invalid_pno,
21
+ 2203 => :pno_not_real_person,
22
+ 2204 => :dead,
23
+ 2205 => :under_aged,
24
+ 2206 => :customer_not_18,
25
+ 2301 => :no_such_person,
26
+ 2302 => :customer_missing,
27
+ 2303 => :no_such_customer,
28
+ 2304 => :unknown_customer,
29
+ 2305 => :bad_customer_password,
30
+ 2401 => :tno_not_found,
31
+ 2402 => :pin_error,
32
+ 2403 => :wrong_pin,
33
+ 2404 => :no_pin,
34
+ 2405 => :orgno,
35
+ 2999 => :misc_customer_error,
36
+ 3101 => :foreign_addr,
37
+ 3102 => :bad_addr,
38
+ 3103 => :bad_address,
39
+ 3104 => :postno,
40
+ 3105 => :bad_postno,
41
+ 3106 => :bad_name,
42
+ 3107 => :address,
43
+ 3108 => :no_address,
44
+ 3201 => :cellno,
45
+ 3202 => :telno,
46
+ 3203 => :email,
47
+ 3204 => :country,
48
+ 3205 => :city,
49
+ 3206 => :postno,
50
+ 3207 => :street,
51
+ 3208 => :client_ip,
52
+ 3209 => :proto_vsn,
53
+ 3210 => :goods_list,
54
+ 3211 => :artnos,
55
+ 3301 => :bad_name_and_address,
56
+ 3302 => :bad_last_name,
57
+ 3303 => :bad_first_name,
58
+ 3304 => :bad_first_name_and_last_name,
59
+ 3999 => :misc_submission_error,
60
+ 6101 => :orgno_pclass_not_allowed,
61
+ 6102 => :sum_low_for_pclass,
62
+ 6103 => :unknown_pclass,
63
+ 6104 => :not_annuity_pclass,
64
+ 6999 => :misc_pclass_error,
65
+ 7101 => :no_such_subscription,
66
+ 7102 => :not_unique_subscription_no,
67
+ 7103 => :terminated,
68
+ 7104 => :already_set,
69
+ 7105 => :need_email_addr,
70
+ 7999 => :misc_subscription_error,
71
+ 8101 => :unknown_invoice,
72
+ 8102 => :negative_invoice,
73
+ 8103 => :invoice_not_active,
74
+ 8104 => :invoice_bad_status,
75
+ 8105 => :invoice_is_passive,
76
+ 8106 => :invoice_is_archived,
77
+ 8107 => :invoice_is_suspect,
78
+ 8108 => :invoice_is_frozen,
79
+ 8109 => :invoice_is_pre_pay,
80
+ 8110 => :invoice_stale,
81
+ 8111 => :invoice_not_passive_or_frozen,
82
+ 8112 => :invoice_in_test_mode,
83
+ 8113 => :invoice_not_passive,
84
+ 8114 => :invno,
85
+ 8999 => :misc_invoice_error,
86
+ 9101 => :cno_already_in_use,
87
+ 9102 => :unknown_csid,
88
+ 9103 => :not_allowed_operation,
89
+ 9104 => :ip_from_wrong_country,
90
+ 9105 => :bad_type,
91
+ 9106 => :unknown_type,
92
+ 9107 => :bad_artnolist,
93
+ 9108 => :unknown_artno,
94
+ 9109 => :rno,
95
+ 9110 => :split,
96
+ 9111 => :bad_order_no,
97
+ 9112 => :bad_ocr,
98
+ 9113 => :unknown_estore,
99
+ 9114 => :invalid_estore_secret,
100
+ 9115 => :bad_module_vsn,
101
+ 9116 => :pno_encoding,
102
+ 9117 => :currency,
103
+ 9118 => :currency_country_pnoencoding,
104
+ 9119 => :timeout
105
+ }.freeze
106
+
107
+ class KlarnaStandardError < ::StandardError
108
+ def initialize(message)
109
+ ::Klarna.log message, :error
110
+ super(message)
111
+ end
112
+ end
113
+
114
+ class KlarnaArgumentError < ::ArgumentError
115
+ def initialize(message)
116
+ ::Klarna.log message, :error
117
+ super(message)
118
+ end
119
+ end
120
+
121
+ class KlarnaCredentialsError < KlarnaStandardError
122
+ end
123
+
124
+ class KlarnaServiceError < ::XMLRPC::FaultException
125
+ alias :error_code :faultCode
126
+ alias :error_message :faultString
127
+
128
+ def initialize(error_code, error_key)
129
+ localized_error_message = ::Klarna::API::Errors.error_message(error_key)
130
+ message = ::Klarna.mode == :test ? "#{error_key} (#{[error_code, ERROR_CODES[error_code]].compact.join(' - ')})" : localized_error_message
131
+ ::Klarna.log message, :error
132
+ super(error_code, message)
133
+ end
134
+
135
+ def to_h
136
+ {:error_code => self.error_code, :error_message => self.error_message}
137
+ end
138
+ end
139
+
140
+ class << self
141
+
142
+ # Lookup localized string value for a specified error/exception code or key.
143
+ #
144
+ def error_message(id_or_key)
145
+ key = id_or_key.is_a?(Fixnum) ? ERROR_CODES[id_or_key] : id_or_key
146
+ key
147
+ end
148
+ alias :localized_error_message :error_message
149
+
150
+ end
151
+
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+ require 'klarna/api/methods/standard'
3
+ require 'klarna/api/methods/invoicing'
4
+ require 'klarna/api/methods/reservation'
5
+ require 'klarna/api/methods/cost_calculations'
6
+
7
+ module Klarna
8
+ module API
9
+ module Methods
10
+ include ::Klarna::API::Methods::Standard
11
+ include ::Klarna::API::Methods::Invoicing
12
+ include ::Klarna::API::Methods::Reservation
13
+ include ::Klarna::API::Methods::CostCalculations
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,134 @@
1
+ # encoding: utf-8
2
+
3
+ module Klarna
4
+ module API
5
+ module Methods
6
+ module CostCalculations
7
+
8
+ # Purpose: Obtain pclass values from Klarna.
9
+ #
10
+ # == Note:
11
+ #
12
+ # This function is only to be used to obtain pclass values for stores ONE TIME ONLY.
13
+ # It is not allowed to use this function for continuous calculation of monthly costs or
14
+ # with every purchase in the checkout. The pclass values do NOT change.
15
+ #
16
+ def fetch_pclasses(currency_code)
17
+ # params = [
18
+ # self.store_id,
19
+ # currency_code,
20
+ # self.digest(currency_code)
21
+ # ]
22
+ # self.call(:fetch_pclasses, *params)
23
+ raise NotImplementedError
24
+ end
25
+
26
+ # Calculate monthly cost "in the most proper way".
27
+ #
28
+ def calculate_monthly_cost(sum, currency, pclass_id, flags = nil)
29
+ # # TODO: OpenStruct this
30
+ # pclasses = ::Klarna.store_pclasses[currency.to_s.underscore]
31
+ # month_count = pclasses[pclass_id][:months]
32
+ # monthly_fee = pclasses[pclass_id][:invoice_fee]
33
+ # start_fee = pclasses[pclass_id][:start_fee]
34
+ # rate = pclasses[pclass_id][:interest_rate]
35
+ # type = pclasses[pclass_id][:type] # QUESTION: Where do I get this from - not in API-call.
36
+ #
37
+ # # TODO: Call Klarna and ask where I get "type" from - not with pclasses.
38
+ # case type
39
+ # when ::Klarna::API::PClassFlags::ANNUITY
40
+ # self.periodic_cost(sum, month_count, monthly_fee, start_fee, rate, currency, flags)
41
+ # when ::Klarna::API::PClassFlags::DIVISOR
42
+ # self.monthly_cost(sum, month_count, monthly_fee, start_fee, rate, currency, flags)
43
+ # else
44
+ # raise InvalidArgumentError, "Invalid Klarna campaign/pclass type: #{type.inspect}"
45
+ # end
46
+ raise NotImplementedError
47
+ end
48
+
49
+ # Calculate the monthly cost for goods which can be paid for by part payment.
50
+ #
51
+ def periodic_cost(sum, month_count, monthly_fee, start_fee, rate, currency, flags = nil)
52
+ # daily_rate = self.calculate_daily_rate(rate)
53
+ # monthly_payment = self.calculate_monthly_payment(sum + start_fee, daily_rate, month_count)
54
+ # monthly_cost = monthly_payment + monthly_fee
55
+ # monthly_cost.round
56
+ raise NotImplementedError
57
+ end
58
+
59
+ # Calculate the monthly cost for account purchases.
60
+ #
61
+ def monthly_cost(sum, month_count, monthly_fee, start_fee, rate, currency, flags = nil)
62
+ # interest_cost = self.calculate_interest_cost(sum, rate)
63
+ # period_cost = (sum + interest_cost) / month_count
64
+ # flags ||= ::Klarna::API::MonthlyCostFlags::LIMIT
65
+ #
66
+ # # TODO: Remove this line - after debugging.
67
+ # ::Klarna.log [::Klarna::API::AVERAGE_INTEREST_PERIOD, calc_rate, interest_cost, period_cost].join(', ')
68
+ #
69
+ # monthly_cost = case flags
70
+ # when ::Klarna::API::MonthlyCostFlags::LIMIT
71
+ # period_cost
72
+ # when ::Klarna::API::MonthlyCostFlags::ACTUAL
73
+ # begin
74
+ # lowest_monthly_payment = ::Klarna::API::LOWEST_PAYMENT_BY_CURRENCY[currency]
75
+ # rescue
76
+ # raise ::Klarna::API::KlarnaStandardError,
77
+ # "No such currency: #{currency.inspect}. Valid currencies: SEK:1, NOK:2, EUR:3, DKK:4"
78
+ # end
79
+ # period_cost += monthly_fee
80
+ # (period_cost < lowest_monthly_payment) ? lowest_monthly_payment : period_cost
81
+ # else
82
+ # raise ::Klarna::API::KlarnaStandardError,
83
+ # "Invalid flag: #{flags.inspect}. Valid flags: LIMIT:0, ACTUAL:1"
84
+ # end
85
+ # ::Klarna.log_result("Calculation: monthly_cost = %s") do
86
+ # self.round_up(monthly_cost, currency)
87
+ # end
88
+ raise NotImplementedError
89
+ end
90
+
91
+ protected
92
+
93
+ def calculate_interest_cost(sum, rate)
94
+ # rate *= 100.0 if rate < 100.0
95
+ # calc_rate = (rate / 10000)
96
+ # (::Klarna::API::AVERAGE_INTEREST_PERIOD / ::Klarna::API::DAYS_IN_A_YEAR) * calc_rate * sum
97
+ raise NotImplementedError
98
+ end
99
+
100
+ def calculate_monthly_payment(sum, daily_rate, month_count)
101
+ # sum = sum.to_double
102
+ # total_dates = (month_count - 1) * 30
103
+ # denominator = self.get_denominator(dailyrate, total_dates)
104
+ # total_dates += 60
105
+ # (dailyrate ** totdates) * sum / denominator
106
+ raise NotImplementedError
107
+ end
108
+
109
+ def calculate_daily_rate(rate)
110
+ # ((rate.to_decimal / 10000.0) + 1.0) ** (1 / ::Klarna::API::DAYS_IN_A_YEAR)
111
+ raise NotImplementedError
112
+ end
113
+
114
+ def get_denominator(daily_rate, total_dates)
115
+ # start_dates = 0.0
116
+ # sum = 1.0
117
+ # while total_dates > start_dates
118
+ # start_dates += 30
119
+ # sum += total_dates ** start_dates
120
+ # end
121
+ # sum
122
+ raise NotImplementedError
123
+ end
124
+
125
+ def round_up(amount, currency)
126
+ # divisor = currency == ::Klarna::API::Currencies::EUR ? 10.0 : 100.0
127
+ # (((divisor / 2) + amount) / divisor) #.round
128
+ raise NotImplementedError
129
+ end
130
+
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,304 @@
1
+ # encoding: utf-8
2
+
3
+ module Klarna
4
+ module API
5
+ module Methods
6
+ module Invoicing
7
+
8
+ # == TODO:
9
+ #
10
+ # * #total_credit_purchase_cost
11
+ #
12
+
13
+ # Create an invoice.
14
+ #
15
+ def add_invoice(store_user_id, order_id, articles, shipping_fee,
16
+ handling_fee, shipment_type, pno, first_name, last_name, address, client_ip,
17
+ currency, country, language, pno_encoding, pclass = nil, annual_salary = nil,
18
+ password = nil, ready_date = nil, comment = nil, rand_string = nil, new_password = nil, flags = nil)
19
+ shipment_type = ::Klarna::API.id_for(:shipment_type, shipment_type)
20
+ currency = ::Klarna::API.id_for(:currency, currency)
21
+ country = ::Klarna::API.id_for(:country, country)
22
+ language = ::Klarna::API.id_for(:language, language)
23
+ pno_encoding = ::Klarna::API.id_for(:pno_format, pno_encoding)
24
+ pclass = pclass ? ::Klarna::API.id_for(:pclass, pclass) : -1
25
+ flags = ::Klarna::API.parse_flags(:INVOICE, flags)
26
+ articles = Array.wrap(articles).compact
27
+
28
+ params = [
29
+ self.store_id,
30
+ store_user_id,
31
+ self.digest(articles.collect { |g| g[:goods][:title] }, :store_id => false),
32
+ order_id,
33
+ articles,
34
+ shipping_fee,
35
+ shipment_type,
36
+ handling_fee,
37
+ pno,
38
+ first_name,
39
+ last_name,
40
+ address,
41
+ password.to_s,
42
+ client_ip.to_s,
43
+ new_password.to_s,
44
+ flags.to_i,
45
+ comment.to_s,
46
+ ready_date.to_s,
47
+ rand_string.to_s,
48
+ currency,
49
+ country,
50
+ language,
51
+ pno_encoding,
52
+ pclass,
53
+ annual_salary.to_i
54
+ ]
55
+
56
+ self.call(:add_invoice, *params)
57
+ end
58
+ alias :add_transaction :add_invoice
59
+
60
+ # Activate a passive invoice - optionally only partly.
61
+ #
62
+ # == Note:
63
+ #
64
+ # This function call cannot activate an invoice created in test mode. It is however possible
65
+ # to manually activate such an invoice.
66
+ #
67
+ # When partially activating an invoice only the articles and quantities specified by
68
+ # you will be activated. A passive invoice is created containing the articles on the
69
+ # original invoice not activated.
70
+ #
71
+ def activate_invoice(invoice_no, articles = nil)
72
+ # TODO: Parse/Validate invoice_no as :integer
73
+ # TODO: Parse/Valdiate articles as array of articles
74
+ articles = Array.wrap(articles).compact
75
+
76
+ params = [
77
+ self.store_id,
78
+ invoice_no
79
+ ]
80
+
81
+ # Only partly?
82
+ if articles.present?
83
+ params << articles
84
+ params << self.digest(invoice_no, articles.collect { |a| [a[:goods][:artno], a[:qty]].join(':') }.join(':'))
85
+ method = :activate_part
86
+ else
87
+ params << self.digest(invoice_no)
88
+ method = :activate_invoice
89
+ end
90
+
91
+ self.call(method, *params)
92
+ end
93
+ alias :activate_part :activate_invoice
94
+
95
+ # Delete a passive invoice.
96
+ #
97
+ def delete_invoice(invoice_no)
98
+ # TODO: Parse/Validate invoice_no as :integer
99
+
100
+ params = [
101
+ self.store_id,
102
+ invoice_no,
103
+ self.digest(invoice_no)
104
+ ]
105
+
106
+ self.call(:delete_invoice, *params)
107
+ end
108
+
109
+ # Give discounts for invoices.
110
+ #
111
+ def return_amount(invoice_no, amount, vat)
112
+ params = [
113
+ self.store_id,
114
+ invoice_no,
115
+ amount,
116
+ vat,
117
+ self.digest(invoice_no)
118
+ ]
119
+ self.call(:return_amount, *params) # raise NotImplementedError
120
+ end
121
+
122
+ # Return a invoice - optionally only partly.
123
+ #
124
+ def credit_invoice(invoice_no, credit_id, articles = nil)
125
+ articles = Array.wrap(articles).compact
126
+
127
+ params = [
128
+ self.store_id,
129
+ invoice_no,
130
+ credit_id,
131
+ ]
132
+
133
+ if articles.present? # Only partly?
134
+ params << articles
135
+ params << self.digest(invoice_no, articles.collect { |a| [a[:goods][:artno], a[:qty]].join(':') }.join(':'))
136
+ method = :credit_part
137
+ else
138
+ params << self.digest(invoice_no)
139
+ method = :credit_invoice
140
+ end
141
+
142
+ self.call(method, *params)
143
+ end
144
+
145
+ # Send an active invoice to the customer via e-mail.
146
+ #
147
+ # == Note:
148
+ #
149
+ # Regular postal service is used if the customer lacks an e-mail address.
150
+ #
151
+ def email_invoice(invoice_no)
152
+ # TODO: Parse/Validate invoice_no as integer
153
+
154
+ params = [
155
+ self.store_id,
156
+ invoice_no,
157
+ self.digest(invoice_no)
158
+ ]
159
+
160
+ self.call(:email_invoice, *params)
161
+ end
162
+
163
+ # Request a postal send-out of an active invoice to a customer by Klarna.
164
+ #
165
+ def send_invoice(invoice_no)
166
+ # TODO: Parse/Validate invoice_no as integer
167
+
168
+ params = [
169
+ self.store_id,
170
+ invoice_no,
171
+ self.digest(invoice_no)
172
+ ]
173
+
174
+ self.call(:send_invoice, *params)
175
+ end
176
+
177
+ # Create quantity and article number (i.e. the +articles+ argument for the
178
+ # +activate_part+ and +credit_part+ function).
179
+ #
180
+ def make_article(quantity, article_no)
181
+ quantity = quantity.to_i
182
+ article_no = article_no.to_s
183
+ [quantity, article_no]
184
+ end
185
+
186
+ # Change the quantity of a specific item in a passive invoice.
187
+ #
188
+ def update_goods_quantity(invoice_no, article_no, new_quantity)
189
+ # TODO: Parse/Validate invoice_no as integer
190
+ # TODO: Parse/Validate article_no as string
191
+ # TODO: Parse/Validate new_quantity as integer
192
+
193
+ params = [
194
+ self.store_id,
195
+ self.digest(invoice_no, article_no, new_quantity, :store_id => false),
196
+ invoice_no,
197
+ article_no,
198
+ new_quantity
199
+ ]
200
+
201
+ self.call(:update_goods_qty, *params)
202
+ end
203
+
204
+ # Change the amount of a fee (for example the invoice fee) in a passive invoice.
205
+ #
206
+ def update_charge_amount(invoice_no, charge_type, new_amount)
207
+ # TODO: Parse/Validate invoice_no as integer
208
+ # TODO: Parse/Validate charge_type as integer/charge-type
209
+ # TODO: Parse/Validate new_amount as integer (or parse from decimal)
210
+
211
+ params = [
212
+ self.store_id,
213
+ self.digest(invoice_no, charge_type, new_amount),
214
+ invoice_no,
215
+ charge_type,
216
+ new_amount
217
+ ]
218
+
219
+ self.call(:update_charge_amount, *params)
220
+ end
221
+
222
+ # Change the store’s order number for a specific invoice.
223
+ #
224
+ def update_order_no(invoice_no, new_order_no)
225
+ # TODO: Parse/Validate invoice_no as integer
226
+ # TODO: Parse/Validate new_order_no as integer
227
+
228
+ params = [
229
+ self.store_id,
230
+ invoice_no,
231
+ self.digest(invoice_no, new_order_no),
232
+ new_order_no
233
+ ]
234
+
235
+ self.call(:update_orderno, *params)
236
+ end
237
+
238
+ # Retrieve the address for an invoice.
239
+ #
240
+ def invoice_address(invoice_no)
241
+ # TODO: Parse/Validate invoice_no as integer
242
+
243
+ params = [
244
+ self.store_id,
245
+ invoice_no,
246
+ self.digest(invoice_no)
247
+ ]
248
+
249
+ self.call(:invoice_address, *params).tap do |result|
250
+ result[5] = result[5].to_s.upcase
251
+ end
252
+ end
253
+ alias :get_invoice_address :invoice_address
254
+
255
+ # Retrieve the total amount of an invoice - optionally only partly.
256
+ #
257
+ def invoice_amount(invoice_no, articles = nil)
258
+ # TODO: Parse/Validate invoice_no as integer
259
+ articles = Array.wrap(articles).compact
260
+ artnos =
261
+ if articles.first.respond_to?(:key?) && articles.first.key?(:qty) && articles.first.key?(:artno)
262
+ articles
263
+ else
264
+ articles.collect { |a| {:artno => a[:goods][:artno], :qty => a[:qty]} }
265
+ end
266
+
267
+ params = [
268
+ self.store_id,
269
+ invoice_no
270
+ ]
271
+
272
+ # Only partly?
273
+ if articles.present?
274
+ params << artnos
275
+ params << self.digest(invoice_no, artnos.collect { |an| [an[:artno], an[:qty]].join(':') }.join(':'))
276
+ method = :invoice_part_amount
277
+ else
278
+ params << self.digest(invoice_no)
279
+ method = :invoice_amount
280
+ end
281
+
282
+ self.call(method, *params)
283
+ end
284
+ alias :get_invoice_amount :invoice_amount
285
+
286
+ # Check if invoice is paid.
287
+ #
288
+ def invoice_paid?(invoice_no)
289
+ # TODO: Parse/Validate invoice_no as numeric value (string)
290
+
291
+ params = [
292
+ invoice_no,
293
+ self.store_id,
294
+ self.digest(invoice_no)
295
+ ]
296
+
297
+ result = self.call(:is_invoice_paid, *params)
298
+ result == 'unpaid' ? false : true
299
+ end
300
+
301
+ end
302
+ end
303
+ end
304
+ end