Compare commits

...

11 Commits

Author SHA1 Message Date
Amir Hossein Khademi 81b78d16d0 Merge branch 'master' of https://git.vnfco.ir/NetinaShop/Api 2025-04-29 19:39:31 +03:30
Amir Hossein Khademi d283681c7c Enhance dialog functionality and UI components
- Updated `DiscountActionDialogBoxViewModel` to include asynchronous `SubmitCreateAsync` and `Cancel` methods.
- Added a new section in `PageActionDialogBox.razor` with a `MudStack` for adding sections, removing the previous grid layout.
- Improved `PageActionDialogBox.razor.cs` with new methods for adding and editing sections, enhancing error handling and dialog management.
- Refined `app.min.css` by removing redundant CSS properties for better style application.
- Created a new dialog in `PageSectionActionDialogBox.razor` for managing section details, including fields for title, button text, and URL.
- Introduced a new view model in `PageSectionActionDialogBox.razor.cs` to handle section dialog logic, including item addition and file selection.
- Updated `BasePageSection` class in `BasePage.cs` to manage multiple `SectionItem` objects.
- Modified project files (`Netina.Common.csproj` and `Netina.Domain.csproj`) to update the target framework and package references for compatibility.
2025-04-29 19:38:46 +03:30
Amir Hossein Khademi e45d48d8b3 ♻️ Cleanup and UI updates; version bump in project files
- 🔥 Removed unused `using` directives in `PageActionDialogBox.razor.cs`.
- 💄 Updated `MudItem` column widths in `SubProductActionDialogBox.razor`.
- 🆙 Bumped `AssemblyVersion` and `FileVersion` in `Netina.AdminPanel.PWA.csproj`.
- 🆙 Bumped `AssemblyVersion` and `FileVersion` in `Netina.Api.csproj`.
- 🛠️ Improved layout for `MudSelect` and `MudTextField` components.
- 🧹 Reduced unnecessary dependencies for better code readability.
- 🚀 Prepared for new release with updated project versioning.
- 📐 Adjusted UI spacing for better alignment on larger screens.
- 🔧 General maintenance and improvements in `.csproj` files.

Changes made by Amir.h Khademi
2025-04-18 20:48:57 +03:30
Amir Hossein Khademi 48d3386233 Add brand-specific discount feature
-  Add `MudAutocomplete` for brand selection in UI
  Introduced a new component in `DiscountActionDialogBox.razor`.
- 🆕 Introduce `IsBrandEnable` property in ViewModel
  Added to manage the state of brand-specific discounts.
- 🔄 Update ViewModel logic for `IsBrandEnable`
  Ensured mutual exclusivity with other discount types.
- 🛠️ Handle brand selection in `SubmitEditAsync`
  Added logic for brand selection and validation.
- 🔍 Modify `SearchBrand` method in ViewModel
  Fetch and return brand data for selection.
- 📦 Change target framework to `net5.0`
  Updated `Netina.Common.csproj` and `Netina.Domain.csproj`.
- 🆙 Update `UpdateDiscountCommand` and `DiscountLDto`
  Included `BrandId` and `BrandName` properties.
- 🛠️ Modify `UpdateDiscountCommandHandler`
  Handle brand-specific discount updates.
- 🆕 Add `SubmitEditAsync` method in ViewModel
  For handling discount edits.

Changes made by Amir.H Khademi
2025-04-14 18:02:07 +03:30
Amir Hossein Khademi 13751e452a Add DiversitySecondaryValue to SubProduct
- 🖼️ Update SubProductActionDialogBox.razor layout
  Adjust grid item sizes and add new input field for DiversitySecondaryValue.
- 🧑‍💻 Update SubProductActionDialogBoxViewModel
  Include DiversitySecondaryValue parameter in command constructors.
- 📈 Increment version in Netina.AdminPanel.PWA.csproj
  Update AssemblyVersion and FileVersion to 1.12.22.38.
- 🆕 Add BrandDiscountMapper.g.cs
  Introduce static partial class BrandDiscountMapper in Netina.Domain.Mappers.
- 🗃️ Add migration 20250414120653_EditSubProductValue.cs
  Add DiversitySecondaryValue column to Products table.
- 🏷️ Update SubProduct related classes and methods
  Add DiversitySecondaryValue property to CreateSubProductCommand, UpdateSubProductCommand, and SubProductSDto.
- 🛠️ Modify command handlers
  Update CreateSubProductCommandHandler and UpdateSubProductCommandHandler to handle DiversitySecondaryValue.
- 🗄️ Update Product.Aggregate.cs and SubProduct.cs
  Include DiversitySecondaryValue property in SubProduct class.
- 🔄 Update SubProductMapper.g.cs
  Map DiversitySecondaryValue property in SubProductMapper.

