From 574f498975a4dcb264db41fd258b3b1031898627 Mon Sep 17 00:00:00 2001 From: javadnezamdost Date: Wed, 26 Nov 2025 12:35:09 +0400 Subject: [PATCH 1/2] feat:Product --- .../Products/ProductsApplication.cs | 104 ++++++++++++++++++ .../ViewModels/CreateProductViewModel.cs | 21 ++++ .../Products/ViewModels/ProductViewModel.cs | 11 ++ .../ViewModels/UpdateProductViewModel.cs | 13 +++ .../Products/Data/IProductRepository.cs | 17 +++ .../Domain/Aggregates/Products/Product.cs | 54 +++++++++ .../Aggregates/Products/ProductRepository.cs | 50 +++++++++ .../Server/Pages/Panel/Products/Create.cshtml | 42 +++++++ .../Pages/Panel/Products/Create.cshtml.cs | 30 +++++ .../Server/Pages/Panel/Products/Delete.cshtml | 41 +++++++ .../Pages/Panel/Products/Delete.cshtml.cs | 39 +++++++ .../Server/Pages/Panel/Products/Index.cshtml | 37 +++++++ .../Pages/Panel/Products/Index.cshtml.cs | 20 ++++ .../Server/Pages/Panel/Products/Update.cshtml | 42 +++++++ .../Pages/Panel/Products/Update.cshtml.cs | 42 +++++++ 15 files changed, 563 insertions(+) create mode 100644 src/Core/Application/Aggregates/Products/ProductsApplication.cs create mode 100644 src/Core/Application/Aggregates/Products/ViewModels/CreateProductViewModel.cs create mode 100644 src/Core/Application/Aggregates/Products/ViewModels/ProductViewModel.cs create mode 100644 src/Core/Application/Aggregates/Products/ViewModels/UpdateProductViewModel.cs create mode 100644 src/Core/Domain/Aggregates/Products/Data/IProductRepository.cs create mode 100644 src/Core/Domain/Aggregates/Products/Product.cs create mode 100644 src/Persistence/Persistence/Repositories/Aggregates/Products/ProductRepository.cs create mode 100644 src/Presentation/Server/Pages/Panel/Products/Create.cshtml create mode 100644 src/Presentation/Server/Pages/Panel/Products/Create.cshtml.cs create mode 100644 src/Presentation/Server/Pages/Panel/Products/Delete.cshtml create mode 100644 src/Presentation/Server/Pages/Panel/Products/Delete.cshtml.cs create mode 100644 src/Presentation/Server/Pages/Panel/Products/Index.cshtml create mode 100644 src/Presentation/Server/Pages/Panel/Products/Index.cshtml.cs create mode 100644 src/Presentation/Server/Pages/Panel/Products/Update.cshtml create mode 100644 src/Presentation/Server/Pages/Panel/Products/Update.cshtml.cs diff --git a/src/Core/Application/Aggregates/Products/ProductsApplication.cs b/src/Core/Application/Aggregates/Products/ProductsApplication.cs new file mode 100644 index 0000000..744de7b --- /dev/null +++ b/src/Core/Application/Aggregates/Products/ProductsApplication.cs @@ -0,0 +1,104 @@ +using Application.Aggregates.Products.ViewModels; +using Application.Aggregates.Stores.ViewModels; +using Domain.Aggregates.Products; +using Domain.Aggregates.Products.Data; +using Domain.Aggregates.Stores; +using Mapster; +using Persistence.Repositories.Aggregates.Stores; +using Resources.Messages; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Aggregates.Products; + +public class ProductsApplication(IProductRepository productRepository) +{ + public async Task CreateAsync(CreateProductViewModel productViewModel) + { + var product = Product.Create( + name: productViewModel.Name, + description: productViewModel.Description, + price: productViewModel.Price, + quantity: productViewModel.Quantity); + + if (productViewModel.IsActive == true) + { + product.Activate(); + } + + await productRepository.AddAsync(product); + await productRepository.SaveChangesAsync(); + + return product.Adapt(); + } + + public async Task> GetStors() + { + var products = + await productRepository.GetAllAsync(); + + return products.Adapt>(); + } + + public async Task GetAsync(Guid productId) + { + var product = + await productRepository.GetAsync(productId); + + return product.Adapt(); + } + + public async Task UpdateAsync(UpdateProductViewModel updateProductView) + { + var update = + await productRepository.GetAsync(updateProductView.Id); + + if (update == null || updateProductView.Id == Guid.Empty) + { + var error = + string.Format(Errors.NotFound, Resources.DataDictionary.Store); + + throw new Exception(error); + } + + update.Update( + name: updateProductView.Name, + description: updateProductView.Description, + price: updateProductView.Price, + quantity: updateProductView.Quantity); + + if (update.IsActive == true) + { + update.Activate(); + } + else + { + update.Deactivate(); + } + + await productRepository.SaveChangesAsync(); + + return update.Adapt(); + } + + public async Task DeleteAsync(Guid productId) + { + var delete = + await productRepository.GetAsync(productId); + + if (delete == null || delete.Id == Guid.Empty) + { + var error = + string.Format(Errors.NotFound, Resources.DataDictionary.Store); + + throw new Exception(error); + } + + productRepository.Remove(delete); + + await productRepository.SaveChangesAsync(); + } +} diff --git a/src/Core/Application/Aggregates/Products/ViewModels/CreateProductViewModel.cs b/src/Core/Application/Aggregates/Products/ViewModels/CreateProductViewModel.cs new file mode 100644 index 0000000..994270f --- /dev/null +++ b/src/Core/Application/Aggregates/Products/ViewModels/CreateProductViewModel.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Aggregates.Products.ViewModels; + +public class CreateProductViewModel +{ + public CreateProductViewModel() + { + + } + + public string Name { get; set; } + public string Description { get; set; } + public int Price { get; set; } + public int Quantity { get; set; } + public bool IsActive { get; set; } +} diff --git a/src/Core/Application/Aggregates/Products/ViewModels/ProductViewModel.cs b/src/Core/Application/Aggregates/Products/ViewModels/ProductViewModel.cs new file mode 100644 index 0000000..1a4f326 --- /dev/null +++ b/src/Core/Application/Aggregates/Products/ViewModels/ProductViewModel.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Aggregates.Products.ViewModels; + +public class ProductViewModel : UpdateProductViewModel +{ +} diff --git a/src/Core/Application/Aggregates/Products/ViewModels/UpdateProductViewModel.cs b/src/Core/Application/Aggregates/Products/ViewModels/UpdateProductViewModel.cs new file mode 100644 index 0000000..0a3a393 --- /dev/null +++ b/src/Core/Application/Aggregates/Products/ViewModels/UpdateProductViewModel.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Application.Aggregates.Products.ViewModels; + +public class UpdateProductViewModel:CreateProductViewModel +{ + public Guid Id { get; set; } + +} diff --git a/src/Core/Domain/Aggregates/Products/Data/IProductRepository.cs b/src/Core/Domain/Aggregates/Products/Data/IProductRepository.cs new file mode 100644 index 0000000..90468d0 --- /dev/null +++ b/src/Core/Domain/Aggregates/Products/Data/IProductRepository.cs @@ -0,0 +1,17 @@ +using Domain.Aggregates.Stores; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Domain.Aggregates.Products.Data; + +public interface IProductRepository +{ + Task AddAsync(Product product); + Task> GetAllAsync(); + Task GetAsync(Guid productId); + void Remove(Product product); + Task SaveChangesAsync(); +} \ No newline at end of file diff --git a/src/Core/Domain/Aggregates/Products/Product.cs b/src/Core/Domain/Aggregates/Products/Product.cs new file mode 100644 index 0000000..641e7b3 --- /dev/null +++ b/src/Core/Domain/Aggregates/Products/Product.cs @@ -0,0 +1,54 @@ +using Domain.Aggregates.Stores; +using Domain.SeedWork; +using Framework.DataType; +using Resources.Messages; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Domain.Aggregates.Products; + +public class Product : Entity +{ + public Product() + { + ////********** + } + + private Product(string name, string description, int price, int quantity) + { + Name = name; + Description = description; + Price = price; + Quantity = quantity; + } + + public string Name { get; set; } + public string Description { get; set; } + public int Price { get; set; } + public int Quantity { get; set; } + + + public static Product Create(string name, string description, int price, int quantity) + { + var store = new Product( + name: name.Fix() ?? "", + description: description.Fix() ?? "", + price: price, + quantity: quantity); + + return store; + } + + public void Update(string name, string description, int price, int quantity) + { + Name = name.Fix() ?? ""; + Description = description.Fix() ?? ""; + Price = price; + Quantity = quantity; + + SetUpdateDateTime(); + } +} diff --git a/src/Persistence/Persistence/Repositories/Aggregates/Products/ProductRepository.cs b/src/Persistence/Persistence/Repositories/Aggregates/Products/ProductRepository.cs new file mode 100644 index 0000000..d7516d8 --- /dev/null +++ b/src/Persistence/Persistence/Repositories/Aggregates/Products/ProductRepository.cs @@ -0,0 +1,50 @@ +using Domain.Aggregates.Products.Data; +using Domain.Aggregates.Stores; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Domain.Aggregates.Products; +using Microsoft.EntityFrameworkCore; + +namespace Persistence.Repositories.Aggregates.Products; + +public class ProductRepository(DatabaseContext databaseContext):IProductRepository +{ + public async Task AddAsync(Product product) + { + await databaseContext.AddAsync(product); + } + + public async Task> GetAllAsync() + { + var products = + await databaseContext.Products + .Where(x => x.IsDeleted == false) + .ToListAsync(); + + return products; + } + + public async Task GetAsync(Guid productId) + { + var product = + await databaseContext.Products + .Where(x => x.IsDeleted == false) + .Where(x => x.Id == productId) + .FirstOrDefaultAsync(); + + return product; + } + + public void Remove(Product product) + { + product.Delete(); + } + + public async Task SaveChangesAsync() + { + await databaseContext.SaveChangesAsync(); + } +} diff --git a/src/Presentation/Server/Pages/Panel/Products/Create.cshtml b/src/Presentation/Server/Pages/Panel/Products/Create.cshtml new file mode 100644 index 0000000..8a03f75 --- /dev/null +++ b/src/Presentation/Server/Pages/Panel/Products/Create.cshtml @@ -0,0 +1,42 @@ +@page +@model Server.Pages.Panel.Products.CreateModel +@{ + var pageTitle = + $"{Resources.DataDictionary.CreateOf} {Resources.DataDictionary.Product}"; + + ViewData[Constants.ViewDataKeyName.PageTitle] = pageTitle; +} +} +
+ + + +
+ + + @(pageTitle) + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
diff --git a/src/Presentation/Server/Pages/Panel/Products/Create.cshtml.cs b/src/Presentation/Server/Pages/Panel/Products/Create.cshtml.cs new file mode 100644 index 0000000..085a904 --- /dev/null +++ b/src/Presentation/Server/Pages/Panel/Products/Create.cshtml.cs @@ -0,0 +1,30 @@ +using Application.Aggregates.Products; +using Application.Aggregates.Products.ViewModels; +using Application.Aggregates.Stores; +using Application.Aggregates.Stores.ViewModels; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Server.Pages.Panel.Products; + +public class CreateModel(ProductsApplication productsApplication) : PageModel +{ + [BindProperty] + public CreateProductViewModel createProductView { get; set; } + + public void OnGet() + { + } + + public async Task OnPostAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + await productsApplication.CreateAsync(createProductView); + + return RedirectToPage("Index"); + } +} diff --git a/src/Presentation/Server/Pages/Panel/Products/Delete.cshtml b/src/Presentation/Server/Pages/Panel/Products/Delete.cshtml new file mode 100644 index 0000000..17d70b2 --- /dev/null +++ b/src/Presentation/Server/Pages/Panel/Products/Delete.cshtml @@ -0,0 +1,41 @@ +@page +@model Server.Pages.Panel.Products.DeleteModel +@{ + var pageTitle = + $"{Resources.DataDictionary.CreateOf} {Resources.DataDictionary.Product}"; + + ViewData[Constants.ViewDataKeyName.PageTitle] = pageTitle; +} +
+ + + + + +
+ + + @(pageTitle) + + + + + + + + + + + + + + +
+ + + + + +
+ +
\ No newline at end of file diff --git a/src/Presentation/Server/Pages/Panel/Products/Delete.cshtml.cs b/src/Presentation/Server/Pages/Panel/Products/Delete.cshtml.cs new file mode 100644 index 0000000..a3ae369 --- /dev/null +++ b/src/Presentation/Server/Pages/Panel/Products/Delete.cshtml.cs @@ -0,0 +1,39 @@ +using Application.Aggregates.Products; +using Application.Aggregates.Products.ViewModels; +using Application.Aggregates.Stores; +using Application.Aggregates.Stores.ViewModels; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Server.Pages.Panel.Products; + +public class DeleteModel(ProductsApplication productsApplication) : PageModel +{ + [BindProperty] + public ProductViewModel DeleteViewModel { get; set; } = new(); + + public async Task OnGetAsync(Guid id) + { + if (id == Guid.Empty) + { + return RedirectToPage("Index"); + } + + DeleteViewModel = + await productsApplication.GetAsync(id); + + if (DeleteViewModel == null) + { + return RedirectToPage("Index"); + } + + return Page(); + } + + public async Task OnPostAsync() + { + await productsApplication.DeleteAsync(DeleteViewModel.Id); + + return RedirectToPage("Index"); + } +} diff --git a/src/Presentation/Server/Pages/Panel/Products/Index.cshtml b/src/Presentation/Server/Pages/Panel/Products/Index.cshtml new file mode 100644 index 0000000..fe3357c --- /dev/null +++ b/src/Presentation/Server/Pages/Panel/Products/Index.cshtml @@ -0,0 +1,37 @@ +@page +@model Server.Pages.Panel.Products.IndexModel +@{ +} + + + + + @(Html.Ub_DisplayStringWithTh(Resources.DataDictionary.Name)) + @(Html.Ub_DisplayStringWithTh(Resources.DataDictionary.Description)) + @(Html.Ub_DisplayStringWithTh(Resources.DataDictionary.Price)) + @(Html.Ub_DisplayStringWithTh(Resources.DataDictionary.Quantity)) + @(Html.Ub_DisplayStringWithTh(Resources.DataDictionary.IsActive)) + + + + + @foreach (var item in Model.product) + { + + @(Html.Ub_DisplayStringWithTd(item.Name)) + @(Html.Ub_DisplayStringWithTd(item.Description)) + @(Html.Ub_DisplayIntegerWithTd(item.Price)) + @(Html.Ub_DisplayIntegerWithTd(item.Quantity)) + @(Html.Ub_DisplayBooleanWithTd(item.IsActive)) + + + } + +
+ +
diff --git a/src/Presentation/Server/Pages/Panel/Products/Index.cshtml.cs b/src/Presentation/Server/Pages/Panel/Products/Index.cshtml.cs new file mode 100644 index 0000000..7285de6 --- /dev/null +++ b/src/Presentation/Server/Pages/Panel/Products/Index.cshtml.cs @@ -0,0 +1,20 @@ +using Application.Aggregates.Products; +using Application.Aggregates.Products.ViewModels; +using Application.Aggregates.Stores; +using Application.Aggregates.Stores.ViewModels; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Server.Pages.Panel.Products +{ + public class IndexModel(ProductsApplication productsApplication) : PageModel + { + public List product { get; set; } = new(); + + public async Task OnGetAsync() + { + product = await productsApplication.GetStors(); + } + } + +} diff --git a/src/Presentation/Server/Pages/Panel/Products/Update.cshtml b/src/Presentation/Server/Pages/Panel/Products/Update.cshtml new file mode 100644 index 0000000..6827f31 --- /dev/null +++ b/src/Presentation/Server/Pages/Panel/Products/Update.cshtml @@ -0,0 +1,42 @@ +@page +@model Server.Pages.Panel.Products.UpdateModel +@{ + var pageTitle = + $"{Resources.DataDictionary.CreateOf} {Resources.DataDictionary.Product}"; + + ViewData[Constants.ViewDataKeyName.PageTitle] = pageTitle; +} +
+ + + +
+ + + @(pageTitle) + + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
diff --git a/src/Presentation/Server/Pages/Panel/Products/Update.cshtml.cs b/src/Presentation/Server/Pages/Panel/Products/Update.cshtml.cs new file mode 100644 index 0000000..56877b6 --- /dev/null +++ b/src/Presentation/Server/Pages/Panel/Products/Update.cshtml.cs @@ -0,0 +1,42 @@ +using Application.Aggregates.Products; +using Application.Aggregates.Products.ViewModels; +using Application.Aggregates.Stores; +using Application.Aggregates.Stores.ViewModels; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Server.Pages.Panel.Products; + +public class UpdateModel(ProductsApplication productsApplication) : PageModel +{ + [BindProperty] + public ProductViewModel UpdateViewModel { get; set; } + + public async Task OnGetAsync(Guid id) + { + if (id == Guid.Empty) + { + return RedirectToPage("Index"); + } + + UpdateViewModel = + await productsApplication.GetAsync(id); + + if (UpdateViewModel == null) + { + return RedirectToPage("Index"); + } + + return Page(); + } + + public async Task OnPostAsync() + { + if (ModelState.IsValid) + { + await productsApplication.UpdateAsync(UpdateViewModel); + } + + return RedirectToPage("Index"); + } +} -- 2.49.0 From 5680ae826531379c370fa50793608a23d34b86bb Mon Sep 17 00:00:00 2001 From: javadnezamdost Date: Fri, 28 Nov 2025 16:23:07 +0400 Subject: [PATCH 2/2] fix:Product --- .../Application/Aggregates/Products/ProductsApplication.cs | 2 +- src/Persistence/Persistence/DatabaseContext.cs | 2 ++ src/Presentation/Server/Pages/Index.cshtml | 1 + src/Presentation/Server/Program.cs | 6 ++++++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Core/Application/Aggregates/Products/ProductsApplication.cs b/src/Core/Application/Aggregates/Products/ProductsApplication.cs index 744de7b..1f4f64b 100644 --- a/src/Core/Application/Aggregates/Products/ProductsApplication.cs +++ b/src/Core/Application/Aggregates/Products/ProductsApplication.cs @@ -70,7 +70,7 @@ public class ProductsApplication(IProductRepository productRepository) price: updateProductView.Price, quantity: updateProductView.Quantity); - if (update.IsActive == true) + if (updateProductView.IsActive == true) { update.Activate(); } diff --git a/src/Persistence/Persistence/DatabaseContext.cs b/src/Persistence/Persistence/DatabaseContext.cs index 31fc3b8..072ed50 100644 --- a/src/Persistence/Persistence/DatabaseContext.cs +++ b/src/Persistence/Persistence/DatabaseContext.cs @@ -1,5 +1,6 @@ using Domain.Aggregates.Stores; using Domain.Aggregates.Users; +using Domain.Aggregates.Products; using Microsoft.EntityFrameworkCore; namespace Persistence; @@ -14,5 +15,6 @@ public class DatabaseContext: DbContext public DbSet Users { get; set; } public DbSet Stores { get; set; } + public DbSet Products { get; set; } } diff --git a/src/Presentation/Server/Pages/Index.cshtml b/src/Presentation/Server/Pages/Index.cshtml index 5042f38..1365887 100644 --- a/src/Presentation/Server/Pages/Index.cshtml +++ b/src/Presentation/Server/Pages/Index.cshtml @@ -8,4 +8,5 @@

Pages

[ Users ] [ Stores ] + [ Products ] diff --git a/src/Presentation/Server/Program.cs b/src/Presentation/Server/Program.cs index 059aa92..fc359f7 100644 --- a/src/Presentation/Server/Program.cs +++ b/src/Presentation/Server/Program.cs @@ -1,9 +1,12 @@ +using Application.Aggregates.Products; using Application.Aggregates.Stores; using Application.Aggregates.Users; +using Domain.Aggregates.Products.Data; using Domain.Aggregates.Stores.Data; using Domain.Aggregates.Users.Data; using Microsoft.EntityFrameworkCore; using Persistence; +using Persistence.Repositories.Aggregates.Products; using Persistence.Repositories.Aggregates.Stores; using Persistence.Repositories.Aggregates.Users; using Server.Infrastructure.Extensions.ServiceCollections; @@ -34,6 +37,9 @@ public class Program builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + var app = builder.Build(); if (!app.Environment.IsDevelopment()) -- 2.49.0