Browse Source

Analysis controller completed

pull/4/head
Benjamin Arhen 2 years ago
parent
commit
e912e6279d
  1. 104
      Server/Controllers/AnalyticsController.cs
  2. 172
      Server/Services/AnalyticalService.cs
  3. 29
      Shared/CustomModels/MostPurchaseItem.cs
  4. 2
      Shared/CustomModels/ProductItem.cs
  5. 22
      Shared/CustomModels/SaleItem.cs
  6. 5
      Shared/Interfaces/IAnalytics.cs

104
Server/Controllers/AnalyticsController.cs

@ -1,16 +1,10 @@
using Biskilog_Accounting.Server.POSModels; using Biskilog_Accounting.Shared.CustomModels;
using Biskilog_Accounting.Server.Services;
using Biskilog_Accounting.Shared.CustomModels;
using Biskilog_Accounting.Shared.Interfaces; using Biskilog_Accounting.Shared.Interfaces;
using Biskilog_Accounting.Shared.POSModels; using Biskilog_Accounting.Shared.POSModels;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using System.ComponentModel.DataAnnotations; using NuGet.Common;
using System.Data.Entity;
namespace Biskilog_Accounting.Server.Controllers namespace Biskilog_Accounting.Server.Controllers
{ {
@ -18,12 +12,12 @@ namespace Biskilog_Accounting.Server.Controllers
[ApiController] [ApiController]
public class AnalyticsController : ControllerBase public class AnalyticsController : ControllerBase
{ {
private readonly IConnectionService m_connection;
private readonly ITokenService m_tokenService; private readonly ITokenService m_tokenService;
public AnalyticsController(ITokenService tokenService, IConnectionService connection) private readonly IAnalytics m_analyticService;
public AnalyticsController(ITokenService tokenService, IAnalytics a_analytics)
{ {
m_tokenService = tokenService; m_tokenService = tokenService;
m_connection = connection; m_analyticService = a_analytics;
} }
/// <summary> /// <summary>
@ -36,18 +30,9 @@ namespace Biskilog_Accounting.Server.Controllers
public IEnumerable<CancelledSales> GetCancelledSalesAsync(DateTime a_start, DateTime a_end) public IEnumerable<CancelledSales> GetCancelledSalesAsync(DateTime a_start, DateTime a_end)
{ {
string token = Request.Headers[HeaderNames.Authorization]!; string token = Request.Headers[HeaderNames.Authorization]!;
int? databaseId = m_tokenService.GetDatabaseIdFromToken(token); m_analyticService.SetContraints(token);
string connectionString = m_connection.GetClientConnectionString(databaseId!.Value);
bool? comparisonMode = m_tokenService.GetComparison(token);
string? currentBranch = m_tokenService.GetBaseBranch(token);
//Creates a new db context return m_analyticService.GetCancelledSales(a_start, a_end);
BiskAcdbContext newContext = (BiskAcdbContext)m_connection.PrepareDBContext(connectionString);
AnalyticalService analysis = new(newContext, comparisonMode!.Value, currentBranch!);
var result = analysis.GetCancelledSales(a_start, a_end);
return result;
} }
/// <summary> /// <summary>
/// Endpoint to return analysis on Sales within a specified period /// Endpoint to return analysis on Sales within a specified period
@ -59,16 +44,9 @@ namespace Biskilog_Accounting.Server.Controllers
public IEnumerable<Tblcart> GetSalesAsync(DateTime a_start, DateTime a_end) public IEnumerable<Tblcart> GetSalesAsync(DateTime a_start, DateTime a_end)
{ {
string token = Request.Headers[HeaderNames.Authorization]!; string token = Request.Headers[HeaderNames.Authorization]!;
int? databaseId = m_tokenService.GetDatabaseIdFromToken(token);
string connectionString = m_connection.GetClientConnectionString(databaseId!.Value);
bool? comparisonMode = m_tokenService.GetComparison(token);
string? currentBranch = m_tokenService.GetBaseBranch(token);
//Creates a new db context
BiskAcdbContext newContext = (BiskAcdbContext)m_connection.PrepareDBContext(connectionString);
AnalyticalService analysis = new(newContext, comparisonMode!.Value, currentBranch!); m_analyticService.SetContraints(token);
return analysis.GetSalesTransaction(a_start, a_end); return m_analyticService.GetSalesTransaction(a_start, a_end);
} }
/// <summary> /// <summary>
/// Endpoint to return analysis on in-debt customers /// Endpoint to return analysis on in-debt customers
@ -80,17 +58,65 @@ namespace Biskilog_Accounting.Server.Controllers
public IEnumerable<InDebtCustomers> GetInDebtCustomers() public IEnumerable<InDebtCustomers> GetInDebtCustomers()
{ {
string token = Request.Headers[HeaderNames.Authorization]!; string token = Request.Headers[HeaderNames.Authorization]!;
int? databaseId = m_tokenService.GetDatabaseIdFromToken(token);
string connectionString = m_connection.GetClientConnectionString(databaseId!.Value);
bool? comparisonMode = m_tokenService.GetComparison(token);
string? currentBranch = m_tokenService.GetBaseBranch(token);
//Creates a new db context m_analyticService.SetContraints(token);
BiskAcdbContext newContext = (BiskAcdbContext)m_connection.PrepareDBContext(connectionString); return m_analyticService.GetInDebtCustomers();
}
/// <summary>
/// Endpoint to return analysis on product price changes
/// </summary>
/// <param name="a_start"></param>
/// <param name="a_end"></param>
[Authorize]
[HttpGet, Route("pricechanges/{a_start}/{a_end}")]
public IEnumerable<ProductPriceChange> GetPriceChanges(DateTime a_start, DateTime a_end)
{
string token = Request.Headers[HeaderNames.Authorization]!;
AnalyticalService analysis = new(newContext, comparisonMode!.Value, currentBranch!); m_analyticService.SetContraints(token);
return analysis.GetInDebtCustomers(); return m_analyticService.GetPriceChanges(a_start, a_end);
} }
/// <summary>
/// Endpoint to return analysis on sales by employees
/// </summary>
/// <param name="a_start"></param>
/// <param name="a_end"></param>
[Authorize]
[HttpGet, Route("employeesales/{a_start}/{a_end}")]
public Dictionary<string, List<SaleItem>> GetEmployeeSales(DateTime a_start, DateTime a_end)
{
string token = Request.Headers[HeaderNames.Authorization]!;
m_analyticService.SetContraints(token);
return m_analyticService.GetEmployeeSales(a_start, a_end);
}
/// <summary>
/// Endpoint to return analysis on product items low on stock
/// </summary>
/// <param name="a_start"></param>
/// <param name="a_end"></param>
[Authorize]
[HttpGet, Route("lowonstock")]
public IEnumerable<ProductItem> GetLowOnStockItems()
{
string token = Request.Headers[HeaderNames.Authorization]!;
m_analyticService.SetContraints(token);
return m_analyticService.GetOutOfStockItems();
}
/// <summary>
/// Endpoint to return analysis on the most purchased product item
/// </summary>
/// <param name="a_start"></param>
/// <param name="a_end"></param>
[Authorize]
[HttpGet, Route("mostpurchaseditem/{a_start}/{a_end}")]
public IEnumerable<MostPurchasedItem> GetMostPurchased(DateTime a_start, DateTime a_end)
{
string token = Request.Headers[HeaderNames.Authorization]!;
m_analyticService.SetContraints(token);
return m_analyticService.GetMostPurchasedItem(a_start, a_end);
}
} }
} }