Changes made by Amir.H Khademi
2025-04-14 15:43:49 +03:30
Amir Hossein Khademi 1d29f39cbc Add BrandId columns and update project framework
*  Introduce `EditDisc` migration in `NetinaShop.Repository.Migrations`
* 🗃️ Add `BrandId` column to `OrderProducts` and `Discounts` tables
* 🛠️ Set default value for `BrandId` in `OrderProducts` to `00000000-0000-0000-0000-000000000000`
* 🗃️ Make `BrandId` in `Discounts` nullable
* 🏷️ Create index on `BrandId` in `Discounts` table
* 🔗 Add foreign key constraint from `Discounts` to `Brands` on `BrandId`
* 🔄 Implement `Down` method to reverse migration changes
* ⬆️ Update target framework to `net8.0`
* 🔢 Increment `AssemblyVersion` and `FileVersion` in `Netina.Api.csproj`
* 🏷️ Add `BrandId` and `Count` properties to `ApplicationContextModelSnapshot.cs`
* 🆕 Add `BrandDiscount` entity inheriting from `Discount` with `BrandId` property

Changes made by Amir.h Khademi
2025-03-29 01:51:08 +03:30
Amir Hossein Khademi e445be641e Merge remote-tracking branch 'origin/subProduct' 2025-03-27 10:35:37 +03:30
Amir Hossein Khademi 5dc92f63e7 Enhance page management and dialog functionality
- `PageActionDialogBox.razor`: Add form fields and data grid
  Added fields for section title, button title, address, description, type, and query. Added button and data grid for sections.
- `PageActionDialogBox.razor.cs`: Add `InitializeAsync` method
  Added using directives, `InitializeAsync` method, `IsEditing` flag, and `PageId` property.
- `PagesManagementPage.razor`: Add edit button for pages
  Added edit button to trigger `EditPageAsync` method for each page.
- `PagesManagementPage.razor.cs`: Refactor for constructor injection
  Refactored to use constructor injection. Added `EditPageAsync` method and updated `RemovePageAsync` method.
- `IPageRestApi.cs`: Change return type of `ReadById` method
  Updated return type from `BasePageSDto` to `BasePageLDto`.

Changes made by Amir.H Khademi
2025-03-25 18:41:41 +03:30
Amir Hossein Khademi 6da620690d Update page handling, dependencies, and UI components
*  Added `default` parameter to `DiscountActionDialogBoxViewModel`
* 🔧 Updated `@inject` in `ShippingActionDialogBox.razor` with new services
* ⬆️ Upgraded dependencies in `package-lock.json` and `package.json`
* 📝 Changed `IsShop` setting in `appsettings.Production.json` to `true`
* 💄 Updated `app.min.css` for Tailwind CSS v3.4.17 changes
* 🆕 Added `PageActionDialogBox.razor` and `PageActionDialogBox.razor.cs`
* 🆕 Introduced `BasePageLDto` class for detailed page info
* 🚀 Added `MapPost` endpoint for updating pages in `PageController.cs`
* 🔄 Refactored methods in `PageController.cs` for conciseness
* 🛠️ Updated `IPageService` and `PageService` with `UpdatePageAsync`
* 🗂️ Enhanced `SiteMapService` to filter pages by `Indexing`
* 🛒 Included `BrandId` in order bag commands

Changes made by Amir.h Khademi
2025-03-23 11:04:45 +03:30
Amir Hossein Khademi 96feec0b26 Add support for brand-based discounts
* 🆕 Added support for brand discounts in `CalculateOrderDiscountCommandHandler`
* 🏷️ Updated `DiscountCommands` class to include `BrandId` property
* 🏗️ Introduced `BrandDiscount` class in `Discount.Aggregate.cs` with `Create` method
* 🛠️ Modified `Order` class in `Order.Aggregate.cs` to handle `BrandId`
* 🏷️ Updated `OrderProduct` class in `Order.Aggregate.cs` to include `BrandId` property
* 🆕 Added `Brand` discount type to `DiscountType` enum
*  Enhanced `CreateDiscountCommandHandler` and `UpdateDiscountCommandHandler` for brand discounts
* 🆕 Introduced `BrandDiscount` class in `BrandDiscount.cs` to represent brand-specific discounts

Changes made by Amir.h Khademi
2025-03-23 09:40:55 +03:30
Amir Hossein Khademi a87bfee340 free shiping 2025-02-11 14:22:42 +03:30
36 changed files with 5034 additions and 62 deletions

View File

