/** * iDoklad: vystaveni vydane faktury s jednou polozkou. * * Sluzba: https://services.csbot.cz/apps/idoklad * Endpointy: GET /issued-invoices/default, POST /issued-invoices * * Vzor **dvou volani za sebou**. iDoklad u faktury vyzaduje pole, ktera nikdo * rucne vyplnovat nechce (`documentSerialNumber`, `isEet`, `isIncomeTax`), * takze se nejdriv vezme predvyplneny vzor z `/issued-invoices/default` * a prepisou se v nem jen ty veci, ktere prisly ze stromu. * * Kdyby se telo skladalo od nuly, rozbila by ho kazda zmena povinnych poli * na strane iDokladu. Takhle se prepisuje jen to, cemu rozumime. */ export const manifest = { id: 'idoklad.create-issued-invoice', name: 'Vystavit vydanou fakturu', description: 'Vystaví v iDokladu vydanou fakturu s jednou položkou. Chybějící údaje ' + 'se doplní z předvyplněného vzoru iDokladu.', timeoutMs: 25000, inputs: [ { id: 'partnerId', label: 'ID odběratele v iDokladu', type: 'number', required: true, hint: 'Umí ho dohledat akce Najít kontakt.', }, { id: 'description', label: 'Popis dokladu', type: 'string', required: true, hint: 'Text v hlavičce faktury, například Objednávka {{orderNumber}}.', }, { id: 'itemName', label: 'Název položky', type: 'string', required: true }, { id: 'unitPrice', label: 'Cena za jednotku', type: 'number', required: true, hint: 'V měně dokladu. Desetinná čárka i tečka projdou.', }, { id: 'amount', label: 'Počet jednotek', type: 'number', required: false, default: 1 }, { id: 'unit', label: 'Jednotka', type: 'string', required: false, default: 'ks' }, { id: 'dateOfIssue', label: 'Datum vystavení', type: 'date', required: false, hint: 'Nevyplněno = dnes.', }, { id: 'maturityDays', label: 'Splatnost ve dnech', type: 'number', required: false, default: 14, }, { id: 'variableSymbol', label: 'Variabilní symbol', type: 'string', required: false }, { id: 'note', label: 'Poznámka', type: 'string', required: false, multiline: true }, { id: 'vatRateType', label: 'Kód sazby DPH', type: 'number', required: false, default: 0, hint: 'Číselný kód VatRateType z iDokladu. Když nevíte, nechte 0.', }, { id: 'priceType', label: 'Kód typu ceny', type: 'number', required: false, default: 0, hint: 'Číselný kód PriceType z iDokladu (cena s DPH nebo bez). Když nevíte, nechte 0.', }, ], outputs: [ { id: 'invoiceId', label: 'ID faktury', type: 'number', required: true }, { id: 'documentNumber', label: 'Číslo dokladu', type: 'string', required: true }, { id: 'totalWithVat', label: 'Celkem s DPH', type: 'number', required: false }, { id: 'dateOfMaturity', label: 'Datum splatnosti', type: 'date', required: true }, ], }; /** Datum ve tvaru, ktery iDoklad ceka. */ function isoDay(value) { return new Date(value).toISOString().slice(0, 10); } function addDays(value, days) { const date = new Date(value); date.setUTCDate(date.getUTCDate() + days); return date; } export async function run(inputs, ctx) { const { unwrap, pick, text, num, date, need } = ctx.util; if (inputs.unitPrice < 0) ctx.fail('Cena za jednotku nemůže být záporná.'); const amount = inputs.amount ?? 1; if (amount <= 0) ctx.fail('Počet jednotek musí být větší než nula.'); // 1. Predvyplneny vzor. Nese povinna pole, ktera nechceme vyplnovat rucne. const defaults = unwrap((await ctx.http.get('/issued-invoices/default')).body); if (!defaults || typeof defaults !== 'object') { ctx.retry('iDoklad nevrátil předvyplněný vzor faktury.'); } const issuedAt = inputs.dateOfIssue ? new Date(inputs.dateOfIssue) : new Date(); const maturityAt = addDays(issuedAt, inputs.maturityDays ?? 14); // 2. Prepisou se jen ta pole, kterym rozumime. Zbytek zustava ze vzoru. const payload = { ...defaults, partnerId: inputs.partnerId, description: inputs.description, dateOfIssue: isoDay(issuedAt), dateOfTaxing: isoDay(issuedAt), dateOfMaturity: isoDay(maturityAt), items: [ { name: inputs.itemName, amount, unit: inputs.unit ?? 'ks', unitPrice: inputs.unitPrice, discountPercentage: 0, isTaxMovement: false, priceType: inputs.priceType ?? 0, vatRateType: inputs.vatRateType ?? 0, }, ], }; if (inputs.variableSymbol) payload.variableSymbol = inputs.variableSymbol; if (inputs.note) payload.note = inputs.note; const { body } = await ctx.http.post('/issued-invoices', payload); const invoice = unwrap(body); ctx.log(`Faktura vystavena pro odběratele ${inputs.partnerId}.`); return { invoiceId: need(num(pick(invoice, 'id')), 'ID vystavené faktury'), documentNumber: need(text(pick(invoice, 'documentNumber', 'number')), 'číslo dokladu'), totalWithVat: num(pick(invoice, 'totalWithVat', 'totalWithVatHc', 'total')), dateOfMaturity: date(pick(invoice, 'dateOfMaturity')) ?? need(date(maturityAt), 'datum splatnosti'), }; }