Modifications to Existing Promotions Code
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.
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}]