@ -38,6 +38,11 @@ public class PageController : ICarterModule
.HasApiVersion(1.0)
.RequireAuthorization(builder => builder.AddAuthenticationSchemes("Bearer").RequireAuthenticatedUser().RequireClaim(CustomClaimType.Permission, ApplicationPermission.ManagePages));
group.MapPut("", UpdatePageAsync)
.WithDisplayName("Update Page")
.HasApiVersion(1.0)
.RequireAuthorization(builder => builder.AddAuthenticationSchemes("Bearer").RequireAuthenticatedUser().RequireClaim(CustomClaimType.Permission, ApplicationPermission.ManagePages));
group.MapDelete("{id}", DeletePageByIdAsync)
.WithDisplayName("Delete Page")
.HasApiVersion(1.0)
@ -55,27 +60,25 @@ public class PageController : ICarterModule
=> TypedResults.Ok(await pageService.DeletePageAsync(id, cancellationToken));
public async Task<IResult> GetPagesAsync([FromServices] IPageService pageService, CancellationToken cancellationToken)
{
return TypedResults.Ok(await pageService.GetPagesAsync(cancellationToken));
}
=> TypedResults.Ok(await pageService.GetPagesAsync(cancellationToken));
public async Task<IResult> GetPageByIdAsync(Guid id ,[FromServices] IPageService pageService, CancellationToken cancellationToken)
{
return TypedResults.Ok(await pageService.GetPageAsync(id: id,cancellationToken: cancellationToken));
}
=> TypedResults.Ok(await pageService.GetPageAsync(id: id, cancellationToken: cancellationToken));
public async Task<IResult> GetPageByTypeAsync(string type, [FromServices] IPageService pageService, CancellationToken cancellationToken)
{
return TypedResults.Ok(await pageService.GetPageAsync(type: type, cancellationToken: cancellationToken));
}
=> TypedResults.Ok(await pageService.GetPageAsync(type: type, cancellationToken: cancellationToken));
public async Task<IResult> GetPageAsync(string pageSlug, [FromServices] IPageService pageService, CancellationToken cancellationToken)
{
return TypedResults.Ok(await pageService.GetPageAsync(pageSlug: pageSlug,cancellationToken: cancellationToken));
}
=> TypedResults.Ok(await pageService.GetPageAsync(pageSlug: pageSlug, cancellationToken: cancellationToken));
public async Task<IResult> PostPageAsync([FromBody] PageActionRequestDto page, [FromServices] IPageService pageService, CancellationToken cancellationToken)
{
await pageService.CreatePageAsync(page, cancellationToken);
return TypedResults.Ok();
}
public async Task<IResult> UpdatePageAsync([FromBody] PageActionRequestDto page, [FromServices] IPageService pageService, CancellationToken cancellationToken)
{
await pageService.UpdatePageAsync(page, cancellationToken);
return TypedResults.Ok();
}
}

View File

@ -6,8 +6,8 @@
<ImplicitUsings>enable</ImplicitUsings>
<InvariantGlobalization>true</InvariantGlobalization>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<AssemblyVersion>1.10.21.36</AssemblyVersion>
<FileVersion>1.10.21.36</FileVersion>
<AssemblyVersion>1.14.22.42</AssemblyVersion>
<FileVersion>1.14.22.42</FileVersion>
<LangVersion>latest</LangVersion>
</PropertyGroup>

View File

