Last active
May 21, 2024 17:17
-
-
Save dwilkie/18fcfd29c60a15d1e719d10e06e492df to your computer and use it in GitHub Desktop.
Bulk Download all Invoices from Wave
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
*.csv | |
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require_relative "wave_client" | |
wave_client = WaveClient.new | |
response = wave_client.fetch_invoices | |
response.json_body.each do |invoice| | |
wave_client.delete_invoice(invoice.fetch("id")) | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Usage | |
# 1. Use WaveConnect to download a list of invoices ensure you select the PDF Download Link option | |
# 2. Extract the link using the following formula in Excel/Google Sheets: | |
# =REGEXEXTRACT(FORMULATEXT(T2),"""(.+)"",") | |
# 3. Add the column header "Raw Link" for the extracted link | |
# 4. Export as CSV and save the file as "invoices.csv" | |
# 5. Run this script with the following WAVE_BUSINESS_ID="change-me" WAVE_API_TOKEN="change-me" ruby download_invoices.rb | |
require "csv" | |
require "pry" | |
require "money" | |
require "uri" | |
require "open-uri" | |
require_relative "wave_client" | |
INVOICES_CSV = "invoices.csv".freeze | |
LINK_HEADER = "raw_link".freeze | |
wave_client = WaveClient.new | |
CSV.foreach(INVOICES_CSV, headers: true) do |row| | |
row_data = row.to_h.transform_keys { |key| key.to_s.strip.gsub(/\s/, "_").downcase } | |
pdf_link = row_data.fetch(LINK_HEADER) | |
invoice_date = row_data.fetch("invoice_date") | |
invoice_total = row_data.fetch("invoice_total") | |
invoice_currency = row_data.fetch("currency") | |
invoice_status = row_data.fetch("status") | |
invoice_customer = row_data.fetch("customer_name") | |
invoice_amount = Money.from_amount(invoice_total.to_d, invoice_currency) | |
output_filename = [ | |
invoice_date, | |
invoice_amount.cents.to_s, | |
invoice_currency, | |
invoice_status, | |
invoice_customer.strip.gsub(/\s/, "_").downcase | |
].join("_") | |
invoice_id, public_invoice_id = URI(pdf_link).path.split("/").last(2) | |
response = wave_client.generate_invoice_pdf(invoice_id:, public_invoice_id:) | |
File.write("#{output_filename}.pdf", URI(response.attributes.fetch("pdfUrl")).open.read) | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "base64" | |
require "uri" | |
require "net/http" | |
require "net/https" | |
require "json" | |
class WaveClient | |
class Response | |
attr_reader :http_response | |
def initialize(http_response) | |
@http_response = http_response | |
end | |
def json_body | |
JSON.parse(http_response.body) | |
end | |
def fetch(key) | |
json_body.fetch(key.to_s) | |
end | |
end | |
class GraphQLResponse < Response | |
attr_reader :key | |
def initialize(http_response, key: nil) | |
super(http_response) | |
@key = key | |
end | |
def attributes | |
json_body.dig("data", key) | |
end | |
end | |
GRAPHQL_API_ENDPOINT = URI("https://gql.waveapps.com/graphql/internal").freeze | |
API_ENDPOINT = URI("https://api.waveapps.com") | |
attr_reader :graphql_http_client, :api_http_client, :api_token, :business_id | |
def initialize(**options) | |
@graphql_http_client = options.fetch(:graphql_http_client) { default_http_client(endpoint: GRAPHQL_API_ENDPOINT) } | |
@api_http_client = options.fetch(:api_http_client) { default_http_client(endpoint: API_ENDPOINT) } | |
@api_token = options.fetch(:api_token) { ENV.fetch("WAVE_API_TOKEN") } | |
@business_id = options.fetch(:business_id) { ENV.fetch("WAVE_BUSINESS_ID") } | |
end | |
def generate_invoice_pdf(public_invoice_id:, invoice_id:) | |
do_graphql_request( | |
response_key: "publicInvoiceGeneratePdf", | |
body: { | |
operationName: "PublicInvoiceGeneratePdf", | |
variables: { | |
input: { | |
id: Base64.strict_encode64("Business:#{business_id};PublicInvoice:#{public_invoice_id}"), | |
invoiceId: Base64.strict_encode64("Business:#{business_id};Invoice:#{invoice_id}"), | |
} | |
}, | |
query: <<-'GRAPHQL' | |
mutation PublicInvoiceGeneratePdf($input: PublicInvoiceGeneratePdfInput!) { | |
publicInvoiceGeneratePdf(input: $input) { | |
pdfUrl | |
} | |
} | |
GRAPHQL | |
} | |
) | |
end | |
def delete_invoice(invoice_id) | |
do_api_request( | |
"/businesses/#{business_id}/invoices/#{invoice_id}/", | |
request_method: Net::HTTP::Delete | |
) | |
end | |
def fetch_invoices | |
do_api_request( | |
"/businesses/#{business_id}/invoices/", | |
request_method: Net::HTTP::Get | |
) | |
end | |
private | |
def default_http_client(endpoint:) | |
http = Net::HTTP.new(endpoint.host, endpoint.port) | |
http.use_ssl = true | |
http.verify_mode = OpenSSL::SSL::VERIFY_PEER | |
http | |
end | |
def do_graphql_request(body:, response_key:) | |
request = Net::HTTP::Post.new(GRAPHQL_API_ENDPOINT) | |
request.add_field("Authorization", "Bearer #{api_token}") | |
request.add_field("Content-Type", "application/json") | |
request.body = JSON.dump(body) | |
GraphQLResponse.new(graphql_http_client.request(request), key: response_key) | |
end | |
def do_api_request(path, request_method:) | |
request = request_method.new("#{API_ENDPOINT}#{path}") | |
request.add_field("Authorization", "Bearer #{api_token}") | |
request.add_field("Content-Type", "application/json") | |
Response.new(api_http_client.request(request)) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment