papierkram_api_client 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -0
  3. data/CHANGELOG.md +13 -0
  4. data/Gemfile.lock +3 -3
  5. data/README.md +155 -55
  6. data/lib/papierkram_api/client.rb +92 -0
  7. data/lib/papierkram_api/v1/business_intelligence/base.rb +23 -0
  8. data/lib/papierkram_api/v1/business_intelligence/expenses_by_category.rb +98 -0
  9. data/lib/papierkram_api/v1/business_intelligence/smart_queries/expense_vouchers_for_month_in_year.rb +58 -0
  10. data/lib/papierkram_api/v1/endpoints/banking/bank_connections.rb +30 -0
  11. data/lib/papierkram_api/v1/endpoints/banking/transactions.rb +20 -0
  12. data/lib/papierkram_api/v1/endpoints/base.rb +77 -0
  13. data/lib/papierkram_api/v1/endpoints/contact/companies.rb +30 -0
  14. data/lib/papierkram_api/v1/endpoints/contact/companies_persons.rb +28 -0
  15. data/lib/papierkram_api/v1/endpoints/expense/vouchers.rb +45 -0
  16. data/lib/papierkram_api/v1/endpoints/income/estimates.rb +46 -0
  17. data/lib/papierkram_api/v1/endpoints/income/invoices.rb +46 -0
  18. data/lib/papierkram_api/v1/endpoints/income/propositions.rb +20 -0
  19. data/lib/papierkram_api/v1/endpoints/info.rb +14 -0
  20. data/lib/papierkram_api/v1/endpoints/projects.rb +26 -0
  21. data/lib/papierkram_api/v1/endpoints/tracker/tasks.rb +33 -0
  22. data/lib/papierkram_api/v1/endpoints/tracker/time_entries.rb +62 -0
  23. data/lib/papierkram_api/v1/helper/date_helper.rb +14 -0
  24. data/lib/{api → papierkram_api}/v1/helper/pdf_from_response.rb +1 -1
  25. data/lib/papierkram_api/v1/validators/expense_voucher.rb +105 -0
  26. data/lib/papierkram_api_client/version.rb +1 -1
  27. data/lib/papierkram_api_client.rb +21 -102
  28. metadata +22 -16
  29. data/lib/api/v1/banking/bank_connections.rb +0 -28
  30. data/lib/api/v1/banking/transactions.rb +0 -18
  31. data/lib/api/v1/base.rb +0 -75
  32. data/lib/api/v1/contact/companies.rb +0 -28
  33. data/lib/api/v1/contact/companies_persons.rb +0 -26
  34. data/lib/api/v1/expense/vouchers.rb +0 -43
  35. data/lib/api/v1/income/estimates.rb +0 -44
  36. data/lib/api/v1/income/invoices.rb +0 -44
  37. data/lib/api/v1/income/propositions.rb +0 -18
  38. data/lib/api/v1/info.rb +0 -12
  39. data/lib/api/v1/projects.rb +0 -24
  40. data/lib/api/v1/tracker/tasks.rb +0 -31
  41. data/lib/api/v1/tracker/time_entries.rb +0 -59
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PapierkramApi
4
+ module V1
5
+ module BusinessIntelligence
6
+ # Business Intelligence for Expenses
7
+ class ExpensesByCategory
8
+ #
9
+ # Calculate the expenses by category
10
+ #
11
+ # @param [Array<Hash>] expense_vouchers represents a list of expense vouchers in the format:
12
+ #
13
+ # @param [<Type>] &block <description>
14
+ #
15
+ # @return [<Type>] <description>
16
+ #
17
+ def call(expense_vouchers:, &block)
18
+ return {} if expense_vouchers.is_a?(Array) && expense_vouchers.empty?
19
+
20
+ validate_expense_vouchers!(expense_vouchers)
21
+ if block_given?
22
+ results(expense_vouchers, &block)
23
+ else
24
+ results(expense_vouchers)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def results(expense_vouchers)
31
+ coll = prepare_voucher_data(expense_vouchers)
32
+ coll = yield(coll) if block_given?
33
+ line_items_grouped_by_category = coll.group_by { |v| v['category'] }
34
+ sum_categories!(line_items_grouped_by_category)
35
+ end
36
+
37
+ def sum_categories!(line_items_grouped_by_category)
38
+ lookup_table = {}
39
+ line_items_grouped_by_category.each do |category_name, line_items|
40
+ line_items_by_creditor_lot = {}
41
+ line_items_by_creditor = line_items.group_by { |v| v['creditor'] }
42
+ sum_up_creditor!(line_items_by_creditor, line_items_by_creditor_lot)
43
+ build_result_lookup_table!(lookup_table, category_name, line_items, line_items_by_creditor_lot)
44
+ end
45
+ lookup_table
46
+ end
47
+
48
+ def sum_up_creditor!(line_items_by_creditor, line_items_by_creditor_lot)
49
+ line_items_by_creditor.each do |category_name, line_items|
50
+ line_items_by_creditor_lot[category_name] = line_items.reduce(0) do |sum, creditor|
51
+ sum + creditor['amount']
52
+ end
53
+ end
54
+ line_items_by_creditor_lot
55
+ end
56
+
57
+ def build_result_lookup_table!(lookup_table, category_name, line_items, line_items_by_creditor_lot)
58
+ lookup_table[category_name] = {
59
+ 'amount' => line_items.reduce(0) { |sum, line_item| sum + line_item['amount'] },
60
+ 'amount_by_creditor' => line_items_by_creditor_lot,
61
+ 'line_items' => line_items
62
+ }
63
+ end
64
+
65
+ def prepare_voucher_data(vouchers)
66
+ coll = []
67
+ vouchers.flat_map do |voucher|
68
+ voucher['line_items'].each do |line_item|
69
+ res = line_item.merge(
70
+ { 'voucher_name' => voucher['name'] },
71
+ { 'voucher_id' => voucher['id'] },
72
+ 'creditor' => voucher['creditor']
73
+ )
74
+ coll.push(res)
75
+ end
76
+ end
77
+ coll
78
+ end
79
+
80
+ # TODO: move to a validator class forPapierkramApi::V1::Validators::ExpenseVouchers
81
+ def validate_expense_vouchers!(expense_vouchers)
82
+ raise ArgumentError, 'expense_vouchers must be an Array' unless expense_vouchers.is_a?(Array)
83
+ raise ArgumentError, 'expense_vouchers must not be empty' if expense_vouchers.empty?
84
+ raise ArgumentError, 'expense_vouchers must contain Hashes' unless expense_vouchers.all?(Hash)
85
+
86
+ validator = PapierkramApi::V1::Validators::ExpenseVoucher.new
87
+ expense_vouchers.all? do |voucher|
88
+ validate_expense_voucher!(validator, voucher)
89
+ end
90
+ end
91
+
92
+ def validate_expense_voucher!(validator, expense_voucher)
93
+ validator.validate!(expense_voucher)
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PapierkramApi
4
+ module V1
5
+ module BusinessIntelligence
6
+ module SmartQueries
7
+ # This class is responsible for all the API calls related to expense vouchers.
8
+ class ExpenseVouchersForMonthInYear
9
+ include PapierkramApi::V1::Helper::DateHelper
10
+
11
+ def initialize(expense_voucher_api)
12
+ @expense_voucher_api = expense_voucher_api
13
+ end
14
+
15
+ def for_month_in_year(year:, month:)
16
+ @year = year
17
+ @month = month
18
+ @start_date = Date.new(@year, @month, 1)
19
+ @end_date = Date.new(@year, @month + 1, 1) - 1
20
+ collect_expense_vouchers
21
+ end
22
+
23
+ private
24
+
25
+ def collect_expense_vouchers
26
+ all_expense_vouchers_in_date_range.map do |voucher|
27
+ @expense_voucher_api.by(id: voucher['id']).body
28
+ end
29
+ end
30
+
31
+ def all_expense_vouchers_in_date_range
32
+ page = 1
33
+ vouchers = []
34
+ res = expense_vouchers(page: page)
35
+ res.body['entries'].each { |v| vouchers << v }
36
+ while res.body['page'] != res.body['total_pages']
37
+ res = expense_vouchers(page: page)
38
+ res.body['entries'].each { |v| vouchers << v }
39
+ end
40
+ vouchers
41
+ end
42
+
43
+ def expense_vouchers(page: 1)
44
+ @expense_voucher_api.all(
45
+ page: page,
46
+ document_date_range_start: build_date_string_for_api(@start_date),
47
+ document_date_range_end: build_date_string_for_api(@end_date)
48
+ )
49
+ end
50
+
51
+ def expense_voucher(id)
52
+ @expense_voucher_api.by(id: id)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PapierkramApi
4
+ module V1
5
+ module Endpoints
6
+ module Banking
7
+ # This class is responsible for all the API calls related to banking bank connections.
8
+ class BankConnections < PapierkramApi::V1::Endpoints::Base
9
+ def by(id:)
10
+ get("#{@url_api_path}/banking/bank_connections/#{id}")
11
+ end
12
+
13
+ def all(page: 1,
14
+ page_size: 100,
15
+ order_by: nil,
16
+ order_direction: nil)
17
+ query = {
18
+ page: page,
19
+ page_size: page_size
20
+ }
21
+ query[:order_by] = order_by if order_by
22
+ query[:order_direction] = order_direction if order_direction
23
+
24
+ get("#{@url_api_path}/banking/bank_connections", query)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PapierkramApi
4
+ module V1
5
+ module Endpoints
6
+ module Banking
7
+ # This class is responsible for all the API calls related to banking transactions.
8
+ class Transactions
9
+ def by(id:)
10
+ # wip
11
+ end
12
+
13
+ def all
14
+ # wip
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PapierkramApi
4
+ module V1
5
+ module Endpoints
6
+ # This class is the base class for all the API calls.
7
+ class Base
8
+ attr_reader :client, :url_api_path
9
+
10
+ def initialize(client)
11
+ @client = client
12
+ @url_api_path = '/api/v1'
13
+ end
14
+
15
+ def remaining_quota(response)
16
+ return response.headers['x-remaining-quota'].to_i if response.is_a?(Faraday::Response)
17
+ raise ArgumentError, 'Invalid response object' unless response.is_a?(Hash)
18
+
19
+ quota = response.dig('headers', 'x-remaining-quota') || response['x-remaining-quota']
20
+ return Integer(quota) if quota
21
+
22
+ raise ArgumentError, "No remaining quota found in response: #{response}"
23
+ end
24
+
25
+ def get(url, params = {}, headers = {})
26
+ validate_get!(params)
27
+ call_wrapper!(:get, url, params, headers)
28
+ end
29
+
30
+ def post(url, params = {}, headers = {})
31
+ call_wrapper!(:post, url, params, headers)
32
+ end
33
+
34
+ def put(url, params = {}, headers = {})
35
+ call_wrapper!(:put, url, params, headers)
36
+ end
37
+
38
+ def patch(url, params = {}, headers = {})
39
+ call_wrapper!(:patch, url, params, headers)
40
+ end
41
+
42
+ def delete(url, params = {}, headers = {})
43
+ call_wrapper!(:delete, url, params, headers)
44
+ end
45
+
46
+ private
47
+
48
+ def call_wrapper!(method, url, params = {}, headers = {})
49
+ @client.send(method, url, params, headers)
50
+ end
51
+
52
+ def validate_order_direction!(order_direction)
53
+ return if order_direction.nil?
54
+
55
+ raise ArgumentError, 'Invalid order direction' unless %w[asc desc].include?(order_direction)
56
+ end
57
+
58
+ def validate_billing_state!(billing_state)
59
+ return if billing_state.nil?
60
+
61
+ raise ArgumentError, 'Invalid billing state' unless %w[billed
62
+ unbilled
63
+ billable
64
+ unbillable
65
+ archived].include?(billing_state)
66
+ end
67
+
68
+ def validate_get!(params)
69
+ return unless params
70
+
71
+ validate_order_direction!(params[:order_direction])
72
+ validate_billing_state!(params[:billing_state])
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PapierkramApi
4
+ module V1
5
+ module Endpoints
6
+ module Contact
7
+ # This class is responsible for all the API calls related to banking bank connections.
8
+ class Companies < PapierkramApi::V1::Endpoints::Base
9
+ def by(id:)
10
+ get("#{@url_api_path}/contact/companies/#{id}")
11
+ end
12
+
13
+ def all(page: 1,
14
+ page_size: 100,
15
+ order_by: nil,
16
+ order_direction: nil)
17
+ query = {
18
+ page: page,
19
+ page_size: page_size
20
+ }
21
+ query[:order_by] = order_by if order_by
22
+ query[:order_direction] = order_direction if order_direction
23
+
24
+ get("#{@url_api_path}/contact/companies", query)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PapierkramApi
4
+ module V1
5
+ module Endpoints
6
+ module Contact
7
+ # This class is responsible for all the API calls related to companies' persons connections.
8
+ class CompaniesPersons < PapierkramApi::V1::Endpoints::Base
9
+ def by(company_id:, id:)
10
+ get("#{@url_api_path}/contact/companies/#{company_id}/persons/#{id}")
11
+ end
12
+
13
+ def all(company_id:, page: 1, page_size: 100, order_by: nil, order_direction: nil)
14
+ query = {
15
+ company_id: company_id,
16
+ page: page,
17
+ page_size: page_size
18
+ }
19
+ query[:order_by] = order_by if order_by
20
+ query[:order_direction] = order_direction if order_direction
21
+
22
+ get("#{@url_api_path}/contact/companies/#{company_id}/persons", query)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PapierkramApi
4
+ module V1
5
+ module Endpoints
6
+ module Expense
7
+ # This class is responsible for all the API calls related to expense vouchers.
8
+ class Vouchers < PapierkramApi::V1::Endpoints::Base
9
+ def by(id:, pdf: false)
10
+ if pdf == true
11
+ return get("#{@url_api_path}/expense/vouchers/#{id}/pdf", nil,
12
+ { headers: { 'Content-Type' => 'application/pdf' } })
13
+ end
14
+ get("#{@url_api_path}/expense/vouchers/#{id}")
15
+ end
16
+
17
+ def all(page: 1, # rubocop:disable Metrics/CyclomaticComplexity
18
+ page_size: 100,
19
+ order_by: nil,
20
+ order_direction: nil,
21
+ creditor_id: nil,
22
+ project_id: nil,
23
+ document_date_range_start: nil,
24
+ document_date_range_end: nil)
25
+ query = {
26
+ page: page,
27
+ page_size: page_size
28
+ }
29
+ query[:order_by] = order_by if order_by
30
+ query[:order_direction] = order_direction if order_direction
31
+ query[:creditor_id] = creditor_id if creditor_id
32
+ query[:project_id] = project_id if project_id
33
+ query[:document_date_range_start] = document_date_range_start if document_date_range_start
34
+ if document_date_range_end && document_date_range_start
35
+ query[:document_date_range_end] =
36
+ document_date_range_end
37
+ end
38
+
39
+ get("#{@url_api_path}/expense/vouchers", query)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PapierkramApi
4
+ module V1
5
+ module Endpoints
6
+ module Income
7
+ # This class is responsible for all the API calls related to income estimates.
8
+ class Estimates < PapierkramApi::V1::Endpoints::Base
9
+ def by(id:, pdf: false)
10
+ if pdf == true
11
+ return get("#{@url_api_path}/income/estimates/#{id}/pdf", nil,
12
+ { headers: { 'Content-Type' => 'application/pdf' } })
13
+ end
14
+
15
+ get("#{@url_api_path}/income/estimates/#{id}")
16
+ end
17
+
18
+ def all(page: 1, # rubocop:disable Metrics/CyclomaticComplexity
19
+ page_size: 100,
20
+ order_by: nil,
21
+ order_direction: nil,
22
+ creditor_id: nil,
23
+ project_id: nil,
24
+ document_date_range_start: nil,
25
+ document_date_range_end: nil)
26
+ query = {
27
+ page: page,
28
+ page_size: page_size
29
+ }
30
+ query[:order_by] = order_by if order_by
31
+ query[:order_direction] = order_direction if order_direction
32
+ query[:creditor_id] = creditor_id if creditor_id
33
+ query[:project_id] = project_id if project_id
34
+ query[:document_date_range_start] = document_date_range_start if document_date_range_start
35
+ if document_date_range_end && document_date_range_start
36
+ query[:document_date_range_end] =
37
+ document_date_range_end
38
+ end
39
+
40
+ get("#{@url_api_path}/income/estimates", query)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PapierkramApi
4
+ module V1
5
+ module Endpoints
6
+ module Income
7
+ # This class is responsible for all the API calls related to income invoices.
8
+ class Invoices < PapierkramApi::V1::Endpoints::Base
9
+ def by(id:, pdf: false)
10
+ if pdf == true
11
+ return get("#{@url_api_path}/income/invoices/#{id}/pdf", nil,
12
+ { headers: { 'Content-Type' => 'application/pdf' } })
13
+ end
14
+
15
+ get("#{@url_api_path}/income/invoices/#{id}")
16
+ end
17
+
18
+ def all(page: 1, # rubocop:disable Metrics/CyclomaticComplexity
19
+ page_size: 100,
20
+ order_by: nil,
21
+ order_direction: nil,
22
+ creditor_id: nil,
23
+ project_id: nil,
24
+ document_date_range_start: nil,
25
+ document_date_range_end: nil)
26
+ query = {
27
+ page: page,
28
+ page_size: page_size
29
+ }
30
+ query[:order_by] = order_by if order_by
31
+ query[:order_direction] = order_direction if order_direction
32
+ query[:creditor_id] = creditor_id if creditor_id
33
+ query[:project_id] = project_id if project_id
34
+ query[:document_date_range_start] = document_date_range_start if document_date_range_start
35
+ if document_date_range_end && document_date_range_start
36
+ query[:document_date_range_end] =
37
+ document_date_range_end
38
+ end
39
+
40
+ get("#{@url_api_path}/income/invoices", query)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PapierkramApi
4
+ module V1
5
+ module Endpoints
6
+ module Income
7
+ # This class is responsible for all the API calls related to income propositions.
8
+ class Propositions < PapierkramApi::V1::Endpoints::Base
9
+ def by(id:)
10
+ get("#{@url_api_path}/income/propositions/#{id}")
11
+ end
12
+
13
+ def all
14
+ get("#{@url_api_path}/income/propositions")
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PapierkramApi
4
+ module V1
5
+ module Endpoints
6
+ # This class is responsible for all the API calls related to info connections.
7
+ class Info < PapierkramApi::V1::Endpoints::Base
8
+ def details
9
+ get("#{@url_api_path}/info")
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PapierkramApi
4
+ module V1
5
+ module Endpoints
6
+ # This class is responsible for all the API calls related to projects connections.
7
+ class Projects < PapierkramApi::V1::Endpoints::Base
8
+ def by(id:)
9
+ get("#{@url_api_path}/projects/#{id}")
10
+ end
11
+
12
+ def all(page: 1, per_page: 100, order_by: nil, order_direction: nil, company_id: nil)
13
+ query = {
14
+ page: page,
15
+ per_page: per_page
16
+ }
17
+ query[:order_by] = order_by if order_by
18
+ query[:order_direction] = order_direction if order_direction
19
+ query[:company_id] = company_id if company_id
20
+
21
+ get("#{@url_api_path}/projects", query)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PapierkramApi
4
+ module V1
5
+ module Endpoints
6
+ module Tracker
7
+ # This class is responsible for all the API calls related to tracker tasks connections.
8
+ class Tasks < PapierkramApi::V1::Endpoints::Base
9
+ def by(id:)
10
+ get("#{@url_api_path}/tracker/tasks/#{id}")
11
+ end
12
+
13
+ def all(page: 1,
14
+ page_size: 100,
15
+ order_by: nil,
16
+ order_direction: nil,
17
+ project_id: nil,
18
+ proposition_id: nil)
19
+ query = {
20
+ page: page,
21
+ page_size: page_size
22
+ }
23
+ query[:order_by] = order_by if order_by
24
+ query[:order_direction] = order_direction if order_direction
25
+ query[:project_id] = project_id if project_id
26
+ query[:proposition_id] = proposition_id if proposition_id
27
+ get("#{@url_api_path}/tracker/tasks", query)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PapierkramApi
4
+ module V1
5
+ module Endpoints
6
+ module Tracker
7
+ # This class is responsible for all the API calls related to tracker time entries connections.
8
+ class TimeEntries < PapierkramApi::V1::Endpoints::Base
9
+ def by(id:)
10
+ get("#{@url_api_path}/tracker/time_entries/#{id}")
11
+ end
12
+
13
+ def all(page: 1, # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/ParameterLists
14
+ page_size: 100,
15
+ order_by: nil,
16
+ order_direction: nil,
17
+ project_id: nil,
18
+ task_id: nil,
19
+ invoice_id: nil,
20
+ user_id: nil,
21
+ billing_state: nil,
22
+ start_time_range_start: nil,
23
+ start_time_range_end: nil)
24
+ validate!(billing_state: billing_state,
25
+ start_time_range_start: start_time_range_start,
26
+ start_time_range_end: start_time_range_end)
27
+
28
+ query = {
29
+ page: page,
30
+ page_size: page_size
31
+ }
32
+ query[:order_by] = order_by if order_by
33
+ query[:order_direction] = order_direction if order_direction
34
+ query[:project_id] = project_id if project_id
35
+ query[:task_id] = task_id if task_id
36
+ query[:invoice_id] = invoice_id if invoice_id
37
+ query[:user_id] = user_id if user_id
38
+ query[:billing_state] = billing_state if billing_state
39
+ query[:start_time_range_start] = start_time_range_start if start_time_range_start
40
+ query[:start_time_range_end] = start_time_range_end if start_time_range_end
41
+ get("#{@url_api_path}/tracker/time_entries", query)
42
+ end
43
+
44
+ private
45
+
46
+ def validate!(billing_state:, start_time_range_start:, start_time_range_end:)
47
+ if billing_state && !%w[billed unbilled billable unbillable archived].include?(billing_state)
48
+ raise ArgumentError,
49
+ 'billing_state must be one of: "billed" "unbilled"" billable" "unbillable" "archived"'
50
+ end
51
+ if start_time_range_start && !start_time_range_start.is_a?(Time)
52
+ raise ArgumentError, 'start_time_range_start must be a Time object'
53
+ end
54
+ return unless start_time_range_end && !start_time_range_end.is_a?(Time)
55
+
56
+ raise ArgumentError, 'start_time_range_end must be a Time object'
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PapierkramApi
4
+ module V1
5
+ module Helper
6
+ # Helper module for date related methods
7
+ module DateHelper
8
+ def build_date_string_for_api(date)
9
+ date.strftime('%Y-%m-%d')
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Api
3
+ module PapierkramApi
4
4
  module V1
5
5
  module Helper
6
6
  # This class is responsible to convert a response to a pdf file.