This sample of code takes data gathered by hitting the Invoice endpoint and calculates the date of the next invoice in it. It is used in two places: first, in the registration summary page, as shown below. It is also shown on the Update Card Information page on the payment user accounts.
public class TotalProfitReportEngine : ITotalProfitReportEngine
{
private readonly IInvoiceAccessor _invoiceAccessor;
private readonly IInvoiceItemAccessor _invoiceItemAccessor;
private readonly IStudentAccessor _studentAccessor;
public TotalProfitReportEngine(IInvoiceAccessor ia, IInvoiceItemAccessor it, IStudentAccessor sa)
{
_invoiceAccessor = ia;
_invoiceItemAccessor = it;
_studentAccessor = sa;
}
public byte[] CreateReport(ReportPreferences preferences)
{
var recordsToWrite = CreateReportItemsFromDatabase(preferences);
return CsvGenerator.GenerateCsv(recordsToWrite, new TotalProfitClassMap());
}
public IList<TotalProfitReportItem> CreateReportItemsFromDatabase(ReportPreferences preferences)
{
var invoiceItems = _invoiceItemAccessor.Get();
IList<TotalProfitReportItem> reportItems = new List<TotalProfitReportItem>();
var startDate = DateTime.Parse(preferences.StartTime);
var endDate = DateTime.Parse(preferences.EndTime);
foreach (var invoiceItem in invoiceItems)
{
LoadInvoiceItem(invoiceItem);
if (invoiceItem.Type == InvoiceItemType.BaseTuition || invoiceItem.Type == InvoiceItemType.LateFee)
{
if (startDate <= invoiceItem.Invoice.InvoiceDate && endDate >= invoiceItem.Invoice.InvoiceDate)
{
var type = invoiceItem.Type;
type = StringUtils.ConvertConstantToTitleCase(type);
reportItems.Add(new TotalProfitReportItem
{
StudentFirstName = invoiceItem.Invoice.Student.FirstName,
StudentLastName = invoiceItem.Invoice.Student.LastName,
FeeType = type,
Amount = Math.Round(invoiceItem.Amount, 2)
});
}
}
}
return reportItems;
}
public ReportTotal GetTotal(ReportPreferences preferences)
{
var totalProfitEarned = CreateReportItemsFromDatabase(preferences).Sum(profitItem => profitItem.Amount);
return new ReportTotal
{
Amount = Math.Round(totalProfitEarned, 2),
Description = "Total Profit Earned In This Period"
};
}
}
This code chunk demonstrates good use of IDesign Architecture and dependency injection. As seen in the constructor
of the TotalProfitReportEngine, because this is an engine, it is only communicating with the Accessors (the
layer below it). Additionally, this code follows the dependency inversion principle by using constructor
injection for all of the Accessors it is using. One large roadblock I ran into while writing all of the report
engines was standardizing the way different languages represent dates. C# and JavaScript each use different
ways of representing dates as strings. I fixed this by creating a C#-formatted date string on the JavaScript
side, and allowing the report engines to parse it into DateTime objects. Because that is a piece of business
logic, I put this code in the Engine layer. This sample demonstrates high cohesion because all methods relating
to the
TotalProfitReportEngine
are encapsulated in one class, allowing for maximum code reuse. Efficiency and clean code was taken into
account while writing this method - I took care to only create report column objects for those invoices that
fall in the desired date range. This class implements the
ITotalProfitReportEngine
interface, which implements the
IReportEngine
interface. The
IReportEngine
interface contains methods for all of the bare-bones necessities of a revenue report -
CreateReportItemsFromDatabase
, the method that takes persisted data and constructs rows based on user preferences. There is the
ReportTotal
, which calculates any kind of aggregate amount the user wants, and finally,
CreateReport
, which creates the CSV to send up to the web layer. Finally, this Engine utilizes LINQ to help make the
C# code clean and readable.