7.10.4. Displaying the Invoices

The JSP page contains the layout for displaying the grid with invoice headers and the navigation bar. Invoice items are displayed as a drop-down grid when the header of the selected invoice is clicked.

  1. <%@page contentType="text/html" pageEncoding="UTF-8"%>
  2. <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
  3. <c:set var="cp" value="${pageContext.request.servletContext.contextPath}"
  4. scope="request" />
  5. <!DOCTYPE html>
  6. <html>
  7. <head>
  8. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  9. <title>An example of a Spring MVC application using Firebird and jOOQ</title>
  10. <!-- Scripts and styles -->
  11. <%@ include file="../jspf/head.jspf" %>
  12. <script src="${cp}/resources/js/jqGridProduct.js"></script>
  13. <script src="${cp}/resources/js/jqGridCustomer.js"></script>
  14. <script src="${cp}/resources/js/jqGridInvoice.js"></script>
  15. </head>
  16. <body>
  17. <!-- Navigation menu -->
  18. <%@ include file="../jspf/menu.jspf" %>
  19. <div class="container body-content">
  20. <h2>Invoices</h2>
  21. <table id="jqGridInvoice"></table>
  22. <div id="jqPagerInvoice"></div>
  23. <hr />
  24. <footer>
  25. <p>&copy; 2016 - An example of a Spring MVC application using
  26. Firebird and jOOQ</p>
  27. </footer>
  28. </div>
  29. <script type="text/javascript">
  30. var invoiceGrid = null;
  31. $(document).ready(function () {
  32. invoiceGrid = JqGridInvoice({
  33. baseAddress: '${cp}'
  34. });
  35. });
  36. </script>
  37. </body>
  38. </html>

