economic-rest 0.6.2 → 0.6.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +98 -96
  5. data/README.md +89 -3
  6. data/economic-rest.gemspec +7 -8
  7. data/lib/economic/accounting_year_repo.rb +2 -0
  8. data/lib/economic/attribute.rb +9 -0
  9. data/lib/economic/base_repo.rb +7 -12
  10. data/lib/economic/configuration.rb +7 -0
  11. data/lib/economic/credentials.rb +26 -0
  12. data/lib/economic/customer_contact_repo.rb +2 -0
  13. data/lib/economic/customer_repo.rb +2 -0
  14. data/lib/economic/invoice.rb +0 -25
  15. data/lib/economic/invoices/booked_repo.rb +1 -1
  16. data/lib/economic/invoices/drafts_repo.rb +2 -0
  17. data/lib/economic/invoices/repo.rb +1 -1
  18. data/lib/economic/line.rb +0 -21
  19. data/lib/economic/model.rb +99 -0
  20. data/lib/economic/models/accounting_year.rb +9 -0
  21. data/lib/economic/models/attention.rb +8 -0
  22. data/lib/economic/models/customer.rb +41 -0
  23. data/lib/economic/models/customer_group.rb +11 -0
  24. data/lib/economic/models/customers/contact.rb +19 -0
  25. data/lib/economic/models/invoice.rb +32 -0
  26. data/lib/economic/models/invoices/booked.rb +11 -0
  27. data/lib/economic/models/invoices/draft.rb +9 -0
  28. data/lib/economic/models/layout.rb +9 -0
  29. data/lib/economic/models/line.rb +21 -0
  30. data/lib/economic/models/note.rb +9 -0
  31. data/lib/economic/models/payment_term.rb +10 -0
  32. data/lib/economic/models/product.rb +22 -0
  33. data/lib/economic/models/recipient.rb +17 -0
  34. data/lib/economic/models/reference.rb +11 -0
  35. data/lib/economic/models/sales_person.rb +7 -0
  36. data/lib/economic/models/unit.rb +8 -0
  37. data/lib/economic/models/vat_zone.rb +10 -0
  38. data/lib/economic/models/vendor_reference.rb +7 -0
  39. data/lib/economic/nested_base_repo.rb +0 -5
  40. data/lib/economic/orders/repo.rb +1 -1
  41. data/lib/economic/relation.rb +17 -0
  42. data/lib/economic/repo.rb +127 -0
  43. data/lib/economic/repos/accounting_year.rb +6 -0
  44. data/lib/economic/repos/customer.rb +6 -0
  45. data/lib/economic/repos/customers/contact.rb +21 -0
  46. data/lib/economic/repos/invoices/booked.rb +19 -0
  47. data/lib/economic/repos/invoices/draft.rb +8 -0
  48. data/lib/economic/repos/invoices/drafts/line.rb +26 -0
  49. data/lib/economic/response/pagination.rb +16 -0
  50. data/lib/economic/response.rb +51 -0
  51. data/lib/economic/rest/version.rb +1 -1
  52. data/lib/economic/rest.rb +41 -6
  53. metadata +58 -40
  54. data/lib/economic/concerns/soap_methods.rb +0 -217
  55. data/lib/economic/soap_api.rb +0 -24
