Príklady pokročilého skriptovania
Táto kapitola dopĺňa všeobecný úvod do problematiky skriptovania v systéme ABRA Gen o ukážky pokročilých praktických príkladov.
Pokročilé příklady skriptování
Příklady skriptování pro pokročilé

Funkcia SQLSelectFirst slúži na vrátenie prvého riadku výsledku SQL dopytu.
Konkrétne ide o funkcie SQLSelectFirstAsString , SQLSelectFirstAsInteger, SQLSelectFirstAsExtended, SQLSelectFirstAsBlob
Príklad použitia:
..
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 prevod času na UnixTime slúži funkcia DateTimeToUnix.
Príklad použitia:
..
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 verzie 19.4.3 je možné používať parametrizované SQL dopyty zo skriptovania. Používanie parametrov v SQL je jednoduchšie, bezpečnejšie a pre SQL servery aj rýchlejšie na realizáciu vďaka cachovaniu plánov SQL dopytov. Cez parametre je možné ľahko a bezpečne preniesť ľubovoľný dátový typ, teda aj binary blob dáta.
Príklad použitia parametrov pri vykonávaní SQL dopytov (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;
..
Príklad použitia parametrov pri získavaní dát z databázy (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;
..

Funkcia FindPaymentDestByVarsymb slúži na na vyhľadanie platené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 parametrov:
// 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ů v agendě Firemné údaje
// 0 - Vrátit vyhovující dle VS, částky a stavu zaplacení s ohledem na nastavení parametrů v agendě Firemné údaje
// 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
Príklad použitia:
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;

Ide o nasledujúce metódy triedy TNxCustomAccountedDocument:
function AttachSourceGroup(const ASourceGroup_ID: TNxOID);
function RemoveSourceGroup(const ASourceGroup_ID: TNxOID);
function IsSourceGroupAttached(const ASourceGroup_ID: TNxOID): Boolean;
function GetSourceGroup: TNxOID;
Príklad použitia:
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ážkový skript, ktorý pridá riadok 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ážkový skript, ktorý umožňuje rozšíriť zoznam objektov na záložke X-väzby, a to jednak pridaním objektov, tak i celých nových skupín objektov:
{
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ážkový skript, ktorý umožňuje upraviť poznámku zobrazovanú vo vizuálne na záložke X-väzby.:
{
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. Príklad založenia užívateľskej väzby zo skriptovania:
{
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áčik umožňuje priamo ovplyvniť SQL. Kedykoľvek číselník potrebuje dáta z databázy, zostavuje sa SQL. Háčik sa volá pred zostavení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'
Príklad:
Chceme, aby užívateľ Daniel Rasák (‘4300000101’) videl iba firmy s názvom začínajúcim na A. (Jednoduchý príklad nad demodátami)
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;
Keď nie je dostupný zdroj dát (tabuľka), môžem si súčasne s podmienkou zdroj dát pridať:
Názov aliasu voľte s rozumom, nesmie kolidovať s existujúcimi.
Dajte si pozor, aby join nezmnožil riadky namiesto toho, aby ich obmedzil.
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;
Vo väčšine prípadov si vystačíte s vyššie uvedenými príkladmi.
Máme ale k dispozícii ešte jeden variant, ktorý je zložitý na pochopenie, ale v určitých prípadoch môže zásadne ovplyvniť rýchlosť prevedenia dopytu.
Jadro ho používa napr. pre FULLTEXT.
Skúste použiť, pokiaľ základné WHERE alebo JOIN+WHERE budú 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;
Poddopyt musí spĺňať tieto kritériá:
- vracať jeden stĺpec s názvom Parent_ID
- Parent_ID musí byť ID z číselníka, z ktorého sa získavajú dáta (podmienka obmedzuje proti A.ID
- hodnoty Parent_ID musia byť unikátne

Príklad:
- rozpoznanie miesta objednávka prijatá riadok s ukážkou odovzdania hodnoty z iného poľa
- rozpoznanie X položky na riadku objednávky prijatej
- rozpoznanie, že ide o vizuálnu editáciu z definovateľného formulára
Háčik: Business objekt: Objednávka prijatá - riadok
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áčik: Číselník: Číselník skladových kariet
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;
Doplnenie parametra z definície definovateľného formulára.

Príklad použitia triedy CFxLog s metódou SaveLog, ktorá vytvorí a uloží nový log v agende 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);
..

Príklad ako otvoriť číselník a vybranú hodnotu spracovať:
// 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;

Príklad použitia funkcie GetARESCZData príp. GetSKFirmData - vytvorenie business objektu firmy a zavolanie príslušných funkcií na naplnenie (na vyskúšanie vo vizuálne s interakciou užívateľa):
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žnosť v skripte ručne vyvolať výnimku z breakpointov alebo za priameho chodu.
Po stlačení tlačidla Vyvolať výnimku a potvrdenie dialógu, kde je možné zmeniť text výnimky sa zašle do Abry požiadavka na budúcu výnimku. Abra pri ďalšej realizácii umelo zlyhá.
Oproti vyvolaniu výnimky priamo zo skriptovania príkazom RaiseException sa však ale nezobrazuje CallStack, kde chyba vznikla.

Uvedený príklad demonštruje, ako je možné na základe logiky v business objekte (objednávky prijaté) - po nastavení radu dokladu - docieliť zapnutie/vypnutie riadkovej zľavy, a následne zobrazenie/skrytie stĺpcov v gride riadkov tej 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;

Ide o triedy TPDFDocument a TPDFFileAttachment, ktoré umožňujú prácu s prílohami PDF dokumentov. Triedy sú založené na produkte tretej strany SecureBlackBox, ktorý je súčasťou systému ABRA Gen, vie ako načítať existujúce prílohy, tak vložiť 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 príklade potrebujeme preniesť zákazku a projekt z výrobného príkazu do CRM aktivity. V agendách existuje akcia Aktivity - Založiť novú aktivitu a pripojiť, štandardne ale nemáme v skriptingových háčikoch pri otvorení aktivity cez túto akciu k dispozícii zdrojový business objekt (tu výrobný príkaz). Tento príklad ukáže, ako sa dá potrebný BO získať a použiť v nejakom háčiku (tu AfterSiteOpen_Hook).
V _InitSelectionParams_Hook si zapamätáme objekt zdrojového dokladu a z neho potom preberá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.

Tu je príklad, ako zo skriptingu vytvoriť a odoslať e-mail v programe Outlook. Pomocou skriptovania môžeme vytvoriť akýkoľvek zaregistrovaný objekt vo Windows, teda aj napríklad Outlook.Application. Používa sa na to metóda CreateOleObject.
Aké vlastnosti, udalosti alebo metódy objekt používa, je možné nájsť v dokumentácii: 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;

Pri prihlásení sa volá háčik Aplikačný modul: Systémové udalosti - AfterLogon_Hook. Tento háčik sa však nevolá iba pri prvotnom prihlásení užívateľa do AbraGen.exe, ale pri každom vytvorení kontextu z klienta na aplikačný server. Na vytváranie viacerých kontextov na aplikačný server (z jednej aplikácie AbraGen.exe ) dochádza od verzie 23.2, a to pri vyhodnotení údajov definovateľných panelov, ktoré teraz prebieha v samostatnom vlákne.
Existencia viacerých vlákien má vplyv aj na použitie globálnej premennej GlobParams, parametre v nej je možné použiť len v rámci jedného vlákna. Hodnotu parametra nastavenú v jednom vlákne tak nie je možné získať v inom vlákne. Aby bolo možné zdieľať hodnoty parametrov medzi rôznymi vláknami, bola vytvorená nová premenná GlobThreadParams.
Nasledujúci príklad demonštruje, ako zabezpečiť, aby sa kód v háčiku AfterLogon_Hook zavolal iba raz (pri prihlásení užívateľa do ABRA Gen), a ako hodnotu parametra nastavenú v jednom vlákne (pri prihlásení užívateľa do ABRA Gen) získať v inom vlákne (pri vyhodnotení definovateľného panela).
Zároveň je potrebné mať na pamäti, že v háčiku AfterLogon_Hook je možné pristúpiť ku GUI (napríklad zobraziť formulár) iba, ak je kód vyvolaný z hlavného vlákna. Pokiaľ k prístupu ku GUI dôjde z iného vlákna, celá aplikácia môže zamrznúť.
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;

Príklad použitia funkcie GetUserParameters pre získanie užívateľských parametrov na business objekte skladu, zmena hodnoty vybraného parametra a uvoľnenie cache pre užívateľské parametre pre daný sklad pomocou ClearUserParametersCache:
Príklad použitia:
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;
..

Príklad tvorby JSON objektu s poľami:
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ží nasledujúce 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
}

Príklad spustenia exportu Účtovné exporty - Export predvahy do MS Excel, ktorý vytvára HTML obsah, ktorý je uložený ako súbor s prílohou XLS. Takýto súbor vie následne otvoriť MS Excel a je možné pracovať s hodnotami.
Príklad použitia:
// 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;

V skriptovaní je možné využiť jednoduchý zápis do logu, ktorý sa konfiguruje pomocou štandardného nastavenia cez nexus.cfg. Nastavením je možné ľahko určiť, do akej miery bude logovanie zo skriptovania detailné, pomocou určenia hodnoty úrovne (Level).
Pre zapnutie logovania (nastavenie Enabled=1) nie je potrebné reštartovať klientov ani aplikačný server. Nastavenie si všetky aplikácie raz za cca 60 sekúnd obnovujú.
Prí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 potrebná, aby smerovala na sieťové úložisko, kam majú klienti prístup zápisu.
V prípade chybnej cesty môže dochádzať k veľkému výkonovému spomaleniu celého systému.
Prí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ýsledkom je zápis do súboru s názvom, ktorý obsahuje dátum a čas vzniku súboru, názov aplikácie, proces, identifikátor aplikácie a meno počítača.
Príklad mena vzniknutého logu: 24-03-26 16-32-17-946 AbraGen 16556 JAVU-SRV.JAVU-NTB ABRAGen.log
Obsah súboru z uvedeného prí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)

Príklad demonštruje vytvorenie XML dokumentu s digitálnym podpisom a jeho vloženie do SOAP obálky za účelom komunikácie so SÚKL (Štátny ústav pre kontrolu lieč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;

Pri použití interného OLE sa skriptingu dbajte na jeho uvoľnenie!
Nielen, že neuvoľnené OLE spôsobuje Memory Leaky, ale v kombinácii s prihlásením iného užívateľa môže viesť k situácii, kedy systém prestane fungovať, pretože dôjde k čiastočnej likvidácii pôvodného ObjectSpace, ktorý už pre ďalšie použitie nie je plne funkčný !!!
Je potrebné dbať na to, aby boli uvoľnené aj všetky ďalšie objekty, ktoré s OLE môžu ďalej držať, napríklad objekt vytvorený prostredníctvom metódy 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áca s definovateľnými importmi: Definovateľné importy (txt, xls a csv) vyžadujú, aby v importnom súbore bola rozlíšená hlavička a riadky pomocou masky. Pokiaľ tomu tak nie je, sú jednotlivé riadky zo súboru importované ako samostatné doklady.
Pre prípad, keď sú v importných dátach len riadkové položky viac ako jednej hlavičky a zároveň ide o riadky len jedného druhu business objektu, boli vytvorené nové skritptingové funkcie dostupné na triede TNxIEImportDefinition.
Súbor typu TXT:
Funkcia ConvertOnlyRowsTXTData, metóda 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.
Súbor typu CSV a XLS (súbor XLS sa pri importe prevedie na CSV formát):
Funkcia ConvertOnlyRowsCSVData, metóda 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.

Príklad použitia:
Na skladovej karte potrebujeme zobraziť hodnotu predaja za posledných 365 dní. Nechceme však, aby sa táto hodnota načítala dynamicky (napríklad do stĺpca cez NxSQLSelect) ale, aby bola perzistentná. Podľa danej položky je možné aj zoradiť zoznam, prípadne hodnotu použiť pre ďalšie výpočty.
Načítanie bude prebiehať naplánovanou úlohou typu skript. Skript zapíše pre všetky skladové karty predaj za posledných 365 dní. Zápis prebieha sekvenčne, aby nedošlo k uzamknutiu tabuľky StoreCards.
Príklad nastavenia a prevedenia:
Na objekte skladovej karty vytvoríme definovateľnú položku X_Prodej365 typu Číslo.
Položku X_Predaj365 si zobrazíme do stĺpca v agende Skladové karty. Pred prvým prepočtom je pochopiteľne hodnota 0 pri každej skladovej karte – východisková hodnota (z definície definovateľnej položky).
V agende Balíčky skriptov založíme skript na výpočet predajov za posledných 365 dní.
V zdrojovom kóde je možné rýchlo vygenerovať hlavičku skriptu „Volanie z naplánovanej úlohy“.
Success – určuje, či úloha prebehla v poriadku
LogInfoStr – naplánovanej úlohe môžeme vrátiť dodatočné informácie o priebehu, napríklad počet zmenených záznamov, prípadne iné informácie.
Metódu pomenujeme a dopíšeme obsah podľa nasledujúceho prí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 najskôr načíta dáta predaja za posledných 365 dní a hodnoty uloží do MemoryDataset. Potom sekvenčne prejde všetky záznamy datasetu a vykoná zmenu definovateľnej položky X_Predaj365. Tu máme na výber z dvoch možností:
A. Zmena cez SQL Update - priama DB zmena bez zápisu sledovania zmien - obchádza business logiku
B. Zmena cez business logiku - zmeny hodnôt sú viditeľné v sledovaní zmien (ak je nastavené na danej triede).
(v ukážkovom skripte teda ponechať iba jednu z možností)
Založíme novú naplánovanú úlohu typu Skript.
Realizácia naplánovanej úlohy vykoná prepočet predajov a hodnoty zapíše do položky X_Predaj365. V číselníku skladových kariet po občerstvení už máme napočítané hodnoty predajov za posledných 365 dní. Podľa položky X_Predaj365 je možné zoznam aj zoradiť alebo triediť.

Pre agendu Skladové karty sa v okne náhľadov zobrazí záložka Predaj, kde bude na Google Charts grafoch ukázaný predaj
Je potrebné mať vystavané faktúry s tovarom za posledných 14 a 30 dní na rôzne strediská, aby skript zobrazoval dáta.
Druh skriptu je Aplikačný modul - Systémové udalosti.
{
Vyvolá se během načítání záložek pro náhled příloh.
}
procedure DocumentsViewer_AddTabs_Hook(const AContext: TNxContext; const ASourceObject: TNxCustomBusinessObject; const ASiteCLSID: string; var AParams: TNxParameters);
var
mTabData: TNxParameters;
begin
if ASourceObject.CLSID = Class_StoreCard then
begin
mTabData := TNxParameters.Create;
mTabData.GetOrCreateParam(dtString, 'Name').AsString := 'Prodeje';
mTabData.GetOrCreateParam(dtString, 'ID').AsString := ASourceObject.OID;
AParams.AsList.Add(mTabData);
end;
end;
{
Vyvolá se při kliknutí na záložku bez dat vytvořenou skriptem.
}
procedure DocumentsViewer_AddContent_Hook(const AContext: TNxContext; const ASourceObject: TNxCustomBusinessObject; const ASiteCLSID: string; AID: string; var AParams: TNxParameters);
var
mInputParams: TNxParameters;
mTableParams: TNxParameters;
mParams, mSteppedChartParams: TNxParameters;
mMemTable: TMemTable;
mChartPage: TGoogleChartsHtmPage;
mTableChart, mPieChart, mSteppedChart: string;
mTableColumns, mPieChartNames: array of string;
mPieChartValues: array of Double;
I: Integer;
begin
if (ASourceObject.CLSID = Class_StoreCard) then
begin
mInputParams := TNxParameters.Create;
mMemTable := TMemTable.Create(nil);
mTableParams := TNxParameters.Create;
mSteppedChartParams := TNxParameters.Create;
try
mInputParams.NewFromDataType(dtString, 'ID').AsString := ASourceObject.OID;
mInputParams.NewFromDataType(dtDate, 'DateFrom30').AsDateTime := Now - 30;
mInputParams.NewFromDataType(dtDate, 'DateFrom14').AsDateTime := Now - 14;
AContext.SQLSelect2(
'SELECT D.Code AS Code, '+
'(SELECT SUM(II2.QUANTITY) '+
'FROM ISSUEDINVOICES2 II2 '+
'LEFT JOIN ISSUEDINVOICES II ON II2.Parent_ID = II.ID '+
'WHERE '+
' II2.DIVISION_ID = D.ID AND '+
' II2.StoreCard_ID = :ID AND '+
' II.DOCDATE$DATE >= :DateFrom30 '+
') AS Q30, '+
'(SELECT SUM(II2.QUANTITY) '+
'FROM ISSUEDINVOICES2 II2 '+
'LEFT JOIN ISSUEDINVOICES II ON II2.Parent_ID = II.ID '+
' WHERE '+
' II2.DIVISION_ID = D.ID AND '+
' II2.StoreCard_ID = :ID AND '+
' II.DOCDATE$DATE >= :DateFrom14 '+
') AS Q14 '+
'FROM DIVISIONS D',
mMemTable, mInputParams);
SetLength(mPieChartNames, mMemTable.RecordCount);
SetLength(mPieChartValues, mMemTable.RecordCount);
mParams := mSteppedChartParams.NewFromDataType(dtList, '').AsList;
mParams.NewFromDataType(dtString, '').AsString := 'Středisko';
mParams.NewFromDataType(dtString, '').AsString := 'Q14';
mParams.NewFromDataType(dtString, '').AsString := 'Q30';
mMemTable.First;
while not mMemTable.Eof do
begin
mParams := mTableParams.NewFromDataType(dtList, IntToStr(mMemTable.RecNo)).AsList;
mParams.NewFromDataType(dtString, 'Středisko').AsString := mMemTable.FieldByName('Code').AsString;
mParams.NewFromDataType(dtFloat, 'Q14').AsFloat := mMemTable.FieldByName('Q14').AsFloat;
mParams.NewFromDataType(dtFloat, 'Q30').AsFloat := mMemTable.FieldByName('Q30').AsFloat;
mPieChartNames[mMemTable.RecNo - 1] := mMemTable.FieldByName('Code').AsString;
mPieChartValues[mMemTable.RecNo - 1] := mMemTable.FieldByName('Q30').AsFloat;
mParams := mSteppedChartParams.NewFromDataType(dtList, '').AsList;
mParams.NewFromDataType(dtString, '').AsString := mMemTable.FieldByName('Code').AsString;
mParams.NewFromDataType(dtFloat, '').AsFloat := mMemTable.FieldByName('Q14').AsFloat;
mParams.NewFromDataType(dtFloat, '').AsFloat := mMemTable.FieldByName('Q30').AsFloat;
mMemTable.Next;
end;
SetLength(mTableColumns, 3);
mTableColumns[0] := 'Středisko';
mTableColumns[1] := 'Q14';
mTableColumns[2] := 'Q30';
mTableChart := CFxGoogleCharts.RenderTableChart('tablechart_Sales', 'Sales', mTableColumns, mTableParams);
mPieChart := CFxGoogleCharts.RenderPieChart('piechart_Sales', 'Sales', 'Střediska', 'Počet prodaných kusů', mPieChartNames, mPieChartValues);
mSteppedChart := CFxGoogleCharts.RenderSteppedAreaChart('steppedchart_Sales', 'Sales', mSteppedChartParams);
mChartPage := TGoogleChartsHtmPage.Create;
try
mChartPage.AddChart(mTableChart);
mChartPage.AddChart(mPieChart);
mChartPage.AddChart(mSteppedChart);
AParams.NewFromDataType(dtString, 'Content').AsString := mChartPage.Render('Sales Chart');;
AParams.NewFromDataType(dtString, 'Format').AsString := 'HTML';
finally
mChartPage.Free;
end;
finally
mMemTable.Free;
mInputParams.Free;
mTableParams.Free;
mSteppedChartParams.Free;
end;
end;
end;
begin
end.

Nasledujúci skript načíta pre okno náhľadov v agende Servisované predmety z definovateľnej extra položky s názvom Folder a dátového typu Znaky cestu k adresáru (cestu zadávame bez úvodzoviek), v ktorom môžeme mať uložené prílohy. Tie sa zobrazia v samostatných záložkách okna náhľadov. Ak je navyše v tomto externom priečinku podpriečinok s názvom Fotodokumentácia, v ktorom sú uložené obrázky, vytvorí z týchto obrázkov skript náhľadovú HTML galériu, ktorá je v okne náhľadov zobrazená ako jedna samostatná záložka.
Výsledok v agende Servisované predmety
Druh skriptu je Aplikačný modul - Systémové udalosti.
procedure DocumentsViewer_AddTabs_Hook(const AContext: TNxContext; const ASourceObject: TNxCustomBusinessObject; const ASiteCLSID: string; var AParams: TNxParameters);
var
mPath: string;
mData: TmemoryDataSet;
procedure FillParametersFromUNCFiles;
var
mList: TStringList;
i: integer;
mTabData: TNxParameters;
mName: string;
mPathName: string;
begin
mList := TStringList.Create();
try
NxGetFileList(mPath, mList, '*.*', false);
if mList.Count > 0 then
begin
for i := 0 to mList.Count - 1 do
begin
mName := extractFileName(mList[i]);
mPathName := mPath + '\' + mList[i];
if (not DirectoryExists(mPathName)) then
begin
mTabData := TNxParameters.Create;
mTabData.GetOrCreateParam(dtString, 'Name').AsString := mName;
mTabData.GetOrCreateParam(dtString, 'ID').AsString := mPath + IntToStr(i);
mTabData.GetOrCreateParam(dtString, 'Path').AsString := mPathName;
AParams.AsList.Add(mTabData);
end;
end;
end;
finally
mList.Free;
end;
end;
procedure FillGaleryTabFromUNCFiles(aName: String);
var
mTabData: TNxParameters;
begin
mTabData := TNxParameters.Create;
mTabData.GetOrCreateParam(dtString, 'Name').AsString := aName;
mTabData.GetOrCreateParam(dtString, 'ID').AsString := mPath;
AParams.AsList.Add(mTabData);
end;
begin
case ASourceObject.CLSID of
Class_Storecard:
begin
mPath := NxEvalObjectExprAsStringDef(ASourceObject, 'X_Folder', '');
if DirectoryExists(mPath) and (mPath <> '') then FillParametersFromUNCFiles;
end;
Class_BusOrder:
begin
mPath := NxEvalObjectExprAsStringDef(ASourceObject, 'X_Folder', '');
if DirectoryExists(mPath) and (mPath <> '') then FillParametersFromUNCFiles;
end;
Class_PLMProduceRequest, Class_PLMJobOrder:
begin
mPath := NxEvalObjectExprAsStringDef(ASourceObject, 'Storecard_ID.X_Folder', '');
if DirectoryExists(mPath) and (mPath <> '') then FillParametersFromUNCFiles;
mPath := NxEvalObjectExprAsStringDef(ASourceObject, 'BusOrder_ID.X_Folder', '');
if DirectoryExists(mPath) and (mPath <> '') then FillParametersFromUNCFiles;
end;
Class_ServiceDocument:
begin
mPath := NxEvalObjectExprAsStringDef(ASourceObject, 'ServicedObject_ID.X_Folder', '');
if DirectoryExists(mPath) and (mPath <> '') then FillParametersFromUNCFiles;
mPath := NxEvalObjectExprAsStringDef(ASourceObject, 'BusOrder_ID.X_Folder', '');
if DirectoryExists(mPath) and (mPath <> '') then FillParametersFromUNCFiles;
mPath := NxEvalObjectExprAsStringDef(ASourceObject, 'ServicedObject_ID.X_Folder', '') + '\Fotodokumentace';
if DirectoryExists(mPath) and (mPath <> '') then FillGaleryTabFromUNCFiles('Fotodokumentace');
end;
Class_ServicedObject:
begin
mPath := NxEvalObjectExprAsStringDef(ASourceObject, 'X_Folder', '');
if DirectoryExists(mPath) and (mPath <> '') then FillParametersFromUNCFiles;
mPath := NxEvalObjectExprAsStringDef(ASourceObject, 'X_Folder', '') + '\Fotodokumentace';
if DirectoryExists(mPath) and (mPath <> '') then FillGaleryTabFromUNCFiles('Fotodokumentace');
end;
Class_ServicedObjectType:
begin
try
mData := TMemoryDataSet.Create(nil);
ASourceObject.ObjectSpace.SQLSElect2('select x_folder,x_sn from servicedobjects where servicedobjecttype_id = ' + QuotedStr(ASourceObject.OID), mData);
if mData.Active then
begin
mData.First;
while not mData.Eof do
begin
mPath := mData.FieldByName('X_Folder').AsString + '\Fotodokumentace';
if DirectoryExists(mPath) and (mPath <> '') then FillGaleryTabFromUNCFiles(mData.FieldByName('x_sn').AsString);
mData.Next;
end;
end;
finally
mData.Free;
end;
end;
end;
end;
procedure DocumentsViewer_AddContent_Hook(const AContext: TNxContext; const ASourceObject: TNxCustomBusinessObject; const ASiteCLSID: string; AID: string; var AParams: TNxParameters);
var
mPath: string;
procedure FillDataSetFromUNCPictures;
var
mList, mHTMLPict: TStringList;
i: integer;
mFileNamepict: string;
mContent: TNxParameters;
begin
mList := TStringList.Create();
mHTMLPict := TStringList.Create();
try
NxGetFileList(mPath, mList, '*.jpg', false);
if mList.Count > 0 then
for i := 0 to mList.Count - 1 do
if Fileexists(mPath + '\' + mList[i]) then
mHTMLPict.Add('<strong>' + mList[i] + '</strong><br /><a href="' + mPath + '\' + mList[i] + '" target="_blank"><img src="' + mPath + '\' + mList[i] + '" width="350" /></a><br />');
NxGetFileList(mPath, mList, '*.jpeg', false);
if mList.Count > 0 then
for i := 0 to mList.Count - 1 do
if Fileexists(mPath + '\' + mList[i]) then
mHTMLPict.Add('<strong>' + mList[i] + '</strong><br /><a href="' + mPath + '\' + mList[i] + '" target="_blank"><img src="' + mPath + '\' + mList[i] + '" width="350" /></a><br />');
NxGetFileList(mPath, mList, '*.png', false);
if mList.Count > 0 then
for i := 0 to mList.Count - 1 do
if Fileexists(mPath + '\' + mList[i]) then
mHTMLPict.Add('<strong>' + mList[i] + '</strong><br /><a href="' + mPath + '\' + mList[i] + '" target="_blank"><img src="' + mPath + '\' + mList[i] + '" width="350" /></a><br />');
NxGetFileList(mPath, mList, '*.bmp', false);
if mList.Count > 0 then
for i := 0 to mList.Count - 1 do
if Fileexists(mPath + '\' + mList[i]) then
mHTMLPict.Add('<strong>' + mList[i] + '</strong><br /><a href="' + mPath + '\' + mList[i] + '" target="_blank"><img src="' + mPath + '\' + mList[i] + '" width="350" /></a><br />');
if mHTMLPict.Count > 0 then
begin
AParams.NewFromDataType(dtString, 'Content').AsString := mHTMLPict.Text;
AParams.NewFromDataType(dtString, 'Format').AsString := 'HTML';
end;
finally
mList.Free;
mHTMLPict.Free;
end;
end;
begin
mPath := AID;
case ASourceObject.CLSID of
Class_ServiceDocument:
begin
if DirectoryExists(mPath) and (mPath <> '') then FillDataSetFromUNCPictures;
end;
Class_ServicedObject:
begin
if DirectoryExists(mPath) and (mPath <> '') then FillDataSetFromUNCPictures;
end;
Class_ServicedObjectType:
begin
if DirectoryExists(mPath) and (mPath <> '') then FillDataSetFromUNCPictures;
end;
end;
end;
begin
end.

Nasledujúci príklad ukazuje, ako získať prístupový token potrebný na autentifikáciu pri komunikácii s novým Firebase Cloud Messaging API (V1) pomocou protokolu OAuth 2.0. Token je generovaný na základe privátneho kľúča servisného účtu Google a obsahuje oprávnenia špecifické pre službu Firebase Messaging.
procedure GetAccessToken(Sender: TBasicAction);
const
GOOGLE_AUTH_JSON = '{' +
'"type": "service_account",' +
'"project_id": "**********",' +
'"private_key_id": "**************************************",' +
'"private_key": "-----BEGIN PRIVATE KEY-----\*******************************' +
'****************************\n-----END PRIVATE KEY-----\n",' +
'"client_email": "firebase-adminsdk-*****@********.iam.gserviceaccount.com",' +
'"client_id": "*******************",' +
'"auth_uri": "https://accounts.google.com/o/oauth2/auth",' +
'"token_uri": "https://oauth2.googleapis.com/token",' +
'"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",' +
'"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-************.iam.gserviceaccount.com",' +
'"universe_domain": "googleapis.com"' +
'}';
begin
// získáme access token ke službě "firebase.messaging" přes OAuth 2.0 na základě údajů Google servisního účtu
ShowMessage(CFxInternet.GetGoogleOAuth2AccessToken(GOOGLE_AUTH_JSON, 'https://www.googleapis.com/auth/firebase.messaging', jwtaRS256, 60));
end;

procedure InitSite_Hook(Self: TSiteForm);
var
mAction: TMultiAction;
begin
mAction := Self.GetNewMultiAction;
mAction.ShowControl := True;
mAction.ShowMenuItem := True;
mAction.Caption := 'Dohledat ve fakturách';
mAction.Items.Add('Dohledat ve fakturách vydaných (script)');
mAction.Category := 'tabList';
mAction.OnExecuteItem := @Test;
end;
procedure Test(Sender: TObject; AIndex :Integer);
var
mSite: TSiteForm;
mParams, mDefaultSelection: TNxParameters;
mParCondition: TNxParameter;
mTmpList: TStringList;
mTmpPar: TNxParameter;
mValues: TNxParameters;
begin
mSite := TComponent(Sender).Site;
mParams := TNxParameters.Create;
try
mParams.NewFromDataType(dtString, '_SelectionCaption').AsString := 'Otevřeno ze skriptování řada FV nezaplacené pro firmy A%';
mDefaultSelection := mParams.NewFromDataType(dtList, '_DefaultSelection').AsList;
mParCondition := mDefaultSelection.AsList.NewFromDataType(dtList, 'CONDITIONS');
mTmpPar := mParCondition.AsList.NewFromDataType(dtList, 'DocDate');
mTmpPar.AsList.NewFromDataType(dtInteger, 'USEDKIND').AsInteger := ckRange;
mValues := mTmpPar.AsList.NewFromDataType(dtList, 'VALUES').AsList;
mValues.NewFromDataType(dtString, '{:LOW}').AsString := '0';
mValues.NewFromDataType(dtString, '{:HIGH}').AsString := '45659';
mTmpPar := mParCondition.AsList.NewFromDataType(dtList, 'PaidStatus');
mTmpPar.AsList.NewFromDataType(dtInteger, 'USEDKIND').AsInteger := ckSingle;
mValues := mTmpPar.AsList.NewFromDataType(dtList, 'VALUES').AsList;
mValues.NewFromDataType(dtString, '{:VALUE}').AsString := '1;2;';
mTmpPar := mParCondition.AsList.NewFromDataType(dtList, 'UserDynSQLCondition');
mTmpPar.AsList.NewFromDataType(dtInteger, 'USEDKIND').AsInteger := ckSingle;
mValues := mTmpPar.AsList.NewFromDataType(dtList, 'VALUEBAG').AsList;
mValues.NewFromDataType(dtString, 'DYNUSERSQL').AsString := '(SELECT Name FROM Firms UserSQLFirm WHERE UserSQLFirm.ID = A.Firm_ID) LIKE ''A%''';
mTmpPar := mParCondition.AsList.NewFromDataType(dtList, 'DocQueue_ID');
mTmpPar.AsList.NewFromDataType(dtInteger, 'USEDKIND').AsInteger := ckList;
mTmpPar.AsList.NewFromDataType(dtString, 'VALUELIST').AsString := '''5600000101''';
mSite.ShowDynForm(Site_IssuedInvoices, mParams, nil, True, '');
finally
mParams.Free;
end;
end;
begin
end.

Od verzie 25.0.93 je možné využiť tento pomerne rozšírený a bezpečný štandard priamo z prostredia skriptovania pomocou triedy TOAuth2Wizard. Vďaka tomu sme schopní využívať služby poskytovateľov, ktorí podporujú overenie užívateľa prostredníctvom tretej strany, prípadne sami používajú vlastný identity server.
Príklad napojenia na službu Everifín, ktorá poskytuje služby otvoreného bankovníctva s licenciou PSD2:
procedure UserLogin(aSite: TSiteForm);
var
mOauth: TOAuth2Wizard;
begin
mOauth := TOAuth2Wizard.Create(aSite.SiteContext);
try
mOauth.ClientSecret := 'XXXXX';
mOauth.ClientId := 'abra-test';
mOauth.Scope := 'ais';
mOauth.AuthorizationUrl := 'https://api.everifin.com/auth/realms/everifin_app/protocol/openid-connect/auth?...';
mOauth.OnResponse := @OAuth2Wizard_Response;
mOauth.SkipLoginPage := True;
mOauth.Execute(aSite.FindParentForm);
finally
mOauth.Free;
end;
end;
procedure OAuth2Wizard_Response(Sender: TObject; aRequestParams: TStrings; var aState: TNxOAuth2ResultAuthorizationStatus; var aMessage: string);
var
mCode: String;
mOS: TNxCustomObjectSpace;
mToken: String;
mTokenEncrypted: String;
begin
aState := noasNone;
aMessage := '';
try
mCode := aRequestParams.Values('code');
if NxIsBlank(mCode) then begin
RaiseException('Parameter ''code'' se nenašel.');
end;
if not (Sender is TOAuth2Wizard) then begin
RaiseException('Incompatible sender type.');
end;
mOS := TOAuth2Wizard(Sender).ObjectSpace;
mToken := ObtainAccessToken(mOS, mCode); // Získání přístupového tokenu přes Web API
mTokenEncrypted := CFxCrypt.EncryptWithANSIKeyToBase64(cCryptoSecretKey, TEncoding.UTF8.GetBytes(mToken)); // Zašifrování tokenu
// Zde může přijít uchování zašifrovaného tokenu.
aState := noasOK;
except
aState := noasError;
aMessage := TrimExMessage(ExceptionMessage);
end;
end;
Proces overenia v skratke:
-
Vyvolanie sprievodcu OAuth2
-
Zadanie prihlasovacích údajov
-
V prípade úspešného overenia presmerovania na adresu uvedenú v parametri „redirect_uri“ vlastnosti AuthorizationURL
-
V obsluhe udalosti OnResponse získame autorizačný kód, pomocou ktorého požiadame o prístupový token
-
Získaný token zašifrujeme s využitím triedy CFxCrypt a uložíme napr. do CompanyCache ABRA Gen

Pre agendu Objednávky prijaté (OP) sa v okne náhľadov zobrazí záložka Objednávka prijatá, kde bude zobrazené číslo dokladu, celková lokálna cena a riadky danej objednávky
Najprv je potrebné vytvoriť súbor style.css, ktorý umiestnime do inštalačného adresár systému ABRA Gen, podzložky _Nahledy. V našom príklade ide o cestu:
c:/ABRA/INSTALACE/DEVELOP/CS/25.2/AbraGen-25.3.0-cs-CZ-debug-250318-1919-d2fc8a6/_Nahledy/style.css
V skripte nižšie je potrebné túto cestu nahradiť podľa vašej potreby.
Obsah style.css:
body {
font-family: Arial, sans-serif;
margin: 20px;
background-color: #f8f8f8;
color: #333;
}
h1 {
color: #0057a3;
font-size: 24px;
margin-bottom: 10px;
}
h2 {
color: #0057a3;
font-size: 18px;
margin-bottom: 10px;
}
p {
font-size: 14px;
line-height: 1.6;
}
Ďalej vytvoríme skript v agende Balíčky skriptov. Druh skriptu bude Aplikačný modul - Systémové udalosti.
procedure DocumentsViewer_AddTabs_Hook(const AContext: TNxContext; const ASourceObject: TNxCustomBusinessObject;
const ASiteCLSID: string; var AParams: TNxParameters);
var
mTabData: TNxParameters;
begin
if ASourceObject.CLSID = Class_ReceivedOrder then
begin
mTabData := TNxParameters.Create;
mTabData.GetOrCreateParam(dtString, 'Name').AsString := 'Objednávka přijatá'; // Název záložky
mTabData.GetOrCreateParam(dtString, 'ID').AsString := 'HTMLFormTab_' + ASourceObject.OID;
AParams.AsList.Add(mTabData);
end;
end;
procedure DocumentsViewer_AddContent_Hook(const AContext: TNxContext; const ASourceObject: TNxCustomBusinessObject;
const ASiteCLSID: string; AID: string; var AParams: TNxParameters);
var
mHtml: string;
mMemTableAmount, mMemTableRows: TMemTable;
mInputParams: TNxParameters;
mAmount: Double;
mRowType: Integer;
mText, mStoreCardName, mDisplayText: string;
mOrderNumber: string;
mDocQueueCode, mPeriodCode: string;
begin
if (ASourceObject.CLSID = Class_ReceivedOrder) and (AID = 'HTMLFormTab_' + ASourceObject.OID) then
begin
mInputParams := TNxParameters.Create;
mMemTableAmount := TMemTable.Create(nil);
mMemTableRows := TMemTable.Create(nil);
try
mInputParams.NewFromDataType(dtString, 'ID').AsString := ASourceObject.OID;
// Načtení hlavičkových údajů: Amount, DQ.Code, OrdNumber, P.Code
AContext.SQLSelect2(
'SELECT RO.LOCALAMOUNT, RO.ORDNUMBER, DQ.CODE AS DOCQUEUECODE, P.CODE AS PERIODCODE ' +
'FROM RECEIVEDORDERS RO ' +
'JOIN PERIODS P ON P.ID = RO.PERIOD_ID ' +
'JOIN DOCQUEUES DQ ON DQ.ID = RO.DOCQUEUE_ID ' +
'WHERE RO.ID = :ID',
mMemTableAmount,
mInputParams
);
if not mMemTableAmount.IsEmpty then
begin
mAmount := mMemTableAmount.FieldByName('LOCALAMOUNT').AsFloat;
mDocQueueCode := mMemTableAmount.FieldByName('DOCQUEUECODE').AsString;
mPeriodCode := mMemTableAmount.FieldByName('PERIODCODE').AsString;
mOrderNumber := mDocQueueCode + '-' + IntToStr(mMemTableAmount.FieldByName('ORDNUMBER').AsInteger) + '/' + mPeriodCode;
end
else
begin
mAmount := 0;
mOrderNumber := '[neznámé číslo]';
end;
// Načtení řádků objednávky
AContext.SQLSelect2(
'SELECT RO2.ROWTYPE, RO2.TEXT, SC.NAME ' +
'FROM RECEIVEDORDERS2 RO2 ' +
'LEFT JOIN STORECARDS SC ON SC.ID = RO2.STORECARD_ID ' +
'WHERE RO2.PARENT_ID = :ID',
mMemTableRows,
mInputParams
);
// HTML výstup
mHtml :=
'<!DOCTYPE html>'#13#10 +
'<html>'#13#10 +
'<head>'#13#10 +
' <meta charset="UTF-8">'#13#10 +
' <link rel="stylesheet" href="file:///c:/ABRA/INSTALACE/DEVELOP/CS/25.2/AbraGen-25.3.0-cs-CZ-debug-250318-1919-d2fc8a6/_Nahledy/style.css">'#13#10 +
' <title>Objednávka přijatá</title>'#13#10 +
'</head>'#13#10 +
'<body>'#13#10 +
' <h1>' + mOrderNumber + '</h1>'#13#10 +
' <p><strong>Celková cena (lok.):</strong> ' + FormatFloat('#,##0.00 Kč', mAmount) + '</p>'#13#10 +
' <h2>Řádky:</h2>'#13#10 +
' <ul>'#13#10;
mMemTableRows.First;
while not mMemTableRows.Eof do
begin
mRowType := mMemTableRows.FieldByName('ROWTYPE').AsInteger;
mText := mMemTableRows.FieldByName('TEXT').AsString;
mStoreCardName := mMemTableRows.FieldByName('NAME').AsString;
if mRowType = 3 then
mDisplayText := mStoreCardName
else
mDisplayText := mText;
mHtml := mHtml + ' <li>Typ (' + IntToStr(mRowType) + ') - ' + mDisplayText + '</li>'#13#10;
mMemTableRows.Next;
end;
mHtml := mHtml +
' </ul>'#13#10 +
'</body>'#13#10 +
'</html>'#13#10;
AParams.NewFromDataType(dtString, 'Content').AsString := mHtml;
AParams.NewFromDataType(dtString, 'Format').AsString := 'HTML';
finally
mMemTableAmount.Free;
mMemTableRows.Free;
mInputParams.Free;
end;
end;
end;
begin
end.

Toto je príklad skriptu, ktorý vypĺňa text do políčka “Popis zľavy” v závislosti na hodnotách objektu zliav. V tomto skripte je využitý háčik BeforeShowPOSCashDiscountParamsForm_Hook. Háčik je dostupný vo všetkých aplikačných moduloch pokladní. Háčik sa vyvolá pred zobrazením formulára pre zadanie zľavy. V parametroch háčika je odovzdávaný objekt zliav na pokladni a vlastný formulár na zadanie zliav.
{
Vyvolá se před zobrazením formuláře pro zadání slevy.)
}
procedure BeforeShowPOSCashDiscountParamsForm_Hook(AContext: TNxContext; ADiscount: TNxCustomBusinessObject; var APOSCashDiscountParamsForm: TForm);
var
edDiscountText: TEdit;
mDiscountKind: integer;
mDiscountKindText: string;
begin
edDiscountText := TEdit(APOSCashDiscountParamsForm.FindChildControl('edDiscountText'));
//edDiscountText.Text := 'init sleva kód: ' + ADiscount.GetFieldValueAsString('Code');
mDiscountKind := ADiscount.GetFieldValueAsInteger('DiscountKind');
{
0 - Akční sleva
1 - Finanční na zboží automatická
2 - Finanční na řádek
3 - Finanční na zboží
4 - Finanční na doklad
5 - Procentní na doklad
6 - Automatická procentní na zboží na dokladu
7 - Procentní řádková
8 - Procentní řádková na zboží
9 - Procentní...
}
case mDiscountKind of
0: mDiscountKindText := 'Akční';
1..4: mDiscountKindText := 'Finanční';
5..9: mDiscountKindText := 'Procentní';
else
mDiscountKindText := 'Mimo rozsah';
end;
edDiscountText.Text := ADiscount.GetFieldValueAsString('Code') + '-' + mDiscountKindText + ': ' + ADiscount.GetFieldValueAsString('DiscountDescription') + ' (' + IntToStr(mDiscountKind) + ')';
end;
begin
end.;

Príklady použitia NxPrintByIDs, NxPrintByConditions:
procedure PrintReports_Test(Sender: TControl);
var
mContext: TNxContext;
i: Integer;
mSite: TSiteForm;
mSCList: TStringList;
mConditions, mCondParams, mCondParamsValues, mExtraParams: TNxParameters;
begin
mSite := TSiteForm(TComponent(Sender).Site);
mContext := NxCreateContext(mSite.BaseObjectSpace);
try
mConditions := TNxParameters.Create;
try
mSCList := TStringList.Create;
try
//Tisk Reportu (Agenda Reporty)
//Nastavení omezení reportu za datum, sklady a skladové karty
//Omezení za Datum
mCondParams := mConditions.NewFromDataType(dtList, 'Date').AsList;
mCondParams.NewFromDataType(dtInteger, 'UsedKind').AsInteger := ckRange;
mCondParamsValues := mCondParams.NewFromDataType(dtList, 'Values').AsList;
// Omezení za datum je sice typu ckRange, ale jako údaj pro omezení (k datu)
// se bere jenom hodnota "LOW"
mCondParamsValues.NewFromDataType(dtFloat, '{:LOW}').AsFloat := 43449; // 15.12.2018
//mCondParamsValues.NewFromDataType(dtFloat, '{:HIGH}').AsFloat := Date; // dnes
// Omezení za sklady (výběr seznamem - řetězec reprezentující seznam ID - oddělené Entrem)
mCondParams := mConditions.NewFromDataType(dtList, 'Store_ID').AsList;
mCondParams.NewFromDataType(dtInteger, 'UsedKind').AsInteger := ckList;
mCondParams.NewFromDataType(dtString, 'ValueList').AsString := '2100000101'#13#10'3500000101';
// Omezení za skladové karty (výběr seznamem - TStringList)
// StringList naplníme přes SQL SELECT
mSite.BaseObjectSpace.SQLSelect('SELECT ID FROM StoreCards WHERE Code LIKE ''0%''', mSCList);
mCondParams := mConditions.NewFromDataType(dtList, 'StoreCard_ID').AsList;
mCondParams.NewFromDataType(dtInteger, 'UsedKind').AsInteger := ckList;
mCondParams.NewFromDataType(dtString, 'ValueList').AsString := NxStringsToCkListStr(mSCList);
//Provede tisk přímo na tiskárnu dle omezení v mConditions - Report "Stav skladu k datu"
// tisk přímo na tiskárnu
NxPrintByConditions(mContext,
mConditions, // Omezení pro report - podmínky
'DCGGWH4VRREL3FWD002BG34ZPK', // DynSQL - "Sklad - Stav k datu"
'V700000001', // Report ID - "Stav skladu k datu"
rtoPrint, // Typ operace - Tisk na tiskárnu
pekARP, // Typ exportu
'KONICA MINOLTA C223', // Výstup - název tiskárny
''); // (slouží pro název souboru v případě tisku do souboru)
//Provede tisk do souboru PDF dle omezení v mConditions - Report "Stav skladu k datu"
// tisk do souboru PDF
NxPrintByConditions(mContext,
mConditions, // Omezení pro report - podmínky
'DCGGWH4VRREL3FWD002BG34ZPK', // DynSQL - "Sklad - Stav k datu"
'V700000001', // Report ID - "Stav skladu k datu"
rtoFile, // Typ operace - Tisk na tiskárnu
pekPDF, // Typ expByCoortu
'C:\ABRA', // Výstup - cesta k souboru (zadávat formát C:\ABRA\)
'Report Skladu - Stav k datu.pdf'); // Název souboru
//Tisk tiskových sestav dle identifikací objektů
//S rozšířeným nastavením tiskárny (Collate, Duplex)
// Rozšířené nastavení riskárny
mExtraParams := TNxParameters.Create;
try
// Zapneme kompletování kopií
// true -> kopie budou tisknuty 1,2,3 1,2,3
// false -> kopie budou tisknuty 1,1 2,2 3,3
mExtraParams.GetOrCreateParam(dtBoolean, 'REPORT_COLLATE').AsBoolean := True;
// Zapneme oboustranný tisk
mExtraParams.GetOrCreateParam(dtBoolean, 'REPORT_DUPLEX').AsBoolean := True;
//Provede tisk tiskové sestavy “Seznam zboží (kód)” přímo na tiskárnu - omezení je dáno seznamem ID skladových karet
// tisk tiskové sestavy
NxPrintByIDs(mContext,
mSCList, // Seznam ID skladových karet
'OGQQA2C25JDL342N01C0CX3FCC', // DynSource - Skladové karty
'F300000001', // Report ID - "Seznam zboží (kód)"
rtoPrint, // Typ operace - Tisk na tiskárnu
pekARP, // Typ expByCoortu
'KONICA MINOLTA C223', // Výstup - cesta k souboru
'', // Název souboru
2, // Počet kopií
false, // Emulovat kopie
mExtraParams); // Předává parametry pro tisk - Duplex nebo Collate
//Provede tisk tiskové sestavy “Seznam zboží (kód)” do souboru PDF - omezení je dáno seznamem ID skladových karet
// tisk tiskové sestavy.do souboru
NxPrintByIDs(mContext,
mSCList, // Seznam ID skladových karet
'OGQQA2C25JDL342N01C0CX3FCC', // DynSource - Skladové karty
'F300000001', // Report ID - "Seznam zboží (kód)"
rtoFile, // Typ operace - Tisk na tiskárnu
pekPDF, // Typ expByCoortu
'C:\ABRA', // Výstup - cesta k souboru (zadávat formát C:\ABRA\)
'Seznam zboží(kód).pdf', // Název souboru
2); // Počet kopií
// Provede tisk tiskové sestavy "Seznam zboží (kód)" do souboru PDF - omezení je dáno seznamem ID skladových karet
// Parametr tiskárny při tisku do souboru způsobí, že se vynutí převzetí nastavení papíru z této tiskárny
CFxReportManager.PrintByIDs(mContext,
mSCList, // Seznam ID skladových karet
'OGQQA2C25JDL342N01C0CX3FCC', // DynSource - Skladové karty
'F300000001', // Report ID - "Seznam zboží (kód)"
rtoFile, // Typ operace - Tisk na tiskárnu
pekPDF, // Typ exoortu
'C:\ABRA\test\develop', // Výstup - cesta k souboru
'Seznam zboží(kód)_printer_KM.pdf', // Název souboru
1, false, nil, // Počet kopií + emulateCopies + AParams
'KONICA MINOLTA C223'); // Název tiskárny, z které se přebírá nastavení stránky/papíru
finally
mExtraParams.Free;
end;
finally
mSCList.Free;
end;
finally
mConditions.Free;
end;
finally
mContext.Free;
end;
end;
Nastavenie kompletizácie a obojstranná tlač sa odovzdáva pomocou parametra triedy TNxParameters.
Emulácia kópií sa používa pre tlačiarne, ktoré nepreberajú počet kópií.
POZOR: Ak tlačiareň umožňuje preberanie počtu kópií, majte parameter EmulateCopies vypnutý. V opačnom prípade bude tlačiareň tlačiť exponenciálny počet kópií, miesto 2 vytlačí 4, miesto 3 vytlačí 9 atď.
Funkcia NxPrintByConditions umožňuje tlač Reportov (agenda Reporty) podľa zadaného obmedzenia, napríklad Stav skladu k dátumu, Predaný tovar podľa skladov, Obraty účtov atď.
Parameter určujúci názov tlačiarne Počet kópií a emulácie boli pridané ako parametre funkcie už vo verzii 19.0.
Od verzie 25.3 bola pridaná možnosť výberu tlačiarne pri tlači do súboru - tým sa vynúti prevzatie nastavenia.

Príklad na pridanie náhľadu obrázku skladovej karty do ľavej časti stromu kusovníka. Pri prechode položkami stromu sa v ľavej časti zobrazuje obrázok z danej skladovej karty. Pokiaľ položka obrázok nemá, načítame vopred pripravený obrázok no_image. To môže byť napríklad biely obrázok alebo upozornenie, že obrázok nebol načítaný.
{
Vyvolává se po provedení inicializace agendy/formuláře. V tento okamžik je již na formuláři dostupný SiteContext.
}
procedure InitSite_Hook(Self: TSiteForm);
var
TreeView: TVirtualStringTree;
begin
With TPanel.Create(Self) do
begin
Parent:= TTabSheet(Self.FindChildControl('tabTree'));
Align:= alLeft;
Width:= 600;
Name:= 'Tree_Pict';
Caption:= '';
end;
with TImage.Create(TPanel(Self.FindChildControl('Tree_Pict'))) do
begin
Parent:= TPanel(Self.FindChildControl('Tree_Pict'));
Name:= 'pctStoreCard_Picture';
Align:= alClient;
AutoSize:=True;
end;
TreeView:= TVirtualStringTree(TTabSheet(Self.FindChildControl('tabTree')).FindChildControl('TreeView'));
if Assigned(TreeView) then
begin
TreeView.OnAfterFocusChanged := @My_OnAfterFocusChanged;
end;
end;
{
Vyvolá se před zobrazením formuláře pro zadání slevy.)
}
procedure My_OnAfterFocusChanged(Sender: TObject);
var
mPicture: TNxCustomBusinessObject;
mStream: TMemoryStream;
mBO: TNxCustomBusinessObject;
mSQL,mID: String;
begin
try
if Assigned(TImage(TVirtualStringTree(Sender).GetParentForm.FindChildControl('pctStoreCard_Picture'))) then
if TVirtualStringTree(Sender).GetFocusedNodeTextByColumn(0) <> '' then
begin
mBO:= TVirtualStringTree(Sender).Site.BaseObjectSpace.CreateObject(Class_StoreCard);
try
// TVirtualStringTree(Sender).GetFocusedNodeTextByColumn(0) - získá z prvního sloupce kód skladové karty
mSQL:= 'Select ID '+
'From StoreCards '+
'Where Hidden = ''N'' and Code = '+QuotedStr(TVirtualStringTree(Sender).GetFocusedNodeTextByColumn(0));
mID:= TVirtualStringTree(Sender).Site.BaseObjectSpace.SQLSelectFirstAsString(mSQL,'');
// Obrázek uložený na Skl. Kartě
if Not NxIsEmptyOID(mID) then
begin
mBO.Load(mID,nil);
mStream:= TMemoryStream.Create;
mPicture:= TVirtualStringTree(Sender).Site.BaseObjectSpace.CreateObject(Class_Picture);
try
if mPicture.Test(mBO.GetFieldValueAsString('Picture_ID')) then
begin
mPicture.Load(mBO.GetFieldValueAsString('Picture_ID'),nil);
mStream.SetBytes(mPicture.GetFieldValueAsBytes('BlobData'));
TImage(TVirtualStringTree(Sender).Site.FindChildControl('pctStoreCard_Picture')).Picture.LoadMultiFormatFromStream(mStream);
end
else
// Pokud položka nemá obrázek, načteme předem připraven obrázek no_image - to může být například upozornění, že neobsahuje obrázek nebo prázdný obrázek
TImage(TVirtualStringTree(Sender).Site.FindChildControl('pctStoreCard_Picture')).Picture.LoadFromFile('.\no_image.png');
finally
mPicture.Free;
mStream.Free;
end;
end;
finally
mBO.Free;
end;
end;
except
// Zahazuju chyby - případně vyvést chybu pomocí RaiseException
// nebo do okna s obrázkem vypsat text chyby (ExceptionMessage)
end;
end;

null
a existencia parametra v objekte TJSONSuperObject
Tento príklad ukazuje, ako v prostredí skriptovania testovať, či má parameter v objekte TJSONSuperObject
hodnotu null
, a ako takú hodnotu výslovne zapísať. Zároveň ukazuje, ako overiť existenciu parametra, ktorý môže alebo nemusí byť vo vstupnom JSONe prítomný.
var
mSQL: string;
mJSON: TJSONSuperObject;
mO: TJSONSuperObject;
begin
mJSON := TJSONSuperObject.Create;
try
mJSON.O['hodnotaNull'] := TJSONSuperObject.CreateByDataType(jtNull); // Nastavení null hodnoty
mJSON.I['cislo_cele'] := 12345;
ShowMessage(mJSON.AsJson);
mO := mJSON.O['neexistuje'];
if not mO.Exists then // Test existence parametru
ShowMessage('O[''neexistuje''].Exists = False');
if mJSON.O['hodnotaNull'].DataType = jtNull then // Test null hodnoty
ShowMessage('mJSON.O[''hodnotaNull''].DataType = jtNull');
finally
mJSON.Free;
end;
end;
Výsledok skriptu:
![]() |
![]() |
![]() |