From 556407fcaf06beaf4469aae98f1ec89e237bf42e Mon Sep 17 00:00:00 2001 From: Lepros311 Date: Mon, 25 Aug 2025 19:59:20 -0400 Subject: [PATCH 1/2] Project finished --- Ecommerce.Api.Lepros311/.gitignore | 484 +++++++++++ .../Ecommerce API.postman_collection.json | 765 ++++++++++++++++++ Ecommerce.Api.Lepros311/Ecommerce.Api.sln | 25 + .../Controllers/CategoryController.cs | 119 +++ .../Controllers/LineItemController.cs | 112 +++ .../Controllers/ProductController.cs | 109 +++ .../Controllers/SaleController.cs | 117 +++ .../Ecommerce.Api/Data/EcommerceDbContext.cs | 197 +++++ .../Ecommerce.Api/Ecommerce.Api.csproj | 25 + .../Ecommerce.Api/Ecommerce.Api.http | 6 + .../20250811003905_InitialCreate.Designer.cs | 351 ++++++++ .../20250811003905_InitialCreate.cs | 178 ++++ ...15003638_AddIsDeletedToProduct.Designer.cs | 362 +++++++++ .../20250815003638_AddIsDeletedToProduct.cs | 85 ++ ...efactorFromAnnotationsToFluent.Designer.cs | 362 +++++++++ ...7141236_RefactorFromAnnotationsToFluent.cs | 22 + ...7233457_AddIsDeletedToCategory.Designer.cs | 369 +++++++++ .../20250817233457_AddIsDeletedToCategory.cs | 57 ++ ...433_DeclareCategoryFKOnProduct.Designer.cs | 369 +++++++++ ...250818000433_DeclareCategoryFKOnProduct.cs | 22 + ...222829_AddIsDeletedToSaleModel.Designer.cs | 377 +++++++++ .../20250821222829_AddIsDeletedToSaleModel.cs | 64 ++ ...1231008_AddCascadeOnDeleteSale.Designer.cs | 377 +++++++++ .../20250821231008_AddCascadeOnDeleteSale.cs | 22 + ...1346_RemoveCascadeOnDeleteSale.Designer.cs | 377 +++++++++ ...0250821231346_RemoveCascadeOnDeleteSale.cs | 22 + ...25_AddIsDeletedToLineItemModel.Designer.cs | 389 +++++++++ ...50821231525_AddIsDeletedToLineItemModel.cs | 92 +++ ...AddPriceAndQuantityConstraints.Designer.cs | 395 +++++++++ ...21233712_AddPriceAndQuantityConstraints.cs | 36 + ...ngePriceCheckToGreaterThanZero.Designer.cs | 395 +++++++++ ...30010_ChangePriceCheckToGreaterThanZero.cs | 36 + .../EcommerceDbContextModelSnapshot.cs | 392 +++++++++ .../Ecommerce.Api/Models/Category.cs | 14 + .../Ecommerce.Api/Models/CategoryDto.cs | 10 + .../Ecommerce.Api/Models/LineItem.cs | 20 + .../Ecommerce.Api/Models/LineItemDto.cs | 18 + .../Ecommerce.Api/Models/PaginationParams.cs | 41 + .../Ecommerce.Api/Models/Product.cs | 18 + .../Ecommerce.Api/Models/ProductDto.cs | 17 + .../Ecommerce.Api/Models/Sale.cs | 16 + .../Ecommerce.Api/Models/SaleDto.cs | 14 + .../Models/UpdateLineItemOnSaleDto.cs | 10 + .../Ecommerce.Api/Models/UpdateSaleDto.cs | 6 + .../Ecommerce.Api/Models/WriteCategoryDto.cs | 6 + .../Ecommerce.Api/Models/WriteLineItemDto.cs | 10 + .../Models/WriteLineItemOnSaleDto.cs | 8 + .../Ecommerce.Api/Models/WriteProductDto.cs | 15 + .../Ecommerce.Api/Models/WriteSaleDto.cs | 6 + .../Ecommerce.Api/Program.cs | 41 + .../Properties/launchSettings.json | 25 + .../Repository/CategoryRepository.cs | 179 ++++ .../Repository/ICategoryRepository.cs | 17 + .../Repository/ILineItemRepository.cs | 17 + .../Repository/IProductRepository.cs | 21 + .../Repository/ISaleRepository.cs | 17 + .../Repository/LineItemRepository.cs | 209 +++++ .../Repository/ProductRepository.cs | 211 +++++ .../Repository/SaleRepository.cs | 203 +++++ .../Ecommerce.Api/Responses/BaseResponse.cs | 10 + .../Ecommerce.Api/Responses/PagedResponse.cs | 24 + .../Ecommerce.Api/Responses/ResponseStatus.cs | 7 + .../Ecommerce.Api/Services/CategoryService.cs | 134 +++ .../Services/ICategoryService.cs | 18 + .../Services/ILineItemService.cs | 17 + .../Ecommerce.Api/Services/IProductService.cs | 17 + .../Ecommerce.Api/Services/ISaleService.cs | 19 + .../Ecommerce.Api/Services/LineItemService.cs | 191 +++++ .../Ecommerce.Api/Services/ProductService.cs | 163 ++++ .../Ecommerce.Api/Services/SaleService.cs | 248 ++++++ .../appsettings.Development.json | 8 + .../Ecommerce.Api/appsettings.json | 10 + Ecommerce.Api.Lepros311/README.md | 18 + 73 files changed, 9163 insertions(+) create mode 100644 Ecommerce.Api.Lepros311/.gitignore create mode 100644 Ecommerce.Api.Lepros311/Ecommerce API.postman_collection.json create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api.sln create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Controllers/CategoryController.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Controllers/LineItemController.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Controllers/ProductController.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Controllers/SaleController.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Data/EcommerceDbContext.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Ecommerce.Api.csproj create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Ecommerce.Api.http create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250811003905_InitialCreate.Designer.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250811003905_InitialCreate.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250815003638_AddIsDeletedToProduct.Designer.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250815003638_AddIsDeletedToProduct.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250817141236_RefactorFromAnnotationsToFluent.Designer.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250817141236_RefactorFromAnnotationsToFluent.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250817233457_AddIsDeletedToCategory.Designer.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250817233457_AddIsDeletedToCategory.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250818000433_DeclareCategoryFKOnProduct.Designer.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250818000433_DeclareCategoryFKOnProduct.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821222829_AddIsDeletedToSaleModel.Designer.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821222829_AddIsDeletedToSaleModel.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231008_AddCascadeOnDeleteSale.Designer.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231008_AddCascadeOnDeleteSale.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231346_RemoveCascadeOnDeleteSale.Designer.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231346_RemoveCascadeOnDeleteSale.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231525_AddIsDeletedToLineItemModel.Designer.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231525_AddIsDeletedToLineItemModel.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821233712_AddPriceAndQuantityConstraints.Designer.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821233712_AddPriceAndQuantityConstraints.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250822230010_ChangePriceCheckToGreaterThanZero.Designer.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250822230010_ChangePriceCheckToGreaterThanZero.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/EcommerceDbContextModelSnapshot.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Models/Category.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Models/CategoryDto.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Models/LineItem.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Models/LineItemDto.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Models/PaginationParams.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Models/Product.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Models/ProductDto.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Models/Sale.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Models/SaleDto.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Models/UpdateLineItemOnSaleDto.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Models/UpdateSaleDto.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteCategoryDto.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteLineItemDto.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteLineItemOnSaleDto.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteProductDto.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteSaleDto.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Program.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Properties/launchSettings.json create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/CategoryRepository.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ICategoryRepository.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ILineItemRepository.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/IProductRepository.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ISaleRepository.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/LineItemRepository.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ProductRepository.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/SaleRepository.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Responses/BaseResponse.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Responses/PagedResponse.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Responses/ResponseStatus.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Services/CategoryService.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ICategoryService.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ILineItemService.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Services/IProductService.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ISaleService.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Services/LineItemService.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ProductService.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/Services/SaleService.cs create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/appsettings.Development.json create mode 100644 Ecommerce.Api.Lepros311/Ecommerce.Api/appsettings.json create mode 100644 Ecommerce.Api.Lepros311/README.md diff --git a/Ecommerce.Api.Lepros311/.gitignore b/Ecommerce.Api.Lepros311/.gitignore new file mode 100644 index 00000000..bc78471d --- /dev/null +++ b/Ecommerce.Api.Lepros311/.gitignore @@ -0,0 +1,484 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from `dotnet new gitignore` + +# dotenv files +.env + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea/ + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/main/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/main/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp diff --git a/Ecommerce.Api.Lepros311/Ecommerce API.postman_collection.json b/Ecommerce.Api.Lepros311/Ecommerce API.postman_collection.json new file mode 100644 index 00000000..09ae17b4 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce API.postman_collection.json @@ -0,0 +1,765 @@ +{ + "info": { + "_postman_id": "a41fbdd1-f8b1-49f0-be65-b1e128c40f8e", + "name": "Ecommerce API", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "45055274", + "_collection_link": "https://andrewstrickland.postman.co/workspace/Andrew-Strickland's-Workspace~bbe62028-fd77-4407-88e4-a7c7ff76f1df/collection/45055274-a41fbdd1-f8b1-49f0-be65-b1e128c40f8e?action=share&source=collection_link&creator=45055274" + }, + "item": [ + { + "name": "GetPagedCategories", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseURL}}/api/categories?page=1&pageSize=10", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "categories" + ], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "pageSize", + "value": "10" + }, + { + "key": "categoryname", + "value": "pants" + } + ] + } + }, + "response": [], + "description": "Retrieve a paginated list of categories with optional search and sort parameters.", + "parameters": { + "categoryname": { + "description": "Search term to filter categories by their name.", + "example": "pants" + }, + "sortBy": { + "description": "Field to sort the results by. Acceptable values: 'categoryname' and 'categoryid'. Default sort is by categoryId, descending. Other sortBy values default sort ascending.", + "example": "categoryname" + }, + "sortAscending": { + "description": "Control the sort direction. Default is ascending unless the sortBy is categoryid.", + "example": "false" + }, + "page": { + "description": "Page number for pagination. Default is 1.", + "example": "1" + }, + "pageSize": { + "description": "Number of results per page. Default is 10", + "example": "10" + } + } + }, + { + "name": "GetCategoryById", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseURL}}/api/categories/{{CategoryId}}", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "categories", + "{{CategoryId}}" + ] + } + }, + "response": [] + }, + { + "name": "CreateCategory", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"categoryName\": \"{{CategoryName}}\"\r\n}" + }, + "url": { + "raw": "{{baseURL}}/api/categories", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "UpdateCategory", + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"categoryName\": \"{{CategoryName}}\"\r\n}" + }, + "url": { + "raw": "{{baseURL}}/api/categories/{{CategoryId}}", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "categories", + "{{CategoryId}}" + ] + } + }, + "response": [] + }, + { + "name": "DeleteCategory", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseURL}}/api/categories/{{CategoryId}}", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "categories", + "{{CategoryId}}" + ] + } + }, + "response": [] + }, + { + "name": "GetPagedProducts", + "request": { + "method": "GET", + "header": [ + { + "key": "page", + "value": "1", + "type": "text" + }, + { + "key": "pageSize", + "value": "10", + "type": "text" + } + ], + "url": { + "raw": "{{baseURL}}/api/products?page=1&pageSize=10", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "products" + ], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "pageSize", + "value": "10" + }, + { + "key": "productname", + "value": "jeans" + }, + { + "key": "categoryname", + "value": "pants" + }, + { + "key": "minprice", + "value": "10" + }, + { + "key": "maxprice", + "value": "75" + } + ] + } + }, + "response": [], + "description": "Retrieve a paginated list of products with optional search and sort parameters.", + "parameters": { + "productname": { + "description": "Search term to filter products by their name.", + "example": "jeans" + }, + "categoryname": { + "description": "Search term to filter products by their category name.", + "example": "pants" + }, + "minprice": { + "description": "Return products with price equal to or higher than a given number.", + "example": "10" + }, + "maxprice": { + "description": "Return products with price equal to or lower than a given number.", + "example": "75" + }, + "sortBy": { + "description": "Field to sort the results by. Acceptable values: 'productname', 'categoryname', 'price', or ''productid'. Default sort is by productid, descending. Other sortBy values default sort ascending.", + "example": "productname" + }, + "sortAscending": { + "description": "Control the sort direction. Default is ascending unless the sortBy is productid.", + "example": "false" + }, + "page": { + "description": "Page number for pagination. Default is 1.", + "example": "1" + }, + "pageSize": { + "description": "Number of results per page. Default is 10", + "example": "10" + } + } + }, + { + "name": "GetProductById", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseURL}}/api/products/{{ProductId}}", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "products", + "{{ProductId}}" + ] + } + }, + "response": [] + }, + { + "name": "CreateProduct", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"productName\": \"{{ProductName}}\",\r\n \"price\": {{Price}},\r\n \"categoryId\": {{CategoryId}}\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseURL}}/api/products", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "products" + ] + } + }, + "response": [] + }, + { + "name": "UpdateProduct", + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"productName\": \"{{ProductName}}\",\r\n \"price\": {{Price}},\r\n \"categoryId\": {{CategoryId}}\r\n}" + }, + "url": { + "raw": "{{baseURL}}/api/products/{{ProductId}}", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "products", + "{{ProductId}}" + ] + } + }, + "response": [] + }, + { + "name": "DeleteProduct", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseURL}}/api/products/{{ProductId}}", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "products", + "{{ProductId}}" + ] + } + }, + "response": [] + }, + { + "name": "GetPagedSales", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseURL}}/api/sales?page=1&pageSize=10", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "sales" + ], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "pageSize", + "value": "10" + }, + { + "key": "minprice", + "value": "10" + }, + { + "key": "maxprice", + "value": "75" + }, + { + "key": "lineitemid", + "value": "8" + }, + { + "key": "minlineitems", + "value": "2" + }, + { + "key": "maxlineitems", + "value": "4" + } + ] + } + }, + "response": [], + "description": "Retrieve a paginated list of sales with optional search and sort parameters.", + "parameters": { + "minprice": { + "description": "Return sales with total price equal to or higher than a given number.", + "example": "10" + }, + "maxprice": { + "description": "Return sales with total price equal to or lower than a given number.", + "example": "75" + }, + "lineitemid": { + "description": "Return sale with the matching line item id", + "example": "8" + }, + "minlineitems": { + "description": "Return sales with number of line items that are equal to or higher than the given number.", + "example": "2" + }, + "maxlineitems": { + "description": "Return sales with number of line items that are equal to or lower than the given number.", + "example": "2" + }, + "sortBy": { + "description": "Field to sort the results by. Acceptable values: 'totalprice', 'lineitemcount', or 'saleid'. Default sort is by saleid, descending. Other sortBy values default sort ascending.", + "example": "totalprice" + }, + "sortAscending": { + "description": "Control the sort direction. Default is ascending unless the sortBy is saleid.", + "example": "false" + }, + "page": { + "description": "Page number for pagination. Default is 1.", + "example": "1" + }, + "pageSize": { + "description": "Number of results per page. Default is 10", + "example": "10" + } + } + }, + { + "name": "GetSaleById", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseURL}}/api/sales/{{SaleId}}", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "sales", + "{{SaleId}}" + ] + } + }, + "response": [] + }, + { + "name": "CreateSale", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"lineItems\": [\r\n {\r\n \"productId\": {{ProductId}},\r\n \"quantity\": {{Quantity}}\r\n }\r\n ]\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseURL}}/api/sales", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "sales" + ] + } + }, + "response": [] + }, + { + "name": "UpdateSale", + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"lineItems\": [\r\n {\r\n \"lineItemId\": {{LineItemId}},\r\n \"productId\": {{ProductId}},\r\n \"quantity\": {{Quantity}}\r\n }\r\n ]\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseURL}}/api/sales/{{SaleId}}", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "sales", + "{{SaleId}}" + ] + } + }, + "response": [] + }, + { + "name": "DeleteSale", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseURL}}/api/sales/{{SaleId}}", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "sales", + "{{SaleId}}" + ] + } + }, + "response": [] + }, + { + "name": "GetPagedLineItems", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseURL}}/api/lineitems?page=1&pageSize=10", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "lineitems" + ], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "pageSize", + "value": "10" + }, + { + "key": "productname", + "value": "jeans" + }, + { + "key": "categoryname", + "value": "pants" + }, + { + "key": "minprice", + "value": "10" + }, + { + "key": "maxprice", + "value": "75" + }, + { + "key": "minquantity", + "value": "2" + }, + { + "key": "maxquantity", + "value": "4" + }, + { + "key": "productid", + "value": "7" + }, + { + "key": "saleid", + "value": "9" + } + ] + } + }, + "response": [], + "description": "Retrieve a paginated list of line items with optional search and sort parameters.", + "parameters": { + "productname": { + "description": "Return line items that contain a matching string in the product name.", + "example": "jeans" + }, + "categoryname": { + "description": "Return line items that contain a matching string in the category name.", + "example": "pants" + }, + "minprice": { + "description": "Return line items with a unit price equal to or higher than a given price.", + "example": "10" + }, + "maxprice": { + "description": "Return line items with a unit price equal to or lower than a given price.", + "example": "75" + }, + "minquantity": { + "description": "Return line items with a quantity equal to or higher than a given price.", + "example": "2" + }, + "maxquantity": { + "description": "Return line items with a quantity equal to or lower than a given price.", + "example": "3" + }, + "productid": { + "description": "Return line items with the matching product id", + "example": "8" + }, + "saleid": { + "description": "Return line items with the matching sale id", + "example": "7" + }, + "sortBy": { + "description": "Field to sort the results by. Acceptable values: 'productname', 'categoryname', 'unitprice', 'quantity', 'productid', or 'saleid'. Default sort is by lineitemid, descending. Other sortBy values default sort ascending.", + "example": "totalprice" + }, + "sortAscending": { + "description": "Control the sort direction. Default is ascending unless the sortBy is lineitemid.", + "example": "false" + }, + "page": { + "description": "Page number for pagination. Default is 1.", + "example": "1" + }, + "pageSize": { + "description": "Number of results per page. Default is 10", + "example": "10" + } + } + }, + { + "name": "GetLineItemById", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseURL}}/api/lineitems/{{LineItemId}}", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "lineitems", + "{{LineItemId}}" + ] + } + }, + "response": [] + }, + { + "name": "CreateLineItem", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"productId\": {{ProductId}},\r\n \"quantity\": {{Quantity}},\r\n \"saleId\": {{SaledId}}\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseURL}}/api/lineitems", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "lineitems" + ] + } + }, + "response": [] + }, + { + "name": "UpdateLineItem", + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"productId\": {{ProductId}},\r\n \"quantity\": {{Quantity}},\r\n \"saleId\": {{SaledId}}\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseURL}}/api/lineitems/{{LineItemId}}", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "lineitems", + "{{LineItemId}}" + ] + } + }, + "response": [] + }, + { + "name": "DeleteLineItem", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseURL}}/api/lineitems/{{LineItemId}}", + "host": [ + "{{baseURL}}" + ], + "path": [ + "api", + "lineitems", + "{{LineItemId}}" + ] + } + }, + "response": [] + } + ], + "variable": [ + { + "key": "baseURL", + "value": "https://localhost:7150" + } + ] +} \ No newline at end of file diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api.sln b/Ecommerce.Api.Lepros311/Ecommerce.Api.sln new file mode 100644 index 00000000..c227fb83 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36327.8 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ecommerce.Api", "Ecommerce.Api\Ecommerce.Api.csproj", "{D8CA0565-FF7F-4CE6-A92D-7B29C2CB14B0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D8CA0565-FF7F-4CE6-A92D-7B29C2CB14B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D8CA0565-FF7F-4CE6-A92D-7B29C2CB14B0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D8CA0565-FF7F-4CE6-A92D-7B29C2CB14B0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D8CA0565-FF7F-4CE6-A92D-7B29C2CB14B0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F8125966-4238-49C5-BE43-7136A2350AA5} + EndGlobalSection +EndGlobal diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Controllers/CategoryController.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Controllers/CategoryController.cs new file mode 100644 index 00000000..51080c5d --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Controllers/CategoryController.cs @@ -0,0 +1,119 @@ +using Ecommerce.Api.Models; +using Ecommerce.Api.Responses; +using Ecommerce.Api.Services; +using Microsoft.AspNetCore.Mvc; + +namespace Ecommerce.Api.Controllers +{ + [Route("api/categories")] + [ApiController] + public class CategoryController : ControllerBase + { + private readonly ICategoryService _categoryService; + + public CategoryController(ICategoryService categoryService) + { + _categoryService = categoryService; + } + + [HttpGet] + public async Task>> GetPagedCategories([FromQuery] PaginationParams paginationParams) + { + var responseWithDtos = await _categoryService.GetPagedCategories(paginationParams); + + if (responseWithDtos.Status == ResponseStatus.Fail) + { + return BadRequest(responseWithDtos.Message); + } + + return Ok(responseWithDtos.Data); + } + + [HttpGet("{id}")] + public async Task> GetCategoryById(int id) + { + var response = await _categoryService.GetCategoryById(id); + + if (response.Status == ResponseStatus.Fail) + { + return NotFound(response.Message); + } + + var returnedCategory = response.Data; + + var categoryDto = new CategoryDto + { + CategoryId = returnedCategory.CategoryId, + CategoryName = returnedCategory.CategoryName, + Products = returnedCategory.Products.Select(p => new ProductDto + { + ProductId = p.ProductId, + ProductName = p.ProductName, + Price = p.Price, + Category = p.Category.CategoryName + }).ToList() + }; + + return Ok(categoryDto); + } + + [HttpPost] + public async Task> CreateCategory([FromBody] WriteCategoryDto writeCategoryDto) + { + var responseWithDataDto = await _categoryService.CreateCategory(writeCategoryDto); + + if (responseWithDataDto.Message == "Category not found.") + { + return NotFound(responseWithDataDto.Message); + } + + if (responseWithDataDto.Status == ResponseStatus.Fail) + { + return BadRequest(responseWithDataDto.Message); + } + + return CreatedAtAction(nameof(GetCategoryById), + new { id = responseWithDataDto.Data.CategoryId }, responseWithDataDto.Data); + } + + [HttpPut("{id}")] + public async Task> UpdateCategory(int id, [FromBody] WriteCategoryDto writeCategoryDto) + { + var response = await _categoryService.UpdateCategory(id, writeCategoryDto); + + if (response.Message == "Category not found.") + { + return NotFound(response.Message); + } + + if (response.Status == ResponseStatus.Fail) + { + return BadRequest(response.Message); + } + + return NoContent(); + } + + [HttpDelete("{id}")] + public async Task DeleteCategory(int id) + { + var response = await _categoryService.DeleteCategory(id); + + if (response.Message == "Category not found.") + { + return NotFound(response.Message); + } + else if (response.Message == "Cannot delete categories that contain products.") + { + return Conflict(response.Message); + } + else if (response.Status == ResponseStatus.Fail) + { + return BadRequest(response.Message); + } + + return NoContent(); + } + } +} + diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Controllers/LineItemController.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Controllers/LineItemController.cs new file mode 100644 index 00000000..17cf4c5a --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Controllers/LineItemController.cs @@ -0,0 +1,112 @@ +using Ecommerce.Api.Models; +using Ecommerce.Api.Responses; +using Ecommerce.Api.Services; +using Microsoft.AspNetCore.Mvc; + +namespace Ecommerce.Api.Controllers +{ + [Route("api/lineitems")] + [ApiController] + public class LineItemController : ControllerBase + { + private readonly ILineItemService _lineItemService; + + public LineItemController(ILineItemService lineItemService) + { + _lineItemService = lineItemService; + } + + [HttpGet] + public async Task>> GetPagedLineItems([FromQuery] PaginationParams paginationParams) + { + var responseWithDtos = await _lineItemService.GetPagedLineItems(paginationParams); + + if (responseWithDtos.Status == ResponseStatus.Fail) + { + return BadRequest(responseWithDtos.Message); + } + + return Ok(responseWithDtos.Data); + } + + [HttpGet("{id}")] + public async Task> GetLineItemById(int id) + { + var response = await _lineItemService.GetLineItemById(id); + + if (response.Status == ResponseStatus.Fail) + { + return NotFound(response.Message); + } + + var returnedLineItem = response.Data; + + var lineItemDto = new LineItemDto + { + LineItemId = returnedLineItem.LineItemId, + ProductId = returnedLineItem.ProductId, + ProductName = returnedLineItem.Product.ProductName, + Category = returnedLineItem.Product.Category.CategoryName, + Quantity = returnedLineItem.Quantity, + UnitPrice = returnedLineItem.UnitPrice, + SaleId = returnedLineItem.SaleId, + }; + + return Ok(lineItemDto); + } + + [HttpPost] + public async Task> CreateLineItem([FromBody] WriteLineItemDto writeLineItemDto) + { + var responseWithDataDto = await _lineItemService.CreateLineItem(writeLineItemDto); + + if (responseWithDataDto.Message == "Sale not found.") + { + return NotFound(responseWithDataDto.Message); + } + + if (responseWithDataDto.Status == ResponseStatus.Fail) + { + return BadRequest(responseWithDataDto.Message); + } + + return CreatedAtAction(nameof(GetLineItemById), + new { id = responseWithDataDto.Data.LineItemId }, responseWithDataDto.Data); + } + + [HttpPut("{id}")] + public async Task> UpdateLineItem(int id, [FromBody] WriteLineItemDto writeLineItemDto) + { + var response = await _lineItemService.UpdateLineItem(id, writeLineItemDto); + + if (response.Message == "Line Item not found." || response.Message == "Sale not found." || response.Message == "Product not found.") + { + return NotFound(response.Message); + } + + if (response.Status == ResponseStatus.Fail) + { + return BadRequest(response.Message); + } + + return NoContent(); + } + + [HttpDelete("{id}")] + public async Task DeleteLineItem(int id) + { + var response = await _lineItemService.DeleteLineItem(id); + + if (response.Message == "Line Item not found.") + { + return NotFound(response.Message); + } + else if (response.Status == ResponseStatus.Fail) + { + return BadRequest(response.Message); + } + + return NoContent(); + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Controllers/ProductController.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Controllers/ProductController.cs new file mode 100644 index 00000000..bf65128f --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Controllers/ProductController.cs @@ -0,0 +1,109 @@ +using Ecommerce.Api.Models; +using Ecommerce.Api.Responses; +using Ecommerce.Api.Services; +using Microsoft.AspNetCore.Mvc; + +namespace Ecommerce.Api.Controllers +{ + [Route("api/products")] + [ApiController] + public class ProductController : ControllerBase + { + private readonly IProductService _productService; + + public ProductController(IProductService productService) + { + _productService = productService; + } + + [HttpGet] + public async Task>> GetPagedProducts([FromQuery] PaginationParams paginationParams) + { + var responseWithDtos = await _productService.GetPagedProducts(paginationParams); + + if (responseWithDtos.Status == ResponseStatus.Fail) + { + return BadRequest(responseWithDtos.Message); + } + + return Ok(responseWithDtos.Data); + } + + [HttpGet("{id}")] + public async Task> GetProductById(int id) + { + var response = await _productService.GetProductById(id); + + if (response.Status == ResponseStatus.Fail) + { + return NotFound(response.Message); + } + + var returnedProduct = response.Data; + + var productDto = new ProductDto + { + ProductId = returnedProduct.ProductId, + ProductName = returnedProduct.ProductName, + Price = returnedProduct.Price, + Category = returnedProduct.Category.CategoryName + }; + + return Ok(productDto); + } + + [HttpPost] + public async Task> CreateProduct([FromBody] WriteProductDto writeProductDto) + { + var responseWithDataDto = await _productService.CreateProduct(writeProductDto); + + if (responseWithDataDto.Message == "Category not found.") + { + return NotFound(responseWithDataDto.Message); + } + + if (responseWithDataDto.Status == ResponseStatus.Fail) + { + return BadRequest(responseWithDataDto.Message); + } + + return CreatedAtAction(nameof(GetProductById), + new { id = responseWithDataDto.Data.ProductId }, responseWithDataDto.Data); + } + + [HttpPut("{id}")] + public async Task> UpdateProduct(int id, [FromBody] WriteProductDto writeProductDto) + { + var response = await _productService.UpdateProduct(id, writeProductDto); + + if (response.Message == "Product not found." || response.Message == "Category not found.") + { + return NotFound(response.Message); + } + + if (response.Status == ResponseStatus.Fail) + { + return BadRequest(response.Message); + } + + return NoContent(); + } + + [HttpDelete("{id}")] + public async Task DeleteProduct(int id) + { + var response = await _productService.DeleteProduct(id); + + if (response.Message == "Product not found.") + { + return NotFound(response.Message); + } + else if (response.Status == ResponseStatus.Fail) + { + return BadRequest(response.Message); + } + + return NoContent(); + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Controllers/SaleController.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Controllers/SaleController.cs new file mode 100644 index 00000000..852a4ca0 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Controllers/SaleController.cs @@ -0,0 +1,117 @@ +using Ecommerce.Api.Models; +using Ecommerce.Api.Responses; +using Ecommerce.Api.Services; +using Microsoft.AspNetCore.Mvc; + +namespace Ecommerce.Api.Controllers +{ + [Route("api/sales")] + [ApiController] + public class SaleController : ControllerBase + { + private readonly ISaleService _saleService; + + public SaleController(ISaleService saleService) + { + _saleService = saleService; + } + + [HttpGet] + public async Task>> GetPagedSales([FromQuery] PaginationParams paginationParams) + { + var responseWithDtos = await _saleService.GetPagedSales(paginationParams); + + if (responseWithDtos.Status == ResponseStatus.Fail) + { + return BadRequest(responseWithDtos.Message); + } + + return Ok(responseWithDtos.Data); + } + + [HttpGet("{id}")] + public async Task> GetSaleById(int id) + { + var response = await _saleService.GetSaleById(id); + + if (response.Status == ResponseStatus.Fail) + { + return NotFound(response.Message); + } + + var returnedSale = response.Data; + + var saleDto = new SaleDto + { + SaleId = returnedSale.SaleId, + DateAndTimeOfSale = returnedSale.DateAndTimeOfSale, + TotalPrice = returnedSale.TotalPrice, + LineItems = returnedSale.LineItems.Select(li => new LineItemDto + { + LineItemId = li.LineItemId, + ProductId = li.ProductId, + ProductName = li.Product.ProductName, + Category = li.Product.Category.CategoryName, + Quantity = li.Quantity, + UnitPrice = li.UnitPrice, + }).ToList(), + }; + + return Ok(saleDto); + } + + [HttpPost] + public async Task> CreateCategory([FromBody] WriteSaleDto writeSaleDto) + { + var responseWithDataDto = await _saleService.CreateSale(writeSaleDto); + + if (responseWithDataDto.Message == "Something not found.") + { + return NotFound(responseWithDataDto.Message); + } + + if (responseWithDataDto.Status == ResponseStatus.Fail) + { + return BadRequest(responseWithDataDto.Message); + } + + return CreatedAtAction(nameof(GetSaleById), + new { id = responseWithDataDto.Data.SaleId }, responseWithDataDto.Data); + } + + [HttpPut("{id}")] + public async Task> UpdateSale(int id, [FromBody] UpdateSaleDto updateSaleDto) + { + var response = await _saleService.UpdateSale(id, updateSaleDto); + + if (response.Message == "Product not found.") + { + return NotFound(response.Message); + } + + if (response.Status == ResponseStatus.Fail) + { + return BadRequest(response.Message); + } + + return NoContent(); + } + + [HttpDelete("{id}")] + public async Task DeleteCategory(int id) + { + var response = await _saleService.DeleteSale(id); + + if (response.Message == "Sale not found.") + { + return NotFound(response.Message); + } + else if (response.Status == ResponseStatus.Fail) + { + return BadRequest(response.Message); + } + + return NoContent(); + } + } +} \ No newline at end of file diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Data/EcommerceDbContext.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Data/EcommerceDbContext.cs new file mode 100644 index 00000000..8781abe2 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Data/EcommerceDbContext.cs @@ -0,0 +1,197 @@ +using Ecommerce.Api.Models; +using Microsoft.EntityFrameworkCore; + +namespace Ecommerce.Api.Data; + +public class EcommerceDbContext : DbContext +{ + public EcommerceDbContext(DbContextOptions options) : base(options) { } + + public DbSet Categories { get; set; } + + public DbSet Products { get; set; } + + public DbSet Sales { get; set; } + + public DbSet LineItems { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EcommerceDb;Trusted_Connection=True;Initial Catalog=EcommerceDb"); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.ToTable("Categories"); + entity.Property(c => c.CategoryName).IsRequired(); + entity.Property(c => c.IsDeleted).IsRequired(); + entity.HasQueryFilter(c => !c.IsDeleted); + }); + + modelBuilder.Entity(entity => + { + entity.ToTable("Products", t => t.HasCheckConstraint("CK_Products_Price", "Price > 0")); + entity.Property(p => p.ProductName).IsRequired(); + entity.Property(p => p.Price).IsRequired().HasColumnType("decimal(18,2)"); + entity.Property(p => p.CategoryId).IsRequired(); + entity.Property(p => p.IsDeleted).IsRequired(); + entity.HasOne(p => p.Category).WithMany(c => c.Products).HasForeignKey(p => p.CategoryId).IsRequired(); + entity.HasQueryFilter(p => !p.IsDeleted); + }); + + modelBuilder.Entity(entity => + { + entity.ToTable("Sales"); + entity.Property(s => s.DateAndTimeOfSale).IsRequired(); + entity.Property(s => s.TotalPrice).IsRequired().HasColumnType("decimal(18,2)"); + entity.Ignore(s => s.Subtotal); + entity.HasMany(s => s.LineItems).WithOne(li => li.Sale).HasForeignKey(li => li.SaleId).IsRequired(); + entity.HasQueryFilter(s => !s.IsDeleted); + }); + + modelBuilder.Entity(entity => + { + entity.ToTable("LineItems", t => t.HasCheckConstraint("CK_LineItems_Quantity", "Quantity > 0")); + entity.Property(li => li.SaleId).IsRequired(); + entity.Property(li => li.ProductId).IsRequired(); + entity.Property(li => li.Quantity).IsRequired(); + entity.Property(li => li.UnitPrice).IsRequired().HasColumnType("decimal(18,2)"); + entity.HasOne(li => li.Sale).WithMany(s => s.LineItems).HasForeignKey(li => li.SaleId).IsRequired(); + entity.HasOne(li => li.Product).WithMany(p => p.LineItems).HasForeignKey(li => li.ProductId).IsRequired(); + entity.HasQueryFilter(li => !li.IsDeleted); + }); + + modelBuilder.Entity() + .HasData(new List + { + new Category + { + CategoryId = 1, + CategoryName = "Shoes", + }, + new Category + { + CategoryId = 2, + CategoryName = "Socks", + }, + new Category + { + CategoryId = 3, + CategoryName = "Pants", + }, + new Category + { + CategoryId = 4, + CategoryName = "Shirts", + } + }); + + modelBuilder.Entity() + .HasData(new List + { + new Product + { + ProductId = 1, + ProductName = "Hightop Sneakers", + Price = 75.50m, + CategoryId = 1, + }, + new Product + { + ProductId = 2, + ProductName = "Boat Loafers", + Price = 53.75m, + CategoryId = 1, + }, + new Product + { + ProductId = 3, + ProductName = "Dress Socks", + Price = 15.25m, + CategoryId = 2, + }, + new Product + { + ProductId = 4, + ProductName = "Ankle Socks", + Price = 10.15m, + CategoryId = 2, + }, + new Product + { + ProductId = 5, + ProductName = "Dress Slacks", + Price = 35.99m, + CategoryId = 3, + }, + new Product + { + ProductId = 6, + ProductName = "Stonewash Jeans", + Price = 45.95m, + CategoryId = 3, + }, + new Product + { + ProductId = 7, + ProductName = "Flannel Shirt", + Price = 34.75m, + CategoryId = 4, + }, + new Product + { + ProductId = 8, + ProductName = "Shortsleeve Polo", + Price = 22.99m, + CategoryId = 4, + } + }); + + modelBuilder.Entity() + .HasData(new List + { + new Sale + { + SaleId = 1, + DateAndTimeOfSale = new DateTime(2025, 08, 01, 12, 37, 22), + TotalPrice = 22.99m + }, + new Sale + { + SaleId = 2, + DateAndTimeOfSale = new DateTime(2025, 08, 03, 14, 30, 48), + TotalPrice = 61.20m + }, + new Sale + { + SaleId = 3, + DateAndTimeOfSale = new DateTime(2025, 08, 07, 11, 39, 10), + TotalPrice = 156.20m + }, + new Sale + { + SaleId = 4, + DateAndTimeOfSale = new DateTime(2025, 08, 07, 19, 13, 55), + TotalPrice = 107.50m + }, + new Sale + { + SaleId = 5, + DateAndTimeOfSale = new DateTime(2025, 08, 08, 9, 04, 17), + TotalPrice = 95.80m + } + }); + + modelBuilder.Entity().HasData( + new LineItem { LineItemId = 1, SaleId = 1, ProductId = 8, Quantity = 1, UnitPrice = 22.99m }, + new LineItem { LineItemId = 2, SaleId = 2, ProductId = 6, Quantity = 1, UnitPrice = 45.95m }, + new LineItem { LineItemId = 3, SaleId = 2, ProductId = 3, Quantity = 1, UnitPrice = 15.25m }, + new LineItem { LineItemId = 4, SaleId = 3, ProductId = 7, Quantity = 1, UnitPrice = 34.75m }, + new LineItem { LineItemId = 5, SaleId = 3, ProductId = 6, Quantity = 1, UnitPrice = 45.95m }, + new LineItem { LineItemId = 6, SaleId = 3, ProductId = 1, Quantity = 1, UnitPrice = 75.50m }, + new LineItem { LineItemId = 7, SaleId = 4, ProductId = 2, Quantity = 2, UnitPrice = 53.75m }, + new LineItem { LineItemId = 8, SaleId = 5, ProductId = 4, Quantity = 2, UnitPrice = 10.15m }, + new LineItem { LineItemId = 9, SaleId = 5, ProductId = 1, Quantity = 1, UnitPrice = 75.50m } + ); + + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Ecommerce.Api.csproj b/Ecommerce.Api.Lepros311/Ecommerce.Api/Ecommerce.Api.csproj new file mode 100644 index 00000000..b48812e3 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Ecommerce.Api.csproj @@ -0,0 +1,25 @@ + + + + net9.0 + enable + enable + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Ecommerce.Api.http b/Ecommerce.Api.Lepros311/Ecommerce.Api/Ecommerce.Api.http new file mode 100644 index 00000000..bd58effb --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Ecommerce.Api.http @@ -0,0 +1,6 @@ +@Ecommerce.Api_HostAddress = http://localhost:5101 + +GET {{Ecommerce.Api_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250811003905_InitialCreate.Designer.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250811003905_InitialCreate.Designer.cs new file mode 100644 index 00000000..7e64c2d8 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250811003905_InitialCreate.Designer.cs @@ -0,0 +1,351 @@ +// +using System; +using Ecommerce.Api.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + [DbContext(typeof(EcommerceDbContext))] + [Migration("20250811003905_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("CategoryId")); + + b.Property("CategoryName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("CategoryId"); + + b.ToTable("Categories", (string)null); + + b.HasData( + new + { + CategoryId = 1, + CategoryName = "Shoes" + }, + new + { + CategoryId = 2, + CategoryName = "Socks" + }, + new + { + CategoryId = 3, + CategoryName = "Pants" + }, + new + { + CategoryId = 4, + CategoryName = "Shirts" + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.Property("LineItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("LineItemId")); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("SaleId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("LineItemId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SaleId"); + + b.ToTable("LineItems", (string)null); + + b.HasData( + new + { + LineItemId = 1, + ProductId = 8, + Quantity = 1, + SaleId = 1, + UnitPrice = 22.99m + }, + new + { + LineItemId = 2, + ProductId = 6, + Quantity = 1, + SaleId = 2, + UnitPrice = 45.95m + }, + new + { + LineItemId = 3, + ProductId = 3, + Quantity = 1, + SaleId = 2, + UnitPrice = 15.25m + }, + new + { + LineItemId = 4, + ProductId = 7, + Quantity = 1, + SaleId = 3, + UnitPrice = 34.75m + }, + new + { + LineItemId = 5, + ProductId = 6, + Quantity = 1, + SaleId = 3, + UnitPrice = 45.95m + }, + new + { + LineItemId = 6, + ProductId = 1, + Quantity = 1, + SaleId = 3, + UnitPrice = 75.50m + }, + new + { + LineItemId = 7, + ProductId = 2, + Quantity = 2, + SaleId = 4, + UnitPrice = 53.75m + }, + new + { + LineItemId = 8, + ProductId = 4, + Quantity = 2, + SaleId = 5, + UnitPrice = 10.15m + }, + new + { + LineItemId = 9, + ProductId = 1, + Quantity = 1, + SaleId = 5, + UnitPrice = 75.50m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Property("ProductId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ProductId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("ProductName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("ProductId"); + + b.HasIndex("CategoryId"); + + b.ToTable("Products", (string)null); + + b.HasData( + new + { + ProductId = 1, + CategoryId = 1, + Price = 75.50m, + ProductName = "Hightop Sneakers" + }, + new + { + ProductId = 2, + CategoryId = 1, + Price = 53.75m, + ProductName = "Boat Loafers" + }, + new + { + ProductId = 3, + CategoryId = 2, + Price = 15.25m, + ProductName = "Dress Socks" + }, + new + { + ProductId = 4, + CategoryId = 2, + Price = 10.15m, + ProductName = "Ankle Socks" + }, + new + { + ProductId = 5, + CategoryId = 3, + Price = 35.99m, + ProductName = "Dress Slacks" + }, + new + { + ProductId = 6, + CategoryId = 3, + Price = 45.95m, + ProductName = "Stonewash Jeans" + }, + new + { + ProductId = 7, + CategoryId = 4, + Price = 34.75m, + ProductName = "Flannel Shirt" + }, + new + { + ProductId = 8, + CategoryId = 4, + Price = 22.99m, + ProductName = "Shortsleeve Polo" + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Property("SaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("SaleId")); + + b.Property("DateAndTimeOfSale") + .HasColumnType("datetime2"); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("SaleId"); + + b.ToTable("Sales", (string)null); + + b.HasData( + new + { + SaleId = 1, + DateAndTimeOfSale = new DateTime(2025, 8, 1, 12, 37, 22, 0, DateTimeKind.Unspecified), + TotalPrice = 22.99m + }, + new + { + SaleId = 2, + DateAndTimeOfSale = new DateTime(2025, 8, 3, 14, 30, 48, 0, DateTimeKind.Unspecified), + TotalPrice = 61.20m + }, + new + { + SaleId = 3, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 11, 39, 10, 0, DateTimeKind.Unspecified), + TotalPrice = 156.20m + }, + new + { + SaleId = 4, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 19, 13, 55, 0, DateTimeKind.Unspecified), + TotalPrice = 107.50m + }, + new + { + SaleId = 5, + DateAndTimeOfSale = new DateTime(2025, 8, 8, 9, 4, 17, 0, DateTimeKind.Unspecified), + TotalPrice = 95.80m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.HasOne("Ecommerce.Api.Models.Product", "Product") + .WithMany("LineItems") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ecommerce.Api.Models.Sale", "Sale") + .WithMany("LineItems") + .HasForeignKey("SaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Sale"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.HasOne("Ecommerce.Api.Models.Category", "Category") + .WithMany("Products") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Navigation("LineItems"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Navigation("LineItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250811003905_InitialCreate.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250811003905_InitialCreate.cs new file mode 100644 index 00000000..61d9b4ca --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250811003905_InitialCreate.cs @@ -0,0 +1,178 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace Ecommerce.Api.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Categories", + columns: table => new + { + CategoryId = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + CategoryName = table.Column(type: "nvarchar(max)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Categories", x => x.CategoryId); + }); + + migrationBuilder.CreateTable( + name: "Sales", + columns: table => new + { + SaleId = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + DateAndTimeOfSale = table.Column(type: "datetime2", nullable: false), + TotalPrice = table.Column(type: "decimal(18,2)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Sales", x => x.SaleId); + }); + + migrationBuilder.CreateTable( + name: "Products", + columns: table => new + { + ProductId = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + ProductName = table.Column(type: "nvarchar(max)", nullable: false), + Price = table.Column(type: "decimal(18,2)", nullable: false), + CategoryId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Products", x => x.ProductId); + table.ForeignKey( + name: "FK_Products_Categories_CategoryId", + column: x => x.CategoryId, + principalTable: "Categories", + principalColumn: "CategoryId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "LineItems", + columns: table => new + { + LineItemId = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + SaleId = table.Column(type: "int", nullable: false), + ProductId = table.Column(type: "int", nullable: false), + Quantity = table.Column(type: "int", nullable: false), + UnitPrice = table.Column(type: "decimal(18,2)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_LineItems", x => x.LineItemId); + table.ForeignKey( + name: "FK_LineItems_Products_ProductId", + column: x => x.ProductId, + principalTable: "Products", + principalColumn: "ProductId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_LineItems_Sales_SaleId", + column: x => x.SaleId, + principalTable: "Sales", + principalColumn: "SaleId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.InsertData( + table: "Categories", + columns: new[] { "CategoryId", "CategoryName" }, + values: new object[,] + { + { 1, "Shoes" }, + { 2, "Socks" }, + { 3, "Pants" }, + { 4, "Shirts" } + }); + + migrationBuilder.InsertData( + table: "Sales", + columns: new[] { "SaleId", "DateAndTimeOfSale", "TotalPrice" }, + values: new object[,] + { + { 1, new DateTime(2025, 8, 1, 12, 37, 22, 0, DateTimeKind.Unspecified), 22.99m }, + { 2, new DateTime(2025, 8, 3, 14, 30, 48, 0, DateTimeKind.Unspecified), 61.20m }, + { 3, new DateTime(2025, 8, 7, 11, 39, 10, 0, DateTimeKind.Unspecified), 156.20m }, + { 4, new DateTime(2025, 8, 7, 19, 13, 55, 0, DateTimeKind.Unspecified), 107.50m }, + { 5, new DateTime(2025, 8, 8, 9, 4, 17, 0, DateTimeKind.Unspecified), 95.80m } + }); + + migrationBuilder.InsertData( + table: "Products", + columns: new[] { "ProductId", "CategoryId", "Price", "ProductName" }, + values: new object[,] + { + { 1, 1, 75.50m, "Hightop Sneakers" }, + { 2, 1, 53.75m, "Boat Loafers" }, + { 3, 2, 15.25m, "Dress Socks" }, + { 4, 2, 10.15m, "Ankle Socks" }, + { 5, 3, 35.99m, "Dress Slacks" }, + { 6, 3, 45.95m, "Stonewash Jeans" }, + { 7, 4, 34.75m, "Flannel Shirt" }, + { 8, 4, 22.99m, "Shortsleeve Polo" } + }); + + migrationBuilder.InsertData( + table: "LineItems", + columns: new[] { "LineItemId", "ProductId", "Quantity", "SaleId", "UnitPrice" }, + values: new object[,] + { + { 1, 8, 1, 1, 22.99m }, + { 2, 6, 1, 2, 45.95m }, + { 3, 3, 1, 2, 15.25m }, + { 4, 7, 1, 3, 34.75m }, + { 5, 6, 1, 3, 45.95m }, + { 6, 1, 1, 3, 75.50m }, + { 7, 2, 2, 4, 53.75m }, + { 8, 4, 2, 5, 10.15m }, + { 9, 1, 1, 5, 75.50m } + }); + + migrationBuilder.CreateIndex( + name: "IX_LineItems_ProductId", + table: "LineItems", + column: "ProductId"); + + migrationBuilder.CreateIndex( + name: "IX_LineItems_SaleId", + table: "LineItems", + column: "SaleId"); + + migrationBuilder.CreateIndex( + name: "IX_Products_CategoryId", + table: "Products", + column: "CategoryId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "LineItems"); + + migrationBuilder.DropTable( + name: "Products"); + + migrationBuilder.DropTable( + name: "Sales"); + + migrationBuilder.DropTable( + name: "Categories"); + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250815003638_AddIsDeletedToProduct.Designer.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250815003638_AddIsDeletedToProduct.Designer.cs new file mode 100644 index 00000000..99249c5c --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250815003638_AddIsDeletedToProduct.Designer.cs @@ -0,0 +1,362 @@ +// +using System; +using Ecommerce.Api.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + [DbContext(typeof(EcommerceDbContext))] + [Migration("20250815003638_AddIsDeletedToProduct")] + partial class AddIsDeletedToProduct + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("CategoryId")); + + b.Property("CategoryName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("CategoryId"); + + b.ToTable("Categories", (string)null); + + b.HasData( + new + { + CategoryId = 1, + CategoryName = "Shoes" + }, + new + { + CategoryId = 2, + CategoryName = "Socks" + }, + new + { + CategoryId = 3, + CategoryName = "Pants" + }, + new + { + CategoryId = 4, + CategoryName = "Shirts" + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.Property("LineItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("LineItemId")); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("SaleId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("LineItemId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SaleId"); + + b.ToTable("LineItems", (string)null); + + b.HasData( + new + { + LineItemId = 1, + ProductId = 8, + Quantity = 1, + SaleId = 1, + UnitPrice = 22.99m + }, + new + { + LineItemId = 2, + ProductId = 6, + Quantity = 1, + SaleId = 2, + UnitPrice = 45.95m + }, + new + { + LineItemId = 3, + ProductId = 3, + Quantity = 1, + SaleId = 2, + UnitPrice = 15.25m + }, + new + { + LineItemId = 4, + ProductId = 7, + Quantity = 1, + SaleId = 3, + UnitPrice = 34.75m + }, + new + { + LineItemId = 5, + ProductId = 6, + Quantity = 1, + SaleId = 3, + UnitPrice = 45.95m + }, + new + { + LineItemId = 6, + ProductId = 1, + Quantity = 1, + SaleId = 3, + UnitPrice = 75.50m + }, + new + { + LineItemId = 7, + ProductId = 2, + Quantity = 2, + SaleId = 4, + UnitPrice = 53.75m + }, + new + { + LineItemId = 8, + ProductId = 4, + Quantity = 2, + SaleId = 5, + UnitPrice = 10.15m + }, + new + { + LineItemId = 9, + ProductId = 1, + Quantity = 1, + SaleId = 5, + UnitPrice = 75.50m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Property("ProductId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ProductId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("ProductName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("ProductId"); + + b.HasIndex("CategoryId"); + + b.ToTable("Products", (string)null); + + b.HasData( + new + { + ProductId = 1, + CategoryId = 1, + IsDeleted = false, + Price = 75.50m, + ProductName = "Hightop Sneakers" + }, + new + { + ProductId = 2, + CategoryId = 1, + IsDeleted = false, + Price = 53.75m, + ProductName = "Boat Loafers" + }, + new + { + ProductId = 3, + CategoryId = 2, + IsDeleted = false, + Price = 15.25m, + ProductName = "Dress Socks" + }, + new + { + ProductId = 4, + CategoryId = 2, + IsDeleted = false, + Price = 10.15m, + ProductName = "Ankle Socks" + }, + new + { + ProductId = 5, + CategoryId = 3, + IsDeleted = false, + Price = 35.99m, + ProductName = "Dress Slacks" + }, + new + { + ProductId = 6, + CategoryId = 3, + IsDeleted = false, + Price = 45.95m, + ProductName = "Stonewash Jeans" + }, + new + { + ProductId = 7, + CategoryId = 4, + IsDeleted = false, + Price = 34.75m, + ProductName = "Flannel Shirt" + }, + new + { + ProductId = 8, + CategoryId = 4, + IsDeleted = false, + Price = 22.99m, + ProductName = "Shortsleeve Polo" + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Property("SaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("SaleId")); + + b.Property("DateAndTimeOfSale") + .HasColumnType("datetime2"); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("SaleId"); + + b.ToTable("Sales", (string)null); + + b.HasData( + new + { + SaleId = 1, + DateAndTimeOfSale = new DateTime(2025, 8, 1, 12, 37, 22, 0, DateTimeKind.Unspecified), + TotalPrice = 22.99m + }, + new + { + SaleId = 2, + DateAndTimeOfSale = new DateTime(2025, 8, 3, 14, 30, 48, 0, DateTimeKind.Unspecified), + TotalPrice = 61.20m + }, + new + { + SaleId = 3, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 11, 39, 10, 0, DateTimeKind.Unspecified), + TotalPrice = 156.20m + }, + new + { + SaleId = 4, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 19, 13, 55, 0, DateTimeKind.Unspecified), + TotalPrice = 107.50m + }, + new + { + SaleId = 5, + DateAndTimeOfSale = new DateTime(2025, 8, 8, 9, 4, 17, 0, DateTimeKind.Unspecified), + TotalPrice = 95.80m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.HasOne("Ecommerce.Api.Models.Product", "Product") + .WithMany("LineItems") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ecommerce.Api.Models.Sale", "Sale") + .WithMany("LineItems") + .HasForeignKey("SaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Sale"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.HasOne("Ecommerce.Api.Models.Category", "Category") + .WithMany("Products") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Navigation("LineItems"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Navigation("LineItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250815003638_AddIsDeletedToProduct.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250815003638_AddIsDeletedToProduct.cs new file mode 100644 index 00000000..fac49ed7 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250815003638_AddIsDeletedToProduct.cs @@ -0,0 +1,85 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + /// + public partial class AddIsDeletedToProduct : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "IsDeleted", + table: "Products", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.UpdateData( + table: "Products", + keyColumn: "ProductId", + keyValue: 1, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "Products", + keyColumn: "ProductId", + keyValue: 2, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "Products", + keyColumn: "ProductId", + keyValue: 3, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "Products", + keyColumn: "ProductId", + keyValue: 4, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "Products", + keyColumn: "ProductId", + keyValue: 5, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "Products", + keyColumn: "ProductId", + keyValue: 6, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "Products", + keyColumn: "ProductId", + keyValue: 7, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "Products", + keyColumn: "ProductId", + keyValue: 8, + column: "IsDeleted", + value: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsDeleted", + table: "Products"); + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250817141236_RefactorFromAnnotationsToFluent.Designer.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250817141236_RefactorFromAnnotationsToFluent.Designer.cs new file mode 100644 index 00000000..8b098693 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250817141236_RefactorFromAnnotationsToFluent.Designer.cs @@ -0,0 +1,362 @@ +// +using System; +using Ecommerce.Api.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + [DbContext(typeof(EcommerceDbContext))] + [Migration("20250817141236_RefactorFromAnnotationsToFluent")] + partial class RefactorFromAnnotationsToFluent + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("CategoryId")); + + b.Property("CategoryName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("CategoryId"); + + b.ToTable("Categories", (string)null); + + b.HasData( + new + { + CategoryId = 1, + CategoryName = "Shoes" + }, + new + { + CategoryId = 2, + CategoryName = "Socks" + }, + new + { + CategoryId = 3, + CategoryName = "Pants" + }, + new + { + CategoryId = 4, + CategoryName = "Shirts" + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.Property("LineItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("LineItemId")); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("SaleId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("LineItemId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SaleId"); + + b.ToTable("LineItems", (string)null); + + b.HasData( + new + { + LineItemId = 1, + ProductId = 8, + Quantity = 1, + SaleId = 1, + UnitPrice = 22.99m + }, + new + { + LineItemId = 2, + ProductId = 6, + Quantity = 1, + SaleId = 2, + UnitPrice = 45.95m + }, + new + { + LineItemId = 3, + ProductId = 3, + Quantity = 1, + SaleId = 2, + UnitPrice = 15.25m + }, + new + { + LineItemId = 4, + ProductId = 7, + Quantity = 1, + SaleId = 3, + UnitPrice = 34.75m + }, + new + { + LineItemId = 5, + ProductId = 6, + Quantity = 1, + SaleId = 3, + UnitPrice = 45.95m + }, + new + { + LineItemId = 6, + ProductId = 1, + Quantity = 1, + SaleId = 3, + UnitPrice = 75.50m + }, + new + { + LineItemId = 7, + ProductId = 2, + Quantity = 2, + SaleId = 4, + UnitPrice = 53.75m + }, + new + { + LineItemId = 8, + ProductId = 4, + Quantity = 2, + SaleId = 5, + UnitPrice = 10.15m + }, + new + { + LineItemId = 9, + ProductId = 1, + Quantity = 1, + SaleId = 5, + UnitPrice = 75.50m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Property("ProductId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ProductId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("ProductName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("ProductId"); + + b.HasIndex("CategoryId"); + + b.ToTable("Products", (string)null); + + b.HasData( + new + { + ProductId = 1, + CategoryId = 1, + IsDeleted = false, + Price = 75.50m, + ProductName = "Hightop Sneakers" + }, + new + { + ProductId = 2, + CategoryId = 1, + IsDeleted = false, + Price = 53.75m, + ProductName = "Boat Loafers" + }, + new + { + ProductId = 3, + CategoryId = 2, + IsDeleted = false, + Price = 15.25m, + ProductName = "Dress Socks" + }, + new + { + ProductId = 4, + CategoryId = 2, + IsDeleted = false, + Price = 10.15m, + ProductName = "Ankle Socks" + }, + new + { + ProductId = 5, + CategoryId = 3, + IsDeleted = false, + Price = 35.99m, + ProductName = "Dress Slacks" + }, + new + { + ProductId = 6, + CategoryId = 3, + IsDeleted = false, + Price = 45.95m, + ProductName = "Stonewash Jeans" + }, + new + { + ProductId = 7, + CategoryId = 4, + IsDeleted = false, + Price = 34.75m, + ProductName = "Flannel Shirt" + }, + new + { + ProductId = 8, + CategoryId = 4, + IsDeleted = false, + Price = 22.99m, + ProductName = "Shortsleeve Polo" + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Property("SaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("SaleId")); + + b.Property("DateAndTimeOfSale") + .HasColumnType("datetime2"); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("SaleId"); + + b.ToTable("Sales", (string)null); + + b.HasData( + new + { + SaleId = 1, + DateAndTimeOfSale = new DateTime(2025, 8, 1, 12, 37, 22, 0, DateTimeKind.Unspecified), + TotalPrice = 22.99m + }, + new + { + SaleId = 2, + DateAndTimeOfSale = new DateTime(2025, 8, 3, 14, 30, 48, 0, DateTimeKind.Unspecified), + TotalPrice = 61.20m + }, + new + { + SaleId = 3, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 11, 39, 10, 0, DateTimeKind.Unspecified), + TotalPrice = 156.20m + }, + new + { + SaleId = 4, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 19, 13, 55, 0, DateTimeKind.Unspecified), + TotalPrice = 107.50m + }, + new + { + SaleId = 5, + DateAndTimeOfSale = new DateTime(2025, 8, 8, 9, 4, 17, 0, DateTimeKind.Unspecified), + TotalPrice = 95.80m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.HasOne("Ecommerce.Api.Models.Product", "Product") + .WithMany("LineItems") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ecommerce.Api.Models.Sale", "Sale") + .WithMany("LineItems") + .HasForeignKey("SaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Sale"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.HasOne("Ecommerce.Api.Models.Category", "Category") + .WithMany("Products") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Navigation("LineItems"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Navigation("LineItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250817141236_RefactorFromAnnotationsToFluent.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250817141236_RefactorFromAnnotationsToFluent.cs new file mode 100644 index 00000000..e2fa4373 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250817141236_RefactorFromAnnotationsToFluent.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + /// + public partial class RefactorFromAnnotationsToFluent : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250817233457_AddIsDeletedToCategory.Designer.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250817233457_AddIsDeletedToCategory.Designer.cs new file mode 100644 index 00000000..e3085b96 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250817233457_AddIsDeletedToCategory.Designer.cs @@ -0,0 +1,369 @@ +// +using System; +using Ecommerce.Api.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + [DbContext(typeof(EcommerceDbContext))] + [Migration("20250817233457_AddIsDeletedToCategory")] + partial class AddIsDeletedToCategory + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("CategoryId")); + + b.Property("CategoryName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.HasKey("CategoryId"); + + b.ToTable("Categories", (string)null); + + b.HasData( + new + { + CategoryId = 1, + CategoryName = "Shoes", + IsDeleted = false + }, + new + { + CategoryId = 2, + CategoryName = "Socks", + IsDeleted = false + }, + new + { + CategoryId = 3, + CategoryName = "Pants", + IsDeleted = false + }, + new + { + CategoryId = 4, + CategoryName = "Shirts", + IsDeleted = false + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.Property("LineItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("LineItemId")); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("SaleId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("LineItemId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SaleId"); + + b.ToTable("LineItems", (string)null); + + b.HasData( + new + { + LineItemId = 1, + ProductId = 8, + Quantity = 1, + SaleId = 1, + UnitPrice = 22.99m + }, + new + { + LineItemId = 2, + ProductId = 6, + Quantity = 1, + SaleId = 2, + UnitPrice = 45.95m + }, + new + { + LineItemId = 3, + ProductId = 3, + Quantity = 1, + SaleId = 2, + UnitPrice = 15.25m + }, + new + { + LineItemId = 4, + ProductId = 7, + Quantity = 1, + SaleId = 3, + UnitPrice = 34.75m + }, + new + { + LineItemId = 5, + ProductId = 6, + Quantity = 1, + SaleId = 3, + UnitPrice = 45.95m + }, + new + { + LineItemId = 6, + ProductId = 1, + Quantity = 1, + SaleId = 3, + UnitPrice = 75.50m + }, + new + { + LineItemId = 7, + ProductId = 2, + Quantity = 2, + SaleId = 4, + UnitPrice = 53.75m + }, + new + { + LineItemId = 8, + ProductId = 4, + Quantity = 2, + SaleId = 5, + UnitPrice = 10.15m + }, + new + { + LineItemId = 9, + ProductId = 1, + Quantity = 1, + SaleId = 5, + UnitPrice = 75.50m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Property("ProductId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ProductId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("ProductName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("ProductId"); + + b.HasIndex("CategoryId"); + + b.ToTable("Products", (string)null); + + b.HasData( + new + { + ProductId = 1, + CategoryId = 1, + IsDeleted = false, + Price = 75.50m, + ProductName = "Hightop Sneakers" + }, + new + { + ProductId = 2, + CategoryId = 1, + IsDeleted = false, + Price = 53.75m, + ProductName = "Boat Loafers" + }, + new + { + ProductId = 3, + CategoryId = 2, + IsDeleted = false, + Price = 15.25m, + ProductName = "Dress Socks" + }, + new + { + ProductId = 4, + CategoryId = 2, + IsDeleted = false, + Price = 10.15m, + ProductName = "Ankle Socks" + }, + new + { + ProductId = 5, + CategoryId = 3, + IsDeleted = false, + Price = 35.99m, + ProductName = "Dress Slacks" + }, + new + { + ProductId = 6, + CategoryId = 3, + IsDeleted = false, + Price = 45.95m, + ProductName = "Stonewash Jeans" + }, + new + { + ProductId = 7, + CategoryId = 4, + IsDeleted = false, + Price = 34.75m, + ProductName = "Flannel Shirt" + }, + new + { + ProductId = 8, + CategoryId = 4, + IsDeleted = false, + Price = 22.99m, + ProductName = "Shortsleeve Polo" + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Property("SaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("SaleId")); + + b.Property("DateAndTimeOfSale") + .HasColumnType("datetime2"); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("SaleId"); + + b.ToTable("Sales", (string)null); + + b.HasData( + new + { + SaleId = 1, + DateAndTimeOfSale = new DateTime(2025, 8, 1, 12, 37, 22, 0, DateTimeKind.Unspecified), + TotalPrice = 22.99m + }, + new + { + SaleId = 2, + DateAndTimeOfSale = new DateTime(2025, 8, 3, 14, 30, 48, 0, DateTimeKind.Unspecified), + TotalPrice = 61.20m + }, + new + { + SaleId = 3, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 11, 39, 10, 0, DateTimeKind.Unspecified), + TotalPrice = 156.20m + }, + new + { + SaleId = 4, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 19, 13, 55, 0, DateTimeKind.Unspecified), + TotalPrice = 107.50m + }, + new + { + SaleId = 5, + DateAndTimeOfSale = new DateTime(2025, 8, 8, 9, 4, 17, 0, DateTimeKind.Unspecified), + TotalPrice = 95.80m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.HasOne("Ecommerce.Api.Models.Product", "Product") + .WithMany("LineItems") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ecommerce.Api.Models.Sale", "Sale") + .WithMany("LineItems") + .HasForeignKey("SaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Sale"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.HasOne("Ecommerce.Api.Models.Category", "Category") + .WithMany("Products") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Navigation("LineItems"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Navigation("LineItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250817233457_AddIsDeletedToCategory.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250817233457_AddIsDeletedToCategory.cs new file mode 100644 index 00000000..abecac5e --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250817233457_AddIsDeletedToCategory.cs @@ -0,0 +1,57 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + /// + public partial class AddIsDeletedToCategory : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "IsDeleted", + table: "Categories", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.UpdateData( + table: "Categories", + keyColumn: "CategoryId", + keyValue: 1, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "Categories", + keyColumn: "CategoryId", + keyValue: 2, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "Categories", + keyColumn: "CategoryId", + keyValue: 3, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "Categories", + keyColumn: "CategoryId", + keyValue: 4, + column: "IsDeleted", + value: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsDeleted", + table: "Categories"); + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250818000433_DeclareCategoryFKOnProduct.Designer.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250818000433_DeclareCategoryFKOnProduct.Designer.cs new file mode 100644 index 00000000..61348cf1 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250818000433_DeclareCategoryFKOnProduct.Designer.cs @@ -0,0 +1,369 @@ +// +using System; +using Ecommerce.Api.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + [DbContext(typeof(EcommerceDbContext))] + [Migration("20250818000433_DeclareCategoryFKOnProduct")] + partial class DeclareCategoryFKOnProduct + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("CategoryId")); + + b.Property("CategoryName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.HasKey("CategoryId"); + + b.ToTable("Categories", (string)null); + + b.HasData( + new + { + CategoryId = 1, + CategoryName = "Shoes", + IsDeleted = false + }, + new + { + CategoryId = 2, + CategoryName = "Socks", + IsDeleted = false + }, + new + { + CategoryId = 3, + CategoryName = "Pants", + IsDeleted = false + }, + new + { + CategoryId = 4, + CategoryName = "Shirts", + IsDeleted = false + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.Property("LineItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("LineItemId")); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("SaleId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("LineItemId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SaleId"); + + b.ToTable("LineItems", (string)null); + + b.HasData( + new + { + LineItemId = 1, + ProductId = 8, + Quantity = 1, + SaleId = 1, + UnitPrice = 22.99m + }, + new + { + LineItemId = 2, + ProductId = 6, + Quantity = 1, + SaleId = 2, + UnitPrice = 45.95m + }, + new + { + LineItemId = 3, + ProductId = 3, + Quantity = 1, + SaleId = 2, + UnitPrice = 15.25m + }, + new + { + LineItemId = 4, + ProductId = 7, + Quantity = 1, + SaleId = 3, + UnitPrice = 34.75m + }, + new + { + LineItemId = 5, + ProductId = 6, + Quantity = 1, + SaleId = 3, + UnitPrice = 45.95m + }, + new + { + LineItemId = 6, + ProductId = 1, + Quantity = 1, + SaleId = 3, + UnitPrice = 75.50m + }, + new + { + LineItemId = 7, + ProductId = 2, + Quantity = 2, + SaleId = 4, + UnitPrice = 53.75m + }, + new + { + LineItemId = 8, + ProductId = 4, + Quantity = 2, + SaleId = 5, + UnitPrice = 10.15m + }, + new + { + LineItemId = 9, + ProductId = 1, + Quantity = 1, + SaleId = 5, + UnitPrice = 75.50m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Property("ProductId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ProductId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("ProductName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("ProductId"); + + b.HasIndex("CategoryId"); + + b.ToTable("Products", (string)null); + + b.HasData( + new + { + ProductId = 1, + CategoryId = 1, + IsDeleted = false, + Price = 75.50m, + ProductName = "Hightop Sneakers" + }, + new + { + ProductId = 2, + CategoryId = 1, + IsDeleted = false, + Price = 53.75m, + ProductName = "Boat Loafers" + }, + new + { + ProductId = 3, + CategoryId = 2, + IsDeleted = false, + Price = 15.25m, + ProductName = "Dress Socks" + }, + new + { + ProductId = 4, + CategoryId = 2, + IsDeleted = false, + Price = 10.15m, + ProductName = "Ankle Socks" + }, + new + { + ProductId = 5, + CategoryId = 3, + IsDeleted = false, + Price = 35.99m, + ProductName = "Dress Slacks" + }, + new + { + ProductId = 6, + CategoryId = 3, + IsDeleted = false, + Price = 45.95m, + ProductName = "Stonewash Jeans" + }, + new + { + ProductId = 7, + CategoryId = 4, + IsDeleted = false, + Price = 34.75m, + ProductName = "Flannel Shirt" + }, + new + { + ProductId = 8, + CategoryId = 4, + IsDeleted = false, + Price = 22.99m, + ProductName = "Shortsleeve Polo" + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Property("SaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("SaleId")); + + b.Property("DateAndTimeOfSale") + .HasColumnType("datetime2"); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("SaleId"); + + b.ToTable("Sales", (string)null); + + b.HasData( + new + { + SaleId = 1, + DateAndTimeOfSale = new DateTime(2025, 8, 1, 12, 37, 22, 0, DateTimeKind.Unspecified), + TotalPrice = 22.99m + }, + new + { + SaleId = 2, + DateAndTimeOfSale = new DateTime(2025, 8, 3, 14, 30, 48, 0, DateTimeKind.Unspecified), + TotalPrice = 61.20m + }, + new + { + SaleId = 3, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 11, 39, 10, 0, DateTimeKind.Unspecified), + TotalPrice = 156.20m + }, + new + { + SaleId = 4, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 19, 13, 55, 0, DateTimeKind.Unspecified), + TotalPrice = 107.50m + }, + new + { + SaleId = 5, + DateAndTimeOfSale = new DateTime(2025, 8, 8, 9, 4, 17, 0, DateTimeKind.Unspecified), + TotalPrice = 95.80m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.HasOne("Ecommerce.Api.Models.Product", "Product") + .WithMany("LineItems") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ecommerce.Api.Models.Sale", "Sale") + .WithMany("LineItems") + .HasForeignKey("SaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Sale"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.HasOne("Ecommerce.Api.Models.Category", "Category") + .WithMany("Products") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Navigation("LineItems"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Navigation("LineItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250818000433_DeclareCategoryFKOnProduct.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250818000433_DeclareCategoryFKOnProduct.cs new file mode 100644 index 00000000..c4c6c0bd --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250818000433_DeclareCategoryFKOnProduct.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + /// + public partial class DeclareCategoryFKOnProduct : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821222829_AddIsDeletedToSaleModel.Designer.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821222829_AddIsDeletedToSaleModel.Designer.cs new file mode 100644 index 00000000..7b2b205b --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821222829_AddIsDeletedToSaleModel.Designer.cs @@ -0,0 +1,377 @@ +// +using System; +using Ecommerce.Api.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + [DbContext(typeof(EcommerceDbContext))] + [Migration("20250821222829_AddIsDeletedToSaleModel")] + partial class AddIsDeletedToSaleModel + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("CategoryId")); + + b.Property("CategoryName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.HasKey("CategoryId"); + + b.ToTable("Categories", (string)null); + + b.HasData( + new + { + CategoryId = 1, + CategoryName = "Shoes", + IsDeleted = false + }, + new + { + CategoryId = 2, + CategoryName = "Socks", + IsDeleted = false + }, + new + { + CategoryId = 3, + CategoryName = "Pants", + IsDeleted = false + }, + new + { + CategoryId = 4, + CategoryName = "Shirts", + IsDeleted = false + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.Property("LineItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("LineItemId")); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("SaleId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("LineItemId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SaleId"); + + b.ToTable("LineItems", (string)null); + + b.HasData( + new + { + LineItemId = 1, + ProductId = 8, + Quantity = 1, + SaleId = 1, + UnitPrice = 22.99m + }, + new + { + LineItemId = 2, + ProductId = 6, + Quantity = 1, + SaleId = 2, + UnitPrice = 45.95m + }, + new + { + LineItemId = 3, + ProductId = 3, + Quantity = 1, + SaleId = 2, + UnitPrice = 15.25m + }, + new + { + LineItemId = 4, + ProductId = 7, + Quantity = 1, + SaleId = 3, + UnitPrice = 34.75m + }, + new + { + LineItemId = 5, + ProductId = 6, + Quantity = 1, + SaleId = 3, + UnitPrice = 45.95m + }, + new + { + LineItemId = 6, + ProductId = 1, + Quantity = 1, + SaleId = 3, + UnitPrice = 75.50m + }, + new + { + LineItemId = 7, + ProductId = 2, + Quantity = 2, + SaleId = 4, + UnitPrice = 53.75m + }, + new + { + LineItemId = 8, + ProductId = 4, + Quantity = 2, + SaleId = 5, + UnitPrice = 10.15m + }, + new + { + LineItemId = 9, + ProductId = 1, + Quantity = 1, + SaleId = 5, + UnitPrice = 75.50m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Property("ProductId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ProductId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("ProductName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("ProductId"); + + b.HasIndex("CategoryId"); + + b.ToTable("Products", (string)null); + + b.HasData( + new + { + ProductId = 1, + CategoryId = 1, + IsDeleted = false, + Price = 75.50m, + ProductName = "Hightop Sneakers" + }, + new + { + ProductId = 2, + CategoryId = 1, + IsDeleted = false, + Price = 53.75m, + ProductName = "Boat Loafers" + }, + new + { + ProductId = 3, + CategoryId = 2, + IsDeleted = false, + Price = 15.25m, + ProductName = "Dress Socks" + }, + new + { + ProductId = 4, + CategoryId = 2, + IsDeleted = false, + Price = 10.15m, + ProductName = "Ankle Socks" + }, + new + { + ProductId = 5, + CategoryId = 3, + IsDeleted = false, + Price = 35.99m, + ProductName = "Dress Slacks" + }, + new + { + ProductId = 6, + CategoryId = 3, + IsDeleted = false, + Price = 45.95m, + ProductName = "Stonewash Jeans" + }, + new + { + ProductId = 7, + CategoryId = 4, + IsDeleted = false, + Price = 34.75m, + ProductName = "Flannel Shirt" + }, + new + { + ProductId = 8, + CategoryId = 4, + IsDeleted = false, + Price = 22.99m, + ProductName = "Shortsleeve Polo" + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Property("SaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("SaleId")); + + b.Property("DateAndTimeOfSale") + .HasColumnType("datetime2"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("SaleId"); + + b.ToTable("Sales", (string)null); + + b.HasData( + new + { + SaleId = 1, + DateAndTimeOfSale = new DateTime(2025, 8, 1, 12, 37, 22, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 22.99m + }, + new + { + SaleId = 2, + DateAndTimeOfSale = new DateTime(2025, 8, 3, 14, 30, 48, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 61.20m + }, + new + { + SaleId = 3, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 11, 39, 10, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 156.20m + }, + new + { + SaleId = 4, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 19, 13, 55, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 107.50m + }, + new + { + SaleId = 5, + DateAndTimeOfSale = new DateTime(2025, 8, 8, 9, 4, 17, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 95.80m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.HasOne("Ecommerce.Api.Models.Product", "Product") + .WithMany("LineItems") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ecommerce.Api.Models.Sale", "Sale") + .WithMany("LineItems") + .HasForeignKey("SaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Sale"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.HasOne("Ecommerce.Api.Models.Category", "Category") + .WithMany("Products") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Navigation("LineItems"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Navigation("LineItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821222829_AddIsDeletedToSaleModel.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821222829_AddIsDeletedToSaleModel.cs new file mode 100644 index 00000000..f497b84e --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821222829_AddIsDeletedToSaleModel.cs @@ -0,0 +1,64 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + /// + public partial class AddIsDeletedToSaleModel : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "IsDeleted", + table: "Sales", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.UpdateData( + table: "Sales", + keyColumn: "SaleId", + keyValue: 1, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "Sales", + keyColumn: "SaleId", + keyValue: 2, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "Sales", + keyColumn: "SaleId", + keyValue: 3, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "Sales", + keyColumn: "SaleId", + keyValue: 4, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "Sales", + keyColumn: "SaleId", + keyValue: 5, + column: "IsDeleted", + value: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsDeleted", + table: "Sales"); + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231008_AddCascadeOnDeleteSale.Designer.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231008_AddCascadeOnDeleteSale.Designer.cs new file mode 100644 index 00000000..045f5319 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231008_AddCascadeOnDeleteSale.Designer.cs @@ -0,0 +1,377 @@ +// +using System; +using Ecommerce.Api.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + [DbContext(typeof(EcommerceDbContext))] + [Migration("20250821231008_AddCascadeOnDeleteSale")] + partial class AddCascadeOnDeleteSale + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("CategoryId")); + + b.Property("CategoryName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.HasKey("CategoryId"); + + b.ToTable("Categories", (string)null); + + b.HasData( + new + { + CategoryId = 1, + CategoryName = "Shoes", + IsDeleted = false + }, + new + { + CategoryId = 2, + CategoryName = "Socks", + IsDeleted = false + }, + new + { + CategoryId = 3, + CategoryName = "Pants", + IsDeleted = false + }, + new + { + CategoryId = 4, + CategoryName = "Shirts", + IsDeleted = false + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.Property("LineItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("LineItemId")); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("SaleId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("LineItemId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SaleId"); + + b.ToTable("LineItems", (string)null); + + b.HasData( + new + { + LineItemId = 1, + ProductId = 8, + Quantity = 1, + SaleId = 1, + UnitPrice = 22.99m + }, + new + { + LineItemId = 2, + ProductId = 6, + Quantity = 1, + SaleId = 2, + UnitPrice = 45.95m + }, + new + { + LineItemId = 3, + ProductId = 3, + Quantity = 1, + SaleId = 2, + UnitPrice = 15.25m + }, + new + { + LineItemId = 4, + ProductId = 7, + Quantity = 1, + SaleId = 3, + UnitPrice = 34.75m + }, + new + { + LineItemId = 5, + ProductId = 6, + Quantity = 1, + SaleId = 3, + UnitPrice = 45.95m + }, + new + { + LineItemId = 6, + ProductId = 1, + Quantity = 1, + SaleId = 3, + UnitPrice = 75.50m + }, + new + { + LineItemId = 7, + ProductId = 2, + Quantity = 2, + SaleId = 4, + UnitPrice = 53.75m + }, + new + { + LineItemId = 8, + ProductId = 4, + Quantity = 2, + SaleId = 5, + UnitPrice = 10.15m + }, + new + { + LineItemId = 9, + ProductId = 1, + Quantity = 1, + SaleId = 5, + UnitPrice = 75.50m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Property("ProductId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ProductId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("ProductName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("ProductId"); + + b.HasIndex("CategoryId"); + + b.ToTable("Products", (string)null); + + b.HasData( + new + { + ProductId = 1, + CategoryId = 1, + IsDeleted = false, + Price = 75.50m, + ProductName = "Hightop Sneakers" + }, + new + { + ProductId = 2, + CategoryId = 1, + IsDeleted = false, + Price = 53.75m, + ProductName = "Boat Loafers" + }, + new + { + ProductId = 3, + CategoryId = 2, + IsDeleted = false, + Price = 15.25m, + ProductName = "Dress Socks" + }, + new + { + ProductId = 4, + CategoryId = 2, + IsDeleted = false, + Price = 10.15m, + ProductName = "Ankle Socks" + }, + new + { + ProductId = 5, + CategoryId = 3, + IsDeleted = false, + Price = 35.99m, + ProductName = "Dress Slacks" + }, + new + { + ProductId = 6, + CategoryId = 3, + IsDeleted = false, + Price = 45.95m, + ProductName = "Stonewash Jeans" + }, + new + { + ProductId = 7, + CategoryId = 4, + IsDeleted = false, + Price = 34.75m, + ProductName = "Flannel Shirt" + }, + new + { + ProductId = 8, + CategoryId = 4, + IsDeleted = false, + Price = 22.99m, + ProductName = "Shortsleeve Polo" + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Property("SaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("SaleId")); + + b.Property("DateAndTimeOfSale") + .HasColumnType("datetime2"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("SaleId"); + + b.ToTable("Sales", (string)null); + + b.HasData( + new + { + SaleId = 1, + DateAndTimeOfSale = new DateTime(2025, 8, 1, 12, 37, 22, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 22.99m + }, + new + { + SaleId = 2, + DateAndTimeOfSale = new DateTime(2025, 8, 3, 14, 30, 48, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 61.20m + }, + new + { + SaleId = 3, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 11, 39, 10, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 156.20m + }, + new + { + SaleId = 4, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 19, 13, 55, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 107.50m + }, + new + { + SaleId = 5, + DateAndTimeOfSale = new DateTime(2025, 8, 8, 9, 4, 17, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 95.80m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.HasOne("Ecommerce.Api.Models.Product", "Product") + .WithMany("LineItems") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ecommerce.Api.Models.Sale", "Sale") + .WithMany("LineItems") + .HasForeignKey("SaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Sale"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.HasOne("Ecommerce.Api.Models.Category", "Category") + .WithMany("Products") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Navigation("LineItems"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Navigation("LineItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231008_AddCascadeOnDeleteSale.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231008_AddCascadeOnDeleteSale.cs new file mode 100644 index 00000000..416c1224 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231008_AddCascadeOnDeleteSale.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + /// + public partial class AddCascadeOnDeleteSale : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231346_RemoveCascadeOnDeleteSale.Designer.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231346_RemoveCascadeOnDeleteSale.Designer.cs new file mode 100644 index 00000000..9fe15584 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231346_RemoveCascadeOnDeleteSale.Designer.cs @@ -0,0 +1,377 @@ +// +using System; +using Ecommerce.Api.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + [DbContext(typeof(EcommerceDbContext))] + [Migration("20250821231346_RemoveCascadeOnDeleteSale")] + partial class RemoveCascadeOnDeleteSale + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("CategoryId")); + + b.Property("CategoryName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.HasKey("CategoryId"); + + b.ToTable("Categories", (string)null); + + b.HasData( + new + { + CategoryId = 1, + CategoryName = "Shoes", + IsDeleted = false + }, + new + { + CategoryId = 2, + CategoryName = "Socks", + IsDeleted = false + }, + new + { + CategoryId = 3, + CategoryName = "Pants", + IsDeleted = false + }, + new + { + CategoryId = 4, + CategoryName = "Shirts", + IsDeleted = false + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.Property("LineItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("LineItemId")); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("SaleId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("LineItemId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SaleId"); + + b.ToTable("LineItems", (string)null); + + b.HasData( + new + { + LineItemId = 1, + ProductId = 8, + Quantity = 1, + SaleId = 1, + UnitPrice = 22.99m + }, + new + { + LineItemId = 2, + ProductId = 6, + Quantity = 1, + SaleId = 2, + UnitPrice = 45.95m + }, + new + { + LineItemId = 3, + ProductId = 3, + Quantity = 1, + SaleId = 2, + UnitPrice = 15.25m + }, + new + { + LineItemId = 4, + ProductId = 7, + Quantity = 1, + SaleId = 3, + UnitPrice = 34.75m + }, + new + { + LineItemId = 5, + ProductId = 6, + Quantity = 1, + SaleId = 3, + UnitPrice = 45.95m + }, + new + { + LineItemId = 6, + ProductId = 1, + Quantity = 1, + SaleId = 3, + UnitPrice = 75.50m + }, + new + { + LineItemId = 7, + ProductId = 2, + Quantity = 2, + SaleId = 4, + UnitPrice = 53.75m + }, + new + { + LineItemId = 8, + ProductId = 4, + Quantity = 2, + SaleId = 5, + UnitPrice = 10.15m + }, + new + { + LineItemId = 9, + ProductId = 1, + Quantity = 1, + SaleId = 5, + UnitPrice = 75.50m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Property("ProductId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ProductId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("ProductName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("ProductId"); + + b.HasIndex("CategoryId"); + + b.ToTable("Products", (string)null); + + b.HasData( + new + { + ProductId = 1, + CategoryId = 1, + IsDeleted = false, + Price = 75.50m, + ProductName = "Hightop Sneakers" + }, + new + { + ProductId = 2, + CategoryId = 1, + IsDeleted = false, + Price = 53.75m, + ProductName = "Boat Loafers" + }, + new + { + ProductId = 3, + CategoryId = 2, + IsDeleted = false, + Price = 15.25m, + ProductName = "Dress Socks" + }, + new + { + ProductId = 4, + CategoryId = 2, + IsDeleted = false, + Price = 10.15m, + ProductName = "Ankle Socks" + }, + new + { + ProductId = 5, + CategoryId = 3, + IsDeleted = false, + Price = 35.99m, + ProductName = "Dress Slacks" + }, + new + { + ProductId = 6, + CategoryId = 3, + IsDeleted = false, + Price = 45.95m, + ProductName = "Stonewash Jeans" + }, + new + { + ProductId = 7, + CategoryId = 4, + IsDeleted = false, + Price = 34.75m, + ProductName = "Flannel Shirt" + }, + new + { + ProductId = 8, + CategoryId = 4, + IsDeleted = false, + Price = 22.99m, + ProductName = "Shortsleeve Polo" + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Property("SaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("SaleId")); + + b.Property("DateAndTimeOfSale") + .HasColumnType("datetime2"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("SaleId"); + + b.ToTable("Sales", (string)null); + + b.HasData( + new + { + SaleId = 1, + DateAndTimeOfSale = new DateTime(2025, 8, 1, 12, 37, 22, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 22.99m + }, + new + { + SaleId = 2, + DateAndTimeOfSale = new DateTime(2025, 8, 3, 14, 30, 48, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 61.20m + }, + new + { + SaleId = 3, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 11, 39, 10, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 156.20m + }, + new + { + SaleId = 4, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 19, 13, 55, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 107.50m + }, + new + { + SaleId = 5, + DateAndTimeOfSale = new DateTime(2025, 8, 8, 9, 4, 17, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 95.80m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.HasOne("Ecommerce.Api.Models.Product", "Product") + .WithMany("LineItems") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ecommerce.Api.Models.Sale", "Sale") + .WithMany("LineItems") + .HasForeignKey("SaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Sale"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.HasOne("Ecommerce.Api.Models.Category", "Category") + .WithMany("Products") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Navigation("LineItems"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Navigation("LineItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231346_RemoveCascadeOnDeleteSale.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231346_RemoveCascadeOnDeleteSale.cs new file mode 100644 index 00000000..2261a565 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231346_RemoveCascadeOnDeleteSale.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + /// + public partial class RemoveCascadeOnDeleteSale : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231525_AddIsDeletedToLineItemModel.Designer.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231525_AddIsDeletedToLineItemModel.Designer.cs new file mode 100644 index 00000000..915674dd --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231525_AddIsDeletedToLineItemModel.Designer.cs @@ -0,0 +1,389 @@ +// +using System; +using Ecommerce.Api.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + [DbContext(typeof(EcommerceDbContext))] + [Migration("20250821231525_AddIsDeletedToLineItemModel")] + partial class AddIsDeletedToLineItemModel + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("CategoryId")); + + b.Property("CategoryName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.HasKey("CategoryId"); + + b.ToTable("Categories", (string)null); + + b.HasData( + new + { + CategoryId = 1, + CategoryName = "Shoes", + IsDeleted = false + }, + new + { + CategoryId = 2, + CategoryName = "Socks", + IsDeleted = false + }, + new + { + CategoryId = 3, + CategoryName = "Pants", + IsDeleted = false + }, + new + { + CategoryId = 4, + CategoryName = "Shirts", + IsDeleted = false + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.Property("LineItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("LineItemId")); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("SaleId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("LineItemId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SaleId"); + + b.ToTable("LineItems", (string)null); + + b.HasData( + new + { + LineItemId = 1, + IsDeleted = false, + ProductId = 8, + Quantity = 1, + SaleId = 1, + UnitPrice = 22.99m + }, + new + { + LineItemId = 2, + IsDeleted = false, + ProductId = 6, + Quantity = 1, + SaleId = 2, + UnitPrice = 45.95m + }, + new + { + LineItemId = 3, + IsDeleted = false, + ProductId = 3, + Quantity = 1, + SaleId = 2, + UnitPrice = 15.25m + }, + new + { + LineItemId = 4, + IsDeleted = false, + ProductId = 7, + Quantity = 1, + SaleId = 3, + UnitPrice = 34.75m + }, + new + { + LineItemId = 5, + IsDeleted = false, + ProductId = 6, + Quantity = 1, + SaleId = 3, + UnitPrice = 45.95m + }, + new + { + LineItemId = 6, + IsDeleted = false, + ProductId = 1, + Quantity = 1, + SaleId = 3, + UnitPrice = 75.50m + }, + new + { + LineItemId = 7, + IsDeleted = false, + ProductId = 2, + Quantity = 2, + SaleId = 4, + UnitPrice = 53.75m + }, + new + { + LineItemId = 8, + IsDeleted = false, + ProductId = 4, + Quantity = 2, + SaleId = 5, + UnitPrice = 10.15m + }, + new + { + LineItemId = 9, + IsDeleted = false, + ProductId = 1, + Quantity = 1, + SaleId = 5, + UnitPrice = 75.50m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Property("ProductId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ProductId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("ProductName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("ProductId"); + + b.HasIndex("CategoryId"); + + b.ToTable("Products", (string)null); + + b.HasData( + new + { + ProductId = 1, + CategoryId = 1, + IsDeleted = false, + Price = 75.50m, + ProductName = "Hightop Sneakers" + }, + new + { + ProductId = 2, + CategoryId = 1, + IsDeleted = false, + Price = 53.75m, + ProductName = "Boat Loafers" + }, + new + { + ProductId = 3, + CategoryId = 2, + IsDeleted = false, + Price = 15.25m, + ProductName = "Dress Socks" + }, + new + { + ProductId = 4, + CategoryId = 2, + IsDeleted = false, + Price = 10.15m, + ProductName = "Ankle Socks" + }, + new + { + ProductId = 5, + CategoryId = 3, + IsDeleted = false, + Price = 35.99m, + ProductName = "Dress Slacks" + }, + new + { + ProductId = 6, + CategoryId = 3, + IsDeleted = false, + Price = 45.95m, + ProductName = "Stonewash Jeans" + }, + new + { + ProductId = 7, + CategoryId = 4, + IsDeleted = false, + Price = 34.75m, + ProductName = "Flannel Shirt" + }, + new + { + ProductId = 8, + CategoryId = 4, + IsDeleted = false, + Price = 22.99m, + ProductName = "Shortsleeve Polo" + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Property("SaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("SaleId")); + + b.Property("DateAndTimeOfSale") + .HasColumnType("datetime2"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("SaleId"); + + b.ToTable("Sales", (string)null); + + b.HasData( + new + { + SaleId = 1, + DateAndTimeOfSale = new DateTime(2025, 8, 1, 12, 37, 22, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 22.99m + }, + new + { + SaleId = 2, + DateAndTimeOfSale = new DateTime(2025, 8, 3, 14, 30, 48, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 61.20m + }, + new + { + SaleId = 3, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 11, 39, 10, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 156.20m + }, + new + { + SaleId = 4, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 19, 13, 55, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 107.50m + }, + new + { + SaleId = 5, + DateAndTimeOfSale = new DateTime(2025, 8, 8, 9, 4, 17, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 95.80m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.HasOne("Ecommerce.Api.Models.Product", "Product") + .WithMany("LineItems") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ecommerce.Api.Models.Sale", "Sale") + .WithMany("LineItems") + .HasForeignKey("SaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Sale"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.HasOne("Ecommerce.Api.Models.Category", "Category") + .WithMany("Products") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Navigation("LineItems"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Navigation("LineItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231525_AddIsDeletedToLineItemModel.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231525_AddIsDeletedToLineItemModel.cs new file mode 100644 index 00000000..f2f6e52d --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821231525_AddIsDeletedToLineItemModel.cs @@ -0,0 +1,92 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + /// + public partial class AddIsDeletedToLineItemModel : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "IsDeleted", + table: "LineItems", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.UpdateData( + table: "LineItems", + keyColumn: "LineItemId", + keyValue: 1, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "LineItems", + keyColumn: "LineItemId", + keyValue: 2, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "LineItems", + keyColumn: "LineItemId", + keyValue: 3, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "LineItems", + keyColumn: "LineItemId", + keyValue: 4, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "LineItems", + keyColumn: "LineItemId", + keyValue: 5, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "LineItems", + keyColumn: "LineItemId", + keyValue: 6, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "LineItems", + keyColumn: "LineItemId", + keyValue: 7, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "LineItems", + keyColumn: "LineItemId", + keyValue: 8, + column: "IsDeleted", + value: false); + + migrationBuilder.UpdateData( + table: "LineItems", + keyColumn: "LineItemId", + keyValue: 9, + column: "IsDeleted", + value: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsDeleted", + table: "LineItems"); + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821233712_AddPriceAndQuantityConstraints.Designer.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821233712_AddPriceAndQuantityConstraints.Designer.cs new file mode 100644 index 00000000..18cd6d96 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821233712_AddPriceAndQuantityConstraints.Designer.cs @@ -0,0 +1,395 @@ +// +using System; +using Ecommerce.Api.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + [DbContext(typeof(EcommerceDbContext))] + [Migration("20250821233712_AddPriceAndQuantityConstraints")] + partial class AddPriceAndQuantityConstraints + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("CategoryId")); + + b.Property("CategoryName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.HasKey("CategoryId"); + + b.ToTable("Categories", (string)null); + + b.HasData( + new + { + CategoryId = 1, + CategoryName = "Shoes", + IsDeleted = false + }, + new + { + CategoryId = 2, + CategoryName = "Socks", + IsDeleted = false + }, + new + { + CategoryId = 3, + CategoryName = "Pants", + IsDeleted = false + }, + new + { + CategoryId = 4, + CategoryName = "Shirts", + IsDeleted = false + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.Property("LineItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("LineItemId")); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("SaleId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("LineItemId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SaleId"); + + b.ToTable("LineItems", null, t => + { + t.HasCheckConstraint("CK_LineItems_Quantity", "Quantity > 0"); + }); + + b.HasData( + new + { + LineItemId = 1, + IsDeleted = false, + ProductId = 8, + Quantity = 1, + SaleId = 1, + UnitPrice = 22.99m + }, + new + { + LineItemId = 2, + IsDeleted = false, + ProductId = 6, + Quantity = 1, + SaleId = 2, + UnitPrice = 45.95m + }, + new + { + LineItemId = 3, + IsDeleted = false, + ProductId = 3, + Quantity = 1, + SaleId = 2, + UnitPrice = 15.25m + }, + new + { + LineItemId = 4, + IsDeleted = false, + ProductId = 7, + Quantity = 1, + SaleId = 3, + UnitPrice = 34.75m + }, + new + { + LineItemId = 5, + IsDeleted = false, + ProductId = 6, + Quantity = 1, + SaleId = 3, + UnitPrice = 45.95m + }, + new + { + LineItemId = 6, + IsDeleted = false, + ProductId = 1, + Quantity = 1, + SaleId = 3, + UnitPrice = 75.50m + }, + new + { + LineItemId = 7, + IsDeleted = false, + ProductId = 2, + Quantity = 2, + SaleId = 4, + UnitPrice = 53.75m + }, + new + { + LineItemId = 8, + IsDeleted = false, + ProductId = 4, + Quantity = 2, + SaleId = 5, + UnitPrice = 10.15m + }, + new + { + LineItemId = 9, + IsDeleted = false, + ProductId = 1, + Quantity = 1, + SaleId = 5, + UnitPrice = 75.50m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Property("ProductId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ProductId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("ProductName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("ProductId"); + + b.HasIndex("CategoryId"); + + b.ToTable("Products", null, t => + { + t.HasCheckConstraint("CK_Products_Price", "Price >= 0"); + }); + + b.HasData( + new + { + ProductId = 1, + CategoryId = 1, + IsDeleted = false, + Price = 75.50m, + ProductName = "Hightop Sneakers" + }, + new + { + ProductId = 2, + CategoryId = 1, + IsDeleted = false, + Price = 53.75m, + ProductName = "Boat Loafers" + }, + new + { + ProductId = 3, + CategoryId = 2, + IsDeleted = false, + Price = 15.25m, + ProductName = "Dress Socks" + }, + new + { + ProductId = 4, + CategoryId = 2, + IsDeleted = false, + Price = 10.15m, + ProductName = "Ankle Socks" + }, + new + { + ProductId = 5, + CategoryId = 3, + IsDeleted = false, + Price = 35.99m, + ProductName = "Dress Slacks" + }, + new + { + ProductId = 6, + CategoryId = 3, + IsDeleted = false, + Price = 45.95m, + ProductName = "Stonewash Jeans" + }, + new + { + ProductId = 7, + CategoryId = 4, + IsDeleted = false, + Price = 34.75m, + ProductName = "Flannel Shirt" + }, + new + { + ProductId = 8, + CategoryId = 4, + IsDeleted = false, + Price = 22.99m, + ProductName = "Shortsleeve Polo" + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Property("SaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("SaleId")); + + b.Property("DateAndTimeOfSale") + .HasColumnType("datetime2"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("SaleId"); + + b.ToTable("Sales", (string)null); + + b.HasData( + new + { + SaleId = 1, + DateAndTimeOfSale = new DateTime(2025, 8, 1, 12, 37, 22, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 22.99m + }, + new + { + SaleId = 2, + DateAndTimeOfSale = new DateTime(2025, 8, 3, 14, 30, 48, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 61.20m + }, + new + { + SaleId = 3, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 11, 39, 10, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 156.20m + }, + new + { + SaleId = 4, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 19, 13, 55, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 107.50m + }, + new + { + SaleId = 5, + DateAndTimeOfSale = new DateTime(2025, 8, 8, 9, 4, 17, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 95.80m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.HasOne("Ecommerce.Api.Models.Product", "Product") + .WithMany("LineItems") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ecommerce.Api.Models.Sale", "Sale") + .WithMany("LineItems") + .HasForeignKey("SaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Sale"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.HasOne("Ecommerce.Api.Models.Category", "Category") + .WithMany("Products") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Navigation("LineItems"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Navigation("LineItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821233712_AddPriceAndQuantityConstraints.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821233712_AddPriceAndQuantityConstraints.cs new file mode 100644 index 00000000..680d4b2d --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250821233712_AddPriceAndQuantityConstraints.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + /// + public partial class AddPriceAndQuantityConstraints : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddCheckConstraint( + name: "CK_Products_Price", + table: "Products", + sql: "Price >= 0"); + + migrationBuilder.AddCheckConstraint( + name: "CK_LineItems_Quantity", + table: "LineItems", + sql: "Quantity > 0"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropCheckConstraint( + name: "CK_Products_Price", + table: "Products"); + + migrationBuilder.DropCheckConstraint( + name: "CK_LineItems_Quantity", + table: "LineItems"); + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250822230010_ChangePriceCheckToGreaterThanZero.Designer.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250822230010_ChangePriceCheckToGreaterThanZero.Designer.cs new file mode 100644 index 00000000..74c4f81c --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250822230010_ChangePriceCheckToGreaterThanZero.Designer.cs @@ -0,0 +1,395 @@ +// +using System; +using Ecommerce.Api.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + [DbContext(typeof(EcommerceDbContext))] + [Migration("20250822230010_ChangePriceCheckToGreaterThanZero")] + partial class ChangePriceCheckToGreaterThanZero + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("CategoryId")); + + b.Property("CategoryName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.HasKey("CategoryId"); + + b.ToTable("Categories", (string)null); + + b.HasData( + new + { + CategoryId = 1, + CategoryName = "Shoes", + IsDeleted = false + }, + new + { + CategoryId = 2, + CategoryName = "Socks", + IsDeleted = false + }, + new + { + CategoryId = 3, + CategoryName = "Pants", + IsDeleted = false + }, + new + { + CategoryId = 4, + CategoryName = "Shirts", + IsDeleted = false + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.Property("LineItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("LineItemId")); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("SaleId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("LineItemId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SaleId"); + + b.ToTable("LineItems", null, t => + { + t.HasCheckConstraint("CK_LineItems_Quantity", "Quantity > 0"); + }); + + b.HasData( + new + { + LineItemId = 1, + IsDeleted = false, + ProductId = 8, + Quantity = 1, + SaleId = 1, + UnitPrice = 22.99m + }, + new + { + LineItemId = 2, + IsDeleted = false, + ProductId = 6, + Quantity = 1, + SaleId = 2, + UnitPrice = 45.95m + }, + new + { + LineItemId = 3, + IsDeleted = false, + ProductId = 3, + Quantity = 1, + SaleId = 2, + UnitPrice = 15.25m + }, + new + { + LineItemId = 4, + IsDeleted = false, + ProductId = 7, + Quantity = 1, + SaleId = 3, + UnitPrice = 34.75m + }, + new + { + LineItemId = 5, + IsDeleted = false, + ProductId = 6, + Quantity = 1, + SaleId = 3, + UnitPrice = 45.95m + }, + new + { + LineItemId = 6, + IsDeleted = false, + ProductId = 1, + Quantity = 1, + SaleId = 3, + UnitPrice = 75.50m + }, + new + { + LineItemId = 7, + IsDeleted = false, + ProductId = 2, + Quantity = 2, + SaleId = 4, + UnitPrice = 53.75m + }, + new + { + LineItemId = 8, + IsDeleted = false, + ProductId = 4, + Quantity = 2, + SaleId = 5, + UnitPrice = 10.15m + }, + new + { + LineItemId = 9, + IsDeleted = false, + ProductId = 1, + Quantity = 1, + SaleId = 5, + UnitPrice = 75.50m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Property("ProductId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ProductId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("ProductName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("ProductId"); + + b.HasIndex("CategoryId"); + + b.ToTable("Products", null, t => + { + t.HasCheckConstraint("CK_Products_Price", "Price > 0"); + }); + + b.HasData( + new + { + ProductId = 1, + CategoryId = 1, + IsDeleted = false, + Price = 75.50m, + ProductName = "Hightop Sneakers" + }, + new + { + ProductId = 2, + CategoryId = 1, + IsDeleted = false, + Price = 53.75m, + ProductName = "Boat Loafers" + }, + new + { + ProductId = 3, + CategoryId = 2, + IsDeleted = false, + Price = 15.25m, + ProductName = "Dress Socks" + }, + new + { + ProductId = 4, + CategoryId = 2, + IsDeleted = false, + Price = 10.15m, + ProductName = "Ankle Socks" + }, + new + { + ProductId = 5, + CategoryId = 3, + IsDeleted = false, + Price = 35.99m, + ProductName = "Dress Slacks" + }, + new + { + ProductId = 6, + CategoryId = 3, + IsDeleted = false, + Price = 45.95m, + ProductName = "Stonewash Jeans" + }, + new + { + ProductId = 7, + CategoryId = 4, + IsDeleted = false, + Price = 34.75m, + ProductName = "Flannel Shirt" + }, + new + { + ProductId = 8, + CategoryId = 4, + IsDeleted = false, + Price = 22.99m, + ProductName = "Shortsleeve Polo" + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Property("SaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("SaleId")); + + b.Property("DateAndTimeOfSale") + .HasColumnType("datetime2"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("SaleId"); + + b.ToTable("Sales", (string)null); + + b.HasData( + new + { + SaleId = 1, + DateAndTimeOfSale = new DateTime(2025, 8, 1, 12, 37, 22, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 22.99m + }, + new + { + SaleId = 2, + DateAndTimeOfSale = new DateTime(2025, 8, 3, 14, 30, 48, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 61.20m + }, + new + { + SaleId = 3, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 11, 39, 10, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 156.20m + }, + new + { + SaleId = 4, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 19, 13, 55, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 107.50m + }, + new + { + SaleId = 5, + DateAndTimeOfSale = new DateTime(2025, 8, 8, 9, 4, 17, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 95.80m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.HasOne("Ecommerce.Api.Models.Product", "Product") + .WithMany("LineItems") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ecommerce.Api.Models.Sale", "Sale") + .WithMany("LineItems") + .HasForeignKey("SaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Sale"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.HasOne("Ecommerce.Api.Models.Category", "Category") + .WithMany("Products") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Navigation("LineItems"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Navigation("LineItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250822230010_ChangePriceCheckToGreaterThanZero.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250822230010_ChangePriceCheckToGreaterThanZero.cs new file mode 100644 index 00000000..7e22bb9e --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/20250822230010_ChangePriceCheckToGreaterThanZero.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + /// + public partial class ChangePriceCheckToGreaterThanZero : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropCheckConstraint( + name: "CK_Products_Price", + table: "Products"); + + migrationBuilder.AddCheckConstraint( + name: "CK_Products_Price", + table: "Products", + sql: "Price > 0"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropCheckConstraint( + name: "CK_Products_Price", + table: "Products"); + + migrationBuilder.AddCheckConstraint( + name: "CK_Products_Price", + table: "Products", + sql: "Price >= 0"); + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/EcommerceDbContextModelSnapshot.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/EcommerceDbContextModelSnapshot.cs new file mode 100644 index 00000000..b4e569c7 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Migrations/EcommerceDbContextModelSnapshot.cs @@ -0,0 +1,392 @@ +// +using System; +using Ecommerce.Api.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Ecommerce.Api.Migrations +{ + [DbContext(typeof(EcommerceDbContext))] + partial class EcommerceDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Property("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("CategoryId")); + + b.Property("CategoryName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.HasKey("CategoryId"); + + b.ToTable("Categories", (string)null); + + b.HasData( + new + { + CategoryId = 1, + CategoryName = "Shoes", + IsDeleted = false + }, + new + { + CategoryId = 2, + CategoryName = "Socks", + IsDeleted = false + }, + new + { + CategoryId = 3, + CategoryName = "Pants", + IsDeleted = false + }, + new + { + CategoryId = 4, + CategoryName = "Shirts", + IsDeleted = false + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.Property("LineItemId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("LineItemId")); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("SaleId") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("LineItemId"); + + b.HasIndex("ProductId"); + + b.HasIndex("SaleId"); + + b.ToTable("LineItems", null, t => + { + t.HasCheckConstraint("CK_LineItems_Quantity", "Quantity > 0"); + }); + + b.HasData( + new + { + LineItemId = 1, + IsDeleted = false, + ProductId = 8, + Quantity = 1, + SaleId = 1, + UnitPrice = 22.99m + }, + new + { + LineItemId = 2, + IsDeleted = false, + ProductId = 6, + Quantity = 1, + SaleId = 2, + UnitPrice = 45.95m + }, + new + { + LineItemId = 3, + IsDeleted = false, + ProductId = 3, + Quantity = 1, + SaleId = 2, + UnitPrice = 15.25m + }, + new + { + LineItemId = 4, + IsDeleted = false, + ProductId = 7, + Quantity = 1, + SaleId = 3, + UnitPrice = 34.75m + }, + new + { + LineItemId = 5, + IsDeleted = false, + ProductId = 6, + Quantity = 1, + SaleId = 3, + UnitPrice = 45.95m + }, + new + { + LineItemId = 6, + IsDeleted = false, + ProductId = 1, + Quantity = 1, + SaleId = 3, + UnitPrice = 75.50m + }, + new + { + LineItemId = 7, + IsDeleted = false, + ProductId = 2, + Quantity = 2, + SaleId = 4, + UnitPrice = 53.75m + }, + new + { + LineItemId = 8, + IsDeleted = false, + ProductId = 4, + Quantity = 2, + SaleId = 5, + UnitPrice = 10.15m + }, + new + { + LineItemId = 9, + IsDeleted = false, + ProductId = 1, + Quantity = 1, + SaleId = 5, + UnitPrice = 75.50m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Property("ProductId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ProductId")); + + b.Property("CategoryId") + .HasColumnType("int"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("ProductName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("ProductId"); + + b.HasIndex("CategoryId"); + + b.ToTable("Products", null, t => + { + t.HasCheckConstraint("CK_Products_Price", "Price > 0"); + }); + + b.HasData( + new + { + ProductId = 1, + CategoryId = 1, + IsDeleted = false, + Price = 75.50m, + ProductName = "Hightop Sneakers" + }, + new + { + ProductId = 2, + CategoryId = 1, + IsDeleted = false, + Price = 53.75m, + ProductName = "Boat Loafers" + }, + new + { + ProductId = 3, + CategoryId = 2, + IsDeleted = false, + Price = 15.25m, + ProductName = "Dress Socks" + }, + new + { + ProductId = 4, + CategoryId = 2, + IsDeleted = false, + Price = 10.15m, + ProductName = "Ankle Socks" + }, + new + { + ProductId = 5, + CategoryId = 3, + IsDeleted = false, + Price = 35.99m, + ProductName = "Dress Slacks" + }, + new + { + ProductId = 6, + CategoryId = 3, + IsDeleted = false, + Price = 45.95m, + ProductName = "Stonewash Jeans" + }, + new + { + ProductId = 7, + CategoryId = 4, + IsDeleted = false, + Price = 34.75m, + ProductName = "Flannel Shirt" + }, + new + { + ProductId = 8, + CategoryId = 4, + IsDeleted = false, + Price = 22.99m, + ProductName = "Shortsleeve Polo" + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Property("SaleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("SaleId")); + + b.Property("DateAndTimeOfSale") + .HasColumnType("datetime2"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("SaleId"); + + b.ToTable("Sales", (string)null); + + b.HasData( + new + { + SaleId = 1, + DateAndTimeOfSale = new DateTime(2025, 8, 1, 12, 37, 22, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 22.99m + }, + new + { + SaleId = 2, + DateAndTimeOfSale = new DateTime(2025, 8, 3, 14, 30, 48, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 61.20m + }, + new + { + SaleId = 3, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 11, 39, 10, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 156.20m + }, + new + { + SaleId = 4, + DateAndTimeOfSale = new DateTime(2025, 8, 7, 19, 13, 55, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 107.50m + }, + new + { + SaleId = 5, + DateAndTimeOfSale = new DateTime(2025, 8, 8, 9, 4, 17, 0, DateTimeKind.Unspecified), + IsDeleted = false, + TotalPrice = 95.80m + }); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.LineItem", b => + { + b.HasOne("Ecommerce.Api.Models.Product", "Product") + .WithMany("LineItems") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ecommerce.Api.Models.Sale", "Sale") + .WithMany("LineItems") + .HasForeignKey("SaleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Sale"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.HasOne("Ecommerce.Api.Models.Category", "Category") + .WithMany("Products") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Category", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Product", b => + { + b.Navigation("LineItems"); + }); + + modelBuilder.Entity("Ecommerce.Api.Models.Sale", b => + { + b.Navigation("LineItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/Category.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/Category.cs new file mode 100644 index 00000000..05703b78 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/Category.cs @@ -0,0 +1,14 @@ +namespace Ecommerce.Api.Models; + +public class Category +{ + public int CategoryId { get; set; } + + public string CategoryName { get; set; } + + public List Products { get; } = []; + + public bool IsDeleted { get; set; } = false; + + public override string ToString() => $"{CategoryName}"; +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/CategoryDto.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/CategoryDto.cs new file mode 100644 index 00000000..50083eb1 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/CategoryDto.cs @@ -0,0 +1,10 @@ +namespace Ecommerce.Api.Models; + +public class CategoryDto +{ + public int CategoryId { get; set; } + + public string CategoryName { get; set; } + + public List Products { get; set; } = []; +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/LineItem.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/LineItem.cs new file mode 100644 index 00000000..3c4b003c --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/LineItem.cs @@ -0,0 +1,20 @@ +namespace Ecommerce.Api.Models; + +public class LineItem +{ + public int LineItemId { get; set; } + + public int SaleId { get; set; } + + public Sale Sale { get; set; } + + public int ProductId { get; set; } + + public Product Product { get; set; } + + public int Quantity { get; set; } + + public decimal UnitPrice { get; set; } + + public bool IsDeleted { get; set; } = false; +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/LineItemDto.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/LineItemDto.cs new file mode 100644 index 00000000..42c04530 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/LineItemDto.cs @@ -0,0 +1,18 @@ +namespace Ecommerce.Api.Models; + +public class LineItemDto +{ + public int LineItemId { get; set; } + + public int ProductId { get; set; } + + public string ProductName { get; set; } + + public string Category { get; set; } + + public int Quantity { get; set; } + + public decimal UnitPrice { get; set; } + + public int SaleId { get; set; } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/PaginationParams.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/PaginationParams.cs new file mode 100644 index 00000000..bc8b6955 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/PaginationParams.cs @@ -0,0 +1,41 @@ +namespace Ecommerce.Api.Models; + +public class PaginationParams +{ + private const int MaxPageSize = 50; + public int PageNumber { get; set; } = 1; + + private int _pageSize = 10; + + public int PageSize + { + get => _pageSize; + set => _pageSize = (value > MaxPageSize) ? MaxPageSize : value; + } + + public string? SortBy { get; set; } + + public bool? SortAscending { get; set; } + + public string? ProductName { get; set; } + + public string? CategoryName { get; set; } + + public decimal? MinPrice { get; set; } + + public decimal? MaxPrice { get; set; } + + public int? MinQuantity { get; set; } + + public int? MaxQuantity { get; set; } + + public int? ProductId { get; set; } + + public int? SaleId { get; set; } + + public int? LineItemId { get; set; } + + public int? MinLineItems { get; set; } + + public int? MaxLineItems { get; set; } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/Product.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/Product.cs new file mode 100644 index 00000000..c87aaae7 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/Product.cs @@ -0,0 +1,18 @@ +namespace Ecommerce.Api.Models; + +public class Product +{ + public int ProductId { get; set; } + + public string ProductName { get; set; } + + public decimal Price { get; set; } + + public int CategoryId { get; set; } + + public Category Category { get; set; } + + public List LineItems { get; } = []; + + public bool IsDeleted { get; set; } = false; +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/ProductDto.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/ProductDto.cs new file mode 100644 index 00000000..f89ef438 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/ProductDto.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace Ecommerce.Api.Models; + +public class ProductDto +{ + public int ProductId { get; set; } + + [Required] + public string? ProductName { get; set; } + + [Required] + public decimal Price { get; set; } + + [Required] + public string? Category { get; set; } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/Sale.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/Sale.cs new file mode 100644 index 00000000..cced1b2c --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/Sale.cs @@ -0,0 +1,16 @@ +namespace Ecommerce.Api.Models; + +public class Sale +{ + public int SaleId { get; set; } + + public DateTime DateAndTimeOfSale { get; set; } + + public decimal TotalPrice { get; set; } + + public decimal Subtotal => LineItems.Sum(li => li.Quantity * li.UnitPrice); + + public List LineItems { get; set; } = []; + + public bool IsDeleted { get; set; } = false; +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/SaleDto.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/SaleDto.cs new file mode 100644 index 00000000..4ab2824b --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/SaleDto.cs @@ -0,0 +1,14 @@ +namespace Ecommerce.Api.Models; + +public class SaleDto +{ + public int SaleId { get; set; } + + public DateTime DateAndTimeOfSale { get; set; } + + public decimal TotalPrice { get; set; } + + public decimal Subtotal => LineItems.Sum(li => li.Quantity * li.UnitPrice); + + public List LineItems { get; set; } = []; +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/UpdateLineItemOnSaleDto.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/UpdateLineItemOnSaleDto.cs new file mode 100644 index 00000000..5462f0d8 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/UpdateLineItemOnSaleDto.cs @@ -0,0 +1,10 @@ +namespace Ecommerce.Api.Models; + +public class UpdateLineItemOnSaleDto +{ + public int? LineItemId { get; set; } + + public int ProductId { get; set; } + + public int Quantity { get; set; } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/UpdateSaleDto.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/UpdateSaleDto.cs new file mode 100644 index 00000000..754aec2a --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/UpdateSaleDto.cs @@ -0,0 +1,6 @@ +namespace Ecommerce.Api.Models; + +public class UpdateSaleDto +{ + public List LineItems { get; set; } = new List(); +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteCategoryDto.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteCategoryDto.cs new file mode 100644 index 00000000..c2c27708 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteCategoryDto.cs @@ -0,0 +1,6 @@ +namespace Ecommerce.Api.Models; + +public class WriteCategoryDto +{ + public string CategoryName { get; set; } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteLineItemDto.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteLineItemDto.cs new file mode 100644 index 00000000..ca52274e --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteLineItemDto.cs @@ -0,0 +1,10 @@ +namespace Ecommerce.Api.Models; + +public class WriteLineItemDto +{ + public int ProductId { get; set; } + + public int Quantity { get; set; } + + public int SaleId { get; set; } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteLineItemOnSaleDto.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteLineItemOnSaleDto.cs new file mode 100644 index 00000000..90b0960b --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteLineItemOnSaleDto.cs @@ -0,0 +1,8 @@ +namespace Ecommerce.Api.Models; + +public class WriteLineItemOnSaleDto +{ + public int ProductId { get; set; } + + public int Quantity { get; set; } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteProductDto.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteProductDto.cs new file mode 100644 index 00000000..54ea3d66 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteProductDto.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + +namespace Ecommerce.Api.Models; + +public class WriteProductDto +{ + [Required] + public string ProductName { get; set; } + + [Required] + public decimal Price { get; set; } + + [Required] + public int CategoryId { get; set; } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteSaleDto.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteSaleDto.cs new file mode 100644 index 00000000..59cf8eb5 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/WriteSaleDto.cs @@ -0,0 +1,6 @@ +namespace Ecommerce.Api.Models; + +public class WriteSaleDto +{ + public List LineItems { get; set; } = new List(); +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Program.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Program.cs new file mode 100644 index 00000000..2b0e72c2 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Program.cs @@ -0,0 +1,41 @@ +using Microsoft.EntityFrameworkCore; +using Ecommerce.Api.Data; +using Ecommerce.Api.Repository; +using Ecommerce.Api.Services; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); +builder.Services.AddControllers(); + +builder.Services.AddDbContext(opt => opt.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); + +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); + +builder.Services.AddOpenApi(); + +var app = builder.Build(); + +using (var scope = app.Services.CreateScope()) +{ + var dbContext = scope.ServiceProvider.GetRequiredService(); + dbContext.Database.Migrate(); +} + +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.MapControllers(); + +app.Run(); \ No newline at end of file diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Properties/launchSettings.json b/Ecommerce.Api.Lepros311/Ecommerce.Api/Properties/launchSettings.json new file mode 100644 index 00000000..26110b5a --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Properties/launchSettings.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5101", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "workingDirectory": "C:\\Users\\Andrew\\Documents\\coding stuff\\C#\\The C# Academy\\Ecommerce.Api\\", + "launchBrowser": true, + "launchUrl": "swagger", + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:7150;http://localhost:5101", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/CategoryRepository.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/CategoryRepository.cs new file mode 100644 index 00000000..b9922bda --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/CategoryRepository.cs @@ -0,0 +1,179 @@ +using Ecommerce.Api.Data; +using Ecommerce.Api.Models; +using Ecommerce.Api.Responses; +using Microsoft.EntityFrameworkCore; + +namespace Ecommerce.Api.Repository; + +public class CategoryRepository : ICategoryRepository +{ + private readonly EcommerceDbContext _dbContext; + + public CategoryRepository(EcommerceDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task>> GetPagedCategories(PaginationParams paginationParams) + { + var response = new PagedResponse>(data: new List(), + pageNumber: paginationParams.PageNumber, + pageSize: paginationParams.PageSize, + totalRecords: 0); + try + { + var query = _dbContext.Categories.Include(c => c.Products).AsQueryable(); + + if (!string.IsNullOrEmpty(paginationParams.CategoryName)) + query = query.Where(c => c.CategoryName.Contains(paginationParams.CategoryName)); + + var totalCount = await query.CountAsync(); + + var sortBy = paginationParams.SortBy?.Trim().ToLower() ?? "categoryid"; + var sortAscending = paginationParams.SortAscending; + + bool useAscending = sortAscending ?? (sortBy == "categoryid" ? false : true); + + query = sortBy switch + { + "categoryname" => useAscending ? query.OrderBy(c => c.CategoryName) : query.OrderByDescending(c => c.CategoryName), + _ => useAscending ? query.OrderBy(c => c.CategoryId) : query.OrderByDescending(c => c.CategoryId) + }; + + var pagedCategories = await query + .Skip((paginationParams.PageNumber - 1) * paginationParams.PageSize) + .Take(paginationParams.PageSize) + .ToListAsync(); + + response.Status = ResponseStatus.Success; + response.Data = pagedCategories; + } + catch (Exception ex) + { + response.Message = $"Error in CategoryRepository {nameof(GetPagedCategories)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } + + public async Task> GetCategoryById(int id) + { + var response = new BaseResponse(); + + try + { + var category = await _dbContext.Categories.Include(c => c.Products).FirstOrDefaultAsync(c => c.CategoryId == id); + + if (category == null) + { + response.Status = ResponseStatus.Fail; + response.Message = "Category not found."; + } + else + { + response.Status = ResponseStatus.Success; + response.Data = category; + } + } + catch (Exception ex) + { + response.Message = $"Error in CategoryRepository {nameof(CategoryRepository)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } + + public async Task> CreateCategory(Category category) + { + var response = new BaseResponse(); + + try + { + _dbContext.Categories.Add(category); + + await _dbContext.SaveChangesAsync(); + + if (category == null) + { + response.Status = ResponseStatus.Fail; + response.Message = "Category not created."; + } + else + { + response.Status = ResponseStatus.Success; + response.Data = category; + } + } + catch (Exception ex) + { + response.Message = $"Error in CategoryRepository {nameof(CreateCategory)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } + + public async Task> UpdateCategory(Category updatedCategory) + { + var response = new BaseResponse(); + + try + { + _dbContext.Categories.Update(updatedCategory); + var affectedRows = await _dbContext.SaveChangesAsync(); + + if (affectedRows == 0) + { + response.Status = ResponseStatus.Fail; + response.Message = "No changes were saved."; + } + else + { + response.Status = ResponseStatus.Success; + response.Data = updatedCategory; + } + } + catch (Exception ex) + { + response.Message = $"Error in CategoryRepository {nameof(UpdateCategory)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } + + public async Task> DeleteCategory(int id) + { + var response = new BaseResponse(); + + try + { + response = await GetCategoryById(id); + + response.Data.IsDeleted = true; + + _dbContext.Categories.Update(response.Data); + var affectedRows = await _dbContext.SaveChangesAsync(); + + if (affectedRows == 0) + { + response.Status = ResponseStatus.Fail; + response.Message = "Deletion failed."; + } + else + { + response.Status = ResponseStatus.Success; + response.Message = "Category deleted."; + } + } + catch (Exception ex) + { + response.Message = $"Error in CategoryRepository {nameof(DeleteCategory)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ICategoryRepository.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ICategoryRepository.cs new file mode 100644 index 00000000..46d75457 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ICategoryRepository.cs @@ -0,0 +1,17 @@ +using Ecommerce.Api.Models; +using Ecommerce.Api.Responses; + +namespace Ecommerce.Api.Repository; + +public interface ICategoryRepository +{ + public Task>> GetPagedCategories(PaginationParams paginationParams); + + public Task> GetCategoryById(int id); + + public Task> CreateCategory(Category category); + + public Task> UpdateCategory(Category updatedCategory); + + public Task> DeleteCategory(int id); +} \ No newline at end of file diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ILineItemRepository.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ILineItemRepository.cs new file mode 100644 index 00000000..9e430cf6 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ILineItemRepository.cs @@ -0,0 +1,17 @@ +using Ecommerce.Api.Models; +using Ecommerce.Api.Responses; + +namespace Ecommerce.Api.Repository; + +public interface ILineItemRepository +{ + public Task>> GetPagedLineItems(PaginationParams paginationParams); + + public Task> GetLineItemById(int id); + + public Task> CreateLineItem(LineItem lineItem); + + public Task> UpdateLineItem(LineItem updatedLineItem); + + public Task> DeleteLineItem(int id); +} \ No newline at end of file diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/IProductRepository.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/IProductRepository.cs new file mode 100644 index 00000000..76338071 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/IProductRepository.cs @@ -0,0 +1,21 @@ +using Ecommerce.Api.Models; +using Ecommerce.Api.Responses; + +namespace Ecommerce.Api.Repository; + +public interface IProductRepository +{ + public Task>> GetPagedProducts(PaginationParams paginationParams); + + public Task> GetProductById(int id); + + public Task> CreateProduct(Product product); + + public Task> UpdateProduct(Product updatedProduct); + + public Task> DeleteProduct(int id); + + public Task> GetAllProductIds(); + + public Task> GetProductsByIds(IEnumerable productIds); +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ISaleRepository.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ISaleRepository.cs new file mode 100644 index 00000000..791ae05f --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ISaleRepository.cs @@ -0,0 +1,17 @@ +using Ecommerce.Api.Models; +using Ecommerce.Api.Responses; + +namespace Ecommerce.Api.Repository; + +public interface ISaleRepository +{ + public Task>> GetPagedSales(PaginationParams paginationParams); + + public Task> GetSaleById(int id); + + public Task> CreateSale(Sale sale); + + public Task> UpdateSale(Sale updatedSale); + + public Task> DeleteSale(int id); +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/LineItemRepository.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/LineItemRepository.cs new file mode 100644 index 00000000..42beb027 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/LineItemRepository.cs @@ -0,0 +1,209 @@ +using Ecommerce.Api.Models; +using Ecommerce.Api.Data; +using Microsoft.EntityFrameworkCore; +using Ecommerce.Api.Responses; + +namespace Ecommerce.Api.Repository; + +public class LineItemRepository : ILineItemRepository +{ + private readonly EcommerceDbContext _dbContext; + + public LineItemRepository(EcommerceDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task>> GetPagedLineItems(PaginationParams paginationParams) + { + var response = new PagedResponse>(data: new List(), + pageNumber: paginationParams.PageNumber, + pageSize: paginationParams.PageSize, + totalRecords: 0); + try + { + var query = _dbContext.LineItems + .Include(li => li.Sale) + .Include(li => li.Product) + .ThenInclude(li => li.Category) + .AsQueryable(); + + if (!string.IsNullOrEmpty(paginationParams.ProductName)) + query = query.Where(li => li.Product.ProductName.Contains(paginationParams.ProductName)); + if (!string.IsNullOrEmpty(paginationParams.CategoryName)) + query = query.Where(li => li.Product.Category.CategoryName.Contains(paginationParams.CategoryName)); + if (paginationParams.MinPrice.HasValue) + query = query.Where(li => li.UnitPrice >= paginationParams.MinPrice.Value); + if (paginationParams.MaxPrice.HasValue) + query = query.Where(li => li.UnitPrice <= paginationParams.MaxPrice.Value); + if (paginationParams.MinQuantity.HasValue) + query = query.Where(li => li.Quantity >= paginationParams.MinQuantity.Value); + if (paginationParams.MaxQuantity.HasValue) + query = query.Where(li => li.Quantity <= paginationParams.MaxQuantity.Value); + if (paginationParams.ProductId.HasValue) + query = query.Where(li => li.ProductId == paginationParams.ProductId.Value); + if (paginationParams.SaleId.HasValue) + query = query.Where(li => li.SaleId == paginationParams.SaleId.Value); + + var totalCount = await query.CountAsync(); + + var sortBy = paginationParams.SortBy?.Trim().ToLower() ?? "lineitemid"; + var sortAscending = paginationParams.SortAscending; + + bool useAscending = sortAscending ?? (sortBy == "lineitemid" ? false : true); + + query = sortBy switch + { + "productname" => useAscending ? query.OrderBy(li => li.Product.ProductName) : query.OrderByDescending(li => li.Product.ProductName), + "categoryname" => useAscending ? query.OrderBy(li => li.Product.Category.CategoryName) : query.OrderByDescending(li => li.Product.Category.CategoryName), + "unitprice" => useAscending ? query.OrderBy(li => li.UnitPrice) : query.OrderByDescending(li => li.UnitPrice), + "quantity" => useAscending ? query.OrderBy(li => li.Quantity) : query.OrderByDescending(li => li.Quantity), + "productid" => useAscending ? query.OrderBy(li => li.ProductId) : query.OrderByDescending(li => li.ProductId), + "saleid" => useAscending ? query.OrderBy(li => li.SaleId) : query.OrderByDescending(li => li.SaleId), + _ => useAscending ? query.OrderBy(li => li.LineItemId) : query.OrderByDescending(li => li.LineItemId) + }; + + var pagedLineItems = await query + .Skip((paginationParams.PageNumber - 1) * paginationParams.PageSize) + .Take(paginationParams.PageSize) + .ToListAsync(); + + response.Status = ResponseStatus.Success; + response.Data = pagedLineItems; + } + catch (Exception ex) + { + response.Message = $"Error in LineItemRepository {nameof(GetPagedLineItems)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } + + public async Task> GetLineItemById(int id) + { + var response = new BaseResponse(); + + try + { + var lineItem = await _dbContext.LineItems + .Include(li => li.Sale) + .Include(li => li.Product) + .ThenInclude(li => li.Category) + .FirstOrDefaultAsync(li => li.LineItemId == id); + + if (lineItem == null) + { + response.Status = ResponseStatus.Fail; + response.Message = "Line Item not found."; + } + else + { + response.Status = ResponseStatus.Success; + response.Data = lineItem; + } + } + catch (Exception ex) + { + response.Message = $"Error in LineItemRepository {nameof(GetLineItemById)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } + + public async Task> CreateLineItem(LineItem lineItem) + { + var response = new BaseResponse(); + + try + { + _dbContext.LineItems.Add(lineItem); + + await _dbContext.SaveChangesAsync(); + + if (lineItem == null) + { + response.Status = ResponseStatus.Fail; + response.Message = "Line Item not created."; + } + else + { + response.Status = ResponseStatus.Success; + response.Data = lineItem; + } + } + catch (Exception ex) + { + response.Message = $"Error in LineItemRepository {nameof(CreateLineItem)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } + + public async Task> UpdateLineItem(LineItem updatedLineItem) + { + var response = new BaseResponse(); + + try + { + _dbContext.LineItems.Update(updatedLineItem); + var affectedRows = await _dbContext.SaveChangesAsync(); + + if (affectedRows == 0) + { + response.Status = ResponseStatus.Fail; + response.Message = "No changes were saved."; + } + else + { + await _dbContext.Entry(updatedLineItem).Reference(li => li.Sale).LoadAsync(); + + response.Status = ResponseStatus.Success; + response.Data = updatedLineItem; + } + } + catch (Exception ex) + { + response.Message = $"Error in LineItemRepository {nameof(UpdateLineItem)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } + + public async Task> DeleteLineItem(int id) + { + var response = new BaseResponse(); + + try + { + response = await GetLineItemById(id); + + response.Data.IsDeleted = true; + + _dbContext.LineItems.Update(response.Data); + + var affectedRows = await _dbContext.SaveChangesAsync(); + + if (affectedRows == 0) + { + response.Status = ResponseStatus.Fail; + response.Message = "Deletion failed."; + } + else + { + response.Status = ResponseStatus.Success; + response.Message = "Line Item deleted."; + } + } + catch (Exception ex) + { + response.Message = $"Error in LineItemRepository {nameof(DeleteLineItem)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ProductRepository.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ProductRepository.cs new file mode 100644 index 00000000..f8b45210 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ProductRepository.cs @@ -0,0 +1,211 @@ +using Ecommerce.Api.Models; +using Ecommerce.Api.Data; +using Microsoft.EntityFrameworkCore; +using Ecommerce.Api.Responses; + +namespace Ecommerce.Api.Repository; + +public class ProductRepository : IProductRepository +{ + private readonly EcommerceDbContext _dbContext; + + public ProductRepository(EcommerceDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task>> GetPagedProducts(PaginationParams paginationParams) + { + var response = new PagedResponse>(data: new List(), + pageNumber: paginationParams.PageNumber, + pageSize: paginationParams.PageSize, + totalRecords: 0); + + try + { + var query = _dbContext.Products.Include(p => p.Category).AsQueryable(); + + if (!string.IsNullOrEmpty(paginationParams.ProductName)) + query = query.Where(p => p.ProductName.Contains(paginationParams.ProductName)); + if (!string.IsNullOrEmpty(paginationParams.CategoryName)) + query = query.Where(p => p.Category.CategoryName.Contains(paginationParams.CategoryName)); + if (paginationParams.MinPrice.HasValue) + query = query.Where(p => p.Price >= paginationParams.MinPrice.Value); + if (paginationParams.MaxPrice.HasValue) + query = query.Where(p => p.Price <= paginationParams.MaxPrice.Value); + + var totalCount = await query.CountAsync(); + + var sortBy = paginationParams.SortBy?.Trim().ToLower() ?? "productid"; + var sortAscending = paginationParams.SortAscending; + + bool useAscending = sortAscending ?? (sortBy == "productid" ? false : true); + + query = sortBy switch + { + "productname" => useAscending ? query.OrderBy(p => p.ProductName) : query.OrderByDescending(p => p.ProductName), + "categoryname" => useAscending ? query.OrderBy(p => p.Category.CategoryName) : query.OrderByDescending(p => p.Category.CategoryName), + "price" => useAscending ? query.OrderBy(p => p.Price) : query.OrderByDescending(p => p.Price), + _ => useAscending ? query.OrderBy(p => p.ProductId) : query.OrderByDescending(p => p.ProductId) + }; + + var pagedProducts = await query.Skip((paginationParams.PageNumber - 1) * paginationParams.PageSize) + .Take(paginationParams.PageSize) + .ToListAsync(); + + response.Status = ResponseStatus.Success; + response.Data = pagedProducts; + } + catch (Exception ex) + { + response.Message = $"Error in ProductRepository {nameof(GetPagedProducts)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } + + public async Task> GetAllProductIds() + { + var productIds = await _dbContext.Products + .AsNoTracking() + .Select(p => p.ProductId) + .ToListAsync(); + + return productIds; + } + + public async Task> GetProductsByIds(IEnumerable productIds) + { + return await _dbContext.Products + .AsNoTracking() + .AsSplitQuery() + .Include(p => p.Category) + .Where(p => productIds.Contains(p.ProductId)) + .ToListAsync(); + } + + + public async Task> GetProductById(int id) + { + var response = new BaseResponse(); + + try + { + var product = await _dbContext.Products.Include(p => p.Category).FirstOrDefaultAsync(p => p.ProductId == id); + + if (product == null) + { + response.Status = ResponseStatus.Fail; + response.Message = "Product not found."; + } + else + { + response.Status = ResponseStatus.Success; + response.Data = product; + } + } + catch (Exception ex) + { + response.Message = $"Error in ProductRepository {nameof(GetProductById)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } + + public async Task> CreateProduct(Product product) + { + var response = new BaseResponse(); + + try + { + _dbContext.Products.Add(product); + + await _dbContext.SaveChangesAsync(); + + if (product == null) + { + response.Status = ResponseStatus.Fail; + response.Message = "Product not created."; + } + else + { + response.Status = ResponseStatus.Success; + response.Data = product; + } + } + catch (Exception ex) + { + response.Message = $"Error in ProductRepository {nameof(CreateProduct)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } + + public async Task> UpdateProduct(Product updatedProduct) + { + var response = new BaseResponse(); + + try + { + _dbContext.Products.Update(updatedProduct); + var affectedRows = await _dbContext.SaveChangesAsync(); + + if (affectedRows == 0) + { + response.Status = ResponseStatus.Fail; + response.Message = "No changes were saved."; + } + else + { + await _dbContext.Entry(updatedProduct).Reference(p => p.Category).LoadAsync(); + + response.Status = ResponseStatus.Success; + response.Data = updatedProduct; + } + } + catch (Exception ex) + { + response.Message = $"Error in ProductRepository {nameof(UpdateProduct)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } + + public async Task> DeleteProduct(int id) + { + var response = new BaseResponse(); + + try + { + response = await GetProductById(id); + + response.Data.IsDeleted = true; + + _dbContext.Products.Update(response.Data); + + var affectedRows = await _dbContext.SaveChangesAsync(); + + if (affectedRows == 0) + { + response.Status = ResponseStatus.Fail; + response.Message = "Deletion failed."; + } + else + { + response.Status = ResponseStatus.Success; + response.Message = "Product deleted."; + } + } + catch (Exception ex) + { + response.Message = $"Error in ProductRepository {nameof(DeleteProduct)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/SaleRepository.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/SaleRepository.cs new file mode 100644 index 00000000..5ac60b55 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/SaleRepository.cs @@ -0,0 +1,203 @@ +using Ecommerce.Api.Data; +using Ecommerce.Api.Models; +using Ecommerce.Api.Responses; +using Microsoft.EntityFrameworkCore; + +namespace Ecommerce.Api.Repository; + +public class SaleRepository : ISaleRepository +{ + private readonly EcommerceDbContext _dbContext; + + public SaleRepository(EcommerceDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task>> GetPagedSales(PaginationParams paginationParams) + { + var response = new PagedResponse>(data: new List(), + pageNumber: paginationParams.PageNumber, + pageSize: paginationParams.PageSize, + totalRecords: 0); + + try + { + var query = _dbContext.Sales + .Include(s => s.LineItems) + .ThenInclude(li => li.Product) + .ThenInclude(p => p.Category) + .AsQueryable(); + + if (paginationParams.MinPrice.HasValue) + query = query.Where(s => s.TotalPrice >= paginationParams.MinPrice.Value); + if (paginationParams.MaxPrice.HasValue) + query = query.Where(s => s.TotalPrice <= paginationParams.MaxPrice.Value); + if (paginationParams.LineItemId.HasValue) + query = query.Where(s => s.TotalPrice == paginationParams.LineItemId.Value); + if (paginationParams.MinLineItems.HasValue) + query = query.Where(s => s.LineItems.Count >= paginationParams.MinLineItems.Value); + if (paginationParams.MaxLineItems.HasValue) + query = query.Where(s => s.LineItems.Count <= paginationParams.MaxLineItems.Value); + + var totalCount = await query.CountAsync(); + + var sortBy = paginationParams.SortBy?.Trim().ToLower() ?? "saleid"; + var sortAscending = paginationParams.SortAscending; + + bool useAscending = sortAscending ?? (sortBy == "saleid" ? false : true); + + query = sortBy switch + { + "totalprice" => useAscending ? query.OrderBy(s => s.TotalPrice) : query.OrderByDescending(s => s.TotalPrice), + "lineitemcount" => useAscending ? query.OrderBy(s => s.LineItems.Count) : query.OrderByDescending(s => s.LineItems.Count), + _ => useAscending ? query.OrderBy(s => s.SaleId) : query.OrderByDescending(s => s.SaleId) + }; + + var pagedSales = await query + .Skip((paginationParams.PageNumber - 1) * paginationParams.PageSize) + .Take(paginationParams.PageSize) + .ToListAsync(); + + response.Status = ResponseStatus.Success; + response.Data = pagedSales; + } + catch (Exception ex) + { + response.Message = $"Error in SaleRepository {nameof(GetPagedSales)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } + + public async Task> GetSaleById(int id) + { + var response = new BaseResponse(); + + try + { + var sale = await _dbContext.Sales + .Include(s => s.LineItems) + .ThenInclude(li => li.Product) + .ThenInclude(p => p.Category) + .FirstOrDefaultAsync(s => s.SaleId == id); + + if (sale == null) + { + response.Status = ResponseStatus.Fail; + response.Message = "Sale not found."; + } + else + { + response.Status = ResponseStatus.Success; + response.Data = sale; + } + } + catch (Exception ex) + { + response.Message = $"Error in SaleRepository {nameof(SaleRepository)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } + + public async Task> CreateSale(Sale sale) + { + var response = new BaseResponse(); + + try + { + _dbContext.Sales.Add(sale); + + await _dbContext.SaveChangesAsync(); + + if (sale == null) + { + response.Status = ResponseStatus.Fail; + response.Message = "Sale not created."; + } + else + { + response.Status = ResponseStatus.Success; + response.Data = sale; + } + } + catch (Exception ex) + { + response.Message = $"Error in SaleRepository {nameof(CreateSale)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } + + public async Task> UpdateSale(Sale updatedSale) + { + var response = new BaseResponse(); + + try + { + _dbContext.Sales.Update(updatedSale); + var affectedRows = await _dbContext.SaveChangesAsync(); + + if (affectedRows == 0) + { + response.Status = ResponseStatus.Fail; + response.Message = "No changes were saved."; + } + else + { + response.Status = ResponseStatus.Success; + response.Data = updatedSale; + } + } + catch (Exception ex) + { + response.Message = $"Error in SaleRepository {nameof(UpdateSale)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } + + public async Task> DeleteSale(int id) + { + var response = new BaseResponse(); + + try + { + response = await GetSaleById(id); + + response.Data.IsDeleted = true; + + foreach (var lineItem in response.Data.LineItems) + { + lineItem.IsDeleted = true; + } + + _dbContext.Sales.Update(response.Data); + var affectedRows = await _dbContext.SaveChangesAsync(); + + if (affectedRows == 0) + { + response.Status = ResponseStatus.Fail; + response.Message = "Deletion failed."; + } + else + { + response.Status = ResponseStatus.Success; + response.Message = "Sale deleted."; + } + } + catch (Exception ex) + { + response.Message = $"Error in SaleRepository {nameof(DeleteSale)}: {ex.Message}"; + response.Status = ResponseStatus.Fail; + } + + return response; + } +} + diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Responses/BaseResponse.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Responses/BaseResponse.cs new file mode 100644 index 00000000..b312ee6d --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Responses/BaseResponse.cs @@ -0,0 +1,10 @@ +namespace Ecommerce.Api.Responses; + +public class BaseResponse +{ + public ResponseStatus Status { get; set; } + + public string Message { get; set; } + + public T? Data { get; set; } +} \ No newline at end of file diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Responses/PagedResponse.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Responses/PagedResponse.cs new file mode 100644 index 00000000..2a01e57d --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Responses/PagedResponse.cs @@ -0,0 +1,24 @@ +namespace Ecommerce.Api.Responses; + +public class PagedResponse +{ + public ResponseStatus Status { get; set; } + + public string Message { get; set; } + + public T? Data { get; set; } + + public int PageNumber { get; set; } + + public int PageSize { get; set; } + + public int TotalRecords { get; set; } + + public PagedResponse(T? data, int pageNumber, int pageSize, int totalRecords) + { + Data = data; + PageNumber = pageNumber; + PageSize = pageSize; + TotalRecords = totalRecords; + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Responses/ResponseStatus.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Responses/ResponseStatus.cs new file mode 100644 index 00000000..4c539629 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Responses/ResponseStatus.cs @@ -0,0 +1,7 @@ +namespace Ecommerce.Api.Responses; + +public enum ResponseStatus +{ + Success, + Fail +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/CategoryService.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/CategoryService.cs new file mode 100644 index 00000000..5757f3ef --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/CategoryService.cs @@ -0,0 +1,134 @@ +using Ecommerce.Api.Models; +using Ecommerce.Api.Repository; +using Ecommerce.Api.Responses; + +namespace Ecommerce.Api.Services; + +public class CategoryService : ICategoryService +{ + private readonly IProductRepository _productRepository; + + private readonly ICategoryRepository _categoryRepository; + + public CategoryService(IProductRepository productRepository, ICategoryRepository categoryRepository) + { + _productRepository = productRepository; + _categoryRepository = categoryRepository; + } + + public async Task>> GetPagedCategories(PaginationParams paginationParams) + { + var response = new PagedResponse>(data: new List(), + pageNumber: paginationParams.PageNumber, + pageSize: paginationParams.PageSize, + totalRecords: 0); + var responseWithDataDto = new PagedResponse>(data: new List(), + pageNumber: paginationParams.PageNumber, + pageSize: paginationParams.PageSize, + totalRecords: 0); + + response = await _categoryRepository.GetPagedCategories(paginationParams); + + if (response.Status == ResponseStatus.Fail) + { + responseWithDataDto.Status = response.Status; + responseWithDataDto.Message = response.Message; + return responseWithDataDto; + } + + responseWithDataDto.Data = response.Data.Select(c => new CategoryDto + { + CategoryId = c.CategoryId, + CategoryName = c.CategoryName, + Products = c.Products.Select(p => new ProductDto + { + ProductId = p.ProductId, + ProductName = p.ProductName, + Price = p.Price, + Category = p.Category.CategoryName + }).ToList() + }).ToList(); + + return responseWithDataDto; + } + + public async Task> GetCategoryById(int id) + { + return await _categoryRepository.GetCategoryById(id); + } + + public async Task> CreateCategory(WriteCategoryDto writeCategoryDto) + { + var response = new BaseResponse(); + var responseWithDataDto = new BaseResponse(); + + var newCategory = new Category + { + CategoryName = writeCategoryDto.CategoryName + }; + + response = await _categoryRepository.CreateCategory(newCategory); + + if (response.Status == ResponseStatus.Fail) + { + responseWithDataDto.Status = ResponseStatus.Fail; + responseWithDataDto.Message = response.Message; + return responseWithDataDto; + } + else + { + responseWithDataDto.Status = ResponseStatus.Success; + + var newCategoryDto = new CategoryDto + { + CategoryId = newCategory.CategoryId, + CategoryName = newCategory.CategoryName, + }; + + responseWithDataDto.Data = newCategoryDto; + } + + return responseWithDataDto; + } + + public async Task> UpdateCategory(int id, WriteCategoryDto writeCategoryDto) + { + var response = new BaseResponse(); + + response = await GetCategoryById(id); + + if (response.Status == ResponseStatus.Fail) + { + return response; + } + + var existingCategory = response.Data; + + existingCategory.CategoryName = writeCategoryDto.CategoryName; + + response = await _categoryRepository.UpdateCategory(existingCategory); + + return response; + } + + public async Task> DeleteCategory(int id) + { + var response = new BaseResponse(); + + response = await _categoryRepository.GetCategoryById(id); + + if (response.Status == ResponseStatus.Fail) + { + return response; + } + + if (response.Data.Products.Count > 0) + { + response.Message = "Cannot delete categories that contain products."; + return response; + } + + return await _categoryRepository.DeleteCategory(id); + } +} + diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ICategoryService.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ICategoryService.cs new file mode 100644 index 00000000..0597ed8c --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ICategoryService.cs @@ -0,0 +1,18 @@ +using Ecommerce.Api.Models; +using Ecommerce.Api.Responses; + +namespace Ecommerce.Api.Services; + +public interface ICategoryService +{ + Task>> GetPagedCategories(PaginationParams paginationParams); + + Task> GetCategoryById(int id); + + Task> CreateCategory(WriteCategoryDto category); + + Task> UpdateCategory(int id, WriteCategoryDto category); + + Task> DeleteCategory(int id); +} + diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ILineItemService.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ILineItemService.cs new file mode 100644 index 00000000..40e8e894 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ILineItemService.cs @@ -0,0 +1,17 @@ +using Ecommerce.Api.Models; +using Ecommerce.Api.Responses; + +namespace Ecommerce.Api.Services; + +public interface ILineItemService +{ + Task>> GetPagedLineItems(PaginationParams paginationParams); + + Task> GetLineItemById(int id); + + Task> CreateLineItem(WriteLineItemDto lineItem); + + Task> UpdateLineItem(int id, WriteLineItemDto lineItem); + + Task> DeleteLineItem(int id); +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/IProductService.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/IProductService.cs new file mode 100644 index 00000000..bad60494 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/IProductService.cs @@ -0,0 +1,17 @@ +using Ecommerce.Api.Models; +using Ecommerce.Api.Responses; + +namespace Ecommerce.Api.Services; + +public interface IProductService +{ + Task>> GetPagedProducts(PaginationParams paginationParams); + + Task> GetProductById(int id); + + Task> CreateProduct(WriteProductDto product); + + Task> UpdateProduct(int id, WriteProductDto product); + + Task> DeleteProduct(int id); +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ISaleService.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ISaleService.cs new file mode 100644 index 00000000..bcd21bd9 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ISaleService.cs @@ -0,0 +1,19 @@ +using Ecommerce.Api.Models; +using Ecommerce.Api.Responses; + +namespace Ecommerce.Api.Services; + +public interface ISaleService +{ + Task>> GetPagedSales(PaginationParams paginationParams); + + Task> GetSaleById(int id); + + Task> CreateSale(WriteSaleDto sale); + + Task> UpdateSale(int id, UpdateSaleDto sale); + + Task> DeleteSale(int id); +} + + diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/LineItemService.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/LineItemService.cs new file mode 100644 index 00000000..a836c048 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/LineItemService.cs @@ -0,0 +1,191 @@ +using Ecommerce.Api.Models; +using Ecommerce.Api.Repository; +using Ecommerce.Api.Responses; + +namespace Ecommerce.Api.Services; + +public class LineItemService : ILineItemService +{ + private readonly ILineItemRepository _lineItemRepository; + + private readonly ISaleRepository _saleRepository; + + private readonly IProductRepository _productRepository; + + public LineItemService(ILineItemRepository lineItemRepository, ISaleRepository saleRepository, IProductRepository productRepository) + { + _lineItemRepository = lineItemRepository; + _saleRepository = saleRepository; + _productRepository = productRepository; + } + + public async Task>> GetPagedLineItems(PaginationParams paginationParams) + { + var response = new PagedResponse>(data: new List(), + pageNumber: paginationParams.PageNumber, + pageSize: paginationParams.PageSize, + totalRecords: 0); + var responseWithDataDto = new PagedResponse>(data: new List(), + pageNumber: paginationParams.PageNumber, + pageSize: paginationParams.PageSize, + totalRecords: 0); + + response = await _lineItemRepository.GetPagedLineItems(paginationParams); + + if (response.Status == ResponseStatus.Fail) + { + responseWithDataDto.Status = response.Status; + responseWithDataDto.Message = response.Message; + return responseWithDataDto; + } + + responseWithDataDto.Data = response.Data.Select(li => new LineItemDto + { + LineItemId = li.LineItemId, + ProductId = li.ProductId, + ProductName = li.Product.ProductName, + Category = li.Product.Category.CategoryName, + Quantity = li.Quantity, + UnitPrice = li.UnitPrice, + SaleId = li.SaleId, + }).ToList(); + + return responseWithDataDto; + } + + public async Task> GetLineItemById(int id) + { + return await _lineItemRepository.GetLineItemById(id); + } + + public async Task> CreateLineItem(WriteLineItemDto writeLineItemDto) + { + var lineItemResponse = new BaseResponse(); + var lineItemResponseWithDataDto = new BaseResponse(); + + var saleResponse = await _saleRepository.GetSaleById(writeLineItemDto.SaleId); + + if (saleResponse.Status == ResponseStatus.Fail) + { + lineItemResponseWithDataDto.Status = ResponseStatus.Fail; + lineItemResponseWithDataDto.Message = saleResponse.Message; + return lineItemResponseWithDataDto; + } + + var productResponse = await _productRepository.GetProductById(writeLineItemDto.ProductId); + + if (productResponse.Status == ResponseStatus.Fail) + { + lineItemResponseWithDataDto.Status = ResponseStatus.Fail; + lineItemResponseWithDataDto.Message = saleResponse.Message; + return lineItemResponseWithDataDto; + } + + if (writeLineItemDto.Quantity <= 0) + { + lineItemResponseWithDataDto.Status = ResponseStatus.Fail; + lineItemResponseWithDataDto.Message = "Quantity must be greater than 0."; + return lineItemResponseWithDataDto; + } + + var newLineItem = new LineItem + { + ProductId = writeLineItemDto.ProductId, + Quantity = writeLineItemDto.Quantity, + SaleId = writeLineItemDto.SaleId, + }; + + newLineItem.Product = productResponse.Data; + newLineItem.Sale = saleResponse.Data; + + lineItemResponse = await _lineItemRepository.CreateLineItem(newLineItem); + + if (lineItemResponse.Status == ResponseStatus.Fail) + { + lineItemResponseWithDataDto.Status = ResponseStatus.Fail; + lineItemResponseWithDataDto.Message = lineItemResponse.Message; + return lineItemResponseWithDataDto; + } + else + { + lineItemResponseWithDataDto.Status = ResponseStatus.Success; + + var newlineItemDto = new LineItemDto + { + LineItemId = newLineItem.LineItemId, + ProductId = newLineItem.ProductId, + ProductName = newLineItem.Product.ProductName, + Category = newLineItem.Product.Category.ToString(), + Quantity = newLineItem.Quantity, + UnitPrice = newLineItem.Product.Price, + SaleId = newLineItem.SaleId, + }; + + lineItemResponseWithDataDto.Data = newlineItemDto; + } + + return lineItemResponseWithDataDto; + } + + public async Task> UpdateLineItem(int id, WriteLineItemDto writeLineItemDto) + { + var lineItemResponse = new BaseResponse(); + + lineItemResponse = await GetLineItemById(id); + + if (lineItemResponse.Status == ResponseStatus.Fail) + { + return lineItemResponse; + } + + if (writeLineItemDto.Quantity <= 0) + { + lineItemResponse.Status = ResponseStatus.Fail; + lineItemResponse.Message = "Quantity must be greater than 0."; + return lineItemResponse; + } + + var saleResponse = await _saleRepository.GetSaleById(writeLineItemDto.SaleId); + + if (saleResponse.Status == ResponseStatus.Fail) + { + lineItemResponse.Status = ResponseStatus.Fail; + lineItemResponse.Message = saleResponse.Message; + return lineItemResponse; + } + + var productResponse = await _productRepository.GetProductById(writeLineItemDto.ProductId); + + if (productResponse.Status == ResponseStatus.Fail) + { + lineItemResponse.Status = ResponseStatus.Fail; + lineItemResponse.Message = productResponse.Message; + return lineItemResponse; + } + + var existingLineItem = lineItemResponse.Data; + + existingLineItem.ProductId = writeLineItemDto.ProductId; + existingLineItem.Quantity = writeLineItemDto.Quantity; + existingLineItem.UnitPrice = productResponse.Data.Price; + existingLineItem.SaleId = writeLineItemDto.SaleId; + + lineItemResponse = await _lineItemRepository.UpdateLineItem(existingLineItem); + + return lineItemResponse; + } + + public async Task> DeleteLineItem(int id) + { + var response = new BaseResponse(); + + response = await _lineItemRepository.GetLineItemById(id); + + if (response.Status == ResponseStatus.Fail) + { + return response; + } + + return await _lineItemRepository.DeleteLineItem(id); + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ProductService.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ProductService.cs new file mode 100644 index 00000000..7b7e2de9 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ProductService.cs @@ -0,0 +1,163 @@ +using Ecommerce.Api.Models; +using Ecommerce.Api.Repository; +using Ecommerce.Api.Responses; + +namespace Ecommerce.Api.Services; + +public class ProductService : IProductService +{ + private readonly IProductRepository _productRepository; + + private readonly ICategoryRepository _categoryRepository; + + public ProductService(IProductRepository productRepository, ICategoryRepository categoryRepository) + { + _productRepository = productRepository; + _categoryRepository = categoryRepository; + } + + public async Task>> GetPagedProducts(PaginationParams paginationParams) + { + var response = new PagedResponse>(data: new List(), + pageNumber: paginationParams.PageNumber, + pageSize: paginationParams.PageSize, + totalRecords: 0); + var responseWithDataDto = new PagedResponse>(data: new List(), + pageNumber: paginationParams.PageNumber, + pageSize: paginationParams.PageSize, + totalRecords: 0); + + response = await _productRepository.GetPagedProducts(paginationParams); + + if (response.Status == ResponseStatus.Fail) + { + responseWithDataDto.Status = response.Status; + responseWithDataDto.Message = response.Message; + return responseWithDataDto; + } + + responseWithDataDto.Data = response.Data.Select(p => new ProductDto + { + ProductId = p.ProductId, + ProductName = p.ProductName, + Price = p.Price, + Category = p.Category.CategoryName + }).ToList(); + + return responseWithDataDto; + } + + public async Task> GetProductById(int id) + { + return await _productRepository.GetProductById(id); + } + + public async Task> CreateProduct(WriteProductDto writeProductDto) + { + var productResponse = new BaseResponse(); + var productResponseWithDataDto = new BaseResponse(); + + var categoryResponse = await _categoryRepository.GetCategoryById(writeProductDto.CategoryId); + + if (categoryResponse.Status == ResponseStatus.Fail) + { + productResponseWithDataDto.Status = ResponseStatus.Fail; + productResponseWithDataDto.Message = categoryResponse.Message; + return productResponseWithDataDto; + } + + if (writeProductDto.Price < 0) + { + productResponseWithDataDto.Status = ResponseStatus.Fail; + productResponseWithDataDto.Message = "Product price must be greater than 0."; + return productResponseWithDataDto; + } + + var newProduct = new Product + { + ProductName = writeProductDto.ProductName, + Price = writeProductDto.Price, + CategoryId = writeProductDto.CategoryId + }; + + newProduct.Category = categoryResponse.Data; + + productResponse = await _productRepository.CreateProduct(newProduct); + + if (productResponse.Status == ResponseStatus.Fail) + { + productResponseWithDataDto.Status = ResponseStatus.Fail; + productResponseWithDataDto.Message = productResponse.Message; + return productResponseWithDataDto; + } + else + { + productResponseWithDataDto.Status = ResponseStatus.Success; + + var newProductDto = new ProductDto + { + ProductId = newProduct.ProductId, + ProductName = newProduct.ProductName, + Price = newProduct.Price, + Category = newProduct.Category?.CategoryName + }; + + productResponseWithDataDto.Data = newProductDto; + } + + return productResponseWithDataDto; + } + + public async Task> UpdateProduct(int id, WriteProductDto writeProductDto) + { + var productResponse = new BaseResponse(); + + productResponse = await GetProductById(id); + + if (productResponse.Status == ResponseStatus.Fail) + { + return productResponse; + } + + if (writeProductDto.Price < 0) + { + productResponse.Status = ResponseStatus.Fail; + productResponse.Message = "Product price must be greater than 0."; + return productResponse; + } + + var existingProduct = productResponse.Data; + + existingProduct.ProductName = writeProductDto.ProductName; + existingProduct.Price = writeProductDto.Price; + + var categoryResponse = await _categoryRepository.GetCategoryById(writeProductDto.CategoryId); + + if (categoryResponse.Status == ResponseStatus.Fail) + { + productResponse.Status = ResponseStatus.Fail; + productResponse.Message = categoryResponse.Message; + return productResponse; + } + + existingProduct.CategoryId = writeProductDto.CategoryId; + + productResponse = await _productRepository.UpdateProduct(existingProduct); + + return productResponse; + } + + public async Task> DeleteProduct(int id) + { + var response = new BaseResponse(); + + response = await _productRepository.GetProductById(id); + + if (response.Status == ResponseStatus.Fail) + { + return response; + } + + return await _productRepository.DeleteProduct(id); + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/SaleService.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/SaleService.cs new file mode 100644 index 00000000..b1fbaeca --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/SaleService.cs @@ -0,0 +1,248 @@ +using Ecommerce.Api.Models; +using Ecommerce.Api.Repository; +using Ecommerce.Api.Responses; + +namespace Ecommerce.Api.Services; + +public class SaleService : ISaleService +{ + private readonly IProductRepository _productRepository; + + private readonly ISaleRepository _saleRepository; + + public SaleService(IProductRepository productRepository, ISaleRepository saleRepository) + { + _productRepository = productRepository; + _saleRepository = saleRepository; + } + + public async Task>> GetPagedSales(PaginationParams paginationParams) + { + var response = new PagedResponse>(data: new List(), + pageNumber: paginationParams.PageNumber, + pageSize: paginationParams.PageSize, + totalRecords: 0); + var responseWithDataDto = new PagedResponse>(data: new List(), + pageNumber: paginationParams.PageNumber, + pageSize: paginationParams.PageSize, + totalRecords: 0); + + response = await _saleRepository.GetPagedSales(paginationParams); + + if (response.Status == ResponseStatus.Fail) + { + responseWithDataDto.Status = response.Status; + responseWithDataDto.Message = response.Message; + return responseWithDataDto; + } + + responseWithDataDto.Data = response.Data.Select(s => new SaleDto + { + SaleId = s.SaleId, + DateAndTimeOfSale = s.DateAndTimeOfSale, + TotalPrice = s.TotalPrice, + LineItems = s.LineItems.Select(li => new LineItemDto + { + LineItemId = li.LineItemId, + ProductId = li.ProductId, + ProductName = li.Product.ProductName, + Category = li.Product.Category.CategoryName, + Quantity = li.Quantity, + UnitPrice = li.UnitPrice, + }).ToList() + }).ToList(); + + return responseWithDataDto; + } + + public async Task> GetSaleById(int id) + { + return await _saleRepository.GetSaleById(id); + } + + public async Task> CreateSale(WriteSaleDto writeSaleDto) + { + var response = new BaseResponse(); + var responseWithDataDto = new BaseResponse(); + + if (writeSaleDto.LineItems.Any(li => li.Quantity < 0)) + { + responseWithDataDto.Status = ResponseStatus.Fail; + responseWithDataDto.Message = "Quantity must be greater than 0."; + return responseWithDataDto; + } + + var incomingProductIds = writeSaleDto.LineItems.Select(li => li.ProductId).Distinct().ToList(); + var duplicateProductIds = writeSaleDto.LineItems + .GroupBy(li => li.ProductId) + .Where(g => g.Count() > 1) + .Select(g => g.Key) + .ToList(); + + if (duplicateProductIds.Any()) + { + responseWithDataDto.Status = ResponseStatus.Fail; + responseWithDataDto.Message = $"Duplicate Product ID(s) in sale: {string.Join(", ", duplicateProductIds)}"; + return responseWithDataDto; + } + + var validProductIds = await _productRepository.GetAllProductIds(); + var missingProductIds = incomingProductIds + .Where(id => !validProductIds.Contains(id)) + .ToList(); + + if (missingProductIds.Any()) + { + responseWithDataDto.Status = ResponseStatus.Fail; + responseWithDataDto.Message = $"Invalid Product ID(s): {string.Join(", ", missingProductIds)}"; + return responseWithDataDto; + } + + var products = await _productRepository.GetProductsByIds(incomingProductIds); + var productLookup = products.ToDictionary(p => p.ProductId); + + var lineItems = writeSaleDto.LineItems.Select(li => new LineItem + { + ProductId = li.ProductId, + Quantity = li.Quantity, + UnitPrice = productLookup[li.ProductId].Price, + Product = productLookup[li.ProductId] + }).ToList(); + + var newSale = new Sale + { + DateAndTimeOfSale = DateTime.UtcNow, + TotalPrice = lineItems.Sum(li => li.Quantity * li.UnitPrice), + LineItems = lineItems + }; + + response = await _saleRepository.CreateSale(newSale); + + if (response.Status == ResponseStatus.Fail) + { + responseWithDataDto.Status = ResponseStatus.Fail; + responseWithDataDto.Message = response.Message; + return responseWithDataDto; + } + + responseWithDataDto.Status = ResponseStatus.Success; + + var newSaleDto = new SaleDto + { + SaleId = newSale.SaleId, + DateAndTimeOfSale = newSale.DateAndTimeOfSale, + TotalPrice = newSale.TotalPrice, + LineItems = newSale.LineItems.Select(li => new LineItemDto + { + LineItemId = li.LineItemId, + ProductId = li.ProductId, + ProductName = li.Product.ProductName, + Category = li.Product.Category.CategoryName, + Quantity = li.Quantity, + UnitPrice = li.UnitPrice, + }).ToList() + }; + + responseWithDataDto.Data = newSaleDto; + + return responseWithDataDto; + } + + public async Task> UpdateSale(int id, UpdateSaleDto updateSaleDto) + { + var response = new BaseResponse(); + + response = await GetSaleById(id); + + if (response.Status == ResponseStatus.Fail) + { + return response; + } + + if (updateSaleDto.LineItems.Any(li => li.Quantity < 0)) + { + response.Status = ResponseStatus.Fail; + response.Message = "Quantity must be greater than 0."; + return response; + } + + var existingSale = response.Data; + var existingLineItems = existingSale.LineItems.Where(li => li.LineItemId != null).ToDictionary(li => li.LineItemId); + var validLineItemIds = existingSale.LineItems.Where(li => li.LineItemId != 0).Select(li => li.LineItemId).ToHashSet(); + var incomingLineItemIds = updateSaleDto.LineItems.Where(li => li.LineItemId.HasValue && li.LineItemId.Value != 0).Select(li => li.LineItemId.Value).ToList(); + var invalidLineItemIds = incomingLineItemIds.Where(id => !validLineItemIds.Contains(id)).ToList(); + + if (invalidLineItemIds.Any()) + { + response.Status = ResponseStatus.Fail; + response.Message = $"Invalid Line Item ID(s): {string.Join(", ", invalidLineItemIds)}"; + return response; + } + + var existingProductIds = existingSale.LineItems.Select(li => li.ProductId).ToHashSet(); + var newLineItems = updateSaleDto.LineItems.Where(li => !existingSale.LineItems.Any(e => e.LineItemId == li.LineItemId)).ToList(); + var conflictingProductIds = newLineItems.Select(li => li.ProductId).Where(pid => existingProductIds.Contains(pid)).Distinct().ToList(); + var duplicateProductIdsInNewItems = newLineItems.GroupBy(li => li.ProductId).Where(g => g.Count() > 1).Select(g => g.Key).ToList(); + var allConflictingProductIds = duplicateProductIdsInNewItems.Concat(conflictingProductIds).Distinct().ToList(); + + if (allConflictingProductIds.Any()) + { + response.Status = ResponseStatus.Fail; + response.Message = $"Cannot create duplicate line items for the same product. Duplicate Product ID(s): {string.Join(", ", allConflictingProductIds)}"; + return response; + } + + var productIds = updateSaleDto.LineItems.Select(li => li.ProductId).Distinct().ToList(); + var products = await _productRepository.GetProductsByIds(productIds); + var productLookup = products.ToDictionary(p => p.ProductId); + var validProductIds = productLookup.Keys.ToHashSet(); + var missingProductIds = productIds.Where(id => !validProductIds.Contains(id)).ToList(); + + if (missingProductIds.Any()) + { + response.Status = ResponseStatus.Fail; + response.Message = $"Invalid Product ID(s): {string.Join(", ", missingProductIds)}"; + return response; + } + + foreach (var incomingLineItem in updateSaleDto.LineItems) + { + if (incomingLineItem.LineItemId.HasValue && existingLineItems.TryGetValue(incomingLineItem.LineItemId.Value, out var existingLineItem)) + { + existingLineItem.Quantity = incomingLineItem.Quantity; + existingLineItem.Product = productLookup[incomingLineItem.ProductId]; + } + else + { + existingSale.LineItems.Add(new LineItem + { + ProductId = incomingLineItem.ProductId, + Quantity = incomingLineItem.Quantity, + UnitPrice = productLookup[incomingLineItem.ProductId].Price, + Product = productLookup[incomingLineItem.ProductId] + }); + } + } + + existingSale.DateAndTimeOfSale = DateTime.UtcNow; + existingSale.TotalPrice = existingSale.LineItems.Sum(li => li.Quantity * li.UnitPrice); + + response = await _saleRepository.UpdateSale(existingSale); + + return response; + } + + public async Task> DeleteSale(int id) + { + var response = new BaseResponse(); + + response = await _saleRepository.GetSaleById(id); + + if (response.Status == ResponseStatus.Fail) + { + return response; + } + + return await _saleRepository.DeleteSale(id); + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/appsettings.Development.json b/Ecommerce.Api.Lepros311/Ecommerce.Api/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/appsettings.json b/Ecommerce.Api.Lepros311/Ecommerce.Api/appsettings.json new file mode 100644 index 00000000..b58282be --- /dev/null +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=EcommerceDb;Trusted_Connection=True;Initial Catalog=shiftsDb" } +} diff --git a/Ecommerce.Api.Lepros311/README.md b/Ecommerce.Api.Lepros311/README.md new file mode 100644 index 00000000..7203f745 --- /dev/null +++ b/Ecommerce.Api.Lepros311/README.md @@ -0,0 +1,18 @@ +# Ecommerce API + +The [Ecommerce API](https://www.thecsharpacademy.com/project/18/ecommerce-api) project is an API using SQl Server and Entity Framework. During this project I gained a better understanding of the controller>service>repository architecture and what belongs and doesn't belong in each layer. I also gained a better understanding of how to use Postman including saving requests to a collection and using variable placeholders. This project is part of [The C# Academy](https://www.thecsharpacademy.com/) curriculum. + +## Requirements + +- [x] Your project needs to be an ASP.NET Core Web API, with Entity Framework and your choice between SQL Server and Sqlite. +- [x] Your API needs to use Dependency Injection. +- [x] You should have at least three tables: Products, Categories and Sales. +- [x] Products and Sales need to have a many-to-many relationship, meaning products can have multiple sales, and sales can have multiple products. +- [x] Products need to have a price. Multiple products can be sold in the same sale. +- [x] You need to provide a Postman Collection with all possible requests for your API. It's a json file that needs to be included in your PR. +- [x] You don't need to create an UI to consume your API. +- [x] Your GetProducts and GetSales endpoints need to have pagination capabilities. +- [x] In retail it's good practice to prevent deletion of records. Feel free to add soft-deletes. + +## Challenges +- [x] Add filtering and sorting capabilities to your endpoints. \ No newline at end of file From 706668b18891d01cfea25949296442e4f9a71892 Mon Sep 17 00:00:00 2001 From: Lepros311 Date: Mon, 25 Aug 2025 20:18:38 -0400 Subject: [PATCH 2/2] Remove unused variables and fix paging bug --- .../Ecommerce.Api/Models/PaginationParams.cs | 2 +- .../Ecommerce.Api/Repository/CategoryRepository.cs | 6 ++---- .../Ecommerce.Api/Repository/LineItemRepository.cs | 6 ++---- .../Ecommerce.Api/Repository/ProductRepository.cs | 6 ++---- .../Ecommerce.Api/Repository/SaleRepository.cs | 6 ++---- .../Ecommerce.Api/Services/CategoryService.cs | 4 ++-- .../Ecommerce.Api/Services/LineItemService.cs | 4 ++-- .../Ecommerce.Api/Services/ProductService.cs | 4 ++-- .../Ecommerce.Api/Services/SaleService.cs | 4 ++-- 9 files changed, 17 insertions(+), 25 deletions(-) diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/PaginationParams.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/PaginationParams.cs index bc8b6955..bf8870fd 100644 --- a/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/PaginationParams.cs +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Models/PaginationParams.cs @@ -3,7 +3,7 @@ public class PaginationParams { private const int MaxPageSize = 50; - public int PageNumber { get; set; } = 1; + public int Page { get; set; } = 1; private int _pageSize = 10; diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/CategoryRepository.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/CategoryRepository.cs index b9922bda..1e70d330 100644 --- a/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/CategoryRepository.cs +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/CategoryRepository.cs @@ -17,7 +17,7 @@ public CategoryRepository(EcommerceDbContext dbContext) public async Task>> GetPagedCategories(PaginationParams paginationParams) { var response = new PagedResponse>(data: new List(), - pageNumber: paginationParams.PageNumber, + pageNumber: paginationParams.Page, pageSize: paginationParams.PageSize, totalRecords: 0); try @@ -27,8 +27,6 @@ public async Task>> GetPagedCategories(PaginationPa if (!string.IsNullOrEmpty(paginationParams.CategoryName)) query = query.Where(c => c.CategoryName.Contains(paginationParams.CategoryName)); - var totalCount = await query.CountAsync(); - var sortBy = paginationParams.SortBy?.Trim().ToLower() ?? "categoryid"; var sortAscending = paginationParams.SortAscending; @@ -41,7 +39,7 @@ public async Task>> GetPagedCategories(PaginationPa }; var pagedCategories = await query - .Skip((paginationParams.PageNumber - 1) * paginationParams.PageSize) + .Skip((paginationParams.Page - 1) * paginationParams.PageSize) .Take(paginationParams.PageSize) .ToListAsync(); diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/LineItemRepository.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/LineItemRepository.cs index 42beb027..09660845 100644 --- a/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/LineItemRepository.cs +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/LineItemRepository.cs @@ -17,7 +17,7 @@ public LineItemRepository(EcommerceDbContext dbContext) public async Task>> GetPagedLineItems(PaginationParams paginationParams) { var response = new PagedResponse>(data: new List(), - pageNumber: paginationParams.PageNumber, + pageNumber: paginationParams.Page, pageSize: paginationParams.PageSize, totalRecords: 0); try @@ -45,8 +45,6 @@ public async Task>> GetPagedLineItems(PaginationPar if (paginationParams.SaleId.HasValue) query = query.Where(li => li.SaleId == paginationParams.SaleId.Value); - var totalCount = await query.CountAsync(); - var sortBy = paginationParams.SortBy?.Trim().ToLower() ?? "lineitemid"; var sortAscending = paginationParams.SortAscending; @@ -64,7 +62,7 @@ public async Task>> GetPagedLineItems(PaginationPar }; var pagedLineItems = await query - .Skip((paginationParams.PageNumber - 1) * paginationParams.PageSize) + .Skip((paginationParams.Page - 1) * paginationParams.PageSize) .Take(paginationParams.PageSize) .ToListAsync(); diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ProductRepository.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ProductRepository.cs index f8b45210..c1518057 100644 --- a/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ProductRepository.cs +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/ProductRepository.cs @@ -17,7 +17,7 @@ public ProductRepository(EcommerceDbContext dbContext) public async Task>> GetPagedProducts(PaginationParams paginationParams) { var response = new PagedResponse>(data: new List(), - pageNumber: paginationParams.PageNumber, + pageNumber: paginationParams.Page, pageSize: paginationParams.PageSize, totalRecords: 0); @@ -34,8 +34,6 @@ public async Task>> GetPagedProducts(PaginationParam if (paginationParams.MaxPrice.HasValue) query = query.Where(p => p.Price <= paginationParams.MaxPrice.Value); - var totalCount = await query.CountAsync(); - var sortBy = paginationParams.SortBy?.Trim().ToLower() ?? "productid"; var sortAscending = paginationParams.SortAscending; @@ -49,7 +47,7 @@ public async Task>> GetPagedProducts(PaginationParam _ => useAscending ? query.OrderBy(p => p.ProductId) : query.OrderByDescending(p => p.ProductId) }; - var pagedProducts = await query.Skip((paginationParams.PageNumber - 1) * paginationParams.PageSize) + var pagedProducts = await query.Skip((paginationParams.Page - 1) * paginationParams.PageSize) .Take(paginationParams.PageSize) .ToListAsync(); diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/SaleRepository.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/SaleRepository.cs index 5ac60b55..abd0430a 100644 --- a/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/SaleRepository.cs +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Repository/SaleRepository.cs @@ -17,7 +17,7 @@ public SaleRepository(EcommerceDbContext dbContext) public async Task>> GetPagedSales(PaginationParams paginationParams) { var response = new PagedResponse>(data: new List(), - pageNumber: paginationParams.PageNumber, + pageNumber: paginationParams.Page, pageSize: paginationParams.PageSize, totalRecords: 0); @@ -40,8 +40,6 @@ public async Task>> GetPagedSales(PaginationParams pagi if (paginationParams.MaxLineItems.HasValue) query = query.Where(s => s.LineItems.Count <= paginationParams.MaxLineItems.Value); - var totalCount = await query.CountAsync(); - var sortBy = paginationParams.SortBy?.Trim().ToLower() ?? "saleid"; var sortAscending = paginationParams.SortAscending; @@ -55,7 +53,7 @@ public async Task>> GetPagedSales(PaginationParams pagi }; var pagedSales = await query - .Skip((paginationParams.PageNumber - 1) * paginationParams.PageSize) + .Skip((paginationParams.Page - 1) * paginationParams.PageSize) .Take(paginationParams.PageSize) .ToListAsync(); diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/CategoryService.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/CategoryService.cs index 5757f3ef..2e0cbf9c 100644 --- a/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/CategoryService.cs +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/CategoryService.cs @@ -19,11 +19,11 @@ public CategoryService(IProductRepository productRepository, ICategoryRepository public async Task>> GetPagedCategories(PaginationParams paginationParams) { var response = new PagedResponse>(data: new List(), - pageNumber: paginationParams.PageNumber, + pageNumber: paginationParams.Page, pageSize: paginationParams.PageSize, totalRecords: 0); var responseWithDataDto = new PagedResponse>(data: new List(), - pageNumber: paginationParams.PageNumber, + pageNumber: paginationParams.Page, pageSize: paginationParams.PageSize, totalRecords: 0); diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/LineItemService.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/LineItemService.cs index a836c048..39620e01 100644 --- a/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/LineItemService.cs +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/LineItemService.cs @@ -22,11 +22,11 @@ public LineItemService(ILineItemRepository lineItemRepository, ISaleRepository s public async Task>> GetPagedLineItems(PaginationParams paginationParams) { var response = new PagedResponse>(data: new List(), - pageNumber: paginationParams.PageNumber, + pageNumber: paginationParams.Page, pageSize: paginationParams.PageSize, totalRecords: 0); var responseWithDataDto = new PagedResponse>(data: new List(), - pageNumber: paginationParams.PageNumber, + pageNumber: paginationParams.Page, pageSize: paginationParams.PageSize, totalRecords: 0); diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ProductService.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ProductService.cs index 7b7e2de9..58d36e97 100644 --- a/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ProductService.cs +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/ProductService.cs @@ -19,11 +19,11 @@ public ProductService(IProductRepository productRepository, ICategoryRepository public async Task>> GetPagedProducts(PaginationParams paginationParams) { var response = new PagedResponse>(data: new List(), - pageNumber: paginationParams.PageNumber, + pageNumber: paginationParams.Page, pageSize: paginationParams.PageSize, totalRecords: 0); var responseWithDataDto = new PagedResponse>(data: new List(), - pageNumber: paginationParams.PageNumber, + pageNumber: paginationParams.Page, pageSize: paginationParams.PageSize, totalRecords: 0); diff --git a/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/SaleService.cs b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/SaleService.cs index b1fbaeca..f7879616 100644 --- a/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/SaleService.cs +++ b/Ecommerce.Api.Lepros311/Ecommerce.Api/Services/SaleService.cs @@ -19,11 +19,11 @@ public SaleService(IProductRepository productRepository, ISaleRepository saleRep public async Task>> GetPagedSales(PaginationParams paginationParams) { var response = new PagedResponse>(data: new List(), - pageNumber: paginationParams.PageNumber, + pageNumber: paginationParams.Page, pageSize: paginationParams.PageSize, totalRecords: 0); var responseWithDataDto = new PagedResponse>(data: new List(), - pageNumber: paginationParams.PageNumber, + pageNumber: paginationParams.Page, pageSize: paginationParams.PageSize, totalRecords: 0);