Overview

Namespaces

  • FlexiPeeHP
  • PHP

Classes

  • DateTime
  • FlexiPeeHP\Actions
  • FlexiPeeHP\Adresar
  • FlexiPeeHP\Banka
  • FlexiPeeHP\Cenik
  • FlexiPeeHP\Changes
  • FlexiPeeHP\Company
  • FlexiPeeHP\DodavatelskaSmlouva
  • FlexiPeeHP\EvidenceList
  • FlexiPeeHP\FakturaPrijata
  • FlexiPeeHP\FakturaVydana
  • FlexiPeeHP\FakturaVydanaPolozka
  • FlexiPeeHP\FlexiBeeRO
  • FlexiPeeHP\FlexiBeeRW
  • FlexiPeeHP\Formats
  • FlexiPeeHP\Hooks
  • FlexiPeeHP\InterniDoklad
  • FlexiPeeHP\Kontakt
  • FlexiPeeHP\Nastaveni
  • FlexiPeeHP\Pokladna
  • FlexiPeeHP\PokladniPohyb
  • FlexiPeeHP\Priloha
  • FlexiPeeHP\RadaPokladniPohyb
  • FlexiPeeHP\Relations
  • FlexiPeeHP\SkladovaKarta
  • FlexiPeeHP\SkladovyPohyb
  • FlexiPeeHP\SkladovyPohybPolozka
  • FlexiPeeHP\SkupinaFirem
  • FlexiPeeHP\Smlouva
  • FlexiPeeHP\Status
  • FlexiPeeHP\Stitek
  • FlexiPeeHP\Strom
  • FlexiPeeHP\StromCenik
  • FlexiPeeHP\UcetniObdobi
  • FlexiPeeHP\VyrobniCislo
  • FlexiPeeHP\Zavazek
  • FlexiPeeHP\Zurnal

Interfaces

  • DateTimeInterface
  • Throwable

Traits

  • FlexiPeeHP\Firma
  • FlexiPeeHP\Stitky
  • FlexiPeeHP\Sum