172
Server/Services/AnalyticalService.cs

@ -1,8 +1,11 @@
using Biskilog_Accounting.Server.POSModels; using Azure.Core;
using Biskilog_Accounting.Server.POSModels;
using Biskilog_Accounting.ServiceRepo;
using Biskilog_Accounting.Shared.CustomModels; using Biskilog_Accounting.Shared.CustomModels;
using Biskilog_Accounting.Shared.Interfaces; using Biskilog_Accounting.Shared.Interfaces;
using Biskilog_Accounting.Shared.POSModels; using Biskilog_Accounting.Shared.POSModels;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Net.Http.Headers;
using System.Data.Entity; using System.Data.Entity;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory; using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
@ -15,13 +18,13 @@ namespace Biskilog_Accounting.Server.Services
public class AnalyticalService : IAnalytics public class AnalyticalService : IAnalytics
{ {
private readonly BiskAcdbContext m_context; private readonly BiskAcdbContext m_context;
private readonly ITokenService m_tokenService;
private bool m_comparisonMode; private bool m_comparisonMode;
private string m_activeBranch; private string m_activeBranch;
public AnalyticalService(BiskAcdbContext a_context, bool a_comparisonMode, string a_activeBranchId) public AnalyticalService(BiskAcdbContext a_context, ITokenService a_tokenService)
{ {
m_context = a_context; m_context = a_context;
m_comparisonMode = a_comparisonMode; m_tokenService = a_tokenService;
m_activeBranch = a_activeBranchId;
} }
public IEnumerable<CancelledSales> GetCancelledSales(DateTime a_start, DateTime a_end) public IEnumerable<CancelledSales> GetCancelledSales(DateTime a_start, DateTime a_end)
{ {
@ -61,56 +64,143 @@ namespace Biskilog_Accounting.Server.Services
} }
} }
public IEnumerable<Dictionary<string, List<Tblcart>>> GetEmployeeSales(DateTime a_start, DateTime a_end) public Dictionary<string, List<SaleItem>> GetEmployeeSales(DateTime a_start, DateTime a_end)
{ {
throw new NotImplementedException(); Dictionary<string, List<SaleItem>> sales = new Dictionary<string, List<SaleItem>>();
if (m_comparisonMode)
{
var employeeSales = m_context.Tblcarts.Where(c => c.Date >= a_start && c.Date <= a_end).Select(e => e.Cashier).Distinct();
foreach (string employeeName in employeeSales)
{
var list = (from a in employeeSales
join c in m_context.Tblcarts on a equals c.Cashier into Sales
from s in Sales
group s by s.Transno into saleItem
select new SaleItem
{
Total = saleItem.Sum(c => c.Total),
Transno = saleItem.Key,
Cashier = employeeName,
Date = saleItem.First().Date,
Status = saleItem.First().Status,
BranchId = saleItem.First().BranchId
}).ToList();
sales.Add(employeeName, list);
}
}
else
{
var employeeSales = m_context.Tblcarts.Where(c => c.Date >= a_start && c.Date <= a_end && c.BranchId == m_activeBranch).Select(e => e.Cashier).Distinct().ToList();
foreach (var employeeName in employeeSales)
{
var list = (from a in employeeSales
join c in m_context.Tblcarts on a equals c.Cashier into Sales
from s in Sales
group s by s.Transno into saleItem
select new SaleItem
{
Total = saleItem.Sum(c => c.Total),
Transno = saleItem.Key,
Cashier = employeeName,
Date = saleItem.First().Date,
Status = saleItem.First().Status,
BranchId = saleItem.First().BranchId
}).ToList();
sales.Add(employeeName, list);
}
}
return sales;
} }
public IEnumerable<InDebtCustomers> GetInDebtCustomers() public IEnumerable<InDebtCustomers> GetInDebtCustomers()
{ {
if (m_comparisonMode) if (m_comparisonMode)
{ {
return from c in m_context.Tblcustomers var listDebts = m_context.Customeraccounts.Where(t => t.Balance < 0).OrderByDescending(d => d.Date).Select(t => t.CustomerId).Distinct().ToList();
join a in m_context.Customeraccounts on c.CustomerId equals a.CustomerId into CustomerAccounts foreach (var customerId in listDebts)
from ac in CustomerAccounts.OrderByDescending(ac => ac.Date).Take(1).DefaultIfEmpty() {
where ac.Balance < 0 yield return new InDebtCustomers
orderby ac.Date descending {
select new InDebtCustomers Customer = m_context.Tblcustomers.FirstOrDefault(i => i.CustomerId == customerId),
{ Debt = m_context.Customeraccounts.OrderByDescending(d => d.Date).FirstOrDefault(t => t.Balance < 0 && t.CustomerId == customerId).Balance,
Customer = c, };
Debt = ac != null ? ac.Balance : 0 }
};
} }
else else
{ {
return null; var listDebts = m_context.Customeraccounts.Where(t => t.Balance < 0 && t.BranchId == m_activeBranch).OrderByDescending(d => d.Date).Select(t => t.CustomerId).Distinct().ToList();
foreach (var customerId in listDebts)
{
yield return new InDebtCustomers
{
Customer = m_context.Tblcustomers.FirstOrDefault(i => i.CustomerId == customerId),
Debt = m_context.Customeraccounts.OrderByDescending(d => d.Date).FirstOrDefault(t => t.Balance < 0 && t.CustomerId == customerId).Balance,
};
}
} }
} }
public IEnumerable<ProductItem> GetMostPurchasedItem(DateTime a_start, DateTime a_end) public IEnumerable<MostPurchasedItem> GetMostPurchasedItem(DateTime a_start, DateTime a_end)
{ {
throw new NotImplementedException(); var items = (from s in m_context.Tblcarts
join p in m_context.Tblproducts on s.Id equals p.Pcode
where s.Date >= a_start && s.Date <= a_end
group s by p into g
orderby g.Count() descending
select new MostPurchasedItem
{
ProductId = g.Key.Pcode,
ProductName = g.Key.ProductName,
NbrTimesSold = g.Count(),
Revenue = g.Sum(s => s.Price)
}).Take(50).ToList();
return items;
} }
public IEnumerable<ProductItem> GetOutOfStockItems() public IEnumerable<ProductItem> GetOutOfStockItems()
{ {
//if (m_comparisonMode) if (m_comparisonMode)
//{ {
// return from r in m_context.Restocklevels return (from item in m_context.Tblinventories
// join i in m_context.Tblinventories on r.ProductId equals i.Pcode join p in m_context.Tblproducts on item.Pcode equals p.Pcode
// join p in m_context.Tblproducts on i.Pcode equals p.Pcode join pu in m_context.Productaltunits on item.Pcode equals pu.Pcode into AltUnit
// where i.Quantity == r.WarnLevel from au in AltUnit.DefaultIfEmpty()
// select new ProductItem join rs in m_context.Restocklevels on item.Pcode equals rs.ProductId
// { join un in m_context.Unitofmeasures on p.BaseUnit equals un.UnitCode
// Product = p, where p.Status!.ToLower() != "inactive" &&
// Stock = i, ((rs.WarnLevel >= item.Quantity && rs.Unit == p.BaseUnit) || (rs.WarnLevel >= (item.Quantity / au.QuantityUnit) &&
// Unitofmeasure = rs.Unit == au.UnitCode)
// } )
//} select new ProductItem
//else { {
//} Product = p,
return null; Stock = item,
BaseUnit = un.Unitshort!
});
}
else
{
return (from item in m_context.Tblinventories
join p in m_context.Tblproducts on item.Pcode equals p.Pcode
join pu in m_context.Productaltunits on item.Pcode equals pu.Pcode into AltUnit
from au in AltUnit.DefaultIfEmpty()
join rs in m_context.Restocklevels on item.Pcode equals rs.ProductId
join un in m_context.Unitofmeasures on p.BaseUnit equals un.UnitCode
where p.Status!.ToLower() != "inactive" && item.BranchId == m_activeBranch &&
((rs.WarnLevel >= item.Quantity && rs.Unit == p.BaseUnit) || (rs.WarnLevel >= (item.Quantity / au.QuantityUnit) &&
rs.Unit == au.UnitCode)
)
select new ProductItem
{
Product = p,
Stock = item,
BaseUnit = un.Unitshort!
});
}
//return null;
} }
public IEnumerable<ProductPriceChange> GetPriceChanges(DateTime a_start, DateTime a_end) public IEnumerable<ProductPriceChange> GetPriceChanges(DateTime a_start, DateTime a_end)
@ -119,7 +209,7 @@ namespace Biskilog_Accounting.Server.Services
{ {
return from change in m_context.Tblpricechanges return from change in m_context.Tblpricechanges
join p in m_context.Tblproducts on change.Pcode equals p.Pcode join p in m_context.Tblproducts on change.Pcode equals p.Pcode
where change.ChangeDate <= a_start && change.ChangeDate >= a_end where change.ChangeDate >= a_start && change.ChangeDate <= a_end
select new ProductPriceChange select new ProductPriceChange
{ {
BranchId = change.BranchId, BranchId = change.BranchId,
@ -135,7 +225,7 @@ namespace Biskilog_Accounting.Server.Services
{ {
return from change in m_context.Tblpricechanges return from change in m_context.Tblpricechanges
join p in m_context.Tblproducts on change.Pcode equals p.Pcode join p in m_context.Tblproducts on change.Pcode equals p.Pcode
where change.ChangeDate <= a_start && change.ChangeDate >= a_end && change.BranchId == m_activeBranch where change.ChangeDate >= a_start && change.ChangeDate <= a_end && change.BranchId == m_activeBranch
select new ProductPriceChange select new ProductPriceChange
{ {
BranchId = change.BranchId, BranchId = change.BranchId,
@ -161,5 +251,11 @@ namespace Biskilog_Accounting.Server.Services
return m_context.Tblcarts.Where(t => t.Date >= a_start && t.Date <= a_end && t.BranchId == m_activeBranch); return m_context.Tblcarts.Where(t => t.Date >= a_start && t.Date <= a_end && t.BranchId == m_activeBranch);
} }
} }
public void SetContraints(string a_token)
{
m_comparisonMode = m_tokenService.GetComparison(a_token)!.Value;
m_activeBranch = m_tokenService.GetBaseBranch(a_token)!;
}
} }
} }

