Obecně o API

ApiComPrem


Api (application programming interface ) - aplikační programovací rozhraní (API) je soubor definicí podprogramů, komunikačních protokolů a nástrojů pro vývoj softwaru.

Com (communication) - z latinského communicare, což znamená "sdílet",

Prem - program Premier,

ApiComPrem - komunikační nástroj pro sdílení dat v programu Premier.


Aplikace slouží pro software třetích stran, které mají možnost přímo komunikovat s programem Premier. 

ApiComPrem komunikuje přes protokol HTTP 1.1 metodou POST. Odesílaná a přijatá data jsou ve formátu JSON.

JavaScript Object Notation (JavaScriptový objektový zápis, JSON) je způsob zápisu dat (datový formát) nezávislý na počítačové platformě, určený pro přenos dat, která mohou být organizována v polích nebo agregována v objektech. Vstupem je libovolná datová struktura (číslo, řetězec, boolean, objekt nebo z nich složené pole), výstupem je vždy řetězec. Složitost hierarchie vstupní proměnné není teoreticky nijak omezena.


Header (hlavička HTTP):

POST /api/comm HTTP/1.1
Content-Type: application/json; charset=utf-8
Authorization: Basic Z3JhczpncjE3
ID-UJ: c8175fe2-5aba-460e-a492-5da487105394
Content-Length: xy
{
        JSON command
}


Authorization                   Jméno a heslo uživatele z programu Premier ve formátu Base64.

ID-UJ                                     nastavení ID účetní jednotky na serveru HTTP

Content-Length               délka odeslaných dat (JSON command)



Na této stránce najdete:


Obecný popis ApiComPrem souborů

ApiComPrem.dll

  • třída volání HTTP/HTTPS , server
  • komunikace s SQL
  • jednoduché SQL procedury (select)
  • registrovaní CLR SQL procedur přes AutoRegisterAsm
  • podpora pro VFP (čtení DBF tabulek, mem souborů, volaní procedur z VFP, ...)
  • struktury tříd
  • struktury tabulek
  • třídy pro JSON
  • šifrování hesel
  • práce s certifikáty
  • čtení Premier licence
  • ...

ApiComPremForm.exe

  • nastavení komunikace (funguje pouze s SQL verzí programu Premier)
  • nastavení účetních jednotek
  • testovací server pro vývojáře
  • testovací formulář pro vývojáře

ApiComSql.dll

  • CLR SQL procedury

AutoRegisterAsm.dll

  • automatické registrovaní CLR procedur pro SQL

ApiComPremService.exe

  • windows služba pro volaní ApiComPrem.dll
  • podpora pro instalování a odinstalování služby

ApiComUpdate.exe

  • aktualizace ApiComPrem z FTP
  • podpora pro automatické spuštění na pozadí

setup.xml

  • uložená konfigurace ApiComPrem

Newtonsoft.Json.dll

JSON command

Používají se dva typy příkazů:

  • OUT       čtení dat z API serveru
  • IN           zápis dat na API server

OUT příkaz (výstupní formát)

Formát HTTP_OUT

  • Result
  • CommandIn
  • Error
    • { row }
      • number
      • desc
      • Warning
        • { row }
          • desc
          • help
          • Data
            •  vrátí JSON data v závislosti od příkazu

Příklad načítání seznamu všech příkazů s popisem použití


{ "command": {
    "inComm": "INFO",
    "inParam": {
      "parameters": {
        "prikaz": "FULL"
      } } } }


inComm              název příkazu

inParam              parametre příkazu které se liší podle požadovaného inComm


Vrácený JSON v případě úspěšného načtení (RESULT – OK)

CommandIn       vrácený poslaný příkaz

Data                      vrátí data podle typu příkazu


