QuellCode/CentronERP war nur als Gitlink (Submodul-Referenz auf 79c1142) getrackt, ohne .gitmodules und ohne erreichbares Remote. Der Untersuchungsgegenstand der Versuchsreihe war damit nicht reproduzierbar gesichert: Ein Klon haette ein leeres Verzeichnis erhalten, und die Belege der 3.287 Anforderungen waeren nicht ueberpruefbar gewesen. Umstellung: - Historie nach c:\DEV\CentronERP_git_snapshot_79c1142 ausgelagert (vollstaendig lesbar, enthaelt 79c1142 und Vorgaenger 89ccfd6) - Gitlink aus dem Index entfernt - Dateiinhalt aufgenommen: 24.557 Dateien, rund 333 MB Die verschachtelte .gitignore der Codebasis gilt weiter, Build-Artefakte bleiben ausgeschlossen. Details in Versuche/Versuch_01/_Codebasis-Nachweis.md
583 lines
22 KiB
C#
583 lines
22 KiB
C#
using Centron.BusinessLogic;
|
|
using Centron.BusinessLogic.EmployeeArea;
|
|
using Centron.BusinessLogic.Sales.Customers;
|
|
using Centron.BusinessLogic.Sales.Receipts.DataAndResults;
|
|
using Centron.BusinessLogic.WebServices;
|
|
using Centron.BusinessLogic.WebServices.Sales.Receipts;
|
|
using Centron.Data.Entities.Administration.Logins;
|
|
using Centron.Data.Entities.CustomerArea;
|
|
using Centron.Data.Entities.Sales.Receipts.Invoices.Dunning;
|
|
using Centron.Data.WebServices.Mail;
|
|
using Centron.Data.WebServices.Sales.Receipts.CreditVouchers;
|
|
using Centron.Interfaces;
|
|
using Centron.Interfaces.Administration.Environment;
|
|
using Centron.Interfaces.BL;
|
|
using Centron.Interfaces.Sales.Receipts.InsertReceipts;
|
|
using Centron.Interfaces.Sales.Receipts.Invoices.Dunning;
|
|
using Centron.Tests.EndToEnd.Infrastructure;
|
|
using CentronSoftware.Centron.WebServices.Entities.Mail.Templates;
|
|
using CentronSoftware.Centron.WebServices.Entities.Sales.Receipts.Invoices.Dunning;
|
|
using CentronSoftware.Centron.WebServices.EntitiesWrongPlace.Sales.Receipts.DataAndResults;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Xunit;
|
|
using Xunit.Abstractions;
|
|
|
|
namespace Centron.Tests.EndToEnd.Tests.Dunning
|
|
{
|
|
public class DunningTests : EndToEndTest
|
|
{
|
|
private readonly int _referenceCustomerI3D = 10000;
|
|
|
|
// NOTE: these tests also test OPOS at the same time
|
|
|
|
public DunningTests(ITestOutputHelper testOutputHelper)
|
|
: base(testOutputHelper)
|
|
{
|
|
this.Verifier.Settings.IgnoreProperty<DunningSettingsDTO>(f => f.AdvisorEmailTextVariables);
|
|
this.Verifier.Settings.IgnoreProperty<DunningSettingsDTO>(f => f.CustomerEmailTextVariables);
|
|
|
|
this.Verifier.Settings.IgnoreProperty<InvoiceDunningDTO>(f => f.NextDueDateInDays);
|
|
this.Verifier.Settings.IgnoreProperty<InvoiceDunningDTO>(f => f.NextDueDate);
|
|
|
|
this.Verifier.Settings.IgnoreProperty<DunningRunResultDTO>(f => f.ReceiptReports);
|
|
this.Verifier.Settings.IgnoreProperty<DunningRunResultDTO>(f => f.Report);
|
|
this.Verifier.Settings.IgnoreProperty<DunningRunResultDTO>(f => f.ReportFileName);
|
|
|
|
this.Verifier.Settings.IgnoreProperty<DunningRunItemDTO>(f => f.DeletedDate); //Test is a bit flaky with this property sometimes being equal to Date, sometimes not
|
|
|
|
this.Verifier.Settings.IgnoreProperty<MailDTO>(f => f.MailAttachments);
|
|
}
|
|
|
|
public override void Execute()
|
|
{
|
|
this.PrepareDatabase();
|
|
|
|
// dunningBL
|
|
this.GetCustomers();
|
|
this.CalculateDunningStatistics();
|
|
this.UpdateDunningStopAndInfo();
|
|
this.GetAndUpdateSettings();
|
|
this.GetAndUpdateCustomerSettings();
|
|
|
|
// dunningRunBL
|
|
this.ExecuteDunningRun();
|
|
this.LoadDunningRuns();
|
|
this.ResetDunningRun();
|
|
}
|
|
|
|
private void PrepareDatabase()
|
|
{
|
|
// reset all invoices dunning stuff and close all
|
|
this.Sql(@"UPDATE RechKopf
|
|
SET Mahnung1Datum = NULL,
|
|
Mahnung1BearbeiterI3D = NULL,
|
|
Mahnung2Datum = NULL,
|
|
Mahnung2BearbeiterI3D = NULL,
|
|
Mahnung3Datum = NULL,
|
|
Mahnung3BearbeiterI3D = NULL,
|
|
MahnInfo = NULL,
|
|
MahnStop = NULL,
|
|
Mahnstufe = 0,
|
|
Datum = '2020-01-01 00:00:00.000',
|
|
FaelligAm = '2020-01-20 00:00:00.000',
|
|
Status = 2");
|
|
|
|
this.Sql(@"UPDATE RechKopf
|
|
SET Status = 1
|
|
WHERE I3D = 1 OR I3D = 2 OR I3D = 11 OR I3D = 13 OR I3D = 14");
|
|
|
|
this.Sql(@"UPDATE RechKopf
|
|
SET Status = 1,
|
|
FaelligAm = DATEADD(day, 5, GETDATE())
|
|
WHERE I3D = 3 OR I3D = 4 OR I3D = 26");
|
|
|
|
this.Sql(@"UPDATE RechKopf
|
|
SET Status = 1,
|
|
Mahnung1Datum = '2020-01-25 00:00:00.000',
|
|
Mahnung1BearbeiterI3D = 26,
|
|
Mahnstufe = 1
|
|
WHERE I3D = 29 OR I3D = 30 OR I3D = 35");
|
|
|
|
this.Sql(@"UPDATE RechKopf
|
|
SET Status = 1,
|
|
Mahnung1Datum = '2020-01-25 00:00:00.000',
|
|
Mahnung1BearbeiterI3D = 26,
|
|
Mahnstufe = 1,
|
|
MahnStop = 1,
|
|
MahnInfo = 'MahnInfo entry'
|
|
WHERE I3D = 31 OR I3D = 32");
|
|
|
|
this.Sql(@"UPDATE RechKopf
|
|
SET Status = 1,
|
|
Mahnung1Datum = '2020-01-25 00:00:00.000',
|
|
Mahnung1BearbeiterI3D = 26,
|
|
Mahnung2Datum = '2020-02-20 00:00:00.000',
|
|
Mahnung2BearbeiterI3D = 26,
|
|
Mahnstufe = 2
|
|
WHERE I3D = 36 OR I3D = 38 OR I3D = 39 OR I3D = 37");
|
|
|
|
this.Sql(@"UPDATE RechKopf
|
|
SET Status = 1,
|
|
Mahnung1Datum = '2020-01-25 00:00:00.000',
|
|
Mahnung1BearbeiterI3D = 26,
|
|
Mahnung2Datum = '2020-02-20 00:00:00.000',
|
|
Mahnung2BearbeiterI3D = 26,
|
|
Mahnung3Datum = '2020-03-20 00:00:00.000',
|
|
Mahnung3BearbeiterI3D = 26,
|
|
Mahnstufe = 3
|
|
WHERE I3D = 40 OR I3D = 41 OR I3D = 51 OR I3D = 54");
|
|
|
|
this.Sql(@$"UPDATE AccountCustomers
|
|
SET DunningInfo = 'DunningInfo',
|
|
DunningStop = 1,
|
|
DunningStopBegin = '2018-01-01 00:00:00.000',
|
|
DunningStopEnd = '2018-02-02 00:00:00.000'
|
|
WHERE Number = {this._referenceCustomerI3D}");
|
|
|
|
this.ForwardInvoiceToCreditVoucherPartsByPrice();
|
|
this.ForwardInvoiceToCreditVoucherPartsByQuantity();
|
|
}
|
|
|
|
private void ForwardInvoiceToCreditVoucherPartsByPrice()
|
|
{
|
|
var referenceInvoiceI3D = 51;
|
|
|
|
using var session = new BLSession();
|
|
var bl = session.GetBL<ReceiptWebServiceBL>();
|
|
var user = this.GetLoggedInUser();
|
|
|
|
var invoice = bl.GetReceiptByI3D(referenceInvoiceI3D, CentronObjectKindNumeric.InvoiceClass, user).ThrowIfError();
|
|
|
|
var forwardedReceipt = bl.ForwardReceipt(
|
|
CentronObjectKindNumeric.CreditVoucherClass,
|
|
user.User,
|
|
new ForwardReceiptData
|
|
{
|
|
CreateReceiptData = new CreateReceiptData
|
|
{
|
|
IgnoreCallbacks = true,
|
|
},
|
|
CreateReceiptItemsFromExistingItemsData = new List<CreateReceiptItemsFromExistingItemsData>
|
|
{
|
|
new()
|
|
{
|
|
IgnoreCallbacks = true,
|
|
},
|
|
},
|
|
IgnoreCallbacks = true
|
|
},
|
|
new List<ReceiptToForward>
|
|
{
|
|
new ReceiptToForward
|
|
{
|
|
ReceiptKind = invoice.ReceiptKind,
|
|
ReceiptI3D = invoice.I3D
|
|
}
|
|
},
|
|
null,
|
|
InsertReceiptTakeoverOptions.Reference,
|
|
true,
|
|
false).ThrowIfError();
|
|
|
|
var creditVoucher = (ReceiptCreditVoucherDTO)forwardedReceipt.Receipt;
|
|
|
|
var articlePos = creditVoucher.Items.First(f => f.ArticleCode == "SERHM");
|
|
|
|
articlePos.BasePrice = 100;
|
|
|
|
session.GetBL<ReceiptWebServiceBL>().SaveReceipt(
|
|
creditVoucher,
|
|
user.User,
|
|
new SaveReceiptData
|
|
{
|
|
IgnoreCallbacks = true
|
|
},
|
|
false,
|
|
CreatedThroughApplication.CentronNet,
|
|
null).ThrowIfError();
|
|
|
|
// the invoice is now closed so we need to manually reopen it
|
|
this.Sql(@$"UPDATE RechKopf
|
|
SET Status = 1
|
|
WHERE I3D = {referenceInvoiceI3D}");
|
|
}
|
|
|
|
private void ForwardInvoiceToCreditVoucherPartsByQuantity()
|
|
{
|
|
using var session = new BLSession();
|
|
var bl = session.GetBL<ReceiptWebServiceBL>();
|
|
var user = this.GetLoggedInUser();
|
|
|
|
var invoice = bl.GetReceiptByI3D(54, CentronObjectKindNumeric.InvoiceClass, user).ThrowIfError();
|
|
|
|
var forwardedReceipt = bl.ForwardReceipt(
|
|
CentronObjectKindNumeric.CreditVoucherClass,
|
|
user.User,
|
|
new ForwardReceiptData
|
|
{
|
|
CreateReceiptData = new CreateReceiptData
|
|
{
|
|
IgnoreCallbacks = true,
|
|
},
|
|
CreateReceiptItemsFromExistingItemsData = new List<CreateReceiptItemsFromExistingItemsData>
|
|
{
|
|
new()
|
|
{
|
|
IgnoreCallbacks = true,
|
|
},
|
|
},
|
|
IgnoreCallbacks = true
|
|
},
|
|
new List<ReceiptToForward>
|
|
{
|
|
new ReceiptToForward
|
|
{
|
|
ReceiptKind = invoice.ReceiptKind,
|
|
ReceiptI3D = invoice.I3D
|
|
}
|
|
},
|
|
null,
|
|
InsertReceiptTakeoverOptions.Reference,
|
|
true,
|
|
false).ThrowIfError();
|
|
|
|
var creditVoucher = (ReceiptCreditVoucherDTO)forwardedReceipt.Receipt;
|
|
|
|
var articlePos = creditVoucher.Items.First(f => f.ArticleCode == "SERHM");
|
|
|
|
Assert.NotNull(articlePos.QuantityComplete);
|
|
// the value should be exactly 2.0 but comparing decimals like this is not a great idea
|
|
Assert.InRange(articlePos.QuantityComplete.Value, (decimal)1.9, (decimal)2.1);
|
|
|
|
articlePos.QuantityComplete = 1;
|
|
|
|
session.GetBL<ReceiptWebServiceBL>().SaveReceipt(
|
|
creditVoucher,
|
|
user.User,
|
|
new SaveReceiptData
|
|
{
|
|
IgnoreCallbacks = true
|
|
},
|
|
false,
|
|
CreatedThroughApplication.CentronNet,
|
|
null).ThrowIfError();
|
|
}
|
|
|
|
#region DunningBL
|
|
private void GetCustomers()
|
|
{
|
|
using (var session = new BLSession())
|
|
{
|
|
InternalLoad("WithoutFilter", new DunningCustomerFilter());
|
|
|
|
InternalLoad("IncludeDunningStop", new DunningCustomerFilter
|
|
{
|
|
InDunningStop = true
|
|
});
|
|
|
|
InternalLoad("IncludingNoDunningLevel", new DunningCustomerFilter
|
|
{
|
|
IncludeNoDunningLevel = true
|
|
});
|
|
|
|
InternalLoad("IncludingCreditVouchers", new DunningCustomerFilter
|
|
{
|
|
IncludeCreditVouchers = true
|
|
});
|
|
|
|
InternalLoad("IncludingNotReadyForDunning", new DunningCustomerFilter
|
|
{
|
|
IncludeNotReadyForDunning = true
|
|
});
|
|
|
|
InternalLoad("NoneLevel", new DunningCustomerFilter
|
|
{
|
|
DunningLevels = new List<DunningLevel>
|
|
{
|
|
DunningLevel.None
|
|
}
|
|
});
|
|
|
|
InternalLoad("Level1", new DunningCustomerFilter
|
|
{
|
|
DunningLevels = new List<DunningLevel>
|
|
{
|
|
DunningLevel.Level1
|
|
}
|
|
});
|
|
|
|
InternalLoad("Level2", new DunningCustomerFilter
|
|
{
|
|
DunningLevels = new List<DunningLevel>
|
|
{
|
|
DunningLevel.Level2
|
|
}
|
|
});
|
|
|
|
InternalLoad("Level3", new DunningCustomerFilter
|
|
{
|
|
DunningLevels = new List<DunningLevel>
|
|
{
|
|
DunningLevel.Level3
|
|
}
|
|
});
|
|
|
|
InternalLoad("Level12", new DunningCustomerFilter
|
|
{
|
|
DunningLevels = new List<DunningLevel>
|
|
{
|
|
DunningLevel.Level1,
|
|
DunningLevel.Level2
|
|
}
|
|
});
|
|
|
|
|
|
InternalLoad("Level13", new DunningCustomerFilter
|
|
{
|
|
DunningLevels = new List<DunningLevel>
|
|
{
|
|
DunningLevel.Level1,
|
|
DunningLevel.Level3
|
|
}
|
|
});
|
|
|
|
void InternalLoad(string name, DunningCustomerFilter filter)
|
|
{
|
|
using (var bl2 = new BLSession())
|
|
{
|
|
// we need to set this to always true, otherwise we'll get 0 because this will filter everything before today
|
|
filter.IncludeBeforeDueDate = true;
|
|
|
|
var customers = bl2.GetBL<DunningWebServiceBL>().GetDunningCustomers(filter, this.GetLoggedInUser());
|
|
this.Verifier.Verify("DunningCustomers" + name, customers);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private void UpdateDunningStopAndInfo()
|
|
{
|
|
using (var session = new BLSession())
|
|
{
|
|
var invoiceDunningI3D = 2;
|
|
|
|
session.GetBL<DunningWebServiceBL>().UpdateDunningStopAndInfo(invoiceDunningI3D,
|
|
CentronObjectKindNumeric.InvoiceClass,
|
|
true,
|
|
"Updated dunning stop info for receipt",
|
|
new DateTime(2020, 1, 1),
|
|
new DateTime(2022, 1, 1),
|
|
this.GetLoggedInUser());
|
|
|
|
var invoiceDunning = session.GetBL<DunningWebServiceBL>().GetEntityById<InvoiceDunning>(invoiceDunningI3D);
|
|
|
|
this.Verifier.Verify("UpdatedInvoiceDunning", ObjectMapper.Map<InvoiceDunning, InvoiceDunningDTO>(invoiceDunning));
|
|
}
|
|
|
|
using (var session = new BLSession())
|
|
{
|
|
session.GetBL<DunningWebServiceBL>().UpdateDunningStopAndInfo(this._referenceCustomerI3D,
|
|
CentronObjectKindNumeric.CustomerClass,
|
|
true,
|
|
"Updated dunning stop info for customer",
|
|
new DateTime(2020, 1, 1),
|
|
new DateTime(2022, 1, 1),
|
|
this.GetLoggedInUser());
|
|
|
|
var customer = session.GetBL<DunningWebServiceBL>().GetDunningCustomers(new DunningCustomerFilter
|
|
{
|
|
CustomerI3Ds = new List<int>
|
|
{
|
|
this._referenceCustomerI3D
|
|
}
|
|
}, this.GetLoggedInUser()).FirstOrDefault();
|
|
|
|
this.Verifier.Verify("UpdatedCustomerDunningStop", customer);
|
|
}
|
|
}
|
|
|
|
private void CalculateDunningStatistics()
|
|
{
|
|
using (var session = new BLSession())
|
|
{
|
|
var statistics = session.GetBL<DunningWebServiceBL>().CalculateDunningStatistics(new DunningCustomerFilter
|
|
{
|
|
// we need to set this to always true, otherwise we'll get 0 because this will filter everything before today
|
|
IncludeBeforeDueDate = true
|
|
}, this.GetLoggedInUser());
|
|
|
|
this.Verifier.Verify("DunningStatistics", statistics);
|
|
}
|
|
}
|
|
|
|
private void GetAndUpdateSettings()
|
|
{
|
|
using (var session = new BLSession())
|
|
{
|
|
var bl = session.GetBL<DunningWebServiceBL>();
|
|
|
|
var settings = bl.GetSettings();
|
|
|
|
this.Verifier.Verify("DunningSettings", settings);
|
|
|
|
settings.DefaultInterest = 20;
|
|
|
|
settings.ShowLevel1 = true;
|
|
settings.Level1AfterDays = 20;
|
|
settings.Level1Fees = 20;
|
|
settings.Level1Text = "Updated settings text";
|
|
|
|
settings.ShowLevel2 = true;
|
|
settings.Level2AfterDays = 20;
|
|
settings.Level2Fees = 20;
|
|
settings.Level2Text = "Updated settings text";
|
|
|
|
settings.ShowLevel3 = true;
|
|
settings.Level3AfterDays = 20;
|
|
settings.Level3Fees = 20;
|
|
settings.Level3Text = "Updated settings text";
|
|
|
|
settings.PaymentToleranceInDays = 20;
|
|
|
|
bl.UpdateSettings(settings);
|
|
|
|
var updatedSettings = bl.GetSettings();
|
|
|
|
this.Verifier.Verify("UpdatedDunningSettings", updatedSettings);
|
|
}
|
|
}
|
|
|
|
private void GetAndUpdateCustomerSettings()
|
|
{
|
|
using (var session = new BLSession())
|
|
{
|
|
session.GetBL<DunningWebServiceBL>().UpdateDunningSettingsForCustomer(this._referenceCustomerI3D,
|
|
2,
|
|
DunningSendType.Print,
|
|
true,
|
|
this.GetLoggedInUser());
|
|
|
|
var updatedCustomer = session.GetBL<CustomerBL>().GetCustomer(this._referenceCustomerI3D);
|
|
|
|
this.Verifier.Verify("DunningCustomerSettings", new CustomerDunningSettingsTestClass(updatedCustomer));
|
|
}
|
|
}
|
|
|
|
private class CustomerDunningSettingsTestClass
|
|
{
|
|
public int CustomerI3D { get; set; }
|
|
|
|
public int? ContactPersonI3D { get; set; }
|
|
|
|
public DunningSendType? SendType { get; set; }
|
|
|
|
public bool UseAlternativeInvoiceAddress { get; set; }
|
|
|
|
public CustomerDunningSettingsTestClass(Customer customer)
|
|
{
|
|
this.CustomerI3D = customer.I3D;
|
|
this.ContactPersonI3D = customer.DunningContactPerson?.I3D;
|
|
|
|
switch (customer.DunningKind)
|
|
{
|
|
case "Mail":
|
|
this.SendType = DunningSendType.Mail;
|
|
break;
|
|
case "Print":
|
|
this.SendType = DunningSendType.Print;
|
|
break;
|
|
case "Fax":
|
|
this.SendType = DunningSendType.Fax;
|
|
break;
|
|
default:
|
|
this.SendType = null;
|
|
break;
|
|
}
|
|
|
|
this.UseAlternativeInvoiceAddress = customer.DunningAtAlternateInvoiceAddress;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
private void ExecuteDunningRun()
|
|
{
|
|
DunningCustomerDTO customer;
|
|
using (var session = new BLSession())
|
|
{
|
|
customer = session.GetBL<DunningWebServiceBL>().GetDunningCustomers(new DunningCustomerFilter
|
|
{
|
|
IncludeBeforeDueDate = true,
|
|
CustomerI3Ds = new List<int>
|
|
{
|
|
this._referenceCustomerI3D
|
|
}
|
|
}, this.GetLoggedInUser()).FirstOrDefault();
|
|
|
|
}
|
|
|
|
using (var session = new BLSession())
|
|
{
|
|
var bl = session.GetBL<DunningRunWebServiceBL>();
|
|
|
|
bl.EndToEndTestMode = true;
|
|
|
|
var result = bl.ExecuteDunningRun(new DunningRunForCustomerDTO
|
|
{
|
|
Customer = customer,
|
|
Invoices = customer.Invoices.Where(f => f.DunningLevel != DunningLevel.Level3).ToList(),
|
|
CreditVouchers = customer.CreditVouchers,
|
|
MailBody = new MailBodyDTO
|
|
{
|
|
Text = "Email Body",
|
|
Type = Interfaces.Mail.Enums.CentronMailBodyType.Plain
|
|
},
|
|
MailSubject = "Email Subject",
|
|
SendType = DunningSendType.Mail
|
|
}, this.GetLoggedInUser());
|
|
|
|
this.Verifier.Verify("DunningRun", result);
|
|
}
|
|
}
|
|
|
|
private void LoadDunningRuns()
|
|
{
|
|
using (var session = new BLSession())
|
|
{
|
|
var runs = session.GetBL<DunningRunWebServiceBL>().GetDunningRuns(new DunningRunFilter
|
|
{
|
|
TimeSpan = LogTimeSpan.Last30Days,
|
|
CustomerI3Ds = new List<int>
|
|
{
|
|
this._referenceCustomerI3D
|
|
}
|
|
}, this.GetLoggedInUser());
|
|
|
|
this.Verifier.Verify("SingleDunningRun", runs);
|
|
}
|
|
}
|
|
|
|
private void ResetDunningRun()
|
|
{
|
|
using (var session = new BLSession())
|
|
{
|
|
var dunningRunNumber = 9;
|
|
|
|
session.GetBL<DunningRunWebServiceBL>().ResetDunningRun(dunningRunNumber, this.GetLoggedInUser());
|
|
|
|
var resetDunningRun = session.GetBL<DunningRunWebServiceBL>().GetDunningRuns(new DunningRunFilter
|
|
{
|
|
DunningRunNumbers = new List<int>
|
|
{
|
|
dunningRunNumber
|
|
}
|
|
}, this.GetLoggedInUser());
|
|
|
|
this.Verifier.Verify("ResetSingleDunningRun", resetDunningRun);
|
|
}
|
|
}
|
|
}
|
|
}
|