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.
[Authorize(Roles = "manager")]
public class InvoiceController : Controller
{
private DbModel db = new DbModel();
// display view
public ActionResult Index()
{
return View();
}
// Receiving data in the JSON format for the main grid
public ActionResult GetData(int? rows, int? page, string sidx, string sord,
string searchField, string searchString, string searchOper)
{
// get the page number, the number of data displayed
int pageNo = page ?? 1;
int limit = rows ?? 20;
// calculate offset
int offset = (pageNo - 1) * limit;
// building a request for receipt of invoices
var invoicesQuery =
from invoice in db.INVOICES
where (invoice.INVOICE_DATE >= AppVariables.StartDate) &&
(invoice.INVOICE_DATE <= AppVariables.FinishDate)
select new
{
INVOICE_ID = invoice.INVOICE_ID,
CUSTOMER_ID = invoice.CUSTOMER_ID,
CUSTOMER_NAME = invoice.CUSTOMER.NAME,
INVOICE_DATE = invoice.INVOICE_DATE,
TOTAL_SALE = invoice.TOTAL_SALE,
PAID = invoice.PAID
};
// adding a search condition to the query, if it is produced
// for different fields, different comparison operators
// are available when searching
if (searchField == "CUSTOMER_NAME")
{
switch (searchOper)
{
case "eq": // equal
invoicesQuery = invoicesQuery.Where(
c => c.CUSTOMER_NAME == searchString);
break;
case "bw": // starting with
invoicesQuery = invoicesQuery.Where(
c => c.CUSTOMER_NAME.StartsWith(searchString));
break;
case "cn": // containing
invoicesQuery = invoicesQuery.Where(
c => c.CUSTOMER_NAME.Contains(searchString));
break;
}
}
if (searchField == "INVOICE_DATE")
{
var dateValue = DateTime.Parse(searchString);
switch (searchOper)
{
case "eq": // =
invoicesQuery = invoicesQuery.Where(
c => c.INVOICE_DATE == dateValue);
break;
case "lt": // <
invoicesQuery = invoicesQuery.Where(
c => c.INVOICE_DATE < dateValue);
break;
case "le": // <=
invoicesQuery = invoicesQuery.Where(
c => c.INVOICE_DATE <= dateValue);
break;
case "gt": // >
invoicesQuery = invoicesQuery.Where(
c => c.INVOICE_DATE > dateValue);
break;
case "ge": // >=
invoicesQuery = invoicesQuery.Where(
c => c.INVOICE_DATE >= dateValue);
break;
}
}
if (searchField == "PAID")
{
int iVal = (searchString == "on") ? 1 : 0;
invoicesQuery = invoicesQuery.Where(c => c.PAID == iVal);
}
// get the total number of invoices
int totalRows = invoicesQuery.Count();
// add sorting
switch (sord)
{
case "asc":
invoicesQuery = invoicesQuery.OrderBy(
invoice => invoice.INVOICE_DATE);
break;
case "desc":
invoicesQuery = invoicesQuery.OrderByDescending(
invoice => invoice.INVOICE_DATE);
break;
}
// get invoice list
var invoices = invoicesQuery
.Skip(offset)
.Take(limit)
.ToList();
// calculate the total number of pages
int totalPages = totalRows / limit + 1;
// create the result for jqGrid
var result = new
{
page = pageNo,
total = totalPages,
records = totalRows,
rows = invoices
};
// convert the result to JSON
return Json(result, JsonRequestBehavior.AllowGet);
}
// Receiving data in the form of JSON for the detail grid
public ActionResult GetDetailData(int? invoice_id)
{
// build a LINQ query for receiving invoice items
// filtered by invoice id
var lines =
from line in db.INVOICE_LINES
where line.INVOICE_ID == invoice_id
select new
{
INVOICE_LINE_ID = line.INVOICE_LINE_ID,
INVOICE_ID = line.INVOICE_ID,
PRODUCT_ID = line.PRODUCT_ID,
Product = line.PRODUCT.NAME,
Quantity = line.QUANTITY,
Price = line.SALE_PRICE,
Total = line.QUANTITY * line.SALE_PRICE
};
// get invoice position list
var invoices = lines
.ToList();
// create the result for jqGrid
var result = new
{
rows = invoices
};
// convert the result to JSON
return Json(result, JsonRequestBehavior.AllowGet);
}
// Add new invoice
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(
[Bind(Include = "CUSTOMER_ID,INVOICE_DATE")] INVOICE invoice)
{
// check the correctness of the model
if (ModelState.IsValid)
{
try
{
var INVOICE_ID = new FbParameter("INVOICE_ID", FbDbType.Integer);
var CUSTOMER_ID = new FbParameter("CUSTOMER_ID", FbDbType.Integer);
var INVOICE_DATE = new FbParameter("INVOICE_DATE",
FbDbType.TimeStamp);
// initialize parameters query
INVOICE_ID.Value = db.NextValueFor("GEN_INVOICE_ID");
CUSTOMER_ID.Value = invoice.CUSTOMER_ID;
INVOICE_DATE.Value = invoice.INVOICE_DATE;
// execute stored procedure
db.Database.ExecuteSqlCommand(
"EXECUTE PROCEDURE SP_ADD_INVOICE(@INVOICE_ID, @CUSTOMER_ID, @INVOICE_DATE)",
INVOICE_ID,
CUSTOMER_ID,
INVOICE_DATE);
// return success in JSON format
return Json(true);
}
catch (Exception ex)
{
// return error in JSON format
return Json(new { error = ex.Message });
}
}
else {
string messages = string.Join("; ", ModelState.Values
.SelectMany(x => x.Errors)
.Select(x => x.ErrorMessage));
// return error in JSON format
return Json(new { error = messages });
}
}
// Edit invoice
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(
[Bind(Include = "INVOICE_ID,CUSTOMER_ID,INVOICE_DATE")] INVOICE invoice)
{
// check the correctness of the model
if (ModelState.IsValid)
{
try
{
var INVOICE_ID = new FbParameter("INVOICE_ID", FbDbType.Integer);
var CUSTOMER_ID = new FbParameter("CUSTOMER_ID", FbDbType.Integer);
var INVOICE_DATE = new FbParameter("INVOICE_DATE",
FbDbType.TimeStamp);
// initialize parameters query
INVOICE_ID.Value = invoice.INVOICE_ID;
CUSTOMER_ID.Value = invoice.CUSTOMER_ID;
INVOICE_DATE.Value = invoice.INVOICE_DATE;
// execute stored procedure
db.Database.ExecuteSqlCommand(
"EXECUTE PROCEDURE SP_EDIT_INVOICE(@INVOICE_ID, @CUSTOMER_ID, @INVOICE_DATE)",
INVOICE_ID,
CUSTOMER_ID,
INVOICE_DATE);
// return success in JSON format
return Json(true);
}
catch (Exception ex)
{
// return error in JSON format
return Json(new { error = ex.Message });
}
}
else {
string messages = string.Join("; ", ModelState.Values
.SelectMany(x => x.Errors)
.Select(x => x.ErrorMessage));
// return error in JSON format
return Json(new { error = messages });
}
}
// Delete invoice
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(int id)
{
try
{
var INVOICE_ID = new FbParameter("INVOICE_ID", FbDbType.Integer);
// initialize parameters query
INVOICE_ID.Value = id;
// execute stored procedure
db.Database.ExecuteSqlCommand(
"EXECUTE PROCEDURE SP_DELETE_INVOICE(@INVOICE_ID)",
INVOICE_ID);
// return success in JSON format
return Json(true);
}
catch (Exception ex)
{
// return error in JSON format
return Json(new { error = ex.Message });
}
}
// Payment of invoice
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Pay(int id)
{
try
{
var INVOICE_ID = new FbParameter("INVOICE_ID", FbDbType.Integer);
// initialize parameters query
INVOICE_ID.Value = id;
// execute stored procedure
db.Database.ExecuteSqlCommand(
"EXECUTE PROCEDURE SP_PAY_FOR_INOVICE(@INVOICE_ID)",
INVOICE_ID);
// return success in JSON format
return Json(true);
}
catch (Exception ex)
{
// return error in JSON format
return Json(new { error = ex.Message });
}
}
// Add invoice position
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateDetail(
[Bind(Include = "INVOICE_ID,PRODUCT_ID,QUANTITY")] INVOICE_LINE invoiceLine)
{
// check the correctness of the model
if (ModelState.IsValid)
{
try
{
var INVOICE_ID = new FbParameter("INVOICE_ID", FbDbType.Integer);
var PRODUCT_ID = new FbParameter("PRODUCT_ID", FbDbType.Integer);
var QUANTITY = new FbParameter("QUANTITY", FbDbType.Integer);
// initialize parameters query
INVOICE_ID.Value = invoiceLine.INVOICE_ID;
PRODUCT_ID.Value = invoiceLine.PRODUCT_ID;
QUANTITY.Value = invoiceLine.QUANTITY;
// execute stored procedure
db.Database.ExecuteSqlCommand(
""EXECUTE PROCEDURE SP_ADD_INVOICE_LINE(@INVOICE_ID, @PRODUCT_ID, @QUANTITY)",
INVOICE_ID,
PRODUCT_ID,
QUANTITY);
// return success in JSON format
return Json(true);
}
catch (Exception ex)
{
// return error in JSON format
return Json(new { error = ex.Message });
}
}
else {
string messages = string.Join("; ", ModelState.Values
.SelectMany(x => x.Errors)
.Select(x => x.ErrorMessage));
// return error in JSON format
return Json(new { error = messages });
}
}
// Edit invoice position
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditDetail(
[Bind(Include = "INVOICE_LINE_ID,INVOICE_ID,PRODUCT_ID,QUANTITY")]
INVOICE_LINE invoiceLine)
{
// check the correctness of the model
if (ModelState.IsValid)
{
try
{
// Create parameters
var INVOICE_LINE_ID = new FbParameter("INVOICE_LINE_ID",
FbDbType.Integer);
var QUANTITY = new FbParameter("QUANTITY", FbDbType.Integer);
// initialize parameters query
INVOICE_LINE_ID.Value = invoiceLine.INVOICE_LINE_ID;
QUANTITY.Value = invoiceLine.QUANTITY;
// execute stored procedure
db.Database.ExecuteSqlCommand(
"EXECUTE PROCEDURE SP_EDIT_INVOICE_LINE(@INVOICE_LINE_ID, @QUANTITY)",
INVOICE_LINE_ID,
QUANTITY);
// return success in JSON format
return Json(true);
}
catch (Exception ex)
{
// return error in JSON format
return Json(new { error = ex.Message });
}
}
else {
string messages = string.Join("; ", ModelState.Values
.SelectMany(x => x.Errors)
.Select(x => x.ErrorMessage));
// return error in JSON format
return Json(new { error = messages });
}
}
// Delete invoice position
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult DeleteDetail(int id)
{
try
{
// create parameters
var INVOICE_LINE_ID = new FbParameter("INVOICE_LINE_ID",
FbDbType.Integer);
// initialize parameters query
INVOICE_LINE_ID.Value = id;
// execute stored procedure
db.Database.ExecuteSqlCommand(
"EXECUTE PROCEDURE SP_DELETE_INVOICE_LINE(@INVOICE_LINE_ID)",
INVOICE_LINE_ID);
// return success in JSON format
return Json(true);
}
catch (Exception ex)
{
// return error in JSON format
return Json(new { error = ex.Message });
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
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