using Centron.BusinessLogic; using Centron.BusinessLogic.EmployeeArea; using Centron.BusinessLogic.Sales.CustomerAssets.TimerBilling; using Centron.BusinessLogic.WebServices.Sales.Receipts; using Centron.Data.WebServices.Sales.Receipts.Invoices; using Centron.Interfaces; using Centron.Tests.EndToEnd.Infrastructure; using CentronSoftware.Centron.WebServices.Entities.Sales.CustomerAssets.TimerBilling; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Centron.BusinessLogic.Administration.Settings; using Centron.BusinessLogic.Sales.Receipts.DataAndResults; using Centron.BusinessLogic.Sales.Receipts.Internal; using Centron.BusinessLogic.Sales.Support; using Centron.BusinessLogic.WebServices.Sales.Support; using Centron.Data.Entities.Sales.Support.HelpdeskTimerArea; using Centron.Data.WebServices.Sales.Receipts; using Centron.Data.WebServices.Sales.Receipts.Offers; using Centron.Data.WebServices.Sales.Support; using Centron.Interfaces.Administration.Environment; using Centron.Interfaces.Administration.Settings; using Centron.Interfaces.Sales.Receipts; using CentronSoftware.Centron.WebServices.Extensions; using Xunit; using Xunit.Abstractions; namespace Centron.Tests.EndToEnd.Tests.TimerBilling { public class TimerBillingTest : EndToEndTest { public TimerBillingTest(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { } public override void Execute() { this.PrepareDatabase(); this.CreateReceiptWithDate(CentronObjectKindNumeric.InvoiceClass, reminderDateInDays: 0); this.CreateReceiptWithDate(CentronObjectKindNumeric.InvoiceClass, reminderDateInDays: 10); this.CreateReceiptWithDate(CentronObjectKindNumeric.DeliveryListClass, reminderDateInDays: 0); this.CreateReceiptWithDate(CentronObjectKindNumeric.DeliveryListClass, reminderDateInDays: 10); // Reset it to 0, so the invoices created afterwards don't change this.SetReminderDateInDays(0); this.FindTimersAndCreateInvoiceForCustomer(10008); this.FindTimersAndCreateInvoiceForCustomer(10012); this.FindTimersAndCreateInvoiceForCustomer(10017); this.FindTimersAndCreateInvoiceForCustomer(10024); this.ValidateUseTicketTimeAsServicePeriod(); this.ValidateBreakTimeFormat(); // Timer from orders this.ExecuteMatrix( new bool?[] {null, true, false}, new bool[] {true, false}, (takePriceFromOrder, globalTakePriceFromOrder) => { this.CreateReceiptWithTimerFromOrder(takePriceFromOrder, globalTakePriceFromOrder); }); // Timer with Master-Data-List infos this.CreateInvoiceFromTicketWithMasterDataList(); // Timer start- and end-time fix this.ExecuteMatrix( new bool[] {true, false}, new bool[] {true, false}, (startTimeFormat, endTimeFormat) => { this.CreateReceiptWithTimerFormat(startTimeFormat, endTimeFormat); }); this.TestGetContractsMethod(); } private void TestGetContractsMethod() { // This is just a function test so the different combinations work. We use in GetContracts a complex table valued function // If it is called for one customer or one contract, we pass down it too the function too. using var session = new BLSession(); var result = session.GetBL().GetContracts(new List() { 10000 }, null); Verifier.Verify("TimerBillingTest_TestGetContractsMethod_SingleCustomer", result); result = session.GetBL().GetContracts(null, new List() { 28 }); Verifier.Verify("TimerBillingTest_TestGetContractsMethod_SingleContract", result); result = session.GetBL().GetContracts(new List() { 10000, 10003 }, null); Verifier.Verify("TimerBillingTest_TestGetContractsMethod_MultipleCustomer", result); result = session.GetBL().GetContracts(null, new List() { 28, 6 }); Verifier.Verify("TimerBillingTest_TestGetContractsMethod_MultipleContract", result); } private void PrepareDatabase() { this.Sql($"UPDATE dbo.Stammdat SET WertText = 'g' WHERE I3D = {(int)AppSettingsConst.HelpdeskTimerStartTimeFormat}"); this.Sql($"UPDATE dbo.Stammdat SET WertText = 'g' WHERE I3D = {(int)AppSettingsConst.HelpdeskTimerEndTimeFormat}"); } private void CreateReceiptWithDate(CentronObjectKindNumeric receiptKind, int reminderDateInDays) { this.SetReminderDateInDays(reminderDateInDays); // Prepare var customerI3D = 10008; var name = $"{receiptKind.GetReceiptName()}WithDateResultForCustomer{customerI3D}ReminderIn{reminderDateInDays}"; var timerI3Ds = this.FindTimers(customerI3D); var billingDate = new DateTime(2021, 10, 30); var settings = new TimerBillingSettingsDTO { ReceiptKind = receiptKind, BillingDate = billingDate }; // Act var receipt = this.CreateReceipt(name, timerI3Ds, settings); // Assert Assert.Equal(billingDate, receipt.Date); // The ReminderDate should be X days after the receipt-date // So when today is X, billing date is X + 7, and reminderDateInDays is 5, // then reminderDate should be X + 7 + 5 var expectedReminderDate = reminderDateInDays == 0 ? (DateTime?)null : billingDate.AddDays(reminderDateInDays); Assert.Equal(expectedReminderDate, receipt.Get((IReceiptWithReminderDate r) => r.ReminderDate)); } private void SetReminderDateInDays(int reminderDateInDays) { // Set reminder date settings using (var session = new BLSession()) { var updateSettings = session.GetBL().GetSettingsForUpdate( AppSettingsConst.InvoiceReminderDays, ApplicationSettingID.DeliveryListReminderDays); updateSettings.UpdateInt(AppSettingsConst.InvoiceReminderDays, reminderDateInDays); updateSettings.UpdateInt(ApplicationSettingID.DeliveryListReminderDays, reminderDateInDays); updateSettings.SaveSettings(); } } private void FindTimersAndCreateInvoiceForCustomer(int customerI3D) { var name = $"InvoiceResultForCustomer{customerI3D}"; var timerI3Ds = this.FindTimers(customerI3D); var settings = new TimerBillingSettingsDTO { InsertHelpdeskDescription = true, InsertHelpdeskShortDescription = true, InsertTimerText = true, InsertTimerTime = true, InsertTheFollowingAreTimersFromHelpdeskText = true, Sorting = Interfaces.Sales.Receipts.TimerBilling.TimerSorting.ChronologicalUp, ReceiptKind = CentronObjectKindNumeric.InvoiceClass, AdditionalText = "Hier mein Text!" }; var invoice = this.CreateReceipt(name, timerI3Ds, settings); this.SaveInvoice(invoice); } private List FindTimers(int customerI3D) { using (var session = new BLSession()) { var filter = new TimerBillingFilter { OnlyOpenHelpdesks = true, CustomerI3Ds = new List { customerI3D } }; var timers = session.GetBL().SearchTimers(filter); this.Verifier.Verify("FindTimersForCustomer" + customerI3D, timers); return timers.Select(f => f.I3D).ToList(); } } private ReceiptBaseDTO CreateReceipt(string name, List timerI3Ds, TimerBillingSettingsDTO settings) { using (var session = new BLSession()) { var user = session.GetBL().GetAppUser(f => f.I3D == 11); var invoiceResult = session.GetBL().CreateNewReceiptForHelpdekTimers(timerI3Ds, settings, user); // Allow the caller to pass in a NULL name, to skip the .Verify call if (name is not null) this.Verifier.Verify(name, invoiceResult); return invoiceResult.Data; } } private void SaveInvoice(ReceiptBaseDTO invoice) { using (var session = new BLSession()) { var user = session.GetBL().GetAppUser(f => f.I3D == 11); var saveResult = session.GetBL().SaveReceipt(invoice, user, new SaveReceiptData { IgnoreCallbacks = true }, false, CreatedThroughApplication.CentronNet, null); this.Verifier.Verify($"SavedInvoice{invoice.Number}", saveResult); } } private void ValidateBreakTimeFormat() { // Set break-time format using (var session = new BLSession()) { var updateSettings = session.GetBL().GetSettingsForUpdate( ApplicationSettingID.HelpdeskTimerBreakTimeFormat); updateSettings.UpdateString(ApplicationSettingID.HelpdeskTimerBreakTimeFormat, "davon @@Stunden@@ bzw. @@Minuten@@ bzw. @@StundenDezimal@@ Pause"); updateSettings.SaveSettings(); } // Test the logic using (var session = new BLSession()) { var testTimer = new HelpdeskTimer { Start = new DateTime(2021, 1, 1, 15, 30, 0), Stop = new DateTime(2021, 1, 1, 17, 45, 0), LunchTime = (int)TimeSpan.FromMinutes(12).TotalSeconds }; var text = session.GetBL().GetTimerTimeText(testTimer); this.Verifier.Verify("TimerTimeTextWithBreak", text); } } private void ValidateUseTicketTimeAsServicePeriod() { const int timerI3D = 10511; var (start, stop) = this.GetHelpdeskTimerPeriod(timerI3D); var receiptWithServicePeriod = this.CreateReceipt(null, new List { timerI3D }, new TimerBillingSettingsDTO { ReceiptKind = CentronObjectKindNumeric.InvoiceClass, UseTicketTimeAsServicePeriod = true, }); var itemWithServicePeriod = receiptWithServicePeriod.GetReceiptItems().OfType().First(f => f.ServicePeriodFrom != null); Assert.Equal(start, itemWithServicePeriod.ServicePeriodFrom); Assert.Equal(stop, itemWithServicePeriod.ServicePeriodTo); var receiptPeriod = Assert.IsType(receiptWithServicePeriod); Assert.Equal(start, receiptPeriod.ServicePeriodFrom); Assert.Equal(stop, receiptPeriod.ServicePeriodTo); var receiptWithoutServicePeriod = this.CreateReceipt(null, new List { timerI3D }, new TimerBillingSettingsDTO { ReceiptKind = CentronObjectKindNumeric.InvoiceClass, UseTicketTimeAsServicePeriod = false, }); foreach (var itemWithoutServicePeriod in receiptWithoutServicePeriod.GetReceiptItems().OfType()) { Assert.Null(itemWithoutServicePeriod.ServicePeriodFrom); Assert.Null(itemWithoutServicePeriod.ServicePeriodTo); } var receiptWithoutPeriod = Assert.IsType(receiptWithoutServicePeriod); Assert.Null(receiptWithoutPeriod.ServicePeriodFrom); Assert.Null(receiptWithoutPeriod.ServicePeriodTo); } private (DateTime Start, DateTime Stop) GetHelpdeskTimerPeriod(int timerI3D) { using var session = new BLSession(); var timer = session.GetBL().GetHelpdeskTimerByI3D(timerI3D); return (timer.Start, timer.Stop); } private void CreateReceiptWithTimerFromOrder(bool? takePriceFromOrder, bool globalTakePriceFromOrder) { var name = $"CreateReceiptWithTimerFromOrder_PriceFromOrder_{takePriceFromOrder?.ToString() ?? "NULL"}_GlobalPriceFromOrder_{globalTakePriceFromOrder}"; const int TimerI3DFromOrder = 10511; const int OrderItemI3D = 7258; var settings = new TimerBillingSettingsDTO { ReceiptKind = CentronObjectKindNumeric.InvoiceClass, TakePriceFromOrder = globalTakePriceFromOrder, OrderItemI3DForHelpdeskTimerI3Ds = new List { new() { HelpdeskTimerI3D = TimerI3DFromOrder, OrderItemI3D = OrderItemI3D, TakePriceFromOrder = takePriceFromOrder } } }; var invoice = this.CreateReceipt(name:null, new List { TimerI3DFromOrder }, settings); var item = invoice.GetReceiptItems().First(f => f.ArticleI3D == 12); this.Verifier.Verify(name, item); // Important here is mostly the IReceiptItemBase.BasePrice property var expectedPriceFromOrder = takePriceFromOrder ?? globalTakePriceFromOrder; var expectedPrice = expectedPriceFromOrder ? 400 : 0; var price = item.BasePrice; Assert.Equal(expectedPrice, price); } private void CreateInvoiceFromTicketWithMasterDataList() { // Add new timer to Helpdesk Ticket I3D 9447 int? timerI3D; using (var session = new BLSession()) { timerI3D = session.GetBL().SaveHelpdeskTimer(new HelpdeskTimerDTO { Start = new DateTime(2022, 2, 10, 12, 0, 0), Stop = new DateTime(2022, 2, 10, 14, 30, 0), Calculable = true, HelpdeskI3D = 9447, InternalNote = "Interne Notiz Blabla", ExternalNote = "Externe Notiz Blabla", IsPlanned = false, ArticleI3D = 94, State = 1, }, this.GetLoggedInUser()); Assert.NotNull(timerI3D); } // Activate the setting to add Master-Data-List info when billing timers this.Sql($"UPDATE dbo.Stammdat SET Wert = 1 WHERE I3D = {(int)AppSettingsConst.HelpdeskToCustomerAssetInsertMasterSheetData}"); // Act var settings = new TimerBillingSettingsDTO { ReceiptKind = CentronObjectKindNumeric.InvoiceClass, }; var receipt = this.CreateReceipt("InvoiceFromTicketWithMasterDataList", new List {timerI3D.Value}, settings); } private void CreateReceiptWithTimerFormat(bool hasStartTimeFormat, bool hasEndTimeFormat) { var startTimeFormat = hasStartTimeFormat ? "g" : ""; this.Sql($"UPDATE dbo.Stammdat SET WertText = '{startTimeFormat}' WHERE I3D = {(int)AppSettingsConst.HelpdeskTimerStartTimeFormat}"); var endTimeFormat = hasEndTimeFormat ? "g" : ""; this.Sql($"UPDATE dbo.Stammdat SET WertText = '{endTimeFormat}' WHERE I3D = {(int)AppSettingsConst.HelpdeskTimerEndTimeFormat}"); using (var session = new BLSession()) { var testTimer = new HelpdeskTimer { Start = new DateTime(2021, 1, 1, 15, 30, 0), Stop = new DateTime(2021, 1, 1, 17, 45, 0), LunchTime = (int)TimeSpan.FromMinutes(12).TotalSeconds }; var text = session.GetBL().GetTimerTimeText(testTimer); this.Verifier.Verify($"TimerTimeText_HasStartTime_{hasStartTimeFormat}_HasEndTime_{hasEndTimeFormat}", text); } } } }