Tenant-Settings in Inst-Modul
This documentation is only for using radzen along with ERP system eEvolution. It could work as an inspiration though for similar purposes.
You can get basic information about eevolution tenant configuration here: https://webhelp.eevolution.de/11.0.9/inst/inst.htm#cshid=inst/dlgMdtBearb.htm
So in this dialog you configure which entities in eEvolution are tenant specific. This configuration information is stored in table SYSTEM like this:
Field WERT (value) contains a string. Each char represents an entity area in eEvolution. In eEvolution code you find a class “MStammDatValues” which explains the meaning of each char.
namespace nGroup.Info.eEvolution.Global
{
/// <summary>Die <see cref="MStammDatValues" /> Klasse enthält die Werte die in dem MStammDat Parameter stehen können.</summary>
public class MStammDatValues
{
#region Fields
/// <summary>Der Wert für Mandantengeführte Artikel.</summary>
public static string Artikel = "A";
/// <summary>Der Wert für Mandantengeführte Aufträge.</summary>
public static string Auftrag = "U";
/// <summary>Der Wert für Mandantengeführte Bestellungen.</summary>
public static string Bestell = "B";
/// <summary>Der Wert für Mandantengeführte Inventare.</summary>
public static string Inventar = "I";
/// <summary>Der Wert für Mandantengeführte Kunden.</summary>
public static string Kunde = "K";
/// <summary>Der Wert für Mandantengeführte Lieferant.</summary>
public static string Lieferant = "L";
/// <summary>Der Wert für Mandantengeführte Mitarbeiter.</summary>
public static string Mitarbeiter = "M";
/// <summary>Der Wert für Mandantengeführte Modelle.</summary>
public static string Model = "O";
/// <summary>Die Parameterbezeichnung in der Systemtabelle.</summary>
public static string SystemParameter = "MStammDat";
/// <summary>Der Wert für Mandantengeführte Produktionen.</summary>
public static string Produktion = "P";
/// <summary>Der Wert für Mandantengeführte Verträge.</summary>
public static string Vertrag = "V";
#endregion
}
}
In eEvolution database each table has a column called mandant of type int default 0. Up to current eEvolution version 11 not all entities can be tenant specific. Only those defined in this class can be. Especially most of the system settings and system tables like “Lieferbedingungen”, “Mwstschl”, “Liefart”, “Bestart” etc. are not tenant specific which means their values are the same over all tenants in one database. This a very important limitation and has to be kept in mind when you work on storing and retrieving data.
Tenant configuration in RADZen
First of all there is a built in tenant support in radzen. It works with separate databases for each tenant which differs from eEvolution concept where different tenants can reside inside one single database. This offers the possibility to share certain data like for example customers between tenants.
My implementation of eEvolutions tenant mechanism is explained here. I do not explain radzen´s built in tenant mechanism.
First of all it is important to know that tenant master data is located in table “Firmendaten”. To be able to select a tenant inside radzen I placed a dropdown list in menu bar of MainLayout so that this is available and changeable in every screen.
The corresponding data in “Firmendaten” looks like this:
In radzen I defined a global property called SelectedTenant which is data bound to this dropdown list.
Definition of SelectedTenant.
After having initialized SelectedTenant with null, I look for a cookie to find out whether a tenant has been selected before. If so, the retrieved cookie value is then assigned to SelectedTenant.
var tenant = await cookie.GetValue("SelectedTenant");
if (tenant != "") { Globals.SelectedTenant = Convert.ToInt32(tenant); }
Services.TenantService tenantService = new Services.TenantService(EEvolution, Globals);
tenantService.InitTenant((int)Globals.SelectedTenant);
Finally I call InitTenant where all tenant properties are assigned according to tenant configuration in inst module:
public void InitTenant(int tenantId)
{
// Systemkonfiguration der Mandanteneinstellungen abfragen
string tenantConfiguration = context.System1s.Where(s => s.para == SystemParameter).Select(s => s.WERT).First();
// Mandantenkonfigurationen der einzelnen eEvolution-Bereiche zuordnen
if (tenantConfiguration.Contains(Artikel))
{
globals.TenantArtikel = tenantId;
}
else
{
globals.TenantArtikel = 0;
}
if (tenantConfiguration.Contains(Auftrag))
{
globals.TenantAuftrag = tenantId;
}
else
{
globals.TenantAuftrag = 0;
}
There are tenant properties according to configuration which are also defined in radzen globals:
public partial class GlobalsService
{
// Definition der Mandantenvariablen nach Softwarebereichen, weil es in eEvolution so gemacht ist und weil es die Möglichkeit gibt,
// mandantenübergreifende Bereiche zu definieren wie zum Beispiel, dass die Artikel in allen Mandanten gleich sind
int _TenantArtikel;
public int TenantArtikel
{
get
{
return _TenantArtikel;
}
set
{
if (!object.Equals(_TenantArtikel, value))
{
var args = new PropertyChangedEventArgs() { Name = "TenantArtikel", NewValue = value, OldValue = _TenantArtikel, IsGlobal = true };
_TenantArtikel = value;
PropertyChanged?.Invoke(args);
}
}
}
int _TenantAuftrag;
public int TenantAuftrag
{
get
{
return _TenantAuftrag;
}
set
{
if (!object.Equals(_TenantAuftrag, value))
{
var args = new PropertyChangedEventArgs() { Name = "TenantAuftrag", NewValue = value, OldValue = _TenantAuftrag, IsGlobal = true };
_TenantAuftrag = value;
PropertyChanged?.Invoke(args);
}
}
}
int _TenantBestell;
public int TenantBestell
{
get
{
return _TenantBestell;
}
set
{
if (!object.Equals(_TenantBestell, value))
{
var args = new PropertyChangedEventArgs() { Name = "TenantBestell", NewValue = value, OldValue = _TenantBestell, IsGlobal = true };
_TenantBestell = value;
PropertyChanged?.Invoke(args);
}
}
}
int _TenantInventar;
public int TenantInventar
{
get
{
return _TenantInventar;
}
set
{
if (!object.Equals(_TenantInventar, value))
{
var args = new PropertyChangedEventArgs() { Name = "TenantInventar", NewValue = value, OldValue = _TenantInventar, IsGlobal = true };
_TenantInventar = value;
PropertyChanged?.Invoke(args);
}
}
}
int _TenantKunde;
public int TenantKunde
{
get
{
return _TenantKunde;
}
set
{
if (!object.Equals(_TenantKunde, value))
{
var args = new PropertyChangedEventArgs() { Name = "TenantKunde", NewValue = value, OldValue = _TenantKunde, IsGlobal = true };
_TenantKunde = value;
PropertyChanged?.Invoke(args);
}
}
}
int _TenantLieferant;
public int TenantLieferant
{
get
{
return _TenantLieferant;
}
set
{
if (!object.Equals(_TenantLieferant, value))
{
var args = new PropertyChangedEventArgs() { Name = "TenantLieferant", NewValue = value, OldValue = _TenantLieferant, IsGlobal = true };
_TenantLieferant = value;
PropertyChanged?.Invoke(args);
}
}
}
int _TenantMitarbeiter;
public int TenantMitarbeiter
{
get
{
return _TenantMitarbeiter;
}
set
{
if (!object.Equals(_TenantMitarbeiter, value))
{
var args = new PropertyChangedEventArgs() { Name = "TenantMitarbeiter", NewValue = value, OldValue = _TenantMitarbeiter, IsGlobal = true };
_TenantMitarbeiter = value;
PropertyChanged?.Invoke(args);
}
}
}
int _TenantModel;
public int TenantModel
{
get
{
return _TenantModel;
}
set
{
if (!object.Equals(_TenantModel, value))
{
var args = new PropertyChangedEventArgs() { Name = "TenantModel", NewValue = value, OldValue = _TenantModel, IsGlobal = true };
_TenantModel = value;
PropertyChanged?.Invoke(args);
}
}
}
int _TenantProduktion;
public int TenantProduktion
{
get
{
return _TenantProduktion;
}
set
{
if (!object.Equals(_TenantProduktion, value))
{
var args = new PropertyChangedEventArgs() { Name = "TenantProduktion", NewValue = value, OldValue = _TenantProduktion, IsGlobal = true };
_TenantProduktion = value;
PropertyChanged?.Invoke(args);
}
}
}
int _TenantVertrag;
public int TenantVertrag
{
get
{
return _TenantVertrag;
}
set
{
if (!object.Equals(_TenantVertrag, value))
{
var args = new PropertyChangedEventArgs() { Name = "TenantVertrag", NewValue = value, OldValue = _TenantVertrag, IsGlobal = true };
_TenantVertrag = value;
PropertyChanged?.Invoke(args);
}
}
}
}
So after InitTenant has finished, all tenant area properties are assigned. Finally I extend db service so that tenant separation is maintained. I’ll explain this along with table “aagfakt” which contains document header informations like invoice customer etc.
My db data source is called eEvolution so my radzen generated db service is named EEvolutionService and my modifications are defined in Eevolution.Custom.cs.
I only have to use two events per entity, OnAagfaktsRead and OnAagfaktCreated:
partial void OnAagfaktsRead(ref IQueryable<Aagfakt> items)
{
items = items.Where(i => i.mandant == Globals.TenantAuftrag);
}
partial void OnAagfaktCreated(Aagfakt item)
{
item.mandant = Globals.TenantAuftrag;
}
In OnAagfaktsRead I simply filter the records according to my selected tenant and in OnAagfaktCreated I set tenant value to item.mandant so that my new record is stored accordingly to my selected tenant.
To have Globals available in my service I declare a variable of GlobalsService:
and on loading of MainLayout I assign Globals value:
Like this it is available on all pages derived from MainLayout.