using System; using System.Linq; using Centron.BusinessLogic; using Centron.BusinessLogic.Administration.Settings; using Centron.BusinessLogic.Mail.Factory; using Centron.BusinessLogic.WebServices.Accounts.SpecialPrices; using Centron.BusinessLogic.WebServices.Administration.Logins; using Centron.BusinessLogic.WebServices.Sales.Receipts; using Centron.Data.WebServices.Sales.Receipts; using Centron.Data.WebServices.Sales.Receipts.Orders; using Centron.Interfaces; using Centron.Interfaces.Accounts.SpecialPrices; using Centron.Interfaces.BL; using Centron.Interfaces.Sales.Receipts; using Centron.Tests.EndToEnd.Infrastructure; using CentronSoftware.Centron.WebServices.Entities.Accounts.SpecialPrices; using CentronSoftware.Centron.WebServices.Entities.Sales.Receipts; using CentronSoftware.Centron.WebServices.EntitiesWrongPlace.Administration.Rights; using Xunit; using Xunit.Abstractions; namespace Centron.Tests.EndToEnd.Tests.ReceiptCartReleaseSystem; public class ReceiptCartReleaseSystemTests : EndToEndTest { public ReceiptCartReleaseSystemTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { // Order Table this.Verifier.Settings.IgnoreColumn("Date"); this.Verifier.Settings.IgnoreColumn("CreatedAt"); this.Verifier.Settings.IgnoreColumn("ChangedAt"); this.Verifier.Settings.IgnoreColumn("ReminderDate"); this.Verifier.Settings.IgnoreColumn("ConcurrencyControlGuid"); this.Verifier.Settings.IgnoreColumn("RichText"); this.Verifier.Settings.IgnoreColumn("PaymentConditionText"); this.Verifier.Settings.IgnoreColumn("ToDoI3D"); this.Verifier.Settings.IgnoreColumn("CreatedThroughApplicationVersion"); this.Verifier.Settings.IgnoreColumn("ChangedThroughApplicationVersion"); // AnlageLog Table this.Verifier.Settings.IgnoreColumn("Datum"); this.Verifier.Settings.IgnoreColumn("CentronVersion"); // ReceiptLogDTO this.Verifier.Settings.IgnoreProperty(f => f.CentronVersion); this.Verifier.Settings.IgnoreProperty(f => f.Employee); } // This test checks that the release-system workflow for receipt-carts is working correctly // See the ReceiptCartState enum for the different states, and also for more information on how the workflow is supposed to work private const int ArticleI3D = 80; public override void Execute() { this.PrepareDatabase(); // Create a cart var cartI3D = this.CreateCart(); // Make sure the right-checks work this.AssertRightsMissingFor(cartI3D, new [] { ReceiptCartAction.CheckerApprove, ReceiptCartAction.CheckerDecline, ReceiptCartAction.OrdererApprove, ReceiptCartAction.OrdererDecline }); // Give us the rights to call the release-system workflow methods this.AddRights(); // Check successful release-system workflow // This goes through every step of the workflow // Created -> ReadyForCheck -> DeclinedByChecker -> ReadyForCheck -> Checked -> DeclinedByOrderer -> ReadyForCheck -> Checked -> Ordered this.AssertCartState( cartI3D, ReceiptCartState.Created, possibleActions: new []{ ReceiptCartAction.ReadyForCheck }); this.ExecuteAction(cartI3D, ReceiptCartAction.ReadyForCheck); this.AssertCartState( cartI3D, ReceiptCartState.ReadyForCheck, possibleActions: new [] { ReceiptCartAction.CheckerApprove, ReceiptCartAction.CheckerDecline }); this.ExecuteAction(cartI3D, ReceiptCartAction.CheckerDecline); this.AssertCartState( cartI3D, ReceiptCartState.DeclinedByChecker, possibleActions: new [] { ReceiptCartAction.ImproveCheckerDeclined }); this.ExecuteAction(cartI3D, ReceiptCartAction.ImproveCheckerDeclined); this.AssertCartState( cartI3D, ReceiptCartState.ReadyForCheck, possibleActions: new [] { ReceiptCartAction.CheckerApprove, ReceiptCartAction.CheckerDecline }); this.ExecuteAction(cartI3D, ReceiptCartAction.CheckerApprove); this.AssertCartState( cartI3D, ReceiptCartState.Checked, possibleActions: new [] { ReceiptCartAction.OrdererApprove, ReceiptCartAction.OrdererDecline }); this.ExecuteAction(cartI3D, ReceiptCartAction.OrdererDecline); this.AssertCartState( cartI3D, ReceiptCartState.DeclinedByOrderer, possibleActions: new [] { ReceiptCartAction.ImproveOrdererDeclined }); this.ExecuteAction(cartI3D, ReceiptCartAction.ImproveOrdererDeclined); this.AssertCartState( cartI3D, ReceiptCartState.ReadyForCheck, possibleActions: new [] { ReceiptCartAction.CheckerApprove, ReceiptCartAction.CheckerDecline }); this.ExecuteAction(cartI3D, ReceiptCartAction.CheckerApprove); // CartState already asserted above // Check logs now, because after the orderer approves the cart, the cart will not be accessible anymore this.VerifyReceiptLogs(cartI3D); var orderCartResult = this.ExecuteAction(cartI3D, ReceiptCartAction.OrdererApprove); this.AssertCartState( cartI3D, ReceiptCartState.Ordered, possibleActions: new ReceiptCartAction[0]); //At this point, no actions are possible anymore this.VerifyOrderCartResult(orderCartResult); this.VerifyOfferState(cartI3D); } private void PrepareDatabase() { using var session = new BLSession(); // Create a special price so we can test session.GetBL().SaveAccountSpecialPrice(this.GetLoggedInUser().User, new AccountSpecialPriceDTO { ObjectKind = CentronObjectKindNumeric.CustomerClass, ObjectI3D = 10000, ValueKind = SpecialPriceKind.ReduceListPrice, Value = 10, ArticleI3D = ArticleI3D, CreatedByI3D = 1, CreatedDate = new DateTime(2022, 1, 1) }); // Specify email settings, otherwise the release-system workflow won't send any mails var settings = session.GetBL().GetWebCartSettings(this.GetLoggedInUser()); settings.CartOrderedEmailSender = "noreply@c-entron.de"; settings.CartOrderedInternalEmailReceiver = "vertrieb@c-entron.de"; session.GetBL().UpdateWebCartSettings(settings); // Set a easy list-price to calculate with this.Sql($"UPDATE dbo.ARTIK SET Listenpreis = 600 WHERE I3D = {ArticleI3D}"); // Update setting, so offers don't close automatically this.WithSession(session => { var settings = session.GetBL().GetSettingsForUpdate(AppSettingsConst.CloseOfferAutomatically); settings.UpdateInt(AppSettingsConst.CloseOfferAutomatically, 1); // 1 = Never settings.SaveSettings(); }); } private int CreateCart() { var cart = this.WithSession(s => s.GetBL().CreateNewCart(this.GetLoggedInUserWebAccount(), "Test-Cart", string.Empty)); this.WithSession(s => s.GetBL().AddArticleToCart(cart.I3D, ArticleI3D, 10, this.GetLoggedInUserWebAccount())); var updateCartInfo = new UpdateCartInfoDTO { AssembleArticles = true, ProjectNumber = "Projektnummer", PurchaseOrderNumber = "Besondere Bestellnummer", IsPartialDeliveryPossible = false, DeliveryAddressZip = "12345", DeliveryAddressCity = "Stadt", DeliveryAddressStreet = "Straße", DeliveryAddressCountryI3D = 2, DeliveryAddressFirstName = "Günther", DeliveryAddressLastName = "Netzer", DeliveryAddressDepartment = "Lieferung auf das Helikopterpad bitte", DeliveryAddressSalutationI3D = 2, }; this.WithSession(s => s.GetBL().UpdateCartInfo(cart.I3D, updateCartInfo, this.GetLoggedInUserWebAccount())); return cart.I3D; } private void AssertRightsMissingFor(int cartI3D, ReceiptCartAction[] actions) { // In the beginning we don't have foreach (var action in actions) { using var session = new BLSession(); var method = action.GetActualMethod(session); var ex = Assert.Throws(() => method(cartI3D, this.GetLoggedInUserWebAccount())); Assert.Equal("Sie haben nicht die benötigten Rechte für diese Operation.", ex.Message); } } private void AddRights() { var webAccount = this.WithSession(s => s.GetBL().GetWebAccountByContactPersonI3D(23)).ThrowIfError(); var webRights = this.WithSession(s => s.GetBL().GetAllWebRights()).ThrowIfError(); webAccount.WebRights.Add(webRights.First(f => f.I3D == (int)WebAccountRightsConst.WEBRIGHT_WEBCART2_CHECK_CART)); webAccount.WebRights.Add(webRights.First(f => f.I3D == (int)WebAccountRightsConst.WEBRIGHT_WEBCART2_ORDER_CART)); this.WithSession(s => s.GetBL().SaveWebAccount(webAccount, this.GetLoggedInUser())); } private void AssertCartState(int cartI3D, ReceiptCartState cartState, ReceiptCartAction[] possibleActions) { // Ensure that the current cart-state is the expected one // Also make sure, that we can NOT call any of the not-possible action var currentCartState = Enum.Parse(this.Sql($"SELECT O.CartState FROM dbo.Offers O WHERE O.I3D = {cartI3D}")); Assert.Equal(cartState, currentCartState); var notPossibleActions = Enum.GetValues().Except(possibleActions); foreach (var notPossibleAction in notPossibleActions) { using var session = new BLSession(); var method = notPossibleAction.GetActualMethod(session); var ex = Assert.Throws(() => method(cartI3D, this.GetLoggedInUserWebAccount())); // After the cart is ordered, we don't allow access to it anymore, and behave as it doesn't exist var expectedMessage = cartState is ReceiptCartState.Ordered ? "Der Warenkorb wurde nicht gefunden." : "Der Warenkorb befindet sich bereits an einem anderen Schritt des Freigabewesens."; Assert.Equal(expectedMessage, ex.Message); } } private OrderCartResultDTO ExecuteAction(int cartI3D, ReceiptCartAction action) { // Execute the given ReceiptCartAction, to move the cart forward into the next step of the release-system workflow OrderCartResultDTO result = null; var mails = TestMails.Start(() => { using var session = new BLSession(); var method = action.GetActualMethod(session); result = method(cartI3D, this.GetLoggedInUserWebAccount()); }); this.Verifier.Verify($"{action}_Mails", mails); return result; } private void VerifyOrderCartResult(OrderCartResultDTO orderCartResult) { this.Verifier.Verify("OrderCartResult", orderCartResult); var order = (ReceiptOrderDTO)this.WithSession(s => s.GetBL().GetReceiptByI3D(orderCartResult.OrderI3D, CentronObjectKindNumeric.OrderClass)).ThrowIfError(); this.Verifier.Verify($"Order", order); } private void VerifyOfferState(int cartI3D) { this.Verifier.VerifySql("OfferState", $"SELECT State FROM dbo.Offers WHERE I3D = {cartI3D}"); } private void VerifyReceiptLogs(int cartI3D) { this.Verifier.VerifySql("ReceiptLogsTable", $"SELECT * FROM dbo.AnlageLog WHERE AnlageI3D = {cartI3D} AND AnlageArt = 1 ORDER BY I3D"); var logs = this.WithSession(s => s.GetBL().GetCartLogs(cartI3D, this.GetLoggedInUserWebAccount())); this.Verifier.Verify("ReceiptLogs", logs); } }