Files
csbot-prototype/scripts/sap-bo.find-business-partner.js
T
JiriUhlirandClaude Fable 5.1 104ae36783 Revize projektu: prava, vykon, runtime, portal a ARES
Prava a bezpecnost: spravce firmy uz nemuze nastavit priznak spravce
platformy ani clenstvi v cizi firme; pozvanky, konektory a automatizace
kontroluji sve pravo; cizi firma v query je 404; zivy stream posila
udalosti jen firmam, kterych se tykaji; akce nad ticketem maji kontrolu
prava za firmu ticketu a strop viditelnosti; tokeny se nelogujou; limit
pokusu na prihlaseni, kontakt a pozvanky; bezpecnostni hlavicky;
zachyceni chyb v async handlerech; timing-safe porovnani tokenu.

Vykon: audit neskenuje celou kolekci pri kazdem zapisu a konecne maze
firemni zaznamy; ticket se uklada jednou misto trikrat; zapisy do
Postgresu jsou serializovane podle ID; prava se pocitaji jednou na
request; widgety nacitaji tickety jednou; strankovani seznamu; worker
je pool misto kol; na webu udalost ze streamu neodmontuje stranku,
dotazy maji spolecny debounce a cache, ciselniky drzi typovany sklad.

Runtime: opakuji se jen chyby oznacene retryable; smycka nenarazi na
strop 50 kroku (novy strop 1000 akci); podminka nad datem funguje;
vystup MCP nastroje neprepisuje spoustec; sandbox skriptu firmy nejde
opustit; MCP session id se drzi mezi volanimi; incident z kroku patri
firme; jedno rozhodnuti o rezimu uloziste; snapshot neprepise soubor
po chybe cteni.

Refaktory: sdilene typy API v src/shared (web nic nekopiruje, osm
rozjetych tvaru sjednoceno); spolecny modul net/guard pro volani ven;
formularova vrstva ui/form; rozdeleni Connectors a FlowCanvas; jeden
helper pro firmu z query, validaci a CRUD udalosti; pomucky ctx.util
pro skripty konektoru; i18n verejneho webu vcetne anglictiny.

Nova funkce: zalozeni firmy z registru ARES v Nastaveni (IC nebo nazev,
dotazeni IC, DIC, sidla a pravni formy, vyber soucasnych statutarnich
zastupcu a prokury, ucty spravce firmy s nahradnim e-mailem
IC-poradi@placeholder.cz).

Dokumentace: zaznam v 99-zmeny.md a aktualizace 15 dalsich dokumentu.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 10:26:07 +02:00

108 lines
3.3 KiB
JavaScript

/**
* SAP Business One: dohledani obchodniho partnera.
*
* Sluzba: https://services.csbot.cz/apps/sap-bo
* Endpoint: GET /api/business-partners?$filter=...
*
* Vzor **predvalidace nad OData**. Service Layer filtruje jazykem OData, kde
* se retezce uvozuji apostrofem - a apostrof v nazvu firmy by dotaz rozbil,
* takze se zdvojuje. Nenalezeno neni chyba: podle toho se strom vetvi na
* zalozeni noveho partnera.
*/
export const manifest = {
id: 'sap-bo.find-business-partner',
name: 'Najít obchodního partnera',
description:
'Dohledá partnera v SAP Business One podle kódu, IČO nebo názvu. ' +
'Nic nezakládá, výsledek slouží k větvení stromu.',
inputs: [
{
id: 'cardCode',
label: 'Kód partnera',
type: 'string',
required: false,
hint: 'Například C00123. Nejpřesnější, hledá se podle něj první.',
},
{
id: 'taxId',
label: 'IČO',
type: 'string',
required: false,
hint: 'Porovnává se s polem FederalTaxID.',
},
{
id: 'name',
label: 'Název',
type: 'string',
required: false,
hint: 'Stačí část názvu, hledá se podřetězec.',
},
],
outputs: [
{ id: 'found', label: 'Partner nalezen', type: 'boolean', required: true },
{ id: 'cardCode', label: 'Kód partnera', type: 'string', required: false },
{ id: 'cardName', label: 'Název partnera', type: 'string', required: false },
{ id: 'taxId', label: 'IČO', type: 'string', required: false },
{ id: 'currency', label: 'Měna', type: 'string', required: false },
{ id: 'matchedBy', label: 'Podle čeho se našel', type: 'string', required: true },
],
timeoutMs: 30000,
};
const notFound = {
found: false,
cardCode: null,
cardName: null,
taxId: null,
currency: null,
matchedBy: 'nenalezeno',
};
export async function run(inputs, ctx) {
const { pick, text } = ctx.util;
if (!inputs.cardCode && !inputs.taxId && !inputs.name) {
ctx.fail('Vyplňte kód partnera, IČO nebo název, jinak není podle čeho hledat.');
}
// Poradi je zamer: kod je klic, ICO je jednoznacne, nazev se pise pokazde jinak.
const attempts = [];
if (inputs.cardCode) attempts.push([`CardCode eq ${ctx.util.quote(inputs.cardCode)}`, 'kód']);
if (inputs.taxId) attempts.push([`FederalTaxID eq ${ctx.util.quote(inputs.taxId)}`, 'IČO']);
if (inputs.name) attempts.push([`contains(CardName,${ctx.util.quote(inputs.name)})`, 'název']);
for (const [filter, label] of attempts) {
const { body } = await ctx.http.get('/api/business-partners', {
query: {
$filter: filter,
$select: 'CardCode,CardName,FederalTaxID,Currency,CardType',
$top: 5,
},
});
const raw = pick(body, 'value', 'data', 'items');
const items = Array.isArray(raw) ? raw : Array.isArray(body) ? body : [];
if (items.length === 0) {
ctx.log(`Podle ${label} se nic nenašlo.`);
continue;
}
if (items.length > 1) ctx.log(`Podle ${label} vyšlo ${items.length} partnerů, bere se první.`);
const partner = items[0];
return {
found: true,
cardCode: text(pick(partner, 'CardCode')),
cardName: text(pick(partner, 'CardName')),
taxId: text(pick(partner, 'FederalTaxID')),
currency: text(pick(partner, 'Currency')),
matchedBy: label,
};
}
return notFound;
}