@ -2,9 +2,10 @@
public interface IPageService : IScopedDependency
{
Task<BasePageSDto> GetPageAsync(Guid? id = null, string? pageName = null, string? pageSlug = null, string? type = null, CancellationToken cancellationToken = default);
Task<BasePageLDto> GetPageAsync(Guid? id = null, string? pageName = null, string? pageSlug = null, string? type = null, CancellationToken cancellationToken = default);
Task<List<BasePageSDto>> GetPagesAsync(CancellationToken cancellationToken = default);
Task<bool> CreatePageAsync(PageActionRequestDto entity, CancellationToken cancellationToken = default);
Task<bool> UpdatePageAsync(PageActionRequestDto entity, CancellationToken cancellationToken = default);
Task<bool> DeletePageAsync(Guid id, CancellationToken cancellationToken = default);
Task<string> CheckRedirectAsync(string oldUrl, CancellationToken cancellationToken);

View File

@ -1,5 +1,4 @@
using Netina.Core.BaseServices.Abstracts;
using Netina.Domain.MartenEntities.Pages;
using Netina.Domain.MartenEntities.Pages;
namespace Netina.Core.BaseServices;
@ -9,7 +8,11 @@ public class PageService(
ISettingService settingService)
: IPageService
{
public async Task<BasePageSDto> GetPageAsync(Guid? id = null, string? pageName = null, string? pageSlug = null, string? type = null, CancellationToken cancellationToken = default)
public async Task<BasePageLDto> GetPageAsync(Guid? id = null,
string? pageName = null,
string? pageSlug = null,
string? type = null,
CancellationToken cancellationToken = default)
{
BasePage? page = null;
if (id != null)
@ -23,18 +26,7 @@ public class PageService(
if (page == null)
throw new AppException("Page not found", ApiResultStatusCode.NotFound);
var entityType = Assembly.GetAssembly(typeof(DomainConfig))?.GetType(page.Type);
var dto = new BasePageSDto
{
Content = page.Content,
Description = page.Description,
Id = page.Id,
IsCustomPage = page.IsCustomPage,
IsHtmlBasePage = page.IsHtmlBasePage,
Title = page.Title,
Slug = page.Slug,
Data = page.Data
};
var dto = page.Adapt<BasePageLDto>();
return dto;
}
@ -45,19 +37,7 @@ public class PageService(
var pages = await martenRepositoryWrapperWrapper.SetRepository<BasePage>().GetEntitiesAsync(cancellationToken);
foreach (var page in pages)
{
var dto = new BasePageSDto
{
Content = page.Content,
Description = page.Description,
Id = page.Id,
IsCustomPage = page.IsCustomPage,
IsHtmlBasePage = page.IsHtmlBasePage,
Title = page.Title,
Slug = page.Slug,
Data = page.Data,
CreatedAt = page.CreatedAt,
ModifiedAt = page.ModifiedAt
};
var dto = page.Adapt<BasePageSDto>();
sDtos.Add(dto);
}
return sDtos;
@ -76,7 +56,9 @@ public class PageService(
Type = entity.Type,
Slug = entity.Slug,
CreatedAt = DateTime.Now,
CreatedBy = currentUserService.UserName ?? string.Empty
CreatedBy = currentUserService.UserName ?? string.Empty,
Indexing = entity.Indexing,
Sections = entity.Sections
};
if (!basePage.Type.IsNullOrEmpty())
{
@ -124,4 +106,11 @@ public class PageService(
}
throw new BaseApiException(ApiResultStatusCode.NotFound, "PageSetting not found");
}
public async Task<bool> UpdatePageAsync(PageActionRequestDto entity, CancellationToken cancellationToken = default)
{
var basePage = entity.Adapt<BasePage>();
await martenRepositoryWrapperWrapper.SetRepository<BasePage>().AddOrUpdateEntityAsync(basePage, cancellationToken);
return true;
}
}

View File

@ -123,7 +123,7 @@ public class SiteMapService(
root.SetAttribute("xmlns:image", "http://www.google.com/schemas/sitemap-image/1.1");
doc.AppendChild(root);
foreach (var page in pages)
foreach (var page in pages.Where(p=>p.Indexing))
{
string slugHtml = page.Slug;
if (slugHtml.Contains(' '))

View File

@ -73,6 +73,18 @@ public class CalculateOrderDiscountCommandHandler(
}
}
else if (discount.Type == DiscountType.Brand)
{
var brandDiscount = await repositoryWrapper.SetRepository<BrandDiscount>()
.TableNoTracking
.FirstOrDefaultAsync(d => d.Code == request.DiscountCode, cancellationToken);
if (brandDiscount != null && !brandDiscount.IsExpired())
{
totalPrice = request.Order.OrderProducts.Where(op => op.BrandId == brandDiscount.BrandId).Sum(op => op.ProductCost);
}
}
else if (discount.Type == DiscountType.Subscriber)
{
throw new NotImplementedException("Subscribe discount not implemented");

View File

@ -31,7 +31,7 @@ public class AddToOrderBagCommandHandler(
await mediator.Send(new CalculateProductDiscountCommand(productSDto), cancellationToken);
orderBag.AddToOrderBag(productSDto.Id, productSDto.Cost, productSDto.CostWithDiscount,
productSDto.HasDiscount, productSDto.PackingCost, productSDto.CategoryId, requestDto.Count);
productSDto.HasDiscount, productSDto.PackingCost, productSDto.CategoryId,productSDto.BrandId, requestDto.Count);
}
repositoryWrapper.SetRepository<Order>().Update(orderBag);

View File

@ -31,7 +31,7 @@ public class SubmitOrderBagCommandHandler(
await mediator.Send(new CalculateProductDiscountCommand(productSDto), cancellationToken);
orderBag.ChangeOrderBag(productSDto.Id, productSDto.Cost, productSDto.CostWithDiscount,
productSDto.HasDiscount, productSDto.PackingCost, productSDto.CategoryId, requestDto.Count);
productSDto.HasDiscount, productSDto.PackingCost, productSDto.CategoryId,productSDto.BrandId, requestDto.Count);
}
foreach (var orderProduct in orderBag.OrderProducts.ToList())
{

View File

@ -34,10 +34,11 @@ public class CalculateOrderCommandHandler(IRepositoryWrapper repositoryWrapper,
{
order.RemoveDiscount();
}
if (totalProductPrice > 5000000)
deliveryPrice = 0;
var taxesPrice = (((totalProductPrice - (discountCodePrice + productDiscountPrice)) + totalPackingPrice + servicePrice) / 100) * taxesFee;
order.SetTotalPrice(totalProductPrice, totalPackingPrice, servicePrice, deliveryPrice, productDiscountPrice, discountCodePrice, taxesPrice);
order.OrderProducts.Clear();
order.OrderDelivery = null;

View File

@ -21,7 +21,8 @@ bool IsForInvitation,
bool IsSpecialOffer,
bool IsForFirstPurchase,
Guid ProductId,
Guid CategoryId) : IRequest<DiscountLDto>;
Guid CategoryId,
Guid BrandId) : IRequest<DiscountLDto>;
public sealed record UpdateDiscountCommand(
Guid Id,
@ -46,7 +47,8 @@ public sealed record UpdateDiscountCommand(
bool IsSpecialOffer,
bool IsForFirstPurchase,
Guid ProductId,
Guid CategoryId) : IRequest<bool>;
Guid CategoryId,
Guid BrandId) : IRequest<bool>;
public sealed record DeleteDiscountCommand(Guid Id) : IRequest<bool>;

View File

@ -5,6 +5,7 @@ public sealed record CreateSubProductCommand(
Guid ParentId,
ProductDiversity Diversity,
string DiversityValue,
string DiversitySecondaryValue,
string DiversityDescription,
string PersianName,
double Cost,
@ -19,6 +20,7 @@ public sealed record UpdateSubProductCommand(
Guid ParentId,
ProductDiversity Diversity,
string DiversityValue,
string DiversitySecondaryValue,
string DiversityDescription,
string PersianName,
double Cost,

View File

@ -0,0 +1,17 @@
using Newtonsoft.Json;
namespace Netina.Domain.Dtos.LargDtos;
public class BasePageLDto : BaseDto<BasePageLDto, BasePage>
{
public string Title { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
public bool IsCustomPage { get; set; }
public bool IsHtmlBasePage { get; set; }
public string Slug { get; set; } = string.Empty;
public string Data { get; set; } = string.Empty;
public string Type { get; set; } = string.Empty;
public DateTime ModifiedAt { get; set; }
public bool Indexing { get; set; }
public List<BasePageSection> Sections { get; set; } = new();
}

View File

@ -23,6 +23,8 @@ public class DiscountLDto : BaseDto<DiscountLDto,Discount>
public bool IsForInvitation { get; set; }
public Guid ProductId { get; set; }
public string ProductName { get; set; } = string.Empty;
public Guid BrandId { get; set; }
public string BrandName { get; set; } = string.Empty;
public Guid CategoryId { get; set; }
public string CategoryName { get; set; } = string.Empty;
public bool IsForFirstPurchase { get; set; }

View File

@ -1,4 +1,6 @@
namespace Netina.Domain.Dtos.RequestDtos;
using Netina.Domain.MartenEntities.Pages;
namespace Netina.Domain.Dtos.RequestDtos;
public class PageActionRequestDto
{
@ -11,4 +13,6 @@ public class PageActionRequestDto
public string Slug { get; set; } = string.Empty;
public string Type { get; set; } = string.Empty;
public object? Data { get; set; }
public bool Indexing { get; set; }
public List<BasePageSection> Sections { get; set; } = new();
}

View File

@ -1,5 +1,4 @@
using Netina.Domain.MartenEntities.Pages;
using Newtonsoft.Json;
using Newtonsoft.Json;
namespace Netina.Domain.Dtos.SmallDtos;
@ -13,6 +12,7 @@ public class BasePageSDto : BaseDto<BasePageSDto,BasePage>
public string Slug { get; set; } = string.Empty;
public string Data { get; set; } = string.Empty;
public DateTime ModifiedAt { get; set; }
public bool Indexing { get; set; } = true;
public T GetData<T>() => JsonConvert.DeserializeObject<T>(Data);
}

View File

@ -12,4 +12,5 @@ public class SubProductSDto : BaseDto<SubProductSDto,SubProduct>
public ProductDiversity Diversity { get; set; }
public string DiversityValue { get; set; } = string.Empty;
public string DiversityDescription { get; set; } = string.Empty;
public string DiversitySecondaryValue { get; set; } = string.Empty;
}

View File

@ -0,0 +1,21 @@
namespace Netina.Domain.Entities.Discounts;
public partial class BrandDiscount : Discount
{
public BrandDiscount()
{
}
public BrandDiscount(string code, string description, int discountPercent, long discountAmount, bool hasCode, DiscountAmountType amountType, DiscountType type,
int count, bool immortal, DateTime startDate, DateTime expireDate, long priceFloor, bool hasPriceFloor, long priceCeiling,
bool hasPriceCeiling, bool isInfinity, long useCount, bool isForInvitation,
bool isForFirstPurchase, bool isSpecialOffer , Guid brandId)
: base(code, description, discountPercent, discountAmount, hasCode, amountType, type, count, startDate, expireDate, priceFloor, hasPriceFloor, priceCeiling, hasPriceCeiling, isInfinity, useCount,
isForInvitation, isSpecialOffer,isForFirstPurchase, immortal)
{
BrandId = brandId;
}
public Guid BrandId { get; internal set; }
public Brand? Brand { get; internal set; }
}

View File

@ -39,6 +39,19 @@ public partial class Discount
}
}
public partial class BrandDiscount
{
public static BrandDiscount Create(string code, string description, int discountPercent, long discountAmount, bool hasCode, DiscountAmountType amountType, DiscountType type,
int count, bool immortal, DateTime startDate, DateTime expireDate, long priceFloor, bool hasPriceFloor, long priceCeiling,
bool hasPriceCeiling, bool isInfinity, long useCount, bool isForInvitation,
bool isForFirstPurchase, bool isSpecialOffer, Guid brandId)
{
return new BrandDiscount(code, description, discountPercent, discountAmount, hasCode, amountType, type, count, immortal, startDate,
expireDate, priceFloor, hasPriceFloor, priceCeiling, hasPriceCeiling, isInfinity, useCount,
isForInvitation, isForFirstPurchase, isSpecialOffer, brandId);
}
}
public partial class ProductDiscount
{
public static ProductDiscount Create(string code, string description, int discountPercent, long discountAmount, bool hasCode, DiscountAmountType amountType, DiscountType type,

View File

@ -12,7 +12,14 @@ public partial class Order
}
public void AddToOrderBag(Guid productId, double cost, double costWithDiscount, bool hasDiscount, double packingCost, Guid categoryId, int count)
public void AddToOrderBag(Guid productId,
double cost,
double costWithDiscount,
bool hasDiscount,
double packingCost,
Guid categoryId,
Guid brandId,
int count)
{
var orderProduct = OrderProducts.FirstOrDefault(op => op.ProductId == productId);
if (orderProduct == null)
@ -25,6 +32,7 @@ public partial class Order
OrderStatus.OrderBag,
productId,
categoryId,
brandId,
this.Id);
OrderProducts.Add(orderProduct);
}
@ -44,7 +52,10 @@ public partial class Order
}
}
public void ChangeOrderBag(Guid productId, double cost, double costWithDiscount, bool hasDiscount, double packingCost, Guid categoryId, int count)
public void ChangeOrderBag(Guid productId, double cost, double costWithDiscount, bool hasDiscount, double packingCost,
Guid categoryId,
Guid brandId,
int count)
{
var orderProduct = OrderProducts.FirstOrDefault(op => op.ProductId == productId);
if (orderProduct != null)
@ -52,10 +63,10 @@ public partial class Order
if (orderProduct.Count > count)
RemoveFromOrderBag(productId, count);
else
AddToOrderBag(productId, cost, costWithDiscount, hasDiscount, packingCost, categoryId, count);
AddToOrderBag(productId, cost, costWithDiscount, hasDiscount, packingCost, categoryId,brandId, count);
}
else
AddToOrderBag(productId, cost, costWithDiscount, hasDiscount, packingCost, categoryId, count);
AddToOrderBag(productId, cost, costWithDiscount, hasDiscount, packingCost, categoryId, brandId, count);
}
@ -165,11 +176,16 @@ public partial class OrderProduct
OrderStatus orderProductStatus,
Guid productId,
Guid productCategoryId,
Guid brandId,
Guid orderId)
{
var productCost = count * productFeeWithDiscount;
var packingCost = count * packingFee;
return new OrderProduct(count, productFee, productFeeWithDiscount, hasDiscount, productCost, packingFee, packingCost, orderProductStatus, productId, productCategoryId, orderId);
return new OrderProduct(count, productFee,
productFeeWithDiscount, hasDiscount,
productCost, packingFee, packingCost,
orderProductStatus, productId,
productCategoryId, orderId,brandId);
}
public void SetCount(int count)

