├── .eslintrc
├── .gitignore
├── .prettierrc
├── LICENSE
├── README.md
├── images
├── favicon-32x32.png
└── promise-logo.png
├── package-lock.json
├── package.json
├── src
├── base.client.ts
├── contact
│ ├── contact.client.ts
│ └── contact.type.ts
├── country
│ ├── country.client.ts
│ └── country.type.ts
├── creditNote
│ ├── creditNote.client.ts
│ └── creditNote.type.ts
├── downPaymentInvoice
│ ├── downPaymentInvoice.client.ts
│ └── downPaymentInvoice.type.ts
├── eventSubscription
│ ├── eventSubscription.client.ts
│ └── eventSubscription.type.ts
├── file
│ ├── file.client.ts
│ └── file.type.ts
├── index.ts
├── invoice
│ ├── invoice-dto.type.ts
│ ├── invoice.client.ts
│ └── invoice.type.ts
├── orderConfirmation
│ ├── orderConfirmation.type.ts
│ └── orderConfrmation.client.ts
├── payment
│ ├── payment.client.ts
│ └── payment.type.ts
├── paymentCondition
│ ├── paymentCondition.client.ts
│ └── paymentCondition.type.ts
├── postingCategory
│ ├── postingCategory.client.ts
│ └── postingCategory.type.ts
├── profile
│ ├── profile.client.ts
│ └── profile.type.ts
├── quotation
│ ├── quotation.client.ts
│ └── quotation.type.ts
├── recurringTemplate
│ ├── recurringTemplate.client.ts
│ └── recurringTemplate.type.ts
├── request-error.ts
├── utils.ts
├── voucher
│ ├── voucher.client.ts
│ └── voucher.type.ts
└── voucherlist
│ ├── voucherlist.client.ts
│ └── voucherlist.type.ts
└── tsconfig.json
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@typescript-eslint/parser",
3 | "parserOptions": {
4 | "project": "tsconfig.json",
5 | "sourceType": "module"
6 | },
7 | "plugins": ["@typescript-eslint/eslint-plugin"],
8 | "extends": [
9 | "plugin:@typescript-eslint/eslint-recommended",
10 | "plugin:@typescript-eslint/recommended",
11 | "prettier/@typescript-eslint",
12 | "plugin:prettier/recommended"
13 | ],
14 | "root": true,
15 | "env": {
16 | "node": true,
17 | "jest": true
18 | },
19 | "rules": {
20 | "no-console": "warn",
21 | "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
22 | "@typescript-eslint/naming-convention": "error"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | *.log
4 | build
5 | dist
6 | .vscode
7 | example
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "printWidth": 100,
4 | "singleQuote": true,
5 | "trailingComma": "all",
6 | "semi": true
7 | }
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 elbstack GmbH
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
lexoffice-client-js
2 |
3 |
4 | Javascript client library for the lexoffice Public API.
5 |
6 |
7 |
fully typed
8 |
promise based
9 | ❌ non throwing
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
36 |
37 |
38 |
39 | ## 📦 Contents
40 |
41 | - [⚙️ Installation](#installation)
42 | - [📚 Documentation](#documentation)
43 | - [🔑 Usage](#usage)
44 | - [💡 Examples](#examples)
45 | - [Retrieve an invoice](#retrieve-an-invoice)
46 | - [Create an invoice](#create-an-invoice)
47 | - [Upload file](#upload-file)
48 | - [❌ Error handling](#error-handling)
49 | - [🛠 Provided methods grouped by endpoint](#provided-methods)
50 | - [Contact](#contact)
51 | - [Country](#country)
52 | - [Credit note](#credit-note)
53 | - [Down payment invoice](#down-payment-invoice)
54 | - [Event subscription](#event-subscription)
55 | - [File](#file)
56 | - [Invoice](#invoice)
57 | - [Order confirmation](#order-confirmation)
58 | - [Payment](#payment)
59 | - [Payment condition](#payment-condition)
60 | - [Posting category](#posting-category)
61 | - [Profile](#profile)
62 | - [Quotation](#quotation)
63 | - [Recurring Template](#recurring-template)
64 | - [Voucher](#voucher)
65 | - [Voucherlist](#voucherlist)
66 | - [🔖 Side notes](#side-notes)
67 |
68 |
69 | ---
70 |
71 | ⚙️ Installation
72 |
73 | ```bash
74 | npm install @elbstack/lexoffice-client-js
75 | ```
76 |
77 | or
78 |
79 | ```bash
80 | yarn add @elbstack/lexoffice-client-js
81 | ```
82 |
83 | 📚 Documentation
84 |
85 | You can find the official lexoffice API documentation [here](https://developers.lexoffice.io/docs/#lexoffice-api-documentation).
86 |
87 | 🔑 Usage
88 |
89 | To get your API Key, you must already be a lexoffice user. Get it [here](https://app.lexoffice.de/settings/#/public-api).
90 |
91 | ```ts
92 | import { Client } from '@elbstack/lexoffice-client-js';
93 |
94 | const client = new Client(YOUR_LEXOFFICE_API_KEY);
95 | ```
96 |
97 | 💡 Examples
98 |
99 | All functions are promise based. These promises are formatted by the [ts-results package](https://github.com/vultix/ts-results), for extended error handling see [Error handling](#error-handling).
100 |
101 | ### Retrieve an invoice
102 |
103 | ```ts
104 | const invoiceResult = await client.retrieveInvoice('caf4e0c3-c3e8-4a06-bcfe-346bc7190b2');
105 |
106 | if (invoiceResult.ok) {
107 | const invoice = invoiceResult.val;
108 | } else {
109 | console.error('An error occured');
110 | }
111 | ```
112 |
113 | ### Create an invoice
114 |
115 | ```ts
116 | const invoice = {
117 | voucherDate: '2017-02-22T00:00:00.000+01:00',
118 | address: {
119 | name: 'Bike & Ride GmbH & Co. KG',
120 | supplement: 'Gebäude 10',
121 | street: 'Musterstraße 42',
122 | city: 'Freiburg',
123 | zip: '79112',
124 | countryCode: 'DE',
125 | },
126 | lineItems: [
127 | {
128 | type: 'custom',
129 | name: 'Energieriegel Testpaket',
130 | quantity: 1,
131 | unitName: 'Stück',
132 | unitPrice: {
133 | currency: 'EUR',
134 | netAmount: 5,
135 | taxRatePercentage: 0,
136 | },
137 | discountPercentage: 0,
138 | },
139 | {
140 | type: 'text',
141 | name: 'Strukturieren Sie Ihre Belege durch Text-Elemente.',
142 | description: 'Das hilft beim Verständnis',
143 | },
144 | ],
145 | totalPrice: {
146 | currency: 'EUR',
147 | },
148 | taxConditions: {
149 | taxType: 'net',
150 | },
151 | shippingConditions: {
152 | shippingDate: '2017-04-22T00:00:00.000+02:00',
153 | shippingType: 'delivery',
154 | },
155 | title: 'Rechnung',
156 | introduction: 'Ihre bestellten Positionen stellen wir Ihnen hiermit in Rechnung',
157 | remark: 'Vielen Dank für Ihren Einkauf',
158 | };
159 |
160 | const createdInvoiceResult = await client.createInvoice(invoice, { finalize: true });
161 |
162 | if (createdInvoiceResult.ok) {
163 | const invoice = createdInvoiceResult.val;
164 | } else {
165 | console.error('An error occured');
166 | }
167 | ```
168 |
169 | ### Upload File
170 |
171 | ```ts
172 | let fs = require('fs');
173 | let FormData = require('form-data');
174 |
175 | let data = new FormData();
176 |
177 | data.append('file', fs.createReadStream(__dirname + '/yourFolder/yourFile'));
178 | data.append('type', 'voucher');
179 |
180 | const uploadedFileResult = await client.uploadFile(data);
181 |
182 | if (uploadedFileResult.ok) {
183 | const invoice = uploadedFileResult.val;
184 | } else {
185 | console.error('An error occured');
186 | }
187 | ```
188 |
189 | ❌ Error handling
190 |
191 | As mentioned above, the returned promises are formatted by ts-results which is a typescript implementation of Rust's [Result](https://doc.rust-lang.org/std/result/) and [Option](https://doc.rust-lang.org/std/option/) objects. It brings compile-time error checking and optional values to typescript.
192 | All errors are instances of type RequestError.
193 | If you want to use the advantages of ts-results, all client responses should be processed similar to the following:
194 |
195 | ```ts
196 | if (YOUR_RESULT.ok) {
197 | const YOUR_VARIABLE = YOUR_RESULT.val;
198 | // Work with your result
199 | } else {
200 | // Your request returned an error
201 | const error = YOUR_RESULT.val;
202 | console.log('error:', error);
203 | // Further error checking is possible by
204 | if (error instanceof RequestError) {
205 | console.log('Hello instance of RequestError!');
206 |
207 | if (error instanceof RequestNotFoundError) {
208 | console.log('Wow, it looks like you have many instances!');
209 | }
210 | if (error instanceof RequestMethodNotAcceptableLegacyError) {
211 | console.log('Seems, that you take care of your legacy!');
212 | }
213 | }
214 | }
215 | ```
216 |
217 | ### Error codes and types
218 |
219 |
220 | Regular errors |
221 |
222 | ❌ Error code |
223 | Error type |
224 | ❌ Server error code |
225 | Error type |
226 |
227 |
228 | 400 |
229 | RequestBadRequestError |
230 | 500 |
231 | RequestInternalServerError |
232 |
233 |
234 | 401 |
235 | RequestUnauthorizedError |
236 | 503 |
237 | RequestServiceUnavailableError |
238 |
239 |
240 | 402 |
241 | RequestPaymentRequiredError |
242 | 504 |
243 | RequestGatewayTimeoutError |
244 |
245 |
246 | 403 |
247 | RequestForbiddenError |
248 | |
249 | |
250 |
251 |
252 | 404 |
253 | RequestNotFoundError |
254 | |
255 | |
256 |
257 |
258 | 405 |
259 | RequestMethodNotAllowedError |
260 | |
261 | |
262 |
263 |
264 | 406 |
265 | RequestMethodNotAcceptableError |
266 | |
267 | |
268 |
269 |
270 | 409 |
271 | RequestConflictError |
272 | |
273 | |
274 |
275 |
276 | 415 |
277 | RequestUnsupportedMediaTypeError |
278 | |
279 | |
280 |
281 |
282 | 429 |
283 | RequestTooManyRequestsError |
284 | |
285 | |
286 |
287 | Legacy errors (used by the endpoints files, profiles and contacts) |
288 |
289 | 400 |
290 | RequestBadRequestLegacyError |
291 | 500 |
292 | RequestInternalServerLegacyError |
293 |
294 |
295 | 406 |
296 |
297 | RequestMethodNotAcceptableLegacyError |
298 | |
299 | |
300 |
301 |
302 |
303 | 🛠 Provided methods grouped by endpoint
304 |
305 | ### Contact
306 |
307 | ```ts
308 |
309 | createContact(contact: ContactCreatePerson | ContactCreateCompany): Promise>
310 |
311 | retrieveContact(id: string): Promise>
312 |
313 | updateContact(id: string, contact: ContactUpdatePerson | ContactUpdateCompany): Promise>
314 |
315 | filterContact(filter?: OptionalFilters & Partial): Promise>
316 | ```
317 |
318 | ### Country
319 |
320 | ```ts
321 | retrieveListOfCountries(): Promise>
322 | ```
323 |
324 | ### Credit note
325 |
326 | ```ts
327 | createCreditNote(creditNote: CreditNoteCreate, optionalFinalized?: OptionalFinalized): Promise>
328 |
329 | retrieveCreditNote(id: string): Promise>
330 |
331 | renderCreditNoteDocumentFileId(id: string): Promise>
332 | ```
333 |
334 | ### Down payment invoice
335 |
336 | ```ts
337 | retrieveDownPaymentInvoice(id: string): Promise>
338 | ```
339 |
340 | ### Event subscription
341 |
342 | ```ts
343 | createEventSubscription(eventSubscription: EventSubscriptionCreate): Promise>
344 |
345 | retrieveEventSubscription(id: string): Promise>
346 |
347 | retrieveAllEventSubscriptions(): Promise>
348 |
349 | deleteEventSubscription(id: string): Promise>
350 | ```
351 |
352 | ### File
353 |
354 | ```ts
355 | uploadFile(data: FormData): Promise>
356 |
357 | downloadFile(documentFileId: string, optionalParameter?: RenderType): Promise>
358 | ```
359 |
360 | ### Invoice
361 |
362 | ```ts
363 | createInvoice(invoice: InvoiceCreate | XRechnung, optionalFinalized?: OptionalFinalized): Promise>
364 |
365 | retrieveInvoice(id: string): Promise>
366 |
367 | renderInvoiceDocumentFileId(id: string): Promise>
368 | ```
369 |
370 | ### Order confirmation
371 |
372 | ```ts
373 | createOrderConfirmation(orderConfirmation: OrderConfirmation): Promise>
374 |
375 | retrieveOrderConfirmation(id: string): Promise>
376 |
377 | renderOrderConfirmationDocumentFileId(id: string): Promise>
378 | ```
379 |
380 | ### Payment
381 |
382 | ```ts
383 | retrievePayment(id: string): Promise>
384 | ```
385 |
386 | ### Payment condition
387 |
388 | ```ts
389 | retrievePaymentConditionList(): Promise>
390 | ```
391 |
392 | ### Posting category
393 |
394 | ```ts
395 | retrieveListPostingCategories(): Promise>
396 | ```
397 |
398 | ### Profile
399 |
400 | ```ts
401 | retrieveProfile(): Promise>
402 | ```
403 |
404 | ### Quotation
405 |
406 | ```ts
407 | createQuotation(quotation: QuotationCreate, optionalFilter?: OptionalFinalized): Promise>
408 |
409 | retrieveQuotation(id: string): Promise, RequestError>>
410 |
411 | renderQuotationDocumentFileId(id: string): Promise>
412 | ```
413 |
414 | ### Recurring template
415 |
416 | ```ts
417 | retrieveRecurringTemplate(id: string): Promise, RequestError>>
418 |
419 | retrieveAllRecurringTemplates(optionalFilter?: PagingParameters): Promise>
420 | ```
421 |
422 | ### Voucher
423 |
424 | ```ts
425 | createVoucher(voucher: CreateVoucher): Promise>
426 |
427 | retrieveVoucher(id: string): Promise, RequestError>>
428 |
429 | updateVoucher(id: string, voucher: CreateVoucher): Promise>
430 |
431 | filterVoucher(voucherNumber: VoucherNumber): Promise, RequestError>>
432 |
433 | uploadFileToVoucher(data: FormData, id: string): Promise>
434 | ```
435 |
436 | ### Voucherlist
437 |
438 | ```ts
439 | retrieveVoucherlist(filterParameter: FilterParameter): Promise>
440 | ```
441 |
442 | 🔖 Side notes
443 |
444 | ### Updating
445 |
446 | For updating any type of vouchers where the "version" property is required, you first need to retrieve it and use the current "version" value to properly update.
447 |
448 | ### Rendering Document File Id
449 |
450 | Only possible for any type of vouchers that are not in "draft" mode.
451 |
452 | ### Download File
453 |
454 | The required id is not the id itself, it is the documentFileId, which can be required with the matching method and the vouchers id:
455 |
456 | ```ts
457 | renderCreditNoteDocumentFileId(id);
458 | renderInvoiceDocumentFileId(id);
459 | renderOrderConfirmationDocumentFileId(id);
460 | renderQuotationDocumentFileId(id);
461 | ```
462 |
463 |
This package has been brought to you by elbstack!
464 |
465 | elbstack is a software engineering & design company. We question, we advise, and we're excited to help your next project to succeed.
466 | We offer software development and design as service. That's how we support you with the realisation of your projects - with individual employees or with a whole team. We love to work remotely, but we will work at your place, too.
467 |
468 | ### 👩🏻💻👨🏽💻 We are hiring!
469 |
470 | We are more than a classic software agency, rather like a highly self organized company with our own start-up incubator. You can choose how much, for whom and what you want to work for. Acquire your own customers and projects, simply make your sideproject become reality or even a proper start-up.
471 | Sounds like a scam?
472 |
473 | ➡️ Go and check kununu.com
474 | ➡️ elbstack.com
475 |
--------------------------------------------------------------------------------
/images/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elbstack/lexoffice-client-js/725253b4c8c32e42c244448a23e7d66221026b5f/images/favicon-32x32.png
--------------------------------------------------------------------------------
/images/promise-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elbstack/lexoffice-client-js/725253b4c8c32e42c244448a23e7d66221026b5f/images/promise-logo.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@elbstack/lexoffice-client-js",
3 | "version": "1.0.1",
4 | "description": "An universal client for the Lexoffice API written in Typescript",
5 | "main": "dist/index.js",
6 | "module": "dist/index.m.js",
7 | "umd:main": "dist/index.umd.js",
8 | "types": "dist/index.d.ts",
9 | "source": "src/index.ts",
10 | "scripts": {
11 | "build": "rm -rf dist && microbundle",
12 | "dev": "microbundle watch",
13 | "release": "npm run build && np",
14 | "lint": "eslint 'src/**/*.ts' --fix",
15 | "test": "echo \"No test specified\"",
16 | "start:example": "ts-node example/index.ts"
17 | },
18 | "repository": {
19 | "type": "git",
20 | "url": "git+https://github.com/elbstack/lexoffice-client-js.git"
21 | },
22 | "keywords": [
23 | "lexoffice",
24 | "api",
25 | "client",
26 | "javascript",
27 | "typescript",
28 | "node",
29 | "browser"
30 | ],
31 | "files": [
32 | "dist",
33 | "src"
34 | ],
35 | "author": "elbstack ",
36 | "contributors": [
37 | "Annaelle Rössert "
38 | ],
39 | "license": "MIT",
40 | "bugs": {
41 | "url": "https://github.com/elbstack/lexoffice-client-js/issues"
42 | },
43 | "homepage": "https://github.com/elbstack/lexoffice-client-js#readme",
44 | "publishConfig": {
45 | "access": "public"
46 | },
47 | "devDependencies": {
48 | "@types/jest": "^26.0.20",
49 | "@typescript-eslint/eslint-plugin": "^4.14.2",
50 | "@typescript-eslint/parser": "^4.14.2",
51 | "eslint": "^7.19.0",
52 | "eslint-config-prettier": "^7.2.0",
53 | "eslint-plugin-import": "^2.22.1",
54 | "jest": "^26.6.3",
55 | "local-package-publisher": "^1.0.4",
56 | "microbundle": "^0.13.1",
57 | "nock": "^13.0.7",
58 | "np": "^7.5.0",
59 | "prettier": "^2.2.1",
60 | "ts-node": "^9.1.1",
61 | "typescript": "^4.1.3"
62 | },
63 | "dependencies": {
64 | "axios": "^0.21.1",
65 | "form-data": "^4.0.0",
66 | "ts-results": "^3.1.1",
67 | "tslib": "^2.2.0",
68 | "uri-tag": "^1.3.0",
69 | "utility-types": "^3.10.0"
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/base.client.ts:
--------------------------------------------------------------------------------
1 | import Axios, { AxiosInstance } from 'axios';
2 |
3 | export abstract class BaseClient {
4 | protected axios: AxiosInstance;
5 |
6 | constructor(apiKey: string) {
7 | this.axios = Axios.create({
8 | baseURL: 'https://api.lexoffice.io/v1',
9 | headers: {
10 | Authorization: `Bearer ${apiKey}`,
11 | 'Content-Type': 'application/json',
12 | Accept: 'application/json',
13 | },
14 | });
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/contact/contact.client.ts:
--------------------------------------------------------------------------------
1 | import { BaseClient } from '../base.client';
2 | import {
3 | ContactCreatePerson,
4 | ContactCreateCompany,
5 | ContactUpdatePerson,
6 | ContactUpdateCompany,
7 | ContactCreateResponse,
8 | ContactRetrieveResponse,
9 | ContactUpdateResponse,
10 | PagingParameters,
11 | } from '../index';
12 | import { Err, Ok, Result } from 'ts-results';
13 | import { handleRequestError, RequestError } from '../request-error';
14 | import { ContactFilterRetrieveResponse, OptionalFilters } from './contact.type';
15 | import uri from 'uri-tag';
16 |
17 | export class ContactsClient extends BaseClient {
18 | async createContact(
19 | contact: ContactCreatePerson | ContactCreateCompany,
20 | ): Promise> {
21 | return this.axios
22 | .post('/contacts', contact)
23 | .then((result) => Ok(result.data))
24 | .catch((error) => {
25 | return Err(handleRequestError(error));
26 | });
27 | }
28 |
29 | async retrieveContact(id: string): Promise> {
30 | return this.axios
31 | .get(uri`/contacts/${id}`)
32 | .then((result) => Ok(result.data))
33 | .catch((error) => {
34 | return Err(handleRequestError(error));
35 | });
36 | }
37 |
38 | async updateContact(
39 | id: string,
40 | contact: ContactUpdatePerson | ContactUpdateCompany,
41 | ): Promise> {
42 | return this.axios
43 | .put(uri`/contacts/${id}`, contact)
44 | .then((result) => Ok(result.data))
45 | .catch((error) => {
46 | return Err(handleRequestError(error));
47 | });
48 | }
49 |
50 | async filterContact(
51 | filter?: OptionalFilters & Partial,
52 | ): Promise> {
53 | return this.axios
54 | .get(uri`/contacts`, { params: filter })
55 | .then((result) => Ok(result.data))
56 | .catch((error) => {
57 | return Err(handleRequestError(error));
58 | });
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/contact/contact.type.ts:
--------------------------------------------------------------------------------
1 | import { Required } from 'utility-types';
2 | import { InvoiceCreateResponse, PagingOfRessources } from '..';
3 |
4 | export type ContactFull = {
5 | id: string;
6 | organizationId: string;
7 | version: number;
8 | roles: {
9 | customer?: {
10 | number: number;
11 | };
12 | vendor?: {
13 | number: number;
14 | };
15 | };
16 | // 1
17 | company?: Company;
18 | person?: {
19 | salutation?: string;
20 | firstName?: string;
21 | lastName: string;
22 | };
23 | // 2
24 | addresses: {
25 | billing?: AddressContact[];
26 | shipping?: AddressContact[];
27 | };
28 | xRechnung?: {
29 | buyerReference: string;
30 | vendorNumberAtCustomer: string;
31 | };
32 | // 3
33 | emailAddresses: EmailAddresses;
34 | // 4
35 | phoneNumbers: PhoneNumbers;
36 | note: string;
37 | archived: boolean;
38 | };
39 | // 1
40 | export type Company = {
41 | name: string;
42 | taxNumber?: string;
43 | vatRegistrationId?: string;
44 | allowTaxFreeInvoices?: boolean;
45 | contactPersons?: {
46 | salutation?: string;
47 | firstName?: string;
48 | lastName: string;
49 | primary?: boolean;
50 | emailAddress?: string;
51 | phoneNumber?: string;
52 | }[];
53 | };
54 | // 2
55 | export type AddressContact = {
56 | supplement?: string;
57 | street?: string;
58 | zip?: string;
59 | city?: string;
60 | countryCode: string;
61 | };
62 | // 3
63 | export type EmailAddresses = {
64 | business?: string[];
65 | office?: string[];
66 | private?: string[];
67 | other?: string[];
68 | };
69 | // 4
70 | export type PhoneNumbers = EmailAddresses & {
71 | mobile?: string[];
72 | fax?: string[];
73 | };
74 | ///////////////////////////////////////////
75 | export type ContactCreatePerson = {
76 | id?: string;
77 | organizationId?: string;
78 | version: number;
79 | roles:
80 | | {
81 | customer: {};
82 | vendor: {};
83 | }
84 | | { customer: {} }
85 | | { vendor: {} };
86 | person: {
87 | salutation?: string;
88 | firstName?: string;
89 | lastName: string;
90 | };
91 | addresses?: {
92 | billing?: AddressContact[];
93 | shipping?: AddressContact[];
94 | };
95 | xRechnung?: {
96 | buyerReference: string;
97 | vendorNumberAtCustomer: string;
98 | };
99 | emailAddresses?: EmailAddresses;
100 | phoneNumbers?: PhoneNumbers;
101 | note?: string;
102 | };
103 |
104 | export type ContactCreateCompany = {
105 | id?: string;
106 | organizationId?: string;
107 | version: number;
108 | roles:
109 | | {
110 | customer: {};
111 | vendor: {};
112 | }
113 | | { customer: {} }
114 | | { vendor: {} };
115 | company: Company;
116 | addresses?: {
117 | billing?: AddressContact[];
118 | shipping?: AddressContact[];
119 | };
120 | xRechnung?: {
121 | buyerReference: string;
122 | vendorNumberAtCustomer: string;
123 | };
124 | emailAddresses?: EmailAddresses;
125 | phoneNumbers?: PhoneNumbers;
126 | note?: string;
127 | };
128 | export type ContactUpdatePerson = ContactCreatePerson;
129 |
130 | export type ContactUpdateCompany = ContactCreateCompany;
131 |
132 | export type ContactCreateResponse = InvoiceCreateResponse;
133 | export type ContactUpdateResponse = ContactCreateResponse;
134 |
135 | export type ContactRetrieveResponse = Partial;
136 |
137 | export type OptionalFilters = {
138 | email?: string;
139 | name?: string;
140 | number?: number;
141 | customer?: boolean;
142 | vendor?: boolean;
143 | };
144 |
145 | export type ContactFilterRetrieveResponse = {
146 | content: Partial[] & Partial;
147 | };
148 |
--------------------------------------------------------------------------------
/src/country/country.client.ts:
--------------------------------------------------------------------------------
1 | import { BaseClient } from '../base.client';
2 | import { Country } from './country.type';
3 | import { Err, Ok, Result } from 'ts-results';
4 | import { handleRequestError, RequestError } from '../request-error';
5 |
6 | export class CountryClient extends BaseClient {
7 | async retrieveListOfCountries(): Promise> {
8 | return this.axios
9 | .get(`/countries`)
10 | .then((result) => Ok(result.data))
11 | .catch((error) => {
12 | return Err(handleRequestError(error));
13 | });
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/country/country.type.ts:
--------------------------------------------------------------------------------
1 | export type Country = {
2 | countryCode: string;
3 | countryNameDE: string;
4 | countryNameEN: string;
5 | taxClassification: string;
6 | };
7 |
--------------------------------------------------------------------------------
/src/creditNote/creditNote.client.ts:
--------------------------------------------------------------------------------
1 | import { BaseClient } from '../base.client';
2 | import { Err, Ok, Result } from 'ts-results';
3 | import { handleRequestError, RequestError } from '../request-error';
4 | import {
5 | CreditNoteCreate,
6 | CreditNoteCreateResponse,
7 | CreditNoteRetrieveResponse,
8 | } from './creditNote.type';
9 | import { DocumentFileId, OptionalFinalized } from '..';
10 | import uri from 'uri-tag';
11 |
12 | export class CreditNoteClient extends BaseClient {
13 | async createCreditNote(
14 | creditNote: CreditNoteCreate,
15 | optionalFinalized?: OptionalFinalized,
16 | ): Promise> {
17 | return this.axios
18 | .post('/credit-notes', creditNote, { params: optionalFinalized })
19 | .then((result) => Ok(result.data))
20 | .catch((error) => {
21 | return Err(handleRequestError(error));
22 | });
23 | }
24 |
25 | async retrieveCreditNote(id: string): Promise> {
26 | return this.axios
27 | .get(uri`/credit-notes/${id}`)
28 | .then((result) => Ok(result.data))
29 | .catch((error) => {
30 | return Err(handleRequestError(error));
31 | });
32 | }
33 |
34 | async renderCreditNoteDocumentFileId(id: string): Promise> {
35 | return this.axios
36 | .get(uri`/credit-notes/${id}/document`)
37 | .then((result) => Ok(result.data))
38 | .catch((error) => {
39 | return Err(handleRequestError(error));
40 | });
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/creditNote/creditNote.type.ts:
--------------------------------------------------------------------------------
1 | import { Invoice } from '..';
2 | import { InvoiceCreate, InvoiceCreateResponse } from '../invoice/invoice-dto.type';
3 | import { Required } from 'utility-types';
4 |
5 | import {
6 | Address,
7 | AddressExistingLexofficeContact,
8 | AddressNonExistingLexofficeContact,
9 | CustomLineItem,
10 | LineItem,
11 | TaxAmount,
12 | TaxType,
13 | TextLineItem,
14 | TotalPrice,
15 | TotalPriceInvoiceCreate,
16 | } from '../invoice/invoice.type';
17 |
18 | export type CreditNote = Omit<
19 | Invoice,
20 | 'dueDate' | 'XRechnung' | 'paymentConditions' | 'shippingConditions'
21 | > & {
22 | voucherStatus: string;
23 | lineItem:
24 | | Omit
25 | | Omit
26 | | TextLineItem;
27 | };
28 |
29 | export type CreditNoteForCreate = {
30 | language: string;
31 | archived: boolean;
32 | voucherDate: string;
33 | address: Address | AddressExistingLexofficeContact | AddressNonExistingLexofficeContact;
34 | lineItems: (
35 | | Omit
36 | | Omit
37 | | TextLineItem
38 | )[];
39 | totalPrice: TotalPrice | TotalPriceInvoiceCreate;
40 | taxAmounts: TaxAmount[];
41 | taxConditions: {
42 | taxType: TaxType;
43 | taxTypeNote?: string;
44 | };
45 | title?: string;
46 | introduction?: string;
47 | remark?: string;
48 | };
49 |
50 | export type CreditNoteCreate = Required<
51 | Partial,
52 | 'voucherDate' | 'address' | 'lineItems' | 'totalPrice' | 'taxConditions'
53 | >;
54 |
55 | export type CreditNoteCreateResponse = InvoiceCreateResponse;
56 | export type CreditNoteRetrieveResponse = Partial;
57 |
--------------------------------------------------------------------------------
/src/downPaymentInvoice/downPaymentInvoice.client.ts:
--------------------------------------------------------------------------------
1 | import { Err, Ok, Result } from 'ts-results';
2 | import { BaseClient } from '../base.client';
3 | import { handleRequestError, RequestError } from '../request-error';
4 | import { DownPaymentInvoice } from './downPaymentInvoice.type';
5 | import uri from 'uri-tag';
6 |
7 | export class DownPaymentInvoiceClient extends BaseClient {
8 | async retrieveDownPaymentInvoice(id: string): Promise> {
9 | return this.axios
10 | .get(uri`/down-payment-invoices/${id}`)
11 | .then((result) => Ok(result.data))
12 | .catch((error) => {
13 | return Err(handleRequestError(error));
14 | });
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/downPaymentInvoice/downPaymentInvoice.type.ts:
--------------------------------------------------------------------------------
1 | import { Invoice } from '../invoice/invoice.type';
2 |
3 | export type DownPaymentInvoice = Partial<
4 | Omit<
5 | Invoice,
6 | 'XRechnung' | 'claimedGrossAmount' | 'downPaymentDeductions' | 'recurringTemplateId'
7 | >
8 | >;
9 |
--------------------------------------------------------------------------------
/src/eventSubscription/eventSubscription.client.ts:
--------------------------------------------------------------------------------
1 | import { Err, Ok, Result } from 'ts-results';
2 | import { BaseClient } from '../base.client';
3 | import { handleRequestError, RequestError } from '../request-error';
4 | import {
5 | EventSubscription,
6 | EventSubscriptionCreate,
7 | EventSubscriptions,
8 | } from './eventSubscription.type';
9 | import uri from 'uri-tag';
10 |
11 | export class EventSubscriptionClient extends BaseClient {
12 | async createEventSubscription(
13 | eventSubscription: EventSubscriptionCreate,
14 | ): Promise> {
15 | return this.axios
16 | .post('/event-subscriptions', eventSubscription)
17 | .then((result) => Ok(result.data))
18 | .catch((error) => {
19 | return Err(handleRequestError(error));
20 | });
21 | }
22 |
23 | async retrieveEventSubscription(id: string): Promise> {
24 | return this.axios
25 | .get(uri`/event-subscriptions/${id}`)
26 | .then((result) => Ok(result.data))
27 | .catch((error) => {
28 | return Err(handleRequestError(error));
29 | });
30 | }
31 |
32 | async retrieveAllEventSubscriptions(): Promise> {
33 | return this.axios
34 | .get(`/event-subscriptions`)
35 | .then((result) => Ok(result.data))
36 | .catch((error) => {
37 | return Err(handleRequestError(error));
38 | });
39 | }
40 |
41 | async deleteEventSubscription(id: string): Promise> {
42 | return this.axios
43 | .delete(uri`/event-subscriptions/${id}`)
44 | .then((result) => Ok(result.data))
45 | .catch((error) => {
46 | return Err(handleRequestError(error));
47 | });
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/eventSubscription/eventSubscription.type.ts:
--------------------------------------------------------------------------------
1 | export type EventSubscription = {
2 | subscriptionId: string;
3 | organizationId: string;
4 | createdDate: string;
5 | eventType: string;
6 | callbackUrl: string;
7 | };
8 |
9 | export type EventSubscriptionCreate = {
10 | eventType: string;
11 | callbackUrl: string;
12 | };
13 |
14 | export type EventSubscriptions = { content: EventSubscription[] };
15 |
16 | export type WebhookCallback = {
17 | organizationId: string;
18 | eventType: string;
19 | resourceId: string;
20 | eventDate: string;
21 | };
22 |
--------------------------------------------------------------------------------
/src/file/file.client.ts:
--------------------------------------------------------------------------------
1 | import { Err, Ok, Result } from 'ts-results';
2 | import { BaseClient } from '../base.client';
3 | import { handleRequestError, RequestError } from '../request-error';
4 | import { FileResponse } from './file.type';
5 | import { RenderType } from './file.type';
6 | import FormData from 'form-data';
7 | import uri from 'uri-tag';
8 |
9 | export class FileClient extends BaseClient {
10 | async uploadFile(data: FormData): Promise> {
11 | return this.axios
12 | .post('/files', data, {
13 | headers: {
14 | ...data.getHeaders(),
15 | },
16 | })
17 | .then((result) => Ok(result.data))
18 | .catch((error) => {
19 | return Err(handleRequestError(error));
20 | });
21 | }
22 |
23 | async downloadFile(
24 | documentFileId: string,
25 | optionalParameter?: RenderType,
26 | ): Promise> {
27 | return this.axios
28 | .get(uri`/files/${documentFileId}`, {
29 | headers: {
30 | Accept: '*/*',
31 | },
32 | params: optionalParameter,
33 | responseType: 'arraybuffer',
34 | })
35 | .then((result) => Ok(result.data))
36 | .catch((error) => {
37 | return Err(handleRequestError(error));
38 | });
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/file/file.type.ts:
--------------------------------------------------------------------------------
1 | export type FileResponse = {
2 | id: string;
3 | };
4 | export type RenderType = {
5 | renderType: string;
6 | };
7 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { InvoiceClient } from './invoice/invoice.client';
2 | import { ContactsClient } from './contact/contact.client';
3 | import { BaseClient } from './base.client';
4 | import { applyMixins } from './utils';
5 | import { CountryClient } from './country/country.client';
6 | import { CreditNoteClient } from './creditNote/creditNote.client';
7 | import { DownPaymentInvoiceClient } from './downPaymentInvoice/downPaymentInvoice.client';
8 | import { EventSubscriptionClient } from './eventSubscription/eventSubscription.client';
9 | import { OrderConfirmationClient } from './orderConfirmation/orderConfrmation.client';
10 | import { PaymentClient } from './payment/payment.client';
11 | import { PaymentConditionClient } from './paymentCondition/paymentCondition.client';
12 | import { ProfileClient } from './profile/profile.client';
13 | import { QuotationClient } from './quotation/quotation.client';
14 | import { RecurringTemplateClient } from './recurringTemplate/recurringTemplate.client';
15 | import { VoucherlistClient } from './voucherList/voucherList.client';
16 | import { VoucherClient } from './voucher/voucher.client';
17 | import { FileClient } from './file/file.client';
18 | import { PostingCategoryClient } from './postingCategory/postingCategory.client';
19 |
20 | class Client extends BaseClient {}
21 | interface Client
22 | extends InvoiceClient,
23 | ContactsClient,
24 | CountryClient,
25 | CreditNoteClient,
26 | DownPaymentInvoiceClient,
27 | EventSubscriptionClient,
28 | OrderConfirmationClient,
29 | PaymentClient,
30 | PaymentConditionClient,
31 | ProfileClient,
32 | QuotationClient,
33 | RecurringTemplateClient,
34 | VoucherlistClient,
35 | VoucherClient,
36 | FileClient,
37 | PostingCategoryClient {}
38 | applyMixins(Client, [
39 | InvoiceClient,
40 | ContactsClient,
41 | CountryClient,
42 | CreditNoteClient,
43 | DownPaymentInvoiceClient,
44 | EventSubscriptionClient,
45 | OrderConfirmationClient,
46 | PaymentClient,
47 | PaymentConditionClient,
48 | ProfileClient,
49 | QuotationClient,
50 | RecurringTemplateClient,
51 | VoucherlistClient,
52 | VoucherClient,
53 | FileClient,
54 | PostingCategoryClient,
55 | ]);
56 |
57 | export { Client };
58 | export * from './request-error';
59 | export * from './invoice/invoice-dto.type';
60 | export * from './invoice/invoice.type';
61 | export * from './contact/contact.type';
62 | export * from './country/country.type';
63 | export * from './creditNote/creditNote.type';
64 | export * from './downPaymentInvoice/downPaymentInvoice.type';
65 | export * from './eventSubscription/eventSubscription.type';
66 | export * from './orderConfirmation/orderConfirmation.type';
67 | export * from './payment/payment.type';
68 | export * from './paymentCondition/paymentCondition.type';
69 | export * from './profile/profile.type';
70 | export * from './quotation/quotation.type';
71 | export * from './recurringTemplate/recurringTemplate.type';
72 | export * from './voucherList/voucherList.type';
73 | export * from './voucher/voucher.type';
74 | export * from './file/file.type';
75 | export * from './postingCategory/postingCategory.type';
76 |
--------------------------------------------------------------------------------
/src/invoice/invoice-dto.type.ts:
--------------------------------------------------------------------------------
1 | import { Required } from 'utility-types';
2 | import {
3 | Invoice,
4 | InvoiceForCreate,
5 | AddressExistingLexofficeContact,
6 | CustomLineItemXRechnung,
7 | TextLineItem,
8 | } from './invoice.type';
9 |
10 | // Create an Invoice
11 | export type InvoiceCreate = InvoiceForCreate;
12 | // // Create an XRechnung
13 | export type XRechnung = InvoiceCreate & {
14 | address: AddressExistingLexofficeContact;
15 | xRechnung: {
16 | buyerReference: string;
17 | vendorNumberAtCustomer: string;
18 | };
19 | lineItems: (CustomLineItemXRechnung | TextLineItem)[];
20 | taxConditions: {
21 | taxType: string;
22 | taxTypeNote?: string;
23 | };
24 | };
25 | // Response from creating any type of invoice
26 | export type InvoiceCreateResponse = {
27 | id: string;
28 | resourceUri: string;
29 | createdDate: string;
30 | updatedDate: string;
31 | version: number;
32 | };
33 | // Response from retrieving an invoice by id
34 | export type InvoiceRetrieveResponse = Partial;
35 | // Response from rendering DocumentFileId
36 | export type DocumentFileId = {documentFileId:string;}
37 | ////////////////////////////////////////
38 |
--------------------------------------------------------------------------------
/src/invoice/invoice.client.ts:
--------------------------------------------------------------------------------
1 | import { Err, Ok, Result } from 'ts-results';
2 | import { BaseClient } from '../base.client';
3 | import { handleRequestError, RequestError } from '../request-error';
4 | import {
5 | InvoiceCreate,
6 | InvoiceCreateResponse,
7 | InvoiceRetrieveResponse,
8 | XRechnung,
9 | DocumentFileId,
10 | } from './invoice-dto.type';
11 | import { OptionalFinalized } from './invoice.type';
12 | import uri from 'uri-tag';
13 |
14 | export class InvoiceClient extends BaseClient {
15 | async createInvoice(
16 | invoice: InvoiceCreate | XRechnung,
17 | optionalFinalized?: OptionalFinalized,
18 | ): Promise> {
19 | return this.axios
20 | .post('/invoices', invoice, { params: optionalFinalized })
21 | .then((result) => Ok(result.data))
22 | .catch((error) => {
23 | return Err(handleRequestError(error));
24 | });
25 | }
26 |
27 | async retrieveInvoice(id: string): Promise> {
28 | return this.axios
29 | .get(uri`/invoices/${id}`)
30 | .then((result) => Ok(result.data))
31 | .catch((error) => {
32 | return Err(handleRequestError(error));
33 | });
34 | }
35 |
36 | async renderInvoiceDocumentFileId(id: string): Promise> {
37 | return this.axios
38 | .get(uri`/invoices/${id}/document`)
39 | .then((result) => Ok(result.data))
40 | .catch((error) => {
41 | return Err(handleRequestError(error));
42 | });
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/invoice/invoice.type.ts:
--------------------------------------------------------------------------------
1 | import { Required } from 'utility-types';
2 |
3 | export type Invoice = {
4 | id: string;
5 | organizationId: string;
6 | createdDate: string;
7 | updatedDate: string;
8 | version: number;
9 | language: string;
10 | archived: boolean;
11 | voucherStatus: string;
12 | voucherNumber: string;
13 | voucherDate: string;
14 | dueDate: string | null;
15 | // 1
16 | address: Address | AddressExistingLexofficeContact | AddressNonExistingLexofficeContact;
17 | xRechnung?: {
18 | buyerReference: string;
19 | vendorNumberAtCustomer: string;
20 | } | null;
21 | // 2
22 | lineItems: (LineItem | CustomLineItemXRechnung | CustomLineItem | TextLineItem)[];
23 | // 3
24 | totalPrice: TotalPrice | TotalPriceInvoiceCreate;
25 | // 4
26 | taxAmounts: TaxAmount[];
27 | // 5
28 | taxConditions: {
29 | taxType: TaxType;
30 | taxTypeNote?: string;
31 | };
32 | // 6
33 | paymentConditions?: PaymentConditions;
34 | // 7
35 | shippingConditions: ShippingConditions | ShippingConditionsNone | ShippingConditionsPeriod;
36 | closingInvoice: boolean;
37 | claimedGrossAmount: number;
38 | downPaymentDeductions: unknown[];
39 | recurringTemplateId: string | null;
40 | title?: string;
41 | introduction?: string;
42 | remark?: string;
43 | files: { documentFileId: string };
44 | };
45 |
46 | export type InvoiceForCreate = {
47 | language?: string;
48 | archived?: boolean;
49 | voucherDate: string;
50 |
51 | address: Address | AddressExistingLexofficeContact | AddressNonExistingLexofficeContact;
52 | xRechnung?: {
53 | buyerReference: string;
54 | vendorNumberAtCustomer: string;
55 | } | null;
56 | lineItems: (CustomLineItem | TextLineItem)[];
57 | totalPrice: TotalPrice | TotalPriceInvoiceCreate;
58 | taxAmounts?: TaxAmount[];
59 | taxConditions: {
60 | taxType: TaxType;
61 | taxTypeNote?: string;
62 | };
63 | paymentConditions?: PaymentConditions;
64 | shippingConditions: ShippingConditions | ShippingConditionsNone | ShippingConditionsPeriod;
65 | recurringTemplateId?: string | null;
66 | title?: string;
67 | introduction?: string;
68 | remark?: string;
69 | };
70 | // 1
71 | export type Address = {
72 | contactId: string;
73 | name: string;
74 | supplement?: string;
75 | street: string;
76 | city: string;
77 | zip: string;
78 | countryCode: string;
79 | contactPerson: string;
80 | };
81 | export type AddressExistingLexofficeContact = Required, 'contactId'>;
82 | export type AddressNonExistingLexofficeContact = Required, 'name' | 'countryCode'>;
83 |
84 | // 2
85 | export type LineItem = {
86 | id?: string;
87 | type: string;
88 | name: string;
89 | description?: string;
90 | quantity: number;
91 | unitName: string;
92 | unitPrice: UnitPrice | UnitPriceGross;
93 | discountPercentage?: number;
94 | lineItemAmount: number;
95 |
96 | };
97 |
98 | export type CustomLineItem = Omit;
99 | export type CustomLineItemXRechnung = CustomLineItem;
100 | export type TextLineItem = {
101 | type: string;
102 | name: string;
103 | description: string;
104 |
105 | };
106 |
107 | export type UnitPrice = {
108 | currency: string;
109 | netAmount: number;
110 | taxRatePercentage: TaxRatePercentage;
111 | };
112 |
113 | export type UnitPriceGross = Required, 'currency' | 'taxRatePercentage'> & {
114 | grossAmount: number;
115 | };
116 |
117 | // 3
118 | export type TotalPrice = {
119 | currency: string;
120 | totalNetAmount: number;
121 | totalGrossAmount: number;
122 | totalTaxAmount: number;
123 | totalDiscountAbsolute?: number;
124 | totalDiscountPercentage?: number;
125 | };
126 | export type TotalPriceInvoiceCreate = Required, 'currency'>;
127 | // 4
128 | export type TaxAmount = {
129 | taxRatePercentage: TaxRatePercentage;
130 | taxAmount: number;
131 | netAmount: number;
132 | };
133 | // 5
134 | export type TaxType = string;
135 | // 6
136 | export type PaymentConditions = {
137 | paymentTermLabel: string;
138 | paymentTermLabelTemplate?: string;
139 | paymentTermDuration: number;
140 | paymentDiscountConditions?: {
141 | discountPercentage: number;
142 | discountRange: number;
143 | };
144 | };
145 | // 7
146 | export type ShippingConditions = {
147 | shippingDate: string;
148 | shippingType: string;
149 | };
150 | export type ShippingConditionsPeriod = ShippingConditions & {
151 | shippingType: string;
152 | shippingEndDate: string;
153 | };
154 | export type ShippingConditionsNone = Required, 'shippingType'>;
155 |
156 | // multiple usage
157 | export type TaxRatePercentage = number;
158 |
159 | export type OptionalFinalized = {
160 | finalize?: boolean;
161 | };
162 |
--------------------------------------------------------------------------------
/src/orderConfirmation/orderConfirmation.type.ts:
--------------------------------------------------------------------------------
1 | import {
2 | InvoiceCreate,
3 | InvoiceCreateResponse,
4 | InvoiceRetrieveResponse,
5 | } from '../invoice/invoice-dto.type';
6 |
7 | export type OrderConfirmation = InvoiceCreate & {
8 | deliveryTerms?: string;
9 | };
10 |
11 | export type OrderConfirmationResponse = InvoiceCreateResponse;
12 |
13 | export type OrderConfirmationRetrieveResponse = InvoiceRetrieveResponse & {
14 | deliveryTerms?: string;
15 | };
16 |
--------------------------------------------------------------------------------
/src/orderConfirmation/orderConfrmation.client.ts:
--------------------------------------------------------------------------------
1 | import { Err, Ok, Result } from 'ts-results';
2 | import { DocumentFileId } from '..';
3 | import { BaseClient } from '../base.client';
4 | import { handleRequestError, RequestError } from '../request-error';
5 | import {
6 | OrderConfirmation,
7 | OrderConfirmationResponse,
8 | OrderConfirmationRetrieveResponse,
9 | } from './orderConfirmation.type';
10 | import uri from 'uri-tag';
11 |
12 | export class OrderConfirmationClient extends BaseClient {
13 | async createOrderConfirmation(
14 | orderConfirmation: OrderConfirmation,
15 | ): Promise> {
16 | return this.axios
17 | .post('/order-confirmations', orderConfirmation)
18 | .then((result) => Ok(result.data))
19 | .catch((error) => {
20 | return Err(handleRequestError(error));
21 | });
22 | }
23 |
24 | async retrieveOrderConfirmation(
25 | id: string,
26 | ): Promise> {
27 | return this.axios
28 | .get(uri`/order-confirmations/${id}`)
29 | .then((result) => Ok(result.data))
30 | .catch((error) => {
31 | return Err(handleRequestError(error));
32 | });
33 | }
34 |
35 | async renderOrderConfirmationDocumentFileId(
36 | id: string,
37 | ): Promise> {
38 | return this.axios
39 | .get(uri`/order-confirmations/${id}/document`)
40 | .then((result) => Ok(result.data))
41 | .catch((error) => {
42 | return Err(handleRequestError(error));
43 | });
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/payment/payment.client.ts:
--------------------------------------------------------------------------------
1 | import { Err, Ok, Result } from 'ts-results';
2 | import { BaseClient } from '../base.client';
3 | import { handleRequestError, RequestError } from '../request-error';
4 | import { Payment } from './payment.type';
5 | import uri from 'uri-tag';
6 |
7 | export class PaymentClient extends BaseClient {
8 | async retrievePayment(id: string): Promise> {
9 | return this.axios
10 | .get(uri`/payments/${id}`)
11 | .then((result) => Ok(result.data))
12 | .catch((error) => {
13 | return Err(handleRequestError(error));
14 | });
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/payment/payment.type.ts:
--------------------------------------------------------------------------------
1 | export type Payment = {
2 | openAmount: number;
3 | currency: string;
4 | paymentStatus: string;
5 | voucherType: string;
6 | voucherStatus: string;
7 | };
8 |
--------------------------------------------------------------------------------
/src/paymentCondition/paymentCondition.client.ts:
--------------------------------------------------------------------------------
1 | import { Err, Ok, Result } from 'ts-results';
2 | import { BaseClient } from '../base.client';
3 | import { handleRequestError, RequestError } from '../request-error';
4 | import { PaymentCondition } from './paymentCondition.type';
5 | import uri from 'uri-tag';
6 |
7 | export class PaymentConditionClient extends BaseClient {
8 | async retrievePaymentConditionList(): Promise> {
9 | return this.axios
10 | .get(`/payment-conditions`)
11 | .then((result) => Ok(result.data))
12 | .catch((error) => {
13 | return Err(handleRequestError(error));
14 | });
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/paymentCondition/paymentCondition.type.ts:
--------------------------------------------------------------------------------
1 | export type PaymentCondition = {
2 | id: string;
3 | paymentTermLabelTemplate?: string;
4 | paymentTermDuration?: number;
5 | paymentDiscountConditions?: {
6 | discountRange?: number;
7 | discountPercentage?: number;
8 | };
9 | organizationDefault: boolean;
10 | };
11 |
--------------------------------------------------------------------------------
/src/postingCategory/postingCategory.client.ts:
--------------------------------------------------------------------------------
1 | import { Err, Ok, Result } from 'ts-results';
2 | import { BaseClient } from '../base.client';
3 | import { handleRequestError, RequestError } from '../request-error';
4 | import { PostingCategory } from './postingCategory.type';
5 |
6 | export class PostingCategoryClient extends BaseClient {
7 | async retrieveListPostingCategories(): Promise> {
8 | return this.axios
9 | .get(`/posting-categories`)
10 | .then((result) => Ok(result.data))
11 | .catch((error) => {
12 | return Err(handleRequestError(error));
13 | });
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/postingCategory/postingCategory.type.ts:
--------------------------------------------------------------------------------
1 | export type PostingCategory = {
2 | id: string;
3 | name: string;
4 | type: string;
5 | contactRequired: boolean;
6 | splitAllowed: boolean;
7 | groupName: string;
8 | };
9 |
--------------------------------------------------------------------------------
/src/profile/profile.client.ts:
--------------------------------------------------------------------------------
1 | import { Err, Ok, Result } from 'ts-results';
2 | import { BaseClient } from '../base.client';
3 | import { handleRequestError, RequestError } from '../request-error';
4 | import { Profile } from './profile.type';
5 |
6 | export class ProfileClient extends BaseClient {
7 | async retrieveProfile(): Promise> {
8 | return this.axios
9 | .get(`/profile`)
10 | .then((result) => Ok(result.data))
11 | .catch((error) => {
12 | return Err(handleRequestError(error));
13 | });
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/profile/profile.type.ts:
--------------------------------------------------------------------------------
1 | export type Profile = {
2 | organizationId: string;
3 | companyName: string;
4 | created: {
5 | userId: string;
6 | userName: string;
7 | userEmail: string;
8 | date: string;
9 | };
10 | connectionId: string;
11 | features: string[];
12 | subscriptionStatus: string;
13 | taxType: string;
14 | smallBusiness: string;
15 | };
16 |
--------------------------------------------------------------------------------
/src/quotation/quotation.client.ts:
--------------------------------------------------------------------------------
1 | import { Err, Ok, Result } from 'ts-results';
2 | import { DocumentFileId, OptionalFinalized } from '..';
3 | import { BaseClient } from '../base.client';
4 | import { handleRequestError, RequestError } from '../request-error';
5 | import { Quotation, QuotationCreate } from './quotation.type';
6 | import uri from 'uri-tag';
7 |
8 | export class QuotationClient extends BaseClient {
9 | async createQuotation(
10 | quotation: QuotationCreate,
11 | optionalFilter?: OptionalFinalized,
12 | ): Promise> {
13 | return this.axios
14 | .post('/quotations', quotation, { params: optionalFilter })
15 | .then((result) => Ok(result.data))
16 | .catch((error) => {
17 | return Err(handleRequestError(error));
18 | });
19 | }
20 |
21 | async retrieveQuotation(id: string): Promise, RequestError>> {
22 | return this.axios
23 | .get>(uri`/quotations/${id}`)
24 | .then((result) => Ok(result.data))
25 | .catch((error) => {
26 | return Err(handleRequestError(error));
27 | });
28 | }
29 | async renderQuotationDocumentFileId(id: string): Promise> {
30 | return this.axios
31 | .get(uri`/quotations/${id}/document`)
32 | .then((result) => Ok(result.data))
33 | .catch((error) => {
34 | return Err(handleRequestError(error));
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/quotation/quotation.type.ts:
--------------------------------------------------------------------------------
1 | import { Required } from 'utility-types';
2 |
3 | import {
4 | Address,
5 | AddressExistingLexofficeContact,
6 | AddressNonExistingLexofficeContact,
7 | CustomLineItem,
8 | InvoiceCreateResponse,
9 | LineItem,
10 | PaymentConditions,
11 | ShippingConditions,
12 | ShippingConditionsNone,
13 | ShippingConditionsPeriod,
14 | TaxAmount,
15 | TaxType,
16 | TextLineItem,
17 | TotalPrice,
18 | TotalPriceInvoiceCreate,
19 | } from '..';
20 |
21 | export type Quotation = {
22 | id: string;
23 | organizationId: string;
24 | createdDate: string;
25 | updatedDate: string;
26 | expirationDate: string;
27 | version: number;
28 | language: string;
29 | archived: boolean;
30 | voucherStatus: string;
31 | voucherNumber: string;
32 | voucherDate: string;
33 | address: Address | AddressExistingLexofficeContact | AddressNonExistingLexofficeContact;
34 | lineItems: ((LineItem & Optionals) | (CustomLineItem & Optionals) | (TextLineItem & Optionals))[];
35 | totalPrice: TotalPrice | TotalPriceInvoiceCreate;
36 | taxAmounts: TaxAmount[];
37 | taxConditions: {
38 | taxType: TaxType;
39 | taxTypeNote?: string;
40 | };
41 | paymentConditions?: PaymentConditions;
42 | shippingConditions: ShippingConditions | ShippingConditionsNone | ShippingConditionsPeriod;
43 | closingInvoice: boolean;
44 | title?: string;
45 | introduction?: string;
46 | remark?: string;
47 | files: { documentFileId: string };
48 | };
49 |
50 | export type Optionals = {
51 | optional?: boolean;
52 | alternative?: boolean;
53 | subItems?: (
54 | | (LineItem & {
55 | optional?: boolean;
56 | alternative?: boolean;
57 | })
58 | | (CustomLineItem & {
59 | optional?: boolean;
60 | alternative?: boolean;
61 | })
62 | | (TextLineItem & {
63 | optional?: boolean;
64 | alternative?: boolean;
65 | })
66 | )[];
67 | };
68 |
69 | export type QuotationCreate = Required<
70 | Partial,
71 | 'voucherDate' | 'expirationDate' | 'address' | 'lineItems' | 'totalPrice' | 'taxConditions'
72 | >;
73 | export type QuotationCreateResponse = InvoiceCreateResponse;
74 |
--------------------------------------------------------------------------------
/src/recurringTemplate/recurringTemplate.client.ts:
--------------------------------------------------------------------------------
1 | import { Err, Ok, Result } from 'ts-results';
2 | import { BaseClient } from '../base.client';
3 | import { handleRequestError, RequestError } from '../request-error';
4 | import { RecurringTemplate, RecurringTemplates } from './recurringTemplate.type';
5 | import { PagingParameters } from './recurringTemplate.type';
6 | import uri from 'uri-tag';
7 |
8 | export class RecurringTemplateClient extends BaseClient {
9 | async retrieveRecurringTemplate(
10 | id: string,
11 | ): Promise, RequestError>> {
12 | return this.axios
13 | .get>(uri`/recurring-templates/${id}`)
14 | .then((result) => Ok(result.data))
15 | .catch((error) => {
16 | return Err(handleRequestError(error));
17 | });
18 | }
19 |
20 | async retrieveAllRecurringTemplates(
21 | optionalFilter?: PagingParameters,
22 | ): Promise> {
23 | return this.axios
24 | .get(uri`/recurring-templates`, { params: optionalFilter })
25 | .then((result) => Ok(result.data))
26 | .catch((error) => {
27 | return Err(handleRequestError(error));
28 | });
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/recurringTemplate/recurringTemplate.type.ts:
--------------------------------------------------------------------------------
1 | import { Invoice } from '..';
2 |
3 | export type RecurringTemplate = Omit<
4 | Invoice,
5 | | 'voucherStatus'
6 | | 'voucherNumber'
7 | | 'voucherDate'
8 | | 'dueDate'
9 | | 'shippingConditions'
10 | | 'files'
11 | | 'XRechnung'
12 | > & {
13 | recurringTemplateSettings: {
14 | id: string;
15 | startDate?: string;
16 | endDate?: string;
17 | finalize: boolean;
18 | shippingType: 'service' | 'delivery' | 'serviceperiod' | 'deliveryperiod' | 'none';
19 | executionInterval: 'WEEKLY' | 'BIWEEKLY' | 'MONTHLY' | 'QUARTERLY' | 'BIANNUALLY' | 'ANNUALLY';
20 | nextExecutionDate: string;
21 | lastExecutionFailed: boolean;
22 | lastExecutionErrorMessage?: string | null;
23 | executionStatus: 'ACTIVE' | 'PAUSED' | 'ENDED';
24 | };
25 | };
26 |
27 | export type PagingParameters = {
28 | size?: number;
29 | page?: number;
30 | sort?: string;
31 | };
32 |
33 | export type RecurringTemplates = {
34 | content: Partial & Partial;
35 | };
36 |
37 | export type PagingOfRessources = {
38 | first: boolean;
39 | last: boolean;
40 | totalPages: number;
41 | totalElements: number;
42 | numberOfElements: number;
43 | size: number;
44 | number: number;
45 | sort?: [
46 | {
47 | property: string;
48 | direction: string;
49 | ignoreCase: boolean;
50 | nullHandling: string;
51 | ascending: boolean;
52 | },
53 | ];
54 | };
55 |
--------------------------------------------------------------------------------
/src/request-error.ts:
--------------------------------------------------------------------------------
1 | import { AxiosError } from 'axios';
2 | import { Required } from 'utility-types';
3 |
4 | type LexofficeErrorRegular = {
5 | timestamp?: string;
6 | status?: number;
7 | error?: string;
8 | path?: string;
9 | traceId?: string;
10 | message: string;
11 | details?: ErrorDetails;
12 | };
13 |
14 | type ErrorDetails = {
15 | violation: string;
16 | field: string;
17 | message: string;
18 | }[];
19 |
20 | type LexofficeErrorLegacy = {
21 | IssueList: IssueList;
22 | };
23 |
24 | type IssueList =
25 | | {
26 | i18nKey?: string;
27 | source?: string;
28 | type?: string;
29 | additionalData?: string;
30 | args?: string;
31 | }[]
32 | | undefined;
33 |
34 | type LexofficeLegacyError = Required, 'response'>;
35 |
36 | function isAxiosError(error: Error | AxiosError): error is AxiosError {
37 | return typeof (error as any).isAxiosError !== 'undefined' && (error as any).isAxiosError;
38 | }
39 |
40 | function isLexofficeLegacyError(error: AxiosError): error is LexofficeLegacyError {
41 | return error.response && error.response.data.IssueList;
42 | }
43 |
44 | export function handleRequestError(error: Error | AxiosError): RequestError {
45 | if (isAxiosError(error) && isLexofficeLegacyError(error)) {
46 | const statusText = error.response.statusText;
47 | const status = error.response.status;
48 | const issueList = error.response.data.IssueList;
49 | switch (status) {
50 | case 400:
51 | return new RequestBadRequestLegacyError(statusText, issueList);
52 | case 406:
53 | return new RequestMethodNotAcceptableLegacyError(statusText, issueList);
54 | case 500:
55 | return new RequestInternalServerLegacyError(statusText, issueList);
56 | default:
57 | return new RequestLexofficeLegacyError(statusText, status, issueList);
58 | }
59 | } else if (isAxiosError(error) && error.response) {
60 | const status = error.response.status;
61 | const timestamp = error.response.data.timestamp
62 | ? new Date(error.response.data.timestamp)
63 | : undefined;
64 | const errorDescription = error.response.data.error;
65 | const path = error.response.data.path;
66 | const traceId = error.response.data.traceId;
67 | const messageOrig = error.response.data.message;
68 | const message =
69 | messageOrig ??
70 | 'No further error informations provided by the API for this status code on this specific endpoint.';
71 | const errorDetails = error.response.data.details;
72 |
73 | switch (status) {
74 | case 400:
75 | return new RequestBadRequestError(
76 | message,
77 | messageOrig,
78 | errorDescription,
79 | traceId,
80 | timestamp,
81 | path,
82 | errorDetails,
83 | );
84 | case 401:
85 | return new RequestUnauthorizedError(
86 | message,
87 | messageOrig,
88 | errorDescription,
89 | traceId,
90 | timestamp,
91 | path,
92 | errorDetails,
93 | );
94 | case 402:
95 | return new RequestPaymentRequiredError(
96 | message,
97 | messageOrig,
98 | errorDescription,
99 | traceId,
100 | timestamp,
101 | path,
102 | errorDetails,
103 | );
104 | case 403:
105 | return new RequestForbiddenError(
106 | message,
107 | messageOrig,
108 | errorDescription,
109 | traceId,
110 | timestamp,
111 | path,
112 | errorDetails,
113 | );
114 | case 404:
115 | return new RequestNotFoundError(
116 | message,
117 | messageOrig,
118 | errorDescription,
119 | traceId,
120 | timestamp,
121 | path,
122 | errorDetails,
123 | );
124 | case 405:
125 | return new RequestMethodNotAllowedError(
126 | message,
127 | messageOrig,
128 | errorDescription,
129 | traceId,
130 | timestamp,
131 | path,
132 | errorDetails,
133 | );
134 | case 406:
135 | return new RequestMethodNotAcceptableError(
136 | message,
137 | messageOrig,
138 | errorDescription,
139 | traceId,
140 | timestamp,
141 | path,
142 | errorDetails,
143 | );
144 | case 409:
145 | return new RequestConflictError(
146 | message,
147 | messageOrig,
148 | errorDescription,
149 | traceId,
150 | timestamp,
151 | path,
152 | errorDetails,
153 | );
154 | case 415:
155 | return new RequestUnsupportedMediaTypeError(
156 | message,
157 | messageOrig,
158 | errorDescription,
159 | traceId,
160 | timestamp,
161 | path,
162 | errorDetails,
163 | );
164 | case 429:
165 | return new RequestTooManyRequestsError(
166 | message,
167 | messageOrig,
168 | errorDescription,
169 | traceId,
170 | timestamp,
171 | path,
172 | errorDetails,
173 | );
174 | case 500:
175 | return new RequestInternalServerError(
176 | message,
177 | messageOrig,
178 | errorDescription,
179 | traceId,
180 | timestamp,
181 | path,
182 | errorDetails,
183 | );
184 | case 503:
185 | return new RequestServiceUnavailableError(
186 | message,
187 | messageOrig,
188 | errorDescription,
189 | traceId,
190 | timestamp,
191 | path,
192 | errorDetails,
193 | );
194 | case 504:
195 | return new RequestGatewayTimeoutError(
196 | message,
197 | messageOrig,
198 | errorDescription,
199 | traceId,
200 | timestamp,
201 | path,
202 | errorDetails,
203 | );
204 | default:
205 | return new RequestError(
206 | message,
207 | messageOrig,
208 | status,
209 | errorDescription,
210 | traceId,
211 | timestamp,
212 | path,
213 | errorDetails,
214 | );
215 | }
216 | }
217 |
218 | return new RequestError('Unknown Request Error');
219 | }
220 |
221 | export class RequestError extends Error {
222 | public readonly status?: number;
223 | public readonly error?: string;
224 | public readonly traceId?: string;
225 | public readonly path?: string;
226 | public readonly timestamp?: Date;
227 | public readonly details?: ErrorDetails;
228 | public readonly issueList?: IssueList;
229 | public readonly messageOrig?: string;
230 |
231 | constructor(
232 | message?: string,
233 | messageOrig?: string,
234 | status?: number,
235 | errorDescription?: string,
236 | traceId?: string,
237 | timestamp?: Date,
238 | path?: string,
239 | errorDetails?: ErrorDetails,
240 | ) {
241 | super('[Lexoffice Client Error] ' + message);
242 | this.messageOrig = messageOrig;
243 | this.status = status;
244 | this.error = errorDescription;
245 | this.traceId = traceId;
246 | this.timestamp = timestamp;
247 | this.path = path;
248 | this.details = errorDetails;
249 | }
250 | }
251 |
252 | abstract class RequestLexofficeError extends RequestError {
253 | public readonly message!: string;
254 | public declare readonly issueList: undefined;
255 | }
256 |
257 | class RequestLexofficeLegacyError extends RequestError {
258 | public declare readonly issueList: IssueList;
259 | constructor(statusText: string, status: number, issueList: IssueList) {
260 | super('Legacy: ' + statusText, undefined, status);
261 | this.issueList = issueList;
262 | }
263 | }
264 |
265 | // Regular Errors
266 | export class RequestBadRequestError extends RequestLexofficeError {
267 | constructor(
268 | message: string,
269 | messageOrig?: string,
270 | errorDescription?: string,
271 | traceId?: string,
272 | timestamp?: Date,
273 | path?: string,
274 | errorDetails?: ErrorDetails,
275 | ) {
276 | super(message, messageOrig, 400, errorDescription, traceId, timestamp, path, errorDetails);
277 | }
278 | }
279 |
280 | export class RequestUnauthorizedError extends RequestLexofficeError {
281 | constructor(
282 | message: string,
283 | messageOrig?: string,
284 | errorDescription?: string,
285 | traceId?: string,
286 | timestamp?: Date,
287 | path?: string,
288 | errorDetails?: ErrorDetails,
289 | ) {
290 | super(message, messageOrig, 401, errorDescription, traceId, timestamp, path, errorDetails);
291 | }
292 | }
293 |
294 | export class RequestPaymentRequiredError extends RequestLexofficeError {
295 | constructor(
296 | message: string,
297 | messageOrig?: string,
298 | errorDescription?: string,
299 | traceId?: string,
300 | timestamp?: Date,
301 | path?: string,
302 | errorDetails?: ErrorDetails,
303 | ) {
304 | super(message, messageOrig, 402, errorDescription, traceId, timestamp, path, errorDetails);
305 | }
306 | }
307 |
308 | export class RequestForbiddenError extends RequestLexofficeError {
309 | constructor(
310 | message: string,
311 | messageOrig?: string,
312 | errorDescription?: string,
313 | traceId?: string,
314 | timestamp?: Date,
315 | path?: string,
316 | errorDetails?: ErrorDetails,
317 | ) {
318 | super(message, messageOrig, 403, errorDescription, traceId, timestamp, path, errorDetails);
319 | }
320 | }
321 |
322 | export class RequestNotFoundError extends RequestLexofficeError {
323 | constructor(
324 | message: string,
325 | messageOrig?: string,
326 | errorDescription?: string,
327 | traceId?: string,
328 | timestamp?: Date,
329 | path?: string,
330 | errorDetails?: ErrorDetails,
331 | ) {
332 | super(message, messageOrig, 404, errorDescription, traceId, timestamp, path, errorDetails);
333 | }
334 | }
335 |
336 | export class RequestMethodNotAllowedError extends RequestLexofficeError {
337 | constructor(
338 | message: string,
339 | messageOrig?: string,
340 | errorDescription?: string,
341 | traceId?: string,
342 | timestamp?: Date,
343 | path?: string,
344 | errorDetails?: ErrorDetails,
345 | ) {
346 | super(message, messageOrig, 405, errorDescription, traceId, timestamp, path, errorDetails);
347 | }
348 | }
349 |
350 | export class RequestMethodNotAcceptableError extends RequestLexofficeError {
351 | constructor(
352 | message: string,
353 | messageOrig?: string,
354 | errorDescription?: string,
355 | traceId?: string,
356 | timestamp?: Date,
357 | path?: string,
358 | errorDetails?: ErrorDetails,
359 | ) {
360 | super(message, messageOrig, 406, errorDescription, traceId, timestamp, path, errorDetails);
361 | }
362 | }
363 |
364 | export class RequestConflictError extends RequestLexofficeError {
365 | constructor(
366 | message: string,
367 | messageOrig?: string,
368 | errorDescription?: string,
369 | traceId?: string,
370 | timestamp?: Date,
371 | path?: string,
372 | errorDetails?: ErrorDetails,
373 | ) {
374 | super(message, messageOrig, 409, errorDescription, traceId, timestamp, path, errorDetails);
375 | }
376 | }
377 | export class RequestUnsupportedMediaTypeError extends RequestLexofficeError {
378 | constructor(
379 | message: string,
380 | messageOrig?: string,
381 | errorDescription?: string,
382 | traceId?: string,
383 | timestamp?: Date,
384 | path?: string,
385 | errorDetails?: ErrorDetails,
386 | ) {
387 | super(message, messageOrig, 415, errorDescription, traceId, timestamp, path, errorDetails);
388 | }
389 | }
390 |
391 | export class RequestTooManyRequestsError extends RequestLexofficeError {
392 | constructor(
393 | message: string,
394 | messageOrig?: string,
395 | errorDescription?: string,
396 | traceId?: string,
397 | timestamp?: Date,
398 | path?: string,
399 | errorDetails?: ErrorDetails,
400 | ) {
401 | super(message, messageOrig, 429, errorDescription, traceId, timestamp, path, errorDetails);
402 | }
403 | }
404 |
405 | export class RequestInternalServerError extends RequestLexofficeError {
406 | constructor(
407 | message: string,
408 | messageOrig?: string,
409 | errorDescription?: string,
410 | traceId?: string,
411 | timestamp?: Date,
412 | path?: string,
413 | errorDetails?: ErrorDetails,
414 | ) {
415 | super(message, messageOrig, 500, errorDescription, traceId, timestamp, path, errorDetails);
416 | }
417 | }
418 | export class RequestServiceUnavailableError extends RequestLexofficeError {
419 | constructor(
420 | message: string,
421 | messageOrig?: string,
422 | errorDescription?: string,
423 | traceId?: string,
424 | timestamp?: Date,
425 | path?: string,
426 | errorDetails?: ErrorDetails,
427 | ) {
428 | super(message, messageOrig, 503, errorDescription, traceId, timestamp, path, errorDetails);
429 | }
430 | }
431 | export class RequestGatewayTimeoutError extends RequestLexofficeError {
432 | constructor(
433 | message: string,
434 | messageOrig?: string,
435 | errorDescription?: string,
436 | traceId?: string,
437 | timestamp?: Date,
438 | path?: string,
439 | errorDetails?: ErrorDetails,
440 | ) {
441 | super(message, messageOrig, 504, errorDescription, traceId, timestamp, path, errorDetails);
442 | }
443 | }
444 | // Legacy errors
445 | export class RequestBadRequestLegacyError extends RequestLexofficeLegacyError {
446 | constructor(statusText: string, issueList?: IssueList) {
447 | super(statusText, 400, issueList);
448 | }
449 | }
450 | export class RequestMethodNotAcceptableLegacyError extends RequestLexofficeLegacyError {
451 | constructor(statusText: string, issueList?: IssueList) {
452 | super(statusText, 406, issueList);
453 | }
454 | }
455 | export class RequestInternalServerLegacyError extends RequestLexofficeLegacyError {
456 | constructor(statusText: string, issueList?: IssueList) {
457 | super(statusText, 500, issueList);
458 | }
459 | }
460 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { ContactCreateCompany, ContactCreatePerson } from '.';
2 |
3 | export function applyMixins(derivedCtor: any, baseCtors: any[]) {
4 | for (const baseCtor of baseCtors) {
5 | const group = baseCtor.name;
6 | const propertyNames = Object.getOwnPropertyNames(baseCtor.prototype);
7 | for (const name of propertyNames) {
8 | const baseCtorName = Object.getOwnPropertyDescriptor(baseCtor.prototype, name);
9 | if (!baseCtorName) {
10 | return;
11 | }
12 | Object.defineProperty(derivedCtor.prototype, name, baseCtorName);
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/voucher/voucher.client.ts:
--------------------------------------------------------------------------------
1 | import { Err, Ok, Result } from 'ts-results';
2 | import { FileResponse } from '..';
3 | import { BaseClient } from '../base.client';
4 | import { handleRequestError, RequestError } from '../request-error';
5 | import {
6 | CreateVoucher,
7 | VoucherNumber,
8 | Voucher,
9 | VoucherCreateResponse,
10 | Vouchers,
11 | } from './voucher.type';
12 | import FormData from 'form-data';
13 | import uri from 'uri-tag';
14 |
15 | export class VoucherClient extends BaseClient {
16 | async createVoucher(
17 | voucher: CreateVoucher,
18 | ): Promise> {
19 | return this.axios
20 | .post('/vouchers', voucher)
21 | .then((result) => Ok(result.data))
22 | .catch((error) => {
23 | return Err(handleRequestError(error));
24 | });
25 | }
26 |
27 | async retrieveVoucher(id: string): Promise, RequestError>> {
28 | return this.axios
29 | .get>(uri`/vouchers/${id}`)
30 | .then((result) => Ok(result.data))
31 | .catch((error) => {
32 | return Err(handleRequestError(error));
33 | });
34 | }
35 |
36 | async updateVoucher(
37 | id: string,
38 | voucher: CreateVoucher,
39 | ): Promise> {
40 | return this.axios
41 | .put(uri`/vouchers/${id}`, voucher)
42 | .then((result) => Ok(result.data))
43 | .catch((error) => {
44 | return Err(handleRequestError(error));
45 | });
46 | }
47 |
48 | async filterVoucher(
49 | voucherNumber: VoucherNumber,
50 | ): Promise, RequestError>> {
51 | return this.axios
52 | .get>(`/vouchers`, { params: voucherNumber })
53 | .then((result) => Ok(result.data))
54 | .catch((error) => {
55 | return Err(handleRequestError(error));
56 | });
57 | }
58 |
59 | async uploadFileToVoucher(
60 | data: FormData,
61 | id: string,
62 | ): Promise> {
63 | return this.axios
64 | .post(uri`/vouchers/${id}/files`, data, {
65 | headers: {
66 | ...data.getHeaders(),
67 | },
68 | })
69 | .then((result) => Ok(result.data))
70 | .catch((error) => {
71 | return Err(handleRequestError(error));
72 | });
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/voucher/voucher.type.ts:
--------------------------------------------------------------------------------
1 | import { Required } from 'utility-types';
2 | import { InvoiceCreateResponse } from '..';
3 |
4 | export type Voucher = {
5 | id: string;
6 | organizationId: string;
7 | type: string;
8 | voucherStatus: string;
9 | voucherNumber: string;
10 | voucherDate: string;
11 | shippingDate: string;
12 | dueDate: string;
13 | totalGrossAmount: number;
14 | totalTaxAmount: number;
15 | taxType: string;
16 | useCollectiveContact: boolean;
17 | contactId?: string;
18 | remark?: string;
19 | voucherItems: {
20 | amount: number;
21 | taxAmount: number;
22 | taxRatePercent: number;
23 | categoryId: string;
24 | }[];
25 | files: string[] | [];
26 | createdDate: string;
27 | updatedDate: string;
28 | version: number;
29 | };
30 |
31 | export type CreateVoucher = Required<
32 | Partial,
33 | | 'type'
34 | | 'voucherNumber'
35 | | 'voucherDate'
36 | | 'totalGrossAmount'
37 | | 'totalTaxAmount'
38 | | 'taxType'
39 | | 'voucherItems'
40 | | 'version'
41 | >;
42 |
43 | export type VoucherCreateResponse = InvoiceCreateResponse;
44 |
45 | export type Vouchers = {
46 | content: Voucher[];
47 | };
48 |
49 | export type VoucherNumber = {
50 | voucherNumber: string;
51 | };
52 |
--------------------------------------------------------------------------------
/src/voucherlist/voucherlist.client.ts:
--------------------------------------------------------------------------------
1 | import { Err, Ok, Result } from 'ts-results';
2 | import { BaseClient } from '../base.client';
3 | import { handleRequestError, RequestError } from '../request-error';
4 | import { FilterParameter, Voucherlist } from './voucherList.type';
5 | import uri from 'uri-tag';
6 |
7 | export class VoucherlistClient extends BaseClient {
8 | async retrieveVoucherlist(
9 | filterParameter: FilterParameter,
10 | ): Promise> {
11 | return this.axios
12 | .get(uri`/voucherlist`, { params: filterParameter })
13 | .then((result) => Ok(result.data))
14 | .catch((error) => {
15 | return Err(handleRequestError(error));
16 | });
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/voucherlist/voucherlist.type.ts:
--------------------------------------------------------------------------------
1 | export type Voucherlist = {
2 | content: {
3 | id: string;
4 | voucherType: string;
5 | voucherStatus: string;
6 | voucherNumber: string;
7 | voucherDate: string;
8 | updatedDate: string;
9 | dueDate: string;
10 | contactName: string;
11 | totalAmount: number;
12 | openAmount: number;
13 | currency: string;
14 | archived: boolean;
15 | }[];
16 | };
17 |
18 | export type FilterParameter = {
19 | voucherType: string;
20 | voucherStatus: string;
21 | archived?: boolean;
22 | size?: number;
23 | sort?: string;
24 | };
25 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "module": "ESNext",
5 | "declaration": true,
6 | "declarationMap": true,
7 | "removeComments": true,
8 | "strict": true,
9 | "outDir": "dist",
10 | "moduleResolution": "node",
11 | "esModuleInterop": true,
12 | "skipLibCheck": true,
13 | "forceConsistentCasingInFileNames": true
14 | },
15 | "exclude": ["node_modules", "dist", "**/*.test.ts", "example"]
16 | }
17 |
--------------------------------------------------------------------------------