diff --git a/lib/stripe-force/translate/order.rb b/lib/stripe-force/translate/order.rb index bb2d066513..b5b984024b 100644 --- a/lib/stripe-force/translate/order.rb +++ b/lib/stripe-force/translate/order.rb @@ -882,7 +882,6 @@ def update_subscription_phases_from_order_amendments(contract_structure) if effective_termination_date.present? previous_phase = T.must(subscription_schedule.phases.delete_at(subscription_schedule.phases.count - 1)) subscription_schedule.phases << add_termination_metadata_phase_items(effective_termination_date, terminated_phase_items, sf_order_amendment, previous_phase) - subscription_schedule.phases = OrderHelpers.sanitize_subscription_schedule_phase_params(subscription_schedule.phases) else # ideally this should never happen, but let's log for now log.info 'no effective termination date found, skipping add phase item metadata' @@ -1028,19 +1027,15 @@ def update_subscription_phases_from_order_amendments(contract_structure) is_subscription_schedule_cancelled = is_order_terminated && (is_same_day || is_order_backdated) if is_subscription_schedule_cancelled if update_terminated_phase_items_metadata - subscription_schedule.save({}, @user.stripe_credentials) + log.info 'updating subscription schedule with termination metadata', sf_order_amendment_id: sf_order_amendment + subscription_phases = OrderAmendment.delete_past_phases(@user, stripe_customer_id, subscription_phases) + subscription_schedule.phases = OrderHelpers.sanitize_subscription_schedule_phase_params(subscription_phases) + subscription_schedule = T.cast(subscription_schedule.save({}, @user.stripe_credentials), Stripe::SubscriptionSchedule) end - log.info 'cancelling subscription immediately', sf_order_amendment_id: sf_order_amendment - # NOTE the intention here is to void/reverse out the entire contract, this is the closest API call we have - subscription_schedule.cancel( - { - invoice_now: false, - prorate: false, - }, - @user.stripe_credentials - ) + log.info 'cancelling subscription schedule immediately', sf_order_amendment_id: sf_order_amendment + subscription_schedule = T.cast(subscription_schedule.cancel({invoice_now: false, prorate: false}, @user.stripe_credentials), Stripe::SubscriptionSchedule) else log.info 'adding phase', sf_order_amendment_id: sf_order_amendment.Id, @@ -1054,10 +1049,6 @@ def update_subscription_phases_from_order_amendments(contract_structure) # https://jira.corp.stripe.com/browse/PLATINT-1832 subscription_phases = OrderAmendment.delete_past_phases(@user, stripe_customer_id, subscription_phases) - # TODO we do not currently map to the subscription schedule (again) when there is an amendment order - # we should consider remapping the subscription schedule when there is an amendment order but for now we will map specific fields - subscription_schedule = apply_amendment_order_mappings(mapper, subscription_schedule, sf_order_amendment) - # we do NOT want the next amendment loop to use the version of subscription phases with the backend proration in place final_subscription_phases = if is_initial_order_backend_prorated OrderAmendment.inject_backend_proration(subscription_phases, backend_proration) @@ -1069,6 +1060,10 @@ def update_subscription_phases_from_order_amendments(contract_structure) subscription_schedule.proration_behavior = StripeProrationBehavior::NONE.serialize subscription_schedule.phases = final_subscription_phases + # TODO we do not currently map to the subscription schedule (again) when there is an amendment order + # we should consider remapping the subscription schedule when there is an amendment order but for now we will map specific fields + subscription_schedule = apply_amendment_order_mappings(mapper, subscription_schedule, sf_order_amendment) + # note: to support stacked amendments, we want to update the local sub_schedule and sub_phases # because Stripe converts 'now' to a timestamp # and we want to use that timestamp when there is a stacked amendment @@ -1076,6 +1071,8 @@ def update_subscription_phases_from_order_amendments(contract_structure) end end + # this is important for supporting stacked amendments + # to ensure that the next amendment uses the latest subscription schedule phases and items update_sf_stripe_id(sf_order_amendment, subscription_schedule) subscription_phases = T.cast(subscription_schedule.phases, T::Array[Stripe::SubscriptionSchedulePhase]) previous_phase_items = aggregate_phase_items diff --git a/test/integration/amendments/test_amendments.rb b/test/integration/amendments/test_amendments.rb index 588349dcc7..9fb4567d5b 100644 --- a/test/integration/amendments/test_amendments.rb +++ b/test/integration/amendments/test_amendments.rb @@ -12,6 +12,7 @@ class Critic::OrderAmendmentTranslation < Critic::OrderAmendmentFunctionalTest @user.enable_feature FeatureFlags::DAY_PRORATIONS, update: true @user.enable_feature FeatureFlags::BACKDATED_AMENDMENTS, update: true @user.enable_feature FeatureFlags::TERMINATED_ORDER_ITEM_CREDIT, update: true + @user.enable_feature FeatureFlags::TERMINATION_METADATA, update: true end it 'creates a new phase from an order amendment with monthly billed products' do @@ -1602,8 +1603,8 @@ class Critic::OrderAmendmentTranslation < Critic::OrderAmendmentFunctionalTest assert_equal(sf_order.OrderNumber, subscription_schedule.metadata['OrderNumber']) # ensure that termination metadata was added to the last phase items - # amendment_opportunity_close_date = sf_get(sf_order_amendment_3["OpportunityId"])[SF_OPPORTUNITY_CLOSE_DATE] - # third_phase.items.each {|item| assert_equal(amendment_opportunity_close_date, item.metadata['salesforce_termination_date']) } + amendment_opportunity_close_date = sf_get(sf_order_amendment_3["OpportunityId"])[SF_OPPORTUNITY_CLOSE_DATE] + third_phase.items.each {|item| assert_equal(amendment_opportunity_close_date, item.metadata[StripeForce::Translate::Metadata.metadata_key(@user, MetadataKeys::EFFECTIVE_TERMINATION_DATE)]) } end it 'syncs three stacked backdated amendments with quantity changes on different runs' do diff --git a/test/integration/amendments/test_termination.rb b/test/integration/amendments/test_termination.rb index de29110edf..ba6f26442a 100644 --- a/test/integration/amendments/test_termination.rb +++ b/test/integration/amendments/test_termination.rb @@ -6,6 +6,7 @@ class Critic::OrderAmendmentTermination < Critic::OrderAmendmentFunctionalTest before do @user = make_user(save: true) + @user.enable_feature(FeatureFlags::TERMINATION_METADATA) end # assumes monthly subscription @@ -91,8 +92,6 @@ def create_amendment_and_adjust_quantity(sf_contract:, quantity:) # stripe allows for zero-quantity line items, we need to make sure they are removed it 'removes a line item that is partially terminated' do - @user.enable_feature(FeatureFlags::TERMINATION_METADATA) - # initial order: two lines # amendment order: removes one of the lines # resulting last sub phase: should have a single item @@ -159,8 +158,6 @@ def create_amendment_and_adjust_quantity(sf_contract:, quantity:) # use case: user decides *right* after signing the contract they want to change their order competely it 'cancels a subscription on the same day it started' do - @user.enable_feature(FeatureFlags::TERMINATION_METADATA) - sf_order = create_subscription_order StripeForce::Translate.perform_inline(@user, sf_order.Id) @@ -261,7 +258,11 @@ def create_amendment_and_adjust_quantity(sf_contract:, quantity:) subscription_schedule = Stripe::SubscriptionSchedule.retrieve({id: stripe_id, expand: %w{phases.items.price}}, @user.stripe_credentials) assert_equal(3, subscription_schedule.phases.count) - # TODO test end date + last_phase = T.must(subscription_schedule.phases.last) + assert((now_time + 3.months).to_i - last_phase.end_date < 1.hour) + + amendment_opportunity_close_date = sf_get(amendment_3["OpportunityId"])[SF_OPPORTUNITY_CLOSE_DATE] + T.must(last_phase).items.each {|item| assert_equal(amendment_opportunity_close_date, item.metadata[StripeForce::Translate::Metadata.metadata_key(@user, MetadataKeys::EFFECTIVE_TERMINATION_DATE)]) } active_price = T.cast(subscription_schedule.phases.first&.items&.first&.price, Stripe::Price) assert(active_price.active)