Refactor partner model, add CargoPartner/Truck entities

- Refactored entity classes to be sealed and use new PartnerBase
- Introduced CargoPartner and CargoTruck entities with associations
- Centralized partner properties in abstract PartnerBase
- Updated IPartner, added IPartnerBase and ICargoPartner interfaces
- Added table name constants for new entities in FruitBankConstClient
- Changed AcBinaryHubProtocol FlushPolicy to "Coalesced"
- Cleaned up unused table item classes in DatabaseClient.cs
- Added serialization and documentation attributes to entities
- Updated Preorder and PreorderItem with new structure and docs
This commit is contained in:
Loretta 2026-05-28 17:47:53 +02:00
parent db9f7aa12f
commit a32f91271d
24 changed files with 103 additions and 45 deletions

View File

@ -0,0 +1,17 @@
using AyCode.Core.Serializers.Attributes;
using AyCode.Core.Serializers.Toons;
using FruitBank.Common.Interfaces;
using LinqToDB.Mapping;
using Mango.Nop.Core.Entities;
namespace FruitBank.Common.Entities;
[AcBinarySerializable(false, true, false, true, false, false)]
//[ToonDescription("Business partner with address and tax information", Purpose = "Represents an external legal entity, specifically a Supplier who provides goods or a business partner involved in the procurement chain")]
[Table(Name = FruitBankConstClient.CargoPartnerDbTableName)]
[System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.CargoPartnerDbTableName)]
public sealed class CargoPartner : PartnerBase, ICargoPartner
{
[Association(ThisKey = nameof(Id), OtherKey = nameof(CargoTruck.CargoPartnerId), CanBeNull = true)]
public List<CargoTruck>? CargoTrucks { get; set; }
}

View File

@ -0,0 +1,14 @@
using AyCode.Core.Serializers.Attributes;
using LinqToDB.Mapping;
using Mango.Nop.Core.Entities;
namespace FruitBank.Common.Entities;
[AcBinarySerializable(false, true, false, true, false, false)]
//[ToonDescription("Business partner with address and tax information", Purpose = "Represents an external legal entity, specifically a Supplier who provides goods or a business partner involved in the procurement chain")]
[Table(Name = FruitBankConstClient.CargoTruckDbTableName)]
[System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.CargoTruckDbTableName)]
public sealed class CargoTruck: MgEntityBase//, ICargoPartner
{
public int CargoPartnerId { get; set; }
}

View File

