5.10.1. Controllers for Invoices

The controller of the invoice module must be able to return data for both invoice headers and the associated invoice lines. The same applies to the methods for adding, editing and deleting records.

  1. [Authorize(Roles = "manager")]
  2. public class InvoiceController : Controller
  3. {
  4. private DbModel db = new DbModel();
  5. // display view
  6. public ActionResult Index()
  7. {
  8. return View();
  9. }
  10. // Receiving data in the JSON format for the main grid
  11. public ActionResult GetData(int? rows, int? page, string sidx, string sord,
  12. string searchField, string searchString, string searchOper)
  13. {
  14. // get the page number, the number of data displayed
  15. int pageNo = page ?? 1;
  16. int limit = rows ?? 20;
  17. // calculate offset
  18. int offset = (pageNo - 1) * limit;
  19. // building a request for receipt of invoices
  20. var invoicesQuery =
  21. from invoice in db.INVOICES
  22. where (invoice.INVOICE_DATE >= AppVariables.StartDate) &&
  23. (invoice.INVOICE_DATE <= AppVariables.FinishDate)
  24. select new
  25. {
  26. INVOICE_ID = invoice.INVOICE_ID,
  27. CUSTOMER_ID = invoice.CUSTOMER_ID,
  28. CUSTOMER_NAME = invoice.CUSTOMER.NAME,
  29. INVOICE_DATE = invoice.INVOICE_DATE,
  30. TOTAL_SALE = invoice.TOTAL_SALE,
  31. PAID = invoice.PAID
  32. };
  33. // adding a search condition to the query, if it is produced
  34. // for different fields, different comparison operators
  35. // are available when searching
  36. if (searchField == "CUSTOMER_NAME")
  37. {
  38. switch (searchOper)
  39. {
  40. case "eq": // equal
  41. invoicesQuery = invoicesQuery.Where(
  42. c => c.CUSTOMER_NAME == searchString);
  43. break;
  44. case "bw": // starting with
  45. invoicesQuery = invoicesQuery.Where(
  46. c => c.CUSTOMER_NAME.StartsWith(searchString));
  47. break;
  48. case "cn": // containing
  49. invoicesQuery = invoicesQuery.Where(
  50. c => c.CUSTOMER_NAME.Contains(searchString));
  51. break;
  52. }
  53. }
  54. if (searchField == "INVOICE_DATE")
  55. {
  56. var dateValue = DateTime.Parse(searchString);
  57. switch (searchOper)
  58. {
  59. case "eq": // =
  60. invoicesQuery = invoicesQuery.Where(
  61. c => c.INVOICE_DATE == dateValue);
  62. break;
  63. case "lt": // <
  64. invoicesQuery = invoicesQuery.Where(
  65. c => c.INVOICE_DATE < dateValue);
  66. break;
  67. case "le": // <=
  68. invoicesQuery = invoicesQuery.Where(
  69. c => c.INVOICE_DATE <= dateValue);
  70. break;
  71. case "gt": // >
  72. invoicesQuery = invoicesQuery.Where(
  73. c => c.INVOICE_DATE > dateValue);
  74. break;
  75. case "ge": // >=
  76. invoicesQuery = invoicesQuery.Where(
  77. c => c.INVOICE_DATE >= dateValue);
  78. break;
  79. }
  80. }
  81. if (searchField == "PAID")
  82. {
  83. int iVal = (searchString == "on") ? 1 : 0;
  84. invoicesQuery = invoicesQuery.Where(c => c.PAID == iVal);
  85. }
  86. // get the total number of invoices
  87. int totalRows = invoicesQuery.Count();
  88. // add sorting
  89. switch (sord)
  90. {
  91. case "asc":
  92. invoicesQuery = invoicesQuery.OrderBy(
  93. invoice => invoice.INVOICE_DATE);
  94. break;
  95. case "desc":
  96. invoicesQuery = invoicesQuery.OrderByDescending(
  97. invoice => invoice.INVOICE_DATE);
  98. break;
  99. }
  100. // get invoice list
  101. var invoices = invoicesQuery
  102. .Skip(offset)
  103. .Take(limit)
  104. .ToList();
  105. // calculate the total number of pages
  106. int totalPages = totalRows / limit + 1;
  107. // create the result for jqGrid
  108. var result = new
  109. {
  110. page = pageNo,
  111. total = totalPages,
  112. records = totalRows,
  113. rows = invoices
  114. };
  115. // convert the result to JSON
  116. return Json(result, JsonRequestBehavior.AllowGet);
  117. }
  118. // Receiving data in the form of JSON for the detail grid
  119. public ActionResult GetDetailData(int? invoice_id)
  120. {
  121. // build a LINQ query for receiving invoice items
  122. // filtered by invoice id
  123. var lines =
  124. from line in db.INVOICE_LINES
  125. where line.INVOICE_ID == invoice_id
  126. select new
  127. {
  128. INVOICE_LINE_ID = line.INVOICE_LINE_ID,
  129. INVOICE_ID = line.INVOICE_ID,
  130. PRODUCT_ID = line.PRODUCT_ID,
  131. Product = line.PRODUCT.NAME,
  132. Quantity = line.QUANTITY,
  133. Price = line.SALE_PRICE,
  134. Total = line.QUANTITY * line.SALE_PRICE
  135. };
  136. // get invoice position list
  137. var invoices = lines
  138. .ToList();
  139. // create the result for jqGrid
  140. var result = new
  141. {
  142. rows = invoices
  143. };
  144. // convert the result to JSON
  145. return Json(result, JsonRequestBehavior.AllowGet);
  146. }
  147. // Add new invoice
  148. [HttpPost]
  149. [ValidateAntiForgeryToken]
  150. public ActionResult Create(
  151. [Bind(Include = "CUSTOMER_ID,INVOICE_DATE")] INVOICE invoice)
  152. {
  153. // check the correctness of the model
  154. if (ModelState.IsValid)
  155. {
  156. try
  157. {
  158. var INVOICE_ID = new FbParameter("INVOICE_ID", FbDbType.Integer);
  159. var CUSTOMER_ID = new FbParameter("CUSTOMER_ID", FbDbType.Integer);
  160. var INVOICE_DATE = new FbParameter("INVOICE_DATE",
  161. FbDbType.TimeStamp);
  162. // initialize parameters query
  163. INVOICE_ID.Value = db.NextValueFor("GEN_INVOICE_ID");
  164. CUSTOMER_ID.Value = invoice.CUSTOMER_ID;
  165. INVOICE_DATE.Value = invoice.INVOICE_DATE;
  166. // execute stored procedure
  167. db.Database.ExecuteSqlCommand(
  168. "EXECUTE PROCEDURE SP_ADD_INVOICE(@INVOICE_ID, @CUSTOMER_ID, @INVOICE_DATE)",
  169. INVOICE_ID,
  170. CUSTOMER_ID,
  171. INVOICE_DATE);
  172. // return success in JSON format
  173. return Json(true);
  174. }
  175. catch (Exception ex)
  176. {
  177. // return error in JSON format
  178. return Json(new { error = ex.Message });
  179. }
  180. }
  181. else {
  182. string messages = string.Join("; ", ModelState.Values
  183. .SelectMany(x => x.Errors)
  184. .Select(x => x.ErrorMessage));
  185. // return error in JSON format
  186. return Json(new { error = messages });
  187. }
  188. }
  189. // Edit invoice
  190. [HttpPost]
  191. [ValidateAntiForgeryToken]
  192. public ActionResult Edit(
  193. [Bind(Include = "INVOICE_ID,CUSTOMER_ID,INVOICE_DATE")] INVOICE invoice)
  194. {
  195. // check the correctness of the model
  196. if (ModelState.IsValid)
  197. {
  198. try
  199. {
  200. var INVOICE_ID = new FbParameter("INVOICE_ID", FbDbType.Integer);
  201. var CUSTOMER_ID = new FbParameter("CUSTOMER_ID", FbDbType.Integer);
  202. var INVOICE_DATE = new FbParameter("INVOICE_DATE",
  203. FbDbType.TimeStamp);
  204. // initialize parameters query
  205. INVOICE_ID.Value = invoice.INVOICE_ID;
  206. CUSTOMER_ID.Value = invoice.CUSTOMER_ID;
  207. INVOICE_DATE.Value = invoice.INVOICE_DATE;
  208. // execute stored procedure
  209. db.Database.ExecuteSqlCommand(
  210. "EXECUTE PROCEDURE SP_EDIT_INVOICE(@INVOICE_ID, @CUSTOMER_ID, @INVOICE_DATE)",
  211. INVOICE_ID,
  212. CUSTOMER_ID,
  213. INVOICE_DATE);
  214. // return success in JSON format
  215. return Json(true);
  216. }
  217. catch (Exception ex)
  218. {
  219. // return error in JSON format
  220. return Json(new { error = ex.Message });
  221. }
  222. }
  223. else {
  224. string messages = string.Join("; ", ModelState.Values
  225. .SelectMany(x => x.Errors)
  226. .Select(x => x.ErrorMessage));
  227. // return error in JSON format
  228. return Json(new { error = messages });
  229. }
  230. }
  231. // Delete invoice
  232. [HttpPost]
  233. [ValidateAntiForgeryToken]
  234. public ActionResult Delete(int id)
  235. {
  236. try
  237. {
  238. var INVOICE_ID = new FbParameter("INVOICE_ID", FbDbType.Integer);
  239. // initialize parameters query
  240. INVOICE_ID.Value = id;
  241. // execute stored procedure
  242. db.Database.ExecuteSqlCommand(
  243. "EXECUTE PROCEDURE SP_DELETE_INVOICE(@INVOICE_ID)",
  244. INVOICE_ID);
  245. // return success in JSON format
  246. return Json(true);
  247. }
  248. catch (Exception ex)
  249. {
  250. // return error in JSON format
  251. return Json(new { error = ex.Message });
  252. }
  253. }
  254. // Payment of invoice
  255. [HttpPost]
  256. [ValidateAntiForgeryToken]
  257. public ActionResult Pay(int id)
  258. {
  259. try
  260. {
  261. var INVOICE_ID = new FbParameter("INVOICE_ID", FbDbType.Integer);
  262. // initialize parameters query
  263. INVOICE_ID.Value = id;
  264. // execute stored procedure
  265. db.Database.ExecuteSqlCommand(
  266. "EXECUTE PROCEDURE SP_PAY_FOR_INOVICE(@INVOICE_ID)",
  267. INVOICE_ID);
  268. // return success in JSON format
  269. return Json(true);
  270. }
  271. catch (Exception ex)
  272. {
  273. // return error in JSON format
  274. return Json(new { error = ex.Message });
  275. }
  276. }
  277. // Add invoice position
  278. [HttpPost]
  279. [ValidateAntiForgeryToken]
  280. public ActionResult CreateDetail(
  281. [Bind(Include = "INVOICE_ID,PRODUCT_ID,QUANTITY")] INVOICE_LINE invoiceLine)
  282. {
  283. // check the correctness of the model
  284. if (ModelState.IsValid)
  285. {
  286. try
  287. {
  288. var INVOICE_ID = new FbParameter("INVOICE_ID", FbDbType.Integer);
  289. var PRODUCT_ID = new FbParameter("PRODUCT_ID", FbDbType.Integer);
  290. var QUANTITY = new FbParameter("QUANTITY", FbDbType.Integer);
  291. // initialize parameters query
  292. INVOICE_ID.Value = invoiceLine.INVOICE_ID;
  293. PRODUCT_ID.Value = invoiceLine.PRODUCT_ID;
  294. QUANTITY.Value = invoiceLine.QUANTITY;
  295. // execute stored procedure
  296. db.Database.ExecuteSqlCommand(
  297. ""EXECUTE PROCEDURE SP_ADD_INVOICE_LINE(@INVOICE_ID, @PRODUCT_ID, @QUANTITY)",
  298. INVOICE_ID,
  299. PRODUCT_ID,
  300. QUANTITY);
  301. // return success in JSON format
  302. return Json(true);
  303. }
  304. catch (Exception ex)
  305. {
  306. // return error in JSON format
  307. return Json(new { error = ex.Message });
  308. }
  309. }
  310. else {
  311. string messages = string.Join("; ", ModelState.Values
  312. .SelectMany(x => x.Errors)
  313. .Select(x => x.ErrorMessage));
  314. // return error in JSON format
  315. return Json(new { error = messages });
  316. }
  317. }
  318. // Edit invoice position
  319. [HttpPost]
  320. [ValidateAntiForgeryToken]
  321. public ActionResult EditDetail(
  322. [Bind(Include = "INVOICE_LINE_ID,INVOICE_ID,PRODUCT_ID,QUANTITY")]
  323. INVOICE_LINE invoiceLine)
  324. {
  325. // check the correctness of the model
  326. if (ModelState.IsValid)
  327. {
  328. try
  329. {
  330. // Create parameters
  331. var INVOICE_LINE_ID = new FbParameter("INVOICE_LINE_ID",
  332. FbDbType.Integer);
  333. var QUANTITY = new FbParameter("QUANTITY", FbDbType.Integer);
  334. // initialize parameters query
  335. INVOICE_LINE_ID.Value = invoiceLine.INVOICE_LINE_ID;
  336. QUANTITY.Value = invoiceLine.QUANTITY;
  337. // execute stored procedure
  338. db.Database.ExecuteSqlCommand(
  339. "EXECUTE PROCEDURE SP_EDIT_INVOICE_LINE(@INVOICE_LINE_ID, @QUANTITY)",
  340. INVOICE_LINE_ID,
  341. QUANTITY);
  342. // return success in JSON format
  343. return Json(true);
  344. }
  345. catch (Exception ex)
  346. {
  347. // return error in JSON format
  348. return Json(new { error = ex.Message });
  349. }
  350. }
  351. else {
  352. string messages = string.Join("; ", ModelState.Values
  353. .SelectMany(x => x.Errors)
  354. .Select(x => x.ErrorMessage));
  355. // return error in JSON format
  356. return Json(new { error = messages });
  357. }
  358. }
  359. // Delete invoice position
  360. [HttpPost]
  361. [ValidateAntiForgeryToken]
  362. public ActionResult DeleteDetail(int id)
  363. {
  364. try
  365. {
  366. // create parameters
  367. var INVOICE_LINE_ID = new FbParameter("INVOICE_LINE_ID",
  368. FbDbType.Integer);
  369. // initialize parameters query
  370. INVOICE_LINE_ID.Value = id;
  371. // execute stored procedure
  372. db.Database.ExecuteSqlCommand(
  373. "EXECUTE PROCEDURE SP_DELETE_INVOICE_LINE(@INVOICE_LINE_ID)",
  374. INVOICE_LINE_ID);
  375. // return success in JSON format
  376. return Json(true);
  377. }
  378. catch (Exception ex)
  379. {
  380. // return error in JSON format
  381. return Json(new { error = ex.Message });
  382. }
  383. }
  384. protected override void Dispose(bool disposing)
  385. {
  386. if (disposing)
  387. {
  388. db.Dispose();
  389. }
  390. base.Dispose(disposing);
  391. }
  392. }

The GetDetailData method for retrieving the list of lines in an invoice lacks the code for page-by-page navigation. Realistically, a typical invoice does not have enough lines to justify using page-by-page navigation for them. Omitting it simplifies and speeds up the code.

In our project, all data modification operations are performed in stored procedures, but you could do the same work using Entity Framework. DDL code for the stored procedures can be found in the database creation script in an earlier chapter and also in the .zip archives of all the DDL scripts:

https://github.com/sim1984/example-db_2_5/archive/1.0.zip
or https://github.com/sim1984/example-db_3_0/archive/1.0.zip