├── 01_WebTech ├── 01_test.html ├── 02_CSS.html ├── 03_testJS.html └── js │ ├── jQuerySample.js │ └── sample.js ├── 05_XSClassic ├── 01_GetBP_html.xsjs ├── 02_GetBP_json.xsjs ├── 03_GetBP_json_params.xsjs ├── 04_CreateProcedure.sql ├── 04_GetBPListProc.xsjs ├── 05_GetData_xsjslib.xsjs ├── 05_sampleLib.xsjslib ├── 06_b1.xsodata └── model │ └── 06_SAP_CA_SALES.calculationview ├── 07_B1XappF ├── 01_xappLogin.html ├── Mashup.html ├── index.html └── js │ └── b1xApp.js ├── 08_ServiceLayer ├── 08_SL_Ex_04_SCRIPTENG_SOL │ ├── MyFirstScriptEngineExtension.ard │ ├── MyOrderAndInvoiceEntity_Script.js │ └── MyOrderEntity_Script.js └── XSJS │ ├── B1SL.xshttpdest │ ├── B1SLLogic.xsjslib │ ├── getOrder.xsjs │ └── runBatch.xsjs ├── 09_SAPUI5 ├── neo-app.json └── webapp │ ├── Component.js │ ├── controller │ ├── main.controller.js │ ├── odata.controller.js │ ├── serviceLayer.controller.js │ └── xsjs.controller.js │ ├── css │ └── style.css │ ├── i18n │ └── i18n.properties │ ├── index.html │ ├── manifest.json │ ├── model │ └── models.js │ └── view │ ├── main.view.js │ ├── odata.view.js │ ├── serviceLayer.view.js │ └── xsjs.view.js ├── 12_Fiori ├── BPExplorerApp │ ├── .project.json │ ├── .user.project.json │ ├── .xsaccess │ ├── .xsapp │ ├── OData │ │ └── BusinessPartner.xsodata │ ├── index.html │ ├── neo-app.json │ └── webapp │ │ ├── Component.js │ │ ├── controller │ │ ├── App.controller.js │ │ ├── BaseController.js │ │ ├── Detail.controller.js │ │ ├── ErrorHandler.js │ │ ├── ListSelector.js │ │ └── Master.controller.js │ │ ├── i18n │ │ └── i18n.properties │ │ ├── localService │ │ ├── metadata.xml │ │ └── mockserver.js │ │ ├── manifest.json │ │ ├── model │ │ ├── GroupSortState.js │ │ ├── formatter.js │ │ ├── grouper.js │ │ └── models.js │ │ ├── test.html │ │ ├── test │ │ ├── flpSandbox.html │ │ ├── flpSandboxMockServer.html │ │ ├── integration │ │ │ ├── AllJourneys.js │ │ │ ├── BusyJourney.js │ │ │ ├── BusyJourneyPhone.js │ │ │ ├── FLPIntegrationJourney.js │ │ │ ├── MasterJourney.js │ │ │ ├── NavigationJourney.js │ │ │ ├── NavigationJourneyPhone.js │ │ │ ├── NotFoundJourney.js │ │ │ ├── NotFoundJourneyPhone.js │ │ │ ├── PhoneJourneys.js │ │ │ ├── opaTests.qunit.html │ │ │ ├── opaTestsPhone.qunit.html │ │ │ └── pages │ │ │ │ ├── App.js │ │ │ │ ├── Browser.js │ │ │ │ ├── Common.js │ │ │ │ ├── Detail.js │ │ │ │ ├── Master.js │ │ │ │ └── NotFound.js │ │ ├── testsuite.qunit.html │ │ └── unit │ │ │ ├── allTests.js │ │ │ ├── controller │ │ │ ├── App.controller.js │ │ │ └── ListSelector.js │ │ │ ├── model │ │ │ ├── GroupSortState.js │ │ │ ├── formatter.js │ │ │ ├── grouper.js │ │ │ └── models.js │ │ │ └── unitTests.qunit.html │ │ └── view │ │ ├── App.view.xml │ │ ├── Detail.view.xml │ │ ├── DetailNoObjectsAvailable.view.xml │ │ ├── DetailObjectNotFound.view.xml │ │ ├── Master.view.xml │ │ ├── NotFound.view.xml │ │ └── ViewSettingsDialog.fragment.xml ├── BusinessPartners.xsodata ├── SLFioriAppCLJS │ └── SLFiori │ │ ├── .project │ │ ├── .project.json │ │ ├── .xsaccess │ │ ├── .xsapp │ │ ├── Component-preload.js │ │ ├── Component.js │ │ ├── SAPLogo.gif │ │ ├── global.js │ │ ├── i18n │ │ ├── i18n.properties │ │ └── i18n_en.properties │ │ ├── index.html │ │ ├── js │ │ ├── ServiceLayerObjects.js │ │ └── callSL.js │ │ ├── libs │ │ └── jquery.min.js │ │ ├── localIndex.html │ │ ├── login.html │ │ ├── manifest.json │ │ ├── manifest_mine.json │ │ ├── model │ │ └── metadata.xml │ │ ├── neo-app.json │ │ ├── style.css │ │ └── view │ │ ├── App.controller.js │ │ ├── App.view.xml │ │ ├── Detail.controller.js │ │ ├── Detail.view.xml │ │ ├── Master.controller.js │ │ └── Master.view.xml ├── SLFioriAppXSJS │ └── SLFiori │ │ ├── .project │ │ ├── .project.json │ │ ├── .xsaccess │ │ ├── .xsapp │ │ ├── Component-preload.js │ │ ├── Component.js │ │ ├── SAPLogo.gif │ │ ├── global.js │ │ ├── i18n │ │ ├── i18n.properties │ │ └── i18n_en.properties │ │ ├── index.html │ │ ├── js │ │ ├── ServiceLayerObjects.js │ │ ├── callSL.js │ │ ├── datajs-1.0.1.min.js │ │ ├── jquery.cookie.js │ │ ├── jquery.min.js │ │ └── webtoolkit.base64.js │ │ ├── libs │ │ ├── jquery.cookie.js │ │ ├── jquery.encode64.js │ │ ├── jquery.js │ │ ├── jquery.marco │ │ ├── jquery.min.js │ │ └── webtoolkit.base64.js │ │ ├── localIndex.html │ │ ├── login.html │ │ ├── manifest.json │ │ ├── manifest_mine.json │ │ ├── model │ │ └── metadata.xml │ │ ├── neo-app.json │ │ ├── style.css │ │ ├── view │ │ ├── App.controller.js │ │ ├── App.view.xml │ │ ├── Detail.controller.js │ │ ├── Detail.view.xml │ │ ├── Master.controller.js │ │ └── Master.view.xml │ │ └── xsjs │ │ ├── B1SL.xshttpdest │ │ └── B1SLLogic.xsjs ├── SalesInvoices.xsodata └── index.html └── README.md /01_WebTech/01_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | My first HTML Page 4 | 5 | 6 |
7 | 8 | 9 |
10 |

This is my first Header 1

11 |

this is a paragrapgh that talks about HTML...

12 |

and here is the Header 2

13 | 14 | -------------------------------------------------------------------------------- /01_WebTech/02_CSS.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HTML and CSS 6 | 29 | 51 | 52 | 53 |

HTML and CSS

54 |

One way to visualize how HTML and CSS work together is to think about a new building under construction. As the building goes up, the structure of the building is built first. At just the structural level, all you see is the frame of the new building, and other than the basic shape, you don’t really know how the building is going to look. Once the frame is complete, the “skin” of the building is added. This could be brick, wood, glass, stucco, or any number of outer materials that determine what the final look of the building will be.

55 |

HTML and CSS work much the same way. HTML gives us the structure, or “frame”, of our pages. We can use CSS to then control how this structure looks, where elements are positioned, and add additional decorative styling. What’s more, much the same way a building can change dramatically by adding a new façade; web pages can change their visual design by simply changing the page’s CSS.

56 |

This separation of structure and presentation creates a very flexible and efficient workflow where the structure of pages is independent of how the pages are presented. This allows you to update styling without changing page content, and provide different visual designs based on the context of where the page is being displayed.

57 |

Allow me to demonstrate

58 | 59 | 60 | -------------------------------------------------------------------------------- /01_WebTech/03_testJS.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | My first HTML Page 4 | 5 | 6 |
7 | 8 | 9 |
10 |

This is my first Header 1

11 |

this is a paragrapgh that talks about HTML...

12 |

and here is the Header 2

13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /01_WebTech/js/jQuerySample.js: -------------------------------------------------------------------------------- 1 | // Only runs once DOM is ready 2 | $(document).ready(function(){ 3 | //When button with ID btn1 is pressed 4 | $("#btn1").click(function(){ 5 | //change all text on h1 tag 6 | $("h1").text("this is a jQuery text") 7 | $("h2").fadeIn(); 8 | }); 9 | 10 | $("#btn2").click(function(){ 11 | $("h2").fadeOut(); 12 | }); 13 | }); 14 | 15 | -------------------------------------------------------------------------------- /01_WebTech/js/sample.js: -------------------------------------------------------------------------------- 1 | //create new elements 2 | var newHeading = document.createElement("h1"); 3 | var newParagraph = document.createElement("p"); 4 | 5 | //to add content, use innerHTML 6 | newHeading.innerHTML = "Did You Know?"; 7 | newParagraph.innerHTML = "Most kangaroos are left-handed"; 8 | 9 | // Add new elements to the HTML 10 | document.getElementById("jsDiv").appendChild(newHeading); 11 | document.getElementById("jsDiv").appendChild(newParagraph); 12 | 13 | // Untyped variables 14 | var a = 10; 15 | var b = 20; 16 | var c = 'The sum of a + b is ->' + (a+b) ; 17 | 18 | //Pops up Alert to user 19 | alert(c); -------------------------------------------------------------------------------- /05_XSClassic/01_GetBP_html.xsjs: -------------------------------------------------------------------------------- 1 | //Output variable (Will become a HTML Document) 2 | var output = "Hi " + $.session.getUsername() + "

"; 3 | //Parameter to be used in the query 4 | var bpType = "C"; 5 | //HANA Connection initialized 6 | var conn = $.hdb.getConnection(); 7 | 8 | // ? = PlaceHolder for parameter 9 | var query = 'SELECT "CardCode", "CardName", "CardType", "Balance" '+ 10 | 'FROM "SBODEMOUS"."OCRD" WHERE "CardType"= ? '; 11 | 12 | // Execute query, parameters should be set in order of appearance 13 | var rs = conn.executeQuery(query, bpType); 14 | 15 | for(var i = 0; i < rs.length; i++){ 16 | output += '
'; 17 | 18 | output += rs[i].CardCode + " - " + 19 | rs[i].CardName + " - " + 20 | rs[i].CardType + " - " + 21 | rs[i].Balance; 22 | } 23 | 24 | //Close HANA connection 25 | conn.close(); 26 | 27 | // Set response in the output 28 | $.response.contentType = "text/html"; 29 | $.response.setBody(output); -------------------------------------------------------------------------------- /05_XSClassic/02_GetBP_json.xsjs: -------------------------------------------------------------------------------- 1 | function getBPJSON(bpType){ 2 | //Initialize connection 3 | var conn = $.hdb.getConnection(); 4 | 5 | // ? = PlaceHolder for parameter 6 | var query = 'SELECT "CardCode", "CardName", "CardType", "Balance" ' + 7 | 'FROM "SBODEMOUS"."OCRD" WHERE "CardType"= ? '; 8 | 9 | // Execute query, parameters should be set in order of appearance 10 | var rs = conn.executeQuery(query, bpType); 11 | 12 | //Close Connection 13 | conn.close(); 14 | 15 | // formatting not required for JSON response, already JSON format 16 | $.response.contentType = "application/json"; 17 | $.response.setBody(JSON.stringify(rs)); 18 | //Set HTTP status (OK = 200) - https://tinyurl.com/httpstatus 19 | $.response.status = $.net.http.OK; 20 | } 21 | 22 | //On server side JavaScript. Functions should be declared before called 23 | getBPJSON('C'); 24 | 25 | -------------------------------------------------------------------------------- /05_XSClassic/03_GetBP_json_params.xsjs: -------------------------------------------------------------------------------- 1 | /** 2 | * This service return a list of Business Partners based on 2 parameters 3 | * bpType = C, L or S // Customer, Lead or Supplier 4 | * top = Number of records 5 | * to call it access http://:/?bpType=&top= 6 | **/ 7 | 8 | function showData(query, bpType, topNb){ 9 | var conn = $.hdb.getConnection(); 10 | var rs = conn.executeQuery(query, topNb, bpType); 11 | 12 | $.response.contentType = "application/json"; // Specify output 13 | $.response.setBody(JSON.stringify(rs)); 14 | $.response.status = $.net.http.OK; 15 | 16 | conn.close(); 17 | } 18 | 19 | var Statement = 'SELECT TOP ? "CardCode", "CardName", "CardType", "Balance"'+ 20 | ' FROM "SBODEMOUS"."OCRD" WHERE "CardType"= ?'; 21 | 22 | //Input parameter handling 23 | var CardType = $.request.parameters.get('bpType'); 24 | var Records = $.request.parameters.get('top'); 25 | 26 | showData(Statement, CardType, Records); -------------------------------------------------------------------------------- /05_XSClassic/04_CreateProcedure.sql: -------------------------------------------------------------------------------- 1 | /* Create a TABLE TYPE for the procedure results */ 2 | CREATE TYPE "SBODEMOUS"."BPDETAILS" AS TABLE ( 3 | "CardCode" NVARCHAR(15), 4 | "CardName" NVARCHAR(100), 5 | "CardType" CHAR(1), 6 | "Balance" DECIMAL 7 | ); 8 | 9 | /* Create the Procedure */ 10 | /* This procedure is invoked via a XSJS Service */ 11 | CREATE PROCEDURE GETBPBYTYPE(IN cardtype CHAR(1), OUT bpListDetails "SBODEMOUS"."BPDETAILS") 12 | LANGUAGE SQLSCRIPT 13 | READS SQL DATA AS 14 | BEGIN 15 | bpListDetails = 16 | SELECT T0."CardCode", T0."CardName", T0."CardType", T0."Balance" 17 | FROM OCRD T0 18 | WHERE T0."CardType" =:cardtype; 19 | END; -------------------------------------------------------------------------------- /05_XSClassic/04_GetBPListProc.xsjs: -------------------------------------------------------------------------------- 1 | function showData(bpType){ 2 | $.response.contentType = "application/json"; 3 | 4 | var connection = $.hdb.getConnection(); 5 | 6 | //Load procedure of specified schema 7 | var getBpByTypeProc = connection.loadProcedure('SBODEMOUS', 'GETBPBYTYPE'); 8 | 9 | //The getBpByTypeProc object act as proxy to the procedure 10 | var results = getBpByTypeProc(bpType); 11 | 12 | //Build the response 13 | $.response.status = $.net.http.OK; 14 | $.response.contentType = "application/json"; 15 | $.response.setBody(JSON.stringify(results)); 16 | 17 | connection.close(); 18 | } 19 | 20 | var bpType = $.request.parameters.get("bpType"); 21 | 22 | showData(bpType); -------------------------------------------------------------------------------- /05_XSClassic/05_GetData_xsjslib.xsjs: -------------------------------------------------------------------------------- 1 | //Complete Library Path and Library Name without extension 2 | var lib = $.import(""," 2 | 3 | B1 Extreme App framework Login 4 | 5 | 6 | 7 |

Login Service

8 |

9 | 10 | 11 |
12 | 13 | 14 |
15 | 16 | 17 | 18 |
19 | 20 | 21 |
22 | 23 | 24 |
25 | 26 | 27 |
28 | 29 |

30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /07_B1XappF/Mashup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Mashup Demo 4 | 5 | 6 | 7 | 8 |

SAP B1 App Framework Mashup Sample

9 |

The buttons below uses the Webbrige Library to post a message and open forms in the B1 client.

10 |

11 |

Messages

12 | Message
13 | 14 | 15 | 16 | 17 |
18 |
19 |

Open Forms

20 | Key
21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /07_B1XappF/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Working with B1 Extreme App Framework 4 | 5 | 6 | 7 |

B1 Extreme App Framework Calls

