Modifications to Existing Promotions Code

Important:

Each section below describes the required changes needed. Since many files are affected, and for simplicity, we have not described the detailed implementation steps here. However, you should implement these changes using the best practices of using extensions and overrides while ensuring that you do not impact previous customizations to your implementation. For more information, see Best Practices for Customizing SCA.

In addition to making the changes described, you must create ns.package.json files and update your distro.json file for any custom modules you include the code updates in.

Note:

After creating modifications to existing modules, you also need to create a new PromocodeNotifications custom module, Updates to the distro.json file must include this module as well. See Custom PromocodeNotifications Module.

Modify LiveOrder\SuiteScript\LiveOrder.Model.js

Replace

In the update method:

          var current_order = this.get(); 

        
With
          var current_order = this.get();
this.setOldPromocodes(); 

        
Replace

In the confirmationCreateResult method:

          result.promocodes.push({
internalid: placed_order.getLineItemValue('promotions', 'couponcode', i)
,   code: placed_order.getLineItemValue('promotions', 'couponcode_display', i)
,   isvalid: placed_order.getLineItemValue('promotions', 'promotionisvalid', i) === 'T'
,   discountrate_formatted: '' //placed_order.getLineItemValue('promotions', 'discountrate', i)
}); 

        
With
          if(placed_order.getLineItemValue('promotions', 'applicabilitystatus', i) !== 'NOT_APPLIED')
{
   result.promocodes.push({
      internalid: placed_order.getLineItemValue('promotions', 'couponcode', i)
   ,   code: placed_order.getLineItemValue('promotions', 'couponcode_display', i)
   ,   isvalid: placed_order.getLineItemValue('promotions', 'promotionisvalid', i) === 'T'
   ,   discountrate_formatted: '' //placed_order.getLineItemValue('promotions', 'discountrate', i)
   });
} 

        
Replace

in the addLines method:

          var items = []
, self = this; 

        
With
          var items = []
, self = this;

this.setOldPromocodes(); 

        
Replace

In the removeLine method:

          // Removes the line
ModelsInit.order.removeItem(line_id); 

        
With
          this.setOldPromocodes();
// Removes the line
ModelsInit.order.removeItem(line_id); 

        
Replace

