7.10. Creating Secondary Modules

A secondary module typically contains many more records than a primary one and new records are added frequently. Most secondary tables contain a field with the record creation date. In order to reduce the amount of retrieved data, the notion of a work period is often incorporated to limit the range of data sent to the client. A work period is a range of dates for which the records are required. The work period is described by the WorkingPeriod class, defined via the workingPeriod bean in the ru.ibase.fbjavaex.config.JooqConfig configuration class.

  1. package ru.ibase.fbjavaex.config;
  2. import java.sql.Timestamp;
  3. import java.time.LocalDateTime;
  4. /**
  5. * Working period
  6. *
  7. * @author Simonov Denis
  8. */
  9. public class WorkingPeriod {
  10. private Timestamp beginDate;
  11. private Timestamp endDate;
  12. /**
  13. * Constructor
  14. */
  15. WorkingPeriod() {
  16. // in real applications is calculated from the current date
  17. this.beginDate = Timestamp.valueOf("2015-06-01 00:00:00");
  18. this.endDate = Timestamp.valueOf(LocalDateTime.now().plusDays(1));
  19. }
  20. /**
  21. * Returns the start date of the work period
  22. *
  23. * @return
  24. */
  25. public Timestamp getBeginDate() {
  26. return this.beginDate;
  27. }
  28. /**
  29. * Returns the end date of the work period
  30. *
  31. * @return
  32. */
  33. public Timestamp getEndDate() {
  34. return this.endDate;
  35. }
  36. /**
  37. * Setting the start date of the work period
  38. *
  39. * @param value
  40. */
  41. public void setBeginDate(Timestamp value) {
  42. this.beginDate = value;
  43. }
  44. /**
  45. * Setting the end date of the work period
  46. *
  47. * @param value
  48. */
  49. public void setEndDate(Timestamp value) {
  50. this.endDate = value;
  51. }
  52. /**
  53. * Setting the working period
  54. *
  55. * @param beginDate
  56. * @param endDate
  57. */
  58. public void setRangeDate(Timestamp beginDate, Timestamp endDate) {
  59. this.beginDate = beginDate;
  60. this.endDate = endDate;
  61. }
  62. }

In our project we have only one secondary module called “Invoices”. An invoice consists of a header where some general attributes are described (number, date, customer …​) and one or more invoice items (product name, quantity, price, etc.). The invoice header is displayed in the main grid while items can be viewed in a detail grid that is opened with a click on the “+” icon of the selected document.

We implement a class, inherited from the ru.ibase.fbjavaex.jqgrid.JqGrid abstract class described earlier, for viewing the invoice headers via jqGrid. Searching can be by customer name or invoice date and reversible date order is supported, too.

  1. package ru.ibase.fbjavaex.jqgrid;
  2. import java.sql.*;
  3. import org.jooq.*;
  4. import java.util.List;
  5. import java.util.Map;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import ru.ibase.fbjavaex.config.WorkingPeriod;
  8. import static ru.ibase.fbjavaex.exampledb.Tables.INVOICE;
  9. import static ru.ibase.fbjavaex.exampledb.Tables.CUSTOMER;
  10. /**
  11. * Grid handler for the invoice journal
  12. *
  13. * @author Simonov Denis
  14. */
  15. public class JqGridInvoice extends JqGrid {
  16. @Autowired(required = true)
  17. private WorkingPeriod workingPeriod;
  18. /**
  19. * Adding a search condition
  20. *
  21. * @param query
  22. */
  23. private void makeSearchCondition(SelectQuery<?> query) {
  24. // adding a search condition to the query,
  25. // if it is produced for different fields,
  26. // different comparison operators are available when searching.
  27. if (this.searchString.isEmpty()) {
  28. return;
  29. }
  30. if (this.searchField.equals("CUSTOMER_NAME")) {
  31. switch (this.searchOper) {
  32. case "eq": // equal
  33. query.addConditions(CUSTOMER.NAME.eq(this.searchString));
  34. break;
  35. case "bw": // starting with
  36. query.addConditions(CUSTOMER.NAME.startsWith(this.searchString));
  37. break;
  38. case "cn": // containing
  39. query.addConditions(CUSTOMER.NAME.contains(this.searchString));
  40. break;
  41. }
  42. }
  43. if (this.searchField.equals("INVOICE_DATE")) {
  44. Timestamp dateValue = Timestamp.valueOf(this.searchString);
  45. switch (this.searchOper) {
  46. case "eq": // =
  47. query.addConditions(INVOICE.INVOICE_DATE.eq(dateValue));
  48. break;
  49. case "lt": // <
  50. query.addConditions(INVOICE.INVOICE_DATE.lt(dateValue));
  51. break;
  52. case "le": // <=
  53. query.addConditions(INVOICE.INVOICE_DATE.le(dateValue));
  54. break;
  55. case "gt": // >
  56. query.addConditions(INVOICE.INVOICE_DATE.gt(dateValue));
  57. break;
  58. case "ge": // >=
  59. query.addConditions(INVOICE.INVOICE_DATE.ge(dateValue));
  60. break;
  61. }
  62. }
  63. }
  64. /**
  65. * Returns the total number of records
  66. *
  67. * @return
  68. */
  69. @Override
  70. public int getCountRecord() {
  71. SelectFinalStep<?> select
  72. = dsl.selectCount()
  73. .from(INVOICE)
  74. .where(INVOICE.INVOICE_DATE.between(
  75. this.workingPeriod.getBeginDate(),
  76. this.workingPeriod.getEndDate()));
  77. SelectQuery<?> query = select.getQuery();
  78. if (this.searchFlag) {
  79. makeSearchCondition(query);
  80. }
  81. return (int) query.fetch().getValue(0, 0);
  82. }
  83. /**
  84. * Returns the list of invoices
  85. *
  86. * @return
  87. */
  88. @Override
  89. public List<Map<String, Object>> getRecords() {
  90. SelectFinalStep<?> select = dsl.select(
  91. INVOICE.INVOICE_ID,
  92. INVOICE.CUSTOMER_ID,
  93. CUSTOMER.NAME.as("CUSTOMER_NAME"),
  94. INVOICE.INVOICE_DATE,
  95. INVOICE.PAID,
  96. INVOICE.TOTAL_SALE)
  97. .from(INVOICE)
  98. .innerJoin(CUSTOMER).on(CUSTOMER.CUSTOMER_ID.eq(INVOICE.CUSTOMER_ID))
  99. .where(INVOICE.INVOICE_DATE.between(
  100. this.workingPeriod.getBeginDate(),
  101. this.workingPeriod.getEndDate()));
  102. SelectQuery<?> query = select.getQuery();
  103. // add a search condition
  104. if (this.searchFlag) {
  105. makeSearchCondition(query);
  106. }
  107. // add sorting
  108. if (this.sIdx.equals("INVOICE_DATE")) {
  109. switch (this.sOrd) {
  110. case "asc":
  111. query.addOrderBy(INVOICE.INVOICE_DATE.asc());
  112. break;
  113. case "desc":
  114. query.addOrderBy(INVOICE.INVOICE_DATE.desc());
  115. break;
  116. }
  117. }
  118. // limit the number of records and add an offset
  119. if (this.limit != 0) {
  120. query.addLimit(this.limit);
  121. }
  122. if (this.offset != 0) {
  123. query.addOffset(this.offset);
  124. }
  125. return query.fetchMaps();
  126. }
  127. }