fortnox-api 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +3 -2
- data/.env.test +3 -0
- data/.rubocop.yml +32 -23
- data/.travis.yml +3 -2
- data/Guardfile +2 -2
- data/LICENSE.txt +165 -22
- data/README.md +56 -37
- data/fortnox-api.gemspec +9 -6
- data/lib/fortnox/api.rb +18 -2
- data/lib/fortnox/api/base.rb +5 -3
- data/lib/fortnox/api/environment_validation.rb +51 -6
- data/lib/fortnox/api/mappers.rb +49 -0
- data/lib/fortnox/api/mappers/base.rb +47 -0
- data/lib/fortnox/api/mappers/base/from_json.rb +78 -0
- data/lib/fortnox/api/mappers/base/to_json.rb +65 -0
- data/lib/fortnox/api/mappers/customer.rb +27 -0
- data/lib/fortnox/api/mappers/default_delivery_types.rb +13 -0
- data/lib/fortnox/api/mappers/default_templates.rb +15 -0
- data/lib/fortnox/api/mappers/edi_information.rb +22 -0
- data/lib/fortnox/api/mappers/email_information.rb +18 -0
- data/lib/fortnox/api/mappers/invoice.rb +27 -0
- data/lib/fortnox/api/mappers/invoice_row.rb +20 -0
- data/lib/fortnox/api/mappers/order.rb +23 -0
- data/lib/fortnox/api/mappers/order_row.rb +16 -0
- data/lib/fortnox/api/models.rb +2 -0
- data/lib/fortnox/api/models/base.rb +56 -13
- data/lib/fortnox/api/models/customer.rb +112 -101
- data/lib/fortnox/api/models/document_base.rb +189 -0
- data/lib/fortnox/api/models/invoice.rb +29 -195
- data/lib/fortnox/api/models/label.rb +17 -0
- data/lib/fortnox/api/models/order.rb +27 -0
- data/lib/fortnox/api/repositories.rb +2 -0
- data/lib/fortnox/api/repositories/base.rb +4 -5
- data/lib/fortnox/api/repositories/base/loaders.rb +22 -14
- data/lib/fortnox/api/repositories/base/savers.rb +30 -16
- data/lib/fortnox/api/repositories/customer.rb +3 -25
- data/lib/fortnox/api/repositories/invoice.rb +3 -22
- data/lib/fortnox/api/repositories/order.rb +16 -0
- data/lib/fortnox/api/request_handling.rb +3 -3
- data/lib/fortnox/api/types.rb +44 -0
- data/lib/fortnox/api/types/default_delivery_types.rb +20 -0
- data/lib/fortnox/api/types/default_templates.rb +23 -0
- data/lib/fortnox/api/types/defaulted.rb +11 -0
- data/lib/fortnox/api/types/document_row.rb +65 -0
- data/lib/fortnox/api/types/edi_information.rb +29 -0
- data/lib/fortnox/api/types/email_information.rb +26 -0
- data/lib/fortnox/api/types/enums.rb +75 -0
- data/lib/fortnox/api/types/invoice_row.rb +19 -0
- data/lib/fortnox/api/types/model.rb +40 -0
- data/lib/fortnox/api/types/nullable.rb +21 -0
- data/lib/fortnox/api/types/order_row.rb +16 -0
- data/lib/fortnox/api/types/required.rb +13 -0
- data/lib/fortnox/api/types/sized.rb +25 -0
- data/lib/fortnox/api/version.rb +1 -1
- data/spec/fortnox/api/base_spec.rb +85 -14
- data/spec/fortnox/api/mappers/base/from_json_spec.rb +70 -0
- data/spec/fortnox/api/mappers/base/to_json_spec.rb +76 -0
- data/spec/fortnox/api/mappers/base_spec.rb +156 -0
- data/spec/fortnox/api/mappers/contexts/json_conversion.rb +56 -0
- data/spec/fortnox/api/mappers/customer_spec.rb +25 -0
- data/spec/fortnox/api/mappers/default_delivery_types_spec.rb +12 -0
- data/spec/fortnox/api/mappers/edi_information_spec.rb +21 -0
- data/spec/fortnox/api/mappers/email_information_spec.rb +17 -0
- data/spec/fortnox/api/mappers/examples/mapper.rb +26 -0
- data/spec/fortnox/api/mappers/invoice_row_spec.rb +19 -0
- data/spec/fortnox/api/mappers/invoice_spec.rb +24 -0
- data/spec/fortnox/api/mappers/order_row_spec.rb +14 -0
- data/spec/fortnox/api/mappers/order_spec.rb +20 -0
- data/spec/fortnox/api/models/base_spec.rb +44 -22
- data/spec/fortnox/api/models/customer_spec.rb +9 -0
- data/spec/fortnox/api/models/examples/document_base.rb +13 -0
- data/spec/fortnox/api/models/examples/model.rb +13 -0
- data/spec/fortnox/api/models/invoice_spec.rb +7 -31
- data/spec/fortnox/api/models/order_spec.rb +13 -0
- data/spec/fortnox/api/repositories/customer_spec.rb +20 -76
- data/spec/fortnox/api/repositories/examples/all.rb +17 -0
- data/spec/fortnox/api/repositories/examples/find.rb +25 -0
- data/spec/fortnox/api/repositories/examples/only.rb +42 -0
- data/spec/fortnox/api/repositories/examples/save.rb +69 -0
- data/spec/fortnox/api/repositories/examples/save_with_nested_model.rb +32 -0
- data/spec/fortnox/api/repositories/examples/save_with_specially_named_attribute.rb +27 -0
- data/spec/fortnox/api/repositories/examples/search.rb +31 -0
- data/spec/fortnox/api/repositories/invoice_spec.rb +36 -5
- data/spec/fortnox/api/repositories/order_spec.rb +35 -0
- data/spec/fortnox/api/types/account_number_spec.rb +28 -0
- data/spec/fortnox/api/types/default_delivery_types_spec.rb +10 -0
- data/spec/fortnox/api/types/edi_information_spec.rb +13 -0
- data/spec/fortnox/api/types/email_information_spec.rb +13 -0
- data/spec/fortnox/api/types/email_spec.rb +29 -0
- data/spec/fortnox/api/types/enums_spec.rb +13 -0
- data/spec/fortnox/api/types/examples/document_row.rb +15 -0
- data/spec/fortnox/api/types/examples/enum.rb +48 -0
- data/spec/fortnox/api/types/examples/types.rb +9 -0
- data/spec/fortnox/api/types/house_work_types_spec.rb +60 -0
- data/spec/fortnox/api/types/invoice_row_spec.rb +9 -0
- data/spec/fortnox/api/types/model_spec.rb +56 -0
- data/spec/fortnox/api/types/nullable_spec.rb +57 -0
- data/spec/fortnox/api/types/order_row_spec.rb +13 -0
- data/spec/fortnox/api/types/required_spec.rb +42 -0
- data/spec/fortnox/api/types/sized_spec.rb +74 -0
- data/spec/fortnox/api_spec.rb +16 -15
- data/spec/spec_helper.rb +19 -9
- data/spec/support/helpers/dummy_class_helper.rb +19 -0
- data/spec/support/helpers/environment_helper.rb +7 -0
- data/spec/support/helpers/repository_helper.rb +8 -0
- data/spec/support/helpers/when_performing_helper.rb +5 -0
- data/spec/support/matchers.rb +1 -1
- data/spec/support/matchers/type.rb +17 -0
- data/spec/support/matchers/type/attribute_matcher.rb +39 -0
- data/spec/support/matchers/type/enum_matcher.rb +21 -0
- data/spec/support/matchers/type/have_account_number_matcher.rb +21 -0
- data/spec/support/matchers/type/have_country_code_matcher.rb +13 -0
- data/spec/support/matchers/type/have_currency_matcher.rb +7 -0
- data/spec/support/matchers/type/have_customer_type_matcher.rb +13 -0
- data/spec/support/matchers/type/have_default_delivery_type_matcher.rb +7 -0
- data/spec/support/matchers/type/have_discount_type_matcher.rb +7 -0
- data/spec/support/matchers/type/have_email_matcher.rb +22 -0
- data/spec/support/matchers/type/have_house_work_type_matcher.rb +7 -0
- data/spec/support/matchers/type/have_nullable_date_matcher.rb +58 -0
- data/spec/support/matchers/type/have_nullable_matcher.rb +52 -0
- data/spec/support/matchers/type/have_nullable_string_matcher.rb +49 -0
- data/spec/support/matchers/type/have_sized_float_matcher.rb +8 -0
- data/spec/support/matchers/type/have_sized_integer_matcher.rb +8 -0
- data/spec/support/matchers/type/have_sized_string_matcher.rb +35 -0
- data/spec/support/matchers/type/have_vat_type_matcher.rb +7 -0
- data/spec/support/matchers/type/numeric_matcher.rb +50 -0
- data/spec/support/matchers/type/require_attribute_matcher.rb +69 -0
- data/spec/support/matchers/type/type_matcher.rb +38 -0
- data/spec/vcr_cassettes/customers/all.yml +119 -9
- data/spec/vcr_cassettes/customers/find_id_1.yml +8 -9
- data/spec/vcr_cassettes/customers/find_new.yml +46 -0
- data/spec/vcr_cassettes/customers/save_new.yml +9 -11
- data/spec/vcr_cassettes/customers/save_old.yml +9 -12
- data/spec/vcr_cassettes/customers/save_with_specially_named_attribute.yml +45 -0
- data/spec/vcr_cassettes/customers/search_by_name.yml +66 -0
- data/spec/vcr_cassettes/customers/search_miss.yml +45 -0
- data/spec/vcr_cassettes/invoices/all.yml +104 -0
- data/spec/vcr_cassettes/invoices/filter_hit.yml +46 -0
- data/spec/vcr_cassettes/invoices/filter_invalid.yml +42 -0
- data/spec/vcr_cassettes/invoices/find_id_1.yml +47 -0
- data/spec/vcr_cassettes/invoices/find_new.yml +49 -0
- data/spec/vcr_cassettes/invoices/save_new.yml +48 -0
- data/spec/vcr_cassettes/invoices/save_old.yml +49 -0
- data/spec/vcr_cassettes/invoices/save_with_nested_model.yml +47 -0
- data/spec/vcr_cassettes/invoices/save_with_specially_named_attribute.yml +47 -0
- data/spec/vcr_cassettes/invoices/search_by_name.yml +48 -0
- data/spec/vcr_cassettes/invoices/search_miss.yml +45 -0
- data/spec/vcr_cassettes/orders/all.yml +144 -0
- data/spec/vcr_cassettes/orders/filter_hit.yml +48 -0
- data/spec/vcr_cassettes/orders/filter_invalid.yml +42 -0
- data/spec/vcr_cassettes/orders/find_id_1.yml +48 -0
- data/spec/vcr_cassettes/orders/find_new.yml +49 -0
- data/spec/vcr_cassettes/orders/house_work_type_babysitting.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_cleaning.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_construction.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_cooking.yml +43 -0
- data/spec/vcr_cassettes/orders/house_work_type_electricity.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_gardening.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_glassmetalwork.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_grounddrainagework.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_hvac.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_masonry.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_othercare.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_othercosts.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_paintingwallpapering.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_snowplowing.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_textileclothing.yml +47 -0
- data/spec/vcr_cassettes/orders/house_work_type_tutoring.yml +43 -0
- data/spec/vcr_cassettes/orders/save_new.yml +48 -0
- data/spec/vcr_cassettes/orders/save_old.yml +49 -0
- data/spec/vcr_cassettes/orders/save_with_nested_model.yml +47 -0
- data/spec/vcr_cassettes/orders/search_by_name.yml +47 -0
- data/spec/vcr_cassettes/orders/search_miss.yml +45 -0
- metadata +301 -71
- data/lib/fortnox/api/models/attributes/country_code.rb +0 -17
- data/lib/fortnox/api/models/attributes/currency.rb +0 -17
- data/lib/fortnox/api/models/edi_information.rb +0 -28
- data/lib/fortnox/api/models/email_information.rb +0 -25
- data/lib/fortnox/api/models/row.rb +0 -82
- data/lib/fortnox/api/repositories/base/json_convertion.rb +0 -68
- data/lib/fortnox/api/repositories/base/options.rb +0 -33
- data/lib/fortnox/api/validators.rb +0 -1
- data/lib/fortnox/api/validators/attributes/country_code.rb +0 -42
- data/lib/fortnox/api/validators/attributes/currency.rb +0 -38
- data/lib/fortnox/api/validators/base.rb +0 -70
- data/lib/fortnox/api/validators/constant.rb +0 -21
- data/lib/fortnox/api/validators/customer.rb +0 -29
- data/lib/fortnox/api/validators/edi_information.rb +0 -11
- data/lib/fortnox/api/validators/email_information.rb +0 -19
- data/lib/fortnox/api/validators/invoice.rb +0 -33
- data/lib/fortnox/api/validators/row.rb +0 -22
- data/spec/fortnox/api/models/attributes/country_code_spec.rb +0 -23
- data/spec/fortnox/api/models/attributes/currency_spec.rb +0 -23
- data/spec/fortnox/api/models/attributes/dummy_model_context.rb +0 -9
- data/spec/fortnox/api/models/row_spec.rb +0 -13
- data/spec/fortnox/api/repositories/context.rb +0 -10
- data/spec/fortnox/api/repositories/examples.rb +0 -16
- data/spec/fortnox/api/validators/attributes/country_code_spec.rb +0 -9
- data/spec/fortnox/api/validators/attributes/currency_spec.rb +0 -9
- data/spec/fortnox/api/validators/attributes/examples_for_validate.rb +0 -29
- data/spec/fortnox/api/validators/base_spec.rb +0 -61
- data/spec/fortnox/api/validators/constant_spec.rb +0 -12
- data/spec/fortnox/api/validators/context.rb +0 -102
- data/spec/fortnox/api/validators/customer_spec.rb +0 -31
- data/spec/fortnox/api/validators/edi_information_spec.rb +0 -18
- data/spec/fortnox/api/validators/email_information_spec.rb +0 -26
- data/spec/fortnox/api/validators/invoice_spec.rb +0 -36
- data/spec/fortnox/api/validators/row_spec.rb +0 -27
- data/spec/fortnox/api/validators/validator_examples.rb +0 -20
- data/spec/support/matchers/models.rb +0 -27
- data/spec/support/matchers/validators.rb +0 -36
@@ -0,0 +1,29 @@
|
|
1
|
+
require "fortnox/api/types"
|
2
|
+
|
3
|
+
module Fortnox
|
4
|
+
module API
|
5
|
+
module Types
|
6
|
+
class EDIInformation < Types::Model
|
7
|
+
STUB = {}.freeze
|
8
|
+
|
9
|
+
#EDIGlobalLocationNumber Invoice address GLN for EDI
|
10
|
+
attribute :edi_global_location_number, Types::Nullable::String
|
11
|
+
|
12
|
+
#EDIGlobalLocationNumberDelivery Delivery address GLN for EDI
|
13
|
+
attribute :edi_global_location_number_delivery, Types::Nullable::String
|
14
|
+
|
15
|
+
#EDIInvoiceExtra1 Extra EDI Information
|
16
|
+
attribute :edi_invoice_extra1, Types::Nullable::String
|
17
|
+
|
18
|
+
#EDIInvoiceExtra2 Extra EDI Information
|
19
|
+
attribute :edi_invoice_extra2, Types::Nullable::String
|
20
|
+
|
21
|
+
#EDIOurElectronicReference Our electronic reference for EDI
|
22
|
+
attribute :edi_our_electronic_reference, Types::Nullable::String
|
23
|
+
|
24
|
+
#EDIYourElectronicReference Your electronic reference for EDI
|
25
|
+
attribute :edi_your_electronic_reference, Types::Nullable::String
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "fortnox/api/types"
|
2
|
+
|
3
|
+
module Fortnox
|
4
|
+
module API
|
5
|
+
module Types
|
6
|
+
class EmailInformation < Types::Model
|
7
|
+
STUB = {}.freeze
|
8
|
+
|
9
|
+
#EmailAddressTo Customer e-mail address. Must be a valid e-mail address. 1024 characters
|
10
|
+
attribute :email_address_to, Types::Email
|
11
|
+
|
12
|
+
#EmailAddressCC Customer e-mail address – Carbon copy. Must be a valid e-mail address. 1024 characters
|
13
|
+
attribute :email_address_cc, Types::Email
|
14
|
+
|
15
|
+
#EmailAddressBCC Customer e-mail address – Blind carbon copy. Must be a valid e-mail address. 1024 characters
|
16
|
+
attribute :email_address_bcc, Types::Email
|
17
|
+
|
18
|
+
#EmailSubject Subject of the e-mail, 100 characters.
|
19
|
+
attribute :email_subject, Types::Sized::String[ 100 ]
|
20
|
+
|
21
|
+
#EmailBody Body of the e-mail, 20000 characters.
|
22
|
+
attribute :email_body, Types::Sized::String[ 20000 ]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Fortnox
|
2
|
+
module API
|
3
|
+
module Types
|
4
|
+
|
5
|
+
module EnumConstructors
|
6
|
+
def self.sized( size )
|
7
|
+
-> (value){ return nil if value == ''; value.to_s.upcase[0...size] unless value.nil? }
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.default
|
11
|
+
-> (value){ return nil if value == ''; value.to_s.upcase unless value.nil? }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
DiscountTypes = Types::Strict::String.enum(
|
16
|
+
'AMOUNT','PERCENT'
|
17
|
+
)
|
18
|
+
CURRENT_HOUSE_WORK_TYPES = [
|
19
|
+
'CONSTRUCTION','ELECTRICITY','GLASSMETALWORK','GROUNDDRAINAGEWORK',
|
20
|
+
'MASONRY','PAINTINGWALLPAPERING','HVAC','CLEANING','TEXTILECLOTHING',
|
21
|
+
'SNOWPLOWING','GARDENING','BABYSITTING','OTHERCARE', 'OTHERCOSTS'
|
22
|
+
].freeze
|
23
|
+
LEGACY_HOUSE_WORK_TYPES = [ 'COOKING', 'TUTORING' ].freeze
|
24
|
+
HouseWorkTypes = Types::Strict::String.enum(
|
25
|
+
*(CURRENT_HOUSE_WORK_TYPES + LEGACY_HOUSE_WORK_TYPES)
|
26
|
+
)
|
27
|
+
CountryCodes = Types::Strict::String.enum(
|
28
|
+
'AF','AX','AL','DZ','AS','AD','AO','AI','AQ','AG','AR','AM','AW','AU',
|
29
|
+
'AT','AZ','BS','BH','BD','BB','BY','BE','BZ','BJ','BM','BT','BO','BQ',
|
30
|
+
'BA','BW','BV','BR','IO','BN','BG','BF','BI','CV','KH','CM','CA','KY',
|
31
|
+
'CF','TD','CL','CN','CX','CC','CO','KM','CG','CD','CK','CR','CI','HR',
|
32
|
+
'CU','CW','CY','CZ','DK','DJ','DM','DO','EC','EG','SV','GQ','ER','EE',
|
33
|
+
'ET','FK','FO','FJ','FI','FR','GF','PF','TF','GA','GM','GE','DE','GH',
|
34
|
+
'GI','GR','GL','GD','GP','GU','GT','GG','GN','GW','GY','HT','HM','VA',
|
35
|
+
'HN','HK','HU','IS','IN','ID','IR','IQ','IE','IM','IL','IT','JM','JP',
|
36
|
+
'JE','JO','KZ','KE','KI','KP','KR','KW','KG','LA','LV','LB','LS','LR',
|
37
|
+
'LY','LI','LT','LU','MO','MK','MG','MW','MY','MV','ML','MT','MH','MQ',
|
38
|
+
'MR','MU','YT','MX','FM','MD','MC','MN','ME','MS','MA','MZ','MM','NA',
|
39
|
+
'NR','NP','NL','NC','NZ','NI','NE','NG','NU','NF','MP','NO','OM','PK',
|
40
|
+
'PW','PS','PA','PG','PY','PE','PH','PN','PL','PT','PR','QA','RE','RO',
|
41
|
+
'RU','RW','BL','SH','KN','LC','MF','PM','VC','WS','SM','ST','SA','SN',
|
42
|
+
'RS','SC','SL','SG','SX','SK','SI','SB','SO','ZA','GS','SS','ES','LK',
|
43
|
+
'SD','SR','SJ','SZ','SE','CH','SY','TW','TJ','TZ','TH','TL','TG','TK',
|
44
|
+
'TO','TT','TN','TR','TM','TC','TV','UG','UA','AE','GB','US','UM','UY',
|
45
|
+
'UZ','VU','VE','VN','VG','VI','WF','EH','YE','ZM','ZW'
|
46
|
+
)
|
47
|
+
Currencies = Types::Strict::String.enum(
|
48
|
+
'AED','AFN','ALL','AMD','ANG','AOA','ARS','AUD','AWG','AZN','BAM','BBD',
|
49
|
+
'BDT','BGN','BHD','BIF','BMD','BND','BOB','BOV','BRL','BSD','BTN','BWP',
|
50
|
+
'BYR','BZD','CAD','CDF','CHE','CHF','CHW','CLF','CLP','CNY','COP','COU',
|
51
|
+
'CRC','CUP','CVE','CZK','DJF','DKK','DOP','DZD','EGP','ERN','ETB','EUR',
|
52
|
+
'FJD','FKP','GBP','GEL','GHS','GIP','GMD','GNF','GTQ','GYD','HKD','HNL',
|
53
|
+
'HRK','HTG','HUF','IDR','ILS','INR','IQD','IRR','ISK','JMD','JOD','JPY',
|
54
|
+
'KES','KGS','KHR','KUR','KMF','KPW','KRW','KWD','KYD','KZT','LAK','LBP',
|
55
|
+
'LKR','LRD','LSL','LYD','MAD','MDL','MGA','MKD','MMK','MNT','MOP','MRO',
|
56
|
+
'MUR','MVR','MWK','MXN','MXV','MYR','MZN','NAD','NGN','NIO','NOK','NPR',
|
57
|
+
'NZD','OMR','PAB','PEN','PGK','PHP','PKR','PLN','PYG','QAR','RON','RSD',
|
58
|
+
'RUB','RWF','SAR','SBD','SCR','SDG','SEK','SGD','SHP','SLL','SOS','SRD',
|
59
|
+
'SSP','STD','SYP','SZL','THB','TJS','TMM','TND','TOP','TRY','TTD','TWD',
|
60
|
+
'TZS','UAH','UGX','USD','USN','USS','UYU','UZS','VEF','VND','VUV','WST',
|
61
|
+
'XAF','XAG','XAU','XBA','XBB','XBC','XBD','XCD','XDR','XFU','XOF','XPD',
|
62
|
+
'XPF','XPT','XTS','XXX','YER','ZAR','ZMK','ZWD'
|
63
|
+
)
|
64
|
+
CustomerTypes = Types::Strict::String.enum(
|
65
|
+
'PRIVATE', 'COMPANY'
|
66
|
+
)
|
67
|
+
VATTypes = Types::Strict::String.enum(
|
68
|
+
'SEVAT', 'SEREVERSEDVAT', 'EUREVERSEDVAT', 'EUVAT', 'EXPORT'
|
69
|
+
)
|
70
|
+
DefaultDeliveryTypeValues = Types::Strict::String.enum(
|
71
|
+
'PRINT', 'EMAIL', 'PRINTSERVICE'
|
72
|
+
)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "fortnox/api/types/document_row"
|
2
|
+
|
3
|
+
module Fortnox
|
4
|
+
module API
|
5
|
+
module Types
|
6
|
+
class InvoiceRow < Fortnox::API::Types::Model
|
7
|
+
STUB = {}.freeze
|
8
|
+
|
9
|
+
DocumentRow.ify self
|
10
|
+
|
11
|
+
#PriceExcludingVAT Price per unit excluding VAT.
|
12
|
+
attribute :price_excluding_vat, Types::Nullable::Float.with( private: true )
|
13
|
+
|
14
|
+
#TotalExcludingVAT Total amount for the row excluding VAT.
|
15
|
+
attribute :total_excluding_vat, Types::Nullable::Float.with( private: true )
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Fortnox
|
2
|
+
module API
|
3
|
+
module Types
|
4
|
+
class Model < Dry::Struct
|
5
|
+
constructor_type(:schema)
|
6
|
+
|
7
|
+
def initialize( input_attributes )
|
8
|
+
if (missing_key = first_missing_required_key( input_attributes ))
|
9
|
+
raise Fortnox::API::MissingAttributeError.new( "Missing attribute #{ missing_key.inspect } in attributes: #{ input_attributes }" )
|
10
|
+
end
|
11
|
+
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def missing_keys( attributes )
|
18
|
+
non_nil_attributes = attributes.select{ |_,value| !value.nil? }
|
19
|
+
|
20
|
+
attribute_keys = non_nil_attributes.keys
|
21
|
+
schema_keys = self.class.schema.keys
|
22
|
+
|
23
|
+
schema_keys - attribute_keys
|
24
|
+
end
|
25
|
+
|
26
|
+
def first_missing_required_key( attributes )
|
27
|
+
all_missing_keys = missing_keys( attributes )
|
28
|
+
missing_required = all_missing_keys.select do |name|
|
29
|
+
attribute = self.class.schema[ name ]
|
30
|
+
next unless attribute.respond_to? :options
|
31
|
+
attribute.options[:required]
|
32
|
+
end
|
33
|
+
|
34
|
+
missing_required.first
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Fortnox
|
4
|
+
module API
|
5
|
+
module Types
|
6
|
+
|
7
|
+
module Nullable
|
8
|
+
String = Types::Strict::String.optional.constructor{ |value| value.to_s unless value.nil? }
|
9
|
+
Float = Types::Strict::Float.optional.constructor{ |value| value.to_f unless value.nil? }
|
10
|
+
Integer = Types::Strict::Int.optional.constructor{ |value| value.to_i unless value.nil? }
|
11
|
+
Boolean = Types::Bool.optional.constructor{ |value| THE_TRUTH.fetch( value ){ false } }
|
12
|
+
# TODO: Improve parsing!
|
13
|
+
# In case date parsing fails, ArgumentError is thrown. Currently, it is rescued in Repository::Loaders.find.
|
14
|
+
# That method assumes that the exception is due to invalid argument to the find method, which is not the case
|
15
|
+
# if it is raised from here!
|
16
|
+
Date = Types::Date.optional.constructor{ |value| ::Date.parse( value.to_s ) unless value.nil? || value == '' }
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "fortnox/api/types/document_row"
|
2
|
+
|
3
|
+
module Fortnox
|
4
|
+
module API
|
5
|
+
module Types
|
6
|
+
class OrderRow < Fortnox::API::Types::Model
|
7
|
+
STUB = { ordered_quantity: 0 }.freeze
|
8
|
+
|
9
|
+
DocumentRow.ify self
|
10
|
+
|
11
|
+
#OrderedQuantity Ordered quantity
|
12
|
+
attribute :ordered_quantity, Types::Required::Float
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Fortnox
|
2
|
+
module API
|
3
|
+
module Types
|
4
|
+
|
5
|
+
module Required
|
6
|
+
String = Types::Strict::String.constructor{ |value| value.to_s unless value.nil? }.with( required: true )
|
7
|
+
Integer = Types::Strict::Int.constructor{ |value| value.to_i unless value.nil? }.with( required: true )
|
8
|
+
Float = Types::Strict::Float.constructor{ |value| value.to_f unless value.nil? }.with( required: true )
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Fortnox
|
2
|
+
module API
|
3
|
+
module Types
|
4
|
+
module Sized
|
5
|
+
module String
|
6
|
+
def self.[]( size )
|
7
|
+
Types::Strict::String.constrained( max_size: size ).optional.constructor{ |value| value.to_s unless value.nil? }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Integer
|
12
|
+
def self.[]( low, high )
|
13
|
+
Types::Strict::Int.constrained( gteq: low, lteq: high ).optional.constructor{ |value| value.to_i unless value.nil? }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module Float
|
18
|
+
def self.[]( low, high )
|
19
|
+
Types::Strict::Float.constrained( gteq: low, lteq: high ).optional.constructor{ |value| value.to_f unless value.nil? }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/fortnox/api/version.rb
CHANGED
@@ -2,20 +2,28 @@ require 'spec_helper'
|
|
2
2
|
require 'fortnox/api'
|
3
3
|
|
4
4
|
describe Fortnox::API::Base do
|
5
|
+
include Helpers::Environment
|
5
6
|
|
6
7
|
describe 'creation' do
|
8
|
+
before do
|
9
|
+
stub_environment('FORTNOX_API_BASE_URL' => nil,
|
10
|
+
'FORTNOX_API_CLIENT_SECRET' => nil,
|
11
|
+
'FORTNOX_API_ACCESS_TOKEN' => nil)
|
12
|
+
end
|
7
13
|
|
8
|
-
subject{ ->{
|
14
|
+
subject{ ->{ described_class.new() } }
|
9
15
|
|
10
16
|
context 'without FORTNOX_API_BASE_URL' do
|
11
|
-
before
|
17
|
+
before do
|
18
|
+
stub_environment('FORTNOX_API_BASE_URL' => nil)
|
19
|
+
end
|
12
20
|
|
13
21
|
it{ is_expected.to raise_error( ArgumentError, /base url/ ) }
|
14
22
|
end
|
15
23
|
|
16
24
|
context 'without FORTNOX_API_CLIENT_SECRET' do
|
17
25
|
before do
|
18
|
-
|
26
|
+
stub_environment('FORTNOX_API_BASE_URL' => 'xxx')
|
19
27
|
end
|
20
28
|
|
21
29
|
it{ is_expected.to raise_error( ArgumentError, /client secret/ ) }
|
@@ -23,8 +31,8 @@ describe Fortnox::API::Base do
|
|
23
31
|
|
24
32
|
context 'without FORTNOX_API_ACCESS_TOKEN' do
|
25
33
|
before do
|
26
|
-
|
27
|
-
|
34
|
+
stub_environment('FORTNOX_API_BASE_URL' => 'xxx',
|
35
|
+
'FORTNOX_API_CLIENT_SECRET' => 'xxx')
|
28
36
|
end
|
29
37
|
|
30
38
|
it{ is_expected.to raise_error( ArgumentError, /access token/ ) }
|
@@ -34,9 +42,11 @@ describe Fortnox::API::Base do
|
|
34
42
|
|
35
43
|
context 'making a request including the proper headers' do
|
36
44
|
before do
|
37
|
-
|
38
|
-
|
39
|
-
|
45
|
+
stub_environment(
|
46
|
+
'FORTNOX_API_BASE_URL' => 'http://api.fortnox.se/3',
|
47
|
+
'FORTNOX_API_CLIENT_SECRET' => 'P5K5vE3Kun',
|
48
|
+
'FORTNOX_API_ACCESS_TOKEN' => '3f08d038-f380-4893-94a0-a08f6e60e67a'
|
49
|
+
)
|
40
50
|
|
41
51
|
stub_request(
|
42
52
|
:get,
|
@@ -53,17 +63,78 @@ describe Fortnox::API::Base do
|
|
53
63
|
)
|
54
64
|
end
|
55
65
|
|
56
|
-
subject{
|
66
|
+
subject{ described_class.new.get( '/test', { body: '' }) }
|
57
67
|
|
58
68
|
it{ is_expected.to be_nil }
|
59
69
|
end
|
60
70
|
|
71
|
+
describe 'making requests with multiple access tokens' do
|
72
|
+
|
73
|
+
before do
|
74
|
+
stub_environment(
|
75
|
+
'FORTNOX_API_BASE_URL' => 'http://api.fortnox.se/3',
|
76
|
+
'FORTNOX_API_CLIENT_SECRET' => 'P5K5vE3Kun',
|
77
|
+
'FORTNOX_API_ACCESS_TOKEN' => '3f08d038-f380-4893-94a0-a08f6e60e67a,aaee8217-0bbd-2e16-441f-668931d582cd'
|
78
|
+
)
|
79
|
+
|
80
|
+
stub_request(
|
81
|
+
:get,
|
82
|
+
'http://api.fortnox.se/3/test',
|
83
|
+
).with(
|
84
|
+
headers: {
|
85
|
+
'Access-Token' => '3f08d038-f380-4893-94a0-a08f6e60e67a',
|
86
|
+
'Client-Secret' => 'P5K5vE3Kun',
|
87
|
+
'Content-Type' => 'application/json',
|
88
|
+
'Accept' => 'application/json',
|
89
|
+
}
|
90
|
+
).to_return(
|
91
|
+
status: 200,
|
92
|
+
body: '1'
|
93
|
+
)
|
94
|
+
|
95
|
+
stub_request(
|
96
|
+
:get,
|
97
|
+
'http://api.fortnox.se/3/test',
|
98
|
+
).with(
|
99
|
+
headers: {
|
100
|
+
'Access-Token' => 'aaee8217-0bbd-2e16-441f-668931d582cd',
|
101
|
+
'Client-Secret' => 'P5K5vE3Kun',
|
102
|
+
'Content-Type' => 'application/json',
|
103
|
+
'Accept' => 'application/json',
|
104
|
+
}
|
105
|
+
).to_return(
|
106
|
+
status: 200,
|
107
|
+
body: '2'
|
108
|
+
)
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'with subsequent requests on same object' do
|
112
|
+
let!(:response1){ api.get( '/test', body: '' ) }
|
113
|
+
let!(:response2){ api.get( '/test', body: '' ) }
|
114
|
+
let!(:response3){ api.get( '/test', body: '' ) }
|
115
|
+
|
116
|
+
let(:api){ described_class.new }
|
117
|
+
|
118
|
+
# rubocop:disable RSpec/MultipleExpectations
|
119
|
+
# All these checks must be run in same it-statement because
|
120
|
+
# of the random starting index.
|
121
|
+
it 'works' do
|
122
|
+
expect(response1).not_to eq( response2 )
|
123
|
+
expect(response1).to eq( response3 )
|
124
|
+
expect(response3).not_to eq( response2 )
|
125
|
+
end
|
126
|
+
# rubocop:enable RSpec/MultipleExpectations
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
61
130
|
context 'raising error from remote server' do
|
62
131
|
|
63
132
|
before do
|
64
|
-
|
65
|
-
|
66
|
-
|
133
|
+
stub_environment(
|
134
|
+
'FORTNOX_API_BASE_URL' => 'http://api.fortnox.se/3',
|
135
|
+
'FORTNOX_API_CLIENT_SECRET' => 'P5K5vE3Kun',
|
136
|
+
'FORTNOX_API_ACCESS_TOKEN' => '3f08d038-f380-4893-94a0-a08f6e60e67a'
|
137
|
+
)
|
67
138
|
|
68
139
|
stub_request(
|
69
140
|
:post,
|
@@ -75,14 +146,14 @@ describe Fortnox::API::Base do
|
|
75
146
|
)
|
76
147
|
end
|
77
148
|
|
78
|
-
subject{ ->{
|
149
|
+
subject{ ->{ described_class.new.post( '/test', { body: '' }) } }
|
79
150
|
|
80
151
|
it{ is_expected.to raise_error( Fortnox::API::RemoteServerError ) }
|
81
152
|
it{ is_expected.to raise_error( 'Räkenskapsår finns inte upplagt. För att kunna skapa en faktura krävs det att det finns ett räkenskapsår' ) }
|
82
153
|
|
83
154
|
context 'with debugging enabled' do
|
84
155
|
|
85
|
-
around
|
156
|
+
around do |example|
|
86
157
|
Fortnox::API.debugging = true
|
87
158
|
example.run
|
88
159
|
Fortnox::API.debugging = false
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fortnox/api/mappers/base/from_json'
|
3
|
+
require 'fortnox/api/mappers/contexts/json_conversion'
|
4
|
+
|
5
|
+
describe Fortnox::API::Mapper::FromJSON do
|
6
|
+
include_context 'JSON conversion'
|
7
|
+
|
8
|
+
before do
|
9
|
+
module Test
|
10
|
+
class BaseMapper
|
11
|
+
include Fortnox::API::Mapper::FromJSON
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
register_mapper( :categories, Test::CategoryMapper )
|
16
|
+
register_mapper( :designer, Test::ProductDesignerMapper )
|
17
|
+
end
|
18
|
+
|
19
|
+
let( :mapper ){ Test::ProductMapper.new }
|
20
|
+
|
21
|
+
describe 'wrapped_json_hash_to_entity_hash' do
|
22
|
+
let( :entity_hash ){ mapper.wrapped_json_hash_to_entity_hash( wrapped_json_hash ) }
|
23
|
+
let( :wrapped_json_hash ) do
|
24
|
+
{
|
25
|
+
'Product' => {
|
26
|
+
'@url': 'someurl@example.com',
|
27
|
+
'Name': 'Ford Mustang',
|
28
|
+
'VAT': 30000,
|
29
|
+
'Categories': [{ 'Name' => 'Cars', 'ID' => '1' }, { 'Name' => 'Fast cars', 'ID' => '2' }],
|
30
|
+
'Designer': { 'Name' => 'John Najjar', 'ID' => '23' }
|
31
|
+
}
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
specify 'converts keys without mapping correctly' do
|
36
|
+
expect( entity_hash[:name] ).to eq 'Ford Mustang'
|
37
|
+
end
|
38
|
+
|
39
|
+
specify 'converts keys with mapping correctly' do
|
40
|
+
expect( entity_hash[:vat] ).to eq 30000
|
41
|
+
end
|
42
|
+
|
43
|
+
specify 'converts keys starting with "@" correctly'
|
44
|
+
# do
|
45
|
+
# expect( entity_hash[:url] ).to eq 'someurl@example.com'
|
46
|
+
# end
|
47
|
+
|
48
|
+
context 'with nested models' do
|
49
|
+
let( :expected_nested_model_hash ) do
|
50
|
+
[{ name: 'Cars', id: '1' }, { name: 'Fast cars', id: '2' }]
|
51
|
+
end
|
52
|
+
|
53
|
+
specify 'are converted correctly' do
|
54
|
+
expect( entity_hash[:categories] ).to eq( expected_nested_model_hash )
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'with nested model' do
|
59
|
+
let( :expected_nested_model_hash ){ { name: 'John Najjar', id: '23' } }
|
60
|
+
|
61
|
+
specify 'is converted correctly' do
|
62
|
+
expect( entity_hash[:designer] ).to eq( expected_nested_model_hash )
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe 'wrapped_json_collection_to_entities_hash' do
|
68
|
+
it 'is tested'
|
69
|
+
end
|
70
|
+
end
|