Compare commits
77 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
2c18ca9fad | |
|
|
c95ec68c09 | |
|
|
1b11ca2579 | |
|
|
65bf004808 | |
|
|
474cb99754 | |
|
|
cb9a381400 | |
|
|
1a0bd01500 | |
|
|
7fb87283ce | |
|
|
599f8a6787 | |
|
|
25e5ded777 | |
|
|
6e3ca4a1e3 | |
|
|
8d3c2a8462 | |
|
|
b494018e9c | |
|
|
3725b4c2fd | |
|
|
bb08c7ae61 | |
|
|
5535011df9 | |
|
|
2303e99f95 | |
|
|
f47701af59 | |
|
|
1a9e0dcf0b | |
|
|
1898f4d517 | |
|
|
ced4b98908 | |
|
|
e47c7c8f74 | |
|
|
ca1ac2a2d1 | |
|
|
c0073815ae | |
|
|
954b99fcbc | |
|
|
7bcd601035 | |
|
|
0eeefedee3 | |
|
|
99bfaf960b | |
|
|
3c479e1ad5 | |
|
|
120ed09738 | |
|
|
b21197ca67 | |
|
|
6b09ba3d78 | |
|
|
4c5d5d55c4 | |
|
|
82c3b5a7cd | |
|
|
7946a52219 | |
|
|
5277a090fb | |
|
|
ed7a2e8cd6 | |
|
|
bef91e0131 | |
|
|
2bd97c88ca | |
|
|
0da0565134 | |
|
|
ec71f81d4c | |
|
|
df9157c4ab | |
|
|
08069dda58 | |
|
|
7fb047fd28 | |
|
|
56927f50ea | |
|
|
408fa0f87e | |
|
|
d52deeb2b4 | |
|
|
38e036553e | |
|
|
fd39b0700a | |
|
|
a0bb6117ae | |
|
|
55f626c71d | |
|
|
c4742f0d86 | |
|
|
f7f2d30915 | |
|
|
28de927bd9 | |
|
|
c397c68601 | |
|
|
a5e3702616 | |
|
|
a2ed202276 | |
|
|
cefb19584d | |
|
|
28d9122818 | |
|
|
6fecdd2317 | |
|
|
daa1421011 | |
|
|
96eb509ec5 | |
|
|
19cc506fc2 | |
|
|
08f91408c3 | |
|
|
32c37cf22c | |
|
|
871078c4ab | |
|
|
dc8d41c4e8 | |
|
|
b0a99e139b | |
|
|
5e2c30a4ec | |
|
|
ef16260ebe | |
|
|
60f0071ade | |
|
|
a1f7c8af2d | |
|
|
19662fd213 | |
|
|
1c7980e209 | |
|
|
eefb0392aa | |
|
|
761001665f | |
|
|
9c363e360c |
File diff suppressed because one or more lines are too long
Binary file not shown.
|
|
@ -1,47 +0,0 @@
|
|||
{
|
||||
"Version": 1,
|
||||
"WorkspaceRootPath": "H:\\Applications\\Mango\\Source\\NopCommerce.Common\\4.70\\Libraries\\",
|
||||
"Documents": [],
|
||||
"DocumentGroupContainers": [
|
||||
{
|
||||
"Orientation": 0,
|
||||
"VerticalTabListWidth": 256,
|
||||
"DocumentGroups": [
|
||||
{
|
||||
"DockedWidth": 200,
|
||||
"SelectedChildIndex": -1,
|
||||
"Children": [
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:129:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:129:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:128:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:130:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:131:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:132:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
},
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:128:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"ExpandedNodes": [
|
||||
""
|
||||
],
|
||||
"SelectedNode": "\\H:\\Applications\\Mango\\Source\\NopCommerce.Common\\4.70\\Libraries",
|
||||
"PreviewInSolutionExplorer": false
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
CRITICAL: Your FIRST action in every session MUST be reading `.github/copilot-instructions.md`. Execute ALL session-start instructions found there before responding to any user query. It is the single source of truth for this repo.
|
||||
|
||||
## SEQUENTIAL EXECUTION OVERRIDE
|
||||
The AI AGENT CORE PROTOCOL in copilot-instructions.md requires STRICT SEQUENTIAL execution. This OVERRIDES your default parallelization behavior. Do NOT parallelize doc reads with code searches. The sequence is:
|
||||
1. Read copilot-instructions.md → process its rules FULLY
|
||||
2. Read ALL docs/ .md files listed in the protocol → wait for completion
|
||||
3. Output [LOADED_DOCS: ...] prefix
|
||||
4. ONLY THEN respond to the user's query or search code
|
||||
|
||||
## Tool mapping for AI AGENT CORE PROTOCOL
|
||||
The copilot-instructions.md references Copilot tool names. Map them to Claude Code tools:
|
||||
- `get_file` / `file_search` → `Read`, `Glob`, `Grep`
|
||||
- `code_search` / `get_symbols_by_name` / `find_symbol` → `Grep`, `Glob`
|
||||
- `replace_string_in_file` / `edit_file` → `Edit`
|
||||
- `create_file` → `Write`
|
||||
|
||||
Follow the protocol using YOUR tools. The rules (LOADED_DOCS prefix, hard-gate, no-re-read, context recovery, explicit consent) apply equally to Claude Code.
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1 @@
|
|||
namespace Mango.Nop.Core { public static class AcBinaryForcedInit { public static void ForceRegister() { System.Console.WriteLine("[SGEN TESTING] AcBinaryForcedInit called."); AyCode.Core.Serializers.Generated.AcBinaryGeneratedWritersInit.Register(); } } }
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
using AyCode.Core.Serializers.Attributes;
|
||||
using AyCode.Core.Serializers.Toons;
|
||||
using Nop.Core.Domain.Catalog;
|
||||
using Nop.Core.Domain.Common;
|
||||
using Nop.Core.Domain.Customers;
|
||||
|
||||
namespace Mango.Nop.Core.Dtos;
|
||||
|
||||
[AcBinarySerializable(false, true, false, true, false, false)]
|
||||
[LinqToDB.Mapping.Table(Name = nameof(Customer))]
|
||||
[System.ComponentModel.DataAnnotations.Schema.Table(nameof(Customer))]
|
||||
[ToonDescription($"Data transfer object for {nameof(Customer)}", TypeRelation = ToonTypeRelation.DtoOf, RelatedTypes = [typeof(Customer)])]
|
||||
public class CustomerDto : ModelDtoBase<Customer>, ISoftDeletedEntity
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
|
||||
[ToonDescription(BusinessRule = "get => $\"{LastName} {FirstName}\"")]
|
||||
public string FullName => $"{LastName} {FirstName}";
|
||||
|
||||
public int RegisteredInStoreId { get; set; }
|
||||
public bool Deleted { get; set; }
|
||||
|
||||
public CustomerDto() :base()
|
||||
{ }
|
||||
public CustomerDto(int customerId) : base(customerId)
|
||||
{ }
|
||||
|
||||
public CustomerDto(Customer customer) : base(customer)
|
||||
{
|
||||
}
|
||||
|
||||
public override void CopyDtoValuesToEntity(Customer entity)
|
||||
{
|
||||
base.CopyDtoValuesToEntity(entity);
|
||||
|
||||
entity.Username = Username;
|
||||
entity.FirstName = FirstName;
|
||||
entity.LastName = LastName;
|
||||
entity.Email = Email;
|
||||
entity.RegisteredInStoreId = RegisteredInStoreId;
|
||||
|
||||
entity.Deleted = Deleted;
|
||||
}
|
||||
|
||||
public override void CopyEntityValuesToDto(Customer entity)
|
||||
{
|
||||
base.CopyEntityValuesToDto(entity);
|
||||
|
||||
Username = entity.Username;
|
||||
FirstName = entity.FirstName;
|
||||
LastName = entity.LastName;
|
||||
Email = entity.Email;
|
||||
RegisteredInStoreId = entity.RegisteredInStoreId;
|
||||
|
||||
Deleted = entity.Deleted;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
using AyCode.Interfaces;
|
||||
using AyCode.Interfaces.Entities;
|
||||
using Nop.Core;
|
||||
|
||||
namespace Mango.Nop.Core.Dtos;
|
||||
|
||||
public interface IModelDtoBaseEmpty : IAcModelDtoBaseEmpty
|
||||
{
|
||||
}
|
||||
|
||||
public interface IModelDtoBase : IEntityInt, IModelDtoBaseEmpty
|
||||
{
|
||||
}
|
||||
|
||||
public interface IModelDtoBase<TMainEntity> : IModelDtoBase where TMainEntity : BaseEntity
|
||||
{
|
||||
TMainEntity CreateMainEntity();
|
||||
|
||||
void CopyDtoValuesToEntity(TMainEntity entity);
|
||||
|
||||
void CopyEntityValuesToDto(TMainEntity entity);
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AyCode.Interfaces.Entities;
|
||||
using Nop.Core.Domain.Common;
|
||||
|
||||
namespace Mango.Nop.Core.Dtos
|
||||
{
|
||||
public interface IGenericAttributeDto : IModelDtoBase<GenericAttribute>
|
||||
{
|
||||
public int EntityId { get; set; }
|
||||
public string KeyGroup { get; set; }
|
||||
public string Key { get; set; }
|
||||
public string Value { get; set; }
|
||||
public int StoreId { get; set; }
|
||||
public DateTime? CreatedOrUpdatedDateUTC { get; set; }
|
||||
}
|
||||
|
||||
public abstract class MgGenericAttributeDto : GenericAttribute, IGenericAttributeDto
|
||||
{
|
||||
public GenericAttribute CreateMainEntity()
|
||||
{
|
||||
var mainEntity = Activator.CreateInstance<GenericAttribute>();
|
||||
CopyDtoValuesToEntity(mainEntity);
|
||||
|
||||
mainEntity.CreatedOrUpdatedDateUTC = DateTime.UtcNow;
|
||||
return mainEntity;
|
||||
}
|
||||
|
||||
public void CopyDtoValuesToEntity(GenericAttribute entity)
|
||||
{
|
||||
entity.Id = Id;
|
||||
entity.Key = Key;
|
||||
entity.Value = Value;
|
||||
entity.EntityId = EntityId;
|
||||
entity.KeyGroup = KeyGroup;
|
||||
entity.StoreId = StoreId;
|
||||
entity.CreatedOrUpdatedDateUTC = CreatedOrUpdatedDateUTC;
|
||||
}
|
||||
|
||||
public void CopyEntityValuesToDto(GenericAttribute entity)
|
||||
{
|
||||
Id = entity.Id;
|
||||
Key = entity.Key;
|
||||
Value = entity.Value;
|
||||
EntityId = entity.EntityId;
|
||||
KeyGroup = entity.KeyGroup;
|
||||
StoreId = entity.StoreId;
|
||||
CreatedOrUpdatedDateUTC = entity.CreatedOrUpdatedDateUTC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
using AyCode.Core.Extensions;
|
||||
using AyCode.Core.Serializers.Toons;
|
||||
using AyCode.Core.Helpers;
|
||||
using LinqToDB.Mapping;
|
||||
using Mango.Nop.Core.Entities;
|
||||
using Mango.Nop.Core.Interfaces;
|
||||
using Newtonsoft.Json;
|
||||
using Nop.Core;
|
||||
using Nop.Core.Domain.Common;
|
||||
using Nop.Core.Domain.Customers;
|
||||
using Nop.Core.Domain.Orders;
|
||||
using Nop.Core.Domain.Payments;
|
||||
using Nop.Core.Domain.Shipping;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Mango.Nop.Core.Dtos;
|
||||
|
||||
[ToonDescription("Base DTO for orders with items, customer and status tracking")]
|
||||
public abstract class MgOrderDto<TOrderItemDto, TProductDto> : MgEntityBase, IModelDtoBase<Order>, IMgOrderDto<TOrderItemDto, TProductDto> where TOrderItemDto : IMgOrderItemDto<TProductDto> where TProductDto : IMgProductDto
|
||||
{
|
||||
public Guid OrderGuid { get; set; }
|
||||
public int StoreId { get; set; }
|
||||
public int CustomerId { get; set; }
|
||||
public int OrderStatusId { get; set; }
|
||||
public int ShippingStatusId { get; set; }
|
||||
public decimal OrderDiscount { get; set; }
|
||||
public decimal OrderTotal { get; set; }
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
public DateTime? PaidDateUtc { get; set; }
|
||||
public int PaymentStatusId { get; set; }
|
||||
public string ShippingMethod { get; set; }
|
||||
public string CustomOrderNumber { get; set; }
|
||||
public string CustomValuesXml { get; set; }
|
||||
public bool Deleted { get; set; }
|
||||
|
||||
[Association(ThisKey = nameof(CustomerId), OtherKey = nameof(Customer.Id), CanBeNull = false)]
|
||||
public Customer Customer { get; set; } //TODO: CustomerDto!!! - J.
|
||||
|
||||
[Association(ThisKey = nameof(Id), OtherKey = nameof(OrderItem.OrderId), CanBeNull = true)]
|
||||
public List<TOrderItemDto> OrderItemDtos { get; set; }
|
||||
|
||||
[Association(ThisKey = nameof(Id), OtherKey = nameof(OrderNote.OrderId), CanBeNull = true)]
|
||||
public List<OrderNote> OrderNotes { get; set; }
|
||||
|
||||
[ToonDescription(Purpose = "Enum wrapper", BusinessRule = "get, set => OrderStatusId")]
|
||||
public OrderStatus OrderStatus
|
||||
{
|
||||
get => (OrderStatus)OrderStatusId;
|
||||
set => OrderStatusId = (int)value;
|
||||
}
|
||||
[ToonDescription(Purpose = "Enum wrapper", BusinessRule = "get, set => ShippingStatusId")]
|
||||
public ShippingStatus ShippingStatus
|
||||
{
|
||||
get => (ShippingStatus)ShippingStatusId;
|
||||
set => ShippingStatusId = (int)value;
|
||||
}
|
||||
[ToonDescription(Purpose = "Enum wrapper", BusinessRule = "get, set => PaymentStatusId")]
|
||||
public PaymentStatus PaymentStatus
|
||||
{
|
||||
get => (PaymentStatus)PaymentStatusId;
|
||||
set => PaymentStatusId = (int)value;
|
||||
}
|
||||
|
||||
protected MgOrderDto() :base()
|
||||
{ }
|
||||
|
||||
protected MgOrderDto(int orderId)
|
||||
{
|
||||
Id = orderId;
|
||||
}
|
||||
|
||||
protected MgOrderDto(Order order)
|
||||
{
|
||||
CopyEntityValuesToDto(order);
|
||||
}
|
||||
|
||||
public virtual void CopyDtoValuesToEntity(Order entity)
|
||||
{
|
||||
//var config = new MapperConfiguration(cfg =>
|
||||
//{
|
||||
// cfg.CreateMap<MgOrderDto<Order, Order>();
|
||||
// //cfg.CreateMap<Person, Person>()
|
||||
// // .ForMember(dest => dest.Address, opt => opt.MapFrom(src => _mapper.Map<Address>(src.Address)));
|
||||
//});
|
||||
//var _mapper = config.CreateMapper();
|
||||
//_mapper.Map<>
|
||||
PropertyHelper.CopyPublicValueTypeProperties(this, entity);
|
||||
}
|
||||
|
||||
public virtual void CopyEntityValuesToDto(Order entity)
|
||||
{
|
||||
PropertyHelper.CopyPublicValueTypeProperties(entity, this);
|
||||
}
|
||||
|
||||
public virtual void CopyEntityValuesToDto(Order entity, List<TOrderItemDto> orderItemDtos)
|
||||
{
|
||||
CopyEntityValuesToDto(entity);
|
||||
|
||||
InitializeOrderItemDtos(orderItemDtos);
|
||||
}
|
||||
|
||||
public virtual void InitializeOrderItemDtos(List<TOrderItemDto> orderItemDtos)
|
||||
{
|
||||
OrderItemDtos = orderItemDtos;
|
||||
}
|
||||
|
||||
public virtual Order CreateMainEntity()
|
||||
{
|
||||
//base.CreateMainEntity();
|
||||
|
||||
var order = new Order();
|
||||
CopyDtoValuesToEntity(order);
|
||||
|
||||
return order;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
using AyCode.Core.Extensions;
|
||||
using AyCode.Core.Serializers.Toons;
|
||||
using AyCode.Core.Helpers;
|
||||
using LinqToDB.Mapping;
|
||||
using Mango.Nop.Core.Entities;
|
||||
using Mango.Nop.Core.Interfaces;
|
||||
using Nop.Core;
|
||||
using Nop.Core.Domain.Orders;
|
||||
|
||||
namespace Mango.Nop.Core.Dtos;
|
||||
|
||||
[ToonDescription("Base DTO for order items with product association")]
|
||||
public abstract class MgOrderItemDto<TProductDto> : MgEntityBase, IModelDtoBase<OrderItem>, IMgOrderItemDto<TProductDto> where TProductDto : IMgProductDto
|
||||
{
|
||||
public Guid OrderItemGuid { get; set; }
|
||||
public int OrderId { get; set; }
|
||||
public int ProductId { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
|
||||
public decimal UnitPriceInclTax { get; set; }
|
||||
public decimal UnitPriceExclTax { get; set; }
|
||||
|
||||
public decimal PriceInclTax { get; set; }
|
||||
public decimal PriceExclTax { get; set; }
|
||||
|
||||
public string AttributesXml { get; set; }
|
||||
public decimal? ItemWeight { get; set; }
|
||||
|
||||
[ToonDescription(BusinessRule = "get => ProductDto?.Name ?? 'ProductDto is null!!!'")]
|
||||
public string ProductName => ProductDto?.Name ?? "ProductDto is null!!!";
|
||||
|
||||
[Association(ThisKey = nameof(ProductId), OtherKey = nameof(BaseEntity.Id), CanBeNull = true)]
|
||||
public TProductDto? ProductDto { get; set; }
|
||||
|
||||
protected MgOrderItemDto() :base()
|
||||
{ }
|
||||
|
||||
protected MgOrderItemDto(int orderItemId)
|
||||
{
|
||||
Id = orderItemId;
|
||||
}
|
||||
|
||||
protected MgOrderItemDto(OrderItem orderItem)
|
||||
{
|
||||
CopyEntityValuesToDto(orderItem);
|
||||
}
|
||||
|
||||
public virtual void CopyDtoValuesToEntity(OrderItem entity)
|
||||
{
|
||||
PropertyHelper.CopyPublicValueTypeProperties(this, entity);
|
||||
}
|
||||
|
||||
public virtual void CopyEntityValuesToDto(OrderItem entity)
|
||||
{
|
||||
PropertyHelper.CopyPublicValueTypeProperties(entity, this);
|
||||
}
|
||||
|
||||
public virtual void CopyEntityValuesToDto(OrderItem entity, TProductDto productDto)
|
||||
{
|
||||
CopyEntityValuesToDto(entity);
|
||||
|
||||
InitializeProductDto(productDto);
|
||||
}
|
||||
|
||||
public virtual void InitializeProductDto(TProductDto productDto)
|
||||
{
|
||||
ProductDto = productDto;
|
||||
}
|
||||
|
||||
public virtual OrderItem CreateMainEntity()
|
||||
{
|
||||
//base.CreateMainEntity();
|
||||
|
||||
var orderItem = new OrderItem();
|
||||
CopyDtoValuesToEntity(orderItem);
|
||||
|
||||
return orderItem;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
using AyCode.Interfaces.Entities;
|
||||
using AyCode.Core.Serializers.Toons;
|
||||
using Mango.Nop.Core.Entities;
|
||||
using Mango.Nop.Core.Interfaces;
|
||||
//using Nop.Core.Domain.Catalog;
|
||||
|
||||
namespace Mango.Nop.Core.Dtos;
|
||||
|
||||
[ToonDescription("Base DTO for products with warehouse and pricing")]
|
||||
public abstract class MgProductDto : MgEntityBase, /*Product,*/ IMgProductDto//IModelDtoBase<Product>//, IDiscountSupported<DiscountProductMapping>
|
||||
{
|
||||
//public int Id { get; set; }
|
||||
public int ProductTypeId { get; set; }
|
||||
public int ParentGroupedProductId { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
public string ShortDescription { get; set; }
|
||||
public string FullDescription { get; set; }
|
||||
|
||||
public string Sku { get; set; }
|
||||
|
||||
public int WarehouseId { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
public int StockQuantity { get; set; }
|
||||
public decimal ProductCost { get; set; }
|
||||
|
||||
public decimal Weight { get; set; }
|
||||
public decimal Length { get; set; }
|
||||
public decimal Width { get; set; }
|
||||
public decimal Height { get; set; }
|
||||
|
||||
public bool Deleted { get; set; }
|
||||
|
||||
public bool SubjectToAcl { get; set; }
|
||||
public bool LimitedToStores { get; set; }
|
||||
|
||||
protected MgProductDto() :base()
|
||||
{ }
|
||||
|
||||
protected MgProductDto(int productId)
|
||||
{
|
||||
Id = productId;
|
||||
}
|
||||
|
||||
//protected MgProductDto(Product product)
|
||||
//{
|
||||
// CopyEntityValuesToDto(product);
|
||||
//}
|
||||
|
||||
//public virtual void CopyDtoValuesToEntity(Product entity)
|
||||
//{
|
||||
// entity.Id = Id;
|
||||
|
||||
// entity.ProductTypeId = ProductTypeId;
|
||||
// entity.ParentGroupedProductId = ParentGroupedProductId;
|
||||
|
||||
// entity.Name = Name;
|
||||
// entity.ShortDescription = ShortDescription;
|
||||
// entity.FullDescription = FullDescription;
|
||||
|
||||
// entity.WarehouseId = WarehouseId;
|
||||
// entity.StockQuantity = StockQuantity;
|
||||
|
||||
// entity.Weight = Weight;
|
||||
// entity.Length = Length;
|
||||
// entity.Width = Width;
|
||||
// entity.Height = Height;
|
||||
|
||||
// entity.Deleted = Deleted;
|
||||
//}
|
||||
|
||||
//public virtual void CopyEntityValuesToDto(Product entity)
|
||||
//{
|
||||
// Id = entity.Id;
|
||||
// ProductTypeId = entity.ProductTypeId;
|
||||
// ParentGroupedProductId = entity.ParentGroupedProductId;
|
||||
|
||||
// Name = entity.Name;
|
||||
// ShortDescription = entity.ShortDescription;
|
||||
// FullDescription = entity.FullDescription;
|
||||
|
||||
// WarehouseId = entity.WarehouseId;
|
||||
// StockQuantity = entity.StockQuantity;
|
||||
|
||||
// Weight = entity.Weight;
|
||||
// Length = entity.Length;
|
||||
// Width = entity.Width;
|
||||
// Height = entity.Height;
|
||||
|
||||
// Deleted = entity.Deleted;
|
||||
//}
|
||||
|
||||
//public virtual Product CreateMainEntity()
|
||||
//{
|
||||
// //base.CreateMainEntity();
|
||||
|
||||
// var product = new Product();
|
||||
// CopyDtoValuesToEntity(product);
|
||||
|
||||
// return product;
|
||||
//}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
using AyCode.Interfaces.Entities;
|
||||
using AyCode.Core.Serializers.Toons;
|
||||
using LinqToDB.Mapping;
|
||||
using Mango.Nop.Core.Entities;
|
||||
using Mango.Nop.Core.Interfaces;
|
||||
using Nop.Core.Domain.Catalog;
|
||||
using Nop.Core.Domain.Common;
|
||||
using Nop.Core.Domain.Orders;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Mango.Nop.Core.Dtos
|
||||
{
|
||||
public interface IMgTStockQuantityHistoryDto<TProductDto> : IEntityInt, IMgStockQuantityHistory where TProductDto : IMgProductDto
|
||||
{
|
||||
public TProductDto ProductDto { get; set; }
|
||||
}
|
||||
|
||||
[ToonDescription("Base DTO for stock quantity history with product")]
|
||||
public abstract class MgStockQuantityHistoryDto<TProductDto> : MgEntityBase, IModelDtoBase<StockQuantityHistory>,
|
||||
IMgTStockQuantityHistoryDto<TProductDto> where TProductDto : IMgProductDto
|
||||
{
|
||||
public int ProductId { get; set; }
|
||||
public int QuantityAdjustment { get; set; }
|
||||
|
||||
public int StockQuantity { get; set; }
|
||||
|
||||
public string Message { get; set; }
|
||||
|
||||
public int? CombinationId { get; set; }
|
||||
|
||||
public int? WarehouseId { get; set; }
|
||||
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
|
||||
[Association(ThisKey = nameof(ProductId), OtherKey = nameof(IMgProductDto.Id), CanBeNull = false)]
|
||||
public TProductDto ProductDto { get; set; }
|
||||
|
||||
public void CopyDtoValuesToEntity(Order entity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void CopyDtoValuesToEntity(StockQuantityHistory entity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void CopyEntityValuesToDto(Order entity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void CopyEntityValuesToDto(StockQuantityHistory entity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Order CreateMainEntity()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
StockQuantityHistory IModelDtoBase<StockQuantityHistory>.CreateMainEntity()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
using AyCode.Core.Interfaces;
|
||||
using Nop.Core;
|
||||
|
||||
namespace Mango.Nop.Core.Dtos;
|
||||
|
||||
public abstract class ModelDtoBase : IModelDtoBase
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
protected ModelDtoBase(){}
|
||||
protected ModelDtoBase(int id) => Id = id;
|
||||
}
|
||||
|
||||
public abstract class ModelDtoBase<TMainEntity> : ModelDtoBase, IModelDtoBase<TMainEntity> where TMainEntity : BaseEntity
|
||||
{
|
||||
protected ModelDtoBase() : base()
|
||||
{
|
||||
}
|
||||
|
||||
protected ModelDtoBase(int id) : base(id)
|
||||
{
|
||||
}
|
||||
|
||||
protected ModelDtoBase(TMainEntity mainEntity)
|
||||
{
|
||||
CopyEntityValuesToDto(mainEntity);
|
||||
}
|
||||
|
||||
public virtual TMainEntity CreateMainEntity()
|
||||
{
|
||||
var mainEntity = Activator.CreateInstance<TMainEntity>();
|
||||
CopyDtoValuesToEntity(mainEntity);
|
||||
|
||||
return mainEntity;
|
||||
}
|
||||
|
||||
public virtual void CopyDtoValuesToEntity(TMainEntity entity)
|
||||
{
|
||||
entity.Id = Id;
|
||||
}
|
||||
|
||||
public virtual void CopyEntityValuesToDto(TMainEntity entity)
|
||||
{
|
||||
Id = entity.Id;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,14 @@
|
|||
using AyCode.Interfaces.Entities;
|
||||
using AyCode.Core.Serializers.Toons;
|
||||
using Nop.Core;
|
||||
|
||||
namespace Mango.Nop.Core.Entities;
|
||||
|
||||
public class MgEntityBase : BaseEntity, IEntityInt
|
||||
[ToonDescription("Base entity class with Id property and ToString override")]
|
||||
public abstract class MgEntityBase : BaseEntity, IEntityInt
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{GetType().Name}; Id: {Id}";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
using System.ComponentModel.Design;
|
||||
using AyCode.Core.Serializers.Toons;
|
||||
using AyCode.Interfaces.Entities;
|
||||
using AyCode.Interfaces.TimeStampInfo;
|
||||
using LinqToDB.Mapping;
|
||||
|
||||
namespace Mango.Nop.Core.Entities;
|
||||
|
||||
public interface IMgStockTaking : IEntityInt, ITimeStampInfo
|
||||
{
|
||||
public DateTime StartDateTime { get; set; }
|
||||
public bool IsClosed { get; set; }
|
||||
|
||||
public bool IsReadyForClose();
|
||||
}
|
||||
|
||||
[ToonDescription("Base entity for stock taking sessions with items")]
|
||||
public abstract class MgStockTaking<TStockTakingItem> : MgEntityBase, IMgStockTaking where TStockTakingItem : class, IMgStockTakingItem
|
||||
{
|
||||
public DateTime StartDateTime { get; set; }
|
||||
public bool IsClosed { get; set; }
|
||||
|
||||
public abstract bool IsReadyForClose();
|
||||
|
||||
[Association(ThisKey = nameof(Id), OtherKey = nameof(IMgStockTakingItem.StockTakingId), CanBeNull = true)]
|
||||
public List<TStockTakingItem>? StockTakingItems { get; set; }
|
||||
|
||||
public int Creator { get; set; }
|
||||
public DateTime Created { get; set; }
|
||||
public DateTime Modified { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
using AyCode.Interfaces.Entities;
|
||||
using AyCode.Core.Serializers.Toons;
|
||||
using AyCode.Interfaces.TimeStampInfo;
|
||||
using LinqToDB;
|
||||
using LinqToDB.Mapping;
|
||||
using Mango.Nop.Core.Dtos;
|
||||
using Mango.Nop.Core.Interfaces;
|
||||
|
||||
namespace Mango.Nop.Core.Entities;
|
||||
|
||||
public interface IMgStockTakingItem : IEntityInt, ITimeStampInfo
|
||||
{
|
||||
public int StockTakingId { get; set; }
|
||||
public int ProductId { get; set; }
|
||||
|
||||
public bool IsMeasured { get; set; }
|
||||
|
||||
public int OriginalStockQuantity { get; set; }
|
||||
public int MeasuredStockQuantity { get; set; }
|
||||
}
|
||||
|
||||
[ToonDescription("Base entity for stock taking items with product association")]
|
||||
public abstract class MgStockTakingItem<TStockTaking, TProduct> : MgEntityBase, IMgStockTakingItem
|
||||
where TStockTaking : class, IMgStockTaking where TProduct : class, IMgProductDto
|
||||
{
|
||||
public int StockTakingId { get; set; }
|
||||
public int ProductId { get; set; }
|
||||
|
||||
public bool IsMeasured { get; set; }
|
||||
|
||||
public int OriginalStockQuantity { get; set; }
|
||||
public int MeasuredStockQuantity { get; set; }
|
||||
|
||||
[Association(ThisKey = nameof(StockTakingId), OtherKey = nameof(IMgStockTaking.Id), CanBeNull = true)]
|
||||
public TStockTaking? StockTaking{ get; set; }
|
||||
|
||||
[Association(ThisKey = nameof(ProductId), OtherKey = nameof(IMgProductDto.Id), CanBeNull = true)]
|
||||
public TProduct? Product { get; set; }
|
||||
|
||||
public DateTime Created { get; set; }
|
||||
public DateTime Modified { get; set; }
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
using AyCode.Core.Enums;
|
||||
using AyCode.Core.Extensions;
|
||||
using Nop.Core;
|
||||
|
||||
namespace Mango.Nop.Core.Extensions;
|
||||
|
||||
public static class CollectionExtensionsNopBaseEntity
|
||||
{
|
||||
public static void UpdateBaseEntityCollection<TDataItem>(this IList<TDataItem> source, IList<TDataItem> dataItems, bool isRemove) where TDataItem : BaseEntity
|
||||
{
|
||||
if (source == null) throw new ArgumentNullException(nameof(source), $"source == null");
|
||||
if (dataItems == null) throw new ArgumentNullException(nameof(dataItems), $"dataItems == null");
|
||||
|
||||
foreach (var dataItem in dataItems)
|
||||
{
|
||||
source.UpdateBaseEntityCollection(dataItem, isRemove);
|
||||
}
|
||||
}
|
||||
|
||||
public static TrackingState UpdateBaseEntityCollection<TDataItem>(this IList<TDataItem> source, TDataItem dataItem, bool isRemove) where TDataItem : BaseEntity
|
||||
{
|
||||
if (source == null) throw new ArgumentNullException(nameof(source), $"source == null");
|
||||
|
||||
var index = source.FindIndex(x => x.Id == dataItem.Id);
|
||||
return source.UpdateCollectionByIndex(index, dataItem, isRemove);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AyCode.Utils.Extensions;
|
||||
using Mango.Nop.Core.Utils;
|
||||
using Nop.Core.Domain.Common;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace Mango.Nop.Core.Extensions;
|
||||
|
||||
public static class GenericAttributeExtensions
|
||||
{
|
||||
public static TValue? GetValueOrNull<TValue>(this IEnumerable<GenericAttribute> src, string key) where TValue : struct
|
||||
{
|
||||
var ga = src.SingleOrDefault(x => x.Key == key);
|
||||
if (ga == null || ga.Value.IsNullOrWhiteSpace()) return null;
|
||||
|
||||
return CommonHelper2.To<TValue>(ga.Value);
|
||||
}
|
||||
|
||||
public static TValue GetValueOrDefault<TValue>(this IEnumerable<GenericAttribute> src, string key, TValue defaultValue = default) where TValue : struct
|
||||
{
|
||||
var gaValue = GetValueOrNull<TValue>(src, key);
|
||||
return gaValue == null ? defaultValue : CommonHelper2.To<TValue>(gaValue);
|
||||
}
|
||||
|
||||
public static bool TryGetValue<TValue>(this IEnumerable<GenericAttribute> src, string key, [NotNullWhen(true)] out TValue? value) where TValue : struct
|
||||
{
|
||||
value = null;
|
||||
|
||||
var gaValue = GetValueOrNull<TValue>(src, key);
|
||||
if (gaValue == null) return false;
|
||||
|
||||
value = CommonHelper2.To<TValue>(gaValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static GenericAttribute AddNewGenericAttribute(this ICollection<GenericAttribute> src, string keyGroup, string key, string value, int entityId, int storeId)
|
||||
{
|
||||
var ga = new GenericAttribute
|
||||
{
|
||||
KeyGroup = keyGroup,
|
||||
Key = key,
|
||||
Value = value,
|
||||
EntityId = entityId,
|
||||
StoreId = storeId,
|
||||
CreatedOrUpdatedDateUTC = DateTime.UtcNow
|
||||
};
|
||||
|
||||
src.Add(ga);
|
||||
return ga;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using AyCode.Core.Interfaces;
|
||||
using AyCode.Interfaces;
|
||||
|
||||
namespace Mango.Nop.Core.Interfaces.ForeignKeys;
|
||||
|
||||
public interface ICustomerForeignKey : IForeignKey
|
||||
{
|
||||
int CustomerId { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
using System.Collections.Generic;
|
||||
using AyCode.Core.Interfaces;
|
||||
using Nop.Core.Domain.Common;
|
||||
|
||||
namespace Mango.Nop.Core.Interfaces.ForeignKeys;
|
||||
|
||||
public interface IGenericAttributeForeignList : IGenericAttributeForeignCollection<List<GenericAttribute>>
|
||||
{
|
||||
}
|
||||
|
||||
public interface IGenericAttributeForeignCollection<T> : IForeignCollection<T> where T : IEnumerable<GenericAttribute>
|
||||
{
|
||||
T GenericAttributes { get; }
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
namespace Mango.Nop.Core.Interfaces;
|
||||
|
||||
public interface IMgDbContextBase //: IAcDbContextBase
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
using AyCode.Interfaces.Entities;
|
||||
using Mango.Nop.Core.Dtos;
|
||||
using Nop.Core.Domain.Common;
|
||||
using Nop.Core.Domain.Customers;
|
||||
using Nop.Core.Domain.Orders;
|
||||
using Nop.Core.Domain.Shipping;
|
||||
|
||||
namespace Mango.Nop.Core.Interfaces;
|
||||
|
||||
public interface IMgOrderDto<TOrderItemDto, TProductDto> : IEntityInt, ISoftDeletedEntity where TOrderItemDto : IMgOrderItemDto<TProductDto> where TProductDto : IMgProductDto
|
||||
{
|
||||
public Guid OrderGuid { get; set; }
|
||||
public int StoreId { get; set; }
|
||||
|
||||
public int CustomerId { get; set; }
|
||||
|
||||
public int OrderStatusId { get; set; }
|
||||
public OrderStatus OrderStatus { get; set; }
|
||||
|
||||
public int ShippingStatusId { get; set; }
|
||||
public ShippingStatus ShippingStatus { get; set; }
|
||||
|
||||
public decimal OrderDiscount { get; set; }
|
||||
|
||||
public decimal OrderTotal { get; set; }
|
||||
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
public DateTime? PaidDateUtc { get; set; }
|
||||
|
||||
public string ShippingMethod { get; set; }
|
||||
|
||||
public string CustomOrderNumber { get; set; }
|
||||
public string CustomValuesXml { get; set; }
|
||||
|
||||
public Customer Customer { get; } //TODO: CustomerDto!!! - J.
|
||||
public List<TOrderItemDto> OrderItemDtos { get; }
|
||||
public List<OrderNote> OrderNotes { get; }
|
||||
|
||||
void InitializeOrderItemDtos(List<TOrderItemDto> orderItemDtos);
|
||||
void CopyEntityValuesToDto(Order entity, List<TOrderItemDto> orderItemDtos);
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
using AyCode.Interfaces.Entities;
|
||||
using Nop.Core.Domain.Orders;
|
||||
|
||||
namespace Mango.Nop.Core.Interfaces;
|
||||
|
||||
public interface IMgOrderItemDto<TProductDto> : IEntityInt where TProductDto : IMgProductDto
|
||||
{
|
||||
public Guid OrderItemGuid { get; set; }
|
||||
|
||||
public int OrderId { get; set; }
|
||||
|
||||
public int ProductId { get; set; }
|
||||
|
||||
public int Quantity { get; set; }
|
||||
|
||||
public string AttributesXml { get; set; }
|
||||
public decimal? ItemWeight { get; set; }
|
||||
|
||||
|
||||
public string ProductName { get; }
|
||||
public TProductDto? ProductDto { get; set; }
|
||||
|
||||
void InitializeProductDto(TProductDto productDto);
|
||||
void CopyEntityValuesToDto(OrderItem entity, TProductDto productDto);
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
using AyCode.Interfaces.Entities;
|
||||
using Nop.Core.Domain.Common;
|
||||
using Nop.Core.Domain.Localization;
|
||||
using Nop.Core.Domain.Security;
|
||||
using Nop.Core.Domain.Seo;
|
||||
using Nop.Core.Domain.Stores;
|
||||
|
||||
namespace Mango.Nop.Core.Interfaces;
|
||||
|
||||
public interface IMgProductDto : IEntityInt, ILocalizedEntity, ISlugSupported, IAclSupported, IStoreMappingSupported/*, IDiscountSupported<DiscountProductMapping>*/, ISoftDeletedEntity
|
||||
{
|
||||
int ProductTypeId { get; set; }
|
||||
int ParentGroupedProductId { get; set; }
|
||||
|
||||
string Name { get; set; }
|
||||
string ShortDescription { get; set; }
|
||||
string FullDescription { get; set; }
|
||||
|
||||
string Sku { get; set; }
|
||||
int WarehouseId { get; set; }
|
||||
decimal Price { get; set; }
|
||||
int StockQuantity { get; set; }
|
||||
decimal ProductCost { get; set; }
|
||||
|
||||
decimal Weight { get; set; }
|
||||
decimal Length { get; set; }
|
||||
decimal Width { get; set; }
|
||||
decimal Height { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
using AyCode.Core.Loggers;
|
||||
|
||||
namespace Mango.Nop.Core.Loggers;
|
||||
|
||||
public interface ILogger<TCategory> : ILogger
|
||||
{
|
||||
|
||||
}
|
||||
public interface ILogger : IAcLoggerBase
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
using AyCode.Core.Enums;
|
||||
using AyCode.Core.Loggers;
|
||||
|
||||
namespace Mango.Nop.Core.Loggers;
|
||||
|
||||
public class Logger<TCategory> : Logger, ILogger<TCategory>
|
||||
{
|
||||
//public Logger() : base(typeof(TCategory).Name)
|
||||
//{ }
|
||||
|
||||
public Logger(params IAcLogWriterBase[] logWriters) : base(typeof(TCategory).Name, logWriters)
|
||||
{ }
|
||||
|
||||
public Logger(AppType appType, LogLevel logLevel, params IAcLogWriterBase[] logWriters) : base(appType, logLevel, typeof(TCategory).Name, logWriters)
|
||||
{ }
|
||||
}
|
||||
|
||||
public class Logger : AcLoggerBase, ILogger
|
||||
{
|
||||
public Logger(params IAcLogWriterBase[] logWriters) : this(null, logWriters)
|
||||
{ }
|
||||
|
||||
public Logger(string? categoryName) : base(categoryName)
|
||||
{ }
|
||||
|
||||
public Logger(string? categoryName, params IAcLogWriterBase[] logWriters) : base(categoryName, logWriters)
|
||||
{ }
|
||||
|
||||
public Logger(AppType appType, LogLevel logLevel, string? categoryName, params IAcLogWriterBase[] logWriters) : base(appType, logLevel, categoryName, logWriters)
|
||||
{ }
|
||||
}
|
||||
|
|
@ -1,44 +1,60 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<BaseOutputPath>bin\FruitBank</BaseOutputPath>
|
||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<CopyRefAssembliesToPublishDirectory>true</CopyRefAssembliesToPublishDirectory>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MessagePack.Annotations" Version="2.5.192" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="linq2db" Version="5.4.1" />
|
||||
<PackageReference Include="MessagePack" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.11" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\NopCommerce\Libraries\Nop.Core\Nop.Core.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\NopCommerce\Libraries\Nop.Data\Nop.Data.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\NopCommerce\Libraries\Nop.Services\Nop.Services.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="AyCode.Core">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Core.Server">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Core.Server.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Entities">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Entities.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Entities.Server">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Entities.Server.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Interfaces">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Interfaces.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Interfaces.Server">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Interfaces.Server.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Utils">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Utils.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="AyCode.Core">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\Debug\net8.0\AyCode.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Core.Server">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\Debug\net8.0\AyCode.Core.Server.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Entities">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\Debug\net8.0\AyCode.Entities.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Entities.Server">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\Debug\net8.0\AyCode.Entities.Server.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Interfaces">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\Debug\net8.0\AyCode.Interfaces.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Interfaces.Server">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\Debug\net8.0\AyCode.Interfaces.Server.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Utils">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\Debug\net8.0\AyCode.Utils.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Core.Serializers.SourceGenerator\AyCode.Core.Serializers.SourceGenerator.csproj"
|
||||
OutputItemType="Analyzer"
|
||||
ReferenceOutputAssembly="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="ExtendedFactories\" />
|
||||
<Folder Include="ExtendedModels\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="docs\**\*.md" />
|
||||
<None Include="**\README.md" Exclude="$(DefaultItemExcludes);docs\**" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
using AyCode.Interfaces;
|
||||
using Mango.Nop.Core.Interfaces;
|
||||
|
||||
namespace Mango.Nop.Core.Models;
|
||||
|
||||
public class MgLoginModelRequest : IAcModelDtoBaseEmpty
|
||||
{
|
||||
public MgLoginModelRequest(){}
|
||||
public MgLoginModelRequest(string email, string password)
|
||||
{
|
||||
Email = email;
|
||||
Password = password;
|
||||
}
|
||||
|
||||
public string Email { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
using AyCode.Interfaces;
|
||||
using AyCode.Utils.Extensions;
|
||||
using Mango.Nop.Core.Dtos;
|
||||
using Nop.Core.Domain.Customers;
|
||||
|
||||
namespace Mango.Nop.Core.Models;
|
||||
|
||||
public class MgLoginModelResponse : IAcModelDtoBaseEmpty
|
||||
{
|
||||
public CustomerDto? CustomerDto { get; set; }
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
public MgLoginModelResponse()
|
||||
{ }
|
||||
public MgLoginModelResponse(Customer customer, string errorMessage) : this(new CustomerDto(customer), errorMessage)
|
||||
{ }
|
||||
public MgLoginModelResponse(CustomerDto customerDto, string errorMessage)
|
||||
{
|
||||
CustomerDto = customerDto;
|
||||
ErrorMessage = errorMessage;
|
||||
}
|
||||
|
||||
public bool IsSuccesLogin => CustomerDto != null && ErrorMessage.IsNullOrWhiteSpace();
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AyCode.Core.Consts;
|
||||
|
||||
namespace Mango.Nop.Core
|
||||
{
|
||||
public abstract class NopCommonConst: AcConst
|
||||
{
|
||||
//public static string DefaultLocale = "en-US";
|
||||
//public static string BaseUrl = "https://localhost:7144";
|
||||
|
||||
//public static string LoggerHubName = "loggerHub";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a backorder mode
|
||||
/// </summary>
|
||||
public enum BackorderMode
|
||||
{
|
||||
/// <summary>
|
||||
/// No backorders
|
||||
/// </summary>
|
||||
NoBackorders = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Allow qty below 0
|
||||
/// </summary>
|
||||
AllowQtyBelow0 = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Allow qty below 0 and notify customer
|
||||
/// </summary>
|
||||
AllowQtyBelow0AndNotifyCustomer = 2,
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
//namespace Nop.Core.Domain.Common;
|
||||
using AyCode.Core.Serializers.Toons;
|
||||
|
||||
|
||||
using AyCode.Interfaces.Entities;
|
||||
|
||||
namespace Nop.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the base class for entities
|
||||
/// </summary>
|
||||
public abstract partial class BaseEntity : IBaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the entity identifier
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{GetType().Name} [Id: {Id}]";
|
||||
}
|
||||
}
|
||||
|
||||
public interface IBaseEntity //: IEntityInt
|
||||
{
|
||||
public int Id { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,275 @@
|
|||
using AyCode.Core.Serializers.Attributes;
|
||||
using AyCode.Core.Serializers.Toons;
|
||||
using Nop.Core.Domain.Common;
|
||||
using Nop.Core.Domain.Tax;
|
||||
|
||||
namespace Nop.Core.Domain.Customers;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a customer
|
||||
/// </summary>
|
||||
[AcBinarySerializable(false, true, false, true, false, false)]
|
||||
[ToonDescription("NopCommerce customer entity")]
|
||||
public partial class Customer : BaseEntity, ISoftDeletedEntity
|
||||
{
|
||||
public Customer()
|
||||
{
|
||||
CustomerGuid = Guid.NewGuid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the customer GUID
|
||||
/// </summary>
|
||||
public Guid CustomerGuid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the username
|
||||
/// </summary>
|
||||
//[ToonIgnore]
|
||||
public string Username { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the email
|
||||
/// </summary>
|
||||
//[ToonIgnore]
|
||||
public string Email { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the first name
|
||||
/// </summary>
|
||||
//[ToonIgnore]
|
||||
public string FirstName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last name
|
||||
/// </summary>
|
||||
//[ToonIgnore]
|
||||
public string LastName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the gender
|
||||
/// </summary>
|
||||
public string Gender { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date of birth
|
||||
/// </summary>
|
||||
public DateTime? DateOfBirth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the company
|
||||
/// </summary>
|
||||
public string Company { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the street address
|
||||
/// </summary>
|
||||
public string StreetAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the street address 2
|
||||
/// </summary>
|
||||
public string StreetAddress2 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the zip
|
||||
/// </summary>
|
||||
public string ZipPostalCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the city
|
||||
/// </summary>
|
||||
public string City { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the county
|
||||
/// </summary>
|
||||
public string County { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the country id
|
||||
/// </summary>
|
||||
public int CountryId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the state province id
|
||||
/// </summary>
|
||||
public int StateProvinceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the phone number
|
||||
/// </summary>
|
||||
public string Phone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the fax
|
||||
/// </summary>
|
||||
public string Fax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vat number
|
||||
/// </summary>
|
||||
public string VatNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vat number status id
|
||||
/// </summary>
|
||||
public int VatNumberStatusId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time zone id
|
||||
/// </summary>
|
||||
public string TimeZoneId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the custom attributes
|
||||
/// </summary>
|
||||
public string CustomCustomerAttributesXML { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currency id
|
||||
/// </summary>
|
||||
public int? CurrencyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the language id
|
||||
/// </summary>
|
||||
public int? LanguageId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tax display type id
|
||||
/// </summary>
|
||||
public int? TaxDisplayTypeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the email that should be re-validated. Used in scenarios when a customer is already registered and wants to change an email address.
|
||||
/// </summary>
|
||||
public string EmailToRevalidate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the admin comment
|
||||
/// </summary>
|
||||
public string AdminComment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the customer is tax exempt
|
||||
/// </summary>
|
||||
public bool IsTaxExempt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the affiliate identifier
|
||||
/// </summary>
|
||||
public int AffiliateId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vendor identifier with which this customer is associated (manager)
|
||||
/// </summary>
|
||||
public int VendorId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this customer has some products in the shopping cart
|
||||
/// <remarks>The same as if we run ShoppingCartItems.Count > 0
|
||||
/// We use this property for performance optimization:
|
||||
/// if this property is set to false, then we do not need to load "ShoppingCartItems" navigation property for each page load
|
||||
/// It's used only in a couple of places in the presentation layer
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
public bool HasShoppingCartItems { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the customer is required to re-login
|
||||
/// </summary>
|
||||
public bool RequireReLogin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating number of failed login attempts (wrong password)
|
||||
/// </summary>
|
||||
public int FailedLoginAttempts { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time until which a customer cannot login (locked out)
|
||||
/// </summary>
|
||||
public DateTime? CannotLoginUntilDateUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the customer is active
|
||||
/// </summary>
|
||||
public bool Active { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the customer has been deleted
|
||||
/// </summary>
|
||||
public bool Deleted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the customer account is system
|
||||
/// </summary>
|
||||
public bool IsSystemAccount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the customer system name
|
||||
/// </summary>
|
||||
public string SystemName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last IP address
|
||||
/// </summary>
|
||||
public string LastIpAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of entity creation
|
||||
/// </summary>
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of last login
|
||||
/// </summary>
|
||||
public DateTime? LastLoginDateUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of last activity
|
||||
/// </summary>
|
||||
public DateTime LastActivityDateUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the store identifier in which customer registered
|
||||
/// </summary>
|
||||
public int RegisteredInStoreId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the billing address identifier
|
||||
/// </summary>
|
||||
public int? BillingAddressId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the customer is required to change password
|
||||
/// </summary>
|
||||
public bool MustChangePassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the shipping address identifier
|
||||
/// </summary>
|
||||
public int? ShippingAddressId { get; set; }
|
||||
|
||||
#region Custom properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vat number status
|
||||
/// </summary>
|
||||
public VatNumberStatus VatNumberStatus
|
||||
{
|
||||
get => (VatNumberStatus)VatNumberStatusId;
|
||||
set => VatNumberStatusId = (int)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tax display type
|
||||
/// </summary>
|
||||
public TaxDisplayType? TaxDisplayType
|
||||
{
|
||||
get => TaxDisplayTypeId.HasValue ? (TaxDisplayType)TaxDisplayTypeId : null;
|
||||
set => TaxDisplayTypeId = value.HasValue ? (int)value : null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
using AyCode.Core.Serializers.Attributes;
|
||||
|
||||
namespace Nop.Core.Domain.Customers;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a customer role
|
||||
/// </summary>
|
||||
[AcBinarySerializable(false, true, false, true, false, false)]
|
||||
public partial class CustomerRole : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the customer role name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the customer role is marked as free shipping
|
||||
/// </summary>
|
||||
public bool FreeShipping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the customer role is marked as tax exempt
|
||||
/// </summary>
|
||||
public bool TaxExempt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the customer role is active
|
||||
/// </summary>
|
||||
public bool Active { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the customer role is system
|
||||
/// </summary>
|
||||
public bool IsSystemRole { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the customer role system name
|
||||
/// </summary>
|
||||
public string SystemName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the customers must change passwords after a specified time
|
||||
/// </summary>
|
||||
public bool EnablePasswordLifetime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the customers of this role have other tax display type chosen instead of the default one
|
||||
/// </summary>
|
||||
public bool OverrideTaxDisplayType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets identifier of the default tax display type (used only with "OverrideTaxDisplayType" enabled)
|
||||
/// </summary>
|
||||
public int DefaultTaxDisplayTypeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a product identifier that is required by this customer role.
|
||||
/// A customer is added to this customer role once a specified product is purchased.
|
||||
/// </summary>
|
||||
public int PurchasedWithProductId { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
namespace Nop.Core.Domain.Discounts;
|
||||
|
||||
public abstract partial class DiscountMapping : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the entity identifier
|
||||
/// </summary>
|
||||
public new int Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the discount identifier
|
||||
/// </summary>
|
||||
public int DiscountId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the entity identifier
|
||||
/// </summary>
|
||||
public abstract int EntityId { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
using AyCode.Core.Serializers.Attributes;
|
||||
|
||||
namespace Nop.Core.Domain.Discounts;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a discount-product mapping class
|
||||
/// </summary>
|
||||
// SGen incompatible: DiscountMapping.Id is readonly (new int Id { get; }) — generated reader cannot set it (CS0200)
|
||||
//[AcBinarySerializable(false, true, false, true, false)]
|
||||
public partial class DiscountProductMapping : DiscountMapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// </summary>
|
||||
public override int EntityId { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
using AyCode.Core.Serializers.Attributes;
|
||||
using AyCode.Core.Serializers.Toons;
|
||||
|
||||
namespace Nop.Core.Domain.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a generic attribute
|
||||
/// </summary>
|
||||
[AcBinarySerializable(false, true, false, true, false, false)]
|
||||
[ToonDescription("NopCommerce generic attribute for key-value storage", Purpose = "A flexible key-value store used to extend entities with custom business logic data without changing the database schema")]
|
||||
public partial class GenericAttribute : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the entity identifier
|
||||
/// </summary>
|
||||
///
|
||||
[ToonDescription(Constraints = "polymorphic-fk(KeyGroup)")]
|
||||
public int EntityId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the key group
|
||||
/// </summary>
|
||||
public string KeyGroup { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the key
|
||||
/// </summary>
|
||||
public string Key { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Raw string representation of the Key's value")]
|
||||
public string Value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the store identifier
|
||||
/// </summary>
|
||||
public int StoreId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the created or updated date
|
||||
/// </summary>
|
||||
public DateTime? CreatedOrUpdatedDateUTC { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,344 @@
|
|||
using AyCode.Core.Serializers.Attributes;
|
||||
using AyCode.Core.Serializers.Toons;
|
||||
using Nop.Core.Domain.Common;
|
||||
using Nop.Core.Domain.Payments;
|
||||
using Nop.Core.Domain.Shipping;
|
||||
using Nop.Core.Domain.Tax;
|
||||
|
||||
namespace Nop.Core.Domain.Orders;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an order
|
||||
/// </summary>
|
||||
[AcBinarySerializable(false, true, false, true, false, false)]
|
||||
[ToonDescription("NopCommerce order entity with payment and shipping")]
|
||||
public partial class Order : BaseEntity, ISoftDeletedEntity
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order identifier
|
||||
/// </summary>
|
||||
public Guid OrderGuid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the store identifier
|
||||
/// </summary>
|
||||
public int StoreId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the customer identifier
|
||||
/// </summary>
|
||||
public int CustomerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the billing address identifier
|
||||
/// </summary>
|
||||
public int BillingAddressId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the shipping address identifier
|
||||
/// </summary>
|
||||
public int? ShippingAddressId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pickup address identifier
|
||||
/// </summary>
|
||||
public int? PickupAddressId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether a customer chose "pick up in store" shipping option
|
||||
/// </summary>
|
||||
public bool PickupInStore { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an order status identifier
|
||||
/// </summary>
|
||||
public int OrderStatusId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the shipping status identifier
|
||||
/// </summary>
|
||||
public int ShippingStatusId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the payment status identifier
|
||||
/// </summary>
|
||||
public int PaymentStatusId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the payment method system name
|
||||
/// </summary>
|
||||
public string PaymentMethodSystemName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the customer currency code (at the moment of order placing)
|
||||
/// </summary>
|
||||
public string CustomerCurrencyCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currency rate
|
||||
/// </summary>
|
||||
public decimal CurrencyRate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the customer tax display type identifier
|
||||
/// </summary>
|
||||
public int CustomerTaxDisplayTypeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the VAT number (the European Union Value Added Tax)
|
||||
/// </summary>
|
||||
public string VatNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order subtotal (include tax)
|
||||
/// </summary>
|
||||
public decimal OrderSubtotalInclTax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order subtotal (exclude tax)
|
||||
/// </summary>
|
||||
public decimal OrderSubtotalExclTax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order subtotal discount (include tax)
|
||||
/// </summary>
|
||||
public decimal OrderSubTotalDiscountInclTax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order subtotal discount (exclude tax)
|
||||
/// </summary>
|
||||
public decimal OrderSubTotalDiscountExclTax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order shipping (include tax)
|
||||
/// </summary>
|
||||
public decimal OrderShippingInclTax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order shipping (exclude tax)
|
||||
/// </summary>
|
||||
public decimal OrderShippingExclTax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the payment method additional fee (incl tax)
|
||||
/// </summary>
|
||||
public decimal PaymentMethodAdditionalFeeInclTax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the payment method additional fee (exclude tax)
|
||||
/// </summary>
|
||||
public decimal PaymentMethodAdditionalFeeExclTax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tax rates
|
||||
/// </summary>
|
||||
public string TaxRates { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order tax
|
||||
/// </summary>
|
||||
public decimal OrderTax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order discount (applied to order total)
|
||||
/// </summary>
|
||||
public decimal OrderDiscount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order total
|
||||
/// </summary>
|
||||
public decimal OrderTotal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the refunded amount
|
||||
/// </summary>
|
||||
public decimal RefundedAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the reward points history entry identifier when reward points were earned (gained) for placing this order
|
||||
/// </summary>
|
||||
public int? RewardPointsHistoryEntryId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the checkout attribute description
|
||||
/// </summary>
|
||||
public string CheckoutAttributeDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the checkout attributes in XML format
|
||||
/// </summary>
|
||||
public string CheckoutAttributesXml { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the customer language identifier
|
||||
/// </summary>
|
||||
public int CustomerLanguageId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the affiliate identifier
|
||||
/// </summary>
|
||||
public int AffiliateId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the customer IP address
|
||||
/// </summary>
|
||||
public string CustomerIp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether storing of credit card number is allowed
|
||||
/// </summary>
|
||||
public bool AllowStoringCreditCardNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the card type
|
||||
/// </summary>
|
||||
public string CardType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the card name
|
||||
/// </summary>
|
||||
public string CardName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the card number
|
||||
/// </summary>
|
||||
public string CardNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the masked credit card number
|
||||
/// </summary>
|
||||
public string MaskedCreditCardNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the card CVV2
|
||||
/// </summary>
|
||||
public string CardCvv2 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the card expiration month
|
||||
/// </summary>
|
||||
public string CardExpirationMonth { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the card expiration year
|
||||
/// </summary>
|
||||
public string CardExpirationYear { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the authorization transaction identifier
|
||||
/// </summary>
|
||||
public string AuthorizationTransactionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the authorization transaction code
|
||||
/// </summary>
|
||||
public string AuthorizationTransactionCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the authorization transaction result
|
||||
/// </summary>
|
||||
public string AuthorizationTransactionResult { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the capture transaction identifier
|
||||
/// </summary>
|
||||
public string CaptureTransactionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the capture transaction result
|
||||
/// </summary>
|
||||
public string CaptureTransactionResult { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the subscription transaction identifier
|
||||
/// </summary>
|
||||
public string SubscriptionTransactionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the paid date and time
|
||||
/// </summary>
|
||||
public DateTime? PaidDateUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the shipping method
|
||||
/// </summary>
|
||||
public string ShippingMethod { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the shipping rate computation method identifier or the pickup point provider identifier (if PickupInStore is true)
|
||||
/// </summary>
|
||||
public string ShippingRateComputationMethodSystemName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the serialized CustomValues (values from ProcessPaymentRequest)
|
||||
/// </summary>
|
||||
public string CustomValuesXml { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity has been deleted
|
||||
/// </summary>
|
||||
public bool Deleted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of order creation
|
||||
/// </summary>
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the custom order number without prefix
|
||||
/// </summary>
|
||||
public string CustomOrderNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the reward points history record (spent by a customer when placing this order)
|
||||
/// </summary>
|
||||
public virtual int? RedeemedRewardPointsEntryId { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Custom properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order status
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Enum wrapper", BusinessRule = "get, set => OrderStatusId")]
|
||||
public OrderStatus OrderStatus
|
||||
{
|
||||
get => (OrderStatus)OrderStatusId;
|
||||
set => OrderStatusId = (int)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the payment status
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Enum wrapper", BusinessRule = "get, set => PaymentStatusId")]
|
||||
public PaymentStatus PaymentStatus
|
||||
{
|
||||
get => (PaymentStatus)PaymentStatusId;
|
||||
set => PaymentStatusId = (int)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the shipping status
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Enum wrapper", BusinessRule = "get, set => ShippingStatusId")]
|
||||
public ShippingStatus ShippingStatus
|
||||
{
|
||||
get => (ShippingStatus)ShippingStatusId;
|
||||
set => ShippingStatusId = (int)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the customer tax display type
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Enum wrapper", BusinessRule = "get, set => CustomerTaxDisplayTypeId")]
|
||||
public TaxDisplayType CustomerTaxDisplayType
|
||||
{
|
||||
get => (TaxDisplayType)CustomerTaxDisplayTypeId;
|
||||
set => CustomerTaxDisplayTypeId = (int)value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
namespace Nop.Core.Domain.Orders;
|
||||
|
||||
using AyCode.Core.Serializers.Attributes;
|
||||
using AyCode.Core.Serializers.Toons;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents an order item
|
||||
/// </summary>
|
||||
[AcBinarySerializable(false, true, false, true, false, false)]
|
||||
[ToonDescription("NopCommerce order item entity")]
|
||||
public partial class OrderItem : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the order item identifier
|
||||
/// </summary>
|
||||
public Guid OrderItemGuid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order identifier
|
||||
/// </summary>
|
||||
public int OrderId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the quantity
|
||||
/// </summary>
|
||||
public int Quantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the unit price in primary store currency (include tax)
|
||||
/// </summary>
|
||||
public decimal UnitPriceInclTax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the unit price in primary store currency (exclude tax)
|
||||
/// </summary>
|
||||
public decimal UnitPriceExclTax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the price in primary store currency (include tax)
|
||||
/// </summary>
|
||||
public decimal PriceInclTax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the price in primary store currency (exclude tax)
|
||||
/// </summary>
|
||||
public decimal PriceExclTax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the discount amount (include tax)
|
||||
/// </summary>
|
||||
public decimal DiscountAmountInclTax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the discount amount (exclude tax)
|
||||
/// </summary>
|
||||
public decimal DiscountAmountExclTax { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the original cost of this order item (when an order was placed), qty 1
|
||||
/// </summary>
|
||||
public decimal OriginalProductCost { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute description
|
||||
/// </summary>
|
||||
public string AttributeDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the product attributes in XML format
|
||||
/// </summary>
|
||||
public string AttributesXml { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the download count
|
||||
/// </summary>
|
||||
public int DownloadCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether download is activated
|
||||
/// </summary>
|
||||
public bool IsDownloadActivated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a license download identifier (in case this is a downloadable product)
|
||||
/// </summary>
|
||||
public int? LicenseDownloadId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the total weight of one item
|
||||
/// It's nullable for compatibility with the previous version of nopCommerce where was no such property
|
||||
/// </summary>
|
||||
public decimal? ItemWeight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rental product start date (null if it's not a rental product)
|
||||
/// </summary>
|
||||
public DateTime? RentalStartDateUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rental product end date (null if it's not a rental product)
|
||||
/// </summary>
|
||||
public DateTime? RentalEndDateUtc { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
using AyCode.Core.Serializers.Attributes;
|
||||
using AyCode.Core.Serializers.Toons;
|
||||
|
||||
namespace Nop.Core.Domain.Orders;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an order note
|
||||
/// </summary>
|
||||
[AcBinarySerializable(false, true, false, true, false, false)]
|
||||
[ToonDescription("NopCommerce order note entity")]
|
||||
public partial class OrderNote : BaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the order identifier
|
||||
/// </summary>
|
||||
[ToonDescription($"Foreign key to parent {nameof(Order)}", ForeignKey = nameof(Order))]
|
||||
public int OrderId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the note
|
||||
/// </summary>
|
||||
public string Note { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attached file (download) identifier
|
||||
/// </summary>
|
||||
public int DownloadId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether a customer can see a note
|
||||
/// </summary>
|
||||
public bool DisplayToCustomer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of order note creation
|
||||
/// </summary>
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,618 @@
|
|||
using AyCode.Core.Serializers.Attributes;
|
||||
using AyCode.Core.Serializers.Toons;
|
||||
using Nop.Core.Domain.Common;
|
||||
using Nop.Core.Domain.Discounts;
|
||||
using Nop.Core.Domain.Localization;
|
||||
using Nop.Core.Domain.Security;
|
||||
using Nop.Core.Domain.Seo;
|
||||
using Nop.Core.Domain.Stores;
|
||||
|
||||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product
|
||||
/// </summary>
|
||||
[AcBinarySerializable(false, true, false, true, false, false)]
|
||||
[ToonDescription("Core nopCommerce product entity with catalog, pricing, and inventory management")]
|
||||
public partial class Product : BaseEntity, ILocalizedEntity, ISlugSupported, IAclSupported, IStoreMappingSupported, IDiscountSupported<DiscountProductMapping>, ISoftDeletedEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the product type identifier
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Defines if the product is Simple or Grouped", BusinessRule = "Enum: ProductType")]
|
||||
public int ProductTypeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parent product identifier. It's used to identify associated products (only with "grouped" products)
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Hierarchical link for grouped product variants", BusinessRule = "Foreign Key to Product.Id")]
|
||||
public int ParentGroupedProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the values indicating whether this product is visible in catalog or search results.
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Controls independent visibility in search/catalog", BusinessRule = "Used primarily for associated products")]
|
||||
public bool VisibleIndividually { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the short description
|
||||
/// </summary>
|
||||
public string ShortDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the full description
|
||||
/// </summary>
|
||||
public string FullDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the admin comment
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Internal administrative notes", BusinessRule = "Never exposed to customer")]
|
||||
public string AdminComment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value of used product template identifier
|
||||
/// </summary>
|
||||
public int ProductTemplateId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a vendor identifier
|
||||
/// </summary>
|
||||
public int VendorId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to show the product on home page
|
||||
/// </summary>
|
||||
public bool ShowOnHomepage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the meta keywords
|
||||
/// </summary>
|
||||
public string MetaKeywords { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the meta description
|
||||
/// </summary>
|
||||
public string MetaDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the meta title
|
||||
/// </summary>
|
||||
public string MetaTitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the product allows customer reviews
|
||||
/// </summary>
|
||||
public bool AllowCustomerReviews { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rating sum (approved reviews)
|
||||
/// </summary>
|
||||
public int ApprovedRatingSum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rating sum (not approved reviews)
|
||||
/// </summary>
|
||||
public int NotApprovedRatingSum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the total rating votes (approved reviews)
|
||||
/// </summary>
|
||||
public int ApprovedTotalReviews { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the total rating votes (not approved reviews)
|
||||
/// </summary>
|
||||
public int NotApprovedTotalReviews { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is subject to ACL
|
||||
/// </summary>
|
||||
public bool SubjectToAcl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is limited/restricted to certain stores
|
||||
/// </summary>
|
||||
public bool LimitedToStores { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the SKU
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Stock Keeping Unit", BusinessRule = "Unique alphanumeric identifier")]
|
||||
public string Sku { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the manufacturer part number
|
||||
/// </summary>
|
||||
public string ManufacturerPartNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Global Trade Item Number (GTIN).
|
||||
/// </summary>
|
||||
public string Gtin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the product is gift card
|
||||
/// </summary>
|
||||
public bool IsGiftCard { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the gift card type identifier
|
||||
/// </summary>
|
||||
public int GiftCardTypeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets gift card amount that can be used after purchase.
|
||||
/// </summary>
|
||||
public decimal? OverriddenGiftCardAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the product requires that other products are added to the cart
|
||||
/// </summary>
|
||||
public bool RequireOtherProducts { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a required product identifiers (comma separated)
|
||||
/// </summary>
|
||||
public string RequiredProductIds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether required products are automatically added to the cart
|
||||
/// </summary>
|
||||
public bool AutomaticallyAddRequiredProducts { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the product is download
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Digital good flag")]
|
||||
public bool IsDownload { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the download identifier
|
||||
/// </summary>
|
||||
public int DownloadId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this downloadable product can be downloaded unlimited number of times
|
||||
/// </summary>
|
||||
public bool UnlimitedDownloads { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum number of downloads
|
||||
/// </summary>
|
||||
public int MaxNumberOfDownloads { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of days during customers keeps access to the file.
|
||||
/// </summary>
|
||||
public int? DownloadExpirationDays { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the download activation type
|
||||
/// </summary>
|
||||
public int DownloadActivationTypeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the product has a sample download file
|
||||
/// </summary>
|
||||
public bool HasSampleDownload { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sample download identifier
|
||||
/// </summary>
|
||||
public int SampleDownloadId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the product has user agreement
|
||||
/// </summary>
|
||||
public bool HasUserAgreement { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text of license agreement
|
||||
/// </summary>
|
||||
public string UserAgreementText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the product is recurring
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Subscription flag", BusinessRule = "Triggers automated billing cycles")]
|
||||
public bool IsRecurring { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the cycle length
|
||||
/// </summary>
|
||||
public int RecurringCycleLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the cycle period
|
||||
/// </summary>
|
||||
public int RecurringCyclePeriodId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the total cycles
|
||||
/// </summary>
|
||||
public int RecurringTotalCycles { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the product is rental
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Rental flag", BusinessRule = "Requires date selection in UI")]
|
||||
public bool IsRental { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rental length for some period (price for this period)
|
||||
/// </summary>
|
||||
public int RentalPriceLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rental period (price for this period)
|
||||
/// </summary>
|
||||
public int RentalPricePeriodId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is ship enabled
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Physical shipping toggle")]
|
||||
public bool IsShipEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is free shipping
|
||||
/// </summary>
|
||||
public bool IsFreeShipping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value this product should be shipped separately (each item)
|
||||
/// </summary>
|
||||
public bool ShipSeparately { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the additional shipping charge
|
||||
/// </summary>
|
||||
public decimal AdditionalShippingCharge { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a delivery date identifier
|
||||
/// </summary>
|
||||
public int DeliveryDateId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the product is marked as tax exempt
|
||||
/// </summary>
|
||||
public bool IsTaxExempt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tax category identifier
|
||||
/// </summary>
|
||||
public int TaxCategoryId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating how to manage inventory
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Inventory logic selector", BusinessRule = "0:None, 1:Product, 2:Attributes")]
|
||||
public int ManageInventoryMethodId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a product availability range identifier
|
||||
/// </summary>
|
||||
public int ProductAvailabilityRangeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether multiple warehouses are used for this product
|
||||
/// </summary>
|
||||
public bool UseMultipleWarehouses { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a warehouse identifier
|
||||
/// </summary>
|
||||
public int WarehouseId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the stock quantity
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Physical quantity in warehouse", BusinessRule = "Decremented on order placement or paid")]
|
||||
public int StockQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to display stock availability
|
||||
/// </summary>
|
||||
public bool DisplayStockAvailability { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to display stock quantity
|
||||
/// </summary>
|
||||
public bool DisplayStockQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum stock quantity
|
||||
/// </summary>
|
||||
public int MinStockQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the low stock activity identifier
|
||||
/// </summary>
|
||||
public int LowStockActivityId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the quantity when admin should be notified
|
||||
/// </summary>
|
||||
public int NotifyAdminForQuantityBelow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value backorder mode identifier
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Handling of out-of-stock purchases", BusinessRule = "Controls if StockQuantity can go negative")]
|
||||
public int BackorderModeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to back in stock subscriptions are allowed
|
||||
/// </summary>
|
||||
public bool AllowBackInStockSubscriptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order minimum quantity
|
||||
/// </summary>
|
||||
public int OrderMinimumQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order maximum quantity
|
||||
/// </summary>
|
||||
public int OrderMaximumQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the comma separated list of allowed quantities. null or empty if any quantity is allowed
|
||||
/// </summary>
|
||||
public string AllowedQuantities { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether we allow adding to the cart/wishlist only attribute combinations that exist and have stock greater than zero.
|
||||
/// </summary>
|
||||
public bool AllowAddingOnlyExistingAttributeCombinations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to display attribute combination images only
|
||||
/// </summary>
|
||||
public bool DisplayAttributeCombinationImagesOnly { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this product is returnable
|
||||
/// </summary>
|
||||
public bool NotReturnable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to disable buy (Add to cart) button
|
||||
/// </summary>
|
||||
public bool DisableBuyButton { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to disable "Add to wishlist" button
|
||||
/// </summary>
|
||||
public bool DisableWishlistButton { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this item is available for Pre-Order
|
||||
/// </summary>
|
||||
public bool AvailableForPreOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start date and time of the product availability (for pre-order products)
|
||||
/// </summary>
|
||||
public DateTime? PreOrderAvailabilityStartDateTimeUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to show "Call for Pricing" or "Call for quote" instead of price
|
||||
/// </summary>
|
||||
public bool CallForPrice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the price
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Base selling price", BusinessRule = "Used as primary value before discounts")]
|
||||
public decimal Price { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the old price
|
||||
/// </summary>
|
||||
public decimal OldPrice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the product cost
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Purchase price from vendor", BusinessRule = "Used for profit/margin calculation")]
|
||||
public decimal ProductCost { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether a customer enters price
|
||||
/// </summary>
|
||||
public bool CustomerEntersPrice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum price entered by a customer
|
||||
/// </summary>
|
||||
public decimal MinimumCustomerEnteredPrice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum price entered by a customer
|
||||
/// </summary>
|
||||
public decimal MaximumCustomerEnteredPrice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether base price (PAngV) is enabled.
|
||||
/// </summary>
|
||||
public bool BasepriceEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an amount in product for PAngV
|
||||
/// </summary>
|
||||
public decimal BasepriceAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a unit of product for PAngV (MeasureWeight entity)
|
||||
/// </summary>
|
||||
public int BasepriceUnitId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a reference amount for PAngV
|
||||
/// </summary>
|
||||
public decimal BasepriceBaseAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a reference unit for PAngV (MeasureWeight entity)
|
||||
/// </summary>
|
||||
public int BasepriceBaseUnitId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this product is marked as new
|
||||
/// </summary>
|
||||
public bool MarkAsNew { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start date and time of the new product
|
||||
/// </summary>
|
||||
public DateTime? MarkAsNewStartDateTimeUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the end date and time of the new product
|
||||
/// </summary>
|
||||
public DateTime? MarkAsNewEndDateTimeUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the weight
|
||||
/// </summary>
|
||||
public decimal Weight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the length
|
||||
/// </summary>
|
||||
public decimal Length { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width
|
||||
/// </summary>
|
||||
public decimal Width { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the height
|
||||
/// </summary>
|
||||
public decimal Height { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the available start date and time
|
||||
/// </summary>
|
||||
public DateTime? AvailableStartDateTimeUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the available end date and time
|
||||
/// </summary>
|
||||
public DateTime? AvailableEndDateTimeUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a display order.
|
||||
/// </summary>
|
||||
public int DisplayOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is published
|
||||
/// </summary>
|
||||
public bool Published { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity has been deleted
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Soft-delete flag", BusinessRule = "Records remain in DB but hidden from UI")]
|
||||
public bool Deleted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of product creation
|
||||
/// </summary>
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of product update
|
||||
/// </summary>
|
||||
public DateTime UpdatedOnUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the product type
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Enum wrapper", BusinessRule = "get, set => ProductTypeId")]
|
||||
public ProductType ProductType
|
||||
{
|
||||
get => (ProductType)ProductTypeId;
|
||||
set => ProductTypeId = (int)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the backorder mode
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Enum wrapper", BusinessRule = "get, set => BackorderModeId")]
|
||||
public BackorderMode BackorderMode
|
||||
{
|
||||
get => (BackorderMode)BackorderModeId;
|
||||
set => BackorderModeId = (int)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the download activation type
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Enum wrapper", BusinessRule = "get, set => DownloadActivationTypeId")]
|
||||
public DownloadActivationType DownloadActivationType
|
||||
{
|
||||
get => (DownloadActivationType)DownloadActivationTypeId;
|
||||
set => DownloadActivationTypeId = (int)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the gift card type
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Enum wrapper", BusinessRule = "get, set => GiftCardTypeId")]
|
||||
public GiftCardType GiftCardType
|
||||
{
|
||||
get => (GiftCardType)GiftCardTypeId;
|
||||
set => GiftCardTypeId = (int)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the low stock activity
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Enum wrapper", BusinessRule = "get, set => LowStockActivityId")]
|
||||
public LowStockActivity LowStockActivity
|
||||
{
|
||||
get => (LowStockActivity)LowStockActivityId;
|
||||
set => LowStockActivityId = (int)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value indicating how to manage inventory
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Enum wrapper", BusinessRule = "get, set => ManageInventoryMethodId")]
|
||||
public ManageInventoryMethod ManageInventoryMethod
|
||||
{
|
||||
get => (ManageInventoryMethod)ManageInventoryMethodId;
|
||||
set => ManageInventoryMethodId = (int)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the cycle period for recurring products
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Enum wrapper", BusinessRule = "get, set => RecurringCyclePeriodId")]
|
||||
public RecurringProductCyclePeriod RecurringCyclePeriod
|
||||
{
|
||||
get => (RecurringProductCyclePeriod)RecurringCyclePeriodId;
|
||||
set => RecurringCyclePeriodId = (int)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the period for rental products
|
||||
/// </summary>
|
||||
[ToonDescription(Purpose = "Enum wrapper", BusinessRule = "get, set => RentalPricePeriod")]
|
||||
public RentalPricePeriod RentalPricePeriod
|
||||
{
|
||||
get => (RentalPricePeriod)RentalPricePeriodId;
|
||||
set => RentalPricePeriodId = (int)value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
using AyCode.Core.Serializers.Attributes;
|
||||
using AyCode.Core.Serializers.Toons;
|
||||
using AyCode.Interfaces.Entities;
|
||||
using LinqToDB.Mapping;
|
||||
using Mango.Nop.Core.Interfaces;
|
||||
|
||||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
public interface IMgStockQuantityHistory
|
||||
{
|
||||
public int ProductId { get; set; }
|
||||
public int QuantityAdjustment { get; set; }
|
||||
public int StockQuantity { get; set; }
|
||||
public string Message { get; set; }
|
||||
public int? CombinationId { get; set; }
|
||||
public int? WarehouseId { get; set; }
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a stock quantity change entry
|
||||
/// </summary>
|
||||
[AcBinarySerializable(false, true, false, true, false, false)]
|
||||
[Table(Name = nameof(StockQuantityHistory))]
|
||||
[ToonDescription("NopCommerce stock movement log", Purpose = "Audit trail for physical and logical stock movements")]
|
||||
public partial class StockQuantityHistory : BaseEntity, IMgStockQuantityHistory
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the stock quantity adjustment
|
||||
/// </summary>
|
||||
public int QuantityAdjustment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets current stock quantity
|
||||
/// </summary>
|
||||
public int StockQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the message
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of instance creation
|
||||
/// </summary>
|
||||
public DateTime CreatedOnUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the product identifier
|
||||
/// </summary>
|
||||
public int ProductId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the product attribute combination identifier
|
||||
/// </summary>
|
||||
public int? CombinationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the warehouse identifier
|
||||
/// </summary>
|
||||
public int? WarehouseId { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a download activation type
|
||||
/// </summary>
|
||||
public enum DownloadActivationType
|
||||
{
|
||||
/// <summary>
|
||||
/// When order is paid
|
||||
/// </summary>
|
||||
WhenOrderIsPaid = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Manually
|
||||
/// </summary>
|
||||
Manually = 10,
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a gift card type
|
||||
/// </summary>
|
||||
public enum GiftCardType
|
||||
{
|
||||
/// <summary>
|
||||
/// Virtual
|
||||
/// </summary>
|
||||
Virtual = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Physical
|
||||
/// </summary>
|
||||
Physical = 1,
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
namespace Nop.Core.Domain.Security;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an entity which supports ACL
|
||||
/// </summary>
|
||||
public partial interface IAclSupported
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is subject to ACL
|
||||
/// </summary>
|
||||
bool SubjectToAcl { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
namespace Nop.Core.Domain.Discounts;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an entity which supports discounts
|
||||
/// </summary>
|
||||
public partial interface IDiscountSupported<T> where T : DiscountMapping
|
||||
{
|
||||
int Id { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
namespace Nop.Core.Domain.Localization;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a localized entity
|
||||
/// </summary>
|
||||
public partial interface ILocalizedEntity
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
namespace Nop.Core.Domain.Seo;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an entity which supports slug (SEO friendly one-word URLs)
|
||||
/// </summary>
|
||||
public partial interface ISlugSupported
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
//namespace Mango.Nop.Core.NopDependencies;
|
||||
namespace Nop.Core.Domain.Common;
|
||||
/// <summary>
|
||||
/// Represents a soft-deleted (without actually deleting from storage) entity
|
||||
/// </summary>
|
||||
public partial interface ISoftDeletedEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity has been deleted
|
||||
/// </summary>
|
||||
bool Deleted { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
namespace Nop.Core.Domain.Stores;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an entity which supports store mapping
|
||||
/// </summary>
|
||||
public partial interface IStoreMappingSupported
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is limited/restricted to certain stores
|
||||
/// </summary>
|
||||
bool LimitedToStores { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a low stock activity
|
||||
/// </summary>
|
||||
public enum LowStockActivity
|
||||
{
|
||||
/// <summary>
|
||||
/// Nothing
|
||||
/// </summary>
|
||||
Nothing = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Disable buy button
|
||||
/// </summary>
|
||||
DisableBuyButton = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Unpublish
|
||||
/// </summary>
|
||||
Unpublish = 2,
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a method of inventory management
|
||||
/// </summary>
|
||||
public enum ManageInventoryMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't track inventory for product
|
||||
/// </summary>
|
||||
DontManageStock = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Track inventory for product
|
||||
/// </summary>
|
||||
ManageStock = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Track inventory for product by product attributes
|
||||
/// </summary>
|
||||
ManageStockByAttributes = 2,
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
namespace Nop.Core.Domain.Orders;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an order status enumeration
|
||||
/// </summary>
|
||||
public enum OrderStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Pending
|
||||
/// </summary>
|
||||
Pending = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Processing
|
||||
/// </summary>
|
||||
Processing = 20,
|
||||
|
||||
/// <summary>
|
||||
/// Complete
|
||||
/// </summary>
|
||||
Complete = 30,
|
||||
|
||||
/// <summary>
|
||||
/// Cancelled
|
||||
/// </summary>
|
||||
Cancelled = 40
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
namespace Nop.Core.Domain.Payments;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a payment status enumeration
|
||||
/// </summary>
|
||||
public enum PaymentStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Pending
|
||||
/// </summary>
|
||||
Pending = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Authorized
|
||||
/// </summary>
|
||||
Authorized = 20,
|
||||
|
||||
/// <summary>
|
||||
/// Paid
|
||||
/// </summary>
|
||||
Paid = 30,
|
||||
|
||||
/// <summary>
|
||||
/// Partially Refunded
|
||||
/// </summary>
|
||||
PartiallyRefunded = 35,
|
||||
|
||||
/// <summary>
|
||||
/// Refunded
|
||||
/// </summary>
|
||||
Refunded = 40,
|
||||
|
||||
/// <summary>
|
||||
/// Voided
|
||||
/// </summary>
|
||||
Voided = 50
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a product type
|
||||
/// </summary>
|
||||
public enum ProductType
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple
|
||||
/// </summary>
|
||||
SimpleProduct = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Grouped (product with variants)
|
||||
/// </summary>
|
||||
GroupedProduct = 10,
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a recurring product cycle period
|
||||
/// </summary>
|
||||
public enum RecurringProductCyclePeriod
|
||||
{
|
||||
/// <summary>
|
||||
/// Days
|
||||
/// </summary>
|
||||
Days = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Weeks
|
||||
/// </summary>
|
||||
Weeks = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Months
|
||||
/// </summary>
|
||||
Months = 20,
|
||||
|
||||
/// <summary>
|
||||
/// Years
|
||||
/// </summary>
|
||||
Years = 30,
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
namespace Nop.Core.Domain.Catalog;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a rental product period (for prices)
|
||||
/// </summary>
|
||||
public enum RentalPricePeriod
|
||||
{
|
||||
/// <summary>
|
||||
/// Days
|
||||
/// </summary>
|
||||
Days = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Weeks
|
||||
/// </summary>
|
||||
Weeks = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Months
|
||||
/// </summary>
|
||||
Months = 20,
|
||||
|
||||
/// <summary>
|
||||
/// Years
|
||||
/// </summary>
|
||||
Years = 30,
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
namespace Nop.Core.Domain.Shipping;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the shipping status enumeration
|
||||
/// </summary>
|
||||
public enum ShippingStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Shipping not required
|
||||
/// </summary>
|
||||
ShippingNotRequired = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Not yet shipped
|
||||
/// </summary>
|
||||
NotYetShipped = 20,
|
||||
|
||||
/// <summary>
|
||||
/// Partially shipped
|
||||
/// </summary>
|
||||
PartiallyShipped = 25,
|
||||
|
||||
/// <summary>
|
||||
/// Shipped
|
||||
/// </summary>
|
||||
Shipped = 30,
|
||||
|
||||
/// <summary>
|
||||
/// Delivered
|
||||
/// </summary>
|
||||
Delivered = 40
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
namespace Nop.Core.Domain.Tax;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the tax display type enumeration
|
||||
/// </summary>
|
||||
public enum TaxDisplayType
|
||||
{
|
||||
/// <summary>
|
||||
/// Including tax
|
||||
/// </summary>
|
||||
IncludingTax = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Excluding tax
|
||||
/// </summary>
|
||||
ExcludingTax = 10
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
namespace Nop.Core.Domain.Tax;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the VAT number status enumeration
|
||||
/// </summary>
|
||||
public enum VatNumberStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Unknown
|
||||
/// </summary>
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Empty
|
||||
/// </summary>
|
||||
Empty = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Valid
|
||||
/// </summary>
|
||||
Valid = 20,
|
||||
|
||||
/// <summary>
|
||||
/// Invalid
|
||||
/// </summary>
|
||||
Invalid = 30
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
# Mango.Nop.Core
|
||||
|
||||
@project {
|
||||
type = "framework"
|
||||
own-dep-projects = [
|
||||
"AyCode.Core, AyCode.Core.Server, AyCode.Entities, AyCode.Entities.Server, AyCode.Interfaces, AyCode.Interfaces.Server, AyCode.Utils (in AyCode.Core repo)"
|
||||
]
|
||||
}
|
||||
|
||||
Shared domain library containing entities, DTOs, interfaces, and nopCommerce entity mirrors. **net9.0**. Zero nopCommerce runtime dependency.
|
||||
|
||||
## Documentation
|
||||
|
||||
| Document | Topic |
|
||||
|---|---|
|
||||
| `DTOS.md` | DTO system — two mapping strategies, all DTO types, GenericAttribute typed access |
|
||||
| `NOP_DEPENDENCIES.md` | NopDependencies pattern — mirror copies, namespace rules, file list |
|
||||
|
||||
## Folder Structure
|
||||
|
||||
| Folder | Purpose |
|
||||
|---|---|
|
||||
| `Dtos/` | DTO classes shared across consumers |
|
||||
| `Entities/` | Custom domain entities |
|
||||
| `Extensions/` | Extension methods for `BaseEntity` collections and `GenericAttribute` |
|
||||
| `Interfaces/` | DTO interfaces, soft-delete, foreign key markers |
|
||||
| `Loggers/` | `ILogger` / `Logger` — logging abstraction wrapping AyCode logger |
|
||||
| `Models/` | Login request/response models |
|
||||
| `NopDependencies/` | Mirror copies of nopCommerce entity classes (same namespaces as originals) |
|
||||
| `Services/` | `IMgLockService` interface |
|
||||
| `Utils/` | `CommonHelper2` — email validation, type conversion utilities |
|
||||
|
||||
## Entity Hierarchy
|
||||
|
||||
```
|
||||
Nop.Core.BaseEntity (NopDependencies/, implements IBaseEntity)
|
||||
+-- MgEntityBase (Entities/, implements IEntityInt from AyCode)
|
||||
+-- MgOrderDto<TOrderItemDto, TProductDto>
|
||||
+-- MgOrderItemDto<TProductDto>
|
||||
+-- MgProductDto
|
||||
+-- MgStockQuantityHistoryDto<TProductDto>
|
||||
+-- MgStockTaking<TStockTakingItem>
|
||||
+-- MgStockTakingItem<TStockTaking, TProduct>
|
||||
```
|
||||
|
||||
## Key Types (not in docs/)
|
||||
|
||||
### Entities
|
||||
|
||||
| Type | Key features |
|
||||
|---|---|
|
||||
| `MgEntityBase` | Inherits `BaseEntity`, implements `IEntityInt`. `ToString()` -> `"{TypeName}; Id: {Id}"` |
|
||||
| `MgStockTaking<TStockTakingItem>` | `StartDateTime`, `IsClosed`, `List<TStockTakingItem>` navigation. Implements `ITimeStampInfo` |
|
||||
| `MgStockTakingItem<TStockTaking, TProduct>` | `StockTakingId`, `ProductId`, `IsMeasured`, stock quantities. Navigations to parent and product |
|
||||
|
||||
### Extensions
|
||||
|
||||
| Method | Purpose |
|
||||
|---|---|
|
||||
| `UpdateBaseEntityCollection` | Add/update/remove entities in a list by Id match |
|
||||
|
||||
### Loggers
|
||||
|
||||
Extends AyCode logging infrastructure — for base types see `AyCode.Core/AyCode.Core/docs/LOGGING/README.md`.
|
||||
|
||||
| Type | Inherits | Purpose |
|
||||
|---|---|---|
|
||||
| `ILogger` | `IAcLoggerBase` | Mango-level logger interface |
|
||||
| `ILogger<TCategory>` | `ILogger` | Generic category logger interface |
|
||||
| `Logger` | `AcLoggerBase` | Logger implementation with `IAcLogWriterBase[]` writers |
|
||||
| `Logger<TCategory>` | `Logger` | Typed logger — category name from `typeof(TCategory).Name` |
|
||||
|
||||
### Models
|
||||
|
||||
| Type | Purpose |
|
||||
|---|---|
|
||||
| `MgLoginModelRequest` | `Email` + `Password` for SignalR/API login |
|
||||
| `MgLoginModelResponse` | `CustomerDto?` + `ErrorMessage`. Computed `IsSuccesLogin` |
|
||||
|
||||
### Other
|
||||
|
||||
| Type | Purpose |
|
||||
|---|---|
|
||||
| `NopCommonConst` | Abstract const class inheriting `AcConst` (placeholder) |
|
||||
| `CommonHelper2` | Email validation, IP validation, type conversion (`To<T>`), string utilities |
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `linq2db` — data access, `[Association]` / `[Table]` attributes
|
||||
- `Microsoft.AspNetCore.Mvc.NewtonsoftJson` — JSON serialization
|
||||
- Binary serialization: `AcBinarySerializer` (see `AyCode.Core/AyCode.Core/docs/BINARY/BINARY_FORMAT.md`). SignalR transport: `AcSignalR` (see `AyCode.Core/AyCode.Services/docs/SIGNALR/README.md`).
|
||||
- `AyCode.Core.Serializers.SourceGenerator` (ProjectReference, `OutputItemType="Analyzer"`) — compile-time binary serializer. See [`AyCode.Core/docs/BINARY_SOURCE_GEN.md`](../../../../../../Aycode/Source/AyCode.Core/AyCode.Core/docs/BINARY_SOURCE_GEN.md)
|
||||
- AyCode DLL references: `AyCode.Interfaces`, `AyCode.Core`, `AyCode.Utils`, `AyCode.Entities` (types only, no runtime nopCommerce dependency)
|
||||
|
||||
## AcBinary Source Generator (SGen)
|
||||
|
||||
9 types annotated with `[AcBinarySerializable]`. For general SGen docs see [`AyCode.Core/docs/BINARY_SOURCE_GEN.md`](../../../../../../Aycode/Source/AyCode.Core/AyCode.Core/docs/BINARY_SOURCE_GEN.md).
|
||||
|
||||
Requires `InternalsVisibleTo("Mango.Nop.Core")` in `AyCode.Core/Properties/AssemblyInfo.cs`.
|
||||
|
||||
### Excluded types
|
||||
|
||||
| Type | Reason |
|
||||
|---|---|
|
||||
| `DiscountProductMapping` | Base `DiscountMapping` hides `BaseEntity.Id` with readonly `new int Id { get; }` → CS0200. Uses compiled-expression fallback. |
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
using Mango.Nop.Core.Interfaces;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Nop.Core.Caching;
|
||||
using Nop.Core.Domain.Catalog;
|
||||
using Nop.Core.Events;
|
||||
using Nop.Data;
|
||||
using Nop.Services.Events;
|
||||
using Nop.Services.Logging;
|
||||
|
||||
namespace Mango.Nop.Core.Repositories;
|
||||
|
||||
|
||||
public class MgDbContextBase : IMgDbContextBase
|
||||
{
|
||||
//TODO: ez itt nem ay igazi, kitalálni vmit! - J.
|
||||
private readonly CacheKey _auctionAllKey = new("Nop.auction.all-{0}", AUCTION_PATTERN_KEY);
|
||||
public const string AUCTION_PATTERN_KEY = "Nop.auction.";
|
||||
|
||||
|
||||
protected ILogger Logger;
|
||||
protected INopDataProvider DataProvider;
|
||||
//protected IHttpContextAccessor HttpContextAccessor;
|
||||
|
||||
public MgDbContextBase(INopDataProvider dataProvider, ILogger logger)
|
||||
{
|
||||
Logger = logger;
|
||||
DataProvider = dataProvider;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace Mango.Nop.Services;
|
||||
namespace Mango.Nop.Core.Services;
|
||||
|
||||
public interface IMgLockService
|
||||
{
|
||||
|
|
@ -0,0 +1,312 @@
|
|||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Mango.Nop.Core.Utils;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a common helper
|
||||
/// </summary>
|
||||
public partial class CommonHelper2
|
||||
{
|
||||
#region Fields
|
||||
|
||||
//we use regular expression based on RFC 5322 Official Standard (see https://emailregex.com/)
|
||||
private const string EMAIL_EXPRESSION = @"^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|""(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*"")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Get email validation regex
|
||||
/// </summary>
|
||||
/// <returns>Regular expression</returns>
|
||||
[GeneratedRegex(EMAIL_EXPRESSION, RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture, "en-US")]
|
||||
public static partial Regex GetEmailRegex();
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the subscriber email or throw.
|
||||
/// </summary>
|
||||
/// <param name="email">The email.</param>
|
||||
/// <returns></returns>
|
||||
public static string EnsureSubscriberEmailOrThrow(string email)
|
||||
{
|
||||
var output = EnsureNotNull(email);
|
||||
output = output.Trim();
|
||||
output = EnsureMaximumLength(output, 255);
|
||||
|
||||
if (!IsValidEmail(output))
|
||||
{
|
||||
throw new Exception("Email is not valid.");
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a string is in valid e-mail format
|
||||
/// </summary>
|
||||
/// <param name="email">Email to verify</param>
|
||||
/// <returns>true if the string is a valid e-mail address and false if it's not</returns>
|
||||
public static bool IsValidEmail(string email)
|
||||
{
|
||||
if (string.IsNullOrEmpty(email))
|
||||
return false;
|
||||
|
||||
email = email.Trim();
|
||||
|
||||
return GetEmailRegex().IsMatch(email);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that string is an valid IP-Address
|
||||
/// </summary>
|
||||
/// <param name="ipAddress">IPAddress to verify</param>
|
||||
/// <returns>true if the string is a valid IpAddress and false if it's not</returns>
|
||||
public static bool IsValidIpAddress(string ipAddress)
|
||||
{
|
||||
return IPAddress.TryParse(ipAddress, out var _);
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// Generate random digit code
|
||||
///// </summary>
|
||||
///// <param name="length">Length</param>
|
||||
///// <returns>Result string</returns>
|
||||
//public static string GenerateRandomDigitCode(int length)
|
||||
//{
|
||||
// using var random = new SecureRandomNumberGenerator();
|
||||
// var str = string.Empty;
|
||||
// for (var i = 0; i < length; i++)
|
||||
// str = string.Concat(str, random.Next(10).ToString());
|
||||
// return str;
|
||||
//}
|
||||
|
||||
///// <summary>
|
||||
///// Returns an random integer number within a specified rage
|
||||
///// </summary>
|
||||
///// <param name="min">Minimum number</param>
|
||||
///// <param name="max">Maximum number</param>
|
||||
///// <returns>Result</returns>
|
||||
//public static int GenerateRandomInteger(int min = 0, int max = int.MaxValue)
|
||||
//{
|
||||
// using var random = new SecureRandomNumberGenerator();
|
||||
// return random.Next(min, max);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that a string doesn't exceed maximum allowed length
|
||||
/// </summary>
|
||||
/// <param name="str">Input string</param>
|
||||
/// <param name="maxLength">Maximum length</param>
|
||||
/// <param name="postfix">A string to add to the end if the original string was shorten</param>
|
||||
/// <returns>Input string if its length is OK; otherwise, truncated input string</returns>
|
||||
public static string EnsureMaximumLength(string str, int maxLength, string postfix = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(str))
|
||||
return str;
|
||||
|
||||
if (str.Length <= maxLength)
|
||||
return str;
|
||||
|
||||
var pLen = postfix?.Length ?? 0;
|
||||
|
||||
var result = str[0..(maxLength - pLen)];
|
||||
if (!string.IsNullOrEmpty(postfix))
|
||||
{
|
||||
result += postfix;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that a string only contains numeric values
|
||||
/// </summary>
|
||||
/// <param name="str">Input string</param>
|
||||
/// <returns>Input string with only numeric values, empty string if input is null/empty</returns>
|
||||
public static string EnsureNumericOnly(string str)
|
||||
{
|
||||
return string.IsNullOrEmpty(str) ? string.Empty : new string(str.Where(char.IsDigit).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that a string is not null
|
||||
/// </summary>
|
||||
/// <param name="str">Input string</param>
|
||||
/// <returns>Result</returns>
|
||||
public static string EnsureNotNull(string str)
|
||||
{
|
||||
return str ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the specified strings are null or empty strings
|
||||
/// </summary>
|
||||
/// <param name="stringsToValidate">Array of strings to validate</param>
|
||||
/// <returns>Boolean</returns>
|
||||
public static bool AreNullOrEmpty(params string[] stringsToValidate)
|
||||
{
|
||||
return stringsToValidate.Any(string.IsNullOrEmpty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two arrays
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type</typeparam>
|
||||
/// <param name="a1">Array 1</param>
|
||||
/// <param name="a2">Array 2</param>
|
||||
/// <returns>Result</returns>
|
||||
public static bool ArraysEqual<T>(T[] a1, T[] a2)
|
||||
{
|
||||
//also see Enumerable.SequenceEqual(a1, a2);
|
||||
if (ReferenceEquals(a1, a2))
|
||||
return true;
|
||||
|
||||
if (a1 == null || a2 == null)
|
||||
return false;
|
||||
|
||||
if (a1.Length != a2.Length)
|
||||
return false;
|
||||
|
||||
var comparer = EqualityComparer<T>.Default;
|
||||
return !a1.Where((t, i) => !comparer.Equals(t, a2[i])).Any();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a property on an object to a value.
|
||||
/// </summary>
|
||||
/// <param name="instance">The object whose property to set.</param>
|
||||
/// <param name="propertyName">The name of the property to set.</param>
|
||||
/// <param name="value">The value to set the property to.</param>
|
||||
public static void SetProperty(object instance, string propertyName, object value)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(instance);
|
||||
ArgumentNullException.ThrowIfNull(propertyName);
|
||||
|
||||
var instanceType = instance.GetType();
|
||||
var pi = instanceType.GetProperty(propertyName)
|
||||
?? throw new Exception($"No property '{propertyName}' found on the instance of type '{instanceType}'.");
|
||||
|
||||
if (!pi.CanWrite)
|
||||
throw new Exception($"No property '{propertyName}' found on the instance of type '{instanceType}'.");
|
||||
if (value != null && !value.GetType().IsAssignableFrom(pi.PropertyType))
|
||||
value = To(value, pi.PropertyType);
|
||||
pi.SetValue(instance, value, Array.Empty<object>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a value to a destination type.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to convert.</param>
|
||||
/// <param name="destinationType">The type to convert the value to.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static object To(object value, Type destinationType)
|
||||
{
|
||||
return To(value, destinationType, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a value to a destination type.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to convert.</param>
|
||||
/// <param name="destinationType">The type to convert the value to.</param>
|
||||
/// <param name="culture">Culture</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static object To(object value, Type destinationType, CultureInfo culture)
|
||||
{
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
var sourceType = value.GetType();
|
||||
|
||||
var destinationConverter = TypeDescriptor.GetConverter(destinationType);
|
||||
if (destinationConverter.CanConvertFrom(value.GetType()))
|
||||
return destinationConverter.ConvertFrom(null, culture, value);
|
||||
|
||||
var sourceConverter = TypeDescriptor.GetConverter(sourceType);
|
||||
if (sourceConverter.CanConvertTo(destinationType))
|
||||
return sourceConverter.ConvertTo(null, culture, value, destinationType);
|
||||
|
||||
if (destinationType.IsEnum && value is int)
|
||||
return Enum.ToObject(destinationType, (int)value);
|
||||
|
||||
if (!destinationType.IsInstanceOfType(value))
|
||||
return Convert.ChangeType(value, destinationType, culture);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a value to a destination type.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to convert.</param>
|
||||
/// <typeparam name="T">The type to convert the value to.</typeparam>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static T To<T>(object value)
|
||||
{
|
||||
//return (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
|
||||
return (T)To(value, typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits the camel-case word into separate one
|
||||
/// </summary>
|
||||
/// <param name="str">Input string</param>
|
||||
/// <returns>Splitted string</returns>
|
||||
public static string SplitCamelCaseWord(string str)
|
||||
{
|
||||
if (string.IsNullOrEmpty(str))
|
||||
return string.Empty;
|
||||
|
||||
var result = str.ToCharArray()
|
||||
.Select(p => p.ToString())
|
||||
.Aggregate(string.Empty, (current, c) => current + (c == c.ToUpperInvariant() ? $" {c}" : c));
|
||||
|
||||
//ensure no spaces (e.g. when the first letter is upper case)
|
||||
result = result.TrimStart();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get difference in years
|
||||
/// </summary>
|
||||
/// <param name="startDate"></param>
|
||||
/// <param name="endDate"></param>
|
||||
/// <returns></returns>
|
||||
public static int GetDifferenceInYears(DateTime startDate, DateTime endDate)
|
||||
{
|
||||
//source: http://stackoverflow.com/questions/9/how-do-i-calculate-someones-age-in-c
|
||||
//this assumes you are looking for the western idea of age and not using East Asian reckoning.
|
||||
var age = endDate.Year - startDate.Year;
|
||||
if (startDate > endDate.AddYears(-age))
|
||||
age--;
|
||||
return age;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get DateTime to the specified year, month, and day using the conventions of the current thread culture
|
||||
/// </summary>
|
||||
/// <param name="year">The year</param>
|
||||
/// <param name="month">The month</param>
|
||||
/// <param name="day">The day</param>
|
||||
/// <returns>An instance of the Nullable<System.DateTime></returns>
|
||||
public static DateTime? ParseDate(int? year, int? month, int? day)
|
||||
{
|
||||
if (!year.HasValue || !month.HasValue || !day.HasValue)
|
||||
return null;
|
||||
|
||||
DateTime? date = null;
|
||||
try
|
||||
{
|
||||
date = new DateTime(year.Value, month.Value, day.Value, CultureInfo.CurrentCulture.Calendar);
|
||||
}
|
||||
catch { }
|
||||
return date;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# Architecture
|
||||
|
||||
## Framework vs. Consumer Boundary
|
||||
|
||||
This solution is **Layer 2 — Domain framework** (NopCommerce-plugin base). Built on Layer 0 (AyCode.Core), consumed by Layer 3 (specific plugin apps, plural and unknown). Full doctrine: AyCode.Core's `docs/ARCHITECTURE.md#framework-vs-consumer-boundary`.
|
||||
|
||||
### Layer position
|
||||
|
||||
```
|
||||
Layer 0 — AyCode.Core universal primitives (serializers, logging, entities)
|
||||
Layer 2 — Mango.Nop.Core this solution — NopCommerce-plugin domain framework
|
||||
Layer 3 — Consumer plugin apps specific plugin projects with business logic
|
||||
```
|
||||
|
||||
### Nop-plugin-framework-specific notes
|
||||
|
||||
- **`NopDependencies/`** mirrors NopCommerce entity classes under the original Nop namespaces — no runtime Nop dependency, compile-time type parity only
|
||||
- **DTOs** use type parameters for consumer entity types (e.g. `MgOrderDto<TOrderItemDto, TProductDto>`) — concrete types are provided by plugin projects
|
||||
- **Entity hierarchy**: `Nop.Core.BaseEntity` (mirrored) → `MgEntityBase` → generic DTOs/entities parameterized for consumers
|
||||
- **`Logger` / `ILogger`** extend AyCode logging (`AcLoggerBase`) — plugin projects may subclass again if business-specific writers are needed
|
||||
- **Zero runtime Nop dependency** — plugin apps bring their own Nop runtime
|
||||
|
||||
### What belongs here vs. in a plugin
|
||||
|
||||
**Yes, framework:**
|
||||
- NopCommerce entity mirrors (for cross-plugin type sharing)
|
||||
- Generic DTOs with type parameters
|
||||
- Shared interfaces and extensions for NopCommerce domain entities
|
||||
- Logger base with AyCode integration
|
||||
|
||||
**No, plugin only:**
|
||||
- Plugin-specific business logic
|
||||
- Plugin-named types (`XxxPlugin`, `XxxService` where Xxx is a specific product)
|
||||
- Hardcoded plugin configuration, tenant, or product IDs
|
||||
|
||||
## Related Documents
|
||||
|
||||
| Topic | Document |
|
||||
|---|---|
|
||||
| Framework-first doctrine (full) | `../../../../../../../Aycode/Source/AyCode.Core/docs/ARCHITECTURE.md#framework-vs-consumer-boundary` |
|
||||
| Placement rules, anti-patterns | `CONVENTIONS.md#framework-first-placement` |
|
||||
| DTO system and GenericAttribute typed access | `DTOS.md` |
|
||||
| NopDependencies mirror pattern | `NOP_DEPENDENCIES.md` |
|
||||
| Folder structure and key types index | `../README.md` |
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
# Conventions
|
||||
|
||||
For core framework conventions (Ac prefix, naming patterns, Session/Transaction, etc.) see AyCode.Core's `docs/CONVENTIONS.md`.
|
||||
|
||||
## Framework-First Placement
|
||||
|
||||
Follow the doctrine in AyCode.Core's `docs/CONVENTIONS.md#framework-first-placement`. Same verdict table and hard rules apply.
|
||||
|
||||
**Nop-plugin-framework-specific additions:**
|
||||
- `NopDependencies/` mirror classes stay in their original Nop namespaces — don't rename, don't add plugin-specific fields
|
||||
- DTOs parameterize via type arguments, not concrete plugin types
|
||||
- Any plugin-specific business logic belongs in the plugin project, not here
|
||||
- Adding a new shared DTO: ensure it is useful for 2+ plugin projects (otherwise keep in the single plugin)
|
||||
|
||||
## Naming
|
||||
|
||||
- **`Mg` prefix**: for Mango-framework-level types (e.g. `MgEntityBase`, `MgOrderDto`, `MgProductDto`)
|
||||
- **`IMg` prefix**: for Mango-framework-level interfaces (e.g. `IMgModelDtoBase`, `IMgSoftRemoveEntity`)
|
||||
- **No prefix**: for NopCommerce entity mirrors in `NopDependencies/` — they stay in Nop namespace, same name as upstream
|
||||
|
||||
## XML Documentation
|
||||
|
||||
`<summary>` — brief, developer-facing, readable in VS IntelliSense tooltip. NO implementation details, NO wire-format / byte-level / perf specifics — those live in `docs/TOPIC/*.md`. Add `<example>` only when usage is non-obvious; otherwise omit.
|
||||
|
||||
## See Also
|
||||
|
||||
- Core framework naming (`Ac` / `IAc` prefixes), patterns, serialization conventions: AyCode.Core's `docs/CONVENTIONS.md`
|
||||
- Framework-first verdict table (full): AyCode.Core's `docs/CONVENTIONS.md#framework-first-placement`
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
# DTO System
|
||||
|
||||
> Part of `Mango.Nop.Core`. See `Mango.Nop.Core/README.md` for project overview.
|
||||
|
||||
## Two Mapping Strategies
|
||||
|
||||
### Strategy 1: `ModelDtoBase<T>` (simple DTOs)
|
||||
|
||||
Used by: `CustomerDto`, `MgGenericAttributeDto`
|
||||
|
||||
```
|
||||
ModelDtoBase → ModelDtoBase<Customer> → CustomerDto
|
||||
```
|
||||
|
||||
- Manual `CopyEntityValuesToDto`/`CopyDtoValuesToEntity` overrides
|
||||
- `CreateMainEntity()` uses `Activator.CreateInstance<T>()`
|
||||
- No LinqToDB associations
|
||||
|
||||
### Strategy 2: `MgEntityBase` + `IModelDtoBase<T>` (complex DTOs)
|
||||
|
||||
Used by: `MgOrderDto`, `MgOrderItemDto`, `MgStockQuantityHistoryDto`
|
||||
|
||||
```
|
||||
BaseEntity → MgEntityBase → MgOrderDto<TOrderItemDto, TProductDto> : IModelDtoBase<Order>
|
||||
```
|
||||
|
||||
- Uses `PropertyHelper.CopyPublicValueTypeProperties()` for bulk value-type copy
|
||||
- LinqToDB `[Association]` navigation properties
|
||||
- Generic type parameters for child DTOs
|
||||
|
||||
### Strategy 3: Entity inheritance (MgProductDto)
|
||||
|
||||
```
|
||||
BaseEntity → MgEntityBase → MgProductDto : IMgProductDto
|
||||
```
|
||||
|
||||
- No `IModelDtoBase<Product>` (entity mapping methods are commented out)
|
||||
- Direct property declarations mirroring `Product` fields
|
||||
|
||||
## DTO Types
|
||||
|
||||
| Type | Generic params | Maps to entity | Key features |
|
||||
|---|---|---|---|
|
||||
| `ModelDtoBase` | — | — | Abstract base, `int Id` only |
|
||||
| `ModelDtoBase<TMainEntity>` | `TMainEntity : BaseEntity` | `TMainEntity` | `CreateMainEntity()`, `CopyEntityValuesToDto()`, `CopyDtoValuesToEntity()` |
|
||||
| `MgOrderDto<TOrderItemDto, TProductDto>` | `TOrderItemDto : IMgOrderItemDto<TProductDto>`, `TProductDto : IMgProductDto` | `Order` | Has `Customer`, `List<TOrderItemDto>`, `List<OrderNote>` navigations. Enum wrappers: `OrderStatus`, `ShippingStatus`, `PaymentStatus` |
|
||||
| `MgOrderItemDto<TProductDto>` | `TProductDto : IMgProductDto` | `OrderItem` | Has `TProductDto?` navigation. Computed `ProductName` property |
|
||||
| `MgProductDto` | — | *(commented out)* | Base product DTO. Name, Price, StockQuantity, dimensions. No entity mapping methods (currently commented out) |
|
||||
| `MgGenericAttributeDto` | — | `GenericAttribute` | Inherits `GenericAttribute` directly (not `ModelDtoBase`). Full bidirectional mapping |
|
||||
| `MgStockQuantityHistoryDto<TProductDto>` | `TProductDto : IMgProductDto` | `StockQuantityHistory` | Has `TProductDto` navigation. Mapping methods `NotImplementedException` (stub) |
|
||||
| `CustomerDto` | — | `Customer` | `Username`, `Email`, `FirstName`, `LastName`, `FullName` computed. Uses `[AcBinarySerializable]` and LinqToDB `[Table]` |
|
||||
|
||||
## DTO Interfaces
|
||||
|
||||
| Interface | Extends | Purpose |
|
||||
|---|---|---|
|
||||
| `IModelDtoBase` | `IEntityInt`, `IModelDtoBaseEmpty` | Marker for all DTOs with `int Id` |
|
||||
| `IModelDtoBase<TMainEntity>` | `IModelDtoBase` | Bidirectional mapping contract: `CreateMainEntity()`, `CopyDtoValuesToEntity()`, `CopyEntityValuesToDto()` |
|
||||
| `IMgOrderDto<TOrderItemDto, TProductDto>` | `IEntityInt`, `ISoftDeletedEntity` | Order DTO contract with navigation properties and initialization methods |
|
||||
| `IMgOrderItemDto<TProductDto>` | `IEntityInt` | Order item DTO contract with product navigation |
|
||||
| `IMgProductDto` | `IEntityInt`, `ILocalizedEntity`, `ISlugSupported`, `IAclSupported`, `IStoreMappingSupported`, `ISoftDeletedEntity` | Product DTO contract |
|
||||
|
||||
## GenericAttribute Typed Access
|
||||
|
||||
`GenericAttributeExtensions` provides typed access to nopCommerce's polymorphic key-value store:
|
||||
|
||||
| Method | Signature | Purpose |
|
||||
|---|---|---|
|
||||
| `GetValueOrNull<TValue>` | `(IEnumerable<GenericAttribute>, string key) -> TValue?` | Get typed value, returns null if not found |
|
||||
| `GetValueOrDefault<TValue>` | `(IEnumerable<GenericAttribute>, string key, TValue default) -> TValue` | Same with default fallback |
|
||||
| `TryGetValue<TValue>` | `(IEnumerable<GenericAttribute>, string key, out TValue?) -> bool` | Try-pattern for GA value |
|
||||
| `AddNewGenericAttribute` | `(ICollection<GenericAttribute>, ...) -> GenericAttribute` | Create and add new GA with UTC timestamp |
|
||||
|
||||
**Rule:** Always use these extension methods — never parse `GenericAttribute.Value` strings manually.
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
# NopDependencies Pattern
|
||||
|
||||
> Part of `Mango.Nop.Core`. See `Mango.Nop.Core/README.md` for project overview.
|
||||
|
||||
## Why
|
||||
|
||||
`Mango.Nop.Core` has **zero nopCommerce runtime dependency**. This allows it to be referenced by projects that don't have the full nopCommerce stack (e.g. Blazor/MAUI clients that only need DTOs and interfaces).
|
||||
|
||||
To achieve this, `NopDependencies/` contains **mirror copies** of nopCommerce entity classes with the **same namespace** as the originals:
|
||||
|
||||
```csharp
|
||||
// In NopDependencies/BaseEntity.cs — same namespace as nopCommerce
|
||||
namespace Nop.Core;
|
||||
public abstract partial class BaseEntity : IBaseEntity
|
||||
{
|
||||
public int Id { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
At compile time, projects referencing only `Mango.Nop.Core` get these mirror types. Projects with the full nopCommerce stack get the real types — they are type-compatible because they share the same namespace and shape.
|
||||
|
||||
## Rules
|
||||
|
||||
- **DO NOT modify** files in `NopDependencies/` unless the nopCommerce version changes
|
||||
- **DO NOT change namespaces** — they must match the original nopCommerce types exactly
|
||||
- All mirror classes are `partial` — they can be extended but should not be modified directly
|
||||
|
||||
## File List
|
||||
|
||||
| File | Namespace | Type |
|
||||
|---|---|---|
|
||||
| `BaseEntity.cs` | `Nop.Core` | `BaseEntity` (abstract, `IBaseEntity`) + `IBaseEntity` interface |
|
||||
| `Catalogs/Customer.cs` | `Nop.Core.Domain.Customers` | `Customer` |
|
||||
| `Catalogs/CustomerRole.cs` | `Nop.Core.Domain.Customers` | `CustomerRole` |
|
||||
| `Catalogs/Order.cs` | `Nop.Core.Domain.Orders` | `Order` |
|
||||
| `Catalogs/OrderItem.cs` | `Nop.Core.Domain.Orders` | `OrderItem` |
|
||||
| `Catalogs/OrderNote.cs` | `Nop.Core.Domain.Orders` | `OrderNote` |
|
||||
| `Catalogs/Product.cs` | `Nop.Core.Domain.Catalog` | `Product` |
|
||||
| `Catalogs/GenericAttribute.cs` | `Nop.Core.Domain.Common` | `GenericAttribute` |
|
||||
| `Catalogs/StockQuantityHistory.cs` | `Nop.Core.Domain.Catalog` | `StockQuantityHistory` |
|
||||
| `Catalogs/DiscountMapping.cs` | `Nop.Core.Domain.Catalog` | `DiscountMapping` |
|
||||
| `Catalogs/DiscountProductMapping.cs` | `Nop.Core.Domain.Catalog` | `DiscountProductMapping` |
|
||||
|
||||
**Enums:** `OrderStatus`, `PaymentStatus`, `ShippingStatus`, `ProductType`, `ManageInventoryMethod`, `BackorderMode`, `LowStockActivity`, `GiftCardType`, `RentalPricePeriod`, `RecurringProductCyclePeriod`, `DownloadActivationType`, `TaxDisplayType`, `VatNumberStatus`
|
||||
|
||||
**Interfaces:** `ISoftDeletedEntity`, `ILocalizedEntity`, `ISlugSupported`, `IAclSupported`, `IStoreMappingSupported`, `IDiscountSupported`
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# Mango.Nop.Core documentation
|
||||
|
||||
Topic documentation for the `Mango.Nop.Core` project (Layer 2 framework — NopCommerce-adjacent base types).
|
||||
|
||||
## Reference docs (flat)
|
||||
|
||||
- [`ARCHITECTURE.md`](ARCHITECTURE.md) — Project architecture
|
||||
- [`CONVENTIONS.md`](CONVENTIONS.md) — Project-specific conventions
|
||||
- [`DTOS.md`](DTOS.md) — DTO system, mapping strategies
|
||||
- [`NOP_DEPENDENCIES.md`](NOP_DEPENDENCIES.md) — NopCommerce entity mirror pattern
|
||||
|
||||
## Navigation
|
||||
|
||||
Per the AI Agent Core Protocol (folder navigation rule), start from this README when browsing `docs/`. All docs at this level are single-file reference; topic folders (e.g., LOGGING/) exist only at sub-projects that host a variant (see `../Mango.Nop.Services/docs/LOGGING/`).
|
||||
|
||||
## See also
|
||||
|
||||
- **Repo-level conventions**: `../../docs/CONVENTIONS.md`
|
||||
- **Base framework**: `../../../../../Aycode/Source/AyCode.Core/` docs
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace Mango.Nop.Core.Interfaces;
|
||||
namespace Mango.Nop.Data.Interfaces;
|
||||
|
||||
public interface IMgDalBase //: IAcDalBase//: IDisposable
|
||||
{
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
using Mango.Nop.Core.Loggers;
|
||||
using Nop.Core.Domain.Catalog;
|
||||
using Nop.Core.Domain.Orders;
|
||||
using Nop.Data;
|
||||
using System.Transactions;
|
||||
|
||||
namespace Mango.Nop.Data.Interfaces;
|
||||
|
||||
public interface IMgDbContextBase //: IAcDbContextBase
|
||||
{
|
||||
ILogger Logger { get; init; }
|
||||
INopDataProvider DataProvider { get; init; }
|
||||
|
||||
IRepository<Order> Orders { get; set; }
|
||||
IRepository<Product> Products { get; set; }
|
||||
|
||||
bool Transaction(Func<TransactionScope, bool> callbackTransactionBody, bool throwException = false);
|
||||
bool TransactionSafe(Func<TransactionScope, bool> callbackTransactionBody, bool throwException = false);
|
||||
|
||||
Task<bool> TransactionAsync(Func<TransactionScope, Task<bool>> callbackTransactionBody, bool throwException = false);
|
||||
Task<bool> TransactionSafeAsync(Func<TransactionScope, Task<bool>> callbackTransactionBody, bool throwException = false);
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
using Mango.Nop.Core.Entities;
|
||||
using Nop.Data;
|
||||
|
||||
namespace Mango.Nop.Core.Interfaces;
|
||||
namespace Mango.Nop.Data.Interfaces;
|
||||
|
||||
public interface IMgDbTableBase //: IAcDbTableBase
|
||||
{
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<BaseOutputPath>bin\FruitBank</BaseOutputPath>
|
||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<CopyRefAssembliesToPublishDirectory>true</CopyRefAssembliesToPublishDirectory>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MessagePack" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.11" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\FruitBank\Libraries\Nop.Core\Nop.Core.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\FruitBank\Libraries\Nop.Data\Nop.Data.csproj" />
|
||||
<ProjectReference Include="..\Mango.Nop.Core\Mango.Nop.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="AyCode.Core">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Core.Server">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Core.Server.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Entities">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Entities.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Entities.Server">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Entities.Server.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Interfaces">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Interfaces.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Interfaces.Server">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Interfaces.Server.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Utils">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Utils.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="docs\**\*.md" />
|
||||
<None Include="**\README.md" Exclude="$(DefaultItemExcludes);docs\**" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
# Mango.Nop.Data
|
||||
|
||||
@project {
|
||||
type = "framework"
|
||||
own-dep-projects = [
|
||||
"AyCode.Core, AyCode.Core.Server, AyCode.Entities, AyCode.Entities.Server, AyCode.Interfaces, AyCode.Interfaces.Server, AyCode.Utils (in AyCode.Core repo)"
|
||||
]
|
||||
}
|
||||
|
||||
Data access layer with repository base classes and DB context abstractions. **net9.0**.
|
||||
|
||||
## Documentation
|
||||
|
||||
| Document | Topic |
|
||||
|---|---|
|
||||
| `REPOSITORIES.md` | MgDbTableBase, MgDtoDbTableBase — CRUD hooks, timestamps, delete rules, event bridging |
|
||||
| `TRANSACTIONS.md` | MgDbContextBase — 4 transaction methods, lock strategy, callback contract |
|
||||
|
||||
## Folder Structure
|
||||
|
||||
| Folder | Purpose |
|
||||
|---|---|
|
||||
| `Interfaces/` | Repository interfaces: `IMgDalBase`, `IMgDbContextBase`, `IMgDbTableBase` |
|
||||
| `Repositories/` | Repository base implementations: `MgDalBase`, `MgDbContextBase`, `MgDbTableBase`, `MgDtoDbTableBase` |
|
||||
|
||||
## Inheritance Chains
|
||||
|
||||
### Repository hierarchy
|
||||
```
|
||||
nopCommerce EntityRepository<TEntity> (Nop.Data)
|
||||
+-- MgDbTableBase<TEntity>
|
||||
+-- MgDtoDbTableBase<TDtoEntity, TMainEntity>
|
||||
```
|
||||
|
||||
### Context hierarchy
|
||||
```
|
||||
MgDbContextBase (abstract, implements IMgDbContextBase)
|
||||
+-- [Consumer DbContexts in plugins]
|
||||
```
|
||||
|
||||
### DAL hierarchy
|
||||
```
|
||||
MgDalBase<TDbContext> (implements IMgDalBase<TDbContext>)
|
||||
+-- [Consumer DALs in plugins]
|
||||
```
|
||||
|
||||
## Interfaces
|
||||
|
||||
| Interface | Purpose |
|
||||
|---|---|
|
||||
| `IMgDbTableBase` | Marker interface for repository classes |
|
||||
| `IMgDbContextBase` | Contract: `Logger`, `DataProvider`, `Orders`, `Products`, 4 transaction methods |
|
||||
| `IMgDalBase` | `Name` property |
|
||||
| `IMgDalBase<TDbContext>` | `Context`, `MutextLock` — typed access to DB context |
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `Mango.Nop.Core` (ProjectReference)
|
||||
- `Nop.Core`, `Nop.Data` (nopCommerce ProjectReferences)
|
||||
- `Microsoft.AspNetCore.Mvc.NewtonsoftJson`
|
||||
|
|
@ -1,16 +1,14 @@
|
|||
using System.Linq.Expressions;
|
||||
using System.Transactions;
|
||||
using Mango.Nop.Core.Interfaces;
|
||||
using Mango.Nop.Data.Interfaces;
|
||||
using Nop.Core;
|
||||
using Nop.Core.Caching;
|
||||
using Nop.Core.Configuration;
|
||||
using Nop.Core.Events;
|
||||
using Nop.Data;
|
||||
using Nop.Data.DataProviders;
|
||||
|
||||
namespace Mango.Nop.Core.Repositories;
|
||||
namespace Mango.Nop.Data.Repositories;
|
||||
|
||||
public class MgDalBase<TDbContext> : IMgDalBase<TDbContext> where TDbContext : IMgDbContextBase
|
||||
public abstract class MgDalBase<TDbContext> : IMgDalBase<TDbContext> where TDbContext : IMgDbContextBase
|
||||
{
|
||||
public string Name { get; }
|
||||
public TDbContext Context { get; }
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
using System.Transactions;
|
||||
using AyCode.Core.Consts;
|
||||
using AyCode.Core.Helpers;
|
||||
using AyCode.Core.Loggers;
|
||||
using AyCode.Utils.Extensions;
|
||||
using Mango.Nop.Core.Loggers;
|
||||
using Mango.Nop.Core.Services;
|
||||
using Mango.Nop.Data.Interfaces;
|
||||
using Nop.Core.Caching;
|
||||
using Nop.Core.Domain.Catalog;
|
||||
using Nop.Core.Domain.Orders;
|
||||
using Nop.Data;
|
||||
|
||||
namespace Mango.Nop.Data.Repositories;
|
||||
|
||||
|
||||
public abstract class MgDbContextBase : IMgDbContextBase
|
||||
{
|
||||
//TODO: ez itt nem ay igazi, kitalálni vmit! - J.
|
||||
private readonly CacheKey _auctionAllKey = new("Nop.auction.all-{0}", AUCTION_PATTERN_KEY);
|
||||
public const string AUCTION_PATTERN_KEY = "Nop.auction.";
|
||||
|
||||
protected readonly IMgLockService LockService;
|
||||
|
||||
public ILogger Logger { get; init; }
|
||||
public INopDataProvider DataProvider { get; init; }
|
||||
|
||||
public IRepository<Order> Orders { get; set; }
|
||||
public IRepository<OrderItem> OrderItems { get; set; }
|
||||
|
||||
public IRepository<Product> Products { get; set; }
|
||||
|
||||
//public IHttpContextAccessor HttpContextAccessor { get; init; }
|
||||
|
||||
public MgDbContextBase(IRepository<Product> productRepository, IRepository<Order> orderRepository, IRepository<OrderItem> orderItemRepository, INopDataProvider dataProvider, IMgLockService lockService, ILogger logger)
|
||||
{
|
||||
LockService = lockService;
|
||||
|
||||
Logger = logger;// new Logger<MgDbContextBase>(logWriters.ToArray());
|
||||
DataProvider = dataProvider;
|
||||
|
||||
Products = productRepository;
|
||||
|
||||
Orders = orderRepository;
|
||||
OrderItems = orderItemRepository;
|
||||
}
|
||||
|
||||
private static TransactionScope CreateTransactionScope(TransactionScopeOption transactionScopeOption = TransactionScopeOption.Required)
|
||||
{
|
||||
//TransactionManager.ImplicitDistributedTransactions = true;
|
||||
|
||||
var transactionOptions = new TransactionOptions
|
||||
{
|
||||
IsolationLevel = IsolationLevel.ReadCommitted,
|
||||
Timeout = TransactionManager.DefaultTimeout
|
||||
};
|
||||
|
||||
return new TransactionScope(transactionScopeOption, transactionOptions, TransactionScopeAsyncFlowOption.Enabled);
|
||||
//return new TransactionScope(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled);
|
||||
}
|
||||
|
||||
public bool Transaction(Func<TransactionScope, bool> callbackTransactionBody, bool throwException = false)
|
||||
=> TransactionInner(callbackTransactionBody, throwException);
|
||||
|
||||
/// <summary>
|
||||
/// Using LoskService, global lock!
|
||||
/// </summary>
|
||||
/// <param name="callbackTransactionBody"></param>
|
||||
/// <param name="throwException"></param>
|
||||
/// <returns></returns>
|
||||
public bool TransactionSafe(Func<TransactionScope, bool> callbackTransactionBody, bool throwException = false)
|
||||
{
|
||||
using (LockService.SemaphoreSlim.UseWait())
|
||||
{
|
||||
return TransactionInner(callbackTransactionBody, throwException);
|
||||
}
|
||||
}
|
||||
|
||||
public Task<bool> TransactionAsync(Func<TransactionScope, Task<bool>> callbackTransactionBody, bool throwException = false)
|
||||
=> TaskHelper.ToThreadPoolTask(() => TransactionInnerAsync(callbackTransactionBody, throwException));
|
||||
|
||||
/// <summary>
|
||||
/// Using LoskService, global lock!
|
||||
/// </summary>
|
||||
/// <param name="callbackTransactionBody"></param>
|
||||
/// <param name="throwException"></param>
|
||||
/// <returns></returns>
|
||||
public Task<bool> TransactionSafeAsync(Func<TransactionScope, Task<bool>> callbackTransactionBody, bool throwException = false)
|
||||
{
|
||||
return TaskHelper.ToThreadPoolTask(async () =>
|
||||
{
|
||||
using (await LockService.SemaphoreSlim.UseWaitAsync())
|
||||
{
|
||||
return await TransactionInnerAsync(callbackTransactionBody, throwException);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<bool> TransactionInnerAsync(Func<TransactionScope, Task<bool>> callbackTransactionBody, bool throwException = false)
|
||||
{
|
||||
bool result;
|
||||
|
||||
try
|
||||
{
|
||||
using (var transaction = CreateTransactionScope())// new TransactionScope( /*TransactionScopeOption.RequiresNew, */TransactionScopeAsyncFlowOption.Enabled))
|
||||
{
|
||||
result = await callbackTransactionBody(transaction);
|
||||
|
||||
if (result) transaction.Complete();
|
||||
}
|
||||
|
||||
if (!result) Logger.Warning($"TransactionInnerAsync({this}) transaction ROLLBACK!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (throwException) throw;
|
||||
|
||||
result = false;
|
||||
Logger.Error($"TransactionInnerAsync({this}) transaction ROLLBACK! ex: {ex.Message}{AcEnv.NL}", ex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool TransactionInner(Func<TransactionScope, bool> callbackTransactionBody, bool throwException = false)
|
||||
{
|
||||
bool result;
|
||||
|
||||
try
|
||||
{
|
||||
using(var transaction = CreateTransactionScope()) //new TransactionScope( /*TransactionScopeOption.RequiresNew, */TransactionScopeAsyncFlowOption.Enabled)
|
||||
{
|
||||
result = callbackTransactionBody(transaction);
|
||||
|
||||
if (result) transaction.Complete();
|
||||
}
|
||||
|
||||
if (!result) Logger.Warning($"TransactionInner({this}) transaction ROLLBACK!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (throwException) throw;
|
||||
|
||||
result = false;
|
||||
Logger.Error($"TransactionInnerAsync({this}) transaction error! ex: {ex.Message}{AcEnv.NL}", ex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return GetType().Name;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +1,35 @@
|
|||
using System.Linq.Expressions;
|
||||
using AyCode.Interfaces.Entities;
|
||||
using AyCode.Interfaces.Entities;
|
||||
using AyCode.Interfaces.TimeStampInfo;
|
||||
using Mango.Nop.Core.Interfaces;
|
||||
using Mango.Nop.Core.Loggers;
|
||||
using Mango.Nop.Data.Interfaces;
|
||||
using Nop.Core;
|
||||
using Nop.Core.Caching;
|
||||
using Nop.Core.Configuration;
|
||||
using Nop.Core.Domain.Catalog;
|
||||
using Nop.Core.Domain.Common;
|
||||
using Nop.Core.Events;
|
||||
using Nop.Data;
|
||||
using Nop.Services.Logging;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Mango.Nop.Core.Repositories;
|
||||
namespace Mango.Nop.Data.Repositories;
|
||||
|
||||
public class MgDbTableBase<TEntity>(IEventPublisher eventPublisher, INopDataProvider dataProvider, IShortTermCacheManager shortTermCacheManager, IStaticCacheManager staticCacheManager, AppSettings appSettings, ILogger logger)
|
||||
public abstract class MgDbTableBase<TEntity>(IEventPublisher eventPublisher, INopDataProvider dataProvider, IShortTermCacheManager shortTermCacheManager, IStaticCacheManager staticCacheManager, AppSettings appSettings)
|
||||
: EntityRepository<TEntity>(eventPublisher, dataProvider, shortTermCacheManager, staticCacheManager, appSettings), IMgDbTableBase where TEntity : BaseEntity
|
||||
{
|
||||
protected ILogger Logger = logger;
|
||||
//protected ILogger Logger = logger;
|
||||
protected IEventPublisher EventPublisher = eventPublisher;
|
||||
protected INopDataProvider DataProvider = dataProvider;
|
||||
protected IShortTermCacheManager ShortTermCacheManager = shortTermCacheManager;
|
||||
|
||||
public virtual IQueryable<TEntity> GetAll() => Table;
|
||||
|
||||
#region SetTimeStampInfos
|
||||
|
||||
private static void SetTimeStampCreated(TEntity entity)
|
||||
{
|
||||
if (typeof(TEntity).GetInterface(nameof(ITimeStampCreated)) != null)
|
||||
{
|
||||
((entity as ITimeStampCreated)!).Created = DateTime.UtcNow;
|
||||
(entity as ITimeStampCreated)!.Created = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -33,12 +37,16 @@ public class MgDbTableBase<TEntity>(IEventPublisher eventPublisher, INopDataProv
|
|||
{
|
||||
if (typeof(TEntity).GetInterface(nameof(ITimeStampModified)) != null)
|
||||
{
|
||||
((entity as ITimeStampModified)!).Modified = DateTime.UtcNow;
|
||||
(entity as ITimeStampModified)!.Modified = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion SetTimeStampInfos
|
||||
|
||||
//TODO: Transaction-be tenni az event-eket! - J.
|
||||
|
||||
#region OnCrudEvents
|
||||
|
||||
private void OnInsert(IList<TEntity> entities)
|
||||
{
|
||||
foreach (var entity in entities) OnInsert(entity);
|
||||
|
|
@ -61,7 +69,7 @@ public class MgDbTableBase<TEntity>(IEventPublisher eventPublisher, INopDataProv
|
|||
{
|
||||
SetTimeStampModified(entity);
|
||||
|
||||
_staticCacheManager.ClearAsync().GetAwaiter().GetResult();
|
||||
_staticCacheManager.ClearAsync().GetAwaiter().GetResult(); //TODO: EZ MI??? - J.
|
||||
//_staticCacheManager.RemoveByPrefix(MgDbContextBase.AUCTION_PATTERN_KEY);
|
||||
}
|
||||
|
||||
|
|
@ -79,6 +87,7 @@ public class MgDbTableBase<TEntity>(IEventPublisher eventPublisher, INopDataProv
|
|||
|
||||
//_staticCacheManager.RemoveByPrefix(MgDbContextBase.AUCTION_PATTERN_KEY);
|
||||
}
|
||||
|
||||
#endregion OnCrudEvents
|
||||
|
||||
#region Insert
|
||||
|
|
@ -106,9 +115,11 @@ public class MgDbTableBase<TEntity>(IEventPublisher eventPublisher, INopDataProv
|
|||
OnInsert(entities);
|
||||
return base.InsertAsync(entities, publishEvent);
|
||||
}
|
||||
|
||||
#endregion Insert
|
||||
|
||||
#region Update
|
||||
|
||||
public override void Update(TEntity entity, bool publishEvent = true)
|
||||
{
|
||||
OnUpdate(entity);
|
||||
|
|
@ -132,29 +143,25 @@ public class MgDbTableBase<TEntity>(IEventPublisher eventPublisher, INopDataProv
|
|||
OnUpdate(entities);
|
||||
return base.UpdateAsync(entities, publishEvent);
|
||||
}
|
||||
|
||||
#endregion Update
|
||||
|
||||
#region Delete
|
||||
protected override Task DeleteAsync(IList<TEntity> entities)
|
||||
{
|
||||
OnDelete(entities);
|
||||
return base.DeleteAsync(entities);
|
||||
}
|
||||
|
||||
protected override Task DeleteAsync<T>(IList<T> entities)
|
||||
{
|
||||
if(typeof(T) == typeof(TEntity)) OnDelete(entities.Cast<TEntity>().ToList());
|
||||
|
||||
return base.DeleteAsync(entities);
|
||||
}
|
||||
|
||||
protected override IQueryable<TEntity> AddDeletedFilter(IQueryable<TEntity> query, in bool includeDeleted)
|
||||
{
|
||||
foreach (var entity in query) OnDelete(entity);
|
||||
//EZ NEM DELETE METHOD! A GET-EKNÉL HASZNÁLJA A ISFOTDELETE FILTER-HEZ! - J.
|
||||
//foreach (var entity in query) OnDelete(entity);
|
||||
|
||||
return base.AddDeletedFilter(query, in includeDeleted);
|
||||
}
|
||||
|
||||
public virtual async Task DeleteAsync(int entityId, bool publishEvent = true)
|
||||
{
|
||||
var entity = await GetByIdAsync(entityId);
|
||||
await DeleteAsync(entity, publishEvent);
|
||||
}
|
||||
|
||||
public override Task DeleteAsync(TEntity entity, bool publishEvent = true)
|
||||
{
|
||||
OnDelete(entity);
|
||||
|
|
@ -167,22 +174,46 @@ public class MgDbTableBase<TEntity>(IEventPublisher eventPublisher, INopDataProv
|
|||
return base.DeleteAsync(entities, publishEvent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// It does not call the PublishEvent() and OnDelete() functions!
|
||||
/// </summary>
|
||||
/// <param name="predicate"></param>
|
||||
/// <returns></returns>
|
||||
public override Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate)
|
||||
{
|
||||
//TODO:ide mit kéne?! - J.
|
||||
return base.DeleteAsync(predicate);
|
||||
}
|
||||
|
||||
public virtual async Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, bool publishEvent)
|
||||
{
|
||||
if (publishEvent)
|
||||
{
|
||||
var deletingEntities = await Table.Where(predicate).ToListAsync();
|
||||
await base.DeleteAsync(deletingEntities, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await DeleteAsync(predicate);
|
||||
}
|
||||
|
||||
public override void Delete(TEntity entity, bool publishEvent = true)
|
||||
{
|
||||
OnDelete(entity);
|
||||
base.Delete(entity, publishEvent);
|
||||
}
|
||||
|
||||
public override void Delete(IList<TEntity> entities, bool publishEvent = true)
|
||||
{
|
||||
OnDelete(entities);
|
||||
base.Delete(entities, publishEvent);
|
||||
}
|
||||
|
||||
public override int Delete(Expression<Func<TEntity, bool>> predicate)
|
||||
{
|
||||
//TODO:ide mit kéne?! - J.
|
||||
return base.Delete(predicate);
|
||||
}
|
||||
|
||||
#endregion Delete
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
using System.Linq.Expressions;
|
||||
using LinqToDB;
|
||||
using Mango.Nop.Core.Loggers;
|
||||
using Nop.Core;
|
||||
using Nop.Core.Caching;
|
||||
using Nop.Core.Configuration;
|
||||
using Nop.Core.Domain.Orders;
|
||||
using Nop.Core.Events;
|
||||
using Nop.Data;
|
||||
|
||||
namespace Mango.Nop.Data.Repositories;
|
||||
|
||||
public abstract class MgDtoDbTableBase<TDtoEntity, TMainEntity> : MgDbTableBase<TDtoEntity> where TDtoEntity : BaseEntity/*, IMgModelDtoBase<TDtoEntity>*/ where TMainEntity : BaseEntity
|
||||
{
|
||||
public Type MainEntityType { get; } = typeof(Order);
|
||||
|
||||
public MgDtoDbTableBase(IEventPublisher eventPublisher, INopDataProvider dataProvider, IShortTermCacheManager shortTermCacheManager, IStaticCacheManager staticCacheManager, AppSettings appSettings)
|
||||
: base(eventPublisher, dataProvider, shortTermCacheManager, staticCacheManager, appSettings)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<TMainEntity?> GetMainEntityById(int id) => await _dataProvider.GetTable<TMainEntity>().FirstOrDefaultAsync(x => x.Id == id);
|
||||
|
||||
public virtual async Task<int> DeleteMainEntityById(int id, bool publishEvent = true)
|
||||
{
|
||||
var affectedRows = 0;
|
||||
|
||||
var mainEntity = await GetMainEntityById(id);
|
||||
if (mainEntity == null) return affectedRows;
|
||||
|
||||
affectedRows = await _dataProvider.GetTable<TMainEntity>().DeleteAsync(x => x.Id == id);
|
||||
|
||||
if (publishEvent) await _eventPublisher.PublishAsync(new EntityDeletedEvent<TMainEntity>(mainEntity));
|
||||
return affectedRows;
|
||||
}
|
||||
|
||||
public Task HandleEventAsync(EntityDeletedEvent<TDtoEntity> eventMessage)
|
||||
=> throw new Exception($"To delete, you must use the DeleteMainEntityById<{MainEntityType.Name}> function instead of '{typeof(TDtoEntity).Name}'!");
|
||||
|
||||
public async Task HandleEventAsync(EntityInsertedEvent<TDtoEntity> eventMessage)
|
||||
{
|
||||
var mainEntity = await GetMainEntityById(eventMessage.Entity.Id);
|
||||
if (mainEntity == null) throw new Exception($"MgDtoDbTableBase<{typeof(TDtoEntity).Name}, {MainEntityType.Name}>->EntityInsertedEvent<{typeof(TDtoEntity).Name}>(); (mainEntity == null); Id: {eventMessage.Entity.Id}");
|
||||
|
||||
await _eventPublisher.PublishAsync(new EntityInsertedEvent<TMainEntity>(mainEntity));
|
||||
}
|
||||
|
||||
public async Task HandleEventAsync(EntityUpdatedEvent<TDtoEntity> eventMessage)
|
||||
{
|
||||
var mainEntity = await GetMainEntityById(eventMessage.Entity.Id);
|
||||
if (mainEntity == null) throw new Exception($"MgDtoDbTableBase<{typeof(TDtoEntity).Name}, {MainEntityType.Name}>->EntityUpdatedEvent<{typeof(TDtoEntity).Name}>(); (mainEntity == null); Id: {eventMessage.Entity.Id}");
|
||||
|
||||
await _eventPublisher.PublishAsync(new EntityUpdatedEvent<TMainEntity>(mainEntity));
|
||||
}
|
||||
|
||||
public override Task DeleteAsync(int entityId, bool publishEvent = true) => DeleteMainEntityById(entityId, publishEvent);
|
||||
|
||||
public override Task DeleteAsync(TDtoEntity entity, bool publishEvent = true) => DeleteMainEntityById(entity.Id, publishEvent);
|
||||
|
||||
public override Task DeleteAsync(IList<TDtoEntity> entities, bool publishEvent = true)
|
||||
=> throw new Exception($"To delete, you must use the DeleteMainEntityById<{MainEntityType.Name}> function instead of '{typeof(TDtoEntity).Name}'!");
|
||||
|
||||
public override Task<int> DeleteAsync(Expression<Func<TDtoEntity, bool>> predicate)
|
||||
=> throw new Exception($"To delete, you must use the DeleteMainEntityById<{MainEntityType.Name}> function instead of '{typeof(TDtoEntity).Name}'!");
|
||||
|
||||
public override Task DeleteAsync(Expression<Func<TDtoEntity, bool>> predicate, bool publishEvent)
|
||||
=> throw new Exception($"To delete, you must use the DeleteMainEntityById<{MainEntityType.Name}> function instead of '{typeof(TDtoEntity).Name}'!");
|
||||
|
||||
public override void Delete(TDtoEntity entity, bool publishEvent = true)
|
||||
=> throw new Exception($"To delete, you must use the DeleteMainEntityById<{MainEntityType.Name}> function instead of '{typeof(TDtoEntity).Name}'!");
|
||||
|
||||
public override void Delete(IList<TDtoEntity> entities, bool publishEvent = true)
|
||||
=> throw new Exception($"To delete, you must use the DeleteMainEntityById<{MainEntityType.Name}> function instead of '{typeof(TDtoEntity).Name}'!");
|
||||
|
||||
public override int Delete(Expression<Func<TDtoEntity, bool>> predicate)
|
||||
=> throw new Exception($"To delete, you must use the DeleteMainEntityById<{MainEntityType.Name}> function instead of '{typeof(TDtoEntity).Name}'!");
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# Mango.Nop.Data documentation
|
||||
|
||||
Topic documentation for the `Mango.Nop.Data` project (Layer 2, data access layer).
|
||||
|
||||
## Reference docs (flat)
|
||||
|
||||
- [`REPOSITORIES.md`](REPOSITORIES.md) — Repository pattern usage
|
||||
- [`TRANSACTIONS.md`](TRANSACTIONS.md) — Transaction pattern usage
|
||||
|
||||
## Navigation
|
||||
|
||||
Per the AI Agent Core Protocol (folder navigation rule), start from this README when browsing `docs/`. All docs at this level are single-file reference.
|
||||
|
||||
## See also
|
||||
|
||||
- **Repo-level conventions**: `../../docs/CONVENTIONS.md`
|
||||
- **Core DTOs**: `../Mango.Nop.Core/docs/DTOS.md`
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
# Repository Pattern
|
||||
|
||||
> Part of `Mango.Nop.Data`. See `Mango.Nop.Data/README.md` for project overview.
|
||||
> For transaction patterns see `docs/TRANSACTIONS.md`.
|
||||
|
||||
## MgDbTableBase\<TEntity\>
|
||||
|
||||
Repository base wrapping nopCommerce `EntityRepository<TEntity>`.
|
||||
|
||||
**Constructor:**
|
||||
```csharp
|
||||
MgDbTableBase(IEventPublisher, INopDataProvider, IShortTermCacheManager, IStaticCacheManager, AppSettings)
|
||||
```
|
||||
|
||||
### Features
|
||||
|
||||
| Feature | Detail |
|
||||
|---|---|
|
||||
| `GetAll()` | Returns `IQueryable<TEntity>` from `Table` property |
|
||||
| Automatic timestamps | `ITimeStampCreated.Created` set on insert; `ITimeStampModified.Modified` set on insert/update |
|
||||
| CRUD hooks | `OnInsert(entity)`, `OnUpdate(entity)`, `OnDelete(entity)` — virtual, overridable |
|
||||
| Cache clear | `OnUpdate` clears `IStaticCacheManager` (currently clears all — TODO noted in code) |
|
||||
| All CRUD overrides | Overrides all sync/async `Insert`, `Update`, `Delete` methods to call hooks before `base.*` |
|
||||
| `DeleteAsync(int entityId)` | Convenience: load by id then delete |
|
||||
| `DeleteAsync(predicate, bool publishEvent)` | When `publishEvent=true`, loads entities first then deletes with events |
|
||||
|
||||
## MgDtoDbTableBase\<TDtoEntity, TMainEntity\>
|
||||
|
||||
DTO-aware repository for when the DTO entity (`TDtoEntity`) maps to a different main nopCommerce entity (`TMainEntity`). This is needed because LinqToDB tables are registered for DTOs, but nopCommerce events must fire on the main entity type.
|
||||
|
||||
### Features
|
||||
|
||||
| Feature | Detail |
|
||||
|---|---|
|
||||
| `GetMainEntityById(id)` | Loads `TMainEntity` from `INopDataProvider.GetTable<TMainEntity>()` |
|
||||
| `DeleteMainEntityById(id)` | Deletes the **main entity** (not the DTO) and publishes `EntityDeletedEvent<TMainEntity>` |
|
||||
| Delete overrides | **All Delete methods throw** — forces callers to use `DeleteMainEntityById()` instead |
|
||||
| Event bridging | `EntityInsertedEvent<TDtoEntity>` -> loads main entity -> publishes `EntityInsertedEvent<TMainEntity>` |
|
||||
| Event bridging | `EntityUpdatedEvent<TDtoEntity>` -> loads main entity -> publishes `EntityUpdatedEvent<TMainEntity>` |
|
||||
| Event bridging | `EntityDeletedEvent<TDtoEntity>` -> **throws** (must use `DeleteMainEntityById`) |
|
||||
|
||||
**Critical rule:** Never call `Delete` on a `MgDtoDbTableBase` repository directly. Always use `DeleteMainEntityById(int id)`.
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
# Transaction Pattern
|
||||
|
||||
> Part of `Mango.Nop.Data`. See `Mango.Nop.Data/README.md` for project overview.
|
||||
> For repository base classes see `docs/REPOSITORIES.md`.
|
||||
|
||||
## MgDbContextBase
|
||||
|
||||
Abstract database context base. NOT an EF Core DbContext — wraps `INopDataProvider` and `IRepository<T>` nopCommerce repos.
|
||||
|
||||
**Constructor:**
|
||||
```csharp
|
||||
MgDbContextBase(IRepository<Product>, IRepository<Order>, IRepository<OrderItem>, INopDataProvider, IMgLockService, ILogger)
|
||||
```
|
||||
|
||||
### Standard Repositories
|
||||
|
||||
| Property | Type |
|
||||
|---|---|
|
||||
| `Orders` | `IRepository<Order>` |
|
||||
| `OrderItems` | `IRepository<OrderItem>` |
|
||||
| `Products` | `IRepository<Product>` |
|
||||
| `DataProvider` | `INopDataProvider` (LinqToDB raw queries) |
|
||||
| `Logger` | Mango `ILogger` |
|
||||
| `LockService` | `IMgLockService` (global `SemaphoreSlim`) |
|
||||
|
||||
### 4 Transaction Methods
|
||||
|
||||
| Method | Lock | Async |
|
||||
|---|---|---|
|
||||
| `Transaction(callback)` | No | No |
|
||||
| `TransactionSafe(callback)` | `SemaphoreSlim` | No |
|
||||
| `TransactionAsync(callback)` | No | Yes (thread pool) |
|
||||
| `TransactionSafeAsync(callback)` | `SemaphoreSlim` | Yes (thread pool) |
|
||||
|
||||
**Callback contract:** `Func<TransactionScope, (Task<)bool(>)>` — return `true` to commit (`Complete()`), `false` to rollback.
|
||||
|
||||
**Isolation level:** `ReadCommitted`
|
||||
|
||||
**Error handling:** Catches exceptions, logs, returns `false` (unless `throwException = true`).
|
||||
|
||||
**TransactionSafe variants:** Use `LockService.SemaphoreSlim` for global serialization. Use for order creation, stock adjustment — any operation where concurrent modifications would corrupt data.
|
||||
|
||||
**Async variants:** Run on thread pool via `TaskHelper.ToThreadPoolTask()`.
|
||||
|
||||
## MgDalBase\<TDbContext\>
|
||||
|
||||
Data Access Layer orchestrator. Thin wrapper exposing:
|
||||
|
||||
| Property | Type | Purpose |
|
||||
|---|---|---|
|
||||
| `Name` | `string` | DAL instance name |
|
||||
| `Context` | `TDbContext` | The DB context (typed) |
|
||||
| `MutextLock` | `Mutex` | Cross-process locking |
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
namespace Mango.Nop.Services
|
||||
{
|
||||
public class Class1
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
using AyCode.Core.Helpers;
|
||||
using AyCode.Entities;
|
||||
using AyCode.Entities.Server.LogItems;
|
||||
using AyCode.Utils.Extensions;
|
||||
using LinqToDB;
|
||||
using LinqToDB.Data;
|
||||
using LinqToDB.DataProvider;
|
||||
using LinqToDB.DataProvider.SqlServer;
|
||||
using Mango.Nop.Data.Repositories;
|
||||
using Nop.Core;
|
||||
using Nop.Core.Domain.Common;
|
||||
using Nop.Core.Domain.Customers;
|
||||
using Nop.Core.Domain.Logging;
|
||||
using Nop.Data;
|
||||
using Nop.Data.DataProviders;
|
||||
using System.Data.Common;
|
||||
using System.Transactions;
|
||||
using LogLevel = AyCode.Core.Loggers.LogLevel;
|
||||
using LogLevelNop = Nop.Core.Domain.Logging.LogLevel;
|
||||
|
||||
namespace Mango.Nop.Services.Loggers
|
||||
{
|
||||
public interface INopLoggerMsSqlNopDataProvider
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class NopLoggerMsSqlNopDataProvider : MsSqlNopDataProvider, INopLoggerMsSqlNopDataProvider
|
||||
{
|
||||
//protected override IDataProvider LinqToDbDataProvider => SqlServerTools.GetDataProvider(SqlServerVersion.v2012, SqlServerProvider.MicrosoftDataSqlClient);
|
||||
|
||||
//protected override DbConnection CreateDbConnection(string connectionString = null)
|
||||
//{
|
||||
// connectionString = "Data Source=195.26.231.218;Initial Catalog=MangoManagement;Integrated Security=False;Persist Security Info=False;User ID=sa;Password=v6f_?xNfg9N1;Trust Server Certificate=True";
|
||||
// return base.GetInternalDbConnection(connectionString);
|
||||
//}
|
||||
|
||||
public void InsertLogItem<TEntity>(TEntity entity) where TEntity : notnull
|
||||
{
|
||||
using var transaction = CreateTransactionScope(TransactionScopeAsyncFlowOption.Suppress);
|
||||
using var dataContext = CreateDataConnection();
|
||||
|
||||
dataContext.Insert(entity);
|
||||
transaction.Complete();
|
||||
}
|
||||
|
||||
public async Task InsertLogItemAsync<TEntity>(TEntity entity) where TEntity : notnull
|
||||
{
|
||||
using var transaction = CreateTransactionScope(TransactionScopeAsyncFlowOption.Enabled);
|
||||
await using var dataContext = CreateDataConnection();
|
||||
|
||||
await dataContext.InsertAsync(entity);
|
||||
transaction.Complete();
|
||||
}
|
||||
|
||||
private static TransactionScope CreateTransactionScope(TransactionScopeAsyncFlowOption transactionScopeAsyncFlowOption)
|
||||
{
|
||||
var transactionOptions = new TransactionOptions
|
||||
{
|
||||
IsolationLevel = IsolationLevel.ReadUncommitted,
|
||||
Timeout = TransactionManager.DefaultTimeout
|
||||
};
|
||||
|
||||
return new TransactionScope(TransactionScopeOption.Suppress, transactionOptions, transactionScopeAsyncFlowOption);
|
||||
}
|
||||
}
|
||||
|
||||
public class NopLogWriter : AcLogItemWriterBase<AcLogItem>//AcTextLogWriterBase
|
||||
{
|
||||
private readonly SemaphoreSlim _lockSlim = new(0);
|
||||
|
||||
private NopLoggerMsSqlNopDataProvider _dataProvider;
|
||||
private readonly global::Nop.Services.Logging.ILogger _nopLogger;
|
||||
|
||||
protected readonly CommonSettings _commonSettings;
|
||||
protected readonly CustomerSettings _customerSettings;
|
||||
|
||||
protected readonly IWebHelper _webHelper;
|
||||
|
||||
public NopLogWriter(INopLoggerMsSqlNopDataProvider nopLoggerMsSqlNopDataProvider, CommonSettings commonSettings, CustomerSettings customerSettings, IWebHelper webHelper,
|
||||
global::Nop.Services.Logging.ILogger nopLogger) : this(nopLoggerMsSqlNopDataProvider, commonSettings, customerSettings, webHelper,nopLogger, null)
|
||||
{ }
|
||||
|
||||
public NopLogWriter(INopLoggerMsSqlNopDataProvider nopLoggerMsSqlNopDataProvider,
|
||||
CommonSettings commonSettings, CustomerSettings customerSettings, IWebHelper webHelper, global::Nop.Services.Logging.ILogger nopLogger, string? categoryName = null) : base(categoryName)
|
||||
{
|
||||
_nopLogger = nopLogger;
|
||||
_dataProvider = (NopLoggerMsSqlNopDataProvider)nopLoggerMsSqlNopDataProvider;
|
||||
|
||||
_commonSettings = commonSettings;
|
||||
_customerSettings = customerSettings;
|
||||
_webHelper = webHelper;
|
||||
}
|
||||
|
||||
//public NopLogWriter(ILogger nopLogger, AppType appType, LogLevel logLevel, string? categoryName = null) : base(appType, logLevel, categoryName)
|
||||
//{
|
||||
// _nopLogger=nopLogger;
|
||||
//}
|
||||
|
||||
protected override void WriteLogItemCallback(AcLogItem logItem)
|
||||
{
|
||||
//using (_lockSlim.UseWait())
|
||||
{
|
||||
switch (logItem.LogLevel)
|
||||
{
|
||||
case LogLevel.Detail:
|
||||
case LogLevel.Trace:
|
||||
case LogLevel.Debug:
|
||||
//if (_nopLogger.IsEnabled(LogLevelNop.Debug)) InsertLog(LogLevelNop.Debug, logItem.Text, logItem.Exception, null);
|
||||
//break;
|
||||
case LogLevel.Info:
|
||||
if (_nopLogger.IsEnabled(LogLevelNop.Information)) InsertLog(LogLevelNop.Information, logItem.Text, logItem.Exception, null);
|
||||
//_nopLogger.Information(logItem.Text); //.Forget();
|
||||
break;
|
||||
case LogLevel.Suggest:
|
||||
case LogLevel.Warning:
|
||||
if (_nopLogger.IsEnabled(LogLevelNop.Warning)) InsertLog(LogLevelNop.Warning, logItem.Text, logItem.Exception, null);
|
||||
//_nopLogger.Warning(logItem.Text); //.Forget();
|
||||
break;
|
||||
case LogLevel.Error:
|
||||
if (_nopLogger.IsEnabled(LogLevelNop.Error)) InsertLog(LogLevelNop.Error, logItem.Text, logItem.Exception, null);
|
||||
//_nopLogger.Error(logItem.Text); //.Forget();//, logItem.Exception);
|
||||
break;
|
||||
case LogLevel.Disabled:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InsertLog(LogLevelNop logLevel, string shortMessage, string fullMessage = "", Customer customer = null)
|
||||
{
|
||||
//check ignore word/phrase list?
|
||||
//if (IgnoreLog(shortMessage) || IgnoreLog(fullMessage))
|
||||
// return;
|
||||
|
||||
_dataProvider.InsertLogItem(PrepareLog(logLevel, shortMessage, fullMessage, customer));
|
||||
//_dataProvider.InsertLogItemAsync(PrepareLog(logLevel, shortMessage, fullMessage, customer)).Forget();
|
||||
}
|
||||
|
||||
private Log PrepareLog(LogLevelNop logLevel, string shortMessage, string fullMessage = "", Customer customer = null)
|
||||
{
|
||||
return new Log
|
||||
{
|
||||
LogLevel = logLevel,
|
||||
ShortMessage = shortMessage,
|
||||
FullMessage = fullMessage,
|
||||
IpAddress = _customerSettings.StoreIpAddresses ? _webHelper.GetCurrentIpAddress() : string.Empty,
|
||||
CustomerId = customer?.Id,
|
||||
PageUrl = _webHelper.GetThisPageUrl(true),
|
||||
ReferrerUrl = _webHelper.GetUrlReferrer(),
|
||||
CreatedOnUtc = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,46 +1,56 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<BaseOutputPath>bin\FruitBank</BaseOutputPath>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<CopyRefAssembliesToPublishDirectory>true</CopyRefAssembliesToPublishDirectory>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MessagePack.Annotations" Version="2.5.192" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MessagePack" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.11" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\NopCommerce\Libraries\Nop.Core\Nop.Core.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\NopCommerce\Libraries\Nop.Data\Nop.Data.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\NopCommerce\Libraries\Nop.Services\Nop.Services.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\NopCommerce\Presentation\Nop.Web.Framework\Nop.Web.Framework.csproj" />
|
||||
<ProjectReference Include="..\Mango.Nop.Core\Mango.Nop.Core.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\FruitBank\Libraries\Nop.Core\Nop.Core.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\FruitBank\Libraries\Nop.Data\Nop.Data.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\FruitBank\Libraries\Nop.Services\Nop.Services.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\FruitBank\Presentation\Nop.Web.Framework\Nop.Web.Framework.csproj" />
|
||||
<ProjectReference Include="..\Mango.Nop.Core\Mango.Nop.Core.csproj" />
|
||||
<ProjectReference Include="..\Mango.Nop.Data\Mango.Nop.Data.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="AyCode.Core">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\Debug\net8.0\AyCode.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Core.Server">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\Debug\net8.0\AyCode.Core.Server.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Entities">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\Debug\net8.0\AyCode.Entities.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Entities.Server">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\Debug\net8.0\AyCode.Entities.Server.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Interfaces">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\Debug\net8.0\AyCode.Interfaces.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Interfaces.Server">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\Debug\net8.0\AyCode.Interfaces.Server.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Utils">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\Debug\net8.0\AyCode.Utils.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="AyCode.Core">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Core.Server">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Core.Server.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Entities">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Entities.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Entities.Server">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Entities.Server.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Interfaces">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Interfaces.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Interfaces.Server">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Interfaces.Server.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="AyCode.Utils">
|
||||
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Utils.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="docs\**\*.md" />
|
||||
<None Include="**\README.md" Exclude="$(DefaultItemExcludes);docs\**" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using AyCode.Core.Loggers;
|
||||
using Mango.Nop.Core.Interfaces;
|
||||
using Mango.Nop.Core.Loggers;
|
||||
using Mango.Nop.Data.Interfaces;
|
||||
using Mango.Nop.Data.Repositories;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Nop.Core.Domain.Catalog;
|
||||
using Nop.Core.Domain.Customers;
|
||||
using Nop.Core.Domain.Orders;
|
||||
using Nop.Core.Events;
|
||||
using Nop.Services.Events;
|
||||
using Nop.Services.Logging;
|
||||
using Nop.Web.Framework.Events;
|
||||
|
||||
namespace Mango.Nop.Services;
|
||||
|
|
@ -12,8 +16,9 @@ namespace Mango.Nop.Services;
|
|||
/// <summary>
|
||||
/// Represents plugin event consumer
|
||||
/// </summary>
|
||||
public class MgEventConsumer(IHttpContextAccessor httpContextAccessor, ILogger logger) :
|
||||
public abstract class MgEventConsumerBase(IMgDbContextBase ctx, IHttpContextAccessor httpContextAccessor, IEnumerable<IAcLogWriterBase> logWriters) :
|
||||
IConsumer<EntityUpdatedEvent<Product>>,
|
||||
IConsumer<EntityInsertedEvent<Product>>,
|
||||
IConsumer<CustomerRegisteredEvent>,
|
||||
IConsumer<OrderPlacedEvent>,
|
||||
IConsumer<PageRenderingEvent>,
|
||||
|
|
@ -24,28 +29,36 @@ public class MgEventConsumer(IHttpContextAccessor httpContextAccessor, ILogger l
|
|||
{
|
||||
#region Fields
|
||||
|
||||
protected ILogger Logger { get; } = logger;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
|
||||
protected ILogger Logger { get; } = new Logger<MgEventConsumerBase>(logWriters.ToArray());
|
||||
protected readonly IHttpContextAccessor HttpContextAccessor = httpContextAccessor;
|
||||
#endregion
|
||||
|
||||
protected virtual async Task<Product> CheckAndUpdateProductManageInventoryMethodToManageStock(Product product)
|
||||
{
|
||||
if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStock) return product;
|
||||
|
||||
product.ManageInventoryMethod = ManageInventoryMethod.ManageStock;
|
||||
await ctx.Products.UpdateAsync(product, false);
|
||||
|
||||
return product;
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task HandleEventAsync(EntityUpdatedEvent<Product> eventMessage)
|
||||
{
|
||||
}
|
||||
{ }
|
||||
|
||||
public virtual async Task HandleEventAsync(EntityInsertedEvent<Product> eventMessage)
|
||||
{ }
|
||||
|
||||
public virtual async Task HandleEventAsync(CustomerRegisteredEvent eventMessage)
|
||||
{
|
||||
}
|
||||
{ }
|
||||
|
||||
public virtual async Task HandleEventAsync(OrderPlacedEvent eventMessage)
|
||||
{
|
||||
}
|
||||
{ }
|
||||
|
||||
public virtual async Task HandleEventAsync(PageRenderingEvent eventMessage)
|
||||
{
|
||||
}
|
||||
{ }
|
||||
|
||||
public virtual async Task HandleEventAsync(ProductSearchEvent eventMessage)
|
||||
{
|
||||
}
|
||||
{ }
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
namespace Mango.Nop.Services;
|
||||
|
||||
public abstract class MgLockService : IMgLockService
|
||||
{
|
||||
public SemaphoreSlim SemaphoreSlim { get; protected init; }
|
||||
|
||||
protected MgLockService() : this(new SemaphoreSlim(1))
|
||||
{
|
||||
}
|
||||
|
||||
protected MgLockService(SemaphoreSlim semaphoreSlim)
|
||||
{
|
||||
SemaphoreSlim = semaphoreSlim;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using Mango.Nop.Core.Services;
|
||||
|
||||
namespace Mango.Nop.Services;
|
||||
|
||||
public abstract class MgLockServiceBase : IMgLockService
|
||||
{
|
||||
public SemaphoreSlim SemaphoreSlim { get; protected init; }
|
||||
|
||||
protected MgLockServiceBase() : this(new SemaphoreSlim(1))
|
||||
{
|
||||
}
|
||||
|
||||
protected MgLockServiceBase(SemaphoreSlim semaphoreSlim)
|
||||
{
|
||||
SemaphoreSlim = semaphoreSlim;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
namespace Mango.Nop.Services;
|
||||
|
||||
public abstract class MgSessionItem(string sessionKey) : IMgSessionItem
|
||||
public abstract class MgSessionItemBase(string sessionKey) : IMgSessionItem
|
||||
{
|
||||
public string SessionId { get; protected set; } = sessionKey;
|
||||
public string? SignaRConnectionId { get; set; }
|
||||
|
|
@ -3,7 +3,7 @@ using AyCode.Utils.Extensions;
|
|||
|
||||
namespace Mango.Nop.Services;
|
||||
|
||||
public abstract class MgSessionService<TSessionItem> : IMgSessionService<TSessionItem> where TSessionItem : class, IMgSessionItem
|
||||
public abstract class MgSessionServiceBase<TSessionItem> : IMgSessionService<TSessionItem> where TSessionItem : class, IMgSessionItem
|
||||
{
|
||||
protected ConcurrentDictionary<string, TSessionItem> Sessions { get; } = new();
|
||||
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# Mango.Nop.Services
|
||||
|
||||
@project {
|
||||
type = "framework"
|
||||
own-dep-projects = [
|
||||
"AyCode.Core, AyCode.Core.Server, AyCode.Entities, AyCode.Entities.Server, AyCode.Interfaces, AyCode.Interfaces.Server, AyCode.Utils (in AyCode.Core repo)"
|
||||
]
|
||||
}
|
||||
|
||||
Service base classes for nopCommerce plugin development — background tasks, session management, events, locking, logging. **net9.0**.
|
||||
|
||||
## Documentation
|
||||
|
||||
| Document | Topic |
|
||||
|---|---|
|
||||
| `SERVICES.md` | MgBackgroundServiceBase, MgSessionServiceBase, MgEventConsumerBase, MgLockServiceBase |
|
||||
| `LOGGING/README.md` | NopLogWriter — AyCode-to-nopCommerce log bridge, TransactionScope(Suppress) |
|
||||
|
||||
## Folder Structure
|
||||
|
||||
| Folder | Purpose |
|
||||
|---|---|
|
||||
| `Loggers/` | `NopLogWriter`, `NopLoggerMsSqlNopDataProvider` — AyCode -> nopCommerce log bridge |
|
||||
| *(root)* | `MgBackgroundServiceBase`, `MgSessionServiceBase`, `MgEventConsumerBase`, `MgLockServiceBase`, interfaces |
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `Mango.Nop.Core`, `Mango.Nop.Data` (ProjectReferences)
|
||||
- `Nop.Core`, `Nop.Data`, `Nop.Services`, `Nop.Web.Framework` (nopCommerce ProjectReferences)
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
# NopLogWriter — Logging Bridge
|
||||
|
||||
> Part of `Mango.Nop.Services`. See `Mango.Nop.Services/README.md` for project overview.
|
||||
> For AyCode base logging types (`AcLogItemWriterBase`, `AcLogItem`, log levels) see `AyCode.Core/AyCode.Core/docs/LOGGING/README.md`.
|
||||
|
||||
## Overview
|
||||
|
||||
Bridges AyCode logging to nopCommerce's `Nop.Core.Domain.Logging.Log` table via direct DB insert.
|
||||
|
||||
## NopLogWriter
|
||||
|
||||
| Feature | Detail |
|
||||
|---|---|
|
||||
| Inherits | `AcLogItemWriterBase<AcLogItem>` (AyCode.Core) |
|
||||
| Log level mapping | AyCode `Detail/Trace/Debug/Info` -> nopCommerce `Information`; `Suggest/Warning` -> `Warning`; `Error` -> `Error` |
|
||||
| Direct DB insert | Uses `NopLoggerMsSqlNopDataProvider` with `TransactionScope(Suppress)` to avoid transaction conflicts |
|
||||
|
||||
**Constructor:** `(INopLoggerMsSqlNopDataProvider, CommonSettings, CustomerSettings, IWebHelper, Nop.Services.Logging.ILogger, string? categoryName)`
|
||||
|
||||
## NopLoggerMsSqlNopDataProvider
|
||||
|
||||
Extends `MsSqlNopDataProvider`. Provides isolated DB access for log writes.
|
||||
|
||||
| Method | Purpose |
|
||||
|---|---|
|
||||
| `InsertLogItem<T>()` | Sync insert with own `TransactionScope(Suppress, ReadUncommitted)` |
|
||||
| `InsertLogItemAsync<T>()` | Async insert with own `TransactionScope(Suppress, ReadUncommitted)` |
|
||||
|
||||
The `TransactionScope(Suppress)` is critical — without it, log writes would participate in the caller's transaction, causing nesting conflicts and potential deadlocks.
|
||||
|
||||
## Integration Point
|
||||
|
||||
```
|
||||
Application code -> Mango.Nop.Core.Loggers.ILogger (extends IAcLoggerBase)
|
||||
-> Logger<TCategory> (extends AcLoggerBase, delegates to IAcLogWriterBase[])
|
||||
-> NopLogWriter -> Nop Log table (direct SQL insert)
|
||||
-> [Other AyCode log writers: console, file, SignalR, etc.]
|
||||
```
|
||||
|
||||
**Exception:** `MgBackgroundServiceBase` uses `Nop.Services.Logging.ILogger` directly (nopCommerce logger), not the Mango wrapper.
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# Mango.Nop.Services documentation
|
||||
|
||||
Topic documentation for the `Mango.Nop.Services` project (Layer 2, service patterns).
|
||||
|
||||
## Reference docs (flat)
|
||||
|
||||
- [`SERVICES.md`](SERVICES.md) — Service pattern usage
|
||||
|
||||
## Topic folders
|
||||
|
||||
- [`LOGGING/`](LOGGING/README.md) — Logger bridge between NopCommerce's logger and AyCode.Core's logger (project-specific variant)
|
||||
|
||||
## Navigation
|
||||
|
||||
Per the AI Agent Core Protocol (folder navigation rule), start from this README when browsing `docs/`. Single-file reference docs remain flat; project-specific variants of framework topics (like LOGGING) live in named subfolders.
|
||||
|
||||
## See also
|
||||
|
||||
- **Base logger** (framework): `../../../../../Aycode/Source/AyCode.Core/AyCode.Core/docs/LOGGING/README.md`
|
||||
- **Remote logger variant**: `../../../../../Aycode/Source/AyCode.Core/AyCode.Services/docs/LOGGING/README.md`
|
||||
- **Server-side logger variant**: `../../../../../Aycode/Source/AyCode.Core/AyCode.Core.Server/docs/LOGGING/README.md`
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
# Service Base Classes
|
||||
|
||||
> Part of `Mango.Nop.Services`. See `Mango.Nop.Services/README.md` for project overview.
|
||||
> For logging bridge see `docs/LOGGING/README.md`.
|
||||
|
||||
## MgBackgroundServiceBase
|
||||
|
||||
Abstract hosted background service. Inherits `Microsoft.Extensions.Hosting.BackgroundService`.
|
||||
|
||||
**Constructor:** `(ILogger, IServiceProvider, int executeIntervalMs)`
|
||||
|
||||
| Feature | Detail |
|
||||
|---|---|
|
||||
| Loop pattern | `ExecuteAsync` loops: `Task.Delay(ExecuteIntervalMs)` -> `OnExecuteAsync()`, with pause support |
|
||||
| `OnExecuteAsync(CancellationToken)` | Abstract — subclass implements the actual work |
|
||||
| `Pause(bool)` | Pauses/resumes the loop without stopping the service |
|
||||
| `ExecuteIntervalMs` | Configurable interval between iterations |
|
||||
| Exception handling | Catches and logs errors per iteration, never crashes the loop |
|
||||
| Logging | Uses nopCommerce `Nop.Services.Logging.ILogger` (**not** `Mango.Nop.Core.Loggers.ILogger`) |
|
||||
|
||||
**Interface:** `IMgBackgroundService : IHostedService, IDisposable`
|
||||
|
||||
## MgSessionServiceBase\<TSessionItem\>
|
||||
|
||||
In-memory session management using `ConcurrentDictionary<string, TSessionItem>`.
|
||||
|
||||
| Method | Signature | Purpose |
|
||||
|---|---|---|
|
||||
| `GetOrCreateSessionItem` | `(string sessionId) -> TSessionItem?` | Get existing or create new via `Activator.CreateInstance` |
|
||||
| `TryAddSessionItem` | `(TSessionItem) -> bool` | Add if not exists, throws if duplicate |
|
||||
| `TryGetSessionItem` | `(string sessionId, out TSessionItem) -> bool` | Try-pattern lookup |
|
||||
| `TryRemoveSessionItem` | `(string sessionId, out TSessionItem) -> bool` | Remove and return |
|
||||
| `TryGetSessionItemBySignlaRConnectionId` | `(string connectionId, out TSessionItem?) -> bool` | Find session by SignalR connection |
|
||||
| `Count` | `() -> int` | Active session count |
|
||||
|
||||
**Interface:** `IMgSessionService<TSessionItem> where TSessionItem : IMgSessionItem`
|
||||
|
||||
## MgSessionItemBase
|
||||
|
||||
Base session item. Primary constructor: `(string sessionKey)`.
|
||||
|
||||
| Property | Type | Purpose |
|
||||
|---|---|---|
|
||||
| `SessionId` | `string` | Unique session identifier |
|
||||
| `SignaRConnectionId` | `string?` | Associated SignalR connection ID |
|
||||
| `RequestCount` | `int` | Request counter |
|
||||
|
||||
**Interface:** `IMgSessionItem` — `SessionId`, `SignaRConnectionId`, `RequestCount`
|
||||
|
||||
## MgEventConsumerBase
|
||||
|
||||
Abstract nopCommerce event consumer. Subscribes to:
|
||||
|
||||
| Event | Handler |
|
||||
|---|---|
|
||||
| `EntityUpdatedEvent<Product>` | `HandleEventAsync(...)` — virtual, empty default |
|
||||
| `EntityInsertedEvent<Product>` | `HandleEventAsync(...)` — virtual, empty default |
|
||||
| `CustomerRegisteredEvent` | `HandleEventAsync(...)` — virtual, empty default |
|
||||
| `OrderPlacedEvent` | `HandleEventAsync(...)` — virtual, empty default |
|
||||
| `PageRenderingEvent` | `HandleEventAsync(...)` — virtual, empty default |
|
||||
| `ProductSearchEvent` | `HandleEventAsync(...)` — virtual, empty default |
|
||||
|
||||
Built-in helper: `CheckAndUpdateProductManageInventoryMethodToManageStock(Product)` — ensures product has `ManageStock` inventory method.
|
||||
|
||||
**Constructor:** `(IMgDbContextBase ctx, IHttpContextAccessor, IEnumerable<IAcLogWriterBase> logWriters)`
|
||||
|
||||
## MgLockServiceBase
|
||||
|
||||
Simple in-process lock using `SemaphoreSlim(1)`. Implements `IMgLockService` (defined in `Mango.Nop.Core.Services`).
|
||||
|
||||
Used by `MgDbContextBase.TransactionSafe*` variants for global serialization.
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# Mango.Nop Libraries
|
||||
|
||||
> For library domain rules see: `.github/copilot-instructions.md`
|
||||
> For detailed docs see: `docs/`
|
||||
|
||||
Shared nopCommerce extension libraries providing domain entities, DTOs, data access, and service base classes. All target **net9.0** (nopCommerce 4.80.9 requirement).
|
||||
|
||||
## Projects
|
||||
|
||||
| Project | Purpose | Key Types |
|
||||
|---|---|---|
|
||||
| `Mango.Nop.Core` | Domain entities, DTOs, interfaces, nopCommerce entity mirrors (zero nopCommerce runtime dep) | `MgEntityBase`, `ModelDtoBase<T>`, `MgOrderDto<,>`, `MgOrderItemDto<>`, `MgProductDto`, `CustomerDto`, `MgGenericAttributeDto`, `MgStockTaking<>`, `GenericAttributeExtensions` |
|
||||
| `Mango.Nop.Data` | Data access layer — repository base classes, DB context, transactions | `MgDbTableBase<T>`, `MgDtoDbTableBase<,>`, `MgDbContextBase`, `MgDalBase<T>` |
|
||||
| `Mango.Nop.Services` | Service base classes — background services, session, events, locking, logging | `MgBackgroundServiceBase`, `MgSessionServiceBase<T>`, `MgEventConsumerBase`, `MgLockServiceBase`, `NopLogWriter` |
|
||||
|
||||
## Dependency Graph
|
||||
|
||||
See `docs/ARCHITECTURE.md` for full dependency graph, project roles, DTO mapping strategies, and transaction patterns.
|
||||
|
||||
```
|
||||
AyCode.Core (DLL references)
|
||||
↑
|
||||
Mango.Nop.Core (net9.0, zero nopCommerce runtime dependency)
|
||||
↑
|
||||
Mango.Nop.Data (net9.0) → Nop.Core, Nop.Data
|
||||
↑
|
||||
Mango.Nop.Services (net9.0) → Nop.Core, Nop.Data, Nop.Services, Nop.Web.Framework
|
||||
```
|
||||
|
||||
## Reference Modes
|
||||
|
||||
- **Full stack** (ProjectReference, all 3 libraries) — for nopCommerce plugins that need entity access, data layer, and services.
|
||||
- **Types only** (DLL reference, `Mango.Nop.Core` only) — for projects that only need DTOs, entities, and interfaces without the nopCommerce runtime dependency.
|
||||
|
||||
## Documentation Map
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `.github/copilot-instructions.md` | **Domain rules** — single source of truth for all conventions |
|
||||
| `docs/ARCHITECTURE.md` | Dependency graph, project roles, DTO strategies, transaction patterns, logging architecture |
|
||||
| `docs/CONVENTIONS.md` | Naming, patterns, project boundaries, AyCode integration points |
|
||||
| `docs/GLOSSARY.md` | Term definitions — entity/DTO system, data access, services, AyCode types |
|
||||
| `Mango.Nop.Core/README.md` | Core project overview — entity hierarchy, loggers, extensions, models |
|
||||
| `Mango.Nop.Core/docs/DTOS.md` | DTO system — two mapping strategies, all DTO types, GenericAttribute access |
|
||||
| `Mango.Nop.Core/docs/NOP_DEPENDENCIES.md` | NopDependencies pattern — mirror copies, namespace rules, file list |
|
||||
| `Mango.Nop.Data/README.md` | Data project overview — repository and context hierarchy, interfaces |
|
||||
| `Mango.Nop.Data/docs/REPOSITORIES.md` | MgDbTableBase, MgDtoDbTableBase — CRUD hooks, timestamps, delete rules |
|
||||
| `Mango.Nop.Data/docs/TRANSACTIONS.md` | MgDbContextBase — 4 transaction methods, lock strategy, callback contract |
|
||||
| `Mango.Nop.Services/README.md` | Services project overview |
|
||||
| `Mango.Nop.Services/docs/SERVICES.md` | MgBackgroundServiceBase, MgSessionServiceBase, MgEventConsumerBase, MgLockServiceBase |
|
||||
| `Mango.Nop.Services/docs/LOGGING/README.md` | NopLogWriter — AyCode-to-nopCommerce log bridge |
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
# Architecture
|
||||
|
||||
## Dependency Graph
|
||||
|
||||
```
|
||||
AyCode.Core (net9.0, DLL refs: AyCode.Interfaces, .Entities, .Core, .Utils, + .Server variants)
|
||||
↑
|
||||
Mango.Nop.Core (net9.0, no nopCommerce runtime dependency)
|
||||
↑
|
||||
Mango.Nop.Data (net9.0) → Nop.Core, Nop.Data
|
||||
↑
|
||||
Mango.Nop.Services (net9.0) → Nop.Core, Nop.Data, Nop.Services, Nop.Web.Framework
|
||||
```
|
||||
|
||||
**Rule:** Dependencies flow upward only. `Mango.Nop.Core` has zero nopCommerce runtime dependency — it uses mirror copies in `NopDependencies/`.
|
||||
|
||||
## Project Roles
|
||||
|
||||
### Mango.Nop.Core — Domain Layer
|
||||
Zero nopCommerce runtime dependency. Contains:
|
||||
- **DTOs** (`Dtos/`) — `ModelDtoBase<TMainEntity>` with bidirectional entity mapping (`CopyEntityValuesToDto`, `CopyDtoValuesToEntity`, `CreateMainEntity`)
|
||||
- **Entities** (`Entities/`) — `MgEntityBase` (inherits `BaseEntity`, implements `IEntityInt`)
|
||||
- **NopDependencies** (`NopDependencies/`) — mirror copies of nopCommerce entities (`BaseEntity`, `Customer`, `Order`, `Product`, `GenericAttribute`, enums). Allows referencing without full nopCommerce dependency chain.
|
||||
- **Extensions** (`Extensions/`) — `GenericAttributeExtensions` for typed attribute access, `CollectionExtensionsNopBaseEntity` for collection operations
|
||||
- **Interfaces** (`Interfaces/`) — DTO interfaces, soft-delete, foreign key markers
|
||||
- **Models** (`Models/`) — Login request/response
|
||||
- **Loggers** (`Loggers/`) — `ILogger` abstraction wrapping AyCode `IAcLoggerBase`
|
||||
|
||||
### Mango.Nop.Data — Data Access Layer
|
||||
Wraps nopCommerce data infrastructure with custom base classes:
|
||||
- **`MgDbTableBase<TEntity>`** — extends nopCommerce `EntityRepository<TEntity>`, adds `GetAll()`, timestamp handling (`ITimeStampCreated`, `ITimeStampModified`), event publishing
|
||||
- **`MgDtoDbTableBase<TDtoEntity, TMainEntity>`** — DTO-aware repository. **Critical: Delete operations throw — must use `DeleteMainEntityById()`.** Event bridging: DTO events → main entity events
|
||||
- **`MgDbContextBase`** — DB context base with `Transaction/TransactionSafe/TransactionAsync/TransactionSafeAsync` methods. TransactionSafe variants use global `SemaphoreSlim` lock
|
||||
- **`MgDalBase<TDbContext>`** — Data Access Layer orchestrator with `Context`, `Name`, `MutextLock`
|
||||
|
||||
### Mango.Nop.Services — Service Layer
|
||||
Service base classes for nopCommerce plugin development:
|
||||
- **`MgBackgroundServiceBase`** — hosted background task with configurable interval, pause support, per-iteration error handling
|
||||
- **`MgSessionServiceBase<TSessionItem>`** / `MgSessionItemBase` — in-memory session management via `ConcurrentDictionary`, SignalR connection tracking
|
||||
- **`MgEventConsumerBase`** — nopCommerce entity event handler base (Product insert/update, CustomerRegistered, OrderPlaced, PageRendering, ProductSearch)
|
||||
- **`MgLockServiceBase`** — `SemaphoreSlim(1)` lock wrapper
|
||||
- **`NopLogWriter`** — logging bridge: AyCode log levels → nopCommerce `Log` table via direct DB insert with `TransactionScope(Suppress)`
|
||||
|
||||
## NopDependencies Pattern
|
||||
|
||||
`Mango.Nop.Core/NopDependencies/` contains mirror copies of nopCommerce entity classes with the **same namespace** as the original nopCommerce types:
|
||||
|
||||
```csharp
|
||||
// In NopDependencies/BaseEntity.cs — same namespace as nopCommerce
|
||||
namespace Nop.Core;
|
||||
public abstract partial class BaseEntity : IBaseEntity
|
||||
{
|
||||
public int Id { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
This allows `Mango.Nop.Core` to be referenced by projects that don't have a direct nopCommerce dependency (e.g., Blazor/MAUI clients), while maintaining type compatibility with the real nopCommerce entities at the server level.
|
||||
|
||||
**Files in NopDependencies:**
|
||||
- `BaseEntity.cs` + `IBaseEntity` — root entity base (`Nop.Core`)
|
||||
- `Catalogs/` — `Customer`, `CustomerRole`, `Order`, `OrderItem`, `OrderNote`, `Product`, `GenericAttribute`, `StockQuantityHistory`, `DiscountMapping`, `DiscountProductMapping`
|
||||
- Enums — `OrderStatus`, `PaymentStatus`, `ShippingStatus`, `ProductType`, `ManageInventoryMethod`, `BackorderMode`, `LowStockActivity`, `GiftCardType`, `RentalPricePeriod`, `RecurringProductCyclePeriod`, `DownloadActivationType`, `TaxDisplayType`, `VatNumberStatus`
|
||||
- Interfaces — `ISoftDeletedEntity`, `ILocalizedEntity`, `ISlugSupported`, `IAclSupported`, `IStoreMappingSupported`, `IDiscountSupported`
|
||||
|
||||
## DTO Mapping Strategies
|
||||
|
||||
Two patterns coexist:
|
||||
|
||||
### Strategy 1: `ModelDtoBase<T>` (simple DTOs)
|
||||
Used by: `CustomerDto`, `MgGenericAttributeDto`
|
||||
```
|
||||
ModelDtoBase → ModelDtoBase<Customer> → CustomerDto
|
||||
```
|
||||
- Manual `CopyEntityValuesToDto`/`CopyDtoValuesToEntity` overrides
|
||||
- No LinqToDB associations
|
||||
|
||||
### Strategy 2: `MgEntityBase` + `IModelDtoBase<T>` (complex DTOs)
|
||||
Used by: `MgOrderDto`, `MgOrderItemDto`, `MgStockQuantityHistoryDto`
|
||||
```
|
||||
BaseEntity → MgEntityBase → MgOrderDto<TOrderItemDto, TProductDto> : IModelDtoBase<Order>
|
||||
```
|
||||
- Uses `PropertyHelper.CopyPublicValueTypeProperties()` for bulk copy
|
||||
- LinqToDB `[Association]` navigation properties
|
||||
- Generic type parameters for child DTOs
|
||||
|
||||
### Strategy 3: Entity inheritance (MgProductDto)
|
||||
```
|
||||
BaseEntity → MgEntityBase → MgProductDto : IMgProductDto
|
||||
```
|
||||
- No `IModelDtoBase<Product>` (entity mapping methods are commented out)
|
||||
- Direct property declarations mirroring `Product` fields
|
||||
|
||||
## Transaction Pattern
|
||||
|
||||
`MgDbContextBase` provides 4 transaction methods:
|
||||
|
||||
| Method | Lock | Async |
|
||||
|---|---|---|
|
||||
| `Transaction(callback)` | No | No |
|
||||
| `TransactionSafe(callback)` | `SemaphoreSlim` | No |
|
||||
| `TransactionAsync(callback)` | No | Yes (thread pool) |
|
||||
| `TransactionSafeAsync(callback)` | `SemaphoreSlim` | Yes (thread pool) |
|
||||
|
||||
**Callback contract:** `Func<TransactionScope, (Task<)bool(>)>` — return `true` to commit, `false` to rollback.
|
||||
**Isolation level:** `ReadCommitted`
|
||||
**Error handling:** Catches exceptions, logs, returns `false` (unless `throwException = true`)
|
||||
|
||||
## Logging Architecture
|
||||
|
||||
Base logging infrastructure (`IAcLoggerBase`, `IAcLogWriterBase`, `AcLoggerBase`, `AcLogItemWriterBase`, `AcLogItem`) is defined in AyCode.Core — see `AyCode.Core/AyCode.Core/docs/LOGGING/README.md` for base types, log levels, and writer contracts.
|
||||
|
||||
This layer provides the nopCommerce-specific bridge:
|
||||
|
||||
```
|
||||
Application code → Mango.Nop.Core.Loggers.ILogger (extends IAcLoggerBase)
|
||||
→ Logger<TCategory> (extends AcLoggerBase, delegates to IAcLogWriterBase[])
|
||||
→ NopLogWriter (in Services, extends AcLogItemWriterBase<AcLogItem>) → Nop Log table (direct SQL insert)
|
||||
→ [Other AyCode log writers: console, file, SignalR, etc. — see AyCode.Core/AyCode.Core/docs/LOGGING/README.md]
|
||||
```
|
||||
|
||||
**Important:** `MgBackgroundServiceBase` uses nopCommerce's `Nop.Services.Logging.ILogger` directly (not the Mango wrapper). Other services use `Mango.Nop.Core.Loggers.ILogger`.
|
||||
|
||||
## Reference Modes
|
||||
|
||||
```
|
||||
Mango.Nop Libraries
|
||||
┌──────────────────────┐
|
||||
│ Core Data Services │
|
||||
└──┬──────┬──────┬─────┘
|
||||
│ │ │
|
||||
┌───────────────┤ │ │
|
||||
│ (DLL, Core │ │ │
|
||||
│ only) │ │ │
|
||||
▼ ▼ ▼ ▼
|
||||
Types-only clients Full-stack nopCommerce
|
||||
(Blazor/MAUI/etc.) plugins (server-side)
|
||||
```
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
# Conventions
|
||||
|
||||
## Naming
|
||||
|
||||
- **`Mg` prefix** for all custom types: `MgEntityBase`, `MgOrderDto`, `MgDbTableBase`, `MgDalBase`, etc.
|
||||
- **`I` + name** for interfaces: `IMgDalBase`, `IMgDbTableBase`, `IMgOrderDto`, `IMgLockService`.
|
||||
- **`Dto` suffix** for DTOs wrapping nopCommerce entities: `MgOrderDto`, `MgProductDto`, `MgOrderItemDto`.
|
||||
- **`DbTable` suffix** for repository classes: `MgDbTableBase`, `MgDtoDbTableBase`.
|
||||
- **`Base` suffix** for abstract base classes: `MgEntityBase`, `ModelDtoBase`, `MgBackgroundServiceBase`.
|
||||
- **`Nop` prefix** for nopCommerce bridge types: `NopLogWriter`, `NopLoggerMsSqlNopDataProvider`, `NopCommonConst`.
|
||||
|
||||
## XML Documentation
|
||||
|
||||
`<summary>` — brief, developer-facing, readable in VS IntelliSense tooltip. NO implementation details, NO wire-format / byte-level / perf specifics — those live in `docs/TOPIC/*.md`. Add `<example>` only when usage is non-obvious; otherwise omit.
|
||||
|
||||
## Patterns
|
||||
|
||||
### DTO Bidirectional Mapping
|
||||
- `ModelDtoBase<TMainEntity>` provides `CopyEntityValuesToDto(entity)`, `CopyDtoValuesToEntity(entity)`, and `CreateMainEntity()`. Override all three in concrete DTOs.
|
||||
- Simple DTOs (e.g. `CustomerDto`) — manual property-by-property copy in overrides.
|
||||
- Complex DTOs (e.g. `MgOrderDto`) — use `PropertyHelper.CopyPublicValueTypeProperties(source, target)` for automatic value-type property copy, then manually set reference/navigation properties.
|
||||
|
||||
### LinqToDB Associations
|
||||
- DTOs with navigation properties use `[Association(ThisKey, OtherKey, CanBeNull)]` from LinqToDB.
|
||||
- `ThisKey` = local FK property name, `OtherKey` = target entity property name (typically `Id` or via interface member like `nameof(IMgProductDto.Id)`).
|
||||
|
||||
### NopDependencies Mirror
|
||||
- Entity classes in `NopDependencies/` use the **same namespace** as the original nopCommerce types. Do not change namespaces.
|
||||
- All mirror classes are `partial` — they can be extended but should not be modified directly.
|
||||
|
||||
### GenericAttribute Typed Access
|
||||
- Use `GenericAttributeExtensions.GetValueOrDefault<T>()` / `GetValueOrNull<T>()` / `TryGetValue<T>()` instead of raw string parsing.
|
||||
- Use `AddNewGenericAttribute()` to create new attributes with automatic UTC timestamp.
|
||||
|
||||
### Repository Base Chain
|
||||
- `MgDbTableBase<TEntity>` → `EntityRepository<TEntity>` (nopCommerce). Override virtual `OnInsert`/`OnUpdate`/`OnDelete` hooks, don't replace the chain.
|
||||
- `MgDtoDbTableBase<TDtoEntity, TMainEntity>` → `MgDbTableBase<TDtoEntity>`. **Never call Delete directly** — always use `DeleteMainEntityById(int id)`.
|
||||
|
||||
### Timestamp Interfaces
|
||||
- Entities implementing `ITimeStampCreated` get `Created = DateTime.UtcNow` on insert.
|
||||
- Entities implementing `ITimeStampModified` get `Modified = DateTime.UtcNow` on insert and update.
|
||||
- `ITimeStampInfo` combines both (`Creator`, `Created`, `Modified`).
|
||||
|
||||
### Transaction Pattern
|
||||
- Use `MgDbContextBase.TransactionSafeAsync()` for operations needing global lock (order creation, stock adjustment).
|
||||
- Callback returns `bool` — `true` commits, `false` rolls back.
|
||||
- Exceptions are caught and logged by default (return `false`). Pass `throwException: true` only for fail-fast scenarios.
|
||||
|
||||
### Event Consumer Pattern
|
||||
- Inherit `MgEventConsumerBase` and override relevant `HandleEventAsync` methods.
|
||||
- Base class handles DI of `IMgDbContextBase`, `IHttpContextAccessor`, and `IAcLogWriterBase[]`.
|
||||
- Base provides `CheckAndUpdateProductManageInventoryMethodToManageStock(Product)` helper.
|
||||
|
||||
### Session Pattern
|
||||
- Inherit `MgSessionServiceBase<TSessionItem>` and `MgSessionItemBase`.
|
||||
- Sessions stored in `ConcurrentDictionary<string, TSessionItem>` — thread-safe, in-memory only.
|
||||
- Session items track `SessionId`, `SignaRConnectionId`, `RequestCount`.
|
||||
|
||||
### Logging
|
||||
- Base logging infrastructure (`IAcLoggerBase`, `IAcLogWriterBase`, `AcLoggerBase`, `AcLogItemWriterBase`) — see `AyCode.Core/AyCode.Core/docs/LOGGING/README.md`.
|
||||
- Services create loggers via `new Logger<TCategory>(logWriters.ToArray())` in constructor.
|
||||
- `logWriters` injected as `IEnumerable<IAcLogWriterBase>` — typically contains `NopLogWriter` + other writers.
|
||||
- **Exception:** `MgBackgroundServiceBase` uses `Nop.Services.Logging.ILogger` directly (nopCommerce logger), not the Mango wrapper.
|
||||
|
||||
### Serialization Attributes
|
||||
- `[ToonDescription(...)]` — AyCode metadata for AI/doc tooling. `Purpose`, `BusinessRule`, `TypeRelation`, `RelatedTypes` properties.
|
||||
- `[AcBinarySerializable(false, true, false, true, false)]` — AcBinarySerializer config (see `AyCode.Core/AyCode.Core/docs/BINARY/BINARY_FORMAT.md`). Parameters control serialization behavior for AcSignalR transport (see `AyCode.Core/AyCode.Services/docs/SIGNALR/README.md`).
|
||||
- LinqToDB `[Table(Name = "...")]` and `[Association(...)]` — DB mapping.
|
||||
|
||||
## Project Boundaries
|
||||
|
||||
- `Mango.Nop.Core` — NO nopCommerce runtime dependency. Only NopDependencies mirrors. No `Nop.Data`, `Nop.Services` references.
|
||||
- `Mango.Nop.Data` — depends on nopCommerce data layer (`Nop.Core`, `Nop.Data`). No `Nop.Services` reference.
|
||||
- `Mango.Nop.Services` — depends on full nopCommerce stack (includes `Nop.Services`, `Nop.Web.Framework`).
|
||||
|
||||
## AyCode Integration Points
|
||||
|
||||
Key AyCode types used across these libraries:
|
||||
- `IEntityInt` (AyCode.Interfaces.Entities) — `int Id` entity contract
|
||||
- `IBaseEntity` — defined locally in NopDependencies, mirrors AyCode's concept
|
||||
- `IAcLoggerBase`, `IAcLogWriterBase`, `AcLoggerBase`, `AcLogItemWriterBase` — logging infrastructure (see `AyCode.Core/AyCode.Core/docs/LOGGING/README.md`)
|
||||
- `IAcModelDtoBaseEmpty` — DTO marker interface
|
||||
- `IAcSoftRemoveEntity`, `IAcSoftRemoveEntityInt` — soft-delete contracts
|
||||
- `IForeignKey`, `IForeignCollection<T>` — FK marker interfaces
|
||||
- `ITimeStampCreated`, `ITimeStampModified`, `ITimeStampInfo` — timestamp contracts
|
||||
- `PropertyHelper.CopyPublicValueTypeProperties()` — reflection-based property copy
|
||||
- `TaskHelper.ToThreadPoolTask()` — wraps async work in thread pool
|
||||
- `AcConst` — abstract constants base
|
||||
- `ToonDescription` — metadata attribute for AI tooling
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
# Glossary
|
||||
|
||||
Terminology for the Mango.Nop Libraries. Read this before working on unfamiliar areas.
|
||||
|
||||
## Entity & DTO System
|
||||
|
||||
| Term | Definition |
|
||||
|---|---|
|
||||
| **BaseEntity** | nopCommerce root entity base with `int Id`. Mirror copy in `NopDependencies/`. Namespace: `Nop.Core`. |
|
||||
| **IBaseEntity** | Interface for `BaseEntity` (defined alongside it in NopDependencies). Only `int Id`. |
|
||||
| **MgEntityBase** | Custom entity base — inherits `BaseEntity`, implements `IEntityInt` (AyCode). `ToString()` → `"{TypeName}; Id: {Id}"`. |
|
||||
| **ModelDtoBase** | Non-generic DTO base — only `int Id`. Implements `IModelDtoBase`. |
|
||||
| **ModelDtoBase\<T\>** | Generic DTO base with bidirectional mapping: `CopyEntityValuesToDto()` reads from entity, `CopyDtoValuesToEntity()` writes to entity, `CreateMainEntity()` creates entity from DTO. Uses `Activator.CreateInstance<T>()`. |
|
||||
| **IModelDtoBase** | `IEntityInt` + `IModelDtoBaseEmpty`. Marker for all DTOs with `int Id`. |
|
||||
| **IModelDtoBase\<T\>** | Contract: `CreateMainEntity()`, `CopyDtoValuesToEntity()`, `CopyEntityValuesToDto()`. |
|
||||
| **NopDependencies** | Mirror copies of nopCommerce entity classes in `Mango.Nop.Core`. Same namespaces as originals. Eliminates full nopCommerce dependency for consumers that only need types. |
|
||||
| **GenericAttribute** | nopCommerce polymorphic key-value store. `KeyGroup` = owner type name, `EntityId` = owner ID, `Key` = attribute name, `Value` = string value. `CreatedOrUpdatedDateUTC` tracks last change. |
|
||||
| **PropertyHelper.CopyPublicValueTypeProperties** | AyCode utility — reflection-based copy of all public value-type properties between two objects. Used by complex DTOs (`MgOrderDto`, `MgOrderItemDto`). |
|
||||
| **ToonDescription** | AyCode metadata attribute for AI/doc tooling. Properties: `Purpose`, `BusinessRule`, `TypeRelation`, `RelatedTypes`. |
|
||||
| **AcBinarySerializable** | AyCode binary serialization attribute (AcBinarySerializer, see `AyCode.Core/AyCode.Core/docs/BINARY/BINARY_FORMAT.md`). Configures how a type is serialized for AcSignalR transport (see `AyCode.Core/AyCode.Services/docs/SIGNALR/README.md`). |
|
||||
|
||||
## Data Access
|
||||
|
||||
| Term | Definition |
|
||||
|---|---|
|
||||
| **MgDbTableBase\<T\>** | Repository base wrapping nopCommerce `EntityRepository<T>`. Adds `GetAll()` (`IQueryable<T>`), automatic timestamp management via `ITimeStampCreated`/`ITimeStampModified` interfaces, CRUD hooks (`OnInsert`, `OnUpdate`, `OnDelete`). |
|
||||
| **MgDtoDbTableBase\<TDto, TMain\>** | DTO-aware repository. **Delete operations throw** — must use `DeleteMainEntityById()`. Bridges DTO events → main entity events (`EntityInsertedEvent`, `EntityUpdatedEvent`). |
|
||||
| **MgDbContextBase** | Database context base. Exposes `IRepository<Order>`, `IRepository<OrderItem>`, `IRepository<Product>`, `INopDataProvider`. Provides 4 transaction methods (`Transaction`, `TransactionSafe`, `TransactionAsync`, `TransactionSafeAsync`). |
|
||||
| **MgDalBase\<T\>** | Data Access Layer orchestrator — `Name`, `Context` (typed `TDbContext`), `MutextLock` (cross-process `Mutex`). |
|
||||
| **TransactionSafe** | Transaction with global `SemaphoreSlim` lock. Prevents concurrent modifications. Use for order creation, stock changes. |
|
||||
| **EntityRepository\<T\>** | nopCommerce built-in repository base (`Nop.Data`). `MgDbTableBase` extends this. |
|
||||
| **INopDataProvider** | nopCommerce interface for raw LinqToDB data access. Used by `MgDbContextBase` and `MgDtoDbTableBase`. |
|
||||
|
||||
## Services
|
||||
|
||||
| Term | Definition |
|
||||
|---|---|
|
||||
| **MgBackgroundServiceBase** | Base for hosted background services. Loop: `Task.Delay(interval)` → `OnExecuteAsync()`. Supports pause. Per-iteration exception handling. Uses nopCommerce `ILogger`. |
|
||||
| **MgSessionServiceBase\<T\>** | In-memory session management via `ConcurrentDictionary<string, T>`. Thread-safe. Methods: `GetOrCreateSessionItem`, `TryAddSessionItem`, `TryGetSessionItem`, `TryRemoveSessionItem`, `TryGetSessionItemBySignlaRConnectionId`. |
|
||||
| **MgSessionItemBase** | Session item with `SessionId`, `SignaRConnectionId`, `RequestCount`. Primary constructor: `(string sessionKey)`. |
|
||||
| **MgEventConsumerBase** | nopCommerce event consumer base. Subscribes to: `EntityUpdatedEvent<Product>`, `EntityInsertedEvent<Product>`, `CustomerRegisteredEvent`, `OrderPlacedEvent`, `PageRenderingEvent`, `ProductSearchEvent`. All handlers virtual with empty default. |
|
||||
| **MgLockServiceBase** | `SemaphoreSlim(1)` wrapper. Implements `IMgLockService`. Used by `MgDbContextBase.TransactionSafe*`. |
|
||||
| **NopLogWriter** | Bridges AyCode logging (see `AyCode.Core/AyCode.Core/docs/LOGGING/README.md`) to nopCommerce `Log` table.
|
||||
| **NopLoggerMsSqlNopDataProvider** | Extends `MsSqlNopDataProvider`. Provides `InsertLogItem<T>()` / `InsertLogItemAsync<T>()` with own `TransactionScope(Suppress, ReadUncommitted)` to avoid transaction nesting. |
|
||||
|
||||
## AyCode Types (external, from DLL references)
|
||||
|
||||
| Term | Definition |
|
||||
|---|---|
|
||||
| **IEntityInt** | `int Id` entity contract (AyCode.Interfaces.Entities) |
|
||||
| **IAcLoggerBase** | Logger interface — see `AyCode.Core/AyCode.Core/docs/LOGGING/README.md` (`AyCode.Core.Loggers`) |
|
||||
| **IAcLogWriterBase** | Log writer interface — pluggable output sink (see `AyCode.Core/AyCode.Core/docs/LOGGING/README.md`) |
|
||||
| **AcLoggerBase** | Logger base class with category name and writer array (see `AyCode.Core/AyCode.Core/docs/LOGGING/README.md`) |
|
||||
| **AcLogItemWriterBase\<T\>** | Log writer base for structured log items (see `AyCode.Core/AyCode.Core/docs/LOGGING/README.md`) |
|
||||
| **IAcModelDtoBaseEmpty** | Empty DTO marker interface |
|
||||
| **IAcSoftRemoveEntity** | Soft-delete contract (AyCode) |
|
||||
| **IForeignKey** | FK marker interface |
|
||||
| **IForeignCollection\<T\>** | FK collection marker |
|
||||
| **ITimeStampCreated** | `Created` datetime property |
|
||||
| **ITimeStampModified** | `Modified` datetime property |
|
||||
| **ITimeStampInfo** | Combines `Creator` (int), `Created`, `Modified` |
|
||||
| **AcConst** | Abstract constants base class |
|
||||
| **TaskHelper.ToThreadPoolTask** | Wraps `Func<Task<T>>` into `Task.Run` on thread pool |
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# Mango.Nop Libraries documentation
|
||||
|
||||
Top-level documentation for the Mango.Nop shared libraries (Layer 2 — shared NopCommerce plugin framework).
|
||||
|
||||
## Reference docs (flat)
|
||||
|
||||
- [`ARCHITECTURE.md`](ARCHITECTURE.md) — Repo architecture overview
|
||||
- [`CONVENTIONS.md`](CONVENTIONS.md) — Coding conventions
|
||||
- [`GLOSSARY.md`](GLOSSARY.md) — Domain glossary
|
||||
|
||||
## Sub-projects with docs
|
||||
|
||||
- `Mango.Nop.Core/docs/` — Core entity mirrors, DTOs, architecture
|
||||
- `Mango.Nop.Data/docs/` — Repository and transaction patterns
|
||||
- `Mango.Nop.Services/docs/` — Service patterns, logger bridge
|
||||
|
||||
## Navigation
|
||||
|
||||
Per the AI Agent Core Protocol (folder navigation rule), start from this README when browsing `docs/`. Single-file reference docs remain flat at this level; multi-file topics live in named subfolders at the sub-project level.
|
||||
|
||||
## See also
|
||||
|
||||
- **Base framework**: `../../../../Aycode/Source/AyCode.Core/` (see each project's `docs/` folder).
|
||||
Loading…
Reference in New Issue