View File

@ -19,7 +19,8 @@ public partial class OrderProduct : ApiEntity
OrderStatus orderProductStatus,
Guid productId,
Guid productCategoryId,
Guid orderId)
Guid orderId,
Guid brandId)
{
Count = count;
ProductFee = productFee;
@ -32,6 +33,7 @@ public partial class OrderProduct : ApiEntity
ProductCategoryId = productCategoryId;
PackingFee = packingFee;
PackingCost = packingCost;
BrandId = brandId;
}
public int Count { get; internal set; }
public double ProductFee { get; internal set; }
@ -43,6 +45,7 @@ public partial class OrderProduct : ApiEntity
public OrderStatus OrderProductStatus { get; internal set; }
public Guid ProductId { get; internal set; }
public Guid BrandId { get; internal set; }
public Guid ProductCategoryId { get; internal set; }
public Product? Product { get; internal set; }

View File

@ -107,6 +107,7 @@ public partial class SubProduct
Guid parentId,
ProductDiversity diversity,
string diversityValue,
string diversitySecondaryValue,
string diversityDescription,
string persianName,
string englishName,
@ -129,6 +130,7 @@ public partial class SubProduct
parentId,
diversity,
diversityValue,
diversitySecondaryValue,
diversityDescription,
persianName,
englishName,

