7.10.3. Invoice Controller Class
Now we move on to writing the controller. The input point of our controller will be the index method, that is responsible for displaying the JSP page (view). This page contains the layout for displaying the grid and the tool and navigation bars.
Data for displaying invoice headers are loaded asynchronously by the jqGrid component (the path is /invoice/getdata). The getData method is connected with this path, similarly to the primary modules. Invoice items are returned by the getDetailData method (the path is /invoice/getdetaildata). The primary key of the invoice whose detail grid is currently open is passed to this method.
The methods implemented are addInvoice, editInvoice, deleteInvoice, payInvoice for invoice headers and addInvoiceLine, editInvoiceLine, deleteInvoiceLine for invoice line items.
package ru.ibase.fbjavaex.controllers;import java.sql.Timestamp;import java.util.HashMap;import java.util.Map;import java.util.Date;import java.text.ParseException;import java.text.SimpleDateFormat;import java.beans.PropertyEditorSupport;import javax.ws.rs.core.MediaType;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.ui.ModelMap;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.bind.annotation.InitBinder;import org.springframework.web.bind.WebDataBinder;import ru.ibase.fbjavaex.jqgrid.JqGridInvoice;import ru.ibase.fbjavaex.jqgrid.JqGridInvoiceLine;import ru.ibase.fbjavaex.managers.InvoiceManager;import ru.ibase.fbjavaex.jqgrid.JqGridData;/*** Invoice controller** @author Simonov Denis*/@Controllerpublic class InvoiceController {@Autowired(required = true)private JqGridInvoice invoiceGrid;@Autowired(required = true)private JqGridInvoiceLine invoiceLineGrid;@Autowired(required = true)private InvoiceManager invoiceManager;/*** Describe how a string is converted to a date* from the input parameters of the HTTP request** @param binder*/@InitBinderpublic void initBinder(WebDataBinder binder) {binder.registerCustomEditor(Timestamp.class,new PropertyEditorSupport() {@Overridepublic void setAsText(String value) {try {if ((value == null) || (value.isEmpty())) {setValue(null);} else {Date parsedDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(value);setValue(new Timestamp(parsedDate.getTime()));}} catch (ParseException e) {throw new java.lang.IllegalArgumentException(value);}}});}/*** Default action* Returns the JSP name of the page (view) to display** @param map* @return JSP page name*/@RequestMapping(value = "/invoice/", method = RequestMethod.GET)public String index(ModelMap map) {return "invoice";}/*** Returns a list of invoices in JSON format for jqGrid** @param rows number of entries per page* @param page current page number* @param sIdx sort field* @param sOrd sorting order* @param search search flag* @param searchField search field* @param searchString search value* @param searchOper comparison operation* @param filters filter* @return*/@RequestMapping(value = "/invoice/getdata",method = RequestMethod.GET,produces = MediaType.APPLICATION_JSON)@ResponseBodypublic JqGridData getData(@RequestParam(value = "rows", required = false,defaultValue = "20") int rows,@RequestParam(value = "page", required = false,defaultValue = "1") int page,@RequestParam(value = "sidx", required = false,defaultValue = "") String sIdx,@RequestParam(value = "sord", required = false,defaultValue = "asc") String sOrd,@RequestParam(value = "_search", required = false,defaultValue = "false") Boolean search,@RequestParam(value = "searchField", required = false,defaultValue = "") String searchField,@RequestParam(value = "searchString", required = false,defaultValue = "") String searchString,@RequestParam(value = "searchOper", required = false,defaultValue = "") String searchOper,@RequestParam(value = "filters", required = false,defaultValue = "") String filters) {if (search) {invoiceGrid.setSearchCondition(searchField, searchString, searchOper);}invoiceGrid.setLimit(rows);invoiceGrid.setPageNo(page);invoiceGrid.setOrderBy(sIdx, sOrd);return invoiceGrid.getJqGridData();}/*** Add invoice** @param customerId customer id* @param invoiceDate invoice date* @return*/@RequestMapping(value = "/invoice/create",method = RequestMethod.POST,produces = MediaType.APPLICATION_JSON)@ResponseBodypublic Map<String, Object> addInvoice(@RequestParam(value = "CUSTOMER_ID", required = true,defaultValue = "0") Integer customerId,@RequestParam(value = "INVOICE_DATE", required = false,defaultValue = "") Timestamp invoiceDate) {Map<String, Object> map = new HashMap<>();try {invoiceManager.create(customerId, invoiceDate);map.put("success", true);} catch (Exception ex) {map.put("error", ex.getMessage());}return map;}/*** Edit invoice** @param invoiceId invoice id* @param customerId customer id* @param invoiceDate invoice date* @return*/@RequestMapping(value = "/invoice/edit",method = RequestMethod.POST,produces = MediaType.APPLICATION_JSON)@ResponseBodypublic Map<String, Object> editInvoice(@RequestParam(value = "INVOICE_ID", required = true,defaultValue = "0") Integer invoiceId,@RequestParam(value = "CUSTOMER_ID", required = true,defaultValue = "0") Integer customerId,@RequestParam(value = "INVOICE_DATE", required = false,defaultValue = "") Timestamp invoiceDate) {Map<String, Object> map = new HashMap<>();try {invoiceManager.edit(invoiceId, customerId, invoiceDate);map.put("success", true);} catch (Exception ex) {map.put("error", ex.getMessage());}return map;}/*** Pays an invoice** @param invoiceId invoice id* @return*/@RequestMapping(value = "/invoice/pay",method = RequestMethod.POST,produces = MediaType.APPLICATION_JSON)@ResponseBodypublic Map<String, Object> payInvoice(@RequestParam(value = "INVOICE_ID", required = true,defaultValue = "0") Integer invoiceId) {Map<String, Object> map = new HashMap<>();try {invoiceManager.pay(invoiceId);map.put("success", true);} catch (Exception ex) {map.put("error", ex.getMessage());}return map;}/*** Delete invoice** @param invoiceId invoice id* @return*/@RequestMapping(value = "/invoice/delete",method = RequestMethod.POST,produces = MediaType.APPLICATION_JSON)@ResponseBodypublic Map<String, Object> deleteInvoice(@RequestParam(value = "INVOICE_ID", required = true,defaultValue = "0") Integer invoiceId) {Map<String, Object> map = new HashMap<>();try {invoiceManager.delete(invoiceId);map.put("success", true);} catch (Exception ex) {map.put("error", ex.getMessage());}return map;}/*** Returns invoice item** @param invoice_id invoice id* @return*/@RequestMapping(value = "/invoice/getdetaildata",method = RequestMethod.GET,produces = MediaType.APPLICATION_JSON)@ResponseBodypublic JqGridData getDetailData(@RequestParam(value = "INVOICE_ID", required = true) int invoice_id) {invoiceLineGrid.setInvoiceId(invoice_id);return invoiceLineGrid.getJqGridData();}/*** Add invoice item** @param invoiceId invoice id* @param productId product id* @param quantity quantity of products* @return*/@RequestMapping(value = "/invoice/createdetail",method = RequestMethod.POST,produces = MediaType.APPLICATION_JSON)@ResponseBodypublic Map<String, Object> addInvoiceLine(@RequestParam(value = "INVOICE_ID", required = true,defaultValue = "0") Integer invoiceId,@RequestParam(value = "PRODUCT_ID", required = true,defaultValue = "0") Integer productId,@RequestParam(value = "QUANTITY", required = true,defaultValue = "0") Integer quantity) {Map<String, Object> map = new HashMap<>();try {invoiceManager.addInvoiceLine(invoiceId, productId, quantity);map.put("success", true);} catch (Exception ex) {map.put("error", ex.getMessage());}return map;}/*** Edit invoice item** @param invoiceLineId invoice item id* @param quantity quantity of products* @return*/@RequestMapping(value = "/invoice/editdetail",method = RequestMethod.POST,produces = MediaType.APPLICATION_JSON)@ResponseBodypublic Map<String, Object> editInvoiceLine(@RequestParam(value = "INVOICE_LINE_ID", required = true,defaultValue = "0") Integer invoiceLineId,@RequestParam(value = "QUANTITY", required = true,defaultValue = "0") Integer quantity) {Map<String, Object> map = new HashMap<>();try {invoiceManager.editInvoiceLine(invoiceLineId, quantity);map.put("success", true);} catch (Exception ex) {map.put("error", ex.getMessage());}return map;}/*** Delete invoice item** @param invoiceLineId invoice item id* @return*/@RequestMapping(value = "/invoice/deletedetail",method = RequestMethod.POST,produces = MediaType.APPLICATION_JSON)@ResponseBodypublic Map<String, Object> deleteInvoiceLine(@RequestParam(value = "INVOICE_LINE_ID", required = true,defaultValue = "0") Integer invoiceLineId) {Map<String, Object> map = new HashMap<>();try {invoiceManager.deleteInvoiceLine(invoiceLineId);map.put("success", true);} catch (Exception ex) {map.put("error", ex.getMessage());}return map;}}
The invoice controller is very similar to the primary module controllers except for two things:
The controller displays and works with the data of both the main grid and the detail grid
Invoices are filtered by the date field so that only those invoices that are included in the work period are displayed
Working with Dates in Java
Working with dates in Java throws up a few quirks.
The java.sql.Timestamp type in Java supports precision up to nanoseconds whereas the maximum precision of the TIMESTAMP type in Firebird is one ten-thousandth of a second. That is not really a significant problem.
Date and time types in Java support working with time zones. Firebird does not currently support the TIMESTAMP WITH TIME ZONE type. Java works on the assumption that dates in the database are stored in the time zone of the server. However, time will be converted to UTC during serialization into JSON. It must be taken into account when processing time data in JavaScript.
Attention! Java takes the time offset from its own time zone database, not from the operating system. This practice considerably increases the need to keep up with the latest version of JDK. If you have some old version of JDK installed, working with date and time may be incorrect. |
By default, a date is serialized into JSON in as the number of nanoseconds since January 1, 1970, which is not always what is wanted. A date can be serialized into a text representation, by setting to False the date conversion configuration property SerializationFeature.WRITE_DATES_AS_TIMESTAMPS date conversion in the configureMessageConverters method of the WebAppConfig class.
We will return to date processing a little later.
@Configuration@ComponentScan("ru.ibase.fbjavaex")@EnableWebMvcpublic class WebAppConfig extends WebMvcConfigurerAdapter {@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> httpMessageConverters) {MappingJackson2HttpMessageConverter jsonConverter =new MappingJackson2HttpMessageConverter();ObjectMapper objectMapper = new ObjectMapper();objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);jsonConverter.setObjectMapper(objectMapper);httpMessageConverters.add(jsonConverter);}…}
The initBinder method of the InvoiceController controller describes how the text representation of a date sent by the browser is converted into a value of type Timestamp.