Exceptions

  • Exception
  • Overview
  • Namespace
  • Class
  • Tree
   1:    2:    3:    4:    5:    6:    7:    8:    9:   10:   11:   12:   13:   14:   15:   16:   17:   18:   19:   20:   21:   22:   23:   24:   25:   26:   27:   28:   29:   30:   31:   32:   33:   34:   35:   36:   37:   38:   39:   40:   41:   42:   43:   44:   45:   46:   47:   48:   49:   50:   51:   52:   53:   54:   55:   56:   57:   58:   59:   60:   61:   62:   63:   64:   65:   66:   67:   68:   69:   70:   71:   72:   73:   74:   75:   76:   77:   78:   79:   80:   81:   82:   83:   84:   85:   86:   87:   88:   89:   90:   91:   92:   93:   94:   95:   96:   97:   98:   99:  100:  101:  102:  103:  104:  105:  106:  107:  108:  109:  110:  111:  112:  113:  114:  115:  116:  117:  118:  119:  120:  121:  122:  123:  124:  125:  126:  127:  128:  129:  130:  131:  132:  133:  134:  135:  136:  137:  138:  139:  140:  141:  142:  143:  144:  145:  146:  147:  148:  149:  150:  151:  152:  153:  154:  155:  156:  157:  158:  159:  160:  161:  162:  163:  164:  165:  166:  167:  168:  169:  170:  171:  172:  173:  174:  175:  176:  177:  178:  179:  180:  181:  182:  183:  184:  185:  186:  187:  188:  189:  190:  191:  192:  193:  194:  195:  196:  197:  198:  199:  200:  201:  202:  203:  204:  205:  206:  207:  208:  209:  210:  211:  212:  213:  214:  215:  216:  217:  218:  219:  220:  221:  222:  223:  224:  225:  226:  227:  228:  229:  230:  231:  232:  233:  234:  235:  236:  237:  238:  239:  240:  241:  242:  243:  244:  245:  246:  247:  248:  249:  250:  251:  252:  253:  254:  255:  256:  257:  258:  259:  260:  261:  262:  263:  264:  265:  266:  267:  268:  269:  270:  271:  272:  273:  274:  275:  276:  277:  278:  279:  280:  281:  282:  283:  284:  285:  286:  287:  288:  289:  290:  291:  292:  293:  294:  295:  296:  297:  298:  299:  300:  301:  302:  303:  304:  305:  306:  307:  308:  309:  310:  311:  312:  313:  314:  315:  316:  317:  318:  319:  320:  321:  322:  323:  324:  325:  326:  327:  328:  329:  330:  331:  332:  333:  334:  335:  336:  337:  338:  339:  340:  341:  342:  343:  344:  345:  346:  347:  348:  349:  350:  351:  352:  353:  354:  355:  356:  357:  358:  359:  360:  361:  362:  363:  364:  365:  366:  367:  368:  369:  370:  371:  372:  373:  374:  375:  376:  377:  378:  379:  380:  381:  382:  383:  384:  385:  386:  387:  388:  389:  390:  391:  392:  393:  394:  395:  396:  397:  398:  399:  400:  401:  402:  403:  404:  405:  406:  407:  408:  409:  410:  411:  412:  413:  414:  415:  416:  417:  418:  419:  420:  421:  422:  423:  424:  425:  426:  427:  428:  429:  430:  431:  432:  433:  434:  435:  436:  437:  438:  439:  440:  441:  442:  443:  444:  445:  446:  447:  448:  449:  450:  451:  452:  453:  454:  455:  456:  457:  458:  459:  460:  461:  462:  463:  464:  465:  466:  467:  468:  469:  470:  471:  472:  473:  474:  475:  476:  477:  478:  479:  480:  481:  482:  483:  484:  485:  486:  487:  488:  489:  490:  491:  492:  493:  494:  495:  496:  497:  498:  499:  500:  501:  502:  503:  504:  505:  506:  507:  508:  509:  510:  511:  512:  513:  514:  515:  516:  517:  518:  519:  520:  521:  522:  523:  524:  525:  526:  527:  528:  529:  530:  531:  532:  533:  534:  535:  536:  537:  538:  539:  540:  541:  542:  543:  544:  545:  546:  547:  548:  549:  550:  551:  552:  553:  554:  555:  556:  557:  558:  559:  560:  561:  562:  563:  564:  565:  566:  567:  568:  569:  570:  571:  572:  573:  574:  575:  576:  577:  578:  579:  580:  581:  582:  583:  584:  585:  586:  587:  588:  589:  590:  591:  592:  593:  594:  595:  596:  597:  598:  599:  600:  601:  602:  603:  604:  605:  606:  607:  608:  609:  610:  611:  612:  613:  614:  615:  616:  617:  618:  619:  620:  621:  622:  623:  624:  625:  626:  627:  628:  629:  630:  631:  632:  633:  634:  635:  636:  637:  638:  639:  640:  641:  642:  643:  644:  645:  646:  647:  648:  649:  650:  651:  652:  653:  654:  655:  656:  657:  658:  659:  660:  661:  662:  663:  664:  665:  666:  667:  668:  669:  670:  671:  672:  673:  674:  675:  676:  677:  678:  679:  680:  681:  682:  683:  684:  685:  686:  687:  688:  689:  690:  691:  692:  693:  694:  695:  696:  697:  698:  699:  700:  701:  702:  703:  704:  705:  706:  707:  708:  709:  710:  711:  712:  713:  714:  715:  716:  717:  718:  719:  720:  721:  722:  723:  724:  725:  726:  727:  728:  729:  730:  731:  732:  733:  734:  735:  736:  737:  738:  739:  740:  741:  742:  743:  744:  745:  746:  747:  748:  749:  750:  751:  752:  753:  754:  755:  756:  757:  758:  759:  760:  761:  762:  763:  764:  765:  766:  767:  768:  769:  770:  771:  772:  773:  774:  775:  776:  777:  778:  779:  780:  781:  782:  783:  784:  785:  786:  787:  788:  789:  790:  791:  792:  793:  794:  795:  796:  797:  798:  799:  800:  801:  802:  803:  804:  805:  806:  807:  808:  809:  810:  811:  812:  813:  814:  815:  816:  817:  818:  819:  820:  821:  822:  823:  824:  825:  826:  827:  828:  829:  830:  831:  832:  833:  834:  835:  836:  837:  838:  839:  840:  841:  842:  843:  844:  845:  846:  847:  848:  849:  850:  851:  852:  853:  854:  855:  856:  857:  858:  859:  860:  861:  862:  863:  864:  865:  866:  867:  868:  869:  870:  871:  872:  873:  874:  875:  876:  877:  878:  879:  880:  881:  882:  883:  884:  885:  886:  887:  888:  889:  890:  891:  892:  893:  894:  895:  896:  897:  898:  899:  900:  901:  902:  903:  904:  905:  906:  907:  908:  909:  910:  911:  912:  913:  914:  915:  916:  917:  918:  919:  920:  921:  922:  923:  924:  925:  926:  927:  928:  929:  930:  931:  932:  933:  934:  935:  936:  937:  938:  939:  940:  941:  942:  943:  944:  945:  946:  947:  948:  949:  950:  951:  952:  953:  954:  955:  956:  957:  958:  959:  960:  961:  962:  963:  964:  965:  966:  967:  968:  969:  970:  971:  972:  973:  974:  975:  976:  977:  978:  979:  980:  981:  982:  983:  984:  985:  986:  987:  988:  989:  990:  991:  992:  993:  994:  995:  996:  997:  998:  999: 1000: 1001: 1002: 1003: 1004: 1005: 1006: 1007: 1008: 1009: 1010: 1011: 1012: 1013: 1014: 1015: 1016: 1017: 1018: 1019: 1020: 1021: 1022: 1023: 1024: 1025: 1026: 1027: 1028: 1029: 1030: 1031: 1032: 1033: 1034: 1035: 1036: 1037: 1038: 1039: 1040: 1041: 1042: 1043: 1044: 1045: 1046: 1047: 1048: 1049: 1050: 1051: 1052: 1053: 1054: 1055: 1056: 1057: 1058: 1059: 1060: 1061: 1062: 1063: 1064: 1065: 1066: 1067: 1068: 1069: 1070: 1071: 1072: 1073: 1074: 1075: 1076: 1077: 1078: 1079: 1080: 1081: 1082: 1083: 1084: 1085: 1086: 1087: 1088: 1089: 1090: 1091: 1092: 1093: 1094: 1095: 1096: 1097: 1098: 1099: 1100: 1101: 1102: 1103: 1104: 1105: 1106: 1107: 1108: 1109: 1110: 1111: 1112: 1113: 1114: 1115: 1116: 1117: 1118: 1119: 1120: 1121: 1122: 1123: 1124: 1125: 1126: 1127: 1128: 1129: 1130: 1131: 1132: 1133: 1134: 1135: 1136: 1137: 1138: 1139: 1140: 1141: 1142: 1143: 1144: 1145: 1146: 1147: 1148: 1149: 1150: 1151: 1152: 1153: 1154: 1155: 1156: 1157: 1158: 1159: 1160: 1161: 1162: 1163: 1164: 1165: 1166: 1167: 1168: 1169: 1170: 1171: 1172: 1173: 1174: 1175: 1176: 1177: 1178: 1179: 1180: 1181: 1182: 1183: 1184: 1185: 1186: 1187: 1188: 1189: 1190: 1191: 1192: 1193: 1194: 1195: 1196: 1197: 1198: 1199: 1200: 1201: 1202: 1203: 1204: 1205: 1206: 1207: 1208: 1209: 1210: 1211: 1212: 1213: 1214: 1215: 1216: 1217: 1218: 1219: 1220: 1221: 1222: 1223: 1224: 1225: 1226: 1227: 1228: 1229: 1230: 1231: 1232: 1233: 1234: 1235: 1236: 1237: 1238: 1239: 1240: 1241: 1242: 1243: 1244: 1245: 1246: 1247: 1248: 1249: 1250: 1251: 1252: 1253: 1254: 1255: 1256: 1257: 1258: 1259: 1260: 1261: 1262: 1263: 1264: 1265: 1266: 1267: 1268: 1269: 1270: 1271: 1272: 1273: 1274: 1275: 1276: 1277: 1278: 1279: 1280: 1281: 1282: 1283: 1284: 1285: 1286: 1287: 1288: 1289: 1290: 1291: 1292: 1293: 1294: 1295: 1296: 1297: 1298: 1299: 1300: 1301: 1302: 1303: 1304: 1305: 1306: 1307: 1308: 1309: 1310: 1311: 1312: 1313: 1314: 1315: 1316: 1317: 1318: 1319: 1320: 1321: 1322: 1323: 1324: 1325: 1326: 1327: 1328: 1329: 1330: 1331: 1332: 1333: 1334: 1335: 1336: 1337: 1338: 1339: 1340: 1341: 1342: 1343: 1344: 1345: 1346: 1347: 1348: 1349: 1350: 1351: 1352: 1353: 1354: 1355: 1356: 1357: 1358: 1359: 1360: 1361: 1362: 1363: 1364: 1365: 1366: 1367: 1368: 1369: 1370: 1371: 1372: 1373: 1374: 1375: 1376: 1377: 1378: 1379: 1380: 1381: 1382: 1383: 1384: 1385: 1386: 1387: 1388: 1389: 1390: 1391: 1392: 1393: 1394: 1395: 1396: 1397: 1398: 1399: 1400: 1401: 1402: 1403: 1404: 1405: 1406: 1407: 1408: 1409: 1410: 1411: 1412: 1413: 1414: 1415: 1416: 1417: 1418: 1419: 1420: 1421: 1422: 1423: 1424: 1425: 1426: 1427: 1428: 1429: 1430: 1431: 1432: 1433: 1434: 1435: 1436: 1437: 1438: 1439: 1440: 1441: 1442: 1443: 1444: 1445: 1446: 1447: 1448: 1449: 1450: 1451: 1452: 1453: 1454: 1455: 1456: 1457: 1458: 1459: 1460: 1461: 1462: 1463: 1464: 1465: 1466: 1467: 1468: 1469: 1470: 1471: 1472: 1473: 1474: 1475: 1476: 1477: 1478: 1479: 1480: 1481: 1482: 1483: 1484: 1485: 1486: 1487: 1488: 1489: 1490: 1491: 1492: 1493: 1494: 1495: 1496: 1497: 1498: 1499: 1500: 1501: 1502: 1503: 1504: 1505: 1506: 1507: 1508: 1509: 1510: 1511: 1512: 1513: 1514: 1515: 1516: 1517: 1518: 1519: 1520: 1521: 1522: 1523: 1524: 1525: 1526: 1527: 1528: 1529: 1530: 1531: 1532: 1533: 1534: 1535: 1536: 1537: 1538: 1539: 1540: 1541: 1542: 1543: 1544: 1545: 1546: 1547: 1548: 1549: 1550: 1551: 1552: 1553: 1554: 1555: 1556: 1557: 1558: 1559: 1560: 1561: 1562: 1563: 1564: 1565: 1566: 1567: 1568: 1569: 1570: 1571: 1572: 1573: 1574: 1575: 1576: 1577: 1578: 1579: 1580: 1581: 1582: 1583: 1584: 1585: 1586: 1587: 1588: 1589: 1590: 1591: 1592: 1593: 1594: 1595: 1596: 1597: 1598: 1599: 1600: 1601: 1602: 1603: 1604: 1605: 1606: 1607: 1608: 1609: 1610: 1611: 1612: 1613: 1614: 1615: 1616: 1617: 1618: 1619: 1620: 1621: 1622: 1623: 1624: 1625: 1626: 1627: 1628: 1629: 1630: 1631: 1632: 1633: 1634: 1635: 1636: 1637: 1638: 1639: 1640: 1641: 1642: 1643: 1644: 1645: 1646: 1647: 1648: 1649: 1650: 1651: 1652: 1653: 1654: 1655: 1656: 1657: 1658: 1659: 1660: 1661: 1662: 1663: 1664: 1665: 1666: 1667: 1668: 1669: 1670: 1671: 1672: 1673: 1674: 1675: 1676: 1677: 1678: 1679: 1680: 1681: 1682: 1683: 1684: 1685: 1686: 1687: 1688: 1689: 1690: 1691: 1692: 1693: 1694: 1695: 1696: 1697: 1698: 1699: 1700: 1701: 1702: 1703: 1704: 1705: 1706: 1707: 1708: 1709: 1710: 1711: 1712: 1713: 1714: 1715: 1716: 1717: 1718: 1719: 1720: 1721: 1722: 1723: 1724: 1725: 1726: 1727: 1728: 1729: 1730: 1731: 1732: 1733: 1734: 1735: 1736: 1737: 1738: 1739: 1740: 1741: 1742: 1743: 1744: 1745: 1746: 1747: 1748: 1749: 1750: 1751: 1752: 1753: 1754: 1755: 1756: 1757: 1758: 1759: 1760: 1761: 1762: 1763: 1764: 1765: 1766: 1767: 1768: 1769: 1770: 1771: 1772: 1773: 1774: 1775: 1776: 1777: 1778: 1779: 1780: 1781: 1782: 1783: 1784: 1785: 1786: 1787: 1788: 1789: 1790: 1791: 1792: 1793: 1794: 1795: 1796: 1797: 1798: 1799: 1800: 1801: 1802: 1803: 1804: 1805: 1806: 1807: 1808: 1809: 1810: 1811: 1812: 1813: 1814: 1815: 1816: 1817: 1818: 1819: 1820: 1821: 1822: 1823: 1824: 1825: 1826: 1827: 1828: 1829: 1830: 1831: 1832: 1833: 1834: 1835: 1836: 1837: 1838: 1839: 1840: 1841: 1842: 1843: 1844: 1845: 1846: 1847: 1848: 1849: 1850: 1851: 1852: 1853: 1854: 1855: 1856: 1857: 1858: 1859: 1860: 1861: 1862: 1863: 1864: 1865: 1866: 1867: 1868: 1869: 1870: 1871: 1872: 1873: 1874: 1875: 1876: 1877: 1878: 1879: 1880: 1881: 1882: 1883: 1884: 1885: 1886: 1887: 1888: 1889: 1890: 1891: 1892: 1893: 1894: 1895: 1896: 1897: 1898: 1899: 1900: 1901: 1902: 1903: 1904: 1905: 1906: 1907: 1908: 1909: 1910: 1911: 1912: 1913: 1914: 1915: 1916: 1917: 1918: 1919: 1920: 1921: 1922: 1923: 1924: 1925: 1926: 1927: 1928: 1929: 1930: 1931: 1932: 1933: 1934: 1935: 1936: 1937: 1938: 1939: 1940: 1941: 1942: 1943: 1944: 1945: 1946: 1947: 1948: 1949: 1950: 1951: 1952: 1953: 1954: 1955: 1956: 1957: 1958: 1959: 1960: 1961: 1962: 1963: 1964: 1965: 1966: 1967: 1968: 1969: 1970: 1971: 1972: 1973: 1974: 1975: 1976: 1977: 1978: 1979: 1980: 1981: 1982: 1983: 1984: 1985: 1986: 1987: 1988: 1989: 1990: 1991: 1992: 1993: 1994: 1995: 1996: 1997: 1998: 1999: 2000: 2001: 2002: 2003: 2004: 2005: 2006: 2007: 2008: 2009: 2010: 2011: 2012: 2013: 2014: 2015: 2016: 2017: 2018: 2019: 2020: 2021: 2022: 2023: 2024: 2025: 2026: 2027: 2028: 2029: 2030: 2031: 2032: 2033: 2034: 2035: 2036: 2037: 2038: 2039: 2040: 2041: 2042: 2043: 2044: 2045: 2046: 2047: 2048: 2049: 2050: 2051: 2052: 2053: 2054: 2055: 2056: 2057: 2058: 2059: 2060: 2061: 2062: 2063: 2064: 2065: 2066: 2067: 2068: 2069: 2070: 2071: 2072: 2073: 2074: 2075: 2076: 2077: 2078: 2079: 2080: 2081: 2082: 2083: 2084: 2085: 2086: 2087: 2088: 2089: 2090: 2091: 2092: 2093: 2094: 2095: 2096: 2097: 2098: 2099: 2100: 2101: 2102: 2103: 2104: 2105: 2106: 2107: 2108: 2109: 2110: 2111: 2112: 2113: 2114: 2115: 2116: 2117: 2118: 2119: 2120: 2121: 2122: 2123: 2124: 2125: 2126: 2127: 2128: 2129: 2130: 2131: 2132: 2133: 2134: 2135: 2136: 2137: 2138: 2139: 2140: 2141: 2142: 2143: 2144: 2145: 2146: 2147: 2148: 2149: 2150: 2151: 2152: 2153: 2154: 2155: 2156: 2157: 2158: 2159: 2160: 2161: 2162: 2163: 2164: 2165: 2166: 2167: 2168: 2169: 2170: 2171: 2172: 2173: 2174: 2175: 2176: 2177: 2178: 2179: 2180: 2181: 2182: 2183: 2184: 2185: 2186: 2187: 2188: 2189: 2190: 2191: 2192: 2193: 2194: 2195: 2196: 2197: 2198: 2199: 2200: 2201: 2202: 2203: 2204: 2205: 2206: 2207: 2208: 2209: 2210: 2211: 2212: 2213: 2214: 2215: 2216: 2217: 2218: 2219: 2220: 2221: 2222: 2223: 2224: 2225: 2226: 2227: 2228: 2229: 2230: 2231: 2232: 2233: 2234: 2235: 2236: 2237: 2238: 2239: 2240: 2241: 2242: 2243: 2244: 2245: 2246: 2247: 2248: 2249: 2250: 2251: 2252: 2253: 2254: 2255: 2256: 2257: 2258: 2259: 2260: 2261: 2262: 2263: 2264: 2265: 2266: 2267: 2268: 2269: 2270: 2271: 2272: 2273: 2274: 2275: 2276: 2277: 2278: 2279: 2280: 2281: 2282: 2283: 2284: 2285: 2286: 2287: 2288: 2289: 2290: 2291: 2292: 2293: 2294: 2295: 2296: 2297: 2298: 2299: 2300: 2301: 2302: 2303: 2304: 2305: 2306: 2307: 2308: 2309: 2310: 2311: 2312: 2313: 2314: 2315: 2316: 2317: 2318: 2319: 2320: 2321: 2322: 2323: 2324: 2325: 2326: 2327: 2328: 2329: 2330: 2331: 2332: 2333: 2334: 2335: 2336: 2337: 2338: 2339: 2340: 2341: 2342: 2343: 2344: 2345: 2346: 2347: 2348: 2349: 2350: 2351: 2352: 2353: 2354: 2355: 2356: 2357: 2358: 2359: 2360: 2361: 2362: 2363: 2364: 2365: 2366: 2367: 2368: 2369: 2370: 2371: 2372: 2373: 2374: 2375: 2376: 2377: 2378: 2379: 2380: 2381: 2382: 2383: 2384: 2385: 2386: 2387: 2388: 2389: 2390: 2391: 2392: 2393: 2394: 2395: 2396: 2397: 2398: 2399: 2400: 2401: 2402: 2403: 2404: 2405: 2406: 2407: 2408: 2409: 2410: 2411: 2412: 2413: 2414: 2415: 2416: 2417: 2418: 2419: 2420: 2421: 2422: 2423: 2424: 2425: 2426: 2427: 2428: 2429: 2430: 2431: 2432: 2433: 2434: 2435: 2436: 2437: 2438: 2439: 2440: 2441: 2442: 2443: 2444: 2445: 2446: 2447: 2448: 2449: 2450: 2451: 2452: 2453: 2454: 2455: 2456: 2457: 2458: 2459: 2460: 2461: 2462: 2463: 2464: 2465: 2466: 2467: 2468: 2469: 2470: 2471: 2472: 2473: 2474: 2475: 2476: 2477: 2478: 2479: 2480: 2481: 2482: 2483: 2484: 2485: 2486: 2487: 2488: 2489: 2490: 2491: 2492: 2493: 2494: 2495: 2496: 2497: 2498: 2499: 2500: 2501: 2502: 2503: 2504: 2505: 2506: 2507: 2508: 2509: 2510: 2511: 2512: 2513: 2514: 2515: 2516: 2517: 2518: 2519: 2520: 2521: 2522: 2523: 2524: 2525: 2526: 2527: 2528: 2529: 2530: 2531: 2532: 2533: 2534: 2535: 2536: 2537: 2538: 2539: 2540: 2541: 2542: 2543: 2544: 2545: 2546: 2547: 2548: 2549: 2550: 2551: 2552: 2553: 2554: 2555: 2556: 2557: 2558: 2559: 2560: 2561: 2562: 2563: 2564: 2565: 2566: 2567: 2568: 2569: 2570: 2571: 2572: 2573: 2574: 2575: 2576: 2577: 2578: 2579: 2580: 2581: 2582: 2583: 2584: 2585: 2586: 2587: 2588: 2589: 2590: 2591: 2592: 2593: 2594: 2595: 2596: 2597: 2598: 2599: 2600: 2601: 2602: 2603: 2604: 2605: 2606: 2607: 2608: 2609: 2610: 2611: 2612: 2613: 2614: 2615: 2616: 2617: 2618: 2619: 2620: 2621: 2622: 2623: 2624: 2625: 2626: 2627: 2628: 2629: 2630: 2631: 2632: 2633: 2634: 2635: 2636: 2637: 2638: 2639: 2640: 2641: 2642: 2643: 2644: 2645: 2646: 2647: 2648: 2649: 2650: 2651: 2652: 2653: 2654: 2655: 2656: 2657: 2658: 2659: 2660: 2661: 2662: 2663: 2664: 2665: 2666: 2667: 2668: 2669: 2670: 2671: 2672: 2673: 2674: 2675: 2676: 2677: 2678: 2679: 2680: 2681: 2682: 2683: 2684: 2685: 2686: 2687: 2688: 2689: 2690: 2691: 2692: 2693: 2694: 2695: 2696: 2697: 2698: 2699: 2700: 2701: 2702: 2703: 2704: 2705: 2706: 2707: 2708: 2709: 2710: 2711: 2712: 2713: 2714: 2715: 2716: 2717: 2718: 2719: 2720: 2721: 2722: 2723: 2724: 2725: 2726: 2727: 2728: 2729: 2730: 2731: 2732: 2733: 2734: 2735: 2736: 2737: 2738: 2739: 2740: 2741: 2742: 2743: 2744: 2745: 2746: 2747: 2748: 2749: 2750: 2751: 2752: 2753: 2754: 2755: 2756: 2757: 2758: 2759: 2760: 2761: 2762: 2763: 2764: 2765: 2766: 2767: 2768: 2769: 2770: 2771: 2772: 2773: 2774: 2775: 2776: 2777: 2778: 2779: 2780: 2781: 2782: 2783: 2784: 2785: 2786: 2787: 2788: 2789: 2790: 2791: 2792: 2793: 2794: 2795: 2796: 2797: 2798: 2799: 2800: 2801: 2802: 2803: 2804: 2805: 2806: 2807: 2808: 2809: 2810: 2811: 2812: 2813: 2814: 2815: 2816: 2817: 2818: 2819: 2820: 2821: 2822: 2823: 2824: 2825: 2826: 2827: 2828: 2829: 2830: 2831: 2832: 2833: 2834: 2835: 2836: 2837: 2838: 2839: 2840: 2841: 2842: 2843: 2844: 2845: 2846: 2847: 2848: 2849: 2850: 2851: 2852: 2853: 2854: 2855: 2856: 2857: 2858: 2859: 2860: 2861: 2862: 2863: 2864: 2865: 2866: 2867: 2868: 2869: 2870: 
