diff --git a/.vs/JafnaEcommerceApi/v17/.suo b/.vs/JafnaEcommerceApi/v17/.suo new file mode 100644 index 00000000..7ab98d57 Binary files /dev/null and b/.vs/JafnaEcommerceApi/v17/.suo differ diff --git a/.vs/JafnaEcommerceApi/v17/DocumentLayout.json b/.vs/JafnaEcommerceApi/v17/DocumentLayout.json new file mode 100644 index 00000000..97ce754f --- /dev/null +++ b/.vs/JafnaEcommerceApi/v17/DocumentLayout.json @@ -0,0 +1,27 @@ +{ + "Version": 1, + "WorkspaceRootPath": "D:\\projects\\WEB API APPS\\Development\\code review\\CodeReviews.Console.EcommerceApi\\", + "Documents": [], + "DocumentGroupContainers": [ + { + "Orientation": 0, + "VerticalTabListWidth": 256, + "DocumentGroups": [ + { + "DockedWidth": 200, + "SelectedChildIndex": -1, + "Children": [ + { + "$type": "Bookmark", + "Name": "ST:128:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}" + }, + { + "$type": "Bookmark", + "Name": "ST:128:0:{1fc202d4-d401-403c-9834-5b218574bb67}" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/freddypositive.ecomerceapi/.gitattributes b/freddypositive.ecomerceapi/.gitattributes new file mode 100644 index 00000000..1ff0c423 --- /dev/null +++ b/freddypositive.ecomerceapi/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/freddypositive.ecomerceapi/.gitignore similarity index 100% rename from .gitignore rename to freddypositive.ecomerceapi/.gitignore diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi.sln b/freddypositive.ecomerceapi/JafnaEcommerceApi.sln new file mode 100644 index 00000000..15d4bde0 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35818.85 d17.13 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JafnaEcommerceApi", "JafnaEcommerceApi\JafnaEcommerceApi.csproj", "{533FB58A-287D-40BE-99DD-2C0A7C3AE478}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {533FB58A-287D-40BE-99DD-2C0A7C3AE478}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {533FB58A-287D-40BE-99DD-2C0A7C3AE478}.Debug|Any CPU.Build.0 = Debug|Any CPU + {533FB58A-287D-40BE-99DD-2C0A7C3AE478}.Release|Any CPU.ActiveCfg = Release|Any CPU + {533FB58A-287D-40BE-99DD-2C0A7C3AE478}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5E52E7E8-BBD8-454F-93EB-AEF0C371194E} + EndGlobalSection +EndGlobal diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/AutoMapper/CategoryProfile.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/AutoMapper/CategoryProfile.cs new file mode 100644 index 00000000..eaa7ae9b --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/AutoMapper/CategoryProfile.cs @@ -0,0 +1,18 @@ +using AutoMapper; +using JafnaEcommerceApi.Models.DTOs.CategoryDTOs; +using JafnaEcommerceApi.Models.Entities; + +namespace JafnaEcommerceApi.AutoMapper; + +public class CategoryProfile : Profile +{ + public CategoryProfile() + { + CreateMap(); + CreateMap(); + CreateMap() + .ForAllMembers(opt => + opt.Condition((src, dest, srcMember) => + srcMember != null && !(srcMember is string s && string.IsNullOrWhiteSpace(s)))); ; + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/AutoMapper/ProductProfile.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/AutoMapper/ProductProfile.cs new file mode 100644 index 00000000..94ce913f --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/AutoMapper/ProductProfile.cs @@ -0,0 +1,18 @@ +using AutoMapper; +using JafnaEcommerceApi.Models.DTOs.ProductDTOs; +using JafnaEcommerceApi.Models.Entities; + +namespace JafnaEcommerceApi.AutoMapper; + +public class ProductProfile : Profile +{ + public ProductProfile() + { + CreateMap(); + CreateMap(); + CreateMap() + .ForAllMembers(opt => + opt.Condition((src, dest, srcMember) => + srcMember != null && !(srcMember is string s && string.IsNullOrWhiteSpace(s)))); + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/AutoMapper/SaleDetailProfile.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/AutoMapper/SaleDetailProfile.cs new file mode 100644 index 00000000..cf91a88b --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/AutoMapper/SaleDetailProfile.cs @@ -0,0 +1,15 @@ +using AutoMapper; +using JafnaEcommerceApi.Models.DTOs.SaleDTOs; +using JafnaEcommerceApi.Models.Entities; +namespace JafnaEcommerceApi.AutoMapper; + +public class SaleDetailProfile : Profile +{ + public SaleDetailProfile() + { + CreateMap(); + + + CreateMap(); + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/AutoMapper/SaleProfile.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/AutoMapper/SaleProfile.cs new file mode 100644 index 00000000..b5d6ff2e --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/AutoMapper/SaleProfile.cs @@ -0,0 +1,15 @@ +using AutoMapper; +using JafnaEcommerceApi.Models.DTOs.SaleDTOs; +using JafnaEcommerceApi.Models.Entities; + +namespace JafnaEcommerceApi.AutoMapper; + +public class SaleProfile : Profile +{ + public SaleProfile() + { + CreateMap(); + + } + +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Controllers/CategoryController.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Controllers/CategoryController.cs new file mode 100644 index 00000000..8d69097d --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Controllers/CategoryController.cs @@ -0,0 +1,50 @@ +using JafnaEcommerceApi.Models.DTOs.CategoryDTOs; +using JafnaEcommerceApi.Services; +using Microsoft.AspNetCore.Mvc; +using JafnaEcommerceApi.Models.DTOs.APIResponse; +using JafnaEcommerceApi.Models.DTOs.PaginationDTOs; + +namespace JafnaEcommerceApi.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class CategoryController : ControllerBase +{ + private readonly ICategoryService _categoryService; + + public CategoryController(ICategoryService categoryService) + { + _categoryService = categoryService; + } + + [HttpPost] + public async Task CreateAsync([FromBody] CategoryCreateDto categoryCreateDto) + { + await _categoryService.Create(categoryCreateDto); + return Ok(ApiResponse.Success(null, "Category created successfully")); + } + + [HttpPut("{categoryId}")] + public async Task UpdateAsync([FromRoute] int categoryId, [FromBody] CategoryUpdateDto categoryUpdateDto) + { + await _categoryService.Update(categoryId, categoryUpdateDto); + return Ok(ApiResponse.Success(null, "Category updated successfully")); + + } + + [HttpDelete("{categoryId}")] + public async Task DeleteAsync([FromRoute]int categoryId) + { + await _categoryService.Delete(categoryId); + return Ok(ApiResponse.Success(null, "Category deleted successfully")); + } + + [HttpGet] + public async Task GetAllAsync([FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 10) + { + var categoryList = await _categoryService.GetAll(pageNumber, pageSize); + return Ok(ApiResponse>.Success(categoryList, "Categories obtained successfully")); + } + + +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Controllers/ProductController.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Controllers/ProductController.cs new file mode 100644 index 00000000..e8d8d9fe --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Controllers/ProductController.cs @@ -0,0 +1,55 @@ +using JafnaEcommerceApi.Models.DTOs.APIResponse; +using JafnaEcommerceApi.Models.DTOs.PaginationDTOs; +using JafnaEcommerceApi.Models.DTOs.ProductDTOs; +using JafnaEcommerceApi.Services; +using Microsoft.AspNetCore.Mvc; + +namespace JafnaEcommerceApi.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class ProductController : ControllerBase +{ + private readonly IProductService _productService; + + public ProductController(IProductService productService) + { + _productService = productService; + } + + [HttpPost] + public async Task CreateAsync([FromBody] ProductCreateDto productCreateDto) + { + await _productService.CreateAsync(productCreateDto); + return Ok(ApiResponse.Success(null, "Product created successfully")); + } + + [HttpPut("{productId}")] + public async Task UpdateAsync([FromRoute] int productId, [FromBody] ProductUpdateDto productUpdateDto) + { + await _productService.UpdateAsync(productId, productUpdateDto); + return Ok(ApiResponse.Success(null, "Product updated successfully.")); + + } + + [HttpDelete("{productId}")] + public async Task DeleteAsync([FromRoute] int productId) + { + await _productService.DeleteAsync(productId); + return Ok(ApiResponse.Success(null, "Product deleted successfully.")); + } + + [HttpGet] + public async Task GetAllAsync([FromQuery] int pageNumber = 1,[FromQuery] int pageSize = 10) + { + var productList = await _productService.GetAllAsync(pageNumber, pageSize); + return Ok(ApiResponse>.Success(productList, "Products obtained successfully")); + } + + [HttpGet("{categoryId}")] + public async Task GetProductsByCategoryAsync([FromRoute] int categoryId, [FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 10) + { + var productsList = await _productService.GetProductsByCategoryAsync(categoryId, pageNumber, pageSize); + return Ok(ApiResponse>.Success(productsList, "Products obtained successfully")); + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Controllers/SalesController.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Controllers/SalesController.cs new file mode 100644 index 00000000..ed956171 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Controllers/SalesController.cs @@ -0,0 +1,40 @@ +using JafnaEcommerceApi.Models.DTOs.SaleDTOs; +using JafnaEcommerceApi.Services; +using Microsoft.AspNetCore.Mvc; +using JafnaEcommerceApi.Models.DTOs.APIResponse; +using JafnaEcommerceApi.Models.DTOs.PaginationDTOs; + +namespace JafnaEcommerceApi.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class SalesController : ControllerBase +{ + private readonly ISaleService _saleService; + + public SalesController(ISaleService saleService) + { + _saleService = saleService; + } + + [HttpPost] + public async Task CreateAsync([FromBody] SaleCreateDto saleCreateDto) + { + await _saleService.CreateAsync(saleCreateDto); + return Ok(ApiResponse.Success(null, "Sale created successfully")); + } + + [HttpGet] + public async Task GetAllSaleAsync([FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 10) + { + var saleList = await _saleService.GetAllSaleAsync(pageNumber, pageSize); + return Ok(ApiResponse>.Success(saleList, "Sale obtained successfully")); + } + + [HttpGet("{saleId}/details")] + public async Task GetSaleDetailsAsync([FromRoute] int saleId, [FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 10) + { + var detailsList = await _saleService.GetSaleDetailsAsync(saleId, pageNumber, pageSize); + return Ok(ApiResponse>.Success(detailsList, "Sale details obtained successfully")); + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Data/JafnaDbContext.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Data/JafnaDbContext.cs new file mode 100644 index 00000000..33d196c0 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Data/JafnaDbContext.cs @@ -0,0 +1,145 @@ +using JafnaEcommerceApi.Models.Entities; +using Microsoft.EntityFrameworkCore; + +namespace JafnaEcommerceApi.Data; + +public class JafnaDbContext : DbContext +{ + public DbSet category { get; set; } + public DbSet products { get; set; } + public DbSet sale { get; set; } + public DbSet sale_details { get; set; } + + public JafnaDbContext(DbContextOptions options) : base(options) + { } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + // ---------------- CATEGORY → PRODUCTS (1 to Many) ---------------- + modelBuilder.Entity() + .HasMany(s => s.Products) + .WithOne(c => c.Category) + .HasForeignKey(p => p.CategoryId) + .OnDelete(DeleteBehavior.Restrict); + + // ---------------- PRODUCT → SALEDETAILS (1 to Many) ---------------- + modelBuilder.Entity() + .HasMany(s => s.SaleDetails) + .WithOne(sd => sd.Product) + .HasForeignKey(sd => sd.ProductId) + .OnDelete(DeleteBehavior.Restrict); + + // ---------------- SALE → SALEDETAILS (1 to Many) ------------------- + modelBuilder.Entity() + .HasMany(s => s.SaleDetails) + .WithOne(d => d.Sale) + .HasForeignKey(sd => sd.SaleId) + .OnDelete(DeleteBehavior.Restrict); + + modelBuilder.Entity() + .Property(s => s.CreatedDate) + .HasDefaultValueSql("GETDATE()"); + + modelBuilder.Entity() + .Property(s => s.CreatedDate) + .HasDefaultValueSql("GETDATE()"); + + modelBuilder.Entity() + .Property(s => s.DeletedDate) + .HasDefaultValueSql("GETDATE()"); + + modelBuilder.Entity() + .Property(s => s.DeletedDate) + .HasDefaultValueSql("GETDATE()"); + + modelBuilder.Entity() + .Property(s => s.CreatedDate) + .HasDefaultValueSql("GETDATE()"); + + modelBuilder.Entity() + .Property(s => s.CreatedDate) + .HasDefaultValueSql("GETDATE()"); + modelBuilder.Entity().HasData( + new Category + { + Id = 1, + Name = "Fish", + CreatedDate = DateTime.UtcNow, + IsDeleted = false + }, + new Category + { + Id = 2, + Name = "Plants", + CreatedDate = DateTime.UtcNow, + IsDeleted = false + }, + new Category + { + Id = 3, + Name = "Aquarium Supplies", + CreatedDate = DateTime.UtcNow, + IsDeleted = false + } + ); + modelBuilder.Entity().HasData( + new Product + { + Id = 1, + Name = "Goldfish", + Description = "Beautiful hardy goldfish", + Price = 100, + Image = "goldfish.jpg", + CategoryId = 1, + CreatedDate = DateTime.UtcNow, + IsDeleted = false + }, + new Product + { + Id = 2, + Name = "Amazon Sword", + Description = "Popular aquarium plant", + Price = 150, + Image = "amazonsword.jpg", + CategoryId = 2, + CreatedDate = DateTime.UtcNow, + IsDeleted = false + }, + new Product + { + Id = 3, + Name = "Internal Filter", + Description = "Small internal filter", + Price = 500, + Image = "filter.jpg", + CategoryId = 3, + CreatedDate = DateTime.UtcNow, + IsDeleted = false + } + ); + modelBuilder.Entity().HasData( + new Sale + { + Id = 1, + TotalPrice = 200, + CreatedDate = DateTime.UtcNow + } + ); + modelBuilder.Entity().HasData( + new SaleDetail + { + Id = 1, + SaleId = 1, + ProductId = 1, + ProductName = "Goldfish", // REQUIRED NOW + ProductQuantity = 2, + ProductPrice = 100, + ProductTotalPrice = 200, + CreatedDate = DateTime.UtcNow + } + ); + + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Exceptions/AppException.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Exceptions/AppException.cs new file mode 100644 index 00000000..2b30c79f --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Exceptions/AppException.cs @@ -0,0 +1,12 @@ +namespace JafnaEcommerceApi.Exceptions; + +public abstract class AppException : Exception +{ + public int StatusCode { get; } + + protected AppException(string message, int statusCode) + : base(message) + { + StatusCode = statusCode; + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Exceptions/ConflictException.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Exceptions/ConflictException.cs new file mode 100644 index 00000000..5a3bd5ce --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Exceptions/ConflictException.cs @@ -0,0 +1,7 @@ +namespace JafnaEcommerceApi.Exceptions; + +public class ConflictException : AppException +{ + public ConflictException(string message) + : base(message, StatusCodes.Status409Conflict) { } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Exceptions/NotFoundException.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Exceptions/NotFoundException.cs new file mode 100644 index 00000000..1ed32744 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Exceptions/NotFoundException.cs @@ -0,0 +1,7 @@ +namespace JafnaEcommerceApi.Exceptions; + +public class NotFoundException : AppException +{ + public NotFoundException(string message) + : base(message, StatusCodes.Status404NotFound) { } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Exceptions/UnautorizedException.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Exceptions/UnautorizedException.cs new file mode 100644 index 00000000..59ca0df5 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Exceptions/UnautorizedException.cs @@ -0,0 +1,7 @@ +namespace JafnaEcommerceApi.Exceptions; + +public class UnauthorizedException : AppException +{ + public UnauthorizedException(string message) + : base(message, StatusCodes.Status401Unauthorized) { } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Exceptions/ValidationException.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Exceptions/ValidationException.cs new file mode 100644 index 00000000..73a22f18 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Exceptions/ValidationException.cs @@ -0,0 +1,7 @@ +namespace JafnaEcommerceApi.Exceptions; + +public class ValidationException : AppException +{ + public ValidationException(string message) + : base(message, StatusCodes.Status400BadRequest) { } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/JafnaEcommerceApi.csproj b/freddypositive.ecomerceapi/JafnaEcommerceApi/JafnaEcommerceApi.csproj new file mode 100644 index 00000000..9e5cb07f --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/JafnaEcommerceApi.csproj @@ -0,0 +1,27 @@ + + + + net8.0 + enable + enable + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/JafnaEcommerceApi.http b/freddypositive.ecomerceapi/JafnaEcommerceApi/JafnaEcommerceApi.http new file mode 100644 index 00000000..fd2e3cb0 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/JafnaEcommerceApi.http @@ -0,0 +1,6 @@ +@JafnaEcommerceApi_HostAddress = http://localhost:5083 + +GET {{JafnaEcommerceApi_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Middleware/ExceptionMiddleware.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Middleware/ExceptionMiddleware.cs new file mode 100644 index 00000000..ddf60833 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Middleware/ExceptionMiddleware.cs @@ -0,0 +1,64 @@ +using JafnaEcommerceApi.Exceptions; +using Newtonsoft.Json; +using JafnaEcommerceApi.Models.DTOs.APIResponse; + +namespace JafnaEcommerceApi.Middleware; + +public class ExceptionMiddleware +{ + private readonly RequestDelegate _next; + private readonly ILogger _logger; + private readonly IWebHostEnvironment _env; + + public ExceptionMiddleware(RequestDelegate next, ILogger logger, IWebHostEnvironment env) + { + _next = next; + _logger = logger; + _env = env; + } + public async Task InvokeAsync(HttpContext context) + { + try + { + await _next(context); + } + catch (Exception ex) + { + await HandleExceptionAsync(context, ex); + } + } + + private async Task HandleExceptionAsync(HttpContext context, Exception ex) + { + int statusCode = 500; + string message = "An unexpected error occurred."; + + if (ex is AppException appEx) + { + statusCode = appEx.StatusCode; + message = appEx.Message; + } + + if (Environment.GetEnvironmentVariable("ENABLE_LOGGING") == "true" + || context.RequestServices.GetService().IsDevelopment()) + { + _logger.LogError(ex, + "Error occurred on {Method} {Path}. StatusCode: {StatusCode}", + context.Request.Method, + context.Request.Path, + statusCode); + } + + context.Response.ContentType = "application/json"; + context.Response.StatusCode = statusCode; + + var response = new ApiResponse + { + success = false, + message = message, + errors = new List { message } // Or add stack trace if needed + }; + + await context.Response.WriteAsync(JsonConvert.SerializeObject(response)); + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251123103726_InitialScoolDB.Designer.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251123103726_InitialScoolDB.Designer.cs new file mode 100644 index 00000000..3f42bc0d --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251123103726_InitialScoolDB.Designer.cs @@ -0,0 +1,205 @@ +// +using System; +using JafnaEcommerceApi.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace JafnaEcommerceApi.Migrations +{ + [DbContext(typeof(JafnaDbContext))] + [Migration("20251123103726_InitialScoolDB")] + partial class InitialScoolDB + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.22") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Category", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("created_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("deleted_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("is_deleted") + .HasColumnType("bit"); + + b.Property("name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("id"); + + b.ToTable("category"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Product", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("category_id") + .HasColumnType("int"); + + b.Property("created_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("deleted_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("image") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("is_deleted") + .HasColumnType("bit"); + + b.Property("name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("price") + .HasColumnType("decimal(10,2)"); + + b.HasKey("id"); + + b.HasIndex("category_id"); + + b.ToTable("products"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Sale", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("created_date") + .HasColumnType("datetime2"); + + b.Property("total_price") + .HasColumnType("decimal(10,2)"); + + b.HasKey("id"); + + b.ToTable("sale"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.SaleDetail", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("created_date") + .HasColumnType("datetime2"); + + b.Property("product_id") + .HasColumnType("int"); + + b.Property("product_price") + .HasColumnType("decimal(10,2)"); + + b.Property("product_quantity") + .HasColumnType("int"); + + b.Property("product_total_price") + .HasColumnType("decimal(10,2)"); + + b.Property("sale_id") + .HasColumnType("int"); + + b.HasKey("id"); + + b.HasIndex("product_id"); + + b.HasIndex("sale_id"); + + b.ToTable("sale_details"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Product", b => + { + b.HasOne("JafnaEcommerceApi.Models.Entities.Category", "category") + .WithMany("Products") + .HasForeignKey("category_id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("category"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.SaleDetail", b => + { + b.HasOne("JafnaEcommerceApi.Models.Entities.Product", "Product") + .WithMany("SaleDetails") + .HasForeignKey("product_id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("JafnaEcommerceApi.Models.Entities.Sale", "Sale") + .WithMany("SaleDetails") + .HasForeignKey("sale_id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Sale"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Category", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Product", b => + { + b.Navigation("SaleDetails"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Sale", b => + { + b.Navigation("SaleDetails"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251123103726_InitialScoolDB.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251123103726_InitialScoolDB.cs new file mode 100644 index 00000000..07475360 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251123103726_InitialScoolDB.cs @@ -0,0 +1,131 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace JafnaEcommerceApi.Migrations +{ + /// + public partial class InitialScoolDB : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "category", + columns: table => new + { + id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + name = table.Column(type: "nvarchar(max)", nullable: false), + created_date = table.Column(type: "datetime2", nullable: false, defaultValueSql: "GETDATE()"), + is_deleted = table.Column(type: "bit", nullable: false), + deleted_date = table.Column(type: "datetime2", nullable: true, defaultValueSql: "GETDATE()") + }, + constraints: table => + { + table.PrimaryKey("PK_category", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "sale", + columns: table => new + { + id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + total_price = table.Column(type: "decimal(10,2)", nullable: false), + created_date = table.Column(type: "datetime2", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_sale", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "products", + columns: table => new + { + id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + name = table.Column(type: "nvarchar(max)", nullable: false), + description = table.Column(type: "nvarchar(max)", nullable: false), + price = table.Column(type: "decimal(10,2)", nullable: false), + image = table.Column(type: "nvarchar(max)", nullable: false), + category_id = table.Column(type: "int", nullable: false), + created_date = table.Column(type: "datetime2", nullable: false, defaultValueSql: "GETDATE()"), + is_deleted = table.Column(type: "bit", nullable: false), + deleted_date = table.Column(type: "datetime2", nullable: true, defaultValueSql: "GETDATE()") + }, + constraints: table => + { + table.PrimaryKey("PK_products", x => x.id); + table.ForeignKey( + name: "FK_products_category_category_id", + column: x => x.category_id, + principalTable: "category", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "sale_details", + columns: table => new + { + id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + product_id = table.Column(type: "int", nullable: false), + sale_id = table.Column(type: "int", nullable: false), + product_quantity = table.Column(type: "int", nullable: false), + product_price = table.Column(type: "decimal(10,2)", nullable: false), + product_total_price = table.Column(type: "decimal(10,2)", nullable: false), + created_date = table.Column(type: "datetime2", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_sale_details", x => x.id); + table.ForeignKey( + name: "FK_sale_details_products_product_id", + column: x => x.product_id, + principalTable: "products", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_sale_details_sale_sale_id", + column: x => x.sale_id, + principalTable: "sale", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_products_category_id", + table: "products", + column: "category_id"); + + migrationBuilder.CreateIndex( + name: "IX_sale_details_product_id", + table: "sale_details", + column: "product_id"); + + migrationBuilder.CreateIndex( + name: "IX_sale_details_sale_id", + table: "sale_details", + column: "sale_id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "sale_details"); + + migrationBuilder.DropTable( + name: "products"); + + migrationBuilder.DropTable( + name: "sale"); + + migrationBuilder.DropTable( + name: "category"); + } + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251124122953_SeedData.Designer.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251124122953_SeedData.Designer.cs new file mode 100644 index 00000000..ebe31750 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251124122953_SeedData.Designer.cs @@ -0,0 +1,288 @@ +// +using System; +using JafnaEcommerceApi.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace JafnaEcommerceApi.Migrations +{ + [DbContext(typeof(JafnaDbContext))] + [Migration("20251124122953_SeedData")] + partial class SeedData + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.22") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Category", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("created_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("deleted_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("is_deleted") + .HasColumnType("bit"); + + b.Property("name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("id"); + + b.ToTable("category"); + + b.HasData( + new + { + id = 1, + created_date = new DateTime(2025, 11, 24, 12, 29, 52, 483, DateTimeKind.Utc).AddTicks(9784), + is_deleted = false, + name = "Fish" + }, + new + { + id = 2, + created_date = new DateTime(2025, 11, 24, 12, 29, 52, 483, DateTimeKind.Utc).AddTicks(9788), + is_deleted = false, + name = "Plants" + }, + new + { + id = 3, + created_date = new DateTime(2025, 11, 24, 12, 29, 52, 483, DateTimeKind.Utc).AddTicks(9790), + is_deleted = false, + name = "Aquarium Supplies" + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Product", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("category_id") + .HasColumnType("int"); + + b.Property("created_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("deleted_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("image") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("is_deleted") + .HasColumnType("bit"); + + b.Property("name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("price") + .HasColumnType("decimal(10,2)"); + + b.HasKey("id"); + + b.HasIndex("category_id"); + + b.ToTable("products"); + + b.HasData( + new + { + id = 1, + category_id = 1, + created_date = new DateTime(2025, 11, 24, 12, 29, 52, 484, DateTimeKind.Utc).AddTicks(114), + description = "Beautiful hardy goldfish", + image = "goldfish.jpg", + is_deleted = false, + name = "Goldfish", + price = 100m + }, + new + { + id = 2, + category_id = 2, + created_date = new DateTime(2025, 11, 24, 12, 29, 52, 484, DateTimeKind.Utc).AddTicks(118), + description = "Popular aquarium plant", + image = "amazonsword.jpg", + is_deleted = false, + name = "Amazon Sword", + price = 150m + }, + new + { + id = 3, + category_id = 3, + created_date = new DateTime(2025, 11, 24, 12, 29, 52, 484, DateTimeKind.Utc).AddTicks(120), + description = "Small internal filter", + image = "filter.jpg", + is_deleted = false, + name = "Internal Filter", + price = 500m + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Sale", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("created_date") + .HasColumnType("datetime2"); + + b.Property("total_price") + .HasColumnType("decimal(10,2)"); + + b.HasKey("id"); + + b.ToTable("sale"); + + b.HasData( + new + { + id = 1, + created_date = new DateTime(2025, 11, 24, 12, 29, 52, 484, DateTimeKind.Utc).AddTicks(153), + total_price = 200m + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.SaleDetail", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("created_date") + .HasColumnType("datetime2"); + + b.Property("product_id") + .HasColumnType("int"); + + b.Property("product_name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("product_price") + .HasColumnType("decimal(10,2)"); + + b.Property("product_quantity") + .HasColumnType("int"); + + b.Property("product_total_price") + .HasColumnType("decimal(10,2)"); + + b.Property("sale_id") + .HasColumnType("int"); + + b.HasKey("id"); + + b.HasIndex("product_id"); + + b.HasIndex("sale_id"); + + b.ToTable("sale_details"); + + b.HasData( + new + { + id = 1, + created_date = new DateTime(2025, 11, 24, 12, 29, 52, 484, DateTimeKind.Utc).AddTicks(183), + product_id = 1, + product_name = "Goldfish", + product_price = 100m, + product_quantity = 2, + product_total_price = 200m, + sale_id = 1 + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Product", b => + { + b.HasOne("JafnaEcommerceApi.Models.Entities.Category", "category") + .WithMany("Products") + .HasForeignKey("category_id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("category"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.SaleDetail", b => + { + b.HasOne("JafnaEcommerceApi.Models.Entities.Product", "Product") + .WithMany("SaleDetails") + .HasForeignKey("product_id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("JafnaEcommerceApi.Models.Entities.Sale", "Sale") + .WithMany("SaleDetails") + .HasForeignKey("sale_id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Sale"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Category", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Product", b => + { + b.Navigation("SaleDetails"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Sale", b => + { + b.Navigation("SaleDetails"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251124122953_SeedData.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251124122953_SeedData.cs new file mode 100644 index 00000000..fad4d348 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251124122953_SeedData.cs @@ -0,0 +1,101 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace JafnaEcommerceApi.Migrations +{ + /// + public partial class SeedData : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "product_name", + table: "sale_details", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + + migrationBuilder.InsertData( + table: "category", + columns: new[] { "id", "created_date", "is_deleted", "name" }, + values: new object[,] + { + { 1, new DateTime(2025, 11, 24, 12, 29, 52, 483, DateTimeKind.Utc).AddTicks(9784), false, "Fish" }, + { 2, new DateTime(2025, 11, 24, 12, 29, 52, 483, DateTimeKind.Utc).AddTicks(9788), false, "Plants" }, + { 3, new DateTime(2025, 11, 24, 12, 29, 52, 483, DateTimeKind.Utc).AddTicks(9790), false, "Aquarium Supplies" } + }); + + migrationBuilder.InsertData( + table: "sale", + columns: new[] { "id", "created_date", "total_price" }, + values: new object[] { 1, new DateTime(2025, 11, 24, 12, 29, 52, 484, DateTimeKind.Utc).AddTicks(153), 200m }); + + migrationBuilder.InsertData( + table: "products", + columns: new[] { "id", "category_id", "created_date", "description", "image", "is_deleted", "name", "price" }, + values: new object[,] + { + { 1, 1, new DateTime(2025, 11, 24, 12, 29, 52, 484, DateTimeKind.Utc).AddTicks(114), "Beautiful hardy goldfish", "goldfish.jpg", false, "Goldfish", 100m }, + { 2, 2, new DateTime(2025, 11, 24, 12, 29, 52, 484, DateTimeKind.Utc).AddTicks(118), "Popular aquarium plant", "amazonsword.jpg", false, "Amazon Sword", 150m }, + { 3, 3, new DateTime(2025, 11, 24, 12, 29, 52, 484, DateTimeKind.Utc).AddTicks(120), "Small internal filter", "filter.jpg", false, "Internal Filter", 500m } + }); + + migrationBuilder.InsertData( + table: "sale_details", + columns: new[] { "id", "created_date", "product_id", "product_name", "product_price", "product_quantity", "product_total_price", "sale_id" }, + values: new object[] { 1, new DateTime(2025, 11, 24, 12, 29, 52, 484, DateTimeKind.Utc).AddTicks(183), 1, "Goldfish", 100m, 2, 200m, 1 }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "products", + keyColumn: "id", + keyValue: 2); + + migrationBuilder.DeleteData( + table: "products", + keyColumn: "id", + keyValue: 3); + + migrationBuilder.DeleteData( + table: "sale_details", + keyColumn: "id", + keyValue: 1); + + migrationBuilder.DeleteData( + table: "category", + keyColumn: "id", + keyValue: 2); + + migrationBuilder.DeleteData( + table: "category", + keyColumn: "id", + keyValue: 3); + + migrationBuilder.DeleteData( + table: "products", + keyColumn: "id", + keyValue: 1); + + migrationBuilder.DeleteData( + table: "sale", + keyColumn: "id", + keyValue: 1); + + migrationBuilder.DeleteData( + table: "category", + keyColumn: "id", + keyValue: 1); + + migrationBuilder.DropColumn( + name: "product_name", + table: "sale_details"); + } + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251125003845_SeedDataDB.Designer.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251125003845_SeedDataDB.Designer.cs new file mode 100644 index 00000000..35215186 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251125003845_SeedDataDB.Designer.cs @@ -0,0 +1,288 @@ +// +using System; +using JafnaEcommerceApi.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace JafnaEcommerceApi.Migrations +{ + [DbContext(typeof(JafnaDbContext))] + [Migration("20251125003845_SeedDataDB")] + partial class SeedDataDB + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.22") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Category", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("created_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("deleted_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("is_deleted") + .HasColumnType("bit"); + + b.Property("name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("id"); + + b.ToTable("category"); + + b.HasData( + new + { + id = 1, + created_date = new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(6892), + is_deleted = false, + name = "Fish" + }, + new + { + id = 2, + created_date = new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(6894), + is_deleted = false, + name = "Plants" + }, + new + { + id = 3, + created_date = new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(6895), + is_deleted = false, + name = "Aquarium Supplies" + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Product", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("category_id") + .HasColumnType("int"); + + b.Property("created_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("deleted_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("image") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("is_deleted") + .HasColumnType("bit"); + + b.Property("name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("price") + .HasColumnType("decimal(10,2)"); + + b.HasKey("id"); + + b.HasIndex("category_id"); + + b.ToTable("products"); + + b.HasData( + new + { + id = 1, + category_id = 1, + created_date = new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(7055), + description = "Beautiful hardy goldfish", + image = "goldfish.jpg", + is_deleted = false, + name = "Goldfish", + price = 100m + }, + new + { + id = 2, + category_id = 2, + created_date = new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(7057), + description = "Popular aquarium plant", + image = "amazonsword.jpg", + is_deleted = false, + name = "Amazon Sword", + price = 150m + }, + new + { + id = 3, + category_id = 3, + created_date = new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(7058), + description = "Small internal filter", + image = "filter.jpg", + is_deleted = false, + name = "Internal Filter", + price = 500m + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Sale", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("created_date") + .HasColumnType("datetime2"); + + b.Property("total_price") + .HasColumnType("decimal(10,2)"); + + b.HasKey("id"); + + b.ToTable("sale"); + + b.HasData( + new + { + id = 1, + created_date = new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(7082), + total_price = 200m + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.SaleDetail", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("created_date") + .HasColumnType("datetime2"); + + b.Property("product_id") + .HasColumnType("int"); + + b.Property("product_name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("product_price") + .HasColumnType("decimal(10,2)"); + + b.Property("product_quantity") + .HasColumnType("int"); + + b.Property("product_total_price") + .HasColumnType("decimal(10,2)"); + + b.Property("sale_id") + .HasColumnType("int"); + + b.HasKey("id"); + + b.HasIndex("product_id"); + + b.HasIndex("sale_id"); + + b.ToTable("sale_details"); + + b.HasData( + new + { + id = 1, + created_date = new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(7113), + product_id = 1, + product_name = "Goldfish", + product_price = 100m, + product_quantity = 2, + product_total_price = 200m, + sale_id = 1 + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Product", b => + { + b.HasOne("JafnaEcommerceApi.Models.Entities.Category", "category") + .WithMany("Products") + .HasForeignKey("category_id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("category"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.SaleDetail", b => + { + b.HasOne("JafnaEcommerceApi.Models.Entities.Product", "Product") + .WithMany("SaleDetails") + .HasForeignKey("product_id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("JafnaEcommerceApi.Models.Entities.Sale", "Sale") + .WithMany("SaleDetails") + .HasForeignKey("sale_id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Sale"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Category", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Product", b => + { + b.Navigation("SaleDetails"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Sale", b => + { + b.Navigation("SaleDetails"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251125003845_SeedDataDB.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251125003845_SeedDataDB.cs new file mode 100644 index 00000000..ab7faf1a --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251125003845_SeedDataDB.cs @@ -0,0 +1,130 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace JafnaEcommerceApi.Migrations +{ + /// + public partial class SeedDataDB : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.UpdateData( + table: "category", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(6892)); + + migrationBuilder.UpdateData( + table: "category", + keyColumn: "id", + keyValue: 2, + column: "created_date", + value: new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(6894)); + + migrationBuilder.UpdateData( + table: "category", + keyColumn: "id", + keyValue: 3, + column: "created_date", + value: new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(6895)); + + migrationBuilder.UpdateData( + table: "products", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(7055)); + + migrationBuilder.UpdateData( + table: "products", + keyColumn: "id", + keyValue: 2, + column: "created_date", + value: new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(7057)); + + migrationBuilder.UpdateData( + table: "products", + keyColumn: "id", + keyValue: 3, + column: "created_date", + value: new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(7058)); + + migrationBuilder.UpdateData( + table: "sale", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(7082)); + + migrationBuilder.UpdateData( + table: "sale_details", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(7113)); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.UpdateData( + table: "category", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 24, 12, 29, 52, 483, DateTimeKind.Utc).AddTicks(9784)); + + migrationBuilder.UpdateData( + table: "category", + keyColumn: "id", + keyValue: 2, + column: "created_date", + value: new DateTime(2025, 11, 24, 12, 29, 52, 483, DateTimeKind.Utc).AddTicks(9788)); + + migrationBuilder.UpdateData( + table: "category", + keyColumn: "id", + keyValue: 3, + column: "created_date", + value: new DateTime(2025, 11, 24, 12, 29, 52, 483, DateTimeKind.Utc).AddTicks(9790)); + + migrationBuilder.UpdateData( + table: "products", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 24, 12, 29, 52, 484, DateTimeKind.Utc).AddTicks(114)); + + migrationBuilder.UpdateData( + table: "products", + keyColumn: "id", + keyValue: 2, + column: "created_date", + value: new DateTime(2025, 11, 24, 12, 29, 52, 484, DateTimeKind.Utc).AddTicks(118)); + + migrationBuilder.UpdateData( + table: "products", + keyColumn: "id", + keyValue: 3, + column: "created_date", + value: new DateTime(2025, 11, 24, 12, 29, 52, 484, DateTimeKind.Utc).AddTicks(120)); + + migrationBuilder.UpdateData( + table: "sale", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 24, 12, 29, 52, 484, DateTimeKind.Utc).AddTicks(153)); + + migrationBuilder.UpdateData( + table: "sale_details", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 24, 12, 29, 52, 484, DateTimeKind.Utc).AddTicks(183)); + } + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251128104627_adddata.Designer.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251128104627_adddata.Designer.cs new file mode 100644 index 00000000..f97ff42f --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251128104627_adddata.Designer.cs @@ -0,0 +1,288 @@ +// +using System; +using JafnaEcommerceApi.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace JafnaEcommerceApi.Migrations +{ + [DbContext(typeof(JafnaDbContext))] + [Migration("20251128104627_adddata")] + partial class adddata + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.22") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Category", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("created_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("deleted_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("is_deleted") + .HasColumnType("bit"); + + b.Property("name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("id"); + + b.ToTable("category"); + + b.HasData( + new + { + id = 1, + created_date = new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2478), + is_deleted = false, + name = "Fish" + }, + new + { + id = 2, + created_date = new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2482), + is_deleted = false, + name = "Plants" + }, + new + { + id = 3, + created_date = new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2484), + is_deleted = false, + name = "Aquarium Supplies" + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Product", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("category_id") + .HasColumnType("int"); + + b.Property("created_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("deleted_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("image") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("is_deleted") + .HasColumnType("bit"); + + b.Property("name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("price") + .HasColumnType("decimal(10,2)"); + + b.HasKey("id"); + + b.HasIndex("category_id"); + + b.ToTable("products"); + + b.HasData( + new + { + id = 1, + category_id = 1, + created_date = new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2764), + description = "Beautiful hardy goldfish", + image = "goldfish.jpg", + is_deleted = false, + name = "Goldfish", + price = 100m + }, + new + { + id = 2, + category_id = 2, + created_date = new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2768), + description = "Popular aquarium plant", + image = "amazonsword.jpg", + is_deleted = false, + name = "Amazon Sword", + price = 150m + }, + new + { + id = 3, + category_id = 3, + created_date = new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2770), + description = "Small internal filter", + image = "filter.jpg", + is_deleted = false, + name = "Internal Filter", + price = 500m + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Sale", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("created_date") + .HasColumnType("datetime2"); + + b.Property("total_price") + .HasColumnType("decimal(10,2)"); + + b.HasKey("id"); + + b.ToTable("sale"); + + b.HasData( + new + { + id = 1, + created_date = new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2799), + total_price = 200m + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.SaleDetail", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("created_date") + .HasColumnType("datetime2"); + + b.Property("product_id") + .HasColumnType("int"); + + b.Property("product_name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("product_price") + .HasColumnType("decimal(10,2)"); + + b.Property("product_quantity") + .HasColumnType("int"); + + b.Property("product_total_price") + .HasColumnType("decimal(10,2)"); + + b.Property("sale_id") + .HasColumnType("int"); + + b.HasKey("id"); + + b.HasIndex("product_id"); + + b.HasIndex("sale_id"); + + b.ToTable("sale_details"); + + b.HasData( + new + { + id = 1, + created_date = new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2831), + product_id = 1, + product_name = "Goldfish", + product_price = 100m, + product_quantity = 2, + product_total_price = 200m, + sale_id = 1 + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Product", b => + { + b.HasOne("JafnaEcommerceApi.Models.Entities.Category", "category") + .WithMany("Products") + .HasForeignKey("category_id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("category"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.SaleDetail", b => + { + b.HasOne("JafnaEcommerceApi.Models.Entities.Product", "Product") + .WithMany("SaleDetails") + .HasForeignKey("product_id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("JafnaEcommerceApi.Models.Entities.Sale", "Sale") + .WithMany("SaleDetails") + .HasForeignKey("sale_id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Sale"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Category", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Product", b => + { + b.Navigation("SaleDetails"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Sale", b => + { + b.Navigation("SaleDetails"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251128104627_adddata.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251128104627_adddata.cs new file mode 100644 index 00000000..5ce01e75 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251128104627_adddata.cs @@ -0,0 +1,130 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace JafnaEcommerceApi.Migrations +{ + /// + public partial class adddata : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.UpdateData( + table: "category", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2478)); + + migrationBuilder.UpdateData( + table: "category", + keyColumn: "id", + keyValue: 2, + column: "created_date", + value: new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2482)); + + migrationBuilder.UpdateData( + table: "category", + keyColumn: "id", + keyValue: 3, + column: "created_date", + value: new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2484)); + + migrationBuilder.UpdateData( + table: "products", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2764)); + + migrationBuilder.UpdateData( + table: "products", + keyColumn: "id", + keyValue: 2, + column: "created_date", + value: new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2768)); + + migrationBuilder.UpdateData( + table: "products", + keyColumn: "id", + keyValue: 3, + column: "created_date", + value: new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2770)); + + migrationBuilder.UpdateData( + table: "sale", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2799)); + + migrationBuilder.UpdateData( + table: "sale_details", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2831)); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.UpdateData( + table: "category", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(6892)); + + migrationBuilder.UpdateData( + table: "category", + keyColumn: "id", + keyValue: 2, + column: "created_date", + value: new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(6894)); + + migrationBuilder.UpdateData( + table: "category", + keyColumn: "id", + keyValue: 3, + column: "created_date", + value: new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(6895)); + + migrationBuilder.UpdateData( + table: "products", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(7055)); + + migrationBuilder.UpdateData( + table: "products", + keyColumn: "id", + keyValue: 2, + column: "created_date", + value: new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(7057)); + + migrationBuilder.UpdateData( + table: "products", + keyColumn: "id", + keyValue: 3, + column: "created_date", + value: new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(7058)); + + migrationBuilder.UpdateData( + table: "sale", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(7082)); + + migrationBuilder.UpdateData( + table: "sale_details", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 25, 0, 38, 45, 372, DateTimeKind.Utc).AddTicks(7113)); + } + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251212012320_defaultdate.Designer.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251212012320_defaultdate.Designer.cs new file mode 100644 index 00000000..716eac59 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251212012320_defaultdate.Designer.cs @@ -0,0 +1,292 @@ +// +using System; +using JafnaEcommerceApi.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace JafnaEcommerceApi.Migrations +{ + [DbContext(typeof(JafnaDbContext))] + [Migration("20251212012320_defaultdate")] + partial class defaultdate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.22") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Category", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("created_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("deleted_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("is_deleted") + .HasColumnType("bit"); + + b.Property("name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("id"); + + b.ToTable("category"); + + b.HasData( + new + { + id = 1, + created_date = new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4285), + is_deleted = false, + name = "Fish" + }, + new + { + id = 2, + created_date = new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4288), + is_deleted = false, + name = "Plants" + }, + new + { + id = 3, + created_date = new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4289), + is_deleted = false, + name = "Aquarium Supplies" + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Product", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("category_id") + .HasColumnType("int"); + + b.Property("created_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("deleted_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("image") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("is_deleted") + .HasColumnType("bit"); + + b.Property("name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("price") + .HasColumnType("decimal(10,2)"); + + b.HasKey("id"); + + b.HasIndex("category_id"); + + b.ToTable("products"); + + b.HasData( + new + { + id = 1, + category_id = 1, + created_date = new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4398), + description = "Beautiful hardy goldfish", + image = "goldfish.jpg", + is_deleted = false, + name = "Goldfish", + price = 100m + }, + new + { + id = 2, + category_id = 2, + created_date = new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4400), + description = "Popular aquarium plant", + image = "amazonsword.jpg", + is_deleted = false, + name = "Amazon Sword", + price = 150m + }, + new + { + id = 3, + category_id = 3, + created_date = new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4401), + description = "Small internal filter", + image = "filter.jpg", + is_deleted = false, + name = "Internal Filter", + price = 500m + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Sale", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("created_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("total_price") + .HasColumnType("decimal(10,2)"); + + b.HasKey("id"); + + b.ToTable("sale"); + + b.HasData( + new + { + id = 1, + created_date = new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4425), + total_price = 200m + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.SaleDetail", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("created_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("product_id") + .HasColumnType("int"); + + b.Property("product_name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("product_price") + .HasColumnType("decimal(10,2)"); + + b.Property("product_quantity") + .HasColumnType("int"); + + b.Property("product_total_price") + .HasColumnType("decimal(10,2)"); + + b.Property("sale_id") + .HasColumnType("int"); + + b.HasKey("id"); + + b.HasIndex("product_id"); + + b.HasIndex("sale_id"); + + b.ToTable("sale_details"); + + b.HasData( + new + { + id = 1, + created_date = new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4454), + product_id = 1, + product_name = "Goldfish", + product_price = 100m, + product_quantity = 2, + product_total_price = 200m, + sale_id = 1 + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Product", b => + { + b.HasOne("JafnaEcommerceApi.Models.Entities.Category", "category") + .WithMany("Products") + .HasForeignKey("category_id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("category"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.SaleDetail", b => + { + b.HasOne("JafnaEcommerceApi.Models.Entities.Product", "Product") + .WithMany("SaleDetails") + .HasForeignKey("product_id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("JafnaEcommerceApi.Models.Entities.Sale", "Sale") + .WithMany("SaleDetails") + .HasForeignKey("sale_id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Sale"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Category", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Product", b => + { + b.Navigation("SaleDetails"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Sale", b => + { + b.Navigation("SaleDetails"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251212012320_defaultdate.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251212012320_defaultdate.cs new file mode 100644 index 00000000..8bc61c34 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/20251212012320_defaultdate.cs @@ -0,0 +1,167 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace JafnaEcommerceApi.Migrations +{ + /// + public partial class defaultdate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "created_date", + table: "sale_details", + type: "datetime2", + nullable: false, + defaultValueSql: "GETDATE()", + oldClrType: typeof(DateTime), + oldType: "datetime2"); + + migrationBuilder.AlterColumn( + name: "created_date", + table: "sale", + type: "datetime2", + nullable: false, + defaultValueSql: "GETDATE()", + oldClrType: typeof(DateTime), + oldType: "datetime2"); + + migrationBuilder.UpdateData( + table: "category", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4285)); + + migrationBuilder.UpdateData( + table: "category", + keyColumn: "id", + keyValue: 2, + column: "created_date", + value: new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4288)); + + migrationBuilder.UpdateData( + table: "category", + keyColumn: "id", + keyValue: 3, + column: "created_date", + value: new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4289)); + + migrationBuilder.UpdateData( + table: "products", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4398)); + + migrationBuilder.UpdateData( + table: "products", + keyColumn: "id", + keyValue: 2, + column: "created_date", + value: new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4400)); + + migrationBuilder.UpdateData( + table: "products", + keyColumn: "id", + keyValue: 3, + column: "created_date", + value: new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4401)); + + migrationBuilder.UpdateData( + table: "sale", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4425)); + + migrationBuilder.UpdateData( + table: "sale_details", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4454)); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "created_date", + table: "sale_details", + type: "datetime2", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "datetime2", + oldDefaultValueSql: "GETDATE()"); + + migrationBuilder.AlterColumn( + name: "created_date", + table: "sale", + type: "datetime2", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "datetime2", + oldDefaultValueSql: "GETDATE()"); + + migrationBuilder.UpdateData( + table: "category", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2478)); + + migrationBuilder.UpdateData( + table: "category", + keyColumn: "id", + keyValue: 2, + column: "created_date", + value: new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2482)); + + migrationBuilder.UpdateData( + table: "category", + keyColumn: "id", + keyValue: 3, + column: "created_date", + value: new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2484)); + + migrationBuilder.UpdateData( + table: "products", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2764)); + + migrationBuilder.UpdateData( + table: "products", + keyColumn: "id", + keyValue: 2, + column: "created_date", + value: new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2768)); + + migrationBuilder.UpdateData( + table: "products", + keyColumn: "id", + keyValue: 3, + column: "created_date", + value: new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2770)); + + migrationBuilder.UpdateData( + table: "sale", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2799)); + + migrationBuilder.UpdateData( + table: "sale_details", + keyColumn: "id", + keyValue: 1, + column: "created_date", + value: new DateTime(2025, 11, 28, 10, 46, 27, 110, DateTimeKind.Utc).AddTicks(2831)); + } + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/JaffnaDbContextModelSnapshot.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/JaffnaDbContextModelSnapshot.cs new file mode 100644 index 00000000..8bac6d9c --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Migrations/JaffnaDbContextModelSnapshot.cs @@ -0,0 +1,289 @@ +// +using System; +using JafnaEcommerceApi.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace JafnaEcommerceApi.Migrations +{ + [DbContext(typeof(JafnaDbContext))] + partial class JaffnaDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.22") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Category", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("created_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("deleted_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("is_deleted") + .HasColumnType("bit"); + + b.Property("name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("id"); + + b.ToTable("category", (string)null); + + b.HasData( + new + { + id = 1, + created_date = new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4285), + is_deleted = false, + name = "Fish" + }, + new + { + id = 2, + created_date = new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4288), + is_deleted = false, + name = "Plants" + }, + new + { + id = 3, + created_date = new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4289), + is_deleted = false, + name = "Aquarium Supplies" + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Product", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("category_id") + .HasColumnType("int"); + + b.Property("created_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("deleted_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("image") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("is_deleted") + .HasColumnType("bit"); + + b.Property("name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("price") + .HasColumnType("decimal(10,2)"); + + b.HasKey("id"); + + b.HasIndex("category_id"); + + b.ToTable("products", (string)null); + + b.HasData( + new + { + id = 1, + category_id = 1, + created_date = new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4398), + description = "Beautiful hardy goldfish", + image = "goldfish.jpg", + is_deleted = false, + name = "Goldfish", + price = 100m + }, + new + { + id = 2, + category_id = 2, + created_date = new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4400), + description = "Popular aquarium plant", + image = "amazonsword.jpg", + is_deleted = false, + name = "Amazon Sword", + price = 150m + }, + new + { + id = 3, + category_id = 3, + created_date = new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4401), + description = "Small internal filter", + image = "filter.jpg", + is_deleted = false, + name = "Internal Filter", + price = 500m + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Sale", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("created_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("total_price") + .HasColumnType("decimal(10,2)"); + + b.HasKey("id"); + + b.ToTable("sale", (string)null); + + b.HasData( + new + { + id = 1, + created_date = new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4425), + total_price = 200m + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.SaleDetail", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("id")); + + b.Property("created_date") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETDATE()"); + + b.Property("product_id") + .HasColumnType("int"); + + b.Property("product_name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("product_price") + .HasColumnType("decimal(10,2)"); + + b.Property("product_quantity") + .HasColumnType("int"); + + b.Property("product_total_price") + .HasColumnType("decimal(10,2)"); + + b.Property("sale_id") + .HasColumnType("int"); + + b.HasKey("id"); + + b.HasIndex("product_id"); + + b.HasIndex("sale_id"); + + b.ToTable("sale_details", (string)null); + + b.HasData( + new + { + id = 1, + created_date = new DateTime(2025, 12, 12, 1, 23, 19, 607, DateTimeKind.Utc).AddTicks(4454), + product_id = 1, + product_name = "Goldfish", + product_price = 100m, + product_quantity = 2, + product_total_price = 200m, + sale_id = 1 + }); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Product", b => + { + b.HasOne("JafnaEcommerceApi.Models.Entities.Category", "category") + .WithMany("Products") + .HasForeignKey("category_id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("category"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.SaleDetail", b => + { + b.HasOne("JafnaEcommerceApi.Models.Entities.Product", "Product") + .WithMany("SaleDetails") + .HasForeignKey("product_id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("JafnaEcommerceApi.Models.Entities.Sale", "Sale") + .WithMany("SaleDetails") + .HasForeignKey("sale_id") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Sale"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Category", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Product", b => + { + b.Navigation("SaleDetails"); + }); + + modelBuilder.Entity("JafnaEcommerceApi.Models.Entities.Sale", b => + { + b.Navigation("SaleDetails"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/APIResponse/ApiResponse.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/APIResponse/ApiResponse.cs new file mode 100644 index 00000000..0268bc6b --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/APIResponse/ApiResponse.cs @@ -0,0 +1,31 @@ +namespace JafnaEcommerceApi.Models.DTOs.APIResponse; + +public class ApiResponse +{ + public bool success { get; set; } + public string message { get; set; } + public T data { get; set; } + public List errors { get; set; } + + public static ApiResponse Success(T data, string message = "Operation successful") + { + return new ApiResponse + { + success = true, + message = message, + data = data, + errors = null + }; + } + + public static ApiResponse Failure(List errors, string message = "Operation failed") + { + return new ApiResponse + { + success = false, + message = message, + data = default, + errors = errors + }; + } +} \ No newline at end of file diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/CategoryDTOs/CategoryCreateDto.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/CategoryDTOs/CategoryCreateDto.cs new file mode 100644 index 00000000..5d4c84ea --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/CategoryDTOs/CategoryCreateDto.cs @@ -0,0 +1,8 @@ +using System.ComponentModel.DataAnnotations; + +namespace JafnaEcommerceApi.Models.DTOs.CategoryDTOs; +public class CategoryCreateDto +{ + [Required] + public string Name { get; set; } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/CategoryDTOs/CategoryDto.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/CategoryDTOs/CategoryDto.cs new file mode 100644 index 00000000..4999f4dc --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/CategoryDTOs/CategoryDto.cs @@ -0,0 +1,6 @@ +namespace JafnaEcommerceApi.Models.DTOs.CategoryDTOs; +public class CategoryDto +{ + public int Id { get; set; } + public string Name { get; set; } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/CategoryDTOs/CategoryUpdateDto.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/CategoryDTOs/CategoryUpdateDto.cs new file mode 100644 index 00000000..6a3de460 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/CategoryDTOs/CategoryUpdateDto.cs @@ -0,0 +1,8 @@ +using System.ComponentModel.DataAnnotations; + +namespace JafnaEcommerceApi.Models.DTOs.CategoryDTOs; +public class CategoryUpdateDto +{ + [Required] + public string Name { get; set; } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/PaginationDTOs/PaginationDto.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/PaginationDTOs/PaginationDto.cs new file mode 100644 index 00000000..7e2cc1c1 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/PaginationDTOs/PaginationDto.cs @@ -0,0 +1,24 @@ +namespace JafnaEcommerceApi.Models.DTOs.PaginationDTOs; + + +public class PagedResponse +{ + public int PageNumber { get; set; } + public int PageSize { get; set; } + public int TotalCount { get; set; } + public int TotalPages { get; set; } + public IEnumerable Items { get; set; } + + public PagedResponse() { } + + public PagedResponse(IEnumerable items, int count, int pageNumber, int pageSize) + { + Items = items; + TotalCount = count; + PageNumber = pageNumber; + PageSize = pageSize; + TotalPages = (int)Math.Ceiling(count / (double)pageSize); + } + + +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/ProductDTOs/ProductCreateDto.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/ProductDTOs/ProductCreateDto.cs new file mode 100644 index 00000000..ee458c37 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/ProductDTOs/ProductCreateDto.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace JafnaEcommerceApi.Models.DTOs.ProductDTOs; + +public class ProductCreateDto +{ + [Required] + public string Name { get; set; } + + [Required] + public string Description { get; set; } + + [Required] + [Range(0.01, double.MaxValue)] + public decimal Price { get; set; } + + public string? Image { get; set; } + + [Required] + public int CategoryId { get; set; } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/ProductDTOs/ProductDto.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/ProductDTOs/ProductDto.cs new file mode 100644 index 00000000..cff618fa --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/ProductDTOs/ProductDto.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace JafnaEcommerceApi.Models.DTOs.ProductDTOs; + +public class ProductDto +{ + public int Id { get; set; } + public string Name { get; set; } + + public string Description { get; set; } + + [Range(0.01, double.MaxValue)] + public decimal Price { get; set; } + + public string Image { get; set; } + + public int CategoryId { get; set; } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/ProductDTOs/ProductUpdateDto.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/ProductDTOs/ProductUpdateDto.cs new file mode 100644 index 00000000..9b9e4ce8 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/ProductDTOs/ProductUpdateDto.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace JafnaEcommerceApi.Models.DTOs.ProductDTOs; + +public class ProductUpdateDto +{ + public string? Name { get; set; } + + public string? Description { get; set; } + + [Range(0.01, double.MaxValue)] + public decimal? Price { get; set; } + + public string? Image { get; set; } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/SaleDTOs/SaleCreateDto.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/SaleDTOs/SaleCreateDto.cs new file mode 100644 index 00000000..bd146e77 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/SaleDTOs/SaleCreateDto.cs @@ -0,0 +1,6 @@ +namespace JafnaEcommerceApi.Models.DTOs.SaleDTOs; + +public class SaleCreateDto +{ + public List SaleItems { get; set; } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/SaleDTOs/SaleDetailCreateDto.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/SaleDTOs/SaleDetailCreateDto.cs new file mode 100644 index 00000000..ad6fd493 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/SaleDTOs/SaleDetailCreateDto.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; + +namespace JafnaEcommerceApi.Models.DTOs.SaleDTOs; + +public class SaleDetailCreateDto +{ + public int ProductId { get; set; } + [MinLength(1)] + public int ProductQuantity { get; set; } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/SaleDTOs/SaleDetailDto.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/SaleDTOs/SaleDetailDto.cs new file mode 100644 index 00000000..c3d29bd9 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/SaleDTOs/SaleDetailDto.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + +namespace JafnaEcommerceApi.Models.DTOs.SaleDTOs; + +public class SaleDetailDto +{ + public int ProductId { get; set; } + public string ProductName { get; set; } + [MinLength(1)] + [Range(1, int.MaxValue)] + public int ProductQuantity { get; set; } + [Range(0.01, double.MaxValue)] + public decimal ProductPrice { get; set; } + public decimal ProductTotalPrice { get; set; } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/SaleDTOs/SalesDto.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/SaleDTOs/SalesDto.cs new file mode 100644 index 00000000..9452e31b --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/DTOs/SaleDTOs/SalesDto.cs @@ -0,0 +1,10 @@ +namespace JafnaEcommerceApi.Models.DTOs.SaleDTOs; + +public class SaleDto +{ + public int Id { get; set; } + public decimal TotalPrice { get; set; } + public DateTime CreatedDate { get; set; } + +} + diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/Entities/Category.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/Entities/Category.cs new file mode 100644 index 00000000..d4754c89 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/Entities/Category.cs @@ -0,0 +1,26 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace JafnaEcommerceApi.Models.Entities; + +public class Category +{ + [Key] + [Column("id")] + public int Id { get; set; } + + [Column("name")] + public string Name { get; set; } + + [Column("created_date")] + public DateTime CreatedDate { get; set; } + + [Column("is_deleted")] + public bool IsDeleted { get; set; } + + [Column("deleted_date")] + public DateTime? DeletedDate { get; set; } + + public List Products { get; set; } +} + diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/Entities/Product.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/Entities/Product.cs new file mode 100644 index 00000000..f4e5df61 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/Entities/Product.cs @@ -0,0 +1,39 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace JafnaEcommerceApi.Models.Entities; + +public class Product +{ + [Key] + [Column("id")] + public int Id { get; set; } + + [Column("name")] + public string Name { get; set; } + + [Column("description")] + public string Description { get; set; } + + [Column("price", TypeName = "decimal(10,2)")] + public decimal Price { get; set; } + + [Column("image")] + public string Image { get; set; } + + [Column("category_id")] + public int CategoryId { get; set; } + + public Category Category { get; set; } + + public List SaleDetails { get; set; } + + [Column("created_date")] + public DateTime CreatedDate { get; set; } + + [Column("is_deleted")] + public bool IsDeleted { get; set; } + + [Column("deleted_date")] + public DateTime? DeletedDate { get; set; } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/Entities/Sale.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/Entities/Sale.cs new file mode 100644 index 00000000..6bebdcfe --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/Entities/Sale.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace JafnaEcommerceApi.Models.Entities; + +public class Sale +{ + [Key] + [Column("id")] + public int Id { get; set; } + + [Column("total_price", TypeName = "decimal(10,2)")] + public decimal TotalPrice { get; set; } + + [Column("created_date")] + public DateTime CreatedDate { get; set; } + + public List SaleDetails { get; set; } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/Entities/SaleDetail.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/Entities/SaleDetail.cs new file mode 100644 index 00000000..155b3a9a --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Models/Entities/SaleDetail.cs @@ -0,0 +1,39 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace JafnaEcommerceApi.Models.Entities; + +public class SaleDetail +{ + [Key] + [Column("id")] + public int Id { get; set; } + + [ForeignKey("Product")] + [Column("product_id")] + public int ProductId { get; set; } + + [ForeignKey("Sale")] + [Column("sale_id")] + public int SaleId { get; set; } + + public Product Product { get; set; } + + public Sale Sale { get; set; } + + [Column("product_name")] + public string ProductName { get; set; } + + [Column("product_quantity")] + public int ProductQuantity { get; set; } + + [Column("product_price", TypeName = "decimal(10,2)")] + public decimal ProductPrice { get; set; } + + [Column("product_total_price", TypeName = "decimal(10,2)")] + public decimal ProductTotalPrice { get; set; } + + [Column("created_date")] + public DateTime CreatedDate { get; set; } +} + diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Postman api documentation/JafnaEcommerceApi.postman_collection.json b/freddypositive.ecomerceapi/JafnaEcommerceApi/Postman api documentation/JafnaEcommerceApi.postman_collection.json new file mode 100644 index 00000000..f1dd1b3f --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Postman api documentation/JafnaEcommerceApi.postman_collection.json @@ -0,0 +1,360 @@ + { + "info": { + "_postman_id": "jafna-ecommerce-api-collection", + "name": "JafnaEcommerceApi (Aquarium Store)", + "description": "Postman Collection for JafnaEcommerceApi covering Products, Categories, and Sales. Tailored for a Pet Store Aquarium domain. Updated to match verified routing.", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Categories", + "item": [ + { + "name": "Get All Categories", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/Category?pageNumber=1&pageSize=10", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "Category" + ], + "query": [ + { + "key": "pageNumber", + "value": "1" + }, + { + "key": "pageSize", + "value": "10" + } + ] + } + }, + "response": [] + }, + { + "name": "Create Category", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"Freshwater Fish\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/api/Category", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "Category" + ] + } + }, + "response": [] + }, + { + "name": "Update Category", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"Aquariums & Tanks\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/api/Category/{{id}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "Category", + "{{id}}" + ] + } + }, + "response": [] + }, + { + "name": "Delete Category", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{base_url}}/api/Category/{{id}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "Category", + "{{id}}" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Products", + "item": [ + { + "name": "Get All Products", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/Product?pageNumber=1&pageSize=10", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "Product" + ], + "query": [ + { + "key": "pageNumber", + "value": "1" + }, + { + "key": "pageSize", + "value": "10" + } + ] + } + }, + "response": [] + }, + { + "name": "Get Products By Category", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/Product/{{categoryId}}?pageNumber=1&pageSize=10", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "Product", + "{{categoryId}}" + ], + "query": [ + { + "key": "pageNumber", + "value": "1" + }, + { + "key": "pageSize", + "value": "10" + } + ] + } + }, + "response": [] + }, + { + "name": "Create Product", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"Goldfish (Common)\",\n \"description\": \"Healthy standard goldfish, great for beginners.\",\n \"price\": 5.99,\n \"image\": \"http://example.com/goldfish.jpg\",\n \"categoryId\": 1\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/api/Product", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "Product" + ] + } + }, + "response": [] + }, + { + "name": "Update Product", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"Betta Fish (Veintail)\",\n \"description\": \"Vibrant blue veintail betta.\",\n \"price\": 12.50,\n \"image\": \"http://example.com/betta_v2.jpg\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/api/Product/{{id}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "Product", + "{{id}}" + ] + } + }, + "response": [] + }, + { + "name": "Delete Product", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{base_url}}/api/Product/{{id}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "Product", + "{{id}}" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Sales", + "item": [ + { + "name": "Get All Sales", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/Sales?pageNumber=1&pageSize=10", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "Sales" + ], + "query": [ + { + "key": "pageNumber", + "value": "1" + }, + { + "key": "pageSize", + "value": "10" + } + ] + } + }, + "response": [] + }, + { + "name": "Get Sale Details", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/api/Sales/{{id}}/details?pageNumber=1&pageSize=10", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "Sales", + "{{id}}", + "details" + ], + "query": [ + { + "key": "pageNumber", + "value": "1" + }, + { + "key": "pageSize", + "value": "10" + } + ] + } + }, + "response": [] + }, + { + "name": "Create Sale", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"saleItems\": [\n {\n \"productId\": 1,\n \"productQuantity\": 3\n },\n {\n \"productId\": 2,\n \"productQuantity\": 1\n }\n ]\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{base_url}}/api/Sales", + "host": [ + "{{base_url}}" + ], + "path": [ + "api", + "Sales" + ] + } + }, + "response": [] + } + ] + } + ], + "variable": [ + { + "key": "base_url", + "value": "https://localhost:7210", + "type": "string" + }, + { + "key": "id", + "value": "1", + "type": "string" + }, + { + "key": "categoryId", + "value": "1", + "type": "string" + } + ] +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Program.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Program.cs new file mode 100644 index 00000000..1dd5acea --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Program.cs @@ -0,0 +1,61 @@ +using JafnaEcommerceApi.Data; +using JafnaEcommerceApi.Middleware; +using JafnaEcommerceApi.Repositories; +using JafnaEcommerceApi.Services; +using JafnaEcommerceApi.Services.Implementation; +using JafnaEcommerceApi.Services.Interface; +using Microsoft.EntityFrameworkCore; +using Serilog; + +var builder = WebApplication.CreateBuilder(args); + +Log.Logger = new LoggerConfiguration() + .ReadFrom.Configuration(builder.Configuration) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Day) + .CreateLogger(); + +builder.Host.UseSerilog(); +builder.Services.AddControllers(); +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddDbContext(options => +options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); +builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); +builder.Services.AddCors(options => +{ + options.AddPolicy("AllowLocalhost", policy => + { + policy.WithOrigins("http://127.0.0.1:5500", "http://localhost:7210") + .AllowAnyHeader() + .AllowAnyMethod(); + }); +}); + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseCors("AllowLocalhost"); + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.UseMiddleware(); + +app.Run(); diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Properties/launchSettings.json b/freddypositive.ecomerceapi/JafnaEcommerceApi/Properties/launchSettings.json new file mode 100644 index 00000000..2b97a3a4 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:55635", + "sslPort": 44310 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5083", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7044;http://localhost:5083", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Repositories/Implementation/CategoryRepository.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Repositories/Implementation/CategoryRepository.cs new file mode 100644 index 00000000..002dd307 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Repositories/Implementation/CategoryRepository.cs @@ -0,0 +1,51 @@ +using JafnaEcommerceApi.Data; +using JafnaEcommerceApi.Models.Entities; +using Microsoft.EntityFrameworkCore; + +namespace JafnaEcommerceApi.Repositories; + +public class CategoryRepository : ICategoryRepository +{ + public readonly JafnaDbContext _jaffnaDbContext; + + public CategoryRepository(JafnaDbContext jaffnaDbContext) + { + _jaffnaDbContext = jaffnaDbContext; + } + + public async Task Create(Category categoryEntity) + { + // repository should not perform validation - service handles that + _jaffnaDbContext.Add(categoryEntity); + await _jaffnaDbContext.SaveChangesAsync(); + } + + // changed: accept entity and persist (service will map into existing entity) + public async Task Update(Category categoryEntity) + { + _jaffnaDbContext.category.Update(categoryEntity); + await _jaffnaDbContext.SaveChangesAsync(); + } + public async Task Delete(Category categoryEntity) + { + _jaffnaDbContext.category.Update(categoryEntity); + await _jaffnaDbContext.SaveChangesAsync(); + } + + public IQueryable GetCategoryQuery() + { + return _jaffnaDbContext.category.Where(c => !c.IsDeleted); + } + + public async Task GetByIdAsync(int categoryId) + { + return await _jaffnaDbContext.category + .FirstOrDefaultAsync(c => c.Id == categoryId && !c.IsDeleted); + } + + public async Task GetByNameAsync(string name) + { + return await _jaffnaDbContext.category + .FirstOrDefaultAsync(c => c.Name == name && !c.IsDeleted); + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Repositories/Implementation/ProductRepository.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Repositories/Implementation/ProductRepository.cs new file mode 100644 index 00000000..ba72cfd2 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Repositories/Implementation/ProductRepository.cs @@ -0,0 +1,57 @@ +using JafnaEcommerceApi.Data; +using JafnaEcommerceApi.Models.Entities; +using Microsoft.EntityFrameworkCore; + +namespace JafnaEcommerceApi.Repositories; + +public class ProductRepository : IProductRepository +{ + + public readonly JafnaDbContext _jaffnaDbContext; + + public ProductRepository(JafnaDbContext jaffnaDbContext) + { + _jaffnaDbContext = jaffnaDbContext; + } + public async Task CreateAsync(Product product) + { + _jaffnaDbContext.Add(product); + + await _jaffnaDbContext.SaveChangesAsync(); + } + + public async Task UpdateAsync(Product product) + { + await _jaffnaDbContext.SaveChangesAsync(); + } + + public async Task DeleteAsync(Product productEntity) + { + await _jaffnaDbContext.SaveChangesAsync(); + } + public async Task> GetProductsByIdsAsync(List productIds) + { + return await _jaffnaDbContext.products.Where(x => productIds.Contains(x.Id) && !x.IsDeleted).ToListAsync(); + } + + public IQueryable GetProductQuery() + { + return _jaffnaDbContext.products.Where(c => !c.IsDeleted); + } + public IQueryable GetCategoryProductQuery(int categoryId) + { + return _jaffnaDbContext.products.Where(c => c.CategoryId == categoryId && !c.IsDeleted); + } + public async Task GetByIdAsync(int id) + { + return await _jaffnaDbContext.products + .FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted); + } + public async Task GetNameAsync(string name) + { + return await _jaffnaDbContext.products. + FirstOrDefaultAsync(c => c.Name == name && c.IsDeleted == false); + } + + +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Repositories/Implementation/SalesRepository.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Repositories/Implementation/SalesRepository.cs new file mode 100644 index 00000000..8ee6d26d --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Repositories/Implementation/SalesRepository.cs @@ -0,0 +1,45 @@ +using JafnaEcommerceApi.Data; +using JafnaEcommerceApi.Models.Entities; + +namespace JafnaEcommerceApi.Repositories; + +public class SalesRepository : ISaleRepository +{ + public readonly JafnaDbContext _jaffnaDbContext; + + public SalesRepository(JafnaDbContext jaffnaDbContext) + { + _jaffnaDbContext = jaffnaDbContext; + } + + public async Task CreateSale(decimal totalSalePrice) + { + var sale = new Sale + { + TotalPrice = totalSalePrice + }; + + _jaffnaDbContext.Add(sale); + + await _jaffnaDbContext.SaveChangesAsync(); + + return sale.Id; + } + + public async Task CreateSaleDetails(List saleEntity) + { + _jaffnaDbContext.AddRange(saleEntity); + + await _jaffnaDbContext.SaveChangesAsync(); + } + + public IQueryable GetSalesQuery() + { + return _jaffnaDbContext.sale.AsQueryable(); + } + + public IQueryable GetSaleDetailsQuery(int saleId) + { + return _jaffnaDbContext.sale_details.Where(sd => sd.SaleId == saleId).AsQueryable(); + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Repositories/Interface/ICategoryRepository.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Repositories/Interface/ICategoryRepository.cs new file mode 100644 index 00000000..48f710d1 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Repositories/Interface/ICategoryRepository.cs @@ -0,0 +1,13 @@ +using JafnaEcommerceApi.Models.Entities; + +namespace JafnaEcommerceApi.Repositories; + +public interface ICategoryRepository +{ + Task Create(Category categoryEntity); + Task Update(Category categoryEntity); + Task Delete(Category categoryEntity); + IQueryable GetCategoryQuery(); + Task GetByIdAsync(int categoryId); + Task GetByNameAsync(string name); +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Repositories/Interface/IProductRepository.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Repositories/Interface/IProductRepository.cs new file mode 100644 index 00000000..d142cdf5 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Repositories/Interface/IProductRepository.cs @@ -0,0 +1,16 @@ +using AutoMapper; +using JafnaEcommerceApi.Models.Entities; + +namespace JafnaEcommerceApi.Repositories; + +public interface IProductRepository +{ + Task CreateAsync(Product productEntity); + Task UpdateAsync(Product productEntity); + Task DeleteAsync(Product productEntity); + IQueryable GetProductQuery(); + Task GetByIdAsync(int productId); + Task GetNameAsync(string name); + IQueryable GetCategoryProductQuery(int categoryId); + Task> GetProductsByIdsAsync(List SalesProductIds); +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Repositories/Interface/ISalesRepository.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Repositories/Interface/ISalesRepository.cs new file mode 100644 index 00000000..35ca218f --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Repositories/Interface/ISalesRepository.cs @@ -0,0 +1,12 @@ +using JafnaEcommerceApi.Models.Entities; +using System.Linq; + +namespace JafnaEcommerceApi.Repositories; + +public interface ISaleRepository +{ + Task CreateSale(decimal totalSalePrice); + Task CreateSaleDetails(List saleEntity); + IQueryable GetSalesQuery(); + IQueryable GetSaleDetailsQuery(int saleId); +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Implementation/CategoryService.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Implementation/CategoryService.cs new file mode 100644 index 00000000..a20b0d70 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Implementation/CategoryService.cs @@ -0,0 +1,73 @@ +using AutoMapper; +using JafnaEcommerceApi.Exceptions; +using JafnaEcommerceApi.Models.DTOs.CategoryDTOs; +using JafnaEcommerceApi.Models.DTOs.PaginationDTOs; +using JafnaEcommerceApi.Models.Entities; +using JafnaEcommerceApi.Repositories; +using Microsoft.EntityFrameworkCore; + +namespace JafnaEcommerceApi.Services; + +public class CategoryService : ICategoryService +{ + private readonly ICategoryRepository _categoryRepository; + private readonly IMapper _mapper; + + public CategoryService(ICategoryRepository categoryRepository, IMapper mapper) + { + _categoryRepository = categoryRepository; + _mapper = mapper; + } + + public async Task Create(CategoryCreateDto categoryCreateDto) + { + var existing = await _categoryRepository.GetByNameAsync(categoryCreateDto.Name); + if (existing != null) + throw new ConflictException("Category already exists."); + + var categoryEntity = _mapper.Map(categoryCreateDto); + await _categoryRepository.Create(categoryEntity); + } + + public async Task Update(int categoryId, CategoryUpdateDto categoryUpdateDto) + { + var existingCategory = await _categoryRepository.GetByIdAsync(categoryId); + if (existingCategory == null) + throw new ConflictException("Category not found"); + + if (!string.IsNullOrWhiteSpace(categoryUpdateDto.Name)) + { + var name = await _categoryRepository.GetByNameAsync(categoryUpdateDto.Name); + if (name != null && name.Id != categoryId) + throw new ConflictException("Category already exists."); + } + + _mapper.Map(categoryUpdateDto, existingCategory); + await _categoryRepository.Update(existingCategory); + } + public async Task Delete(int categoryId) + { + var existingCategory = await _categoryRepository.GetByIdAsync(categoryId); + if (existingCategory == null) + throw new ConflictException("Category not found"); + + existingCategory.IsDeleted = true; + await _categoryRepository.Delete(existingCategory); + } + + public async Task> GetAll(int pageNumber, int pageSize) + { + var categoryQuery = _categoryRepository.GetCategoryQuery(); + + var categoryCount = await categoryQuery.CountAsync(); + + var catergoryList = await categoryQuery + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize) + .ToListAsync(); + + var categoryDto = _mapper.Map>(catergoryList); + + return new PagedResponse(categoryDto, categoryCount, pageNumber, pageSize); + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Implementation/ProductService.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Implementation/ProductService.cs new file mode 100644 index 00000000..3492d996 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Implementation/ProductService.cs @@ -0,0 +1,88 @@ +using AutoMapper; +using JafnaEcommerceApi.Exceptions; +using JafnaEcommerceApi.Models.DTOs.PaginationDTOs; +using JafnaEcommerceApi.Models.DTOs.ProductDTOs; +using JafnaEcommerceApi.Models.Entities; +using JafnaEcommerceApi.Repositories; +using Microsoft.EntityFrameworkCore; + +namespace JafnaEcommerceApi.Services; + +public class ProductService : IProductService +{ + private readonly IProductRepository _productRepository; + private readonly IMapper _mapper; + + public ProductService(IProductRepository productRepository, IMapper mapper) + { + _productRepository = productRepository; + _mapper = mapper; + } + public async Task CreateAsync(ProductCreateDto productCreateDto) + { + var existingProductName = await _productRepository.GetNameAsync(productCreateDto.Name); + + if (existingProductName != null) + throw new ConflictException("Product already exists."); + + var productEntity = _mapper.Map(productCreateDto); + + await _productRepository.CreateAsync(productEntity); + } + + public async Task UpdateAsync(int productId, ProductUpdateDto productUpdateDto) + { + var existingProductDetails = await _productRepository.GetByIdAsync(productId); + + if (existingProductDetails == null) + throw new ConflictException("Product not found"); + + _mapper.Map(productUpdateDto, existingProductDetails); + + await _productRepository.UpdateAsync(existingProductDetails); + } + + public async Task DeleteAsync(int productId) + { + var existingProductDetails = await _productRepository.GetByIdAsync(productId); + + if (existingProductDetails == null) + throw new ConflictException("Product not found"); + + existingProductDetails.IsDeleted = true; + + await _productRepository.DeleteAsync(existingProductDetails); + } + public async Task> GetAllAsync(int pageNumber, int pageSize) + { + var productQuery = _productRepository.GetProductQuery(); + + var productCount = await productQuery.CountAsync(); + + var productList = await productQuery + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize) + .ToListAsync(); + + var productDto = _mapper.Map>(productList); + + return new PagedResponse(productDto, productCount, pageNumber, pageSize); + } + + public async Task> GetProductsByCategoryAsync(int categoryId, int pageNumber, int pageSize) + { + var categoryProductQuery = _productRepository.GetCategoryProductQuery(categoryId); + + var productCount = await categoryProductQuery.CountAsync(); + + var productList = await categoryProductQuery + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize) + .ToListAsync(); + + var productDto = _mapper.Map>(productList); + + return new PagedResponse(productDto, productCount, pageNumber, pageSize); + + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Implementation/SaleService.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Implementation/SaleService.cs new file mode 100644 index 00000000..bc061e7e --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Implementation/SaleService.cs @@ -0,0 +1,134 @@ +using AutoMapper; +using JafnaEcommerceApi.Exceptions; +using JafnaEcommerceApi.Models.DTOs.PaginationDTOs; +using JafnaEcommerceApi.Models.DTOs.ProductDTOs; +using JafnaEcommerceApi.Models.DTOs.SaleDTOs; +using JafnaEcommerceApi.Models.Entities; +using JafnaEcommerceApi.Repositories; +using JafnaEcommerceApi.Services.Interface; +using Microsoft.EntityFrameworkCore; + +namespace JafnaEcommerceApi.Services.Implementation; + +public class SaleService : ISaleService +{ + private readonly ISaleRepository _saleRepository; + private readonly IMapper _mapper; + private readonly IValidationService _validationService; + private readonly IProductRepository _productRepository; + + public SaleService(ISaleRepository saleRepository, IMapper mapper, IValidationService validationService, IProductRepository productRepository) + { + _saleRepository = saleRepository; + _mapper = mapper; + _validationService = validationService; + _productRepository = productRepository; + + } + + public async Task CreateAsync(SaleCreateDto saleCreateDto) + { + var validatedItems = ValidateAndAggregateItems(saleCreateDto); + + var productIds = validatedItems.Select(x => x.ProductId).ToList(); + + var products = await GetProductsByIdsAsync(productIds); + + var existingIds = products.Select(x => x.Id).ToList(); + + ThrowIfProductsMissing(productIds, existingIds); + + var (saleDetails, totalPrice) = BuildSaleDetailsAndComputeTotal(validatedItems, products); + + await CreateSaleAsync(saleDetails, totalPrice); + } + + private List ValidateAndAggregateItems(SaleCreateDto saleCreateDto) + { + return _validationService.ValidateAndAggregateItems(saleCreateDto); + } + + private async Task> GetProductsByIdsAsync(List productIds) + { + var entities = await _productRepository.GetProductsByIdsAsync(productIds); + return _mapper.Map>(entities); + } + + private void ThrowIfProductsMissing(List requestedIds, List existingIds) + { + var missing = requestedIds.Except(existingIds).ToList(); + if (missing.Any()) + throw new ValidationException($"Products not found: {string.Join(", ", missing)}"); + } + + private (List saleDetails, decimal totalPrice) BuildSaleDetailsAndComputeTotal(List validatedItems, List products) + { + var details = new List(); + decimal total = 0m; + + foreach (var item in validatedItems) + { + var product = products.FirstOrDefault(p => p.Id == item.ProductId); + if (product == null) continue; + + var lineTotal = product.Price * item.ProductQuantity; + total += lineTotal; + + details.Add(new SaleDetailDto + { + ProductId = product.Id, + ProductName = product.Name, + ProductQuantity = item.ProductQuantity, + ProductPrice = product.Price, + ProductTotalPrice = lineTotal, + }); + } + + return (details, total); + } + + private async Task CreateSaleAsync(List saleDetails, decimal totalPrice) + { + var saleId = await _saleRepository.CreateSale(totalPrice); + + var saleDetailEntity = _mapper.Map>(saleDetails); + + foreach (var d in saleDetailEntity) d.SaleId = saleId; + + await _saleRepository.CreateSaleDetails(saleDetailEntity); + } + + public async Task> GetAllSaleAsync(int pageNumber, int pageSize) + { + var salesQuery = _saleRepository.GetSalesQuery(); + + var totalCount = await salesQuery.CountAsync(); + + var salesList = await salesQuery + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize) + .ToListAsync(); + + var salesDto = _mapper.Map>(salesList); + + return new PagedResponse(salesDto, totalCount, pageNumber, pageSize); + } + + public async Task> GetSaleDetailsAsync(int saleId, int pageNumber, int pageSize) + { + var detailsQuery = _saleRepository.GetSaleDetailsQuery(saleId); + + var totalCount = await detailsQuery.CountAsync(); + + var detailsList = await detailsQuery + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize) + .ToListAsync(); + + var detailsDto = _mapper.Map>(detailsList); + + return new PagedResponse(detailsDto, totalCount, pageNumber, pageSize); + } + + +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Implementation/ValidationService.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Implementation/ValidationService.cs new file mode 100644 index 00000000..f74c81e7 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Implementation/ValidationService.cs @@ -0,0 +1,42 @@ +using JafnaEcommerceApi.Models.DTOs.SaleDTOs; +using JafnaEcommerceApi.Exceptions; +using System.Linq; +using JafnaEcommerceApi.Services.Interface; + +namespace JafnaEcommerceApi.Services.Implementation; + +public class ValidationService : IValidationService +{ + public List ValidateAndAggregateItems(SaleCreateDto saleCreateDto) + { + if (saleCreateDto.SaleItems == null || saleCreateDto.SaleItems.Count == 0) + throw new ValidationException("No products in sales details"); + + var aggregated = new List(); + + foreach (var item in saleCreateDto.SaleItems) + { + if (item.ProductId <= 0) + throw new ValidationException("Invalid Product"); + + if (item.ProductQuantity <= 0) + throw new ValidationException("Invalid Product Quantity"); + + var existing = aggregated.FirstOrDefault(x => x.ProductId == item.ProductId); + + if (existing == null) + { + aggregated.Add(new SaleDetailCreateDto + { + ProductId = item.ProductId, + ProductQuantity = item.ProductQuantity + }); + } + else + { + existing.ProductQuantity += item.ProductQuantity; + } + } + return aggregated; + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Interface/ICategoryService.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Interface/ICategoryService.cs new file mode 100644 index 00000000..5e922c37 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Interface/ICategoryService.cs @@ -0,0 +1,12 @@ +using JafnaEcommerceApi.Models.DTOs.CategoryDTOs; +using JafnaEcommerceApi.Models.DTOs.PaginationDTOs; + +namespace JafnaEcommerceApi.Services; + +public interface ICategoryService +{ + Task Create(CategoryCreateDto categoryCreateDto); + Task Update(int categoryId, CategoryUpdateDto categoryUpdateDto); + Task Delete(int categoryId); + Task> GetAll(int pageNumber, int pageSize); +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Interface/IProductService.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Interface/IProductService.cs new file mode 100644 index 00000000..e6b62475 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Interface/IProductService.cs @@ -0,0 +1,13 @@ +using JafnaEcommerceApi.Models.DTOs.ProductDTOs; +using JafnaEcommerceApi.Models.DTOs.PaginationDTOs; + +namespace JafnaEcommerceApi.Services; + +public interface IProductService +{ + Task CreateAsync(ProductCreateDto productCreateDto); + Task UpdateAsync(int categoryId, ProductUpdateDto productUpdateDto); + Task DeleteAsync(int productId); + Task> GetAllAsync(int pageNumber, int pageSize); + Task> GetProductsByCategoryAsync(int categoryId, int pageNumber, int pageSize); +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Interface/ISaleService.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Interface/ISaleService.cs new file mode 100644 index 00000000..4ccb2739 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Interface/ISaleService.cs @@ -0,0 +1,11 @@ +using JafnaEcommerceApi.Models.DTOs.SaleDTOs; +using JafnaEcommerceApi.Models.DTOs.PaginationDTOs; + +namespace JafnaEcommerceApi.Services; + +public interface ISaleService +{ + Task CreateAsync(SaleCreateDto saleCreateDto); + Task> GetAllSaleAsync(int pageNumber, int pageSize); + Task> GetSaleDetailsAsync(int saleId, int pageNumber, int pageSize); +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Interface/IValidationService.cs b/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Interface/IValidationService.cs new file mode 100644 index 00000000..32b14577 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/Services/Interface/IValidationService.cs @@ -0,0 +1,8 @@ +using JafnaEcommerceApi.Models.DTOs.SaleDTOs; + +namespace JafnaEcommerceApi.Services.Interface; + +public interface IValidationService +{ + List ValidateAndAggregateItems(SaleCreateDto saleCreateDto); +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/appsettings.Development.json b/freddypositive.ecomerceapi/JafnaEcommerceApi/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/freddypositive.ecomerceapi/JafnaEcommerceApi/appsettings.json b/freddypositive.ecomerceapi/JafnaEcommerceApi/appsettings.json new file mode 100644 index 00000000..5819103d --- /dev/null +++ b/freddypositive.ecomerceapi/JafnaEcommerceApi/appsettings.json @@ -0,0 +1,30 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=EcommerceDb;Trusted_Connection=True;" + }, + "Serilog": { + "Using": [ "Serilog.Sinks.File" ], + "MinimumLevel": "Information", + "WriteTo": [ + { + "Name": "File", + "Args": { + "path": "logs/log-.txt", + "rollingInterval": "Day", + "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}" + } + } + ] + }, + "ENABLE_LOGGING": true + + + +}