Interfejs dla reorganizacji artykułów

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")

Tworzy instancję logiki standardowej, która zawiera następujące zapytanie OQL:

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.

Przykład

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.

Przykład

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.

Przykład

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.
Przykład

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;

}

Uwaga
Przykład służy wyłącznie do celów ilustracyjnych. Nie jest on aktualizowany w przypadku ewentualnych zmian tej klasy w systemie.

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).

Uwaga
Dane artykułu dostawcy stanowią przypadek szczególny. W danych zakupowych artykułu można określić, czy zakupy mogą być realizowane tylko u przypisanych dostawców. W takim przypadku dla niektórych BO należy zachować nie tylko dane zakupowe, ale również specjalne dane artykułu dostawcy. Klucz w tym przypadku składa się z artykułu, organizacji i dostawcy. Aby to uwzględnić, w obiekcie Chunk.Entry przechowywany jest atrybut partnerKey. W przypadku standardowym zawiera on ZEROGUID, natomiast w przypadku danych artykułu dostawcy – identyfikator dostawcy.

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.

Przykład
registerDeletionSales(new ItemClassDeletion(com.cisag.app.sales.obj.SalesItem.class));

Główny obiekt dla użycia Sales jest zarejestrowany do usunięcia.

Przykład
registerDeletionInventory(new ItemClassDeletion(com.cisag.app.inventory.obj.ItemStorageData.class));

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.

Czy ten artykuł był pomocny?