diff --git a/lib/stripe-force/constants.rb b/lib/stripe-force/constants.rb index fbf555e76c..9cf0f71899 100644 --- a/lib/stripe-force/constants.rb +++ b/lib/stripe-force/constants.rb @@ -21,7 +21,9 @@ module Constants SF_CONSUMPTION_RATE = 'ConsumptionRate' SF_CONTRACT = 'Contract' SF_STRIPE_COUPON = 'Stripe_Coupon_Beta__c' + SF_STRIPE_COUPON_SERIALIZED = 'Stripe_Coupon_Beta_Serialized__c' SF_STRIPE_COUPON_QUOTE_LINE_ASSOCIATION = 'Stripe_Coupon_Beta_Quote_Line_Associatio__c' + SF_STRIPE_COUPON_ORDER_ITEM_ASSOCIATION = 'Stripe_Coupon_Beta_Order_Item_Associatio__c' SF_ID = 'Id' SF_LAST_MODIFIED_DATE = 'LastModifiedDate' diff --git a/lib/stripe-force/translate/coupon.rb b/lib/stripe-force/translate/coupon.rb index 5dfff16ecf..90b34e1cdf 100644 --- a/lib/stripe-force/translate/coupon.rb +++ b/lib/stripe-force/translate/coupon.rb @@ -13,4 +13,24 @@ def translate_coupon(sf_coupon) coupon end + + def self.get_salesforce_stripe_coupons_associated_to_order_line(sf_client:, sf_order_line_id:) + order_item_associations = sf_client.query("Select Id from #{SF_STRIPE_COUPON_ORDER_ITEM_ASSOCIATION} where Order_Item__c = '#{sf_order_line_id}'") + + if !order_item_associations || order_item_associations.size == 0 + log.info "no stripe coupon order line associations related to this order line", salesforce_object: sf_order_line_id + return + end + + # there could be multiple coupons associated with a single order line + coupons = order_item_associations.map do |order_item_association| + association = sf_client.find(SF_STRIPE_COUPON_ORDER_ITEM_ASSOCIATION, order_item_association.Id) + coupon = sf_client.query("Select Id from #{SF_STRIPE_COUPON_SERIALIZED} where Id = '#{association.Stripe_Coupon__c}'") + + # return the coupon object + sf_client.find(SF_STRIPE_COUPON_SERIALIZED, coupon.first.Id) + end + + coupons + end end diff --git a/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Order_Item_Associatio__c/Stripe_Coupon_Beta_Order_Item_Associatio__c.object-meta.xml b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Order_Item_Associatio__c/Stripe_Coupon_Beta_Order_Item_Associatio__c.object-meta.xml new file mode 100644 index 0000000000..46fc0f1c12 --- /dev/null +++ b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Order_Item_Associatio__c/Stripe_Coupon_Beta_Order_Item_Associatio__c.object-meta.xml @@ -0,0 +1,174 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Default + + + View + Large + Default + + + View + Small + Default + + false + SYSTEM + Deployed + Custom junction object linking a Stripe Coupon and an Order Line. + false + true + false + false + false + false + false + true + true + ControlledByParent + + + {0000} + Quot + AutoNumber + + Stripe Coupon Beta Order Items + + ControlledByParent + + Order_Item_Field_Must_Be_Set + Order_Item__c is a required field. + true + ISBLANK(Order_Item__c) + Order_Item__c is a required field. + + Public + diff --git a/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Order_Item_Associatio__c/fields/Order_Item__c.field-meta.xml b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Order_Item_Associatio__c/fields/Order_Item__c.field-meta.xml new file mode 100644 index 0000000000..6a3a5965a3 --- /dev/null +++ b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Order_Item_Associatio__c/fields/Order_Item__c.field-meta.xml @@ -0,0 +1,12 @@ + + + Order_Item__c + false + + OrderItem + Stripe Coupon Beta Order Item Connection + Stripe_Coupon_Beta_Order_Item_Connection + false + false + Lookup + diff --git a/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Order_Item_Associatio__c/fields/Stripe_Coupon__c.field-meta.xml b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Order_Item_Associatio__c/fields/Stripe_Coupon__c.field-meta.xml new file mode 100644 index 0000000000..75bdc3bbd1 --- /dev/null +++ b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Order_Item_Associatio__c/fields/Stripe_Coupon__c.field-meta.xml @@ -0,0 +1,14 @@ + + + Stripe_Coupon__c + false + + Stripe_Coupon_Beta_Serialized__c + Stripe Coupon Beta Order Item Link + Stripe_Coupon_Beta_Order_Item_Link + 0 + false + false + MasterDetail + true + \ No newline at end of file diff --git a/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/Stripe_Coupon_Beta_Serialized__c.object-meta.xml b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/Stripe_Coupon_Beta_Serialized__c.object-meta.xml new file mode 100644 index 0000000000..4f381077e2 --- /dev/null +++ b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/Stripe_Coupon_Beta_Serialized__c.object-meta.xml @@ -0,0 +1,167 @@ + + + + Accept + Default + + + Accept + Large + Default + + + Accept + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Clone + Default + + + Clone + Large + Default + + + Clone + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Default + + + View + Large + Default + + + View + Small + Default + + false + SYSTEM + Deployed + Custom object representing a Stripe Coupon that has been translated. + false + true + false + false + false + false + true + true + true + Private + + + {0000} + + AutoNumber + + Stripe Coupons Beta Serialized + + ReadWrite + Public + diff --git a/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Amount_Off__c.field-meta.xml b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Amount_Off__c.field-meta.xml new file mode 100644 index 0000000000..644a7e8aa5 --- /dev/null +++ b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Amount_Off__c.field-meta.xml @@ -0,0 +1,13 @@ + + + Amount_Off__c + The amount that is taken off the subtotal for the duration of the coupon. + false + + 18 + false + 0 + false + Number + false + diff --git a/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Duration_In_Months__c.field-meta.xml b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Duration_In_Months__c.field-meta.xml new file mode 100644 index 0000000000..0d679c199c --- /dev/null +++ b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Duration_In_Months__c.field-meta.xml @@ -0,0 +1,12 @@ + + + Duration_In_Months__c + false + + 18 + false + 0 + false + Number + false + diff --git a/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Duration__c.field-meta.xml b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Duration__c.field-meta.xml new file mode 100644 index 0000000000..e3393d4ba8 --- /dev/null +++ b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Duration__c.field-meta.xml @@ -0,0 +1,31 @@ + + + Duration__c + Indicates how long the coupon is valid for. Values include once, forever, or repeating. + false + + true + false + Picklist + + true + + false + + once + true + + + + repeating + false + + + + forever + false + + + + + diff --git a/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Max_Redemptions__c.field-meta.xml b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Max_Redemptions__c.field-meta.xml new file mode 100644 index 0000000000..7792a88589 --- /dev/null +++ b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Max_Redemptions__c.field-meta.xml @@ -0,0 +1,13 @@ + + + Max_Redemptions__c + Maximum number of times a coupon can be redeemed across all customers. + false + + 18 + false + 0 + false + Number + false + diff --git a/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Name__c.field-meta.xml b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Name__c.field-meta.xml new file mode 100644 index 0000000000..86b3c34c9c --- /dev/null +++ b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Name__c.field-meta.xml @@ -0,0 +1,12 @@ + + + Name__c + The name of the Stripe coupon. + false + + 40 + false + false + Text + false + diff --git a/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Original_Stripe_Coupon_Beta_Id__c.field-meta.xml b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Original_Stripe_Coupon_Beta_Id__c.field-meta.xml new file mode 100644 index 0000000000..8ace061f24 --- /dev/null +++ b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Original_Stripe_Coupon_Beta_Id__c.field-meta.xml @@ -0,0 +1,12 @@ + + + Original_Stripe_Coupon_Beta_Id__c + The unique identifier for the initial coupon this coupon was duplicated from. + false + + 255 + true + false + Text + false + diff --git a/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Percent_Off__c.field-meta.xml b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Percent_Off__c.field-meta.xml new file mode 100644 index 0000000000..cefade4314 --- /dev/null +++ b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta_Serialized__c/fields/Percent_Off__c.field-meta.xml @@ -0,0 +1,13 @@ + + + Percent_Off__c + The percentage off that is taken off the subtotal for the duration of the coupon. + false + + 18 + false + 2 + false + Number + false + diff --git a/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta__c/fields/Amount_Off__c.field-meta.xml b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta__c/fields/Amount_Off__c.field-meta.xml index 1040c36bd5..644a7e8aa5 100644 --- a/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta__c/fields/Amount_Off__c.field-meta.xml +++ b/sfdx/force-app/main/default/objects/Stripe_Coupon_Beta__c/fields/Amount_Off__c.field-meta.xml @@ -6,7 +6,7 @@ 18 false - 2 + 0 false Number false diff --git a/sfdx/force-app/main/default/permissionsets/Stripe_Connector_Integration_User.permissionset-meta.xml b/sfdx/force-app/main/default/permissionsets/Stripe_Connector_Integration_User.permissionset-meta.xml index 9720aaffbb..d1121b840f 100644 --- a/sfdx/force-app/main/default/permissionsets/Stripe_Connector_Integration_User.permissionset-meta.xml +++ b/sfdx/force-app/main/default/permissionsets/Stripe_Connector_Integration_User.permissionset-meta.xml @@ -340,6 +340,36 @@ Stripe_Coupon_Beta__c.Stripe_ID__c true + + true + Stripe_Coupon_Beta_Serialized__c.Amount_Off__c + true + + + true + Stripe_Coupon_Beta_Serialized__c.Duration_In_Months__c + true + + + true + Stripe_Coupon_Beta_Serialized__c.Max_Redemptions__c + true + + + true + Stripe_Coupon_Beta_Serialized__c.Name__c + true + + + true + Stripe_Coupon_Beta_Serialized__c.Percent_Off__c + true + + + true + Stripe_Coupon_Beta_Order_Item_Associatio__c.Order_Item__c + true + false @@ -377,6 +407,20 @@ Stripe_Coupon_Beta__c true + + true + true + Stripe_Coupon_Beta_Serialized__c + true + + + true + true + true + true + Stripe_Coupon_Beta_Order_Item_Associatio__c + true + setup true diff --git a/sfdx/force-app/main/default/permissionsets/Stripe_Connector_Sync_Management_User.permissionset-meta.xml b/sfdx/force-app/main/default/permissionsets/Stripe_Connector_Sync_Management_User.permissionset-meta.xml index 9c1b0f0e8c..378bf54e34 100644 --- a/sfdx/force-app/main/default/permissionsets/Stripe_Connector_Sync_Management_User.permissionset-meta.xml +++ b/sfdx/force-app/main/default/permissionsets/Stripe_Connector_Sync_Management_User.permissionset-meta.xml @@ -193,6 +193,36 @@ true Stripe_Coupon_Beta__c.Percent_Off__c true + + + true + Stripe_Coupon_Beta_Serialized__c.Amount_Off__c + true + + + true + Stripe_Coupon_Beta_Serialized__c.Duration_In_Months__c + true + + + true + Stripe_Coupon_Beta_Serialized__c.Max_Redemptions__c + true + + + true + Stripe_Coupon_Beta_Serialized__c.Name__c + true + + + true + Stripe_Coupon_Beta_Serialized__c.Percent_Off__c + true + + + true + Stripe_Coupon_Beta_Order_Item_Associatio__c.Order_Item__c + true false @@ -222,6 +252,20 @@ Stripe_Coupon_Beta__c false + + true + true + Stripe_Coupon_Beta_Serialized__c + true + + + true + true + true + true + Stripe_Coupon_Beta_Order_Item_Associatio__c + true + Error_Log__c Visible diff --git a/sfdx/force-app/main/default/profiles/Admin.profile-meta.xml b/sfdx/force-app/main/default/profiles/Admin.profile-meta.xml index bccd343aa6..5f638f8628 100644 --- a/sfdx/force-app/main/default/profiles/Admin.profile-meta.xml +++ b/sfdx/force-app/main/default/profiles/Admin.profile-meta.xml @@ -242,6 +242,41 @@ Stripe_Coupon_Beta__c.Percent_Off__c true + + true + Stripe_Coupon_Beta__c.Stripe_ID__c + true + + + true + Stripe_Coupon_Beta_Serialized__c.Amount_Off__c + true + + + true + Stripe_Coupon_Beta_Serialized__c.Duration_In_Months__c + true + + + true + Stripe_Coupon_Beta_Serialized__c.Max_Redemptions__c + true + + + true + Stripe_Coupon_Beta_Serialized__c.Name__c + true + + + true + Stripe_Coupon_Beta_Serialized__c.Percent_Off__c + true + + + true + Stripe_Coupon_Beta_Order_Item_Associatio__c.Order_Item__c + true + Error_Log__c-Error Log Layout @@ -289,6 +324,20 @@ Stripe_Coupon_Beta__c true + + true + true + Stripe_Coupon_Beta_Serialized__c + true + + + true + true + true + true + Stripe_Coupon_Beta_Order_Item_Associatio__c + true + setup true diff --git a/sfdx/force-app/main/default/triggers/updateOrderLineCoupons.trigger b/sfdx/force-app/main/default/triggers/updateOrderLineCoupons.trigger new file mode 100644 index 0000000000..4308e73534 --- /dev/null +++ b/sfdx/force-app/main/default/triggers/updateOrderLineCoupons.trigger @@ -0,0 +1,76 @@ +trigger updateOrderLineCoupons on SBQQ__Quote__c (after update) { + try { + // for all newly ordered quotes, check if the quote lines have coupon and duplicate to the corresponbding order lines + for(SBQQ__Quote__c quote : Trigger.new) { + if (quote.SBQQ__Ordered__c == true && Trigger.oldMap.get(quote.Id).SBQQ__Ordered__c == false) { + // fetch the related quote lines + List quoteLines = [ + SELECT Id + FROM SBQQ__QuoteLine__c + WHERE SBQQ__Quote__c = :quote.Id + ]; + + for (SBQQ__QuoteLine__c quoteLine : quoteLines) { + // get the corresponding order line for this quote line + List orderItem = [ + SELECT Id + FROM OrderItem + WHERE SBQQ__QuoteLine__c = :quoteLine.Id + ]; + + if (orderItem == null || orderItem.size() == 0) + { + continue; + } + Id orderItemId = orderItem.get(0).Id; + + // fetch the Stripe Coupon Quote Line Associations for this quote line + List stripeCouponQuoteLineAssociations = [ + SELECT Id, Stripe_Coupon__c + FROM Stripe_Coupon_Beta_Quote_Line_Associatio__c + WHERE Quote_Line__c = :quoteLine.Id + ]; + + if (stripeCouponQuoteLineAssociations == null) + { + continue; + } + + // for each Stripe Coupon Quote Line Association + for (Stripe_Coupon_Beta_Quote_Line_Associatio__c stripeCouponQuoteLineAssociation: stripeCouponQuoteLineAssociations) + { + Stripe_Coupon_Beta__c quoteLineCoupon = [ + SELECT Amount_Off__c, Duration__c, Duration_In_Months__c, Max_Redemptions__c, Name__c, Percent_Off__c + FROM Stripe_Coupon_Beta__c + WHERE Id = :stripeCouponQuoteLineAssociation.Stripe_Coupon__c + ].get(0); + + // clone the Stripe Coupon on the quote line, it will have a different Id + Stripe_Coupon_Beta_Serialized__c clonedCoupon = new Stripe_Coupon_Beta_Serialized__c( + Amount_Off__c = quoteLineCoupon.Amount_Off__c, + Duration__c = quoteLineCoupon.Duration__c, + Duration_In_Months__c = quoteLineCoupon.Duration_In_Months__c, + Max_Redemptions__c = quoteLineCoupon.Max_Redemptions__c, + Name__c = quoteLineCoupon.Name__c, + Percent_Off__c = quoteLineCoupon.Percent_Off__c, + Original_Stripe_Coupon_Beta_Id__c = quoteLineCoupon.Id + ); + // insert the cloned Stripe coupon + Database.insertImmediate((sObject)clonedCoupon); + + // create a Stripe Coupon Order Line Association + Stripe_Coupon_Beta_Order_Item_Associatio__c orderLineStripeCouponAssociation = new Stripe_Coupon_Beta_Order_Item_Associatio__c( + Stripe_Coupon__c = clonedCoupon.Id, + Order_Item__c = orderItemId + ); + + // insert this record + Database.insertImmediate((sObject)orderLineStripeCouponAssociation); + } + } + } + } + } catch (Exception e) { + errorLogger.create('updateOrderLineCouponsTrigger', e); + } +} \ No newline at end of file diff --git a/sfdx/force-app/main/default/triggers/updateOrderLineCoupons.trigger-meta.xml b/sfdx/force-app/main/default/triggers/updateOrderLineCoupons.trigger-meta.xml new file mode 100644 index 0000000000..f502e4bf01 --- /dev/null +++ b/sfdx/force-app/main/default/triggers/updateOrderLineCoupons.trigger-meta.xml @@ -0,0 +1,5 @@ + + + 54.0 + Active + diff --git a/test/integration/translate/test_coupon.rb b/test/integration/translate/test_coupon.rb index 124b162ad8..e57a781707 100644 --- a/test/integration/translate/test_coupon.rb +++ b/test/integration/translate/test_coupon.rb @@ -85,6 +85,51 @@ class Critic::CouponTranslation < Critic::FunctionalTest assert(25, coupon_1.Percent_Off__c) end + it 'coupons are copied to order lines when a quote is ordered' do + # setup + PRODUCT_PRICE = 100 + sf_account_id = create_salesforce_account + sf_product_id, _sf_pricebook_id = salesforce_recurring_product_with_price(price: PRODUCT_PRICE) + + # create a SF CPQ quote + sf_quote_id = create_salesforce_quote(sf_account_id: sf_account_id, additional_quote_fields: { + CPQ_QUOTE_SUBSCRIPTION_START_DATE => now_time_formatted_for_salesforce, + CPQ_QUOTE_SUBSCRIPTION_TERM => 12.0, + }) + + # create a quote with a product + quote_with_product = add_product_to_cpq_quote(sf_quote_id, sf_product_id: sf_product_id) + sf_quote_id = calculate_and_save_cpq_quote(quote_with_product) + + # retrieve the quote line + quote_lines = sf_get_related(sf_quote_id, CPQ_QUOTE_LINE) + assert_equal(1, quote_lines.size) + quote_line_id = quote_lines.first.Id + + # create a coupon and attach to the quote line + sf_stripe_coupon = create_salesforce_stripe_coupon(additional_fields: { + SalesforceStripeCouponFields::NAME => 'Special 50% off coupon', + SalesforceStripeCouponFields::PERCENT_OFF => 50, + }) + + # create the association object to map the coupon to the quote line + create_salesforce_stripe_coupon_quote_line_association(sf_quote_line_id: quote_line_id, sf_stripe_coupon_id: sf_stripe_coupon) + + sf_order = create_order_from_cpq_quote(sf_quote_id) + + # query for the association objects that have a reference to this order line + sf_order_line_items = sf_get_related(sf_order, SF_ORDER_ITEM) + assert_equal(1, sf_order_line_items.count) + + sf_order_item_id = sf_order_line_items.first.Id + associated_coupons = StripeForce::Translate.get_salesforce_stripe_coupons_associated_to_order_line(sf_client: @user.sf_client, sf_order_line_id: sf_order_item_id) + assert_equal(1, associated_coupons.size) + + order_line_coupon = associated_coupons.first + assert_equal('Special 50% off coupon', order_line_coupon.Name__c) + assert_equal(50, order_line_coupon.Percent_Off__c) + end + it 'translate order with coupon' do # TODO https://jira.corp.stripe.com/browse/PLATINT-1952 end