{ "Result":"OK",
    "CommandIn":"INFO",
    "Data":
    [ {
            "CommList":
            [ {
                    "INFO":
                    {
                        "ID": 100,
                        "nazov": "INFO",
                        "pouziva_param": true,
                        "parametre":
                        [ {
                                "name": "table_name",
                                "is_required": false,
                                "desc": "nazov tabulky pre nacitanie struktury",
                                "format": "S",
                                "order": 0
                            }, {
                                "name": "prikaz",
                                "is_required": false,
                                "desc": "informacie o prikaze (FULL - posle vsetky)",
                                "format": "S",
                                "order": 0
                            } ],
                        "popis": "nacitanie inform.udajov",
                        "typ_prikazu": "OUT"
                    } },


Když nastane chyba ve zpracování (RESULT – ERR)

Error                     číslo a popis chyby. API nepošle žádné pole "Data"

Warning              upozorní na nesprávně naplněné parametry, a v případe že RESULT = OK tak pole  Data jsou vytvořené. Warning může mít více řádků


{ "Result":"ERR",
    "CommandIn":"INFO",
    "Error":
    [  {
            "number":502,
            "desc":"Tabulka sa nenasla."
        } ],
    "Warning":
    [
        {
            "desc":"chyba spracovanie SQL stru.tabulky [Exception Type: System.Data.SqlClient.SqlException, Message: Invalid object name 'SDFASFA'.]]"
        } ] }


K příkazu command se může přidat příkaz „queryFields“ který určuje vrácená pole použitých tabulek povolených v inComm (i nad rámec parametrů daného příkazu):

  "queryFields": [ {
      "tableName": "FA_OUT",
      "tableFields": "INTER,CISLO,ZKKOD,STKOD,K_SYMBOL,OBJEDNAVKA,DOD_LIST,BANKA_DOKL,BANKA_NAZ"  },{
      "tableName": "POLOZKY",
      "tableFields": "FAKTURA,MNOZSTVI,CENA_MJ,CENA_SLV,CENA,CENA_DPH,SAZBA_DPH"
    }  ]

Upozornění

Při použití queryfields se v některých případech může vrátit chybová hláška Invalid column name 'název', která znamená nutnost uvést ve výčtu i dané pole.


Když logika inComm povolí, je možnost poslat vlastní podmínky (parametry výběru) „queryCondition“ příkazu:


příklad 1:

    "queryCondition": {
      "tableName": "FA_OUT",
      "conditions": [ {
          "fieldName": "INTER",
          "relationalOperator": "=",
          "value": "15"
        } ] }


příklad 2:
           

          {
            "command": {
            "inComm": "PARTNERI",
            "inParam": {
            "parameters": {
            "part_typ": "OD"
          }
          },
         "queryCondition": {
            "tableName": "PARTNERY",
            "conditions": [
            {
              "fieldName": "INTER",
              "relationalOperator": ">",
              "value": "100"
            },
           {
              "logicalOperator": "AND",
              "fieldName": "INTER",
              "relationalOperator": "<",
              "value": "900"
            }
           ]
         }
        }
      }


IN příkaz (vstupní formát)

Formát HTTP_in

  • Command
    •  inComm
    •  inParam
      • Parameters
        • názvy a hodnoty parametrů v závislosti od příkazu
  •  queryCondition
    • { row }
      • tableName
      • conditions
        • logicalOperator
        • fieldName
        • relationalOperator
        • value
        • queryFields
          • { row }
            • tableName
            • tableFields
            • Select
            • Data


Používá stejnou logiku jako OUT příkaz + doplněné o pole „DATA“, které se posílají na server.

{
  "command": {
    "inComm": "PARTNERI_ADD",
    "inParam": {
      "parameters": {
        "typCmd": null
      }
    }
  },
  "Data": {
    "INTER": 0,
    "ODBERATEL": true,
    "ID": "057fe3f7-3363-49ab-88bc-8c6342ad9a33",
    "CISLO": "0123456789",
    "NAZEV": "Test ADD",
    "ALT_NAZEV": null,
    "ULICE": null,
    "PSC": null,
    "MESTO": null,
    "STAT": null,
    "ICO": null,
    "DIC": null,
    "KON_PRIJEM": null,
    "MOBIL": null,
    "E_MAIL": null
  }
}


Obecné specifikace v příkazech

Vstupní formát Timestamp:        rrrr-MM-dd HH:mm:ss
                                                   (HH = 24 hod. Formát, příklad: 2020-01-31 19:55:00)

Vstupní formát Datumu:              rrrr-MM-dd


Chybové hlášky

Vrácené chybové hlášky mají zpravidla tento formát:

{
    "Result": "ERR",                                                                                                                                                           /* OK/ERR */
    "CommandIn": "FA_OUT",                                                                                                                                           /* název příkazu */
    "Error": [
        {
            "number": 900,                                                                                                                                                    /* číslo chyby */
            "desc": "Exception Type: System.Data.SqlClient.SqlException, Message: Invalid column name 'inter'."             /* podrobnosti k chybě */
        }
    ]
}

Přehled chybových hlášek v result

Arithmetic overflow error converting expression to data type - došlo k přetečení hodnoty, kdy je v databázi nižší, než kterou se pokoušíte zapsat   

Invalid column name 'nazev- obvykle se zobrazí při použití queryFields, kde je nutné uvést ve výčtu i povinnou databázovou položku

Musí být vyplněn alespoň jeden z parametrů ['nazev'] nebo [queryConditions] - chybí vyplnění alespoň jedné z uvedených povinných položek

Cannot continue the execution because the session is in the kill state - může nastat v případě volání příkazů za sebou, kdy se nestihne vrátit Result z předchozího příkazu a dochází ke stornu. Maximální čas pro zpracování jednoho příkazu je nastaveno v API na 2 min. Tento maximální čas spojení by si měl současně nastavit a hlídat ve svém programu i programátor.

Chyba: 3521 - Požadovaná měna není v nastavení pro Kurzovní lístek ČNB. Pro tuzemskou fakturu menu neudávejte - je nutné mít v Premieru povolenou požadovanou měnu:

Chyba: 3514 ... - chybný parametr FORMA ve fakturách

Chyba: 3005 ... – všeobecná chyba SQL

Chyba: 3006 ... – název partnera již existuje

Chyba: 3007 ... – požadované ID [xy] pro update partnera jsem nenašel


ApiComPremForm

Pokud není nastaven správně Compatibiliy Level v administraci MS SQL, zobrazí se při Testu připojení tato chyba:


ApiComPremForm: Nastavení HTTP

Slouží k nastavení základních parametrů. Uložené nastavení přebírá HTTP server i služba ApiComPremService, která musí být po provedení změn restartována.

Pokud nejsou nainstalovány SQL procedury v SQL databázi, může ApiComPrem vyhazovat při odesílání příkazů chyby, potom je třeba provést reinstalaci procedur tl. „Reinstall SQL CLR“ a zároveň musí být správně nastavena a programem přístupná cesta „Default cesta k HTML“.

Tlačítko Přidání nového záznamu (=připojení ke konkrétní firmě v SQL verzi programu Premier)

Konfigurace se zapisuje do souboru setup.xml, který můžete v případě potřeby editovat i ručně, data nejsou zašifrována.


ApiComPremForm: HTTP server

Vizuální zobrazení komunikace HTTP serveru. Po spuštění přebírá nastavení z předchozího kroku a funguje pouze s SQL verzí programu Premier.

Při spuštění http serveru se kontroluje verze ApiComSQL.dll a automaticky se nainstalují nebo přeinstalují požadované SQL procedury na SQL serveru. Pokud k přeinstalaci SQL procedur nedojde z nějakého důvodu automaticky, lze použít v Nastavení http tlačítko Reinstal SQL CRL.


ApiComPremService


Služba HTTP server, nahrazuje spuštění vizuálního formuláře HTTP server. Instalaci provádějte jako správce. Veškerá nastavení se přebírají z Nastavení http (soubor setup.xml).

Instalace:            ApiComPremService.exe" /install
Odinstalace:       ApiComPremService.exe" /uninstall

Pokud si nastavíte spuštění příkazu „ApiComPremUpdate.exe -auto“ v plánovači úloh v požadovanou dobu, dojde k zastavení spuštěné služby a k automatické aktualizaci programu.


ApiComPremForm: HTTP klient

Slouží ke skládání, vyzkoušení a ladění všech odesílaných příkazů.

URI  IP adresa, na které běží serverová část http server.

Port - musí být shodný s nastavením.

ID UJ - ID účetní jednotky, musí být shodné s nastavením.

Basic authorization - je označení pro jednoduchou autentizaci při přístupu na webové stránky. Webový server vyzve pomocí protokolu HTTP přistupujícího klienta, aby poslal v rámci požadavku na stránku také autentizační informace (tj. jméno a heslo).

Uživatel - uživatel zavedený v IS Premier, s nastavenými právy pro čtení, případně zápis podle potřeby s ohledem na možnosti vzdáleného přístupu do systému.

Heslo - uživatel MUSÍ mít vložené heslo.

Create JSON Post - vytvoření příkazu.

JSON parametry, kde je uvedena hodnota null, se nemusí odesílat na server, v příkladech jsou uváděny pro přehlednost.

Post - odeslání vytvořeného příkazu

Pokud vrácená data (JSON Result) jsou příliš velké, vytvoří se soubor s výstupem v podadresáři /temp. Ukládají se zde i stáhnuté obrázky.



Strukturu každé tabulky naleznete vždy u konkrétního příkazu v aplikaci ApiComPrem zde:


ApiComPremUpdate.exe

Spuštěním lze program aktualizovat z FTP. Parametrem -auto se provede aktualizace na pozadí (v případě že se používa služba ApiComPremService).


Popis testovacího webové rozhraní


Naleznete jej na adrese:               https://dev.premier.cz
Přihlašovací jméno:                      test
Heslo:                                             test

Stránka je vytvořená pro to, aby vývojář pro přehled, vyzkoušení všech možností API a otestování zasílání dat, nemusel instalovat Premier a SQL.

Zároveň je v Helpu uveden seznam všech aktuálně podporovaných příkazů a aktuální přehled datových typů databázových položek použitých tabulek. Ten v tomto manuálu není uveden, je generován automaticky a projeví se již v okamžiku aktualizace databáze (např. rozšíření velikosti databázové položky). Zásadnější změny, pokud se nebude jednat jen o pouhé rozšíření velikosti, budou zapsány v tomto manuálu. Současně se přidání nových databázových položek nebo příkazů projeví v daném prostředí obvykle rychleji, než následně dojde k aktualizaci manuálu.

Parametry pro vnější komunikaci naleznete na úvodní stránce:

Popis dostupných příkazů



Naleznete zde popis všech použitelných příkazů, včetně aktuálních přehledů struktur jednotlivých tabulek, který tento manuál jen doplňuje. Případné zásadní změny ve verzích databáze, budou zohledněny a popsány v manuálu.

Zobrazené datové typy


char                                 textový typ
decimal                           číselný typ
datetime                         datum + čas
timestamp
bit                                   logický typ (true/false)
uniqueidentifier            jednoznačný identifikátor 


Pokud kliknete na kterékoliv stránce na tuto značku:

Zobrazí se naposledy odeslaný příkaz společně s vráceným výsledkem:


Obecně pro tvůrce

  • s API musí probíhat pouze synchronní komunikace
  • nelze poslat souběžné požadavky s jedné IP adresy
  • každý příkaz musí projít přes open/close spojení k API

Pro tvůrce v PHP

Tipy na nástroje pro tvorbu

PHP a mySQL – naleznete také pod XAMPP (apachefriends.org)

BRACKETS (brackets.io) – open source textový editor

BOOTSRAP (getbootstrap.com) – komponenty a templates

DataTables (datatables.net) – HTML tabulky

Font Awesome (fontawesome.com) – ikonky

URL kódování 

Upozornění

Při odesílaní dat ve formátu JSON z webového prohlížeče přes HTTP, je nutné všechny použité rezervované znaky nahradit pomocí znaku procenta, viz. tabulka níže. V opačném případě se vrátí chybové hlášení a data se nevloží. Při čtení obdobných dat s rezervovanými znaky, k žádné chybě nedochází a zakódována nejsou. Rovněž při použití v C# je to v pořádku a není nutné nic měnit.

Například po vložení řetězce „Johnson & Johnson“, je pak nutné jej odesílat ve tvaru „Johnson %26 Johnson"

Rezervované znaky a jejich zakódování
(https://cs.wikipedia.org/wiki/URL_kódování)

Vzorový příklad komunikace s Premier API


Výběr prvních deseti záznamů z tabulky partnerů příkazem SELECT

<?php

$DEBUG = false;

$crlf = "\r\n";

$data_in = '';

 

// hodnoty pro testovací server – doplňte váš požadovaný server

$host = 'ssl://'.'dev.premier.cz';

// ID účetní jednotky
$iduj = '3ce17312-8dbd-49a0-94d3-2e01b65e4173';
// číslo portu

$port = 12375;

 

// vlastní příkaz

$data = '{"select": "select top 10 * from partnery"}';

$odpoved = sendAndRec($data);

 

// výpis vrácených hodnot nebo případné chyby

if ($odpoved->status == 200){   

    echo 'Status: '.$odpoved->content->result.'<br>'  ;

    echo 'CommandIn: '.$odpoved->content->commandin.'<br>';

   

    if ($odpoved->content->error != null)   { 

        echo 'Chyba: '.json_encode((array)$odpoved->content->error).'<br>';

    }

   

    if ($odpoved->content->warning != null ){     

        echo 'Varování: '.json_encode((array)$odpoved->content->warning).'<br>';

    }

   

   if ($odpoved->content->data != null ){ 

        echo 'Data: <br>';

        echo json_encode((array)$odpoved->content->data, JSON_PRETTY_PRINT);

   }

} else {

    echo $odpoved->status ;

}

 

// funkce pro odeslání požadavku

function sendAndRec($data){

                global $DEBUG, $crlf,$host,$port,$iduj ;

                $response = "";   

                // vytvorim objekt pre vysledok

                $urlinfo = new stdClass;    

                $urlinfo->status = 0 ;

                $urlinfo->errstr = '';

                $errno = 0;

$errstr = '';

try{

                $fp = fsockopen($host, $port, $errno, $errstr, 30);

}

                               catch(\Exception $ex) {      //used back-slash for global namespace

array_push($errors, $errstr);

                }

   

                if ($fp) {

                                $msg  = 'POST /api/comm HTTP/1.1' . "\r\n";

                $msg .= 'Content-Type: application/json; charset=utf-8' . $crlf;

                                $msg .= 'Content-Length: ' . strlen($data) . $crlf;

                               $msg .= 'ID-UJ: '. $iduj . $crlf;

                $msg .= 'Host: ' . $host . $crlf;

                                $msg .= 'Connection: close' . $crlf . $crlf;

                $msg .= $data;                    

                               $_SESSION['data_out'] = $msg;

                                if (fwrite($fp, $msg) ) {

                while ( !feof($fp) ) {

                $response .= fgets($fp, 1024);

                                               }

                }

                               fclose($fp);          

                } else {

                               $response = false;

                }

 

                if ($response===false){

                               $urlinfo->errstr = 'chyba připojení';

                               return $urlinfo;

                }

 

                $lines = explode(PHP_EOL, $response);

                $code = trim(substr($lines[0],9,4));

               
                // if no status code default to 400 = Bad Request

                if(empty($code) || !is_numeric($code)){

                               $code = 400;

                }

                if ($code!=200){

                               $urlinfo->status = $code ;

                               $urlinfo->errstr = 'chybný status stránky';

                               return $urlinfo;

                }

 

                $result  = explode("\r\n\r\n", $response, 2);

                $header = isset($result[0]) ? $result[0] : '';


                // parse headers

                $headers = array();

                $lines = explode($crlf, $header);

                foreach ($lines as $line) {

                               if (($pos = strpos($line, ':')) !== false) {

                                               $headers[strtolower(trim(substr($line, 0, $pos)))] = trim(substr($line, $pos + 1));

                                }

                }

 

                // načtu info o HTTP

                $urlinfo->status = $code ;

                $urlinfo->header = $headers;

                $urlinfo->content_type = get_content_type($result[0]);


                // načtu data

                $url_data = new stdClass;

                $content = isset($result[1]) ? $result[1] : '';

                $jsonobj = json_decode($content);

                $url_data->result = isset($jsonobj->Result) ? $jsonobj->Result : '' ;

                $url_data->commandin = isset($jsonobj->CommandIn) ? $jsonobj->CommandIn : '';

                $url_data->error = isset($jsonobj->Error) ? $jsonobj->Error : '';

                $url_data->warning = isset($jsonobj->Warning) ? $jsonobj->Warning : '';

                $url_data->data = isset($jsonobj->Data) ? $jsonobj->Data : '';

               
                // zapíšu data

                $urlinfo->content = $url_data;

                $urlinfo->json_out = $msg ;

                $urlinfo->data_in = $response;               

                return $urlinfo;

}

 

// zkontroluje záhlaví content-type, pro správné zobrazení obrázků

function get_content_type($headers){

                $content_type = "";

                if(!empty($headers)){

                               $headerarray = explode("\r\n", $headers);

                               foreach($headerarray as $head){

                               //             echo "header item = ".$head;

                                               if(preg_match("/Content-Type: .+$/i",$head)){

                                                               $content_type = $head;

                                                               break;

                                               }                                                            

                               }

                }

                return $content_type ;

}

 

?>

Pro tvůrce v C#

Tipy na nástroje pro tvorbu

Online převod JSON do C# třídy: https://app.quicktype.io/#

Vzorový příklad komunikace s Premier API

Výběr prvních deseti záznamů z přijatých objednávek (tabulka OB_IN) příkazem SELECT

using System;

using System.IO;

using System.Net;

using System.Text;

 

namespace TestApiConsole {

    class Program {

       

        // doplňte správnou adresu

        static readonly Uri url = new Uri(@"https://dev.premier.cz:12375/api/comm");
       
        // vlastní příkaz

        static readonly string JsonSendData = "{\"select\": \"select top 10 * from ob_in\"}";

    

        static void Main(string[] args) {

            string responseFromServer = "";

            if (!SendAnRec(url, JsonSendData, ref responseFromServer)) {

                Console.WriteLine("Err:"+responseFromServer);

            }

            else {

                // zpracovat odpověď (Result) z Premier API

                Console.WriteLine(responseFromServer);

            }

            Console.ReadKey();

        }

 

        private static bool SendAnRec(Uri url, string JsonData, ref string responseFromServer) {

            bool odpoved = true;

            byte[] byteArray = Encoding.UTF8.GetBytes(JsonData);

            responseFromServer = "";

            // Nastavení TSL 1.2 pro případ, že to server vyžaduje
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11;

            WebRequest request = WebRequest.Create(url);

            request.Method = "POST";


// doplňte správné ID účetní jednotky

            request.Headers.Add("ID-UJ: 3ce17312-8dbd-49a0-94d3-2e01b65e4173");

            request.ContentType = "application/json; charset=utf-8";

            request.ContentLength = byteArray.Length;

            request.Timeout = 200000;

 

// pošlu údaje

            try {

                using (var dataStream = request.GetRequestStream()) {

                    dataStream.Write(byteArray, 0, byteArray.Length);

                }

            }

            catch (Exception ex) {

                responseFromServer = ex.Message;

                return false;

            }

 

// čekám na odpověď

            try {

                Encoding enc = new UTF8Encoding(true, true);

                using (var response = request.GetResponse()) {

                    using (var stream = new StreamReader(response.GetResponseStream(), enc)) {

                        responseFromServer = stream.ReadToEnd();

                    }

                }

            } 

// zpracování výjimek

            catch (WebException ex) {

                if (ex.Status == WebExceptionStatus.ProtocolError) {

                    using (var stream = new StreamReader(ex.Response.GetResponseStream())) {

                        responseFromServer = stream.ReadToEnd();

                        odpoved = false;

                    }

                }

                else {

                    if (ex.Status == WebExceptionStatus.Timeout) {

                        responseFromServer = "{Request timeout is expired.}";

                        odpoved = false;

                    }

                    else {

                        responseFromServer = "{" + ex.Message + "}";

                        odpoved = false;

                    }

                }

            }

            return odpoved;

        }

    }

}