Ab Semiramis 4.2 können zum Löschen markierte Partner und Rollen mit der Reorganisation gelöscht werden. Um zum Löschen markierte Daten physisch löschen zu können, muss deren Verwendung in anderen Daten geprüft werden.
In diesem Dokument werden die technisch notwendigen Registrierungen erklärt und beschrieben, wie eine Erweiterung für eigene zu prüfende Objekte aussehen könnte.
1 Zielgruppe
- Entwickler
2 Programmierschnittstelle
2.1 Erzeugung der Usage-Klassen
Außer in den Rollen „Basis“ und „Mitarbeiter“ werden alle Partnerrollen organisationsabhängig gespeichert. Für den Zugriff auf diese Daten ist also immer eine Organisation erforderlich. Somit ist bei den verwendenden Business Objekten auch ein Organisationsbezug zu ermitteln. Einige Standardfälle werden unterstützt:
- Es wird nur die Partner-GUID ohne Organisationsbezug verwendet, z.B. Verwendungen, die nur auf Ebene des Mandanten erhalten bleiben müssen oder bezogen auf Basisdaten.
- Partner- und Organisations-GUID sind in dem BO (Business Objekt) enthalten.
- Die Partner-GUID ist in einem BO und die Organisations-GUID in einem anderen BO, das über einen einfachen join verbunden werden kann.
Im Normalfall gibt es in den verwendenden BOs nur ein Attribut, dass sich auf den Partner bezieht. Ebenso werden praktisch immer die Joins nur über je ein Attribut gesteuert. Für diese Fälle existieren createUsage(..) Methoden in der Registrierungsklasse com.cisag.app.general.partner.reorg.PartnerUsageRegistry, die diese Möglichkeiten abbilden. Es gibt eine Standardlogik, die mit den Registrierungsinformationen entsprechende OQL-Statements erstellt, ausführt und die möglichen Verwender ermittelt. Der Rückgabewert dieser createUsage(..) Methoden ist eine Instanz der StandardUsage Klasse, die diese Standardlogik implementiert.
Weiterhin existiert eine Signatur der createUsage Methode, die den allgemeinst möglichen Fall der Standardlogik abdeckt. Die joins können mehrere Attribute benötigen. Ebenso ist es möglich, dass ein BO mehrere Partner-GUID Attribute enthält, die gleichzeitig überprüft werden sollen.
Für komplexere Datenmodelle oder wenn besondere Bedingungen erfüllt sein müssen, ist es notwendig eine Prüfklasse zu programmieren, die das Interface com.cisag.app.general.partner.reorg.Usage implementiert.
2.1.1 Partner-GUID ohne Organisationsbezug
Der Aufruf der Methode
createUsage(clazz, „partnerAttribute“)
Erzeugt eine Standardlogik Instanz die im Prinzip folgendes OQL Statement enthält.
Select .. from clazz uc where uc:partnerAttribute = ?
2.1.2 Partner- und Organisations-GUID in einem BO
Der Aufruf der Methode
createUsage(clazz, „partnerAttribute“, “organizationalUnitAttribute”)
Erzeugt eine Standardlogik Instanz die im Prinzip folgendes OQL Statement enthält.
Select .. from clazz uc where uc:partnerAttribute = ? and uc:organizationalUnitAttribute = ?
2.1.3 Partner- und Organisations-GUID in unterschiedlichen BOs (einfacher Join)
Der Aufruf der Methode
createUsage(clazz, „partnerAttribute“, “source”, targetClass, “target” “organizationalUnitAttribute”)
Erzeugt eine Standardlogik Instanz die im Prinzip folgendes OQL Statement enthält.
Select .. from clazz uc
join targetClass tc on uc:source = tc:target
where uc:partnerAttribute = ? and tc:organizationalUnitAttribute = ?
2.2 Registrierung der Usage-Klassen
In der Klasse com.cisag.app.general.partner.reorg.PartnerUsageRegistry werden die zu überprüfenden Business Objekte registriert. Diese Business Objekt müssen einen Partner als Fremdschlüssel enthalten. Außerdem benötigen sie die Partnerdaten auch weiterhin. Enthält ein Business Objekt zwar einen Partner als Fremdschlüssel, benötigt die Daten jedoch nicht zwingend, dann braucht dieses Business Objekt nicht registriert werden. Allgemein ausgedrückt, die Registrierung enthält die Informationen über Business Objekte, die unter bestimmten Bedingungen gegen das endgültige Löschen von Partnerdaten Einspruch einlegen.
Bei der Registrierung muss die Partnerrolle berücksichtigt werden und ob es sich bei dem Business Objekt um Stammdaten handelt. Für jede Rolle existiert eine Methode, deren Name sich an den Einträgen im ValueSet SubapplicationSelection orientiert.
Beispiele:
- registerBase( false, createUsage(com.cisag.app.inventory.obj.InventoryOnhand.class, “owner” ));
- registerCustomer( false, createUsage(com.cisag.app.sales.obj.CustomerInvoiceDetail.class, “deliveryCustomer”, “header”,com.cisag.app.sales.obj.CustomerInvoice.class, “guid”, “invoicingParty” ));
- registerSupplier( true, new PartnerRelationUsage());
Im ersten Beispiel wird das Business Objekt InventoryOnhand unter Verwendung einer der beschriebenen createUsage Methoden registriert. Die Basisdaten eines Partners dürfen somit nur gelöscht werden, falls es keine Einträge im Business Objekt InventoryOnhand gibt, die sich über das Attribut „owner“ auf den in Frage stehenden Partner beziehen.
Im zweiten Beispiel wird das Business Objekt CustomerInvoiceDetail unter Verwendung einer der beschriebenen createUsage Methoden registriert. Die Kundendaten eines Partners dürfen somit nur gelöscht werden, falls es keine Einträge im Business Objekt CustomerInvoiceDetail gibt, die sich über das Attribut „deliveryCustomer“ auf den in Frage stehenden Partner beziehen. In diesem Fall lässt sich der Organisationsbezug über den Zusammenhang zwischen CustomerInvoiceDetail und CustomerInvoice bestimmen.
Im dritten Beispiel wird direkt eine Klasse zur Überprüfung der Abhängigkeiten im Business Objekt PartnerRelation bezogen auf die Lieferantendaten des Partners registriert. Die Klasse PartnerRelationUsage muss das Interface Usage implementieren. Dies ist der Weg eine Prüfung zu registrieren, die sich den mit den createUsage Methoden erzeugten Standardfällen entzieht.
2.3 Implementieren des Usage-Interfaces
Für alle BOs, die sich nicht durch die o.a. Standards behandeln lassen, ist die Programmierung einer Prüfklasse notwendig, die das Interface com.cisag.app.general.partner.reorg.Usage implementiert. Die Namenskonvention ist Name des zu prüfenden BOs plus Suffix „Usage“. Das Interface enthält die Methoden getUsageClass, isInUse und usedBy. Die Methode getUsageClass gibt die zur Überprüfung registrierte Klasse usageClass zurück und dient hauptsächlich der Wartung. Die Methode isInUse prüft die Existenz von Partnerreferenzen und usedBy ermittelt die referenzierenden Instanzen. Die Funktionsweise wird an einem Beispiel erläutert.
public class TaxExemptionUsage implements Usage{
private final CisEnvironment env = CisEnvironment.getInstance();
private final CisObjectManager om = env.getObjectManager();
public TaxExemptionUsage() {
}
public Class getUsageClass() {
return TaxExemption.class;
}
public boolean isInUse(Chunk chunk) {
short role=chunk.getRole();
if (role != SubapplicationSelection.CUSTOMER && role != SubapplicationSelection.SUPPLIER) {
throw new IllegalArgumentException(“Only customer and supplier role are allowed.”);
}
int size = chunk.countToCheck();
if (size <= 0) {
return false;
}
boolean isInUse = false;
StringBuffer oql = new StringBuffer(“select o:partner, o:organizationalUnit from “);
oql.append(” TaxExemption o where (“);
for (int i = 0; i < size; i++) {
oql.append(“(o:partner=? and o:organizationalUnit=?) or “);
}
oql.setLength(oql.length() – 4);
oql.append(“) and o:kind=?”);
CisResultSet rs = om.getResultSet( oql.toString() );
try {
int offset = 0;
Iterator comboIt = chunk.retrieveCombinationsToCheck();
while (comboIt.hasNext()) {
Entry currentCombo = (Entry) comboIt.next();
rs.setGuid( ++offset, currentCombo.getGuid() );
rs.setGuid( ++offset, currentCombo.getOrganizationalUnit() );
}
rs.setShort(++offset, role == SubapplicationSelection.CUSTOMER?
TaxExemptionKind.EXTERNAL :
TaxExemptionKind.OWN);
while (rs.next()) {
chunk.setInUse( rs.getGuid( 1 ), rs.getGuid( 2 ) );
isInUse = true;
}
} catch (java.sql.SQLException ex) {
throw new RuntimeException( ex );
} finally {
rs.close();
}
return isInUse;
}
public CisObject[] usedBy(Chunk.Entry entry) {
short role = entry.getRole();
if (role != SubapplicationSelection.CUSTOMER && role != SubapplicationSelection.SUPPLIER) {
throw new IllegalArgumentException(“Only customer and supplier role are allowed.”);
}
byte[] partnerGuid = entry.getGuid();
byte[] organizationalUnit = entry.getOrganizationalUnit();
String oql = “select o:guid from ” +
“com.cisag.app.financials.obj.TaxExemption o where ” +
“o:partner=? and o:organizationalUnit=? ” +
“and o:kind=?”;
List result = new ArrayList();
CisResultSet rs = om.getResultSet( oql );
try {
rs.setGuid( 1, partnerGuid);
rs.setGuid( 2, organizationalUnit);
rs.setShort( 3, role == SubapplicationSelection.CUSTOMER?
TaxExemptionKind.EXTERNAL :
TaxExemptionKind.OWN);
rs.setMaxRows(PartnerReorganizationLogic.MAX_USAGE);
while(rs.next()){
result.add(TaxExemption.buildPrimaryKey(rs.getGuid(1)));
}
} catch (java.sql.SQLException ex) {
throw new RuntimeException( ex );
} finally {
rs.close();
}
CisObject[] resultObj = om.getObjectArray(result, CisObjectManager.READ);
return resultObj;
}
}
Hinweis: Das Beispiel dient nur der Verdeutlichung. Es entspricht nicht dem aktuellen Stand der Javaklasse im System und wird bei möglichen Änderungen der Javaklasse im System auch nicht aktualisiert.
Die Registrierung erfolgt dann wie folgt:
registerCustomer( true, new TaxExemptionUsage());
Das Überprüfen der richtigen Rolle erfolgt in den Methoden isInUse und usedBy. Die Rolle kann sowohl am Chunk, als auch am Entry abgefragt werden.
Die usedBy-Methode ermittelt die Partner-Organisations-Kombinationen, die von Instanzen des BOs noch referenziert werden. Die Methode bekommt einen Chunk übergeben. Dieser enthält die zu prüfenden Partner-Organisations-Kombinationen. Ein Chunk enthält immer nur die Daten für eine Rolle. Er kann mehrere Partner-GUIDs enthalten. Zu jeder Partner-GUID enthält er alle Organisations-GUIDs, die geprüft werden müssen. Die Kombinationen, die geprüft werden müssen, können am Chunk mit retrieveCombinationsToCheck abgeholt werden. Im Chunk wird dann mittels setInUse-Aufruf angegeben, welche Partner-Organisations-Kombinationen noch verwendet wird. Ist die betrachtete Rolle organisationsunabhängig (Partner-Basis oder Mitarbeiter) so enthält die Organisation trotzdem immer den Mandanten, auch wenn diese Information nicht benötigt wird. Dies ist bei dem setInUse-Aufruf zu berücksichtigen.
Bei der isInUse-Methode sollte bei der Definition der OQL-Abfrage darauf geachtet werden, dass mehrere Partner-Organisations-Kombinationen in einer Abfrage erfolgen. Die Selektionsfelder sollten die Partner-Organisations-Kombinationen sein. So können die referenzierten Kombinationen mittels setInUse gekennzeichnet werden.
Die usedBy-Methode wird nur aufgerufen, wenn bei der Reorganisation ausführliche Meldungen gewünscht werden. Ihr wird ein Chunk.Entry übergeben. Dieser enthält eine Partner-Organisations-Kombination, für die referenzierende Instanzen ermittelt wurden. Diese referenzierenden Instanzen gibt die usedBy-Methode zurück. Bei der Definition der OQL-Abfrage sollte hier als Selektionsfeld der primäre Schlüssel des BOs gewählt werden.
2.4 Registrierung von zu löschenden BOs
Falls eine Schlüssel Kombination durch alle Tests gekommen ist, müssen die entsprechenden Daten nun noch physisch gelöscht werden. Dies erfordert die Registrierung der zugehörigen Business Objekte. Im Normalfall umfasst die Registrierung für die Partner Reorganisation das Business Objekt der Rolle (also z.B. com.cisag.app.sales.obj.Customer für die Kundenrolle) plus zusätzliche abhängige BOs.
Für die Registrierung existieren pro Rolle Methoden an der Klasse PartnerUsageRegistry, hier einige Beispiele:
registerDeletionCustomer( com.cisag.app.sales.obj.SalesOutputSettings.class, “partner”, “organizationalUnit”);
registerDeletionCustomer( new PartnerRelationDeletion());
registerDeletionCustomer( com.cisag.app.sales.obj.Customer.class);
Im ersten Beispiel werden die SalesOutputSettings registriert, die über die Attribute „partner“ und „organizationalUnit“ mit dem Hauptobjekt Customer verknüpft sind. Im zweiten Beispiel wird eine Javaklasse registriert, die das Deletion-Interface implementiert und im letzten Beispiel wird einfach das Hauptobjekt für die Rolle „Kunde“ zum Löschen registriert. Wenn keine Attribute angegeben werden, so geht die Standardlogik davon aus, dass die verknüpfenden Attribute „guid“ und „organizationalUnit“ sind, wie es im Customer-BO auch der Fall ist.
2.5 Implementieren des Deletion-Interfaces
Das Deletion-Interfaces umfasst die Methoden getDeletionClass, delete und deleteExpiredVersion.
Die Methode getDeletionClass gibt die zum Löschen registrierte Klasse deletionClass zurück und dient hauptsächlich der Wartung (pro Verwendung registrierte Klassen können so ausgegeben werden etc.).
Die Methode deleteExpiredVersions ist für zukünftige Erweiterungen vorge-sehen. Sie löscht bei einem zeitabhängigen BO die Versionen, deren Gültig-keitszeitraum vor dem übergebenen Datum liegt. Bei nicht zeitabhängigen BOs braucht die Methode nur true zurückgeben.
Die Methode delete löscht bei zeitabhängigen BOs alle Versionen. Für welche Partner-Organisations-Kombinationen die Daten gelöscht werden müssen, wird über retrieveFreeCombinations am Chunk abgefragt.
Als Beispiel kann com.cisag.app.general.partner.reorg.PartnerRelationDeletion herangezogen werden.