Konektory dostaly vykonnou cast. Jeden skript je jeden soubor, ktery nese manifest (vstupni a vystupni parametry) i kod. Diky manifestu s nim umi pracovat strom automatizace, aniz by o kodu cokoliv vedel. Soubory jsou zamerne obycejny JavaScript, ne TypeScript. TypeScript by se musel prelozit a to je presne to otaceni, ktere tady nema byt. Registr sleduje cas zmeny souboru, takze uprava v portalu, rucni uprava souboru i novy soubor ve slozce funguji stejne a bez restartu. Pridano: - scripts/ se skripty konektoru, nazev souboru je zaroven ID operace - kontrola vstupu i vystupu proti manifestu, jedna funkce pro obe strany. Chybejici povinny vystup je chyba skriptu, ne uzivatele - jinak by strom veril parametru, ktery nikdy nedosel - ctx predavany skriptu: http nad adresou napojeni, util, log, config, idempotencyKey, fail a retry. Skript nedostane pristupove udaje - rozliseni opakovatelne a koncove chyby. Runner nikdy nevyhodi vyjimku, vzdy vraci vysledek vcetne retryable - redakce tajnych hodnot pred zapisem do logu. Cizi API rado vraci prijaty token v chybove zprave a log ticketu vidi klient - napojeni z environment variables vcetne iDokladu - sest ukazkovych skriptu pro iDoklad proti skutecnemu API sluzby services.csbot.cz/apps/idoklad, kazdy na jiny vzor - stranka /dashboard/skripty: seznam, manifest, editor, zkusebni spusteni. Formular testu se sklada z manifestu, nepise se pro kazdy skript - endpointy /api/dashboard/scripts vcetne Swaggeru Zmeneno: - katalog konektoru uz neni jen staticky seznam. Akce ze skriptu se domeruji prekryvem v src/data/connectors.ts, takze se naraz objevi ve validaci stromu, ve vypoctu scope i v sablonach. Pri stejnem ID vyhrava skript - ConnectorOperation ma implementation a scriptId - ApiError na klientovi nese cele telo odpovedi a umi z nej vytahnout issues - Dockerfile kopiruje scripts/ do vysledneho image Ukladani nemuze rozbit fungujici skript: kod se nejdriv zapise do docasneho souboru, ten se nacte a overi, a az pak prepise puvodni. K tomu tri dokumenty navrhu dalsich kroku: 09 datove modely a prava, 10 runtime a rozpocet na 150 klientu, 11 popis skriptu konektoru. Overeno: npm run typecheck prochazi na serveru i webu. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
77 lines
3.0 KiB
JavaScript
77 lines
3.0 KiB
JavaScript
/**
|
|
* iDoklad: nacteni vydane faktury podle ID.
|
|
*
|
|
* Sluzba: https://services.csbot.cz/apps/idoklad
|
|
* Endpoint: GET /issued-invoices/{id}
|
|
*
|
|
* Nejjednodussi tvar skriptu: jedno volani a prevod odpovedi na vystupy.
|
|
*
|
|
* iDoklad vraci pole s velkym pocatecnim pismenem a nekdy obaluje odpoved
|
|
* do `Data`. Proto `unwrap` a `pick` - nespoléhá se na presny tvar odpovedi,
|
|
* protoze ten se u cizich sluzeb meni bez ohlaseni.
|
|
*/
|
|
|
|
export const manifest = {
|
|
id: 'idoklad.get-issued-invoice',
|
|
name: 'Získat vydanou fakturu',
|
|
description:
|
|
'Načte vydanou fakturu z iDokladu podle jejího ID. Používá se před rozhodnutím, ' +
|
|
'co s ní dál, například jestli je už uhrazená.',
|
|
|
|
inputs: [
|
|
{
|
|
id: 'invoiceId',
|
|
label: 'ID faktury v iDokladu',
|
|
type: 'number',
|
|
required: true,
|
|
hint: 'Interní ID, ne číslo dokladu. Číslo dokladu umí dohledat akce Najít vydanou fakturu.',
|
|
},
|
|
],
|
|
|
|
outputs: [
|
|
{ id: 'invoiceId', label: 'ID faktury', type: 'number', required: true },
|
|
{ id: 'documentNumber', label: 'Číslo dokladu', type: 'string', required: true },
|
|
{ id: 'variableSymbol', label: 'Variabilní symbol', type: 'string', required: false },
|
|
{ id: 'partnerId', label: 'ID odběratele', type: 'number', required: false },
|
|
{ id: 'partnerName', label: 'Odběratel', type: 'string', required: false },
|
|
{ id: 'totalWithVat', label: 'Celkem s DPH', type: 'number', required: true },
|
|
{ id: 'currencyId', label: 'ID měny', type: 'number', required: false },
|
|
{ id: 'dateOfIssue', label: 'Datum vystavení', type: 'date', required: true },
|
|
{ id: 'dateOfMaturity', label: 'Datum splatnosti', type: 'date', required: true },
|
|
{ id: 'isPaid', label: 'Je uhrazená', type: 'boolean', required: true },
|
|
],
|
|
};
|
|
|
|
export async function run(inputs, ctx) {
|
|
const { unwrap, pick, text, num, bool, date, need } = ctx.util;
|
|
|
|
const { body } = await ctx.http.get(`/issued-invoices/${inputs.invoiceId}`);
|
|
const invoice = unwrap(body);
|
|
|
|
if (!invoice || typeof invoice !== 'object') {
|
|
ctx.fail(`Faktura ${inputs.invoiceId} v iDokladu neexistuje.`);
|
|
}
|
|
|
|
// Odberatel muze byt jak plocha hodnota, tak vnoreny objekt partnera.
|
|
const partner = pick(invoice, 'partner', 'customer');
|
|
const partnerName =
|
|
text(pick(invoice, 'partnerName', 'customerName')) ??
|
|
text(pick(partner, 'companyName', 'name'));
|
|
|
|
return {
|
|
invoiceId: need(num(pick(invoice, 'id')), 'ID faktury'),
|
|
documentNumber: need(text(pick(invoice, 'documentNumber', 'number')), 'číslo dokladu'),
|
|
variableSymbol: text(pick(invoice, 'variableSymbol')),
|
|
partnerId: num(pick(invoice, 'partnerId')) ?? num(pick(partner, 'id')),
|
|
partnerName,
|
|
totalWithVat: need(
|
|
num(pick(invoice, 'totalWithVat', 'totalWithVatHc', 'total')),
|
|
'celkovou částku',
|
|
),
|
|
currencyId: num(pick(invoice, 'currencyId')),
|
|
dateOfIssue: need(date(pick(invoice, 'dateOfIssue')), 'datum vystavení'),
|
|
dateOfMaturity: need(date(pick(invoice, 'dateOfMaturity')), 'datum splatnosti'),
|
|
isPaid: bool(pick(invoice, 'isPaid')),
|
|
};
|
|
}
|