abra-flexi 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # ABRA Flexi Typescript client
2
2
  **English version follows**
3
3
 
4
- [ABRA Flexi](https://www.abra.eu/flexi/) je český Ekonomický software pro malé a střední podniky vyvýjený společností [ABRA Software](https://www.abra.eu). ABRA Flexi poskytuje přístup k datům pomocí REST API s veřejnou dokumentací.
4
+ [ABRA Flexi](https://www.abra.eu/flexi/) je český Ekonomický software pro malé a střední podniky vyvýjený společností [ABRA Software](https://www.abra.eu). ABRA Flexi poskytuje přístup k datům pomocí REST API s veřejnou dokumentací.
5
5
 
6
- ABRA Flexi Typescript client (AFTC) je knihovna pro přístup k REST API ABRA Flexi. Použitelná v prohlížeči i na serveru (Node.js) a poskytující metody pro dotazování a mutaci dat.
6
+ ABRA Flexi Typescript client (AFTC) je knihovna pro přístup k REST API ABRA Flexi. Použitelná v prohlížeči i na serveru (Node.js) a poskytující metody pro dotazování a mutaci dat.
7
7
 
8
8
  AFTC obsahuje třídu pro každou z evidencí v ABRA Flexi. Vlastnosti těchto tříd zároveň modelují vztahy mezi evidencemi.
9
9
 
@@ -16,29 +16,13 @@ Knihovna je distribuovaná jako npm balíček
16
16
  npm install abra-flexi
17
17
  ```
18
18
 
19
- **!! Zatím nedistribuováno !!**
20
- Místo npm použíjte přímo git repository a `npm link` (knihovnu mustí nejdříve přeložit).
21
- ```
22
- git checkout git@github.com:tomasKavan/abraFlexiClient.git
23
- cd abraFlexiClient
24
- npm install
25
- npm run build:lib
26
- npm link
27
- ```
28
-
29
- Ve svém projektu potom:
30
-
31
- ```
32
- npm link abra-flexi
33
- ```
34
-
35
- ### Gnerování evidenčních tříd
19
+ ### Generování evidenčních tříd
36
20
 
37
21
  Součástní repository knihovny je nástroj pro generování evidenčních tříd z metadat, které poskytuje API. Generátor dovoluje vybrat subset generovaných tříd. To je užitečné, pokud chcete udržet knihovnu co nejmenší a generovat pouze třídy pro Vámi použivané evidence.
38
22
 
39
23
  Generátor je třeba přeložit
40
24
  ```
41
- npm run build:cli
25
+ npm run build:gen
42
26
  chmod a+x ./bin/index.js
43
27
  ```
44
28
 
@@ -49,12 +33,12 @@ a následně lze generování spustit například takto:
49
33
 
50
34
  knihovnu s vygenerovanými třídami lze přeložit
51
35
  ```
52
- npm run build:cli
36
+ npm run build
53
37
  ```
54
38
 
55
39
  ### Příklady
56
40
 
57
- Složka `examples` obsahuje příklady použití knihovny. Prozatím jsem připravil
41
+ Složka `examples` obsahuje příklady použití knihovny. Prozatím jsem připravil
58
42
  - jednoduchý CLI nástroj pro načtení konkrétní evidence (loadEntityCli.ts)
59
43
  - soubor s příkladem volání více typů dotazů (loadEntity.ts)
60
44
  - příklad vytvoŕení nové faktury. Nutno nastavit konstanty na řádku 29-31 (createAndSave.ts)
@@ -69,34 +53,59 @@ ts-node examples/createAndSave.ts -s https://muj.server.cz -c moje-firma -u uziv
69
53
 
70
54
  V návodu se používají pojmy
71
55
  - **evidence** Typ evidence v systému ABRA Flexi, například Faktura přijatá či Položka faktury přijaté. Každá evidence je reprezentováná evidenční třídou, například `AFFakturaPrijata` či `AFPolozkaFakturyPrijate`.
72
- - **instance** Záznam v evidenci. Například faktura přijatá s kódem IV24.0001, identifikátorem 14 a dlšími hodnotami je instancí. Instance je v knihovně reprezentována instancí evidenční třídy, například: `const fp = new AFFakturaPrijata()`.
56
+ - **instance** Záznam v evidenci. Například faktura přijatá s kódem IV24.0001, identifikátorem 14 a dlšími hodnotami je instancí. Instance je v knihovně reprezentována instancí evidenční třídy, například: `const fp = await api.create(AFFakturaPrijata)`.
73
57
 
74
58
  ### Import a inicializace knihovny
75
59
 
76
- ```typescript
77
- import { AFApiClient } from 'abra-flexi'
60
+ ```typescript
61
+ import { AFApiClient, AFApiConfig } from 'abra-flexi'
78
62
 
79
63
  const apiOpts: AFApiConfig = {
80
64
  url: '<doplnte_server>',
81
- company: '<doplnte_spolecnost>
65
+ company: '<doplnte_spolecnost>'
82
66
  }
83
67
 
84
68
  const api = new AFApiClient(apiOpts)
85
69
  ```
86
70
 
71
+ ### Logování
72
+
73
+ Ve výchozím stavu je klient tichý (nevypisuje nic). Logování lze zapnout pomocí `logLevel`:
74
+
75
+ ```typescript
76
+ const api = new AFApiClient({
77
+ url: '<server>',
78
+ company: '<spolecnost>',
79
+ logLevel: 'debug' // 'none' | 'error' | 'warn' | 'info' | 'debug'
80
+ })
81
+ ```
82
+
83
+ Je možné také předat vlastní logger (např. Winston), který musí implementovat rozhraní `{ debug, info, warn, error }`:
84
+
85
+ ```typescript
86
+ import winston from 'winston'
87
+
88
+ const api = new AFApiClient({
89
+ url: '<server>',
90
+ company: '<spolecnost>',
91
+ logger: winston.createLogger({ ... }),
92
+ logLevel: 'info' // pokud neuvedete, přeposílají se všechny úrovně
93
+ })
94
+ ```
95
+
87
96
  ### Dotazování instancí
88
97
 
89
- Pro dotazování instancí jsou k dispozici metody `query` (vrací kolekci instancí evidenčních tříd) a `queryOne` (vrací jedu instanci).
98
+ Pro dotazování instancí jsou k dispozici metody `query` (vrací kolekci instancí evidenčních tříd) a `queryOne` (vrací jedu instanci).
90
99
 
91
100
  Parametrech dotazu může být uveden požadovaný detail. Detail se zapisuje formou pole vlastností evidencí formou textových řetězců. Detaily relací je možné vkládat pomocí pole o 2 prvcích, kde prvním je název vlastnosti relace v evidenci a druhým opět pole vlastností v napojené evidenci. Jasněji je to patrné z příkladu níže.
92
101
 
93
102
  Pozor! Vnořené M:N či 1:N relací knihovna zatím podporuje pouze na základní úrovni. Například detail `faktura-vydana-polozka.cenik.atributy.typAtributu` se načte pouze po instanci ceníku. Bude řešeno v dalších verzích.
94
103
 
95
- Filtrování je možné vkládat pomocí metod `Filter(expr, params)`, `ID(id)` nebo `CODE(code)`. Metoda `Filter` akceptuje textovou šablonu (`expr`), ve které budou všechny návěstí nahrazeny hodnotami předanými ve druhém argumentu (`params`). Návěstí mohou být:
104
+ Filtrování je možné vkládat pomocí metod `Filter(expr, params)`, `ID(id)`, `CODE(code)` nebo `EXT(ext)`. Metoda `Filter` akceptuje textovou šablonu (`expr`), ve které budou všechny návěstí nahrazeny hodnotami předanými ve druhém argumentu (`params`). Návěstí mohou být:
96
105
  - `:key` se nahradí textovou hodnotou klíče `key` ve druhém argumentu,
97
106
  - `::mujKod` se nahradí textovou hodnotou klíče `mujKod` ve druhém argumentu a prefixuje se předponou `code:`,
98
107
 
99
- Obě návěští podporují kolekce. Návěstí se pak nahradí výsledkem `kolekce.join(',')`. Pokud je hodnotou instance evidence (podtřída AFEntity), ignoruje se typ návěští a použije se číselné id resp. kód, není-li číselné id vyplněno/načteno.
108
+ Obě návěští podporují kolekce. Návěstí se pak nahradí výsledkem `kolekce.join(',')`. Pokud je hodnotou instance evidence (podtřída AFEntity), ignoruje se typ návěští a použije se číselné id resp. kód, není-li číselné id vyplněno/načteno.
100
109
 
101
110
  Metoda `Filter` vrací objekt s mající následující metody:
102
111
  - `use(filter, op)` - přidá jiný objekt filtru do stávajícího. Atribut `op` určuje operátor pro spojení se stávajícím filtrem (`and` nebo `or`, defaultní je `or`). Filtr je vložen v závorkách.
@@ -106,19 +115,17 @@ Metoda `Filter` vrací objekt s mající následující metody:
106
115
 
107
116
  Všechny metody vrací vždy novou instanci filtru. Filtry si tedy můžete předpřipravit a následně spojivat pomocí `use()`/`useNot()`.
108
117
 
109
- ```typescript
118
+ ```typescript
110
119
  import { AFInterniDoklad, Filter } from 'abra-flexi'
111
120
 
112
121
  const queryOpts: AFQueryOptions = {
113
122
  detail: ['id', 'kod', 'typDokl', ['uzivatelske-vazby', ['id', 'evidenceType', 'objectId', 'vazbaTyp']]],
114
123
  filter: Filter(`typDokl = '::td'`, { td: 'MUJ_TYP_DOKLADU'})
115
- }
116
-
117
- const query = api.query(AFInterniDoklad, queryOpts)
124
+ }
118
125
 
119
126
  const run = async () => {
120
127
  try {
121
- const data = await query
128
+ const data = await api.query(AFInterniDoklad, queryOpts)
122
129
  console.log(data)
123
130
  } catch (e) {
124
131
  console.error(e)
@@ -128,22 +135,49 @@ run()
128
135
 
129
136
  ```
130
137
 
138
+ ### Stránkování a celkový počet záznamů
139
+
140
+ Dotazy podporují parametry `limit` a `start` pro stránkování. Pro zjištění celkového počtu záznamů (nezávislého na velikosti stránky) předejte `addRowCount: true`. Výsledek metody `query()` je standardní pole, ale navíc obsahuje vlastnost `totalCount` s celkovým počtem nalezených záznamů na serveru.
141
+
142
+ ```typescript
143
+ const PAGE_SIZE = 50
144
+
145
+ const firstPage = await api.query(AFAdresar, {
146
+ detail: ['id', 'kod', 'nazev'],
147
+ limit: PAGE_SIZE,
148
+ start: 0,
149
+ addRowCount: true // vyžádá @rowCount od serveru
150
+ })
151
+
152
+ console.log(firstPage.totalCount) // např. 1234 — celkový počet, bez ohledu na limit
153
+ console.log(firstPage.length) // 50 — velikost stránky
154
+
155
+ const totalPages = Math.ceil(firstPage.totalCount! / PAGE_SIZE)
156
+
157
+ // Další stránky — addRowCount není třeba opakovat (zvyšuje zátěž serveru)
158
+ const secondPage = await api.query(AFAdresar, {
159
+ detail: ['id', 'kod', 'nazev'],
160
+ limit: PAGE_SIZE,
161
+ start: PAGE_SIZE
162
+ })
163
+ ```
164
+
165
+ > **Poznámka:** `addRowCount: true` způsobí extra COUNT dotaz na straně serveru. Používejte ho pouze pro první stránku a hodnotu `totalCount` si uložte pro výpočet dalších stránek.
166
+
131
167
  ### Načtení většího detailu
132
168
 
133
169
  Pro načtení většího detailu či aktualizaci dříve načtených instancí můžete použít metody `populate` a `populateOne`. Detailem požadované vlastnosti se aktualizují na původní instanci.
134
170
 
135
- ```typescript
171
+ ```typescript
136
172
  const loadedEntity // Zde máme uloženou dříve načtenou instanci entity AFInterniDoklad
137
173
 
138
174
  const options = {
139
175
  detail: AFQueryDetail.FULL
140
176
  }
141
177
 
142
- const populate = api.populateOne(loadEntity)
143
-
144
178
  const run = async () => {
145
179
  try {
146
- const populatedEntity = await populate
180
+ const populatedEntity = await api.populateOne(loadedEntity, options)
147
181
  console.log(populatedEntity)
148
182
  console.log(populatedEntity === loadedEntity) // Vypíše true
149
183
  } catch (e) {
@@ -156,20 +190,18 @@ run()
156
190
 
157
191
  ### Načtení instancí z uživatelských vazeb
158
192
 
159
- Instance entit odkazované pomocí uživatelských vazeb je možné načíst metodou `queryURel`. Metodě je třeba zaslat zdrojovou kolekci instancí. Instance neumsí být stejného typu (evidence), ale musí mít klíč `uzivatelske-vazby`. Metoda načítá vazby na kokrétní evidenci (vazby ostatních typů jsou vynechány). Vazby je možné filtrovat dle typu `typVazby`. Načtená data jsou přidána do vazeb zrojových instancí (vlastnost `object`). Data jsou dále vrácena jako kolekce párů `{ entity: zdrojova_entity, referencedFrom: načtená_entita }`.
193
+ Instance entit odkazované pomocí uživatelských vazeb je možné načíst metodou `queryURels`. Metodě je třeba zaslat zdrojovou kolekci instancí. Instance neumsí být stejného typu (evidence), ale musí mít klíč `uzivatelske-vazby`. Metoda načítá vazby na kokrétní evidenci (vazby ostatních typů jsou vynechány). Vazby je možné filtrovat dle typu `typVazby`. Načtená data jsou přidána do vazeb zrojových instancí (vlastnost `object`). Data jsou dále vrácena jako kolekce párů `{ entity: zdrojova_entity, referencedFrom: načtená_entita }`.
160
194
 
161
- Načteny jsou pouze instance vazeb, které se již vyskytují ve zdrojové kolekci! Metoda `queryURel` tedy sama uživatelské vazby na jednotlivých zdrojových instancích nenačítá. Načítá pouze instance, na které vazba odkazuje.
195
+ Načteny jsou pouze instance vazeb, které se již vyskytují ve zdrojové kolekci! Metoda `queryURels` tedy sama uživatelské vazby na jednotlivých zdrojových instancích nenačítá. Načítá pouze instance, na které vazba odkazuje.
162
196
 
163
- ```typescript
197
+ ```typescript
164
198
  const soureEntities // Zde máme kolekci entit, pro které načítáme instance odkazované uživatelskou vazbou
165
199
 
166
- const query = api.queryURels(AFInterniDoklad, soureEntities, {
167
- detail: ['id', 'kod', 'typDokl']
168
- })
169
-
170
200
  const run = async () => {
171
201
  try {
172
- const pairs = await query
202
+ const pairs = await api.queryURels(AFInterniDoklad, soureEntities, {
203
+ detail: ['id', 'kod', 'typDokl']
204
+ })
173
205
  console.log(pairs)
174
206
  } catch (e) {
175
207
  console.error(e)
@@ -181,21 +213,136 @@ run()
181
213
 
182
214
  ### Práce se štítky
183
215
 
184
- *TODO: Přidat popis*
216
+ Štítky (stitky) jsou na každé instanci dostupné jako textový řetězec ve vlastnosti `stitky`. Pro pohodlnější práci jsou k dispozici metody `getStitky()` a `getStitkyBySkupina(skup)`, které vrací pole instancí `AFStitek`.
217
+
218
+ Knihovna udržuje interní cache štítků, která se průběžně aktualizuje. Strategii cache lze nastavit v konfiguraci klienta pomocí `stitkyCacheStrategy` (`None`, `Lazy` (výchozí), `Eager`).
185
219
 
186
220
  ### Vytváření, změna a odstraňování instancí
187
221
 
188
- *TODO: Přidat popis*
222
+ #### Vytvoření nové instance
223
+
224
+ Novou instanci evidence vytvoříte pomocí `api.create()`. **Nepoužívejte `new` přímo** — konstruktor vyžaduje interní závislosti, které správně injektuje pouze klient.
225
+
226
+ ```typescript
227
+ import { AFFakturaVydana } from 'abra-flexi'
228
+
229
+ const faktura = await api.create(AFFakturaVydana)
230
+ faktura.popis = 'Testovací faktura'
231
+ // ... nastavte další vlastnosti
232
+
233
+ const saved = await api.save(faktura)
234
+ console.log(saved.id) // ID přidělené serverem je automaticky přiřazeno zpět na instanci
235
+ ```
236
+
237
+ #### Odkaz na existující záznam bez načítání
238
+
239
+ Pokud potřebujete odkázat na existující záznam (např. jako relaci na jiné entitě), ale nechcete ho načítat, použijte `createIdStub()`:
240
+
241
+ ```typescript
242
+ const firma = await api.createIdStub(AFAdresar, { id: 123 })
243
+ // nebo podle kódu:
244
+ const firma = await api.createIdStub(AFAdresar, { kod: 'MOJE_FIRMA' })
245
+ ```
246
+
247
+ Instance vytvořená přes `createIdStub()` je správně označena jako existující (`isNew === false`) a lze ji použít v relacích jiných entit.
248
+
249
+ #### Uložení změn
250
+
251
+ Metoda `save()` uloží jak novou instanci (POST/PUT), tak změny na existující. Na server se odesílají pouze změněné vlastnosti.
252
+
253
+ ```typescript
254
+ const faktura = await api.queryOne(AFFakturaVydana, { filter: ID(42) })
255
+ faktura.popis = 'Aktualizovaný popis'
256
+ await api.save(faktura)
257
+ ```
258
+
259
+ **Poznámka k uživatelským vazbám:** ABRA Flexi nepodporuje vytvoření nové entity společně s uživatelskými vazbami v jednom požadavku. Pokud entita obsahuje `uzivatelske-vazby`, je třeba ji nejdříve uložit bez vazeb a vazby přidat v samostatném volání `save()`.
260
+
261
+ #### Smazání instance
262
+
263
+ ```typescript
264
+ await api.delete(faktura)
265
+ ```
266
+
267
+ Metoda funguje pro všechny typy evidencí, včetně `AFUzivatelskaVazba` (kde ABRA API nevystavuje standardní DELETE endpoint — knihovna to řeší automaticky).
268
+
269
+ ### Stahování souborů
270
+
271
+ Metoda `queryFile()` slouží ke stažení dat v jiném formátu než JSON — například PDF, XML, CSV nebo ISDOC. Vrací objekt `{ blob, contentType, filename }`.
272
+
273
+ ```typescript
274
+ import { AFFakturaVydana, ID } from 'abra-flexi'
275
+ import fs from 'fs'
276
+
277
+ // PDF faktury
278
+ const pdf = await api.queryFile(AFFakturaVydana, 'pdf', { filter: ID(42) })
279
+ fs.writeFileSync(pdf.filename ?? 'faktura.pdf', Buffer.from(await pdf.blob.arrayBuffer()))
280
+
281
+ // XML
282
+ const xml = await api.queryFile(AFFakturaVydana, 'xml', { filter: ID(42) })
283
+
284
+ // CSV kolekce
285
+ const csv = await api.queryFile(AFAdresar, 'csv', { limit: 100 })
286
+
287
+ // ISDOC / ISDOCX a další rozšíření
288
+ const isdocx = await api.queryFile(AFFakturaVydana, 'isdocx', { filter: ID(42) })
289
+ ```
290
+
291
+ #### Tisk pomocí pojmenované sestavy
292
+
293
+ Parametry `reportName` a `reportLang` umožňují zvolit konkrétní tiskovou sestavu:
294
+
295
+ ```typescript
296
+ const pdf = await api.queryFile(AFFakturaVydana, 'pdf', {
297
+ filter: ID(42),
298
+ reportName: 'faktura-dph',
299
+ reportLang: 'cs'
300
+ })
301
+ ```
302
+
303
+ #### Seznam dostupných sestav
304
+
305
+ Sestavy dostupné pro danou evidenci lze získat metodou `queryReports()`. Vrací pole `AFReportInfo` — každá obsahuje mimo jiné unikátní `reportId` (to je hodnota posílaná do `reportName`) a seznam podporovaných jazyků (`languages.language[]`).
306
+
307
+ Objekt `AFReportInfo` (resp. `AFReportLanguage` z jeho `languages.language`) lze předat přímo do `reportName` / `reportLang` v `queryFile()` — knihovna z něj vytáhne správné hodnoty:
308
+
309
+ ```typescript
310
+ const reports = await api.queryReports(AFFakturaVydana)
311
+ const reportDef = reports.find(r => r.isDefault === 'true') ?? reports[0]
312
+
313
+ const pdf = await api.queryFile(AFFakturaVydana, 'pdf', {
314
+ filter: ID(42),
315
+ reportName: reportDef, // → reportId
316
+ reportLang: reportDef.languages?.language.find(l => l.code === 'en') ?? 'cs'
317
+ })
318
+ ```
319
+
320
+ ### Akce na instancích
321
+
322
+ Pojmenované akce definované v ABRA Flexi (např. storno, uhrazení zápočtem) lze volat metodou `action()`. Instance musí být uložená (mít přidělené `id`) nebo v rozpoznatelném stavu — pokud má pouze `kod` / `ext`, klient si nejdříve dohledá `id`.
323
+
324
+ Pro každou evidenci s reálnými business akcemi generátor vyrobí samostatný TypeScript enum (např. `AFFakturaPrijataAction`). CRUD pseudo-akce (`new`/`edit`/`delete`/`copy`) jsou vynechány — používají se přes dedikované metody `create()`, `save()` a `delete()`. Akce se validují podle metadat dané evidence; volání nepodporované akce skončí chybou ještě před jakýmkoli HTTP požadavkem.
325
+
326
+ ```typescript
327
+ import { AFFakturaPrijata, AFFakturaPrijataAction } from 'abra-flexi'
328
+
329
+ const faktura = await api.resolveStubId(AFFakturaPrijata, 2452)
330
+ await api.action(faktura, AFFakturaPrijataAction.Storno)
331
+ // nebo s textovou hodnotou:
332
+ await api.action(faktura, 'uhrad-zapoctem')
333
+ ```
189
334
 
190
335
  ## Task list pro vydání stabilní verze
191
336
 
192
337
  - [X] Generování evidenčních tříd dle metadat REST API
193
338
  - [X] Načítání kolekcí a jednotlivých instancí z REST API
194
- - [X] Načítání užviatelských relací
339
+ - [X] Načítání užviatelských relací
195
340
  - [X] Refersh již načtených dat
196
341
  - [X] Pohodlná práce se štítky
342
+ - [X] Vytváření, mazání a změna záznamů v REST API
343
+ - [X] Stahování souborů (PDF, CSV, XML, ISDOC, ...)
344
+ - [X] Akce na instancích
197
345
  - [ ] Jednotný handling identifikátorů záznamů
198
- - [x] Vytváření, mazání a změna záznamů v REST API
199
346
  - [ ] Lokální keš a společné instance pro jedno ID
200
347
  - [ ] Battle tested - reálné nasazení, úprava rozhraní dle reálného použití
201
348
  - [ ] Verzování, npm balíčkování
@@ -204,8 +351,8 @@ run()
204
351
 
205
352
  # English
206
353
 
207
- [ABRA Flexi]([https://www.abra.eu/flexi/](https://www.abra.eu/en/flexi/) is Czech ERP for small businesses, developer by [ABRA Software](https://www.abra.eu/en). To access data in ABRA Flexi there is a REST API with public documentation.
354
+ [ABRA Flexi](https://www.abra.eu/en/flexi/) is Czech ERP for small businesses, developed by [ABRA Software](https://www.abra.eu/en). To access data in ABRA Flexi there is a REST API with public documentation.
208
355
 
209
- ABRA Flexi Typescript client (AFTC) is library to call ABRA Fflexi REST API. It's for browsers and server environments (Node.js). It provides methods for CRUD.
356
+ ABRA Flexi Typescript client (AFTC) is library to call ABRA Flexi REST API. It's for browsers and server environments (Node.js). It provides methods for CRUD.
210
357
 
211
- Because ABRA Flexi is Czech software, it's expected to have mainly Czech developer audience. English version of the documentation will be finalized when the library API becomes stable.
358
+ Because ABRA Flexi is Czech software, it's expected to have mainly Czech developer audience. English version of the documentation will be finalized when the library API becomes stable.