Skip to content

Commit 549161b

Browse files
committed
feat(taxes): support product provider payloads
1 parent 7e5a8c9 commit 549161b

11 files changed

Lines changed: 324 additions & 20 deletions

File tree

app/services/integrations/aggregator/base_payload.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ def fixed_charge_item(fee)
3030
lookup_mapping("AddOn", fee.fixed_charge_add_on.id)
3131
end
3232

33+
def product_item(fee)
34+
lookup_mapping("Product", fee.invoiceable_id)
35+
end
36+
3337
def account_item
3438
lookup_collection_mapping(:account)
3539
end

app/services/integrations/aggregator/taxes/credit_notes/payloads/anrok.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ def body
4141
def cn_item(item)
4242
fee = item.fee
4343

44-
mapped_item = if fee.charge?
44+
mapped_item = if fee.product?
45+
product_item(fee)
46+
elsif fee.charge?
4547
billable_metric_item(fee)
4648
elsif fee.add_on_id.present?
4749
add_on_item(fee)

app/services/integrations/aggregator/taxes/credit_notes/payloads/avalara.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ def body
5050
def cn_item(item)
5151
fee = item.fee
5252

53-
mapped_item = if fee.charge?
53+
mapped_item = if fee.product?
54+
product_item(fee)
55+
elsif fee.charge?
5456
billable_metric_item(fee)
5557
elsif fee.add_on_id.present?
5658
add_on_item(fee)

app/services/integrations/aggregator/taxes/invoices/payloads/anrok.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ def body
3939
end
4040

4141
def fee_item(fee)
42-
mapped_item = if fee.charge?
42+
mapped_item = if fee.product?
43+
product_item(fee)
44+
elsif fee.charge?
4345
billable_metric_item(fee)
4446
elsif fee.add_on_id.present?
4547
add_on_item(fee)

app/services/integrations/aggregator/taxes/invoices/payloads/avalara.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ def body
4646
end
4747

4848
def fee_item(fee)
49-
mapped_item = if fee.charge?
49+
mapped_item = if fee.product?
50+
product_item(fee)
51+
elsif fee.charge?
5052
billable_metric_item(fee)
5153
elsif fee.add_on_id.present?
5254
add_on_item(fee)

spec/services/integrations/aggregator/base_payload_spec.rb

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,44 @@
99
let(:billing_entity) { create(:billing_entity, organization:) }
1010
let(:integration) { create(:anrok_integration, organization:) }
1111
let(:product) { create(:product, organization:) }
12+
let(:fee) { instance_double(Fee, invoiceable_id: product.id) }
1213

