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>
95 lines
3.1 KiB
JavaScript
95 lines
3.1 KiB
JavaScript
/**
|
|
* iDoklad: zapsani uhrady k vydane fakture.
|
|
*
|
|
* Sluzba: https://services.csbot.cz/apps/idoklad
|
|
* Endpointy: GET /issued-payments/default/{invoiceId}, POST /issued-payments
|
|
*
|
|
* Vzor akce, ktera **neco meni**. U te zalezi na idempotenci: kdyz runtime krok
|
|
* zopakuje po timeoutu, nesmi vzniknout druha uhrada. Klic `ctx.idempotencyKey`
|
|
* je pro tentyz krok stejny pres vsechny pokusy a runtime ho posila v hlavicce
|
|
* `Idempotency-Key` automaticky, takze skript nemusi delat nic navic.
|
|
*
|
|
* Castka se necha prazdna pro plnou uhradu - vzor z iDokladu uz nese zbytek
|
|
* k zaplaceni, takze se nemusi pocitat tady.
|
|
*/
|
|
|
|
export const manifest = {
|
|
id: 'idoklad.register-payment',
|
|
name: 'Zapsat úhradu faktury',
|
|
description:
|
|
'Zapíše k vydané faktuře úhradu. Bez zadané částky se použije zbytek ' +
|
|
'k zaplacení podle iDokladu.',
|
|
|
|
inputs: [
|
|
{ id: 'invoiceId', label: 'ID faktury v iDokladu', type: 'number', required: true },
|
|
{
|
|
id: 'amount',
|
|
label: 'Uhrazená částka',
|
|
type: 'number',
|
|
required: false,
|
|
hint: 'Nevyplněno = celý zbytek k zaplacení.',
|
|
},
|
|
{
|
|
id: 'dateOfPayment',
|
|
label: 'Datum úhrady',
|
|
type: 'date',
|
|
required: false,
|
|
hint: 'Nevyplněno = dnes.',
|
|
},
|
|
{
|
|
id: 'sendConfirmation',
|
|
label: 'Poslat potvrzení odběrateli',
|
|
type: 'boolean',
|
|
required: false,
|
|
default: false,
|
|
},
|
|
],
|
|
|
|
outputs: [
|
|
{ id: 'paymentId', label: 'ID úhrady', type: 'number', required: true },
|
|
{ id: 'amount', label: 'Zapsaná částka', type: 'number', required: true },
|
|
{ id: 'dateOfPayment', label: 'Datum úhrady', type: 'date', required: true },
|
|
],
|
|
};
|
|
|
|
export async function run(inputs, ctx) {
|
|
const { unwrap, pick, num, date, need, day } = ctx.util;
|
|
|
|
if (inputs.amount !== null && inputs.amount <= 0) {
|
|
ctx.fail('Uhrazená částka musí být větší než nula.');
|
|
}
|
|
|
|
// Vzor nese zbytek k zaplaceni i vychozi zpusob platby.
|
|
const defaults = unwrap((await ctx.http.get(`/issued-payments/default/${inputs.invoiceId}`)).body);
|
|
if (!defaults || typeof defaults !== 'object') {
|
|
ctx.fail(`K faktuře ${inputs.invoiceId} nejde zapsat úhradu, iDoklad ji nezná.`);
|
|
}
|
|
|
|
const suggested = num(pick(defaults, 'paymentAmount'));
|
|
const amount = inputs.amount ?? suggested;
|
|
if (amount === null) {
|
|
ctx.fail('iDoklad nevrátil zbytek k zaplacení, zadejte částku ručně.');
|
|
}
|
|
|
|
const paidAt = inputs.dateOfPayment ? new Date(inputs.dateOfPayment) : new Date();
|
|
|
|
const payload = {
|
|
...defaults,
|
|
invoiceId: inputs.invoiceId,
|
|
paymentAmount: amount,
|
|
dateOfPayment: day(paidAt),
|
|
sendPaymentConfirmation: inputs.sendConfirmation ?? false,
|
|
};
|
|
|
|
const { body } = await ctx.http.post('/issued-payments', payload);
|
|
const payment = unwrap(body);
|
|
|
|
ctx.log(`K faktuře ${inputs.invoiceId} zapsána úhrada ${amount}.`);
|
|
|
|
return {
|
|
paymentId: need(num(pick(payment, 'id')), 'ID úhrady'),
|
|
amount: need(num(pick(payment, 'paymentAmount')) ?? amount, 'zapsanou částku'),
|
|
dateOfPayment: need(date(pick(payment, 'dateOfPayment')) ?? date(paidAt), 'datum úhrady'),
|
|
};
|
|
}
|