fortnox-api 0.9.2 → 1.0.0.rc2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +120 -95
- data/{LICENSE.txt → LICENSE.md} +0 -0
- data/README.md +245 -248
- data/bin/fortnox-setup +211 -0
- data/bin/fortnox-update-env +38 -0
- data/fortnox.gemspec +29 -0
- data/lib/fortnox/auth/thread_local.rb +28 -0
- data/lib/fortnox/collection.rb +23 -0
- data/lib/fortnox/mappers/country_code.rb +34 -0
- data/lib/fortnox/mappers/date.rb +21 -0
- data/lib/fortnox/mappers/document_row.rb +12 -0
- data/lib/fortnox/mappers/edi_information.rb +15 -0
- data/lib/fortnox/mappers/email_information.rb +11 -0
- data/lib/fortnox/mappers/invoice_row.rb +14 -0
- data/lib/fortnox/mappers/label_references.rb +24 -0
- data/lib/fortnox/mappers/order_row.rb +9 -0
- data/lib/fortnox/mappers/struct.rb +71 -0
- data/lib/fortnox/mappers/struct_array.rb +31 -0
- data/lib/fortnox/resource.rb +131 -0
- data/lib/fortnox/resources/article.rb +160 -0
- data/lib/fortnox/resources/customer.rb +209 -0
- data/lib/fortnox/resources/document.rb +202 -0
- data/lib/fortnox/resources/invoice.rb +92 -0
- data/lib/fortnox/resources/label.rb +15 -0
- data/lib/fortnox/resources/order.rb +38 -0
- data/lib/fortnox/resources/project.rb +41 -0
- data/lib/fortnox/resources/terms_of_payment.rb +23 -0
- data/lib/fortnox/resources/unit.rb +26 -0
- data/lib/fortnox/struct.rb +24 -0
- data/lib/fortnox/structs/default_delivery_types.rb +16 -0
- data/lib/fortnox/structs/default_templates.rb +19 -0
- data/lib/fortnox/structs/document_row.rb +62 -0
- data/lib/fortnox/structs/edi_information.rb +25 -0
- data/lib/fortnox/structs/email_information.rb +22 -0
- data/lib/fortnox/structs/invoice_row.rb +13 -0
- data/lib/fortnox/structs/order_row.rb +10 -0
- data/lib/fortnox/types.rb +129 -0
- data/lib/fortnox/{api/version.rb → version.rb} +1 -3
- data/lib/fortnox.rb +111 -0
- metadata +55 -580
- data/.codeclimate.yml +0 -20
- data/.env.template +0 -7
- data/.env.test +0 -11
- data/.gitignore +0 -20
- data/.rspec +0 -2
- data/.rubocop.yml +0 -41
- data/.tool-versions +0 -1
- data/.travis.yml +0 -32
- data/CLAUDE.md +0 -79
- data/CONTRIBUTE.md +0 -38
- data/DEVELOPER_README.md +0 -69
- data/Gemfile +0 -6
- data/Guardfile +0 -16
- data/Rakefile +0 -140
- data/bin/console +0 -22
- data/bin/fortnox +0 -285
- data/bin/get_tokens +0 -79
- data/bin/renew_tokens +0 -28
- data/docs/gotchas.md +0 -146
- data/fortnox-api.gemspec +0 -50
- data/lib/fortnox/api/mappers/article.rb +0 -23
- data/lib/fortnox/api/mappers/base/canonical_name_sym.rb +0 -21
- data/lib/fortnox/api/mappers/base/from_json.rb +0 -91
- data/lib/fortnox/api/mappers/base/to_json.rb +0 -66
- data/lib/fortnox/api/mappers/base.rb +0 -30
- data/lib/fortnox/api/mappers/customer.rb +0 -27
- data/lib/fortnox/api/mappers/default_delivery_types.rb +0 -15
- data/lib/fortnox/api/mappers/default_templates.rb +0 -16
- data/lib/fortnox/api/mappers/edi_information.rb +0 -23
- data/lib/fortnox/api/mappers/email_information.rb +0 -19
- data/lib/fortnox/api/mappers/invoice.rb +0 -29
- data/lib/fortnox/api/mappers/invoice_row.rb +0 -25
- data/lib/fortnox/api/mappers/order.rb +0 -26
- data/lib/fortnox/api/mappers/order_row.rb +0 -23
- data/lib/fortnox/api/mappers/project.rb +0 -17
- data/lib/fortnox/api/mappers/terms_of_payment.rb +0 -17
- data/lib/fortnox/api/mappers/unit.rb +0 -17
- data/lib/fortnox/api/mappers/value/array.rb +0 -18
- data/lib/fortnox/api/mappers/value/country_string.rb +0 -24
- data/lib/fortnox/api/mappers/value/date.rb +0 -11
- data/lib/fortnox/api/mappers/value/hash.rb +0 -16
- data/lib/fortnox/api/mappers/value/identity.rb +0 -18
- data/lib/fortnox/api/mappers.rb +0 -21
- data/lib/fortnox/api/models/article.rb +0 -134
- data/lib/fortnox/api/models/base.rb +0 -128
- data/lib/fortnox/api/models/customer.rb +0 -210
- data/lib/fortnox/api/models/document.rb +0 -189
- data/lib/fortnox/api/models/invoice.rb +0 -87
- data/lib/fortnox/api/models/label.rb +0 -19
- data/lib/fortnox/api/models/order.rb +0 -27
- data/lib/fortnox/api/models/project.rb +0 -42
- data/lib/fortnox/api/models/terms_of_payment.rb +0 -28
- data/lib/fortnox/api/models/unit.rb +0 -24
- data/lib/fortnox/api/models.rb +0 -9
- data/lib/fortnox/api/repositories/article.rb +0 -17
- data/lib/fortnox/api/repositories/authentication.rb +0 -61
- data/lib/fortnox/api/repositories/base/loaders.rb +0 -64
- data/lib/fortnox/api/repositories/base/savers.rb +0 -57
- data/lib/fortnox/api/repositories/base.rb +0 -93
- data/lib/fortnox/api/repositories/customer.rb +0 -17
- data/lib/fortnox/api/repositories/invoice.rb +0 -17
- data/lib/fortnox/api/repositories/order.rb +0 -17
- data/lib/fortnox/api/repositories/project.rb +0 -17
- data/lib/fortnox/api/repositories/terms_of_payment.rb +0 -17
- data/lib/fortnox/api/repositories/unit.rb +0 -17
- data/lib/fortnox/api/repositories.rb +0 -10
- data/lib/fortnox/api/request_handling.rb +0 -46
- data/lib/fortnox/api/types/default_delivery_types.rb +0 -20
- data/lib/fortnox/api/types/default_templates.rb +0 -23
- data/lib/fortnox/api/types/defaulted.rb +0 -11
- data/lib/fortnox/api/types/document_row.rb +0 -64
- data/lib/fortnox/api/types/edi_information.rb +0 -29
- data/lib/fortnox/api/types/email_information.rb +0 -26
- data/lib/fortnox/api/types/enums.rb +0 -116
- data/lib/fortnox/api/types/invoice_row.rb +0 -19
- data/lib/fortnox/api/types/model.rb +0 -37
- data/lib/fortnox/api/types/nullable.rb +0 -25
- data/lib/fortnox/api/types/order_row.rb +0 -16
- data/lib/fortnox/api/types/required.rb +0 -13
- data/lib/fortnox/api/types/shim/country_string.rb +0 -10
- data/lib/fortnox/api/types/sized.rb +0 -33
- data/lib/fortnox/api/types.rb +0 -144
- data/lib/fortnox/api.rb +0 -62
- data/spec/fortnox/api/mappers/article_spec.rb +0 -17
- data/spec/fortnox/api/mappers/base/canonical_name_sym_spec.rb +0 -36
- data/spec/fortnox/api/mappers/base/from_json_spec.rb +0 -70
- data/spec/fortnox/api/mappers/base/to_json_spec.rb +0 -68
- data/spec/fortnox/api/mappers/base_spec.rb +0 -154
- data/spec/fortnox/api/mappers/contexts/json_conversion.rb +0 -62
- data/spec/fortnox/api/mappers/customer_spec.rb +0 -27
- data/spec/fortnox/api/mappers/default_delivery_types_spec.rb +0 -14
- data/spec/fortnox/api/mappers/edi_information_spec.rb +0 -23
- data/spec/fortnox/api/mappers/email_information_spec.rb +0 -19
- data/spec/fortnox/api/mappers/examples/mapper.rb +0 -34
- data/spec/fortnox/api/mappers/invoice_row_spec.rb +0 -24
- data/spec/fortnox/api/mappers/invoice_spec.rb +0 -27
- data/spec/fortnox/api/mappers/order_row_spec.rb +0 -21
- data/spec/fortnox/api/mappers/order_spec.rb +0 -23
- data/spec/fortnox/api/mappers/project_spec.rb +0 -12
- data/spec/fortnox/api/mappers/terms_of_payment_spec.rb +0 -16
- data/spec/fortnox/api/mappers/unit_spec.rb +0 -56
- data/spec/fortnox/api/models/article_spec.rb +0 -9
- data/spec/fortnox/api/models/base_spec.rb +0 -117
- data/spec/fortnox/api/models/customer_spec.rb +0 -9
- data/spec/fortnox/api/models/examples/document_base.rb +0 -15
- data/spec/fortnox/api/models/examples/model.rb +0 -22
- data/spec/fortnox/api/models/invoice_spec.rb +0 -11
- data/spec/fortnox/api/models/order_spec.rb +0 -12
- data/spec/fortnox/api/models/project_spec.rb +0 -9
- data/spec/fortnox/api/models/terms_of_payment_spec.rb +0 -9
- data/spec/fortnox/api/models/unit_spec.rb +0 -33
- data/spec/fortnox/api/repositories/article_spec.rb +0 -80
- data/spec/fortnox/api/repositories/authentication_spec.rb +0 -103
- data/spec/fortnox/api/repositories/base_spec.rb +0 -168
- data/spec/fortnox/api/repositories/customer_spec.rb +0 -119
- data/spec/fortnox/api/repositories/examples/all.rb +0 -17
- data/spec/fortnox/api/repositories/examples/find.rb +0 -84
- data/spec/fortnox/api/repositories/examples/only.rb +0 -34
- data/spec/fortnox/api/repositories/examples/save.rb +0 -76
- data/spec/fortnox/api/repositories/examples/save_with_nested_model.rb +0 -28
- data/spec/fortnox/api/repositories/examples/save_with_specially_named_attribute.rb +0 -26
- data/spec/fortnox/api/repositories/examples/search.rb +0 -39
- data/spec/fortnox/api/repositories/invoice_spec.rb +0 -297
- data/spec/fortnox/api/repositories/order_spec.rb +0 -53
- data/spec/fortnox/api/repositories/project_spec.rb +0 -36
- data/spec/fortnox/api/repositories/terms_of_payment_spec.rb +0 -34
- data/spec/fortnox/api/repositories/unit_spec.rb +0 -39
- data/spec/fortnox/api/types/account_number_spec.rb +0 -35
- data/spec/fortnox/api/types/country_code_spec.rb +0 -42
- data/spec/fortnox/api/types/country_spec.rb +0 -67
- data/spec/fortnox/api/types/default_delivery_types_spec.rb +0 -12
- data/spec/fortnox/api/types/edi_information_spec.rb +0 -15
- data/spec/fortnox/api/types/email_information_spec.rb +0 -15
- data/spec/fortnox/api/types/email_spec.rb +0 -56
- data/spec/fortnox/api/types/enums_spec.rb +0 -17
- data/spec/fortnox/api/types/examples/document_row.rb +0 -25
- data/spec/fortnox/api/types/examples/enum.rb +0 -55
- data/spec/fortnox/api/types/examples/types.rb +0 -11
- data/spec/fortnox/api/types/housework_types_spec.rb +0 -149
- data/spec/fortnox/api/types/invoice_row_spec.rb +0 -11
- data/spec/fortnox/api/types/model_spec.rb +0 -69
- data/spec/fortnox/api/types/nullable_spec.rb +0 -79
- data/spec/fortnox/api/types/order_row_spec.rb +0 -15
- data/spec/fortnox/api/types/required_spec.rb +0 -36
- data/spec/fortnox/api/types/sales_account_spec.rb +0 -57
- data/spec/fortnox/api/types/sized_spec.rb +0 -76
- data/spec/fortnox/api_spec.rb +0 -66
- data/spec/spec_helper.rb +0 -35
- data/spec/support/helpers/configuration_helper.rb +0 -39
- data/spec/support/helpers/repository_helper.rb +0 -10
- data/spec/support/helpers.rb +0 -3
- data/spec/support/matchers/type/attribute_matcher.rb +0 -40
- data/spec/support/matchers/type/enum_matcher.rb +0 -23
- data/spec/support/matchers/type/have_account_number_matcher.rb +0 -23
- data/spec/support/matchers/type/have_currency_matcher.rb +0 -9
- data/spec/support/matchers/type/have_customer_type_matcher.rb +0 -15
- data/spec/support/matchers/type/have_default_delivery_type_matcher.rb +0 -9
- data/spec/support/matchers/type/have_discount_type_matcher.rb +0 -9
- data/spec/support/matchers/type/have_email_matcher.rb +0 -24
- data/spec/support/matchers/type/have_housework_type_matcher.rb +0 -9
- data/spec/support/matchers/type/have_nullable_date_matcher.rb +0 -60
- data/spec/support/matchers/type/have_nullable_matcher.rb +0 -54
- data/spec/support/matchers/type/have_nullable_string_matcher.rb +0 -47
- data/spec/support/matchers/type/have_sized_float_matcher.rb +0 -10
- data/spec/support/matchers/type/have_sized_integer_matcher.rb +0 -10
- data/spec/support/matchers/type/have_sized_string_matcher.rb +0 -36
- data/spec/support/matchers/type/have_vat_type_matcher.rb +0 -9
- data/spec/support/matchers/type/numeric_matcher.rb +0 -52
- data/spec/support/matchers/type/require_attribute_matcher.rb +0 -68
- data/spec/support/matchers/type/type_matcher.rb +0 -40
- data/spec/support/matchers/type.rb +0 -19
- data/spec/support/matchers.rb +0 -3
- data/spec/support/vcr_setup.rb +0 -25
- data/spec/vcr_cassettes/articles/all.yml +0 -67
- data/spec/vcr_cassettes/articles/find_by_hash_failure.yml +0 -62
- data/spec/vcr_cassettes/articles/find_failure.yml +0 -62
- data/spec/vcr_cassettes/articles/find_id_1.yml +0 -63
- data/spec/vcr_cassettes/articles/find_new.yml +0 -63
- data/spec/vcr_cassettes/articles/limits/quantity_in_stock_min_value.yml +0 -63
- data/spec/vcr_cassettes/articles/limits/quantity_in_stock_rounding_positive_value.yml +0 -63
- data/spec/vcr_cassettes/articles/multi_param_find_by_hash.yml +0 -62
- data/spec/vcr_cassettes/articles/save_new.yml +0 -63
- data/spec/vcr_cassettes/articles/save_old.yml +0 -63
- data/spec/vcr_cassettes/articles/save_with_specially_named_attribute.yml +0 -63
- data/spec/vcr_cassettes/articles/search_by_name.yml +0 -65
- data/spec/vcr_cassettes/articles/search_miss.yml +0 -62
- data/spec/vcr_cassettes/articles/search_with_special_char.yml +0 -62
- data/spec/vcr_cassettes/articles/single_param_find_by_hash.yml +0 -62
- data/spec/vcr_cassettes/authentication/expired_token.yml +0 -54
- data/spec/vcr_cassettes/authentication/invalid_authorization.yml +0 -57
- data/spec/vcr_cassettes/authentication/invalid_refresh_token.yml +0 -58
- data/spec/vcr_cassettes/authentication/valid_request.yml +0 -63
- data/spec/vcr_cassettes/customers/all.yml +0 -69
- data/spec/vcr_cassettes/customers/find_by_hash_failure.yml +0 -62
- data/spec/vcr_cassettes/customers/find_failure.yml +0 -62
- data/spec/vcr_cassettes/customers/find_id_1.yml +0 -64
- data/spec/vcr_cassettes/customers/find_new.yml +0 -63
- data/spec/vcr_cassettes/customers/find_with_sales_account.yml +0 -63
- data/spec/vcr_cassettes/customers/multi_param_find_by_hash.yml +0 -63
- data/spec/vcr_cassettes/customers/save_new.yml +0 -63
- data/spec/vcr_cassettes/customers/save_new_with_country_code_SE.yml +0 -64
- data/spec/vcr_cassettes/customers/save_new_with_idn_email.yml +0 -67
- data/spec/vcr_cassettes/customers/save_new_with_sales_account.yml +0 -63
- data/spec/vcr_cassettes/customers/save_old.yml +0 -63
- data/spec/vcr_cassettes/customers/save_with_specially_named_attribute.yml +0 -63
- data/spec/vcr_cassettes/customers/search_by_name.yml +0 -64
- data/spec/vcr_cassettes/customers/search_miss.yml +0 -62
- data/spec/vcr_cassettes/customers/search_with_special_char.yml +0 -62
- data/spec/vcr_cassettes/customers/single_param_find_by_hash.yml +0 -64
- data/spec/vcr_cassettes/invoices/all.yml +0 -96
- data/spec/vcr_cassettes/invoices/filter_hit.yml +0 -64
- data/spec/vcr_cassettes/invoices/filter_invalid.yml +0 -60
- data/spec/vcr_cassettes/invoices/find_by_hash_failure.yml +0 -62
- data/spec/vcr_cassettes/invoices/find_failure.yml +0 -62
- data/spec/vcr_cassettes/invoices/find_id_1.yml +0 -65
- data/spec/vcr_cassettes/invoices/find_new.yml +0 -65
- data/spec/vcr_cassettes/invoices/multi_param_find_by_hash.yml +0 -63
- data/spec/vcr_cassettes/invoices/row_delivered_quantity_decimals.yml +0 -65
- data/spec/vcr_cassettes/invoices/row_delivered_quantity_decimals_round_up.yml +0 -65
- data/spec/vcr_cassettes/invoices/row_description_limit.yml +0 -65
- data/spec/vcr_cassettes/invoices/row_price_limit.yml +0 -65
- data/spec/vcr_cassettes/invoices/row_price_limit_round_up.yml +0 -65
- data/spec/vcr_cassettes/invoices/save_new.yml +0 -65
- data/spec/vcr_cassettes/invoices/save_new_with_comments.yml +0 -65
- data/spec/vcr_cassettes/invoices/save_new_with_country.yml +0 -65
- data/spec/vcr_cassettes/invoices/save_new_with_country_GB.yml +0 -66
- data/spec/vcr_cassettes/invoices/save_new_with_country_Norge.yml +0 -65
- data/spec/vcr_cassettes/invoices/save_new_with_country_Norway.yml +0 -65
- data/spec/vcr_cassettes/invoices/save_new_with_country_Sverige.yml +0 -65
- data/spec/vcr_cassettes/invoices/save_new_with_country_VA.yml +0 -66
- data/spec/vcr_cassettes/invoices/save_new_with_country_VI.yml +0 -66
- data/spec/vcr_cassettes/invoices/save_new_with_country_empty_string.yml +0 -65
- data/spec/vcr_cassettes/invoices/save_new_with_country_nil.yml +0 -65
- data/spec/vcr_cassettes/invoices/save_new_with_unsaved_parent.yml +0 -65
- data/spec/vcr_cassettes/invoices/save_old.yml +0 -65
- data/spec/vcr_cassettes/invoices/save_old_with_empty_comments.yml +0 -65
- data/spec/vcr_cassettes/invoices/save_old_with_empty_country.yml +0 -65
- data/spec/vcr_cassettes/invoices/save_old_with_nil_comments.yml +0 -65
- data/spec/vcr_cassettes/invoices/save_old_with_nil_country.yml +0 -65
- data/spec/vcr_cassettes/invoices/save_with_nested_model.yml +0 -65
- data/spec/vcr_cassettes/invoices/save_with_specially_named_attribute.yml +0 -65
- data/spec/vcr_cassettes/invoices/search_by_name.yml +0 -63
- data/spec/vcr_cassettes/invoices/search_miss.yml +0 -62
- data/spec/vcr_cassettes/invoices/search_with_special_char.yml +0 -62
- data/spec/vcr_cassettes/invoices/single_param_find_by_hash.yml +0 -64
- data/spec/vcr_cassettes/orders/all.yml +0 -69
- data/spec/vcr_cassettes/orders/filter_hit.yml +0 -64
- data/spec/vcr_cassettes/orders/filter_invalid.yml +0 -60
- data/spec/vcr_cassettes/orders/find_by_hash_failure.yml +0 -62
- data/spec/vcr_cassettes/orders/find_failure.yml +0 -62
- data/spec/vcr_cassettes/orders/find_id_1.yml +0 -67
- data/spec/vcr_cassettes/orders/find_new.yml +0 -65
- data/spec/vcr_cassettes/orders/housework_invalid_tax_reduction_type.yml +0 -61
- data/spec/vcr_cassettes/orders/housework_othercoses_invalid.yml +0 -61
- data/spec/vcr_cassettes/orders/housework_type_babysitting.yml +0 -65
- data/spec/vcr_cassettes/orders/housework_type_cleaning.yml +0 -65
- data/spec/vcr_cassettes/orders/housework_type_construction.yml +0 -65
- data/spec/vcr_cassettes/orders/housework_type_cooking.yml +0 -61
- data/spec/vcr_cassettes/orders/housework_type_electricity.yml +0 -65
- data/spec/vcr_cassettes/orders/housework_type_gardening.yml +0 -65
- data/spec/vcr_cassettes/orders/housework_type_glassmetalwork.yml +0 -65
- data/spec/vcr_cassettes/orders/housework_type_grounddrainagework.yml +0 -65
- data/spec/vcr_cassettes/orders/housework_type_hvac.yml +0 -65
- data/spec/vcr_cassettes/orders/housework_type_itservices.yml +0 -65
- data/spec/vcr_cassettes/orders/housework_type_majorappliancerepair.yml +0 -65
- data/spec/vcr_cassettes/orders/housework_type_masonry.yml +0 -65
- data/spec/vcr_cassettes/orders/housework_type_movingservices.yml +0 -65
- data/spec/vcr_cassettes/orders/housework_type_othercare.yml +0 -65
- data/spec/vcr_cassettes/orders/housework_type_othercosts.yml +0 -65
- data/spec/vcr_cassettes/orders/housework_type_paintingwallpapering.yml +0 -65
- data/spec/vcr_cassettes/orders/housework_type_snowplowing.yml +0 -65
- data/spec/vcr_cassettes/orders/housework_type_textileclothing.yml +0 -65
- data/spec/vcr_cassettes/orders/housework_type_tutoring.yml +0 -61
- data/spec/vcr_cassettes/orders/multi_param_find_by_hash.yml +0 -63
- data/spec/vcr_cassettes/orders/save_new.yml +0 -65
- data/spec/vcr_cassettes/orders/save_old.yml +0 -65
- data/spec/vcr_cassettes/orders/save_with_nested_model.yml +0 -65
- data/spec/vcr_cassettes/orders/search_by_name.yml +0 -63
- data/spec/vcr_cassettes/orders/search_miss.yml +0 -62
- data/spec/vcr_cassettes/orders/search_with_special_char.yml +0 -62
- data/spec/vcr_cassettes/orders/single_param_find_by_hash.yml +0 -64
- data/spec/vcr_cassettes/projects/all.yml +0 -64
- data/spec/vcr_cassettes/projects/find_by_hash_failure.yml +0 -62
- data/spec/vcr_cassettes/projects/find_failure.yml +0 -62
- data/spec/vcr_cassettes/projects/find_id_1.yml +0 -63
- data/spec/vcr_cassettes/projects/find_new.yml +0 -63
- data/spec/vcr_cassettes/projects/multi_param_find_by_hash.yml +0 -64
- data/spec/vcr_cassettes/projects/save_new.yml +0 -63
- data/spec/vcr_cassettes/projects/save_old.yml +0 -63
- data/spec/vcr_cassettes/projects/single_param_find_by_hash.yml +0 -63
- data/spec/vcr_cassettes/termsofpayments/all.yml +0 -68
- data/spec/vcr_cassettes/termsofpayments/find_failure.yml +0 -62
- data/spec/vcr_cassettes/termsofpayments/find_id_1.yml +0 -62
- data/spec/vcr_cassettes/termsofpayments/find_new.yml +0 -63
- data/spec/vcr_cassettes/termsofpayments/save_new.yml +0 -63
- data/spec/vcr_cassettes/termsofpayments/save_old.yml +0 -63
- data/spec/vcr_cassettes/units/all.yml +0 -64
- data/spec/vcr_cassettes/units/find_failure.yml +0 -62
- data/spec/vcr_cassettes/units/find_id_1.yml +0 -63
- data/spec/vcr_cassettes/units/find_new.yml +0 -63
- data/spec/vcr_cassettes/units/save_new.yml +0 -63
- data/spec/vcr_cassettes/units/save_old.yml +0 -63
- data/spec/vcr_cassettes/units/save_with_specially_named_attribute.yml +0 -63
data/README.md
CHANGED
|
@@ -1,153 +1,89 @@
|
|
|
1
|
-
#
|
|
1
|
+
# fortnox-api
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
PRs of your own 😃
|
|
3
|
+
Ruby gem for Fortnox's version 3 REST API, built on
|
|
4
|
+
[rest-easy](https://github.com/accodeing/rest-easy). If you need to integrate an existing or new Ruby
|
|
5
|
+
or Rails app against Fortnox this gem will save you a lot of time.
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
Feel free to repay the community with some nice PRs of your own.
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
[](https://app.travis-ci.com/github/accodeing/fortnox-api)
|
|
9
|
+
## Supported resources
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
Article, Customer, Invoice, Label, Order, Project, TermsOfPayment, Unit
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
[
|
|
17
|
-
[](https://codeclimate.com/github/accodeing/fortnox-api/test_coverage)
|
|
13
|
+
Adding more resources is quick and easy, see the
|
|
14
|
+
[Contributing](#contributing) section.
|
|
18
15
|
|
|
19
|
-
|
|
16
|
+
## Status
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
- We have ideas for more advanced features, like sorting entities, pagination of
|
|
27
|
-
results but it's not implemented...
|
|
28
|
-
- A few models implemented. Right now we pretty good support for `Customer`,
|
|
29
|
-
`Invoice`, `Order`, `Article`, `Label` and `Project`. Adding more models in
|
|
30
|
-
general is quick and easy (that's the whole point with this gem), see the
|
|
31
|
-
developer guide further down.
|
|
18
|
+
Version 1.0 is a complete rewrite, currently in release candidate
|
|
19
|
+
(`1.0.0.rc2`). It is built on
|
|
20
|
+
[rest-easy](https://github.com/accodeing/rest-easy), replacing the old
|
|
21
|
+
HTTParty + Data Mapper architecture with a single resource class per entity.
|
|
22
|
+
Authorization uses the Fortnox client credentials flow.
|
|
32
23
|
|
|
33
|
-
|
|
24
|
+
## Migrating from 0.x
|
|
34
25
|
|
|
35
|
-
|
|
36
|
-
and saving state. These are called: model, type, mapper and repository.
|
|
26
|
+
See the [Migration guide](MIGRATING_TO_1.0.md).
|
|
37
27
|
|
|
38
|
-
|
|
39
|
-
structuring the solution to the CRUD problem this might seem strange to you
|
|
40
|
-
since ActiveRecord merges these roles into the `ActiveRecord::Base` class.
|
|
28
|
+
## Architecture overview
|
|
41
29
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
into active record like classes than to separate active records parts to get a
|
|
48
|
-
data mapper style structure.
|
|
49
|
-
|
|
50
|
-
If you are interested in a more detailed description of the difference between
|
|
51
|
-
the two architectures you can read this post that explains it well using simple
|
|
52
|
-
examples:
|
|
53
|
-
[What’s the difference between Active Record and Data Mapper?](http://culttt.com/2014/06/18/whats-difference-active-record-data-mapper/)
|
|
54
|
-
|
|
55
|
-
## Model
|
|
56
|
-
|
|
57
|
-
The model role classes serve as dumb data objects. They do have some logic to
|
|
58
|
-
coheres values etc, but they do not contain validation logic nor any business
|
|
59
|
-
logic at all.
|
|
60
|
-
|
|
61
|
-
### Attribute
|
|
62
|
-
|
|
63
|
-
Several of the models share attributes. One example is account, as in a
|
|
64
|
-
`Bookkeeping` account number. These attributes have the same definition,
|
|
65
|
-
cohesion and validation logic so it makes sense to extract them from the models
|
|
66
|
-
and put them in separate classes. For more information, see Types below.
|
|
30
|
+
The gem uses the [rest-easy](https://github.com/accodeing/rest-easy) framework to map between Ruby
|
|
31
|
+
objects and the Fortnox JSON API. Each resource is a class that declares its
|
|
32
|
+
attributes with types and constraints. rest-easy handles the HTTP requests,
|
|
33
|
+
JSON serialisation, and attribute convention mapping (PascalCase in the API,
|
|
34
|
+
snake_case in Ruby).
|
|
67
35
|
|
|
68
36
|
### Immutability
|
|
69
37
|
|
|
70
|
-
|
|
38
|
+
Resource instances are immutable. That means:
|
|
71
39
|
|
|
72
40
|
```ruby
|
|
73
41
|
customer.name # => "Old Name"
|
|
74
|
-
customer.name = 'New Name' # =>
|
|
75
|
-
|
|
76
|
-
customer.name == "New Name" # => false
|
|
42
|
+
customer.name = 'New Name' # => NoMethodError
|
|
77
43
|
```
|
|
78
44
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
way, any operation that attempts to update state needs to return a new instance
|
|
82
|
-
with the updated state while leaving the old instance alone.
|
|
83
|
-
|
|
84
|
-
So you might think you should do this instead:
|
|
85
|
-
|
|
86
|
-
```ruby
|
|
87
|
-
customer = customer.name = 'New Name' # => "New Name"
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
But if you are familiar with chaining assignments in Ruby you will see that this
|
|
91
|
-
does not work. The result of any assignment, `LHS = RHS`, operation in Ruby is
|
|
92
|
-
`RHS`. Even if you implement your own `=` method and explicitly return something
|
|
93
|
-
else. This is a feature of the language and not something we can get around. So
|
|
94
|
-
instead you have to do:
|
|
45
|
+
Any operation that updates state returns a new instance with the updated
|
|
46
|
+
attributes while leaving the old instance alone:
|
|
95
47
|
|
|
96
48
|
```ruby
|
|
97
49
|
customer.name # => "Old Name"
|
|
98
|
-
updated_customer = customer.update(
|
|
99
|
-
updated_customer.name
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
And note that:
|
|
103
|
-
|
|
104
|
-
```ruby
|
|
50
|
+
updated_customer = customer.update(name: 'New Name')
|
|
51
|
+
updated_customer.name # => "New Name"
|
|
105
52
|
customer.name # => "Old Name"
|
|
106
|
-
customer.update( name: 'New Name' ) # => <Fortnox::API::Model::Customer:0x007fdf21100b00 ... >
|
|
107
|
-
customer.name == "New Name" # => false
|
|
108
53
|
```
|
|
109
54
|
|
|
110
|
-
This is how all
|
|
111
|
-
|
|
112
|
-
### Exceptions
|
|
113
|
-
|
|
114
|
-
Models can throw `Fortnox::API::AttributeError` if an attribute is invalid in
|
|
115
|
-
some way (for instance if you try to assign a too long string to a limited
|
|
116
|
-
string attribute) and `Fortnox::API::MissingAttributeError` if a required
|
|
117
|
-
attribute is missing.
|
|
55
|
+
This is how all resources work, they are all immutable.
|
|
118
56
|
|
|
119
|
-
|
|
57
|
+
### Types
|
|
120
58
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
the exception we throw when we get a 4xx/5xx response from the server (you can
|
|
59
|
+
Types automatically enforce constraints on values, lengths and, in some cases,
|
|
60
|
+
content of the resource attributes. Types force your data to be correct before
|
|
61
|
+
sending it to the API, which saves you API calls and time debugging. You can
|
|
125
62
|
still get errors from the server; our implementation is not perfect. Also,
|
|
126
|
-
Fortnox sometimes requires a specific combination of attributes
|
|
63
|
+
Fortnox sometimes requires a specific combination of attributes.
|
|
127
64
|
|
|
128
|
-
|
|
65
|
+
#### Exceptions
|
|
129
66
|
|
|
130
|
-
|
|
131
|
-
actually wrapping the HTTP REST API requests against Fortnox's server.
|
|
67
|
+
The gem raises the following exceptions:
|
|
132
68
|
|
|
133
|
-
|
|
69
|
+
- `Fortnox::Error` — base class for everything below. Rescue this to catch
|
|
70
|
+
any error raised by the gem.
|
|
71
|
+
- `Fortnox::RequestError` — 4xx/5xx responses from the Fortnox API. Carries
|
|
72
|
+
the response object as `.response`.
|
|
73
|
+
- `Fortnox::AttributeError` — base for attribute validation failures.
|
|
74
|
+
- `Fortnox::ConstraintError` — an attribute value violates a type
|
|
75
|
+
constraint (max size, format, etc.). Carries `.attribute_name` and
|
|
76
|
+
`.value`.
|
|
77
|
+
- `Fortnox::MissingAttributeError` — a required attribute is missing
|
|
78
|
+
from an API response. Carries `.attribute_name`.
|
|
79
|
+
- `Fortnox::MissingAccessToken` — `Fortnox.access_token=` was not called
|
|
80
|
+
on the current thread before an API call.
|
|
134
81
|
|
|
135
|
-
|
|
136
|
-
at Fortnox.
|
|
82
|
+
## Requirements
|
|
137
83
|
|
|
138
|
-
|
|
84
|
+
Ruby 3.1 or higher.
|
|
139
85
|
|
|
140
|
-
|
|
141
|
-
and Fortnox JSON requests. The repositories use the mappers to map models to
|
|
142
|
-
JSON requests and JSON to model instances when working with the Fortnox API, you
|
|
143
|
-
will not need to use them directly.
|
|
144
|
-
|
|
145
|
-
# Requirements
|
|
146
|
-
|
|
147
|
-
This gem is built for Ruby 2.6 or higher (see Travis configuration file for what
|
|
148
|
-
versions we are testing against).
|
|
149
|
-
|
|
150
|
-
## Installation
|
|
86
|
+
### Installation
|
|
151
87
|
|
|
152
88
|
Add this line to your application's Gemfile:
|
|
153
89
|
|
|
@@ -158,198 +94,259 @@ gem 'fortnox-api'
|
|
|
158
94
|
And then execute:
|
|
159
95
|
|
|
160
96
|
```shell
|
|
161
|
-
|
|
97
|
+
bundle install
|
|
162
98
|
```
|
|
163
99
|
|
|
164
|
-
|
|
100
|
+
## Authorization
|
|
101
|
+
|
|
102
|
+
Fortnox uses OAuth2 for authorization. This gem supports the **client
|
|
103
|
+
credentials** flow, which is the recommended way to authenticate server-to-server
|
|
104
|
+
integrations. The older refresh token flow is no longer supported. Read more
|
|
105
|
+
about this change in the
|
|
106
|
+
[Fortnox blog post](https://www.fortnox.se/developer/blog/say-goodbye-to-refresh-tokens-).
|
|
107
|
+
|
|
108
|
+
With client credentials you can request a new access token at any time using
|
|
109
|
+
three pieces of information: your **client ID**, **client secret**, and the
|
|
110
|
+
**tenant ID** of the Fortnox account you are integrating with.
|
|
111
|
+
|
|
112
|
+
### Prerequisites
|
|
113
|
+
|
|
114
|
+
You need:
|
|
115
|
+
|
|
116
|
+
- A Fortnox developer account
|
|
117
|
+
([register here](https://developer.fortnox.se/getting-started/))
|
|
118
|
+
- A Fortnox app in the developer portal with:
|
|
119
|
+
- Service account setting enabled
|
|
120
|
+
- Correct scopes configured
|
|
121
|
+
- A redirect URL
|
|
122
|
+
- A Fortnox test environment for testing your integration
|
|
123
|
+
|
|
124
|
+
Read the
|
|
125
|
+
[Fortnox getting started guide](https://developer.fortnox.se/getting-started/)
|
|
126
|
+
and
|
|
127
|
+
[authorization documentation](https://www.fortnox.se/developer/authorization/get-access-token-using-client-credentials)
|
|
128
|
+
for more details.
|
|
129
|
+
|
|
130
|
+
### Initial setup
|
|
131
|
+
|
|
132
|
+
Before you can use client credentials you need to perform a one-time
|
|
133
|
+
authorization code exchange. This grants your app access to a specific Fortnox
|
|
134
|
+
account and gives you the **tenant ID** you need for all future token requests.
|
|
135
|
+
|
|
136
|
+
This gem includes an executable to help with this:
|
|
165
137
|
|
|
166
138
|
```shell
|
|
167
|
-
|
|
139
|
+
fortnox-setup
|
|
168
140
|
```
|
|
169
141
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
## Authorization
|
|
142
|
+
The script will:
|
|
173
143
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
144
|
+
1. Ask for your client ID and client secret.
|
|
145
|
+
2. List the OAuth scopes covered by the gem's resources and ask which ones
|
|
146
|
+
you need. Enter a space-separated list, or `all` for everything the gem
|
|
147
|
+
supports. You can also enter scopes the gem doesn't expose directly
|
|
148
|
+
if you plan to call those endpoints manually.
|
|
149
|
+
3. Offer to use a local server to catch the authorization response automatically.
|
|
150
|
+
If you choose this, set your Fortnox app's redirect URL to `http://localhost:4242`.
|
|
151
|
+
Otherwise, enter your existing redirect URL and paste the authorization code manually.
|
|
152
|
+
4. Open your browser to the Fortnox authorization page.
|
|
153
|
+
5. You log in to Fortnox and grant your app access.
|
|
154
|
+
6. The script exchanges the authorization code for an access token and extracts
|
|
155
|
+
the tenant ID from the JWT.
|
|
156
|
+
7. The tenant ID is printed for you to store in your application's configuration.
|
|
178
157
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
integration to Fortnox, not build a public app to in the marketplace". Yeah, we
|
|
182
|
-
agree... You don't need to release the app on the Fortnox Marketplace, but you
|
|
183
|
-
need that Fortnox app. Also, see further Fortnox app requirements down below.
|
|
158
|
+
After this you have a tenant ID and never need to run this script again (unless
|
|
159
|
+
you need to authorize against a different Fortnox account).
|
|
184
160
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
Note that there's a script to authorize the Fortnox app to your Fortnox account
|
|
188
|
-
bundled with this gem to help you getting started, see
|
|
189
|
-
[Initialization](#initialization). Also read
|
|
190
|
-
[Authorizing your integration](https://developer.fortnox.se/general/authentication/).
|
|
161
|
+
Note: If you change the integration configuration in Fortnox it takes some time for
|
|
162
|
+
Fortnox to propagate the changes (for instance changing the Redirect URI or the scope).
|
|
191
163
|
|
|
192
|
-
|
|
164
|
+
### Requesting access tokens
|
|
193
165
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
- Service account setting enabled (it's used in server to server integrations,
|
|
197
|
-
which this is)
|
|
198
|
-
- Correct scopes set
|
|
199
|
-
- A redirect URL (just use a dummy URL if you want to, you just need the
|
|
200
|
-
parameters send to that URL)
|
|
201
|
-
- A Fortnox test environment so that you can test your integration.
|
|
202
|
-
|
|
203
|
-
When you have authorized your integration you get an access token from Fortnox.
|
|
204
|
-
It's a JWT with a expiration time (currently **1 hour**). You also get a long
|
|
205
|
-
lived refresh token (currently lasts for **45 days** ). When you need a new
|
|
206
|
-
access token you send a renewal request to Fortnox. That request contains the
|
|
207
|
-
new access token as well as a new refresh token and some other data. Note that
|
|
208
|
-
**the old refresh token is invalidated when new tokens are requested**. As long
|
|
209
|
-
as you have a valid refresh token you will be available to request new tokens.
|
|
210
|
-
|
|
211
|
-
The gem exposes a specific repository for renewing tokens. You use it like this:
|
|
166
|
+
Once you have a tenant ID, you can request access tokens programmatically.
|
|
167
|
+
Access tokens expire after 1 hour, but you can request a new one at any time:
|
|
212
168
|
|
|
213
169
|
```ruby
|
|
214
|
-
require 'fortnox
|
|
170
|
+
require 'fortnox'
|
|
215
171
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
172
|
+
token = Fortnox.request_access_token(
|
|
173
|
+
client_id: 'your-client-id',
|
|
174
|
+
client_secret: 'your-client-secret',
|
|
175
|
+
tenant_id: 'your-tenant-id'
|
|
220
176
|
)
|
|
221
177
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
178
|
+
Fortnox.access_token = token
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
It is up to you to manage the token lifecycle in your application. A common
|
|
182
|
+
approach is to request a new token before each batch of API calls, or to cache
|
|
183
|
+
the token and refresh it when it expires.
|
|
184
|
+
|
|
185
|
+
Note: Fortnox only allows one active access token per integration. Requesting a
|
|
186
|
+
new token invalidates the previous one. If you need multiple active access
|
|
187
|
+
tokens in parallel, you need a separate integration (client ID and secret) for
|
|
188
|
+
each token.
|
|
189
|
+
|
|
190
|
+
### Updating access tokens in env files
|
|
225
191
|
|
|
226
|
-
|
|
227
|
-
|
|
192
|
+
For development and testing, the gem includes an executable that reads your
|
|
193
|
+
credentials from an env file, requests a new access token, and writes it back:
|
|
228
194
|
|
|
229
|
-
|
|
230
|
-
|
|
195
|
+
```shell
|
|
196
|
+
fortnox-update-env # reads/writes .env
|
|
197
|
+
fortnox-update-env .env.local # reads/writes a specific file
|
|
231
198
|
```
|
|
232
199
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
200
|
+
See [.env.test.local.template](.env.test.local.template) for the required
|
|
201
|
+
variables.
|
|
202
|
+
|
|
203
|
+
### Multiple Fortnox accounts
|
|
236
204
|
|
|
237
|
-
|
|
205
|
+
The access token is stored per thread, so concurrent threads — Sidekiq
|
|
206
|
+
workers, Puma threads, etc. — can use different tokens without leaking to
|
|
207
|
+
each other. Each thread must set its own token before making API calls.
|
|
238
208
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
script itself to see what's needed.
|
|
209
|
+
Within a single thread you can switch tokens between calls. Each call uses
|
|
210
|
+
the token currently set on the calling thread:
|
|
242
211
|
|
|
243
|
-
|
|
212
|
+
```ruby
|
|
213
|
+
Fortnox.access_token = 'account1_token'
|
|
214
|
+
Fortnox::Customer.all
|
|
244
215
|
|
|
245
|
-
|
|
246
|
-
|
|
216
|
+
Fortnox.access_token = 'account2_token'
|
|
217
|
+
Fortnox::Customer.all
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Usage
|
|
221
|
+
|
|
222
|
+
Set the access token before making any API calls:
|
|
247
223
|
|
|
248
224
|
```ruby
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
225
|
+
require 'fortnox'
|
|
226
|
+
|
|
227
|
+
Fortnox.access_token = 'your-access-token'
|
|
252
228
|
```
|
|
253
229
|
|
|
254
|
-
|
|
255
|
-
| ----------- | --------------------------------- | -------- | --------------------------------------------------------------- |
|
|
256
|
-
| `base_url` | The base url to Fortnox API | No | `'https://api.fortnox.se/3/'` |
|
|
257
|
-
| `token_url` | The url to Fortnox token endpoint | No | `'https://apps.fortnox.se/oauth-v1/token'` |
|
|
258
|
-
| `debugging` | For debugging | No | `false` |
|
|
259
|
-
| `logger` | The logger to use | No | A simple logger that writes to `$stdout` with log level `WARN`. |
|
|
230
|
+
### Listing all records
|
|
260
231
|
|
|
261
|
-
|
|
232
|
+
```ruby
|
|
233
|
+
Fortnox::Customer.all
|
|
234
|
+
```
|
|
262
235
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
236
|
+
`.all`, `.search`, `.only`, and `.find(hash)` return a `Fortnox::Collection`
|
|
237
|
+
— an iterable wrapper that also exposes the pagination metadata Fortnox
|
|
238
|
+
returns alongside collection responses:
|
|
266
239
|
|
|
267
240
|
```ruby
|
|
268
|
-
|
|
241
|
+
customers = Fortnox::Customer.all
|
|
242
|
+
customers.first.name # => "Acme Corp"
|
|
243
|
+
customers.size # => 50
|
|
244
|
+
customers.total # => 327
|
|
245
|
+
customers.pages # => 7
|
|
246
|
+
customers.current_page # => 1
|
|
247
|
+
```
|
|
269
248
|
|
|
270
|
-
|
|
271
|
-
|
|
249
|
+
`Collection` is `Enumerable`, so `.each`, `.map`, `.select`, `.first`, etc.
|
|
250
|
+
all work as expected.
|
|
272
251
|
|
|
273
|
-
Fortnox
|
|
274
|
-
|
|
252
|
+
Fortnox's collection endpoints return fewer attributes per record than
|
|
253
|
+
single-resource endpoints, so instances from a `Collection` are flagged as
|
|
254
|
+
partial. Check `instance.meta.partial?` and re-fetch via `find(id)` if you
|
|
255
|
+
need the full record:
|
|
256
|
+
|
|
257
|
+
```ruby
|
|
258
|
+
customers = Fortnox::Customer.all
|
|
259
|
+
customers.first.meta.partial? # => true
|
|
260
|
+
Fortnox::Customer.find(1).meta.partial? # => false
|
|
275
261
|
```
|
|
276
262
|
|
|
277
|
-
###
|
|
263
|
+
### Finding a record
|
|
278
264
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
auth flow, but that flow is deprecated in this gem since v0.9.0).
|
|
265
|
+
```ruby
|
|
266
|
+
# By ID
|
|
267
|
+
customer = Fortnox::Customer.find(1)
|
|
283
268
|
|
|
284
|
-
#
|
|
269
|
+
# By query parameters (pagination, limits, etc.)
|
|
270
|
+
customers = Fortnox::Customer.find(limit: 10, offset: 0)
|
|
271
|
+
```
|
|
285
272
|
|
|
286
|
-
|
|
273
|
+
Note that `find` supports a hash as an argument, which adds the given keys as
|
|
274
|
+
HTTP parameters to the call. This lets you use limits, offsets, and pagination.
|
|
275
|
+
See the
|
|
276
|
+
[Fortnox documentation](https://developer.fortnox.se/general/parameters/)
|
|
277
|
+
for available parameters.
|
|
287
278
|
|
|
288
|
-
|
|
289
|
-
The calls are subject to network latency and are blocking. Do make sure to
|
|
290
|
-
rescue appropriate network errors in your code.
|
|
279
|
+
Attributes are exposed directly on the returned instance:
|
|
291
280
|
|
|
292
281
|
```ruby
|
|
293
|
-
|
|
282
|
+
customer = Fortnox::Customer.find(1)
|
|
283
|
+
customer.name # => "Acme Corp"
|
|
284
|
+
customer.city # => "Stockholm"
|
|
285
|
+
customer.unique_id # => "1"
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Creating a record
|
|
294
289
|
|
|
295
|
-
|
|
290
|
+
Use `.stub` to build a new instance and `.save` to persist it:
|
|
296
291
|
|
|
297
|
-
|
|
298
|
-
|
|
292
|
+
```ruby
|
|
293
|
+
customer = Fortnox::Customer.stub(name: 'Acme Corp', city: 'Stockholm')
|
|
294
|
+
result = Fortnox::Customer.save(customer)
|
|
295
|
+
result.customer_number # => "1"
|
|
296
|
+
```
|
|
299
297
|
|
|
300
|
-
|
|
301
|
-
repo.all #=> <Fortnox::API::Collection:0x007fdf2104575638 @entities: [<Fortnox::API::Customer::Simple:0x007fdf21033ee8>, <Fortnox::API::Customer::Simple:0x007fdf22994310>, ... ]
|
|
298
|
+
### Updating a record
|
|
302
299
|
|
|
303
|
-
|
|
304
|
-
|
|
300
|
+
Fetch, update, and save. Models are immutable, so `.update` returns a new
|
|
301
|
+
instance:
|
|
305
302
|
|
|
306
|
-
|
|
307
|
-
|
|
303
|
+
```ruby
|
|
304
|
+
customer = Fortnox::Customer.find(1)
|
|
305
|
+
updated = customer.update(name: 'Acme Inc')
|
|
306
|
+
Fortnox::Customer.save(updated)
|
|
308
307
|
```
|
|
309
308
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
309
|
+
### Searching
|
|
310
|
+
|
|
311
|
+
```ruby
|
|
312
|
+
Fortnox::Customer.search(name: 'Acme')
|
|
313
|
+
```
|
|
315
314
|
|
|
316
|
-
###
|
|
315
|
+
### Filtering
|
|
317
316
|
|
|
318
|
-
|
|
319
|
-
hash keys as HTTP parameters to the call. This will let you search, sort, use
|
|
320
|
-
limits and offsets as well as do pagination. See
|
|
321
|
-
[Fortnox documentation](https://developer.fortnox.se/general/parameters/) for
|
|
322
|
-
more information about available parameters.
|
|
317
|
+
Some resources support server-side filters:
|
|
323
318
|
|
|
324
319
|
```ruby
|
|
325
|
-
|
|
326
|
-
Fortnox::API::Repository::Customer.new.find { page: 2 }
|
|
320
|
+
Fortnox::Invoice.only('unpaid')
|
|
327
321
|
```
|
|
328
322
|
|
|
329
|
-
|
|
323
|
+
### Gotchas
|
|
330
324
|
|
|
331
|
-
|
|
332
|
-
|
|
325
|
+
See [docs/gotchas.md](docs/gotchas.md) for known quirks and edge cases in the
|
|
326
|
+
Fortnox API.
|
|
333
327
|
|
|
334
|
-
|
|
335
|
-
appropriate attributes changed (see the Immutable section under Architecture
|
|
336
|
-
above for more details). To change the properties of a model works like this:
|
|
328
|
+
### Resources
|
|
337
329
|
|
|
338
|
-
|
|
339
|
-
|
|
330
|
+
Each resource maps to a Fortnox API endpoint. Attributes are typed and
|
|
331
|
+
validated before sending data to the API. Read-only attributes (like `url`)
|
|
332
|
+
cannot be set when creating or updating records.
|
|
340
333
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
customer.name #=> "Nelly Bloom"
|
|
334
|
+
Resources that share structure inherit from a common base. For example,
|
|
335
|
+
`Invoice` and `Order` both extend `Document`, which defines shared attributes
|
|
336
|
+
like administration fee, delivery address, and row items.
|
|
345
337
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
338
|
+
## Changelog
|
|
339
|
+
|
|
340
|
+
See the [Changelog](CHANGELOG.md).
|
|
341
|
+
|
|
342
|
+
## Development
|
|
343
|
+
|
|
344
|
+
See the [Developer readme](DEVELOPER_README.md).
|
|
345
|
+
|
|
346
|
+
## Contributing
|
|
349
347
|
|
|
350
|
-
|
|
351
|
-
update as many as you like in one go.
|
|
348
|
+
See the [Contribute readme](CONTRIBUTE.md).
|
|
352
349
|
|
|
353
|
-
|
|
350
|
+
## License
|
|
354
351
|
|
|
355
|
-
|
|
352
|
+
[LGPL-3.0](LICENSE.md). Copyright (c) 2015-2026 Accodeing to you KB.
|