1314
describe "Product mapping lookup" do
14-
let!(:fallback_mapping) do
15-
create(
15+
it "returns the Billing Entity Product mapping" do
16+
create(:anrok_mapping, integration:, organization:, mappable: product)
17+
product_mapping = create(:anrok_mapping, integration:, organization:, billing_entity:, mappable: product)
18+
19+
expect(payload.product_item(fee)).to eq(product_mapping)
20+
end
21+
22+
it "returns the organization Product mapping when no Billing Entity mapping exists" do
23+
product_mapping = create(:anrok_mapping, integration:, organization:, mappable: product)
24+
25+
expect(payload.product_item(fee)).to eq(product_mapping)
26+
end
27+
28+
it "returns the Billing Entity fallback when the Product has no mapping" do
29+
create(:anrok_collection_mapping, integration:, organization:, mapping_type: :fallback_item)
30+
fallback_mapping = create(
1631
:anrok_collection_mapping,
1732
integration:,
1833
organization:,
34+
billing_entity:,
1935
mapping_type: :fallback_item
2036
)
21-
end
2237

23-
it "returns the integration fallback when the Product has no mapping" do
24-
mapping = payload.send(:lookup_mapping, "Product", product.id)
25-
26-
expect(mapping).to eq(fallback_mapping)
38+
expect(payload.product_item(fee)).to eq(fallback_mapping)
2739
end
2840

29-
context "when the Product has a mapping" do
30-
let!(:product_mapping) do
31-
create(:anrok_mapping, integration:, organization:, mappable: product)
32-
end
33-
34-
it "returns the Product mapping" do
35-
mapping = payload.send(:lookup_mapping, "Product", product.id)
41+
it "returns the organization fallback when no Product or Billing Entity mapping exists" do
42+
fallback_mapping = create(
43+
:anrok_collection_mapping,
44+
integration:,
45+
organization:,
46+
mapping_type: :fallback_item
47+
)
3648

37-
expect(mapping).to eq(product_mapping)
38-
end
49+
expect(payload.product_item(fee)).to eq(fallback_mapping)
3950
end
4051
end
4152
end

spec/services/integrations/aggregator/taxes/credit_notes/payloads/anrok_spec.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,45 @@ def build_expected_payload(mapping_codes)
5656
end
5757
end
5858

59+
context "with a Product fee" do
60+
subject(:fee_payload) do
61+
described_class.new(integration:, customer:, integration_customer:, credit_note:).body.sole["fees"].sole
62+
end
63+
64+
let(:organization) { create(:organization) }
65+
let(:billing_entity) { create(:billing_entity, organization:) }
66+
let(:integration) { create(:anrok_integration, organization:) }
67+
let(:customer) { create(:customer, organization:, billing_entity:) }
68+
let(:integration_customer) { create(:anrok_customer, integration:, customer:) }
69+
let(:invoice) { create(:invoice, organization:, billing_entity:, customer:) }
70+
let(:subscription) { create(:subscription, organization:, customer:) }
71+
let(:product) { create(:product, organization:) }
72+
let(:rate_card) { create(:rate_card, organization:, product:) }
73+
let(:rate) { create(:rate_card_rate, organization:, rate_card:) }
74+
let(:fee) { create(:product_fee, invoice:, subscription:, rate_card_rate: rate, units: 2, amount_cents: 250) }
75+
let(:credit_note) { create(:credit_note, organization:, customer:, invoice:) }
76+
77+
before do
78+
create(:credit_note_item, credit_note:, fee:, amount_cents: 190)
79+
create(
80+
:anrok_mapping,
81+
integration:,
82+
organization:,
83+
billing_entity:,
84+
mappable: product,
85+
settings: {external_id: "product-code"}
86+
)
87+
end
88+
89+
it "uses the Product mapping" do
90+
expect(fee_payload).to eq(
91+
"item_id" => fee.item_id,
92+
"item_code" => "product-code",
93+
"amount_cents" => -190
94+
)
95+
end
96+
end
97+
5998
context "with precision edge case" do
6099
let(:integration) { create(:anrok_integration) }
61100
let(:customer) { create(:customer, organization: integration.organization) }

spec/services/integrations/aggregator/taxes/credit_notes/payloads/avalara_spec.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,4 +146,44 @@ def build_expected_payload(mapping_codes)
146146
]
147147
end
148148
end
149+
150+
context "with a Product fee" do
151+
subject(:fee_payload) do
152+
described_class.new(integration:, customer:, integration_customer:, credit_note:).body.sole["fees"].sole
153+
end
154+
155+
let(:organization) { create(:organization) }
156+
let(:billing_entity) { create(:billing_entity, organization:) }
157+
let(:integration) { create(:avalara_integration, organization:) }
158+
let(:customer) { create(:customer, organization:, billing_entity:) }
159+
let(:integration_customer) { create(:avalara_customer, integration:, customer:) }
160+
let(:invoice) { create(:invoice, organization:, billing_entity:, customer:) }
161+
let(:subscription) { create(:subscription, organization:, customer:) }
162+
let(:product) { create(:product, :fixed, organization:) }
163+
let(:rate_card) { create(:rate_card, organization:, product:) }
164+
let(:rate) { create(:rate_card_rate, organization:, rate_card:) }
165+
let(:fee) { create(:product_fee, invoice:, subscription:, rate_card_rate: rate, units: 2, amount_cents: 250) }
166+
let(:credit_note) { create(:credit_note, organization:, customer:, invoice:) }
167+
168+
before do
169+
create(:credit_note_item, credit_note:, fee:, amount_cents: 190)
170+
create(
171+
:avalara_mapping,
172+
integration:,
173+
organization:,
174+
billing_entity:,
175+
mappable: product,
176+
settings: {external_id: "product-code"}
177+
)
178+
end
179+
180+
it "uses the Product mapping" do
181+
expect(fee_payload).to eq(
182+
"item_id" => fee.item_id,
183+
"item_code" => "product-code",
184+
"unit" => 2.0,
185+
"amount" => "-1.9"
186+
)
187+
end
188+
end
149189
end

spec/services/integrations/aggregator/taxes/invoices/payloads/anrok_spec.rb

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,70 @@ def build_expected_payload(mapping_codes)
6767
end
6868
end
6969

