From 830b74561bc30963474d0b9d0194128c808c6746 Mon Sep 17 00:00:00 2001 From: Patrick Viglianti Date: Mon, 30 Dec 2024 23:30:47 -0500 Subject: [PATCH] commit 12_30_24 --- .vscode/extensions.json | 5 + .vscode/settings.json | 4 + Background Scripts/BACKGROUND_UpdateCHG.txt | 11 + Customer Instance Customizations/README.md | 28 + .../Weis/Configurations/jsconfig.json | 4 + ...0af0310c7a7_storeportal_activerequests.xml | 389 +++ .../client_script.js | 16 + .../css.scss | 22 + .../script.js | 281 +++ .../template.html | 34 + .../weisdev/global/sp_widget/_map.json | 1 + .../Weis/Configurations/weisdev/settings.json | 5 + .../SC Catalog Item/CSS -SCSS | 264 ++ .../SC Catalog Item/Client Script | 1693 +++++++++++++ .../SC Catalog Item/Options Schema | 1 + .../SC Catalog Item/README.md | 4 + .../SC Catalog Item/Server Script | 586 +++++ Service Portal Widgets/SC Catalog Item/html | 316 +++ autocomplete/GlideQuery.js | 886 +++++++ autocomplete/client.d.ts | 344 +++ autocomplete/server.d.ts | 2140 +++++++++++++++++ 21 files changed, 7034 insertions(+) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 Background Scripts/BACKGROUND_UpdateCHG.txt create mode 100644 Customer Instance Customizations/README.md create mode 100644 Customer Instance Customizations/Weis/Configurations/jsconfig.json create mode 100644 Customer Instance Customizations/Weis/Configurations/sp_widget_73ef16224ff84700334cd0af0310c7a7_storeportal_activerequests.xml create mode 100644 Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/StorePortal_Active_Reqeuests_Report/client_script.js create mode 100644 Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/StorePortal_Active_Reqeuests_Report/css.scss create mode 100644 Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/StorePortal_Active_Reqeuests_Report/script.js create mode 100644 Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/StorePortal_Active_Reqeuests_Report/template.html create mode 100644 Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/_map.json create mode 100644 Customer Instance Customizations/Weis/Configurations/weisdev/settings.json create mode 100644 Service Portal Widgets/SC Catalog Item/CSS -SCSS create mode 100644 Service Portal Widgets/SC Catalog Item/Client Script create mode 100644 Service Portal Widgets/SC Catalog Item/Options Schema create mode 100644 Service Portal Widgets/SC Catalog Item/README.md create mode 100644 Service Portal Widgets/SC Catalog Item/Server Script create mode 100644 Service Portal Widgets/SC Catalog Item/html create mode 100644 autocomplete/GlideQuery.js create mode 100644 autocomplete/client.d.ts create mode 100644 autocomplete/server.d.ts diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000..897af65d73 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "dbaeumer.vscode-eslint" + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..82391b62df --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "files.autoGuessEncoding": true, + "files.autoSaveWhenNoErrors": true +} \ No newline at end of file diff --git a/Background Scripts/BACKGROUND_UpdateCHG.txt b/Background Scripts/BACKGROUND_UpdateCHG.txt new file mode 100644 index 0000000000..3f33e64eed --- /dev/null +++ b/Background Scripts/BACKGROUND_UpdateCHG.txt @@ -0,0 +1,11 @@ +var uC = new GlideRecord('u_change_legacy'); +uC.setLimit(1); +uC.query(); + +while(uC.next()) { +uC.number = 'L' + uC.number +uC.autoSysField(false); +uC.setWorkflow(false); +uC.update(); +gs.info(uC.number); +} \ No newline at end of file diff --git a/Customer Instance Customizations/README.md b/Customer Instance Customizations/README.md new file mode 100644 index 0000000000..0cbb0336fb --- /dev/null +++ b/Customer Instance Customizations/README.md @@ -0,0 +1,28 @@ +# Customer Instance Customizations + +This directory contains customizations specific to customer instances. These customizations are tailored to meet the unique requirements of individual customers and may include scripts, configurations, and other resources. + +## Structure + +The directory is organized by client as follows: + +- **Weis/** + - **weis-dev**: Contains customizations for the "Weis Dev" customer instance. + - **global/sp_widget**: Contains global scripts and configurations. + - **StorePortal_Active_Requests_Report**: Contains configuration breakdown, scripts and files for Weis custom service portal widget to display ticket status on the store portal (files included in this folder include the html, css,client script and server script fields on the widget form) + - **Scripts/**: Contains custom scripts used for various purposes. + - **Configurations/**: Holds configuration files and settings. + - **Resources/**: Includes additional resources such as documentation, images, etc. +- **PVH/** + - **Scripts/**: Contains custom scripts used for various purposes. + - **Configurations/**: Holds configuration files and settings. + - **Resources/**: Includes additional resources such as documentation, images, etc. + + +## Contributing + +If you wish to contribute to this directory, please follow the guidelines outlined in the [CONTRIBUTING.md](../CONTRIBUTING.md) file. + +## Support + +For any issues or questions regarding these customizations, please contact the support team or refer to the documentation provided in the `Resources` directory. diff --git a/Customer Instance Customizations/Weis/Configurations/jsconfig.json b/Customer Instance Customizations/Weis/Configurations/jsconfig.json new file mode 100644 index 0000000000..283224a208 --- /dev/null +++ b/Customer Instance Customizations/Weis/Configurations/jsconfig.json @@ -0,0 +1,4 @@ +/** + this should be in root of working directory, created by sn-scriptsync + without this file, autocintellisense does not work +*/ \ No newline at end of file diff --git a/Customer Instance Customizations/Weis/Configurations/sp_widget_73ef16224ff84700334cd0af0310c7a7_storeportal_activerequests.xml b/Customer Instance Customizations/Weis/Configurations/sp_widget_73ef16224ff84700334cd0af0310c7a7_storeportal_activerequests.xml new file mode 100644 index 0000000000..e7d3f8004d --- /dev/null +++ b/Customer Instance Customizations/Weis/Configurations/sp_widget_73ef16224ff84700334cd0af0310c7a7_storeportal_activerequests.xml @@ -0,0 +1,389 @@ + + + +custom + +c +td {padding:15px;} + +tr:nth-child(even) { + background-color: #e8e8ee; +} + +.Container {border-left:.5px solid #d3d3d3; +border-right:.5px solid #d3d3d3; +border-bottom:.5px solid #d3d3d3; +} +.DisplayFields {font-size:18px; +padding-bottom:2px; +} +.SecondaryFields{font-size:12px; +padding-bottom:1px; +} + +button {width:100%; background-color:green; color:white; +border: 1px solid black; +} + +.Pending{background-color:#9edb0f;} +sp_instance + +should include incidents w/o workorders, ad hoc without work orders, and work orders + + +true + +false + +StorePortal_Active_Reqeuests_Report + +false + + +false +sp_widget +jvincent +2017-08-28 15:05:53 +true +73ef16224ff84700334cd0af0310c7a7 +247 +StorePortal_Active_Reqeuests_Report +global + +false +global +sp_widget_73ef16224ff84700334cd0af0310c7a7 +mjames +2019-10-16 02:07:14 + + + diff --git a/Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/StorePortal_Active_Reqeuests_Report/client_script.js b/Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/StorePortal_Active_Reqeuests_Report/client_script.js new file mode 100644 index 0000000000..4f3d47d218 --- /dev/null +++ b/Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/StorePortal_Active_Reqeuests_Report/client_script.js @@ -0,0 +1,16 @@ +function($scope) { + /* widget controller */ + var c = this; + + $scope.orderField = "openedat"; + +$scope.changeSort = function(field){ + if($scope.orderField == field){ + $scope.orderReverse = !$scope.orderReverse; + }else{ + $scope.orderField=field; + } + + }; + +} \ No newline at end of file diff --git a/Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/StorePortal_Active_Reqeuests_Report/css.scss b/Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/StorePortal_Active_Reqeuests_Report/css.scss new file mode 100644 index 0000000000..6e169933c2 --- /dev/null +++ b/Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/StorePortal_Active_Reqeuests_Report/css.scss @@ -0,0 +1,22 @@ +td {padding:15px;} + +tr:nth-child(even) { + background-color: #e8e8ee; +} + +.Container {border-left:.5px solid #d3d3d3; +border-right:.5px solid #d3d3d3; +border-bottom:.5px solid #d3d3d3; +} +.DisplayFields {font-size:18px; +padding-bottom:2px; +} +.SecondaryFields{font-size:12px; +padding-bottom:1px; +} + +button {width:100%; background-color:green; color:white; +border: 1px solid black; +} + +.Pending{background-color:#9edb0f;} \ No newline at end of file diff --git a/Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/StorePortal_Active_Reqeuests_Report/script.js b/Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/StorePortal_Active_Reqeuests_Report/script.js new file mode 100644 index 0000000000..f60d49ec07 --- /dev/null +++ b/Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/StorePortal_Active_Reqeuests_Report/script.js @@ -0,0 +1,281 @@ +(function() { + + + data.requests = []; + data.requestlist=[]; + //get the current user + //var myUserObject = gs.getUser().getID(); +var ggrp; + +var grp = new GlideRecord('sys_user_group'); +grp.addActiveQuery(); +grp.addQuery('name','Pharmacy Support Desk'); +grp.query(); +while(grp.next()){ +ggrp = grp.sys_id; + } + + var gru = new GlideRecord('sys_user'); + gru.addActiveQuery(); + gru.addQuery('sys_id','=',gs.getUser().getID()); + gru.query(); + var grunum; + var storeid; + var managerid; + + while (gru.next()){ + + + + + +//Tickets --------------------------------------------------------------------------------------------------------------------------- + //get all tickets based on retreived users location + var gr = new GlideRecord('ticket'); + gr.addActiveQuery(); + //gr.addQuery('location', gru.location); + gr.addQuery('watch_list','CONTAINS', gru.sys_id); + //gr.addQuery('u_routing_suggestion_group','NOT','ggrp'); + var qc2 = gr.addQuery('u_routing_suggestion_group','!=',ggrp); + qc2.addOrCondition('u_routing_suggestion_group',''); + gr.orderByDesc('sys_updated_on'); + gr.query(); + + + while(gr.next()){ + try { + var request = {}; + request.number = gr.getDisplayValue('number'); + request.sys_id = gr.getUniqueValue(); + request.tableName = gr.getTableName(); + request.shortdescription = gr.getDisplayValue('short_description'); + request.url = {id: 'sc_request', table: 'ticket', sys_id: request.sys_id}; + //if (gr.getValue('short_description').length > 60) {request.shortdescription = request.shortdescription + "..."} + + request.openedat = gr.getValue('opened_at'); + + + request.state = gr.getDisplayValue('state'); + data.requestlist.push(request); + } +catch(err) { + + + +} + + } + + + } + + + + + +//Incidents --------------------------------------------------------------------------------------------------------------------------- + + var gr = new GlideRecord('incident'); + gr.addActiveQuery(); + //gr.setLimit(10); + + //gr.addQuery('location', gru.location); + gr.addQuery('watch_list','CONTAINS', gru.sys_id); + gr.addQuery('u_associated_work_order', 'false'); + //gr.addQuery('assignment_group','!=',ggrp); + var qc2 = gr.addQuery('assignment_group','!=',ggrp); + qc2.addOrCondition('assignment_group',''); + gr.orderByDesc('sys_updated_on'); + gr.query(); + + + while(gr.next()){ + try{ + var request = {}; + request.number = gr.getDisplayValue('number'); + request.sys_id = gr.getUniqueValue(); + request.tableName = gr.getTableName(); + + //if (gr.short_description.getValue() == ''){ + //request.shortdescription = "(No description)"; + //}else{ + request.shortdescription = "No Description"; + try{ + request.shortdescription = gr.getValue('short_description').substring(0,60) ; + } + catch(err){ + + } + //} + + request.url = {id: 'sc_request', table: 'incident', sys_id: request.sys_id}; + //if (gr.getValue('short_description').length > 60) {request.shortdescription = request.shortdescription + "..."} + //request.enviro = gs.getProperty("instance_name"); + request.openedat = gr.getValue('opened_at'); + + + request.state = gr.getDisplayValue('state'); + data.requestlist.push(request); + } +catch(errIncident) { + + + +} + } + +//Ad Hoc --------------------------------------------------------------------------------------------------------------------------- + + var grx = new GlideRecord('u_ad_hoc_request'); + grx.addActiveQuery(); + //grx.setLimit(10); + grx.addQuery('u_associated_work_order','0'); + var qc2 = grx.addQuery('assignment_group','!=',ggrp); + qc2.addOrCondition('assignment_group',''); + //gr.addQuery('assignment_group','NOT',ggrp); + //grx.addQuery('location', gru.location); + grx.addQuery('watch_list','CONTAINS', gru.sys_id); + grx.orderByDesc('sys_updated_on'); + grx.query(); + + + while(grx.next()){ + try{ + var request = {}; + request.number = grx.getDisplayValue('number'); + request.sys_id = grx.getUniqueValue(); + request.tableName = grx.getTableName(); + request.shortdescription = grx.getValue('short_description').substring(0,60); + //if (grx.getValue('short_description').length > 60) {requestad.shortdescription = requestad.shortdescription + "..."} + request.openedat = grx.getValue('opened_at'); + request.state = grx.getDisplayValue('state'); + //requestad.enviro = gs.getProperty('glide.servlet.uri'); + data.requestlist.push(request); + } +catch(errAdHoc) { + + + +} + } + +//Work Order --------------------------------------------------------------------------------------------------------------------------- + + var grw = new GlideRecord('wm_order'); + grw.addActiveQuery(); + //grw.setLimit(10); + //grw.addQuery('location', gru.location); + grw.addQuery('watch_list','CONTAINS', gru.sys_id); + var qc2 = gr.addQuery('assignment_group','!=',ggrp); + qc2.addOrCondition('assignment_group',''); + //gr.addQuery('assignment_group','!=',ggrp); + grw.orderByDesc('sys_updated_on'); + grw.query(); + + + while(grw.next()){ + try { + var request = {}; + request.number = grw.getDisplayValue('number'); + request.sys_id = grw.getUniqueValue(); + request.tableName = grw.getTableName(); + //requestwo.shortdescription = grw.getValue('short_description').substring(0,60); + request.shortdescription = grw.getValue('short_description'); + // if (grw.getValue('short_description').length > 60) {requestwo.shortdescription = requestwo.shortdescription + "..."} + request.openedat = grw.getValue('opened_at'); + request.state = grw.getDisplayValue('state'); + // requestwo.enviro = gs.getProperty("instance_name"); + data.requestlist.push(request); + } +catch(errWorkOrder) { + + + +} + + } + +//sc request item + var gr = new GlideRecord('sc_req_item'); + gr.addActiveQuery(); + //gr.setLimit(10); + //gr.addQuery('assignment_group','!=',ggrp); + //var qc2 = gr.addQuery('assignment_group','!=',ggrp); + //qc2.addOrCondition('assignment_group',''); + //gr.addQuery('opened_by.location', gru.location); + gr.addQuery('watch_list','CONTAINS', gru.sys_id); + gr.orderByDesc('sys_updated_on'); + gr.query(); + + + while(gr.next()){ + try{ + var request = {}; + request.number = gr.getDisplayValue('number'); + request.sys_id = gr.getUniqueValue(); + request.shortdescription = gr.getValue('short_description').substring(0,60) ; + request.url = {id: 'sc_request', table: 'incident', sys_id: request.sys_id}; + //if (gr.getValue('short_description').length > 60) {request.shortdescription = request.shortdescription + "..."} + //request.enviro = gs.getProperty("instance_name"); + request.openedat = gr.getValue('opened_at'); + request.tableName = gr.getTableName(); + + + request.state = gr.getDisplayValue('state'); + data.requestlist.push(request); + } +catch(errreqtask) { + + + +} + //} + +//request tasks --------------------------------------------------------------------------------------------------------------------------- +//var gr = new GlideRecord('sc_task'); + //gr.addActiveQuery(); + + //var qc2 = gr.addQuery('assignment_group','!=',ggrp); + // qc2.addOrCondition('assignment_group',''); +// gr.addQuery('opened_by.location', gru.location); +// gr.orderByDesc('sys_updated_on'); +// gr.query(); + + +// while(gr.next()){ + // try{ + // var request = {}; + // request.number = gr.getDisplayValue('number'); + // request.sys_id = gr.getUniqueValue(); + // request.shortdescription = gr.getValue('short_description').substring(0,60) ; + // request.url = {id: 'sc_request', table: 'incident', sys_id: request.sys_id}; + + // request.openedat = gr.getValue('opened_at'); + + + //request.state = gr.getDisplayValue('state'); + // data.requestlist.push(request); + } +//catch(errreqtask) { + + + +//} +// } + + + data.requests = data.requestlist.sort(function(a,b) {return (a.opened_at > b.opened_at) ? 1 : ((b.opened_at > a.opened_at) ? -1 : 0);} ); + +//data.requests.sort(function(a, b){return b.sortOrder - a.sortOrder;}); +//data.requests.sort(function(a, b){return b - a;}); +//var newlist = data.requests.sort(function(a,b) {return (a.opened_at > b.opened_at) ? 1 : ((b.opened_at > a.opened_at) ? -1 : 0);} ); + + //var newList = data.requests.sort(sortByOrder); + +//function sortByOrder() { + +//} + + + +})(); \ No newline at end of file diff --git a/Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/StorePortal_Active_Reqeuests_Report/template.html b/Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/StorePortal_Active_Reqeuests_Report/template.html new file mode 100644 index 0000000000..b220230f41 --- /dev/null +++ b/Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/StorePortal_Active_Reqeuests_Report/template.html @@ -0,0 +1,34 @@ +
+
+ + + + + + + + + + + + + + + +
+ {{request.number}} + {{request.shortdescription}} + + {{request.openedat}} + + {{request.state}} +
+
diff --git a/Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/_map.json b/Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/_map.json new file mode 100644 index 0000000000..44b54b7a5d --- /dev/null +++ b/Customer Instance Customizations/Weis/Configurations/weisdev/global/sp_widget/_map.json @@ -0,0 +1 @@ +{"StorePortal_Active_Reqeuests_Report":"73ef16224ff84700334cd0af0310c7a7"} \ No newline at end of file diff --git a/Customer Instance Customizations/Weis/Configurations/weisdev/settings.json b/Customer Instance Customizations/Weis/Configurations/weisdev/settings.json new file mode 100644 index 0000000000..60137afa14 --- /dev/null +++ b/Customer Instance Customizations/Weis/Configurations/weisdev/settings.json @@ -0,0 +1,5 @@ +{ + "name": "weisdev", + "url": "https://weisdev.service-now.com", + "g_ck": "0ac6b61feb051a108a07fc2ccad0cd085c82bf4ab169514727bf9b06a510ce433d80a247" +} \ No newline at end of file diff --git a/Service Portal Widgets/SC Catalog Item/CSS -SCSS b/Service Portal Widgets/SC Catalog Item/CSS -SCSS new file mode 100644 index 0000000000..bcbc61f077 --- /dev/null +++ b/Service Portal Widgets/SC Catalog Item/CSS -SCSS @@ -0,0 +1,264 @@ +$border-radius-base: 4px !default; + $sp-space--xs: 4px !default; + $sp-space--xxs: 2px !default; + .btn-group.attachment-mobile > button { + padding: 1px 6px; + } + + .panel-button { + float: none; + } + + .sp-attachment-add { + padding: 5px; + } + + .p-t-none { + padding-top: 0 !important; + } + + .file-list-wrap { + margin-bottom: 10px; + } + + .sc-btn { + color: $text-color; + } + + .dismiss-button { + position: absolute; + top: 1rem; + right: 10px; + color: $text-color; + cursor: pointer; + } + + .item-header { + padding-top: 10px !important; + padding-bottom: 10px !important; + } + + .sc-item-description img { + max-width: 100%; + height: auto; + } + .sc-sticky-item-header { + position: sticky; + position: -webkit-sticky; + position: -ms-sticky; + top: -($font-size-h2 + $font-size-base); + z-index: 10; + background-color: $sp-homepage-bg; + border-radius: $border-radius-base $border-radius-base 0 0; + } + .sc-fixed { + position: fixed; + max-height: 100%; + width: 17.67777%; + z-index: 1; + } + .no-margin { + margin: 0px; + } + .sc-item-error-messages { + max-height: 100%; + position: absolute; + overflow-y: auto; + overflow-x: hidden; + width: 100%; + } + .sc-field-error-label { + margin-right: .5em; + display: inline-block; + cursor: pointer; + background-color: $sc-field-error-color; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + .image-gallery { + font-size: 8px; + color: #CECECE; + .active { + color: #717171; + } + i { + padding: 0 5px; + } + } + .relative { + position: relative; + } + .sc-order-widget-btn { + position: absolute; + left: 9%; + } + + .sc-cat-item-legend { + margin-top: 1rem; + margin-bottom: 1.5rem; + } + + .cart-icon-margin { + margin-right: 3px; + } + + .cart-icon-padding-top { + padding-top: $sp-space--xxs; + } + + .bottom-button-width { + width: 20% + } + + .cat-item-quantity-box-bottom-cart { + padding-top: $sp-space--xs; + width: 20%; + } + + .cat-item-quantity-box { + flex-grow: 1; + padding-left: 1.2rem; + } + + .cat-item-quantity-group { + display: flex; + justify-content: space-between; + align-items: baseline; + } + + .close-notification { + position: absolute; + top: 10px; + right: 18px; + } + .help-tag-icon { + color: $sc-field-error-color; + } + + .sc-reqd-info-btn { + margin-right: .5em; + display: inline-block; + white-space:normal !important; + word-break: break-word !important; + } + + .cat-mobile-favorite{ + margin-top:24px; + margin-bottom:16px; + margin-left: 0.525rem; + width: fit-content; + width: -moz-fit-content; + .v523f6529532d0110fedfddeeff7b1298{ + float: left; + } + .favorite-button{ + width : 1.2rem; + height: auto; + background-color: transparent; + outline: transparent; + box-shadow: none; + .fa{ + color: $tropical-rain; + } + } + .favorite-text{ + margin-left: 8px; + font-size: $font-size-base; + font-weight: 600; + color: $tropical-rain; + } + } + + @media only screen and (max-width : 992px) { + .sc-fixed { + position: relative; + width: 100%; + } + .wishlist-update-message { + display : block; + } + .sc-item-error-messages { + position: relative; + } + .read-more, .read-less { + font-size: $font-size-h2/3; + cursor: pointer; + font-weight: 850; + border-width: 0.125rem; + } + .more-text { + display: none; + } + .inline-cart { + display: none; + } + .right-side-cart { + display: inherit !important; + } + .sp-attachment-add { + margin-left: 0; + padding: 0; + } + } + @media only screen and (max-width : 768px) { + .sc-sticky-item-header { + position: inherit; + } + + .sc-cat-item-short-description { + width: 80%; + } + } + .inline-cart { + .quantity-selector { + display: inline-block; + width: 65px; + padding-top: $sp-space--xxs; + } + } + .alert-success .link { + text-decoration: underline; + } + + .alert-info-border { + border-color: $alert-info-border; + } + + .attachment-text { + color: $primary; + } + + .native-mobile { + .panel { + border-left: 0; + border-right: 0; + } + .read-more, .read-less { + font-size: $font-size-h2/2; + cursor: pointer; + } + .more-text { + display: none; + } + } + + .flex-center { + display: flex; + justify-content: center; + align-items: center; + } + + .flex-end { + display: flex; + justify-content: flex-end; + } + + #catItemTop .panel.panel-default > div:last-child { + border-bottom: none; + } + + .attachment-height { + height: 80px; + } + \ No newline at end of file diff --git a/Service Portal Widgets/SC Catalog Item/Client Script b/Service Portal Widgets/SC Catalog Item/Client Script new file mode 100644 index 0000000000..009fc31702 --- /dev/null +++ b/Service Portal Widgets/SC Catalog Item/Client Script @@ -0,0 +1,1693 @@ +function($scope, $http, spScUtil, spUtil, nowAttachmentHandler, $rootScope, $sanitize, $window, $sce, i18n, $timeout, $log, spAriaUtil, $document, spModal, $q, spAtf, $location, spAriaFocusManager, spSCNavStateManager, cabrillo, snAnalytics) { + var c = this; + c.isNative = cabrillo.isNative() && c.data.isMEE == 'true'; + c.isAgentApp = navigator.userAgent.indexOf('Agent') > -1; + var webAnalyticsMsgSuffix = c.isNative ? " - NOW Mobile" : ""; + if (c.data.sc_cat_item && $scope.data.hasOwnProperty("_generatedItemGUID")) { + c.data.sc_cat_item._attachmentGUID = $scope.data._generatedItemGUID; + } + + $scope.m = $scope.data.msgs; + + if ($scope.data.redirectToItem) { + $location.search("id=sc_cat_item&sys_id=" + $scope.data.sc_cat_item.sys_id); + return; + } + + if (c.isNative && c.data.hasDraftItem && !c.data.is_draft_item && !c.data.sc_cat_item.no_save_as_draft) + cabrillo.message.showMessage(cabrillo.message.INFO_MESSAGE_STYLE, $scope.m.draftAlreadyExistCabrilloMsg); + + c.isAccessibilityEnabled = $window.g_accessibility; + + c.draftExistsWithLinkMsg = (!!c.data.sc_cat_item && !!c.data.sc_cat_item.name) ? $sce.trustAsHtml(generateMessageWithLink($scope.m.draftExistsMsg, { + "message": $scope.m.hereMsg, + "id": "view_draft", + "link": "?id=my_requests&draftSearchText=" + encodeURIComponent(c.data.sc_cat_item.name) + }, $scope.m.newDraftInfoMsg, false)) : ""; + + $scope.isSCCartFixed = (c.options.display_cart_on_right === 'true' && c.isAccessibilityEnabled == 'false'); + $rootScope.isSCCartFixed = $scope.isSCCartFixed; + + $scope.setDefaultValue = !$scope.data.is_cart_item && !$scope.data.is_wishlist_item && !$scope.data.is_draft_item; + + $scope.disableControls = function() { + return $scope.submitting || $scope.submitted || c.data.isPreview || !!$scope.validating; + } + + if (c.data.isPreview) { + spUtil.recordWatch($scope, "sc_item_produced_record", "record_key=" + c.data.sys_id, function(response, data) { + $window.location.reload(); + }); + } + $scope.getFocus = function(field) { + var focusVar = (field.type == "reference") ? "sp_formfield_reference_" : "sp_formfield_"; + focusVar += (field.name.startsWith("IO:") ? field._children[0] : field.name); + var elem = document.getElementById(focusVar); + if (field.type == "url" && elem.style.display == "none") + elem = document.getElementById(focusVar + "_unlock"); + else if (field.type == "sc_multi_row") + elem = document.getElementById(field.sys_id + "_add_row"); + else if (field.type == "sc_attachment") { + var buttonElem = angular.element(elem).find('button')[0]; + if (buttonElem) + elem = buttonElem; + } else if (field.type != "html") { + var inputElem = angular.element(elem).find('input')[0]; + if (inputElem) + elem = inputElem; + } + if (elem) + elem.focus(); + } + + c.quantity = c.data.quantity ? c.data.quantity + "" : "1"; + c.mandatory = []; + $scope.stickyHeaderTop = '0px'; + c.widget._debugContextMenu = [ + [i18n.getMessage("Open") + " sc_cat_item", function() { + var item = c.data.sc_cat_item; + $window.open("/sp_config?id=form&table=" + item.table + "&sys_id=" + item.sys_id); + }] + ]; + + spUtil.recordWatch($scope, "sys_attachment", "table_sys_id=" + $scope.data._generatedItemGUID, function(response, data) { + $scope.attachmentHandler.getAttachmentList(); + if (response.data) { + var options = {}; + options.operation = response.data.operation; + options.filename = response.data.display_value; + options.state = (response.data.record && response.data.record.state) ? response.data.record.state.value : ""; + if (options.operation === 'update' && options.state === 'not_available') + $rootScope.$broadcast("$$uiNotification", { + type: 'error', + message: i18n.getMessage('Upload file scan failed').withValues([options.filename]) + }); + } + }); + + + $rootScope.$on('spModel.gForm.rendered', function() { + $timeout(function() { + spAtf.init().then(function(atf) { + $scope._atf = atf; + atf.expose('catalog_util', catalogUtil); + }); + }, 10); + }); + + // ATF helper object + var catalogUtil = { + updateGform: function() { + $scope._atf.expose('g_form', spAtf.augmentForm(g_form)); + }, + + addRow: function(fieldId, timeoutInMS) { + if (timeoutInMS <= 0) + timeoutInMS = 1000; + var deferred = $q.defer(); + $scope.$broadcast("$sp.sc_multi_row.create_row", fieldId, $scope.data.sc_cat_item.sys_id); + $scope.$on('spModel.gForm.initialized', function(e, gFormInstance) { + if (gFormInstance.getSysId() == fieldId) + deferred.resolve(); + }); + $timeout(function() { + deferred.reject(); + }, timeoutInMS); + return deferred.promise; + }, + + submit: function(timeout) { + var deferred = $q.defer(); + $scope.triggerOnSubmit(timeout); + var cleanup = $scope.$on('$sp.service_catalog.form_submit_failed', function() { + cleanup(); + deferred.reject("Can't submit form"); + }); + if (g_form) { + g_form.$private.events.on('submitted', function() { + var cleanUp = $scope.$on('$sp.sc_cat_item.submitted', function(o, result) { + $timeout(function() { + cleanUp(); + deferred.resolve(result); + }, 10); + }); + }); + } else + deferred.reject('g_form not initialized'); + + return deferred.promise; + }, + + addToCart: function(timeout) { + var defer = $q.defer(); + $scope.triggerAddToCart(timeout); + var cleanup = $scope.$on('$sp.service_catalog.form_submit_failed', function() { + cleanup(); + defer.reject("Can't submit form"); + }); + if (g_form) { + g_form.$private.events.on('submitted', function() { + var cleanup = $scope.$on('$sp.sc_cat_item.add_to_cart', function(o, result) { + $timeout(function() { + cleanup(); + defer.resolve(result); + }, 10); + }); + }); + } else + defer.reject('g_form not initialized'); + return defer.promise; + }, + + submitCatItem: function(timeout) { + var defer = $q.defer(); + $scope.triggerOnSubmit(timeout); + var cleanup = $scope.$on('$sp.service_catalog.form_submit_failed', function() { + cleanup(); + defer.reject("Can't submit form"); + }); + if (g_form) { + g_form.$private.events.on('submitted', function() { + if ($scope.data.sys_properties.twostep && $scope.data.sc_cat_item.request_method != "submit") { + defer.resolve(); + } else { //When not two step, if reached this step means the form validation is done and it is the item submission that should be monitored + if (cleanup && typeof cleanup === 'function') + cleanup(); + + var cleanUp = $scope.$on('$sp.sc_cat_item.submitted', function(o, result) { + $timeout(function() { + cleanUp(); + result.single_step = true; + defer.resolve(result); + }, 10); + }); + + var failedSubmitCleanUp = $scope.$on('$sp.sc_cat_item.submit_failed', function() { + failedSubmitCleanUp(); + defer.reject("Can't submit form"); + }); + } + }); + } else + defer.reject('g_form not initialized'); + + return defer.promise; + }, + + setQuantity: function(quantity) { + if ($scope.c.data.sc_cat_item.sys_class_name !== "sc_cat_item_producer" && $scope.c.data.sc_cat_item.sys_class_name !== "std_change_record_producer") { + $scope.c.quantity = quantity; + $scope.$apply(); + } + }, + + getQuantity: function() { + return $scope.c.quantity; + }, + + getPrice: function() { + var obj = {}; + obj.price = $scope.data.sc_cat_item.price_display; + obj.recurring_price = $scope.data.sc_cat_item.recurring_price_display; + obj.recurring_frequency = $scope.data.sc_cat_item.recurring_frequency; + return obj; + } + } + + c.showAddCartBtn = function() { + return !$scope.submitted && + c.options.show_add_cart_button && + c.data.sc_cat_item.sys_class_name !== "sc_cat_item_producer" && + c.data.sc_cat_item.sys_class_name !== "std_change_record_producer" && + !c.data.sc_cat_item.no_cart && !c.data.is_cart_item; + } + + c.showPrice = function() { + return c.data.showPrices && + ((c.data.sc_cat_item.price ? true : false) || + (c.data.sc_cat_item.recurring_price ? true : false)); + } + + c.showDeliveryTime = function() { + return !c.data.hideDeliveryTime && + !c.data.sc_cat_item.no_delivery_time && + (c.data.sc_cat_item.estimated_delivery_time ? true : false); + } + var i18nQuantity = i18n.getMessage("Quantity {0}"); + c.showQuantitySelector = function() { + if (c.data.isMEE == 'true' && $('#catItemQuantity') != null) { + var quantityElement = $('#catItemQuantity'); + $timeout(function() { + var prevDiv = quantityElement.prev(); + var anchor = prevDiv.find('.select2-choice'); + anchor.attr('role', 'button').attr('tabindex', '0').attr('aria-label', i18nQuantity.withValues([''])); + prevDiv.find('.select2-search-choice-close').attr('aria-hidden', 'true'); + }, 100); + } + + return c.data.sc_cat_item.sys_class_name !== "sc_cat_item_producer" && + c.data.sc_cat_item.sys_class_name !== "std_change_record_producer" && + !c.data.sc_cat_item.no_quantity && !c.data.sc_cat_item.read_only_quantity && + (c.data.sc_cat_item.cart_guide === undefined || c.data.sc_cat_item.cart_guide === null) && + (!c.data.sc_cat_item.no_order_now || !c.data.sc_cat_item.no_cart); + } + c.showOrderNowButton = function() { + return !$scope.data.is_cart_item && (c.data.sc_cat_item.use_sc_layout || !c.data.sc_cat_item.no_order_now); + } + c.showAddToWishlist = function() { + return !$scope.submitted && + (c.data.showWishlist && + !c.data.sc_cat_item.no_wishlist && !c.data.is_draft_item && + !c.data.is_cart_item && + c.options.show_add_to_wishlist_button === 'true'); + } + + c.showDraftButtons = function() { + return !!spScUtil.saveCatalogItem && !c.data.sc_cat_item.no_save_as_draft && !(c.options.hide_save_as_draft_button == "true") && !c.data.draft_buttons_hidden_via_property; + } + + + c.allowOrder = function() { + if (c.data.sc_cat_item.use_sc_layout) + return true; + if (c.data.sc_cat_item.no_order) + return false; + if (c.data.sc_cat_item.no_order_now && c.data.sc_cat_item.no_cart) + return false; + return true; + } + + c.showCart = function() { + return c.data.can_create_cart_item && (c.data.is_cart_item || + c.showPrice() || c.showDeliveryTime() || c.showAddCartBtn() || + c.showOrderNowButton() || c.showAddToWishlist()); + } + + c.hideCartMsg = function() { + $scope.data.showMsg = false; + } + + c.showAttachments = function() { + return !$scope.submitted && + c.data.sc_cat_item && !c.data.sc_cat_item.no_attachments && + c.data.sc_cat_item.sys_class_name !== "std_change_record_producer"; + }; + + c.updateQuantity = function(item) { + spAriaUtil.sendLiveMessage(c.data.msgs.updatedMsg + " " + item.name + " " + c.data.msgs.quantityToMsg + " " + c.quantity); + } + + $scope.$on('dialog.upload_too_large.show', function(e) { + $log.error($scope.m.largeAttachmentMsg); + spUtil.addErrorMessage($scope.m.largeAttachmentMsg); + }); + + var ah = $scope.attachmentHandler = new nowAttachmentHandler(setAttachments, appendError); + + function appendError(error) { + spUtil.addErrorMessage(error.msg + error.fileName); + } + ah.setParams($scope.data._attachmentTable, $scope.data._generatedItemGUID, 1024 * 1024 * $scope.data.maxAttachmentSize); + + function setAttachments(attachments, action) { + if (!angular.equals($scope.attachments, attachments)) + $scope.attachments = attachments; + if (action === "added") { + spAriaUtil.sendLiveMessage($scope.m.attachmentAddedMsg); + if ($scope.attachments.length > 0) + $scope.data.sc_cat_item.attachment_submitted = true; + } + if (action === "renamed") + spAriaUtil.sendLiveMessage($scope.m.renameSuccessMsg); + if (action === "deleted") { + spAriaUtil.sendLiveMessage($scope.m.deleteSuccessMsg); + if ($scope.attachments.length == 0) + $scope.data.sc_cat_item.attachment_submitted = false; + } + $scope.data.sc_cat_item.attachment_action_in_progress = false; + spUtil.get($scope, { + action: "from_attachment" + }); + } + if (c.showAttachments() && + (c.data.is_cart_item || c.data.is_wishlist_item || c.data.is_draft_item)) + $scope.attachmentHandler.getAttachmentList(); + $scope.confirmDeleteAttachment = function(attachment) { + if (c.isNative) { + if (confirm($scope.data.msgs.delete_attachment)) { + $scope.data.sc_cat_item.attachment_action_in_progress = true; + $scope.attachmentHandler.deleteAttachment(attachment); + $scope.setFocusToAttachmentButton(); + } + } else { + spModal.confirm($scope.data.msgs.delete_attachment).then(function() { + $scope.data.sc_cat_item.attachment_action_in_progress = true; + $scope.attachmentHandler.deleteAttachment(attachment); + $scope.setFocusToAttachmentButton(); + }); + } + } + $scope.dismissWishListMessage = function() { + $scope.is_update_wishlist = false; + } + + if ($scope.data.sc_cat_item) { + + /*if ($scope.data.sc_cat_item.content_type == 'external') { + $window.location.href = $scope.data.sc_cat_item.url; + return; + } + + if ($scope.data.sc_cat_item.content_type == 'kb') { + $location.search("id=kb_article&sys_id=" + $scope.data.sc_cat_item.kb_article); + return; + }*/ + + $scope.data.sc_cat_item.trusted_description = $sce.trustAsHtml($scope.data.sc_cat_item.description); + if (!$scope.data.sc_cat_item._fields || angular.equals($scope.data.sc_cat_item._fields, {})) + $scope.data.no_fields = true; + if ($scope.data.sc_cat_item.sys_class_name !== "sc_cat_item_producer" && + $scope.data.sc_cat_item.sys_class_name !== "std_change_record_producer") { + if ($scope.data.sc_cat_item.request_method == "request") + $scope.submitButtonMsg = $scope.m.requestMsg; + else if ($scope.data.sc_cat_item.request_method == "submit") + $scope.submitButtonMsg = $scope.m.submitMsg; + else + $scope.submitButtonMsg = $scope.m.orderNowMsg; + } else { + if ($scope.data.sc_cat_item.sys_class_name == "sc_cat_item_producer" && $scope.data.record_producer_label) + $scope.submitButtonMsg = $scope.data.record_producer_label; + else + $scope.submitButtonMsg = $scope.m.submitMsg; + } + + // Breadcrumbs + if (!$scope.data.categories) + $scope.data.categories = []; + $scope.data.categories.forEach(function(category, index, categories) { + categories[index].url = category.url + '&catalog_id=' + $scope.data.catalog_id; + }); + if ($scope.data.is_wishlist_item) { + $scope.data.categories.unshift({ + label: $scope.m.wishlistMsg, + url: '?id=sc_wishlist' + }); + $scope.data.categories.push({ + label: $scope.data.sc_cat_item.name, + url: '#' + }); + } else if ($scope.data.is_cart_item) { + $scope.data.categories.unshift({ + label: $scope.m.cartMsg, + url: '?id=sc_cart' + }); + $scope.data.categories.push({ + label: $scope.data.sc_cat_item.name, + url: '#' + }); + } else if ($scope.data.is_draft_item) { + $scope.data.categories.unshift({ + label: $scope.m.draftItemMsg, + url: '?id=my_requests&selectDraftTab=true' + }); + $scope.data.categories.unshift({ + label: $scope.m.myRequestsMsg, + url: '?id=my_requests' + }); + $scope.data.categories.push({ + label: $scope.data.draftItemName, + url: '#' + }); + } else if ($scope.data.categories.length > 0) { + $scope.data.categories.unshift({ + label: $scope.data.sc_catalog || $scope.page.title, + url: '?id=' + $scope.data.sc_category_page + "&catalog_id=" + $scope.data.catalog_id + }); + $scope.data.categories.push({ + label: $scope.data.sc_cat_item.name, + url: '#' + }); + if ($scope.data.all_catalog_msg) { + $scope.data.categories.unshift({ + label: $scope.data.all_catalog_msg, + url: '?id=' + $scope.data.sc_category_page + "&catalog_id=-1" + }); + } + } else { + $scope.data.categories.push({ + label: $scope.data.sc_cat_item.name, + url: '#' + }); + } + + $timeout(function() { + $scope.$emit('sp.update.breadcrumbs', $scope.data.categories); + }); + spUtil.setSearchPage('sc'); + + // Set Title in Mobile + if (c.isNative) + cabrillo.viewLayout.setTitle($scope.data.sc_cat_item.name); + + // Set Title in Workspace + else if ($scope.options.isServiceWorkspace) + $window.postMessage({ + msg: 'CATALOG_ITEM_SET_TITLE', + title: $scope.data.sc_cat_item.name + }, $location.origin); + + } else { + var notFoundBC = [{ + label: $scope.page.title, + url: '?id=' + $scope.data.sc_catalog_page + }]; + $timeout(function() { + $scope.$emit('sp.update.breadcrumbs', notFoundBC); + }); + spUtil.setSearchPage('sc'); + } + c.getItemId = function() { + return $scope.data.sc_cat_item ? $scope.data.sc_cat_item.sys_id : -1; + }; + + function showNativeMobileButtons() { + if ($scope.data.sc_cat_item.sys_class_name == 'sc_cat_item_content') + return; + if (c.isNative) { + cabrillo.viewLayout.setTitle($scope.data.sc_cat_item.name); + if ($scope.data.is_cart_item) + addCartItemButtons(); + else if ($scope.data.sc_cat_item.sys_class_name == "sc_cat_item_producer" || $scope.data.sc_cat_item.sys_class_name == "std_change_record_producer") + addRPButton(); + else + addOrderButtons(); + } + } + + function nativeGoBackToCart() { + cabrillo.viewLayout.setNavigationBarButtons(); + var button = [{ + imageName: 'back', + buttonStyle: cabrillo.viewLayout.REPLACE_BACK_BUTTON_STYLE, + enabled: true + }]; + + cabrillo.viewLayout.setNavigationBarButtons(button, function() { + $location.search('id=sc_cart'); + }); + } + + function displayNativeButtons() { + if (c.isNative && (!$scope.orderConfirmation && !$scope.saveConfirmation)) { + cabrillo.viewLayout.setTitle($scope.data.sc_cat_item.name); + showNativeMobileButtons(); + cabrillo.viewLayout.showBackButton(); + + if ($scope.data.is_cart_item) { + cabrillo.viewLayout.hideBackButton(); + cabrillo.viewLayout.setNavigationBarButtons(); + nativeGoBackToCart(); + } + } + } + + var mespClosePopupUnregister = $rootScope.$on("mesp.popup.close", function() { + //Do not display cabrillo buttons when there is an active mrvs row as it will have it's own buttons. + var activeRowElement = document.getElementById("mrvs_active_row"); + if (!!activeRowElement) + return; + + // Timeout is to give a better user experience otherwise when this popup opens, + // the cabrillo buttons will be displayed immediately giving a bad user experience. + $timeout(function() { + displayNativeButtons(); + }); + }); + + var mespOpenPopupUnregister = $rootScope.$on("mesp.popup.open", function() { + // Timeout is to give a better user experience otherwise when this popup opens, + // the cabrillo buttons will be displayed immediately giving a bad user experience. + $timeout(function() { + removeCabrilloButtons(); + }); + }); + + if ($scope.options.isServiceWorkspace && $window.frameElement) { + var workspaceParams = {}; + workspaceParams.sysparm_parent_table = $window.frameElement.getAttribute('parent-table') || $window.frameElement.dataParentTable; + workspaceParams.sysparm_parent_sys_id = $window.frameElement.getAttribute('parent-sys-id') || $window.frameElement.dataParentSysId; + //Extract the query if there is one passed in + var urlParams = new URLSearchParams($window.frameElement.src); + var params = Object.fromEntries(urlParams); + if (params.query) + workspaceParams.target_query = params.query; + $scope.data.parentParams = workspaceParams; + } else if (!$scope.options.isServiceWorkspace && $scope.options.parentTable && $scope.options.parentSysId) { + var portalParentParams = {}; + portalParentParams.sysparm_parent_table = $scope.options.parentTable; + portalParentParams.sysparm_parent_sys_id = $scope.options.parentSysId; + $scope.data.parentParams = portalParentParams; + } + + var g_form; + $scope.$on('spModel.gForm.initialized', function(e, gFormInstance) { + if (gFormInstance.getSysId() != -1 && gFormInstance.getSysId() != c.getItemId()) + return; + g_form = gFormInstance; + spSCNavStateManager.register(g_form); + spSCNavStateManager.isNative(c.isNative); + spSCNavStateManager.isPreview(c.data.isPreview); + + if (c.isNative) { + cabrillo.viewLayout.setTitle($scope.data.sc_cat_item.name); + $rootScope.$on('spModel.gForm.showNativeMobileButtons', displayNativeButtons); + } + + if ($scope.setDefaultValue && c.options.requested_for_id && c.options.requested_for_display && $scope.data.sc_cat_item.requested_for_variable_name) { + $scope.setDefaultValue = false; + $scope.data.sc_cat_item.hideAlsoRequestFor = true; + setValueInNextDigestCycle(g_form, c.options.requested_for_id, c.options.requested_for_display); + } else if ($scope.setDefaultValue && $scope.data.parentParams && $scope.data.parentParams.sysparm_parent_table && $scope.data.parentParams.sysparm_parent_sys_id) { + $scope.setDefaultValue = false; + $scope.data.sc_cat_item.hideAlsoRequestFor = true; + $scope.server.get({ + action: 'get_requested_for', + parentParams: $scope.data.parentParams + }).then(function(response) { + if (response.data.requested_for) { + $scope.data.requested_for = response.data.requested_for; + setValueInNextDigestCycle(g_form, response.data.requested_for.id, response.data.requested_for.displayValue); + } + }); + } + + $timeout(function() { + $rootScope.$emit('spModel.gForm.rendered', g_form); + showNativeMobileButtons(); + }, 175); + + // This runs after all onSubmit scripts have executed + g_form.$private.events.on('submitted', function() { + cleanFailedSubmit(); + $scope.submitting = true; + if ($scope.data.sc_cat_item.item_action === "order") + getOne(); + else if ($scope.data.sc_cat_item.item_action === "add_to_cart") + addToCart(); + else if ($scope.data.sc_cat_item.item_action == "update_cart") + updateCart(); + + spUtil.simulateFakeFormSubmitForAutoComplete('catalog-form', $scope.data.sc_cat_item._fields); + }); + }); + + function setValueInNextDigestCycle(g_form, value, displayValue) { + $timeout(function() { + g_form.setValue($scope.data.sc_cat_item.requested_for_variable_name, value, displayValue); + }); + } + + function getVarData(fields) { + var reqData = {}; + for (var obj in fields) + reqData[fields[obj].name] = fields[obj].value; + return reqData; + } + + function getValidatedVarData(fields) { + //Filtering out invalid masked variables, to avoid adding them to wishlist + var validFields = Object.values(fields).filter(function(field) { + return !(field.type == 'masked' && field.useConfirmation && field.value != field.confirmPassword); + }); + return getVarData(validFields); + } + + function addLink(url, msg, elem_id) { + return "" + msg + ""; + } + + function getAlsoRequestForValue(fields) { + if ($scope.data.sc_cat_item.requested_for_variable_name) { + var requested_for_variable = fields[$scope.data.sc_cat_item.requested_for_variable_name]; + if (!!requested_for_variable && requested_for_variable.hasOwnProperty('also_request_for_value')) + return fields[$scope.data.sc_cat_item.requested_for_variable_name].also_request_for_value; + + return ""; + } + } + + function confirmAlsoRequestedFor(successFn) { + $scope.submitting = true; + var alsoRequestFor = getAlsoRequestForValue($scope.data.sc_cat_item._fields); + if (alsoRequestFor) { + spModal.confirm($scope.m.alsoReqForClearConfirmMsg).then(successFn, function() { + $scope.submitting = false; + }); + } else + successFn(); + } + + + $scope.triggerAddToWishlist = function() { + confirmAlsoRequestedFor(addToWishlist); + } + + $scope.triggerSaveItem = function() { + confirmAlsoRequestedFor(function() { + if ($scope.data.hasDraftItem && !$scope.data.is_draft_item) + showSaveAsDraftModal($scope.data.draftItemName); + else + saveCatalogItem($scope.data.draftItemName); + }); + } + + function showSaveAsDraftModal(name) { + var payload = { + action: "save_item", + draftName: name, + message: $scope.m.save_draft_description, + infoMessage: $scope.data.is_cart_item ? $scope.m.cartItemDeletionMsg : ($scope.data.is_wishlist_item ? $scope.m.wishlistItemDeletionMsg : null), + }; + + $scope.server.get(payload).then(function(response) { + var saveItemModalCtrl; + var unregisterSave = $scope.$on('$sp.service_catalog.save.cancel', function() { + $scope.submitting = false; + $timeout(function() { + if (saveItemModalCtrl) + saveItemModalCtrl.close(); + }); + $scope.saveConfirmation = false; + displayNativeButtons(); + }); + var closeModalOnSave = $scope.$on('$sp.service_catalog.save.submitted', function(event, payload) { + $timeout(function() { + if (saveItemModalCtrl) + saveItemModalCtrl.close(); + + $scope.saveConfirmation = false; + saveCatalogItem(payload.draft_name); + }); + + }); + var saveItemModal = angular.copy(response.data.saveItemModal); + saveItemModal.options.afterOpen = function(ctrl) { + saveItemModalCtrl = ctrl; + if (c.data.isMEE == 'true') { + spAriaUtil.sendLiveMessage($scope.m.saveItemDialogMsg); + } + }; + saveItemModal.options.afterClose = function() { + unregisterSave(); + closeModalOnSave(); + c.saveItemModal = null; + saveItemModalCtrl = null; + $scope.saveConfirmation = false; + displayNativeButtons(); + }; + c.saveItemModal = saveItemModal; + }); + $scope.saveConfirmation = true; + } + + function saveCatalogItem(name) { + spAriaUtil.sendLiveMessage($scope.m.submittingMsg); + showPageLoader(); + $scope.savingItem = true; + displayNativeButtons(); + var additional_params = {}; + if (!!$scope.data.wishlist_item_id) { + additional_params.wishlist_item_id = $scope.data.wishlist_item_id; + if ($scope.data.sc_cat_item.sys_class_name == 'sc_cat_item_producer' || $scope.data.sc_cat_item.sys_class_name == 'std_change_record_producer') + additional_params.wishlistAttachmentTable = $scope.data._attachmentTable; + } + if ($scope.data.is_cart_item || $scope.data.is_wishlist_item) + additional_params.current_cart_item_id = $scope.data._generatedItemGUID; + + + spScUtil.saveCatalogItem($scope.data.sc_cat_item.sys_id, name, c.quantity, getValidatedVarData($scope.data.sc_cat_item._fields), $scope.data.is_cart_item || $scope.data.is_wishlist_item ? "" : $scope.data._generatedItemGUID, additional_params).then(function(response) { + var cartItemId = ""; + if (response && response.data && response.data.result && response.data.result.saved_cart_item_id) { + cartItemId = response.data.result.saved_cart_item_id; + if (!$scope.data.is_draft_item) { + var payload = {}; + payload.data = {}; + payload.name = 'Catalog item added to draft'; + payload.data['Class name'] = $scope.data.sc_cat_item.sys_class_name; + payload.data['Item id'] = $scope.data.sc_cat_item.sys_id; + payload.data['Engagement channel'] = ['sp','mesp','esc'].indexOf($scope.data.portal_suffix) !== -1 ? $scope.data.portal_suffix : 'custom'; + snAnalytics.addEvent(payload); + } + } + + g_form.$private.userState.clearModifiedFields(); + + if ($scope.data.is_cart_item || $scope.data.is_wishlist_item) { + spAriaUtil.sendLiveMessage($scope.data.sc_cat_item.name + " " + $scope.data.is_cart_item ? $scope.m.itemRemovedFromCartMsg : $scope.m.itemRemovedFromWishlistMsg); + $rootScope.$broadcast($scope.data.is_cart_item ? "$sp.service_catalog.cart.update" : "$sp.service_catalog.wishlist.update"); + if ($scope.data.is_cart_item || $scope.data.current_cart_item == $scope.data.wishlist_item_id) { + $location.search('id=sc_cat_item&edit=draft&sys_id=' + cartItemId); + return; + } else { + $scope.data.wishlist_item_id = ""; + $scope.data.is_wishlist_item = false; + } + } + + c.status = $scope.data.is_draft_item ? $scope.m.draftUpdateMsg : $scope.m.draftSaveMsg; + + if (!c.isNative) { + $scope.m.actionMsg = generateMessageWithLink($scope.data.is_draft_item ? $scope.m.draftUpdateMsg : $scope.m.draftSaveMsg, { + "link": '?id=my_requests&draftSearchText=' + encodeURIComponent($scope.data.sc_cat_item.name), + "message": $scope.m.viewDraftItemMsg, + "id": "view_draft_item" + }, '', true); + $scope.m.actionMsg = $sce.trustAsHtml($scope.m.actionMsg); + $scope.data.showMsg = true; + } else + cabrillo.message.showMessage(cabrillo.message.SUCCESS_MESSAGE_STYLE, c.status); + + if (!!cartItemId) { + $scope.data._generatedItemGUID = cartItemId; + $scope.data.sc_cat_item._attachmentGUID = cartItemId; + $scope.data.draftItemName = name; + $scope.data.is_draft_item = true; + $scope.data.sc_cat_item.isCartItem = true; + //This is to refresh the item level attachments in the UI. As we might be changing the _generatedItemGUID above, we are updating parameters required by the attachment handler. + $scope.attachmentHandler.setParams($scope.data._attachmentTable, $scope.data._generatedItemGUID, 1024 * 1024 * $scope.data.maxAttachmentSize); + $scope.attachmentHandler.getAttachmentList(); + } + + hidePageLoader(); + $scope.savingItem = false; + $scope.submitting = false; + displayNativeButtons(); + spUtil.scrollTo('#sc_cat_item', 300); + }, function(response) { + $scope.savingItem = false; + displayNativeButtons(); + handleFailure(response); + }); + } + + function addToWishlist() { + spAriaUtil.sendLiveMessage($scope.m.submittingMsg); + var wishlistMsg = $scope.data.is_wishlist_item ? $scope.m.wishlistUpdateMsg : $scope.m.wishlistAddMsg; + $scope.m.actionMsg = generateMessageWithLink(wishlistMsg, { + "link": "?id=sc_wishlist", + "id": "view_wishlist", + "message": $scope.m.viewWishListMsg + }, '', true); + $scope.m.actionMsg = $sce.trustAsHtml($scope.m.actionMsg); + $scope.is_update_wishlist = false; + + spScUtil.addToWishlist($scope.data.sc_cat_item.sys_id, c.quantity, getValidatedVarData($scope.data.sc_cat_item._fields), $scope.data._generatedItemGUID).then(function(response) { + var cartItemId = ""; + if (response && response.data && response.data.result && response.data.result.items) { + for (var i = 0; i < response.data.result.items.length; i++) { + var item = response.data.result.items[i]; + if (item.catalog_item_id === $scope.data.sc_cat_item.sys_id) { + cartItemId = item.cart_item_id; + break; + } + } + } + + $rootScope.$broadcast("$sp.service_catalog.wishlist.add_item"); + $rootScope.$broadcast("$sp.service_catalog.wishlist.update", cartItemId); + + if (!$scope.data.is_wishlist_item) + $scope.clearAttachmentFields(); + g_form.$private.userState.clearModifiedFields(); + $scope.is_update_wishlist = true; + $scope.data.is_wishlist_item = true; + $scope.data.sc_cat_item.isCartItem = true; + if (!!cartItemId && $scope.data._generatedItemGUID != cartItemId) { + $scope.data._generatedItemID = cartItemId; + $scope.data.sc_cat_item._attachmentGUID = cartItemId; + } + $scope.data.showMsg = true; + $scope.data.wishlist_item_id = cartItemId; + $scope.submitting = false; + spUtil.scrollTo('#sc_cat_item', 300); + $scope.focusElement("view_wishlist", wishlistMsg); + }, function(response) { + handleFailure(response); + }); + } + $scope.clearAttachmentFields = function() { + var fields = $scope.data.sc_cat_item._fields; + for (var x in fields) { + if (fields[x].type == 'sc_attachment') + g_form.clearValue(fields[x].name); + } + } + + $scope.triggerAddToCart = function(timeout) { + $scope.data.sc_cat_item.item_action = "add_to_cart"; + $scope.data.sc_cat_item.quantity = c.quantity; + $scope.$evalAsync(function() { + if (g_form && !$scope.submitting) { + $scope.submitting = true; + spAriaUtil.sendLiveMessage($scope.m.submittingMsg); + if (!spScUtil.isRegexDone($scope.data.sc_cat_item._fields)) { + $scope.submitting = false; + $scope.validating = true; + $scope.triggerPostValidation = $scope.triggerAddToCart; + } else if (!g_form.submit()) { + timeout = timeout || 1000; + $timeout(function() { + $scope.$broadcast('$sp.service_catalog.form_submit_failed', { + action_name: 'submit' + }); + }, timeout); + if (!$scope.data.is_wishlist_item || !$scope.data.is_draft_item) + window.GlideWebAnalytics.trackEvent('Service Catalog', 'Catalog Cart' + webAnalyticsMsgSuffix, 'Catalog Item Added to Cart', 0, 0); + } + } + }) + } + + $scope.triggerUpdateCart = function(timeout) { + $scope.data.sc_cat_item.item_action = "update_cart"; + $scope.data.sc_cat_item.quantity = c.quantity; + $scope.$evalAsync(function() { + if (g_form && !$scope.submitting) { + $scope.submitting = true; + spAriaUtil.sendLiveMessage($scope.m.submittingMsg); + if (!spScUtil.isRegexDone($scope.data.sc_cat_item._fields)) { + $scope.submitting = false; + $scope.validating = true; + $scope.triggerPostValidation = $scope.triggerUpdateCart; + } else if (!g_form.submit()) { + timeout = timeout || 1000; + $timeout(function() { + $scope.$broadcast('$sp.service_catalog.form_submit_failed', { + action_name: 'submit' + }); + }, timeout); + window.GlideWebAnalytics.trackEvent('Service Catalog', 'Catalog Cart' + webAnalyticsMsgSuffix, 'Catalog Cart Updated', 0, 0); + } + } + }) + return false; + } + + $scope.triggerOnSubmit = function(timeout) { + if (c.data.isPreview) return; + + $scope.data.sc_cat_item.item_action = "order"; + $scope.data.sc_cat_item.quantity = c.quantity; + $scope.$evalAsync(function() { + if (g_form && !$scope.submitting) { + $scope.submitting = true; + spAriaUtil.sendLiveMessage($scope.m.submittingMsg); + if (!spScUtil.isRegexDone($scope.data.sc_cat_item._fields)) { + $scope.submitting = false; + $scope.validating = true; + $scope.triggerPostValidation = $scope.triggerOnSubmit; + } else if (!g_form.submit()) { + timeout = timeout || 1000; + $timeout(function() { + $scope.$broadcast('$sp.service_catalog.form_submit_failed', { + action_name: 'submit' + }); + }, timeout); + } + } + }) + return false; + } + + function setFieldsReadonly() { + var allFields = g_form.getFieldNames(); + for (var fieldName in allFields) { + g_form.setReadonly(allFields[fieldName], true); + } + } + // order / create request + function getOne() { + var requested_for_id = ""; + var requested_for_display = ""; + if ($scope.data.requested_for && $scope.data.requested_for.id && $scope.data.requested_for.displayValue) { + requested_for_id = $scope.data.requested_for.id; + requested_for_display = $scope.data.requested_for.displayValue; + } + //Required to pass as payload for usage as embeddedWidget + var embeddedWidgetOptions = { + "auto_redirect": "true", + "requested_for_id": requested_for_id, + "requested_for_display": requested_for_display + }; + if ($scope.data.sc_cat_item.sys_class_name != "sc_cat_item_producer" && $scope.data.sc_cat_item.sys_class_name != "std_change_record_producer") { + if ($scope.data.sys_properties.twostep && $scope.data.sc_cat_item.request_method != "submit") { + var payload = { + cart: 'cart_' + $scope.data.sc_cat_item.sys_id, + itemDetails: { + sys_id: $scope.data.sc_cat_item.sys_id, + name: $scope.data.sc_cat_item.name, + sys_class_name: $scope.data.sc_cat_item.sys_class_name, + quantity: $scope.data.sc_cat_item.quantity, + fields: getVarData($scope.data.sc_cat_item._fields), + newRecordID: $scope.data._generatedItemGUID, + request_method: $scope.data.sc_cat_item.request_method, + }, + action: $scope.data.is_wishlist_item ? "order_wishlist_item" : ($scope.data.is_draft_item ? "order_draft_item" : "order_item"), + parentParams: $scope.data.parentParams + }; + for (var embeddedOption in embeddedWidgetOptions) { + payload[embeddedOption] = c.options[embeddedOption] || embeddedWidgetOptions[embeddedOption]; + } + $scope.server.get(payload).then(function(response) { + var orderItemModalCtrl; + var unregister = $scope.$on('$sp.service_catalog.cart.cancel_order', function() { + $scope.submitting = false; + $scope.orderConfirmation = false; + registerSubmitListeners(); + $timeout(function() { + if (orderItemModalCtrl) + orderItemModalCtrl.close(); + + displayNativeButtons(); + }); + }); + var closeModalOnSubmit = $scope.$on('$sp.service_catalog.cart.submitted', function() { + orderItemModalCtrl.close(); + setFieldsReadonly(); + $scope.submitted = true; + }); + var orderItemModal = angular.copy(response.data.orderItemModal); + orderItemModal.options.afterOpen = function(ctrl) { + orderItemModalCtrl = ctrl; + if (c.data.isMEE == 'true') { + spAriaUtil.sendLiveMessage($scope.m.checkoutDialogMsg); + } + }; + orderItemModal.options.afterClose = function() { + unregister(); + closeModalOnSubmit(); + c.orderItemModal = null; + orderItemModalCtrl = null; + $('#submit-btn').focus(); + }; + c.orderItemModal = orderItemModal; + }); + $scope.orderConfirmation = true; + } else { + var additionalParms = {}; + if ($scope.data.parentParams) { + additionalParms.sysparm_parent_sys_id = $scope.data.parentParams.sysparm_parent_sys_id; + additionalParms.sysparm_parent_table = $scope.data.parentParams.sysparm_parent_table; + additionalParms.is_service_workspace = c.options.isServiceWorkspace; + } + additionalParms.engagement_channel = $scope.data.engagement_channel; + additionalParms.referrer = $scope.data.referrer; + $scope.submitting = true; + showPageLoader(); + addOrderButtons(); + + if ($scope.data.is_wishlist_item || $scope.data.is_draft_item) { + var orderMethod = $scope.data.is_wishlist_item ? spScUtil.orderWishlistedItem : spScUtil.orderDraftItem; + + orderMethod($scope.data.sc_cat_item.sys_id, $scope.data.sc_cat_item.quantity, getVarData($scope.data.sc_cat_item._fields), $scope.data._generatedItemGUID, additionalParms).then(function(response) { + $scope.server.get({ + action: 'log_order_one_step', + itemDetails: { + sys_id: $scope.data.sc_cat_item.sys_id, + name: $scope.data.sc_cat_item.name, + sys_class_name: $scope.data.sc_cat_item.sys_class_name + } + }); + var a = response.data.result; + $scope.$emit("$$uiNotification", a.$$uiNotification); + $scope.$emit("$sp.sc_cat_item.submitted", a); + if ($scope.data.is_wishlist_item) + $rootScope.$broadcast("$sp.service_catalog.wishlist.update"); + + if (c.options.auto_redirect == 'false') { + setFieldsReadonly(); + $scope.submitting = false; + $scope.submitted = true; + $rootScope.$broadcast("$sp.service_catalog.cart.submitted", true); + spUtil.addInfoMessage($scope.m.requestSubmitted); + return; + } else { + if (a.universal_request && !c.options.native_mobile && !c.options.isServiceWorkspace) + $location.search('id=standard_ticket&is_new_order=true&table=universal_request&sys_id=' + a.universal_request); + else { + var url = 'id=sc_request&is_new_order=true&table=sc_request&sys_id=' + a.sys_id; + if ($scope.data.referrer) + url = url + "&referrer=" + $scope.data.referrer; + $location.search(url); + } + } + }); + } else { + spScUtil.orderNow($scope.data.sc_cat_item.sys_id, $scope.data.sc_cat_item.quantity, getVarData($scope.data.sc_cat_item._fields), $scope.data._generatedItemGUID, additionalParms, getAlsoRequestForValue($scope.data.sc_cat_item._fields)).then(function(response) { + $scope.server.get({ + action: 'log_order_one_step', + itemDetails: { + sys_id: $scope.data.sc_cat_item.sys_id, + name: $scope.data.sc_cat_item.name, + sys_class_name: $scope.data.sc_cat_item.sys_class_name + } + }); + var a = response.data.result; + $scope.$emit("$$uiNotification", a.$$uiNotification); + $scope.$emit("$sp.sc_cat_item.submitted", a); + if (c.options.auto_redirect == 'false') { + hidePageLoader(); + setFieldsReadonly(); + $scope.submitting = false; + $scope.submitted = true; + $rootScope.$broadcast("$sp.service_catalog.cart.submitted", true); + spUtil.addInfoMessage($scope.m.requestSubmitted); + return; + } else if (!$scope._atf) { + removeCabrilloButtons(); + //redirect to standard ticket page if universal request is created + if (a.universal_request && !c.options.native_mobile && !c.options.isServiceWorkspace) + $location.search('id=standard_ticket&is_new_order=true&table=universal_request&sys_id=' + a.universal_request); + else { + var url = 'id=sc_request&is_new_order=true&table=sc_request&sys_id=' + a.sys_id; + if ($scope.data.referrer) + url = url + "&referrer=" + $scope.data.referrer; + if (c.options.isServiceWorkspace && c.options.hide_actions) + url = url + "&hide_actions=" + c.options.hide_actions; + $location.search(url); + } + } + }, function(response) { + $scope.$emit('$sp.sc_cat_item.submit_failed'); + handleFailure(response); + }); + } + } + } else { + postCatalogFormRequest().then(function(response) { + var a = response.data.result; + + if ($scope.data.sys_properties.stopNavigationOnError || $scope.options.isServiceWorkspace) { + if (a.sys_id == -1) { //Record Producer didnot generate any record + //check for BR Error + if (a.$$uiNotification.length > 0) { + var errorNotify = a.$$uiNotification.find(function(elem) { + if (elem.type == 'error') + return true; + }); + if (errorNotify) { + $scope.$emit("$$uiNotification", a.$$uiNotification); + $scope.submitted = false; + $scope.submitting = false; + if ($scope.data.record_producer_label) + $scope.submitButtonMsg = $scope.data.record_producer_label; + else + $scope.submitButtonMsg = $scope.m.submitMsg; + + return; + + } + + } + } + } + $scope.server.get({ + action: 'log_request_producer', + itemDetails: { + sys_id: $scope.data.sc_cat_item.sys_id, + name: $scope.data.sc_cat_item.name, + sys_class_name: $scope.data.sc_cat_item.sys_class_name + } + }); + + if (!$scope.options.isServiceWorkspace) + $scope.$emit("$$uiNotification", a.$$uiNotification); + $scope.$emit("$sp.sc_cat_item.submitted", a); + if ($scope.data.is_wishlist_item) + $rootScope.$broadcast("$sp.service_catalog.wishlist.update"); + + hidePageLoader(); + if (c.options.auto_redirect == 'false') { + setFieldsReadonly(); + $scope.submitted = true; + $scope.submitting = false; + $scope.submitButtonMsg = $scope.m.submittedMsg; + } else if (!$scope._atf) + handleRedirect(a.number, a.table, a.sys_id, a.redirect_to, a.redirect_portal_url); + + }); + } + } + + function addToCart() { + $scope.server.get({ + action: 'log_request_cart', + itemDetails: { + sys_id: $scope.data.sc_cat_item.sys_id, + name: $scope.data.sc_cat_item.name, + sys_class_name: $scope.data.sc_cat_item.sys_class_name + } + }); + + postCatalogFormRequest().then(function(response) { + $rootScope.$broadcast("$sp.service_catalog.cart.add_item"); + $rootScope.$broadcast("$sp.service_catalog.cart.update"); + $scope.$emit("$sp.sc_cat_item.add_to_cart", $scope.data._generatedItemGUID); + g_form.$private.userState.clearModifiedFields(); + if ($scope.data.is_wishlist_item) { + $rootScope.$broadcast("$sp.service_catalog.wishlist.update"); + $scope.data.is_wishlist_item = false; + $scope.data.sc_cat_item.isCartItem = false; + if ($location.$$search.edit === "wishlist") { + $location.search("id=sc_wishlist"); + return; + } + } + if ($scope.data.is_draft_item) { + $scope.data.is_draft_item = false; + $scope.data.sc_cat_item.isCartItem = false; + if ($location.$$search.edit === "draft") { + $location.search('id=sc_cart'); + return; + } + } + c.status = i18n.getMessage("Added item to shopping cart"); + var cartResponse = response; + $scope.server.get({ + action: 'init_item' + }).then(function(response) { + $scope.data._generatedItemGUID = response.data._generatedItemGUID; + $scope.data.sc_cat_item._attachmentGUID = response.data._generatedItemGUID; + ah.setParams($scope.data._attachmentTable, $scope.data._generatedItemGUID, 1024 * 1024 * $scope.data.maxAttachmentSize); + $scope.attachmentHandler.getAttachmentList(); + $scope.attachments = []; + $scope.clearAttachmentFields(); + $scope.data.sc_cat_item.attachment_action_in_progress = false; + $scope.data.sc_cat_item.attachment_submitted = false; + + if (!c.isNative) { + $scope.m.actionMsg = $scope.m.cartAddMsg + $scope.m.cartMakeChangesMsg + addLink('?id=sc_cart', $scope.m.viewCartMsg, "view_cart"); + $scope.m.actionMsg += ''; + $scope.m.actionMsg = $sce.trustAsHtml($scope.m.actionMsg); + $scope.data.showMsg = true; + } else { + cabrillo.message.showMessage(cabrillo.message.SUCCESS_MESSAGE_STYLE, c.status); + if (cartResponse && cartResponse.data && cartResponse.data.result) { + var items = cartResponse.data.result.items || []; + $scope.showCabrilloCart = true; + $scope.cartItemCount = items.length; + showCartButton(); + } + } + $scope.submitting = false; + hidePageLoader(); + cleanFailedSubmit = $scope.$on('$sp.service_catalog.form_submit_failed', function() { + $scope.submitting = false; + }); + spUtil.scrollTo('#sc_cat_item', 300); + $scope.focusElement("view_cart", $scope.m.cartAddMsg); + if (c.isNative) + displayNativeButtons(); + }); + }); + } + + function updateCart() { + postCatalogFormRequest().then(function(response) { + g_form.$private.userState.clearModifiedFields(); + c.status = i18n.getMessage("Updated Item to shopping cart"); + if (c.isNative) + cabrillo.message.showMessage(cabrillo.message.SUCCESS_MESSAGE_STYLE, c.status); + removeCabrilloButtons(); + $location.search('id=sc_cart'); + }) + } + + function postCatalogFormRequest() { + $scope.submitting = true; + showPageLoader(); + if ($scope.data.sc_cat_item.item_action !== "add_to_cart") + addOrderButtons(); + + var additionalParms = {}; + if ($scope.data.parentParams) { + mergeMap($scope.data.parentParams, additionalParms); + } + additionalParms.engagement_channel = $scope.data.engagement_channel; + additionalParms.referrer = $scope.data.referrer; + + var isDraftOrWishlistItem = $scope.data.is_wishlist_item || $scope.data.is_draft_item; + + if ($scope.data.is_wishlist_item) { + $scope.is_update_wishlist = false; + if ($scope.data.sc_cat_item.sys_class_name === "sc_cat_item_producer") + return spScUtil.submitWishlistedProducer($scope.data.sc_cat_item.sys_id, getVarData($scope.data.sc_cat_item._fields), $scope.data._generatedItemGUID, additionalParms); + else if ($scope.data.sc_cat_item.sys_class_name === "std_change_record_producer") { + additionalParms.is_wishlist_item = true; + return spScUtil.submitStdChgProducer($scope.data.sc_cat_item.sys_id, $scope.data.stdChg.twoStep, $scope.data.stdChg.currentVersion, $scope.data._generatedItemGUID, $scope.portal.url_suffix, additionalParms, $scope.data.stdChg.chgModel, $scope.data.stdChg.defaultType); + } else if ($scope.data.sc_cat_item.item_action === "add_to_cart") { + if ($scope.data.sc_cat_item.sys_class_name == 'sc_cat_item_guide') + window.GlideWebAnalytics.trackEvent("Service Catalog", "Catalog Cart" + webAnalyticsMsgSuffix, "Order Guide Added to Cart", 0, 0); + else if ($scope.data.sc_cat_item.sys_class_name == 'sc_cat_item_producer') + window.GlideWebAnalytics.trackEvent("Service Catalog", "Catalog Cart" + webAnalyticsMsgSuffix, "Record Producer Added to Cart", 0, 0); + else if ($scope.data.sc_cat_item.sys_class_name == "sc_cat_item" || $scope.data.sc_cat_item.sys_class_name == "pc_hardware_cat_item" || $scope.data.sc_cat_item.sys_class_name == "pc_software_cat_item") + window.GlideWebAnalytics.trackEvent("Service Catalog", "Catalog Cart", "Catalog Item Added to Cart" + webAnalyticsMsgSuffix, 0, 0); + return spScUtil.addWishlistedItemToCart($scope.data.sc_cat_item.sys_id, $scope.data.sc_cat_item.quantity, getVarData($scope.data.sc_cat_item._fields), $scope.data._generatedItemGUID, additionalParms).then(null, function(response) { + return handleFailure(response); + }); + } + } else if ($scope.data.is_draft_item) { + additionalParms.wishlist_item_id = $scope.data.wishlist_item_id; + if ($scope.data.sc_cat_item.sys_class_name === "sc_cat_item_producer") + return spScUtil.submitDraftProducer($scope.data.sc_cat_item.sys_id, getVarData($scope.data.sc_cat_item._fields), $scope.data._generatedItemGUID, additionalParms); + else if ($scope.data.sc_cat_item.sys_class_name === "std_change_record_producer") { + additionalParms.is_draft_item = true; + return spScUtil.submitStdChgProducer($scope.data.sc_cat_item.sys_id, $scope.data.stdChg.twoStep, $scope.data.stdChg.currentVersion, $scope.data._generatedItemGUID, $scope.portal.url_suffix, additionalParms, $scope.data.stdChg.chgModel, $scope.data.stdChg.defaultType); + } else if ($scope.data.sc_cat_item.item_action === "add_to_cart") { + if ($scope.data.sc_cat_item.sys_class_name == 'sc_cat_item_guide') + window.GlideWebAnalytics.trackEvent("Service Catalog", "Catalog Cart" + webAnalyticsMsgSuffix, "Order Guide Added to Cart", 0, 0); + else if ($scope.data.sc_cat_item.sys_class_name == 'sc_cat_item_producer') + window.GlideWebAnalytics.trackEvent("Service Catalog", "Catalog Cart" + webAnalyticsMsgSuffix, "Record Producer Added to Cart", 0, 0); + else if ($scope.data.sc_cat_item.sys_class_name == "sc_cat_item" || $scope.data.sc_cat_item.sys_class_name == "pc_hardware_cat_item" || $scope.data.sc_cat_item.sys_class_name == "pc_software_cat_item") + window.GlideWebAnalytics.trackEvent("Service Catalog", "Catalog Cart", "Catalog Item Added to Cart" + webAnalyticsMsgSuffix, 0, 0); + return spScUtil.addDraftItemToCart($scope.data.sc_cat_item.sys_id, $scope.data.sc_cat_item.quantity, getVarData($scope.data.sc_cat_item._fields), $scope.data._generatedItemGUID).then(null, function(response) { + return handleFailure(response); + }); + } else if ($scope.data.sc_cat_item.item_action === "update_cart") { + return spScUtil.updateCart($scope.data._generatedItemGUID, $scope.data.sc_cat_item.quantity, getVarData($scope.data.sc_cat_item._fields), $scope.data.sc_cat_item.sys_id).then(null, function(response) { + return handleFailure(response); + }); + } + } + if ($scope.data.is_cart_item && !isDraftOrWishlistItem) { + return spScUtil.updateCart($scope.data._generatedItemGUID, $scope.data.sc_cat_item.quantity, getVarData($scope.data.sc_cat_item._fields), $scope.data.sc_cat_item.sys_id).then(null, function(response) { + return handleFailure(response); + }); + } else if ($scope.data.sc_cat_item.sys_class_name === "sc_cat_item_producer") { + return spScUtil.submitProducer($scope.data.sc_cat_item.sys_id, getVarData($scope.data.sc_cat_item._fields), $scope.data._generatedItemGUID, additionalParms).then(null, function(response) { + return handleFailure(response); + }); + } else if ($scope.data.sc_cat_item.sys_class_name === "std_change_record_producer") { + return spScUtil.submitStdChgProducer($scope.data.sc_cat_item.sys_id, $scope.data.stdChg.twoStep, $scope.data.stdChg.currentVersion, $scope.data._generatedItemGUID, $scope.portal.url_suffix, additionalParms, $scope.data.stdChg.chgModel, $scope.data.stdChg.defaultType); + } else if ($scope.data.sc_cat_item.item_action === "add_to_cart") { + return spScUtil.addToCart($scope.data.sc_cat_item.sys_id, $scope.data.sc_cat_item.quantity, getVarData($scope.data.sc_cat_item._fields), $scope.data._generatedItemGUID, getAlsoRequestForValue($scope.data.sc_cat_item._fields)).then(null, function(response) { + return handleFailure(response); + }); + } + } + // spModel populates mandatory - hasMandatory is called by the submit button + $scope.hasMandatory = function() { + return c.mandatory && c.mandatory.length > 0; + }; + // Listeners + var cleanFailedSubmit; + var validationComplete; + + function registerSubmitListeners() { + cleanFailedSubmit = $scope.$on('$sp.service_catalog.form_submit_failed', function() { + $scope.submitting = false; + }); + validationComplete = $rootScope.$on('$sp.service_catalog.form_validation_complete', function() { + if ($scope.validating) { + $scope.validating = false; + if (typeof $scope.triggerPostValidation === 'function') + $scope.triggerPostValidation(); + } + $scope.triggerPostValidation = null; + }); + } + registerSubmitListeners(); + $scope.$on("$sp.sc_cat_item.submitted", function() { + $rootScope.$broadcast("$sp.sc_cat_item.rp_submitted"); + + if ($scope.data.sc_cat_item.item_action == "order") { + if ($scope.data.sc_cat_item.sys_class_name == "sc_cat_item" || $scope.data.sc_cat_item.sys_class_name == "pc_hardware_cat_item" || $scope.data.sc_cat_item.sys_class_name == "pc_software_cat_item") + window.GlideWebAnalytics.trackEvent("Service Catalog", "Catalog Item Request" + webAnalyticsMsgSuffix, "Catalog Request Submitted", 1, 0); + else if (scope.data.sc_cat_item.sys_class_name == "sc_cat_item_producer") + window.GlideWebAnalytics.trackEvent("Service Catalog", "Record Producer Request" + webAnalyticsMsgSuffix, "Catalog Request Submitted", 0, 0); + } + g_form.$private.userState.clearModifiedFields(); + if (c.options.auto_redirect == 'false') + spAriaUtil.sendLiveMessage($scope.m.formSubmittedMsg); + var payload = {}; + payload.name = "Submit Record Producer Request"; + var className = $scope.data.sc_cat_item.sys_class_name; + if (className == "sc_cat_item" || className == "pc_hardware_cat_item" || className == "pc_software_cat_item") + payload.name = "Order Catalog Item Request"; + + payload.data = {}; + payload.data["Record Item"] = $scope.data.sc_cat_item.name; + payload.data["Record ID"] = $scope.data.sc_cat_item.sys_id; + snAnalytics.addEvent(payload); + }); + + // switch catalog items + var unregister = $scope.$on('$sp.list.click', onListClick); + $scope.$on("$destroy", function() { + $rootScope.$broadcast("$sp.service_catalog.item.close"); + if (g_form) + spSCNavStateManager.unregisterForms([g_form.getSysId()]); + + unregister(); + mespClosePopupUnregister(); + mespOpenPopupUnregister(); + validationComplete(); + }); + $rootScope.$on('spModel.gForm.rendered', function() { + spAriaUtil.sendLiveMessage($scope.m.catItemOpenedMsg); + }); + + function onListClick(evt, arg) { + $scope.data.sys_id = arg.sys_id; + spUtil.update($scope); + } + + function formatRedirectUrl(page, table, sys_id, hide_actions) { + var url; + var paramObj = { + page: page, + table: table, + sys_id: sys_id + }; + url = spUtil.format(c.options.url, paramObj); + if ($scope.data.referrer) + url = url + '&referrer=' + $scope.data.referrer; + if (hide_actions) + url = url + "&hide_actions=" + hide_actions; + return url; + } + + function handleRedirect(n, table, sys_id, redirectTo, redirectUrl) { + var page = 'form'; + if (table == 'sc_request') + page = 'sc_request'; + else if (n) + page = 'ticket'; + + if (sys_id == -1) + sys_id = undefined; + + if (redirectTo == 'catalog_home') + page = 'sc_home'; + + //For Standard change, always direct to form if not in Workspace + if ($scope.data.sc_cat_item.sys_class_name === "std_change_record_producer") { + if ($scope.options.isServiceWorkspace == 'true') { + var params = {}; + params.msg = 'TARGET_RECORD_SELECTED'; + params.target_table = table; + params.target_sys_id = '-1'; + if (sys_id) + params.target_sys_id = sys_id; + + if ($scope.data.stdChg.twoStep) { + var genURL = new URL($window.location.origin + "/" + redirectUrl).searchParams; + params.target_query = genURL.get("query"); + } + + window.postMessage(params, $window.location.origin); + return; + } else + page = 'form'; + } + removeCabrilloButtons(); + if (c.options.page) { + page = c.options.page; + } + if (c.options.table) { + table = c.options.table; + } + var hide_actions = c.options.isServiceWorkspace && c.options.hide_actions; + var url; + if (page === 'sc_home') { + url = "id=" + page + } else { + url = formatRedirectUrl(page, table, sys_id, hide_actions); + } + if ($scope.data.sc_cat_item.sys_class_name === "sc_cat_item_producer" || $scope.data.sc_cat_item.sys_class_name === "std_change_record_producer") { + if (redirectUrl) { + if (isPortalURL(redirectUrl)) { + var queryParamURL = getQueryParams(redirectUrl); + var currentParamURL = getQueryParams($location.$$url); + if (queryParamURL == currentParamURL) + $location.search(queryParamURL + '&' + Date.now()); + else + $location.search(queryParamURL); + } else + $window.location.href = redirectUrl; + } else { + var newURL = $location.search(url); + spAriaFocusManager.navigateToLink(newURL.url()); + } + return; + } + var hide_actions = c.options.isServiceWorkspace && c.options.hide_actions; + $location.search(formatRedirectUrl('sc_request', 'sc_request', sys_id, hide_actions)); + return; + } + + function isPortalURL(url) { + var currentPortalName = $location.path().replace('/', ''); + var paramIndex = getQueryParameterIndex(url) + var redirectPortalName = url.substr(0, paramIndex).replace('/', ''); + return currentPortalName === redirectPortalName || paramIndex == 0; + } + + function getQueryParams(url) { + var paramIndex = getQueryParameterIndex(url); + return url.substr(paramIndex + 1, url.length); + } + + function getQueryParameterIndex(url) { + var paramIndex = url.search(/\?/); + return paramIndex >= 0 ? paramIndex : url.length; + } + + $timeout(function() { + if ($document[0].getElementsByClassName('sc-sticky-item-header').length > 0) { + var titleHeight = $document[0].getElementsByClassName('sc-sticky-item-header')[0].clientHeight; + $scope.stickyHeaderTop = '-' + (titleHeight - 20 - $document[0].getElementsByClassName('sc-cat-item-short-description')[0].clientHeight) + 'px;'; + } + }); + + + function setBottomButtons(buttonInfoArray) { + if (!c.isNative) return; + + var buttons = buttonInfoArray.map(function(buttonInfo) { + return { + title: buttonInfo.title, + enabled: buttonInfo.enabled, + backgroundColor: buttonInfo.backgroundColor, + textColor: buttonInfo.textColor + }; + }); + + cabrillo.viewLayout.setBottomButtons(buttons, function(buttonIndex) { + if (buttonInfoArray[buttonIndex] && typeof buttonInfoArray[buttonIndex].action === 'function') { + $timeout(function() { + buttonInfoArray[buttonIndex].action(); + }, 500); + } + }); + } + + function getSaveButtonTitle() { + return !$scope.data.is_draft_item ? (!$scope.savingItem ? $scope.m.saveAsDraft : $scope.m.savingMsg) : (!$scope.savingItem ? $scope.m.updateDraft : $scope.m.updatingMsg); + } + + function addOrderButtons() { + if (!c.isNative) return; + showCartButton(); + + var orderButtons = []; + + if (c.showDraftButtons()) { + orderButtons.push({ + title: getSaveButtonTitle(), + enabled: !$scope.submitting && !$scope.savingItem, + backgroundColor: '#f7f7f7', + textColor: '#000000', + action: function() { + $scope.triggerSaveItem(); + } + }); + } + + if ($scope.c.data.sys_properties.cartEnabled && $scope.c.showAddCartBtn()) { + orderButtons.push({ + title: $scope.m.addToCart, + enabled: !$scope.submitting && !$scope.savingItem, + backgroundColor: '#f7f7f7', + textColor: '#000000', + action: function() { + $scope.triggerAddToCart(); + } + }); + } + + orderButtons.push({ + title: $scope.submitting && !$scope.savingItem ? $scope.m.submittingMsg : $scope.submitButtonMsg, + enabled: !$scope.submitting && !$scope.savingItem, + backgroundColor: $scope.data.sys_properties.isPolaris == "true" ? null : $scope.data.sys_properties.mobileNativeColor, + textColor: $scope.data.sys_properties.isPolaris == "true" ? null : '#FFFFFF', + action: function() { + $scope.triggerOnSubmit(); + } + }); + + + setBottomButtons(orderButtons); + } + + + + function addRPButton() { + if (!c.isNative) return; + + var rpButtons = []; + + if (c.showDraftButtons()) { + rpButtons.push({ + title: getSaveButtonTitle(), + enabled: !$scope.submitting && !$scope.savingItem, + backgroundColor: '#f7f7f7', + textColor: '#000000', + action: function() { + $scope.triggerSaveItem(); + } + }); + } + + rpButtons.push({ + title: $scope.submitting && !$scope.savingItem ? $scope.m.submittingMsg : $scope.submitButtonMsg, + enabled: !$scope.submitting && !$scope.savingItem, + backgroundColor: $scope.data.sys_properties.isPolaris == "true" ? null : $scope.data.sys_properties.mobileNativeColor, + textColor: $scope.data.sys_properties.isPolaris == "true" ? null : '#FFFFFF', + action: function() { + $scope.triggerOnSubmit(); + } + }); + + setBottomButtons(rpButtons); + } + + function showCartButton() { + if (!c.isNative || !$scope.showCabrilloCart) return; + + var button = [{ + imageName: 'cart', + badgeCount: $scope.cartItemCount, + backgroundColor: '#2ff5f9', + textColor: '#FFFFFF', + enabled: true + }]; + + cabrillo.viewLayout.setNavigationBarButtons(button, function(index) { + $location.search('id=sc_cart'); + }); + + } + + function addCartItemButtons() { + if (!c.isNative) return; + var cartItemButtons = [ + + { + title: getSaveButtonTitle(), + enabled: !$scope.submitting && !$scope.savingItem, + backgroundColor: '#f7f7f7', + textColor: '#000000', + action: function() { + $scope.triggerSaveItem(); + } + }, + + { + title: $scope.m.updateCart, + enabled: !$scope.submitting && !$scope.savingItem, + backgroundColor: $scope.data.sys_properties.isPolaris == "true" ? null : $scope.data.sys_properties.mobileNativeColor, + textColor: $scope.data.sys_properties.isPolaris == "true" ? null : '#FFFFFF', + action: function() { + $scope.triggerUpdateCart(); + } + } + + ]; + + setBottomButtons(cartItemButtons); + nativeGoBackToCart(); + } + + function removeCabrilloButtons() { + if (!c.isNative) return; + cabrillo.viewLayout.setBottomButtons(); + } + + function showPageLoader() { + if (!c.isNative) return; + cabrillo.viewLayout.showSpinner(); + } + + function hidePageLoader() { + if (!c.isNative) return; + cabrillo.viewLayout.hideSpinner(); + } + + function handleFailure(response) { + registerSubmitListeners(); + + $scope.submitting = false; + hidePageLoader(); + if ($scope.data.sc_cat_item.item_action !== "add_to_cart") + addOrderButtons(); + if (response.data.result && response.data.result.errMsg) + spUtil.addErrorMessage(response.data.result.errMsg); + + return $q.reject(response); + } + + function mergeMap(fromMap, toMap) { + for (var key in fromMap) { + toMap[key] = fromMap[key]; + } + } + + + function generateMessageWithLink(preLinkMsg, linkMsgObj, postLinkMsg, showCloseBtn) { + var linkTemplate = ('{0} ' + ' {1} ') + (!!postLinkMsg ? '{2}' : ''); + if (showCloseBtn) + linkTemplate += ''; + + return i18n.format(linkTemplate, preLinkMsg, linkMsgObj.message, postLinkMsg); + } + + $scope.sendLiveMessage = function(message, timeout) { + if (!message) + return; + + if (!timeout) + timeout = 0; + + setTimeout(function() { + spAriaUtil.sendLiveMessage(message); + }, timeout); + } + + $window.onpageshow = function() { + if (c.isNative) + $scope.$emit('spModel.gForm.showNativeMobileButtons'); + }; + + var favoriteEvent = $rootScope.$on('favorite', function(e, favorite) { + $scope.showFavorite = favorite.showFavorite; + $scope.isFavorite = favorite.isFavorite; + }); + $scope.$on("$destroy", favoriteEvent); + + $scope.toggleFavorite = function($event) { + $event.preventDefault(); + $event.stopPropagation(); + $scope.$broadcast('toggleFavorite'); + } +} \ No newline at end of file diff --git a/Service Portal Widgets/SC Catalog Item/Options Schema b/Service Portal Widgets/SC Catalog Item/Options Schema new file mode 100644 index 0000000000..f63ebf290a --- /dev/null +++ b/Service Portal Widgets/SC Catalog Item/Options Schema @@ -0,0 +1 @@ +[{"name":"show_add_cart_button","section":"other","label":"Show Add Cart Button","type":"boolean"},{"hint":"If you turn on this, you will be able to see field validation messages on right side","name":"show_field_validation_messages","default_value":"true","section":"other","label":"Show field validation messages","type":"boolean"},{"hint":"Show Add/Update Wish List buttons","name":"show_add_to_wishlist_button","default_value":"false","section":"other","label":"Show Add/Update Wish List buttons","type":"boolean"},{"hint":"Order item section on top. If you uncheck this, order section will be displayed at bottom of screen","name":"display_cart_on_right","default_value":"true","section":"other","label":"Order Item Section On Top","type":"boolean"},{"hint":"Show item description in few words with an option to read more","name":"show_less_description","default_value":"true","section":"other","label":"Enable Show More/Less for Item description on Mobile","type":"boolean"},{"hint":"Hide Save as Draft button","name":"hide_save_as_draft_button","default_value":"false","section":"other","label":"Hide Save as Draft button","type":"boolean"},{"hint":"Hide the Delivery Time in the widget","name":"hide_delivery_time","section":"other","default_value":"false","label":"Hide Delivery Time","type":"boolean"},{"hint":"Makes it a preview page","name":"is_preview","section":"other","default_value":"false","label":"Is preview","type":"boolean"}] \ No newline at end of file diff --git a/Service Portal Widgets/SC Catalog Item/README.md b/Service Portal Widgets/SC Catalog Item/README.md new file mode 100644 index 0000000000..098f85ddfa --- /dev/null +++ b/Service Portal Widgets/SC Catalog Item/README.md @@ -0,0 +1,4 @@ +##SC Catalog Item Widget - Out of the box +ID: widget-sc-cat-item-v2 +This is the OOB (out of the box) catalog item widget used in the service portal to display catalog items. + diff --git a/Service Portal Widgets/SC Catalog Item/Server Script b/Service Portal Widgets/SC Catalog Item/Server Script new file mode 100644 index 0000000000..6f61914c2f --- /dev/null +++ b/Service Portal Widgets/SC Catalog Item/Server Script @@ -0,0 +1,586 @@ +// populate the 'data' variable with catalog item, variables, and variable view +(function() { + var localInput = input; //to safeguard pullution of "input" via BR or other scripts + + var engagementChannelOptions = { //extracting here to safeguard pollution of 'options' via other scripts + native_mobile: options.native_mobile, + isServiceWorkspace: options.isServiceWorkspace + }; + data.engagement_channel = new global.GlobalServiceCatalogUtil().getEngagementChannel(engagementChannelOptions); + data.referrer = $sp.getParameter("referrer"); + data.portal_suffix = $sp.getPortalRecord().getValue("url_suffix"); + + var embeddedWidgetOptions = ['auto_redirect', 'requested_for_id', 'requested_for_display']; + if (localInput && localInput.action == "from_attachment") + return; + + if (localInput && localInput.action == "get_requested_for") { + data.requested_for = new global.GlobalServiceCatalogUtil().getRequestedFor(localInput.parentParams); + return; + } + + if (localInput && localInput.action == 'init_item') { + data._generatedItemGUID = gs.generateGUID(); + return; + } else if (localInput && localInput.action === "order_one_step") { + + } else if (localInput && localInput.action === "order_item") { + //Minimum set of widget options supported for Embedded widget + embeddedWidgetOptions.forEach(function(embeddedWidgetOption) { + if (typeof localInput[embeddedWidgetOption] != 'undefined') + options[embeddedWidgetOption] = localInput[embeddedWidgetOption]; + }); + data.orderItemModal = $sp.getWidget('widget-modal', { + embeddedWidgetId: 'sc-checkout', + embeddedWidgetOptions: { + cart: { + name: localInput.cart + }, + action: 'order_now', + item: localInput.itemDetails, + requested_for: { + id: options.requested_for_id, + displayValue: options.requested_for_display + }, + auto_redirect: options.auto_redirect, + parentParams: localInput.parentParams, + native_mobile: options.native_mobile, + isServiceWorkspace: options.isServiceWorkspace, + referrer: data.referrer, + hide_actions: options.hide_actions + }, + backdrop: 'static', + keyboard: false, + size: 'md' + }); + return; + } else if (localInput && localInput.action === "save_item") { + data.saveItemModal = $sp.getWidget('widget-modal', { + embeddedWidgetId: 'sc-item-save', + embeddedWidgetOptions: { + native_mobile: options.native_mobile, + draftName: localInput.draftName, + message: localInput.message, + infoMessage: localInput.infoMessage, + }, + backdrop: 'static', + keyboard: false, + size: 'md' + }); + return; + + } else if (localInput && (localInput.action == "order_wishlist_item" || localInput.action == "order_draft_item")) { + //Minimum set of widget options supported for Embedded widget + embeddedWidgetOptions.forEach(function(embeddedWidgetOption) { + if (typeof localInput[embeddedWidgetOption] != 'undefined') + options[embeddedWidgetOption] = localInput[embeddedWidgetOption]; + }); + data.orderItemModal = $sp.getWidget('widget-modal', { + embeddedWidgetId: 'sc-checkout', + embeddedWidgetOptions: { + cart: { + name: localInput.cart + }, + action: localInput.action == "order_wishlist_item" ? 'order_now_wishlisted_item' : 'order_now_draft_item', + item: localInput.itemDetails, + requested_for: { + id: options.requested_for_id, + displayValue: options.requested_for_display + }, + auto_redirect: options.auto_redirect, + parentParams: localInput.parentParams, + native_mobile: options.native_mobile, + referrer: data.referrer + }, + backdrop: 'static', + keyboard: false, + size: 'md' + }); + return; + } else if (localInput && localInput.action === 'log_request_cart') { + $sp.logStat('Add to Cart Request', localInput.itemDetails.sys_class_name, localInput.itemDetails.sys_id, localInput.itemDetails.name, $sp.getPortalRecord().getUniqueValue()); + return; + } else if (localInput && localInput.action === "log_order_one_step") { + $sp.logStat('Order Now Request', localInput.itemDetails.sys_class_name, localInput.itemDetails.sys_id, localInput.itemDetails.name, $sp.getPortalRecord().getUniqueValue()); + $sp.logStat('Cat Item Request', localInput.itemDetails.sys_class_name, localInput.itemDetails.sys_id, localInput.itemDetails.name, $sp.getPortalRecord().getUniqueValue()); + return; + } else if (localInput && localInput.action === 'log_request_producer') { + $sp.logStat('Cat Item Request', localInput.itemDetails.sys_class_name, localInput.itemDetails.sys_id, localInput.itemDetails.name, $sp.getPortalRecord().getUniqueValue()); + return; + } + + // portal can specify a catalog and catalog category home page + var catalogID = $sp.getParameter("catalog_id") ? $sp.getParameter("catalog_id") + "" : "-1"; + data.sc_catalog_page = $sp.getDisplayValue("sc_catalog_page") || "sc_home"; + data.sc_category_page = $sp.getDisplayValue("sc_category_page") || "sc_category"; + var edit_parm = $sp.getParameter('edit'); + data.is_cart_item = edit_parm == 'cart'; + data.is_wishlist_item = edit_parm == 'wishlist'; + data.is_draft_item = edit_parm == 'draft'; + data.isPreview = options.is_preview == 'true' || $sp.getParameter("is_preview") == 'true'; + data.wishlist_item_id = ""; + data.recordFound = true; + options.show_add_cart_button = (options.show_add_cart_button == "true"); + data.isMEE = options.native_mobile; + var athTblName = 'sc_cart_item'; + + var clGenerator = new GlideChoiceList(); + var choiceListQuantity = clGenerator.getChoiceList("sc_cart_item", "quantity"); + var choicelistQuantityData = []; + for (var i = 0; i < choiceListQuantity.size(); i++) { + var choice = choiceListQuantity.get(i); + if (!isNaN(choice.getValue())) + choicelistQuantityData.push({ + value: parseInt(choice.getValue()), + label: choice.getLabel() + }); + } + data.choiceListQuantity = choicelistQuantityData; + data.quantity = choicelistQuantityData[0].value; + if (options.page) { + var pageGR = new GlideRecord("sp_page"); + options.page = (pageGR.get(options.page)) ? pageGR.getValue("id") : null; + } + if (options.table) { + var tableGR = new GlideRecord("sys_db_object"); + options.table = (tableGR.get(options.table)) ? tableGR.getValue("name") : null; + } + options.url = options.url || "id={page}&is_new_order=true&table={table}&sys_id={sys_id}"; + + data.showPrices = $sp.showCatalogPrices(); + var m = data.msgs = {}; + m.submitMsg = gs.getMessage("Submit"); + m.deletedOutdatedItemMsg = gs.getMessage("The draft catalog item you’re trying to open is deleted because the catalog item was updated after you saved it as a draft. You’re redirected to a new catalog item page."); + m.requestMsg = gs.getMessage("Request"); + m.orderNowMsg = gs.getMessage("Order Now"); + m.submittedMsg = gs.getMessage("Submitted"); + m.formSubmittedMsg = gs.getMessage("Form submitted successfully"); + m.submittingMsg = gs.getMessage("Submitting"); + m.savingMsg = gs.getMessage("Saving"); + m.updatingMsg = gs.getMessage("Updating"); + m.createdMsg = gs.getMessage("Created"); + m.trackMsg = gs.getMessage("track using 'Requests' in the header or"); + m.clickMsg = gs.getMessage("click here to view"); + m.dialogTitle = gs.getMessage("Delete Attachment"); + m.dialogMessage = gs.getMessage("Are you sure?"); + m.dialogOK = gs.getMessage("OK"); + m.dialogCancel = gs.getMessage("Cancel"); + m.dialogSave = gs.getMessage("Save"); + m.dialogUpdate = gs.getMessage("Update"); + m.addToCart = gs.getMessage("Add to Cart"); + m.updateCart = gs.getMessage("Update Cart"); + m.saveAsDraft = gs.getMessage("Save as draft"); + m.updateDraft = gs.getMessage("Update draft"); + m.alsoReqForClearConfirmMsg = gs.getMessage("This will clear the values entered in 'Also requested for'. Do you want to proceed?"); + m.attachmentAddedMsg = gs.getMessage("Attachment added successfully"); + m.renameSuccessMsg = gs.getMessage("Attachment renamed successfully"); + m.deleteSuccessMsg = gs.getMessage("Attachment deleted successfully"); + m.wishlistMsg = gs.getMessage('Wish List'); + m.cartMsg = gs.getMessage('Cart'); + m.myRequestsMsg = gs.getMessage('My Requests'); + m.draftItemMsg = gs.getMessage('Draft Items'); + m.itemWishlistMsg = gs.getMessage('This item is already in your Wish List. If you attempt to add this item to your Wish List it will overwrite the existing item.'); + m.invalidRecordMsg = gs.getMessage('You are either not authorized or record is not valid.'); + m.wishlistUpdateMsg = gs.getMessage('Your Wish List has been updated.'); + m.wishlistAddMsg = gs.getMessage('Your item has been added to your Wish List.'); + m.draftSaveMsg = gs.getMessage('Your item has been saved in My Requests.'); + m.draftUpdateMsg = gs.getMessage('Your draft item has been updated.'); + m.viewDraftItemMsg = gs.getMessage('View Drafts'); + m.cartAddMsg = gs.getMessage('Your item has been added to your Cart. '); + m.cartMakeChangesMsg = gs.getMessage('To make changes to the items in your cart, click ') + m.viewWishListMsg = gs.getMessage('View Wish List'); + m.viewCartMsg = gs.getMessage('View Cart'); + m.delete_attachment = gs.getMessage("Delete Attachment?"); + m.regexError = gs.getMessage("Item with invalid variable can't be saved"); + m.requestSubmitted = gs.getMessage("Thank you, your request has been submitted."); + data.maxAttachmentSize = parseInt(gs.getProperty("com.glide.attachment.max_size", 1024)); + m.updatedMsg = gs.getMessage("Updated"); + m.quantityToMsg = gs.getMessage("quantity to"); + if (isNaN(data.maxAttachmentSize)) + data.maxAttachmentSize = 24; + m.largeAttachmentMsg = gs.getMessage("Attached files must be smaller than {0} - please try again", "" + data.maxAttachmentSize + "MB"); + m.checkoutDialogMsg = gs.getMessage("Catalog checkout dialog"); + m.saveItemDialogMsg = gs.getMessage("Catalog item save dialog"); + m.notForMobileMsg = gs.getMessage('Not viewable in mobile'); + m.save_draft = gs.getMessage('Save draft'); + m.update_draft = gs.getMessage('Update draft'); + m.save_draft_as = gs.getMessage('Save draft as'); + m.hereMsg = gs.getMessage("here."); + m.draftExistsMsg = gs.getMessage('A draft for this item already exists. You can view all the drafts'); + m.newDraftInfoMsg = gs.getMessage("If you save this item now, a new draft item would be created."); + m.itemSavedMsg = gs.getMessage('Item saved successfully'); + m.itemRemovedFromCartMsg = gs.getMessage("has been removed from your cart"); + m.itemRemovedFromWishlistMsg = gs.getMessage("has been removed from your wishlist"); + m.saveDraftNameLengthErrMsg = gs.getMessage('Draft Item name should not exceed 200 characters.'); + m.save_draft_description = gs.getMessage('Give a unique name to save this item as a draft to continue working on it later.'); + m.wishlistItemDeletionMsg = gs.getMessage("This catalog item is on your wish list. After you save the draft, the item will be deleted from the wishlist."); + m.cartItemDeletionMsg = gs.getMessage("This catalog item is in your cart. After you save the draft, the item will be deleted from the cart."); + m.draftAlreadyExistCabrilloMsg = gs.getMessage("A draft for this item already exists."); + if (options.record_producer_label) + data.record_producer_label = gs.getMessage(options.record_producer_label); + + if (options.native_mobile == 'true') { + var listScreenGr = new GlideRecord("sys_sg_list_screen"); + listScreenGr.addQuery("sys_id", "31307d1787232300e0ef0cf888cb0b15"); + listScreenGr.setWorkflow(false); + listScreenGr.query(); + if (listScreenGr.next()) { + var deepLinkGen = new global.MobileDeepLinkGenerator('request'); + data.nativeMyRequestsScreenLink = deepLinkGen.getScreenLink('31307d1787232300e0ef0cf888cb0b15'); + } else + data.nativeMyRequestsScreenLink = ""; + } + + if (edit_parm) { + var cartName = data.is_cart_item ? 'DEFAULT' : (data.is_wishlist_item ? 'saved_items' : 'draft_items'); + var cart = new sn_sc.CartJS(cartName); + + var cart_item_id = $sp.getParameter("sys_id"); + var gr = new GlideRecord("sc_cart_item"); + if (!gr.get(cart_item_id) || gr.cart != cart.getCartID() || + (!new sn_sc.CatItem(gr.getValue('cat_item')).canView())) { + data.recordFound = false; + return; + } + data.showWishlist = data.is_wishlist_item; + if (gr.isValidField('cart_item_name')) + data.draftItemName = gr.getValue('cart_item_name'); + + var catItemData = {}; + catItemData.sys_id = gr.getValue('cat_item'); + catItemData.cart_item_id = gr.getUniqueValue(); + catItemData.table = "sc_cart_item"; + catItemData.is_ordering = true; + catItemData.from_guide = !!gr.getValue('order_guide'); + data.current_cart_item = cart_item_id; + data.sc_cat_item = $sp.getCatalogItem(catItemData); + var className = data.sc_cat_item.sys_class_name; + if (!new global.CatalogItemTypeProcessor().canCreateNormalCartItem(className)) { + if (className == 'sc_cat_item_producer' || className == 'std_change_record_producer') + athTblName = data.sc_cat_item.target_table_name; + } + data._attachmentTable = athTblName; + + data.sc_cat_item.isCartItem = true; + data.sc_cat_item.cart_guide = gr.getValue('order_guide'); + data.sc_cat_item.native_mobile = data.isMEE == 'true'; + data.hideDeliveryTime = data.sc_cat_item.no_delivery_time; + if (!data.hideDeliveryTime) + data.hideDeliveryTime = (options.hide_delivery_time == "true" || data.sc_cat_item.sys_class_name == 'sc_cat_item_producer' || data.sc_cat_item.sys_class_name == 'sc_cat_item_guide' || data.sc_cat_item.sys_class_name == 'std_change_record_producer'); + + if (data.is_draft_item && typeof sn_sc.CartJS.isSameVersion == "function" && !sn_sc.CartJS.isSameVersion(cart_item_id, gr.getValue('cat_item'))) { + var cartRecord = new sn_sc.CartJS("draft_items"); + cartRecord.remove(cart_item_id); + if (athTblName != 'sc_cart_item') + new global.GlobalServiceCatalogUtil().deleteAttachments(athTblName, cart_item_id); + + gs.getSession().putClientData('invalidVersionItem_' + data.sc_cat_item.sys_id, true); + data.redirectToItem = true; + return; + } + var values = getValues(cart_item_id); + for (var f in data.sc_cat_item._fields) { + // Put the values into the cat item fields + var field = data.sc_cat_item._fields[f]; + if (typeof values[f] != "undefined" && typeof values[f].value != "undefined") { + if (values[f].type == 9 || values[f].type == 10) + field.value = values[f].displayValue; + else if (values[f].type == 25) + field.value = values[f].decrypted_value; + else + field.value = values[f].value; + field.displayValue = values[f].displayValue; + field.display_value_list = values[f].display_value_list; + } + updatePriceOnField(field); + + } + + data._generatedItemGUID = cart_item_id; + data.quantity = '' + gr.quantity; + } else { + + if (localInput) + data.sys_id = localInput.sys_id; + else if (options.sys_id) + data.sys_id = options.sys_id; + else + data.sys_id = $sp.getParameter("sys_id") || $sp.getParameter('sl_sys_id'); + + if (!data.sys_id) { + data.recordFound = false; + return; + } + + data._generatedItemGUID = gs.generateGUID(); + + var validatedItem = new sn_sc.CatItem('' + data.sys_id); + if (!data.isPreview) { + if (!validatedItem.canView() || !validatedItem.isVisibleServicePortal()) { + data.recordFound = false; + return; + } + } + + data.sc_cat_item = $sp.getCatalogItem({ + sys_id: data.sys_id + '', + is_ordering: true + }); + + var className = data.sc_cat_item.sys_class_name; + if (!new global.CatalogItemTypeProcessor().canCreateNormalCartItem(className)) { + if (className == 'sc_cat_item_producer' || className == 'std_change_record_producer') + athTblName = data.sc_cat_item.target_table_name; + } + data._attachmentTable = athTblName; + + data.favoriteWidget = $sp.getWidget('ec_favorite', { + 'table': data.sc_cat_item.sys_class_name, + 'sys_id': data.sc_cat_item.sys_id + }); + + if (options.native_mobile == 'true') { + if (gs.getProperty('glide.sc.mobile.item_class_not_supported', '').split(',').indexOf(data.sc_cat_item.sys_class_name) > -1) { + data.not_for_mobile = true; + data.sc_cat_item = {}; + return; + } + if (gs.getProperty('glide.sc.mobile.include_desktop_only_items', 'true') == 'false') { + if (data.sc_cat_item.availability == 'on_desktop') { + data.not_for_mobile = true; + data.sc_cat_item = {}; + return; + } + } + } + + data.sc_cat_item.native_mobile = data.isMEE == 'true'; + data.hideDeliveryTime = data.sc_cat_item.no_delivery_time; + if (!data.hideDeliveryTime) + data.hideDeliveryTime = (options.hide_delivery_time == "true" || data.sc_cat_item.sys_class_name == 'sc_cat_item_producer' || data.sc_cat_item.sys_class_name == 'sc_cat_item_guide' || data.sc_cat_item.sys_class_name == 'std_change_record_producer'); + + if (data.sc_cat_item.dynamic) + updatePortalConfigOfDynamicContentItem(data.sc_cat_item.dynamic, data.sc_cat_item); + + + if (data.sc_cat_item.category) { + var categoryJS; + var categoryID = validatedItem.getFirstAccessibleCategoryForSearch((catalogID && catalogID != "-1") ? catalogID : $sp.getCatalogs().value + ""); + if (GlideStringUtil.isEligibleSysID($sp.getParameter("sysparm_category"))) { + categoryJS = new sn_sc.CatCategory($sp.getParameter("sysparm_category") + ""); + categoryID = $sp.getParameter("sysparm_category") + ""; + } else if (categoryID) { + categoryJS = new sn_sc.CatCategory(categoryID); + } + if (categoryJS && GlideStringUtil.isEligibleSysID(categoryJS.getID())) { + if (categoryJS.getCatalog()) { + catalogID = categoryJS.getCatalog(); + data.catalog_id = catalogID; + var catalogObj = new sn_sc.Catalog('' + catalogID); + data.sc_catalog = catalogObj.getTitle(); + data.showWishlist = catalogObj.isWishlistEnabled(); + } + data.category = { + name: categoryJS.getTitle(), + url: '?id=' + data.sc_category_page + '&sys_id=' + categoryID + } + data.categories = []; + data.categories.push({ + label: categoryJS.getTitle(), + url: '?id=' + data.sc_category_page + '&sys_id=' + categoryID + }); + while (categoryJS.getParent()) { + var parentId = categoryJS.getParent(); + categoryJS = new sn_sc.CatCategory(parentId); + var category = { + label: categoryJS.getTitle(), + url: '?id=' + data.sc_category_page + '&sys_id=' + parentId + }; + data.categories.unshift(category); + } + if ((($sp.getCatalogs().value + "").split(",")).length > 1) { + data.all_catalog_msg = gs.getMessage("All Catalogs"); + } + } + } + + } + + var wishlistGr = new GlideRecord('sc_cart_item'); + wishlistGr.addQuery('cart', new sn_sc.CartJS('saved_items').getCartID()); + wishlistGr.addQuery('cat_item', data.sc_cat_item.sys_id); + wishlistGr.query(); + if (wishlistGr.next() && !options.isServiceWorkspace) + data.wishlist_item_id = wishlistGr.getUniqueValue(); + + if (gs.nil(data.draftItemName)) + data.draftItemName = gs.getMessage("{0} draft", data.sc_cat_item.name); + + data.draft_buttons_hidden_via_property = (gs.getProperty('glide.sc.disable.save_as_draft') == 'true') || (gs.getProperty('glide.sc.enable.save_as_draft.portal.' + data.portal_suffix) != 'true'); + if (!!gs.getSession().getClientData('invalidVersionItem_' + data.sc_cat_item.sys_id)) { + gs.getSession().clearClientData('invalidVersionItem_' + data.sc_cat_item.sys_id); + data.isInvalidVersion = true; + } + + if (!data.draft_buttons_hidden_via_property && (options.hide_save_as_draft_button != "true") && !data.sc_cat_item.no_save_as_draft) { + var draftItemGr = new GlideRecord('sc_cart_item'); + draftItemGr.addQuery('cart', new sn_sc.CartJS('draft_items').getCartID()); + draftItemGr.addQuery('cat_item', data.sc_cat_item.sys_id); + draftItemGr.query(); + data.hasDraftItem = draftItemGr.hasNext(); + } + + data.sys_properties = { + twostep: gs.getProperty("glide.sc.sp.twostep", "true") == 'true' && !data.sc_cat_item.has_requested_for_variable, + mobileNativeColor: gs.getProperty("glide.sc.mobile.primary_color", "#1f8476"), + cartEnabled: gs.getProperty("glide.sc.cart.enabled", "false") == "true", + stopNavigationOnError: gs.getProperty("glide.sc.stop_navigation_on_error", "true") == "true", + isPolaris: gs.getProperty("glide.ui.polaris.experience", "false") + }; + + m.catItemOpenedMsg = gs.getMessage("Catalog item {0} opened", data.sc_cat_item.name); + + var className = data.sc_cat_item.sys_class_name; + data.can_create_cart_item = canCreateCartItem(className) && !gs.hasRole('snc_read_only'); + + function canCreateCartItem(className) { + var allowedClasses = ['sc_cat_item_producer', 'std_change_record_producer', 'sc_cat_item_producer_service']; + if (allowedClasses.indexOf(className) > -1) + return true; + + var invalidClasses = ('sc_cat_item_content,' + gs.getProperty('glide.sc.item.not_normal_cart_item', '')).split(','); + + return invalidClasses.indexOf(className) < 0; + } + + + data.stdChg = {}; + if (className === 'std_change_record_producer') { + //Populate scope with the porperty for two step + data.stdChg.twoStep = false; + var twoStepProp = new global.StdChangeUtils().getValue('two_step') + ''; + if (twoStepProp) + data.stdChg.twoStep = twoStepProp === '1'; + var stdChgProducerGr = new GlideRecord('std_change_record_producer'); + stdChgProducerGr.get(data.sc_cat_item.sys_id); + if (stdChgProducerGr.isValidRecord()) + data.stdChg.currentVersion = stdChgProducerGr.getValue('current_version'); + if (gs.getProperty('com.snc.change_management.change_model.type_compatibility', 'false') !== 'true') + data.stdChg.chgModel = new global.StdChangeUtils().DEFAULT_CHG_MODEL; + data.stdChg.defaultType = new global.StdChangeUtils().DEFAULT_CHG_TYPE; + } + + if (data.sc_cat_item.sys_class_name !== 'sc_cat_item_content') + $sp.logStat('Cat Item View', data.sc_cat_item.sys_class_name, data.sc_cat_item.sys_id, data.sc_cat_item.name, $sp.getPortalRecord().getUniqueValue()); + + function getValues(sys_id) { + var qs = new sn_sc.VariablePoolQuestionSetJS(); + qs.setCartID(sys_id); + qs.load(); + var values = {}; + var questions = qs.getFlatQuestions(); + for (var i = 0; i < questions.length; i++) { + var qKey = questions[i].name; + if (typeof qKey == 'undefined' || qKey == '') + qKey = "IO:" + questions[i].sys_id; + values[qKey] = questions[i]; + } + return values; + } + + function setPrice(field, p, rp) { + if (p != undefined) + field.price = p; + if (rp != undefined) + field.recurring_price = rp; + } + + function updatePriceForReferenceTable(field) { + var tableName = field.refTable + ''; + if (tableName != undefined && tableName != null && tableName != '') { + var gr = new GlideRecord(tableName); + if (gr.isValid()) { + if (gr.get(field.value) && gr.isValidRecord()) { + updatePrice(gr, field); + updateRecurringPrice(gr, field); + } + } + } + } + + function updatePriceForListCollector(field) { + var tableName = field.refTable + ''; + if (tableName != undefined && tableName != null && tableName != '') { + var gr = new GlideRecord(tableName); + if (gr.isValid()) { + var values = field.value.split(','); + gr.addQuery('sys_id', values); + gr.query(); + var p = 0.0; + var rp = 0.0; + var price_value_list = []; + while (gr.next()) { + var price_field = {}; + updatePrice(gr, price_field); + updateRecurringPrice(gr, price_field); + if (price_field.price) + p += Number(price_field.price); + else + price_field.price = 0.0; + if (price_field.recurring_price) + rp += Number(price_field.recurring_price); + else + price_field.recurring_price = 0.0; + price_value_list.push(price_field); + } + field.price = p; + field.recurring_price = rp; + field.price_value_list = price_value_list; + } + } + } + + function updatePrice(gr, field) { + if (gr.isValidField('price')) + field.price = gr.getValue('price'); + else if (gr.isValidField('u_price')) + field.price = gr.getValue('u_price'); + } + + function updateRecurringPrice(gr, field) { + if (gr.isValidField('recurring_price')) + field.recurring_price = gr.getValue('recurring_price'); + else if (gr.isValidField('u_recurring_price')) + field.recurring_price = gr.getValue('u_recurring_price'); + } + + function updatePriceOnField(field) { + if (field.type == 'boolean' || field.type == 'boolean_confirm') { + if (field.value == 'true' || field.value == true) + setPrice(field, field._pricing.price_if_checked, field._pricing.recurring_price_if_checked); + else + setPrice(field, 0, 0); + } else if (field.choices) { + var valueExistInChoices = false; + field.choices.forEach(function(choice) { + if (choice.value + '' == field.value + '') { + setPrice(field, choice.price, choice.recurring_price); + valueExistInChoices = true; + } + }); + if (!valueExistInChoices) + setPrice(field, 0, 0); + } else if (field._pricing && field._pricing.pricing_implications === true) { + if (field.type == 'reference') + updatePriceForReferenceTable(field); + else if (field.type == 'glide_list') + updatePriceForListCollector(field); + } + } + + function updatePortalConfigOfDynamicContentItem(dynamicContents, item) { + var portalUrl = new global.DynamicCatalogContentUtil().getServicePortalUrl(dynamicContents.model, dynamicContents.content); + if (portalUrl) + item.portalUrl = portalUrl; + } +})() \ No newline at end of file diff --git a/Service Portal Widgets/SC Catalog Item/html b/Service Portal Widgets/SC Catalog Item/html new file mode 100644 index 0000000000..995cd9521d --- /dev/null +++ b/Service Portal Widgets/SC Catalog Item/html @@ -0,0 +1,316 @@ +
+ + +
+
+ +
+
+
+ {{::m.itemWishlistMsg}} +
+
+ + {{::m.deletedOutdatedItemMsg}} + + +
+ +
+
+
+ +
+
+ + + +

{{::data.sc_cat_item.name}}

+
{{::data.sc_cat_item.short_description}}
+
+ + + ${Favorited} + ${Favorite} + +
+
+
+
+
+ + {{c.isNative ? data.sc_cat_item.name : ''}} + + +
+
+
+
+
+
+
+ +
+
+
+
+
+ +
+ + ${asterisk} + + ${Indicates required} +
+
+ + +
+
+ + +
+
+
+ +
+ +
+
+
+ ${Price} +
+ {{data.sc_cat_item.price_display}} {{data.sc_cat_item.price ? '+' : ''}} {{data.sc_cat_item.recurring_price_display + ' ' + data.sc_cat_item.recurring_price_frequency}} +
+
+
+ ${Delivery Time} +
+ {{::data.sc_cat_item.estimated_delivery_time}} +
+
+
+ + + + + + + + + + + + ${Submitting...} + ${Validating...} +
+
+
+ +
+
+
+ ${Required information} + +
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+
+ + ${Price} {{data.sc_cat_item.price_display}} + + ${Recurring Price} {{data.sc_cat_item.recurring_price_display}} {{data.sc_cat_item.recurring_price_frequency}} +
+
+ ${Delivery Time}: {{::data.sc_cat_item.estimated_delivery_time}} +
+
+
+
+ ${Price}: {{data.sc_cat_item.price_display}} {{data.sc_cat_item.price ? '+' : ''}} {{data.sc_cat_item.recurring_price_display + ' ' + data.sc_cat_item.recurring_price_frequency}} +
+
+ ${Delivery Time}: {{::data.sc_cat_item.estimated_delivery_time}} +
+ + +
+
+ + +
+
+ + +
+
+ + ${Submitting...} + ${Validating...} +
+
+ + +
+ + ${Submitting...} +
+
+
+
+
+
+
+ +
+
+ ${Required information} + +
+
+
+
+
+
+
+
+
+
+
+

${Item not found}

+
+

${This item is not found or currently not available}

+

${Suggestions}:

+
    +
  • ${Try searching for the item}
  • +
  • ${Go to the Service Catalog homepage}
  • +
+
+
+
+
{{::c.status}}
+
+
{{::m.invalidRecordMsg}}
+
+
+ ${We're sorry, this item isn't available on mobile} +
+
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/autocomplete/GlideQuery.js b/autocomplete/GlideQuery.js new file mode 100644 index 0000000000..ce78a465d9 --- /dev/null +++ b/autocomplete/GlideQuery.js @@ -0,0 +1,886 @@ +/** + * Main GlideQuery class used to build and execute queries. + * @example + * var query = new GlideQuery('sys_user'); + * @constructor + * @param {string} table Table to query + * @param {Array} [plan] Contains an array of Step objects describing the + * query. Generally only used by GlideQuery itself. + */ +function GlideQuery(table, plan) { + Object.defineProperties( + this, + { + table: { value: table }, + plan: { value: plan || [] } + } + ); +} + +GlideQuery.prototype.table = null; +GlideQuery.prototype.plan = null; +GlideQuery.prototype.type = 'GlideQuery'; + +/** + * Returns a new GlideQuery containing a where clause. Cannot be + * preceded by an orWhere, orWhereNull, or orWhereNotNull expression, + * to avoid ambiguity. + * @example + * new GlideQuery('sys_user') + * .where('active', true) + * .where('last_login', '>', '2016-04-15') + * + * // active = true AND (priority = 1 OR severity = 1) + * new GlideQuery('incident') + * .where('active', true) + * .where(new GlideQuery() + * .where('priority', 1) + * .orWhere('severity', 1)) + * @param {string | GlideQuery} fieldOrQuery field (or another GlideQuery) related to the where clause + * @param {string} [operator] Operator used in where clause. Is considered '=' when only two arguments given. + * @param {any} value Value used in where clause + * @returns {GlideQuery} New GlideQuery containing where clause + */ +GlideQuery.prototype.where = function where(field, operator, value) { + var query; + if (field instanceof GlideQuery) { + query = new GlideQuery(this.table, this.plan.concat({ + type: 'where', + query: field, + whereClause: true, + action: GlideQueryActions.whereNestedQuery(this.table, field), + })); + } else { + var op = value === undefined ? '=' : operator; + var val = value === undefined ? operator : value; + + GlideQuery.checkWhereOperator(op, val); + + query = new GlideQuery(this.table, this.plan.concat({ + type: 'where', + field: field, + operator: op, + value: val, + whereClause: true, + action: GlideQueryActions.where(this.table, field, op, val), + })); + } + + return GlideQuery.checkWhereAmbiguity(query); +}; + +/** + * Returns a new GlideQuery containing an orWhere clause. Must be + * preceded by a single where, whereNull, or whereNotNull expression. However + * it cannot be followed by a where, whereNull, or whereNotNull expression + * to avoid ambiguity. + * @example + * new GlideQuery('sys_user') + * .where('failed_attempts', '>', 0) + * .orWhere('last_login', '<', '2019-04-15') + * .select() + * + * // active = true OR (title = 'Vice President' AND state = 'CA') + * new GlideQuery('sys_user') + * .where('active', true) + * .orWhere(new GlideQuery() + * .where('title', 'Vice President') + * .where('state', 'CA')) + * .select('name') + * @param {string | GlideQuery} fieldOrQuery field (or another GlideQuery) related to the where clause + * @param {string} [operator] Operator used in where clause. Is considered '=' when only two arguments given. + * @param {any} value Value used in where clause + * @returns {GlideQuery} New GlideQuery containing where clause + */ +GlideQuery.prototype.orWhere = function orWhere(field, operator, value) { + var query; + if (field instanceof GlideQuery) { + query = new GlideQuery(this.table, this.plan.concat({ + type: 'orWhere', + query: field, + whereClause: true, + action: GlideQueryActions.whereNestedQuery(this.table, field), + })); + } else { + var op = value === undefined ? '=' : operator; + var val = value === undefined ? operator : value; + + GlideQuery.checkWhereOperator(op, val); + + query = new GlideQuery(this.table, this.plan.concat({ + type: 'orWhere', + field: field, + operator: op, + value: val, + whereClause: true, + action: GlideQueryActions.where(this.table, field, op, val), + })); + } + + return GlideQuery.checkWhereAmbiguity(query); +}; + +/** + * Returns a new GlideQuery containing NOT NULL clause. Cannot be + * preceded by an orWhere, orWhereNull, or orWhereNotNull expression. + * @example + * new GlideQuery('sys_user') + * .whereNotNull('first_name') + * @param {string} field Field related to the clause + * @returns {GlideQuery} New GlideQuery containing NOT NULL clause + */ +GlideQuery.prototype.whereNotNull = function whereNotNull(field) { + return GlideQuery.checkWhereAmbiguity( + new GlideQuery( + this.table, + this.plan.concat({ + type: 'whereNotNull', + field: field, + whereClause: true, + action: GlideQueryActions.where(this.table, field), + }) + ) + ); +}; + +/** + * Returns a new GlideQuery containing NOT NULL clause. Must be + * preceded by a single where, whereNull, or whereNotNull expression. + * @example + * new GlideQuery('sys_user') + * .whereNotNull('first_name') + * .orWhereNotNull('last_name') + * @param {string} field Field related to the clause + * @returns {GlideQuery} New GlideQuery containing NOT NULL clause + */ +GlideQuery.prototype.orWhereNotNull = function orWhereNotNull(field) { + return GlideQuery.checkWhereAmbiguity( + new GlideQuery( + this.table, + this.plan.concat({ + type: 'orWhereNotNull', + field: field, + whereClause: true, + action: GlideQueryActions.where(this.table, field), + }) + ) + ); +}; + +/** + * Returns a new GlideQuery containing WHERE NULL clause. Cannot be + * preceded by an orWhere, orWhereNull, or orWhereNotNull expression. + * @example + * new GlideQuery('sys_user') + * .whereNull('last_name') + * @param {string} field Field related to the clause + * @returns {GlideQuery} New GlideQuery containing NULL clause + */ +GlideQuery.prototype.whereNull = function whereNull(field) { + return GlideQuery.checkWhereAmbiguity( + new GlideQuery( + this.table, + this.plan.concat({ + type: 'whereNull', + field: field, + whereClause: true, + action: GlideQueryActions.where(this.table, field), + }) + ) + ); +}; + +/** + * Returns a new GlideQuery containing WHERE NULL clause. Must be + * preceded by a single where, whereNull, or whereNotNull expression. + * @example + * new GlideQuery('sys_user') + * .whereNull('last_name') + * .orWhereNull('first_name') + * @param {string} field Field related to the clause + * @returns {GlideQuery} New GlideQuery containing NULL clause + */ +GlideQuery.prototype.orWhereNull = function orWhereNull(field) { + return GlideQuery.checkWhereAmbiguity( + new GlideQuery( + this.table, + this.plan.concat({ + type: 'orWhereNull', + field: field, + whereClause: true, + action: GlideQueryActions.where(this.table, field), + }) + ) + ); +}; + +/** + * Returns a single record, using `keyValues` as a set of key-values to query by. + * getBy assumes the '=' operator for each key-value. Returns + * @example + * var user = new GlideQuery('sys_user') + * .getBy({ + * first_name: 'Fred', + * last_name: 'Luddy' + * }, ['first_name', 'last_name', 'city', 'active']) // select first_name, last_name, city, active + * .orElse({ + * first_name: 'Nobody', + * last_name: 'Found', + * city: 'Nowhere', + * active: false + * }); + * @param {Object} keyValues Object where the keys are the name of the fields, and the values are + * @param {Array} [selectedFields] Additional fields to return in result + * @returns {Optional} Optional containing result of query + */ +GlideQuery.prototype.getBy = function getBy(keyValues, selectedFields) { + var queryFields = Object.keys(keyValues); + GlideQueryEvaluator.checkFieldsHaveNoFlag(queryFields, 'getBy'); + var table = this.table; + + var whereSteps = queryFields.map(function (field) { + return { + type: 'where', + field: field, + operator: '=', + value: keyValues[field], + whereClause: true, + action: GlideQueryActions.where(table, field, '=', keyValues[field]), + }; + }); + + return GlideQueryEvaluator.selectOne( + new GlideQuery(this.table, this.plan.concat(whereSteps)), + queryFields.concat(selectedFields || []) + ); +}; + +/** + * Returns a single record by querying primary key `key`. + * @example + * var user = new GlideQuery('sys_user') + * .get('5137153cc611227c000bbd1bd8cd2005', ['first_name', 'last_name']) + * .orElse({ first_name: 'Default', last_name: 'User' }); + * @param {string} key Object where the keys are the name of the fields, and the values are + * @param {Array} [selectedFields] Additional fields to return in result + * @returns {Optional} Optional containing result of query + */ +GlideQuery.prototype.get = function get(key, schema, selectFields) { + return GlideQueryEvaluator.get(this, key, schema, selectFields); +}; + +/** + * Inserts a single record, returning an Optional of the newly-created record + * @example + * var fred = new GlideQuery('sys_user') + * .insert({ first_name: 'Fred', last_name: 'Luddy' }) + * .get(); + * @param {Object} keyValues Object containing key-values to insert into table + * @param {Array} [selectedFields] Fields to return in result Optional + * @throws {Error} When insert fails (e.g. when a business rule rejects the insert) + * @returns {Optional} Optional containing result of query + */ +GlideQuery.prototype.insert = function insert(keyValues, selectFields) { + return GlideQueryEvaluator.insert(this, keyValues, selectFields || []); +}; + +/** + * Updates an existing record (just like update), however instead of requiring + * where calls, it uses the primary key(s) in the recordValues object passed + * in. If the primary key(s) isn't there, insertOrUpdate will insert a new + * record instead. Returns an Optional of the newly created/updated record. + * Often useful when you want to want to ensure a record exists and has the + * correct values, as you don't need to check for the record's existence beforehand. + * @example + * // insert a new record + * var user = new GlideQuery('sys_user') + * .insertOrUpdate({ + * first_name: 'George', + * last_name: 'Griffey' + * }) + * .orElse(null); + * + * // update existing record + * var user = new GlideQuery('sys_user') + * .insertOrUpdate({ + * sys_id: '2d0efd6c73662300bb513198caf6a72e', + * first_name: 'George', + * last_name: 'Griffey' }) + * .orElse(null); + * @param {Object} changes Object containing key-values to update/insert into table + * @param {Array} [selectedFields] Fields to return in result Optional + * @throws {Error} When insert fails (e.g. when a business rule rejects the insert) + * @returns {Optional} Optional containing result of query + */ +GlideQuery.prototype.insertOrUpdate = function insertOrUpdate(changes, selectFields, reason) { + return GlideQueryEvaluator.insertOrUpdate(this, changes, selectFields || [], reason); +}; + +/** + * Updates an existing record. Requires a where call, specifying + * all existing primary keys (usually sys_id). Returns an Optional + * of the newly-updated record. Passes in a reason string (just + * like GlideRecord's update). + * @example + * new GlideQuery('sys_user') + * .where('sys_id', userId) + * .update({ city: 'Los Angeles' }); + * @param {Object} changes Object containing key-values to update/insert into table + * @param {Array} [selectedFields] Fields to return in result Optional + * @throws {Error} When insert fails (e.g. when a business rule rejects the insert) + * @returns {Optional} Optional containing result of query + */ +GlideQuery.prototype.update = function update( + changes, selectFields, reason, prefetchedSchema, planOverride, insertWhenNotFound +) { + return GlideQueryEvaluator.update( + this, + changes || {}, + selectFields || [], + reason, + prefetchedSchema, + planOverride, + insertWhenNotFound + ); +}; + +/** + * Updates all records in the table (specified by preceding where clauses) + * with the values contained in the changes object. Returns # of records + * updated. + * @example + * new GlideQuery('sys_user') + * .where('active', false) + * .where('last_name', 'Griffey') + * .updateMultiple({ active: true }); + * @param {Object} changes Object containing key-values to update/insert into table + * @returns {Object} Object with field rowCount + */ +GlideQuery.prototype.updateMultiple = function updateMultiple(changes) { + return GlideQueryEvaluator.updateMultiple(this, changes); +}; + +GlideQuery.prototype.del = function del() { + GlideQueryEvaluator.del(this); +}; + +/** + * Deletes all records in the table specified by preceding where clauses. + * @example + * new GlideQuery('sys_user') + * .where('active', true) + * .where('last_name', 'Jeter') + * .deleteMultiple(); + * @returns {nothing} + */ +GlideQuery.prototype.deleteMultiple = GlideQuery.prototype.del; + +/** + * Specifies which fields to return and returns a Stream containing the + * results of the query. Note that records aren't actually read from the + * database until a terminal Stream method is called (such as reduce() or + * toArray()). The Stream is intended for reading multiple records in + * a similar fashion to Java's Stream class. + * + * You can append a flag to a field name when metadata about the field is + * needed, instead of the value itself. For example using the field name + * "company$DISPLAY" will return the display value of a company field. + * Existing flags are: + * * DISPLAY - returns the display value of a field + * * CURRENCY_CODE - Returns the currency code (e.g. "USD") of a currency field + * * CURRENCY_DISPLAY - Returns the currency display value (e.g. "Â¥123.45") of a currency field + * * CURRENCY_STRING - Returns the currency string (e.g. "JPY;123.45") of a currency field + * @example + * var stream = new GlideQuery('sys_user') + * .select('first_name', 'last_name', 'company$DISPLAY'); + * @param {...string} fields Fields to select + * @returns {Stream} Stream containing results of query + */ +GlideQuery.prototype.select = function select(fields) { + return GlideQueryEvaluator.createStream(this, GlideQuery.flattenFields(fields, arguments)); +}; + +/** + * Similar to [select()]{@link GlideQuery#select}, however only returns an Optional + * which may contain a single record. This is more efficient + * than select() if you only need one record, or want to + * test if a record exists. + * @example + * var user = new GlideQuery('sys_user') + * .where('zip', '12345') + * .whereNotNull('last_name') + * .selectOne('first_name', 'last_name', 'company$DISPLAY') + * .get(); + * @param {...string} fields Fields to select + * @returns {Optional} Optional containing result of query + */ +GlideQuery.prototype.selectOne = function selectOne(fields) { + return GlideQueryEvaluator.selectOne(this, GlideQuery.flattenFields(fields, arguments)); +}; + +/** + * Returns a GlideQuery which disables the running of business + * rules, script engines, and audit. + * @example + * var query = new GlideQuery('task') + * .disableWorkflow() + * .where('active', true) + * .updateMultiple({ priority: 1 }); + * @returns {GlideQuery} New GlideQuery which disables business rules + */ +GlideQuery.prototype.disableWorkflow = function disableWorkflow() { + return new GlideQuery(this.table, this.plan.concat({ + type: 'disableWorkflow', + action: GlideQueryActions.disableWorkflow, + })); +}; + +/** + * Returns a GlideQuery which does not update sys fields such as + * sys_created_on, sys_updated_on, and sys_mod_count. This is the + * equivalent of using autoSysFields(false) with GlideRecord. + * @example + * new GlideQuery('task') + * .disableAutoSysFields() + * .insert({ description: 'example', priority: 1 }); + * @returns {GlideQuery} + */ +GlideQuery.prototype.disableAutoSysFields = function disableAutoSysFields() { + return new GlideQuery(this.table, this.plan.concat({ + type: 'disableAutoSysFields', + action: GlideQueryActions.disableAutoSysFields, + })); +}; + +/** + * Returns a GlideQuery which forces an update even when no + * changes are made. Useful when you want to force a business + * rule to execute. + * @example + * new GlideQuery('task') + * .forceUpdate() + * .where('sys_id', taskId) + * .update() + * @returns {GlideQuery} + */ +GlideQuery.prototype.forceUpdate = function forceUpdate() { + return new GlideQuery(this.table, this.plan.concat({ + type: 'forceUpdate', + action: GlideQueryActions.forceUpdate, + })); +}; + +/** + * Returns a GlideQuery which specifies that the records should + * be returned in ascending order by a given field. + * @example + * var query = new GlideQuery('incident') + * .orderBy('number'); + * @param {string} field Fields to order by (ascending) + * @returns {GlideQuery} New GlideQuery which contains orderBy + */ +GlideQuery.prototype.orderBy = function orderBy(field) { + return new GlideQuery( + this.table, + this.plan.concat({ + type: 'orderBy', + field: field, + action: GlideQueryActions.orderBy(this.table, field), + }) + ); +}; + +/** + * Returns a GlideQuery which specifies that the records should + * be returned in descending order by a given field. Can be used + * with aggregate queries + * @example + * var query = new GlideQuery('incident') + * .orderByDesc('number'); + * + * new GlideQuery('incident') + * .aggregate('sum', 'child_incidents') + * .groupBy('category') + * .orderByDesc('sum', 'child_incidents') + * + * @param {string} fieldOrAggregate Field to order by with non-aggregate queries or + aggregate type if used with aggregate queries + * @param {string} [field] Field to order by (only used with aggregate queries) + * @returns {GlideQuery} New GlideQuery which contains orderByDesc + */ +GlideQuery.prototype.orderByDesc = function orderByDesc(fieldOrAggregate, field) { + return new GlideQuery( + this.table, + this.plan.concat({ + type: 'orderByDesc', + field: field || fieldOrAggregate, + action: GlideQueryActions.orderByDesc(this.table, fieldOrAggregate, field), + }) + ); +}; + +/** + * Returns a GlideQuery which limits the number of records returned. + * @example + * var incidents = new GlideQuery('incident') + * .limit(20) + * .select('priority', 'description'); + * @param {number} limit Max number of records to return + * @returns {GlideQuery} New GlideQuery which contains limit + */ +GlideQuery.prototype.limit = function limit(limit) { + return new GlideQuery( + this.table, + this.plan.concat({ + type: 'limit', + value: limit, + action: GlideQueryActions.limit(limit), + }) + ); +}; + +/** + * By default GlideQuery uses GlideRecord for database interactions. + * By calling withAcls() GlideQuery will use GlideRecordSecure, which + * honors ACLs. + * @example + * var users = new GlideQuery('sys_user') + * .withAcls() + * .limit(20) + * .orderByDesc('first_name') + * .select('first_name') + * .toArray(100); + * @returns {GlideQuery} New GlideQuery which uses GlideRecordSecure + */ +GlideQuery.prototype.withAcls = function withAcls() { + return new GlideQuery( + this.table, + this.plan.concat({ + type: 'withAcls', + }) + ); +}; + +/** + * Returns the aggregate average of a given numeric field. Can be + * used on fields of type: + * * integer + * * longint + * * float + * * double + * * currency + * @example + * var faults = new GlideQuery('cmdb_ci') + * .avg('fault_count') + * .orElse(0); + * @param {string} field Numeric field + * @throws {Error} When invalid field given + * @returns {Optional} Optional of results of aggregate, or empty if no records found + */ +GlideQuery.prototype.avg = function avg(field) { + return GlideQueryEvaluator.callSimpleAggregate(this, 'avg', field); +}; + +/** + * Returns the aggregate maximum of a given field. + * @example + * var name = new GlideQuery('sys_user') + * .max('first_name') + * .orElse(''); + * @param {string} field + * @throws {Error} When invalid field given + * @returns {Optional} Optional of results of aggregate, or empty if no records found + */ +GlideQuery.prototype.max = function max(field) { + return GlideQueryEvaluator.callSimpleAggregate(this, 'max', field); +}; + +/** + * Returns the aggregate minimum of a given field. + * @example + * var lowestModCount = new GlideQuery('sys_user') + * .min('sys_mod_count') + * .orElse(0); + * @param {string} field + * @throws {Error} When invalid field given + * @returns {Optional} Optional of results of aggregate, or empty if no records found + */ +GlideQuery.prototype.min = function min(field) { + return GlideQueryEvaluator.callSimpleAggregate(this, 'min', field); +}; + +/** + * Returns the aggregate sum of a given numeric field. Can be + * used on fields of type: + * * integer + * * longint + * * float + * * double + * * currency + * @example + * var totalFaults = new GlideQuery('cmdb_ci') + * .sum('fault_count') + * .orElse(0); + * @param {string} field Numeric field + * @throws {Error} When invalid field given + * @returns {Optional} Optional of results of aggregate, or empty if no records found + */ +GlideQuery.prototype.sum = function sum(field) { + return GlideQueryEvaluator.callSimpleAggregate(this, 'sum', field); +}; + +/** + * Returns the row count of records matching the query + * @example + * var userCount = new GlideQuery('sys_user') + * .where('active', true) + * .count(); + * @returns {number} + */ +GlideQuery.prototype.count = function count() { + return GlideQueryEvaluator.callSimpleAggregate(this, 'count'); +}; + +/** + * Groups query results. Used with `aggregate()` + * @example + * new GlideQuery('task') + * .aggregate('count') + * .groupBy('contact_type') + * .select() + * @param {...string} fields Fields to group by + * @returns {GlideQuery} GlideQuery which groups results + */ +GlideQuery.prototype.groupBy = function groupBy(fields) { + if (!fields) { + NiceError.raise('groupBy expects a field name'); + } + + var table = this.table; + var fieldArray = GlideQuery.flattenFields(fields, arguments); + + return new GlideQuery( + this.table, + this.plan.concat(fieldArray.map(function (f) { + return { + type: 'groupBy', + field: f, + action: GlideQueryActions.groupBy(table, f), + }; + })) + ); +}; + +/** + * Aggregates a field using an aggregate function. Used to build + * queries which aggregate against multiple fields and/or multiple + * aggregate functions. If you only need to aggregate against one + * field with one function, and you don't need to use groupBy(), then + * use one of the terminal functions instead: + * * avg() + * * min() + * * max() + * * count() + * @example + * new GlideQuery('task') + * .aggregate('avg', 'reassignment_count') + * .groupBy('contact_type') + * .select() + * @param {string} aggregateType Aggregate type ('sum', 'avg', 'min', 'max', or 'count') + * @param {string} field Field to aggregate + * @returns {GlideQuery} GlideQuery which aggregates by a field + */ +GlideQuery.prototype.aggregate = function aggregate(aggregateType, field) { + var type = aggregateType.toLowerCase(); + + if (!GlideQueryEvaluator.aggregateQueries[type]) { + NiceError.raise('Invalid aggregate type: ' + type); + } + + return new GlideQuery( + this.table, + this.plan.concat({ + type: type, + field: field, + aggregate: true, + action: GlideQueryActions.aggregate(this.table, field, aggregateType), + }) + ); +}; + +/** + * Filters aggregate groups. Used with `aggregate()` and `groupBy`. + * @example + * new GlideQuery('task') + * .where('description', description) + * .groupBy('priority') + * .aggregate('sum', 'reassignment_count') + * .having('sum', 'reassignment_count', '>', 4) + * .select() + * @param {string} aggregateType Aggregate type ('sum', 'avg', 'min', 'max', or 'count') + * @param {string} field Field to aggregate + * @param {string} operator Only numeric operators allowed: '>', '<', '>=', '<=', '=', and '!=' + * @param {number} value + * @returns {GlideQuery} GlideQuery containing HAVING clause + */ +GlideQuery.prototype.having = function having(aggregate, field, operator, value) { + GlideQuery.checkHavingOperatorAndValue(operator, value); + var aggregateType = aggregate.toLowerCase(); + + return new GlideQuery( + this.table, + this.plan.concat({ + type: 'having', + aggregateType: aggregateType, + field: field, + operator: operator, + value: value, + action: GlideQueryActions.having(this.table, field, operator, value, aggregateType), + }) + ); +}; + +/** + * Returns a GlideRecord representing the current GlideQuery. The + * GlideRecord has not yet been queried, and so query() may need to + * be called before using the GlideReord. The GlideRecord may + * be a GlideAggregate in case aggregate queries are used. + * @example + * var userGr = new GlideQuery('sys_user') + * .where('active', true) + * .whereNotNull('first_name') + * .limit(10) + * .toGlideRecord(); + * userGr.query(); + * while (userGr.next()) { + * doSomething(userGr); + * } + * @returns {GlideRecord} + */ +GlideQuery.prototype.toGlideRecord = function toGlideRecord() { + var gr = GlideQueryEvaluator.createGlideRecord(this); + var schema = GlideQueryEvaluator.loadSchemaForTable(this, []); + GlideQueryEvaluator.executePlan(this, gr, schema); + return gr; +}; + +/** + * Parses an encoded query. + * @example + * GlideQuery.parse('task', 'active=true^descriptionISNOTEMPTY') + * .select('description') + * .forEach(function (task) { gs.info(task.description); }); + * @param {string} table Table to query against + * @param {string} encodedQuery Encoded query to parse + * @returns {GlideRecord} + */ +GlideQuery.parse = function (table, encodedQuery) { + return GlideQueryParser.parse(table, encodedQuery); +}; + +GlideQuery.prototype.toString = function toString() { + return 'GlideQuery<' + this.table + '> ' + JSON.stringify(this.plan, null, 2); +}; + +GlideQuery.operators = { + '=': 'comparable', + '!=': 'comparable', + '>': 'comparable', + '>=': 'comparable', + '<': 'comparable', + '<=': 'comparable', + IN: 'array', + 'NOT IN': 'array', + STARTSWITH: 'string', + ENDSWITH: 'string', + CONTAINS: 'string', + 'DOES NOT CONTAIN': 'string', + INSTANCEOF: 'string', + SAMEAS: 'string', + NSAMEAS: 'string', + GT_FIELD: 'string', + LT_FIELD: 'string', + GT_OR_EQUALS_FIELD: 'string', + LT_OR_EQUALS_FIELD: 'string', + BETWEEN: 'array', + DYNAMIC: 'string', + EMPTYSTRING: 'string', + ANYTHING: 'string', + LIKE: 'string', + 'NOT LIKE': 'string', + ON: 'string', +}; + +GlideQuery.checkHavingOperatorAndValue = function checkHavingOperatorAndValue(operator, value) { + if (!JSUtil.instance_of(value, 'java.lang.Double') || !isFinite(value)) { + NiceError.raise('Numeric value expected for having() value. Found ' + value); + } + if (GlideQuery.operators[operator] !== 'comparable') { + NiceError.raise("Operator '" + operator + "' is not supported by having"); + } +}; + +GlideQuery.checkWhereOperator = function checkWhereOperator(operator, value) { + if (!GlideQuery.operators[operator]) { + NiceError.raise("Operator '" + operator + "' is not supported by where"); + } + + if (GlideQuery.operators[operator] === 'array' && !Array.isArray(value)) { + NiceError.raise("Operator '" + operator + "' can only be used on array values"); + } + + if (operator === 'BETWEEN' && value.length !== 2) { + NiceError.raise('BETWEEN requires array with two values, but was given one with ' + value.length + ' value(s)'); + } + + if (Array.isArray(value) && GlideQuery.operators[operator] !== 'array') { + NiceError.raise("Array values can only be used with 'NOT IN' or 'IN' operators"); + } + + if (GlideQuery.operators[operator] === 'string' && !JSUtil.instance_of(value, 'java.lang.String')) { + NiceError.raise("Operator '" + operator + "' can only be used on string values"); + } +}; + +GlideQuery.flattenFields = function flattenFields(firstArg, args) { + if (Array.isArray(firstArg)) { + return firstArg; + } + + var flattenedFields = []; + for (var i in args) { + flattenedFields.push(args[i]); + } + + return flattenedFields; +}; + +GlideQuery.checkWhereAmbiguity = function checkWhereAmbiguity(glideQuery) { + var whereFound = 0; + var orWhereFound = 0; + glideQuery.plan + .filter(function (step) { return step.whereClause; }) + .forEach(function (step) { + if (step.type.startsWith('where')) { + whereFound += 1; + } else if (step.type.startsWith('or')) { + orWhereFound += 1; + } + + if (orWhereFound && !whereFound) { + NiceError.raise(step.type + ' must be preceeded by where/whereNull/whereNotNull expression'); + } + + if (step.query && step.query.plan.some(function (s) { return s.query; })) { + NiceError.raise('Cannot nest queries 3 or more levels'); + } + }); + + if (whereFound > 1 && orWhereFound) { + NiceError.raise( + 'Ambiguous query: cannot contain multiple where***() expressions with an orWhere***() expression' + ); + } + + return glideQuery; +}; \ No newline at end of file diff --git a/autocomplete/client.d.ts b/autocomplete/client.d.ts new file mode 100644 index 0000000000..cc3d54bf85 --- /dev/null +++ b/autocomplete/client.d.ts @@ -0,0 +1,344 @@ +/** Interact with Service Portal utility functions. */ +class spUtil_proto{ +/** Add an info message. */ +addInfoMessage(message: string) {}; + +/** Call widget on server with data. */ +get(scope: ?, data: ?) {}; + +/** Update data in the scope by getting it from the server. */ +update(scope: ?) {}; + +} + +var spUtil = new spUtil_proto(); +/** Navigation API. Note: The code in this file is compatible with API Level 1 and API Level 2 */ +class g_navigation_proto{ +/** Redirects to a record */ +openRecord(table: string, sys_id: string) {}; + +/** Reload the current frame */ +reloadWindow() {}; + +/** Open a popup window with features \nurl: The url to open \nname: The name of the new window \nfeatures: is a comma separated list of features. See https://developer.mozilla.org/en-US/docs/Web/API/Window/open \nnoStack: True to append sysparm_stack=no to the url. This prevents weirdness when using the form back button \nreturns the instance of newly opened Window */ +openPopup(url: string, name: string, features: string, noStack: bool) : ? {}; + +/** Refresh the navigator contents */ +refreshNavigator() {}; + +/** Redirects to another URL. \nurl: URL to be loaded. It can be any URL supported by the browser \ntarget: is the target frame. If left blank, the URL will load in the current frame */ +open(url: string, target: string) {}; + +} + +var g_navigation = new g_navigation_proto(); +/** g_form is a global object used in client-side scripts to customize forms */ +class g_form_proto{ +/** Shows or hides a section Works in both tab and flat modes. This method is available starting with the Fuji release */ +setSectionDisplay(sectionName: string, display: bool) : bool {}; + +/** Removes all options from a choice list */ +clearOptions(fieldName: string) {}; + +/** Returns the