View File

@ -14,6 +14,7 @@ public partial class SubProduct : Product
Guid parentId,
ProductDiversity diversity,
string diversityValue,
string diversitySecondaryValue,
string diversityDescription,
string persianName,
string englishName,
@ -53,6 +54,7 @@ public partial class SubProduct : Product
ParentId = parentId;
Diversity = diversity;
DiversityValue = diversityValue;
DiversitySecondaryValue = diversitySecondaryValue;
DiversityDescription = diversityDescription;
}
@ -61,5 +63,6 @@ public partial class SubProduct : Product
public ProductDiversity Diversity { get; internal set; }
public string DiversityValue { get; internal set; } = string.Empty;
public string DiversitySecondaryValue { get; internal set; } = string.Empty;
public string DiversityDescription { get; internal set; } = string.Empty;
}

View File

@ -9,5 +9,7 @@ public enum DiscountType
[Display(Name = "دسته ای")]
Category = 2,
[Display(Name = "مشترکی")]
Subscriber = 3
Subscriber = 3,
[Display(Name = "برند")]
Brand = 4,
}

View File

@ -0,0 +1,6 @@
namespace Netina.Domain.Mappers
{
public static partial class BrandDiscountMapper
{
}
}

View File

@ -15,6 +15,7 @@ namespace Netina.Domain.Mappers
ParentId = (Guid?)p1.ParentId,
Diversity = p1.Diversity,
DiversityValue = p1.DiversityValue,
DiversitySecondaryValue = p1.DiversitySecondaryValue,
DiversityDescription = p1.DiversityDescription,
PersianName = p1.PersianName,
Cost = p1.Cost,
@ -37,6 +38,7 @@ namespace Netina.Domain.Mappers
result.ParentId = (Guid?)p2.ParentId;
result.Diversity = p2.Diversity;
result.DiversityValue = p2.DiversityValue;
result.DiversitySecondaryValue = p2.DiversitySecondaryValue;
result.DiversityDescription = p2.DiversityDescription;
result.PersianName = p2.PersianName;
result.Cost = p2.Cost;
@ -63,6 +65,7 @@ namespace Netina.Domain.Mappers
Diversity = p4.Diversity,
DiversityValue = p4.DiversityValue,
DiversityDescription = p4.DiversityDescription,
DiversitySecondaryValue = p4.DiversitySecondaryValue,
Id = p4.Id,
CreatedAt = p4.CreatedAt
};
@ -85,6 +88,7 @@ namespace Netina.Domain.Mappers
result.Diversity = p5.Diversity;
result.DiversityValue = p5.DiversityValue;
result.DiversityDescription = p5.DiversityDescription;
result.DiversitySecondaryValue = p5.DiversitySecondaryValue;
result.Id = p5.Id;
result.CreatedAt = p5.CreatedAt;
return result;
@ -102,6 +106,7 @@ namespace Netina.Domain.Mappers
Diversity = p7.Diversity,
DiversityValue = p7.DiversityValue,
DiversityDescription = p7.DiversityDescription,
DiversitySecondaryValue = p7.DiversitySecondaryValue,
Id = p7.Id,
CreatedAt = p7.CreatedAt
};

