Modifications to Existing Promotions Code

This topic applies to

Applies to

Elbrus | SuiteCommerce Advanced

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 SuiteCommerce Advanced.


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}]