29
Shared/CustomModels/MostPurchaseItem.cs

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Biskilog_Accounting.Shared.CustomModels
{
public class MostPurchasedItem
{
/// <summary>
/// Specifies the id of the product
/// </summary>
public string ProductId { get; set; }
/// <summary>
/// Specifies the name of the product
/// </summary>
public string ProductName { get; set; }
/// <summary>
/// The total revenue generated from the sale
/// </summary>
public decimal? Revenue { get; set; }
/// <summary>
/// This is the number of times the item has been sold
/// </summary>
public int? NbrTimesSold { get; set; }
}
}

2
Shared/CustomModels/ProductItem.cs

@ -11,6 +11,6 @@ namespace Biskilog_Accounting.Shared.CustomModels
{ {
public Tblproduct? Product { get; set; } public Tblproduct? Product { get; set; }
public Tblinventory? Stock { get; set; } public Tblinventory? Stock { get; set; }
public List<Unitofmeasure> Unitofmeasure { get; set; } = new List<Unitofmeasure>(); public string BaseUnit { get;set; } = string.Empty;
} }
} }

22
Shared/CustomModels/SaleItem.cs

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Biskilog_Accounting.Shared.CustomModels
{
public class SaleItem
{
public string? Transno { get; set; }
public DateTime? Date { get; set; }
public string? Cashier { get; set; }
public string? Status { get; set; }
public decimal? Total { get; set; }
public string BranchId { get; set; } = null!;
public string Customer { get; set; } = "Walk-In Purchase"!;
}
}