@@ -0,0 +1,99 @@
1
+ module Economic
2
+ class Model
3
+ class << self
4
+ def field(name, as: nil)
5
+ (@attributes ||= []) << Attribute.new(name:, as:)
6
+ attr_accessor name
7
+ end
8
+
9
+ def relation(name, as: nil, multiple: false, klass: nil)
10
+ (@relations ||= []) << Relation.new(name:, as:, multiple:, klass:)
11
+ attr_accessor name
12
+ end
13
+
14
+ def from_json(json)
15
+ from_hash(JSON.parse(json))
16
+ end
17
+
18
+ def from_hash(hash)
19
+ translated_attributes = translate_attributes(hash)
20
+ translated_relations = translate_relations(hash, translated_attributes)
21
+ new(**translated_relations)
22
+ end
23
+
24
+ def translate_attributes(hash)
25
+ economic_attribute_names = attributes.map(&:economic_name)
26
+ attributes_hash = hash.slice(*economic_attribute_names)
27
+
28
+ attributes_hash.each_with_object({}) do |(key, value), result|
29
+ attribute = attributes.find { |attr| attr.as.to_s == key || attr.name.to_s.camelize(:lower) == key }
30
+ result[attribute.name] = value
31
+ end
32
+ end
33
+
34
+ def translate_relations(hash, translated_attributes)
35
+ return translated_attributes unless relations&.any?
36
+
37
+ economic_relation_names = relations.map(&:economic_name)
38
+ relations_hash = hash.slice(*economic_relation_names)
39
+
40
+ relations_hash.each_with_object(translated_attributes) do |(key, value), result|
41
+ relation = relations.find { |rel| rel.as.to_s == key || rel.name.to_s.camelize(:lower) == key }
42
+ result[relation.name] = relation.multiple? ? value.map { relation.klass.from_hash(_1) } : relation.klass.from_hash(value)
43
+ end
44
+ end
45
+
46
+ def inherited(subclass)
47
+ return if self == Economic::Model
48
+
49
+ attributes.each do |attribute|
50
+ subclass.attributes << attribute
51
+ end
52
+ relations.each do |relation|
53
+ subclass.relations << relation
54
+ end
55
+ end
56
+
57
+ def attributes
58
+ @attributes ||= []
59
+ end
60
+
61
+ def relations
62
+ @relations ||= []
63
+ end
64
+ end
65
+
66
+ def initialize(**kwargs)
67
+ valid_keys = self.class.attributes.map(&:name) + (self.class.relations&.map(&:name) || [])
68
+ invalid_keys = kwargs.keys - valid_keys
69
+ raise ArgumentError, "invalid keys: #{invalid_keys.join(", ")}" unless invalid_keys.empty?
70
+
71
+ self.class.attributes.each { |attribute| instance_variable_set :"@#{attribute.name}", kwargs[attribute.name] }
72
+ self.class.relations&.each { |relation| instance_variable_set :"@#{relation.name}", relation_value(relation, kwargs) }
73
+ end
74
+
75
+ def to_h
76
+ result = {}
77
+ self.class.attributes.each_with_object(result) { |attribute, hash| hash[attribute.economic_name] = instance_variable_get :"@#{attribute.name}" }
78
+ self.class.relations&.each_with_object(result) { |relation, hash| hash[relation.economic_name] = relation_to_h(relation) }
79
+ result.compact
80
+ end
81
+
82
+ def to_json
83
+ to_h.to_json
84
+ end
85
+
86
+ private
87
+
88
+ def relation_value(relation, kwargs)
89
+ relation.multiple? ? (kwargs[relation.name] || []) : kwargs[relation.name]
90
+ end
91
+
92
+ def relation_to_h(relation)
93
+ value = instance_variable_get(:"@#{relation.name}")
94
+ return if value.nil? || (value.respond_to?(:empty?) && value.empty?)
95
+
96
+ relation.multiple? ? value.map(&:to_h) : value.to_h
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,9 @@
1
+ module Economic
2
+ module Models
3
+ class AccountingYear < Economic::Model
4
+ field :from_date
5
+ field :to_date
6
+ field :year
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ module Economic
2
+ module Models
3
+ class Attention < Economic::Model
4
+ field :customer_contact_number
5
+ field :supplier_contact_number
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,41 @@
1
+ module Economic
2
+ module Models
3
+ class Customer < Economic::Model
4
+ field :id, as: :customerNumber
5
+ field :address
6
+ field :balance
7
+ field :barred
8
+ field :city
9
+ field :contacts
10
+ field :corporate_identification_number
11
+ field :country
12
+ field :credit_limit
13
+ field :currency
14
+ field :delivery_locations
15
+ field :due_amount
16
+ field :ean
17
+ field :email
18
+ field :last_updated
19
+ field :name
20
+ field :mobile_phone
21
+ field :p_number
22
+ field :public_entry_number
23
+ field :telephone_and_fax_number
24
+ field :vat_number
25
+ field :website
26
+ field :zip
27
+
28
+ # field :attention
29
+ # field :customer_contact
30
+ relation :customer_group
31
+ # field :default_delivery_location
32
+ # field :invoices
33
+ # relation :layout
34
+ relation :payment_terms
35
+ # field :sales_person
36
+ # field :templates
37
+ # field :totals
38
+ relation :vat_zone
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,11 @@
1
+ module Economic
2
+ module Models
3
+ class CustomerGroup < Economic::Model
4
+ field :id, as: :customerGroupNumber
5
+ field :account
6
+ field :customers
7
+ field :layout
8
+ field :name
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ module Economic
2
+ module Models
3
+ module Customers
4
+ class Contact < Economic::Model
5
+ field :id, as: :customerContactNumber
6
+ field :deleted
7
+ field :e_invoice_id
8
+ field :email
9
+ field :email_notifications
10
+ field :name
11
+ field :notes
12
+ field :phone
13
+ field :sort_key
14
+
15
+ relation :customer
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,32 @@
1
+ module Economic
2
+ module Models
3
+ class Invoice < Economic::Model
4
+ field :currency
5
+ field :date
6
+ field :due_date
7
+ field :exchange_rate
8
+ field :gross_amount
9
+ field :gross_amount_in_base_currency
10
+ field :margin_in_base_currency
11
+ field :margin_percentage
12
+ field :net_amount
13
+ field :rounding_amount
14
+ field :vat_amount
15
+ field :remainder
16
+ field :cost_price_in_base_currency
17
+ field :net_amount_in_base_currency
18
+
19
+ relation :lines, multiple: true
20
+ relation :customer
21
+ relation :payment_terms
22
+ relation :recipient
23
+ relation :notes
24
+ relation :references
25
+ relation :layout
26
+ # relation :delivery
27
+ # relation :delivery_location
28
+ # relation :pdf
29
+ # relation :project
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,11 @@
1
+ module Economic
2
+ module Models
3
+ module Invoices
4
+ class Booked < Economic::Models::Invoice
5
+ field :id, as: :bookedInvoiceNumber
6
+
7
+ relation :draft_invoice, klass: "Economic::Models::Invoices::Draft"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ module Economic
2
+ module Models
3
+ module Invoices
4
+ class Draft < Economic::Models::Invoice
5
+ field :id, as: :draftInvoiceNumber
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Economic
2
+ module Models
3
+ class Layout < Economic::Model
4
+ field :deleted
5
+ field :id, as: :layoutNumber
6
+ field :name
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ module Economic
2
+ module Models
3
+ class Line < Economic::Model
4
+ field :id, as: :lineNumber
5
+ field :description
6
+ field :sort_key
7
+ field :quantity
8
+ field :unit_net_price
9
+ field :discount_percentage
10
+ field :unit_cost_price
11
+ field :margin_in_base_currency
12
+ field :margin_percentage
13
+ field :total_net_amount
14
+
15
+ relation :product
16
+ relation :unit
17
+ # relation :delivery
18
+ # relation :departmental_distribution
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ module Economic
2
+ module Models
3
+ class Note < Economic::Model
4
+ field :heading
5
+ field :text_line1
6
+ field :text_line2
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ module Economic
2
+ module Models
3
+ class PaymentTerm < Economic::Model
4
+ field :days_of_credit
5
+ field :name
6
+ field :payment_terms_number
7
+ field :payment_terms_type
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ module Economic
2
+ module Models
3
+ class Product < Economic::Model
4
+ field :id, as: :productNumber
5
+ field :bar_code
6
+ field :barred
7
+ field :cost_price
8
+ field :description
9
+ field :last_updated
10
+ field :name
11
+ field :recommended_price
12
+ field :sales_price
13
+
14
+ # field :departmental_distribution
15
+ # relation :inventory
16
+ # field :invoices
17
+ # field :pricing
18
+ # relation :product_group
19
+ # relation :unit
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ module Economic
2
+ module Models
3
+ class Recipient < Economic::Model
4
+ field :address
5
+ field :name
6
+ field :city
7
+ field :country
8
+ field :ean
9
+ field :mobile_phone
10
+ field :public_entry_number
11
+ field :zip
12
+
13
+ relation :vat_zone
14
+ relation :attention
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ module Economic
2
+ module Models
3
+ class Reference < Economic::Model
4
+ field :other
5
+
6
+ relation :customer_contact, klass: "Economic::Models::Customers::Contact"
7
+ relation :sales_person
8
+ relation :vendor_reference
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module Economic
2
+ module Models
3
+ class SalesPerson < Economic::Model
4
+ field :employee_number
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ module Economic
2
+ module Models
3
+ class Unit < Economic::Model
4
+ field :id, as: :unitNumber
5
+ field :name
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ module Economic
2
+ module Models
3
+ class VatZone < Economic::Model
4
+ field :vat_zone_number
5
+ field :enabled_for_customer
6
+ field :enabled_for_supplier
7
+ field :name
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ module Economic
2
+ module Models
3
+ class VendorReference < Economic::Model
4
+ field :employee_number
5
+ end
6
+ end
7
+ end
@@ -17,11 +17,6 @@ module Economic
17
17
  "#{kebab(model.class.name.demodulize.pluralize)}/#{model.id_key}/#{endpoint_name}"
