diff --git a/Server/BiskAcdbContext.cs b/Server/BiskAcdbContext.cs index 10468ab..d0ba890 100644 --- a/Server/BiskAcdbContext.cs +++ b/Server/BiskAcdbContext.cs @@ -1,21 +1,46 @@ -using System; -using System.Collections.Generic; +using Azure.Core; +using Biskilog_Accounting.Shared.Enums; +using Biskilog_Accounting.Shared.Interfaces; using Biskilog_Accounting.Shared.POSModels; using Microsoft.EntityFrameworkCore; +using Microsoft.Net.Http.Headers; +using System.Security.Claims; namespace Biskilog_Accounting.Server.POSModels; public partial class BiskAcdbContext : DbContext { + private readonly HttpContext m_httpContext; + private readonly IConnectionService m_connection; + private readonly ITokenService m_tokenService; public BiskAcdbContext() { } - - public BiskAcdbContext(DbContextOptions options) - : base(options) + public BiskAcdbContext(DbContextOptions options, ITokenService tokenService, IConnectionService connection, IHttpContextAccessor a_httpContextAccessor = null) + : base(options) { + m_tokenService = tokenService; + m_connection = connection; + m_httpContext = a_httpContextAccessor?.HttpContext; } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + if (!optionsBuilder.IsConfigured) + { + string token = m_httpContext.Request.Headers[HeaderNames.Authorization]!; + if (AuthEnums.Valid == m_tokenService.ValidateToken(token)) + { + int? databaseId = m_tokenService.GetDatabaseIdFromToken(token); + string connectionString = m_connection.GetClientConnectionString(databaseId!.Value); + optionsBuilder.UseMySql(connectionString, new MariaDbServerVersion(new Version())); + } + else + { + m_httpContext.Abort(); + } + } + } public virtual DbSet Creditpurchases { get; set; } public virtual DbSet Customeraccounts { get; set; } diff --git a/Server/BiskilogContext.cs b/Server/BiskilogContext.cs index 0795270..fd6d206 100644 --- a/Server/BiskilogContext.cs +++ b/Server/BiskilogContext.cs @@ -90,6 +90,13 @@ public partial class BiskilogContext : DbContext .HasDefaultValueSql("current_timestamp()") .HasColumnType("datetime") .HasColumnName("date_joined"); + entity.Property(e => e.BusinessExternalId) + .HasMaxLength(50) + .HasDefaultValueSql("''") + .HasColumnName("businessExternalId") + .UseCollation("utf8mb4_general_ci") + .HasCharSet("utf8mb4"); + }); modelBuilder.Entity(entity => diff --git a/Server/Controllers/AnalyticsController.cs b/Server/Controllers/AnalyticsController.cs new file mode 100644 index 0000000..33e35bb --- /dev/null +++ b/Server/Controllers/AnalyticsController.cs @@ -0,0 +1,101 @@ +using Biskilog_Accounting.Shared.CustomModels; +using Biskilog_Accounting.Shared.Interfaces; +using Biskilog_Accounting.Shared.POSModels; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Net.Http.Headers; +using NuGet.Common; + +namespace Biskilog_Accounting.Server.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class AnalyticsController : ControllerBase + { + private readonly IAnalytics m_analyticService; + public AnalyticsController(IAnalytics a_analytics) + { + m_analyticService = a_analytics; + } + + /// + /// Endpoint to return analysis on CancelledSales within a specified period + /// + /// + /// + [Authorize] + [HttpGet, Route("cancelledsales/{a_start}/{a_end}")] + public IEnumerable GetCancelledSalesAsync(DateTime a_start, DateTime a_end) + { + return m_analyticService.GetCancelledSales(a_start, a_end); + } + /// + /// Endpoint to return analysis on Sales within a specified period + /// + /// + /// + [Authorize] + [HttpGet, Route("sales/{a_start}/{a_end}")] + public IEnumerable GetSalesAsync(DateTime a_start, DateTime a_end) + { + return m_analyticService.GetSalesTransaction(a_start, a_end); + } + /// + /// Endpoint to return analysis on in-debt customers + /// + /// + /// + [Authorize] + [HttpGet, Route("debtors")] + public IEnumerable GetInDebtCustomers() + { + return m_analyticService.GetInDebtCustomers(); + } + /// + /// Endpoint to return analysis on product price changes + /// + /// + /// + [Authorize] + [HttpGet, Route("pricechanges/{a_start}/{a_end}")] + public IEnumerable GetPriceChanges(DateTime a_start, DateTime a_end) + { + return m_analyticService.GetPriceChanges(a_start, a_end); + } + /// + /// Endpoint to return analysis on sales by employees + /// + /// + /// + [Authorize] + [HttpGet, Route("employeesales/{a_start}/{a_end}")] + public Dictionary> GetEmployeeSales(DateTime a_start, DateTime a_end) + { + return m_analyticService.GetEmployeeSales(a_start, a_end); + } + /// + /// Endpoint to return analysis on product items low on stock + /// + /// + /// + [Authorize] + [HttpGet, Route("lowonstock")] + public IEnumerable GetLowOnStockItems() + { + string token = Request.Headers[HeaderNames.Authorization]!; + + return m_analyticService.GetOutOfStockItems(); + } + /// + /// Endpoint to return analysis on the most purchased product item + /// + /// + /// + [Authorize] + [HttpGet, Route("mostpurchaseditem/{a_start}/{a_end}")] + public IEnumerable GetMostPurchased(DateTime a_start, DateTime a_end) + { + return m_analyticService.GetMostPurchasedItem(a_start, a_end); + } + } +} diff --git a/Server/Program.cs b/Server/Program.cs index b898305..7e20b15 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -9,6 +9,7 @@ using Biskilog_Accounting.Server; using Biskilog_Accounting.ServiceRepo; using Biskilog_Accounting.Shared.Interfaces; using Biskilog_Accounting.Server.Services; +using Biskilog_Accounting.Server.POSModels; var builder = WebApplication.CreateBuilder(args); @@ -21,8 +22,12 @@ builder.Services.AddEntityFrameworkMySql().AddDbContext(options { options.UseMySql(builder.Configuration.GetConnectionString("Connection"), new MariaDbServerVersion(new Version())); }); +builder.Services.AddSingleton(); +builder.Services.AddDbContext(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddCors(options => { diff --git a/Server/Services/AnalyticalService.cs b/Server/Services/AnalyticalService.cs new file mode 100644 index 0000000..2795e05 --- /dev/null +++ b/Server/Services/AnalyticalService.cs @@ -0,0 +1,164 @@ +using Biskilog_Accounting.Server.POSModels; +using Biskilog_Accounting.Shared.CustomModels; +using Biskilog_Accounting.Shared.Interfaces; +using Biskilog_Accounting.Shared.POSModels; +using Microsoft.Net.Http.Headers; +using System.Data.Entity; + +namespace Biskilog_Accounting.Server.Services +{ + /// + /// Gets the KPIs/ Analysis of operations made with the software + /// + public class AnalyticalService : IAnalytics + { + private readonly BiskAcdbContext m_context; + private readonly ITokenService m_tokenService; + private readonly HttpContext m_httpContext; + + public AnalyticalService(BiskAcdbContext a_context, ITokenService a_tokenService, IHttpContextAccessor a_httpContextAccessor) + { + m_context = a_context; + m_tokenService = a_tokenService; + m_httpContext = a_httpContextAccessor?.HttpContext; + } + public IEnumerable GetCancelledSales(DateTime a_start, DateTime a_end) + { + string token = m_httpContext.Request.Headers[HeaderNames.Authorization]!; + IEnumerable accessiblebranches = m_tokenService.BranchIds(token); + + return from cSale in m_context.Tblcancelledtransactions + join aSale in m_context.Tblcarts on cSale.Transno equals aSale.Transno into activeSale + join cPurchase in m_context.Tblcustomerpurchases on cSale.Transno equals cPurchase.TransactionId into customerSales + from c in customerSales.DefaultIfEmpty() + join customer in m_context.Tblcustomers on c.CustomerId equals customer.CustomerId into Customers + from cc in Customers.DefaultIfEmpty() + where cSale.DateCancelled >= a_start && cSale.DateCancelled <= a_end && accessiblebranches.Contains(cSale.BranchId) + select new CancelledSales + { + CancelledTransaction = cSale, + Value = (from a in activeSale where accessiblebranches.Contains(a.BranchId) select a.Total).Sum(), + Customer = !String.IsNullOrEmpty(cc.CustomerId) ? $"{cc.Firstname} {cc.Surname}" : "Walk-IN Purchase" + }; + + } + + public Dictionary> GetEmployeeSales(DateTime a_start, DateTime a_end) + { + string token = m_httpContext.Request.Headers[HeaderNames.Authorization]!; + IEnumerable accessiblebranches = m_tokenService.BranchIds(token); + + Dictionary> sales = new Dictionary>(); + var employeeSales = m_context.Tblcarts.Where(c => c.Date >= a_start && c.Date <= a_end + && accessiblebranches.Contains(c.BranchId)).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 GetInDebtCustomers() + { + string token = m_httpContext.Request.Headers[HeaderNames.Authorization]!; + IEnumerable accessiblebranches = m_tokenService.BranchIds(token); + + var listDebts = m_context.Customeraccounts.Where(t => t.Balance < 0 && accessiblebranches.Contains(t.BranchId)).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 GetMostPurchasedItem(DateTime a_start, DateTime a_end) + { + //TODO either rewrite query or increase memory on server to deal with comparison mode performance issue + string token = m_httpContext.Request.Headers[HeaderNames.Authorization]!; + IEnumerable accessiblebranches = m_tokenService.BranchIds(token); + + 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 && s.BranchId == accessiblebranches.First() + 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 GetOutOfStockItems() + { + string token = m_httpContext.Request.Headers[HeaderNames.Authorization]!; + IEnumerable accessiblebranches = m_tokenService.BranchIds(token); + + 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" && accessiblebranches.Contains(item.BranchId) && + ((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! + }); + } + + public IEnumerable GetPriceChanges(DateTime a_start, DateTime a_end) + { + string token = m_httpContext.Request.Headers[HeaderNames.Authorization]!; + IEnumerable accessiblebranches = m_tokenService.BranchIds(token); + + return from change in m_context.Tblpricechanges + join p in m_context.Tblproducts on change.Pcode equals p.Pcode + where change.ChangeDate >= a_start && change.ChangeDate <= a_end && accessiblebranches.Contains(change.BranchId) + select new ProductPriceChange + { + BranchId = change.BranchId, + ChangeDate = change.ChangeDate, + Pcode = change.Pcode, + CountId = change.CountId, + CurrentPrice = change.CurrentPrice, + PreviousPrice = change.PreviousPrice, + ProductName = p.ProductName + }; + } + + public IEnumerable GetSalesTransaction(DateTime a_start, DateTime a_end) + { + string token = m_httpContext.Request.Headers[HeaderNames.Authorization]!; + IEnumerable accessiblebranches = m_tokenService.BranchIds(token); + + return m_context.Tblcarts.Where(t => t.Date >= a_start && t.Date <= a_end && accessiblebranches.Contains(t.BranchId)); + + } + } +} diff --git a/Server/Services/AuthenticationService.cs b/Server/Services/AuthenticationService.cs index 172071b..c1ae403 100644 --- a/Server/Services/AuthenticationService.cs +++ b/Server/Services/AuthenticationService.cs @@ -55,11 +55,12 @@ namespace Biskilog_Accounting.Server.Services List businessIds = GetSiteaccesspermission(user.ClientId, user.UserId).Select(t => t.BusinessId).ToList(); Contract? contract = GetContract(user.ClientId, businessIds); + List businesses = GetClientbusiness(user.ClientId, user.UserId).Select(t=>t.BusinessExternalId).ToList(); if (contract == null) return AuthEnums.Invalid.ToString(); - return m_tokenService.GenerateToken(user, contract, databasemap); + return m_tokenService.GenerateToken(user, contract, databasemap, businesses, false); } /// @@ -94,9 +95,12 @@ namespace Biskilog_Accounting.Server.Services throw new NotImplementedException(); } - public List GetClientbusiness(int a_clientId) + public List GetClientbusiness(int a_clientId, int userId) { - return m_context.Clientbusinesses.Where(i => i.ClientId == a_clientId).ToList(); + return (from b in m_context.Clientbusinesses + join p in m_context.Siteaccesspermissions on new {b.ClientId, b.BusinessId} equals new {p.ClientId, p.BusinessId} + where p.UserId == userId && p.ClientId == a_clientId + select b).ToList(); } public Databasemap GetClientDB(int a_clientId) diff --git a/Server/Services/ConnectionService.cs b/Server/Services/ConnectionService.cs index fa89b02..6fa3e8c 100644 --- a/Server/Services/ConnectionService.cs +++ b/Server/Services/ConnectionService.cs @@ -10,13 +10,11 @@ namespace Biskilog_Accounting.Server.Services public class ConnectionService : IConnectionService { private readonly BiskilogContext m_context; - private readonly ITokenService m_tokenService; private readonly IConfiguration m_configuration; - public ConnectionService(BiskilogContext a_context, ITokenService a_tokenService, IConfiguration configuration) + public ConnectionService(BiskilogContext a_context, IConfiguration configuration) { m_context = a_context; - m_tokenService = a_tokenService; m_configuration = configuration; } /// @@ -42,11 +40,11 @@ namespace Biskilog_Accounting.Server.Services /// A configured BiskAcdbContext public object PrepareDBContext(string a_connectionString) { - - DbContextOptionsBuilder acdbContext = new DbContextOptionsBuilder(); - acdbContext.UseMySql(a_connectionString, new MariaDbServerVersion(new Version())); + throw new NotImplementedException(); + //DbContextOptionsBuilder acdbContext = new DbContextOptionsBuilder(); + //acdbContext.UseMySql(a_connectionString, new MariaDbServerVersion(new Version())); - return acdbContext; + //return new BiskAcdbContext(acdbContext.Options); } } } diff --git a/Server/appsettings.json b/Server/appsettings.json index 140c6ab..984fb06 100644 --- a/Server/appsettings.json +++ b/Server/appsettings.json @@ -7,7 +7,7 @@ }, "ConnectionStrings": { "Connection": "server=54.37.19.162;database=dev_biskilogclients;user=biskilog;password=mefbuk-6niFsu-fytrew", - "PrivateConnection": "server={0};database={1};user=biskilog;password=mefbuk-6niFsu-fytrew" + "PrivateConnection": "server={0};database={1};user=biskilog;password=mefbuk-6niFsu-fytrew;default command timeout=0;" }, "AllowedHosts": "*", "JWT": { diff --git a/Shared/ClientContractModels/Clientbusiness.cs b/Shared/ClientContractModels/Clientbusiness.cs index 83a4577..661f01b 100644 --- a/Shared/ClientContractModels/Clientbusiness.cs +++ b/Shared/ClientContractModels/Clientbusiness.cs @@ -17,4 +17,5 @@ public partial class Clientbusiness public string BiskilogVersion { get; set; } = null!; public DateTime DateJoined { get; set; } + public string BusinessExternalId { get; set; } = string.Empty!; } diff --git a/Shared/CustomModels/CancelledSales.cs b/Shared/CustomModels/CancelledSales.cs new file mode 100644 index 0000000..c7d60f3 --- /dev/null +++ b/Shared/CustomModels/CancelledSales.cs @@ -0,0 +1,16 @@ +using Biskilog_Accounting.Shared.POSModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Biskilog_Accounting.Shared.CustomModels +{ + public class CancelledSales + { + public Tblcancelledtransaction? CancelledTransaction { get; set; } + public string Customer { get; set; } = "WALK-IN Purchase"; + public decimal? Value { get; set; } + } +} diff --git a/Shared/CustomModels/InDebtCustomers.cs b/Shared/CustomModels/InDebtCustomers.cs new file mode 100644 index 0000000..07ab50f --- /dev/null +++ b/Shared/CustomModels/InDebtCustomers.cs @@ -0,0 +1,15 @@ +using Biskilog_Accounting.Shared.POSModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Biskilog_Accounting.Shared.CustomModels +{ + public class InDebtCustomers + { + public Tblcustomer Customer { get; set; } + public decimal Debt { get; set; } = 0; + } +} diff --git a/Shared/CustomModels/MostPurchaseItem.cs b/Shared/CustomModels/MostPurchaseItem.cs new file mode 100644 index 0000000..bbe8b16 --- /dev/null +++ b/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 + { + /// + /// Specifies the id of the product + /// + public string ProductId { get; set; } + /// + /// Specifies the name of the product + /// + public string ProductName { get; set; } + /// + /// The total revenue generated from the sale + /// + public decimal? Revenue { get; set; } + /// + /// This is the number of times the item has been sold + /// + public int? NbrTimesSold { get; set; } + + } +} diff --git a/Shared/CustomModels/ProductItem.cs b/Shared/CustomModels/ProductItem.cs new file mode 100644 index 0000000..26dc93a --- /dev/null +++ b/Shared/CustomModels/ProductItem.cs @@ -0,0 +1,16 @@ +using Biskilog_Accounting.Shared.POSModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Biskilog_Accounting.Shared.CustomModels +{ + public class ProductItem + { + public Tblproduct? Product { get; set; } + public Tblinventory? Stock { get; set; } + public string BaseUnit { get;set; } = string.Empty; + } +} diff --git a/Shared/CustomModels/ProductPriceChange.cs b/Shared/CustomModels/ProductPriceChange.cs new file mode 100644 index 0000000..7cd9131 --- /dev/null +++ b/Shared/CustomModels/ProductPriceChange.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Biskilog_Accounting.Shared.CustomModels +{ + public class ProductPriceChange + { + public string? Pcode { get; set; } + public string? ProductName { get; set; } + public decimal? PreviousPrice { get; set; } + + public decimal? CurrentPrice { get; set; } + + public DateTime? ChangeDate { get; set; } + + public string BranchId { get; set; } = null!; + + public string CountId { get; set; } = null!; + } +} diff --git a/Shared/CustomModels/SaleItem.cs b/Shared/CustomModels/SaleItem.cs new file mode 100644 index 0000000..82ad876 --- /dev/null +++ b/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"!; + } +} diff --git a/Shared/Interfaces/IAnalytics.cs b/Shared/Interfaces/IAnalytics.cs new file mode 100644 index 0000000..fb00c93 --- /dev/null +++ b/Shared/Interfaces/IAnalytics.cs @@ -0,0 +1,54 @@ +using Biskilog_Accounting.Shared.CustomModels; +using Biskilog_Accounting.Shared.POSModels; + +namespace Biskilog_Accounting.Shared.Interfaces +{ + public interface IAnalytics + { + /// + /// Fetches a collection of sales transaction made from the specified start date to the end date + /// + /// Specified Start Date + /// Specified end Date + /// + IEnumerable GetSalesTransaction(DateTime a_start, DateTime a_end); + /// + /// Fetches a collection of in-debt customers + /// + IEnumerable GetInDebtCustomers(); + /// + /// Fetches a collection of Product Items which are currently out of stock + /// + /// + IEnumerable GetOutOfStockItems(); + /// + /// Fetches a collection of the most purchased Product Items within a specified date range + /// + /// + /// + /// + IEnumerable GetMostPurchasedItem(DateTime a_start, DateTime a_end); + /// + /// Fetches a collection of cancelled transaction within a specified date range + /// + /// + /// + /// + IEnumerable GetCancelledSales(DateTime a_start, DateTime a_end); + /// + /// Fetches a collection of transaction made by employees within a specified date range + /// + /// + /// + /// A dictionary of transactions made by employees with employee name as key + Dictionary> GetEmployeeSales(DateTime a_start, DateTime a_end); + /// + /// Fetches a collection of product price changes with a specified date range + /// + /// + /// + /// + IEnumerable GetPriceChanges(DateTime a_start, DateTime a_end); + //void SetContraints(string a_token); + } +} diff --git a/Shared/Interfaces/IAuthService.cs b/Shared/Interfaces/IAuthService.cs index 70a8960..f0f93b9 100644 --- a/Shared/Interfaces/IAuthService.cs +++ b/Shared/Interfaces/IAuthService.cs @@ -14,6 +14,6 @@ namespace Biskilog_Accounting.Shared.Interfaces Contract? GetContract(int a_clientId, List a_businessId); Databasemap GetClientDB(int a_clientId); List GetSiteaccesspermission(int a_clientId, int a_userId); - List GetClientbusiness(int a_clientId); + List GetClientbusiness(int a_clientId, int userId); } } diff --git a/Shared/Interfaces/ITokenService.cs b/Shared/Interfaces/ITokenService.cs index 730926f..f4dc1f7 100644 --- a/Shared/Interfaces/ITokenService.cs +++ b/Shared/Interfaces/ITokenService.cs @@ -6,10 +6,13 @@ namespace Biskilog_Accounting.Shared.Interfaces public interface ITokenService { AuthEnums ValidateToken(string a_token); - string GenerateToken(Userauth a_user, Contract a_clientContract, Databasemap a_database); + string GenerateToken(Userauth a_user, Contract a_clientContract, Databasemap a_database, List a_business, bool a_comparison); int? GetDatabaseIdFromToken(string a_token); int? GetUserIdFromToken(string a_token); string? GetUserNameFromToken(string a_token); - + string? GetBaseBranch(string a_token); + bool? GetComparison(string a_token); + IEnumerable BranchIds(string a_token); + string? GetAllBranch(string a_token); } } diff --git a/Shared/ServiceRepo/TokenService.cs b/Shared/ServiceRepo/TokenService.cs index 8d3639a..d86df04 100644 --- a/Shared/ServiceRepo/TokenService.cs +++ b/Shared/ServiceRepo/TokenService.cs @@ -43,7 +43,7 @@ namespace Biskilog_Accounting.ServiceRepo /// Generates an access token based on the user /// /// A tokenized string - public string GenerateToken(Userauth a_user, Contract a_clientContract, Databasemap a_database) + public string GenerateToken(Userauth a_user, Contract a_clientContract, Databasemap a_database, List a_business, bool a_comparison) { try { @@ -56,6 +56,9 @@ namespace Biskilog_Accounting.ServiceRepo new Claim("UserId", a_user.UserId.ToString()), new Claim("Username", a_user.Username.ToString()), new Claim("DbId",a_database.DbNo.ToString()), + new Claim("ComparisonMode",a_comparison.ToString()), + new Claim("BranchId",a_business[0].ToString()), + new Claim("BranchAccess",string.Join(", ", a_business.ToArray())), new Claim("ClientId", a_user.ClientId.ToString()), }; @@ -120,5 +123,78 @@ namespace Biskilog_Accounting.ServiceRepo } return null; } + /// + ///Deserializes the token string if valid to return the specified branchId in the token string + /// + /// + /// Username + public string? GetBaseBranch(string a_token) + { + if (ValidateToken(a_token) == AuthEnums.Valid) + { + string token = a_token.Substring(6).Trim(); + var handler = new JwtSecurityTokenHandler(); + JwtSecurityToken jwtToken = (JwtSecurityToken)handler.ReadToken(token); + return jwtToken.Claims.First(claim => claim.Type == "BranchId").Value; + } + return null; + } + + public bool? GetComparison(string a_token) + { + if (ValidateToken(a_token) == AuthEnums.Valid) + { + string token = a_token.Substring(6).Trim(); + var handler = new JwtSecurityTokenHandler(); + JwtSecurityToken jwtToken = (JwtSecurityToken)handler.ReadToken(token); + return bool.Parse(jwtToken.Claims.First(claim => claim.Type == "ComparisonMode").Value); + } + return null; + } + /// + ///Deserializes the token string if valid to return the specified list of branches a user has access to in the token string + /// + /// + /// Username + public string? GetAllBranch(string a_token) + { + if (ValidateToken(a_token) == AuthEnums.Valid) + { + string token = a_token.Substring(6).Trim(); + var handler = new JwtSecurityTokenHandler(); + JwtSecurityToken jwtToken = (JwtSecurityToken)handler.ReadToken(token); + return jwtToken.Claims.First(claim => claim.Type == "BranchAccess").Value; + } + return null; + } + /// + /// Return a specified list of branches a user has access if comparison mode is set otherwise returns only the + /// active branch on the list + /// + /// + /// + public IEnumerable BranchIds(string a_token) + { + List branchIds = new List(); + if (ValidateToken(a_token) == AuthEnums.Valid) + { + bool comparison = GetComparison(a_token)!.Value; + if (comparison) + { + string? branches = GetAllBranch(a_token); + if (branches != null) + { + string[] branchArray = branches!.Split(); + branchIds.AddRange(branchArray); + } + } + else + { + string? baseBranch = GetBaseBranch(a_token); + branchIds.Add(baseBranch!); + } + } + return branchIds.AsEnumerable(); + } } }