Wprowadzenie
W systemie możliwe jest usuwanie artykułów i ich zastosowań oznaczonych do usunięcia za pomocą funkcji reorganizacji. Aby możliwe było fizyczne usunięcie danych oznaczonych do usunięcia, konieczne jest sprawdzenie ich powiązań z innymi danymi.
Grupa docelowa
- programiści
Interfejs programowania
Aby zarejestrować obiekt biznesowy (BO) do sprawdzania w ramach reorganizacji artykułów, konieczna jest klasa implementująca interfejs com.cisag.app.general.item.reorg.Usage oraz jej rejestracja.
Jeśli dany artykuł lub jego zastosowanie nie są używane, należy również zarejestrować wszystkie obiekty, które w takim przypadku mają zostać usunięte. W tym celu również musi zostać zarejestrowana klasa, która implementuje interfejs com.cisag.app.general.item.reorg.Deletion.
Tworzenie klas użytkowania
Z wyjątkiem użycia Podstawowe, wszystkie użycia artykułów są zapisywane w sposób zależny od organizacji. Oznacza to, że do uzyskania dostępu do tych danych zawsze wymagana jest organizacja. W związku z tym, w używanych obiektach biznesowych również należy określić powiązanie z organizacją. Obsługiwane są niektóre standardowe przypadki:
-
wykorzystywany jest jedynie GUID artykułu, bez odniesienia do organizacji, np. dla użycia, które mają być zachowane wyłącznie na poziomie klienta lub odnoszą się do danych podstawowych
-
GUID artykułu i GUID organizacji znajdują się w tym samym obiekcie biznesowym (BO)
-
GUID artykułu znajduje się w jednym BO, a GUID organizacji w innym, połączonym prostym join
-
GUID artykułu znajduje się w jednym BO, a GUID organizacji w innym BO, który jest połączony za pomocą złożonego (dwukrotnego) join
W typowych przypadkach obiekty biznesowe zawierają tylko jedno atrybutowe odniesienie do artykułu. Join są zwykle realizowane również przez jedno pole. Dla takich przypadków dostępne są metody createUsage(..), które umożliwiają ich odwzorowanie. Istnieje logika standardowa, która na podstawie informacji rejestracyjnych generuje odpowiednie zapytania OQL, wykonuje je i identyfikuje możliwe powiązania. Zwracanym wynikiem metod createUsage(..) jest instancja klasy StandardUsage, która implementuje tę logikę.
Dostępna jest także wersja metody createUsage, której sygnatura obsługuje najbardziej ogólny przypadek w ramach logiki standardowej. Join mogą wtedy obejmować wiele atrybutów, a także możliwe jest, że dany BO zawiera wiele atrybutów z identyfikatorami artykułów, które mają być sprawdzane jednocześnie.
W przypadku bardziej złożonych modeli danych lub konieczności uwzględnienia szczególnych warunków, należy zaprogramować klasę kontrolną, która implementuje interfejs com.cisag.app.general.item.reorg.Usage.
GUID artykułu bez odniesienia organizacyjnego
Wywołanie metody
createUsage(clazz, "itemAttribute")
Select .. from clazz uc where uc:itemAttribute = ?
GUID pozycji i organizacji w BO
Wywołanie metody:
createUsage(clazz, „itemAttribute”, „organisationalUnitAttribute”)
Generuje standardową instancję logiczną, która zawiera następujące zapytanie OQL:
Select .. from clazz uc
where uc:itemAttribute = ? and
uc:organizationalUnitAttribute = ?
GUID pozycji i organizacji w różnych BO (proste połączenie)
Wywołanie metody:
createUsage(clazz, "itemAttribute", "source", targetClass, "target"
"organizationalUnitAttribute")
Generuje standardową instancję logiczną, która zawiera następujące zapytanie OQL:
Select ..
from clazz uc
join targetClass tc on uc:source = tc:target
where uc:itemAttribute = ? and tc:organisationalUnitAttribute = ?
GUID pozycji i organizacji w różnych BO (podwójne sprzężenie)
Wywołanie metody:
createUsage(clazz, "itemAttribute", "source", linkingClass, "linkToSource", "linkToTarget", targetClass, "target", "organizationalUnitAttribute")
Generuje standardową instancję logiczną, która zawiera następujące zapytanie OQL:
Select ..
from clazz uc
join linkingClass lc on uc:source = lc:linkToSource
join targetClass tc on lc:linkToTarget = tc:target
where uc:itemAttribute = ? and tc:organizationalUnitAttribute = ?
Rejestrowanie klas użytkowania
Obiekty biznesowe do sprawdzenia są rejestrowane w klasie:
com.cisag.app.general.item.reorg.ItemUsageRegistry.
Te obiekty biznesowe muszą zawierać artykuł jako klucz obcy. Ponadto muszą one nadal korzystać z danych artykułu. Jeżeli obiekt biznesowy zawiera co prawda artykuł jako klucz obcy, ale dane te nie są mu niezbędne, wówczas taki obiekt biznesowy nie musi być rejestrowany. Rejestracja zawiera informacje o obiektach biznesowych, które — pod pewnymi warunkami — mogą zablokować trwałe usunięcie danych artykułu.
Podczas rejestracji należy uwzględnić rodzaj użycia artykułu oraz to, czy dany obiekt biznesowy dotyczy danych podstawowych. Dla każdego typu użycia istnieje metoda rejestracji, której nazwa odpowiada wpisom w ValueSet ItemView.
registerCommon(true, createUsage(com.cisag.app.general.obj.Kit.class, "guid"))
Obiekt biznesowy Kit jest rejestrowany z użyciem jednej z metod createUsage. Oznacza to, że dane podstawowe artykułu mogą zostać usunięte tylko wtedy, gdy nie istnieją żadne wpisy w obiekcie Kit, które odnoszą się do danego artykułu za pomocą atrybutu guid.
registerSales(false, createUsage(com.cisag.app.sales.obj.SalesOrderDetail.class, "item", "header", com.cisag.app.sales.obj.SalesOrder.class, "guid", "invoicingPartyData", com.cisag.app.general.obj.OrderPartnerDataInfo.class, "guid", "partner"))
Obiekt SalesOrderDetail jest również rejestrowany z użyciem jednej z metod createUsage. Dane sprzedażowe artykułu mogą zostać usunięte tylko wtedy, gdy nie istnieją wpisy w obiekcie SalesOrderDetail, które odnoszą się do danego artykułu za pomocą atrybutu item. W tym przypadku powiązanie organizacyjne ustalane jest poprzez zależność między obiektami SalesOrderDetail, SalesOrder i OrderPartnerDataInfo.
registerInventory(false, new InventoryTransactionUsage())
Rejestrowana jest bezpośrednio klasa InventoryTransactionUsage, która sprawdza zależności w obiekcie InventoryTransaction w odniesieniu do danych logistyki magazynowej artykułu. Klasa InventoryTransactionUsage musi implementować interfejs Usage.
Jest to sposób rejestracji sprawdzania zależności, który wykracza poza standardowe przypadki obsługiwane przez metody createUsage.
Implementacja interfejsu użytkowania
Dla wszystkich obiektów biznesowych (BO), które nie mogą zostać obsłużone przez wyżej wymienione standardowe mechanizmy, konieczne jest stworzenie klasy kontrolnej, która implementuje interfejs com.cisag.app.general.item.reorg.Usage.
Konwencja nazewnictwa zakłada użycie nazwy obiektu biznesowego, który ma być sprawdzany, z dodanym sufiksem Usage.
Interfejs Usage zawiera następujące metody:
- getUsageClass – zwraca klasę usageClass, która została zarejestrowana do weryfikacji, i służy głównie do celów administracyjnych (np. umożliwia wyświetlenie klas zarejestrowanych dla danego użycia)
- isInUse – sprawdza, czy istnieją referencje do artykułu
- usedBy – identyfikuje instancje, które odwołują się do danego artykułu.
public class ReceiptOfGoodsDetailUsage implements Usage{
private final CisEnvironment env =CisEnvironment.getInstance();
private final CisObjectManager om = env.getObjectManager();
private final Class usageClass = ReceiptOfGoodsDetail.class;
public Class getUsageClass(){
return usageClass;
}
public boolean isInUse(Chunk chunk){
switch(chunk.getRole()){
case ItemView.INVENTORY:
break;
default:
throw new IllegalArgumentException(
this.getClass().getName()+” not defined for item role
“+chunk.getRole());
}
boolean isInUse = false;
int size = chunk.countToCheck();
if (size<=0)return isInUse;
StringBuffer oql = new StringBuffer();
oql.append(“Select uc:item, lc:inventoryOrganization from
“+usageClass.getName()+” uc join com.cisag.app.purchasing.obj.
ReceiptOfGoods lc on uc:header = lc:guid “+
“join com.cisag.app.purchasing.obj.ReceiptOfGoodsType tc on
lc:type = tc:guid ”
+”where tc:type = ? and (“);
for (int i=0;i<size;i++){
oql.append(“(uc:item =? and lc:inventoryOrganization =?) or “);
}
oql.setLength(oql.length()-4);
oql.append(“)”);
CisResultSet rs =
om.getResultSet(oql.toString(),CisObjectManager.READ);
try {
int offset = 0;
rs.setShort(++offset,
com.cisag.app.purchasing.ReceiptOfGoodsType.PRODUCTION);
Iterator comboIt = chunk.retrieveCombinationsToCheck();
while(comboIt.hasNext()){
Chunk.Entry currentCombo = (Chunk.Entry)comboIt.next();
rs.setGuid(++offset,currentCombo.getGuid());
rs.setGuid(++offset,currentCombo.getOrganizationalUnit());
}
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) {
switch(entry.getRole()){
case ItemView.INVENTORY:
break;
default:
throw new IllegalArgumentException(this.getClass().getName()
+” not defined for item role “+entry.getRole());
}
String oqlString = “Select uc:header, uc:guid from
“+usageClass.getName()+” uc join com.cisag.app.purchasing.obj.
ReceiptOfGoods lc on uc:header = lc:guid join
com.cisag.app.purchasing.obj.ReceiptOfGoodsType tc
on lc:type = tc:guid ”
+”where tc:type=? and uc:item=? and lc:inventoryOrganization= ?”;
CisResultSet rs =
om.getResultSet(oqlString, CisObjectManager.READ);
List keys = new ArrayList();
try {
int offset = 0;
rs.setShort(++offset,
com.cisag.app.purchasing.ReceiptOfGoodsType.PRODUCTION);
rs.setGuid(++offset,entry.getGuid());
rs.setGuid(++offset,entry.getOrganizationalUnit());
rs.setMaxRows(ItemReorganizationLogic.MAX_USAGE);
while (rs.next()){
keys.add(
ReceiptOfGoodsDetail.buildPrimaryKey(rs.getGuid(1),rs.getGuid(2)));
}
} catch (java.sql.SQLException ex) {
throw new RuntimeException(ex);
} finally {
rs.close();
}
CisObject[] resultObj =
om.getObjectArray(keys, CisObjectManager.READ);
return resultObj;
}
Sprawdzenie poprawnej roli odbywa się w metodach isInUse oraz usedBy. Rola może być odczytana zarówno z Chunk, jak i z Entry.
Metoda usedBy identyfikuje kombinacje artykuł–organizacja, do których odwołują się instancje danego obiektu biznesowego (BO).
Metoda ta otrzymuje jako parametr obiekt typu Chunk, który zawiera kombinacje artykuł–organizacja do sprawdzenia.
Jeden Chunk zawsze odnosi się do jednego typu użycia i może zawierać wiele identyfikatorów GUID artykułów. Dla każdego identyfikatora artykułu zawiera wszystkie identyfikatory organizacji, które należy zweryfikować. Kombinacje, które mają zostać sprawdzone, można pobrać z obiektu Chunk za pomocą metody retrieveCombinationsToCheck.
Następnie metodą setInUse określa się, które z tych kombinacji są nadal wykorzystywane.
Jeśli sprawdzane zastosowanie nie jest zależne od organizacji (np. artykuł-podstawowe), organizacja nadal zawiera identyfikator klienta, nawet jeśli ta informacja nie jest potrzebna. Należy to uwzględnić przy wywołaniu metody setInUse.
W przypadku metody isInUse należy przy definiowaniu zapytania OQL zwrócić uwagę, aby sprawdzanie obejmowało wiele kombinacji artykuł–organizacja w ramach jednego zapytania. Pola selekcji powinny odpowiadać tym kombinacjom, aby możliwe było oznaczenie odwołań kombinacji za pomocą setInUse.
Metoda usedBy jest wywoływana tylko wtedy, gdy podczas reorganizacji wymagane są szczegółowe komunikaty. Otrzymuje ona jako parametr Chunk.Entry, który zawiera jedną kombinację artykuł–organizacja, dla której zostały wykryte instancje referencyjne. Metoda usedBy zwraca te instancje.
W zapytaniu OQL należy jako pole selekcji wybrać klucz główny obiektu biznesowego (BO).
Rejestracja BO do usunięcia
Jeśli kombinacja kluczy przeszła pomyślnie wszystkie testy, odpowiednie dane muszą zostać fizycznie usunięte. Wymaga to rejestracji powiązanych obiektów biznesowych. W typowym przypadku rejestracja na potrzeby reorganizacji artykułów obejmuje obiekt biznesowy odpowiadający za użycie (np. InventoryItem dla logistyki magazynowej) oraz dodatkowe obiekty zależne (Dependents).
Dla każdej formy użycia istnieje metoda rejestracji w klasie ItemUsageRegistry.
registerDeletionSales(new ItemClassDeletion(com.cisag.app.sales.obj.SalesItem.class));
Główny obiekt dla użycia Sales jest zarejestrowany do usunięcia.
Rejestrowany jest obiekt zależny (Dependent) — ItemStorageData, który musi zostać usunięty razem z danymi, gdy usuwane jest użycie logistyki magazynowej.
Implementacja interfejsu usuwania
Interfejs usuwania obejmuje metody getDeletionClass, delete i deleteExpiredVersions.
Metoda getDeletionClass zwraca klasę zarejestrowaną do usunięcia deletionClass zarejestrowaną do usuwania i jest używana głównie w celach serwisowych (np. aby wyświetlić klasy zarejestrowane dla danego przypadku użycia).
Metoda deleteExpiredVersions jest przewidziana na potrzeby przyszłych rozszerzeń. W przypadku BO zależnych od czasu, usuwa ona wersje, których okres ważności upłynął przed przekazaną datą. Dla BO, które nie są zależne od czasu metoda musi jedynie zwracać wartość true.
Metoda delete usuwa wszystkie wersje dla BO zależnych od czasu. Informacja o tym, dla których kombinacji artykuł–organizacja dane mają zostać usunięte, pobierana jest za pomocą metody retrieveFreeCombinations z obiektu typu Chunk.
Przykładem może być klasa com.cisag.app.general.item.reorg.ItemClassDeletion, która służy do bezpośredniego usuwania danych powiązanych z artykułem.