Příklady pokročilého skriptování
Tato kapitola doplňuje obecný úvod do problematiky skriptování v systému ABRA Gen o ukázky pokročilých praktických příkladů.
Pokročilé příklady skriptování
Příklady skriptování pro pokročilé
Funkce SQLSelectFirst slouží na vrácení prvního řádku výsledku SQL dotazu.
Konkrétně se jedná o funkce SQLSelectFirstAsString , SQLSelectFirstAsInteger, SQLSelectFirstAsExtended, SQLSelectFirstAsBlob
Příklad použití:
..
mBlob:=Self.ObjectSpace.SQLSelectFirstAsBlob('SELECT NOTE FROM FIRMS'); //Vrátí hodnotu jako TBytes
mString:=Self.ObjectSpace.SQLSelectFirstAsString('SELECT NAME FROM FIRMS'); //Vrátí hodnotu jako String
mInt:=Self.ObjectSpace.SQLSelectFirstAsInteger('SELECT CODE FROM FIRMS'); //Vrátí hodnotu jako Integer
mExt:=Self.ObjectSpace.SQLSelectFirstAsExtended('SELECT PENALTYPERCENT FROM FIRMS'); //Vrátí hodnotu jako Extended
//Hodnoty po provedení funkce jsou následující
//mBlob = ABRA Software a.s. je technologická firma...
//mString = ABRA Software a.s.
//mInt = 00001
//mExt = 1,25
..
Na převod času na UnixTime slouží funkce DateTimeToUnix.
Příklad použití:
..
mDateTimeNow1:= DateTimeToUnix(Now(), True); //vrací výsledek UnixTime s časovým posunem
mDateTimeNow2:= DateTimeToUnix(Now(), False); //vrací výsledek UnixTime v UTC
//Hodnoty po provedení funkce jsou následovný
//mDateTimeNow1 = 1665079779
//mDateTimeNow2 = 1665072579
..
Od verze 19.4.3 je možné používat parametrizované SQL dotazy ze skriptování. Používání parametrů v SQL je snadnější, bezpečnější a pro SQL servery i rychlejší na vykonání díky cachování plánů SQL dotazů. Přes parametry lze snadno a bezpečně přenést libovolný datový typ tedy i binary blob data.
Příklad použití parametrů při vykonávání SQL dotazů (SQLExecute):
UPDATE GlobData SET DueTerm = :DueTerm, Logo = :Logo
..
mInputParams := TNxParameters.Create;
try
mInputParams.NewFromDataType(dtInteger, 'DueTerm').AsInteger := 10;
mRawParameter := TNxRawParameter(mInputParams.NewFromDataType(dtVarBytes, 'Logo'));
mStream := TMemoryStream.Create;
try
mStream.SetBytes(cBytes);
mRawParameter.LoadDataFromStream(mStream);
finally
mStream.Free;
end;
mRowsAffected := ObjectSpace.SQLExecute('UPDATE GlobData SET DueTerm = :DueTerm, Logo = :Logo', mInputParams);
FxCheckValue_Integer(mRowsAffected, 1, 'nebyl modifikován jeden záznam');
finally
mInputParams.Free;
end;
..
Příklad použití parametrů při získávání dat z databáze (SQLSelect, SQLSelect2):
SELECT DueTerm FROM GlobData WHERE ID = :ID
..
mInputParams := TNxParameters.Create;
try
mInputParams.NewFromDataType(dtString, 'ID').AsString := cCompanyGlobDataID;
mSQLResult := TStringList.Create;
try
ObjectSpace.SQLSelect('SELECT DueTerm FROM GlobData WHERE ID = :ID', mSQLResult, mInputParams);
FxCheckValue_String(mSQLResult.Text, '10' + #13#10, 'nedošlo k modifikaci dat nebo došlo k chybnému přečtení');
finally
mSQLResult.Free;
end;
mMemTable := TMemTable.Create(nil);
try
ObjectSpace.SQLSelect2('SELECT DueTerm, Logo FROM GlobData WHERE ID = :ID', mMemTable, mInputParams);
FxCheck(mMemTable.RecordCount = 1, 'mMemTable.RecordCount <> 1');
mMemTable.First;
mMemTable.Next;
FxCheckValue_Integer(mMemTable.FieldByName('DueTerm').AsInteger, 10, 'DueTerm <> 10');
FxCheckValue_TBytes(mMemTable.FieldByName('Logo').AsBytes, cBytes, 'Logo <> 0x0102...FF');
finally
mMemTable.Free;
end;
finally
mInputParams.Free;
end;
..
Funkce FindPaymentDestByVarsymb slouží na na dohledání placeného dokladu.
function NxFindPaymentDestinationsByVarSymb(
AObjectSpace: TNxCustomObjectSpace;
ACredit: Boolean; AVarSymbol: string; ADocTypes, AOIDS: TStrings;
AFlag, AFlagForOneFound: Integer; AAmount: Extended; ACurrency_ID: String;
AFirm_ID: TNxOID)';
Popis parametrů:
// AObjectSpace ObjectSpace
// ACredit True/False typ platby Kredit/Debet
// AVarSymbol Hledaný variabilní symbol
// ADocTypes TStrings do kterých funkce vrací typ dohledaných dokladů
// AOIDS TStrings do kterých funkce vrací OID dohledaných dokladů
// AFlag Příznak jak dohledávat podle částky a zaplacení
// -1 - Vrátit vše vyhovující dle VS bez ohledu na stav zaplacení a částku - ignoruje nastavení parametrů ve firemních údajích
// 0 - Vrátit vyhovující dle VS, částky a stavu zaplacení s ohledem na nastavení parametrů ve firemních údajích
// AFlagForOneFound Příznak jak dohledávat podle částky
// 0 - Musí vyhovovat jen VS
// 1 - Musí vyhovovat VS i částkaa měna placeného dokladu
// AAmount Částka - když je 0, nebere se při vyhledávání na částku zřetel
// ACurrency_ID Měna
// AFirm_ID Firma - pokud je vyplněno, při vyhledávání se bere zřetel i na firmu na dokladu
Příklad použití:
procedure find_payment_destination_by_varsymb(
ACredit: Boolean; AVarSymb: string; AAmount: EWxtended;
mFlag, mFlagForOneFound: Integer;
ACurrency_ID, AFirm_ID: TNxOID);
var
mObjectSpace: TNxCustomObjectSpace;
mDocTypes, mOIDs: TStrings;
begin
...
mOIDs := TStringList.Create;
try
mDocTypes := TStringList.Create;
try
NxFindPaymentDestinationsByVarSymb(
mObjectSpace, ACredit, AVarSymb, mDocTypes, mOIDS, AFlag, AFlagForOneFound,
AAmount, ACurrency_ID, AFirm_ID);
// V StringListech mDocTypes a mOIDS jsou vráceny identifikace dohledaných dokladů (Typ dokladu a OID)
...
finally
mDoctypes.Free;
end;
finally
mOIDs.Free;
end;
end;
Jde o následující metody třídy TNxCustomAccountedDocument:
function AttachSourceGroup(const ASourceGroup_ID: TNxOID);
function RemoveSourceGroup(const ASourceGroup_ID: TNxOID);
function IsSourceGroupAttached(const ASourceGroup_ID: TNxOID): Boolean;
function GetSourceGroup: TNxOID;
Příklad použití:
procedure pair_it;
var
mReceivedInvoice, mReceiptCard: TNxCustomBusinessObject;
mSourceGroup_ID: TNxOID;
begin
...
// Připojení dokladu do ručního párování
// Zde připojujeme existující příjemku do párovací skupiny faktury přijaté
mSourceGroup_ID := TNxCustomAccountedDocument(mReceiptCard).GetSourceGroup;
TNxCustomAccountedDocument(mReceivedInvoice].AttachSourceGroup(mSourceGroup_ID);
mReceivedInvoice.Save; // !!! POZOR - Připojení se projeví až po Save objektu
...
// Odpojení dokladu z ručního párování
// Zde odpojujeme již připojenou příjemku z párovací skupiny faktury přijaté
mSourceGroup_ID := TNxCustomAccountedDocument(mReceiptCard).GetSourceGroup;
TNxCustomAccountedDocument(mReceivedInvoice].RemoveSourceGroup(mSourceGroup_ID);
mReceivedInvoice.Save; // !!! POZOR - Odpojení se projeví až po Save objektu
...
// Zjištění, zda je dokument připojen v ručním párování
// Zde zjišťujeme zda je příjemka připojena do párovací skupiny faktury přijaté
mSourceGroup_ID := TNxCustomAccountedDocument(mReceiptCard).GetSourceGroup;
if TNxCustomAccountedDocument(mReceivedInvoice].IsSourceGroupAttached(mSourceGroup_ID) then
begin
...
end;
...
end;
Ukázkový skript, který přidá řádek do rozeditovaného dokladu:
{
Přidání řádku do rozeditovaného dokladu
}
procedure InsertRow(Sender : TButton);
var
mSite: TSiteForm;
mControl: TControl;
mDataset: TNxRowsObjectDataSet;
mRow: TNxCustomBusinessObject;
begin
try
mSite := TComponent(Sender).Site;
mControl:= mSite.FindChildControl('tabRows.grdRows');
mDataset := TNxRowsObjectDataSet(TMultiGrid(mControl).DataSource.DataSet);
if Assigned(mDataset) then
begin
mDataSet.DisableControls;
mRow := mDataSet.CreateBusinessObject;
mRow.Prefill;
mRow.SetFieldValueAsInteger('RowType',3);
mRow.SetFieldValueAsInteger('PosIndex',1);
mRow.SetFieldValueAsString('Store_Id','2100000101');
mRow.SetFieldValueAsString('Division_ID','2100000101');
mRow.SetFieldValueAsString('Storecard_Id','2100000101');
mRow.SetFieldValueAsFloat('Quantity', 1);
end;
finally
TDynSiteForm(mSite).ActiveDataSet.UpdateFields; //Aby se o změně dozvěděl hlavičkový dataset
mDataset.RefreshAndRestoreLastSelectedItem;
mDataSet.EnableControls;
end;
end;
{
Vyvolává se po vytvoření instance formuláře.
}
procedure FormCreate_Hook(Self: TSiteForm);
var
mAction: TBasicAction;
mMAction: TMultiAction;
begin
// Vytorime novou jednoduchou akci
mAction := Self.GetNewAction;
mAction.ShowControl := True;
mAction.ShowMenuItem := True;
mAction.Caption := 'Přidání řádku';
mAction.Hint := 'Přidání řádku a aktualizace datasetu';
mAction.Category := 'tabDetail';
mAction.OnExecute := @InsertRow;
end;
begin
end.
1. Ukázkový skript, který umožňuje rozšířit seznam objektů na záložce X-vazby, a to jak přidáním objektů, tak i celých nových skupin objektů:
{
Umožňuje rozšířit seznam objektů na záložce X-vazby, a to jak přidáním objektů, tak i celých nových skupin objektů. Doporučujeme přidávat objekty na obě strany, tedy jak na zdrojový, tak cílový objekt, poté uživatel uvidí např. na objektu typu A objekty typu B a také naopak z B uvidí A.
Ukázka skriptu rozšiřující X-vazby business objektu Sklad o skupinu Nabídky vydané a v ní všechny záznamy s pořadovým číslem 1.
A dále o skupinu Skladové karty se záznamy s kódem začínajícím na 0.
}
procedure AddLinks(Self: TNxCustomBusinessObject; const AGroups, AObjects: TStringList; const ASite: TSiteForm);
var
mInputParameters: TNxParameters;
mIDs: TStringList;
I: Integer;
begin
AGroups.Add(Class_IssuedOffer + '=' + 'Nabídky vydané');
mInputParameters := TNxParameters.Create;
try
mInputParameters.NewFromDataType(dtInteger, 'OrdNumber').AsInteger := 1;
mIDs := TStringList.Create;
try
Self.ObjectSpace.SQLSelect('SELECT ID FROM IssuedOffers WHERE OrdNumber = :OrdNumber AND Revided_ID IS NULL', mIDs, mInputParameters);
for I := 0 to mIDs.Count - 1 do begin
AObjects.Add(Class_IssuedOffer + '=' + mIDs[I]);
end;
finally
mIDs.Free;
end;
finally
mInputParameters.Free;
end;
AGroups.Add(Class_StoreCard + '=' + 'Skladové karty');
mInputParameters := TNxParameters.Create;
try
mInputParameters.NewFromDataType(dtString, 'StartWith').AsString := '0%';
mIDs := TStringList.Create;
try
Self.ObjectSpace.SQLSelect('SELECT ID FROM StoreCards WHERE Code LIKE :StartWith AND Hidden = ''N''', mIDs, mInputParameters);
for I := 0 to mIDs.Count - 1 do begin
AObjects.Add(Class_StoreCard + '=' + mIDs[I]);
end;
finally
mIDs.Free;
end;
finally
mInputParameters.Free;
end;
// Lze použít i objekty definovatelných číselníků
//AGroups.Add('1NPNI4M2JIVOFBV23CHPHFPN5W=Eshop - Číselníkové hodnoty vlastnosti skladové karty');
//AObjects.Add('1NPNI4M2JIVOFBV23CHPHFPN5W=1000000101');
// Ukázka prázdné skupiny
//AGroups.Add(Class_IssuedOrder + '=Objednávky přijaté');
//AGroups.Add(Class_Division + '=Střediska');
end;
begin
end.
2. Ukázkový skript, který umožňuje upravit poznámku zobrazovanou ve vizuálnu na záložce X-vazby.:
{
Ukázka skriptu pro business objekt Skladová karta. Na všech X-vazbách skladových karet přidá do poznámky EAN. V X-vazbách agendy Sklady navíc ještě přidává množství dle vybraného skladu.
}
procedure GetLinkDescription2_Hook(Self: TNxCustomBusinessObject; var Result: String; const ASite: TSiteForm);
var
mObjectSpace: TNxCustomObjectSpace;
mStoreID: String;
mQuantity: Double;
begin
if (ASite <> nil) then begin
if (ASite.GetSiteCLSID = Site_Stores) and (TBusRollSiteForm(ASite).CurrentObject <> nil) then begin
mObjectSpace := TBusRollSiteForm(ASite).BaseObjectSpace;
mStoreID := TBusRollSiteForm(ASite).CurrentObject.GetFieldValueAsString('ID');
mQuantity := mObjectSpace.SQLSelectFirstAsExtended('SELECT Quantity FROM StoreSubCards WHERE Store_ID = ' +
QuotedStr(mStoreID) + ' AND StoreCard_ID = ' + QuotedStr(Self.GetFieldValueAsString('ID')));
Result := NxTrim(Result + '; Quantity: ' + FloatToStr(mQuantity), ';');
end;
end;
if Self.GetFieldCode('EAN') > 0 then
Result := NxTrim(Result + '; EAN: ' + Self.GetFieldValueAsString('EAN'), ';');
end;
begin
end
3. Příklad založení uživatelské vazby ze skriptování:
{
Ukázka části skriptu na vytvoření X-vazby v tabulce UserXLink mezi záznamy agend Objednávky přijaté -> Došlá pošta. Záznamy jsou označeny jako systémové.
}
mUserXLink := ObjectSpace.CreateObject(Class_UserXLink);
try
mUserXLink.New;
mUserXLink.Prefill;
mUserXLink.SetFieldValueAsString('SourceCLSID', Class_ReceivedOrder);
mUserXLink.SetFieldValueAsString('Source_ID', mReceivedOrderID);
mUserXLink.SetFieldValueAsString('DestinationCLSID', Class_PDMReceivedDoc);
mUserXLink.SetFieldValueAsString('Destination_ID', mPDMReceivedDocID);
mUserXLink.SetFieldValueAsBoolean('DisplayAsSystem', True);
mUserXLink.Save;
finally
mUserXLink.Free;
end;
Háček umožňuje přímo ovlivnit SQL. Kdykoliv číselník potřebuje data z databáze, sestavuje se SQL. Háček se volá před sestavením SQL.
procedure OnSelectSQL_Hook(Self: TNxBusinessRoll; AParams: TNxParameters; ADSQL: TRollDynamicSQL; AKind: TRollOnSelectSQLKind);
//Self
//Číselník, pro který se háček vyvolal
//AParams
//Parametry, se kterými se číselník zavolal. AParams typicky připravuje CompleteRollValidateParams háček na BO. AParams jsou jenom pro čtení.
//ADSQL
//Prostředník pro úpravu SQL.
//AKind
//Informace, z jaké části číselníku se háček vyvolal
//sskPage - při získání stránky s daty
//sskWhisperer - při zobrazení našeptávače
//sskExists - Služba číselníku - LookUp
//sskOIDByPart - Služba číselníku - FindPart
//sskOID - Služba číselníku - CheckOnly
//sskAllID - Služba číselníku - GetIds
//sskOIDByPrefill - Služba číselníku - Prefill
//sskCorrectSelected - Služba číselníku - CorrectSelected'
Příklad:
Chceme, aby uživatel Daniel Rasák (‘4300000101’) viděl pouze firmy s názvem začínajícím na A. (Jednoduchý příklad nad demodaty)
procedure OnSelectSQL_Hook(Self: TNxBusinessRoll; AParams: TNxParameters; ADSQL: TRollDynamicSQL; AKind: TRollOnSelectSQLKind);
begin
if NxGetActualUserID(Self.ObjectSpace) = '4300000101' then
begin
// do WHERE doplníme SQL podmínku
ADSQL.Where.Add('A.Name LIKE ''A%''');
end;
end;
Když není dostupný zdroj dat (tabulka), mohu si současně s podmínkou zdroj dat přidat:
Název aliasu volte s rozumem, nesmí kolidovat s existujícími.
Dejte si pozor, aby join nezmnožil řádky místo toho, aby je omezil.
procedure OnSelectSQL_Hook(Self: TNxBusinessRoll; AParams: TNxParameters; ADSQL: TRollDynamicSQL; AKind: TRollOnSelectSQLKind);
begin
ADSQL.Joins.Add('MT', 'join MyTable MT on MT.ID = A.Tab_ID', 'MT.Code LIKE ''A%''')
end;
Ve většině případů vystačíte s výše uvedenými příklady.
Máme ale k dispozici ještě jednu variantu, která je složitá na pochopení, ale v určitých případech může zásadně ovlivnit rychlost provedení dotazu.
Jádro ji používá např. pro FULLTEXT.
Zkuste použít, pokud základní WHERE anebo JOIN+WHERE budou pomalé.
procedure OnSelectSQL_Hook(Self: TNxBusinessRoll; AParams: TNxParameters; ADSQL: TRollDynamicSQL; AKind: TRollOnSelectSQLKind);
begin
ADSQL.Joins.Tweak.Add('select ID as Parent_ID from table');
end;
Poddotaz musí splňovat tato kritéria:
- vracet jeden sloupec s názvem Parent_ID
- Parent_ID musí být ID z číselníku, ze kterého se získávají data (podmínka omezuje proti A.ID
- hodnoty Parent_ID musí být unikátní
Příklad:
- rozpoznání místa objednávka přijatá řádek s ukázkou předání hodnoty z jiného pole
- rozpoznání X položky na řádku objednávky přijaté
- rozpoznání, že se jedná o vizuální editaci z definovatelného formuláře
Háček: Business objekt: Objednávka přijatá - řádek
procedure CompleteRollValidateParams_Hook(Self: TNxCustomBusinessObject; AFieldCode: integer; AParams: TNxParameters);
begin
// Doplnění identifikace položky, pro kterou je číselník používán a tím umožnění v háčku OnSelectSQL_Hook číselníku zohlednění daného místa.
if AFieldCode = Self.GetFieldCode('StoreCard_ID') then begin
AParams.GetOrCreateParam(dtString, 'ObjPri_StoreCard_ID');
// Navíc ukázka předání hodnoty jiného pole, které ovlivní výsledné omezení.
AParams.GetOrCreateParam(dtString, 'ObjPri_Store_IDValue').AsString := Self.GetFieldValueAsString('Store_ID');
end;
if AFieldCode = Self.GetFieldCode('X_StoreCard_ID') then begin
AParams.GetOrCreateParam(dtString, 'ObjPri_X_StoreCard_ID');
end;
end;
Háček: Číselník: Číselník skladových karet
procedure OnSelectSQL_Hook(Self: TNxBusinessRoll; AParams: TNxParameters; ADSQL: TRollDynamicSQL; AKind: TRollOnSelectSQLKind);
const
cStorePraha = '2100000101';
begin
if AParams.ParamExist('ObjPri_StoreCard_ID') and
(AParams.GetOrCreateParam(dtString, 'ObjPri_Store_IDValue').AsString = cStorePraha) then begin
// Příklad jen jednoduché karty, kterým název začíná na písmeno A
ADSQL.Where.Add('A.Category = 0 AND A.Name LIKE ''A%''');
end;
if AParams.ParamExist('ObjPri_X_StoreCard_ID') then begin
// Příklad jen karty se sériovými čísly
ADSQL.Where.Add('A.Category = 1');
if AParams.ParamExist('ObjPri_X_StoreCard_ID_FromDefForm') then begin
// Omezení jek pokud je pole editováno z definovatelného formuláře. Tzn. omezení za název nebude použit pro tvorbu např. z API
ADSQL.Where.Add('A.Name LIKE ''B%''');
end;
end;
end;
Doplnění parametru z definice definovatelného formuláře.
Příklad použití třídy CFxLog s metodou SaveLog, která vytvoří a uloží nový log v agendě Logy:
..
var
mLog: CFxLog;
mContext: TNxContext;
mCustomObjectSpace: TNxCustomObjectSpace;
begin
mCustomObjectSpace := Self.BaseObjectSpace;
mContext := NxCreateContext(mCustomObjectSpace);
mLog.SaveLog(mContext, 'LOGIE', 'Test_01', 'Poznámka_01', 0, Now);
mLog.SaveLog(mContext, 'LOGIE', 'Test_02', 'Poznámka_02', 1, Now);
mLog.SaveLog(mContext, 'LOGIE', 'Test_03', 'Poznámka_03', 2, Now);
..
Příklad jak otevřít číselník a vybranou hodnotu zpracovat:
// Vyloučení některých záznamů
//mParams.GetOrCreateParam(dtString, '_Excluded').AsString := '1J50000101;M100000101';
// Omezení jen na seznam povolených záznamů
//mParams.GetOrCreateParam(dtString, '_Allowed').AsString := '1J50000101;M100000101';
procedure SelectOneFromRoll(Sender : TObject);
var
mSite: TSiteForm;
mSiteContext: TNxContext;
mParams: TNxParameters;
mID: String;
begin
mSite := TComponent(Sender).Site;
mSiteContext := mSite.SiteContext;
mParams := TNxParameters.Create;
try
mParams.GetOrCreateParam(dtString, '_ID').AsString := 'O100000101'; // Otevření na konkrétním záznamu. '' - bez určení
if NxShowRoll(mSiteContext, Roll_StoreCards, mParams, 0, '', mSite.GetParentForm) then begin
mID := mParams.ParamByName('_ID').AsString;
ShowMessage('Vybraný záznam: ' + mID);
end;
finally
mParams.Free;
end;
end;
procedure MultiSelectFromRoll(Sender : TObject);
var
mSite: TSiteForm;
mSiteContext: TNxContext;
mParams: TNxParameters;
mIDs: String;
mSelectedList: TNxParameters;
i: Integer;
begin
mSite := TComponent(Sender).Site;
mSiteContext := mSite.SiteContext;
mSelectedList := TNxParameters.Create;
try
mParams := TNxParameters.Create;
try
// Označení vybraných záznamů
mSelectedList.GetOrCreateParam(dtString, '4100000101').AsString := '4100000101';
mSelectedList.GetOrCreateParam(dtString, 'E100000101').AsString := 'E100000101';
mParams.GetOrCreateParam(dtObject, '_SelectedList').AsObject := mSelectedList;
if NxShowRoll(mSiteContext, Roll_StoreCards, mParams, 0, '', mSite.GetParentForm) then begin
mIDs := '';
for i := 0 to mSelectedList.Count - 1 do
begin
mIDs := mIDs + mSelectedList.Params[i].AsString + ';';
end;
ShowMessage('Vybrané záznamy: ' + mIDs);
end;
finally
mParams.Free;
end;
finally
mSelectedList.Free;
end;
end;
Příklad použití funkce GetARESCZData popř. GetSKFirmData - vytvoření business objektu firmy a zavolání příslušných funkcí na naplnění (k vyzkoušení ve vizuálnu s interakcí uživatele):
procedure OnExecute_FillByOrgIdentNumber(Sender: TObject);
var
mSite: TSiteForm;
mFirm: TNxCustomBusinessObject;
mErrText: string;
mOrgIdentNumber: string;
begin
mSite := TComponent(Sender).BusRollSite;
mFirm := mSite.BaseObjectSpace.CreateObject(Class_Firm);
mFirm.New;
mFirm.Prefill;
//zadání IČO k otestování
mOrgIdentNumber := InputBox('IČO pro vyhledání firmy', 'Zadejte IČO:', '00000000');
//naplnění firmy z portálu ARES (CZ)
mFirm.SetFieldValueAsString('OrgIdentNumber', mOrgIdentNumber);
mErrText := '';
TNxFirm(mFirm).GetARESCZData(mErrText, True);
if mErrText = '' then
ShowMessage(mFirm.GetFieldValueAsString('Name'));
else
ShowMessage(mErrText);
//naplnění firmy z veřejné databáze (SK)
mFirm.SetFieldValueAsString('OrgIdentNumber', mOrgIdentNumber);
mErrText := '';
TNxFirm(mFirm).GetSKFirmData( mErrText, True);
if mErrText = '' then
ShowMessage(mFirm.GetFieldValueAsString('Name'));
else
ShowMessage(mErrText);
end;
V nástroji ScriptDebugger existuje možnost ve skriptu ručně vyvolat výjimku z breakpointů nebo za přímého běhu.
Po stisku tlačítka Vyvolat výjimku a potvrzení dialogu, kde lze změnit text výjimky se zašle do Abry požadavek na budoucí výjimku. Abra při příštím vykonání uměle selže.
Oproti vyvolání výjimky přímo ze skriptování příkazem RaiseException se ovšem ale nezobrazuje CallStack kde chyba vznikla.
Uvedený příklad demonstruje, jak lze na základě logiky v business objektu (objedávky přijaté) - po nastavení řady dokladu - docílit zapnutí/vypnutí řádkové slevy, a následně zobrazení/skrytí sloupců v gridu řádků té objednávky:
//v BO po vyplnění určité řady chceme, aby se aktivovala sleva
procedure AfterSetFieldValue_Hook(Self: TNxCustomBusinessObject; AFieldCode: Integer; AValue: TNxParameter; AOriginalValue: TNxParameter);
begin
if AFieldCode = 11000 then //řada dokladů
begin
if AValue.AsString = '1OU0000101' then //uvedená hodnota může reprezentovat např. řadu"OP" - tohle vyplyne z nějaké x položky nebo nějak jinak
begin
Self.SetFieldValueAsBoolean('IsRowDiscount', True)
end
else
begin
Self.SetFieldValueAsBoolean('IsRowDiscount', False)
end
end;
end;
//ve vizuálnu budeme potřebovat globální proměnnou
var
fSite: TSiteForm;
//ve vizuálnu vytvoříme proceduru pro zpracování události změny na řadě dokladu
procedure InitSite_Hook(Self: TSiteForm);
var
mpnDocQueue_ID: TNxGeneralRollMovablePanel;
begin
mpnDocQueue_ID := TNxGeneralRollMovablePanel(Self.FindComponent('mpnDocQueue_ID'));
mpnDocQueue_ID.onInEditAdmit := @DQEditAdmit; //Vytvoření procedury pro zpracování události zadání té řady dokladů
fSite := Self;
end;
//a v rámci té události si ve vizuálnu nastavíme, co potřebujeme - tady to je zobrazení popř. skrytí sloupců v gridu řádků
procedure DQEditAdmit;
var
mpnIsRowDiscount: TNxCheckBoxMovablePanel;
colRowDiscount1: TNxMultiGridColumn;
colRowDiscount2: TNxMultiGridColumn;
colRowDiscount3: TNxMultiGridColumn;
begin
mpnIsRowDiscount := TNxCheckBoxMovablePanel(fSite.FindComponent('mpnIsRowDiscount'));
colRowDiscount1 := TNxMultiGridColumn(fSite.FindComponent('colRowDiscount1'));
colRowDiscount2 := TNxMultiGridColumn(fSite.FindComponent('colRowDiscount2'));
colRowDiscount3 := TNxMultiGridColumn(fSite.FindComponent('colRowDiscount3'));
colRowDiscount1.Visible := mpnIsRowDiscount.InCheckBox_Checked;
colRowDiscount2.Visible := mpnIsRowDiscount.InCheckBox_Checked;
colRowDiscount3.Visible := mpnIsRowDiscount.InCheckBox_Checked;
end;
Jedná se o třídy TPDFDocument a TPDFFileAttachment, které umožňují práci s přílohami PDF dokumentů. Třídy jsou založeny na produktu třetí strany SecureBlackBox, který je součástí Abry, umí jak načíst existující přílohy, tak vložit nové:
procedure AddFileToPDF(const APDFFileName: string;
const AAddFileName: string; const AAddFileType: string; const AAddFileDescription: string);
var
i: Integer;
mPDFStream,
mPDFAttachmentStream: TFileStream;
mPDFDocument: TPDFDocument;
mPDFAttachment: TPDFFileAttachment;
begin
mPDFDocument := TPDFDocument.Create(nil);
try
// otevřeme stream s existujícím PDF souborem
mPDFStream := TFileStream.Create(APDFFileName, fmOpenReadWrite);
try
// pomocí streamu načteme PDF do objektu pro práci s PDF
mPDFDocument.Open(mPDFStream);
// do PDF přidáme přílohu
i := mPDFDocument.AddAttachedFile;
mPDFAttachment := mPDFDocument.AttachedFiles[i];
// vložíme novou přílohu načtením z disku
mPDFAttachmentStream := TFileStream.Create(AAddFileName, fmOpenRead);
try
// načteme obsah přílohy ze souboru
mPDFAttachment.LoadFromStream(mPDFAttachmentStream);
finally
mPDFAttachmentStream.Free;
end;
// doplníme další informace k příloze
mPDFAttachment.ObjectName := ExtractFileName(AAddFileName);
mPDFAttachment.FileName := ExtractFileName(AAddFileName);
mPDFAttachment.UnicodeFilename := ExtractFileName(AAddFileName);
mPDFAttachment.SubType := AAddFileType;
mPDFAttachment.Description := AAddFileDescription;
mPDFAttachment.CreationDate := Now;
mPDFAttachment.ModificationDate := Now;
// uložíme změny do souboru PDF
mPDFDocument.Close(True);
finally
mPDFStream.Free;
end;
finally
mPDFDocument.Free;
end;
end;
procedure ExtractFilesFromPDF(const APDFFileName: string;
const AExtractPath: string);
var
i: Integer;
mPDFStream,
mPDFAttachmentStream: TFileStream;
mPDFDocument: TPDFDocument;
mPDFAttachment: TPDFFileAttachment;
mFilesInformation: TStringList;
begin
mFilesInformation := TStringList.Create;
try
// vytvoříme objekt pro práci s PDF
mPDFDocument := TPDFDocument.Create(nil);
try
// otevřeme stream s existujícím PDF souborem
mPDFStream := TFileStream.Create(APDFFileName, fmOpenReadWrite);
try
// pomocí streamu načteme PDF do objektu pro práci s PDF
mPDFDocument.Open(mPDFStream);
// procházíme všechny přílohy vložené do PDF a uložíme je na disk
for i := 0 to mPDFDocument.AttachedFileCount - 1 do
begin
mPDFAttachment := mPDFDocument.AttachedFiles[i];
// do Stringlistu si uložíme případné informace o souborech
mFilesInformation.Add('Informace o příloze č. ' + IntToStr(i + 1) + ':' + nxCrLf +
' ObjectName: ' + mPDFAttachment.ObjectName + nxCrLf +
' FileName: ' + mPDFAttachment.FileName + nxCrLf +
' UnicodeFilename: ' + mPDFAttachment.UnicodeFilename + nxCrLf +
' SubType: ' + mPDFAttachment.SubType + nxCrLf +
' Description: ' + mPDFAttachment.Description + nxCrLf +
' CreationDate: ' + DateTimeToStr(mPDFAttachment.CreationDate) + nxCrLf +
' ModificationDate: ' + DateTimeToStr(mPDFAttachment.ModificationDate) + nxCrLf +
' Size: ' + IntToStr(mPDFAttachment.Size) + nxCrLf +
'================================================'+ nxCrLf + nxCrLf
);
mPDFAttachmentStream := TFileStream.Create(AExtractPath + mPDFAttachment.UnicodeFilename, fmCreate);
try
// přílohu z PDF uložíme přes stream na disk
mPDFAttachment.SaveToStream(mPDFAttachmentStream);
finally
mPDFAttachmentStream.Free;
end;
end;
mPDFDocument.Close(False);
//Uložíme informace o extrahovaných souborech do stejného adresáře kam se soubory extrahovali.
mFilesInformation.SaveToFile(AExtractPath + '_AttahcmentInfo.txt');
finally
mPDFStream.Free;
end;
finally
mPDFDocument.Free;
end;
finally
mFilesInformation.Free;
end;
end;
V tomto příkladu potřebujeme přenést zakázku a projekt z výrobního příkazu do CRM aktivity. V agendách existuje akce Aktivity - Založit novou aktivitu a připojit, standardně ale nemáme ve skriptingových háčcích při otevření aktivity přes tuto akci k dispozici zdrojový business objekt (zde výrobní příkaz). Tento příklad ukáže, jak se dá potřebný BO získat a použít v nějakém háčku (zde AfterSiteOpen_Hook).
V _InitSelectionParams_Hook si zapamatujeme objekt zdrojového dokladu a z něj pak přebíráme údaje v AfterSiteOpen_Hook.
Zdrojový kód skriptu - Agenda Aktivity:
var
fJO: TNxCustomBusinessObject;
procedure _InitSelectionParams_Hook(Self: TDynSiteForm; ASelection, AParams: TNxParameters);
var
mPar: TNxParameter;
begin
mPar := AParams.ParamByName('DocumentToConnect');
if assigned(mPar) then
begin
fJO := TNxCustomBusinessObject(mPar.asobject);
if fJO.CLSID <> Class_PLMJobOrder then //v této ukázce přebíráme jen z výrobních příkazů
begin
fJO := nil;
end;
end;
end;
procedure AfterSiteOpen_Hook(Self: TSiteForm);
var
mActivity: TNxCustomBusinessObject;
begin
mActivity := TDynSiteForm(Self).ActiveDataSet.CurrentObject;
try
if assigned(fJO) and assigned(mActivity) then //aktivita vytvořena z výrobního příkazu
begin
try
//tady údaje převezmeme
mActivity.SetFieldValueAsString('BusOrder_ID', fJO.GetFieldValueAsString('BusOrder_ID'));
mActivity.SetFieldValueAsString('BusProject_ID', fJO.GetFieldValueAsString('BusProject_ID'));
finally
fJO := nil;
end;
TDynSiteForm(Self).ActiveDataSet.UpdateFields;
end;
finally
mActivity.Free;
end;
end;
begin
end.
Zde je příklad, jak ze skriptingu vytvořit a odeslat e-mail v aplikaci Outlook. Pomocí skriptování můžeme vytvořit jakýkoliv zaregistrovaný objekt ve Windows, tedy i například Outlook.Application. Používá se k tomu metoda CreateOleObject.
Jaké vlastnosti, události nebo metody objekt používá, je možné najít v dokumentaci: Application object (Outlook) | Microsoft Learn.
procedure exeOutlookSend(Sender: TBasicAction);
var
mOutlook, mItem: Variant;
begin
mOutlook := CreateOleObject('Outlook.Application');
if VarIsNull(mOutlook) then NxShowSimpleMessage('Chyba při získání instance Outlooku', Sender.Site)
else begin
mItem := mOutlook.CreateItem(0);
mItem.To := 'adresa1@firma.cz'; //adresa příjemce
mItem.CC := 'adresa2@firma.cz; adresa3@firma.cz'; //adresy příjemců kopie
mItem.Subject := 'Předmět e-mailu';
mItem.Body := 'Tělo e-mailu';
mItem.Attachments.Add('C:\Users\jmeno\priloha.pdf'); //připojení souboru
//zobrazení okna Outlooku s vyvořeným e-mailem
mItem.Display; //případně .Send pro odeslání
end;
mOutlook := Null;
end;
Při přihlášení se volá háček Aplikační modul: Systémové události - AfterLogon_Hook. Tento háček se však nevolá pouze při prvotním přihlášení uživatele do AbraGen.exe, ale při každém vytvoření kontextu z klienta na aplikační server. K vytváření více kontextů na aplikační server (z jedné aplikace AbraGen.exe ) dochází od verze 23.2, a to při vyhodnocení údajů definovatelných panelů, které nyní probíhá v samostatném vlákně.
Existence více vláken má vliv i na použití globální proměnné GlobParams, parametry v ní lze použít jen v rámci jednoho vlákna. Hodnotu parametru nastavenou v jednom vlákně tak nelze získat v jiném vlákně. Aby bylo možné sdílet hodnoty parametrů mezi různými vlákny, byla vytvořena nová proměnná GlobThreadParams.
Následující příklad demonstruje, jak zabezpečit, aby se kód v háčku AfterLogon_Hook zavolal pouze jednou (při přihlášení uživatele do ABRA Gen), a jak hodnotu parametru nastavenou v jednom vlákně (při přihlášení uživatele do ABRA Gen) získat v jiném vlákně (při vyhodnocení definovatelného panelu).
Zároveň je třeba mít na paměti, že v háčku AfterLogon_Hook lze přistoupit ke GUI (například zobrazit formulář) pouze, pokud je kód vyvolán z hlavního vlákna. Pokud k přístupu ke GUI dojde z jiného vlákna celá aplikace může zamrznout.
const
cParSelectedDivisionID = 'SelectedDivisionID'; //Název parametru jehož hodnota se ukládá do GlobThreadParams
//Vyvolává se při tvorbě kontextu z klienta na aplikační server (nejen při přihlášení uživatele).
procedure AfterLogon_Hook(AContext: TNxContext);
begin
if NxIsMainThread and //Kód je volán z hlavního vlákna, ve kterém je možné zobrazit formulář
(GetSelectedDivisionID = '') and //Středisko ještě nebylo vybráno
Application.NxIsInteractive then //Jedná se o GUI aplikaci, která umožňuje zobrazení oken
begin
SetSelectedDivisionID(SelectDivisionID(AContext));
end;
end;
//Vyvolá výběr hodnoty z číselníku středisek.
function SelectDivisionID(AContext: TNxContext): String;
var
mRollParams: TNxParameters;
begin
mRollParams := TNxParameters.Create;
try
mRollParams.GetOrCreateParam(dtString, '_ID').AsString := ''; // Otevření na konkrétním záznamu. '' - bez určení
if NxShowRoll(AContext, Roll_Divisions, mRollParams, 0, '', nil) then
begin
Result := mRollParams.ParamByName('_ID').AsString;
end else
Result := SelectDivisionID(AContext); //Abychom donutili uživatele nějaké středisko vybrat
finally
mRollParams.Free;
end;
end;
//Uloží hodnotu do parametru SelectedDivisionID.
procedure SetSelectedDivisionID(AValue: String);
var
mParameters: TNxParameters;
mParameter: TNxParameter;
begin
mParameters := GlobThreadParams.LockParams;
try
mParameter := mParameters.GetOrCreateParam(dtString, cParSelectedDivisionID);
mParameter.AsString := AValue;
finally
GlobThreadParams.UnLockParams;
end;
end;
//Zjistí hodnotu parametru SelectedDivisionID.
//Tato metoda vrátí správně hodnotu parametru bez ohledu na to, z kterého vlákna je vyvolána.
//Například jí tedy lze použít v definovatelném panelu.
function GetSelectedDivisionID: String;
var
mParameters: TNxParameters;
begin
mParameters := GlobThreadParams.LockParams;
try
Result := mParameters.ParamAsString(cParSelectedDivisionID, '');
finally
GlobThreadParams.UnLockParams;
end;
end;
Příklad použití funkce GetUserParameters pro získání uživatelských parametrů na business objektu skladu, změna hodnoty vybraného parametru a uvolnění cache pro uživatelské parametry pro daný sklad pomocí ClearUserParametersCache:
Příklad použití:
procedure OnExecute_ChangeUserParamValue(Sender: TObject);
var
mSite: TSiteForm;
mStore: TNxCustomBusinessObject;
mUserParamValue: TNxCustomBusinessObject;
mUserParams: TNxParameters;
begin
mSite := TComponent(Sender).BusRollSite;
mStore := mSite.BaseObjectSpace.CreateObject(Class_Store);
try
mUserParamValue := mSite.BaseObjectSpace.CreateObject(Class_UserParamValue);
try
mUserParams := TNxParameters.Create;
try
// načtení BO, v tomto případě skladu
mStore.Load('3200000101');
// získání jeho uživatelských parametrů
mStore.GetUserParameters(mUserParams);
// vypsání hodnoty parametru s kódem "param01" ze skupiny parametrů s kódem "ParamGroup01"
ShowMessage(mUserParams.AsList.ParamByName('ParamGroup01').AsList.ParamByName('UserParameters').AsList.ParamByName('param01').AsList.ParamByName('ParamValue').AsString);
// načtení BO hodnoty parametru
mUserParamValue.Load(mUserParams.AsList.ParamByName('ParamGroup01').AsList.ParamByName('UserParameters').AsList.ParamByName('param01').AsList.ParamByName('ParamValue_ID').AsString);
// změna hodnoty parametru
mUserParamValue.SetFieldValueAsString('ParamValue', 'Nová hodnota');
mUserParamValue.Save;
// vyprázdnění keše parametrů pro daný BO
mStore.ClearUserParametersCache;
// vypsání změněné hodnoty
mStore.GetUserParameters(mUserParams);
ShowMessage(mUserParams.AsList.ParamByName('ParamGroup01').AsList.ParamByName('UserParameters').AsList.ParamByName('param01').AsList.ParamByName('ParamValue').AsString);
finally
mUserParams.Free;
end;
finally
mUserParamValue.Free;
end;
finally
mStore.Free;
end;
end;
..
Příklad tvorby JSON objektu s poli:
mJSON := TJSONSuperObject.Create;
try
mJSON.I['cislo_cele'] := 12345;
mJSON.D['desetinne_cislo'] := 8282.12;
mJSON.S['retezec'] := 'příliš žluťoučký koníček';
mJSON.O['subjson'] := mJSON.CreateJSON;
mJSON.O['subjson'].I['cislo_cele'] := 12345;
mJSON.O['subjson'].D['desetinne_cislo'] := 8282.12;
mJSON.O['subjson'].S['retezec'] := 'příliš žluťoučký koníček';
mJSON.O['pole'] := mJSON.CreateJSONArray;
for i := 0 to 3 do begin
mJSON.A['pole'].I[i] := i;
mJSON.A['pole'].S[i + 3] := IntToStr(i);
end;
mJSON.O['jinepole'] := mJSON.CreateJSONArray;
mJSONArray := mJSON.O['jinepole'].AsArray;
for i := 0 to 4 do begin
mItem := mJSON.CreateJSON;
mItem.S['name'] := 'Str' + IntToStr(i);
mJSONArray.Add(mItem);
end;
mJSON.O['dalsiprikladpole'] := mJSON.CreateJSONArray;
mJSONArray := mJSON.O['dalsiprikladpole'].AsArray;
for i := 0 to 2 do begin
mSubArray := mJSON.CreateJSONArray;
for ii := 0 to i do begin
mItem := mJSON.CreateJSON;
mItem.S['Str' + IntToStr(ii)] := IntToStr(i) + ' ' + IntToStr(ii);
mItem.I['Int' + IntToStr(ii)] := ii;
mSubArray.AsArray.Add(mItem)
end;
mJSONArray.Add(mSubArray);
end;
ShowMessage(mJSON.AsJson);
finally
mJSON.Free;
end;
Skript založí následující JSON:
{
"desetinne_cislo": 8282.12,
"retezec": "příliš žluťoučký koníček",
"subjson": {
"desetinne_cislo": 8282.12,
"retezec": "příliš žluťoučký koníček",
"cislo_cele": 12345
},
"dalsiprikladpole": [
[
{
"Str0": "0 0",
"Int0": 0
}
],
[
{
"Str0": "1 0",
"Int0": 0
},
{
"Str1": "1 1",
"Int1": 1
}
],
[
{
"Str0": "2 0",
"Int0": 0
},
{
"Str1": "2 1",
"Int1": 1
},
{
"Str2": "2 2",
"Int2": 2
}
]
],
"pole": [
0,
1,
2,
3,
"1",
"2",
"3"
],
"jinepole": [
{
"name": "Str0"
},
{
"name": "Str1"
},
{
"name": "Str2"
},
{
"name": "Str3"
},
{
"name": "Str4"
}
],
"cislo_cele": 12345
}
Příklad spuštění exportu Účetní exporty - Export předvahy do MS Excel, který vytváří HTML obsah, který je uložen jako soubor s přílohou XLS. Takovýto soubor umí následně otevřít MS Excel a lze pracovat s hodnotami.
Příklad použití:
// Uložení exportu do souboru
procedure GenerateEpxportTrialBalance(const AOS: TNxCustomObjectSpace; const AFrom, ATo: TDateTime; const APath: string);
const
cExportTrialBalanceID = 'P300000001'; // Identifikátor exportu Účetní exporty - Export předvahy do MS-Excel
var
mFile, mDynSourceID: string;
mContext: TNxContext;
mParameter, mCondParameter, mValuesParameter: TNxParameter;
mConditions: TNxParameters;
begin
mFile := APath + '\Předvaha_' + FormatDateTime('YYYYMMDD', AFrom) + '_' + FormatDateTime('YYYYMMDD', ATo) + '.xls';
if FileExists(mFile) then
DeleteFile(mFile);
mDynSourceID := AOS.SQLSelectFirstAsString('SELECT DataSource FROM Exports WHERE ID =''' + cExportTrialBalanceID + '''');
mContext := NxCreateContext(AOS);
try
mConditions := TNxParameters.Create;
try
// Nastavení data od do podmínky Datum účtování
mParameter := mConditions.GetOrCreateParam(dtList, 'AccDate').AsList;
mParameter.AsList.NewFromDataType(dtInteger, 'USEDKIND', pkUnknown).AsInteger := 2; //ckRange;
mValuesParameter := mParameter.AsList.NewFromDataType(dtList, 'VALUES', pkUnknown);
mValuesParameter.AsList.NewFromDataType(dtFloat,'{:VALUE}', pkUnknown).AsFloat := AFrom;
mValuesParameter.AsList.NewFromDataType(dtFloat,'{:VALUEHIGH}', pkUnknown).AsFloat := ATo;
CFxReportManager.ExportByConditions(mContext, mConditions, mDynSourceID, cExportTrialBalanceID, 0, '', mFile);
finally
mConditions.Free;
end;
finally
mContext.Free;
end;
end;
procedure FormCreate_Hook(Self: TSiteForm);
var
mAction, mAction2: TBasicAction;
begin
mAction := Self.GetNewAction;
mAction.ShowControl := True;
mAction.ShowMenuItem := True;
mAction.Caption := 'Spuštění exportu předvahy';
mAction.Category := 'tabList';
mAction.OnExecute := @RunGenerateEpxportTrialBalance;
end;
procedure RunGenerateEpxportTrialBalance(Sender : TObject);
var
mSite: TSiteForm;
mObjectSpace: TNxCustomObjectSpace;
begin
mSite := TComponent(Sender).Site;
mObjectSpace := mSite.BaseObjectSpace;
GenerateEpxportTrialBalance(mObjectSpace, StrToDate('1.1.2000'), StrToDate('1.1.2025'), 'c:\temp')
end;
Ve skriptování je možné využít snadného zápisu do logu, který se konfiguruje za pomocí standardního nastavení přes nexus.cfg. Nastavením lze snadno určit do jaké míry bude logování ze skriptování detailní pomocí určení hodnoty úrovně (Level).
Pro zapnutí logování (nastavení Enabled=1) není třeba restartovat klienty ani aplikační server. Nastavení si všechny aplikace jednou za cca 60 sekund obnovují.
Příklad: Obsah Nexus.cfg
[Logs]
LogsDirectory=\\Server\složka_pro_zápis
[Log.Scripting]
Enabled=1
# Pro zápis chyb do logu nastavíme Level=2, pro zápis varování hodnotu Level=3 a pro detailní výpis Level=5
Level=5
Uvedená cesta LogsDirectory je třeba aby směřovala na síťové úložiště, kam mají klienti přístup zápisu.
V případě chybné cesty může docházet k velkému výkonovému zpomalení celého systému.
Příklad skriptu:
procedure ExampleUseNxScriptingLog;
begin
if NxScriptingLog.Active then begin
// Úrovně závažnosti jsou definovány výčtem TNxLogLevel = (logSystem,logCritical,logError,logWarning,logNotice,logInfo,logDebug)
// Nejvyšší úroveň závažnosti logSystem hodnota 0, ve skritpování nepoužíváme.
// NxScriptingLog.WriteEvent(logError, 'Text systémové chyby');
// Úroveň vznikla chyba Level=2
NxScriptingLog.WriteEvent(logError, 'Text vznikla chyby');
// Úroveň varování Level=3
NxScriptingLog.WriteEvent(logWarning, 'Text varování');
// Úroveň detailní výpis informací Level=5
NxScriptingLog.WriteEvent(logInfo, 'Informativní text');
NxScriptingLog.EnterSection('Logování bloku');
try
try
Sleep(1000);
RaiseException('Odchycena očekávaná výjimka');
except
NxScriptingLog.WriteEvent(logWarning, ExceptionMessage);
end;
finally
NxScriptingLog.LeaveSection('Logování bloku');
end;
end;
end;
procedure FormCreate_Hook(Self: TSiteForm);
var
mAction: TBasicAction;
begin
mAction := Self.GetNewAction;
mAction.ShowControl := True;
mAction.ShowMenuItem := True;
mAction.Caption := 'Zalogování textu do textového logu';
mAction.Category := 'tabList';
mAction.OnExecute := @RunWriteToLog;
end;
procedure RunWriteToLog(Sender: TObject);
begin
ExampleUseNxScriptingLog;
end;
Výsledkem je zápis do souboru s názvem, který obsahuje datum a čas vzniku souboru, název aplikace, proces, identifikátor aplikace a jméno počítače.
Příklad jména vzniklého logu: 24-03-26 16-32-17-946 AbraGen 16556 JAVU-SRV.JAVU-NTB ABRAGen.log
Obsah souboru z uvedeného příkladu:
26.03.2024 22:03:43.483 [2] 00001A68 (Scripting) Text vznikla chyby
26.03.2024 22:03:43.483 [3] 00001A68 (Scripting) Text varování
26.03.2024 22:03:43.483 [5] 00001A68 (Scripting) Informativní text
26.03.2024 22:03:43.483 [4] 00001A68 (Scripting) Logování bloku
26.03.2024 22:03:43.483 [4] 00001A68 (Scripting) ->
26.03.2024 22:03:44.501 [3] 00001A68 (Scripting) Error in ExampleUseNxScriptingLog (Exception):
očekávaná výjimka
scripting callstack:
ExampleUseNxScriptingLog (): .RaiseException (40:23)
RunWriteToLog (ExampleUseNxScriptingLog.Faktury přijaté (Agenda)): ExampleUseNxScriptingLog (16:27)
RunWriteToLog (14:1)
26.03.2024 22:03:44.501 [5] 00001A68 (Scripting)<-- (Logování bloku)
Příklad demonstruje vytvoření XML dokumentu s digitálním podpisem a jeho vložení do SOAP obálky za účelem komunikace se SÚKL (Státní ústav pro kontrolu léčiv):
procedure SignXmlSukl(Sender: TComponent);
var
mSite: TSiteForm;
mOS: TNxCustomObjectSpace;
mXML, mXMLEnvelope: TNxScriptingXMLWrapper;
mCertStore, mCertHash, mMsgGUID: String;
mContext: TNxContext;
begin
mSite:= Sender.Site;
mOS:= mSite.BaseObjectSpace;
mContext:= NxCreateContext(mOS);
try
//vyvoláme dialog s výběrem podpisového certifikátu a uložíme si jeho hash a místo uložení
mCertHash:= SelectCertificateDlg(mContext, mCertStore, mSite);
//vygenerujeme si GUID odesílané zprávy
mMsgGUID:= NxTrim(LowerCase(GUIDToString(CFxGuid.CreateNew())),'{}');
mXML:= TNxScriptingXMLWrapper.Create;
try
//vytvoříme zprávu k podepsání
mXML.DateTimeFormat:= 'yyyy-mm-dd"T"hh:nn:ss.zzz';
mXML.CreateEmpty('com:AppPingZEPDotaz', 'xmlns:com="http://www.sukl.cz/erp/common"');
mXML.setAttributeValue('com:AppPingZEPDotaz', 'xmlns:com', 'http://www.sukl.cz/erp/common');
mXML.setElementAsString('com:Doklad.com:Pristupujici.com:Uzivatel', cUserLogin);
mXML.setElementAsString('com:Doklad.com:Pristupujici.com:Pracoviste', cPremiseCode);
mXML.setElementAsString('com:Zprava.com:ID_Zpravy', mMsgGUID);
mXML.setElementAsString('com:Zprava.com:Verze', cSUKLInterfaceVersion);
mXML.setElementAsDateTime('com:Zprava.com:Odeslano', Now);
mXML.setElementAsString('com:Zprava.com:SW_Klienta', 'ABRASW');
//uděláme XML kanonickým, a podepíšeme (Kanonozizace upraví xml do konzistentní standardizované formy)
mXML.MakeXMLCannonical(0, false);
mXML.SignXML(mCertHash, mCertStore, 1, mContext, 'ds');
//vytvoříme obálku
mXMLEnvelope:= TNxScriptingXMLWrapper.Create;
try
mXMLEnvelope.CreateEmpty('soapenv:Envelope', 'xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"');
mXMLEnvelope.setAttributeValue('soapenv:Envelope', 'xmlns:soapenv', 'http://schemas.xmlsoap.org/soap/envelope/');
mXMLEnvelope.setAttributeValue('soapenv:Envelope', 'xmlns:com', 'http://www.sukl.cz/erp/common');
mXMLEnvelope.setAttributeValue('soapenv:Envelope', 'xmlns:ds', 'http://www.w3.org/2000/09/xmldsig#');
mXMLEnvelope.addElement('soapenv:Header');
//podepsanou XML zprávu vložíme do obálky
mXMLEnvelope.AddXMLEncodedElement('soapenv:Body', mXML.getElementXML('com:AppPingZEPDotaz'));
mXMLEnvelope.saveToFile('F:\testXML1.xml', 'UTF-8');
finally
mXMLEnvelope.Free;
end;
finally
mXML.Free;
end;
finally
mContext.Free;
end;
end;
Při použití interního OLE se skriptingu dbejte na jeho uvolnění!
Nejen, že neuvolněné OLE způsobuje Memory Leaky, ale v kombinaci s přihlášením jiném uživatele může vést k situaci, kdy systém přestane fungovat neboť dojde k částečné likvidaci původního ObjectSpace, který již pro další použití není plně funkční !!!
Je třeba dbát na to, aby byly uvolněny i všechny další objekty, které so OLE mohou dále držet, například objekt vytvořený prostřednictvím metody CreateDocumentDriver.
try
mOLE := mContext.GetAbraOLEApplication();
mOleDoc := mOLE.CreateDocumentDriver();
...
finally
//Tyto řádky zajistí uvolnění OLE a jsou nezbytné pro další správnou funkci systému
mOleDoc := nil;
mOLE := nil;
end;
Práce s definovatelnými importy: Definovatelné importy (txt, xls a csv) vyžadují, aby v importním souboru byla rozlišena hlavička a řádky pomocí masky. Pokud tomu tak není, jsou jednotlivé řádky ze souboru importovány jako samostatné doklady.
Pro případ, kdy jsou v importních datech pouze řádkové položky více než jedné hlavičky a zároveň jde o řádky jen jednoho druhu business objektu, byly vytvořeny nové skritptingové funkce dostupné na třídě TNxIEImportDefinition.
Soubor typu TXT:
Funkce ConvertOnlyRowsTXTData, metoda ConvertOnlyRowsTXTData, procedure ConvertOnlyRowsTXTData(var ATXTInputData: TStringList; const AKeyPositions: TStringList; const AHeadersPrefix, ARowsPrefix: string; ATrimSpaces: Boolean)
{
Vyvolává se po nastavení importního dokumentu před spuštěním parsingu - umožňuje změnit obsah importního dokumentu.
}
procedure IEImportExport_AfterSetImportDocument_Hook(AContext: TNxContext; const AImportDefinition_BO: TNxCustomBusinessObject; var ADocumentContent: TStringList);
var
mKeyPositions: TStringlist;
begin
mKeyPositions := TStringlist.Create;
try
mKeyPositions.Add('1;12');
mKeyPositions.Add('13;12');
mKeyPositions.Add('27;11');
TNxIEImportDefinition(AImportDefinition_BO).ConvertOnlyRowsTXTData(ADocumentContent, mKeyPositions, 'HEAD ', 'ROW ', false);
{ TNxIEImportDefinition(AImportDefinition_BO).ConvertOnlyRowsTXTData(ADocumentContent, mKeyPositions, 'HEAD ', 'ROW ', true);
}
finally
mKeyPositions.Free;
end;
end;
begin
end.
Soubor typu CSV a XLS (soubor XLS se při importu převede na CSV formát):
Funkce ConvertOnlyRowsCSVData, metoda ConvertOnlyRowsCSVData, procedure ConvertOnlyRowsCSVData(var ACSVInputData: TStringList; const AKeyPositions: TStringList; const AHeadersPrefix, ARowsPrefix: string)
{
Vyvolává se po nastavení importního dokumentu před spuštěním parsingu - umožňuje změnit obsah importního dokumentu.
}
procedure IEImportExport_AfterSetImportDocument_Hook(AContext: TNxContext; const AImportDefinition_BO: TNxCustomBusinessObject; var ADocumentContent: TStringList);
var
mKeyPositions: TStringlist;
begin
mKeyPositions := TStringlist.Create;
try
mKeyPositions.Add('0');
mKeyPositions.Add('1');
mKeyPositions.Add('3');
TNxIEImportDefinition(AImportDefinition_BO).ConvertOnlyRowsCSVData(ADocumentContent, mKeyPositions, 'HEAD', 'ROW');
finally
mKeyPositions.Free;
end;
end;
begin
end.
Příklad použití:
Na skladové kartě potřebujeme zobrazit hodnotu prodeje za posledních 365 dnů. Nechceme však aby se tato hodnota načítala dynamicky (například do sloupce přes NxSQLSelect) ale, aby byla perzistentní. Dle dané položky lze i řadit seznam, případně hodnotu použít pro další výpočty.
Načtení bude probíhat naplánovanou úlohou typu skript. Skript zapíše pro všechny skladové karty prodej za posledních 365 dnů. Zápis probíhá sekvenčně aby nedošlo k uzamčení tabulky StoreCards.
Příklad nastavení a provedení:
Na objektu skladové karty vytvoříme definovatelnou položku X_Prodej365 typu Číslo.
Položku X_Prodej365 si zobrazíme do sloupce v agendě Skladové karty. Před prvním přepočtem je pochopitelně hodnota 0 u každé skladové karty – výchozí hodnota (z definice definovatelné položky).
V agendě Balíčky skriptů založíme skript pro výpočet prodejů za posledních 365 dnů.
Ve zdrojovém kódu je možné rychle vygenerovat hlavičku skriptu „Volání z naplánované úlohy“.
Success – určuje, jestli úloha proběhla v pořádku
LogInfoStr – naplánované úloze můžeme vrátit dodatečné informace o průběhu, například počet změněných záznamů, případně jiné informace.
Metodu pojmenujeme a dopíšeme obsah dle následujícího příkladu.
procedure Run( ObjectSpace: TNxCustomObjectSpace; var Success: Boolean; var LogInfoStr: String); const cStoreCards = ' SELECT SC.ID AS ID, SUM(RO2.DeliveredQuantity) AS DeliveredQuantity FROM ReceivedOrders2 RO2' + ' JOIN ReceivedOrders RO ON RO.ID = RO2.Parent_ID' + ' JOIN StoreCards SC ON RO2.StoreCard_ID = SC.ID' + ' WHERE' + ' SC.Hidden = ''N'' AND' + ' RO.DocDate$DATE >= :FromDate' + ' GROUP BY SC.ID'; cUpdateStoreCard = 'UPDATE StoreCards SET X_Prodej365 = :X_Prodej365 WHERE ID = :ID'; var mStoreCardsData: TMemoryDataset; I: Integer; mInputParams: TNxParameters; mSC: TNxCustomBusinessObject; mStoreCardID: String; mDeliveredQuantity: Extended; begin Success := True; LogInfoStr := ''; mStoreCardsData := TMemoryDataset.Create(nil); try mInputParams := TNxParameters.Create; try // Získání dat přes SQL dotaz mInputParams.Clear; mInputParams.NewFromDataType(dtFloat, 'FromDate').AsFloat := Date - 365; ObjectSpace.SQLSelect2(cStoreCards, mStoreCardsData, mInputParams); // Sekvenčně - modifikace položky X_Prodej365 // A. Změna přes SQL Update - přímá DB změna bez zápisu sledování změn - obchází business logiku // B. Změna přes business logiku - změny jsou viditelné v sledování změn pokud je nastaveno na třídě mStoreCardsData.First; while not mStoreCardsData.Eof do begin mStoreCardID := mStoreCardsData.Fields.FieldByName('ID').AsString; mDeliveredQuantity := mStoreCardsData.Fields.FieldByName('DeliveredQuantity').AsFloat; // A. Změna přes SQL Update mInputParams.Clear; mInputParams.NewFromDataType(dtString, 'ID').AsString := mStoreCardID; mInputParams.NewFromDataType(dtFloat, 'X_Prodej365').AsFloat := 666; ObjectSpace.SQLExecute(cUpdateStoreCard, mInputParams); // ---A. // nebo // B. Změna přes business logiku mSC := ObjectSpace.CreateObject(Class_StoreCard); try mSC.Load(mStoreCardID); mSC.SetFieldValueAsFloat('X_Prodej365', mDeliveredQuantity); mSC.Save; finally mSC.Free; end; // ---B. mStoreCardsData.Next; end; finally mInputParams.Free; end; finally mStoreCardsData.Free; end; end; begin end.
Skript nejdřív načte data prodejů za posledních 365 dnů a hodnoty uloží do MemoryDataset. Poté sekvenčně projde všechny záznamy datasetu a provede změnu definovatelné položky X_Prodej365. Tady máme na výběr z dvou možností:
A. Změna přes SQL Update - přímá DB změna bez zápisu sledování změn - obchází business logiku
B. Změna přes business logiku - změny hodnot jsou viditelné v sledování změn (pokud je nastaveno na dané třídě).
(v ukázkovém skriptu teda ponechat jenom jednu z možností)
Založíme novou naplánovanou úlohu typu Skript.
Provedení naplánované úlohy vykoná přepočet prodejů a hodnoty zapíše do položky X_Prodej365. V číselníku skladových karet po občerstvení již máme napočtené hodnoty prodejů za posledních 365 dnů. Dle položky X_Prodej365 lze seznam i řadit nebo třídit.