API dynamických položek ve výpočtech (di)
Objekt di slouží k práci s řádky proměnné typu Dynamické položky přímo z kódu na záložce Výpočty úkolu (viz Jak a kdy běží výpočty). Řádky umí číst celé, vybírat filtrem po stránkách, přidávat, přepisovat a mazat jednotlivě nebo hromadně. Je k dispozici v kontextu úkolu (Task-JS) a procesu (Process-JS). Jak se proměnná Dynamické položky navrhuje, popisuje článek Proměnná Dynamické položky, jak s tabulkou pracuje uživatel, článek Tabulka dynamických položek ve formuláři. Ostatní objekty výpočtů (vars, proc, task, dr) popisuje Reference API výpočtů: proměnné a případ.
Proměnná Dynamické položky a objekt
di jsou nové ve verzi 5.21. Starší proměnné typu Dynamické řádky dál fungují beze změny a pracuje se s nimi přes dr.Příklady v článku vycházejí z ukázkové objednávky s proměnnou items a sloupci item (text), quantity (číslo), unitPrice (číslo, měna), vatRate (číselník 0/12/21), urgent (zaškrtávací pole) a justification (text).
// Součet objednávky bez DPH z řádků tabulky
const cell = (row, name) => row.cells.find((c) => c.columnName === name)?.value;
const rows = await di.from('items').getRows();
const total = rows.reduce((sum, row) => sum + (cell(row, 'quantity') ?? 0) * (cell(row, 'unitPrice') ?? 0), 0);
await vars['orderTotal'].setValue(total);Jak di funguje
- Nejdřív zavolejte
di.from(proměnná). Všechna další volání pracují s touto proměnnou, dokud nezavolátefrom()znovu s jinou. Bezfrom()skončí každé volání chybouCall di.from(variable) before working with the rows. from()vracídi, takže volání lze řetězit:await di.from('items').getRows().- Všechny metody kromě
from()jsou asynchronní, volejte je sawait. - Sloupce se adresují technickým názvem sloupce (písmena a číslice, začíná písmenem), ne popiskem. Neznámý název sloupce skončí chybou a nic se neuloží.
- Změny se ukládají okamžitě do případu. Uživatel, který má úkol otevřený, je uvidí po novém načtení formuláře.
Tvar řádku
Metody pro čtení vracejí řádky v tomto tvaru, metody pro zápis přijímají stejný tvar (vlastnost label se při zápisu ignoruje, přečtený řádek tedy můžete poslat zpět beze změny):
{
id: 1534, // id řádku; při zápisu určuje, který řádek se přepíše
position: 0, // pořadí řádku v tabulce (od 0)
cells: [
{ columnName: 'item', value: 'Laptop Dell Latitude 7450' },
{ columnName: 'quantity', value: 3 },
{ columnName: 'vatRate', value: '21', label: '21' },
],
}Prázdná buňka v poli cells chybí. Při zápisu buňka s hodnotou null, '' nebo bez hodnoty zůstane prázdná.
| Typ sloupce | Hodnota při čtení | Co přijímá při zápisu |
|---|---|---|
| Text | řetězec | řetězec nebo číslo |
| Číslo (i měna a procento) | číslo | číslo nebo řetězec s číslem (desetinná tečka) |
| Zaškrtávací pole | true / false | true / false (nebo 1 / 0) |
| Datum | řetězec ve formátu ISO 8601 v UTC, např. 2026-12-01T00:00:00.000Z | řetězec s datem nebo časové razítko |
| Uživatel, role, organizační jednotka | id záznamu, v label jeho název | id existujícího záznamu |
| Soubor | id dokumentu, v label název souboru | id existujícího dokumentu |
| Číselník | hodnota položky číselníku (ne její překlad) | hodnota existující položky číselníku |
| Dynamická tabulka | klíč řádku dynamické tabulky, v label zobrazovaný text | klíč existujícího řádku |
Sloupec s více hodnotami vrací a přijímá pole hodnot (a pole popisků v label). Pole poslané do sloupce s jednou hodnotou skončí chybou.
Metody objektu di
| Metoda | Vrací | Popis | Příklad |
|---|---|---|---|
from(variable) | di | Nastaví proměnnou, se kterou pracují další volání. Přijímá název proměnné nebo objekt vars['...']. Proměnná jiného typu než Dynamické položky skončí chybou. | di.from(vars['items']); |
getRows() | Promise<object[]> | Všechny řádky proměnné v pořadí podle pozice. | const rows = await di.from('items').getRows(); |
queryRows(query) | Promise<object> | Stránka řádků vybraných filtrem, v pořadí podle pozice. Popis dotazu je níže. | await di.from('items').queryRows({ filters: [{ field: 'urgent', op: 'eq', value: true }] }); |
addRow(row) | Promise<void> | Přidá řádek na konec tabulky (nebo na zadanou position). Řádek ve tvaru { position?, cells }. | await di.from('items').addRow({ cells: [{ columnName: 'item', value: 'Shipping' }] }); |
updateRow(id, row) | Promise<void> | Přepíše buňky řádku s daným id. Buňky, které v row.cells nejsou, se vyprázdní. Bez position zůstane řádek na svém místě. | await di.from('items').updateRow(1534, { cells }); |
deleteRow(id) | Promise<void> | Smaže řádek s daným id. | await di.from('items').deleteRow(1534); |
setRows(rows) | Promise<void> | Hromadný zápis změn v jednom kroku: řádek s id se přepíše, řádek bez id se přidá, řádek s id a deleted: true se smaže. Řádky, které v poli nejsou, zůstanou beze změny. | await di.from('items').setRows([{ id: 1534, deleted: true }, { cells: [...] }]); |
Když chcete změnit jen jednu buňku, přečtěte řádek, upravte buňku a pošlete zpět všechny buňky. Jinak přijdete o hodnoty ostatních sloupců.
// Označí jako spěšné všechny položky dražší než 20 000 Kč
const rows = await di.from('items').getRows();
for (const row of rows) {
const price = row.cells.find((c) => c.columnName === 'unitPrice')?.value ?? 0;
if (price > 20000) {
const cells = row.cells.filter((c) => c.columnName !== 'urgent');
cells.push({ columnName: 'urgent', value: true });
await di.updateRow(row.id, { cells });
}
}queryRows – výběr řádků filtrem
Dotaz má tvar { filters: [{ field, op, value }], page: { limit, cursor, includeTotalCount } }. Všechny filtry musí platit současně. Bez filtrů vrací queryRows() všechny řádky po stránkách.
| Vlastnost | Popis |
|---|---|
field | Technický název sloupce. Neznámý sloupec skončí chybou. |
op | Operátor: eq, ne, in, nin, contains, startsWith, endsWith, gt, gte, lt, lte, isNull, isNotNull. |
value | Porovnávaná hodnota. U in a nin pole hodnot, u isNull a isNotNull se vynechává. |
page.limit | Počet řádků na stránce, 1 až 100. Bez zadání platí výchozí limit aplikace. |
page.cursor | Hodnota nextCursor z předchozí stránky. Pro první stránku ji vynechte. |
page.includeTotalCount | true doplní do výsledku celkový počet vyhovujících řádků. Výchozí je false. |
Výsledek má tvar { items, limit, totalCount, hasMore, nextCursor }, kde items jsou řádky ve tvaru popsaném výše, totalCount je null, pokud jste počet nežádali, a hasMore říká, jestli existuje další stránka.
contains,startsWithaendsWithfungují jen u textových sloupců a u sloupců s popiskem (uživatel, role, organizační jednotka, soubor, číselník, dynamická tabulka), kde porovnávají popisek. U čísla, data a zaškrtávacího pole skončí chybou.- Ostatní operátory porovnávají uloženou hodnotu: u uživatele, role, jednotky a souboru id, u číselníku hodnotu položky, u dynamické tabulky klíč řádku.
neaninvrací i řádky, kde je buňka prázdná. Filtr zaškrtávacího poleeq falsevrací nezaškrtnuté i prázdné buňky.- Řazení podle sloupce není k dispozici. Dotaz s vlastností
sortskončí chybou a řádky se vždy vracejí podle pozice.
// Projde všechny spěšné položky po stránkách
let cursor;
let urgentCount = 0;
do {
const page = await di.from('items').queryRows({
filters: [{ field: 'urgent', op: 'eq', value: true }],
page: { limit: 100, cursor },
});
urgentCount += page.items.length;
cursor = page.nextCursor;
} while (cursor);
await vars['approverComment'].setValue(`Spěšných položek: ${urgentCount}`);setRows – hromadné změny
setRows() zapíše všechny změny v jednom kroku: přidané, přepsané i smazané řádky. Hodí se, když měníte více řádků najednou.
// Smaže položky s nulovým množstvím a přidá řádek s dopravou
const rows = await di.from('items').getRows();
const changes = rows
.filter((row) => !row.cells.find((c) => c.columnName === 'quantity')?.value)
.map((row) => ({ id: row.id, deleted: true, cells: [] }));
changes.push({ cells: [
{ columnName: 'item', value: 'Shipping' },
{ columnName: 'quantity', value: 1 },
{ columnName: 'unitPrice', value: 350 },
{ columnName: 'vatRate', value: '21' },
] });
await di.setRows(changes);Každý řádek musí obsahovat pole cells, i mazaný (stačí prázdné). Řádek s id, který do proměnné nepatří, skončí chybou. Řádek s deleted: true bez id také.
Proměnná Dynamické položky přes vars
S proměnnou lze pracovat i přes vars['items'], ale jen několika metodami:
| Metoda | Vrací | Popis |
|---|---|---|
getValue() | Promise<object> | Celá tabulka: { columns, items, options, rules, script }. items jsou řádky ve tvaru výše, columns definice sloupců. Volejte s await. |
getJSON() | Promise<object> | Totéž co getValue(). |
setValue(value) | Promise | Přijímá pole řádků, nebo tabulku vrácenou z getValue() (použije se její items). Zápis se chová stejně jako setRows(): řádky s id se přepíšou, bez id přidají, s deleted: true smažou, ostatní zůstanou. |
Když z
getValue().items řádek odfiltrujete a výsledek pošlete do setValue(), řádek v proměnné zůstane. Ke smazání použijte deleted: true nebo di.deleteRow().Metody setAttribute, setJSONAttr, getJSONAttr, getDR a getDRObject nejsou pro proměnnou Dynamické položky dostupné a skončí chybou. Proměnnou nelze použít tam, kde se čeká jedna hodnota, například ji předat do setValue() jiné proměnné.
Omezení
- Počet řádků: tabulka pojme nejvýše tolik řádků, kolik povoluje konfigurace
featureFlag.dynamicItems.maxRows(výchozí 2000). Zápis, po kterém by tabulka měla víc řádků, skončí chybouDYNAMIC_ITEMS_MAX_ROWSa neuloží se nic. - Vypočtené sloupce a pravidla: vzorce, podmíněné výchozí hodnoty a validační pravidla sloupců vyhodnocuje formulář v prohlížeči. Při zápisu přes
dise nepřepočítají ani nekontrolují. Hodnotu vypočteného sloupce zapište sami, nebo ji přepočte formulář, až ho uživatel otevře. - Povinné sloupce se kontrolují až při splnění úkolu, jehož formulář tabulku vyžaduje. Zápis přes
dimůže povinný sloupec nechat prázdný. - Hodnoty odkazů (uživatel, role, jednotka, soubor, položka číselníku, řádek dynamické tabulky) musí existovat, jinak zápis skončí chybou s názvem sloupce.
- Proměnná Dynamické položky nemá snímky (
snaps). Všechny instance vícenásobného úkolu pracují se stejnými řádky.
Související: Proměnná Dynamické položky, Tabulka dynamických položek ve formuláři, Reference API výpočtů: proměnné a případ, Reference API výpočtů: knihovna lib, Jak a kdy běží výpočty.