<?php
/**
 * FlexiPeeHP - Read Only Access to FlexiBee class.
 *
 * @author     Vítězslav Dvořák <vitex@arachne.cz>
 * @copyright  (C) 2015-2019 Spoje.Net
 */

namespace FlexiPeeHP;

/**
 * Základní třída pro čtení z FlexiBee
 *
 * @url https://demo.flexibee.eu/devdoc/
 */
class FlexiBeeRO extends \Ease\Sand
{
    /**
     * Where to get JSON files with evidence stricture etc.
     * @var string
     */
    public static $infoDir = __DIR__.'/../../static';

    /**
     * Version of FlexiPeeHP library
     *
     * @var string
     */
    public static $libVersion = '1.30';

    /**
     * Základní namespace pro komunikaci s FlexiBee.
     * Basic namespace for communication with FlexiBee
     *
     * @var string Jmený prostor datového bloku odpovědi
     */
    public $nameSpace = 'winstrom';

    /**
     * URL of object data in FlexiBee
     * @var string url
     */
    public $apiURL = null;

    /**
     * Datový blok v poli odpovědi.
     * Data block in response field.
     *
     * @var string
     */
    public $resultField = 'results';

    /**
     * Verze protokolu použitého pro komunikaci.
     * Communication protocol version used.
     *
     * @var string Verze použitého API
     */
    public $protoVersion = '1.0';

    /**
     * Evidence užitá objektem.
     * Evidence used by object
     *
     * @link https://demo.flexibee.eu/c/demo/evidence-list Přehled evidencí
     * @var string
     */
    public $evidence = null;

    /**
     * Detaily evidence užité objektem
     * 
     * @var array 
     */
    public $evidenceInfo = [];

    /**
     * Výchozí formát pro komunikaci.
     * Default communication format.
     *
     * @link https://www.flexibee.eu/api/dokumentace/ref/format-types Přehled možných formátů
     *
     * @var string json|xml|...
     */
    public $format = 'json';

    /**
     * formát příchozí odpovědi
     * response format
     *
     * @link https://www.flexibee.eu/api/dokumentace/ref/format-types Přehled možných formátů
     *
     * @var string json|xml|...
     */
    public $responseFormat = 'json';

    /**
     * Curl Handle.
     *
     * @var resource
     */
    public $curl = null;

    /**
     * @link https://demo.flexibee.eu/devdoc/company-identifier Identifikátor firmy
     * @var string
     */
    public $company = null;

    /**
     * Server[:port]
     * @var string
     */
    public $url = null;

    /**
     * REST API Username
     * @var string
     */
    public $user = null;

    /**
     * REST API Password
     * @var string
     */
    public $password = null;

    /**
     * @var array Pole HTTP hlaviček odesílaných s každým požadavkem
     */
    public $defaultHttpHeaders = ['User-Agent' => 'FlexiPeeHP'];

    /**
     * Default additional request url parameters after question mark
     *
     * @link https://www.flexibee.eu/api/dokumentace/ref/urls   Common params
     * @link https://www.flexibee.eu/api/dokumentace/ref/paging Paging params
     * @var array
     */
    public $defaultUrlParams = ['limit' => 0];

    /**
     * Identifikační řetězec.
     *
     * @var string
     */
    public $init = null;

    /**
     * Sloupeček s názvem.
     *
     * @var string
     */
    public $nameColumn = 'nazev';

    /**
     * Sloupeček obsahující datum vložení záznamu do shopu.
     *
     * @var string
     */
    public $myCreateColumn = 'false';

    /**
     * Slopecek obsahujici datum poslení modifikace záznamu do shopu.
     *
     * @var string
     */
    public $myLastModifiedColumn = 'lastUpdate';

    /**
     * Klíčový idendifikátor záznamu.
     *
     * @var string
     */
    public $fbKeyColumn = 'id';

    /**
     * Informace o posledním HTTP requestu.
     *
     * @var *
     */
    public $curlInfo;

    /**
     * Informace o poslední HTTP chybě.
     *
     * @var string
     */
    public $lastCurlError = null;

    /**
     * Used codes storage.
     *
     * @var array
     */
    public $codes = null;

    /**
     * Last Inserted ID.
     *
     * @var int
     */
    public $lastInsertedID = null;

    /**
     * Default Line Prefix.
     *
     * @var string
     */
    public $prefix = '/c/';

    /**
     * Raw Content of last curl response
     *
     * @var string
     */
    public $lastCurlResponse;

    /**
     * HTTP Response code of last request
     *
     * @var int
     */
    public $lastResponseCode = null;

    /**
     * Body data  for next curl POST operation
     *
     * @var string
     */
    protected $postFields = null;

    /**
     * Last operation result data or message(s)
     *
     * @var array
     */
    public $lastResult = null;

    /**
     * Number from  @rowCount in response
     * @var int
     */
    public $rowCount = null;

    /**
     * Number from  @globalVersion
     * @var int
     */
    public $globalVersion = null;

    /**
     * @link https://www.flexibee.eu/api/dokumentace/ref/zamykani-odemykani/
     * @var string filter query
     */
    public $filter;

    /**
     * @link https://demo.flexibee.eu/devdoc/actions Provádění akcí
     * @var string
     */
    protected $action;

    /**
     * Pole akcí které podporuje ta která evidence
     * @link https://demo.flexibee.eu/c/demo/faktura-vydana/actions.json Např. Akce faktury
     * @var array
     */
    public $actionsAvailable = null;

    /**
     * Parmetry pro URL
     * @link https://www.flexibee.eu/api/dokumentace/ref/urls/ Všechny podporované parametry
     * @var array
     */
    public $urlParams = [
        'add-global-version',
        'add-row-count',
        'as-gui',
        'auth',
        'authSessionId',
        'code-as-id',
        'code-in-response',
        'delimeter',
        'detail', //See: https://www.flexibee.eu/api/dokumentace/ref/detail-levels
        'dir',
        'dry-run', // See: https://www.flexibee.eu/api/dokumentace/ref/dry-run/
        'encoding',
        'export-settings',
        'fail-on-warning',
        'filter',
        'format',
        'idUcetniObdobi',
        'includes',
        'inDesktopApp', // Note: Undocumented function (html only)
        'limit',
        'mode',
        'no-ext-ids',
        'no-http-errors',
        'no-ids',
        'only-ext-ids',
        'order',
        'relations',
        'report-lang',
        'report-name',
        'report-sign',
        'skupina-stitku',
        'sort',
        'start',
        'stitky-as-ids',
        'use-ext-id',
        'use-internal-id',
        'xpath', // See: https://www.flexibee.eu/api/dokumentace/ref/xpath/
    ];

    /**
     * Session ID
     * @var string
     */
    public $authSessionId = null;

    /**
     * Token obtained during login procedure
     * @var string 
     */
    public $refreshToken = null;

    /**
     * Save 404 results to log ?
     * @var boolean
     */
    protected $ignoreNotFound = false;

    /**
     * Array of errors caused by last request
     * @var array
     */
    private $errors = [];

    /**
     * List of Error500 reports sent
     * @var array
     */
    private $reports = [];

    /**
     * Send Error500 Report to
     * @var string email address
     */
    public $reportRecipient = 'podpora@flexibee.eu';

    /**
     * Formating string for \DateTime::format() for datetime columns
     * @var string
     */
    static public $DateTimeFormat = 'Y-m-d\TH:i:s.u+P';

    /**
     * Formating string for \DateTime::format() for date columns
     * @var string
     */
    static public $DateFormat = 'Y-m-d';

    /**
     * Last Request response stats
     * @var array 
     */
    protected $responseStats = null;

    /**
     * Chained Objects
     * @var array
     */
    public $chained = [];

    /**
     * We Connect to server by default
     * @var boolean
     */
    public $offline = false;

    /**
     * Override cURL timeout
     * @var int seconds
     */
    public $timeout = null;

    /**
     * Columns Info for serveral evidencies
     * @var array 
     */
    private $columnsInfo = [];

    /**
     * Class for read only interaction with FlexiBee.
     *
     * @param mixed $init default record id or initial data
     * @param array $options Connection settings and other options override
     */
    public function __construct($init = null, $options = [])
    {
        $this->init = $init;

        parent::__construct();
        $this->setUp($options);
        $this->curlInit();
        if (!empty($init)) {
            $this->processInit($init);
        }
    }

    /**
     * Set internal Object name
     *
     * @param string $objectName
     *
     * @return string Jméno objektu
     */
    public function setObjectName($objectName = null)
    {
        return parent::setObjectName(is_null($objectName) ? ( empty($this->getRecordIdent())
                    ? $this->getObjectName() : $this->getRecordIdent().'@'.$this->getObjectName() )
                    : $objectName);
    }

    /**
     * SetUp Object to be ready for work
     *
     * @param array $options Object Options ( user,password,authSessionId
     *                                        company,url,evidence,
     *                                        prefix,defaultUrlParams,debug,
     *                                        detail,offline,filter,ignore404
     *                                        timeout,companyUrl,ver
     */
    public function setUp($options = [])
    {
        if (array_key_exists('ver', $options)) {
            $this->protoVersion = $options['ver'];
            $this->prefix       = 'v'.round($this->protoVersion).'/c/';
        }

        if (array_key_exists('companyUrl', $options)) {
            $options = array_merge(self::companyUrlToOptions($options['companyUrl']),
                $options);
        }

        $this->setupProperty($options, 'company', 'FLEXIBEE_COMPANY');
        $this->setupProperty($options, 'url', 'FLEXIBEE_URL');
        $this->setupProperty($options, 'user', 'FLEXIBEE_LOGIN');
        $this->setupProperty($options, 'password', 'FLEXIBEE_PASSWORD');
        $this->setupProperty($options, 'authSessionId', 'FLEXIBEE_AUTHSESSID');
        $this->setupProperty($options, 'timeout', 'FLEXIBEE_TIMEOUT');
        if (!empty($this->authSessionId)) {
            $this->defaultHttpHeaders['X-authSessionId'] = $this->authSessionId;
        }
        if (isset($options['evidence'])) {
            $this->setEvidence($options['evidence']);
        }
        $this->setupProperty($options, 'defaultUrlParams');
        if (isset($options['prefix'])) {
            $this->setPrefix($options['prefix']);
        }
        if (array_key_exists('detail', $options)) {
            $this->defaultUrlParams['detail'] = $options['detail'];
        }
        $this->setupProperty($options, 'filter');
        if (array_key_exists('offline', $options)) {
            $this->offline = (boolean) $options['offline'];
        }

        if (array_key_exists('ignore404', $options)) {
            $this->ignore404($options['ignore404']);
        }

        $this->setupProperty($options, 'debug');
        $this->updateApiURL();
    }

    /**
     * Set up one of properties
     *
     * @param array  $options  array of given properties
     * @param string $name     name of property to process
     * @param string $constant load default property value from constant
     */
    public function setupProperty($options, $name, $constant = null)
    {
        if (array_key_exists($name, $options)) {
            $this->$name = $options[$name];
        } elseif (array_key_exists($constant, $options)) {
            $this->$name = $options[$constant];
        } else {
            if (property_exists($this, $name) && !empty($constant) && defined($constant)) {
                $this->$name = constant($constant);
            }
        }
    }

    /**
     * Convert companyUrl provided by CustomButton to options array
     * 
     * @param string $companyUrl
     * 
     * @return array Options
     */
    public static function companyUrlToOptions($companyUrl)
    {
        $urlParts = parse_url($companyUrl);
        $scheme   = isset($urlParts['scheme']) ? $urlParts['scheme'].'://' : '';
        $host     = isset($urlParts['host']) ? $urlParts['host'] : '';
        $port     = isset($urlParts['port']) ? ':'.$urlParts['port'] : '';
        $path     = isset($urlParts['path']) ? $urlParts['path'] : '';

        $options['company'] = basename($path);
        $options['url']     = $scheme.$host.$port;
        return $options;
    }