18
18
  end
19
19
 
20
- def send(model, on:)
21
- warn "use #{self}.save(). #{self}.send() is deprecated"
22
- save(model, on: on)
23
- end
24
-
25
20
  def save(model, on:)
26
21
  super(model, url: nested_endpoint_url(on))
27
22
  end
@@ -13,7 +13,7 @@ module Economic
13
13
  orders
14
14
  end
15
15
 
16
- def send(model, url: endpoint_url)
16
+ def save(model, url: endpoint_url)
17
17
  response = send_request(method: :post, url: url, payload: model.to_h.to_json)
18
18
 
19
19
  modelize_response(response)
@@ -0,0 +1,17 @@
1
+ module Economic
2
+ class Relation < Data.define(:name, :as, :multiple, :klass)
3
+ def economic_name
4
+ return as.to_s unless as.to_s.blank?
5
+
6
+ name.to_s.camelize(:lower)
7
+ end
8
+
9
+ def klass
10
+ super&.constantize || "Economic::Models::#{name.to_s.classify}".constantize
11
+ end
12
+
13
+ def multiple?
14
+ multiple
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,127 @@
1
+ module Economic
2
+ class Repo
3
+ ROOT = "https://restapi.e-conomic.com".freeze
4
+ DEFAULT_QUERY_PARAMS = {skippages: 0, pagesize: 1000}.freeze
5
+
6
+ def initialize(credentials: Economic::Credentials.fetch!)
7
+ @credentials = credentials
8
+ raise Economic::MissingCredentialsError if credentials.missing?
9
+ end
10
+
11
+ attr_reader :credentials
12
+
13
+ def all(filter: nil)
14
+ response = make_request(method: :get, query: {filter:})
15
+
16
+ entries = response.collection
17
+ while response.pagination.next_page?
18
+ uri = build_uri(response.pagination.next_page)
19
+ response = make_request(uri:, method: :get)
20
+ entries += response.collection
21
+ end
22
+
23
+ entries
24
+ end
25
+
26
+ def find(id_or_model)
27
+ make_request(id_or_model:, method: :get).entity
28
+ end
29
+
30
+ def create(model)
31
+ make_request(method: :post, data: model).entity
32
+ end
33
+
34
+ def update(model)
35
+ make_request(id_or_model: model, method: :put, data: model).entity
36
+ end
37
+
38
+ def destroy(id_or_model)
39
+ make_request(id_or_model:, method: :delete)
40
+ end
41
+
42
+ private
43
+
44
+ def make_request(method:, uri: url, id_or_model: nil, data: nil, query: nil)
45
+ uri = build_uri(uri, id_or_model:, query:)
46
+ request = build_request(uri)
47
+ args = [method, uri, data&.to_json, headers].compact
48
+ response = request.public_send(*args)
49
+
50
+ parse_response(response)
51
+ end
52
+
53
+ def build_request(uri)
54
+ http = Net::HTTP.new(uri.hostname, Net::HTTP.https_default_port)
55
+ http.use_ssl = true
56
+
57
+ http
58
+ end
59
+
60
+ def build_uri(uri, id_or_model: nil, query: nil)
61
+ uri = id_or_model.present? ? "#{uri}/#{id_or_model.try(:id) || id_or_model}" : uri
62
+ uri = URI(uri)
63
+ uri.query = URI.encode_www_form(query.with_defaults(DEFAULT_QUERY_PARAMS).compact) if query.present?
64
+
65
+ uri
66
+ end
67
+
68
+ def parse_response(response)
69
+ case response.code
70
+ when "200", "201"
71
+ Economic::Response.from_json(response.body)
72
+ when "204"
73
+ true
74
+ when "400"
75
+ raise BadRequestError, response.body
76
+ when "401"
77
+ raise UnauthorizedError, response.body
78
+ when "403"
79
+ raise ForbiddenError, response.body
80
+ when "404"
81
+ raise NotFoundError, response.body
82
+ when "500"
83
+ raise InternalError, response.body
84
+ else
85
+ raise Error, "#{response.code} - #{response.body}"
86
+ end
87
+ end
88
+
89
+ def url
90
+ "#{ROOT}/#{endpoint}"
91
+ end
92
+
93
+ def endpoint
94
+ resource_name.pluralize.underscore.dasherize
95
+ end
96
+
97
+ def resource_name
98
+ self.class.to_s[17..]
99
+ end
100
+
101
+ def headers
102
+ {
103
+ "X-AppSecretToken": credentials.app_secret_token,
104
+ "X-AgreementGrantToken": credentials.agreement_grant_token,
105
+ "Content-Type": "application/json"
106
+ }
107
+ end
108
+ end
109
+
110
+ class BadRequestError < StandardError
111
+ end
112
+
113
+ class UnauthorizedError < StandardError
114
+ end
115
+
116
+ class ForbiddenError < StandardError
117
+ end
118
+
119
+ class NotFoundError < StandardError
120
+ end
121
+
122
+ class InternalError < StandardError
123
+ end
124
+
125
+ class Error < StandardError
126
+ end
127
+ end
@@ -0,0 +1,6 @@
1
+ module Economic
2
+ module Repos
3
+ class AccountingYear < Economic::Repo
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Economic
2
+ module Repos
3
+ class Customer < Economic::Repo
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,21 @@
1
+ module Economic
2
+ module Repos
3
+ module Customers
4
+ class Contact < Economic::Repo
5
+ def initialize(parent, credentials: Economic::Credentials.fetch!)
6
+ @parent = parent
7
+ super(credentials:)
8
+ end
9
+
10
+ private
11
+
12
+ attr_reader :parent
13
+
14
+ def endpoint
15
+ fragments = resource_name.pluralize.underscore.dasherize.split("/")
16
+ fragments.insert(1, "/#{parent.id}/").join
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ module Economic
2
+ module Repos
3
+ module Invoices
4
+ class Booked < Economic::Repo
5
+ def create(model, send_by: nil)
6
+ hash = model.to_h
7
+ hash[:sendBy] = send_by if send_by.to_s == "ean"
8
+ super(hash)
9
+ end
10
+
11
+ private
12
+
13
+ def endpoint
14
+ resource_name.underscore.dasherize
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,8 @@
1
+ module Economic
2
+ module Repos
3
+ module Invoices
4
+ class Draft < Economic::Repo
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,26 @@
1
+ module Economic
2
+ module Repos
3
+ module Invoices
4
+ module Drafts
5
+ class Line < Economic::Repo
6
+ def create(draft_invoice_model_or_id, lines)
7
+ draft_invoice_id = draft_invoice_model_or_id.try(:id) || draft_invoice_model_or_id
8
+ draft_invoice = Economic::Models::Invoices::Draft.new(lines: lines)
9
+
10
+ uri = URI("#{Economic::Repo::ROOT}/invoices/drafts/#{draft_invoice_id}/lines")
11
+
12
+ make_request(uri: uri, method: :post, data: draft_invoice)
13
+ end
14
+
15
+ private
16
+
17
+ def parse_response(response)
18
+ JSON.parse(response.body)["lines"].map do |line_data|
19
+ Economic::Models::Line.from_hash(**line_data)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ module Economic
2
+ class Response::Pagination < Economic::Model
3
+ field :max_page_size_allowed
4
+ field :skip_pages
5
+ field :page_size
6
+ field :results
7
+ field :results_without_filter
8
+ field :first_page
9
+ field :next_page
10
+ field :last_page
11
+
12
+ def next_page?
13
+ next_page.present?
14
+ end
15
+ end
16
+ end