Quotes
Overview
As of Sugar 7.9.0.0, the quotes module has been moved out of Backward Compatibility mode into Sidecar. Customizations to the Quotes interface will need to be rewritten for the new Sidecar framework as an automated migration of these customizations is not possible. This document will outline common customizations and implementation methods.
Quote Identifiers
Administrators can create custom quote identifiers that are more complex than the default auto-incrementing scheme found in an out of box installation. Utilizing this functionality, we can create numbering schemes that match any business needs.
Creating Custom Identifiers
To create a custom identifier, navigate to Admin > Studio > Quotes > Fields. Next, create a TextField type field named to your liking and check the "Calculated Value" checkbox and click "Edit Formula". Once you see the formula builder, you can choose an example below or create your own. Once created, You can add the new field to your Quote module's layouts as desired.
User Field with Pseudo-Random Values
This example creates a quote number in the format of <user last name>-#####
that translates to Smith-87837
. This identifier starts with the last name of the user creating it, followed by 5 pseudo-random numbers based on the creation time.
concat(related($created_by_link, "last_name"), "-", subStr(toString(timestamp($date_entered)), 5, 5))
Note: when using Sugar Logic, updates to related fields will update any corresponding Sugar Logic fields. The example being that an update to the Created By last name field will update all related records with a field using this formula.
Related Field Value and Auto-Incrementing Number
This example creates a quote number in the format of <billing account name>-####
that translates to Start Over Trust-0001
. This number starts with the billing account name followed by a 4 digit auto-incrementing number.
concat(related($billing_accounts, "name"), "-", subStr(toString(add($quote_num, 10000)), 1, 4))
Note: when using Sugar Logic, updates to related fields will update any corresponding Sugar Logic fields. The example being that an update to the Billing Account name will update all related records with a field using this formula.
Record Layout
The following sections outline various changes that can be done to modify the record layout of the Quotes module, by outlining the extra views that can be updated.
Grand Totals Header
The Grand Total Header contains the calculated totals for the quote.
Modifying Fields in the Grand Totals Header
To modify the Grand Totals Header fields, you can copy ./modules/Quotes/clients/base/views/quote-data-grand-totals-header/quote-data-grand-totals-header.php
to ./custom/modules/Quotes/clients/base/views/quote-data-grand-totals-header/quote-data-grand-totals-header.php
or you may create a metadata extension in ./custom/Extension/modules/Quotes/clients/base/views/quote-data-grand-totals-header/
. Next, modify the $viewdefs['Quotes']['base']['view']['quote-data-grand-totals-header']['panels'][0]['fields']
index to add or remove your preferred fields.
Example
The example below will add the "Quote Number" field (quotes.quote_num) field to the layout and removes the "Total Discount" (quotes.deal_tot) from the layout. If adding a custom field from the Quotes module to the view, you will also need to add that field to the related_fields
array as outlined in the Record View documentation below.
./custom/modules/Quotes/clients/base/views/quote-data-grand-totals-header/quote-data-grand-totals-header.php
<?php
$viewdefs['Quotes']['base']['view']['quote-data-grand-totals-header'] = array(
...
'panels' => array(
array(
'name' => 'panel_quote_data_grand_totals_header',
'label' => 'LBL_QUOTE_DATA_GRAND_TOTALS_HEADER',
'fields' => array(
array(
'name' => 'quote_num',
'label' => 'LBL_LIST_QUOTE_NUM',
'css_class' => 'quote-totals-row-item',
),
array(
'name' => 'new_sub',
'css_class' => 'quote-totals-row-item',
),
array(
'name' => 'tax',
'label' => 'LBL_TAX_TOTAL',
'css_class' => 'quote-totals-row-item',
),
array(
'name' => 'shipping',
'css_class' => 'quote-totals-row-item',
),
array(
'name' => 'total',
'label' => 'LBL_LIST_GRAND_TOTAL',
'css_class' => 'quote-totals-row-item',
),
),
),
),
);
Modifying Buttons in the Grand Totals Header
The Quotes List Header contains row actions to create quoted line items, comments, and groupings. The actions are identified by the plus icon in the top left of the header. Editing the 'actions' will allow you to add or remove buttons. The following section will outline how these items can be modified.
To modify the Row Actions in the Grand Total Header, you can copy ./modules/Quotes/clients/base/views/quote-data-grand-totals-header/quote-data-grand-totals-header.php
to ./custom/modules/Quotes/clients/base/views/quote-data-grand-totals-header/quote-data-grand-totals-header.php
or you may create a metadata extension in ./custom/Extension/modules/Quotes/clients/base/views/quote-data-grand-totals-header/
. Next, modify the $viewdefs['Quotes']['base']['view']['quote-data-grand-totals-header']['buttons']
index to add or remove your preferred row actions.
Example
The example below will create a new view that extends the QuotesQuoteDataGrandTotalsHeader
view and append a button to the Grand Total Header button list.
First, create your custom view type that will extend the QuotesQuoteDataGrandTotalsHeader
view.
./custom/modules/Quotes/clients/base/views/quote-data-grand-totals-header/quote-data-grand-totals-header.js
({
extendsFrom: 'QuotesQuoteDataGrandTotalsHeaderView',
initialize: function(options) {
this.events = _.extend({}, this.events, options.def.events, {
'click [name="gth-custom-button"]': 'buttonClicked'
});
this._super('initialize', [options]);
},
/**
* Click event
*/
buttonClicked: function() {
app.alert.show('success_alert', {
level: 'success',
title: 'Grand Total Header Button was clicked!'
});
},
})
This code will append a click event to our button named gth-custom-button and trigger the buttonClicked
method. Once completed, add your new button array to the grand total header in the $viewdefs['Quotes']['base']['view']['quote-data-grand-totals-header']['buttons']
index.
./custom/modules/Quotes/clients/base/views/quote-data-grand-totals-header/quote-data-grand-totals-header.php
<?php
$viewdefs['Quotes']['base']['view']['quote-data-grand-totals-header'] = array(
'buttons' => array(
array(
'type' => 'quote-data-actiondropdown',
'name' => 'panel_dropdown',
'no_default_action' => true,
'buttons' => array(
array(
'type' => 'button',
'icon' => 'fa-plus',
'name' => 'create_qli_button',
'label' => 'LBL_CREATE_QLI_BUTTON_LABEL',
'acl_action' => 'create',
'tooltip' => 'LBL_CREATE_QLI_BUTTON_TOOLTIP',
),
array(
'type' => 'button',
'icon' => 'fa-plus',
'name' => 'create_comment_button',
'label' => 'LBL_CREATE_COMMENT_BUTTON_LABEL',
'acl_action' => 'create',
'tooltip' => 'LBL_CREATE_COMMENT_BUTTON_TOOLTIP',
),
array(
'type' => 'button',
'icon' => 'fa-plus',
'name' => 'create_group_button',
'label' => 'LBL_CREATE_GROUP_BUTTON_LABEL',
'acl_action' => 'create',
'tooltip' => 'LBL_CREATE_GROUP_BUTTON_TOOLTIP',
),
array(
'type' => 'button',
'icon' => 'fa-plus',
'name' => 'gth-custom-button',
'label' => 'LBL_GTH_CUSTOM_BUTTON',
'acl_action' => 'create',
'tooltip' => 'LBL_GTH_CUSTOM_BUTTON_TOOLTIP',
),
),
),
),
...
);
Finally, create labels under Quotes for the label
and tooltip
indexes. To accomplish this, create a language extension:
./custom/Extension/modules/Quotes/Ext/Language/en_us.custom-button.php
<?php
$mod_strings['LBL_GTH_CUSTOM_BUTTON'] = 'Custom Button';
$mod_strings['LBL_GTH_CUSTOM_BUTTON_TOOLTIP'] = 'Custom Button Tooltip';
Once the files are in place, navigate to Admin > Repair > Quick Repair and Rebuild. Your changes will now be reflected in the system.
List Header
The Quotes List Header is a complex view that pulls data from two different locations. The JavaScript controller is located in ./modules/Quotes/clients/base/views/quote-data-list-header/quote-data-list-header.js
and pulls the metadata from ./modules/Products/clients/base/views/quote-data-group-list/quote-data-group-list.php
. You should note that ProductBundleNotes combines its description field with Product fields in this list.
Modifying Fields in the List Header
To modify the List Header fields, you can copy ./modules/Products/clients/base/views/quote-data-group-list/quote-data-group-list.php
to ./custom/modules/Products/clients/base/views/quote-data-group-list/quote-data-group-list.php
or you may create a metadata extension in ./custom/Extension/modules/Products/Ext/clients/base/views/quote-data-group-list/
. Next, modify the $viewdefs['Products']['base']['view']['quote-data-group-list']['panels'][0]['fields']
index to add or remove your preferred fields.
Example
The example below will remove the "Part Number" (products.mft_part_num) field and replace it with the "Weight" (products.weight) field.
./custom/modules/Products/clients/base/views/quote-data-group-list/quote-data-group-list.php
<?php
$viewdefs['Products']['base']['view']['quote-data-group-list'] = array(
'panels' => array(
array(
'name' => 'products_quote_data_group_list',
'label' => 'LBL_PRODUCTS_QUOTE_DATA_LIST',
'fields' => array(
array(
'name' => 'line_num',
'label' => null,
'widthClass' => 'cell-xsmall',
'css_class' => 'line_num tcenter',
'type' => 'line-num',
'readonly' => true,
),
array(
'name' => 'quantity',
'label' => 'LBL_QUANTITY',
'widthClass' => 'cell-small',
'css_class' => 'quantity',
'type' => 'float',
),
array(
'name' => 'product_template_name',
'label' => 'LBL_ITEM_NAME',
'widthClass' => 'cell-large',
'type' => 'quote-data-relate',
'required' => true,
),
array(
'name' => 'weight',
'label' => 'LBL_WEIGHT',
'type' => 'float',
),
array(
'name' => 'discount_price',
'label' => 'LBL_DISCOUNT_PRICE',
'type' => 'currency',
'convertToBase' => true,
'showTransactionalAmount' => true,
'related_fields' => array(
'discount_price',
'currency_id',
'base_rate',
),
),
array(
'name' => 'discount',
'type' => 'fieldset',
'css_class' => 'quote-discount-percent',
'label' => 'LBL_DISCOUNT_AMOUNT',
'fields' => array(
array(
'name' => 'discount_amount',
'label' => 'LBL_DISCOUNT_AMOUNT',
'type' => 'discount',
'convertToBase' => true,
'showTransactionalAmount' => true,
),
array(
'type' => 'discount-select',
'name' => 'discount_select',
'no_default_action' => true,
'buttons' => array(
array(
'type' => 'rowaction',
'name' => 'select_discount_amount_button',
'label' => 'LBL_DISCOUNT_AMOUNT',
'event' => 'button:discount_select_change:click',
),
array(
'type' => 'rowaction',
'name' => 'select_discount_percent_button',
'label' => 'LBL_DISCOUNT_PERCENT',
'event' => 'button:discount_select_change:click',
),
),
),
),
),
array(
'name' => 'total_amount',
'label' => 'LBL_LINE_ITEM_TOTAL',
'type' => 'currency',
'widthClass' => 'cell-medium',
'showTransactionalAmount' => true,
'related_fields' => array(
'total_amount',
'currency_id',
'base_rate',
),
),
),
),
),
);
Next, create the LBL_WEIGHT
label under Quotes as this is not included in the stock installation. To accomplish this, we will need to create a language extension:
./custom/Extension/modules/Quotes/Ext/Language/en_us.weight.php
<?php
$mod_strings['LBL_WEIGHT'] = 'Weight';
If adding a custom field from the Quotes module to the view, you will also need to add that field to the related_fields
array as outlined in the Record View documentation below. Once the files are in place, navigate to Admin > Repair > Quick Repair and Rebuild. Your changes will now be reflected in the system.
Modifying Row Actions in the List Header
The Quotes List Header contains row actions to create and delete QLI groupings. The actions are identified by, the "check all" checkbox and vertical ellipsis or "hamburger" icon buttons. Editing the 'actions' will allow you to add more buttons to (or remove from) the "Group Selected" and "Delete Selected" buttons. The following section will outline how these items can be modified.
To modify the Row Actions in the List Header, you can copy ./modules/Quotes/clients/base/views/quote-data-list-header/quote-data-list-header.php
to ./custom/modules/Quotes/clients/base/views/quote-data-list-header/quote-data-list-header.php
or you may create a metadata extension in ./custom/Extension/modules/Quotes/clients/base/views/quote-data-list-header/
. Next, modify the $viewdefs['Quotes']['base']['view']['quote-data-list-header']['selection']['actions']
index to add or remove your preferred actions.
Example
The example below will create a new view that extends the QuotesQuoteDataListHeader
view and append a row action to the List Header action list.
First, create your custom view type that will extend the QuotesQuoteDataListHeader
view.
./custom/modules/Quotes/clients/base/views/quote-data-list-header/quote-data-list-header.js
({
extendsFrom: 'QuotesQuoteDataListHeaderView',
initialize: function(options) {
this.events = _.extend({}, this.events, options.def.events, {
'click [name="lh-custom-button"]': 'actionClicked'
});
this._super('initialize', [options]);
},
/**
* Click event
*/
actionClicked: function() {
app.alert.show('success_alert', {
level: 'success',
title: 'List Header Row Action was clicked!'
});
},
})
This code will append a click event to our field named lh-custom-button and trigger the actionClicked
method. Once completed, add your new row action to the list header in the $viewdefs['Quotes']['base']['view']['quote-data-list-header']['selection']['actions']
index.
./custom/modules/Quotes/clients/base/views/quote-data-list-header/quote-data-list-header.php
<?php
$viewdefs['Quotes']['base']['view']['quote-data-list-header'] = array(
'selection' => array(
'type' => 'multi',
'actions' => array(
array(
'name' => 'group_button',
'type' => 'rowaction',
'label' => 'LBL_CREATE_GROUP_SELECTED_BUTTON_LABEL',
'tooltip' => 'LBL_CREATE_GROUP_SELECTED_BUTTON_TOOLTIP',
'acl_action' => 'edit',
),
array(
'name' => 'massdelete_button',
'type' => 'rowaction',
'label' => 'LBL_DELETE_SELECTED_LABEL',
'tooltip' => 'LBL_DELETE_SELECTED_TOOLTIP',
'acl_action' => 'delete',
),
array(
'name' => 'lh-custom-button',
'type' => 'rowaction',
'label' => 'LBL_LH_CUSTOM_ACTION',
'tooltip' => 'LBL_LH_CUSTOM_ACTION_TOOLTIP',
'acl_action' => 'edit',
),
),
),
);
Finally, create labels under Quotes for the label
and tooltip
indexes. To accomplish this, create a language extension:
./custom/Extension/modules/Quotes/Ext/Language/en_us.lh-custom-action.php
<?php
$mod_strings['LBL_LH_CUSTOM_ACTION'] = 'Custom Action';
$mod_strings['LBL_LH_CUSTOM_ACTION_TOOLTIP'] = 'Custom Action Tooltip';
Once the files are in place, navigate to Admin > Repair > Quick Repair and Rebuild. Your changes will now be reflected in the system.
Group Header
The Group Header contains the name and options for each grouping of quoted line items.
Modifying Fields in the Group Header
To modify the Group Header fields, you can copy ./modules/ProductBundles/clients/base/views/quote-data-group-header/quote-data-group-header.php
to ./custom/modules/ProductBundles/clients/base/views/quote-data-group-header/quote-data-group-header.php
or you may create a metadata extension in ./custom/Extension/modules/Products/clients/base/views/quote-data-group-header/
. Next, modify the $viewdefs['ProductBundles']['base']['view']['quote-data-group-header']
index to add or remove your preferred fields.
Example
The example below will append the group total (product_bundles.subtotal) field to the Group Header. It's important to note that when adding additional fields, that changes to the corresponding .hbs
file may be necessary to correct any formatting issues.
./custom/modules/ProductBundles/clients/base/views/quote-data-group-header/quote-data-group-header.php
<?php
$viewdefs['ProductBundles']['base']['view']['quote-data-group-header'] = array(
...
'panels' => array(
array(
'name' => 'panel_quote_data_group_header',
'label' => 'LBL_QUOTE_DATA_GROUP_HEADER',
'fields' => array(
array(
'name' => 'name',
'type' => 'quote-group-title',
'css_class' => 'group-name',
),
'subtotal',
),
),
),
);
If adding a custom field from the Quotes module to the view, you will also need to add that field to the related_fields
array as outlined in the Record View documentation below. Once the files are in place, navigate to Admin > Repair > Quick Repair and Rebuild. Your changes will now be reflected in the system.
Modifying Row Actions in the Group Header
The Quotes Group Header contains row actions to add QLIs and comments as well as editing and deleting QLI groupings. The actions are identified by, the plus and vertical ellipsis or "hamburger" icon buttons. The following section will outline how these items can be modified.
To modify the buttons in the Group Header, you can copy ./modules/ProductBundles/clients/base/views/quote-data-group-header/quote-data-group-header.php
to ./custom/modules/ProductBundles/clients/base/views/quote-data-group-header/quote-data-group-header.php
or you may create a metadata extension in ./custom/Extension/modules/ProductBundles/clients/base/views/quote-data-group-header/
. Next, modify the $viewdefs['ProductBundles']['base']['view']['quote-data-group-header']['buttons']
index to add or remove your preferred actions.
Example
The example below will create a new view that extends the ProductBundlesQuoteDataGroupHeader
view and append a row action to the Group Header vertical elipsis action list.
First, create your custom view type that will extend the ProductBundlesQuoteDataGroupHeader
view.
./custom/modules/ProductBundles/clients/base/views/quote-data-group-header/quote-data-group-header.js
({
extendsFrom: 'ProductBundlesQuoteDataGroupHeaderView',
initialize: function(options) {
this.events = _.extend({}, this.events, options.def.events, {
'click [name="gh-custom-action"]': 'actionClicked'
});
this._super('initialize', [options]);
},
/**
* Click event
*/
actionClicked: function() {
app.alert.show('success_alert', {
level: 'success',
title: 'Group Header Button was clicked!'
});
},
})
This code will append a click event to our field named gh-custom-action and trigger the actionClicked
method. Once completed, add your new row action to the appropriate button group in $viewdefs['Quotes']['base']['view']['quote-data-group-header']['buttons']
. For this example we will target a row action to the edit drop down that is identified in the arrays as having a name of "edit-dropdown". Once found, add your new array to the buttons
index of that array.
./custom/modules/ProductBundles/clients/base/views/quote-data-group-header/quote-data-group-header.php
<?php
$viewdefs['ProductBundles']['base']['view']['quote-data-group-header'] = array(
'buttons' => array(
array(
'type' => 'quote-data-actiondropdown',
'name' => 'create-dropdown',
'icon' => 'fa-plus',
'no_default_action' => true,
'buttons' => array(
array(
'type' => 'rowaction',
'css_class' => 'btn-invisible',
'icon' => 'fa-plus',
'name' => 'create_qli_button',
'label' => 'LBL_CREATE_QLI_BUTTON_LABEL',
'tooltip' => 'LBL_CREATE_QLI_BUTTON_TOOLTIP',
'acl_action' => 'create',
),
array(
'type' => 'rowaction',
'css_class' => 'btn-invisible',
'icon' => 'fa-plus',
'name' => 'create_comment_button',
'label' => 'LBL_CREATE_COMMENT_BUTTON_LABEL',
'tooltip' => 'LBL_CREATE_COMMENT_BUTTON_TOOLTIP',
'acl_action' => 'create',
),
),
),
array(
'type' => 'quote-data-actiondropdown',
'name' => 'edit-dropdown',
'icon' => 'fa-ellipsis-v',
'no_default_action' => true,
'buttons' => array(
array(
'type' => 'rowaction',
'name' => 'edit_bundle_button',
'label' => 'LBL_EDIT_BUTTON',
'tooltip' => 'LBL_EDIT_BUNDLE_BUTTON_TOOLTIP',
'acl_action' => 'edit',
),
array(
'type' => 'rowaction',
'name' => 'delete_bundle_button',
'label' => 'LBL_DELETE_GROUP_BUTTON',
'tooltip' => 'LBL_DELETE_BUNDLE_BUTTON_TOOLTIP',
'acl_action' => 'delete',
),
array(
'type' => 'rowaction',
'name' => 'gh-custom-action',
'label' => 'LBL_GH_CUSTOM_ACTION',
'tooltip' => 'LBL_GH_CUSTOM_ACTION_TOOLTIP',
'acl_action' => 'edit',
),
),
),
),
...
);
Finally, create labels under Quotes for the label
and tooltip
indexes. To accomplish this, create a language extension:
./custom/Extension/modules/Quotes/Ext/Language/en_us.gh-custom-action.php
<?php
$mod_strings['LBL_GH_CUSTOM_ACTION'] = 'Custom Action';
$mod_strings['LBL_GH_CUSTOM_ACTION_TOOLTIP'] = 'Custom Action Tooltip';
Once the files are in place, navigate to Admin > Repair > Quick Repair and Rebuild. Your changes will now be reflected in the system.
Group List
The Group List contains the comments and selected quoted line items.
Modifying Fields in the Group List
To modify the Group List fields, you can copy ./modules/Products/clients/base/views/quote-data-group-list/quote-data-group-list.php
to ./custom/modules/Products/clients/base/views/quote-data-group-list/quote-data-group-list.php
or you may create a metadata extension in ./custom/Extension/modules/Products/Ext/clients/base/views/quote-data-group-list/
. Next, modify the $viewdefs['Products']['base']['view']['quote-data-group-list']['panels']
index to add or remove your preferred fields.
Example
The example below will remove the "Manufacturer Part Number" (products.mft_part_num) and append the "Vendor Part Number" (products.vendor_part_num) field to the Group List.
./custom/modules/Products/clients/base/views/quote-data-group-list/quote-data-group-list.php
<?php
$viewdefs['Products']['base']['view']['quote-data-group-list'] = array(
'panels' => array(
array(
'name' => 'products_quote_data_group_list',
'label' => 'LBL_PRODUCTS_QUOTE_DATA_LIST',
'fields' => array(
array(
'name' => 'line_num',
'label' => null,
'widthClass' => 'cell-xsmall',
'css_class' => 'line_num tcenter',
'type' => 'line-num',
'readonly' => true,
),
array(
'name' => 'quantity',
'label' => 'LBL_QUANTITY',
'widthClass' => 'cell-small',
'css_class' => 'quantity',
'type' => 'float',
),
array(
'name' => 'product_template_name',
'label' => 'LBL_ITEM_NAME',
'widthClass' => 'cell-large',
'type' => 'quote-data-relate',
'required' => true,
),
array(
'name' => 'vendor_part_num',
'label' => 'LBL_VENDOR_PART_NUM',
'type' => 'base',
),
array(
'name' => 'discount_price',
'label' => 'LBL_DISCOUNT_PRICE',
'type' => 'currency',
'convertToBase' => true,
'showTransactionalAmount' => true,
'related_fields' => array(
'discount_price',
'currency_id',
'base_rate',
),
),
array(
'name' => 'discount',
'type' => 'fieldset',
'css_class' => 'quote-discount-percent',
'label' => 'LBL_DISCOUNT_AMOUNT',
'fields' => array(
array(
'name' => 'discount_amount',
'label' => 'LBL_DISCOUNT_AMOUNT',
'type' => 'discount',
'convertToBase' => true,
'showTransactionalAmount' => true,
),
array(
'type' => 'discount-select',
'name' => 'discount_select',
'no_default_action' => true,
'buttons' => array(
array(
'type' => 'rowaction',
'name' => 'select_discount_amount_button',
'label' => 'LBL_DISCOUNT_AMOUNT',
'event' => 'button:discount_select_change:click',
),
array(
'type' => 'rowaction',
'name' => 'select_discount_percent_button',
'label' => 'LBL_DISCOUNT_PERCENT',
'event' => 'button:discount_select_change:click',
),
),
),
),
),
array(
'name' => 'total_amount',
'label' => 'LBL_LINE_ITEM_TOTAL',
'type' => 'currency',
'widthClass' => 'cell-medium',
'showTransactionalAmount' => true,
'related_fields' => array(
'total_amount',
'currency_id',
'base_rate',
),
),
),
),
),
);
Next, create the LBL_VENDOR_PART_NUM
label under Quotes as this is not included in the stock installation. To accomplish this, we will need to create a language extension:
./custom/Extension/modules/Quotes/Ext/Language/en_us.vendor.php
<?php
$mod_strings['LBL_VENDOR_PART_NUM'] = 'Vendor Part Number';
If adding a custom field from the Quotes module to the view, you will also need to add that field to the related_fields
array as outlined in the Record View documentation below. Once the files are in place, navigate to Admin > Repair > Quick Repair and Rebuild. Your changes will now be reflected in the system.
Modifying Row Actions in the Group List
The Quotes Group List contains row actions to add/remove QLIs and comments. The actions are identified by, the vertical ellipsis icon buttons. The following section will outline how these items can be modified.
To modify the buttons in the Group List , you can copy ./modules/ProductBundles/clients/base/views/quote-data-group-list/quote-data-group-list.php
to ./custom/modules/ProductBundles/clients/base/views/quote-data-group-list/quote-data-group-list.php
or you may create a metadata extension in ./custom/Extension/modules/ProductBundles/clients/base/views/quote-data-group-list/
. Next, modify the $viewdefs['ProductBundles']['base']['view']['quote-data-group-list']['selection']
index to add or remove your preferred actions.
Example
The example below will create a new view that extends the ProductBundlesQuoteDataGroupList
view and append a row action to the Group List's vertical elipsis action list.
First, create your custom view type that will extend the ProductBundlesQuoteDataGroupList
view. This will contain the JavaScript for your action.
./custom/modules/ProductBundles/clients/base/views/quote-data-group-list/quote-data-group-list.js
({
extendsFrom: 'ProductBundlesQuoteDataGroupListView',
initialize: function(options) {
this.events = _.extend({}, this.events, options.def.events, {
'click [name="gl-custom-action"]': 'actionClicked'
});
this._super('initialize', [options]);
},
/**
* Click event
*/
actionClicked: function() {
app.alert.show('success_alert', {
level: 'success',
title: 'List Header Row Action was clicked!'
});
},
})
This code will append a click event to our field named gl-custom-action and trigger the actionClicked
method. Once completed, add your new row action to the group list in the $viewdefs['ProductBundles']['base']['view']['quote-data-group-list']['selection']['actions']
index.
./custom/modules/ProductBundles/clients/base/views/quote-data-group-list/quote-data-group-list.php
<?php
$viewdefs['ProductBundles']['base']['view']['quote-data-group-list'] = array(
'selection' => array(
'type' => 'multi',
'actions' => array(
array(
'type' => 'rowaction',
'name' => 'edit_row_button',
'label' => 'LBL_EDIT_BUTTON',
'tooltip' => 'LBL_EDIT_BUTTON',
'acl_action' => 'edit',
),
array(
'type' => 'rowaction',
'name' => 'delete_row_button',
'label' => 'LBL_DELETE_BUTTON',
'tooltip' => 'LBL_DELETE_BUTTON',
'acl_action' => 'delete',
),
array(
'type' => 'rowaction',
'name' => 'gl-custom-action',
'label' => 'LBL_GL_CUSTOM_ACTION',
'tooltip' => 'LBL_GL_CUSTOM_ACTION_TOOLTIP',
'acl_action' => 'edit',
),
),
),
);
Finally, create labels under Quotes for the label
and tooltip
indexes. To accomplish this, create a language extension:
./custom/Extension/modules/Quotes/Ext/Language/en_us.gh-custom-action.php
<?php
$mod_strings['LBL_GL_CUSTOM_ACTION'] = 'Custom Action';
$mod_strings['LBL_GL_CUSTOM_ACTION_TOOLTIP'] = 'Custom Action Tooltip';
Once the files are in place, navigate to Admin > Repair > Quick Repair and Rebuild. Your changes will now be reflected in the system.
Group Footer
The Group Footer contains the total for each grouping of quoted line items.
Modifying Fields in the Group Footer
To modify the GroupFooter fields, you can copy ./modules/ProductBundles/clients/base/views/quote-data-group-footer/quote-data-group-footer.php
to ./custom/modules/ProductBundles/clients/base/views/quote-data-group-footer/quote-data-group-footer.php
or you may create a metadata extension in ./custom/Extension/modules/ProductBundles/clients/base/views/quote-data-group-footer/
. Next, modify the $viewdefs['ProductBundles']['base']['view']['quote-data-group-footer']
index to add or remove your preferred fields.
Example
The example below will append the bundle stage (product_bundles.bundle_stage) field to the Group Footer. It's important to note that when adding additional fields, that changes to the corresponding .hbs
file may be necessary to correct any formatting issues.
./custom/modules/ProductBundles/clients/base/views/quote-data-group-header/quote-data-group-footer.php
<?php
$viewdefs['ProductBundles']['base']['view']['quote-data-group-footer'] = array(
'panels' => array(
array(
'name' => 'panel_quote_data_group_footer',
'label' => 'LBL_QUOTE_DATA_GROUP_FOOTER',
'fields' => array(
'bundle_stage',
array(
'name' => 'new_sub',
'label' => 'LBL_GROUP_TOTAL',
'type' => 'currency',
),
),
),
),
);
If adding custom fields from the Product Bundles module to the view, you will also need to add that field to the related_fields
array as outlined in the Record View documentation below. Once the files are in place, navigate to Admin > Repair > Quick Repair and Rebuild. Your changes will now be reflected in the system.
Grand Totals Footer
The Grand Total Footer contains the calculated totals for the quote. It mimics the information found in the Grand Total Header.
Modifying Fields in the Grand Totals Footer
To modify the Grand Totals Footer fields, you can copy ./modules/Quotes/clients/base/views/quote-data-grand-totals-footer/quote-data-grand-totals-footer.php
to ./custom/modules/Quotes/clients/base/views/quote-data-grand-totals-footer/quote-data-grand-totals-footer.php
or you may create a metadata extension in ./custom/Extension/modules/Quotes/clients/base/views/quote-data-grand-totals-footer/
. Next, modify the $viewdefs['Quotes']['base']['view']['quote-data-grand-totals-footer']['panels'][0]['fields']
index to add or remove your preferred fields.
Example
The example below will remove the "Shipping" field (quotes.shipping) field from the layout.
./custom/modules/Quotes/clients/base/views/quote-data-grand-totals-footer/quote-data-grand-totals-footer.php
<?php
$viewdefs['Quotes']['base']['view']['quote-data-grand-totals-footer'] = array(
'panels' => array(
array(
'name' => 'panel_quote_data_grand_totals_footer',
'label' => 'LBL_QUOTE_DATA_GRAND_TOTALS_FOOTER',
'fields' => array(
'quote_num',
array(
'name' => 'new_sub',
'type' => 'currency',
),
array(
'name' => 'tax',
'type' => 'currency',
'related_fields' => array(
'taxrate_value',
),
),
array(
'name' => 'total',
'label' => 'LBL_LIST_GRAND_TOTAL',
'type' => 'currency',
'css_class' => 'grand-total',
),
),
),
),
);
If adding custom fields from the Quotes module to the view, you will also need to add that field to the related_fields
array as outlined in the Record View documentation below. Once the files are in place, navigate to Admin > Repair > Quick Repair and Rebuild. Your changes will now be reflected in the system.
Record View
The record view for Quotes is updated and used like the standard Record View for all modules. The one major difference to note is that the JavaScript models used by the previous views above, are derived from the Model defined in the record view metadata.
Adding Custom Related Fields to Model
In the Record View metadata, the first panel panel_header
containing the picture and name fields for the Quote record, contains a related_fields
property in the name
definition that defines the related Product Bundles and Products data that is pulled into the JavaScript model. When custom fields are added to the above mentioned views you will need to add them to the related_fields
property in their respective related module so that they are properly loaded during the page load.
The following example adds the custom_product_bundle_field_c
field from the Product Bundles module, and the custom_product_field_c
from the Products module into the retrieved Model.
./custom/modules/Quotes/clients/base/views/record/record.php
<?php
$viewdefs['Quotes']['base']['view']['record'] = array(
...
'panels' => array(
array(
'name' => 'panel_header',
'label' => 'LBL_PANEL_HEADER',
'header' => true,
'fields' => array(
array(
'name' => 'picture',
'type' => 'avatar',
'size' => 'large',
'dismiss_label' => true,
'readonly' => true,
),
array(
'name' => 'name',
'events' => array(
'keyup' => 'update:quote',
),
'related_fields' => array(
array(
'name' => 'bundles',
'fields' => array(
'id',
'bundle_stage',
'currency_id',
'base_rate',
'currencies',
'name',
'deal_tot',
'deal_tot_usdollar',
'deal_tot_discount_percentage',
'new_sub',
'new_sub_usdollar',
'position',
'related_records',
'shipping',
'shipping_usdollar',
'subtotal',
'subtotal_usdollar',
'tax',
'tax_usdollar',
'taxrate_id',
'team_count',
'team_count_link',
'team_name',
'taxable_subtotal',
'total',
'total_usdollar',
'default_group',
'custom_product_bundle_field_c',
array(
'name' => 'product_bundle_items',
'fields' => array(
'name',
'quote_id',
'description',
'quantity',
'product_template_name',
'product_template_id',
'deal_calc',
'mft_part_num',
'discount_price',
'discount_amount',
'tax',
'tax_class',
'subtotal',
'position',
'currency_id',
'base_rate',
'discount_select',
'custom_product_field_c',
),
'max_num' => -1,
),
),
'max_num' => -1,
'order_by' => 'position:asc',
),
),
),
),
),
...
),
);
Quote PDFs
Ungrouped Quoted Line Items and Notes
As of Sugar 7.9, the quotes module allows users to add quoted line items and notes without first adding a group. Adding at least one group to your quote was mandatory in earlier versions. This document will cover how to update your custom PDF templates to support ungrouped QLIs and notes. Ungrouped items are technically added to a default group. The goal is to exclude this group when no items exist in it.
PDF Manager Templates
In your PDF Manager templates, you may have code similar to what is shown below to iterate over the groups in a quote:
{foreach from=$product_bundles item="bundle"}
....
{/foreach}
To correct this for 7.9, we will need to add an if
statement to check to see if the group is empty. If it is empty, it will be ignored in your PDF. The benefit is that it will also exclude any other empty groups in your quote and clean the generated PDF.
{foreach from=$product_bundles item="bundle"}
{if $bundle.products|@count}
....
{/if}
{/foreach}
Smarty Templates
In Smarty templates, you may have code similar to what is shown below to iterate over the groups in a quote:
{literal}{foreach from=$product_bundles item="bundle"}{/literal}
...
{literal}{/foreach}{/literal}
To correct this for 7.9, we will need to add an if
statement to check to see if the group is empty. If it is empty, it will be ignored in your PDF. The benefit is that it will also exclude any other empty groups in your quote and clean the generated PDF.
{literal}{foreach from=$product_bundles item="bundle"}{/literal}
{literal}{if $bundle.products|@count}{/literal}
...
{literal}{/if}{/literal}
{literal}{/foreach}{/literal}
Creating Custom PDF Templates
With Sugar, there are generally two routes that can be taken to create custom PDF templates. The first and most recommended route is to use the PDF Manager found in the Admin > PDF Manager. The second route is to extend the Sugarpdf class and write your own template using PHP and the TCPDF library. This method should only be used if you are unable to accomplish your business needs through the PDF Manager.
Creating the TCPDF Template
The first step is to create your custom TCPDF template in ./custom/modules/Quotes/sugarpdf/
. For our example, we will extend QuotesSugarpdfStandard
, found at ./modules/Quotes/sugarpdf/sugarpdf.standard.php
, to a new file in ./custom/modules/Quotes/sugarpdf/
to create a custom invoice. The QuotesSugarpdfStandard
class sets up our general quote requirements and extends the Sugarpdf
class. Technical documentation on templating can be found in the Sugar PDF documentation. Our class will be named QuotesSugarpdfCustomInvoice
as the naming must be in the format of <module>Sugarpdf<pdf view>
.
./custom/modules/Quotes/sugarpdf/sugarpdf.custominvoice.php
<?php
if (!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
require_once('modules/Quotes/sugarpdf/sugarpdf.standard.php');
class QuotesSugarpdfCustomInvoice extends QuotesSugarpdfStandard
{
function preDisplay()
{
global $mod_strings, $timedate;
parent::preDisplay();
$quote[0]['TITLE'] = $mod_strings['LBL_PDF_INVOICE_NUMBER'];
$quote[1]['TITLE'] = $mod_strings['LBL_PDF_QUOTE_DATE'];
$quote[2]['TITLE'] = $mod_strings['LBL_PURCHASE_ORDER_NUM'];
$quote[3]['TITLE'] = $mod_strings['LBL_PAYMENT_TERMS'];
$quote[0]['VALUE']['value'] = format_number_display($this->bean->quote_num, $this->bean->system_id);
$quote[1]['VALUE']['value'] = $timedate->nowDate();
$quote[2]['VALUE']['value'] = $this->bean->purchase_order_num;
$quote[3]['VALUE']['value'] = $this->bean->payment_terms;
// these options override the params of the $options array.
$quote[0]['VALUE']['options'] = array();
$quote[1]['VALUE']['options'] = array();
$quote[2]['VALUE']['options'] = array();
$quote[3]['VALUE']['options'] = array();
$html = $this->writeHTMLTable($quote, true, $this->headerOptions);
$this->SetHeaderData(PDF_HEADER_LOGO, PDF_HEADER_LOGO_WIDTH, $mod_strings['LBL_PDF_INVOICE_TITLE'], $html);
}
/**
* This method build the name of the PDF file to output.
*/
function buildFileName()
{
global $mod_strings;
$fileName = preg_replace("#[^A-Z0-9\-_\.]#i", "_", $this->bean->shipping_account_name);
if (!empty($this->bean->quote_num)) {
$fileName .= "_{$this->bean->quote_num}";
}
$fileName = $mod_strings['LBL_INVOICE'] . "_{$fileName}.pdf";
if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/MSIE/", $_SERVER['HTTP_USER_AGENT'])) {
//$fileName = $locale->translateCharset($fileName, $locale->getExportCharset());
$fileName = urlencode($fileName);
}
$this->fileName = $fileName;
}
}
Next, register the layout. This is a two step process. The first step is to create ./custom/modules/Quotes/Layouts.php
which will map our view action to the physical PHP file.
./custom/modules/Quotes/Layouts.php
<?php
$layouts['CustomInvoice'] = 'custom/modules/Quotes/sugarpdf/sugarpdf.custominvoice.php';
The second step is to update the layouts_dom
dropdown list by navigating to Admin > Dropdown Editor. Once there, create a new entry in the list with an Item Name of "CustomInvoice" and a Display Label of your choice. It's also recommended to remove the Quote and Invoice Templates if they are not being used to avoid confusion. This will create a ./custom/Extension/application/Ext/Language/en_us.sugar_layouts_dom.php
file that should look similar to:
./custom/Extension/application/Ext/Language/en_us.sugar_layouts_dom.php
<?php
$app_list_strings['layouts_dom']=array (
'CustomInvoice' => 'Custom Invoice',
);
Once the layout is registered, we need to extend the pdfaction
field for the Quotes module to render our custom pdf templates in the record view. An example of this is shown below:
./custom/modules/Quotes/clients/base/fields/pdfaction/pdfaction.js
/**
* @class View.Fields.Base.Quotes.PdfactionField
* @alias SUGAR.App.view.fields.BaseQuotesPdfactionField
* @extends View.Fields.Base.PdfactionField
*/
({
extendsFrom: 'PdfactionField',
/**
* @inheritdoc
* Create PDF Template collection in order to get available template list.
*/
initialize: function(options) {
this._super('initialize', [options]);
},
/**
* Define proper filter for PDF template list.
* Fetch the collection to get available template list.
* @private
*/
_fetchTemplate: function() {
this.fetchCalled = true;
var collection = this.templateCollection;
collection.filterDef = {'$and': [{
'base_module': this.module
}, {
'published': 'yes'
}]};
collection.fetch({
success: function(){
var models = [];
_.each(app.lang.getAppListStrings('layouts_dom'), function(template, id){
var model = new Backbone.Model({
id: id,
name: template
});
models.push(model);
});
collection.add(models);
}
});
},
/**
* Build download link url.
*
* @param {String} templateId PDF Template id.
* @return {string} Link url.
* @private
*/
_buildDownloadLink: function(templateId) {
var sugarpdf = this._isUUID(templateId)? 'pdfmanager' : templateId;
var urlParams = $.param({
'action': 'sugarpdf',
'module': this.module,
'sugarpdf': sugarpdf,
'record': this.model.id,
'pdf_template_id': templateId
});
return '?' + urlParams;
},
/**
* Build email pdf link url.
*
* @param {String} templateId PDF Template id.
* @return {string} Email pdf url.
* @private
*/
_buildEmailLink: function(templateId) {
var sugarpdf = this._isUUID(templateId)? 'pdfmanager' : templateId;
return '#' + app.bwc.buildRoute(this.module, null, 'sugarpdf', {
'sugarpdf': sugarpdf,
'record': this.model.id,
'pdf_template_id': templateId,
'to_email': '1'
});
},
/**
* tests to see if a templateId is a uuid or template name.
*
* @param {String} templateId PDF Template id
* @return {boolean} true if uuid, false if not
* @private
*/
_isUUID: function(templateId) {
var regex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
return regex.test(templateId);
}
})
Once the files are in place, navigate to Admin > Repair > Quick Repair and Rebuild. Your changes will now be reflected in the system and navigating to a url in the format of index.php?module=Quotes&record=<record id>&action=sugarpdf&sugarpdf=CustomInvoice
will generate the pdf document.
Hiding PDF Buttons
In some circumstances, users may not have a need to generate PDFs from Quotes. If this should occur, a developer can copy ./modules/Quotes/clients/base/views/record/record.php
to ./custom/modules/Quotes/clients/base/views/record/record.php
if it doesn't already exist. Next, find the array with a name of main_dropwdown
in the $viewdefs['Quotes']['base']['view']['record']['buttons']
index. Once found, you will then need to locate the buttons
index within that array. You will then need to remove the following arrays from that index:
array(
'type' => 'pdfaction',
'name' => 'download-pdf',
'label' => 'LBL_PDF_VIEW',
'action' => 'download',
'acl_action' => 'view',
),
array(
'type' => 'pdfaction',
'name' => 'email-pdf',
'label' => 'LBL_PDF_EMAIL',
'action' => 'email',
'acl_action' => 'view',
),
Your file should look similar to what is shown below:
custom/modules/Quotes/clients/base/views/record/record.php
<?php
$viewdefs['Quotes']['base']['view']['record'] = array(
'buttons' => array(
array(
'type' => 'button',
'name' => 'cancel_button',
'label' => 'LBL_CANCEL_BUTTON_LABEL',
'css_class' => 'btn-invisible btn-link',
'showOn' => 'edit',
'events' => array(
'click' => 'button:cancel_button:click',
),
),
array(
'type' => 'rowaction',
'event' => 'button:save_button:click',
'name' => 'save_button',
'label' => 'LBL_SAVE_BUTTON_LABEL',
'css_class' => 'btn btn-primary',
'showOn' => 'edit',
'acl_action' => 'edit',
),
array(
'type' => 'actiondropdown',
'name' => 'main_dropdown',
'primary' => true,
'showOn' => 'view',
'buttons' => array(
array(
'type' => 'rowaction',
'event' => 'button:edit_button:click',
'name' => 'edit_button',
'label' => 'LBL_EDIT_BUTTON_LABEL',
'acl_action' => 'edit',
),
array(
'type' => 'shareaction',
'name' => 'share',
'label' => 'LBL_RECORD_SHARE_BUTTON',
'acl_action' => 'view',
),
array(
'type' => 'divider',
),
array(
'type' => 'convert-to-opportunity',
'event' => 'button:convert_to_opportunity:click',
'name' => 'convert_to_opportunity_button',
'label' => 'LBL_QUOTE_TO_OPPORTUNITY_LABEL',
'acl_module' => 'Opportunities',
'acl_action' => 'create',
),
array(
'type' => 'divider',
),
array(
'type' => 'rowaction',
'event' => 'button:historical_summary_button:click',
'name' => 'historical_summary_button',
'label' => 'LBL_HISTORICAL_SUMMARY',
'acl_action' => 'view',
),
array(
'type' => 'rowaction',
'event' => 'button:audit_button:click',
'name' => 'audit_button',
'label' => 'LNK_VIEW_CHANGE_LOG',
'acl_action' => 'view',
),
array(
'type' => 'rowaction',
'event' => 'button:find_duplicates_button:click',
'name' => 'find_duplicates_button',
'label' => 'LBL_DUP_MERGE',
'acl_action' => 'edit',
),
array(
'type' => 'divider',
),
array(
'type' => 'rowaction',
'event' => 'button:delete_button:click',
'name' => 'delete_button',
'label' => 'LBL_DELETE_BUTTON_LABEL',
'acl_action' => 'delete',
),
),
),
array(
'name' => 'sidebar_toggle',
'type' => 'sidebartoggle',
),
),
...
);
Once the files are in place, navigate to Admin > Repair > Quick Repair and Rebuild. Your changes will now be reflected in the system.