namespace GrossesMitainesAPI.Controllers; #region Dependencies using GrossesMitainesAPI.Data; using GrossesMitainesAPI.Models; using GrossesMitainesAPI.Services; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.AspNet.Identity; using System.Data; using System.Linq; using Microsoft.Extensions.Options; using Stripe; #endregion [EnableCors("_myAllowSpecificOrigins"), ApiController, Route("api/[controller]"), Authorize(AuthenticationSchemes = "Identity.Application", Roles = "Client, Administrateur")] public class InvoiceController : Controller { #region DI Fields private readonly ILogger _logger; private readonly InventoryContext _context; private readonly DatabaseCacheService _cache; private readonly SignInManager _signInMan; private readonly Microsoft.AspNetCore.Identity.UserManager _userMan; private readonly IOptions _stripeOptions; #endregion #region Ctor public InvoiceController(ILogger logger, InventoryContext context, DatabaseCacheService cache, SignInManager signInMan, Microsoft.AspNetCore.Identity.UserManager userMan, IOptions stripeOptions) { _logger = logger; _context = context; _cache = cache; _userMan = userMan; _signInMan = signInMan; _stripeOptions = stripeOptions; } #endregion #region API Methods [HttpGet] public ActionResult> Get(bool? all = false) { IList roles; string id; try { // Trouver les rôles de l'utilisateur, assumer non-admin si impossible à trouver. var user = _userMan.GetUserAsync(_signInMan.Context.User); user.Wait(); var rolecall = _userMan.GetRolesAsync(user.Result); rolecall.Wait(); roles = rolecall.Result; } catch (Exception e) { _logger.LogError(10, e.Message); roles = new List(); } try { id = _signInMan.Context.User.Identity.GetUserId(); if (all is not null && all == true && roles.Contains("Administrateur")) return Ok(_context.Invoices .Include(x => x.ShippingAddress) .Include(x => x.LinkedAccount) .Include(x => x.Payment) .ThenInclude(x => x.BillingAddress) .Include(x => x.Products) .ThenInclude(y => y.Product) .ToList()); else return Ok(_context.Invoices .Include(x => x.ShippingAddress) .Include(x => x.LinkedAccount) .Include(x => x.Payment) .ThenInclude(x => x.BillingAddress) .Include(x => x.Products) .ThenInclude(y => y.Product) .Where(x => x.LinkedAccount != null && x.LinkedAccount.Id == id).ToList()); } catch (Exception e) { _logger.LogError(10, e.Message); return BadRequest(); } } [HttpGet("{id}")] public async Task> Get(int id) { IList roles; InvoiceModel inv; try { // Trouver les rôles de l'utilisateur, assumer non-admin si impossible à trouver. roles = await _userMan.GetRolesAsync(await _userMan.GetUserAsync(_signInMan.Context.User)); } catch (Exception e) { _logger.LogError(10, e.Message); roles = new List(); } try { inv = _context.Invoices.Where(x => x.Id == id).Include("ShippingAddress") .Include(x => x.Payment) .ThenInclude(x => x.BillingAddress).First(); } catch (Exception e) { _logger.LogError(10, e.Message); return BadRequest(); } if (roles.Contains("Administrateur") || (inv.LinkedAccount is not null && inv.LinkedAccount.Id == _signInMan.Context.User.Identity.GetUserId())) return inv; else return Unauthorized(); } [HttpPost, AllowAnonymous] public ActionResult Post(SendInvoiceModel sinv) { var user = _userMan.GetUserAsync(_signInMan.Context.User); var prodcom = sinv.ProdQuant; Dictionary badprods = new(); List prods; InvoiceModel inv = new() { // Populer les champs d'un invoice FirstName = sinv.FirstName, LastName = sinv.LastName, EmailAddress = sinv.EmailAddress, PhoneNumber = sinv.PhoneNumber, PurchaseDate = DateTime.Now, Products = new List() }; AddressModel ad; user.Wait(); inv.Products = new(); try { // Chercher les produits achetés dans la table de produits. prods = _context.Products.Where(x => sinv.ProdQuant.Select(x => x.Key).Contains(x.Id)).ToList(); } catch (Exception e) { _logger.LogError(8, e.Message); return BadRequest(); } foreach (var prod in prods) { // Les ajouter au invoice. inv.Products.Add(new() { Product = prod, Quantity = sinv.ProdQuant.First(x => x.Key == prod.Id).Value }); } foreach (var prod in sinv.ProdQuant) { // Update de quantités dans l'inventaire. ProductModel inventProd = prods.Where(x => x.Id == prod.Key).First(); if (inventProd.Quantity < prod.Value) badprods.Add(prod.Key, inventProd.Quantity); else if (inventProd.Quantity == prod.Value) { inventProd.Quantity = 0; inventProd.Status = inventProd.Status == ProductModel.States.Clearance ? ProductModel.States.Discontinued : ProductModel.States.BackOrder; } else inventProd.Quantity -= prod.Value; inventProd.LastSale = DateTime.Now; inventProd.Sales += prod.Value; } if (badprods.Count > 0) // Retour des produits non-achetable avec l'inventaire restant. return BadRequest(badprods.ToArray()); // Charges! StripeConfiguration.ApiKey = _stripeOptions.Value.SecretKey; AddressModel bill = new() { CivicNumber = sinv.BillCivicNumber, Street = sinv.BillStreet, City = sinv.BillCity, Appartment= sinv.BillAppartment, Country= sinv.BillCountry, PostalCode= sinv.BillPostalCode, Province= sinv.BillProvince, }; ChargesModel chr = new() { Token = sinv.Token, AmountInCents = sinv.AmountInCents, Name = sinv.Name, Phone = sinv.Phone, Email = sinv.Email, Description = sinv.Description, CurrencyCode = sinv.CurrencyCode, LastFourDigits = sinv.LastFourDigits, BillingAddress = bill }; inv.Payment = chr; var options = new ChargeCreateOptions { Amount = sinv.AmountInCents, Description = sinv.Description, Source = sinv.Token, Currency = sinv.CurrencyCode, }; var service = new ChargeService(); Charge charge = service.Create(options); if (charge.FailureMessage is not null && charge.FailureMessage != "") return Json(charge.ToJson()); if (user.Result is not null) { // Gestion de l'adresse client. inv.LinkedAccount = user.Result; ad = _context.Addresses.FirstOrDefault(x => x.CivicNumber == sinv.CivicNumber && x.Appartment == sinv.Appartment && x.Street == sinv.Street && x.City == sinv.City && x.Province == sinv.Province && x.Country == sinv.Country /*&& user.Result.Adresses.Contains(x)*/) ?? new() { CivicNumber = sinv.CivicNumber, Appartment = sinv.Appartment, Street = sinv.Street, City = sinv.City, Province = sinv.Province, Country = sinv.Country, PostalCode = sinv.PostalCode }; } else ad = new() { CivicNumber = sinv.CivicNumber, Appartment = sinv.Appartment, Street = sinv.Street, City = sinv.City, Province = sinv.Province, Country = sinv.Country, PostalCode = sinv.PostalCode }; inv.ShippingAddress = ad; if (prods.Count == 0) return BadRequest("Vous devez inclure au moins un produit à votre commande."); try { // Faire les updates dans la BD. if (ad.Id == 0) _context.Addresses.Add(ad); _context.Invoices.Add(inv); _context.Products.UpdateRange(prods); _context.SaveChanges(); } catch (Exception e) { _logger.LogError(8, e.Message); return BadRequest(e.InnerException.Message); } _cache.askForRefresh(); return Json(charge.ToJson()); } [HttpPost("Cancel/{id}")] public ActionResult Cancel(int id) { InvoiceModel inv; List prods; IList roles; try { // Trouver la commande. inv = _context.Invoices.Where(x => x.Id == id) .Include(x => x.Products).ThenInclude(x => x.Product).First(); } catch (Exception e) { _logger.LogError(8, e.Message); return BadRequest(); } try { // Trouver les rôles de l'utilisateur, assumer non-admin si impossible à trouver. var user = _userMan.GetUserAsync(_signInMan.Context.User); user.Wait(); var rolecall = _userMan.GetRolesAsync(user.Result); rolecall.Wait(); roles = rolecall.Result; } catch (Exception e) { _logger.LogError(10, e.Message); roles = new List(); } // Pour ne pas pouvoir arbitrairement annuler la commande d'un autre client en tant que client. if (!((inv.LinkedAccount is not null && inv.LinkedAccount.Id == _signInMan.Context.User.Identity.GetUserId()) || roles.Contains("Administrateur"))) return Unauthorized(); if (inv.Status == InvoiceModel.InStates.Cancelled || inv.Status == InvoiceModel.InStates.Returned) return BadRequest("La commande à déjà été annulée."); if (inv.Status == InvoiceModel.InStates.Shipped) return BadRequest("Il est trop tard pour annuler votre commande, veuillez contacter le service à la clientèle pour retourner la commande."); inv.Status = InvoiceModel.InStates.Cancelled; try { prods = _context.Products.Where(x => inv.Products.Select(x => x.Product.Id).Contains(x.Id)).ToList(); foreach (var prod in inv.Products) { // Revert de quantités dans l'inventaire. ProductModel inventProd = prods.First(x => x.Id == prod.Product.Id); inventProd.Quantity += prod.Quantity; inventProd.Status = inventProd.Status == ProductModel.States.Discontinued ? ProductModel.States.Clearance : ProductModel.States.Available; if (inventProd.Sales - prod.Quantity < 0) inventProd.Sales = 0; else inventProd.Sales -= prod.Quantity; } } catch (Exception e) { _logger.LogError(8, e.Message); return BadRequest(); } try { _context.Update(inv); _context.SaveChanges(); } catch (Exception e) { _logger.LogError(8, e.Message); return BadRequest(); } _cache.askForRefresh(); return Ok(inv); } #endregion }