In the getPromoCodes method:

          , getPromoCodes: function getPromoCodes (order_fields) { var result = []; if (order_fields.promocodes && order_fields.promocodes.length) { _.each(order_fields.promocodes, function (promo_code) { // @class LiveOrder.Model.PromoCode result.push({ // @property {String} internalid internalid: promo_code.internalid // @property {String} code , code: promo_code.promocode // @property {Boolean} isvalid , isvalid: promo_code.isvalid === 'T' // @property {String} discountrate_formatted , discountrate_formatted: '' , errormsg : promo_code.errormsg , name: promo_code.discount_name , rate: promo_code.discount_rate , type: promo_code.discount_type }); }); } return result; } 

        
With
          , getPromoCodes: function getPromoCodes (order_fields) { var result = [] , self = this; if (order_fields.promocodes && order_fields.promocodes.length) { _.each(order_fields.promocodes, function (promo_code) { // @class LiveOrder.Model.PromoCode var promocode = { // @property {String} internalid internalid: promo_code.internalid // @property {String} code , code: promo_code.promocode // @property {Boolean} isvalid , isvalid: promo_code.isvalid === 'T' // @property {String} discountrate_formatted , discountrate_formatted: '' , errormsg : promo_code.errormsg , name: promo_code.discount_name , rate: promo_code.discount_rate , type: promo_code.discount_type }; if (!_.isUndefined(promo_code.is_auto_applied)) { // @property {Boolean} isautoapplied promocode.isautoapplied = promo_code.is_auto_applied; // @property {String} applicabilitystatus promocode.applicabilitystatus = (promo_code.applicability_status) ? promo_code.applicability_status : ''; // @property {String} applicabilityreason promocode.applicabilityreason = (promo_code.applicability_reason) ? promo_code.applicability_reason : ''; } if (!_.isUndefined(promo_code.is_auto_applied) && !_.isUndefined(promo_code.applicability_status) && !_.isUndefined(promo_code.applicability_reason) && !_.isUndefined(self.old_promocodes)) { var old_promocode = (self.old_promocodes) ? _.find(self.old_promocodes, function (old_promo_code){ return old_promo_code.internalid === promo_code.internalid; }) : ''; if (!old_promocode || old_promocode.applicability_status !== promo_code.applicability_status || (!promo_code.is_auto_applied && promo_code.applicability_reason !== old_promocode.applicability_reason)) { promocode.notification = true; } } result.push(promocode); }); delete this.old_promocodes; } return result; } 

        
Replace

In the getOptionByCartOptionId method:

          , getOptionByCartOptionId: function getOptionByCartOptionId (options, cart_option_id) { return _.findWhere(options, {cartOptionId: cart_option_id}); } 

        
With
          , getOptionByCartOptionId: function getOptionByCartOptionId (options, cart_option_id) { return _.findWhere(options, {cartOptionId: cart_option_id}); } // @method setOldPromocodes sets a local instance of the order's promocodes, used to be able to detect changes in a promocode.
, setOldPromocodes: function setOldPromocodes () { var order_fields = this.getFieldValues(); this.old_promocodes = order_fields.promocodes; } 

        

Modify Transaction\SuiteScript\Transaction.Model.js

Replace

In the getRecordPromocodes method:

          this.result.promocodes.push({
//@class Transaction.Model.Get.Promocode
 //@property {String} internalid
 internalid: this.record.getLineItemValue('promotions', 'couponcode', i)
 //@property {String} code
, code: this.record.getLineItemValue('promotions', 'couponcode_display', i)
 //@property {Boolean} isvalid
, isvalid: this.record.getLineItemValue('promotions', 'promotionisvalid', i) === 'T'
 //@property {String} discountrate_formatted
, discountrate_formatted: ''//this.record.getLineItemValue('promotions', 'discountrate', i)
}); 

        
With
          if(this.record.getLineItemValue('promotions', 'applicabilitystatus', i) !== 'NOT_APPLIED'){ this.result.promocodes.push({ //@class Transaction.Model.Get.Promocode //@property {String} internalid internalid: this.record.getLineItemValue('promotions', 'couponcode', i) //@property {String} code , code: this.record.getLineItemValue('promotions', 'couponcode_display', i) //@property {Boolean} isvalid , isvalid: this.record.getLineItemValue('promotions', 'promotionisvalid', i) === 'T' //@property {String} discountrate_formatted , discountrate_formatted: ''//this.record.getLineItemValue('promotions', 'discountrate', i) });
} 

        

Modify Cart\Templates\cart_detailed.tpl

Replace
          <div data-confirm-message class="cart-detailed-confirm-message"></div> 

        
With
          <div data-confirm-message class="cart-detailed-confirm-message"></div>
<div data-view="Promocode.Notifications"></div> 

        

Add the file Cart\Templates\cart_promocode_notifications.tpl

          <div data-view="Promocode.Notification"></div> 

        

Add the file Cart/Sass/_cart-promocode-notifications.scss

          //empty file 

        

Modify Cart\JavaScript\Cart.Promocode.List.Item.View.js

Replace
          //@module Cart
define('Cart.Promocode.List.Item.View'
, [ 'cart_promocode_list_item.tpl' , 'Backbone' ]
, function ( cart_promocode_list_item_tpl , Backbone ) 

        
With
          //@module Cart
define('Cart.Promocode.List.Item.View'
, [ 'cart_promocode_list_item.tpl' , 'Backbone' , 'underscore' ]
, function ( cart_promocode_list_item_tpl , Backbone , _ ) 

        
Replace

In the getContext method:

          var code = this.model.get('code')
, internalid = this.model.get('internalid'); 

        
With
          var code = this.model.get('code')
, internalid = this.model.get('internalid')
, hide_autoapply_promo = (!_.isUndefined(this.model.get('isautoapplied'))) ? this.model.get('applicabilityreason') === 'DISCARDED_BEST_OFFER' || (this.model.get('isautoapplied') && this.model.get('applicabilitystatus') === 'NOT_APPLIED') : false; 

        
Replace

In the getContext method:

          //@property {Boolean} showPromo
showPromo: !!code 

        
With
          //@property {Boolean} showPromo
showPromo: !!code && !hide_autoapply_promo 

        
Replace

In the getContext method:

          //@property {Boolean} isEditable
, isEditable: !this.options.isReadOnly 

        
With
          //@property {Boolean} isEditable
, isEditable: !this.options.isReadOnly && !this.model.get('isautoapplied') 

        

Add the file Cart\JavaScript\Cart.Promocode.Notifications.View.js

          //@module Cart
define('Cart.Promocode.Notifications.View'
, [ 'GlobalViews.Message.View' , 'cart_promocode_notifications.tpl' , 'Backbone' , 'Backbone.CompositeView' , 'underscore' ]
, function ( GlobalViewsMessageView , cart_promocode_notifications , Backbone , BackboneCompositeView , _ )
{ 'use strict'; //@class Cart.Promocode.Notification.View @extend Backbone.View return Backbone.View.extend({ //@property {Function} template template:cart_promocode_notifications //@method initialize //@return {Void} , initialize: function initialize () { BackboneCompositeView.add(this); this.on('afterCompositeViewRender', this.afterViewRender, this); } // @property {ChildViews} childViews , childViews: { 'Promocode.Notification': function () { var notification = this.getNotification(); return new GlobalViewsMessageView({ message: notification.message , type: notification.type , closable: true }); } } // @method afterViewRender lets parent model know the promotion already shwoed its current notification // @return {Void} , afterViewRender: function() { this.options.parentModel.trigger('promocodeNotificationShown', this.model.get('internalid')); } // @method getNotification // @return {Notification} , getNotification: function () { var notification = {}; if(this.model.get('applicabilitystatus') === 'APPLIED') { notification.type = 'success'; notification.message = _('Promotion <strong>').translate() + this.model.get('code') + _('</strong> is now affecting your order.').translate(); } else if(this.model.get('applicabilityreason') === 'CRITERIA_NOT_MET') { notification.type = (!this.model.get('isautoapplied')) ? 'warning' : 'info'; notification.message = _('Promotion <strong>').translate() + this.model.get('code') + _('</strong> is not affecting your order. ').translate() + this.model.get('errormsg'); } else if(this.model.get('applicabilityreason') === 'DISCARDED_BEST_OFFER') { notification.type = 'info'; notification.message = _('We have chosen the best possible offer for you. Promotion <strong>').translate() + this.model.get('code') + _('</strong> is not affecting your order.').translate(); } return notification; } //@method getContext //@return {Cart.Promocode.Notifications.View.context} , getContext: function getContext () { //@class Cart.Promocode.Notifications.View.context return {}; //@class Cart.Promocode.Notifications.View } });
}); 

        

Modify Cart\JavaScript\Cart.Detailed.View.js

Replace
          ,   'Cart.Lines.View' 

        
With
          ,   'Cart.Lines.View'
,   'Cart.Promocode.Notifications.View' 

        
Replace
          ,   CartLinesView 

        
With
          ,   CartLinesView
,   CartPromocodeNotifications 

        
Replace

In the initialize method:

          this.model.on('LINE_ROLLBACK', this.render, this); 

        
With
          this.model.on('LINE_ROLLBACK', this.render, this);
this.model.on('promocodeNotificationShown', this.removePromocodeNotification, this); 

        
Replace

In the storeColapsiblesState method:

           // @method storeColapsiblesState // @return {Void}
, storeColapsiblesState: function () { this.$('.collapse').each(function (index, element) { colapsibles_states[Utils.getFullPathForElement(element)] = jQuery(element).hasClass('in'); }); } 

        
With
           // @method storeColapsiblesState // @return {Void}
, storeColapsiblesState: function () { this.$('.collapse').each(function (index, element) { colapsibles_states[Utils.getFullPathForElement(element)] = jQuery(element).hasClass('in'); }); } // @method removePromocodeNotification // @param String promocode_id // @return {Void}
, removePromocodeNotification: function(promocode_id) { var promocode = _.findWhere(this.model.get('promocodes'), {internalid: promocode_id}); delete promocode.notification; } 

        
Replace

In the childViews object:

          , 'Item.ListNavigable': function ()
{ return new BackboneCollectionView({ collection: this.model.get('lines') , viewsPerRow: 1 , childView: CartLinesView , childViewOptions: { navigable: true , application: this.application , SummaryView: CartItemSummaryView , ActionsView: CartItemActionsView , showAlert: false } });
} 

        
With
          , 'Item.ListNavigable': function () { return new BackboneCollectionView({ collection: this.model.get('lines') , viewsPerRow: 1 , childView: CartLinesView , childViewOptions: { navigable: true , application: this.application , SummaryView: CartItemSummaryView , ActionsView: CartItemActionsView , showAlert: false } }); } , 'Promocode.Notifications': function () { var promotions = _.filter(this.model.get('promocodes') || [], function (promocode) { return promocode.notification === true; }); if(promotions.length){ return new BackboneCollectionView({ collection: promotions , viewsPerRow: 1 , childView: CartPromocodeNotifications , childViewOptions: { parentModel: this.model } }); } } 

        

Modify CheckoutApplication\JavaScript\SC.Checkout.Configuration.Steps.BillingFirst.js

Replace
          ,   'OrderWizard.Module.PromocodeForm' 

        
With
          ,   'OrderWizard.Module.PromocodeForm'
,   'OrderWizard.Module.PromocodeNotifications' 

        
Replace
          ,   OrderWizardModulePromocodeForm 

        
With
          ,   OrderWizardModulePromocodeForm
,   OrderWizardModulePromocodeNotification 

        
Replace

In the Billing Address step:

          [OrderWizardModuleMultiShipToEnableLink, {exclude_on_skip_step: true}] 

        
With
           [OrderWizardModulePromocodeNotification, {exclude_on_skip_step: true}]
, [OrderWizardModuleMultiShipToEnableLink, {exclude_on_skip_step: true}] 

        
Replace

In the Shipping Address step:

          , isActive: function () { return !this.wizard.isMultiShipTo(); }
, modules: [ [OrderWizardModuleMultiShipToEnableLink, {exclude_on_skip_step: true}] 

        
With
          , isActive: function () { return !this.wizard.isMultiShipTo(); }
, modules: [ [OrderWizardModulePromocodeNotification, {exclude_on_skip_step: true}]
, [OrderWizardModuleMultiShipToEnableLink, {exclude_on_skip_step: true}] 

        
Replace

In the Shipping method step:

          [OrderWizardModuleAddressShipping, {edit_url: '/shipping/address'}] 

        
With
           [OrderWizardModulePromocodeNotification, {exclude_on_skip_step: true}]
, [OrderWizardModuleAddressShipping, {edit_url: '/shipping/address'}] 

        
Replace

In the Payment step:

          OrderWizardModulePaymentMethodGiftCertificates 

        
With
           [OrderWizardModulePromocodeNotification, {exclude_on_skip_step: true}]
, OrderWizardModulePaymentMethodGiftCertificates 

        
Replace

In the Review step:

          , [OrderWizardModuleSubmitButton, {className: 'order-wizard-submitbutton-module-top'}] 

        
With
          , [OrderWizardModuleSubmitButton, {className: 'order-wizard-submitbutton-module-top'}]
, [OrderWizardModulePromocodeNotification, {exclude_on_skip_step: true}] 

        

Modify CheckoutApplication\JavaScript\SC.Checkout.Configuration.Steps.OPC.js

Replace
          , 'OrderWizard.Module.PromocodeForm' 

        
With
          ,   'OrderWizard.Module.PromocodeForm'
,   'OrderWizard.Module.PromocodeNotifications' 

        
Replace
          ,   OrderWizardModulePromocodeForm 

        
With
          ,   OrderWizardModulePromocodeForm
,   OrderWizardModulePromocodeNotification 

        
Replace

In the Checkout Information step:

          [OrderWizardModuleTitle, {title: _('Shipping Address').translate(), exclude_on_skip_step: true, isActive: function() {return this.wizard.model.shippingAddressIsRequired();}}] 

        
With
           [OrderWizardModulePromocodeNotification, {exclude_on_skip_step: true}]
, [OrderWizardModuleTitle, {title: _('Shipping Address').translate(), exclude_on_skip_step: true, isActive: function() {return this.wizard.model.shippingAddressIsRequired();}}] 

        
Replace

In the Review step:

          , [ //Mobile Top OrderWizardModuleSubmitButton , { className: 'order-wizard-submitbutton-module-top' } ] 

        
With
          , [ //Mobile Top OrderWizardModuleSubmitButton , { className: 'order-wizard-submitbutton-module-top' } ]
, [OrderWizardModulePromocodeNotification, {exclude_on_skip_step: true}] 

        

Modify CheckoutApplication\JavaScript\SC.Checkout.Configuration.Steps.Standard.js

Replace
          ,   'OrderWizard.Module.PromocodeForm' 

        
With
          ,   'OrderWizard.Module.PromocodeForm'
,   'OrderWizard.Module.PromocodeNotifications' 

        
Replace
          ,   OrderWizardModulePromocodeForm 

        
With
          ,   OrderWizardModulePromocodeForm
,   OrderWizardModulePromocodeNotification 

        
Replace

In the Shipping Address step:

          OrderWizardModuleMultiShipToEnableLink 

        
With
           [OrderWizardModulePromocodeNotification, {exclude_on_skip_step: true}]
, OrderWizardModuleMultiShipToEnableLink 

        
Replace

In the Payment step:

          OrderWizardModulePaymentMethodGiftCertificates 

        
With
           [OrderWizardModulePromocodeNotification, {exclude_on_skip_step: true}]
, OrderWizardModulePaymentMethodGiftCertificates 

        
Replace

In the Review step:

          , [ //Mobile Top OrderWizardModuleSubmitButton , { className: 'order-wizard-submitbutton-module-top' } ] 

        
With
          , [ //Mobile Top OrderWizardModuleSubmitButton , { className: 'order-wizard-submitbutton-module-top' } ]
, [OrderWizardModulePromocodeNotification, {exclude_on_skip_step: true}] 

        

General Notices