5
Shared/Interfaces/IAnalytics.cs

@ -27,7 +27,7 @@ namespace Biskilog_Accounting.Shared.Interfaces
/// <param name="a_start"></param> /// <param name="a_start"></param>
/// <param name="a_end"></param> /// <param name="a_end"></param>
/// <returns></returns> /// <returns></returns>
IEnumerable<ProductItem> GetMostPurchasedItem(DateTime a_start, DateTime a_end); IEnumerable<MostPurchasedItem> GetMostPurchasedItem(DateTime a_start, DateTime a_end);
/// <summary> /// <summary>
/// Fetches a collection of cancelled transaction within a specified date range /// Fetches a collection of cancelled transaction within a specified date range
/// </summary> /// </summary>
@ -41,7 +41,7 @@ namespace Biskilog_Accounting.Shared.Interfaces
/// <param name="a_start"></param> /// <param name="a_start"></param>
/// <param name="a_end"></param> /// <param name="a_end"></param>
/// <returns>A dictionary of transactions made by employees with employee name as key</returns> /// <returns>A dictionary of transactions made by employees with employee name as key</returns>
IEnumerable<Dictionary<string, List<Tblcart>>> GetEmployeeSales(DateTime a_start, DateTime a_end); Dictionary<string, List<SaleItem>> GetEmployeeSales(DateTime a_start, DateTime a_end);
/// <summary> /// <summary>
/// Fetches a collection of product price changes with a specified date range /// Fetches a collection of product price changes with a specified date range
/// </summary> /// </summary>
@ -49,5 +49,6 @@ namespace Biskilog_Accounting.Shared.Interfaces
/// <param name="a_end"></param> /// <param name="a_end"></param>
/// <returns></returns> /// <returns></returns>
IEnumerable<ProductPriceChange> GetPriceChanges(DateTime a_start, DateTime a_end); IEnumerable<ProductPriceChange> GetPriceChanges(DateTime a_start, DateTime a_end);
void SetContraints(string a_token);
} }
} }

Loading…
Cancel
Save