    /**
     * Get Current connection options for use in another object
     *
     * @return array usable as second constructor parameter
     */
    public function getConnectionOptions()
    {
        $conOpts = ['url' => $this->url];
        if (empty($this->authSessionId)) {
            $conOpts ['user']    = $this->user;
            $conOpts['password'] = $this->password;
        } else {
            $conOpts['authSessionId'] = $this->authSessionId;
        }
        $company = $this->getCompany();
        if (!empty($company)) {
            $conOpts['company'] = $company;
        }
        if (!is_null($this->timeout)) {
            $conOpts['timeout'] = $this->timeout;
        }
        return $conOpts;
    }

    /**
     * Inicializace CURL
     *
     * @return boolean Online Status
     */
    public function curlInit()
    {
        if ($this->offline === false) {
            $this->curl = \curl_init(); // create curl resource
            curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true); // return content as a string from curl_exec
            curl_setopt($this->curl, CURLOPT_FOLLOWLOCATION, true); // follow redirects (compatibility for future changes in FlexiBee)
            curl_setopt($this->curl, CURLOPT_HTTPAUTH, true);       // HTTP authentication
            curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, false); // FlexiBee by default uses Self-Signed certificates
            curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($this->curl, CURLOPT_VERBOSE, ($this->debug === true)); // For debugging
            if (empty($this->authSessionId)) {
                curl_setopt($this->curl, CURLOPT_USERPWD,
                    $this->user.':'.$this->password); // set username and password
            }
            if (!is_null($this->timeout)) {
                curl_setopt($this->curl, CURLOPT_TIMEOUT, $this->timeout);
            }
        }
        return !$this->offline;
    }

    /**
     * Zinicializuje objekt dle daných dat. Možné hodnoty:
     *
     *  * 234                              - interní číslo záznamu k načtení
     *  * code:LOPATA                      - kód záznamu
     *  * BAGR                             - kód záznamu k načtení
     *  * ['id'=>24,'nazev'=>'hoblík']     - pole hodnot k předvyplnění
     *  * 743.json?relations=adresa,vazby  - část url s parametry k načtení
     *
     * @param mixed $init číslo/"(code:)kód"/(část)URI záznamu k načtení | pole hodnot k předvyplnění
     */
    public function processInit($init)
    {
        if (is_integer($init)) {
            $this->loadFromFlexiBee($init);
        } elseif (is_array($init)) {
            $this->takeData($init);
        } elseif (preg_match('/\.(json|xml|csv)/', $init)) {
            $this->takeData($this->getFlexiData((($init[0] != '/') ? $this->evidenceUrlWithSuffix($init)
                            : $init)));
        } else {
            $this->loadFromFlexiBee($init);
        }
    }

    /**
     * Set Data Field value
     *
     * @param string $columnName field name
     * @param mixed  $value      field data value
     *
     * @return bool Success
     */
    public function setDataValue($columnName, $value)
    {
        switch ($columnName) {
            case 'kod':
                $value = self::uncode($value); //Alwyas uncode "kod" column

            default:
                if (is_object($value)) {
                    switch (get_class($value)) {
                        case 'DateTime':
                            $columnInfo = $this->getColumnInfo($columnName);
                            switch ($columnInfo['type']) {
                                case 'date':
                                    $value = self::dateToFlexiDate($value);
                                    break;
                                case 'datetime':
                                    $value = self::dateToFlexiDateTime($value);
                                    break;
                            }
                            break;
                    }
                }
                $result = parent::setDataValue($columnName, $value);
                break;
        }
        return $result;
    }

    /**
     * PHP Date object to FlexiBee date format
     * 
     * @param \DateTime $date
     */
    public static function dateToFlexiDate($date)
    {
        return $date->format(self::$DateFormat);
    }

    /**
     * PHP Date object to FlexiBee date format
     * 
     * @param \DateTime $dateTime
     */
    public static function dateToFlexiDateTime($dateTime)
    {
        return $dateTime->format(self::$DateTimeFormat);
    }

    /**
     * Set URL prefix
     *
     * @param string $prefix
     */
    public function setPrefix($prefix)
    {
        switch ($prefix) {
            case 'a': //Access
            case 'c': //Company
            case 'u': //User
            case 'g': //License Groups
            case 'admin':
            case 'status':
            case 'login-logout':
                $this->prefix = '/'.$prefix.'/';
                break;
            case null:
            case '':
            case '/':
                $this->prefix = '';
                break;
            default:
                throw new \Exception(sprintf('Unknown prefix %s', $prefix));
        }
    }

    /**
     * Set communication format.
     * One of html|xml|json|csv|dbf|xls|isdoc|isdocx|edi|pdf|pdf|vcf|ical
     *
     * @param string $format
     * 
     * @return boolean format is availble
     */
    public function setFormat($format)
    {
        $result = true;
        if (($this->debug === true) && !empty($this->evidence) && isset(Formats::$$this->evidence)) {
            if (array_key_exists($format, array_flip(Formats::$$this->evidence))
                === false) {
                $result = false;
            }
        }
        if ($result === true) {
            $this->format = $format;
            $this->updateApiURL();
        }
        return $result;
    }

    /**
     * Nastaví Evidenci pro Komunikaci.
     * Set evidence for communication
     *
     * @param string $evidence evidence pathName to use
     * 
     * @return boolean evidence switching status
     */
    public function setEvidence($evidence)
    {
        switch ($this->prefix) {
            case '/c/':
                if ($this->debug === true) {
                    if (array_key_exists($evidence, EvidenceList::$name)) {
                        $this->evidence = $evidence;
                        $result         = true;
                    } else {
                        throw new \Exception(sprintf('Try to set unsupported evidence %s',
                                $evidence));
                    }
                } else {
                    $this->evidence = $evidence;
                    $result         = true;
                }
                break;
            default:
                $this->evidence = $evidence;
                $result         = true;
                break;
        }
        $this->updateApiURL();
        $this->evidenceInfo = $this->getEvidenceInfo();
        return $result;
    }

    /**
     * Vrací právě používanou evidenci pro komunikaci
     * Obtain current used evidence
     *
     * @return string
     */
    public function getEvidence()
    {
        return $this->evidence;
    }

    /**
     * Set used company.
     * Nastaví Firmu.
     *
     * @param string $company
     */
    public function setCompany($company)
    {
        $this->company = $company;
    }

    /**
     * Obtain company now used
     * Vrací právě používanou firmu
     *
     * @return string
     */
    public function getCompany()
    {
        return $this->company;
    }

    /**
     * Vrací název evidence použité v odpovědích z FlexiBee
     *
     * @return string
     */
    public function getResponseEvidence()
    {
        switch ($this->evidence) {
            case 'c':
                $evidence = 'company';
                break;
            case 'evidence-list':
                $evidence = 'evidence';
                break;
            default:
                $evidence = $this->getEvidence();
                break;
        }
        return $evidence;
    }

    /**
     * Převede rekurzivně Objekt na pole.
     *
     * @param object|array $object
     *
     * @return array
     */
    public static function object2array($object)
    {
        $result = null;
        if (is_object($object)) {
            $objectData = get_object_vars($object);
            if (is_array($objectData) && count($objectData)) {
                $result = array_map('self::object2array', $objectData);
            }
        } else {
            if (is_array($object)) {
                foreach ($object as $item => $value) {
                    $result[$item] = self::object2array($value);
                }
            } else {
                $result = $object;
            }
        }

        return $result;
    }

    /**
     * Převede rekurzivně v poli všechny objekty na jejich identifikátory.
     *
     * @param object|array $object
     *
     * @return array
     */
    public static function objectToID($object)
    {
        $resultID = null;
        if (is_object($object) && method_exists($object, '__toString')
        ) {
            $resultID = $object->__toString();
        } else {
            if (is_array($object)) {
                foreach ($object as $item => $value) {
                    $resultID[$item] = self::objectToID($value);
                }
            } else { //String
                $resultID = $object;
            }
        }

        return $resultID;
    }

    /**
     * Return basic URL for used Evidence
     *
     * @link https://www.flexibee.eu/api/dokumentace/ref/urls/ Sestavování URL
     *
     * @return string Evidence URL
     */
    public function getEvidenceURL()
    {
        $evidenceUrl = $this->url.$this->prefix.$this->company;
        $evidence    = $this->getEvidence();
        if (!empty($evidence)) {
            $evidenceUrl .= '/'.$evidence;
        }
        return $evidenceUrl;
    }

    /**
     * Add suffix to Evidence URL
     *
     * @param string $urlSuffix
     *
     * @return string
     */
    public function evidenceUrlWithSuffix($urlSuffix)
    {
        $evidenceUrl = $this->getEvidenceUrl();
        if (!empty($urlSuffix)) {
            if (($urlSuffix[0] != '/') && ($urlSuffix[0] != ';') && ($urlSuffix[0]
                != '?')) {
                $evidenceUrl .= '/';
            }
            $evidenceUrl .= $urlSuffix;
        }
        return $evidenceUrl;
    }

    /**
     * Update $this->apiURL
     */
    public function updateApiURL()
    {
        $this->apiURL  = $this->getEvidenceURL();
        $rowIdentifier = $this->getRecordID();
        if (empty($rowIdentifier)) {
            $rowIdentifier = $this->getRecordCode();
            if (empty($rowIdentifier)) {
                $rowIdentifier = $this->getExternalID();
            }
        }
        if (!empty($rowIdentifier)) {
            $this->apiURL .= '/'.self::urlEncode($rowIdentifier);
        }
        $this->apiURL .= '.'.$this->format;
    }
    /*
     * Add Default Url params to given url if not overrided
     *
     * @param string $urlRaw
     *
     * @return string url with default params added
     */

    public function addDefaultUrlParams($urlRaw)
    {
        return \Ease\Shared::addUrlParams($urlRaw, $this->defaultUrlParams,
                false);
    }

    /**
     * Funkce, která provede I/O operaci a vyhodnotí výsledek.
     *
     * @param string $urlSuffix část URL za identifikátorem firmy.
     * @param string $method    HTTP/REST metoda
     * @param string $format    Requested format
     * 
     * @return array|boolean Výsledek operace
     */
    public function performRequest($urlSuffix = null, $method = 'GET',
                                   $format = null)
    {
        $this->rowCount      = null;
        $this->responseStats = [];

        if (preg_match('/^http/', $urlSuffix)) {
            $url = $urlSuffix;
        } elseif (strlen($urlSuffix) && ($urlSuffix[0] == '/')) {
            $url = $this->url.$urlSuffix;
        } else {
            $url = $this->evidenceUrlWithSuffix($urlSuffix);
        }

        $responseCode = $this->doCurlRequest($this->addDefaultUrlParams($url),
            $method, $format);

        return $this->parseResponse($this->rawResponseToArray($this->lastCurlResponse,
                    $this->responseFormat), $responseCode);
    }

    /**
     * Parse Raw FlexiBee response in several formats
     *
     * @param string $responseRaw raw response body
     * @param string $format      Raw Response format json|xml|etc
     *
     * @return array
     */
    public function rawResponseToArray($responseRaw, $format)
    {
        $responseDecoded = [];
        if (!empty(trim($responseRaw))) {
            switch ($format) {
                case 'json':
                    $responseDecoded = $this->rawJsonToArray($responseRaw);
                    break;
                case 'xml':
                    $responseDecoded = $this->rawXmlToArray($this->lastCurlResponse);
                    break;
                case 'txt':
                default:
                    $responseDecoded = [$this->lastCurlResponse];
                    break;
            }
        }
        return $responseDecoded;
    }

    /**
     * Convert FlexiBee Response JSON to Array
     *
     * @param string $rawJson
     *
     * @return array
     */
    public function rawJsonToArray($rawJson)
    {
        $responseDecoded = json_decode($rawJson, true, 10);
        $decodeError     = json_last_error_msg();
        if ($decodeError == 'No error') {
            if (array_key_exists($this->nameSpace, $responseDecoded)) {
                $responseDecoded = $responseDecoded[$this->nameSpace];
            }
        } else {
            if ($this->debug) {
                $this->addStatusMessage('JSON Decoder: '.$decodeError, 'error');
                $this->addStatusMessage($rawJson, 'debug');
            }
        }
        return $responseDecoded;
    }

    /**
     * Convert FlexiBee Response XML to Array
     *
     * @param string $rawXML
     *
     * @return array
     */
    public function rawXmlToArray($rawXML)
    {
        return self::xml2array($rawXML);
    }

    /**
     * Parse Response array
     *
     * @param array $responseDecoded
     * @param int $responseCode Request Response Code
     *
     * @return array main data part of response
     */
    public function parseResponse($responseDecoded, $responseCode)
    {
        $mainResult = null;
        switch ($responseCode) {
            case 201: //Success Write
                break;
            case 200: //Success Read

                if (is_array($responseDecoded)) {
                    if (isset($responseDecoded['@rowCount'])) {
                        $this->rowCount = (int) $responseDecoded['@rowCount'];
                    }
                    if (isset($responseDecoded['@globalVersion'])) {
                        $this->globalVersion = (int) $responseDecoded['@globalVersion'];
                    }

                    $mainResult = $this->unifyResponseFormat($responseDecoded);

                    if (array_key_exists('stats', $responseDecoded)) {
                        $this->responseStats = $responseDecoded['stats'];
                    } elseif (!empty($mainResult)) {
                        if (array_key_exists('success', $mainResult) && ($mainResult['success']
                            == 'false')) {
                            $this->responseStats = ['read' => 0];
                        } elseif (array_key_exists('properties', $mainResult)) {
                            $this->responseStats = ['read' => 1];
                        } else {
                            $responseEvidence = $this->getResponseEvidence();
                            if (!empty($this->rowCount)) {
                                $this->responseStats = ['read' => $this->rowCount];
                            } elseif (array_key_exists($responseEvidence,
                                    $mainResult)) {
                                $this->responseStats = ['read' => count($mainResult[$responseEvidence])];
                            } else {
                                $this->responseStats = ['read' => count($mainResult)];
                            }
                        }
                    }
                } else {
                    $mainResult = $responseDecoded;
                }

                $this->lastResult = $mainResult;
                break;

            case 500: // Internal Server Error
                if ($this->debug === true) {
                    $this->error500Reporter($responseDecoded);
                }
            case 404: // Page not found
                if ($this->ignoreNotFound === true) {
                    break;
                }
            case 400: //Bad Request parameters
            default: //Something goes wrong
                $this->addStatusMessage($this->lastResponseCode.': '.$this->curlInfo['url'],
                    'warning');
                if (is_array($responseDecoded)) {
                    $this->parseError($responseDecoded);
                }
                $this->logResult($responseDecoded, $this->curlInfo['url']);
                break;
        }
        return $mainResult;
    }

    /**
     * Parse error message response
     *
     * @param array $responseDecoded
     * 
     * @return int number of errors processed
     */
    public function parseError(array $responseDecoded)
    {
        if (array_key_exists('results', $responseDecoded)) {
            $this->errors = $responseDecoded['results'][0]['errors'];
            foreach ($this->errors as $errorInfo) {
                $this->addStatusMessage($errorInfo['message'], 'error');
                if (array_key_exists('for', $errorInfo)) {
                    unset($errorInfo['message']);
                    $this->addStatusMessage(json_encode($errorInfo), 'debug');
                }
            }
        } else {
            if (array_key_exists('message', $responseDecoded)) {
                $this->errors = [['message' => $responseDecoded['message']]];
            }
        }
        return count($this->errors);
    }

    /**
     * Vykonej HTTP požadavek
     *
     * @link https://www.flexibee.eu/api/dokumentace/ref/urls/ Sestavování URL
     * @param string $url    URL požadavku
     * @param string $method HTTP Method GET|POST|PUT|OPTIONS|DELETE
     * @param string $format požadovaný formát komunikace
     * 
     * @return int HTTP Response CODE
     */
    public function doCurlRequest($url, $method, $format = null)
    {
        if (is_null($format)) {
            $format = $this->format;
        }
        curl_setopt($this->curl, CURLOPT_URL, $url);
// Nastavení samotné operace
        curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, strtoupper($method));