8 |
9 | 10 | 11 |
12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /07_B1XappF/js/b1xApp.js: -------------------------------------------------------------------------------- 1 | 2 | $(function () { 3 | //When button with id login is pressed 4 | $("#login").click(function () { 5 | var button = $(this); 6 | 7 | //REST Call using jQuery Ajax function 8 | $.ajax({ 9 | type: "POST", 10 | url: "/sap/sbo/platform/login", 11 | headers: { 12 | "Authorization": "Basic "+ btoa($("#hdbuser").val() 13 | + ":" + $("#hdbpwd").val()) 14 | }, 15 | data: { 16 | "company": $("#company").val(), 17 | "username": $("#b1user").val(), 18 | "password": $("#b1pwd").val(), 19 | "language": $("#b1language").val() 20 | }, 21 | error: function (xhr, status, error) { 22 | window.alert("login failed: " + xhr.responseText); 23 | }, 24 | success: function () { 25 | window.alert("login successfully."); 26 | window.location.href= "index.html"; 27 | } 28 | }); 29 | }); 30 | 31 | }); 32 | 33 | 34 | $(document).ready(function(){ 35 | 36 | // Connect Button (on modal window) 37 | $("#sysinfo").click(function(){ 38 | 39 | $.get('/sap/sbo/platform/env', function() { 40 | }) 41 | .done(function(json) { 42 | //Show message with JSON String 43 | alert(JSON.stringify(json)); 44 | }) 45 | 46 | .fail(function() { 47 | alert("ERROR!") 48 | }) 49 | }); 50 | 51 | $("#udq").click(function(){ 52 | $.ajax({ 53 | type: "POST", 54 | url: "/sap/sbo/platform/query", 55 | contentType: "application/json", 56 | dataType: "json", 57 | data: JSON.stringify({ 58 | "type": "sql", 59 | "category": "System", 60 | "name": "Total Sales Amount", 61 | "param": [], 62 | "format": "JSON"}), 63 | error: function (xhr, status, error) { 64 | alert("UDQ Failed: " + xhr.responseText); 65 | }, 66 | success: function (json) { 67 | alert("UDQ Executed."); 68 | displayResult(json.data[0]); 69 | } 70 | }) 71 | 72 | }); 73 | }); 74 | 75 | 76 | function displayResult(result){ 77 | // Generic function to display any set of record 78 | var data; 79 | var json; 80 | var line; 81 | 82 | for(var i = 0; i < result.length ; i++){ 83 | json = result[i]; 84 | line = "" 85 | for (var property in json) { 86 | if (json.hasOwnProperty(property)) { 87 | data = json[property]; 88 | line += "" + JSON.stringify(data) + ""; 89 | } 90 | } 91 | line += "" 92 | $('#result').append(line); 93 | } 94 | } 95 | 96 | -------------------------------------------------------------------------------- /08_ServiceLayer/08_SL_Ex_04_SCRIPTENG_SOL/MyFirstScriptEngineExtension.ard: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /08_ServiceLayer/XSJS/B1SL.xshttpdest: -------------------------------------------------------------------------------- 1 | host = "hanab1"; 2 | port = 50001; 3 | authType = none; 4 | useSSL = false; 5 | timeout = 0; -------------------------------------------------------------------------------- /08_ServiceLayer/XSJS/B1SLLogic.xsjslib: -------------------------------------------------------------------------------- 1 | var B1SLAddress = "/b1s/v1/"; 2 | 3 | function callServiceLayer(path, method, body, sessionID, routeID) { 4 | try { 5 | $.trace.debug("callServiceLayer (path: " + path + ", method: " + method + ", body: " + body + ", sessionID: " + sessionID + ", routeID: " + routeID + ")"); 6 | 7 | //B1SL.xshttpdest 8 | var destination = $.net.http.readDestination("", "B1SL"); 9 | var client = new $.net.http.Client(); 10 | 11 | var header = ""; 12 | if (method === $.net.http.PATCH) { 13 | method = $.net.http.POST; 14 | header = "X-HTTP-Method-Override: PATCH"; 15 | } 16 | 17 | var req = new $.web.WebRequest(method, path); 18 | 19 | if (header !== "") { 20 | req.headers.set("X-HTTP-Method-Override", "PATCH"); 21 | } 22 | 23 | if (body) { 24 | req.setBody(body); 25 | } 26 | 27 | if (sessionID) { 28 | req.cookies.set("B1SESSION", sessionID); 29 | } 30 | if (routeID) { 31 | req.cookies.set("ROUTEID", routeID); 32 | } 33 | 34 | client.request(req, destination); 35 | 36 | var response = client.getResponse(); 37 | 38 | //The rest of the file (attached) is just a default forward of the response 39 | var myCookies = [], 40 | myHeader = [], 41 | myBody = null; 42 | 43 | //Cookies 44 | for (var c in response.cookies) { 45 | myCookies.push(response.cookies[c]); 46 | } 47 | //Headers 48 | for (var h in response.headers) { 49 | myHeader.push(response.headers[h]); 50 | } 51 | //Body 52 | if (response.body) 53 | try { 54 | myBody = JSON.parse(response.body.asString()); 55 | } catch (e) { 56 | myBody = response.body.asString(); 57 | } 58 | 59 | 60 | $.trace.debug("callServiceLayer response status: " + $.response.status); 61 | return response; 62 | } catch (e) { 63 | $.trace.warning("callServiceLayer Exception: " + e.message); 64 | $.response.contentType = "application/json"; 65 | $.response.setBody(JSON.stringify({ 66 | "error": e.message 67 | })); 68 | } 69 | } 70 | 71 | function SLLogin(body, sessionID, routeID) 72 | { 73 | var path = B1SLAddress + "Login"; 74 | return callServiceLayer(path, $.net.http.POST, body, sessionID, routeID); 75 | } 76 | 77 | function GetOrders(appReq, sessionID, routeID) 78 | { 79 | var path = B1SLAddress + "Orders("+appReq+")"; 80 | 81 | return callServiceLayer(path, $.net.http.GET, null, sessionID, routeID); 82 | } 83 | 84 | function createBusinessPartner(body, sessionID, routeID) 85 | { 86 | var path = B1SLAddress + "BusinessPartners"; 87 | 88 | return callServiceLayer(path, $.net.http.POST, body, sessionID, routeID); 89 | } -------------------------------------------------------------------------------- /08_ServiceLayer/XSJS/getOrder.xsjs: -------------------------------------------------------------------------------- 1 | var slLib = $.import("","B1SLLogic"); 2 | 3 | var output = {}; 4 | var UserName = "manager"; 5 | var UserPass = "1234"; 6 | var UserComp = "SBODEMOUS"; 7 | 8 | function setOutputMessage(){ 9 | 10 | $.response.contentType = "application/json"; 11 | $.response.setBody(JSON.stringify(output)); 12 | $.response.status = $.net.http.OK; 13 | } 14 | 15 | function GetOrders(appRequest){ 16 | 17 | var SESSIONID = ""; 18 | var NODEID = ""; 19 | var body; 20 | 21 | // SL credentials 22 | var loginInfo = {}; 23 | loginInfo.UserName = UserName; 24 | loginInfo.Password = UserPass; 25 | loginInfo.CompanyDB = UserComp; 26 | 27 | // SL LOGIN 28 | var response = slLib.SLLogin(JSON.stringify(loginInfo), null, null); 29 | 30 | // B1SESSION and ROUTEID cookies returned by Login 31 | for (var j in response.cookies){ 32 | if (response.cookies[j].name == "B1SESSION"){ 33 | SESSIONID = response.cookies[j].value; 34 | output.SessionID = SESSIONID; 35 | } 36 | else if (response.cookies[j].name == "ROUTEID") { 37 | NODEID = response.cookies[j].value; 38 | output.NodeID = NODEID; 39 | } 40 | } 41 | 42 | //Get Approval Request Information 43 | response = slLib.GetOrders(appRequest, SESSIONID,NODEID); 44 | 45 | //Parse response body 46 | body = JSON.parse(response.body.asString()); 47 | 48 | // Removes metadata 49 | delete body["odata.metadata"]; 50 | 51 | //Add Approval Request Decision to Approval Request Body 52 | //output = body; 53 | 54 | output.DocEntry = body.DocEntry; 55 | output.Message = 'Call de Service Layer ok'; 56 | output.Total = body.DocTotal; 57 | output.ralph = body.CardCode; 58 | output.kafhai = body.DocDate; 59 | 60 | setOutputMessage(); 61 | 62 | } 63 | 64 | 65 | var req = 0; 66 | req = $.request.parameters.get('order'); 67 | 68 | if (!req){ 69 | output.error = "Invalid Approval Request - " + req; 70 | $.response.status = $.net.http.OK; 71 | $.response.contentType = "application/json"; 72 | $.response.setBody(JSON.stringify(output)); 73 | }else{ 74 | GetOrders(req); 75 | } -------------------------------------------------------------------------------- /08_ServiceLayer/XSJS/runBatch.xsjs: -------------------------------------------------------------------------------- 1 | 2 | var destination = "http://:50003/b1s/v1/$batch"; 3 | var client = new $.net.http.Client(); 4 | 5 | var req = new $.web.WebRequest($.net.http.POST, ""); 6 | req.headers.set("Content-Type","multipart/mixed"); 7 | req.cookies.set("B1SESSION", ); 8 | 9 | req.entities.create(); 10 | req.entities[0].headers.set("Content-Type","multipart/mixed"); 11 | req.entities[0].setBody( 12 | "--batch_myBatch001" 13 | + " \n" 14 | + "Content-Type:application/http" 15 | + " \n" 16 | + "Content-Transfer-Encoding:binary" 17 | + " \n" 18 | + " \n" 19 | + "--changeset_myChangeset001" 20 | + " \n" 21 | + " \n" 22 | + "POST /b1s/v1/Items" 23 | + " \n" 24 | + " \n" 25 | + '{"ItemCode": "TCM351","ItemName": "My first item from SL Batch and XSJS call","ItemType": "itItems"}' 26 | + " \n" 27 | + "--changeset_myChangeset001" 28 | + " \n" 29 | + "Content-Type:application/http" 30 | + " \n" 31 | + "Content-Transfer-Encoding:binary" 32 | + " \n" 33 | + " \n" 34 | + "POST /b1s/v1/Items" 35 | + " \n" 36 | + " \n" 37 | + '{"ItemCode": "TCM350","ItemName": "My second item from SL Batch and XSJS call","ItemType": "itItems"}' 38 | + " \n" 39 | + " \n" 40 | + "--changeset_myChangeset001--" 41 | + " \n" 42 | + "--batch_myBatch001--" ); 43 | 44 | client.request(req, destination); 45 | var response = client.getResponse(); 46 | 47 | //Variables for response handling 48 | var myCookies = [], 49 | myHeader = [], 50 | myBody = null, 51 | myEntityBody = null; 52 | 53 | //Cookies 54 | for (var c in response.cookies) { 55 | myCookies.push(response.cookies[c]); 56 | } 57 | 58 | //Headers 59 | for (var h in response.headers) { 60 | myHeader.push(response.headers[h]); 61 | } 62 | 63 | // Body 64 | if (response.body) 65 | try { 66 | myBody = JSON.parse(response.body.asString()); 67 | } catch (e) { 68 | myBody = response.body.asString(); 69 | } 70 | 71 | //Entities Body 72 | for (var w in response.entities) { 73 | if (response.entities[w].body){ 74 | try { 75 | myEntityBody = JSON.parse(response.entities[w].body.asString()); 76 | } catch (e) { 77 | myEntityBody = response.entities[w].body.asString(); 78 | } 79 | } 80 | } 81 | 82 | $.response.contentType = "application/json"; 83 | $.response.status = response.status; 84 | $.response.setBody(JSON.stringify({ 85 | "status": response.status, 86 | "cookies": myCookies, 87 | "headers": myHeader, 88 | "body": myBody, 89 | "entitybody": myEntityBody 90 | })); 91 | -------------------------------------------------------------------------------- /09_SAPUI5/neo-app.json: -------------------------------------------------------------------------------- 1 | { 2 | "welcomeFile": "/webapp/index.html", 3 | "routes": [ 4 | { 5 | "path": "/destinations/ServiceLayer", 6 | "target": { 7 | "type": "destination", 8 | "name": "ServiceLayer" 9 | }, 10 | "description": "ServiceLayer" 11 | }, 12 | { 13 | "path": "/destinations/XSEngine", 14 | "target": { 15 | "type": "destination", 16 | "name": "XSEngine" 17 | }, 18 | "description": "XSEngine" 19 | }, 20 | { 21 | "path": "/resources", 22 | "target": { 23 | "type": "service", 24 | "name": "sapui5", 25 | "entryPath": "/resources", 26 | "version": "1.38.30" 27 | }, 28 | "description": "SAPUI5 Resources" 29 | }, 30 | { 31 | "path": "/test-resources", 32 | "target": { 33 | "type": "service", 34 | "name": "sapui5", 35 | "entryPath": "/test-resources", 36 | "version": "1.38.30" 37 | }, 38 | "description": "SAPUI5 Test Resources" 39 | } 40 | ], 41 | "sendWelcomeFileRedirect": true 42 | } -------------------------------------------------------------------------------- /09_SAPUI5/webapp/Component.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/core/UIComponent", 3 | "sap/ui/Device", 4 | "UI/model/models" 5 | ], function(UIComponent, Device, models) { 6 | "use strict"; 7 | 8 | return UIComponent.extend("UI.Component", { 9 | 10 | metadata: { 11 | manifest: "json" 12 | }, 13 | 14 | /** 15 | * The component is initialized by UI automatically during the startup of the app and calls the init method once. 16 | * @public 17 | * @override 18 | */ 19 | init: function() { 20 | // call the base component's init function 21 | UIComponent.prototype.init.apply(this, arguments); 22 | 23 | // set the device model 24 | this.setModel(models.createDeviceModel(), "device"); 25 | } 26 | }); 27 | }); -------------------------------------------------------------------------------- /09_SAPUI5/webapp/controller/main.controller.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/core/mvc/Controller" 3 | ], function(Controller) { 4 | "use strict"; 5 | 6 | return Controller.extend("UI.controller.main", { 7 | 8 | onInit: function(){ 9 | sap.ui.localResources("i18n"); 10 | 11 | var oResourceModel = new sap.ui.model.resource.ResourceModel({ 12 | bundleName: "i18n.i18n" 13 | }); 14 | 15 | sap.ui.getCore().setModel(oResourceModel, "i18n"); 16 | }, 17 | 18 | onPressGoToServiceLayer: function(){ 19 | app.to("serviceLayerPage"); 20 | }, 21 | 22 | onPressGoToXSJS: function(){ 23 | app.to("xsjsPage"); 24 | }, 25 | 26 | onPressGoToODATA: function(){ 27 | app.to("odataPage"); 28 | } 29 | }); 30 | }); -------------------------------------------------------------------------------- /09_SAPUI5/webapp/controller/odata.controller.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/core/mvc/Controller" 3 | ], function(Controller) { 4 | "use strict"; 5 | 6 | return Controller.extend("UI.controller.odata", { 7 | 8 | onPressGoBack: function(){ 9 | app.back(); 10 | }, 11 | 12 | onPressLoadOdata: function () { 13 | // Create a model to Load the XSOData 14 | var oModelBP = new sap.ui.model.odata.ODataModel( 15 | "/destinations/XSEngine/SOL2017/odata/b1.xsodata" 16 | ); 17 | 18 | // Load the Table and the columns we’ve created on the view 19 | var oTable = sap.ui.getCore().byId("idPrdList"); 20 | var colItems = sap.ui.getCore().byId("colItems"); 21 | 22 | // Attach the loaded model to the table 23 | oTable.setModel(oModelBP); 24 | 25 | // Bind the table to an specific oData service 26 | oTable.bindAggregation("","/SalesByGroup",colItems); 27 | }, 28 | 29 | /** 30 | * Called when a controller is instantiated and its View controls (if available) are already created. 31 | * Can be used to modify the View before it is displayed, to bind event handlers and do other one-time initialization. 32 | * @memberOf UI.view.odata 33 | */ 34 | // onInit: function() { 35 | // 36 | // }, 37 | 38 | /** 39 | * Similar to onAfterRendering, but this hook is invoked before the controller's View is re-rendered 40 | * (NOT before the first rendering! onInit() is used for that one!). 41 | * @memberOf UI.view.odata 42 | */ 43 | // onBeforeRendering: function() { 44 | // 45 | // }, 46 | 47 | /** 48 | * Called when the View has been rendered (so its HTML is part of the document). Post-rendering manipulations of the HTML could be done here. 49 | * This hook is the same one that SAPUI5 controls get after being rendered. 50 | * @memberOf UI.view.odata 51 | */ 52 | // onAfterRendering: function() { 53 | // 54 | // }, 55 | 56 | /** 57 | * Called when the Controller is destroyed. Use this one to free resources and finalize activities. 58 | * @memberOf UI.view.odata 59 | */ 60 | // onExit: function() { 61 | // 62 | // } 63 | 64 | }); 65 | 66 | }); -------------------------------------------------------------------------------- /09_SAPUI5/webapp/controller/serviceLayer.controller.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/core/mvc/Controller" 3 | ], function(Controller) { 4 | "use strict"; 5 | 6 | return Controller.extend("UI.controller.serviceLayer", { 7 | 8 | onInit: function() { 9 | var user = "manager"; 10 | var pass = "1234"; 11 | var comp = "SBODEMOUS"; 12 | 13 | var jData = JSON.stringify({ 14 | UserName: user, 15 | Password: pass, 16 | CompanyDB: comp 17 | }); 18 | var serviceLayerLoginURL = "/destinations/ServiceLayer/Login"; 19 | 20 | $.ajax({ 21 | url: serviceLayerLoginURL, 22 | xhrFields: {withCredentials:true}, 23 | data:jData, 24 | type:"POST", 25 | dataType:"json" 26 | }); 27 | }, 28 | 29 | onPressGoBack: function(){ 30 | app.back(); 31 | }, 32 | 33 | onPressLoadServiceLayer: function(){ 34 | var serviceLayerItemsURL = "/destinations/ServiceLayer/Items"; 35 | $.ajax({ 36 | url: serviceLayerItemsURL, 37 | xhrFields: {withCredentials:true}, 38 | type:"GET", 39 | dataType:"json", 40 | success: this.onCompleteCall, 41 | error: this.onErrorCall 42 | }); 43 | }, 44 | 45 | onCompleteCall: function(result){ 46 | var oTable = sap.ui.getCore().byId("idPrdListServiceLayer"); 47 | var slColItems = sap.ui.getCore().byId("slColItems"); 48 | var oModelItem = new sap.ui.model.json.JSONModel(); 49 | 50 | oModelItem.setData(result); 51 | oTable.setModel(oModelItem); 52 | oTable.bindItems("/value", slColItems); 53 | }, 54 | 55 | onErrorCall: function(jqXHR, textStatus, errorThrown){ 56 | $.sap.require("sap.m.MessageBox"); 57 | sap.m.MessageBox("Error calling Service Layer", { 58 | icon: sap.m.MessageBox.Icon.Error, 59 | title: "{i18n>error}", 60 | onClose:function(oAction){return;} 61 | } 62 | ); 63 | } 64 | 65 | /** 66 | * Called when a controller is instantiated and its View controls (if available) are already created. 67 | * Can be used to modify the View before it is displayed, to bind event handlers and do other one-time initialization. 68 | * @memberOf UI.view.serviceLayer 69 | */ 70 | // onInit: function() { 71 | // 72 | // }, 73 | 74 | /** 75 | * Similar to onAfterRendering, but this hook is invoked before the controller's View is re-rendered 76 | * (NOT before the first rendering! onInit() is used for that one!). 77 | * @memberOf UI.view.serviceLayer 78 | */ 79 | // onBeforeRendering: function() { 80 | // 81 | // }, 82 | 83 | /** 84 | * Called when the View has been rendered (so its HTML is part of the document). Post-rendering manipulations of the HTML could be done here. 85 | * This hook is the same one that SAPUI5 controls get after being rendered. 86 | * @memberOf UI.view.serviceLayer 87 | */ 88 | // onAfterRendering: function() { 89 | // 90 | // }, 91 | 92 | /** 93 | * Called when the Controller is destroyed. Use this one to free resources and finalize activities. 94 | * @memberOf UI.view.serviceLayer 95 | */ 96 | // onExit: function() { 97 | // 98 | // } 99 | 100 | }); 101 | 102 | }); -------------------------------------------------------------------------------- /09_SAPUI5/webapp/controller/xsjs.controller.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/core/mvc/Controller" 3 | ], function(Controller) { 4 | "use strict"; 5 | 6 | return Controller.extend("UI.controller.xsjs", { 7 | 8 | onPressGoBack: function(){ 9 | app.back(); 10 | }, 11 | 12 | onPressLoadXSJS : function(srvcType, rows) 13 | { 14 | var xsjsUrl = "/destinations/XSEngine/" + 15 | "SOL2017/xsjs/GetBPListSQL_Params.xsjs" + 16 | "?bpType=" + srvcType + "&top=" + rows; 17 | 18 | //jQuery Call 19 | $.ajax({ 20 | url: xsjsUrl, 21 | method: 'GET', 22 | dataType: 'json', 23 | success: this.onCompleteCall, 24 | error: this.onErrorCall 25 | }); 26 | }, 27 | 28 | onCompleteCall: function(result) 29 | { 30 | // Load the Table and the columns we’ve created on the view 31 | var oTable = sap.ui.getCore().byId("idBPList"); 32 | var colItems = sap.ui.getCore().byId("colBP"); 33 | 34 | // Create a model to Load the XSOData 35 | var oModelBP = new sap.ui.model.json.JSONModel(); 36 | 37 | //Load result data set to the model 38 | oModelBP.setData(result); 39 | 40 | // Attach the loaded model to the table 41 | oTable.setModel(oModelBP); 42 | 43 | // Bind the table to an specific oData service 44 | oTable.bindAggregation("items","/",colItems); 45 | 46 | }, 47 | 48 | onErrorCall: function(jqXHR, textStatus, errorThrown) 49 | { 50 | $.sap.require("sap.m.MessageBox"); 51 | sap.m.MessageBox.show( 52 | "Error calling XSJS Service", { 53 | icon: sap.m.MessageBox.Icon.ERROR, 54 | title: "Error", 55 | onClose: function(oAction) { return } 56 | } 57 | ); 58 | }, 59 | /** 60 | * Called when a controller is instantiated and its View controls (if available) are already created. 61 | * Can be used to modify the View before it is displayed, to bind event handlers and do other one-time initialization. 62 | * @memberOf UI.view.xsjs 63 | */ 64 | // onInit: function() { 65 | // 66 | // }, 67 | 68 | /** 69 | * Similar to onAfterRendering, but this hook is invoked before the controller's View is re-rendered 70 | * (NOT before the first rendering! onInit() is used for that one!). 71 | * @memberOf UI.view.xsjs 72 | */ 73 | // onBeforeRendering: function() { 74 | // 75 | // }, 76 | 77 | /** 78 | * Called when the View has been rendered (so its HTML is part of the document). Post-rendering manipulations of the HTML could be done here. 79 | * This hook is the same one that SAPUI5 controls get after being rendered. 80 | * @memberOf UI.view.xsjs 81 | */ 82 | // onAfterRendering: function() { 83 | // 84 | // }, 85 | 86 | /** 87 | * Called when the Controller is destroyed. Use this one to free resources and finalize activities. 88 | * @memberOf UI.view.xsjs 89 | */ 90 | // onExit: function() { 91 | // 92 | // } 93 | 94 | }); 95 | 96 | }); -------------------------------------------------------------------------------- /09_SAPUI5/webapp/css/style.css: -------------------------------------------------------------------------------- 1 | /* Enter your custom styles here */ -------------------------------------------------------------------------------- /09_SAPUI5/webapp/i18n/i18n.properties: -------------------------------------------------------------------------------- 1 | ! Common labels 2 | titleMain=B1 App Demo Based on SAPUI5 3 | 4 | ! Service Layer labels 5 | titleServiceLayer=Get list of Products using Service Layer 6 | buttonSL=Service Layer 7 | tableListofProducts=List of Products 8 | buttonLoad=Load 9 | tableColumnHeaderCode=Code 10 | tableColumnHeaderName=Name 11 | tableColumnHeaderQuantity=Quantity 12 | 13 | ! XSJS labels 14 | titleXSJS=Get list of Business Partners using XSJS 15 | buttonXSJS=XSJS 16 | tableListofBP=List of Business Partners 17 | tableColumnHeaderType=Type 18 | tableColumnHeaderBalance=Balance 19 | 20 | ! ODATA labels 21 | titleODATA=Get Sales by BP Group using XSODATA 22 | buttonODATA=ODATA 23 | tableSalesByBPGroup=Sales by BP Group 24 | tableColumnHeaderGroupCode=Group Code 25 | tableColumnHeaderGroupName=Group Name 26 | tableColumnHeaderTotalAmount=Total Amount 27 | tableColumnHeaderGrossProfit=Gross Profit 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /09_SAPUI5/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | UI 9 | 10 | 17 | 18 | 19 | 20 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /09_SAPUI5/webapp/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "_version": "1.7.0", 3 | "sap.app": { 4 | "id": "UI", 5 | "type": "application", 6 | "i18n": "i18n/i18n.properties", 7 | "applicationVersion": { 8 | "version": "1.0.0" 9 | }, 10 | "title": "{{appTitle}}", 11 | "description": "{{appDescription}}", 12 | "sourceTemplate": { 13 | "id": "ui5template.basicSAPUI5ApplicationProject", 14 | "version": "1.38.11" 15 | } 16 | }, 17 | 18 | "sap.ui": { 19 | "technology": "UI5", 20 | "icons": { 21 | "icon": "", 22 | "favIcon": "", 23 | "phone": "", 24 | "phone@2": "", 25 | "tablet": "", 26 | "tablet@2": "" 27 | }, 28 | "deviceTypes": { 29 | "desktop": true, 30 | "tablet": true, 31 | "phone": true 32 | }, 33 | "supportedThemes": [ 34 | "sap_hcb", 35 | "sap_bluecrystal" 36 | 37 | ] 38 | }, 39 | 40 | "sap.ui5": { 41 | "rootView": { 42 | "viewName": "UI.view.main", 43 | "type": "JS" 44 | }, 45 | "dependencies": { 46 | "minUI5Version": "1.30.0", 47 | "libs": { 48 | "sap.ui.core": {}, 49 | "sap.m": {}, 50 | "sap.ui.layout": {}, 51 | "sap.ushell": {}, 52 | "sap.collaboration": {}, 53 | "sap.ui.comp": {}, 54 | "sap.uxap": {} 55 | } 56 | }, 57 | "contentDensities": { 58 | "compact": true, 59 | "cozy": true 60 | }, 61 | "models": { 62 | "i18n": { 63 | "type": "sap.ui.model.resource.ResourceModel", 64 | "settings": { 65 | "bundleName": "UI.i18n.i18n" 66 | } 67 | } 68 | }, 69 | "resources": { 70 | "css": [{ 71 | "uri": "css/style.css" 72 | }] 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /09_SAPUI5/webapp/model/models.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/model/json/JSONModel", 3 | "sap/ui/Device" 4 | ], function(JSONModel, Device) { 5 | "use strict"; 6 | 7 | return { 8 | 9 | createDeviceModel: function() { 10 | var oModel = new JSONModel(Device); 11 | oModel.setDefaultBindingMode("OneWay"); 12 | return oModel; 13 | } 14 | 15 | }; 16 | }); -------------------------------------------------------------------------------- /09_SAPUI5/webapp/view/main.view.js: -------------------------------------------------------------------------------- 1 | sap.ui.jsview("UI.view.main", { 2 | 3 | /** Specifies the Controller belonging to this View. 4 | * In the case that it is not implemented, or that "null" is returned, this View does not have a Controller. 5 | * @memberOf controller.main 6 | */ 7 | getControllerName: function() { 8 | return "UI.controller.main"; 9 | }, 10 | 11 | /** Is initially called once after the Controller has been instantiated. It is the place where the UI is constructed. 12 | * Since the Controller is given to this method, its event handlers can be attached right away. 13 | * @memberOf controller.main 14 | */ 15 | createContent: function(oController) { 16 | 17 | var buttonSL = new sap.m.Button({ 18 | text:"{i18n>buttonSL}", 19 | width:"150px", 20 | press:[oController.onPressGoToServiceLayer, oController]}); 21 | 22 | var buttonXSJS = new sap.m.Button({ 23 | text:"{i18n>buttonXSJS}", 24 | width:"150px", 25 | press:[oController.onPressGoToXSJS, oController]}); 26 | 27 | var buttonODATA = new sap.m.Button({ 28 | text:"{i18n>buttonODATA}", 29 | width:"150px", 30 | press:[oController.onPressGoToODATA, oController]}); 31 | 32 | var mainPage = new sap.m.Page({ 33 | title: "{i18n>titleMain}", 34 | content: [ 35 | buttonSL, 36 | new sap.m.ToolbarSpacer(), 37 | buttonXSJS, 38 | new sap.m.ToolbarSpacer(), 39 | buttonODATA 40 | ] 41 | }); 42 | return mainPage; 43 | } 44 | 45 | 46 | }); -------------------------------------------------------------------------------- /09_SAPUI5/webapp/view/odata.view.js: -------------------------------------------------------------------------------- 1 | sap.ui.jsview("UI.view.odata", { 2 | 3 | /** Specifies the Controller belonging to this View. 4 | * In the case that it is not implemented, or that "null" is returned, this View does not have a Controller. 5 | * @memberOf UI.view.odata 6 | */ 7 | getControllerName: function() { 8 | return "UI.controller.odata"; 9 | }, 10 | 11 | /** Is initially called once after the Controller has been instantiated. It is the place where the UI is constructed. 12 | * Since the Controller is given to this method, its event handlers can be attached right away. 13 | * @memberOf UI.view.odata 14 | */ 15 | 16 | createContent : function(oController) { 17 | 18 | //Create table and its columns 19 | var oTable = new sap.m.Table("idPrdList", { 20 | inset : true, 21 | headerText : "{i18n>tableSalesByBPGroup}", 22 | headerDesign : sap.m.ListHeaderDesign.Standard, 23 | mode : sap.m.ListMode.None, 24 | includeItemInSelection : false, 25 | growing : true, 26 | growingThreshold :5 27 | }); 28 | 29 | var col1 = new sap.m.Column("col1",{ 30 | header: new sap.m.Label({ 31 | text:"{i18n>tableColumnHeaderGroupCode}" 32 | }) 33 | }); 34 | oTable.addColumn(col1); 35 | 36 | var col2 = new sap.m.Column("col2",{ 37 | header: new sap.m.Label({ 38 | text:"{i18n>tableColumnHeaderGroupName}" 39 | }) 40 | }); 41 | oTable.addColumn(col2); 42 | 43 | var col3 = new sap.m.Column("col3",{ 44 | header: new sap.m.Label({ 45 | text:"{i18n>tableColumnHeaderTotalAmount}" 46 | }) 47 | }); 48 | oTable.addColumn(col3); 49 | 50 | var col4 = new sap.m.Column("col4",{ 51 | header: new sap.m.Label({ 52 | text:"{i18n>tableColumnHeaderGrossProfit}" 53 | }) 54 | }); 55 | oTable.addColumn(col4); 56 | 57 | var colItems = new sap.m.ColumnListItem("colItems",{ 58 | type:"Active" 59 | }); 60 | 61 | var txtNAME = new sap.m.Text({ 62 | text:"{GroupCode}" 63 | }); 64 | colItems.addCell(txtNAME); 65 | 66 | var txtNAME2 = new sap.m.Text({ 67 | text:"{GroupName}" 68 | }); 69 | colItems.addCell(txtNAME2); 70 | 71 | var txtNAME3 = new sap.m.Text({ 72 | text:"{DocTotal}" 73 | }); 74 | colItems.addCell(txtNAME3); 75 | 76 | var txtNAME4 = new sap.m.Text({ 77 | text:"{GrosProfit}" 78 | }); 79 | colItems.addCell(txtNAME4); 80 | 81 | //Create button to load the data into the table 82 | var buttonLoad = new sap.m.Button({ 83 | text : "{i18n>buttonLoad}", 84 | width: "150px", 85 | press: [oController.onPressLoadOdata, oController] 86 | }); 87 | 88 | // create the second page with a back button 89 | var odataPage = new sap.m.Page({ 90 | title : "{i18n>titleODATA}", 91 | showNavButton : true, 92 | navButtonPress:[oController.onPressGoBack, oController], 93 | content: [ 94 | buttonLoad, 95 | oTable 96 | ] 97 | }); 98 | 99 | return odataPage; 100 | } 101 | }); -------------------------------------------------------------------------------- /09_SAPUI5/webapp/view/serviceLayer.view.js: -------------------------------------------------------------------------------- 1 | sap.ui.jsview("UI.view.serviceLayer", { 2 | 3 | /** Specifies the Controller belonging to this View. 4 | * In the case that it is not implemented, or that "null" is returned, this View does not have a Controller. 5 | * @memberOf UI.view.serviceLayer 6 | */ 7 | getControllerName: function() { 8 | return "UI.controller.serviceLayer"; 9 | }, 10 | 11 | /** Is initially called once after the Controller has been instantiated. It is the place where the UI is constructed. 12 | * Since the Controller is given to this method, its event handlers can be attached right away. 13 | * @memberOf UI.view.serviceLayer 14 | */ 15 | createContent: function(oController) { 16 | var oTable = new sap.m.Table("idPrdListServiceLayer",{ 17 | headerText: "{i18n>tableListofProducts}", 18 | growing: true, 19 | growingThreshold:5 20 | }); 21 | 22 | var slCol1 = new sap.m.Column("slCol1", {header: new sap.m.Label({text:"{i18n>tableColumnHeaderCode}"})}); 23 | oTable.addColumn(slCol1); 24 | 25 | var slCol2 = new sap.m.Column("slCol2", {header: new sap.m.Label({text:"{i18n>tableColumnHeaderName}"})}); 26 | oTable.addColumn(slCol2); 27 | 28 | var slCol3 = new sap.m.Column("slCol3", {header: new sap.m.Label({text:"{i18n>tableColumnHeaderQuantity}"})}); 29 | oTable.addColumn(slCol3); 30 | 31 | var slColItems = new sap.m.ColumnListItem("slColItems"); 32 | 33 | var slTxtName1 = new sap.m.Text("slTxtName1", {text:"{ItemCode}"}); 34 | slColItems.addCell(slTxtName1); 35 | var slTxtName2 = new sap.m.Text("slTxtName2", {text:"{ItemName}"}); 36 | slColItems.addCell(slTxtName2); 37 | 38 | var slTxtName3 = new sap.m.Text("slTxtName3", {text:"{QuantityOnStock}"}); 39 | slColItems.addCell(slTxtName3); 40 | 41 | var buttonLoad = new sap.m.Button({ 42 | text:"{i18n>buttonLoad}", 43 | width:"150px", 44 | press: [oController.onPressLoadServiceLayer, oController] 45 | }); 46 | 47 | var serviceLayer = new sap.m.Page({ 48 | title: "{i18n>titleServiceLayer}", 49 | showNavButton:true, 50 | navButtonPress:[oController.onPressGoBack, oController], 51 | content: [ 52 | buttonLoad, 53 | oTable 54 | ] 55 | }); 56 | 57 | return serviceLayer; 58 | } 59 | }); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "projectType": [ 3 | "sap.watt.uitools.ide.fiori", 4 | "com.watt.common.builder.sapui5clientbuild" 5 | ], 6 | "build": { 7 | "targetFolder": "dist", 8 | "sourceFolder": "webapp", 9 | "excludedFolders": [ 10 | "test" 11 | ], 12 | "excludedFiles": [ 13 | "test.html" 14 | ], 15 | "buildRequired": true 16 | }, 17 | "dataBinding": { 18 | "/webapp/view/App.view.xml": { 19 | "entitySet": "UNBINDKEY" 20 | }, 21 | "/webapp/view/NotFound.view.xml": { 22 | "entitySet": "UNBINDKEY" 23 | }, 24 | "/webapp/view/DetailObjectNotFound.view.xml": { 25 | "entitySet": "UNBINDKEY" 26 | }, 27 | "/webapp/view/DetailNoObjectsAvailable.view.xml": { 28 | "entitySet": "UNBINDKEY" 29 | }, 30 | "/webapp/view/Master.view.xml": { 31 | "entitySet": "BusinessPartner" 32 | }, 33 | "/webapp/view/Detail.view.xml": { 34 | "entitySet": "BusinessPartner" 35 | } 36 | }, 37 | "generation": [ 38 | { 39 | "templateId": "sap.ui.ui5-template-plugin.2masterdetail", 40 | "templateVersion": "1.38.1", 41 | "dateTimeStamp": "Fri, 15 Sep 2017 07:48:08 GMT" 42 | } 43 | ], 44 | "translation": { 45 | "translationDomain": "", 46 | "supportedLanguages": "en,fr,de", 47 | "defaultLanguage": "en", 48 | "defaultI18NPropertyFile": "i18n.properties", 49 | "resourceModelName": "i18n" 50 | }, 51 | "mockpreview": { 52 | "mockUri": "", 53 | "metadataFilePath": "", 54 | "loadJSONFiles": false, 55 | "loadCustomRequests": false, 56 | "mockRequestsFilePath": "" 57 | }, 58 | "basevalidator": { 59 | "services": { 60 | "xml": "fioriXmlAnalysis", 61 | "js": "fioriJsValidator" 62 | } 63 | }, 64 | "codeCheckingTriggers": { 65 | "notifyBeforePush": true, 66 | "notifyBeforePushLevel": "Error", 67 | "blockPush": false, 68 | "blockPushLevel": "Error" 69 | } 70 | } -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/.xsaccess: -------------------------------------------------------------------------------- 1 | { 2 | "exposed": true, 3 | "authentication": [{ 4 | "method": "Form" 5 | }], 6 | 7 | "mime_mapping": [{ 8 | "extension": "jpg", 9 | "mimetype": "image/jpeg" 10 | }], 11 | "force_ssl": false, 12 | "enable_etags": true, 13 | "prevent_xsrf": true, 14 | "anonymous_connection": null, 15 | "cors": [{ 16 | "enabled": false 17 | }], 18 | "cache_control": "no-cache, no-store", 19 | "default_file": "index.html" 20 | } -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/.xsapp: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/OData/BusinessPartner.xsodata: -------------------------------------------------------------------------------- 1 | service 2 | { 3 | "SBODEMOUS"."OCRD" as "BusinessPartner" 4 | with("CardCode", "CardName", "CardType", "Balance", "Currency") 5 | navigates ("BP_BPAddress" as "BPAddress","BP_Contacts" as "ContactPersons") 6 | create forbidden 7 | update forbidden 8 | delete forbidden; 9 | 10 | "SBODEMOUS"."CRD1" as "BPAddresses" 11 | with("CardCode","AdresType","Address", "Street", "City", "State", "Country") 12 | create forbidden 13 | update forbidden 14 | delete forbidden; 15 | 16 | association "BP_BPAddress" principal "BusinessPartner"("CardCode") 17 | multiplicity "1" dependent "BPAddresses"("CardCode") multiplicity "*"; 18 | 19 | "SBODEMOUS"."OCPR" as "Contacts" 20 | with("CardCode","CntctCode", "Name", "Position", "Tel1", "Cellolar", "E_MaiL") 21 | create forbidden 22 | update forbidden 23 | delete forbidden; 24 | 25 | association "BP_Contacts" principal "BusinessPartner"("CardCode") 26 | multiplicity "1" dependent "Contacts"("CardCode") multiplicity "*"; 27 | } -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Business Partners List 8 | 9 | 24 | 47 | 48 | 49 | 50 | 51 | 59 | 60 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/neo-app.json: -------------------------------------------------------------------------------- 1 | { 2 | "routes": [ 3 | { 4 | "path": "/webapp/resources", 5 | "target": { 6 | "type": "service", 7 | "name": "sapui5", 8 | "entryPath": "/resources" 9 | }, 10 | "description": "SAPUI5 Resources" 11 | }, 12 | { 13 | "path": "/webapp/test-resources", 14 | "target": { 15 | "type": "service", 16 | "name": "sapui5", 17 | "entryPath": "/test-resources" 18 | }, 19 | "description": "SAPUI5 Test Resources" 20 | }, 21 | { 22 | "path": "/resources", 23 | "target": { 24 | "type": "service", 25 | "name": "sapui5", 26 | "entryPath": "/resources" 27 | }, 28 | "description": "SAPUI5 Resources" 29 | }, 30 | { 31 | "path": "/test-resources", 32 | "target": { 33 | "type": "service", 34 | "name": "sapui5", 35 | "entryPath": "/test-resources" 36 | }, 37 | "description": "SAPUI5 Test Resources" 38 | } 39 | ] 40 | } -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/Component.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/core/UIComponent", 3 | "sap/ui/Device", 4 | "xxx/model/models", 5 | "xxx/controller/ListSelector", 6 | "xxx/controller/ErrorHandler" 7 | ], function (UIComponent, Device, models, ListSelector, ErrorHandler) { 8 | "use strict"; 9 | 10 | return UIComponent.extend("xxx.Component", { 11 | 12 | metadata : { 13 | manifest : "json" 14 | }, 15 | 16 | /** 17 | * The component is initialized by UI5 automatically during the startup of the app and calls the init method once. 18 | * In this method, the FLP and device models are set and the router is initialized. 19 | * @public 20 | * @override 21 | */ 22 | init : function () { 23 | this.oListSelector = new ListSelector(); 24 | this._oErrorHandler = new ErrorHandler(this); 25 | 26 | // set the device model 27 | this.setModel(models.createDeviceModel(), "device"); 28 | // set the FLP model 29 | this.setModel(models.createFLPModel(), "FLP"); 30 | 31 | // call the base component's init function and create the App view 32 | UIComponent.prototype.init.apply(this, arguments); 33 | 34 | // create the views based on the url/hash 35 | this.getRouter().initialize(); 36 | }, 37 | 38 | /** 39 | * The component is destroyed by UI5 automatically. 40 | * In this method, the ListSelector and ErrorHandler are destroyed. 41 | * @public 42 | * @override 43 | */ 44 | destroy : function () { 45 | this.oListSelector.destroy(); 46 | this._oErrorHandler.destroy(); 47 | // call the base component's destroy function 48 | UIComponent.prototype.destroy.apply(this, arguments); 49 | }, 50 | 51 | /** 52 | * This method can be called to determine whether the sapUiSizeCompact or sapUiSizeCozy 53 | * design mode class should be set, which influences the size appearance of some controls. 54 | * @public 55 | * @return {string} css class, either 'sapUiSizeCompact' or 'sapUiSizeCozy' - or an empty string if no css class should be set 56 | */ 57 | getContentDensityClass : function() { 58 | if (this._sContentDensityClass === undefined) { 59 | // check whether FLP has already set the content density class; do nothing in this case 60 | if (jQuery(document.body).hasClass("sapUiSizeCozy") || jQuery(document.body).hasClass("sapUiSizeCompact")) { 61 | this._sContentDensityClass = ""; 62 | } else if (!Device.support.touch) { // apply "compact" mode if touch is not supported 63 | this._sContentDensityClass = "sapUiSizeCompact"; 64 | } else { 65 | // "cozy" in case of touch support; default for most sap.m controls, but needed for desktop-first controls like sap.ui.table.Table 66 | this._sContentDensityClass = "sapUiSizeCozy"; 67 | } 68 | } 69 | return this._sContentDensityClass; 70 | } 71 | 72 | }); 73 | 74 | } 75 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/controller/App.controller.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "xxx/controller/BaseController", 3 | "sap/ui/model/json/JSONModel" 4 | ], function (BaseController, JSONModel) { 5 | "use strict"; 6 | 7 | return BaseController.extend("xxx.controller.App", { 8 | 9 | onInit : function () { 10 | var oViewModel, 11 | fnSetAppNotBusy, 12 | oListSelector = this.getOwnerComponent().oListSelector, 13 | iOriginalBusyDelay = this.getView().getBusyIndicatorDelay(); 14 | 15 | oViewModel = new JSONModel({ 16 | busy : true, 17 | delay : 0 18 | }); 19 | this.setModel(oViewModel, "appView"); 20 | 21 | fnSetAppNotBusy = function() { 22 | oViewModel.setProperty("/busy", false); 23 | oViewModel.setProperty("/delay", iOriginalBusyDelay); 24 | }; 25 | 26 | this.getOwnerComponent().getModel().metadataLoaded() 27 | .then(fnSetAppNotBusy); 28 | 29 | // Makes sure that master view is hidden in split app 30 | // after a new list entry has been selected. 31 | oListSelector.attachListSelectionChange(function () { 32 | this.byId("idAppControl").hideMaster(); 33 | }, this); 34 | 35 | // apply content density mode to root view 36 | this.getView().addStyleClass(this.getOwnerComponent().getContentDensityClass()); 37 | } 38 | 39 | }); 40 | 41 | } 42 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/controller/BaseController.js: -------------------------------------------------------------------------------- 1 | /*global history */ 2 | sap.ui.define([ 3 | "sap/ui/core/mvc/Controller", 4 | "sap/ui/core/routing/History" 5 | ], function (Controller, History) { 6 | "use strict"; 7 | 8 | return Controller.extend("xxx.controller.BaseController", { 9 | /** 10 | * Convenience method for accessing the router in every controller of the application. 11 | * @public 12 | * @returns {sap.ui.core.routing.Router} the router for this component 13 | */ 14 | getRouter : function () { 15 | return this.getOwnerComponent().getRouter(); 16 | }, 17 | 18 | /** 19 | * Convenience method for getting the view model by name in every controller of the application. 20 | * @public 21 | * @param {string} sName the model name 22 | * @returns {sap.ui.model.Model} the model instance 23 | */ 24 | getModel : function (sName) { 25 | return this.getView().getModel(sName); 26 | }, 27 | 28 | /** 29 | * Convenience method for setting the view model in every controller of the application. 30 | * @public 31 | * @param {sap.ui.model.Model} oModel the model instance 32 | * @param {string} sName the model name 33 | * @returns {sap.ui.mvc.View} the view instance 34 | */ 35 | setModel : function (oModel, sName) { 36 | return this.getView().setModel(oModel, sName); 37 | }, 38 | 39 | /** 40 | * Convenience method for getting the resource bundle. 41 | * @public 42 | * @returns {sap.ui.model.resource.ResourceModel} the resourceModel of the component 43 | */ 44 | getResourceBundle : function () { 45 | return this.getOwnerComponent().getModel("i18n").getResourceBundle(); 46 | }, 47 | 48 | /** 49 | * Event handler for navigating back. 50 | * It there is a history entry or an previous app-to-app navigation we go one step back in the browser history 51 | * If not, it will replace the current entry of the browser history with the master route. 52 | * @public 53 | */ 54 | onNavBack : function() { 55 | var sPreviousHash = History.getInstance().getPreviousHash(), 56 | oCrossAppNavigator = sap.ushell.Container.getService("CrossApplicationNavigation"); 57 | 58 | if (sPreviousHash !== undefined || !oCrossAppNavigator.isInitialNavigation()) { 59 | history.go(-1); 60 | } else { 61 | this.getRouter().navTo("master", {}, true); 62 | } 63 | } 64 | 65 | }); 66 | 67 | } 68 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/controller/ErrorHandler.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/base/Object", 3 | "sap/m/MessageBox" 4 | ], function (UI5Object, MessageBox) { 5 | "use strict"; 6 | 7 | return UI5Object.extend("xxx.controller.ErrorHandler", { 8 | 9 | /** 10 | * Handles application errors by automatically attaching to the model events and displaying errors when needed. 11 | * @class 12 | * @param {sap.ui.core.UIComponent} oComponent reference to the app's component 13 | * @public 14 | * @alias xxx.controller.ErrorHandler 15 | */ 16 | constructor : function (oComponent) { 17 | this._oResourceBundle = oComponent.getModel("i18n").getResourceBundle(); 18 | this._oComponent = oComponent; 19 | this._oModel = oComponent.getModel(); 20 | this._bMessageOpen = false; 21 | this._sErrorText = this._oResourceBundle.getText("errorText"); 22 | 23 | this._oModel.attachMetadataFailed(function (oEvent) { 24 | var oParams = oEvent.getParameters(); 25 | this._showServiceError(oParams.response); 26 | }, this); 27 | 28 | this._oModel.attachRequestFailed(function (oEvent) { 29 | var oParams = oEvent.getParameters(); 30 | // An entity that was not found in the service is also throwing a 404 error in oData. 31 | // We already cover this case with a notFound target so we skip it here. 32 | // A request that cannot be sent to the server is a technical error that we have to handle though 33 | if (oParams.response.statusCode !== "404" || (oParams.response.statusCode === 404 && oParams.response.responseText.indexOf("Cannot POST") === 0)) { 34 | this._showServiceError(oParams.response); 35 | } 36 | }, this); 37 | }, 38 | 39 | /** 40 | * Shows a {@link sap.m.MessageBox} when a service call has failed. 41 | * Only the first error message will be display. 42 | * @param {string} sDetails a technical error to be displayed on request 43 | * @private 44 | */ 45 | _showServiceError : function (sDetails) { 46 | if (this._bMessageOpen) { 47 | return; 48 | } 49 | this._bMessageOpen = true; 50 | MessageBox.error( 51 | this._sErrorText, 52 | { 53 | id : "serviceErrorMessageBox", 54 | details : sDetails, 55 | styleClass : this._oComponent.getContentDensityClass(), 56 | actions : [MessageBox.Action.CLOSE], 57 | onClose : function () { 58 | this._bMessageOpen = false; 59 | }.bind(this) 60 | } 61 | ); 62 | } 63 | 64 | }); 65 | 66 | } 67 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/i18n/i18n.properties: -------------------------------------------------------------------------------- 1 | # This is the resource bundle for Business Partners List 2 | 3 | #XTIT: Application name 4 | appTitle=Business Partners List 5 | 6 | #YDES: Application description 7 | appDescription=BP Explorer App 8 | 9 | #~~~ Master View ~~~~~~~~~~~~~~~~~~~~~~~~~~ 10 | 11 | #XTIT: Master view title with placeholder for the number of items 12 | masterTitleCount= ({0}) 13 | 14 | #XTOL: Tooltip for the search field 15 | masterSearchTooltip=Enter an name or a part of it. 16 | 17 | #XBLI: text for a list with no data 18 | masterListNoDataText=No are currently available 19 | 20 | #XBLI: text for a list with no data with filter or search 21 | masterListNoDataWithFilterOrSearchText=No matching found 22 | 23 | #XSEL: Option to sort the master list by CardName 24 | masterSort1=Sort By 25 | 26 | #XSEL: Option to sort the master list by Balance 27 | masterSort2=Sort By 28 | 29 | #XSEL: Option to filter the master list by Balance 30 | masterFilterName= 31 | 32 | #XSEL: Option to not filter the master list 33 | masterFilterNone=none 34 | 35 | #XSEL: Option to filter the master list by Currency if the value is less than 100 36 | masterFilter1=<100 37 | 38 | #XSEL: Option to filter the master list by Currency if the value is greater than 100 39 | masterFilter2=>100 40 | 41 | #YMSG: Filter text that is displayed above the master list 42 | masterFilterBarText=Filtered by {0} 43 | 44 | #XSEL: Option to not group the master list 45 | masterGroupNone=(Not grouped) 46 | 47 | #XSEL: Option to group the master list by Balance 48 | masterGroup1= Group 49 | 50 | #XGRP: Group header Balance 51 | masterGroup1Header1= 20 or less 52 | 53 | #XGRP: Group header Balance 54 | masterGroup1Header2= higher than 20 55 | 56 | #~~~ Detail View ~~~~~~~~~~~~~~~~~~~~~~~~~~ 57 | 58 | #XTIT: Detail view title 59 | detailTitle= 60 | 61 | #XTOL: Icon Tab Bar Info 62 | detailIconTabBarInfo=Info 63 | 64 | #XTOL: Icon Tab Bar Attachments 65 | detailIconTabBarAttachments=Attachments 66 | 67 | #XBLI: Text for the BPAddress table with no data 68 | detailLineItemTableNoDataText=No BPAddress 69 | 70 | #XTIT: Title of the BPAddress table 71 | detailLineItemTableHeading=BPAddresses 72 | 73 | #XTIT: Title of the BPAddress table 74 | detailLineItemTableHeadingCount=BPAddresses ({0}) 75 | 76 | detailLineItemContactTableNoDataText=No Contact 77 | 78 | #XTIT: Title of the BPAddress table 79 | detailLineItemContactTableHeading=Contacts 80 | 81 | #XTIT: Title of the BPAddress table 82 | detailLineItemContactTableHeadingCount=Contacts ({0}) 83 | 84 | #XGRP: Title for the Address column in the BPAddress table 85 | detailLineItemTableIDColumn=Address 86 | 87 | detailLineItemTableStreetColumn=Street 88 | 89 | detailLineItemTableContactNameColumn=Name 90 | 91 | detailLineItemTableContactPhoneColumn=Telephone 92 | 93 | #XGRP: Title for the column in the BPAddress table 94 | detailLineItemTableUnitNumberColumn= 95 | 96 | #XTIT: Save as tile app title 97 | shareSaveTileAppTitle=Business Partners List - {0} 98 | 99 | #XTIT: Send E-Mail subject 100 | shareSendEmailObjectSubject= {0} 101 | 102 | #YMSG: Send E-Mail message 103 | shareSendEmailObjectMessage= {0} (id: {1})\r\n{2} 104 | 105 | #~~~ Not Found View ~~~~~~~~~~~~~~~~~~~~~~~ 106 | 107 | #XTIT: Not found view title 108 | notFoundTitle=Not Found 109 | 110 | #YMSG: The BusinessPartner not found text is displayed when there is no BusinessPartner with this id 111 | noObjectFoundText=This is not available 112 | 113 | #YMSG: The BusinessPartner not available text is displayed when there is no data when starting the app 114 | noObjectsAvailableText=No are currently available 115 | 116 | #YMSG: The not found text is displayed when there was an error loading the resource (404 error) 117 | notFoundText=The requested resource was not found 118 | 119 | #~~~ Not Available View ~~~~~~~~~~~~~~~~~~~~~~~ 120 | 121 | #XTIT: Master view title 122 | notAvailableViewTitle= 123 | 124 | #~~~ Error Handling ~~~~~~~~~~~~~~~~~~~~~~~ 125 | 126 | #YMSG: Error dialog description 127 | errorText=Sorry, a technical error occurred! Please try again later. -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/localService/mockserver.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/core/util/MockServer" 3 | ], function (MockServer) { 4 | "use strict"; 5 | 6 | var oMockServer, 7 | _sAppModulePath = "xxx/", 8 | _sJsonFilesModulePath = _sAppModulePath + "localService/mockdata"; 9 | 10 | return { 11 | /** 12 | * Initializes the mock server. 13 | * You can configure the delay with the URL parameter "serverDelay". 14 | * The local mock data in this folder is returned instead of the real data for testing. 15 | * @public 16 | */ 17 | 18 | init : function () { 19 | var oUriParameters = jQuery.sap.getUriParameters(), 20 | sJsonFilesUrl = jQuery.sap.getModulePath(_sJsonFilesModulePath), 21 | sManifestUrl = jQuery.sap.getModulePath(_sAppModulePath + "manifest", ".json"), 22 | sEntity = "BusinessPartner", 23 | sErrorParam = oUriParameters.get("errorType"), 24 | iErrorCode = sErrorParam === "badRequest" ? 400 : 500, 25 | oManifest = jQuery.sap.syncGetJSON(sManifestUrl).data, 26 | oMainDataSource = oManifest["sap.app"].dataSources.mainService, 27 | sMetadataUrl = jQuery.sap.getModulePath(_sAppModulePath + oMainDataSource.settings.localUri.replace(".xml", ""), ".xml"), 28 | // ensure there is a trailing slash 29 | sMockServerUrl = /.*\/$/.test(oMainDataSource.uri) ? oMainDataSource.uri : oMainDataSource.uri + "/"; 30 | 31 | oMockServer = new MockServer({ 32 | rootUri : sMockServerUrl 33 | }); 34 | 35 | // configure mock server with a delay of 1s 36 | MockServer.config({ 37 | autoRespond : true, 38 | autoRespondAfter : (oUriParameters.get("serverDelay") || 1000) 39 | }); 40 | 41 | oMockServer.simulate(sMetadataUrl, { 42 | sMockdataBaseUrl : sJsonFilesUrl, 43 | bGenerateMissingMockData : true 44 | }); 45 | 46 | var aRequests = oMockServer.getRequests(), 47 | fnResponse = function (iErrCode, sMessage, aRequest) { 48 | aRequest.response = function(oXhr){ 49 | oXhr.respond(iErrCode, {"Content-Type": "text/plain;charset=utf-8"}, sMessage); 50 | }; 51 | }; 52 | 53 | // handling the metadata error test 54 | if (oUriParameters.get("metadataError")) { 55 | aRequests.forEach( function ( aEntry ) { 56 | if (aEntry.path.toString().indexOf("$metadata") > -1) { 57 | fnResponse(500, "metadata Error", aEntry); 58 | } 59 | }); 60 | } 61 | 62 | // Handling request errors 63 | if (sErrorParam) { 64 | aRequests.forEach( function ( aEntry ) { 65 | if (aEntry.path.toString().indexOf(sEntity) > -1) { 66 | fnResponse(iErrorCode, sErrorParam, aEntry); 67 | } 68 | }); 69 | } 70 | oMockServer.start(); 71 | 72 | jQuery.sap.log.info("Running the app with mock data"); 73 | }, 74 | 75 | /** 76 | * @public returns the mockserver of the app, should be used in integration tests 77 | * @returns {sap.ui.core.util.MockServer} the mockserver instance 78 | */ 79 | getMockServer : function () { 80 | return oMockServer; 81 | } 82 | }; 83 | 84 | } 85 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "_version": "1.4.0", 3 | "sap.app": { 4 | "id": "xxx", 5 | "type": "application", 6 | "i18n": "i18n/i18n.properties", 7 | "title": "{{appTitle}}", 8 | "description": "{{appDescription}}", 9 | "applicationVersion": { 10 | "version": "1.0.0" 11 | }, 12 | "dataSources": { 13 | "mainService": { 14 | "uri": "/BPExplorerApp/OData/BusinessPartner.xsodata", 15 | "type": "OData", 16 | "settings": { 17 | "odataVersion": "2.0", 18 | "localUri": "localService/metadata.xml" 19 | } 20 | } 21 | }, 22 | "sourceTemplate": { 23 | "id": "sap.ui.ui5-template-plugin.2masterdetail", 24 | "version": "1.38.1" 25 | } 26 | }, 27 | "sap.ui": { 28 | "technology": "UI5", 29 | "icons": { 30 | "icon": "sap-icon://detail-view", 31 | "favIcon": "", 32 | "phone": "", 33 | "phone@2": "", 34 | "tablet": "", 35 | "tablet@2": "" 36 | }, 37 | "deviceTypes": { 38 | "desktop": true, 39 | "tablet": true, 40 | "phone": true 41 | }, 42 | "supportedThemes": [ 43 | "sap_hcb", 44 | "sap_bluecrystal" 45 | ] 46 | }, 47 | "sap.ui5": { 48 | "rootView": { 49 | "viewName": "xxx.view.App", 50 | "type": "XML", 51 | "id": "app" 52 | }, 53 | "dependencies": { 54 | "minUI5Version": "1.42.0", 55 | "libs": { 56 | "sap.ui.core": { 57 | "minVersion": "1.42.0" 58 | }, 59 | "sap.m": { 60 | "minVersion": "1.42.0" 61 | }, 62 | "sap.ushell": { 63 | "minVersion": "1.42.0" 64 | }, 65 | "sap.collaboration": { 66 | "minVersion": "1.42", 67 | "lazy": true 68 | } 69 | } 70 | }, 71 | "contentDensities": { 72 | "compact": true, 73 | "cozy": true 74 | }, 75 | "models": { 76 | "i18n": { 77 | "type": "sap.ui.model.resource.ResourceModel", 78 | "settings": { 79 | "bundleName": "xxx.i18n.i18n" 80 | } 81 | }, 82 | "": { 83 | "dataSource": "mainService", 84 | "preload": true 85 | } 86 | }, 87 | "routing": { 88 | "config": { 89 | "routerClass": "sap.m.routing.Router", 90 | "viewType": "XML", 91 | "viewPath": "xxx.view", 92 | "controlId": "idAppControl", 93 | "controlAggregation": "detailPages", 94 | "bypassed": { 95 | "target": [ 96 | "master", 97 | "notFound" 98 | ] 99 | }, 100 | "async": true 101 | }, 102 | "routes": [ 103 | { 104 | "pattern": "", 105 | "name": "master", 106 | "target": [ 107 | "object", 108 | "master" 109 | ] 110 | }, 111 | { 112 | "pattern": "BusinessPartner/{objectId}", 113 | "name": "object", 114 | "target": [ 115 | "master", 116 | "object" 117 | ] 118 | } 119 | ], 120 | "targets": { 121 | "master": { 122 | "viewName": "Master", 123 | "viewLevel": 1, 124 | "viewId": "master", 125 | "controlAggregation": "masterPages" 126 | }, 127 | "object": { 128 | "viewName": "Detail", 129 | "viewId": "detail", 130 | "viewLevel": 2 131 | }, 132 | "detailObjectNotFound": { 133 | "viewName": "DetailObjectNotFound", 134 | "viewId": "detailObjectNotFound" 135 | }, 136 | "detailNoObjectsAvailable": { 137 | "viewName": "DetailNoObjectsAvailable", 138 | "viewId": "detailNoObjectsAvailable" 139 | }, 140 | "notFound": { 141 | "viewName": "NotFound", 142 | "viewId": "notFound" 143 | } 144 | } 145 | } 146 | } 147 | } -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/model/GroupSortState.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/base/Object", 3 | "sap/ui/model/Sorter" 4 | ], function (BaseObject, Sorter) { 5 | "use strict"; 6 | 7 | return BaseObject.extend("xxx.model.GroupSortState", { 8 | 9 | /** 10 | * Creates sorters and groupers for the master list. 11 | * Since grouping also means sorting, this class modifies the viewmodel. 12 | * If a user groups by a field, and there is a corresponding sort option, the option will be chosen. 13 | * If a user ungroups, the sorting will be reset to the default sorting. 14 | * @class 15 | * @public 16 | * @param {sap.ui.model.json.JSONModel} oViewModel the model of the current view 17 | * @param {function} fnGroupFunction the grouping function to be applied 18 | * @alias xxx.model.GroupSortState 19 | */ 20 | constructor: function (oViewModel, fnGroupFunction) { 21 | this._oViewModel = oViewModel; 22 | this._fnGroupFunction = fnGroupFunction; 23 | }, 24 | 25 | /** 26 | * Sorts by CardName, or by Balance 27 | * 28 | * @param {string} sKey - the key of the field used for grouping 29 | * @returns {sap.ui.model.Sorter[]} an array of sorters 30 | */ 31 | sort: function (sKey) { 32 | var sGroupedBy = this._oViewModel.getProperty("/groupBy"); 33 | 34 | if (sGroupedBy !== "None") { 35 | // If the list is grouped, remove the grouping since the user wants to sort by something different 36 | // Grouping only works if the list is primary sorted by the grouping - the first sorten contains a grouper function 37 | this._oViewModel.setProperty("/groupBy", "None"); 38 | } 39 | 40 | return [new Sorter(sKey, false)]; 41 | }, 42 | 43 | /** 44 | * Groups by Balance, or resets the grouping for the key "None" 45 | * 46 | * @param {string} sKey - the key of the field used for grouping 47 | * @returns {sap.ui.model.Sorter[]} an array of sorters 48 | */ 49 | group: function (sKey) { 50 | var aSorters = []; 51 | 52 | if (sKey === "Balance") { 53 | // Grouping means sorting so we set the select to the same Entity used for grouping 54 | this._oViewModel.setProperty("/sortBy", "Balance"); 55 | 56 | aSorters.push( 57 | new Sorter("Balance", false, 58 | this._fnGroupFunction.bind(this)) 59 | ); 60 | } else if (sKey === "None") { 61 | // select the default sorting again 62 | this._oViewModel.setProperty("/sortBy", "CardName"); 63 | } 64 | 65 | return aSorters; 66 | } 67 | 68 | }); 69 | }); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/model/formatter.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | ], function () { 3 | "use strict"; 4 | 5 | return { 6 | /** 7 | * Rounds the currency value to 2 digits 8 | * 9 | * @public 10 | * @param {string} sValue value to be formatted 11 | * @returns {string} formatted currency value with 2 digits 12 | */ 13 | currencyValue : function (sValue) { 14 | if (!sValue) { 15 | return ""; 16 | } 17 | 18 | return parseFloat(sValue).toFixed(2); 19 | } 20 | }; 21 | 22 | } 23 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/model/grouper.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | ], function () { 3 | "use strict"; 4 | 5 | /* 6 | * Use this file to implement your custom grouping functions 7 | * The predefined functions are simple examples and might be replaced by your more complex implementations 8 | * to be called with .bind() and handed over to a sap.ui.model.Sorter 9 | * return value for all your functions is an object with key-text pairs 10 | * the oContext parameter is not under your control! 11 | */ 12 | 13 | return { 14 | 15 | /** 16 | * Groups the items by a price in two groups: Lesser equal than 20 and greater than 20 17 | * This grouping function needs the resource bundle so we pass it as a dependency 18 | * @param oResourceBundle {sap.ui.model.resource.ResourceModel} the resource bundle of your i18n model 19 | * @returns {Function} the grouper function you can pass to your sorter 20 | */ 21 | groupUnitNumber : function (oResourceBundle) { 22 | return function (oContext) { 23 | var iPrice = oContext.getProperty("Balance"), 24 | sKey, 25 | sText; 26 | 27 | if (iPrice <= 20) { 28 | sKey = "LE20"; 29 | sText = oResourceBundle.getText("masterGroup1Header1"); 30 | } else { 31 | sKey = "GT20"; 32 | sText = oResourceBundle.getText("masterGroup1Header2"); 33 | } 34 | 35 | return { 36 | key: sKey, 37 | text: sText 38 | }; 39 | }; 40 | } 41 | 42 | }; 43 | } 44 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/model/models.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/model/json/JSONModel", 3 | "sap/ui/Device" 4 | ], function (JSONModel, Device) { 5 | "use strict"; 6 | 7 | return { 8 | createDeviceModel : function () { 9 | var oModel = new JSONModel(Device); 10 | oModel.setDefaultBindingMode("OneWay"); 11 | return oModel; 12 | }, 13 | 14 | createFLPModel : function () { 15 | var fnGetuser = jQuery.sap.getObject("sap.ushell.Container.getUser"), 16 | bIsShareInJamActive = fnGetuser ? fnGetuser().isJamActive() : false, 17 | oModel = new JSONModel({ 18 | isShareInJamActive: bIsShareInJamActive 19 | }); 20 | oModel.setDefaultBindingMode("OneWay"); 21 | return oModel; 22 | } 23 | }; 24 | 25 | } 26 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Testing Overview 5 | 6 | 7 | 8 | 9 |

Testing Overview

10 |

This is an overview page of various ways to test the generated app during development.
Choose one of the access points below to launch the app as a standalone application, e.g. on a Tomcat server.

11 | 12 | 23 | 24 | -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/flpSandbox.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Business Partners List 8 | 9 | 24 | 47 | 48 | 49 | 50 | 51 | 59 | 60 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/flpSandboxMockServer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Business Partners List 8 | 9 | 10 | 25 | 48 | 49 | 50 | 51 | 52 | 61 | 62 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/integration/AllJourneys.js: -------------------------------------------------------------------------------- 1 | jQuery.sap.require("sap.ui.qunit.qunit-css"); 2 | jQuery.sap.require("sap.ui.thirdparty.qunit"); 3 | jQuery.sap.require("sap.ui.qunit.qunit-junit"); 4 | QUnit.config.autostart = false; 5 | 6 | // We cannot provide stable mock data out of the template. 7 | // If you introduce mock data, by adding .json files in your webapp/localService/mockdata folder you have to provide the following minimum data: 8 | // * At least 3 BusinessPartner in the list 9 | // * All 3 BusinessPartner have at least one BPAddress 10 | 11 | sap.ui.require([ 12 | "sap/ui/test/Opa5", 13 | "xxx/test/integration/pages/Common", 14 | "sap/ui/test/opaQunit", 15 | "xxx/test/integration/pages/App", 16 | "xxx/test/integration/pages/Browser", 17 | "xxx/test/integration/pages/Master", 18 | "xxx/test/integration/pages/Detail", 19 | "xxx/test/integration/pages/NotFound" 20 | ], function (Opa5, Common) { 21 | "use strict"; 22 | Opa5.extendConfig({ 23 | arrangements: new Common(), 24 | viewNamespace: "xxx.view." 25 | }); 26 | 27 | sap.ui.require([ 28 | "xxx/test/integration/MasterJourney", 29 | "xxx/test/integration/NavigationJourney", 30 | "xxx/test/integration/NotFoundJourney", 31 | "xxx/test/integration/BusyJourney", 32 | "xxx/test/integration/FLPIntegrationJourney" 33 | ], function () { 34 | QUnit.start(); 35 | }); 36 | }); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/integration/BusyJourney.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/test/opaQunit" 3 | ], function (opaTest) { 4 | "use strict"; 5 | 6 | QUnit.module("Desktop busy indication"); 7 | 8 | opaTest("Should see a global busy indication while loading the metadata", function (Given, When, Then) { 9 | // Arrangements 10 | Given.iStartTheAppWithDelay("", 5000); 11 | 12 | // Actions 13 | When.onTheAppPage.iLookAtTheScreen(); 14 | 15 | // Assertions 16 | Then.onTheAppPage.iShouldSeeTheBusyIndicator(). 17 | and.iTeardownMyAppFrame(); 18 | }); 19 | 20 | opaTest("Should see a busy indication on the master and detail after loading the metadata", function (Given, When, Then) { 21 | // Arrangements 22 | Given.iStartTheAppWithDelay("", 2000); 23 | 24 | // Actions 25 | When.onTheAppPage.iWaitUntilTheBusyIndicatorIsGone(); 26 | 27 | // Assertions 28 | Then.onTheMasterPage.iShouldSeeTheBusyIndicator(). 29 | and.theListHeaderDisplaysZeroHits(); 30 | Then.onTheDetailPage.iShouldSeeTheBusyIndicator(). 31 | and.iTeardownMyAppFrame(); 32 | }); 33 | 34 | } 35 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/integration/BusyJourneyPhone.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/test/opaQunit" 3 | ], function (opaTest) { 4 | "use strict"; 5 | 6 | QUnit.module("Phone busy indication"); 7 | 8 | opaTest("Should see a global busy indication while loading the metadata", function (Given, When, Then) { 9 | // Arrangements 10 | Given.iStartTheAppWithDelay("", 5000); 11 | 12 | //Actions 13 | When.onTheAppPage.iLookAtTheScreen(); 14 | 15 | // Assertions 16 | Then.onTheAppPage.iShouldSeeTheBusyIndicator(). 17 | and.iTeardownMyAppFrame(); 18 | }); 19 | 20 | opaTest("Should see a busy indication on the master after loading the metadata", function (Given, When, Then) { 21 | // Arrangements 22 | Given.iStartTheAppWithDelay("", 2000); 23 | 24 | //Actions 25 | When.onTheAppPage.iWaitUntilTheBusyIndicatorIsGone(); 26 | 27 | // Assertions 28 | Then.onTheMasterPage.iShouldSeeTheBusyIndicator(). 29 | and.iTeardownMyAppFrame(); 30 | }); 31 | 32 | } 33 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/integration/FLPIntegrationJourney.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/test/opaQunit" 3 | ], function (opaTest) { 4 | "use strict"; 5 | 6 | QUnit.module("FLP Integration"); 7 | 8 | opaTest("Should open the share menu and display the share buttons on the detail page", function (Given, When, Then) { 9 | // Arrangements 10 | Given.iStartTheApp(); 11 | 12 | // Actions 13 | When.onTheMasterPage.iRememberTheSelectedItem(); 14 | 15 | // Assertions 16 | Then.onTheDetailPage.iShouldSeeTheRememberedObject(); 17 | 18 | // Actions 19 | When.onTheDetailPage.iPressOnTheShareButton(); 20 | 21 | // Assertions 22 | Then.onTheDetailPage.iShouldSeeTheShareEmailButton(). 23 | and.iShouldSeeTheShareTileButton(). 24 | and.theShareTileButtonShouldContainTheRememberedObjectName(). 25 | and.iTeardownMyAppFrame(); 26 | }); 27 | 28 | } 29 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/integration/MasterJourney.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/test/opaQunit" 3 | ], function (opaTest) { 4 | "use strict"; 5 | 6 | QUnit.module("Master List"); 7 | 8 | opaTest("Should see the master list with all entries", function (Given, When, Then) { 9 | // Arrangements 10 | Given.iStartTheApp(); 11 | 12 | //Actions 13 | When.onTheMasterPage.iLookAtTheScreen(); 14 | 15 | // Assertions 16 | Then.onTheMasterPage.iShouldSeeTheList(). 17 | and.theListShouldHaveAllEntries(). 18 | and.theHeaderShouldDisplayAllEntries(). 19 | and.theListShouldContainOnlyFormattedUnitNumbers(); 20 | }); 21 | 22 | opaTest("Search for the First object should deliver results that contain the firstObject in the name", function (Given, When, Then) { 23 | //Actions 24 | When.onTheMasterPage.iSearchForTheFirstObject(); 25 | 26 | // Assertions 27 | Then.onTheMasterPage.theListShowsOnlyObjectsWithTheSearchStringInTheirTitle(); 28 | }); 29 | 30 | opaTest("Entering something that cannot be found into search field and pressing search field's refresh should leave the list as it was", function (Given, When, Then) { 31 | //Actions 32 | When.onTheMasterPage.iTypeSomethingInTheSearchThatCannotBeFoundAndTriggerRefresh(); 33 | 34 | // Assertions 35 | Then.onTheMasterPage.theListHasEntries(); 36 | }); 37 | 38 | opaTest("Entering something that cannot be found into search field and pressing 'search' should display the list's 'not found' message", function (Given, When, Then) { 39 | //Actions 40 | When.onTheMasterPage.iSearchForSomethingWithNoResults(); 41 | 42 | // Assertions 43 | Then.onTheMasterPage.iShouldSeeTheNoDataTextForNoSearchResults(). 44 | and.theListHeaderDisplaysZeroHits(); 45 | }); 46 | 47 | opaTest("Should display items again if the searchfield is emptied", function (Given, When, Then) { 48 | //Actions 49 | When.onTheMasterPage.iClearTheSearch(); 50 | 51 | // Assertions 52 | Then.onTheMasterPage.theListShouldHaveAllEntries(); 53 | }); 54 | 55 | opaTest("MasterList Sorting on UnitNumber", function(Given, When, Then) { 56 | // Actions 57 | When.onTheMasterPage.iSortTheListOnUnitNumber(); 58 | 59 | // Assertions 60 | Then.onTheMasterPage.theListShouldBeSortedAscendingOnUnitNumber(); 61 | }); 62 | 63 | opaTest("MasterList Sorting on Name", function(Given, When, Then) { 64 | // Actions 65 | When.onTheMasterPage.iSortTheListOnName(); 66 | 67 | // Assertions 68 | Then.onTheMasterPage.theListShouldBeSortedAscendingOnName(); 69 | }); 70 | 71 | opaTest("MasterList Filtering on Balance less than 100", function(Given, When, Then) { 72 | 73 | // Action 74 | When.onTheMasterPage.iOpenViewSettingsDialog(). 75 | and.iSelectListItemInViewSettingsDialog(""). 76 | and.iSelectListItemInViewSettingsDialog("<100 "). 77 | and.iPressOKInViewSelectionDialog(); 78 | 79 | // Assertion 80 | Then.onTheMasterPage.theMasterListShouldBeFilteredOnUnitNumberValueLessThanTheGroupBoundary(); 81 | }); 82 | 83 | opaTest("MasterList Filtering on Balance more than 100", function(Given, When, Then) { 84 | // Action 85 | When.onTheMasterPage.iOpenViewSettingsDialog(). 86 | and.iSelectListItemInViewSettingsDialog(">100 "). 87 | and.iPressOKInViewSelectionDialog(); 88 | 89 | // Assertion 90 | Then.onTheMasterPage.theMasterListShouldBeFilteredOnUnitNumberValueMoreThanTheGroupBoundary(); 91 | }); 92 | 93 | opaTest("MasterList remove filter should display all items", function(Given, When, Then) { 94 | // Action 95 | When.onTheMasterPage.iOpenViewSettingsDialog(). 96 | and.iPressResetInViewSelectionDialog(). 97 | and.iPressOKInViewSelectionDialog(); 98 | 99 | // Assertion 100 | Then.onTheMasterPage.theListShouldHaveAllEntries(); 101 | }); 102 | 103 | opaTest("MasterList grouping created group headers", function(Given, When, Then) { 104 | // Action 105 | When.onTheMasterPage.iGroupTheList(); 106 | 107 | // Assertion 108 | Then.onTheMasterPage.theListShouldContainAGroupHeader(); 109 | }); 110 | 111 | opaTest("Remove grouping from MasterList delivers initial list", function(Given, When, Then) { 112 | // Action 113 | When.onTheMasterPage.iRemoveListGrouping(); 114 | 115 | // Assertion 116 | Then.onTheMasterPage.theListShouldNotContainGroupHeaders(). 117 | and.theListShouldHaveAllEntries(); 118 | }); 119 | 120 | opaTest("Grouping the master list and filtering it by the object identifier should deliver the initial list", function(Given, When, Then) { 121 | // Action 122 | When.onTheMasterPage.iGroupTheList(). 123 | and.iSortTheListOnName(); 124 | 125 | // Assertion 126 | Then.onTheMasterPage.theListShouldNotContainGroupHeaders(). 127 | and.iTeardownMyAppFrame(); 128 | }); 129 | 130 | } 131 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/integration/NavigationJourney.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/test/opaQunit" 3 | ], function (opaTest) { 4 | "use strict"; 5 | 6 | QUnit.module("Desktop navigation"); 7 | 8 | opaTest("Should start the app with empty hash: the hash should reflect the selection of the first item in the list", function (Given, When, Then) { 9 | // Arrangements 10 | Given.iStartTheApp(); 11 | 12 | //Actions 13 | When.onTheMasterPage.iRememberTheSelectedItem(); 14 | 15 | // Assertions 16 | Then.onTheMasterPage.theFirstItemShouldBeSelected(); 17 | Then.onTheDetailPage.iShouldSeeTheRememberedObject().and.iShouldSeeNoBusyIndicator(); 18 | Then.onTheBrowserPage.iShouldSeeTheHashForTheRememberedObject(); 19 | }); 20 | 21 | opaTest("Should react on hashchange", function (Given, When, Then) { 22 | // Actions 23 | When.onTheMasterPage.iRememberTheIdOfListItemAtPosition(2); 24 | When.onTheBrowserPage.iChangeTheHashToTheRememberedItem(); 25 | 26 | // Assertions 27 | Then.onTheDetailPage.iShouldSeeTheRememberedObject().and.iShouldSeeNoBusyIndicator(); 28 | Then.onTheMasterPage.theRememberedListItemShouldBeSelected(); 29 | }); 30 | 31 | 32 | opaTest("Should navigate on press", function (Given, When, Then) { 33 | // Actions 34 | When.onTheMasterPage.iRememberTheIdOfListItemAtPosition(1). 35 | and.iPressOnTheObjectAtPosition(1); 36 | 37 | // Assertions 38 | Then.onTheDetailPage.iShouldSeeTheRememberedObject(); 39 | }); 40 | 41 | opaTest("Detail Page Shows Object Details", function (Given, When, Then) { 42 | // Actions 43 | When.onTheDetailPage.iLookAtTheScreen(); 44 | 45 | // Assertions 46 | Then.onTheDetailPage.iShouldSeeTheObjectLineItemsList(). 47 | and.theDetailViewShouldContainOnlyFormattedUnitNumbers(). 48 | and.theLineItemsListShouldHaveTheCorrectNumberOfItems(). 49 | and.theLineItemsHeaderShouldDisplayTheAmountOfEntries(); 50 | 51 | }); 52 | 53 | opaTest("Navigate to an object not on the client: no item should be selected and the object page should be displayed", function (Given, When, Then) { 54 | //Actions 55 | When.onTheMasterPage.iRememberAnIdOfAnObjectThatsNotInTheList(); 56 | When.onTheBrowserPage.iChangeTheHashToTheRememberedItem(); 57 | 58 | // Assertions 59 | Then.onTheDetailPage.iShouldSeeTheRememberedObject(). 60 | and.iTeardownMyAppFrame(); 61 | }); 62 | 63 | opaTest("Start the App and simulate metadata error: MessageBox should be shown", function (Given, When, Then) { 64 | //Arrangement 65 | Given.iStartMyAppOnADesktopToTestErrorHandler("metadataError=true"); 66 | 67 | // Assertions 68 | Then.onTheAppPage.iShouldSeeTheMessageBox(). 69 | and.iTeardownMyAppFrame(); 70 | }); 71 | 72 | opaTest("Start the App and simulate bad request error: MessageBox should be shown", function (Given, When, Then) { 73 | //Arrangement 74 | Given.iStartMyAppOnADesktopToTestErrorHandler("errorType=serverError"); 75 | 76 | // Assertions 77 | Then.onTheAppPage.iShouldSeeTheMessageBox(). 78 | and.iTeardownMyAppFrame(); 79 | }); 80 | 81 | } 82 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/integration/NavigationJourneyPhone.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/test/opaQunit" 3 | ], function (opaTest) { 4 | "use strict"; 5 | 6 | QUnit.module("Phone navigation"); 7 | 8 | opaTest("Should see the objects list", function (Given, When, Then) { 9 | // Arrangements 10 | Given.iStartTheApp(); 11 | 12 | //Actions 13 | When.onTheMasterPage.iLookAtTheScreen(); 14 | 15 | // Assertions 16 | Then.onTheMasterPage.iShouldSeeTheList(); 17 | Then.onTheBrowserPage.iShouldSeeAnEmptyHash(); 18 | }); 19 | 20 | opaTest("Should react on hashchange", function (Given, When, Then) { 21 | // Actions 22 | When.onTheMasterPage.iRememberTheIdOfListItemAtPosition(3); 23 | When.onTheBrowserPage.iChangeTheHashToTheRememberedItem(); 24 | 25 | // Assertions 26 | Then.onTheDetailPage.iShouldSeeTheRememberedObject(); 27 | }); 28 | 29 | opaTest("Detail Page Shows Object Details", function (Given, When, Then) { 30 | // Actions 31 | When.onTheDetailPage.iLookAtTheScreen(); 32 | 33 | // Assertions 34 | Then.onTheDetailPage.iShouldSeeTheObjectLineItemsList(). 35 | and.theLineItemsListShouldHaveTheCorrectNumberOfItems(). 36 | and.theLineItemsHeaderShouldDisplayTheAmountOfEntries(); 37 | }); 38 | 39 | opaTest("Should navigate on press", function (Given, When, Then) { 40 | // Actions 41 | When.onTheDetailPage.iPressTheBackButton(); 42 | When.onTheMasterPage.iRememberTheIdOfListItemAtPosition(2). 43 | and.iPressOnTheObjectAtPosition(2); 44 | 45 | // Assertions 46 | Then.onTheDetailPage.iShouldSeeTheRememberedObject(). 47 | and.iTeardownMyAppFrame(); 48 | }); 49 | 50 | } 51 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/integration/NotFoundJourney.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/test/opaQunit" 3 | ], function (opaTest) { 4 | "use strict"; 5 | 6 | QUnit.module("Desktop not found"); 7 | 8 | opaTest("Should see the resource not found page and no selection in the master list when navigating to an invalid hash", function (Given, When, Then) { 9 | //Arrangement 10 | Given.iStartTheApp(); 11 | 12 | //Actions 13 | When.onTheMasterPage.iWaitUntilTheListIsLoaded() 14 | .and.iWaitUntilTheFirstItemIsSelected(); 15 | When.onTheBrowserPage.iChangeTheHashToSomethingInvalid(); 16 | 17 | // Assertions 18 | Then.onTheNotFoundPage.iShouldSeeTheNotFoundPage(). 19 | and.theNotFoundPageShouldSayResourceNotFound(); 20 | Then.onTheMasterPage.theListShouldHaveNoSelection(). 21 | and.iTeardownMyAppFrame(); 22 | }); 23 | 24 | opaTest("Should see the not found page if the hash is something that matches no route", function (Given, When, Then) { 25 | // Arrangements 26 | Given.iStartTheApp({ hash : "somethingThatDoesNotExist" }); 27 | 28 | // Actions 29 | When.onTheNotFoundPage.iLookAtTheScreen(); 30 | 31 | // Assertions 32 | Then.onTheNotFoundPage.iShouldSeeTheNotFoundPage(). 33 | and.theNotFoundPageShouldSayResourceNotFound(). 34 | and.iTeardownMyAppFrame(); 35 | }); 36 | 37 | opaTest("Should see the not found master and detail page if an invalid object id has been called", function (Given, When, Then) { 38 | // Arrangements 39 | Given.iStartTheApp({ hash : "/BusinessPartner/SomeInvalidObjectId" }); 40 | 41 | //Actions 42 | When.onTheNotFoundPage.iLookAtTheScreen(); 43 | 44 | // Assertions 45 | Then.onTheNotFoundPage.iShouldSeeTheObjectNotFoundPage(). 46 | and.theNotFoundPageShouldSayObjectNotFound(). 47 | and.iTeardownMyAppFrame(); 48 | }); 49 | 50 | opaTest("Should see the not found text for no search results", function (Given, When, Then) { 51 | // Arrangements 52 | Given.iStartTheApp(); 53 | 54 | //Actions 55 | When.onTheMasterPage.iSearchForSomethingWithNoResults(); 56 | 57 | // Assertions 58 | Then.onTheMasterPage.iShouldSeeTheNoDataTextForNoSearchResults(). 59 | and.iTeardownMyAppFrame(); 60 | }); 61 | 62 | } 63 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/integration/NotFoundJourneyPhone.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/test/opaQunit" 3 | ], function (opaTest) { 4 | "use strict"; 5 | 6 | QUnit.module("Phone not found"); 7 | 8 | opaTest("Should see the not found page if the hash is something that matches no route", function (Given, When, Then) { 9 | // Arrangements 10 | Given.iStartTheApp({ hash : "somethingThatDoesNotExist" }); 11 | 12 | // Actions 13 | When.onTheNotFoundPage.iLookAtTheScreen(); 14 | 15 | // Assertions 16 | Then.onTheNotFoundPage.iShouldSeeTheNotFoundPage(). 17 | and.theNotFoundPageShouldSayResourceNotFound(); 18 | }); 19 | 20 | opaTest("Should end up on the master list, if the back button is pressed", function (Given, When, Then) { 21 | // Actions 22 | When.onTheNotFoundPage.iPressTheBackButton("NotFound"); 23 | 24 | // Assertions 25 | Then.onTheMasterPage.iShouldSeeTheList(). 26 | and.iTeardownMyAppFrame(); 27 | }); 28 | 29 | opaTest("Should see the not found master and detail page if an invalid object id has been called", function (Given, When, Then) { 30 | // Arrangements 31 | Given.iStartTheApp({ hash : "/BusinessPartner/SomeInvalidObjectId" }); 32 | 33 | // Actions 34 | When.onTheNotFoundPage.iLookAtTheScreen(); 35 | 36 | // Assertions 37 | Then.onTheNotFoundPage.iShouldSeeTheObjectNotFoundPage(). 38 | and.theNotFoundPageShouldSayObjectNotFound(); 39 | }); 40 | 41 | opaTest("Should end up on the master list, if the back button is pressed", function (Given, When, Then) { 42 | // Actions 43 | When.onTheNotFoundPage.iPressTheBackButton("DetailObjectNotFound"); 44 | 45 | // Assertions 46 | Then.onTheMasterPage.iShouldSeeTheList(). 47 | and.iTeardownMyAppFrame(); 48 | }); 49 | 50 | opaTest("Should see the not found text for no search results", function (Given, When, Then) { 51 | // Arrangements 52 | Given.iStartTheApp(); 53 | 54 | // Actions 55 | When.onTheMasterPage.iSearchForSomethingWithNoResults(); 56 | 57 | // Assertions 58 | Then.onTheMasterPage.iShouldSeeTheNoDataTextForNoSearchResults(). 59 | and.iTeardownMyAppFrame(); 60 | }); 61 | 62 | } 63 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/integration/PhoneJourneys.js: -------------------------------------------------------------------------------- 1 | jQuery.sap.require("sap.ui.qunit.qunit-css"); 2 | jQuery.sap.require("sap.ui.thirdparty.qunit"); 3 | jQuery.sap.require("sap.ui.qunit.qunit-junit"); 4 | QUnit.config.autostart = false; 5 | 6 | sap.ui.require([ 7 | "sap/ui/test/Opa5", 8 | "xxx/test/integration/pages/Common", 9 | "sap/ui/test/opaQunit", 10 | "xxx/test/integration/pages/App", 11 | "xxx/test/integration/pages/Browser", 12 | "xxx/test/integration/pages/Master", 13 | "xxx/test/integration/pages/Detail", 14 | "xxx/test/integration/pages/NotFound" 15 | ], function (Opa5, Common) { 16 | "use strict"; 17 | Opa5.extendConfig({ 18 | arrangements: new Common(), 19 | viewNamespace: "xxx.view." 20 | }); 21 | 22 | sap.ui.require([ 23 | "xxx/test/integration/NavigationJourneyPhone", 24 | "xxx/test/integration/NotFoundJourneyPhone", 25 | "xxx/test/integration/BusyJourneyPhone" 26 | ], function () { 27 | QUnit.start(); 28 | }); 29 | }); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/integration/opaTests.qunit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Opa tests for Business Partners List 5 | 6 | 7 | 8 | 15 | 16 | 19 | 20 | 21 | 22 |
23 |
24 | 25 | -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/integration/opaTestsPhone.qunit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Opa tests for Business Partners List 5 | 6 | 7 | 8 | 15 | 16 | 19 | 20 | 21 | 22 |
23 |
24 | 25 | -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/integration/pages/App.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/test/Opa5", 3 | "xxx/test/integration/pages/Common", 4 | "sap/ui/test/matchers/PropertyStrictEquals" 5 | ], function(Opa5, Common, PropertyStrictEquals) { 6 | "use strict"; 7 | 8 | var sViewName = "App", 9 | sAppControl = "idAppControl"; 10 | 11 | Opa5.createPageObjects({ 12 | onTheAppPage : { 13 | baseClass : Common, 14 | 15 | actions : { 16 | 17 | iWaitUntilTheBusyIndicatorIsGone : function () { 18 | return this.waitFor({ 19 | id : sAppControl, 20 | viewName : sViewName, 21 | // inline-matcher directly as function 22 | matchers : function(oRootView) { 23 | // we set the view busy, so we need to query the parent of the app 24 | return oRootView.getParent().getBusy() === false; 25 | }, 26 | errorMessage : "The app is still busy." 27 | }); 28 | } 29 | 30 | }, 31 | 32 | assertions : { 33 | 34 | iShouldSeeTheBusyIndicator : function () { 35 | return this.waitFor({ 36 | id : sAppControl, 37 | viewName : sViewName, 38 | success : function (oRootView) { 39 | // we set the view busy, so we need to query the parent of the app 40 | Opa5.assert.ok(oRootView.getParent().getBusy(), "The app is busy"); 41 | }, 42 | errorMessage : "The app is not busy." 43 | }); 44 | }, 45 | 46 | iShouldSeeTheMessageBox : function () { 47 | return this.waitFor({ 48 | searchOpenDialogs: true, 49 | controlType: "sap.m.Dialog", 50 | matchers : new PropertyStrictEquals({ name: "type", value: "Message"}), 51 | success: function () { 52 | Opa5.assert.ok(true, "The correct MessageBox was shown"); 53 | } 54 | }); 55 | } 56 | 57 | } 58 | 59 | } 60 | 61 | }); 62 | 63 | } 64 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/integration/pages/Browser.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/test/Opa5", 3 | "xxx/test/integration/pages/Common" 4 | ], function(Opa5, Common) { 5 | "use strict"; 6 | 7 | Opa5.createPageObjects({ 8 | onTheBrowserPage : { 9 | baseClass : Common, 10 | 11 | actions : { 12 | 13 | iChangeTheHashToObjectN : function (iObjIndex) { 14 | return this.waitFor(this.createAWaitForAnEntitySet({ 15 | entitySet : "Objects", 16 | success : function (aEntitySet) { 17 | Opa5.getHashChanger().setHash("/BusinessPartner/" + aEntitySet[iObjIndex].CardCode); 18 | } 19 | })); 20 | }, 21 | 22 | iChangeTheHashToTheRememberedItem : function () { 23 | return this.waitFor({ 24 | success : function () { 25 | var sObjectId = this.getContext().currentItem.id; 26 | Opa5.getHashChanger().setHash("/BusinessPartner/" + sObjectId); 27 | } 28 | }); 29 | }, 30 | 31 | iChangeTheHashToSomethingInvalid : function () { 32 | return this.waitFor({ 33 | success : function () { 34 | Opa5.getHashChanger().setHash("/somethingInvalid"); 35 | } 36 | }); 37 | } 38 | 39 | }, 40 | 41 | assertions : { 42 | 43 | iShouldSeeTheHashForObjectN : function (iObjIndex) { 44 | return this.waitFor(this.createAWaitForAnEntitySet({ 45 | entitySet : "Objects", 46 | success : function (aEntitySet) { 47 | var oHashChanger = Opa5.getHashChanger(), 48 | sHash = oHashChanger.getHash(); 49 | Opa5.assert.strictEqual(sHash, "BusinessPartner/" + aEntitySet[iObjIndex].CardCode, "The Hash is not correct"); 50 | } 51 | })); 52 | }, 53 | 54 | iShouldSeeTheHashForTheRememberedObject : function () { 55 | return this.waitFor({ 56 | success : function () { 57 | var sObjectId = this.getContext().currentItem.id, 58 | oHashChanger = Opa5.getHashChanger(), 59 | sHash = oHashChanger.getHash(); 60 | Opa5.assert.strictEqual(sHash, "BusinessPartner/" + sObjectId, "The Hash is not correct"); 61 | } 62 | }); 63 | }, 64 | 65 | iShouldSeeAnEmptyHash : function () { 66 | return this.waitFor({ 67 | success : function () { 68 | var oHashChanger = Opa5.getHashChanger(), 69 | sHash = oHashChanger.getHash(); 70 | Opa5.assert.strictEqual(sHash, "", "The Hash should be empty"); 71 | }, 72 | errorMessage : "The Hash is not Correct!" 73 | }); 74 | } 75 | 76 | } 77 | 78 | } 79 | 80 | }); 81 | 82 | } 83 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/integration/pages/Common.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/test/Opa5" 3 | ], function(Opa5) { 4 | "use strict"; 5 | 6 | function getFrameUrl (sHash, sUrlParameters) { 7 | var sUrl = jQuery.sap.getResourcePath("xxx/app", ".html"); 8 | sHash = sHash || ""; 9 | sUrlParameters = sUrlParameters ? "?" + sUrlParameters : ""; 10 | 11 | if (sHash) { 12 | sHash = "#BusinessPartnersList-display&/" + (sHash.indexOf("/") === 0 ? sHash.substring(1) : sHash); 13 | } else { 14 | sHash = "#BusinessPartnersList-display"; 15 | } 16 | 17 | return sUrl + sUrlParameters + sHash; 18 | } 19 | 20 | return Opa5.extend("xxx.test.integration.pages.Common", { 21 | 22 | iStartTheApp : function (oOptions) { 23 | oOptions = oOptions || {}; 24 | // Start the app with a minimal delay to make tests run fast but still async to discover basic timing issues 25 | this.iStartMyAppInAFrame(getFrameUrl(oOptions.hash, "serverDelay=50")); 26 | }, 27 | 28 | iStartTheAppWithDelay : function (sHash, iDelay) { 29 | this.iStartMyAppInAFrame(getFrameUrl(sHash, "serverDelay=" + iDelay)); 30 | }, 31 | 32 | iLookAtTheScreen : function () { 33 | return this; 34 | }, 35 | 36 | iStartMyAppOnADesktopToTestErrorHandler : function (sParam) { 37 | this.iStartMyAppInAFrame(getFrameUrl("", sParam)); 38 | }, 39 | 40 | createAWaitForAnEntitySet : function (oOptions) { 41 | return { 42 | success: function () { 43 | var bMockServerAvailable = false, 44 | aEntitySet; 45 | 46 | this.getMockServer().then(function (oMockServer) { 47 | aEntitySet = oMockServer.getEntitySetData(oOptions.entitySet); 48 | bMockServerAvailable = true; 49 | }); 50 | 51 | return this.waitFor({ 52 | check: function () { 53 | return bMockServerAvailable; 54 | }, 55 | success : function () { 56 | oOptions.success.call(this, aEntitySet); 57 | } 58 | }); 59 | } 60 | }; 61 | }, 62 | 63 | getMockServer : function () { 64 | return new Promise(function (success) { 65 | Opa5.getWindow().sap.ui.require(["xxx/localService/mockserver"], function (mockserver) { 66 | success(mockserver.getMockServer()); 67 | }); 68 | }); 69 | }, 70 | 71 | theUnitNumbersShouldHaveTwoDecimals : function (sControlType, sViewName, sSuccessMsg, sErrMsg) { 72 | var rTwoDecimalPlaces = /^-?\d+\.\d{2}$/; 73 | 74 | return this.waitFor({ 75 | controlType : sControlType, 76 | viewName : sViewName, 77 | success : function (aNumberControls) { 78 | Opa5.assert.ok(aNumberControls.every(function(oNumberControl){ 79 | return rTwoDecimalPlaces.test(oNumberControl.getNumber()); 80 | }), 81 | sSuccessMsg); 82 | }, 83 | errorMessage : sErrMsg 84 | }); 85 | } 86 | 87 | }); 88 | 89 | } 90 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/integration/pages/NotFound.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/test/Opa5", 3 | "sap/ui/test/actions/Press", 4 | "sap/ui/test/matchers/PropertyStrictEquals", 5 | "xxx/test/integration/pages/Common" 6 | ], function(Opa5, Press, PropertyStrictEquals, Common) { 7 | "use strict"; 8 | 9 | var sNotFoundPageId = "page", 10 | sNotFoundView = "NotFound", 11 | sDetailNotFoundView = "DetailObjectNotFound"; 12 | 13 | Opa5.createPageObjects({ 14 | onTheNotFoundPage : { 15 | baseClass : Common, 16 | 17 | actions : { 18 | 19 | iPressTheBackButton : function (sViewName) { 20 | return this.waitFor({ 21 | viewName : sViewName, 22 | controlType : "sap.m.Button", 23 | matchers: new PropertyStrictEquals({name : "type", value : "Back"}), 24 | actions : new Press(), 25 | errorMessage : "Did not find the back button" 26 | }); 27 | } 28 | 29 | }, 30 | 31 | assertions : { 32 | 33 | iShouldSeeTheNotFoundGeneralPage : function (sPageId, sPageViewName) { 34 | return this.waitFor({ 35 | controlType : "sap.m.MessagePage", 36 | viewName : sPageViewName, 37 | success : function () { 38 | Opa5.assert.ok(true, "Shows the message page"); 39 | }, 40 | errorMessage : "Did not reach the empty page" 41 | }); 42 | }, 43 | 44 | iShouldSeeTheNotFoundPage : function () { 45 | return this.iShouldSeeTheNotFoundGeneralPage(sNotFoundPageId, sNotFoundView); 46 | }, 47 | 48 | iShouldSeeTheObjectNotFoundPage : function () { 49 | return this.iShouldSeeTheNotFoundGeneralPage(sNotFoundPageId, sDetailNotFoundView); 50 | }, 51 | 52 | theNotFoundPageShouldSayResourceNotFound : function () { 53 | return this.waitFor({ 54 | id : sNotFoundPageId, 55 | viewName : sNotFoundView, 56 | success : function (oPage) { 57 | Opa5.assert.strictEqual(oPage.getTitle(), oPage.getModel("i18n").getProperty("notFoundTitle"), "The not found text is shown as title"); 58 | Opa5.assert.strictEqual(oPage.getText(), oPage.getModel("i18n").getProperty("notFoundText"), "The resource not found text is shown"); 59 | }, 60 | errorMessage : "Did not display the resource not found text" 61 | }); 62 | }, 63 | 64 | theNotFoundPageShouldSayObjectNotFound : function () { 65 | return this.waitFor({ 66 | id : sNotFoundPageId, 67 | viewName : sDetailNotFoundView, 68 | success : function (oPage) { 69 | Opa5.assert.strictEqual(oPage.getTitle(), oPage.getModel("i18n").getProperty("detailTitle"), "The object text is shown as title"); 70 | Opa5.assert.strictEqual(oPage.getText(), oPage.getModel("i18n").getProperty("noObjectFoundText"), "The object not found text is shown"); 71 | }, 72 | errorMessage : "Did not display the object not found text" 73 | }); 74 | } 75 | 76 | } 77 | 78 | } 79 | 80 | }); 81 | 82 | } 83 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/testsuite.qunit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | QUnit TestSuite for Business Partners List 5 | 6 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/unit/allTests.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "test/unit/model/models", 3 | "test/unit/model/formatter", 4 | "test/unit/controller/App.controller", 5 | "test/unit/controller/ListSelector", 6 | "test/unit/model/grouper", 7 | "test/unit/model/GroupSortState" 8 | ], function() { "use strict"; }); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/unit/controller/App.controller.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "xxx/controller/App.controller", 3 | "sap/m/SplitApp", 4 | "sap/ui/core/Control", 5 | "sap/ui/model/json/JSONModel", 6 | "sap/ui/thirdparty/sinon", 7 | "sap/ui/thirdparty/sinon-qunit" 8 | ], function(AppController, SplitApp, Control, JSONModel) { 9 | "use strict"; 10 | 11 | QUnit.module("AppController - Hide master"); 12 | 13 | QUnit.test("Should hide the master of a SplitApp when selection in the list changes", function (assert) { 14 | // Arrange 15 | var fnOnSelectionChange, 16 | oViewStub = new Control(), 17 | oODataModelStub = new JSONModel(), 18 | oComponentStub = new Control(), 19 | oSplitApp = new SplitApp(), 20 | fnHideMasterSpy = sinon.spy(oSplitApp,"hideMaster"); 21 | 22 | oComponentStub.oListSelector = { 23 | attachListSelectionChange : function (fnFunctionToCall, oListener) { 24 | fnOnSelectionChange = fnFunctionToCall.bind(oListener); 25 | } 26 | }; 27 | oComponentStub.getContentDensityClass = jQuery.noop; 28 | 29 | oODataModelStub.metadataLoaded = function () { 30 | return { 31 | then : jQuery.noop 32 | }; 33 | }; 34 | oComponentStub.setModel(oODataModelStub); 35 | 36 | // System under Test 37 | var oAppController = new AppController(); 38 | 39 | this.stub(oAppController, "byId").withArgs("idAppControl").returns(oSplitApp); 40 | this.stub(oAppController, "getView").returns(oViewStub); 41 | this.stub(oAppController, "getOwnerComponent").returns(oComponentStub); 42 | 43 | // Act 44 | oAppController.onInit(); 45 | assert.ok(fnOnSelectionChange, "Did register to the change event of the ListSelector"); 46 | // Simulate the event of the list 47 | fnOnSelectionChange(); 48 | 49 | // Assert 50 | assert.strictEqual(fnHideMasterSpy.callCount, 1, "Did hide the master"); 51 | }); 52 | 53 | } 54 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/unit/model/GroupSortState.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "xxx/model/GroupSortState", 3 | "sap/ui/model/json/JSONModel" 4 | ], function (GroupSortState, JSONModel) { 5 | "use strict"; 6 | 7 | QUnit.module("GroupSortState - grouping and sorting", { 8 | beforeEach: function () { 9 | this.oModel = new JSONModel({}); 10 | // System under test 11 | this.oGroupSortState = new GroupSortState(this.oModel, function() {}); 12 | } 13 | }); 14 | 15 | QUnit.test("Should always return a sorter when sorting", function (assert) { 16 | // Act + Assert 17 | assert.strictEqual(this.oGroupSortState.sort("Balance").length, 1, "The sorting by Balance returned a sorter"); 18 | assert.strictEqual(this.oGroupSortState.sort("CardName").length, 1, "The sorting by CardName returned a sorter"); 19 | }); 20 | 21 | QUnit.test("Should return a grouper when grouping", function (assert) { 22 | // Act + Assert 23 | assert.strictEqual(this.oGroupSortState.group("Balance").length, 1, "The group by Balance returned a sorter"); 24 | assert.strictEqual(this.oGroupSortState.group("None").length, 0, "The sorting by None returned no sorter"); 25 | }); 26 | 27 | 28 | QUnit.test("Should set the sorting to Balance if the user groupes by Balance", function (assert) { 29 | // Act + Assert 30 | this.oGroupSortState.group("Balance"); 31 | assert.strictEqual(this.oModel.getProperty("/sortBy"), "Balance", "The sorting is the same as the grouping"); 32 | }); 33 | 34 | QUnit.test("Should set the grouping to None if the user sorts by CardName and there was a grouping before", function (assert) { 35 | // Arrange 36 | this.oModel.setProperty("/groupBy", "Balance"); 37 | 38 | this.oGroupSortState.sort("CardName"); 39 | 40 | // Assert 41 | assert.strictEqual(this.oModel.getProperty("/groupBy"), "None", "The grouping got reset"); 42 | }); 43 | }); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/unit/model/formatter.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/m/Text", 3 | "xxx/model/formatter" 4 | ], function (Text, formatter) { 5 | "use strict"; 6 | 7 | QUnit.module("formatter - Currency value"); 8 | 9 | function currencyValueTestCase(assert, sValue, fExpectedNumber) { 10 | // Act 11 | var fCurrency = formatter.currencyValue(sValue); 12 | 13 | // Assert 14 | assert.strictEqual(fCurrency, fExpectedNumber, "The rounding was correct"); 15 | } 16 | 17 | QUnit.test("Should round down a 3 digit number", function (assert) { 18 | currencyValueTestCase.call(this, assert, "3.123", "3.12"); 19 | }); 20 | 21 | QUnit.test("Should round up a 3 digit number", function (assert) { 22 | currencyValueTestCase.call(this, assert, "3.128", "3.13"); 23 | }); 24 | 25 | QUnit.test("Should round a negative number", function (assert) { 26 | currencyValueTestCase.call(this, assert, "-3", "-3.00"); 27 | }); 28 | 29 | QUnit.test("Should round an empty string", function (assert) { 30 | currencyValueTestCase.call(this, assert, "", ""); 31 | }); 32 | 33 | QUnit.test("Should round a zero", function (assert) { 34 | currencyValueTestCase.call(this, assert, "0", "0.00"); 35 | }); 36 | 37 | } 38 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/unit/model/grouper.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "xxx/model/grouper", 3 | "sap/ui/model/resource/ResourceModel", 4 | "jquery.sap.global" 5 | ], function (Grouper, ResourceModel, $) { 6 | "use strict"; 7 | 8 | function createResourceModel () { 9 | return new ResourceModel({ 10 | bundleUrl : [$.sap.getModulePath("xxx"), "i18n/i18n.properties"].join("/") 11 | }); 12 | } 13 | 14 | QUnit.module("Sorter - Grouping functions", { 15 | beforeEach : function () { 16 | this._oResourceModel = createResourceModel(); 17 | }, 18 | afterEach : function () { 19 | this._oResourceModel.destroy(); 20 | } 21 | }); 22 | 23 | function createContextObject(vValue) { 24 | return { 25 | getProperty : function () { 26 | return vValue; 27 | } 28 | }; 29 | } 30 | 31 | QUnit.test("Should group a price lesser equal 20", function (assert) { 32 | // Arrange 33 | var oContextObject = createContextObject(17.2), 34 | oGrouperReturn; 35 | 36 | // System under test 37 | var fnGroup = Grouper.groupUnitNumber(this._oResourceModel.getResourceBundle()); 38 | 39 | // Assert 40 | oGrouperReturn = fnGroup(oContextObject); 41 | assert.strictEqual(oGrouperReturn.key, "LE20", "The key is as expected for a low value"); 42 | assert.strictEqual(oGrouperReturn.text, this._oResourceModel.getResourceBundle().getText("masterGroup1Header1"), "The group header is as expected for a low value"); 43 | }); 44 | 45 | QUnit.test("Should group the price", function (assert) { 46 | // Arrange 47 | var oContextObject = createContextObject(55.5), 48 | oGrouperReturn; 49 | 50 | // System under test 51 | var fnGroup = Grouper.groupUnitNumber(this._oResourceModel.getResourceBundle()); 52 | 53 | // Assert 54 | oGrouperReturn = fnGroup(oContextObject); 55 | assert.strictEqual(oGrouperReturn.key, "GT20", "The key is as expected for a high value"); 56 | assert.strictEqual(oGrouperReturn.text, this._oResourceModel.getResourceBundle().getText("masterGroup1Header2"), "The group header is as expected for a high value"); 57 | }); 58 | 59 | }); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/unit/model/models.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "xxx/model/models", 3 | "sap/ui/thirdparty/sinon", 4 | "sap/ui/thirdparty/sinon-qunit" 5 | ], function (models) { 6 | "use strict"; 7 | 8 | QUnit.module("createDeviceModel", { 9 | afterEach : function () { 10 | this.oDeviceModel.destroy(); 11 | } 12 | }); 13 | 14 | function isPhoneTestCase(assert, bIsPhone) { 15 | // Arrange 16 | this.stub(sap.ui.Device, "system", { phone : bIsPhone }); 17 | 18 | // System under test 19 | this.oDeviceModel = models.createDeviceModel(); 20 | 21 | // Assert 22 | assert.strictEqual(this.oDeviceModel.getData().system.phone, bIsPhone, "IsPhone property is correct"); 23 | } 24 | 25 | QUnit.test("Should initialize a device model for desktop", function (assert) { 26 | isPhoneTestCase.call(this, assert, false); 27 | }); 28 | 29 | QUnit.test("Should initialize a device model for phone", function (assert) { 30 | isPhoneTestCase.call(this, assert, true); 31 | }); 32 | 33 | function isTouchTestCase(assert, bIsTouch) { 34 | // Arrange 35 | this.stub(sap.ui.Device, "support", { touch : bIsTouch }); 36 | 37 | // System under test 38 | this.oDeviceModel = models.createDeviceModel(); 39 | 40 | // Assert 41 | assert.strictEqual(this.oDeviceModel.getData().support.touch, bIsTouch, "IsTouch property is correct"); 42 | } 43 | 44 | QUnit.test("Should initialize a device model for non touch devices", function (assert) { 45 | isTouchTestCase.call(this, assert, false); 46 | }); 47 | 48 | QUnit.test("Should initialize a device model for touch devices", function (assert) { 49 | isTouchTestCase.call(this, assert, true); 50 | }); 51 | 52 | QUnit.test("The binding mode of the device model should be one way", function (assert) { 53 | 54 | // System under test 55 | this.oDeviceModel = models.createDeviceModel(); 56 | 57 | // Assert 58 | assert.strictEqual(this.oDeviceModel.getDefaultBindingMode(), "OneWay", "Binding mode is correct"); 59 | }); 60 | 61 | } 62 | ); -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/test/unit/unitTests.qunit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Unit tests for Business Partners List 5 | 6 | 7 | 8 | 16 | 17 | 32 | 33 | 34 | 35 |
36 |
37 | 38 | -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/view/App.view.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/view/Detail.view.xml: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | 8 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | </Toolbar> 17 | </headerToolbar> 18 | <columns> 19 | <Column> 20 | <Text text="{i18n>detailLineItemTableIDColumn}"/> 21 | </Column> 22 | <Column> 23 | <Text text="{i18n>detailLineItemTableStreetColumn}"/> 24 | </Column> 25 | </columns> 26 | <items> 27 | <ColumnListItem> 28 | <cells> 29 | <ObjectIdentifier title="{Address}" text="{Address}"/> 30 | <ObjectAttribute text="{Street}"> 31 | </ObjectAttribute> 32 | </cells> 33 | </ColumnListItem> 34 | </items> 35 | </Table> 36 | </IconTabFilter> 37 | <IconTabFilter id="iconTabBarFilter2" icon="sap-icon://contacts" tooltip="{i18n>detailIconTabBarAttachments}"> 38 | <Table id="lineItemContactList" width="auto" items="{ContactPersons}" updateFinished="onContactListUpdateFinished" 39 | noDataText="{i18n>detailLineItemContactTableNoDataText}" busyIndicatorDelay="{detailView>/lineItemTableDelay}" class="sapUiResponsiveMargin"> 40 | <headerToolbar> 41 | <Toolbar id="lineItemsContactToolbar"> 42 | <Title id="lineItemsContactHeader" text="{detailView>/lineItemContactListTitle}"/> 43 | </Toolbar> 44 | </headerToolbar> 45 | <columns> 46 | <Column> 47 | <Text text="{i18n>detailLineItemTableContactNameColumn}"/> 48 | </Column> 49 | <Column> 50 | <Text text="{i18n>detailLineItemTableContactPhoneColumn}"/> 51 | </Column> 52 | </columns> 53 | <items> 54 | <ColumnListItem> 55 | <cells> 56 | <ObjectIdentifier title="{Name}" text="{Name}"/> 57 | <ObjectAttribute text="{Tel1}"> 58 | </ObjectAttribute> 59 | </cells> 60 | </ColumnListItem> 61 | </items> 62 | </Table> 63 | </IconTabFilter> 64 | </items> 65 | </IconTabBar> 66 | </semantic:content> 67 | <semantic:sendEmailAction> 68 | <semantic:SendEmailAction id="shareEmail" press="onShareEmailPress"/> 69 | </semantic:sendEmailAction> 70 | <semantic:shareInJamAction> 71 | <semantic:ShareInJamAction id="shareInJam" visible="{FLP>/isShareInJamActive}" press="onShareInJamPress"/> 72 | </semantic:shareInJamAction> 73 | <semantic:saveAsTileAction> 74 | <footerbar:AddBookmarkButton id="shareTile" title="{detailView>/saveAsTileTitle}"/> 75 | </semantic:saveAsTileAction> 76 | </semantic:DetailPage> 77 | </mvc:View> -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/view/DetailNoObjectsAvailable.view.xml: -------------------------------------------------------------------------------- 1 | <mvc:View 2 | controllerName="xxx.controller.BaseController" 3 | xmlns:mvc="sap.ui.core.mvc" 4 | xmlns="sap.m"> 5 | 6 | <MessagePage 7 | id="page" 8 | title="{i18n>notAvailableViewTitle}" 9 | text="{i18n>noObjectsAvailableText}" 10 | icon="{sap-icon://product}" 11 | description="" 12 | showNavButton="{device>/system/phone}" 13 | navButtonPress="onNavBack"> 14 | </MessagePage> 15 | 16 | </mvc:View> -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/view/DetailObjectNotFound.view.xml: -------------------------------------------------------------------------------- 1 | <mvc:View 2 | controllerName="xxx.controller.BaseController" 3 | xmlns:mvc="sap.ui.core.mvc" 4 | xmlns="sap.m"> 5 | 6 | <MessagePage 7 | id="page" 8 | title="{i18n>detailTitle}" 9 | text="{i18n>noObjectFoundText}" 10 | icon="{sap-icon://product}" 11 | description="" 12 | showNavButton="{device>/system/phone}" 13 | navButtonPress="onNavBack"> 14 | </MessagePage> 15 | 16 | </mvc:View> -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/view/Master.view.xml: -------------------------------------------------------------------------------- 1 | <mvc:View xmlns:mvc="sap.ui.core.mvc" xmlns:core="sap.ui.core" xmlns="sap.m" xmlns:semantic="sap.m.semantic" controllerName="xxx.controller.Master"> 2 | 3 | <semantic:MasterPage id="page" title="{masterView>/title}" navButtonPress="onNavBack" showNavButton="true"> 4 | <semantic:subHeader> 5 | <Bar id="headerBar"> 6 | <contentMiddle> 7 | <SearchField id="searchField" showRefreshButton="{= !${device>/support/touch} }" tooltip="{i18n>masterSearchTooltip}" width="100%" search="onSearch"> 8 | </SearchField> 9 | </contentMiddle> 10 | </Bar> 11 | </semantic:subHeader> 12 | 13 | <semantic:content> 14 | <PullToRefresh id="pullToRefresh" visible="{device>/support/touch}" refresh="onRefresh"/> 15 | <!-- For client side filtering add this to the items attribute: parameters: {operationMode: 'Client'}}" --> 16 | <List id="list" items="{ path: '/BusinessPartner', sorter: { path: 'CardName', descending: false }, groupHeaderFactory: '.createGroupHeader' }" busyIndicatorDelay="{masterView>/delay}" noDataText="{masterView>/noDataText}" mode="{= ${device>/system/phone} ? 'None' : 'SingleSelectMaster'}" growing="true" growingScrollToLoad="true" updateFinished="onUpdateFinished" selectionChange="onSelectionChange"> 17 | <infoToolbar> 18 | <Toolbar active="true" id="filterBar" visible="{masterView>/isFilterBarVisible}" press="onOpenViewSettings"> 19 | <Title id="filterBarLabel" text="{masterView>/filterBarLabel}"/> 20 | </Toolbar> 21 | </infoToolbar> 22 | <items> 23 | <ObjectListItem type="{= ${device>/system/phone} ? 'Active' : 'Inactive'}" press="onSelectionChange" title="{CardName}" number="{ path: 'Balance', formatter: '.formatter.currencyValue' }" numberUnit="{Currency}" intro="{CardCode}"> 24 | </ObjectListItem> 25 | </items> 26 | </List> 27 | </semantic:content> 28 | 29 | <semantic:sort> 30 | <semantic:SortSelect id="sort" selectedKey="{masterView>/sortBy}" change="onSort"> 31 | <core:Item id="masterSort1" key="CardName" text="{i18n>masterSort1}"/> 32 | <core:Item id="masterSort2" key="Balance" text="{i18n>masterSort2}"/> 33 | </semantic:SortSelect> 34 | </semantic:sort> 35 | 36 | <semantic:filter> 37 | <semantic:FilterAction id="filter" press="onOpenViewSettings"/> 38 | </semantic:filter> 39 | 40 | <semantic:group> 41 | <semantic:GroupSelect id="group" selectedKey="{masterView>/groupBy}" change="onGroup"> 42 | <core:Item id="masterGroupNone" key="None" text="{i18n>masterGroupNone}"/> 43 | <core:Item id="masterGroup1" key="Balance" text="{i18n>masterGroup1}"/> 44 | </semantic:GroupSelect> 45 | </semantic:group> 46 | 47 | </semantic:MasterPage> 48 | 49 | </mvc:View> -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/view/NotFound.view.xml: -------------------------------------------------------------------------------- 1 | <mvc:View 2 | controllerName="xxx.controller.BaseController" 3 | xmlns:mvc="sap.ui.core.mvc" 4 | xmlns="sap.m"> 5 | 6 | <MessagePage 7 | id="page" 8 | title="{i18n>notFoundTitle}" 9 | text="{i18n>notFoundText}" 10 | icon="{sap-icon://document}" 11 | description="" 12 | showNavButton="{device>/system/phone}" 13 | navButtonPress="onNavBack"> 14 | </MessagePage> 15 | 16 | </mvc:View> -------------------------------------------------------------------------------- /12_Fiori/BPExplorerApp/webapp/view/ViewSettingsDialog.fragment.xml: -------------------------------------------------------------------------------- 1 | <core:FragmentDefinition 2 | xmlns="sap.m" 3 | xmlns:core="sap.ui.core"> 4 | 5 | <ViewSettingsDialog 6 | id="viewSettingsDialog" 7 | confirm="onConfirmViewSettingsDialog" 8 | resetFilters="onViewSettingsDialogResetFilters"> 9 | <filterItems> 10 | <ViewSettingsFilterItem 11 | id="filterItems" 12 | text="{i18n>masterFilterName}" 13 | key="Price" 14 | multiSelect="false"> 15 | <items> 16 | <ViewSettingsItem 17 | text="{i18n>masterFilter1}" 18 | key="Filter1" 19 | id="viewFilter1"/> 20 | <ViewSettingsItem 21 | text="{i18n>masterFilter2}" 22 | key="Filter2" 23 | id="viewFilter2"/> 24 | </items> 25 | </ViewSettingsFilterItem> 26 | </filterItems> 27 | </ViewSettingsDialog> 28 | 29 | </core:FragmentDefinition> -------------------------------------------------------------------------------- /12_Fiori/BusinessPartners.xsodata: -------------------------------------------------------------------------------- 1 | service{ 2 | "SBODEMOUS"."OCRD" as "BusinessPartner" 3 | with("CardCode", "CardName", "CardType", "Balance", "Currency") 4 | navigates ("BP_BPAddress" as "BPAddress","BP_Contacts" as "ContactPersons") 5 | create forbidden 6 | update forbidden 7 | delete forbidden; 8 | 9 | "SBODEMOUS"."CRD1" as "BPAddresses" 10 | with("CardCode","AdresType","Address", "Street", "City", "State", "Country") 11 | create forbidden 12 | update forbidden 13 | delete forbidden; 14 | association "BP_BPAddress" principal "BusinessPartner"("CardCode") 15 | multiplicity "1" dependent "BPAddresses"("CardCode") multiplicity "*"; 16 | 17 | "SBODEMOUS"."OCPR" as "Contacts" 18 | with("CardCode","CntctCode", "Name", "Position", "Tel1", "Cellolar","E_MaiL") 19 | create forbidden 20 | update forbidden 21 | delete forbidden; 22 | association "BP_Contacts" principal "BusinessPartner"("CardCode") multiplicity "1" dependent "Contacts"("CardCode") multiplicity "*"; 23 | } -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/.project: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <projectDescription> 3 | <name>Copy of SLFiori</name> 4 | <comment></comment> 5 | <projects> 6 | </projects> 7 | <buildSpec> 8 | <buildCommand> 9 | <name>com.sap.ndb.studio.view.ui.BIModelsBuilder</name> 10 | <arguments> 11 | </arguments> 12 | </buildCommand> 13 | <buildCommand> 14 | <name>com.sap.ndb.studio.dwb.team.HanaIgnoreFileBuilder</name> 15 | <arguments> 16 | </arguments> 17 | </buildCommand> 18 | <buildCommand> 19 | <name>org.eclipse.wst.jsdt.core.javascriptValidator</name> 20 | <arguments> 21 | </arguments> 22 | </buildCommand> 23 | <buildCommand> 24 | <name>com.sap.ndb.studio.xse.editor.xsjsLintBuilder</name> 25 | <arguments> 26 | </arguments> 27 | </buildCommand> 28 | </buildSpec> 29 | <natures> 30 | <nature>com.sap.ndb.studio.view.ui.modelerNature</nature> 31 | <nature>com.sap.ndb.studio.dwb.tools.core.hanaNature</nature> 32 | <nature>com.sap.ndb.studio.xse.editor.xsjsNature</nature> 33 | <nature>com.sap.ndb.studio.xse.editor.xsjsLintNature</nature> 34 | <nature>org.eclipse.wst.jsdt.core.jsNature</nature> 35 | </natures> 36 | </projectDescription> 37 | -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "generation": [ 3 | { 4 | "templateId": "fioriexttemplate.starterApplication", 5 | "templateVersion": "1.4.0", 6 | "dateTimeStamp": "Thu, 10 Sep 2015 13:20:45 GMT" 7 | } 8 | ], 9 | "mockpreview": { 10 | "mockUri": "", 11 | "loadJSONFiles": false 12 | }, 13 | "translation": { 14 | "translationDomain": "", 15 | "supportedLanguages": "en,fr,de", 16 | "defaultLanguage": "en", 17 | "defaultI18NPropertyFile": "i18n.properties", 18 | "resourceModelName": "i18n" 19 | } 20 | } -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/.xsaccess: -------------------------------------------------------------------------------- 1 | { 2 | "exposed" : true, 3 | "authentication" : null, 4 | "force_ssl" : false, 5 | "enable_etags" : true, 6 | "prevent_xsrf" : false, 7 | "cors" : 8 | { 9 | "enabled" : true 10 | }, 11 | "cache_control" : "no-cache, no-store" 12 | } -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/.xsapp: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/Component-preload.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/B1SA/workshop/8963649710987733f754ff0fa24c84c49d7575bb/12_Fiori/SLFioriAppCLJS/SLFiori/Component-preload.js -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/SAPLogo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/B1SA/workshop/8963649710987733f754ff0fa24c84c49d7575bb/12_Fiori/SLFioriAppCLJS/SLFiori/SAPLogo.gif -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/global.js: -------------------------------------------------------------------------------- 1 | var B1HSL_SessionID; 2 | var B1HSL_RouteID = ""; 3 | var MetadataJSONData; 4 | var ItemsJSONData; 5 | var LastItemIndex= 0; 6 | var selectedOrder= 0; 7 | var ordersList = null; 8 | var connectSLVia = 'XSJS'; -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/i18n/i18n.properties: -------------------------------------------------------------------------------- 1 | #<Describe your i18n file here> 2 | 3 | #XTIT: this is the title for the master section 4 | MASTER_TITLE=List of Sales Orders 5 | #XTIT: this is the title for the detail section 6 | DETAIL_TITLE=More Info about order 7 | #XFLD, 30: this is the place holder dummy in the detail page, to be replaced when the application is implemented 8 | DETAIL_TEXT=Order Detailed Information 9 | #XTIT: this is the title for the shell, displayed above the application when it is active 10 | SHELL_TITLE= 11 | #XTIT: placeholder text inside the search field above the master list 12 | MASTER_SEARCH_PLACEHOLDER=Search for Order by name 13 | #XTOL: tooltip of the search field above the master list 14 | MASTER_SEARCH_TOOLTIP=Search for Orders in the list -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/i18n/i18n_en.properties: -------------------------------------------------------------------------------- 1 | #<Describe your i18n file here> 2 | 3 | #XTIT: this is the title for the master section 4 | MASTER_TITLE=List of Sales Orders 5 | #XTIT: this is the title for the detail section 6 | DETAIL_TITLE=More Info about Order 7 | #XFLD, 30: this is the place holder dummy in the detail page, to be replaced when the application is implemented 8 | DETAIL_TEXT=Order Detailed Information 9 | #XTIT: this is the title for the shell, displayed above the application when it is active 10 | SHELL_TITLE= 11 | #XTIT: placeholder text inside the search field above the master list 12 | MASTER_SEARCH_PLACEHOLDER=Search for Order by name 13 | #XTOL: tooltip of the search field above the master list 14 | MASTER_SEARCH_TOOLTIP=Search for Orders in the list -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE HTML> 2 | <html> 3 | 4 | <!-- IMPORTANT 5 | This HTML file is meant for testing the application/component standalone, outside the Unified Shell. 6 | Some integration functionality, personalization etc. will not be available when running standalone. 7 | --> 8 | 9 | <head> 10 | <meta http-equiv="X-UA-Compatible" content="IE=edge" /> 11 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 12 | <meta charset="UTF-8"> 13 | 14 | <title>SAP B1 Fiori - Sales App 15 | 16 | 17 | 18 | 19 | 20 | 21 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/localIndex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Example - Development Start Page 9 | 21 | 41 | 42 | 43 | 49 | 50 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | SAP B1 Login Form 13 | 14 | 15 | 16 | 17 | 18 | 19 | 45 | 46 | 47 | 48 |
49 | 82 | 84 |
85 | 86 | 87 | -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "_version": "1.2.0", 3 | "sap.app": { 4 | "_version": "1.2.0", 5 | "id": "SLFiori", 6 | "type": "application", 7 | "i18n": "i18n/i18n.properties", 8 | "title": "{{appTitle}}", 9 | "description": "{{appDescription}}", 10 | "applicationVersion": { 11 | "version": "1.0.0" 12 | }, 13 | "dataSources": { 14 | "B1H_ServiceLayer": { 15 | "uri": "https://10.55.129.63:50000/b1s/v1/", 16 | "type": "OData", 17 | "settings": { 18 | "localUri": "model/metadata.xml" 19 | } 20 | } 21 | }, 22 | "sourceTemplate": { 23 | "id": "ui5template.2masterdetailcrud", 24 | "version": "1.34.3" 25 | }, 26 | "crossNavigation": { 27 | "inbounds": {} 28 | } 29 | }, 30 | "sap.ui": { 31 | "_version": "1.2.0", 32 | "technology": "UI5", 33 | "icons": { 34 | "icon": "sap-icon://detail-view", 35 | "favIcon": "", 36 | "phone": "", 37 | "phone@2": "", 38 | "tablet": "", 39 | "tablet@2": "" 40 | }, 41 | "deviceTypes": { 42 | "desktop": true, 43 | "tablet": true, 44 | "phone": true 45 | }, 46 | "supportedThemes": [ 47 | "sap_hcb", 48 | "sap_bluecrystal" 49 | ] 50 | }, 51 | "sap.ui5": { 52 | "_version": "1.1.0", 53 | "rootView": { 54 | "viewName": "SLFiori.view.App", 55 | "type": "XML", 56 | "id": "app" 57 | }, 58 | "dependencies": { 59 | "minUI5Version": "1.30.0", 60 | "libs": { 61 | "sap.ui.core": {}, 62 | "sap.m": {}, 63 | "sap.ui.layout": {} 64 | } 65 | }, 66 | "contentDensities": { 67 | "compact": true, 68 | "cozy": true 69 | }, 70 | "models": { 71 | "i18n": { 72 | "type": "sap.ui.model.resource.ResourceModel", 73 | "settings": { 74 | "bundleName": "SLFiori.i18n" 75 | } 76 | }, 77 | "": { 78 | "dataSource": "B1H_ServiceLayer", 79 | "settings": { 80 | "metadataUrlParams": { 81 | "sap-documentation": "heading" 82 | }, 83 | "defaultBindingMode": "TwoWay" 84 | } 85 | } 86 | }, 87 | 88 | 89 | "routing": { 90 | "config": { 91 | "routerClass": "sap.m.routing.Router", 92 | "viewType": "XML", 93 | "viewPath": "SLFiori.view", 94 | "controlId": "fioriContent", 95 | "controlAggregation": "detailPages", 96 | "bypassed": { 97 | "target": [ 98 | "master", 99 | "notFound" 100 | ] 101 | }, 102 | "async": "true" 103 | }, 104 | "routes": [ 105 | { 106 | "pattern": "", 107 | "name": "master", 108 | "target": [ 109 | "master", 110 | "detail" 111 | ] 112 | }, 113 | { 114 | "pattern": "Detail/{DocEntry}", 115 | "name": "detail", 116 | "target": [ 117 | "master", 118 | "detail" 119 | ] 120 | } 121 | ], 122 | "targets": { 123 | "master": { 124 | "viewName": "Master", 125 | "viewLevel": 1, 126 | "viewId": "master", 127 | "controlAggregation": "masterPages" 128 | }, 129 | "create": { 130 | "viewName": "CreateEdit", 131 | "viewLevel": 2 132 | }, 133 | "detailObjectNotFound": { 134 | "viewName": "DetailObjectNotFound", 135 | "viewId": "detailObjectNotFound" 136 | }, 137 | "detailNoObjectsAvailable": { 138 | "viewName": "DetailNoObjectsAvailable", 139 | "viewId": "detailNoObjectsAvailable" 140 | }, 141 | "notFound": { 142 | "viewName": "NotFound", 143 | "viewId": "notFound" 144 | }, 145 | "detail": { 146 | "viewType": "XML", 147 | "transition": "slide", 148 | "clearAggregation": "true", 149 | "viewName": "Detail", 150 | "viewId": "detail", 151 | "viewLevel": 2 152 | } 153 | } 154 | } 155 | }, 156 | "sap.platform.hcp": { 157 | "uri": "webapp", 158 | "_version": "1.1.0" 159 | } 160 | } -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/manifest_mine.json: -------------------------------------------------------------------------------- 1 | { 2 | "_version": "1.1.0", 3 | 4 | "start_url": "", 5 | 6 | "sap.app": { 7 | "_version": "1.1.0", 8 | "id": "", 9 | "type": "application", 10 | "i18n": "", 11 | "applicationVersion": { 12 | "version": "" 13 | }, 14 | "title": "{{}}", 15 | "tags": { 16 | "keywords": [ 17 | "{{<keyword1>}}", "{{<keyword2>}}" 18 | ] 19 | }, 20 | "dataSources": { 21 | "mainService": { 22 | "uri": "https://:50000/b1s/v1/", 23 | "type": "OData", 24 | "settings": { 25 | "odataVersion": "2.0", 26 | "localUri": "localService/metadata.xml" 27 | } 28 | } 29 | }, 30 | "sap.ui": { 31 | "_version": "1.1.0", 32 | "icons": { 33 | "icon": "<icon>", 34 | "favIcon": "<favIcon>", 35 | "phone": "<phone>", 36 | "phone@2": "<phone@2>", 37 | "tablet": "<tablet>", 38 | "tablet@2": "<tablet@2>" 39 | }, 40 | "deviceTypes": { 41 | "desktop": true, 42 | "tablet": true, 43 | "phone": true 44 | }, 45 | "supportedThemes": [ 46 | "sap_hcb", 47 | "sap_bluecrystal" 48 | ] 49 | }, 50 | "sap.ui5": { 51 | "_version": "1.1.0", 52 | "resources": { 53 | "js": [ 54 | { 55 | "uri": "<uri>" 56 | } 57 | ], 58 | "css": [ 59 | { 60 | "uri": "<uri>", 61 | "id": "<id>" 62 | } 63 | ] 64 | }, 65 | "dependencies": { 66 | "minUI5Version": "<minUI5Version>", 67 | "libs": { 68 | "<ui5lib1>": { 69 | "minVersion": "<minVersion1>" 70 | }, 71 | "<ui5lib2>": { 72 | "minVersion": "<minVersion2>" 73 | } 74 | }, 75 | "components": { 76 | "<ui5component1>": { 77 | "minVersion": "<minComp1Version>" 78 | } 79 | } 80 | }, 81 | "models": { 82 | "i18n": { 83 | "type": "sap.ui.model.resource.ResourceModel", 84 | "uri": "<uriRelativeToManifest>" 85 | }, 86 | "": { 87 | "dataSource": "<dataSourceAlias>", 88 | "settings": {} 89 | } 90 | }, 91 | "rootView": "<rootView>", 92 | "handleValidation": <true|false>, 93 | "config": { 94 | 95 | }, 96 | "routing": { 97 | 98 | }, 99 | "extends": { 100 | "component" : "<extendedComponentId>", 101 | "minVersion": "<minComp1Version>", 102 | "extensions": {} 103 | } 104 | "contentDensities": { 105 | "compact": <true|false>, 106 | "cozy": <true|false> 107 | } 108 | }, 109 | 110 | "sap.platform.abap": { 111 | "_version": "1.1.0", 112 | "uri": "<uri>" 113 | }, 114 | "sap.platform.hcp": { 115 | "_version": "1.1.0", 116 | "uri": "<uri>" 117 | } 118 | } -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/neo-app.json: -------------------------------------------------------------------------------- 1 | { 2 | "welcomeFile": "index.html", 3 | "routes": [ 4 | { 5 | "path": "/resources", 6 | "target": { 7 | "type": "service", 8 | "name": "sapui5", 9 | "entryPath": "/resources" 10 | }, 11 | "description": " SAPUI5 Resources" 12 | }, 13 | { 14 | "path": "/test-resources", 15 | "target": { 16 | "type": "service", 17 | "name": "sapui5", 18 | "entryPath": "/test-resources" 19 | }, 20 | "description": " SAPUI5 Test Resources" 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/view/App.controller.js: -------------------------------------------------------------------------------- 1 | sap.ui.controller("SLFiori.view.App", { 2 | onInit : function() 3 | { 4 | }, 5 | 6 | onCompleteCall: function(result) 7 | { 8 | alert('App.controller.onCompleteCall') 9 | document.cookie = result.cookies[0].value; 10 | B1HSL_SessionID = document.cookie; 11 | alert(B1HSL_SessionID); 12 | 13 | // TO BE MODIFIED // 14 | // You might need to adjust the CMD parameter based on the Business Object of your own choice" // 15 | // In the example below, you will retrieve Items Master Data // 16 | // TO BE MODIFIED // 17 | var xsjsUrl = "xsjs/B1SLLogic.xsjs?cmd=Items&sessionID=" + B1HSL_SessionID; 18 | jQuery.ajax({ 19 | type: "GET", 20 | xhrFields: 21 | { 22 | withCredentials: true 23 | }, 24 | url: xsjsUrl, 25 | dataType: "json", 26 | crossDomain: true, 27 | success: function (result) { 28 | 29 | ItemsJSONData = result.body; 30 | alert(ItemsJSONData); 31 | }, 32 | error: this.onErrorCall 33 | }); 34 | }, 35 | 36 | onErrorCall: function(jqXHR, textStatus, errorThrown) 37 | { 38 | sap.ui.commons.MessageBox.show(jqXHR.responseText, 39 | "ERROR", 40 | "Error in calling Service" ); 41 | return; 42 | } 43 | }); -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/view/App.view.xml: -------------------------------------------------------------------------------- 1 | <mvc:View xmlns:core="sap.ui.core" xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc" 2 | controllerName="SLFiori.view.App" displayBlock="true" height="100%"> 3 | <SplitApp id="fioriContent" showHeader="false" mode="StretchCompressMode"> 4 | <masterPages> 5 | <!-- filled dynamically by routing --> 6 | </masterPages> 7 | <detailPages> 8 | <!-- filled dynamically by routing --> 9 | </detailPages> 10 | </SplitApp> 11 | </mvc:View> -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/view/Detail.controller.js: -------------------------------------------------------------------------------- 1 | sap.ui.core.mvc.Controller.extend("SLFiori.view.Detail", { 2 | 3 | oModel: null, 4 | 5 | // TO BE MODIFIED // 6 | // Below is the binding procedure of the Details view in order to display the Items Master data in the header area // 7 | // TO BE MODIFIED // 8 | onInit: function() 9 | { 10 | var view = this.getView(); 11 | oModel = new sap.ui.model.json.JSONModel(); 12 | // 13 | oModel.setData(ItemsJSONData); 14 | view.setModel(oModel); 15 | 16 | // 17 | sap.ui.core.UIComponent.getRouterFor(this).attachRouteMatched(function(oEvent) { 18 | // when detail navigation occurs, update the binding context 19 | // TO BE MODIFIED // 20 | // Below is the binding procedure of the Items Table view in order to display the Sales Order Document Lines in the Details area // 21 | // You might need to change the Service Identifier from /DocumentLines to match your Entity Lines collection name 22 | // TO BE MODIFIED // 23 | if (oEvent.getParameter("name") === "Detail") 24 | { 25 | var context = new sap.ui.model.Context(view.getModel(), '/value/' + oEvent.getParameter("arguments").contextPath); 26 | view.setBindingContext(context); 27 | // Make sure the master is here 28 | var oItemsTbl = this.getView().byId("idItemsTable"); 29 | var oColListItem = this.getView().byId("idColListItem"); 30 | oItemsTbl.setModel(oModel); 31 | oItemsTbl.bindItems(context.sPath + "/DocumentLines", oColListItem); 32 | } 33 | }, this); 34 | 35 | }, 36 | 37 | openActionSheet: function() { 38 | 39 | if (!this._oActionSheet) { 40 | this._oActionSheet = new sap.m.ActionSheet({ 41 | buttons: new sap.ushell.ui.footerbar.AddBookmarkButton() 42 | }); 43 | this._oActionSheet.setShowCancelButton(true); 44 | this._oActionSheet.setPlacement(sap.m.PlacementType.Top); 45 | } 46 | 47 | this._oActionSheet.openBy(this.getView().byId("actionButton")); 48 | }, 49 | 50 | onExit: function() { 51 | if (this._oActionSheet) { 52 | this._oActionSheet.destroy(); 53 | this._oActionSheet = null; 54 | } 55 | }, 56 | 57 | // TO BE MODIFIED // 58 | // Below is the function definition to call the Service Layer for closing an opened sales order // 59 | // TO BE MODIFIED // 60 | handleCloseBtnPress: function(oEvent) { 61 | var oListItem = ordersList.getSelectedItem(); 62 | oListItem.setMarkLocked(true); 63 | ordersList.setSelectedItem(false); 64 | // 65 | var id = oListItem.getBindingContext().getPath().substr(7); 66 | var docStatusTxt = this.getView().byId("text2"); 67 | docStatusTxt.setText("Closed"); 68 | // 69 | closeOrder(); 70 | }, 71 | 72 | // TO BE MODIFIED // 73 | // Below is the function definition to call the Service Layer for canceling an opened sales order // 74 | // TO BE MODIFIED // 75 | handleCancelBtnPress: function(oEvent) { 76 | var oListItem = ordersList.getSelectedItem(); 77 | oListItem.setMarkLocked(true); 78 | ordersList.setSelectedItem(false); 79 | // 80 | var id = oListItem.getBindingContext().getPath().substr(7); 81 | var docStatusTxt = this.getView().byId("text2"); 82 | docStatusTxt.setText("Cancelled"); 83 | // 84 | cancelOrder(); 85 | }, 86 | 87 | handleNavButtonPress: function(oEvent) { 88 | var history = sap.ui.core.routing.History.getInstance(); 89 | var url = sap.ui.core.UIComponent.getRouterFor(this).getURL("master", {}); 90 | var direction = history.getDirection(url); 91 | if ("Backwards" === direction) { 92 | window.history.go(-1); 93 | } else { 94 | var replace = true; // otherwise we go backwards with a forward history 95 | this.navTo(route, data, replace); 96 | } 97 | } 98 | }); 99 | -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/view/Master.controller.js: -------------------------------------------------------------------------------- 1 | jQuery.sap.require("sap.m.MessageBox"); 2 | 3 | sap.ui.core.mvc.Controller.extend("SLFiori.view.Master", { 4 | 5 | oModel: null, 6 | 7 | onInit: function() 8 | { 9 | // TO BE MODIFIED // 10 | // Below is the binding procedure of the ListItem in order to display the Items Master data in a flat list // 11 | // TO BE MODIFIED // 12 | var oList = this.getView().byId("list"); 13 | ordersList = oList; 14 | var oListitem = this.getView().byId("mainListItem"); 15 | oModel = new sap.ui.model.json.JSONModel(); 16 | oModel.setData(ItemsJSONData); 17 | oList.setModel(oModel); 18 | oList.bindItems("/value", oListitem); 19 | this.getView().setModel(oModel); 20 | 21 | }, 22 | 23 | handleSearch: function() { 24 | // add filter for search 25 | var filters = []; 26 | var searchString = this.getView().byId("searchField").getValue(); 27 | if (searchString && searchString.length > 0) { 28 | filters = [ new sap.ui.model.Filter("CardName", sap.ui.model.FilterOperator.Contains, searchString) ]; 29 | } 30 | 31 | // update list binding 32 | var list = this.getView().byId("list"); 33 | var binding = list.getBinding("items"); 34 | binding.filter(filters); 35 | }, 36 | 37 | onGrowingList: function(oEvent) { 38 | //alert(oEvent.getParameter('actual') + ", " + oEvent.getParameter('total')); 39 | }, 40 | 41 | onUpdatingList: function(oEvent) { 42 | //alert(oEvent.getParameter('reason') + "," + oEvent.getParameter('actual') + ", " + oEvent.getParameter('total')); 43 | }, 44 | 45 | handleSelect: function(oEvent) 46 | { 47 | var oListItem = oEvent.getParameter("listItem") || oEvent.getSource(); 48 | selectedOrder = oListItem.mAggregations.attributes[0].mProperties.text; 49 | // trigger routing to BindingPath of this ListItem - this will update the data on the detail page 50 | sap.ui.core.UIComponent.getRouterFor(this).navTo("Detail",{from: "master", contextPath: oListItem.getBindingContext().getPath().substr(7)}); 51 | } 52 | }); -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppCLJS/SLFiori/view/Master.view.xml: -------------------------------------------------------------------------------- 1 | <mvc:View xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" 2 | xmlns:shellfooter="sap.ushell.ui.footerbar" controllerName="SLFiori.view.Master" afterInit="hanldeAfterInit"> 3 | 4 | <Page id="page" title="List of Open Orders"> 5 | <subHeader id="subHeader"> 6 | <Bar id="bar1"> 7 | <contentMiddle id="contentMiddle"> 8 | <SearchField id="searchField" showRefreshButton="{device>/isNoTouch}" 9 | placeholder="Search for Sales Order by Customer" search="handleSearch" 10 | tooltip="Search for SO by Customer Name" width="100%"> 11 | </SearchField> 12 | </contentMiddle> 13 | </Bar> 14 | </subHeader> 15 | 16 | <content id="content"> 17 | <!-- TO BE MODIFIED --> 18 | <!-- set the below attribute so-called items with your xsodata service identifier --> 19 | <!-- In case of using the B1 Service Layer for reading data, the attribute items must be equal to {/value} --> 20 | <!-- You might need to adjust the attribute text of <ObjectAttribute> inside <ObjectListItem>, based on your own business logic --> 21 | <!-- TO BE MODIFIED --> 22 | <List id="list" items="{/value}" mode="{device>/listMode}" 23 | select="handleSelect" growing="true" growingScrollToLoad="false" growingStarted="onGrowingList" updateStarted="onUpdatingList"> 24 | <customData id="customData1"> 25 | <core:CustomData id="coreCustomData1" key="sapDtResourcePath" value="Items"></core:CustomData> 26 | </customData> 27 | 28 | <headerToolbar> 29 | <Toolbar> 30 | <Title text="Sales Orders" level="H2" /> 31 | </Toolbar> 32 | </headerToolbar> 33 | 34 | <items id="listItems"> 35 | <ObjectListItem id="mainListItem" type="{device>/listItemType}" 36 | title="{CardName}" 37 | number="{DocTotal}" 38 | numberUnit="{DocCurrency}" 39 | press="handleSelect"> 40 | <customData id="customData2"> 41 | <core:CustomData id="coreCustomData2" key="sapDtResourcePath" value="Items"></core:CustomData> 42 | </customData> 43 | <attributes id="attributes"> 44 | 45 | <ObjectAttribute id="ATTR1" text="{DocEntry}"> 46 | <customData id="customData3"> 47 | <core:CustomData id="coreCustomData3" key="sapDtResourcePath" value="Items"></core:CustomData> 48 | </customData> 49 | </ObjectAttribute> 50 | 51 | 52 | <ObjectAttribute id="ATTR2" text="{DocDate}"> 53 | <customData id="customData4"> 54 | <core:CustomData id="coreCustomData4" key="sapDtResourcePath" value="Items"></core:CustomData> 55 | </customData> 56 | </ObjectAttribute> 57 | 58 | </attributes> 59 | </ObjectListItem> 60 | </items> 61 | </List> 62 | </content> 63 | 64 | <footer id="footer"> 65 | <Bar id="bar2"> 66 | <contentLeft id="contentLeft"> 67 | <shellfooter:SettingsButton id="settingsButton"></shellfooter:SettingsButton> 68 | </contentLeft> 69 | </Bar> 70 | </footer> 71 | </Page> 72 | 73 | </mvc:View> -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/.project: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <projectDescription> 3 | <name>Copy of SLFiori</name> 4 | <comment></comment> 5 | <projects> 6 | </projects> 7 | <buildSpec> 8 | <buildCommand> 9 | <name>com.sap.ndb.studio.view.ui.BIModelsBuilder</name> 10 | <arguments> 11 | </arguments> 12 | </buildCommand> 13 | <buildCommand> 14 | <name>com.sap.ndb.studio.dwb.team.HanaIgnoreFileBuilder</name> 15 | <arguments> 16 | </arguments> 17 | </buildCommand> 18 | <buildCommand> 19 | <name>org.eclipse.wst.jsdt.core.javascriptValidator</name> 20 | <arguments> 21 | </arguments> 22 | </buildCommand> 23 | <buildCommand> 24 | <name>com.sap.ndb.studio.xse.editor.xsjsLintBuilder</name> 25 | <arguments> 26 | </arguments> 27 | </buildCommand> 28 | </buildSpec> 29 | <natures> 30 | <nature>com.sap.ndb.studio.view.ui.modelerNature</nature> 31 | <nature>com.sap.ndb.studio.dwb.tools.core.hanaNature</nature> 32 | <nature>com.sap.ndb.studio.xse.editor.xsjsNature</nature> 33 | <nature>com.sap.ndb.studio.xse.editor.xsjsLintNature</nature> 34 | <nature>org.eclipse.wst.jsdt.core.jsNature</nature> 35 | </natures> 36 | </projectDescription> 37 | -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "generation": [ 3 | { 4 | "templateId": "fioriexttemplate.starterApplication", 5 | "templateVersion": "1.4.0", 6 | "dateTimeStamp": "Thu, 10 Sep 2015 13:20:45 GMT" 7 | } 8 | ], 9 | "mockpreview": { 10 | "mockUri": "", 11 | "loadJSONFiles": false 12 | }, 13 | "translation": { 14 | "translationDomain": "", 15 | "supportedLanguages": "en,fr,de", 16 | "defaultLanguage": "en", 17 | "defaultI18NPropertyFile": "i18n.properties", 18 | "resourceModelName": "i18n" 19 | } 20 | } -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/.xsaccess: -------------------------------------------------------------------------------- 1 | { 2 | "exposed" : true, 3 | "authentication" : null, 4 | "force_ssl" : false, 5 | "enable_etags" : true, 6 | "prevent_xsrf" : false, 7 | "cors" : 8 | { 9 | "enabled" : true 10 | }, 11 | "cache_control" : "no-cache, no-store" 12 | } -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/.xsapp: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/Component-preload.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/B1SA/workshop/8963649710987733f754ff0fa24c84c49d7575bb/12_Fiori/SLFioriAppXSJS/SLFiori/Component-preload.js -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/SAPLogo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/B1SA/workshop/8963649710987733f754ff0fa24c84c49d7575bb/12_Fiori/SLFioriAppXSJS/SLFiori/SAPLogo.gif -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/global.js: -------------------------------------------------------------------------------- 1 | var B1HSL_SessionID =""; 2 | var B1HSL_RouteID = ""; 3 | var MetadataJSONData; 4 | var ItemsJSONData; 5 | var LastItemIndex= 0; 6 | var selectedOrder= 0; 7 | var ordersList = null; -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/i18n/i18n.properties: -------------------------------------------------------------------------------- 1 | #<Describe your i18n file here> 2 | 3 | #XTIT: this is the title for the master section 4 | MASTER_TITLE=List of Sales Orders 5 | #XTIT: this is the title for the detail section 6 | DETAIL_TITLE=More Info about order 7 | #XFLD, 30: this is the place holder dummy in the detail page, to be replaced when the application is implemented 8 | DETAIL_TEXT=Order Detailed Information 9 | #XTIT: this is the title for the shell, displayed above the application when it is active 10 | SHELL_TITLE= 11 | #XTIT: placeholder text inside the search field above the master list 12 | MASTER_SEARCH_PLACEHOLDER=Search for Order by name 13 | #XTOL: tooltip of the search field above the master list 14 | MASTER_SEARCH_TOOLTIP=Search for Orders in the list -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/i18n/i18n_en.properties: -------------------------------------------------------------------------------- 1 | #<Describe your i18n file here> 2 | 3 | #XTIT: this is the title for the master section 4 | MASTER_TITLE=List of Sales Orders 5 | #XTIT: this is the title for the detail section 6 | DETAIL_TITLE=More Info about Order 7 | #XFLD, 30: this is the place holder dummy in the detail page, to be replaced when the application is implemented 8 | DETAIL_TEXT=Order Detailed Information 9 | #XTIT: this is the title for the shell, displayed above the application when it is active 10 | SHELL_TITLE= 11 | #XTIT: placeholder text inside the search field above the master list 12 | MASTER_SEARCH_PLACEHOLDER=Search for Order by name 13 | #XTOL: tooltip of the search field above the master list 14 | MASTER_SEARCH_TOOLTIP=Search for Orders in the list -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE HTML> 2 | <html> 3 | 4 | <!-- IMPORTANT 5 | This HTML file is meant for testing the application/component standalone, outside the Unified Shell. 6 | Some integration functionality, personalization etc. will not be available when running standalone. 7 | --> 8 | 9 | <head> 10 | <meta http-equiv="X-UA-Compatible" content="IE=edge" /> 11 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 12 | <meta charset="UTF-8"> 13 | 14 | <title>SAP B1 Fiori - Sales App 15 | 16 | 17 | 18 | 19 | 20 | 28 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/js/callSL.js: -------------------------------------------------------------------------------- 1 | // TO BE MODIFIED // 2 | // Below code is the JS caller of the Service Layer for retrieving sales orders, canceling and closing opened SO // 3 | // TO BE MODIFIED // 4 | function getOrders() 5 | { 6 | if(document.cookie.length > 0) 7 | { 8 | var xsjsUrl = "xsjs/B1SLLogic.xsjs?cmd=Get&actionUri=Orders&filter=DocumentStatus&sessionID=" + document.cookie; 9 | //var xsjsUrl = "xsjs/B1SLLogic.xsjs?cmd=Get&actionUri=Orders?$orderby=DocumentStatus" + "%20" + " desc&sessionID=" + document.cookie; 10 | // 11 | jQuery.ajax({ 12 | type: "GET", 13 | xhrFields: 14 | { 15 | withCredentials: true 16 | }, 17 | url: xsjsUrl, 18 | dataType: "json", 19 | crossDomain: true, 20 | success: function (result) 21 | { 22 | ItemsJSONData = result.body; 23 | //sap.ui.getCore().attachInitEvent(function() { 24 | var oComponentContainer = new sap.ui.core.ComponentContainer({ 25 | height : "100%", 26 | name : "SLFiori" 27 | }); 28 | oComponentContainer.placeAt("content"); 29 | //}); 30 | }, 31 | error: function (request, textStatus, errorThrown) 32 | { 33 | alert("Service Layer Items Call failed: " + textStatus + " / " + errorThrown ); 34 | } 35 | }); 36 | } 37 | else 38 | window.location.href = "login.html"; 39 | } 40 | 41 | function cancelOrder() 42 | { 43 | var xsjsUrl = "xsjs/B1SLLogic.xsjs?cmd=Action&actionUri=Orders(" + selectedOrder + ")/Cancel&sessionID=" + document.cookie; 44 | // 45 | jQuery.ajax({ 46 | type: "POST", 47 | xhrFields: 48 | { 49 | withCredentials: true 50 | }, 51 | url: xsjsUrl, 52 | dataType: "json", 53 | crossDomain: true, 54 | success: function (result) 55 | { 56 | //oModel.refresh(true); 57 | sap.m.MessageBox.show( 58 | "Document " + selectedOrder + " is cancelled successfuly.", { 59 | icon: sap.m.MessageBox.Icon.success, 60 | title: "Confirmation" 61 | } 62 | ); 63 | }, 64 | error: function (request, textStatus, errorThrown) 65 | { 66 | alert("Service Layer Call for document closure has failed: " + textStatus + " / " + errorThrown ); 67 | } 68 | }); 69 | } 70 | 71 | function closeOrder() 72 | { 73 | var xsjsUrl = "xsjs/B1SLLogic.xsjs?cmd=Action&actionUri=Orders(" + selectedOrder + ")/Close&sessionID=" + document.cookie; 74 | // 75 | jQuery.ajax({ 76 | type: "POST", 77 | xhrFields: 78 | { 79 | withCredentials: true 80 | }, 81 | url: xsjsUrl, 82 | dataType: "json", 83 | crossDomain: true, 84 | success: function (result) 85 | { 86 | //oModel.refresh(true); 87 | sap.m.MessageBox.show( 88 | "Document " + selectedOrder + " is closed successfuly.", { 89 | icon: sap.m.MessageBox.Icon.success, 90 | title: "Confirmation" 91 | } 92 | ); 93 | }, 94 | error: function (request, textStatus, errorThrown) 95 | { 96 | alert("Service Layer Call for document closure has failed: " + textStatus + " / " + errorThrown ); 97 | } 98 | }); 99 | } -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/js/webtoolkit.base64.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Base64 encode / decode 4 | * http://www.webtoolkit.info/ 5 | * 6 | **/ 7 | 8 | var Base64 = { 9 | 10 | // private property 11 | _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", 12 | 13 | // public method for encoding 14 | encode : function (input) { 15 | var output = ""; 16 | var chr1, chr2, chr3, enc1, enc2, enc3, enc4; 17 | var i = 0; 18 | 19 | input = Base64._utf8_encode(input); 20 | 21 | while (i < input.length) { 22 | 23 | chr1 = input.charCodeAt(i++); 24 | chr2 = input.charCodeAt(i++); 25 | chr3 = input.charCodeAt(i++); 26 | 27 | enc1 = chr1 >> 2; 28 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 29 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 30 | enc4 = chr3 & 63; 31 | 32 | if (isNaN(chr2)) { 33 | enc3 = enc4 = 64; 34 | } else if (isNaN(chr3)) { 35 | enc4 = 64; 36 | } 37 | 38 | output = output + 39 | this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + 40 | this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); 41 | 42 | } 43 | 44 | return output; 45 | }, 46 | 47 | // public method for decoding 48 | decode : function (input) { 49 | var output = ""; 50 | var chr1, chr2, chr3; 51 | var enc1, enc2, enc3, enc4; 52 | var i = 0; 53 | 54 | input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 55 | 56 | while (i < input.length) { 57 | 58 | enc1 = this._keyStr.indexOf(input.charAt(i++)); 59 | enc2 = this._keyStr.indexOf(input.charAt(i++)); 60 | enc3 = this._keyStr.indexOf(input.charAt(i++)); 61 | enc4 = this._keyStr.indexOf(input.charAt(i++)); 62 | 63 | chr1 = (enc1 << 2) | (enc2 >> 4); 64 | chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 65 | chr3 = ((enc3 & 3) << 6) | enc4; 66 | 67 | output = output + String.fromCharCode(chr1); 68 | 69 | if (enc3 != 64) { 70 | output = output + String.fromCharCode(chr2); 71 | } 72 | if (enc4 != 64) { 73 | output = output + String.fromCharCode(chr3); 74 | } 75 | 76 | } 77 | 78 | output = Base64._utf8_decode(output); 79 | 80 | return output; 81 | 82 | }, 83 | 84 | // private method for UTF-8 encoding 85 | _utf8_encode : function (string) { 86 | string = string.replace(/\r\n/g,"\n"); 87 | var utftext = ""; 88 | 89 | for (var n = 0; n < string.length; n++) { 90 | 91 | var c = string.charCodeAt(n); 92 | 93 | if (c < 128) { 94 | utftext += String.fromCharCode(c); 95 | } 96 | else if((c > 127) && (c < 2048)) { 97 | utftext += String.fromCharCode((c >> 6) | 192); 98 | utftext += String.fromCharCode((c & 63) | 128); 99 | } 100 | else { 101 | utftext += String.fromCharCode((c >> 12) | 224); 102 | utftext += String.fromCharCode(((c >> 6) & 63) | 128); 103 | utftext += String.fromCharCode((c & 63) | 128); 104 | } 105 | 106 | } 107 | 108 | return utftext; 109 | }, 110 | 111 | // private method for UTF-8 decoding 112 | _utf8_decode : function (utftext) { 113 | var string = ""; 114 | var i = 0; 115 | var c = c1 = c2 = 0; 116 | 117 | while ( i < utftext.length ) { 118 | 119 | c = utftext.charCodeAt(i); 120 | 121 | if (c < 128) { 122 | string += String.fromCharCode(c); 123 | i++; 124 | } 125 | else if((c > 191) && (c < 224)) { 126 | c2 = utftext.charCodeAt(i+1); 127 | string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); 128 | i += 2; 129 | } 130 | else { 131 | c2 = utftext.charCodeAt(i+1); 132 | c3 = utftext.charCodeAt(i+2); 133 | string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); 134 | i += 3; 135 | } 136 | 137 | } 138 | 139 | return string; 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/libs/webtoolkit.base64.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Base64 encode / decode 4 | * http://www.webtoolkit.info/ 5 | * 6 | **/ 7 | 8 | var Base64 = { 9 | 10 | // private property 11 | _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", 12 | 13 | // public method for encoding 14 | encode : function (input) { 15 | var output = ""; 16 | var chr1, chr2, chr3, enc1, enc2, enc3, enc4; 17 | var i = 0; 18 | 19 | input = Base64._utf8_encode(input); 20 | 21 | while (i < input.length) { 22 | 23 | chr1 = input.charCodeAt(i++); 24 | chr2 = input.charCodeAt(i++); 25 | chr3 = input.charCodeAt(i++); 26 | 27 | enc1 = chr1 >> 2; 28 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 29 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 30 | enc4 = chr3 & 63; 31 | 32 | if (isNaN(chr2)) { 33 | enc3 = enc4 = 64; 34 | } else if (isNaN(chr3)) { 35 | enc4 = 64; 36 | } 37 | 38 | output = output + 39 | this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + 40 | this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); 41 | 42 | } 43 | 44 | return output; 45 | }, 46 | 47 | // public method for decoding 48 | decode : function (input) { 49 | var output = ""; 50 | var chr1, chr2, chr3; 51 | var enc1, enc2, enc3, enc4; 52 | var i = 0; 53 | 54 | input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 55 | 56 | while (i < input.length) { 57 | 58 | enc1 = this._keyStr.indexOf(input.charAt(i++)); 59 | enc2 = this._keyStr.indexOf(input.charAt(i++)); 60 | enc3 = this._keyStr.indexOf(input.charAt(i++)); 61 | enc4 = this._keyStr.indexOf(input.charAt(i++)); 62 | 63 | chr1 = (enc1 << 2) | (enc2 >> 4); 64 | chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 65 | chr3 = ((enc3 & 3) << 6) | enc4; 66 | 67 | output = output + String.fromCharCode(chr1); 68 | 69 | if (enc3 != 64) { 70 | output = output + String.fromCharCode(chr2); 71 | } 72 | if (enc4 != 64) { 73 | output = output + String.fromCharCode(chr3); 74 | } 75 | 76 | } 77 | 78 | output = Base64._utf8_decode(output); 79 | 80 | return output; 81 | 82 | }, 83 | 84 | // private method for UTF-8 encoding 85 | _utf8_encode : function (string) { 86 | string = string.replace(/\r\n/g,"\n"); 87 | var utftext = ""; 88 | 89 | for (var n = 0; n < string.length; n++) { 90 | 91 | var c = string.charCodeAt(n); 92 | 93 | if (c < 128) { 94 | utftext += String.fromCharCode(c); 95 | } 96 | else if((c > 127) && (c < 2048)) { 97 | utftext += String.fromCharCode((c >> 6) | 192); 98 | utftext += String.fromCharCode((c & 63) | 128); 99 | } 100 | else { 101 | utftext += String.fromCharCode((c >> 12) | 224); 102 | utftext += String.fromCharCode(((c >> 6) & 63) | 128); 103 | utftext += String.fromCharCode((c & 63) | 128); 104 | } 105 | 106 | } 107 | 108 | return utftext; 109 | }, 110 | 111 | // private method for UTF-8 decoding 112 | _utf8_decode : function (utftext) { 113 | var string = ""; 114 | var i = 0; 115 | var c = c1 = c2 = 0; 116 | 117 | while ( i < utftext.length ) { 118 | 119 | c = utftext.charCodeAt(i); 120 | 121 | if (c < 128) { 122 | string += String.fromCharCode(c); 123 | i++; 124 | } 125 | else if((c > 191) && (c < 224)) { 126 | c2 = utftext.charCodeAt(i+1); 127 | string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); 128 | i += 2; 129 | } 130 | else { 131 | c2 = utftext.charCodeAt(i+1); 132 | c3 = utftext.charCodeAt(i+2); 133 | string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); 134 | i += 3; 135 | } 136 | 137 | } 138 | 139 | return string; 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/localIndex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Example - Development Start Page 9 | 21 | 41 | 42 | 43 | 49 | 50 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "_version": "1.2.0", 3 | "sap.app": { 4 | "_version": "1.2.0", 5 | "id": "SLFiori", 6 | "type": "application", 7 | "i18n": "i18n/i18n.properties", 8 | "title": "{{appTitle}}", 9 | "description": "{{appDescription}}", 10 | "applicationVersion": { 11 | "version": "1.0.0" 12 | }, 13 | "dataSources": { 14 | "B1H_ServiceLayer": { 15 | "uri": "https://10.55.129.63:50000/b1s/v1/", 16 | "type": "OData", 17 | "settings": { 18 | "localUri": "model/metadata.xml" 19 | } 20 | } 21 | }, 22 | "sourceTemplate": { 23 | "id": "ui5template.2masterdetailcrud", 24 | "version": "1.34.3" 25 | }, 26 | "crossNavigation": { 27 | "inbounds": {} 28 | } 29 | }, 30 | "sap.ui": { 31 | "_version": "1.2.0", 32 | "technology": "UI5", 33 | "icons": { 34 | "icon": "sap-icon://detail-view", 35 | "favIcon": "", 36 | "phone": "", 37 | "phone@2": "", 38 | "tablet": "", 39 | "tablet@2": "" 40 | }, 41 | "deviceTypes": { 42 | "desktop": true, 43 | "tablet": true, 44 | "phone": true 45 | }, 46 | "supportedThemes": [ 47 | "sap_hcb", 48 | "sap_bluecrystal" 49 | ] 50 | }, 51 | "sap.ui5": { 52 | "_version": "1.1.0", 53 | "rootView": { 54 | "viewName": "SLFiori.view.App", 55 | "type": "XML", 56 | "id": "app" 57 | }, 58 | "dependencies": { 59 | "minUI5Version": "1.30.0", 60 | "libs": { 61 | "sap.ui.core": {}, 62 | "sap.m": {}, 63 | "sap.ui.layout": {} 64 | } 65 | }, 66 | "contentDensities": { 67 | "compact": true, 68 | "cozy": true 69 | }, 70 | "models": { 71 | "i18n": { 72 | "type": "sap.ui.model.resource.ResourceModel", 73 | "settings": { 74 | "bundleName": "SLFiori.i18n" 75 | } 76 | }, 77 | "": { 78 | "dataSource": "B1H_ServiceLayer", 79 | "settings": { 80 | "metadataUrlParams": { 81 | "sap-documentation": "heading" 82 | }, 83 | "defaultBindingMode": "TwoWay" 84 | } 85 | } 86 | }, 87 | 88 | 89 | "routing": { 90 | "config": { 91 | "routerClass": "sap.m.routing.Router", 92 | "viewType": "XML", 93 | "viewPath": "SLFiori.view", 94 | "controlId": "fioriContent", 95 | "controlAggregation": "detailPages", 96 | "bypassed": { 97 | "target": [ 98 | "master", 99 | "notFound" 100 | ] 101 | }, 102 | "async": "true" 103 | }, 104 | "routes": [ 105 | { 106 | "pattern": "", 107 | "name": "master", 108 | "target": [ 109 | "master", 110 | "detail" 111 | ] 112 | }, 113 | { 114 | "pattern": "Detail/{DocEntry}", 115 | "name": "detail", 116 | "target": [ 117 | "master", 118 | "detail" 119 | ] 120 | } 121 | ], 122 | "targets": { 123 | "master": { 124 | "viewName": "Master", 125 | "viewLevel": 1, 126 | "viewId": "master", 127 | "controlAggregation": "masterPages" 128 | }, 129 | "create": { 130 | "viewName": "CreateEdit", 131 | "viewLevel": 2 132 | }, 133 | "detailObjectNotFound": { 134 | "viewName": "DetailObjectNotFound", 135 | "viewId": "detailObjectNotFound" 136 | }, 137 | "detailNoObjectsAvailable": { 138 | "viewName": "DetailNoObjectsAvailable", 139 | "viewId": "detailNoObjectsAvailable" 140 | }, 141 | "notFound": { 142 | "viewName": "NotFound", 143 | "viewId": "notFound" 144 | }, 145 | "detail": { 146 | "viewType": "XML", 147 | "transition": "slide", 148 | "clearAggregation": "true", 149 | "viewName": "Detail", 150 | "viewId": "detail", 151 | "viewLevel": 2 152 | } 153 | } 154 | } 155 | }, 156 | "sap.platform.hcp": { 157 | "uri": "webapp", 158 | "_version": "1.1.0" 159 | } 160 | } -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/manifest_mine.json: -------------------------------------------------------------------------------- 1 | { 2 | "_version": "1.1.0", 3 | 4 | "start_url": "", 5 | 6 | "sap.app": { 7 | "_version": "1.1.0", 8 | "id": "", 9 | "type": "application", 10 | "i18n": "", 11 | "applicationVersion": { 12 | "version": "" 13 | }, 14 | "title": "{{}}", 15 | "tags": { 16 | "keywords": [ 17 | "{{<keyword1>}}", "{{<keyword2>}}" 18 | ] 19 | }, 20 | "dataSources": { 21 | "mainService": { 22 | "uri": "https://:50000/b1s/v1/", 23 | "type": "OData", 24 | "settings": { 25 | "odataVersion": "2.0", 26 | "localUri": "localService/metadata.xml" 27 | } 28 | } 29 | }, 30 | "sap.ui": { 31 | "_version": "1.1.0", 32 | "icons": { 33 | "icon": "<icon>", 34 | "favIcon": "<favIcon>", 35 | "phone": "<phone>", 36 | "phone@2": "<phone@2>", 37 | "tablet": "<tablet>", 38 | "tablet@2": "<tablet@2>" 39 | }, 40 | "deviceTypes": { 41 | "desktop": true, 42 | "tablet": true, 43 | "phone": true 44 | }, 45 | "supportedThemes": [ 46 | "sap_hcb", 47 | "sap_bluecrystal" 48 | ] 49 | }, 50 | "sap.ui5": { 51 | "_version": "1.1.0", 52 | "resources": { 53 | "js": [ 54 | { 55 | "uri": "<uri>" 56 | } 57 | ], 58 | "css": [ 59 | { 60 | "uri": "<uri>", 61 | "id": "<id>" 62 | } 63 | ] 64 | }, 65 | "dependencies": { 66 | "minUI5Version": "<minUI5Version>", 67 | "libs": { 68 | "<ui5lib1>": { 69 | "minVersion": "<minVersion1>" 70 | }, 71 | "<ui5lib2>": { 72 | "minVersion": "<minVersion2>" 73 | } 74 | }, 75 | "components": { 76 | "<ui5component1>": { 77 | "minVersion": "<minComp1Version>" 78 | } 79 | } 80 | }, 81 | "models": { 82 | "i18n": { 83 | "type": "sap.ui.model.resource.ResourceModel", 84 | "uri": "<uriRelativeToManifest>" 85 | }, 86 | "": { 87 | "dataSource": "<dataSourceAlias>", 88 | "settings": {} 89 | } 90 | }, 91 | "rootView": "<rootView>", 92 | "handleValidation": <true|false>, 93 | "config": { 94 | 95 | }, 96 | "routing": { 97 | 98 | }, 99 | "extends": { 100 | "component" : "<extendedComponentId>", 101 | "minVersion": "<minComp1Version>", 102 | "extensions": {} 103 | } 104 | "contentDensities": { 105 | "compact": <true|false>, 106 | "cozy": <true|false> 107 | } 108 | }, 109 | 110 | "sap.platform.abap": { 111 | "_version": "1.1.0", 112 | "uri": "<uri>" 113 | }, 114 | "sap.platform.hcp": { 115 | "_version": "1.1.0", 116 | "uri": "<uri>" 117 | } 118 | } -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/neo-app.json: -------------------------------------------------------------------------------- 1 | { 2 | "welcomeFile": "index.html", 3 | "routes": [ 4 | { 5 | "path": "/resources", 6 | "target": { 7 | "type": "service", 8 | "name": "sapui5", 9 | "entryPath": "/resources" 10 | }, 11 | "description": " SAPUI5 Resources" 12 | }, 13 | { 14 | "path": "/test-resources", 15 | "target": { 16 | "type": "service", 17 | "name": "sapui5", 18 | "entryPath": "/test-resources" 19 | }, 20 | "description": " SAPUI5 Test Resources" 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/view/App.controller.js: -------------------------------------------------------------------------------- 1 | sap.ui.controller("SLFiori.view.App", { 2 | onInit : function() 3 | { 4 | }, 5 | 6 | onCompleteCall1: function(result) 7 | { 8 | document.cookie = result.cookies[0].value; 9 | B1HSL_SessionID = document.cookie; 10 | alert(B1HSL_SessionID); 11 | 12 | // TO BE MODIFIED // 13 | // You might need to adjust the CMD parameter based on the Business Object of your own choice" // 14 | // In the example below, you will retrieve Items Master Data // 15 | // TO BE MODIFIED // 16 | var xsjsUrl = "xsjs/B1SLLogic.xsjs?cmd=Items&sessionID=" + B1HSL_SessionID; 17 | jQuery.ajax({ 18 | type: "GET", 19 | xhrFields: 20 | { 21 | withCredentials: true 22 | }, 23 | url: xsjsUrl, 24 | dataType: "json", 25 | crossDomain: true, 26 | success: function (result) { 27 | 28 | ItemsJSONData = result.body; 29 | alert(ItemsJSONData); 30 | }, 31 | error: this.onErrorCall 32 | }); 33 | }, 34 | 35 | onErrorCall: function(jqXHR, textStatus, errorThrown) 36 | { 37 | sap.ui.commons.MessageBox.show(jqXHR.responseText, 38 | "ERROR", 39 | "Error in calling Service" ); 40 | return; 41 | } 42 | }); -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/view/App.view.xml: -------------------------------------------------------------------------------- 1 | <mvc:View xmlns:core="sap.ui.core" xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc" 2 | controllerName="SLFiori.view.App" displayBlock="true" height="100%"> 3 | <SplitApp id="fioriContent" showHeader="false" mode="StretchCompressMode"> 4 | <masterPages> 5 | <!-- filled dynamically by routing --> 6 | </masterPages> 7 | <detailPages> 8 | <!-- filled dynamically by routing --> 9 | </detailPages> 10 | </SplitApp> 11 | </mvc:View> -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/view/Detail.controller.js: -------------------------------------------------------------------------------- 1 | sap.ui.core.mvc.Controller.extend("SLFiori.view.Detail", { 2 | 3 | oModel: null, 4 | 5 | // TO BE MODIFIED // 6 | // Below is the binding procedure of the Details view in order to display the Items Master data in the header area // 7 | // TO BE MODIFIED // 8 | onInit: function() 9 | { 10 | var view = this.getView(); 11 | oModel = new sap.ui.model.json.JSONModel(); 12 | // 13 | oModel.setData(ItemsJSONData); 14 | view.setModel(oModel); 15 | 16 | // 17 | sap.ui.core.UIComponent.getRouterFor(this).attachRouteMatched(function(oEvent) { 18 | // when detail navigation occurs, update the binding context 19 | // TO BE MODIFIED // 20 | // Below is the binding procedure of the Items Table view in order to display the Sales Order Document Lines in the Details area // 21 | // You might need to change the Service Identifier from /DocumentLines to match your Entity Lines collection name 22 | // TO BE MODIFIED // 23 | if (oEvent.getParameter("name") === "Detail") 24 | { 25 | var context = new sap.ui.model.Context(view.getModel(), '/value/' + oEvent.getParameter("arguments").contextPath); 26 | view.setBindingContext(context); 27 | // Make sure the master is here 28 | var oItemsTbl = this.getView().byId("idItemsTable"); 29 | var oColListItem = this.getView().byId("idColListItem"); 30 | oItemsTbl.setModel(oModel); 31 | oItemsTbl.bindItems(context.sPath + "/DocumentLines", oColListItem); 32 | } 33 | }, this); 34 | 35 | }, 36 | 37 | openActionSheet: function() { 38 | 39 | if (!this._oActionSheet) { 40 | this._oActionSheet = new sap.m.ActionSheet({ 41 | buttons: new sap.ushell.ui.footerbar.AddBookmarkButton() 42 | }); 43 | this._oActionSheet.setShowCancelButton(true); 44 | this._oActionSheet.setPlacement(sap.m.PlacementType.Top); 45 | } 46 | 47 | this._oActionSheet.openBy(this.getView().byId("actionButton")); 48 | }, 49 | 50 | onExit: function() { 51 | if (this._oActionSheet) { 52 | this._oActionSheet.destroy(); 53 | this._oActionSheet = null; 54 | } 55 | }, 56 | 57 | // TO BE MODIFIED // 58 | // Below is the function definition to call the Service Layer for closing an opened sales order // 59 | // TO BE MODIFIED // 60 | handleCloseBtnPress: function(oEvent) { 61 | var oListItem = ordersList.getSelectedItem(); 62 | oListItem.setMarkLocked(true); 63 | ordersList.setSelectedItem(false); 64 | // 65 | var id = oListItem.getBindingContext().getPath().substr(7); 66 | var docStatusTxt = this.getView().byId("text2"); 67 | docStatusTxt.setText("Closed"); 68 | // 69 | closeOrder(); 70 | }, 71 | 72 | // TO BE MODIFIED // 73 | // Below is the function definition to call the Service Layer for canceling an opened sales order // 74 | // TO BE MODIFIED // 75 | handleCancelBtnPress: function(oEvent) { 76 | var oListItem = ordersList.getSelectedItem(); 77 | oListItem.setMarkLocked(true); 78 | ordersList.setSelectedItem(false); 79 | // 80 | var id = oListItem.getBindingContext().getPath().substr(7); 81 | var docStatusTxt = this.getView().byId("text2"); 82 | docStatusTxt.setText("Cancelled"); 83 | // 84 | cancelOrder(); 85 | }, 86 | 87 | handleNavButtonPress: function(oEvent) { 88 | var history = sap.ui.core.routing.History.getInstance(); 89 | var url = sap.ui.core.UIComponent.getRouterFor(this).getURL("master", {}); 90 | var direction = history.getDirection(url); 91 | if ("Backwards" === direction) { 92 | window.history.go(-1); 93 | } else { 94 | var replace = true; // otherwise we go backwards with a forward history 95 | this.navTo(route, data, replace); 96 | } 97 | } 98 | }); 99 | -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/view/Master.controller.js: -------------------------------------------------------------------------------- 1 | jQuery.sap.require("sap.m.MessageBox"); 2 | 3 | sap.ui.core.mvc.Controller.extend("SLFiori.view.Master", { 4 | 5 | oModel: null, 6 | 7 | onInit: function() 8 | { 9 | // TO BE MODIFIED // 10 | // Below is the binding procedure of the ListItem in order to display the Items Master data in a flat list // 11 | // TO BE MODIFIED // 12 | var oList = this.getView().byId("list"); 13 | ordersList = oList; 14 | var oListitem = this.getView().byId("mainListItem"); 15 | oModel = new sap.ui.model.json.JSONModel(); 16 | oModel.setData(ItemsJSONData); 17 | oList.setModel(oModel); 18 | oList.bindItems("/value", oListitem); 19 | this.getView().setModel(oModel); 20 | 21 | }, 22 | 23 | handleSearch: function() { 24 | // add filter for search 25 | var filters = []; 26 | var searchString = this.getView().byId("searchField").getValue(); 27 | if (searchString && searchString.length > 0) { 28 | filters = [ new sap.ui.model.Filter("CardName", sap.ui.model.FilterOperator.Contains, searchString) ]; 29 | } 30 | 31 | // update list binding 32 | var list = this.getView().byId("list"); 33 | var binding = list.getBinding("items"); 34 | binding.filter(filters); 35 | }, 36 | 37 | onGrowingList: function(oEvent) { 38 | //alert(oEvent.getParameter('actual') + ", " + oEvent.getParameter('total')); 39 | }, 40 | 41 | onUpdatingList: function(oEvent) { 42 | //alert(oEvent.getParameter('reason') + "," + oEvent.getParameter('actual') + ", " + oEvent.getParameter('total')); 43 | }, 44 | 45 | handleSelect: function(oEvent) 46 | { 47 | var oListItem = oEvent.getParameter("listItem") || oEvent.getSource(); 48 | selectedOrder = oListItem.mAggregations.attributes[0].mProperties.text; 49 | // trigger routing to BindingPath of this ListItem - this will update the data on the detail page 50 | sap.ui.core.UIComponent.getRouterFor(this).navTo("Detail",{from: "master", contextPath: oListItem.getBindingContext().getPath().substr(7)}); 51 | } 52 | }); -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/view/Master.view.xml: -------------------------------------------------------------------------------- 1 | <mvc:View xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" 2 | xmlns:shellfooter="sap.ushell.ui.footerbar" controllerName="SLFiori.view.Master" afterInit="hanldeAfterInit"> 3 | 4 | <Page id="page" title="List of Open Orders"> 5 | <subHeader id="subHeader"> 6 | <Bar id="bar1"> 7 | <contentMiddle id="contentMiddle"> 8 | <SearchField id="searchField" showRefreshButton="{device>/isNoTouch}" 9 | placeholder="Search for Sales Order by Customer" search="handleSearch" 10 | tooltip="Search for SO by Customer Name" width="100%"> 11 | </SearchField> 12 | </contentMiddle> 13 | </Bar> 14 | </subHeader> 15 | 16 | <content id="content"> 17 | <!-- TO BE MODIFIED --> 18 | <!-- set the below attribute so-called items with your xsodata service identifier --> 19 | <!-- In case of using the B1 Service Layer for reading data, the attribute items must be equal to {/value} --> 20 | <!-- You might need to adjust the attribute text of <ObjectAttribute> inside <ObjectListItem>, based on your own business logic --> 21 | <!-- TO BE MODIFIED --> 22 | <List id="list" items="{/value}" mode="{device>/listMode}" 23 | select="handleSelect" growing="true" growingScrollToLoad="false" growingStarted="onGrowingList" updateStarted="onUpdatingList"> 24 | <customData id="customData1"> 25 | <core:CustomData id="coreCustomData1" key="sapDtResourcePath" value="Items"></core:CustomData> 26 | </customData> 27 | 28 | <headerToolbar> 29 | <Toolbar> 30 | <Title text="Sales Orders" level="H2" /> 31 | </Toolbar> 32 | </headerToolbar> 33 | 34 | <items id="listItems"> 35 | <ObjectListItem id="mainListItem" type="{device>/listItemType}" 36 | title="{CardName}" 37 | number="{DocTotal}" 38 | numberUnit="{DocCurrency}" 39 | press="handleSelect"> 40 | <customData id="customData2"> 41 | <core:CustomData id="coreCustomData2" key="sapDtResourcePath" value="Items"></core:CustomData> 42 | </customData> 43 | <attributes id="attributes"> 44 | 45 | <ObjectAttribute id="ATTR1" text="{DocEntry}"> 46 | <customData id="customData3"> 47 | <core:CustomData id="coreCustomData3" key="sapDtResourcePath" value="Items"></core:CustomData> 48 | </customData> 49 | </ObjectAttribute> 50 | 51 | 52 | <ObjectAttribute id="ATTR2" text="{DocDate}"> 53 | <customData id="customData4"> 54 | <core:CustomData id="coreCustomData4" key="sapDtResourcePath" value="Items"></core:CustomData> 55 | </customData> 56 | </ObjectAttribute> 57 | 58 | </attributes> 59 | </ObjectListItem> 60 | </items> 61 | </List> 62 | </content> 63 | 64 | <footer id="footer"> 65 | <Bar id="bar2"> 66 | <contentLeft id="contentLeft"> 67 | <shellfooter:SettingsButton id="settingsButton"></shellfooter:SettingsButton> 68 | </contentLeft> 69 | </Bar> 70 | </footer> 71 | </Page> 72 | 73 | </mvc:View> -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/xsjs/B1SL.xshttpdest: -------------------------------------------------------------------------------- 1 | description = "B1 SL https connection"; 2 | host = "<TODO: Please replace with your Service Layer host>"; 3 | port = 50000; 4 | authType = none; 5 | useSSL = true; 6 | timeout = 0; 7 | sslHostCheck = true; 8 | sslAuth = client; 9 | -------------------------------------------------------------------------------- /12_Fiori/SLFioriAppXSJS/SLFiori/xsjs/B1SLLogic.xsjs: -------------------------------------------------------------------------------- 1 | function callServiceLayer(path, method, body, sessionID, routeID) { 2 | try { 3 | $.trace.debug("callServiceLayer (path: " + path + ", method: " + method + ", body: " + body + ", sessionID: " + sessionID + ", routeID: " + routeID + ")"); 4 | 5 | var destination = $.net.http.readDestination("SLFioriAppXSJS.SLFiori.xsjs", "B1SL"); 6 | 7 | var client = new $.net.http.Client(); 8 | 9 | var header = ""; 10 | if (method === $.net.http.PATCH) { 11 | method = $.net.http.POST; 12 | header = "X-HTTP-Method-Override: PATCH"; 13 | } 14 | 15 | var req = new $.web.WebRequest(method, path); 16 | 17 | if (header !== "") { 18 | req.headers.set("X-HTTP-Method-Override", "PATCH"); 19 | } 20 | 21 | if (body) { 22 | req.setBody(body); 23 | } 24 | 25 | if (sessionID) { 26 | req.cookies.set("B1SESSION", sessionID); 27 | } 28 | if (routeID) { 29 | req.cookies.set("ROUTEID", routeID); 30 | } 31 | 32 | client.request(req, destination); 33 | 34 | var response = client.getResponse(); 35 | 36 | //The rest of the file (attached) is just a default forward of the response 37 | var myCookies = [], 38 | myHeader = [], 39 | myBody = null; 40 | 41 | //Cookies 42 | for (var c in response.cookies) { 43 | myCookies.push(response.cookies[c]); 44 | } 45 | //Headers 46 | for (var h in response.headers) { 47 | myHeader.push(response.headers[h]); 48 | } 49 | //Body 50 | if (response.body) 51 | try { 52 | myBody = JSON.parse(response.body.asString()); 53 | } catch (e) { 54 | myBody = response.body.asString(); 55 | } 56 | 57 | $.response.contentType = "application/json"; 58 | 59 | $.response.status = response.status; 60 | 61 | $.response.setBody(JSON.stringify({ 62 | "status": response.status, 63 | "cookies": myCookies, 64 | "headers": myHeader, 65 | "body": myBody 66 | })); 67 | 68 | $.trace.debug("callServiceLayer response status: " + $.response.status); 69 | } catch (e) { 70 | $.trace.warning("callServiceLayer Exception: " + e.message); 71 | $.response.contentType = "application/json"; 72 | $.response.setBody(JSON.stringify({ 73 | "error": e.message 74 | })); 75 | } 76 | } 77 | 78 | var B1SLAddress = "/b1s/v1/"; 79 | var aCmd = $.request.parameters.get('cmd'); 80 | var actionURI = $.request.parameters.get('actionUri'); 81 | var sessionID = $.request.parameters.get('sessionID'); 82 | var routeID = $.request.parameters.get('routeID'); 83 | var filter = $.request.parameters.get('filter'); 84 | var path = B1SLAddress + actionURI; 85 | var body = null; 86 | 87 | if ($.request.body) 88 | { 89 | body = $.request.body.asString(); 90 | } 91 | 92 | $.trace.debug("B1SLogic cmd: " + aCmd); 93 | 94 | // TO BE MODIFIED // 95 | // You might need to adjust the switch cases below based on your own business logic "Business Objects on your own choice" // 96 | // TO BE MODIFIED // 97 | 98 | switch (aCmd) { 99 | case 'login': 100 | path = B1SLAddress + "Login"; 101 | callServiceLayer(path, $.net.http.POST, body, sessionID, routeID); 102 | break; 103 | case 'Add': 104 | callServiceLayer(path, $.net.http.POST, body, sessionID, routeID); 105 | break; 106 | case 'Update': 107 | callServiceLayer(path, $.net.http.PATCH, body, sessionID, routeID); 108 | break; 109 | case 'Delete': 110 | callServiceLayer(path, $.net.http.DEL, body, sessionID, routeID); 111 | break; 112 | case 'Get': 113 | if(filter != "") 114 | path += "?$top=10&$filter=" + filter + "%20eq%20'O'"; 115 | callServiceLayer(path, $.net.http.GET, body, sessionID, routeID); 116 | break; 117 | case 'Action': 118 | callServiceLayer(path, $.net.http.POST, body, sessionID, routeID); 119 | break; 120 | default: 121 | $.trace.warning("callServiceLayer Exception: " + e.message); 122 | $.response.status = $.net.http.INTERNAL_SERVER_ERROR; 123 | $.response.contentType = "application/json"; 124 | $.response.setBody(JSON.stringify({ 125 | "Unknown command": aCmd 126 | })); 127 | break; 128 | } -------------------------------------------------------------------------------- /12_Fiori/SalesInvoices.xsodata: -------------------------------------------------------------------------------- 1 | service{ 2 | "SBODEMOUS"."OINV" as "doc" navigates ("ar_doc" as "ar_doc") 3 | create forbidden 4 | update forbidden 5 | delete forbidden; 6 | 7 | "SBODEMOUS"."INV1" as "lines" 8 | with ("DocEntry","LineNum","ItemCode","Description", "Quantity", "Price","LineTotal") 9 | create forbidden 10 | update forbidden 11 | delete forbidden; 12 | 13 | association "ar_doc" principal "lines"("DocEntry") multiplicity "*" dependent "doc"("DocEntry") multiplicity "*"; 14 | } -------------------------------------------------------------------------------- /12_Fiori/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html> 3 | <head> 4 | <meta http-equiv="X-UA-Compatible" content="IE=edge" /> 5 | <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" /> 6 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 7 | <title>Business Partners App 8 | 9 | 17 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # workshop [DEPRECATED] 2 | Samples used in the B1H Workshop/Elearning 3 | --------------------------------------------------------------------------------