Add resource definitions for all 22 API tags, wire client accessors and require tree

This commit is contained in:
2026-05-29 15:01:12 +02:00
parent 8185659f9c
commit 3b4d5ae5c3
21 changed files with 566 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
# frozen_string_literal: true
module Fiken
# The entry point. Holds the connection and exposes the resource accessors.
#
# client = Fiken::Client.new(token: ENV["FIKEN_TOKEN"])
# client.user
# client.companies.list
# client.contacts("my-company-slug").find(123)
# client.invoices("my-company-slug").drafts.create(invoice_attrs)
class Client
attr_reader :connection
def initialize(token: nil, access_token: nil, **options)
bearer = token || access_token
raise ArgumentError, "provide :token or :access_token" if bearer.nil? || bearer.empty?
@connection = Connection.new(token: bearer, **options)
end
# GET /user
def user
Object.new(connection.get("/user").body)
end
# GET /companies, GET /companies/{slug}
def companies
Resources::Companies.new(self)
end
# Convenience: fetch a single company by slug.
def company(slug)
companies.find(slug)
end
# Company-scoped resources. Each takes the company slug.
def accounts(slug) = Resources::Accounts.new(self, slug)
def account_balances(slug) = Resources::AccountBalances.new(self, slug)
def activities(slug) = Resources::Activities.new(self, slug)
def bank_accounts(slug) = Resources::BankAccounts.new(self, slug)
def bank_balances(slug) = Resources::BankBalances.new(self, slug)
def contacts(slug) = Resources::Contacts.new(self, slug)
def credit_notes(slug) = Resources::CreditNotes.new(self, slug)
def groups(slug) = Resources::Groups.new(self, slug)
def inbox(slug) = Resources::Inbox.new(self, slug)
def invoices(slug) = Resources::Invoices.new(self, slug)
def journal_entries(slug) = Resources::JournalEntries.new(self, slug)
def offers(slug) = Resources::Offers.new(self, slug)
def order_confirmations(slug) = Resources::OrderConfirmations.new(self, slug)
def products(slug) = Resources::Products.new(self, slug)
def projects(slug) = Resources::Projects.new(self, slug)
def purchases(slug) = Resources::Purchases.new(self, slug)
def sales(slug) = Resources::Sales.new(self, slug)
def time_entries(slug) = Resources::TimeEntries.new(self, slug)
def time_users(slug) = Resources::TimeUsers.new(self, slug)
def transactions(slug) = Resources::Transactions.new(self, slug)
end
end
+25
View File
@@ -0,0 +1,25 @@
# frozen_string_literal: true
module Fiken
module Resources
# /companies/{slug}/accounts — find by account code (e.g. "1500:10001").
class Accounts < Resource::Base
include Resource::Listable
include Resource::Findable
def resource_path
"accounts"
end
end
# /companies/{slug}/accountBalances
class AccountBalances < Resource::Base
include Resource::Listable
include Resource::Findable
def resource_path
"accountBalances"
end
end
end
end
+18
View File
@@ -0,0 +1,18 @@
# frozen_string_literal: true
module Fiken
module Resources
# /companies/{slug}/activities (time-tracking activities)
class Activities < Resource::Base
include Resource::Listable
include Resource::Findable
include Resource::Creatable
include Resource::PatchUpdatable
include Resource::Deletable
def resource_path
"activities"
end
end
end
end
+25
View File
@@ -0,0 +1,25 @@
# frozen_string_literal: true
module Fiken
module Resources
# /companies/{slug}/bankAccounts
class BankAccounts < Resource::Base
include Resource::Listable
include Resource::Findable
include Resource::Creatable
def resource_path
"bankAccounts"
end
end
# /companies/{slug}/bankBalances
class BankBalances < Resource::Base
include Resource::Listable
def resource_path
"bankBalances"
end
end
end
end
+15
View File
@@ -0,0 +1,15 @@
# frozen_string_literal: true
module Fiken
module Resources
# GET /companies and GET /companies/{companySlug}
class Companies < Resource::Base
include Resource::Listable
include Resource::Findable
def resource_path
"companies"
end
end
end
end
+46
View File
@@ -0,0 +1,46 @@
# frozen_string_literal: true
module Fiken
module Resources
# /companies/{slug}/contacts and nested contact persons.
class Contacts < Resource::Base
include Resource::Listable
include Resource::Findable
include Resource::Creatable
include Resource::Updatable # PUT
include Resource::Deletable
include Resource::Attachable
def resource_path
"contacts"
end
# Contacts only support uploading attachments, not listing them.
def attachments_listable?
false
end
def contact_persons(contact_id)
ContactPersons.new(client, company_slug, "#{base_path}/#{contact_id}")
end
end
# /companies/{slug}/contacts/{id}/contactPerson
class ContactPersons < Resource::Base
include Resource::Listable
include Resource::Findable
include Resource::Creatable
include Resource::Updatable # PUT
include Resource::Deletable
def initialize(client, company_slug, parent_path)
super(client, company_slug)
@parent_path = parent_path
end
def base_path
"#{@parent_path}/contactPerson"
end
end
end
end
+32
View File
@@ -0,0 +1,32 @@
# frozen_string_literal: true
module Fiken
module Resources
# /companies/{slug}/creditNotes
class CreditNotes < Resource::Base
include Resource::Listable
include Resource::Findable
include Resource::Sendable
include Resource::HasCounter
include Resource::Draftable
def resource_path
"creditNotes"
end
def draft_create_action
"createCreditNote"
end
# POST /creditNotes/full — credit a whole invoice.
def create_full(attributes)
post_create("#{base_path}/full", attributes)
end
# POST /creditNotes/partial — credit selected lines/amounts.
def create_partial(attributes)
post_create("#{base_path}/partial", attributes)
end
end
end
end
+14
View File
@@ -0,0 +1,14 @@
# frozen_string_literal: true
module Fiken
module Resources
# /companies/{slug}/groups (contact groups)
class Groups < Resource::Base
include Resource::Listable
def resource_path
"groups"
end
end
end
end
+23
View File
@@ -0,0 +1,23 @@
# frozen_string_literal: true
module Fiken
module Resources
# /companies/{slug}/inbox — documents awaiting bookkeeping.
class Inbox < Resource::Base
include Resource::Listable
include Resource::Findable
include Resource::Deletable
def resource_path
"inbox"
end
# POST /inbox — multipart upload of an inbox document.
def create(path: nil, io: nil, filename: nil, content_type: "application/octet-stream", **fields)
parts = { "file" => build_file_part(path, io, filename, content_type) }
fields.each { |key, value| parts[key.to_s] = value.to_s unless value.nil? }
build_created(connection.post_multipart(base_path, parts))
end
end
end
end
+25
View File
@@ -0,0 +1,25 @@
# frozen_string_literal: true
module Fiken
module Resources
# /companies/{slug}/invoices
class Invoices < Resource::Base
include Resource::Listable
include Resource::Findable
include Resource::Creatable
include Resource::PatchUpdatable # finalized invoices update via PATCH
include Resource::Attachable
include Resource::Sendable
include Resource::HasCounter
include Resource::Draftable
def resource_path
"invoices"
end
def draft_create_action
"createInvoice"
end
end
end
end
+21
View File
@@ -0,0 +1,21 @@
# frozen_string_literal: true
module Fiken
module Resources
# /companies/{slug}/journalEntries
class JournalEntries < Resource::Base
include Resource::Listable
include Resource::Findable
include Resource::Attachable
def resource_path
"journalEntries"
end
# Creation posts to a sibling path: POST /generalJournalEntries
def create_general(attributes)
post_create("/companies/#{company_slug}/generalJournalEntries", attributes)
end
end
end
end
+22
View File
@@ -0,0 +1,22 @@
# frozen_string_literal: true
module Fiken
module Resources
# /companies/{slug}/offers
class Offers < Resource::Base
include Resource::Listable
include Resource::Findable
include Resource::Sendable
include Resource::HasCounter
include Resource::Draftable
def resource_path
"offers"
end
def draft_create_action
"createOffer"
end
end
end
end
@@ -0,0 +1,26 @@
# frozen_string_literal: true
module Fiken
module Resources
# /companies/{slug}/orderConfirmations
class OrderConfirmations < Resource::Base
include Resource::Listable
include Resource::Findable
include Resource::HasCounter
include Resource::Draftable
def resource_path
"orderConfirmations"
end
def draft_create_action
"createOrderConfirmation"
end
# POST /{id}/createInvoiceDraft — turn a confirmation into an invoice draft.
def create_invoice_draft(confirmation_id)
post_create("#{base_path}/#{confirmation_id}/createInvoiceDraft", nil)
end
end
end
end
+24
View File
@@ -0,0 +1,24 @@
# frozen_string_literal: true
module Fiken
module Resources
# /companies/{slug}/products
class Products < Resource::Base
include Resource::Listable
include Resource::Findable
include Resource::Creatable
include Resource::Updatable # PUT
include Resource::Deletable
def resource_path
"products"
end
# POST /products/salesReport — returns an array of per-product report rows.
def sales_report(attributes)
body = connection.post("#{base_path}/salesReport", attributes).body
Array(body).map { |row| wrap(row) }
end
end
end
end
+18
View File
@@ -0,0 +1,18 @@
# frozen_string_literal: true
module Fiken
module Resources
# /companies/{slug}/projects
class Projects < Resource::Base
include Resource::Listable
include Resource::Findable
include Resource::Creatable
include Resource::PatchUpdatable
include Resource::Deletable
def resource_path
"projects"
end
end
end
end
+29
View File
@@ -0,0 +1,29 @@
# frozen_string_literal: true
module Fiken
module Resources
# /companies/{slug}/purchases
class Purchases < Resource::Base
include Resource::Listable
include Resource::Findable
include Resource::Creatable
include Resource::Attachable
include Resource::Payable
include Resource::Draftable
def resource_path
"purchases"
end
def draft_create_action
"createPurchase"
end
# PATCH /{id}/delete — delete a purchase (with a reason).
def delete(id, attributes = nil)
connection.patch("#{base_path}/#{id}/delete", attributes)
true
end
end
end
end
+39
View File
@@ -0,0 +1,39 @@
# frozen_string_literal: true
module Fiken
module Resources
# /companies/{slug}/sales
class Sales < Resource::Base
include Resource::Listable
include Resource::Findable
include Resource::Creatable
include Resource::Attachable
include Resource::Payable
include Resource::Draftable
def resource_path
"sales"
end
def draft_create_action
"createSale"
end
# PATCH /{id}/settled — mark a sale as settled.
def settle(id, attributes = nil)
patch_one("#{base_path}/#{id}/settled", attributes)
end
# PATCH /{id}/writeOff — write off a sale as a loss.
def write_off(id, attributes = nil)
patch_one("#{base_path}/#{id}/writeOff", attributes)
end
# PATCH /{id}/delete — delete a sale (with a reason).
def delete(id, attributes = nil)
connection.patch("#{base_path}/#{id}/delete", attributes)
true
end
end
end
end
+23
View File
@@ -0,0 +1,23 @@
# frozen_string_literal: true
module Fiken
module Resources
# /companies/{slug}/timeEntries
class TimeEntries < Resource::Base
include Resource::Listable
include Resource::Findable
include Resource::Creatable
include Resource::PatchUpdatable
include Resource::Deletable
def resource_path
"timeEntries"
end
# POST /timeEntries/createInvoiceDraft — invoice selected time entries.
def create_invoice_draft(attributes)
post_create("#{base_path}/createInvoiceDraft", attributes)
end
end
end
end
+15
View File
@@ -0,0 +1,15 @@
# frozen_string_literal: true
module Fiken
module Resources
# /companies/{slug}/timeUsers
class TimeUsers < Resource::Base
include Resource::Listable
include Resource::Findable
def resource_path
"timeUsers"
end
end
end
end
+21
View File
@@ -0,0 +1,21 @@
# frozen_string_literal: true
module Fiken
module Resources
# /companies/{slug}/transactions
class Transactions < Resource::Base
include Resource::Listable
include Resource::Findable
def resource_path
"transactions"
end
# Deletion is a PATCH to /{id}/delete (optionally with a description).
def delete(id, attributes = nil)
connection.patch("#{base_path}/#{id}/delete", attributes)
true
end
end
end
end