diff --git a/BreweryAPI.Furiax/BreweryAPI.Furiax.sln b/BreweryAPI.Furiax/BreweryAPI.Furiax.sln
new file mode 100644
index 0000000..1fb0b5a
--- /dev/null
+++ b/BreweryAPI.Furiax/BreweryAPI.Furiax.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.6.33712.159
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BreweryAPI", "BreweryAPI\BreweryAPI.csproj", "{A109146D-61F2-495E-B1AD-1FF0CFAD5F7D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A109146D-61F2-495E-B1AD-1FF0CFAD5F7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A109146D-61F2-495E-B1AD-1FF0CFAD5F7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A109146D-61F2-495E-B1AD-1FF0CFAD5F7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A109146D-61F2-495E-B1AD-1FF0CFAD5F7D}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {0E662D2A-8B34-42F4-8EF4-C1090AFEC2AB}
+ EndGlobalSection
+EndGlobal
diff --git a/BreweryAPI.Furiax/BreweryAPI/BreweryAPI.csproj b/BreweryAPI.Furiax/BreweryAPI/BreweryAPI.csproj
new file mode 100644
index 0000000..4e732ef
--- /dev/null
+++ b/BreweryAPI.Furiax/BreweryAPI/BreweryAPI.csproj
@@ -0,0 +1,21 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
diff --git a/BreweryAPI.Furiax/BreweryAPI/BreweryContext.cs b/BreweryAPI.Furiax/BreweryAPI/BreweryContext.cs
new file mode 100644
index 0000000..4ea8f79
--- /dev/null
+++ b/BreweryAPI.Furiax/BreweryAPI/BreweryContext.cs
@@ -0,0 +1,122 @@
+using BreweryAPI.Models;
+using Microsoft.EntityFrameworkCore;
+
+namespace BreweryAPI
+{
+ public class BreweryContext : DbContext
+ {
+ public BreweryContext(DbContextOptions options) : base(options) { }
+
+ public DbSet Breweries { get; set; }
+ public DbSet Beers { get; set; }
+ public DbSet Wholesalers { get; set; }
+ public DbSet Sales { get; set; }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreating(modelBuilder);
+
+ try
+ {
+ modelBuilder.Entity().HasData(
+ new BreweryModel { Name = "ABInbev", BreweryId = 1 },
+ new BreweryModel { Name = "Achouffe", BreweryId = 2 },
+ new BreweryModel { Name = "Alken Maes", BreweryId = 3 },
+ new BreweryModel { Name = "Belle Vue", BreweryId = 4 },
+ new BreweryModel { Name = "Duvel-Moortgat", BreweryId = 5 },
+ new BreweryModel { Name = "Hoegaarden", BreweryId = 6 },
+ new BreweryModel { Name = "Lindemans", BreweryId = 7 },
+ new BreweryModel { Name = "Palm", BreweryId = 8 },
+ new BreweryModel { Name = "4KNT", BreweryId = 9 }
+ );
+
+ modelBuilder.Entity().HasData(
+ new BeerModel { BeerId = 1, Name = "Jupiler", BrewerId = 1, Price = 0.71m },
+ new BeerModel { BeerId = 2, Name = "Stella Artois", BrewerId = 1, Price = 0.79m },
+ new BeerModel { BeerId = 3, Name = "Leffe Blond", BrewerId = 1, Price = 1.52m },
+ new BeerModel { BeerId = 4, Name = "Leffe Bruin", BrewerId = 1, Price = 1.52m },
+ new BeerModel { BeerId = 5, Name = "Leffe Ruby", BrewerId = 1, Price = 1.66m },
+ new BeerModel { BeerId = 6, Name = "Tripel Karmeliet", BrewerId = 1, Price = 1.75m },
+ new BeerModel { BeerId = 7, Name = "La Chouffe", BrewerId = 2, Price = 1.95m },
+ new BeerModel { BeerId = 8, Name = "Cherry Chouffe", BrewerId = 2, Price = 1.73m },
+ new BeerModel { BeerId = 9, Name = "Houblon Chouffe", BrewerId = 2, Price = 1.95m },
+ new BeerModel { BeerId = 10, Name = "Chouffe Soleil", BrewerId = 2, Price = 1.55m },
+ new BeerModel { BeerId = 11, Name = "Maes Pils", BrewerId = 3, Price = 0.67m },
+ new BeerModel { BeerId = 12, Name = "Cristal", BrewerId = 3, Price = 0.73m },
+ new BeerModel { BeerId = 13, Name = "Grimbergen Blond", BrewerId = 3, Price = 1.51m },
+ new BeerModel { BeerId = 14, Name = "Grimbergen Bruin", BrewerId = 3, Price = 1.51m },
+ new BeerModel { BeerId = 15, Name = "Desperados", BrewerId = 3, Price = 2.04m },
+ new BeerModel { BeerId = 16, Name = "Belle Vue Kriek", BrewerId = 4, Price = 1.39m },
+ new BeerModel { BeerId = 17, Name = "Belle Vue Geuze", BrewerId = 4, Price = 1.31m },
+ new BeerModel { BeerId = 18, Name = "Duvel", BrewerId = 5, Price = 1.65m },
+ new BeerModel { BeerId = 19, Name = "Duvel 6,66", BrewerId = 5, Price = 1.61m },
+ new BeerModel { BeerId = 20, Name = "Vedett", BrewerId = 5, Price = 1.21m },
+ new BeerModel { BeerId = 21, Name = "Maredsous Tripel", BrewerId = 5, Price = 1.97m },
+ new BeerModel { BeerId = 22, Name = "Hoegaarden", BrewerId = 6, Price = 1.00m },
+ new BeerModel { BeerId = 23, Name = "Hoegaarden Grand Cru", BrewerId = 6, Price = 1.92m },
+ new BeerModel { BeerId = 24, Name = "Lindemans Kriek", BrewerId = 7, Price = 1.24m },
+ new BeerModel { BeerId = 25, Name = "Lindemans Oude Geuze", BrewerId = 7, Price = 0.97m },
+ new BeerModel { BeerId = 26, Name = "Lindemans Framboise", BrewerId = 7, Price = 1.49m },
+ new BeerModel { BeerId = 27, Name = "Lindemans Pecheresse", BrewerId = 7, Price = 1.41m },
+ new BeerModel { BeerId = 28, Name = "Palm", BrewerId = 8, Price = 0.89m },
+ new BeerModel { BeerId = 29, Name = "Dobbel Palm", BrewerId = 8, Price = 0.95m },
+ new BeerModel { BeerId = 30, Name = "Rodenbach", BrewerId = 8, Price = 1.00m },
+ new BeerModel { BeerId = 31, Name = "Cornet", BrewerId = 8, Price = 1.67m },
+ new BeerModel { BeerId = 32, Name = "Estaminet Pils", BrewerId = 8, Price = 0.67m },
+ new BeerModel { BeerId = 33, Name = "4KNT Tripel", BrewerId = 9, Price = 1.85m },
+ new BeerModel { BeerId = 34, Name = "4KNT Square B", BrewerId = 9, Price = 1.85m },
+ new BeerModel { BeerId = 35, Name = "4KNT Carre C", BrewerId = 9, Price = 1.85m }
+ );
+
+ modelBuilder.Entity().HasData(
+ new WholesalerModel { WholesalerId = 1, Name = "Colruyt" },
+ new WholesalerModel { WholesalerId = 2, Name = "Prik & Tik" },
+ new WholesalerModel { WholesalerId = 3, Name = "Drankenhal Van Callenberge" },
+ new WholesalerModel { WholesalerId = 4, Name = "Dranken Van Remoortel" },
+ new WholesalerModel { WholesalerId = 5, Name = "Drinkshop Dullaert" },
+ new WholesalerModel { WholesalerId = 6, Name = "Bierland" }
+ );
+
+ modelBuilder.Entity().HasData(
+ new SaleModel { SaleId = 1, WholesalerId = 3, BeerId = 1, Quantity = 240 },
+ new SaleModel { SaleId = 2, WholesalerId = 3, BeerId = 18, Quantity = 240 },
+ new SaleModel { SaleId = 3, WholesalerId = 3, BeerId = 33, Quantity = 24 },
+ new SaleModel { SaleId = 4, WholesalerId = 3, BeerId = 28, Quantity = 120 },
+ new SaleModel { SaleId = 5, WholesalerId = 3, BeerId = 22, Quantity = 36 },
+ new SaleModel { SaleId = 6, WholesalerId = 3, BeerId = 11, Quantity = 200 },
+ new SaleModel { SaleId = 7, WholesalerId = 3, BeerId = 31, Quantity = 180 },
+ new SaleModel { SaleId = 8, WholesalerId = 3, BeerId = 2, Quantity = 96 }
+ );
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Something went wrong when populating the database: {ex.Message}");
+ }
+
+ }
+ public void CreateDatabase()
+ {
+ try
+ {
+ Database.EnsureCreated();
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"The database could not be created: {ex.Message}");
+ }
+ }
+
+ public void DeleteDatabase()
+ {
+ try
+ {
+ Database.EnsureDeleted();
+ }
+ catch ( Exception ex )
+ {
+ Console.WriteLine($"The database could not be deleted: {ex.Message}");
+ }
+ }
+ }
+}
+
diff --git a/BreweryAPI.Furiax/BreweryAPI/Controllers/BeerController.cs b/BreweryAPI.Furiax/BreweryAPI/Controllers/BeerController.cs
new file mode 100644
index 0000000..b050f55
--- /dev/null
+++ b/BreweryAPI.Furiax/BreweryAPI/Controllers/BeerController.cs
@@ -0,0 +1,162 @@
+using BreweryAPI.Models;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+namespace BreweryAPI.Controllers
+{
+ [Route("Beer")]
+ [ApiController]
+ public class BeerController : ControllerBase
+ {
+ private readonly BreweryContext _context;
+
+ public BeerController(BreweryContext context)
+ {
+ _context = context;
+ }
+
+ // GET: api/BeersByBrewer
+ [HttpGet("BeersByBrewer/{brewerId}")]
+ public async Task>> GetBeersByBrewer(int brewerId)
+ {
+ if (_context.Beers == null)
+ {
+ return NotFound();
+ }
+ var beersByBrewer = await _context.Beers
+ .Where(beer => beer.BrewerId == brewerId)
+ .Include(b => b.Brewer)
+ .ToListAsync();
+
+ if (beersByBrewer == null || beersByBrewer.Count == 0)
+ {
+ return NotFound();
+ }
+
+ return beersByBrewer;
+ }
+
+ // GET: api/Beer
+ [HttpGet]
+ public async Task>> GetBeers()
+ {
+ if (_context.Beers == null)
+ {
+ return NotFound();
+ }
+ return await _context.Beers.Include(b => b.Brewer).ToListAsync();
+
+ }
+
+ // GET: api/Beer/5
+ [HttpGet("{id}")]
+ public async Task> GetBeerModel(int id)
+ {
+ if (_context.Beers == null)
+ {
+ return NotFound();
+ }
+ var beerModel = await _context.Beers.Include(b => b.Brewer).FirstOrDefaultAsync(b => b.BeerId == id);
+
+ if (beerModel == null)
+ {
+ return NotFound();
+ }
+
+ return beerModel;
+ }
+
+ // PUT: api/Beer/5
+ // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
+ [HttpPut("{id}")]
+ public async Task PutBeerModel(int id, BeerModel updatedBeerModel)
+ {
+ if (id != updatedBeerModel.BeerId)
+ {
+ return BadRequest();
+ }
+
+ var existingBrewery = await _context.Breweries.FindAsync(updatedBeerModel.BrewerId);
+ if (existingBrewery == null)
+ {
+ return NotFound("That brewery doesn't exist");
+ }
+
+ updatedBeerModel.Brewer = existingBrewery;
+
+ _context.Entry(updatedBeerModel).State = EntityState.Modified;
+
+ try
+ {
+ await _context.SaveChangesAsync();
+ }
+ catch (DbUpdateConcurrencyException)
+ {
+ if (!BeerModelExists(id))
+ {
+ return NotFound();
+ }
+ else
+ {
+ throw;
+ }
+ }
+
+ return NoContent();
+ }
+
+ // POST: api/Beer
+ // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
+ [HttpPost]
+ public async Task> PostBeerModel(BeerModel beerModel)
+ {
+ if (_context.Beers == null || _context.Breweries == null)
+ {
+ return Problem("Entity set 'BreweryContext.Beers' or 'BreweryContext.Breweries' is null.");
+ }
+
+ var existingBrewery = await _context.Breweries.FindAsync(beerModel.BrewerId);
+ if (existingBrewery == null)
+ {
+ return NotFound("Brewery not found.");
+ }
+
+ var existingBeer = await _context.Beers.FirstOrDefaultAsync(b => b.Name == beerModel.Name && b.BrewerId == beerModel.BrewerId);
+
+ if (existingBeer != null)
+ return Conflict("A beer with this name already exists for this brewer");
+
+ beerModel.Brewer = existingBrewery;
+
+ _context.Beers.Add(beerModel);
+ await _context.SaveChangesAsync();
+
+ return CreatedAtAction("GetBeerModel", new { id = beerModel.BeerId }, beerModel);
+ }
+
+ // DELETE: api/Beer/5
+ [HttpDelete("{id}")]
+ public async Task DeleteBeerModel(int id)
+ {
+ if (_context.Beers == null)
+ {
+ return NotFound();
+ }
+ var beerModel = await _context.Beers.FindAsync(id);
+ if (beerModel == null)
+ {
+ return NotFound();
+ }
+
+ _context.Beers.Remove(beerModel);
+ await _context.SaveChangesAsync();
+
+ return NoContent();
+ }
+
+ private bool BeerModelExists(int id)
+ {
+ return (_context.Beers?.Any(b => b.BeerId == id)).GetValueOrDefault();
+ }
+ }
+}
diff --git a/BreweryAPI.Furiax/BreweryAPI/Controllers/QuotesController.cs b/BreweryAPI.Furiax/BreweryAPI/Controllers/QuotesController.cs
new file mode 100644
index 0000000..80f3863
--- /dev/null
+++ b/BreweryAPI.Furiax/BreweryAPI/Controllers/QuotesController.cs
@@ -0,0 +1,93 @@
+using BreweryAPI.Models;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+namespace BreweryAPI.Controllers
+{
+ [Route("api/[controller]")]
+ [ApiController]
+ public class QuotesController : ControllerBase
+ {
+ private readonly BreweryContext _context;
+
+ public QuotesController(BreweryContext context)
+ {
+ _context = context;
+ }
+
+ // POST: api/Quotes
+ [HttpPost]
+ public async Task> RequestQuote(QuoteModel quoteModel)
+ {
+
+ try
+ {
+ if (quoteModel == null)
+ {
+ return BadRequest("Order cannot be empty");
+ }
+
+ var wholesalerExisting = await _context.Wholesalers.FindAsync(quoteModel.WholeSalerId);
+ if (wholesalerExisting == null)
+ {
+ return NotFound("Wholesaler doesn't exist");
+ }
+
+ if(quoteModel.Orders.GroupBy(ord => ord.BeerId).Any(cnt => cnt.Count() >1))
+ {
+ return BadRequest("There can't be any duplicates in the order");
+ }
+
+ var output = new List();
+ decimal totalPrice = 0;
+ int totalQuantity = 0;
+ string summary = "";
+ int discount = 0;
+
+ foreach (var item in quoteModel.Orders)
+ {
+ var isBeerBeingSold = await _context.Sales.FirstOrDefaultAsync(s => s.BeerId == item.BeerId && s.WholesalerId == quoteModel.WholeSalerId);
+ if (isBeerBeingSold == null)
+ {
+ return NotFound("The wholesaler does not sell all items from your list");
+ }
+
+ var isStockHighEnough = await _context.Sales.FirstOrDefaultAsync(s => s.BeerId == item.BeerId && s.WholesalerId == quoteModel.WholeSalerId && s.Quantity > item.Quantity);
+ if (isStockHighEnough == null)
+ {
+ return NotFound("The wholesaler does not have enough stock to fullfill your order");
+ }
+ var beerInfo = await _context.Beers.FindAsync(item.BeerId);
+ totalPrice += beerInfo.Price * item.Quantity;
+ totalQuantity += item.Quantity;
+ summary += $"{beerInfo.Name} * {item.Quantity} = {beerInfo.Price * item.Quantity}\n";
+ }
+
+ if (totalQuantity >= 10)
+ {
+ if (totalQuantity >= 20)
+ {
+ discount = 20;
+ totalPrice -= (totalPrice * discount) / 100;
+ }
+ else
+ {
+ discount = 10;
+ totalPrice -= (totalPrice * discount) / 100;
+ }
+ summary += $"Discount: {discount}% \n";
+ }
+ summary += new string('-',20);
+ summary += $"\nTotal: {totalPrice}";
+ output.Add(new OrderModel { Price = totalPrice, Discount = discount, Quote = quoteModel, Summary = summary});
+
+
+ return CreatedAtAction(nameof(RequestQuote), output);
+ }
+ catch (Exception ex)
+ {
+ return BadRequest(ex.Message);
+ }
+ }
+ }
+}
diff --git a/BreweryAPI.Furiax/BreweryAPI/Controllers/SalesController.cs b/BreweryAPI.Furiax/BreweryAPI/Controllers/SalesController.cs
new file mode 100644
index 0000000..6573b7d
--- /dev/null
+++ b/BreweryAPI.Furiax/BreweryAPI/Controllers/SalesController.cs
@@ -0,0 +1,158 @@
+using BreweryAPI.Models;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+namespace BreweryAPI.Controllers
+{
+ [Route("Sales")]
+ [ApiController]
+ public class SalesController : ControllerBase
+ {
+ private readonly BreweryContext _context;
+
+ public SalesController(BreweryContext context)
+ {
+ _context = context;
+ }
+
+ // GET: api/Sales
+ [HttpGet]
+ public async Task>> GetSales()
+ {
+ if (_context.Sales == null)
+ {
+ return NotFound();
+ }
+ return await _context.Sales
+ .Include(w => w.Wholesaler)
+ .Include(b => b.Beer)
+ .Include(br => br.Beer.Brewer)
+ .ToListAsync();
+ }
+
+ // GET: api/Sales/5
+ [HttpGet("{id}")]
+ public async Task> GetSaleModel(int id)
+ {
+ if (_context.Sales == null)
+ {
+ return NotFound();
+ }
+ var saleModel = await _context.Sales
+ .Include(w => w.Wholesaler)
+ .Include(b => b.Beer)
+ .Include(br => br.Beer.Brewer)
+ .FirstOrDefaultAsync(s => s.SaleId == id);
+
+ if (saleModel == null)
+ {
+ return NotFound();
+ }
+
+ return saleModel;
+ }
+
+ // PUT: api/Sales/5
+ [HttpPut("{id}")]
+ public async Task PutSaleModel(int id, SaleModel updatedSaleModel)
+ {
+ if (id != updatedSaleModel.SaleId)
+ {
+ return BadRequest();
+ }
+
+ _context.Entry(updatedSaleModel).State = EntityState.Modified;
+
+ var existingWholesaler = await _context.Wholesalers.FindAsync(updatedSaleModel.WholesalerId);
+ if (existingWholesaler == null)
+ {
+ return NotFound("The wholesaler found with that id.");
+ }
+ updatedSaleModel.Wholesaler = existingWholesaler;
+
+ var existingBeer = await _context.Beers
+ .Include(br => br.Brewer)
+ .FirstOrDefaultAsync(b => b.BeerId == updatedSaleModel.BeerId);
+ if (existingBeer == null)
+ {
+ return NotFound("A beer with that id doesn't exist.");
+ }
+ updatedSaleModel.Beer = existingBeer;
+
+ _context.Entry(updatedSaleModel).State = EntityState.Modified;
+
+ try
+ {
+ await _context.SaveChangesAsync();
+ }
+ catch (DbUpdateConcurrencyException)
+ {
+ if (!SaleModelExists(id))
+ {
+ return NotFound();
+ }
+ else
+ {
+ throw;
+ }
+ }
+
+ return NoContent();
+ }
+
+ // POST: api/Sales
+ [HttpPost]
+ public async Task> PostSaleModel(SaleModel saleModel)
+ {
+ if (_context.Sales == null)
+ {
+ return Problem("Entity set 'BreweryContext.Sales' is null.");
+ }
+ var existingWholesaler = await _context.Wholesalers.FindAsync(saleModel.WholesalerId);
+ if (existingWholesaler == null)
+ {
+ return NotFound("No Wholesaler found with that id.");
+ }
+ saleModel.Wholesaler = existingWholesaler;
+
+ var existingBeer = await _context.Beers
+ .Include(br => br.Brewer)
+ .FirstOrDefaultAsync(b => b.BeerId == saleModel.BeerId);
+ if (existingBeer == null)
+ {
+ return NotFound("No beer found with that id.");
+ }
+ saleModel.Beer = existingBeer;
+
+ _context.Sales.Add(saleModel);
+ await _context.SaveChangesAsync();
+
+ return CreatedAtAction("GetSaleModel", new { id = saleModel.SaleId }, saleModel);
+ }
+
+ // DELETE: api/Sales/5
+ [HttpDelete("{id}")]
+ public async Task DeleteSaleModel(int id)
+ {
+ if (_context.Sales == null)
+ {
+ return NotFound();
+ }
+ var saleModel = await _context.Sales.FindAsync(id);
+ if (saleModel == null)
+ {
+ return NotFound();
+ }
+
+ _context.Sales.Remove(saleModel);
+ await _context.SaveChangesAsync();
+
+ return NoContent();
+ }
+
+ private bool SaleModelExists(int id)
+ {
+ return (_context.Sales?.Any(e => e.SaleId == id)).GetValueOrDefault();
+ }
+ }
+}
diff --git a/BreweryAPI.Furiax/BreweryAPI/Models/BeerModel.cs b/BreweryAPI.Furiax/BreweryAPI/Models/BeerModel.cs
new file mode 100644
index 0000000..95a2e7c
--- /dev/null
+++ b/BreweryAPI.Furiax/BreweryAPI/Models/BeerModel.cs
@@ -0,0 +1,19 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace BreweryAPI.Models
+{
+ public class BeerModel
+ {
+ [Key]
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int BeerId { get; set; }
+ [Required]
+ public string Name { get; set; }
+ [Required]
+ public decimal Price { get; set; }
+ [Required]
+ public int BrewerId { get; set; }
+ public BreweryModel Brewer { get; set; }
+ }
+}
diff --git a/BreweryAPI.Furiax/BreweryAPI/Models/BrewerModel.cs b/BreweryAPI.Furiax/BreweryAPI/Models/BrewerModel.cs
new file mode 100644
index 0000000..37e08ba
--- /dev/null
+++ b/BreweryAPI.Furiax/BreweryAPI/Models/BrewerModel.cs
@@ -0,0 +1,14 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace BreweryAPI.Models
+{
+ public class BreweryModel
+ {
+ [Key]
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int BreweryId { get; set; }
+ [Required]
+ public string Name { get; set; }
+ }
+}
diff --git a/BreweryAPI.Furiax/BreweryAPI/Models/OrderListModel.cs b/BreweryAPI.Furiax/BreweryAPI/Models/OrderListModel.cs
new file mode 100644
index 0000000..c76f2c0
--- /dev/null
+++ b/BreweryAPI.Furiax/BreweryAPI/Models/OrderListModel.cs
@@ -0,0 +1,10 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace BreweryAPI.Models
+{
+ public class OrderListModel
+ {
+ public int BeerId { get; set; }
+ public int Quantity { get; set; }
+ }
+}
diff --git a/BreweryAPI.Furiax/BreweryAPI/Models/OrderModel.cs b/BreweryAPI.Furiax/BreweryAPI/Models/OrderModel.cs
new file mode 100644
index 0000000..7966846
--- /dev/null
+++ b/BreweryAPI.Furiax/BreweryAPI/Models/OrderModel.cs
@@ -0,0 +1,11 @@
+namespace BreweryAPI.Models
+{
+ public class OrderModel
+ {
+ public decimal Price { get; set; }
+ public string Summary { get; set; }
+ public int? Discount { get; set; }
+ public QuoteModel Quote { get; set; }
+
+ }
+}
diff --git a/BreweryAPI.Furiax/BreweryAPI/Models/QuoteModel.cs b/BreweryAPI.Furiax/BreweryAPI/Models/QuoteModel.cs
new file mode 100644
index 0000000..f578019
--- /dev/null
+++ b/BreweryAPI.Furiax/BreweryAPI/Models/QuoteModel.cs
@@ -0,0 +1,9 @@
+namespace BreweryAPI.Models
+{
+ public class QuoteModel
+ {
+ public int WholeSalerId { get; set; }
+ public WholesalerModel WholeSaler { get; set; }
+ public List Orders { get; set; }
+ }
+}
diff --git a/BreweryAPI.Furiax/BreweryAPI/Models/SaleModel.cs b/BreweryAPI.Furiax/BreweryAPI/Models/SaleModel.cs
new file mode 100644
index 0000000..8d726e0
--- /dev/null
+++ b/BreweryAPI.Furiax/BreweryAPI/Models/SaleModel.cs
@@ -0,0 +1,20 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace BreweryAPI.Models
+{
+ public class SaleModel
+ {
+ [Key]
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int SaleId { get; set; }
+ [Required]
+ public int WholesalerId { get; set; }
+ public WholesalerModel Wholesaler { get; set; }
+ [Required]
+ public int BeerId { get; set; }
+ public BeerModel Beer { get; set; }
+ [Required]
+ public int Quantity { get; set; }
+ }
+}
diff --git a/BreweryAPI.Furiax/BreweryAPI/Models/WholesalerModel.cs b/BreweryAPI.Furiax/BreweryAPI/Models/WholesalerModel.cs
new file mode 100644
index 0000000..ba6c569
--- /dev/null
+++ b/BreweryAPI.Furiax/BreweryAPI/Models/WholesalerModel.cs
@@ -0,0 +1,14 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace BreweryAPI.Models
+{
+ public class WholesalerModel
+ {
+ [Key]
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int WholesalerId { get; set; }
+ [Required]
+ public string Name { get; set; }
+ }
+}
diff --git a/BreweryAPI.Furiax/BreweryAPI/Program.cs b/BreweryAPI.Furiax/BreweryAPI/Program.cs
new file mode 100644
index 0000000..fea3725
--- /dev/null
+++ b/BreweryAPI.Furiax/BreweryAPI/Program.cs
@@ -0,0 +1,38 @@
+using BreweryAPI;
+using Microsoft.EntityFrameworkCore;
+
+var builder = WebApplication.CreateBuilder(args);
+
+// Add services to the container.
+
+builder.Services.AddControllers();
+builder.Services.AddDbContext(opt =>
+ opt.UseSqlServer(builder.Configuration.GetConnectionString("SqlServer")), ServiceLifetime.Scoped);
+
+// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
+builder.Services.AddEndpointsApiExplorer();
+builder.Services.AddSwaggerGen();
+
+var app = builder.Build();
+
+// Configure the HTTP request pipeline.
+if (app.Environment.IsDevelopment())
+{
+ app.UseSwagger();
+ app.UseSwaggerUI();
+}
+
+app.UseHttpsRedirection();
+
+app.UseAuthorization();
+
+using (var scope = app.Services.CreateScope())
+{
+ var dbContext = scope.ServiceProvider.GetRequiredService();
+ dbContext.DeleteDatabase();
+ dbContext.CreateDatabase();
+}
+
+app.MapControllers();
+
+app.Run();
diff --git a/BreweryAPI.Furiax/BreweryAPI/Properties/launchSettings.json b/BreweryAPI.Furiax/BreweryAPI/Properties/launchSettings.json
new file mode 100644
index 0000000..13bdff5
--- /dev/null
+++ b/BreweryAPI.Furiax/BreweryAPI/Properties/launchSettings.json
@@ -0,0 +1,41 @@
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:52884",
+ "sslPort": 44332
+ }
+ },
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "http://localhost:5210",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "https://localhost:7127;http://localhost:5210",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/BreweryAPI.Furiax/BreweryAPI/appsettings.Development.json b/BreweryAPI.Furiax/BreweryAPI/appsettings.Development.json
new file mode 100644
index 0000000..0c208ae
--- /dev/null
+++ b/BreweryAPI.Furiax/BreweryAPI/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/BreweryAPI.Furiax/BreweryAPI/appsettings.json b/BreweryAPI.Furiax/BreweryAPI/appsettings.json
new file mode 100644
index 0000000..aa0bf86
--- /dev/null
+++ b/BreweryAPI.Furiax/BreweryAPI/appsettings.json
@@ -0,0 +1,12 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*",
+ "ConnectionStrings": {
+ "SqlServer": "Data Source=(localdb)\\MSSQLLocalDB;Database=BreweryAPI;Integrated Security=True;Encrypt=False;Trust Server Certificate=False"
+ }
+}
diff --git a/BreweryAPI.Furiax/ReadMe.md b/BreweryAPI.Furiax/ReadMe.md
new file mode 100644
index 0000000..b217e02
--- /dev/null
+++ b/BreweryAPI.Furiax/ReadMe.md
@@ -0,0 +1,43 @@
+ Brewery API
+
+ About the app
+I created this API as a solution to the following challenge:
+-List all beers by brewery
+-A brewer can add, delete and update beers
+-Add the sale of an existing beer to an existing wholesaler
+-Upon a sale, the quantity of a beer needs to be incremented in the wholesaler's inventory
+-A client can request a quote from a wholesaler.
+-If successful, the quote returns a price and a summary of the quote. A 10% discount is applied for orders above 10 units. A 20% discount is applied for orders above 20 drinks.
+-If there is an error, it returns an exception and a message to explain the reason
+-A brewer brews one or several beers
+-A beer is always linked to a brewer
+-A beer can be sold by several wholesalers
+-A wholesaler sells a defined list of beers, from any brewer, and has only a limited stock of those beers
+-The beers sold by the wholesaler have a fixed price imposed by the brewery
+
+For this assessment the database is pre-filled by you, no front-end is needed, just the API.
+Use REST architecture, use Entity Framework, no migrations are needed, use Ensure Deleted and Ensure Created.
+How to use
+This API exists out of 3 parts(Beer,Sales,Quotes), each part has his uses for the diffrent users(Brewer,Wholesaler,Customer).
+Beer:
+The POST/PUT/DELETE methods are only ment for the Brewer. With these methods he can add, update or delete beers from the database.
+The diffrent GET methods can be used by all users. The normal GET method displays a list of all beers from the database while the GET/Beer{Id} only shows one specific beer and the GET/Beer/BeersByBrewer/{brewerId} only displays all beers from a specified brewer.
+Sales:
+Here the wholesaler puts up his sales by adding, adjusting or removing them to the database with the POST/PUT or DELETE methods.
+The GET method gives an overview of all products that are on sale at all the diffrent wholesalers.
+It's also possible to see one specific sale by using the GET/Sales/{id} method
+Quotes:
+Quotes only has a single POST method that will be used by thecustomer
+Customers can send a orderlist, containing one or more diffrent beers and there quantity's, to there desired wholesaler.
+The POST method will then check if the selected wholesaler has the stock to fullfill this order. If so, a summuary with all products and total price is calculated and returned. Discounts are also applied when requirements are met. If the wholesaler doesn't have enough stock or if there is something wrong with the orderlist the customer gets a corresponding message back.
+
+How did I experience this challenge
+This was only the second API I created, and this one was far more complex than the first one. So I learned alot of new stuff from it.
+I started this challenge by first thinking about how many and which tables I would need and then draw them out. I needed a model for beer, brewery, wholesaler, sale. Those were easy, the tables for orders and quotes were a bit more tricky as they contained relationships with multiple other models. I even had to adjust them a bit when I was actually working with them.
+Once my models were finished I started the context page so I could make a connection to my database. This also took some time to figure out since I never used Ensure Create/ Ensure Delete before. But again with some Googling and some chatGPT I got it up and running.
+Once my database was running it was time to seed it with data, again something I hadn't done before. So with some Googling I found out an example of putting my seed data into the context page. So I did, altough now I'm writing this I'm not so sure anymore this is the right place for it.
+Now that my database was running and filled with data is was time to work on the different CRUD commands. I first finished the Beer one and then added Sales and Quotes as last. The Beer and Sales API commands were pretty straight forward and I experienced little to no problems creating them. Quotes was a bit harder to implement since there were some more conditions to be accounted for. You had to check the stock of the wholesaler, get the prices to be displayed, the discounts to be calculated, and so on. But I managed. :)
+
+One of the extra challenges was to add Unit Tests to the project and create an front-end, but at the time I made the API I wasn't far enough in my progress to do these. The unit tests chapter is the next one coming up so perhaps I add these later on.
+
+