Files
dolibarr_shine_reconciliation/lib/dolibarr/client.rb
Kevin Bataille 4decb3cb3c Initial implementation of the reconciliation script
Standalone Ruby script reconciling GoCardless payments, Dolibarr
invoices (via API), and Shine bank statements. Three-pass engine:
GC↔Dolibarr matching, open invoice audit, payout↔bank verification.
Includes dry-run and --fix mode to auto-mark Dolibarr invoices as paid.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 00:23:07 +01:00

84 lines
2.1 KiB
Ruby

# frozen_string_literal: true
require "httparty"
require "json"
module Dolibarr
class Client
include HTTParty
class NotFoundError < StandardError; end
class ValidationError < StandardError; end
class Error < StandardError; end
def initialize(
base_url: ENV.fetch("DOLIBARR_URL"),
api_key: ENV.fetch("DOLIBARR_API_KEY")
)
@base_url = base_url
@headers = {
"DOLAPIKEY" => api_key,
"Content-Type" => "application/json",
"Accept" => "application/json"
}
end
def get(path, params = {})
perform_request(:get, path, params)
end
def post(path, data = {})
perform_request(:post, path, data)
end
def put(path, data = {})
perform_request(:put, path, data)
end
private
def perform_request(method, path, data = {})
url = "#{@base_url}#{path}"
opts = { headers: @headers }
case method
when :get
opts[:query] = data unless data.empty?
response = self.class.get(url, opts)
when :post
opts[:body] = data.to_json
response = self.class.post(url, opts)
when :put
opts[:body] = data.to_json
response = self.class.put(url, opts)
end
handle_response(response)
rescue => e
raise Error, "Connection error: #{e.message}"
end
def handle_response(response)
return nil if response.body.nil? || response.body.strip.empty?
code = response.code.to_i
case code
when 200..299
response.parsed_response
when 404
raise NotFoundError, "Not found (404)"
when 400
raise ValidationError, "Validation error: #{response.body}"
when 401
raise Error, "Authentication failed (401) — check DOLIBARR_API_KEY"
when 500..599
raise Error, "Dolibarr server error (#{code}): #{response.body}"
else
raise Error, "Unexpected status #{code}: #{response.body}"
end
rescue JSON::ParserError => e
raise Error, "JSON parse error: #{e.message}"
end
end
end