Skip to content

Commit

Permalink
Invoicing on prorated credits (#984)
Browse files Browse the repository at this point in the history
* now invoicing credits without debits due to ask from cloudflare

* fixed test
  • Loading branch information
brennen-stripe authored Jan 20, 2023
1 parent cb4ee03 commit f555ba8
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 2 deletions.
3 changes: 2 additions & 1 deletion lib/stripe-force/translate/order/amendments.rb
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ def self.generate_proration_credits_from_terminated_phase_items(user:, sf_order_

credit_stripe_item = Stripe::SubscriptionItem.construct_from({
metadata: Metadata.stripe_metadata_for_sf_object(user, phase_item.order_line).merge(
Metadata.metadata_key(user, MetadataKeys::CREDIT) => true
Metadata.metadata_key(user, MetadataKeys::CREDIT) => true,
Metadata.metadata_key(user, MetadataKeys::PRORATION) => true,
),
})

Expand Down
123 changes: 122 additions & 1 deletion test/integration/amendments/test_proration_amendments.rb
Original file line number Diff line number Diff line change
Expand Up @@ -755,7 +755,7 @@ class Critic::ProratedAmendmentTranslation < Critic::OrderAmendmentFunctionalTes
end

# https://jira.corp.stripe.com/browse/PLATINT-2091
it 'terminates a line without adding a new one' do
it 'fully terminates an order, without issuing credit' do
# Does not create a credit for a fully terminated order
@user.enable_feature FeatureFlags::TERMINATED_ORDER_ITEM_CREDIT, update: true
@user.enable_feature FeatureFlags::TEST_CLOCKS, update: true
Expand Down Expand Up @@ -844,6 +844,127 @@ class Critic::ProratedAmendmentTranslation < Critic::OrderAmendmentFunctionalTes
assert_equal(0, invoice_events.count)
end

it 'terminates one of an orders items without adding a new one' do
# ie, issues a prorated credit without a prorated debit
@user.enable_feature FeatureFlags::TERMINATED_ORDER_ITEM_CREDIT, update: true
@user.enable_feature FeatureFlags::TEST_CLOCKS, update: true

# initial order: 1yr contract (one billing cycle), silver tier (one item), gold tier (one item)
# second order: remove silver tier (one item) two months into contract
# - cancels remaining silver tier (issuing credit via negative line item for $1000)
# - resulting invoice will have a total of -$1000, ie $1000 prorated credit

yearly_silver_tier_price = 1200_00
yearly_gold_tier_price = 2400_00
contract_term = 12
amendment_term = 10

initial_order_start_date = now_time
initial_order_end_date = initial_order_start_date + contract_term
amendment_start_date = now_time + (contract_term - amendment_term).months
amendment_end_date = amendment_start_date + amendment_term.months

# normalize the amendment_end_date so test doesn't fail EOM
amendment_end_date = StripeForce::Translate::OrderHelpers.anchor_time_to_day_of_month(base_time: amendment_end_date, anchor_day_of_month: initial_order_end_date.day)

sf_silver_tier_product_id, sf_silver_tier_pricebook_id = salesforce_recurring_product_with_price(
price: yearly_silver_tier_price,
additional_product_fields: {
CPQ_QUOTE_BILLING_FREQUENCY => CPQBillingFrequencyOptions::ANNUAL.serialize,
}
)

sf_gold_tier_product_id, sf_gold_tier_pricebook_id = salesforce_recurring_product_with_price(
price: yearly_gold_tier_price,
additional_product_fields: {
CPQ_QUOTE_BILLING_FREQUENCY => CPQBillingFrequencyOptions::ANNUAL.serialize,
}
)

sf_account_id = create_salesforce_account

quote_id = create_salesforce_quote(
sf_account_id: sf_account_id,
additional_quote_fields: {
CPQ_QUOTE_SUBSCRIPTION_START_DATE => format_date_for_salesforce(initial_order_start_date),
CPQ_QUOTE_SUBSCRIPTION_TERM => contract_term,
}
)

# add our two products
quote_with_product = add_product_to_cpq_quote(quote_id, sf_product_id: sf_silver_tier_product_id)
quote_id = calculate_and_save_cpq_quote(quote_with_product)

quote_with_product = add_product_to_cpq_quote(quote_id, sf_product_id: sf_gold_tier_product_id)
quote_id = calculate_and_save_cpq_quote(quote_with_product)

sf_order = create_order_from_cpq_quote(quote_id)

sf_contract = create_contract_from_order(sf_order)
amendment_data = create_quote_data_from_contract_amendment(sf_contract)

# cancel silver tier
amendment_data["lineItems"].first["record"][CPQ_QUOTE_QUANTITY] = 0

amendment_data["record"][CPQ_QUOTE_SUBSCRIPTION_START_DATE] = format_date_for_salesforce(amendment_start_date)
amendment_data["record"][CPQ_QUOTE_SUBSCRIPTION_TERM] = amendment_term
sf_quote_id = calculate_and_save_cpq_quote(amendment_data)

sf_order_amendment = create_order_from_quote_data(amendment_data)
sf_order_amendment_contract = create_contract_from_order(sf_order_amendment)

StripeForce::Translate.perform_inline(@user, sf_order_amendment.Id)

# Get SubscriptionSchedule Id
sf_order.refresh
stripe_subscription_schedule_id = sf_order[prefixed_stripe_field(GENERIC_STRIPE_ID)]
stripe_subscription_schedule = Stripe::SubscriptionSchedule.retrieve(stripe_subscription_schedule_id, @user.stripe_credentials)

# Get Customer
sf_account = sf_get(sf_order['AccountId'])
stripe_customer_id = sf_account[prefixed_stripe_field(GENERIC_STRIPE_ID)]
stripe_customer = stripe_get(stripe_customer_id)
refute_nil(stripe_customer.test_clock)

# Pay off initial invoice
invoices = Stripe::Invoice.list({customer: stripe_customer.id}, @user.stripe_credentials).data

assert_equal(1, invoices.length)

initial_invoice = invoices.first

assert_equal(3600_00, initial_invoice.amount_due)
assert_equal(2, initial_invoice.lines.data.length)

initial_invoice.pay({'paid_out_of_band': true})

# now, let's advance the clock and pretent like we are in the future to fully test autobilling

test_clock = advance_test_clock(stripe_customer, (amendment_start_date + 1.day).to_i)

# simulate sending the webhook
events = Stripe::Event.list({
type: 'invoiceitem.created',
}, @user.stripe_credentials)

invoice_events = events.data.select do |event|
event_object = event.data.object
event_object.test_clock == test_clock.id && event.request.id.nil?
end

assert_equal(1, invoice_events.count)
proration_credit_invoice_event = invoice_events.first

assert_equal("true", proration_credit_invoice_event.data.object.metadata[StripeForce::Translate::Metadata.metadata_key(@user, MetadataKeys::CREDIT)])
assert_equal("true", proration_credit_invoice_event.data.object.metadata[StripeForce::Translate::Metadata.metadata_key(@user, MetadataKeys::PRORATION)])

proration_invoice = T.must(StripeForce::ProrationAutoBill.create_invoice_from_invoice_item_event(@user, proration_credit_invoice_event))

assert_equal(1, proration_invoice.lines.count)
assert_equal(-1000_00, proration_invoice.total)
assert_equal("true", proration_invoice.metadata[StripeForce::Translate::Metadata.metadata_key(@user, MetadataKeys::PRORATION_INVOICE)])
end

it 'supports credits on monthly billing cycles' do
# TODO
pass
Expand Down

0 comments on commit f555ba8

Please sign in to comment.