using System.Collections.Generic; using System.Linq; using Centron.BusinessLogic; using Centron.BusinessLogic.Sales.Receipts; using Centron.BusinessLogic.Sales.Receipts.DataAndResults; using Centron.BusinessLogic.WebServices.Sales.Receipts; using Centron.BusinessLogic.WebServices.Warehousing.Commissions; using Centron.Data.WebServices.Sales.Receipts; using Centron.Data.WebServices.Sales.Receipts.DeliveryLists; using Centron.Data.WebServices.Sales.Receipts.Orders; using Centron.Interfaces; using Centron.Interfaces.Administration.Environment; using Centron.Interfaces.BL; using Centron.Interfaces.Sales.Receipts.InsertReceipts; using Centron.Interfaces.Warehousing.Commissions; using Centron.Tests.EndToEnd.Infrastructure; using CentronSoftware.Centron.WebServices.Entities.Warehousing.Commissions; using CentronSoftware.Centron.WebServices.EntitiesWrongPlace.Sales.Receipts.DataAndResults; using Xunit; using Xunit.Abstractions; namespace Centron.Tests.EndToEnd.Tests.PartialCommission; /// /// Verifies that forwarding an order into a delivery note while referencing a partial commission order /// never forwards more than the order position actually still holds. /// /// A partial commission item's CurrentQuantity is a persisted snapshot maintained only by the /// commissioning module. It is not recalculated when the order is changed elsewhere (e.g. the order /// quantity is reduced below the commissioned quantity, or the order is closed). This test sets up such /// a stale state - the order position only holds , but the partial /// commission still believes are commissioned - and asserts that /// the forwarded quantity is clamped to the order's remaining quantity instead of over-delivering. (Ticket 165243) /// public class PartialCommissionForwardStaleQuantityTest : EndToEndTest { private const int CustomerI3D = 10026; private const int ArticleI3D = 139; private const decimal OrderRemainingQuantity = 2m; private const decimal StaleCommissionedQuantity = 5m; public PartialCommissionForwardStaleQuantityTest(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { } public override void Execute() { this.PrepareDatabase(); // 1. Create an order whose position only holds OrderRemainingQuantity (2). var order = (ReceiptOrderDTO)this.SaveReceipt(this.CreateSingleArticleOrder(ArticleI3D, OrderRemainingQuantity)); var orderItem = order.Items.First(f => f.ArticleI3D == ArticleI3D); // 2. Create a partial commission order whose (stale) snapshot believes StaleCommissionedQuantity (5) // are commissioned - more than the order actually still holds. var partialCommissionOrderI3D = this.CreateStalePartialCommissionOrder(order.I3D, orderItem.I3D); // 3. Forward the order into a delivery note referencing that partial commission. var deliveryList = this.ForwardOrderToDeliveryListWithPartialCommission(order.I3D, partialCommissionOrderI3D); // 4. The forwarded quantity must be clamped to the order's remaining quantity (2), not the stale 5. var deliveredItem = deliveryList.Items.Single(f => f.ArticleI3D == ArticleI3D); Assert.Equal(OrderRemainingQuantity, deliveredItem.QuantityComplete.GetValueOrDefault()); } private void PrepareDatabase() { // Make the article a plain article (no mandatory serial numbers / barcodes, no part list) so the // forwarded quantity is driven by the commissioned quantity, not by a scanned barcode count or a // part-list expansion. this.Sql($"UPDATE dbo.ARTIK SET BarcodeScanen = 0, StkListe = 0, SNStueckliste = 0 WHERE I3D = {ArticleI3D}"); // Do not require a SEPA mandate when saving the receipt. this.Sql("UPDATE dbo.Zahkond SET DTALastschrift = 0"); } private ReceiptOrderDTO CreateSingleArticleOrder(int articleI3D, decimal quantity) { using var session = new BLSession(); var currentUser = CommonMethodsHelper.GetUser(session); var order = (ReceiptOrderDTO)session.GetBL().CreateNewReceipt( CentronObjectKindNumeric.OrderClass, currentUser, CustomerI3D, new CreateReceiptData { IgnoreCallbacks = true, }, addressI3D: null, contactPersonI3D: null, insertSalutationAndAgreement: false, ignoreDefaultContract: true, insertCustomerDiscount: false).ThrowIfError().Receipt; var item = (ReceiptOrderItemDTO)session.GetBL().CreateArticleReceiptItems( order, isExternalArticle: false, articleI3D, externalArticleKind: null, externalArticleId: null, warehouseI3D: -1, currentUser, CreateArticleReceiptItemsConfig.None()).ThrowIfError().ReceiptItems.First(); item.InternalPosition = 0; item.QuantityComplete = quantity; order.Items = new List { item }; return order; } private ReceiptBaseDTO SaveReceipt(ReceiptBaseDTO receipt) { using var session = new BLSession(); return session.GetBL().SaveReceipt( receipt, CommonMethodsHelper.GetUser(session).User, new SaveReceiptData(), autoLockIfNewReceipt: false, CreatedThroughApplication.WebServices, receiptTemplateFolderI3D: null).ThrowIfError().Receipt; } private int CreateStalePartialCommissionOrder(int orderI3D, int orderItemI3D) { using var session = new BLSession(); var partialCommissionOrder = new PartialCommissionOrderDTO { ReceiptOrderI3D = orderI3D, Caption = "Teilkommissionierung (stale)", State = PartialCommissionOrderState.Incomplete, CommissionItems = new List { new() { ReceiptOrderItemI3D = orderItemI3D, TargetQuantity = StaleCommissionedQuantity, CurrentQuantity = StaleCommissionedQuantity, BarcodeRelations = new List(), }, }, }; var webServiceBL = session.GetBL(); webServiceBL .SaveOrUpdatePartialCommissionOrders(new List { partialCommissionOrder }, CommonMethodsHelper.GetUser(session)) .ThrowIfError(); return webServiceBL .GetPartialCommissionOrdersByFilter(new GetPartialCommissionOrdersFilter { ReceiptOrderI3D = orderI3D }) .ThrowIfError() .First() .I3D; } private ReceiptDeliveryListDTO ForwardOrderToDeliveryListWithPartialCommission(int orderI3D, int partialCommissionOrderI3D) { using var session = new BLSession(); return (ReceiptDeliveryListDTO)session.GetBL().ForwardReceipt( CentronObjectKindNumeric.DeliveryListClass, CommonMethodsHelper.GetUser(session).User, new ForwardReceiptData { IgnoreCallbacks = true, CreateReceiptData = new CreateReceiptData { IgnoreCallbacks = true, }, CreateReceiptItemsFromExistingItemsData = new List { new() { IgnoreCallbacks = true, }, }, SelectedPartialCommissionOrderI3D = partialCommissionOrderI3D, UseDeliveryAddressFromPartialCommissionOrder = false, }, new List { new() { ReceiptKind = CentronObjectKindNumeric.OrderClass, ReceiptI3D = orderI3D }, }, receiptProjectLayoutItemI3DsToForward: null, InsertReceiptTakeoverOptions.Reference, onlyTakeoverAvailableQuantity: false, ignoreDefaultContract: true).ThrowIfError().Receipt; } }