@ -4,7 +4,7 @@ using Mango.Nop.Core.Entities;
namespace FruitBank.Common.Entities;
public class CustomerCredit: MgEntityBase, IEntityComment
public sealed class CustomerCredit: MgEntityBase, IEntityComment
{
public int CustomerId { get; set; }
public decimal CreditLimit { get; set; }

View File

@ -10,7 +10,7 @@ namespace FruitBank.Common.Entities;
[ToonDescription("Uploaded file with extracted text content", Purpose = "A centralized repository for all uploaded binary content and metadata, featuring a 'RawText' field that stores OCR-extracted information for full-text search and automated data validation across the system")]
[Table(Name = FruitBankConstClient.FilesDbTableName)]
[System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.FilesDbTableName)]
public class Files : MgEntityBase, IFiles
public sealed class Files : MgEntityBase, IFiles
{
public string FileName { get; set; }
public string FileSubPath { get; set; }

View File

@ -16,7 +16,7 @@ namespace FruitBank.Common.Entities;
[ToonDescription("Pallet measurements for order items with audit tracking", Purpose = "A measurement record for outgoing goods, used to verify that the net weight being sent to the customer is accurate and audited. NOTE: Despite the 'Pallet' name, this is a general measurement record that is ALWAYS created for every item. If the product is not measurable (IsMeasurable=false), weights are recorded as 0.0 and only TrayQuantity is stored.")]
[Table(Name = FruitBankConstClient.OrderItemPalletDbTableName)]
[System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.OrderItemPalletDbTableName)]
public class OrderItemPallet : MeasuringItemPalletBase, IOrderItemPallet
public sealed class OrderItemPallet : MeasuringItemPalletBase, IOrderItemPallet
{
public int OrderItemId
{

View File

@ -11,7 +11,7 @@ namespace FruitBank.Common.Entities;
[ToonDescription("Pallet type definition with size and weight")]
[Table(Name = FruitBankConstClient.PalletDbTableName)]
[System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.PalletDbTableName)]
public class Pallet : MgEntityBase, IPallet
public sealed class Pallet : MgEntityBase, IPallet
{
public string Name { get; set; }
public string Size { get; set; }

View File

@ -10,24 +10,8 @@ namespace FruitBank.Common.Entities;
[ToonDescription("Business partner with address and tax information", Purpose = "Represents an external legal entity, specifically a Supplier who provides goods or a business partner involved in the procurement chain")]
[Table(Name = FruitBankConstClient.PartnerDbTableName)]
[System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.PartnerDbTableName)]
public class Partner : MgEntityBase, IPartner
public sealed class Partner : PartnerBase, IPartner
{
public string Name { get; set; }
public string TaxId { get; set; }
public string CertificationNumber { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
public string State { get; set; }
public string County { get; set; }
public string City { get; set; }
public string Street { get; set; }
[Association(ThisKey = nameof(Id), OtherKey = nameof(ShippingDocument.ShippingId), CanBeNull = true)]
public List<ShippingDocument>? ShippingDocuments { get; set; }
[SkipValuesOnUpdate]
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
}

View File

@ -0,0 +1,25 @@
using FruitBank.Common.Interfaces;
using LinqToDB.Mapping;
using Mango.Nop.Core.Entities;
namespace FruitBank.Common.Entities;
public abstract class PartnerBase : MgEntityBase, IPartnerBase
{
public string Name { get; set; }
public string TaxId { get; set; }
public string CertificationNumber { get; set; }
public string CountryCode { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
public string State { get; set; }
public string County { get; set; }
public string City { get; set; }
public string Street { get; set; }
[SkipValuesOnUpdate]
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
}

View File

@ -1,12 +1,16 @@
using FruitBank.Common.Enums;
using AyCode.Core.Serializers.Attributes;
using AyCode.Core.Serializers.Toons;
using FruitBank.Common.Enums;
using LinqToDB.Mapping;
using Mango.Nop.Core.Entities;
namespace FruitBank.Common.Entities;
[AcBinarySerializable(false, true, false, true, false, false)]
[Table(Name = FruitBankConstClient.PreOrderDbTableName)]
[System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.PreOrderDbTableName)]
public class Preorder : MgEntityBase
//[ToonDescription("Shipping document with partner, items and files", Purpose = "A digital representation of a supplier's delivery note or invoice associated with the shipment, used for reconciling paper-based data with measured reality")]
public sealed class Preorder : MgEntityBase
{
public int CustomerId { get; set; }
public int StoreId { get; set; }

View File

@ -1,12 +1,16 @@
using FruitBank.Common.Enums;
using AyCode.Core.Serializers.Attributes;
using AyCode.Core.Serializers.Toons;
using FruitBank.Common.Enums;
using LinqToDB.Mapping;
using Mango.Nop.Core.Entities;
namespace FruitBank.Common.Entities;
[AcBinarySerializable(false, true, false, true, false, false)]
[Table(Name = FruitBankConstClient.PreOrderItemDbTableName)]
[System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.PreOrderItemDbTableName)]
public class PreorderItem : MgEntityBase
//[ToonDescription("Shipping document with partner, items and files", Purpose = "A digital representation of a supplier's delivery note or invoice associated with the shipment, used for reconciling paper-based data with measured reality")]
public sealed class PreorderItem : MgEntityBase
{
public int PreorderId { get; set; }
public int ProductId { get; set; }

View File

@ -11,7 +11,7 @@ namespace FruitBank.Common.Entities;
[ToonDescription("Shipping record with documents and measurement tracking", Purpose = "Represents a physical inbound delivery event (truck arrival) at the warehouse, tracking the vehicle and the overall measurement status of the shipment")]
[Table(Name = FruitBankConstClient.ShippingDbTableName)]
[System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.ShippingDbTableName)]
public class Shipping : MgEntityBase, IShipping, IEntityComment
public sealed class Shipping : MgEntityBase, IShipping, IEntityComment
{
public DateTime ShippingDate { get; set; } = DateTime.Now;
public string LicencePlate { get; set; }

View File

@ -11,7 +11,7 @@ namespace FruitBank.Common.Entities;
[ToonDescription("Shipping document with partner, items and files", Purpose = "A digital representation of a supplier's delivery note or invoice associated with the shipment, used for reconciling paper-based data with measured reality")]
[Table(Name = FruitBankConstClient.ShippingDocumentDbTableName)]
[System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.ShippingDocumentDbTableName)]
public class ShippingDocument : MgEntityBase, IShippingDocument
public sealed class ShippingDocument : MgEntityBase, IShippingDocument
{
public int PartnerId { get; set; }
public int? ShippingId { get; set; }

View File

@ -12,7 +12,7 @@ namespace FruitBank.Common.Entities;
[ToonDescription("Links shipping documents to files with document type", Purpose = "A many-to-many link table that associates general uploaded files with specific shipping documents, assigning a functional context (DocumentType) to each file, such as identifying which PDF is the supplier's invoice versus the packing list")]
[LinqToDB.Mapping.Table(Name = FruitBankConstClient.ShippingDocumentToFilesDbTableName)]
[System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.ShippingDocumentToFilesDbTableName)]
public class ShippingDocumentToFiles : MgEntityBase, IShippingDocumentToFiles
public sealed class ShippingDocumentToFiles : MgEntityBase, IShippingDocumentToFiles
{
public int FilesId { get; set; }

View File

@ -12,7 +12,7 @@ namespace FruitBank.Common.Entities;
[ToonDescription("Pallet measurements for shipping items", Purpose = "The smallest unit of measurement tracking, representing a single physical measurement event. NOTE: Technically named 'Pallet' for legacy reasons, but it is ALWAYS created even if goods arrive without a physical pallet. For non-measurable products, weights are 0.0 and only TrayQuantity is tracked for tare-weight calculations.")]
[LinqToDB.Mapping.Table(Name = FruitBankConstClient.ShippingItemPalletDbTableName)]
[System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.ShippingItemPalletDbTableName)]
public class ShippingItemPallet : MeasuringItemPalletBase, IShippingItemPallet
public sealed class ShippingItemPallet : MeasuringItemPalletBase, IShippingItemPallet
{
public int ShippingItemId
{

View File

@ -19,7 +19,7 @@ namespace FruitBank.Common.Entities
[Table(Name = FruitBankConstClient.StockQuantityHistoryExtDbTableName)]
[System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.StockQuantityHistoryExtDbTableName)]
[ToonDescription("Extended weight-metadata for StockQuantityHistory", Purpose = "Validates quantity deltas against measured weight to detect inconsistencies")]
public class StockQuantityHistoryExt : MgEntityBase, IStockQuantityHistoryExt
public sealed class StockQuantityHistoryExt : MgEntityBase, IStockQuantityHistoryExt
{
public int StockQuantityHistoryId { get; set; }

View File

@ -9,7 +9,7 @@ namespace FruitBank.Common.Entities;
[ToonDescription("Inventory session record", Purpose = "Orchestrates inventory sessions by freezing logical stock states")]
[Table(Name = FruitBankConstClient.StockTakingDbTableName)]
[System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.StockTakingDbTableName)]
public class StockTaking : MgStockTaking<StockTakingItem>
public sealed class StockTaking : MgStockTaking<StockTakingItem>
{
public override bool IsReadyForClose()
{

View File

@ -15,7 +15,7 @@ namespace FruitBank.Common.Entities;
[ToonDescription("Line item for product reconciliation", Purpose = "Reconciles snapshot quantity with physical count to calculate final stock delta")]
[Table(Name = FruitBankConstClient.StockTakingItemDbTableName)]
[System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.StockTakingItemDbTableName)]
public class StockTakingItem : MgStockTakingItem<StockTaking, ProductDto>
public sealed class StockTakingItem : MgStockTakingItem<StockTaking, ProductDto>
{
public bool IsMeasurable { get; set; }

View File

@ -18,7 +18,7 @@ public interface IStockTakingItemPallet : IMeasuringItemPalletBase
[ToonDescription("Weight record for inventory item", Purpose = "Granular weight-based evidence for a stock taking line item. NOTE: This record is mandatory for every inventory item. If weighing is skipped (non-measurable), it serves as a container for TrayQuantity with zeroed weight fields. The term 'Pallet' is a legacy naming convention.")]
[LinqToDB.Mapping.Table(Name = FruitBankConstClient.StockTakingItemPalletDbTableName)]
[System.ComponentModel.DataAnnotations.Schema.Table(FruitBankConstClient.StockTakingItemPalletDbTableName)]
public class StockTakingItemPallet : MeasuringItemPalletBase, IStockTakingItemPallet
public sealed class StockTakingItemPallet : MeasuringItemPalletBase, IStockTakingItemPallet
{
public int StockTakingItemId
{

View File

@ -49,6 +49,9 @@ public static class FruitBankConstClient
public const string PreOrderDbTableName = "fbPreorder";
public const string PreOrderItemDbTableName = "fbPreorderItem";
public const string CargoPartnerDbTableName = "fbCargoPartner";
public const string CargoTruckDbTableName = "fbCargoTruck";
public const string DomainDescription = "This is a nopCommerce plugin developed for FruitBank, a fruit and vegetable wholesaler. The plugin manages supplier inbound delivery (receiving), warehouse weighing (net/gross/pallet/tare weights), and inventory stocktaking. The business logic is centered around FruitBank's requirement for precise physical measurement and quantity tracking.";
//public static Guid[] DevAdminIds = new Guid[2] { Guid.Parse("dcf451d2-cc4c-4ac2-8c1f-da00041be1fd"), Guid.Parse("4cbaed43-2465-4d99-84f1-c8bc6b7025f7") };

View File

@ -4,17 +4,27 @@ using FruitBank.Common.Entities;
namespace FruitBank.Common.Interfaces;
public interface IPartner : IEntityInt, ITimeStampInfo
public interface ICargoPartner : IPartnerBase
{
List<CargoTruck>? CargoTrucks { get; set; }
}
public interface IPartner : IPartnerBase
{
List<ShippingDocument>? ShippingDocuments { get; set; }
}
public interface IPartnerBase : IEntityInt, ITimeStampInfo
{
string Name { get; set; }
string TaxId { get; set; }
string CertificationNumber { get; set; }
string CountryCode { get; set; }
string PostalCode { get; set; }
string Country { get; set; }
string State { get; set; }
string County { get; set; }
string City { get; set; }
string Street { get; set; }
List<ShippingDocument>? ShippingDocuments { get; set; }
}

View File

@ -19,14 +19,11 @@ using AyCode.Core.Helpers;
namespace FruitBankHybrid.Shared.Databases;
public class ShippingTableItem : Shipping
{ }
public class ShippingDocumentTableItem : ShippingDocument
{ }
//public class ShippingTableItem : Shipping { }
//public class ShippingDocumentTableItem : ShippingDocument { }
public class ShippingItemTableItem : ShippingItem
{ }
public class ShippingItemPalletTableItem : ShippingItemPallet
{ }
//public class ShippingItemPalletTableItem : ShippingItemPallet { }
public class ProductDtoTableItem : ProductDto
{
//[Newtonsoft.Json.JsonProperty]

View File

@ -38,7 +38,7 @@
"AcBinaryHubProtocol": {
"ProtocolMode": "AsyncSegment",
"BufferSize": 4096,
"FlushPolicy": "DoubleBuffered",
"FlushPolicy": "Coalesced",
"FlushTimeout": "00:00:10"
}
}

View File

@ -38,7 +38,7 @@
"AcBinaryHubProtocol": {
"ProtocolMode": "AsyncSegment",
"BufferSize": 4096,
"FlushPolicy": "DoubleBuffered",
"FlushPolicy": "Coalesced",
"FlushTimeout": "00:00:10"
}
}

View File

@ -38,7 +38,7 @@
"AcBinaryHubProtocol": {
"ProtocolMode": "AsyncSegment",
"BufferSize": 4096,
"FlushPolicy": "DoubleBuffered",
"FlushPolicy": "Coalesced",
"FlushTimeout": "00:00:10"
}
}