//Vždy nastavíme byť i prázná postdata jako ochranu před chybou 411
        curl_setopt($this->curl, CURLOPT_POSTFIELDS, $this->postFields);

        $httpHeaders = $this->defaultHttpHeaders;

        $formats = Formats::bySuffix();

        if (!isset($httpHeaders['Accept'])) {
            $httpHeaders['Accept'] = $formats[$format]['content-type'];
        }
        if (!isset($httpHeaders['Content-Type'])) {
            $httpHeaders['Content-Type'] = $formats[$format]['content-type'];
        }
        $httpHeadersFinal = [];
        foreach ($httpHeaders as $key => $value) {
            if (($key == 'User-Agent') && ($value == 'FlexiPeeHP')) {
                $value .= ' v'.self::$libVersion;
            }
            $httpHeadersFinal[] = $key.': '.$value;
        }

        curl_setopt($this->curl, CURLOPT_HTTPHEADER, $httpHeadersFinal);

// Proveď samotnou operaci
        $this->lastCurlResponse            = curl_exec($this->curl);
        $this->curlInfo                    = curl_getinfo($this->curl);
        $this->curlInfo['when']            = microtime();
        $this->curlInfo['request_headers'] = $httpHeadersFinal;
        $this->responseFormat              = $this->contentTypeToResponseFormat($this->curlInfo['content_type'],
            $url);
        $this->lastResponseCode            = $this->curlInfo['http_code'];
        $this->lastCurlError               = curl_error($this->curl);
        if (strlen($this->lastCurlError)) {
            $this->addStatusMessage(sprintf('Curl Error (HTTP %d): %s',
                    $this->lastResponseCode, $this->lastCurlError), 'error');
        }

        if ($this->debug === true) {
            $this->saveDebugFiles();
        }

        return $this->lastResponseCode;
    }

    /**
     * Obtain json for application/json
     * 
     * @param string $contentType
     * @param string $url
     * 
     * @return string response format
     */
    public function contentTypeToResponseFormat($contentType, $url = null)
    {
        if (!empty($url)) {
            $url = parse_url($url, PHP_URL_PATH);
        }

        $contentTypeClean = strstr($contentType, ';') ? substr($contentType, 0,
                strpos($contentType, ';')) : $contentType;

        switch ($url) {
            case '/login-logout/login';
                $responseFormat = 'json';
                break;
            default :
                switch ($contentTypeClean) {
                    case 'text/javascript':
                        $responseFormat = 'js';
                        break;

                    default:
                        $responseFormat = Formats::contentTypeToSuffix($contentTypeClean);
                        break;
                }
                break;
        }

        return $responseFormat;
    }

    /**
     * Nastaví druh prováděné akce.
     *
     * @link https://demo.flexibee.eu/devdoc/actions Provádění akcí
     * @param string $action
     * 
     * @return boolean
     */
    public function setAction($action)
    {
        $result           = false;
        $actionsAvailable = $this->getActionsInfo();
        if (is_array($actionsAvailable) && array_key_exists($action,
                $actionsAvailable)) {
            $this->action = $action;
            $result       = true;
        }
        return $result;
    }

    /**
     * Convert XML to array.
     *
     * @param string $xml
     *
     * @return array
     */
    public static function xml2array($xml)
    {
        $arr = [];
        if (!empty($xml)) {
            if (is_string($xml)) {
                $xml = simplexml_load_string($xml);
            }
            foreach ($xml->attributes() as $a) {
                $arr['@'.$a->getName()] = strval($a);
            }
            foreach ($xml->children() as $r) {
                if (count($r->children()) == 0) {
                    $arr[$r->getName()] = strval($r);
                } else {
                    $arr[$r->getName()][] = self::xml2array($r);
                }
            }
        }
        return $arr;
    }

    /**
     * Odpojení od FlexiBee.
     */
    public function disconnect()
    {
        if (is_resource($this->curl)) {
            curl_close($this->curl);
        }
        $this->curl = null;
    }

    /**
     * Disconnect CURL befere pass away
     */
    public function __destruct()
    {
        $this->disconnect();
    }

    /**
     * Načte řádek dat z FlexiBee.
     *
     * @param int $recordID id požadovaného záznamu
     *
     * @return array
     */
    public function getFlexiRow($recordID)
    {
        $record   = null;
        $response = $this->performRequest($this->evidence.'/'.$recordID.'.json');
        if (isset($response[$this->evidence])) {
            $record = $response[$this->evidence][0];
        }

        return $record;
    }

    /**
     * Oddělí z pole podmínek ty jenž patří za ? v URL požadavku
     *
     * @link https://www.flexibee.eu/api/dokumentace/ref/urls/ Sestavování URL
     * @param array $conditions pole podmínek   - rendrují se do ()
     * @param array $urlParams  pole parametrů  - rendrují za ?
     */
    public function extractUrlParams(&$conditions, &$urlParams)
    {
        foreach ($this->urlParams as $urlParam) {
            if (isset($conditions[$urlParam])) {
                \Ease\Sand::divDataArray($conditions, $urlParams, $urlParam);
            }
        }
    }

    /**
     * convert unicode to entities for use with FlexiBee queries
     *
     * @param string $urlRaw
     * 
     * @return string
     */
    public static function urlEncode($urlRaw)
    {
        return str_replace(['%27', '%3A'], ["'", ':'], rawurlencode($urlRaw));
    }

    /**
     * Načte data z FlexiBee.
     *
     * @param string $suffix     dotaz
     * @param string|array       $conditions Custom filters or modifiers
     *
     * @return array Data obtained
     */
    public function getFlexiData($suffix = null, $conditions = null)
    {
        $finalUrl          = '';
        $evidenceToRestore = null;
        $urlParams         = $this->defaultUrlParams;

        if (!empty($conditions)) {
            if (is_array($conditions)) {
                $this->extractUrlParams($conditions, $urlParams);
                if (array_key_exists('evidence', $conditions) && is_null($this->getColumnInfo('evidence'))) {
                    $evidenceToRestore = $this->getEvidence();
                    $this->setEvidence($conditions['evidence']);
                    unset($conditions['evidence']);
                }
                $conditions = $this->flexiUrl($conditions);
            }

            if (strlen($conditions) && ($conditions[0] != '/')) {
                $conditions = '('.self::urlEncode($conditions).')';
            }
        }

        if (strlen($suffix) && ($suffix != '$sum')) {
            if (preg_match('/^http/', $suffix) || ($suffix[0] == '/') || is_numeric($suffix)) {
                $finalUrl = $suffix;
            } else {
                if (preg_match('/^(code|ext):(.*)/', $suffix)) {
                    $finalUrl = self::urlizeId($suffix);
                } else {
                    $finalUrl = $suffix;
                }
            }
        }

        $finalUrl .= $conditions;

        if ($suffix == '$sum') {
            $finalUrl .= '/$sum';
        }

        if (!empty($urlParams)) {
            if (strstr($finalUrl, '?')) {
                $finalUrl .= '&';
            } else {
                $finalUrl .= '?';
            }

            $finalUrl .= http_build_query(array_map(function($a) {
                    return is_bool($a) ? ($a ? 'true' : 'false' ) : $a;
                }, $urlParams), null, '&', PHP_QUERY_RFC3986);
        }

        $transactions     = $this->performRequest($finalUrl, 'GET');
        $responseEvidence = $this->getResponseEvidence();
        if (is_array($transactions) && array_key_exists($responseEvidence,
                $transactions)) {
            $result = $transactions[$responseEvidence];
            if ((count($result) == 1) && empty(current($result))) {
                $result = []; // Response is empty Array
            }
        } else {
            $result = $transactions;
        }
        if (!is_null($evidenceToRestore)) {
            $this->setEvidence($evidenceToRestore);
        }
        return $result;
    }

    /**
     * Načte záznam z FlexiBee a uloží v sobě jeho data
     * Read FlexiBee record and store it inside od object
     *
     * @param int|string $id ID or conditions
     *
     * @return int počet načtených položek
     */
    public function loadFromFlexiBee($id = null)
    {
        $data = [];
        if (is_null($id)) {
            $id = $this->getMyKey();
        }
        $flexidata = $this->getFlexiData($this->getEvidenceUrl().'/'.self::urlizeId($id));
        if ($this->lastResponseCode == 200) {
            $this->apiURL = $this->curlInfo['url'];
            if (is_array($flexidata) && (count($flexidata) == 1) && is_array(current($flexidata))) {
                $data = current($flexidata);
            }
        }
        return $this->takeData($data);
    }

    /**
     * Reload current record from FlexiBee
     * 
     * @return boolean 
     */
    public function reload()
    {
        $id = $this->getRecordIdent();
        $this->dataReset();
        $this->loadFromFlexiBee($id);
        return $this->lastResponseCode == 200;
    }

    /**
     * Set Filter code for requests
     *
     * @link https://www.flexibee.eu/api/dokumentace/ref/zamykani-odemykani/
     *
     * @param array|string $filter filter formula or ['key'=>'value']
     *
     * @return string Filter code
     */
    public function setFilter($filter)
    {
        return $this->filter = is_array($filter) ? self::flexiUrl($filter) : $filter;
    }

    /**
     * Převede data do Json formátu pro FlexiBee.
     * Convert data to FlexiBee like Json format
     *
     * @url https://www.flexibee.eu/api/dokumentace/ref/actions/
     * @url https://www.flexibee.eu/api/dokumentace/ref/zamykani-odemykani/
     *
     * @param array $data    object data
     * @param int   $options json_encode options like JSON_PRETTY_PRINT etc
     *
     * @return string
     */
    public function getJsonizedData($data = null, $options = 0)
    {
        if (is_null($data)) {
            $data = $this->getData();
        }

        $dataToJsonize = array_merge(['@version' => $this->protoVersion],
            $this->getDataForJSON($data));
        $jsonRaw       = json_encode([$this->nameSpace => $dataToJsonize],
            $options);

        return $jsonRaw;
    }

    /**
     * Get Data Fragment specific for current object
     *
     * @param array $data
     *
     * @return array
     */
    public function getDataForJSON($data = null)
    {
        if (is_null($data)) {
            $data = $this->getData();
        }

        $dataForJson = [$this->getEvidence() => $this->objectToID($data)];

        if (!is_null($this->action)) {
            $dataForJson[$this->evidence.'@action'] = $this->action;
            $this->action                           = null;
        }

        if (!is_null($this->filter)) {
            $dataForJson[$this->evidence.'@filter'] = $this->filter;
        }


        foreach ($this->chained as $chained) {
            $chainedData = $chained->getDataForJSON();
            foreach ($chainedData as $chainedItemEvidence => $chainedItemData) {
                if (array_key_exists($chainedItemEvidence, $dataForJson)) {
                    if (is_string(key($dataForJson[$chainedItemEvidence]))) {
                        $dataBackup                          = $dataForJson[$chainedItemEvidence];
                        $dataForJson[$chainedItemEvidence]   = [];
                        $dataForJson[$chainedItemEvidence][] = $dataBackup;
                    }
                    if (array_key_exists(0, $chainedItemData)) {
                        foreach ($chainedItemData as $chainedItem) {
                            $dataForJson[$chainedItemEvidence][] = $chainedItem;
                        }
                    } else {
                        $dataForJson[$chainedItemEvidence][] = $chainedItemData;
                    }
                } else {
                    $dataForJson[$chainedItemEvidence] = $chainedItemData;
                }
            }
        }


        return $dataForJson;
    }

    /**
     * Join another FlexiPeeHP Object
     *
     * @param FlexiBeeRO $object
     *
     * @return boolean adding to stack success
     */
    public function join(&$object)
    {
        $result = true;
        if (method_exists($object, 'getDataForJSON')) {
            $this->chained[] = $object;
        } else {
            throw new \Ease\Exception('$object->getDataForJSON() does not exist');
        }

        return $result;
    }

    /**
     * Prepare record ID to use in URL
     * 
     * @param mixed $id
     * 
     * @return string id ready for use in URL
     */
    public static function urlizeId($id)
    {
        if (is_array($id)) {
            $id = rawurlencode('('.self::flexiUrl($id).')');
        } else if (preg_match('/^ext:/', $id)) {
            $id = self::urlEncode($id);
        } else if (preg_match('/^code:/', $id)) {
            $id = self::code(self::urlEncode(self::uncode($id)));
        }
        return $id;
    }

    /**
     * Test if given record ID exists in FlexiBee.
     *
     * @param mixed $identifer presence state
     *
     * @return boolean
     */
    public function idExists($identifer = null)
    {
        if (is_null($identifer)) {
            $identifer = $this->getMyKey();
        }
        $ignorestate = $this->ignore404();
        $this->ignore404(true);
        $cands       = $this->getFlexiData(null,
            [
                'detail' => 'custom:'.$this->getKeyColumn(),
                $this->getKeyColumn() => $identifer
        ]);
        $this->ignore404($ignorestate);
        return ($this->lastResponseCode == 200) && !empty($cands);
    }

    /**
     * Test if given record exists in FlexiBee.
     *
     * @param array|string|int $data ext:id:23|code:ITEM|['id'=>23]|23
     * 
     * @return boolean Record presence status
     */
    public function recordExists($data = [])
    {

        if (empty($data)) {
            $data = $this->getData();
        }
        $ignorestate = $this->ignore404();
        $this->ignore404(true);
        $keyColumn   = $this->getKeyColumn();
        $res         = $this->getColumnsFromFlexibee([$keyColumn],
            is_array($data) ? $data : [$keyColumn => $data]);

        if (empty($res) || (isset($res['success']) && ($res['success'] == 'false'))
            || ((isset($res) && is_array($res)) && !isset($res[0]) )) {
            $found = false;
        } else {
            $found = true;
        }
        $this->ignore404($ignorestate);
        return $found;
    }

    /**
     * Subitems - ex. items of invoice
     * 
     * @return array of document items or null
     */
    public function getSubItems()
    {
        return array_key_exists('polozkyFaktury', $this->getData()) ? $this->getDataValue('polozkyFaktury')
                : (array_key_exists('polozkyDokladu', $this->getData()) ? $this->getDataValue('polozkyDokladu')
                : null);
    }

    /**
     * Vrací z FlexiBee sloupečky podle podmínek.
     *
     * @param array|int|string $conditions pole podmínek nebo ID záznamu
     * @param string           $indexBy    klice vysledku naplnit hodnotou ze
     *                                     sloupečku
     * @return array
     */
    public function getAllFromFlexibee($conditions = null, $indexBy = null)
    {
        if (is_int($conditions)) {
            $conditions = [$this->getmyKeyColumn() => $conditions];
        }

        $flexiData = $this->getFlexiData('', $conditions);

        if (!is_null($indexBy)) {
            $flexiData = $this->reindexArrayBy($flexiData);
        }

        return $flexiData;
    }

    /**
     * Vrací z FlexiBee sloupečky podle podmínek.
     *
     * @param string|string[] $columnsList seznam položek nebo úrověň detailu: id|summary|full
     * @param array           $conditions  pole podmínek nebo ID záznamu
     * @param string          $indexBy     Sloupeček podle kterého indexovat záznamy
     *
     * @return array
     */
    public function getColumnsFromFlexibee($columnsList, $conditions = [],
                                           $indexBy = null)
    {
        $detail = 'full';
        switch (gettype($columnsList)) {
            case 'integer': //Record ID
                $conditions = [$this->getmyKeyColumn() => $conditions];
            case 'array': //Few Conditions
                if (!is_null($indexBy) && !array_key_exists($indexBy,
                        $columnsList)) {
                    $columnsList[] = $indexBy;
                }
                $columns = implode(',', array_unique($columnsList));
                $detail  = 'custom:'.$columns;
            default:
                switch ($columnsList) {
                    case 'id':
                        $detail = 'id';
                        break;
                    case 'summary':
                        $detail = 'summary';
                        break;
                    default:
                        break;
                }
                break;
        }

        $conditions['detail'] = $detail;

        $flexiData = $this->getFlexiData(null, $conditions);

        if (is_string($indexBy) && is_array($flexiData) && array_key_exists(0,
                $flexiData) && array_key_exists($indexBy, $flexiData[0])) {
            $flexiData = $this->reindexArrayBy($flexiData, $indexBy);
        }

        return $flexiData;
    }

    /**
     * Vrací kód záznamu.
     * Obtain record CODE
     *
     * @param mixed $data
     *
     * @return string
     */
    public function getKod($data = null, $unique = true)
    {
        $kod = null;

        if (is_null($data)) {
            $data = $this->getData();
        }

        if (is_string($data)) {
            $data = [$this->nameColumn => $data];
        }

        if (isset($data['kod'])) {
            $kod = $data['kod'];
        } else {
            if (isset($data[$this->nameColumn])) {
                $kod = preg_replace('/[^a-zA-Z0-9]/', '',
                    \Ease\Sand::rip($data[$this->nameColumn]));
            } else {
                if (isset($data[$this->keyColumn])) {
                    $kod = \Ease\Sand::rip($data[$this->keyColumn]);
                }
            }
            $kod = substr($kod, 0, 20);
        }

        if (!strlen($kod)) {
            $kod = 'NOTSET';
        }

        if (strlen($kod) > 18) {
            $kodfinal = strtoupper(substr($kod, 0, 18));
        } else {
            $kodfinal = strtoupper($kod);
        }

        if ($unique) {
            $counter = 0;
            if (!empty($this->codes) && count($this->codes)) {
                foreach ($this->codes as $codesearch => $keystring) {
                    if (strstr($codesearch, $kodfinal)) {
                        ++$counter;
                    }
                }
            }
            if ($counter) {
                $kodfinal = $kodfinal.$counter;
            }

            $this->codes[$kodfinal] = $kod;
        }

        return self::code($kodfinal);
    }

    /**
     * Write Operation Result.
     *
     * @param array  $resultData
     * @param string $url        URL
     * 
     * @return boolean Log save success
     */
    public function logResult($resultData = null, $url = null)
    {
        $logResult = false;
        if (isset($resultData['success']) && ($resultData['success'] == 'false')) {
            if (isset($resultData['message'])) {
                $this->addStatusMessage($resultData['message'], 'warning');
            }
            $this->addStatusMessage('Error '.$this->lastResponseCode.': '.urldecode($url),
                'warning');
            unset($url);
        }
        if (is_null($resultData)) {
            $resultData = $this->lastResult;
        }
        if (isset($url)) {
            $this->logger->addStatusMessage($this->lastResponseCode.':'.urldecode($url));
        }

        if (isset($resultData['results'])) {
            if ($resultData['success'] == 'false') {
                $status = 'error';
            } else {
                $status = 'success';
            }
            foreach ($resultData['results'] as $result) {
                if (isset($result['request-id'])) {
                    $rid = $result['request-id'];
                } else {
                    $rid = '';
                }
                if (isset($result['errors'])) {
                    foreach ($result['errors'] as $error) {
                        $message = $error['message'];
                        if (isset($error['for'])) {
                            $message .= ' for: '.$error['for'];
                        }
                        if (isset($error['value'])) {
                            $message .= ' value:'.$error['value'];
                        }
                        if (isset($error['code'])) {
                            $message .= ' code:'.$error['code'];
                        }
                        $this->addStatusMessage($rid.': '.$message, $status);
                    }
                }
            }
        }
        return $logResult;
    }

    /**
     * Save RAW Curl Request & Response to files in Temp directory
     */
    public function saveDebugFiles()
    {
        $tmpdir   = sys_get_temp_dir();
        $fname    = $this->evidence.'-'.$this->curlInfo['when'].'.'.$this->format;
        $reqname  = $tmpdir.'/request-'.$fname;
        $respname = $tmpdir.'/response-'.$fname;
        if (file_put_contents($reqname, $this->postFields)) {
            $this->addStatusMessage($reqname, 'debug');
        }
        if (file_put_contents($respname, $this->lastCurlResponse)) {
            $this->addStatusMessage($respname, 'debug');
        }
    }

    /**
     * Připraví data pro odeslání do FlexiBee
     *
     * @param string $data
     */
    public function setPostFields($data)
    {
        $this->postFields = $data;
    }

    /**
     * Get Content ready to be send as POST body
     * @return string
     */
    public function getPostFields()
    {
        return $this->postFields;
    }

    /**
     * Generuje fragment url pro filtrování.
     *
     * @see https://www.flexibee.eu/api/dokumentace/ref/filters
     *
     * @param array  $data   key=>values; value can bee class DatePeriod, DateTime or Array
     * @param string $joiner default and/or
     * @param string $defop  default operator
     *
     * @return string
     */
    public static function flexiUrl(array $data, $joiner = 'and', $defop = 'eq')
    {
        $parts = [];

        foreach ($data as $column => $value) {
            if (!is_numeric($column)) {
                if (is_integer($data[$column]) || is_float($data[$column])) {
                    $parts[$column] = $column.' eq \''.$data[$column].'\'';
                } elseif (is_bool($data[$column])) {
                    $parts[$column] = $data[$column] ? $column.' eq true' : $column.' eq false';
                } elseif (is_null($data[$column])) {
                    $parts[$column] = $column." is null";
                } elseif (is_array($data[$column])) {
                    $parts[$column] = $column." in (".implode(',',
                            array_map(function($a) {
                                return "'$a'";
                            }, $data[$column])).")";
                } elseif (is_object($data[$column])) {
                    switch (get_class($data[$column])) {
                        case 'DatePeriod':
                            $parts[$column] = $column." between '".$data[$column]->getStartDate()->format(self::$DateFormat)."' '".$data[$column]->getEndDate()->format(self::$DateFormat)."'";
                            break;
                        case 'DateTime':
                            $parts[$column] = $column." eq '".$data[$column]->format(self::$DateFormat)."'";
                            break;
                        default:
                            $parts[$column] = $column." $defop '".$data[$column]."'";
                            break;
                    }
                } else {
                    switch ($value) {
                        case '!null':
                            $parts[$column] = $column." is not null";
                            break;
                        case 'is empty':
                        case 'is not empty':
                        case 'is true':
                        case 'is false':
                            $parts[$column] = $column.' '.$value;
                            break;
                        default:
                            $condParts      = explode(' ', trim($value));
                            switch ($condParts[0]) {
                                case '<>':
                                case '!=':
                                case 'ne':
                                case 'neq':
                                case '<':
                                case 'lt':
                                case '<=':
                                case 'lte':
                                case '>':
                                case 'gt':
                                case '>=':
                                case 'gte':
                                case 'like':
                                case 'begins':
                                case 'between':
                                case 'ends':
                                    if (count($condParts) == 1) {
                                        $parts[$column] = $column         .= ' '.$value;
                                    } else {
                                        $parts[$column] = $column         .= ' '.$condParts[0]." '".$condParts[1]."'";
                                    }
                                    break;
                                default:
                                    if ($column == 'stitky') {
                                        $parts[$column] = $column."='".self::code($data[$column])."'";
                                    } else {
                                        $parts[$column] = $column." $defop '".$data[$column]."'";
                                    }
                                    break;
                            }

                            break;
                    }
                }
            } else {
                $parts[] = $value;
            }
        }
        return implode(' '.$joiner.' ', $parts);
    }

    /**
     * Obtain record/object numeric identificator id:
     * Vrací číselný identifikátor objektu id:
     *
     * @link https://demo.flexibee.eu/devdoc/identifiers Identifikátory záznamů
     *
     * @return null|int indentifikátor záznamu reprezentovaného objektem
     */
    public function getRecordID()
    {
        $id = $this->getDataValue('id');
        return is_null($id) ? null : is_numeric($id) ? intval($id) : $id;
    }

    /**
     * Obtain record/object identificator code:
     * Vrací identifikátor objektu code:
     *
     * @link https://demo.flexibee.eu/devdoc/identifiers Identifikátory záznamů
     *
     * @return string record code identifier
     */
    public function getRecordCode()
    {
        return empty($this->getDataValue('kod')) ? null : self::code($this->getDataValue('kod'));
    }

    /**
     * Obtain record/object identificator extId: code: or id:
     * Vrací identifikátor objektu extId: code: nebo id:
     *
     * @link https://demo.flexibee.eu/devdoc/identifiers Identifikátory záznamů
     *
     * @return string|int|null record code identifier
     */
    public function getRecordIdent()
    {
        $ident = $this->getExternalID();
        if (empty($ident)) {
            $ident = $this->getRecordCode();
        }
        if (empty($ident)) {
            $ident = $this->getRecordID();
        }
        return $ident;
    }

    /**
     * Obtain record/object identificator code: or id:
     * Vrací identifikátor objektu code: nebo id:
     *
     * @link https://demo.flexibee.eu/devdoc/identifiers Identifikátory záznamů
     * 
     * @return string indentifikátor záznamu reprezentovaného objektem
     */
    public function __toString()
    {
        return strval($this->getRecordIdent());
    }

    /**
     * Gives you FlexiPeeHP class name for Given Evidence
     *
     * @param string $evidence
     * 
     * @return string Class name
     */
    public static function evidenceToClassName($evidence)
    {
        return str_replace(' ', '', ucwords(str_replace('-', ' ', $evidence)));
    }

    /**
     * Obtain ID of first record in evidence
     *
     * @return string|null id or null if no records
     */
    public function getFirstRecordID()
    {
        $firstID    = null;
        $keyColumn  = $this->getKeyColumn();
        $firstIdRaw = $this->getColumnsFromFlexibee([$keyColumn],
            ['limit' => 1, 'order' => $keyColumn], $keyColumn);
        if (!empty($firstIdRaw) && isset(current($firstIdRaw)[$keyColumn])) {
            $firstID = current($firstIdRaw)[$keyColumn];
        }
        return is_numeric($firstID) ? intval($firstID) : $firstID;
    }

    /**
     * Get previous record ID
     * 
     * @param array $conditions optional
     * 
     * @return int|null
     */
    public function getNextRecordID($conditions = [])
    {
        $conditions['order'] = 'id@D';
        $conditions['limit'] = 1;
        $conditions[]        = 'id gt '.$this->getRecordID();
        $next                = $this->getColumnsFromFlexibee(['id'], $conditions);
        return (is_array($next) && array_key_exists(0, $next) && array_key_exists('id',
                $next[0])) ? intval($next[0]['id']) : null;
    }

    /**
     * Get next record ID
     * 
     * @param array $conditions optional
     * 
     * @return int|null
     */
    public function getPrevRecordID($conditions = [])
    {
        $conditions['order'] = 'id@A';
        $conditions['limit'] = 1;
        $conditions[]        = 'id lt '.$this->getRecordID();
        $prev                = $this->getColumnsFromFlexibee(['id'], $conditions);
        return (is_array($prev) && array_key_exists(0, $prev) && array_key_exists('id',
                $prev[0])) ? intval($prev[0]['id']) : null;
    }

    /**
     * Vrací hodnotu daného externího ID
     *
     * @param string $want Namespace Selector. If empty,you obtain the first one.
     * 
     * @return string|array one id or array if multiplete
     */
    public function getExternalID($want = null)
    {
        $extid = null;
        $ids   = $this->getExternalIDs();
        if (is_null($want)) {
            if (!empty($ids)) {
                $extid = current($ids);
            }
        } else {
            if (!is_null($ids) && is_array($ids)) {
                foreach ($ids as $id) {
                    if (strstr($id, 'ext:'.$want)) {
                        if (is_null($extid)) {
                            $extid = str_replace('ext:'.$want.':', '', $id);
                        } else {
                            if (is_array($extid)) {
                                $extid[] = str_replace('ext:'.$want.':', '', $id);
                            } else {
                                $extid = [$extid, str_replace('ext:'.$want.':',
                                        '', $id)];
                            }
                        }
                    }
                }
            }
        }
        return $extid;
    }

    /**
     * gives you currently loaded extermal IDs
     * 
     * @return array
     */
    public function getExternalIDs()
    {
        return $this->getDataValue('external-ids');
    }

    /**
     * Obtain actual GlobalVersion
     * Vrací aktuální globální verzi změn
     *
     * @link https://www.flexibee.eu/api/dokumentace/ref/changes-api#globalVersion Globální Verze
     * 
     * @return int
     */
    public function getGlobalVersion()
    {
        $this->getFlexiData(null, ['add-global-version' => 'true', 'limit' => 1]);

        return $this->globalVersion;
    }

    /**
     * Gives you current ApiURL with given format suffix
     * 
     * @param string $format json|html|xml|...
     * 
     * @return string API URL for current record or object/evidence
     */
    public function getApiURL($format = null)
    {
        $apiUrl = str_replace(['.'.$this->format, '?limit=0'], '', $this->apiURL);
        return $apiUrl.(empty($format) ? '' : '.'.$format );
    }

    /**
     * Obtain content type of last response
     *
     * @return string
     */
    public function getResponseFormat()
    {
        return $this->responseFormat;
    }

    /**
     * Return the same response format for one and multiplete results
     *
     * @param array $responseBody
     * 
     * @return array
     */
    public function unifyResponseFormat($responseBody)
    {
        if (!is_array($responseBody) || array_key_exists('message',
                $responseBody)) { //Unifi response format
            $response = $responseBody;
        } else {
            $evidence = $this->getResponseEvidence();
            if (array_key_exists($evidence, $responseBody)) {
                $response        = [];
                $evidenceContent = $responseBody[$evidence];
                if (array_key_exists(0, $evidenceContent)) {
                    $response[$evidence] = $evidenceContent; //Multiplete Results
                } else {
                    $response[$evidence][0] = $evidenceContent; //One result
                }
            } else {
                if (isset($responseBody['priloha'])) {
                    $response = $responseBody['priloha'];
                } else {
                    if (array_key_exists('results', $responseBody)) {
                        $response = $responseBody['results'];
                    } else {
                        $response = $responseBody;
                    }
                }
            }
        }
        return $response;
    }

    /**
     * Obtain structure for current (or given) evidence
     *
     * @param string $evidence
     * 
     * @return array Evidence structure
     */
    public function getOfflineColumnsInfo($evidence = null)
    {
        $columnsInfo = null;
        $infoSource  = self::$infoDir.'/Properties.'.(empty($evidence) ? $this->getEvidence()
                : $evidence).'.json';
        if (file_exists($infoSource)) {
            $columnsInfo = json_decode(file_get_contents($infoSource), true);
        }
        return $columnsInfo;
    }

    /**
     * Obtain Current evidence Live structure
     * 
     * @param string $evidence
     * 
     * @return array structure
     */
    public function getOnlineColumnsInfo($evidence = null)
    {
        $properties = [];
        $evidence   = is_null($evidence) ? $this->getEvidence() : $evidence;
        $flexinfo   = $this->performRequest('/c/'.$this->company.'/'.$evidence.'/properties.json');
        if (!empty($flexinfo) && array_key_exists('properties', $flexinfo)) {
            foreach ($flexinfo['properties']['property'] as $evidenceProperty) {
                $key                      = $evidenceProperty['propertyName'];
                $properties[$key]         = $evidenceProperty;
                $properties[$key]['name'] = $evidenceProperty['name'];
                $properties[$key]['type'] = $evidenceProperty['type'];
                if (array_key_exists('url', $evidenceProperty)) {
                    $properties[$key]['url'] = str_replace('?limit=0', '',
                        $evidenceProperty['url']);
                }
            }
        }
        return $properties;
    }

    /**
     * Update evidence info from array or online from properties.json or offline
     * 
     * @param array  $columnsInfo
     * @param string $evidence
     */
    public function updateColumnsInfo($columnsInfo = null, $evidence = null)
    {
        $evidence = is_null($evidence) ? $this->getEvidence() : $evidence;
        if (is_null($columnsInfo)) {
            $this->columnsInfo[$evidence] = $this->offline ? $this->getOfflineColumnsInfo($evidence)
                    : $this->getOnlineColumnsInfo($evidence);
        } else {
            $this->columnsInfo[$evidence] = $columnsInfo;
        }
    }

    /**
     * Gives you evidence structure. You can obtain current online by pre-calling:
     * $this->updateColumnsInfo($evidence, $this->getOnlineColumnsInfo($evidence));
     * 
     * @param string $evidence
     * 
     * @return array
     */
    public function getColumnsInfo($evidence = null)
    {
        $evidence = is_null($evidence) ? $this->getEvidence() : $evidence;
        if (!array_key_exists($evidence, $this->columnsInfo)) {
            $this->updateColumnsInfo($this->getOfflineColumnsInfo($evidence),
                $evidence);
        }
        return $this->columnsInfo[$evidence];
    }

    /**
     * Gives you properties for (current) evidence column
     *
     * @param string $column    name of column
     * @param string $evidence  evidence name if different
     *
     * @return array column properties or null if column not exits
     */
    public function getColumnInfo($column, $evidence = null)
    {
        $columnsInfo = $this->getColumnsInfo(empty($evidence) ? $this->getEvidence()
                : $evidence);
        return (empty($column) || empty($columnsInfo) || !is_array($columnsInfo))
                ? null : array_key_exists($column, $columnsInfo) ? $columnsInfo[$column]
                : null;
    }

    /**
     * Obtain actions for current (or given) evidence
     *
     * @param string $evidence
     * 
     * @return array Evidence structure
     */
    public function getActionsInfo($evidence = null)
    {
        $actionsInfo = null;
        if (is_null($evidence)) {
            $evidence = $this->getEvidence();
        }
        $propsName = lcfirst(FlexiBeeRO::evidenceToClassName($evidence));
        if (isset(\FlexiPeeHP\Actions::$$propsName)) {
            $actionsInfo = Actions::$$propsName;
        }
        return $actionsInfo;
    }

    /**
     * Obtain relations for current (or given) evidence
     *
     * @param string $evidence
     * 
     * @return array Evidence structure
     */
    public function getRelationsInfo($evidence = null)
    {
        $relationsInfo = null;
        if (is_null($evidence)) {
            $evidence = $this->getEvidence();
        }
        $propsName = lcfirst(FlexiBeeRO::evidenceToClassName($evidence));
        if (isset(\FlexiPeeHP\Relations::$$propsName)) {
            $relationsInfo = Relations::$$propsName;
        }
        return $relationsInfo;
    }

    /**
     * Obtain info for current (or given) evidence
     *
     * @param string $evidence
     * 
     * @return array Evidence info
     */
    public function getEvidenceInfo($evidence = null)
    {
        $evidencesInfo = null;
        if (is_null($evidence)) {
            $evidence = $this->getEvidence();
        }
        if (isset(EvidenceList::$evidences[$evidence])) {
            $evidencesInfo = EvidenceList::$evidences[$evidence];
            $propsName     = lcfirst(FlexiBeeRO::evidenceToClassName($evidence));
            if (isset(Formats::$$propsName)) {
                $evidencesInfo['formats'] = Formats::$$propsName;
            }
        }
        return $evidencesInfo;
    }

    /**
     * Obtain name for current (or given) evidence path
     *
     * @param string $evidence Evidence Path
     * 
     * @return array Evidence info
     */
    public function getEvidenceName($evidence = null)
    {
        $evidenceName = null;
        if (is_null($evidence)) {
            $evidence = $this->getEvidence();
        }
        if (isset(EvidenceList::$name[$evidence])) {
            $evidenceName = EvidenceList::$name[$evidence];
        }
        return $evidenceName;
    }

    /**
     * Save current object to file
     *
     * @param string $destfile path to file
     */
    public function saveResponseToFile($destfile)
    {
        if (strlen($this->lastCurlResponse)) {
            $this->doCurlRequest($this->apiURL, 'GET', $this->format);
        }
        file_put_contents($destfile, $this->lastCurlResponse);
    }

    /**
     * Obtain established relations listing
     *
     * @return array Null or Relations
     */
    public function getVazby($id = null)
    {
        if (is_null($id)) {
            $id = $this->getRecordID();
        }
        if (!empty($id)) {
            $vazbyRaw = $this->getColumnsFromFlexibee(['vazby'],
                ['relations' => 'vazby', 'id' => $id]);
            $vazby    = array_key_exists('vazby', $vazbyRaw[0]) ? $vazbyRaw[0]['vazby']
                    : null;
        } else {
            throw new \Exception(_('ID requied to get record relations '));
        }
        return $vazby;
    }

    /**
     * Gives You URL for Current Record in FlexiBee web interface
     *
     * @return string url
     */
    public function getFlexiBeeURL()
    {
        $parsed_url = parse_url(str_replace('.'.$this->format, '', $this->apiURL));
        $scheme     = isset($parsed_url['scheme']) ? $parsed_url['scheme'].'://'
                : '';
        $host       = isset($parsed_url['host']) ? $parsed_url['host'] : '';
        $port       = isset($parsed_url['port']) ? ':'.$parsed_url['port'] : '';
        $user       = isset($parsed_url['user']) ? $parsed_url['user'] : '';
        $pass       = isset($parsed_url['pass']) ? ':'.$parsed_url['pass'] : '';
        $pass       = ($user || $pass) ? "$pass@" : '';
        $path       = isset($parsed_url['path']) ? $parsed_url['path'] : '';
        return $scheme.$user.$pass.$host.$port.$path;
    }

    /**
     * Set Record Key
     *
     * @param int|string $myKeyValue
     * 
     * @return boolean
     */
    public function setMyKey($myKeyValue)
    {
        if (substr($myKeyValue, 0, 4) == 'ext:') {
            if ($this->evidenceInfo['extIdSupported'] == 'false') {
                $this->addStatusMessage(sprintf(_('Evidence %s does not support extIDs'),
                        $this->getEvidence()), 'warning');
                $res = false;
            } else {
                $extIds = $this->getDataValue('external-ids');
                if (!empty($extIds) && count($extIds)) {
                    $extIds = array_combine($extIds, $extIds);
                }

                $extIds[$myKeyValue] = $myKeyValue;
                $res                 = $this->setDataValue('external-ids',
                    $extIds);
            }
        } else {
            $res = parent::setMyKey($myKeyValue);
        }
        $this->updateApiURL();
        return $res;
    }

    /**
     * Set or get ignore not found pages flag
     *
     * @param boolean $ignore set flag to
     *
     * @return boolean get flag state
     */
    public function ignore404($ignore = null)
    {
        if (!is_null($ignore)) {
            $this->ignoreNotFound = $ignore;
        }
        return $this->ignoreNotFound;
    }

    /**
     * Send Document by mail
     *
     * @url https://www.flexibee.eu/api/dokumentace/ref/odesilani-mailem/
     *
     * @param string $to         Email ecipient
     * @param string $subject    Email Subject
     * @param string $body       Email Text
     *
     * @return boolean mail sent status
     */
    public function sendByMail($to, $subject, $body, $cc = null)
    {
        $this->setPostFields($body);

        $this->performRequest(rawurlencode($this->getRecordID()).'/odeslani-dokladu?to='.$to.'&subject='.urlencode($subject).'&cc='.$cc
            , 'PUT', 'xml');

        return $this->lastResponseCode == 200;
    }

    /**
     * Send all unsent Documents by eMail
     *
     * @url https://www.flexibee.eu/api/dokumentace/ref/odesilani-mailem/
     * 
     * @return int http response code
     */
    public function sendUnsent()
    {
        return $this->doCurlRequest('automaticky-odeslat-neodeslane', 'PUT',
                'xml');
    }

    /**
     * FlexiBee date to PHP DateTime conversion
     *
     * @param string $flexidate 2017-05-26 or 2017-05-26+02:00
     *
     * @return \DateTime | false
     */
    public static function flexiDateToDateTime($flexidate)
    {
        return \DateTime::createFromFormat(strstr($flexidate, '+') ? self::$DateFormat.'O'
                    : self::$DateFormat, $flexidate)->setTime(0, 0);
    }

    /**
     * FlexiBee dateTime to PHP DateTime conversion
     *
     * @param string $flexidatetime 2017-09-26T10:00:53.755+02:00 or older 2017-05-19T00:00:00+02:00
     *
     * @return \DateTime | false
     */
    public static function flexiDateTimeToDateTime($flexidatetime)
    {
        if (strchr($flexidatetime, '.')) { //NewFormat
            $format = self::$DateTimeFormat;
        } else { // Old format
            $format = 'Y-m-d\TH:i:s+P';
        }
        return \DateTime::createFromFormat($format, $flexidatetime);
    }

    /**
     * Získá dokument v daném formátu
     * Obtain document in given format
     *
     * @link https://www.flexibee.eu/api/dokumentace/ref/pdf/ PDF Exports
     *
     * @param string  $format     pdf/csv/xml/json/ ...
     * @param string  $reportName Template used to generate PDF
     * @param string  $lang       cs|sk|en|de Template language used to generate PDF
     * @param boolean $sign       sign resulting PDF by certificate ?
     *
     * @return string|null filename downloaded or none
     */
    public function getInFormat($format, $reportName = null, $lang = null,
                                $sign = false)
    {
        $response = null;
        if ($this->setFormat($format)) {
            $urlParams = [];
            switch ($format) {
                case 'pdf':
                    switch ($lang) {
                        case 'cs':
                        case 'sk':
                        case 'en':
                        case 'de':
                            $urlParams['report-lang'] = $lang;
                            break;
                        case null:
                        case '':
                            break;
                        default:
                            throw new \Ease\Exception('Unknown language '.$lang.' for PDF export');
                            break;
                    }
                    if (boolval($sign) === true) {
                        $urlParams['report-sign'] = 'true';
                    }
                    break;
                case 'html':
                    $urlParams['inDesktopApp'] = 'true';
                    break;
            }
            if (!empty($reportName)) {
                $urlParams['report-name'] = $reportName;
            }
            if (($this->doCurlRequest(\Ease\Shared::addUrlParams($this->apiURL,
                        $urlParams), 'GET') == 200)) {
                $response = $this->lastCurlResponse;
            }
        }
        return $response;
    }

    /**
     * Uloží dokument v daném formátu do složky v systému souborů
     * Save document in given format to directory in filesystem
     *
     * @param string $format  pdf/csv/xml/json/ ...
     * @param string $destDir where to put file (prefix)
     * @param string $reportName Template used to generate PDF
     *
     * @return string|null filename downloaded or none
     */
    public function downloadInFormat($format, $destDir = './',
                                     $reportName = null)
    {
        $fileOnDisk   = null;
        $formatBackup = $this->format;
        if ($this->setFormat($format)) {
            $downloadTo = $destDir.$this->getEvidence().'_'.$this->getMyKey().'.'.$format;
            if (($this->doCurlRequest(empty($reportName) ? $this->apiURL : \Ease\Shared::addUrlParams($this->apiURL,
                            ['report-name' => $reportName]), 'GET') == 200) && (file_put_contents($downloadTo,
                    $this->lastCurlResponse) !== false)) {
                $fileOnDisk = $downloadTo;
            }
            $this->setFormat($formatBackup);
        }
        return $fileOnDisk;
    }

    /**
     * Take data for object. separate external IDs
     *
     * @param array $data Data to keep
     * 
     * @return int number of records taken
     */
    public function takeData($data)
    {
        $keyColumn = $this->getKeyColumn();
        if (array_key_exists($keyColumn, $data) && is_array($data[$keyColumn])) {
            foreach ($data[$keyColumn] as $recPos => $recordKey) {
                if (substr($recordKey, 0, 4) == 'ext:') {
                    $data['external-ids'][] = $recordKey;
                    unset($data[$keyColumn][$recPos]);
                }
            }
            if (count($data[$keyColumn]) == 1) {
                $data[$keyColumn] = current($data[$keyColumn]);
            }
        }
        $result = parent::takeData($data);

        if (array_key_exists($keyColumn, $data) || array_key_exists('kod', $data)) {
            $this->updateApiURL();
        }

        return $result;
    }

    /**
     * Get Current Evidence reports listing
     * 
     * @link https://www.flexibee.eu/api/dokumentace/casto-kladene-dotazy-pro-api/vyber-reportu-do-pdf/ Výběr reportu do PDF
     * 
     * @return array
     */
    public function getReportsInfo()
    {
        $reports    = [];
        $reportsRaw = $this->getFlexiData($this->getEvidenceURL().'/reports');
        if (!empty($reportsRaw) && array_key_exists('reports', $reportsRaw) && !empty($reportsRaw['reports'])
            && array_key_exists('report', $reportsRaw['reports']) &&
            !empty($reportsRaw['reports']['report'])) {
            if (\Ease\jQuery\Part::isAssoc($reportsRaw['reports']['report'])) {
                $reports = [$reportsRaw['reports']['report']['reportId'] => $reportsRaw['reports']['report']];
            } else {
                $reports = self::reindexArrayBy($reportsRaw['reports']['report'],
                        'reportId');
            }
        }
        return $reports;
    }

    /**
     * Request authSessionId from current server
     * 
     * @link https://www.flexibee.eu/api/dokumentace/ref/login/ description
     * 
     * @param string $username
     * @param string $password
     * @param string $otp       optional onetime password
     * 
     * @return string authUserId or null in case of problems
     */
    public function requestAuthSessionID($username, $password, $otp = null)
    {
        $this->postFields = http_build_query(is_null($otp) ? ['username' => $username,
            'password' => $password] : ['username' => $username, 'password' => $password,
            'otp' => $otp]);
        $response         = $this->performRequest('/login-logout/login', 'POST',
            'json');
        if (array_key_exists('refreshToken', $response)) {
            $this->refreshToken = $response['refreshToken'];
        } else {
            $this->refreshToken = null;
        }
        return array_key_exists('authSessionId', $response) ? $response['authSessionId']
                : null;
    }

    /**
     * Try to Sign in current user to FlexiBee and keep authSessionId
     * 
     * @return boolean sign in success
     */
    public function login()
    {
        $this->authSessionId = $this->requestAuthSessionID($this->user,
            $this->password);
        return $this->lastResponseCode == 200;
    }

    /**
     * End (current's user) session
     * 
     * 
     * @link https://www.flexibee.eu/api/dokumentace/ref/logout Logout Reference
     * 
     * @param string $username force username to sign off
     * 
     * @return array server response
     */
    public function logout($username = null)
    {
        return $this->performRequest('/status/user/'.(is_null($username) ? $this->user
                    : $username).'/logout', 'POST');
    }

    /**
     * Compile and send Report about Error500 to FlexiBee developers
     * If FlexiBee is running on localost try also include java backtrace
     *
     * @param array $errorResponse result of parseError();
     */
    public function error500Reporter($errorResponse)
    {
        $ur = str_replace('/c/'.$this->company, '',
            str_replace($this->url, '', $this->curlInfo['url']));
        if (!array_key_exists($ur, $this->reports)) {
            $tmpdir   = sys_get_temp_dir();
            $myTime   = $this->curlInfo['when'];
            $curlname = $tmpdir.'/curl-'.$this->evidence.'-'.$myTime.'.json';
            file_put_contents($curlname,
                json_encode($this->curlInfo, JSON_PRETTY_PRINT));

            $report = new \Ease\Mailer($this->reportRecipient,
                'Error report 500 - '.$ur);

            $d     = dir($tmpdir);
            while (false !== ($entry = $d->read())) {
                if (strstr($entry, $myTime)) {
                    $ext  = pathinfo($tmpdir.'/'.$entry, PATHINFO_EXTENSION);
                    $mime = Formats::suffixToContentType($ext);
                    $report->addFile($tmpdir.'/'.$entry,
                        empty($mime) ? 'text/plain' : $mime);
                }
            }
            $d->close();

            if ((strstr($this->url, '://localhost') || strstr($this->url,
                    '://127.')) && file_exists('/var/log/flexibee.log')) {

                $fl = fopen('/var/log/'.'flexibee.log', 'r');
                if ($fl) {
                    $tracelog = [];
                    for ($x_pos = 0, $ln = 0, $output = array(); fseek($fl,
                            $x_pos, SEEK_END) !== -1; $x_pos--) {
                        $char = fgetc($fl);
                        if ($char === "\n") {
                            $tracelog[] = $output[$ln];
                            if (strstr($output[$ln], $errorResponse['message'])) {
                                break;
                            }
                            $ln++;
                            continue;
                        }
                        $output[$ln] = $char.((array_key_exists($ln, $output)) ? $output[$ln]
                                : '');
                    }

                    $trace     = implode("\n", array_reverse($tracelog));
                    $tracefile = $tmpdir.'/trace-'.$this->evidence.'-'.$myTime.'.log';
                    file_put_contents($tracefile, $trace);
                    $report->addItem("\n\n".$trace);
                    fclose($fl);
                }
            } else {
                $report->addItem($errorResponse['message']);
            }

            $licenseInfo = $this->performRequest($this->url.'/default-license.json');

            $report->addItem("\n\n".json_encode($licenseInfo['license'],
                    JSON_PRETTY_PRINT));

            if ($report->send()) {
                $this->reports[$ur] = $myTime;
            }
        }
    }

    /**
     * Returns code:CODE
     *
     * @param string $code
     *
     * @return string
     */
    public static function code($code)
    {
        return ((substr($code, 0, 4) == 'ext:') ? $code : 'code:'.strtoupper(self::uncode($code)));
    }

    /**
     * Returns CODE without code: prefix
     *
     * @param string $code
     *
     * @return string
     */
    public static function uncode($code)
    {
        return str_replace(['code:', 'code%3A'], '', $code);
    }

    /**
     * Remove all @ items from array
     *
     * @param array $data original data
     *
     * @return array data without @ columns
     */
    public static function arrayCleanUP($data)
    {
        return array_filter(
            $data,
            function ($key) {
            return !strchr($key, '@');
        }, ARRAY_FILTER_USE_KEY);
    }

    /**
     * Add Info about used user, server and libraries
     *
     * @param string $additions Additional note text
     */
    public function logBanner($additions = null)
    {
        $this->addStatusMessage('FlexiBee '.str_replace('://',
                '://'.$this->user.'@', $this->getApiUrl()).' FlexiPeeHP v'.self::$libVersion.' (FlexiBee '.EvidenceList::$version.') EasePHP Framework v'.\Ease\Atom::$frameworkVersion.' '.$additions,
            'debug');
    }

    /**
     * Get Last operation type
     * 
     * @return string create|read|update|delete or update,insert for some inserted and updated in one transaction
     */
    public function getLastOperationType()
    {
        return implode(',', array_keys(array_filter($this->responseStats)));
    }

    /**
     * Reconnect After unserialization
     */
    public function __wakeup()
    {
        parent::__wakeup();
        $this->curlInit();
    }
}
FlexiPeeHP API documentation generated by ApiGen