70+
context "with Product fees" do
71+
subject(:fees_payload) do
72+
described_class.new(integration:, customer:, invoice:, integration_customer:, fees:).body.sole["fees"]
73+
end
74+
75+
let(:organization) { create(:organization) }
76+
let(:billing_entity) { create(:billing_entity, organization:) }
77+
let(:integration) { create(:anrok_integration, organization:) }
78+
let(:customer) { create(:customer, organization:, billing_entity:) }
79+
let(:integration_customer) { create(:anrok_customer, integration:, customer:) }
80+
let(:invoice) { create(:invoice, organization:, billing_entity:, customer:) }
81+
let(:subscription) { create(:subscription, organization:, customer:) }
82+
let(:fixed_product) { create(:product, :fixed, organization:) }
83+
let(:usage_product) { create(:product, organization:) }
84+
let(:fixed_rate_card) { create(:rate_card, organization:, product: fixed_product) }
85+
let(:usage_rate_card) { create(:rate_card, organization:, product: usage_product) }
86+
let(:fixed_rate) { create(:rate_card_rate, organization:, rate_card: fixed_rate_card) }
87+
let(:usage_rate) { create(:rate_card_rate, organization:, rate_card: usage_rate_card) }
88+
let(:fixed_fee) do
89+
create(:product_fee, invoice:, subscription:, rate_card_rate: fixed_rate, units: 2, amount_cents: 250)
90+
end
91+
let(:usage_fee) do
92+
create(:product_fee, invoice:, subscription:, rate_card_rate: usage_rate, units: 3, amount_cents: 375)
93+
end
94+
let(:fees) { [fixed_fee, usage_fee] }
95+
96+
before do
97+
create(
98+
:anrok_mapping,
99+
integration:,
100+
organization:,
101+
billing_entity:,
102+
mappable: fixed_product,
103+
settings: {external_id: "fixed-product"}
104+
)
105+
create(
106+
:anrok_mapping,
107+
integration:,
108+
organization:,
109+
mappable: usage_product,
110+
settings: {external_id: "usage-product"}
111+
)
112+
end
113+
114+
it "uses Product mappings for fixed and usage fees" do
115+
expect(fees_payload).to match_array(
116+
[
117+
{
118+
"item_key" => fixed_fee.item_key,
119+
"item_id" => fixed_fee.id,
120+
"item_code" => "fixed-product",
121+
"amount_cents" => 250
122+
},
123+
{
124+
"item_key" => usage_fee.item_key,
125+
"item_id" => usage_fee.id,
126+
"item_code" => "usage-product",
127+
"amount_cents" => 375
128+
}
129+
]
130+
)
131+
end
132+
end
133+
70134
describe "shipping address fallback" do
71135
let(:integration) { create(:anrok_integration) }
72136
let(:customer) do

spec/services/integrations/aggregator/taxes/invoices/payloads/avalara_spec.rb

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,72 @@ def build_expected_payload(mapping_codes, negative_amount: false)
8080
end
8181
end
8282

83+
context "with Product fees" do
84+
subject(:fees_payload) do
85+
described_class.new(integration:, customer:, invoice:, integration_customer:, fees:).body.sole["fees"]
86+
end
87+
88+
let(:organization) { create(:organization) }
89+
let(:billing_entity) { create(:billing_entity, organization:) }
90+
let(:integration) { create(:avalara_integration, organization:) }
91+
let(:customer) { create(:customer, organization:, billing_entity:) }
92+
let(:integration_customer) { create(:avalara_customer, integration:, customer:) }
93+
let(:invoice) { create(:invoice, organization:, billing_entity:, customer:) }
94+
let(:subscription) { create(:subscription, organization:, customer:) }
95+
let(:fixed_product) { create(:product, :fixed, organization:) }
96+
let(:usage_product) { create(:product, organization:) }
97+
let(:fixed_rate_card) { create(:rate_card, organization:, product: fixed_product) }
98+
let(:usage_rate_card) { create(:rate_card, organization:, product: usage_product) }
99+
let(:fixed_rate) { create(:rate_card_rate, organization:, rate_card: fixed_rate_card) }
100+
let(:usage_rate) { create(:rate_card_rate, organization:, rate_card: usage_rate_card) }
101+
let(:fixed_fee) do
102+
create(:product_fee, invoice:, subscription:, rate_card_rate: fixed_rate, units: 2, amount_cents: 250)
103+
end
104+
let(:usage_fee) do
105+
create(:product_fee, invoice:, subscription:, rate_card_rate: usage_rate, units: 3, amount_cents: 375)
106+
end
107+
let(:fees) { [fixed_fee, usage_fee] }
108+
109+
before do
110+
create(
111+
:avalara_mapping,
112+
integration:,
113+
organization:,
114+
billing_entity:,
115+
mappable: fixed_product,
116+
settings: {external_id: "fixed-product"}
117+
)
118+
create(
119+
:avalara_mapping,
120+
integration:,
121+
organization:,
122+
mappable: usage_product,
123+
settings: {external_id: "usage-product"}
124+
)
125+
end
126+
127+
it "uses Product mappings for fixed and usage fees" do
128+
expect(fees_payload).to match_array(
129+
[
130+
{
131+
"item_key" => fixed_fee.item_key,
132+
"item_id" => fixed_fee.id,
133+
"item_code" => "fixed-product",
134+
"unit" => 2.0,
135+
"amount" => "2.5"
136+
},
137+
{
138+
"item_key" => usage_fee.item_key,
139+
"item_id" => usage_fee.id,
140+
"item_code" => "usage-product",
141+
"unit" => 3.0,
142+
"amount" => "3.75"
143+
}
144+
]
145+
)
146+
end
147+
end
148+
83149
describe "shipping address fallback" do
84150
let(:integration) { create(:avalara_integration) }
85151
let(:customer) do

0 commit comments

Comments
 (0)