summaryrefslogtreecommitdiff
path: root/lib/super_good/solidus_taxjar/tax_calculator.rb
blob: 4efa367e8baa102d7c13c5ca814e77eec75a656d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
module SuperGood
  module SolidusTaxJar
    class TaxCalculator
      def self.default_api
        ::SuperGood::SolidusTaxJar::API.new
      end

      def initialize(order, api: self.class.default_api)
        @order = order
        @api = api
      end

      def calculate
        return no_tax if order.tax_address.empty?

        Spree::Tax::OrderTax.new(
          order_id: order.id,
          line_item_taxes: line_item_taxes,
          shipment_taxes: shipment_taxes
        )
      end

      private

      attr_reader :order, :api

      def line_item_taxes
        @line_item_taxes ||=
          if taxjar_breakdown
            taxjar_breakdown.line_items.map do |line_item|
              Spree::Tax::ItemTax.new(
                item_id: line_item.id.to_i,
                label: "Sales Tax",
                tax_rate: tax_rate,
                amount: line_item.tax_collectable,
                included_in_price: false
              )
            end
          else
            []
          end
      end

      def shipment_taxes
        @shipment_taxes ||=
          if (total_shipping_tax = taxjar_tax.shipping) != 0
            # Distribute shipping tax across shipments:
            # TaxJar does not provide a breakdown of shipping taxes, so we have
            # to proportionally distribute the tax across the shipments,
            # accounting for rounding errors.
            tax_items = []
            remaining_tax = total_shipping_tax
            shipments = order.shipments.to_a
            total_shipping_cost = shipments.sum(&:total_before_tax)

            shipments[0...-1].each do |shipment|
              percentage_of_tax = shipment.total_before_tax / total_shipping_cost
              shipping_tax = (percentage_of_tax * total_shipping_tax).round(2)
              remaining_tax -= shipping_tax

              tax_items << ::Spree::Tax::ItemTax.new(
                item_id: shipment.id,
                label: "Sales Tax",
                tax_rate: tax_rate,
                amount: shipping_tax,
                included_in_price: false
              )
            end

            tax_items << ::Spree::Tax::ItemTax.new(
              item_id: shipments.last.id,
              label: "Sales Tax",
              tax_rate: tax_rate,
              amount: remaining_tax,
              included_in_price: false
            )

            tax_items
          else
            []
          end
      end

      def taxjar_breakdown
        @taxjar_breakdown ||= taxjar_tax.breakdown
      end

      def taxjar_tax
        @taxjar_taxes ||= api.tax_for(order)
      end

      def no_tax
        Spree::Tax::OrderTax.new(
          order_id: order.id,
          line_item_taxes: [],
          shipment_taxes: []
        )
      end

      def tax_rate
        Spree::TaxRate.find_by(name: "Sales Tax")
      end
    end
  end
end