Files
ubicloud/serializers/invoice.rb
Enes Cakir 9945ca6bc6 Show the VAT amount in EUR as well
We charge customers in USD, but since we will start charging VAT, we
also need to show the VAT amount in EUR.

We finalize invoices at the end of each month. While doing this, we will
check the EUR rates [^1] and provide it as an argument.

[^1]: https://www.belastingdienst.nl/wps/wcm/connect/nl/douane_voor_bedrijven/content/hulpmiddel-wisselkoersen
2025-03-17 22:09:36 +03:00

89 lines
4.3 KiB
Ruby

# frozen_string_literal: true
require "countries"
class Serializers::Invoice < Serializers::Base
def self.serialize_internal(inv, options = {})
base = {
ubid: inv.id ? inv.ubid : "current",
path: inv.path,
name: inv.name,
filename: "Ubicloud-#{inv.begin_time.strftime("%Y-%m")}-#{inv.invoice_number}",
date: inv.created_at.strftime("%B %d, %Y"),
begin_time: inv.begin_time.strftime("%b %d, %Y"),
end_time: inv.end_time.strftime("%b %d, %Y"),
subtotal: "$%0.02f" % inv.content["subtotal"],
credit: "$%0.02f" % inv.content["credit"],
free_inference_tokens_credit: "$%0.02f" % (inv.content["free_inference_tokens_credit"] || 0),
discount: "$%0.02f" % inv.content["discount"],
total: "$%0.02f" % inv.content["cost"],
status: inv.status,
invoice_number: inv.invoice_number
}
if options[:detailed]
base.merge!(
billing_name: inv.content.dig("billing_info", "name"),
billing_email: inv.content.dig("billing_info", "email"),
billing_address: inv.content.dig("billing_info", "address"),
billing_country: ISO3166::Country.new(inv.content.dig("billing_info", "country"))&.common_name,
billing_city: inv.content.dig("billing_info", "city"),
billing_state: inv.content.dig("billing_info", "state"),
billing_postal_code: inv.content.dig("billing_info", "postal_code"),
billing_in_eu_vat: inv.content.dig("billing_info", "in_eu_vat"),
tax_id: inv.content.dig("billing_info", "tax_id"),
company_name: inv.content.dig("billing_info", "company_name"),
issuer_name: inv.content.dig("issuer_info", "name"),
issuer_address: inv.content.dig("issuer_info", "address"),
issuer_country: ISO3166::Country.new(inv.content.dig("issuer_info", "country"))&.common_name,
issuer_city: inv.content.dig("issuer_info", "city"),
issuer_state: inv.content.dig("issuer_info", "state"),
issuer_postal_code: inv.content.dig("issuer_info", "postal_code"),
issuer_tax_id: inv.content.dig("issuer_info", "tax_id"),
issuer_trade_id: inv.content.dig("issuer_info", "trade_id"),
issuer_in_eu_vat: inv.content.dig("issuer_info", "in_eu_vat"),
vat_rate: inv.content.dig("vat_info", "rate"),
vat_amount: "$%0.02f" % (inv.content.dig("vat_info", "amount") || 0),
vat_amount_eur: "€%0.02f" % ((inv.content.dig("vat_info", "amount") || 0) * (inv.content.dig("vat_info", "eur_rate") || 0)),
vat_reversed: inv.content.dig("vat_info", "reversed"),
items: inv.content["resources"].flat_map do |resource|
resource["line_items"].map do |line_item|
{
name: resource["resource_name"],
description: line_item["description"],
duration: line_item["duration"].to_i,
amount: line_item["amount"],
cost: line_item["cost"],
cost_humanized: humanized_cost(line_item["cost"]),
usage: BillingRate.line_item_usage(line_item["resource_type"], line_item["resource_family"], line_item["amount"], line_item["duration"])
}
end
end.group_by { _1[:description] }.flat_map do |description, line_items|
if line_items.count > 100 && description.end_with?("Address", "Virtual Machine")
duration_sum = line_items.sum { _1[:duration] }
amount_sum = line_items.sum { _1[:amount] }
cost_sum = line_items.sum { _1[:cost] }
{
name: "#{line_items.count} x #{description} (Aggregated)",
description: description,
duration: duration_sum,
amount: amount_sum,
cost: cost_sum,
cost_humanized: humanized_cost(cost_sum),
usage: BillingRate.line_item_usage(line_items.first["resource_type"], line_items.first["resource_family"], amount_sum, duration_sum)
}
else
line_items
end
end.sort_by { _1[:name] }
)
end
base
end
def self.humanized_cost(cost)
(cost < 0.001) ? "less than $0.001" : "$%0.03f" % cost
end
end