/* Účtovníček — Účtovný denník, Hlavná kniha, Saldokonto (čítané zo servera) */
const thisYear = () => new Date().getFullYear();
const yearsAvailable = () => {
const ys = new Set([thisYear()]);
uctStore.invoices.all().forEach(i => ys.add(+String(i.issueDate).slice(0, 4)));
uctStore.documents.list().forEach(d => ys.add(+String(d.date).slice(0, 4)));
return [...ys].filter(Boolean).sort((a, b) => b - a);
};
const MONTHS = ['január', 'február', 'marec', 'apríl', 'máj', 'jún', 'júl', 'august', 'september', 'október', 'november', 'december'];
/* Otvorenie dokladu z denníka: faktúra alebo doklad podľa druhu. */
function openDoc(go, e) {
if (e.docType === 'invoice') go((e.docKind === 'purchase' ? 'purchase' : 'invoices') + ':open:' + e.docId);
else go(e.docKind + ':open:' + e.docId);
}
function PageJournal({ go, route }) {
const [, action, routeArg] = (route || '').split(':');
const [year, setYear] = React.useState(() => thisYear());
const [month, setMonth] = React.useState('');
const [account, setAccount] = React.useState(action === 'account' ? routeArg : '');
const [q, setQ] = React.useState('');
const [rows, setRows] = React.useState(null);
const [err, setErr] = React.useState(null);
React.useEffect(() => { if (action === 'account') { setAccount(routeArg); setYear(0); } }, [route]);
React.useEffect(() => {
let alive = true;
setRows(null);
const f = { year: year || undefined, period: month ? `${year}-${month}` : undefined, account: account || undefined, q: q || undefined };
uctStore.journal.list(f).then(r => { if (alive) setRows(r); }).catch(e => { if (alive) { setErr(e.message); setRows([]); } });
return () => { alive = false; };
}, [year, month, account, q]);
const sum = (f) => (rows || []).reduce((a, e) => a + (f(e) || 0), 0);
const years = yearsAvailable();
return (
Účtovný denník Každý zápis vznikol z dokladu - klikni naň a otvorí sa faktúra, výpis alebo doklad
{ setYear(+e.target.value); setMonth(''); }}>
všetky roky
{years.map(y => {y} )}
setMonth(e.target.value)}>
celý rok
{MONTHS.map((m, i) => {m} )}
setAccount(e.target.value.replace(/\D/g, ''))} />
setQ(e.target.value)} />
{(account || q || month) && { setAccount(''); setQ(''); setMonth(''); }}>Zrušiť filter }
{err &&
setErr(null)} />}
{rows === null ?
: (
Dátum Doklad Účet Text Partner VS MD DAL
{rows.map(e => (
openDoc(go, e)}>
{fmt.date(e.date)}
{e.binder} {e.docNumber}
{e.account}
{e.text}
{e.partnerName}
{e.vs}
{e.md ? fmt.num(e.md) : ''}
{e.dal ? fmt.num(e.dal) : ''}
))}
{rows.length === 0 && Žiadne zápisy Zápisy vznikajú z faktúr, bankových výpisov, pokladne a interných dokladov.
}
{rows.length > 0 && {rows.length} {plural(rows.length, 'zápis', 'zápisy', 'zápisov')} {fmt.num(sum(e => e.md))} {fmt.num(sum(e => e.dal))} }
)}
);
}
function PageLedger({ go, route }) {
const [year, setYear] = React.useState(() => thisYear());
const [from, setFrom] = React.useState(() => `${thisYear()}-01-01`);
const [to, setTo] = React.useState(() => `${thisYear()}-12-31`);
const [level, setLevel] = React.useState('analytika');
const [rows, setRows] = React.useState(null);
const [err, setErr] = React.useState(null);
const setYearRange = (y) => { setYear(y); if (y) { setFrom(`${y}-01-01`); setTo(`${y}-12-31`); } else { setFrom(''); setTo(''); } };
React.useEffect(() => {
let alive = true;
setRows(null);
uctStore.journal.ledger(from, to).then(r => { if (alive) setRows(r); }).catch(e => { if (alive) { setErr(e.message); setRows([]); } });
return () => { alive = false; };
}, [from, to]);
let shown = rows || [];
if (level === 'syntetika' && rows) {
const agg = {};
rows.forEach(r => {
const k = r.account.slice(0, 3);
const a = agg[k] || (agg[k] = { account: k, name: uctStore.accounts.name(k + '001') || r.name, opening: 0, md: 0, dal: 0, closing: 0 });
a.opening += r.opening; a.md += r.md; a.dal += r.dal; a.closing += r.closing;
});
shown = Object.values(agg).sort((a, b) => a.account.localeCompare(b.account));
}
const sum = (f) => shown.reduce((a, r) => a + (f(r) || 0), 0);
const groups = {};
shown.forEach(r => { (groups[r.account[0]] = groups[r.account[0]] || []).push(r); });
const bal = (v) => v === 0 ? '' : fmt.num(v);
return (
Hlavná kniha Počiatočný stav, obraty za obdobie a konečný stav každého účtu
setYearRange(+e.target.value)}>
vlastné obdobie
{yearsAvailable().map(y => {y} )}
{ setYear(0); setFrom(e.target.value); }} />
{ setYear(0); setTo(e.target.value); }} />
setLevel(e.target.value)}>
analytické účty (311001) syntetické účty (311)
{err &&
setErr(null)} />}
{rows === null ?
: (
Účet Názov Počiatočný stav MD DAL Konečný stav
{Object.keys(groups).sort().map(cls => (
Trieda {cls} · {ACCOUNT_CLASSES[cls] || ''}
{groups[cls].map(r => (
go('journal:account:' + r.account)}>
{r.account}
{r.name || účet nie je v osnove }
{bal(r.opening)}
{bal(r.md)}
{bal(r.dal)}
{fmt.num(r.closing)}
))}
))}
{shown.length === 0 && Zatiaľ žiadne pohyby Hlavná kniha sa naplní z denníka.
}
{shown.length > 0 && Spolu {shown.length} účtov {fmt.num(sum(r => r.opening))} {fmt.num(sum(r => r.md))} {fmt.num(sum(r => r.dal))} {fmt.num(sum(r => r.closing))} }
)}
Súčet konečných stavov všetkých účtov má byť 0 (MD = DAL). Kladné číslo = zostatok na strane MD, záporné = na strane DAL.
);
}
function PageSaldo({ go }) {
const [rows, setRows] = React.useState(null);
const [err, setErr] = React.useState(null);
const [tab, setTab] = React.useState('311');
React.useEffect(() => {
let alive = true;
uctStore.journal.saldo().then(r => { if (alive) setRows(r); }).catch(e => { if (alive) { setErr(e.message); setRows([]); } });
return () => { alive = false; };
}, []);
const all = rows || [];
const shown = all.filter(r => tab === 'all' || r.account.startsWith(tab));
const sum = (f) => shown.reduce((a, r) => a + (f(r) || 0), 0);
const counts = { '311': all.filter(r => r.account.startsWith('311')).length, '321': all.filter(r => r.account.startsWith('321')).length, all: all.length };
return (
Saldokonto Otvorené položky podľa partnera a variabilného symbolu - čo kto komu ešte dlhuje
{[['311', 'Odberatelia (311)'], ['321', 'Dodávatelia (321)'], ['all', 'Všetky saldokontné účty']].map(([k, l]) => (
setTab(k)}>{l} {counts[k]}
))}
{err &&
setErr(null)} />}
{rows === null ?
: (
Partner VS Účet Doklad Dátum MD DAL Saldo
{shown.map((r, i) => (
go('journal:account:' + r.account)} title="Zobraziť pohyby účtu v denníku">
{r.partnerName || bez partnera }
{r.vs}
{r.account}
{r.docNumber}
{fmt.date(r.date)}
{fmt.num(r.md)}
{fmt.num(r.dal)}
{fmt.num(r.saldo)}
))}
{shown.length === 0 && Všetko vyrovnané Žiadne otvorené položky.
}
{shown.length > 0 && {shown.length} {plural(shown.length, 'položka', 'položky', 'položiek')} {fmt.num(sum(r => r.md))} {fmt.num(sum(r => r.dal))} {fmt.num(sum(r => r.saldo))} }
)}
Kladné saldo na 311 = odberateľ ešte nezaplatil. Záporné saldo na 321 = my ešte dlhujeme dodávateľovi. Dvojica s opačnými znamienkami na dvoch účtoch (napr. 321001 a 321002) znamená, že faktúra a platba boli zaúčtované na rôzne analytiky - tak to je aj v Profit365.
);
}