The basic logic on the client side is concentrated in the /resources/js/jqGridInvoice.js JavaScript module.

  1. var JqGridInvoice = (function ($, jqGridProductFactory, jqGridCustomerFactory) {
  2. return function (options) {
  3. var jqGridInvoice = {
  4. dbGrid: null,
  5. detailGrid: null,
  6. options: $.extend({
  7. baseAddress: null
  8. }, options),
  9. // return invoice model description
  10. getInvoiceColModel: function () {
  11. return [
  12. {
  13. label: 'Id',
  14. name: 'INVOICE_ID', // field name
  15. key: true,
  16. hidden: true
  17. },
  18. {
  19. label: 'Customer Id'
  20. name: 'CUSTOMER_ID',
  21. hidden: true,
  22. editrules: {edithidden: true, required: true},
  23. editable: true,
  24. edittype: 'custom', // custom type
  25. editoptions: {
  26. custom_element: function (value, options) {
  27. // add hidden input
  28. return $("<input>")
  29. .attr('type', 'hidden')
  30. .attr('rowid', options.rowId)
  31. .addClass("FormElement")
  32. .addClass("form-control")
  33. .val(value)
  34. .get(0);
  35. }
  36. }
  37. },
  38. {
  39. label: 'Date',
  40. name: 'INVOICE_DATE',
  41. width: 60,
  42. sortable: true,
  43. editable: true,
  44. search: true,
  45. edittype: "text", // input type
  46. align: "right",
  47. // format as date
  48. formatter: jqGridInvoice.dateTimeFormatter,
  49. sorttype: 'date', // sort as date
  50. formatoptions: {
  51. srcformat: 'Y-m-d\TH:i:s', // input format
  52. newformat: 'Y-m-d H:i:s' // output format
  53. },
  54. editoptions: {
  55. // initializing the form element for editing
  56. dataInit: function (element) {
  57. // creating datepicker
  58. $(element).datepicker({
  59. id: 'invoiceDate_datePicker',
  60. dateFormat: 'dd.mm.yy',
  61. minDate: new Date(2000, 0, 1),
  62. maxDate: new Date(2030, 0, 1)
  63. });
  64. }
  65. },
  66. searchoptions: {
  67. // initializing the form element for searching
  68. dataInit: function (element) {
  69. // create datepicker
  70. $(element).datepicker({
  71. id: 'invoiceDate_datePicker',
  72. dateFormat: 'dd.mm.yy',
  73. minDate: new Date(2000, 0, 1),
  74. maxDate: new Date(2030, 0, 1)
  75. });
  76. },
  77. searchoptions: { // search types
  78. sopt: ['eq', 'lt', 'le', 'gt', 'ge']
  79. }
  80. }
  81. },
  82. {
  83. label: 'Customer',
  84. name: 'CUSTOMER_NAME',
  85. width: 250,
  86. editable: true,
  87. edittype: "text",
  88. editoptions: {
  89. size: 50,
  90. maxlength: 60,
  91. readonly: true
  92. },
  93. editrules: {required: true},
  94. search: true,
  95. searchoptions: {
  96. sopt: ['eq', 'bw', 'cn']
  97. }
  98. },
  99. {
  100. label: 'Amount',
  101. name: 'TOTAL_SALE',
  102. width: 60,
  103. sortable: false,
  104. editable: false,
  105. search: false,
  106. align: "right",
  107. // foramt as currency
  108. formatter: 'currency',
  109. sorttype: 'number',
  110. searchrules: {
  111. "required": true,
  112. "number": true,
  113. "minValue": 0
  114. }
  115. },
  116. {
  117. label: 'Paid',
  118. name: 'PAID',
  119. width: 30,
  120. sortable: false,
  121. editable: true,
  122. search: true,
  123. searchoptions: {
  124. sopt: ['eq']
  125. },
  126. edittype: "checkbox",
  127. formatter: "checkbox",
  128. stype: "checkbox",
  129. align: "center",
  130. editoptions: {
  131. value: "1",
  132. offval: "0"
  133. }
  134. }
  135. ];
  136. },
  137. initGrid: function () {
  138. // url to retrieve data
  139. var url = jqGridInvoice.options.baseAddress + '/invoice/getdata';
  140. jqGridInvoice.dbGrid = $("#jqGridInvoice").jqGrid({
  141. url: url,
  142. datatype: "json", // data format
  143. mtype: "GET", // http request type
  144. // model description
  145. colModel: jqGridInvoice.getInvoiceColModel(),
  146. rowNum: 500, // number of rows displayed
  147. loadonce: false, // load only once
  148. // default sort by INVOICE_DATE column
  149. sortname: 'INVOICE_DATE',
  150. sortorder: "desc", // sorting order
  151. width: window.innerWidth - 80,
  152. height: 500,
  153. viewrecords: true, // display the number of entries
  154. guiStyle: "bootstrap",
  155. iconSet: "fontAwesome",
  156. caption: "Invoices",
  157. // pagination element
  158. pager: '#jqPagerInvoice',
  159. subGrid: true, // show subGrid
  160. // javascript function to display the child grid
  161. subGridRowExpanded: jqGridInvoice.showChildGrid,
  162. subGridOptions: {
  163. // load only once
  164. reloadOnExpand: false,
  165. // load the subgrid string only when you click on the "+"
  166. selectOnExpand: true
  167. }
  168. });
  169. },
  170. // date format function
  171. dateTimeFormatter: function(cellvalue, options, rowObject) {
  172. var date = new Date(cellvalue);
  173. return date.toLocaleString().replace(",", "");
  174. },
  175. // returns a template for the editing dialog
  176. getTemplate: function () {
  177. var template = "<div style='margin-left:15px;' id='dlgEditInvoice'>";
  178. template += "<div>{CUSTOMER_ID} </div>";
  179. template += "<div> Date: </div><div>{INVOICE_DATE}</div>";
  180. // customer input field with a button
  181. template += "<div> Customer <sup>*</sup>:</div>";
  182. template += "<div>";
  183. template += "<div style='float: left;'>{CUSTOMER_NAME}</div> ";
  184. template += "<a style='margin-left: 0.2em;' class='btn' ";
  185. template += "onclick='invoiceGrid.showCustomerWindow(); ";
  186. template += "return false;'>";
  187. template += "<span class='glyphicon glyphicon-folder-open'>";
  188. template += "</span>Select</a> ";
  189. template += "<div style='clear: both;'></div>";
  190. template += "</div>";
  191. template += "<div> {PAID} Paid </div>";
  192. template += "<hr style='width: 100%;'/>";
  193. template += "<div> {sData} {cData} </div>";
  194. template += "</div>";
  195. return template;
  196. },
  197. // date conversion in UTC
  198. convertToUTC: function(datetime) {
  199. if (datetime) {
  200. var dateParts = datetime.split('.');
  201. var date = dateParts[2].substring(0, 4) + '-' +
  202. dateParts[1] + '-' + dateParts[0];
  203. var time = dateParts[2].substring(5);
  204. if (!time) {
  205. time = '00:00:00';
  206. }
  207. var dt = Date.parse(date + 'T' + time);
  208. var s = dt.getUTCFullYear() + '-' +
  209. dt.getUTCMonth() + '-' +
  210. dt.getUTCDay() + 'T' +
  211. dt.getUTCHour() + ':' +
  212. dt.getUTCMinute() + ':' +
  213. dt.getUTCSecond() + ' GMT';
  214. return s;
  215. } else
  216. return null;
  217. },
  218. // returns the options for editing invoices
  219. getEditInvoiceOptions: function () {
  220. return {
  221. url: jqGridInvoice.options.baseAddress + '/invoice/edit',
  222. reloadAfterSubmit: true,
  223. closeOnEscape: true,
  224. closeAfterEdit: true,
  225. drag: true,
  226. modal: true,
  227. top: $(".container.body-content").position().top + 150,
  228. left: $(".container.body-content").position().left + 150,
  229. template: jqGridInvoice.getTemplate(),
  230. afterSubmit: jqGridInvoice.afterSubmit,
  231. editData: {
  232. INVOICE_ID: function () {
  233. var selectedRow = jqGridInvoice.dbGrid.getGridParam("selrow");
  234. var value = jqGridInvoice.dbGrid
  235. .getCell(selectedRow, 'INVOICE_ID');
  236. return value;
  237. },
  238. CUSTOMER_ID: function () {
  239. return $('#dlgEditInvoice input[name=CUSTOMER_ID]').val();
  240. },
  241. INVOICE_DATE: function () {
  242. var datetime = $('#dlgEditInvoice input[name=INVOICE_DATE]')
  243. .val();
  244. return jqGridInvoice.convertToUTC(datetime);
  245. }
  246. }
  247. };
  248. },
  249. // returns options for adding invoices
  250. getAddInvoiceOptions: function () {
  251. return {
  252. url: jqGridInvoice.options.baseAddress + '/invoice/create',
  253. reloadAfterSubmit: true,
  254. closeOnEscape: true,
  255. closeAfterAdd: true,
  256. drag: true,
  257. modal: true,
  258. top: $(".container.body-content").position().top + 150,
  259. left: $(".container.body-content").position().left + 150,
  260. template: jqGridInvoice.getTemplate(),
  261. afterSubmit: jqGridInvoice.afterSubmit,
  262. editData: {
  263. CUSTOMER_ID: function () {
  264. return $('#dlgEditInvoice input[name=CUSTOMER_ID]').val();
  265. },
  266. INVOICE_DATE: function () {
  267. var datetime = $('#dlgEditInvoice input[name=INVOICE_DATE]')
  268. .val();
  269. return jqGridInvoice.convertToUTC(datetime);
  270. }
  271. }
  272. };
  273. },
  274. // returns the options for deleting invoices
  275. getDeleteInvoiceOptions: function () {
  276. return {
  277. url: jqGridInvoice.options.baseAddress + '/invoice/delete',
  278. reloadAfterSubmit: true,
  279. closeOnEscape: true,
  280. closeAfterDelete: true,
  281. drag: true,
  282. msg: "Delete the selected invoice?",
  283. afterSubmit: jqGridInvoice.afterSubmit,
  284. delData: {
  285. INVOICE_ID: function () {
  286. var selectedRow = jqGridInvoice.dbGrid.getGridParam("selrow");
  287. var value = jqGridInvoice.dbGrid
  288. .getCell(selectedRow, 'INVOICE_ID');
  289. return value;
  290. }
  291. }
  292. };
  293. },
  294. initPager: function () {
  295. // display the navigation bar
  296. jqGridInvoice.dbGrid.jqGrid('navGrid', '#jqPagerInvoice',
  297. {
  298. search: true,
  299. add: true,
  300. edit: true,
  301. del: true,
  302. view: false,
  303. refresh: true,
  304. searchtext: "Search",
  305. addtext: "Add",
  306. edittext: "Edit",
  307. deltext: "Delete",
  308. viewtext: "View",
  309. viewtitle: "Selected record",
  310. refreshtext: "Refresh"
  311. },
  312. jqGridInvoice.getEditInvoiceOptions(),
  313. jqGridInvoice.getAddInvoiceOptions(),
  314. jqGridInvoice.getDeleteInvoiceOptions()
  315. );
  316. // Add a button to pay the invoice
  317. var urlPay = jqGridInvoice.options.baseAddress + '/invoice/pay';
  318. jqGridInvoice.dbGrid.navButtonAdd('#jqPagerInvoice',
  319. {
  320. buttonicon: "glyphicon-usd",
  321. title: "Pay",
  322. caption: "Pay",
  323. position: "last",
  324. onClickButton: function () {
  325. // get the id of the current record
  326. var id = jqGridInvoice.dbGrid.getGridParam("selrow");
  327. if (id) {
  328. $.ajax({
  329. url: urlPay,
  330. type: 'POST',
  331. data: {INVOICE_ID: id},
  332. success: function (data) {
  333. // Check if an error has occurred
  334. if (data.hasOwnProperty("error")) {
  335. jqGridInvoice.alertDialog('??????',
  336. data.error);
  337. } else {
  338. // refresh grid
  339. $("#jqGridInvoice").jqGrid(
  340. 'setGridParam',
  341. {
  342. datatype: 'json'
  343. }
  344. ).trigger('reloadGrid');
  345. }
  346. }
  347. });
  348. }
  349. }
  350. }
  351. );
  352. },
  353. init: function () {
  354. jqGridInvoice.initGrid();
  355. jqGridInvoice.initPager();
  356. },
  357. afterSubmit: function (response, postdata) {
  358. var responseData = response.responseJSON;
  359. // Check if an error has occurred
  360. if (responseData.hasOwnProperty("error")) {
  361. if (responseData.error.length) {
  362. return [false, responseData.error];
  363. }
  364. } else {
  365. // refresh grid
  366. $(this).jqGrid(
  367. 'setGridParam',
  368. {
  369. datatype: 'json'
  370. }
  371. ).trigger('reloadGrid');
  372. }
  373. return [true, "", 0];
  374. },
  375. getInvoiceLineColModel: function (parentRowKey) {
  376. return [
  377. {
  378. label: 'Invoice Line ID',
  379. name: 'INVOICE_LINE_ID',
  380. key: true,
  381. hidden: true
  382. },
  383. {
  384. label: 'Invoice ID',
  385. name: 'INVOICE_ID',
  386. hidden: true,
  387. editrules: {edithidden: true, required: true},
  388. editable: true,
  389. edittype: 'custom',
  390. editoptions: {
  391. custom_element: function (value, options) {
  392. // create hidden input
  393. return $("<input>")
  394. .attr('type', 'hidden')
  395. .attr('rowid', options.rowId)
  396. .addClass("FormElement")
  397. .addClass("form-control")
  398. .val(parentRowKey)
  399. .get(0);
  400. }
  401. }
  402. },
  403. {
  404. label: 'Product ID',
  405. name: 'PRODUCT_ID',
  406. hidden: true,
  407. editrules: {edithidden: true, required: true},
  408. editable: true,
  409. edittype: 'custom',
  410. editoptions: {
  411. custom_element: function (value, options) {
  412. // create hidden input
  413. return $("<input>")
  414. .attr('type', 'hidden')
  415. .attr('rowid', options.rowId)
  416. .addClass("FormElement")
  417. .addClass("form-control")
  418. .val(value)
  419. .get(0);
  420. }
  421. }
  422. },
  423. {
  424. label: 'Product',
  425. name: 'PRODUCT_NAME',
  426. width: 300,
  427. editable: true,
  428. edittype: "text",
  429. editoptions: {
  430. size: 50,
  431. maxlength: 60,
  432. readonly: true
  433. },
  434. editrules: {required: true}
  435. },
  436. {
  437. label: 'Price',
  438. name: 'SALE_PRICE',
  439. formatter: 'currency',
  440. editable: true,
  441. editoptions: {
  442. readonly: true
  443. },
  444. align: "right",
  445. width: 100
  446. },
  447. {
  448. label: 'Quantity',
  449. name: 'QUANTITY',
  450. align: "right",
  451. width: 100,
  452. editable: true,
  453. editrules: {required: true, number: true, minValue: 1},
  454. editoptions: {
  455. dataEvents: [{
  456. type: 'change',
  457. fn: function (e) {
  458. var quantity = $(this).val() - 0;
  459. var price =
  460. $('#dlgEditInvoiceLine input[name=SALE_PRICE]').val()-0;
  461. var total = quantity * price;
  462. $('#dlgEditInvoiceLine input[name=TOTAL]').val(total);
  463. }
  464. }],
  465. defaultValue: 1
  466. }
  467. },
  468. {
  469. label: 'Total',
  470. name: 'TOTAL',
  471. formatter: 'currency',
  472. align: "right",
  473. width: 100,
  474. editable: true,
  475. editoptions: {
  476. readonly: true
  477. }
  478. }
  479. ];
  480. },
  481. // returns the options for editing the invoice item
  482. getEditInvoiceLineOptions: function () {
  483. return {
  484. url: jqGridInvoice.options.baseAddress + '/invoice/editdetail',
  485. reloadAfterSubmit: true,
  486. closeOnEscape: true,
  487. closeAfterEdit: true,
  488. drag: true,
  489. modal: true,
  490. top: $(".container.body-content").position().top + 150,
  491. left: $(".container.body-content").position().left + 150,
  492. template: jqGridInvoice.getTemplateDetail(),
  493. afterSubmit: jqGridInvoice.afterSubmit,
  494. editData: {
  495. INVOICE_LINE_ID: function () {
  496. var selectedRow = jqGridInvoice.detailGrid
  497. .getGridParam("selrow");
  498. var value = jqGridInvoice.detailGrid
  499. .getCell(selectedRow, 'INVOICE_LINE_ID');
  500. return value;
  501. },
  502. QUANTITY: function () {
  503. return $('#dlgEditInvoiceLine input[name=QUANTITY]').val();
  504. }
  505. }
  506. };
  507. },
  508. // returns options for adding an invoice item
  509. getAddInvoiceLineOptions: function () {
  510. return {
  511. url: jqGridInvoice.options.baseAddress + '/invoice/createdetail',
  512. reloadAfterSubmit: true,
  513. closeOnEscape: true,
  514. closeAfterAdd: true,
  515. drag: true,
  516. modal: true,
  517. top: $(".container.body-content").position().top + 150,
  518. left: $(".container.body-content").position().left + 150,
  519. template: jqGridInvoice.getTemplateDetail(),
  520. afterSubmit: jqGridInvoice.afterSubmit,
  521. editData: {
  522. INVOICE_ID: function () {
  523. var selectedRow = jqGridInvoice.dbGrid.getGridParam("selrow");
  524. var value = jqGridInvoice.dbGrid
  525. .getCell(selectedRow, 'INVOICE_ID');
  526. return value;
  527. },
  528. PRODUCT_ID: function () {
  529. return $('#dlgEditInvoiceLine input[name=PRODUCT_ID]').val();
  530. },
  531. QUANTITY: function () {
  532. return $('#dlgEditInvoiceLine input[name=QUANTITY]').val();
  533. }
  534. }
  535. };
  536. },
  537. // returns the option to delete the invoice item
  538. getDeleteInvoiceLineOptions: function () {
  539. return {
  540. url: jqGridInvoice.options.baseAddress + '/invoice/deletedetail',
  541. reloadAfterSubmit: true,
  542. closeOnEscape: true,
  543. closeAfterDelete: true,
  544. drag: true,
  545. msg: "Delete the selected item?",
  546. afterSubmit: jqGridInvoice.afterSubmit,
  547. delData: {
  548. INVOICE_LINE_ID: function () {
  549. var selectedRow = jqGridInvoice.detailGrid
  550. .getGridParam("selrow");
  551. var value = jqGridInvoice.detailGrid
  552. .getCell(selectedRow, 'INVOICE_LINE_ID');
  553. return value;
  554. }
  555. }
  556. };
  557. },
  558. // Event handler for the parent grid expansion event
  559. // takes two parameters: the parent record identifier
  560. // and the primary record key
  561. showChildGrid: function (parentRowID, parentRowKey) {
  562. var childGridID = parentRowID + "_table";
  563. var childGridPagerID = parentRowID + "_pager";
  564. // send the primary key of the parent record
  565. // to filter the entries of the invoice items
  566. var childGridURL = jqGridInvoice.options.baseAddress
  567. + '/invoice/getdetaildata';
  568. childGridURL = childGridURL + "?INVOICE_ID="
  569. + encodeURIComponent(parentRowKey);
  570. // add HTML elements to display the table and page navigation
  571. // as children for the selected row in the master grid
  572. $('<table>')
  573. .attr('id', childGridID)
  574. .appendTo($('#' + parentRowID));
  575. $('<div>')
  576. .attr('id', childGridPagerID)
  577. .addClass('scroll')
  578. .appendTo($('#' + parentRowID));
  579. // create and initialize the child grid
  580. jqGridInvoice.detailGrid = $("#" + childGridID).jqGrid({
  581. url: childGridURL,
  582. mtype: "GET",
  583. datatype: "json",
  584. page: 1,
  585. colModel: jqGridInvoice.getInvoiceLineColModel(parentRowKey),
  586. loadonce: false,
  587. width: '100%',
  588. height: '100%',
  589. guiStyle: "bootstrap",
  590. iconSet: "fontAwesome",
  591. pager: "#" + childGridPagerID
  592. });
  593. // displaying the toolbar
  594. $("#" + childGridID).jqGrid(
  595. 'navGrid', '#' + childGridPagerID,
  596. {
  597. search: false,
  598. add: true,
  599. edit: true,
  600. del: true,
  601. refresh: true
  602. },
  603. jqGridInvoice.getEditInvoiceLineOptions(),
  604. jqGridInvoice.getAddInvoiceLineOptions(),
  605. jqGridInvoice.getDeleteInvoiceLineOptions()
  606. );
  607. },
  608. // returns a template for the invoice item editor
  609. getTemplateDetail: function () {
  610. var template = "<div style='margin-left:15px;' ";
  611. template += "id='dlgEditInvoiceLine'>";
  612. template += "<div>{INVOICE_ID} </div>";
  613. template += "<div>{PRODUCT_ID} </div>";
  614. // input field with a button
  615. template += "<div> Product <sup>*</sup>:</div>";
  616. template += "<div>";
  617. template += "<div style='float: left;'>{PRODUCT_NAME}</div> ";
  618. template += "<a style='margin-left: 0.2em;' class='btn' ";
  619. template += "onclick='invoiceGrid.showProductWindow(); ";
  620. template += "return false;'>";
  621. template += "<span class='glyphicon glyphicon-folder-open'>";
  622. template += "</span> Select</a> ";
  623. template += "<div style='clear: both;'></div>";
  624. template += "</div>";
  625. template += "<div> Quantity: </div><div>{QUANTITY} </div>";
  626. template += "<div> Price: </div><div>{SALE_PRICE} </div>";
  627. template += "<div> Total: </div><div>{TOTAL} </div>";
  628. template += "<hr style='width: 100%;'/>";
  629. template += "<div> {sData} {cData} </div>";
  630. template += "</div>";
  631. return template;
  632. },
  633. // Display selection window from the goods directory.
  634. showProductWindow: function () {
  635. var dlg = $('<div>')
  636. .attr('id', 'dlgChooseProduct')
  637. .attr('aria-hidden', 'true')
  638. .attr('role', 'dialog')
  639. .attr('data-backdrop', 'static')
  640. .css("z-index", '2000')
  641. .addClass('modal')
  642. .appendTo($('body'));
  643. var dlgContent = $("<div>")
  644. .addClass("modal-content")
  645. .css('width', '760px')
  646. .appendTo($('<div>')
  647. .addClass('modal-dialog')
  648. .appendTo(dlg));
  649. var dlgHeader = $('<div>').addClass("modal-header")
  650. .appendTo(dlgContent);
  651. $("<button>")
  652. .addClass("close")
  653. .attr('type', 'button')
  654. .attr('aria-hidden', 'true')
  655. .attr('data-dismiss', 'modal')
  656. .html("&times;")
  657. .appendTo(dlgHeader);
  658. $("<h5>").addClass("modal-title")
  659. .html("Select product")
  660. .appendTo(dlgHeader);
  661. var dlgBody = $('<div>')
  662. .addClass("modal-body")
  663. .appendTo(dlgContent);
  664. var dlgFooter = $('<div>').addClass("modal-footer")
  665. .appendTo(dlgContent);
  666. $("<button>")
  667. .attr('type', 'button')
  668. .addClass('btn')
  669. .html('OK')
  670. .on('click', function () {
  671. var rowId = $("#jqGridProduct")
  672. .jqGrid("getGridParam", "selrow");
  673. var row = $("#jqGridProduct")
  674. .jqGrid("getRowData", rowId);
  675. $('#dlgEditInvoiceLine input[name=PRODUCT_ID]')
  676. .val(row["PRODUCT_ID"]);
  677. $('#dlgEditInvoiceLine input[name=PRODUCT_NAME]')
  678. .val(row["NAME"]);
  679. $('#dlgEditInvoiceLine input[name=SALE_PRICE]')
  680. .val(row["PRICE"]);
  681. var price = $('#dlgEditInvoiceLine input[name=SALE_PRICE]')
  682. .val()-0;
  683. var quantity = $('#dlgEditInvoiceLine input[name=QUANTITY]')
  684. .val()-0;
  685. var total = Math.round(price * quantity * 100) / 100;
  686. $('#dlgEditInvoiceLine input[name=TOTAL]').val(total);
  687. dlg.modal('hide');
  688. })
  689. .appendTo(dlgFooter);
  690. $("<button>")
  691. .attr('type', 'button')
  692. .addClass('btn')
  693. .html('Cancel')
  694. .on('click', function () {
  695. dlg.modal('hide');
  696. })
  697. .appendTo(dlgFooter);
  698. $('<table>')
  699. .attr('id', 'jqGridProduct')
  700. .appendTo(dlgBody);
  701. $('<div>')
  702. .attr('id', 'jqPagerProduct')
  703. .appendTo(dlgBody);
  704. dlg.on('hidden.bs.modal', function () {
  705. dlg.remove();
  706. });
  707. dlg.modal();
  708. jqGridProductFactory({
  709. baseAddress: jqGridInvoice.options.baseAddress
  710. });
  711. },
  712. // Display the selection window from the customer's directory.
  713. showCustomerWindow: function () {
  714. // the main block of the dialog
  715. var dlg = $('<div>')
  716. .attr('id', 'dlgChooseCustomer')
  717. .attr('aria-hidden', 'true')
  718. .attr('role', 'dialog')
  719. .attr('data-backdrop', 'static')
  720. .css("z-index", '2000')
  721. .addClass('modal')
  722. .appendTo($('body'));
  723. // block with the contents of the dialog
  724. var dlgContent = $("<div>")
  725. .addClass("modal-content")
  726. .css('width', '730px')
  727. .appendTo($('<div>')
  728. .addClass('modal-dialog')
  729. .appendTo(dlg));
  730. // block with dialog header
  731. var dlgHeader = $('<div>').addClass("modal-header")
  732. .appendTo(dlgContent);
  733. // button "X" for closing
  734. $("<button>")
  735. .addClass("close")
  736. .attr('type', 'button')
  737. .attr('aria-hidden', 'true')
  738. .attr('data-dismiss', 'modal')
  739. .html("&times;")
  740. .appendTo(dlgHeader);
  741. // title of dialog
  742. $("<h5>").addClass("modal-title")
  743. .html("Select customer")
  744. .appendTo(dlgHeader);
  745. // body of dialog
  746. var dlgBody = $('<div>')
  747. .addClass("modal-body")
  748. .appendTo(dlgContent);
  749. // footer of dialog
  750. var dlgFooter = $('<div>').addClass("modal-footer")
  751. .appendTo(dlgContent);
  752. // "OK" button
  753. $("<button>")
  754. .attr('type', 'button')
  755. .addClass('btn')
  756. .html('OK')
  757. .on('click', function () {
  758. var rowId = $("#jqGridCustomer")
  759. .jqGrid("getGridParam", "selrow");
  760. var row = $("#jqGridCustomer")
  761. .jqGrid("getRowData", rowId);
  762. // Keep the identifier and the name of the customer
  763. // in the input elements of the parent form.
  764. $('#dlgEditInvoice input[name=CUSTOMER_ID]')
  765. .val(rowId);
  766. $('#dlgEditInvoice input[name=CUSTOMER_NAME]')
  767. .val(row["NAME"]);
  768. dlg.modal('hide');
  769. })
  770. .appendTo(dlgFooter);
  771. // "Cancel" button
  772. $("<button>")
  773. .attr('type', 'button')
  774. .addClass('btn')
  775. .html('Cancel')
  776. .on('click', function () {
  777. dlg.modal('hide');
  778. })
  779. .appendTo(dlgFooter);
  780. // add a table to display the customers in the body of the dialog
  781. $('<table>')
  782. .attr('id', 'jqGridCustomer')
  783. .appendTo(dlgBody);
  784. // add the navigation bar
  785. $('<div>')
  786. .attr('id', 'jqPagerCustomer')
  787. .appendTo(dlgBody);
  788. dlg.on('hidden.bs.modal', function () {
  789. dlg.remove();
  790. });
  791. // display dialog
  792. dlg.modal();
  793. jqGridCustomerFactory({
  794. baseAddress: jqGridInvoice.options.baseAddress
  795. });
  796. },
  797. // A window for displaying the error.
  798. alertDialog: function (title, error) {
  799. var alertDlg = $('<div>')
  800. .attr('aria-hidden', 'true')
  801. .attr('role', 'dialog')
  802. .attr('data-backdrop', 'static')
  803. .addClass('modal')
  804. .appendTo($('body'));
  805. var dlgContent = $("<div>")
  806. .addClass("modal-content")
  807. .appendTo($('<div>')
  808. .addClass('modal-dialog')
  809. .appendTo(alertDlg));
  810. var dlgHeader = $('<div>').addClass("modal-header")
  811. .appendTo(dlgContent);
  812. $("<button>")
  813. .addClass("close")
  814. .attr('type', 'button')
  815. .attr('aria-hidden', 'true')
  816. .attr('data-dismiss', 'modal')
  817. .html("&times;")
  818. .appendTo(dlgHeader);
  819. $("<h5>").addClass("modal-title")
  820. .html(title)
  821. .appendTo(dlgHeader);
  822. $('<div>')
  823. .addClass("modal-body")
  824. .appendTo(dlgContent)
  825. .append(error);
  826. alertDlg.on('hidden.bs.modal', function () {
  827. alertDlg.remove();
  828. });
  829. alertDlg.modal();
  830. }
  831. };
  832. jqGridInvoice.init();
  833. return jqGridInvoice;
  834. };
  835. })(jQuery, JqGridProduct, JqGridCustomer);

