From b8ea388f4dc04ad6c3fdc3f335865d32881167de Mon Sep 17 00:00:00 2001 From: Nada Ismail <112021025+nadaismail-stripe@users.noreply.github.com> Date: Wed, 24 May 2023 11:35:50 -0700 Subject: [PATCH] [CF #80] Clean up stacked amendment processing (#1110) --- lib/stripe-force/translate/order.rb | 109 +++++++------- .../translate/order/amendments.rb | 20 ++- scripts/console.rb | 4 + .../integration/amendments/test_amendments.rb | 142 ++++++++++++++++-- .../test_backend_prorated_amendments.rb | 8 +- .../amendments/test_termination.rb | 19 +-- 6 files changed, 208 insertions(+), 94 deletions(-) diff --git a/lib/stripe-force/translate/order.rb b/lib/stripe-force/translate/order.rb index d1da3fbbb4..5eb497a341 100644 --- a/lib/stripe-force/translate/order.rb +++ b/lib/stripe-force/translate/order.rb @@ -109,7 +109,7 @@ def create_stripe_transaction_from_sf_order(sf_order) stripe_transaction ||= retrieve_from_stripe(Stripe::Invoice, sf_order) if !stripe_transaction.nil? - log.info 'order already translated', + log.info 'initial order is already translated', stripe_transaction_id: stripe_transaction.id, salesforce_order_id: sf_order.Id return @@ -417,6 +417,13 @@ def update_subscription_phases_from_order_amendments(contract_structure) contract_structure.amendments.each_with_index do |sf_order_amendment, index| locker.lock_salesforce_record(sf_order_amendment) + # TODO replace with local sync record call in the future + order_amendment_subscription_id = sf_order_amendment[prefixed_stripe_field(GENERIC_STRIPE_ID)] + if order_amendment_subscription_id.present? + log.info "order amendment already translated, skipping", sf_order_amendment_id: sf_order_amendment.Id, index: index + next + end + log.info 'processing amendment', sf_order_amendment_id: sf_order_amendment.Id, index: index invoice_items_in_order, aggregate_phase_items = build_phase_items_from_order_amendment( @@ -425,8 +432,12 @@ def update_subscription_phases_from_order_amendments(contract_structure) ) is_order_terminated = aggregate_phase_items.all?(&:fully_terminated?) - if is_order_terminated && contract_structure.amendments.size - 1 != index - raise Integrations::Errors::UnhandledEdgeCase.new("order terminated, but there's more amendments") + if is_order_terminated + log.info 'amendment is termination order', sf_order_amendment_id: sf_order_amendment.Id + + if contract_structure.amendments.count - 1 != index + raise StripeForce::Errors::RawUserError.new("Processing a termination order, but there's more amendments.") + end end # this loop excludes the initial phase, which is why we are subtracting by 1 @@ -435,16 +446,6 @@ def update_subscription_phases_from_order_amendments(contract_structure) log.info 'number of subscription phase count is greater than this amendment index', subscription_phase_count: subscription_phases.count, amendment_index: index end - # it's possible for users to mutate the subscription schedule phases themselves - # if they do, we can't rely on the naive phase count logic above. This provides us another - # layer of protection, although this is not something we can fully trust right away, which is - # why we are only soft asserting at this point. - # TODO replace with local sync record call in the future - order_amendment_subscription_id = sf_order_amendment[prefixed_stripe_field(GENERIC_STRIPE_ID)] - if order_amendment_subscription_id.present? - Integrations::ErrorContext.report_edge_case("order amendment already marked as processed") - end - # TODO price ID dup check on the invoice items # make sure to run this *after* checking if the amendment is already processed, otherwise # we'll create price IDs which will never be archived since they won't be used in an active phase @@ -474,11 +475,38 @@ def update_subscription_phases_from_order_amendments(contract_structure) aggregate_phase_items, terminated_phase_items = OrderHelpers.remove_terminated_lines(aggregate_phase_items) + # determine if this is a backdated order since this has implications on how we prorate stripe_customer_id = T.cast(subscription_schedule.customer, String) current_time = OrderAmendment.determine_current_time(@user, stripe_customer_id) normalized_current_time = StripeForce::Utilities::SalesforceUtil.datetime_to_unix_timestamp(Time.at(current_time)) is_order_backdated = sf_order_amendment_start_date_as_timestamp < normalized_current_time && @user.feature_enabled?(StripeForce::Constants::FeatureFlags::BACKDATED_AMENDMENTS) + backdated_billing_cycles = nil + next_billing_timestamp = nil + if is_order_backdated + log.info 'processing a backdated amendment order' + backdated_billing_cycles = 0 + subscription_schedule_start = T.must(subscription_schedule.phases.first).start_date + next_billing_timestamp = StripeForce::Utilities::SalesforceUtil.datetime_to_unix_timestamp(Time.at(subscription_schedule_start)) + + # determine if a billing cycle has passed between the amendment start date and current time + while next_billing_timestamp <= normalized_current_time + if next_billing_timestamp > sf_order_amendment_start_date_as_timestamp + backdated_billing_cycles += 1 + end + next_billing_timestamp = (Time.at(next_billing_timestamp).utc.beginning_of_day + billing_frequency.months).to_i + end + + # If a subscription is backdated by 3 months and began on January 30th, our next_billing_timestamp will be March 27th instead of March 30th. + # ie January 30th, + 1 billing cycle (1 month) will result in Febuary 27th, the second iteration will result in March 27th (instead of March 30th). + next_billing_datetime = Time.at(next_billing_timestamp).utc + sf_order_amendment_start_date_datetime = Time.at(sf_order_amendment_start_date_as_timestamp).utc + + if next_billing_datetime.day != sf_order_amendment_start_date_datetime.day + next_billing_timestamp = StripeForce::Translate::OrderHelpers.anchor_time_to_day_of_month(base_time: next_billing_datetime, anchor_day_of_month: sf_order_amendment_start_date_datetime.day).to_i + end + end + negative_invoice_items = [] if @user.feature_enabled?(FeatureFlags::TERMINATED_ORDER_ITEM_CREDIT) # https://jira.corp.stripe.com/browse/PLATINT-2092 @@ -493,7 +521,8 @@ def update_subscription_phases_from_order_amendments(contract_structure) terminated_phase_items: terminated_phase_items, subscription_term: subscription_term_from_salesforce, billing_frequency: billing_frequency, - is_order_backdated: is_order_backdated + is_order_backdated: is_order_backdated, + next_billing_timestamp: next_billing_timestamp, ) end @@ -520,34 +549,6 @@ def update_subscription_phases_from_order_amendments(contract_structure) ) end - # determine if this is a backdated order since this has implications on how we prorate - backdated_billing_cycles = nil - next_billing_timestamp = nil - if is_order_backdated - log.info 'processing backdated amendment order' - backdated_billing_cycles = 0 - subscription_schedule_start = T.must(subscription_schedule.phases.first).start_date - next_billing_timestamp = StripeForce::Utilities::SalesforceUtil.datetime_to_unix_timestamp(Time.at(subscription_schedule_start)) - - # determine if a billing cycle has passed between the amendment start date and current time - while next_billing_timestamp <= normalized_current_time - if next_billing_timestamp > sf_order_amendment_start_date_as_timestamp - backdated_billing_cycles += 1 - end - next_billing_timestamp = (Time.at(next_billing_timestamp).utc.beginning_of_day + billing_frequency.months).to_i - end - - # If a subscription is backdated by 3 months and began on January 30th, our next_billing_timestamp will be March 27th instead of March 30th. - # ie January 30th, + 1 billing cycle (1 month) will result in Febuary 27th, the second iteration will result in March 27th (instead of March 30th). - - next_billing_datetime = Time.at(next_billing_timestamp).utc - sf_order_amendment_start_date_datetime = Time.at(sf_order_amendment_start_date_as_timestamp).utc - - if next_billing_datetime.day != sf_order_amendment_start_date_datetime.day - next_billing_timestamp = StripeForce::Translate::OrderHelpers.anchor_time_to_day_of_month(base_time: next_billing_datetime, anchor_day_of_month: sf_order_amendment_start_date_datetime.day).to_i - end - end - invoice_items_for_prorations = [] if !is_order_terminated && is_prorated log.info 'amendment order is prorated', sf_order_amendment_id: sf_order_amendment.Id, index: index @@ -618,10 +619,19 @@ def update_subscription_phases_from_order_amendments(contract_structure) new_phase["discounts"] = stripe_discounts_for_sf_object(sf_object: sf_order_amendment) end + # if the time ranges are identical, then the previous phase should be removed + # the previous phases subscription items should be overwritten by the latest phase calculation + # but any one-off items would be lost without "merging" these items + previous_phase = T.must(subscription_phases.last) + + # it's important to check this before setting that start date to 'now' below + is_identical_to_previous_phase_time_range = previous_phase.start_date == new_phase.start_date && + previous_phase.end_date == new_phase.end_date + # if the current day is the same day as the start day, then use 'now' is_same_day = normalized_current_time == new_phase.start_date if is_same_day - log.info 'phase starts on the current day, using now' + log.info 'amendment starts on the current day, using now' new_phase.start_date = 'now' elsif @user.feature_enabled?(FeatureFlags::BACKDATED_AMENDMENTS) && is_order_backdated # if this is a backdated amendment, then use the current time to update the subscription schedule @@ -629,14 +639,6 @@ def update_subscription_phases_from_order_amendments(contract_structure) new_phase.start_date = 'now' end - # if the time ranges are identical, then the previous phase should be removed - # the previous phases subscription items should be overwritten by the latest phase calculation - # but any one-off items would be lost without "merging" these items - previous_phase = T.must(subscription_phases.last) - - is_identical_to_previous_phase_time_range = previous_phase.end_date == new_phase.end_date && - previous_phase.start_date == new_phase.start_date - should_merge_phases = is_identical_to_previous_phase_time_range && !is_same_day && !is_order_backdated if should_merge_phases && !previous_phase.add_invoice_items.empty? log.info 'previous phase is identical, merging invoice items' @@ -648,10 +650,7 @@ def update_subscription_phases_from_order_amendments(contract_structure) # TODO I wonder if we can do something smarter here: if the invoice has not been paid/billed, do XYZ? # this is a special case: subscription is cancelled on the same day, the intention here is to not bill the user at all - is_subscription_schedule_cancelled = is_order_terminated && - # use `start_date_as_timestamp` since `previous_phase.end_date` could be `now` - previous_phase.start_date == sf_order_amendment_start_date_as_timestamp && - contract_structure.amendments.count == 1 + is_subscription_schedule_cancelled = is_order_terminated && (is_same_day || is_order_backdated) # if the order is terminated, updating the last phase end date and NOT adding another phase is all that needs to be done if !is_order_terminated diff --git a/lib/stripe-force/translate/order/amendments.rb b/lib/stripe-force/translate/order/amendments.rb index da27628b1d..22bf379e51 100644 --- a/lib/stripe-force/translate/order/amendments.rb +++ b/lib/stripe-force/translate/order/amendments.rb @@ -231,11 +231,12 @@ def self.create_credit_price_data_from_terminated_phase_item(user:, phase_item:, terminated_phase_items: T::Array[ContractItemStructure], subscription_term: Integer, billing_frequency: Integer, - is_order_backdated: T::Boolean + is_order_backdated: T::Boolean, + next_billing_timestamp: T.nilable(Integer), ).returns(T::Array[T::Hash[Symbol, T.untyped]]) end # creating one-time invoice items for terminated lines for the unused prorated amount (which has already been billed) - def self.generate_proration_credits_from_terminated_phase_items(user:, mapper:, sf_order_amendment:, terminated_phase_items:, subscription_term:, billing_frequency:, is_order_backdated:) + def self.generate_proration_credits_from_terminated_phase_items(user:, mapper:, sf_order_amendment:, terminated_phase_items:, subscription_term:, billing_frequency:, is_order_backdated:, next_billing_timestamp:) negative_invoice_items_for_prorations = [] terminated_phase_items.each do |phase_item| @@ -292,18 +293,25 @@ def self.generate_proration_credits_from_terminated_phase_items(user:, mapper:, mapper.apply_mapping(credit_stripe_item, phase_item.order_line) proration_period_start = {type: 'phase_start'} - if is_order_backdated + proration_period_end = {type: 'subscription_period_end'} + if is_order_backdated && next_billing_timestamp.present? amendment_start_date = StripeForce::Utilities::SalesforceUtil.extract_subscription_start_date_from_order(mapper, sf_order_amendment) proration_period_start = {type: 'timestamp', timestamp: amendment_start_date.to_i} + proration_period_end = {type: 'timestamp', timestamp: next_billing_timestamp} + + # https://admin.corp.stripe.com/gates/billing_subscriptions_open_invoicing_interval + # https://jira.corp.stripe.com/browse/PLATINT-2450 + if user.feature_enabled?(StripeForce::Constants::FeatureFlags::BILLING_GATE_OPEN_INVOICING_INTERVAL) + # https://livegrep.corp.stripe.com/view/stripe-internal/pay-server/lib/subscriptions/command/invoicing_period.rb#L26 + proration_period_end[:timestamp] = proration_period_end[:timestamp] - 1 > proration_period_start[:timestamp] ? proration_period_end[:timestamp] - 1 : proration_period_end[:timestamp] + end end negative_invoice_items_for_prorations << credit_stripe_item.to_hash.merge({ quantity: phase_item.reduced_by, price_data: price_data, period: { - end: { - type: 'subscription_period_end', - }, + end: proration_period_end, start: proration_period_start, }, }) diff --git a/scripts/console.rb b/scripts/console.rb index 162acc9f76..3f1e8d4431 100755 --- a/scripts/console.rb +++ b/scripts/console.rb @@ -56,6 +56,10 @@ def example_sf_order sf_get(@sf.query("SELECT Id FROM #{SF_ORDER} ORDER BY CreatedDate DESC LIMIT 1").first.Id) end +def get_terminated_items_from_order(order_id) + @user.sf_client.query("SELECT Id FROM OrderItem WHERE OrderId = '#{order_id}' AND SBQQ__OrderedQuantity__c < 0") +end + def example_sf_customer sf_get(@sf.query("SELECT Id FROM #{SF_ACCOUNT} ORDER BY CreatedDate DESC LIMIT 1").first.Id) end diff --git a/test/integration/amendments/test_amendments.rb b/test/integration/amendments/test_amendments.rb index 9acccfec5b..8bc9f030ab 100644 --- a/test/integration/amendments/test_amendments.rb +++ b/test/integration/amendments/test_amendments.rb @@ -1681,7 +1681,7 @@ class Critic::OrderAmendmentTranslation < Critic::OrderAmendmentFunctionalTest contract_term = 12 amendment_term = 11 backdated_months = 3 - initial_order_start_date = now_time - backdated_months.months + 5.days + initial_order_start_date = now_time - backdated_months.months + 1.days initial_order_end_date = initial_order_start_date + contract_term.months amendment_order_start_date = initial_order_start_date + (contract_term - amendment_term).months monthly_price = 111_00 @@ -1781,7 +1781,7 @@ class Critic::OrderAmendmentTranslation < Critic::OrderAmendmentFunctionalTest # verify the dates on the proration invoice dates assert_equal(amendment_order_start_date.to_i, proration_invoice_event.period.start) - assert_equal((now_time + 5.day).to_i, Time.at(proration_invoice_event.period.end).utc.beginning_of_day.to_i) + assert_equal((now_time + 1.days).to_i, Time.at(proration_invoice_event.period.end).utc.beginning_of_day.to_i) end end @@ -1942,14 +1942,11 @@ class Critic::OrderAmendmentTranslation < Critic::OrderAmendmentFunctionalTest second_phase = T.must(subscription_schedule.phases.second) third_phase = T.must(subscription_schedule.phases.last) - # first phase should have one item with a quantity of one - assert_equal(1, first_phase.items.count) - assert_equal(0, first_phase.add_invoice_items.count) - # first phase should start now and end in a month + # first phase should start at the backdated date assert_equal(0, first_phase.start_date - initial_order_start_date.to_i) - # end date is offset due to using 'now' - assert(first_phase.end_date - now_time.to_i < SECONDS_IN_DAY) - # first phase should have an item with a quantity of 1 + assert_equal(0, first_phase.end_date - second_phase.start_date) + # first phase should have an item with a quantity of 1 and no invoice items + assert_equal(1, first_phase.items.count) first_phase_item = T.must(first_phase.items.first) assert_equal(1, first_phase_item.quantity) assert_empty(first_phase.add_invoice_items) @@ -2050,6 +2047,10 @@ class Critic::OrderAmendmentTranslation < Critic::OrderAmendmentFunctionalTest sf_order.refresh stripe_id = sf_order[prefixed_stripe_field(GENERIC_STRIPE_ID)] + # let's attempt to resync these amendments + Stripe::SubscriptionSchedule.expects(:save).never + StripeForce::Translate.perform_inline(@user, sf_order_amendment_1.Id) + # fetch the subscription schedule subscription_schedule = Stripe::SubscriptionSchedule.retrieve(stripe_id, @user.stripe_credentials) @@ -2058,12 +2059,11 @@ class Critic::OrderAmendmentTranslation < Critic::OrderAmendmentFunctionalTest second_phase = T.must(subscription_schedule.phases.second) third_phase = T.must(subscription_schedule.phases.third) - # first phase should have one item with a quantity of one + # first phase should start at the backdated date + assert_equal(0, first_phase.start_date - initial_order_start_date.to_i) + assert_equal(0, first_phase.end_date - second_phase.start_date) + # first phase should have an item with a quantity of 1 and no invoice items assert_equal(1, first_phase.items.count) - assert_equal(0, first_phase.add_invoice_items.count) - # end date is offset due to using 'now' - assert(first_phase.end_date - now_time.to_i < SECONDS_IN_DAY) - # first phase should have an item with a quantity of 1 first_phase_item = T.must(first_phase.items.first) assert_equal(1, first_phase_item.quantity) assert_empty(first_phase.add_invoice_items) @@ -2101,6 +2101,120 @@ class Critic::OrderAmendmentTranslation < Critic::OrderAmendmentFunctionalTest assert_equal('one_time', prorated_price.type) assert_equal((TEST_DEFAULT_PRICE / (contract_term / BigDecimal(amendment_term))).round(MAX_STRIPE_PRICE_PRECISION), BigDecimal(prorated_price.unit_amount_decimal)) end + + it 'syncs three stacked backdated amendments with last being a termination order' do + # initial order: 1yr contract, billed annually, started 3 months ago + # amendment 1: started 2 months ago + # amendment 2: started 1 month ago + # amendment 3: started 3 days ago + contract_term = TEST_DEFAULT_CONTRACT_TERM + initial_order_start_date = now_time - 3.months - 3.days + initial_order_end_date = initial_order_start_date + contract_term.months + + amendment_1_term = 11 + amendment_1_start_date = initial_order_start_date + (contract_term - amendment_1_term).months + + amendment_2_term = 10 + amendment_2_start_date = initial_order_start_date + (contract_term - amendment_2_term).months + + amendment_3_term = 9 + amendment_3_start_date = initial_order_start_date + (contract_term - amendment_3_term).months + + sf_product_id, _sf_pricebook_id = salesforce_recurring_product_with_price( + additional_product_fields: { + CPQ_QUOTE_BILLING_FREQUENCY => CPQBillingFrequencyOptions::ANNUAL.serialize, + } + ) + + # create the initial sf order + sf_order = create_subscription_order( + sf_product_id: sf_product_id, + additional_fields: { + CPQ_QUOTE_SUBSCRIPTION_START_DATE => format_date_for_salesforce(initial_order_start_date), + CPQ_QUOTE_BILLING_FREQUENCY => CPQBillingFrequencyOptions::ANNUAL.serialize, + CPQ_QUOTE_SUBSCRIPTION_TERM => contract_term, + } + ) + + # create the first amendment to increase quantity (+2) + sf_contract_1 = create_contract_from_order(sf_order) + amendment_quote = create_quote_data_from_contract_amendment(sf_contract_1) + amendment_quote["lineItems"].first["record"][CPQ_QUOTE_QUANTITY] = 3 + amendment_quote["record"][CPQ_QUOTE_SUBSCRIPTION_START_DATE] = format_date_for_salesforce(amendment_1_start_date) + amendment_quote["record"][CPQ_QUOTE_SUBSCRIPTION_TERM] = amendment_1_term + sf_order_amendment_1 = create_order_from_quote_data(amendment_quote) + + # create the second amendment to decrease quantity (-2) + sf_contract_2 = create_contract_from_order(sf_order_amendment_1) + amendment_quote = create_quote_data_from_contract_amendment(sf_contract_2) + amendment_quote["lineItems"].first["record"][CPQ_QUOTE_QUANTITY] = 1 + amendment_quote["record"][CPQ_QUOTE_SUBSCRIPTION_START_DATE] = format_date_for_salesforce(amendment_2_start_date) + amendment_quote["record"][CPQ_QUOTE_SUBSCRIPTION_TERM] = amendment_2_term + sf_order_amendment_2 = create_order_from_quote_data(amendment_quote) + + # create the third amendment to terminate the order + sf_contract_3 = create_contract_from_order(sf_order_amendment_2) + amendment_quote = create_quote_data_from_contract_amendment(sf_contract_3) + amendment_quote["lineItems"].first["record"][CPQ_QUOTE_QUANTITY] = 0 + amendment_quote["record"][CPQ_QUOTE_SUBSCRIPTION_START_DATE] = format_date_for_salesforce(amendment_3_start_date) + amendment_quote["record"][CPQ_QUOTE_SUBSCRIPTION_TERM] = amendment_3_term + _sf_order_amendment_3 = create_order_from_quote_data(amendment_quote) + + # translate all the orders (initial order and three amendments) + StripeForce::Translate.perform_inline(@user, sf_order.Id) + sf_order.refresh + stripe_id = sf_order[prefixed_stripe_field(GENERIC_STRIPE_ID)] + + # fetch the subscription schedule + subscription_schedule = Stripe::SubscriptionSchedule.retrieve(stripe_id, @user.stripe_credentials) + assert_equal(3, subscription_schedule.phases.count) + first_phase = T.must(subscription_schedule.phases.first) + second_phase = T.must(subscription_schedule.phases.second) + third_phase = T.must(subscription_schedule.phases.third) + + # first phase should start at the backdated date + assert_equal(0, first_phase.start_date - initial_order_start_date.to_i) + assert_equal(0, first_phase.end_date - second_phase.start_date) + # first phase should have an item with a quantity of 1 and no invoice items + assert_equal(1, first_phase.items.count) + first_phase_item = T.must(first_phase.items.first) + assert_equal(1, first_phase_item.quantity) + assert_empty(first_phase.add_invoice_items) + + # second phase should start 'now' (since it was a backdated amendment) + # and have two products with total quantity of 2 + assert(second_phase.start_date.to_i - now_time.to_i < SECONDS_IN_DAY) + assert_equal(0, second_phase.end_date - third_phase.start_date.to_i) + # second phase should have a second item with a quantity of 1 + assert_equal(2, second_phase.items.count) + second_phase_item_1 = T.must(second_phase.items.first) + second_phase_item_2 = T.must(second_phase.items.second) + assert_equal(1, second_phase_item_1.quantity) + assert_equal(2, second_phase_item_2.quantity) + + # prorate the added items added since the amendment was backdated and missed a billing cycle + assert_equal(1, second_phase.add_invoice_items.count) + prorated_item = T.unsafe(second_phase.add_invoice_items.first) + assert_equal(2, prorated_item.quantity) + + prorated_price = Stripe::Price.retrieve(T.cast(prorated_item.price, String), @user.stripe_credentials) + assert_equal('one_time', prorated_price.type) + assert_equal((TEST_DEFAULT_PRICE / (contract_term / BigDecimal(amendment_1_term))).round(MAX_STRIPE_PRICE_PRECISION), BigDecimal(prorated_price.unit_amount_decimal)) + assert_equal("true", prorated_price.metadata['salesforce_proration']) + + # third phase should start 'now' (since it was a backdated amendment) + assert_equal(0, third_phase.start_date.to_i - second_phase.end_date.to_i) + assert_equal(0, third_phase.end_date.to_i - initial_order_end_date.to_i) + assert_equal(1, third_phase.items.count) + T.must(third_phase.items.detect {|i| i[:quantity] == 1 }) + + # there should be one invoice items - credit item for decrease quantity + assert_equal(1, third_phase.add_invoice_items.count) + credit_item = T.must(third_phase.add_invoice_items.detect {|i| i[:quantity] == 2 }) + credit_stripe_price = Stripe::Price.retrieve(T.cast(credit_item.price, String), @user.stripe_credentials) + assert_equal('one_time', credit_stripe_price.type) + assert_equal(-1 * (BigDecimal(TEST_DEFAULT_PRICE) * BigDecimal(amendment_2_term) / BigDecimal(contract_term)).round(MAX_STRIPE_PRICE_PRECISION), BigDecimal(credit_stripe_price.unit_amount_decimal)) + end end describe 'metadata' do diff --git a/test/integration/amendments/test_backend_prorated_amendments.rb b/test/integration/amendments/test_backend_prorated_amendments.rb index bdb7cb8922..cbf050ae1f 100644 --- a/test/integration/amendments/test_backend_prorated_amendments.rb +++ b/test/integration/amendments/test_backend_prorated_amendments.rb @@ -113,13 +113,13 @@ class Critic::BackendProratedAmendmentTranslation < Critic::OrderAmendmentFuncti contract_term = 18 amendment_term = 6 - # 18 month contract, billed annually - # amendment starts 12 months in, same day as the proration phase + # initial order: 18 month contract, billed annually + # amendment order: starts 12 months in, same day as the proration phase initial_start_date = now_time amendment_start_date = initial_start_date + (contract_term - amendment_term).months amendment_end_date = amendment_start_date + amendment_term.months - sf_product_id, sf_pricebook_entry_id = salesforce_recurring_product_with_price( + sf_product_id, _sf_pricebook_entry_id = salesforce_recurring_product_with_price( additional_product_fields: { CPQ_QUOTE_BILLING_FREQUENCY => CPQBillingFrequencyOptions::ANNUAL.serialize, } @@ -137,7 +137,7 @@ class Critic::BackendProratedAmendmentTranslation < Critic::OrderAmendmentFuncti # translate the initial order StripeForce::Translate.perform_inline(@user, sf_order.Id) - # create amendment order + # create amendment order increase quantity (+1) sf_contract = create_contract_from_order(sf_order) amendment_data = create_quote_data_from_contract_amendment(sf_contract) amendment_data["lineItems"].first["record"][CPQ_QUOTE_QUANTITY] = 2 diff --git a/test/integration/amendments/test_termination.rb b/test/integration/amendments/test_termination.rb index 93031b10a2..7125cce762 100644 --- a/test/integration/amendments/test_termination.rb +++ b/test/integration/amendments/test_termination.rb @@ -41,16 +41,9 @@ def create_amendment_and_adjust_quantity(sf_contract:, quantity:) # one phase with a shortened end date sf_order = create_subscription_order - StripeForce::Translate.perform_inline(@user, sf_order.Id) - sf_order.refresh - stripe_id = sf_order[prefixed_stripe_field(GENERIC_STRIPE_ID)] - - original_subscription_schedule = Stripe::SubscriptionSchedule.retrieve(stripe_id, @user.stripe_credentials) - sf_contract = create_contract_from_order(sf_order) - # api precondition: initial orders have a nil contract ID sf_order.refresh assert_nil(sf_order.ContractId) @@ -58,15 +51,11 @@ def create_amendment_and_adjust_quantity(sf_contract:, quantity:) # the contract should reference the initial order that was created assert_equal(sf_order[SF_ID], sf_contract[SF_CONTRACT_ORDER_ID]) + amendment_end_date = now_time + 9.months amendment_data = create_quote_data_from_contract_amendment(sf_contract) - # wipe out the product amendment_data["lineItems"].first["record"][CPQ_QUOTE_QUANTITY] = 0 - - # the quote is generated by the contract CPQ API, so we need to set these fields manually - # let's have the second phase start in 9mo - end_date = (now_time + 9.months) - amendment_data["record"][CPQ_QUOTE_SUBSCRIPTION_START_DATE] = format_date_for_salesforce(end_date) + amendment_data["record"][CPQ_QUOTE_SUBSCRIPTION_START_DATE] = format_date_for_salesforce(amendment_end_date) amendment_data["record"][CPQ_QUOTE_SUBSCRIPTION_TERM] = 3 sf_order_amendment = create_order_from_quote_data(amendment_data) @@ -78,12 +67,11 @@ def create_amendment_and_adjust_quantity(sf_contract:, quantity:) stripe_id = sf_order[prefixed_stripe_field(GENERIC_STRIPE_ID)] subscription_schedule = Stripe::SubscriptionSchedule.retrieve(stripe_id, @user.stripe_credentials) - assert_equal(1, subscription_schedule.phases.count) # make sure the end date is modified to match the end date of the amendment phase = T.must(subscription_schedule.phases.first) - assert_equal(0, end_date.to_i - phase.end_date) + assert_equal(0, amendment_end_date.to_i - phase.end_date) assert_equal(0, phase.start_date - now_time.to_i) assert_equal(1, phase.items.count) @@ -91,6 +79,7 @@ def create_amendment_and_adjust_quantity(sf_contract:, quantity:) phase_item = T.must(phase.items.first) assert_equal(1, phase_item.quantity) + original_subscription_schedule = Stripe::SubscriptionSchedule.retrieve(stripe_id, @user.stripe_credentials) original_subscription_phase = T.must(original_subscription_schedule.phases.first) excluded_comparison_fields = %i{metadata end_date} assert_equal(