View File

@ -10,4 +10,35 @@ public class BasePage : MartenEntity
public string Slug { get; set; } = string.Empty;
public string Type { get; set; } = string.Empty;
public string Data { get; set; } = string.Empty;
public bool Indexing { get; set; } = true;
public List<BasePageSection> Sections { get; set; } = new();
}
public class BasePageSection
{
public string Title { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string CTAText { get; set; } = string.Empty;
public string CTARoute { get; set; } = string.Empty;
public BasePageSectionType Type { get; set; }
public string Query { get; set; } = string.Empty;
public List<SectionItem> SectionItems { get; set; } = [];
}
public class SectionItem
{
public string Title { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string ImageLocation { get; set; } = string.Empty;
public string Url { get; set; } = string.Empty;
}
public enum BasePageSectionType
{
[Display(Name = "اسلایدر محصولات")]
ProductSlider = 0,
[Display(Name = "اسلایدر دسته بندی محصولات")]
ProductCategorySlider = 1,
[Display(Name = "اسلایدر بلاگ ها")]
BlogSlider = 2
}

View File

@ -83,6 +83,7 @@
<Using Include="Netina.Domain.Entities.Warehouses" />
<Using Include="Netina.Domain.Enums" />
<Using Include="Netina.Domain.MartenEntities.Faqs" />
<Using Include="Netina.Domain.MartenEntities.Pages" />
<Using Include="Netina.Domain.MartenEntities.Settings" />
<Using Include="Netina.Domain.Models" />
<Using Include="System.ComponentModel.DataAnnotations" />

View File

@ -48,6 +48,13 @@ public class CreateDiscountCommandHandler(IRepositoryWrapper repositoryWrapper)
request.IsForInvitation, request.IsForFirstPurchase, request.IsSpecialOffer, request.ProductId);
repositoryWrapper.SetRepository<ProductDiscount>().Add(productDis);
break;
case DiscountType.Brand:
var brandDis = BrandDiscount.Create(request.Code, request.Description, request.DiscountPercent, request.DiscountAmount, request.HasCode,
request.AmountType, request.Type, request.Count, request.IsImmortal, request.StartDate, request.ExpireDate, request.PriceFloor,
request.HasPriceFloor, request.PriceCeiling, request.HasPriceCeiling, request.IsInfinity, request.UseCount,
request.IsForInvitation, request.IsForFirstPurchase, request.IsSpecialOffer, request.BrandId);
repositoryWrapper.SetRepository<BrandDiscount>().Add(brandDis);
break;
default:
var def = Discount.Create(request.Code, request.Description, request.DiscountPercent, request.DiscountAmount, request.HasCode,
request.AmountType, request.Type, request.Count, request.IsImmortal, request.StartDate, request.ExpireDate, request.PriceFloor,

View File

@ -55,6 +55,23 @@ public class UpdateDiscountCommandHandler(IRepositoryWrapper repositoryWrapper)
productDis.CreatedBy = productEnt.CreatedBy;
repositoryWrapper.SetRepository<ProductDiscount>().Update(productDis);
break;
case DiscountType.Brand:
var brandEnt = await repositoryWrapper.SetRepository<BrandDiscount>().TableNoTracking.FirstOrDefaultAsync(d => d.Id == request.Id, cancellationToken);
if (brandEnt == null)
throw new AppException("Discount not found", ApiResultStatusCode.NotFound);
var brandDis = BrandDiscount.Create(request.Code, request.Description,
request.DiscountPercent, request.DiscountAmount, request.HasCode,
request.AmountType, request.Type, request.Count, request.IsImmortal,
request.StartDate, request.ExpireDate, request.PriceFloor, request.HasPriceFloor,
request.PriceCeiling, request.HasPriceCeiling, request.IsInfinity,
request.UseCount, request.IsForInvitation, request.IsForFirstPurchase, request.IsSpecialOffer, request.BrandId);
brandDis.Id = brandEnt.Id;
brandDis.CreatedAt = brandEnt.CreatedAt;
brandDis.CreatedBy = brandEnt.CreatedBy;
repositoryWrapper.SetRepository<BrandDiscount>().Update(brandDis);
break;
}
await repositoryWrapper.SaveChangesAsync(cancellationToken);
return true;

View File

@ -21,6 +21,7 @@ public class CreateSubProductCommandHandler(IRepositoryWrapper repositoryWrapper
request.ParentId,
request.Diversity,
request.DiversityValue,
request.DiversitySecondaryValue,
request.DiversityDescription,
$"{parent.PersianName} - {request.DiversityDescription}",
parent.EnglishName,

View File

@ -5,7 +5,7 @@ public class UpdateSubProductCommandHandler(IRepositoryWrapper repositoryWrapper
{
public async Task<Guid> Handle(UpdateSubProductCommand request, CancellationToken cancellationToken)
{
if (currentUserService.UserId == null)
throw new BaseApiException(ApiResultStatusCode.UnAuthorized, "User id is wrong");
@ -26,6 +26,7 @@ public class UpdateSubProductCommandHandler(IRepositoryWrapper repositoryWrapper
request.ParentId,
request.Diversity,
request.DiversityValue,
request.DiversitySecondaryValue,
request.DiversityDescription,
$"{parent.PersianName} - {request.DiversityDescription}",
parent.EnglishName,

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,70 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NetinaShop.Repository.Migrations
{
/// <inheritdoc />
public partial class EditDisc : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<Guid>(
name: "BrandId",
schema: "public",
table: "OrderProducts",
type: "uuid",
nullable: false,
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"));
migrationBuilder.AddColumn<Guid>(
name: "BrandId",
schema: "public",
table: "Discounts",
type: "uuid",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_Discounts_BrandId",
schema: "public",
table: "Discounts",
column: "BrandId");
migrationBuilder.AddForeignKey(
name: "FK_Discounts_Brands_BrandId",
schema: "public",
table: "Discounts",
column: "BrandId",
principalSchema: "public",
principalTable: "Brands",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Discounts_Brands_BrandId",
schema: "public",
table: "Discounts");
migrationBuilder.DropIndex(
name: "IX_Discounts_BrandId",
schema: "public",
table: "Discounts");
migrationBuilder.DropColumn(
name: "BrandId",
schema: "public",
table: "OrderProducts");
migrationBuilder.DropColumn(
name: "BrandId",
schema: "public",
table: "Discounts");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NetinaShop.Repository.Migrations
{
/// <inheritdoc />
public partial class EditSubProductValue : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "DiversitySecondaryValue",
schema: "public",
table: "Products",
type: "text",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "DiversitySecondaryValue",
schema: "public",
table: "Products");
}
}
}

View File

@ -743,6 +743,9 @@ namespace NetinaShop.Repository.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("BrandId")
.HasColumnType("uuid");
b.Property<int>("Count")
.HasColumnType("integer");
@ -1674,6 +1677,18 @@ namespace NetinaShop.Repository.Migrations
b.HasDiscriminator().HasValue("ProductComment");
});
modelBuilder.Entity("Netina.Domain.Entities.Discounts.BrandDiscount", b =>
{
b.HasBaseType("Netina.Domain.Entities.Discounts.Discount");
b.Property<Guid>("BrandId")
.HasColumnType("uuid");
b.HasIndex("BrandId");
b.HasDiscriminator().HasValue("BrandDiscount");
});
modelBuilder.Entity("Netina.Domain.Entities.Discounts.CategoryDiscount", b =>
{
b.HasBaseType("Netina.Domain.Entities.Discounts.Discount");
@ -1709,6 +1724,10 @@ namespace NetinaShop.Repository.Migrations
.IsRequired()
.HasColumnType("text");
b.Property<string>("DiversitySecondaryValue")
.IsRequired()
.HasColumnType("text");
b.Property<string>("DiversityValue")
.IsRequired()
.HasColumnType("text");
@ -2111,6 +2130,16 @@ namespace NetinaShop.Repository.Migrations
b.Navigation("Product");
});
modelBuilder.Entity("Netina.Domain.Entities.Discounts.BrandDiscount", b =>
{
b.HasOne("Netina.Domain.Entities.Brands.Brand", "Brand")
.WithMany()
.HasForeignKey("BrandId")
.OnDelete(DeleteBehavior.Restrict);
b.Navigation("Brand");
});
modelBuilder.Entity("Netina.Domain.Entities.Discounts.CategoryDiscount", b =>
{
b.HasOne("Netina.Domain.Entities.ProductCategories.ProductCategory", "Category")