Displaying and Editing Invoice Lines

In the invoice module, the main grid is used to display headers and the detail grid, opened with a click, is used to display invoice items. For the child grid to be displayed, the True value is assigned to the subGrid property. The child grid is displayed using the subGridRowExpanded event connected with the showChildGrid method.

The items are filtered by the primary key of the invoice. Along with the main buttons on the navigation bar, a custom button for paying for the invoice is added to the invoice header using the jqGridInvoice.dbGrid.navButtonAdd function (see the initPager method).

Dialog Boxes

Dialog boxes for editing secondary modules are much more complicated than their primary counterparts. They often use options selected from other modules. For that reason, these edit dialog boxes cannot be built automatically using jqGrid. However, this library has an option to build dialog boxes using templates, which we use.

The dialog box template is returned by the getTemplate function. The invoiceGrid.showCustomerWindow() function opens the customer module for selecting a customer. It uses the functions of the JqGridCustomer module described earlier. After the customer is selected in the modal window, its key is inserted into the CUSTOMER_ID field. Fields that are to be sent to the server using pre-processing or from hidden fields are described in the editData property of the Edit and Add options.

Processing Dates

To get back to processing dates: as we already know, the InvoiceController controller returns the date in UTC. Because we want to display it in the current time zone, we specify the jqGridInvoice.dateTimeFormatter date formatting function via the formatter property of the corresponding INVOICE_DATE field.

When sending data to the server, we need the reverse operation — convert time from the current time zone to UTC. The convertToUTC function is responsible for that.

The custom template returned by the getTemplateDetail function is also used for editing invoice items. The invoiceGrid.showProductWindow() function opens a window for selecting a product from the product list. This function uses the functions of the JqGridProduct module.

The code for the JqGridInvoice module contains detailed comments and more explanation so that you can understand the logic of its workings.