├── .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 | GitHub Stars 17 | 18 | 19 | npm (latest) 20 | 21 | 22 | npm (next) 23 | 24 |
25 | 26 | Activity 27 | 28 | 29 | Contributors 30 | 31 | 32 | MIT license 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 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 298 | 299 | 300 | 301 |
Regular errors
❌ Error code Error type❌ Server error code Error type
400RequestBadRequestError500RequestInternalServerError
401RequestUnauthorizedError503RequestServiceUnavailableError
402RequestPaymentRequiredError504RequestGatewayTimeoutError
403RequestForbiddenError
404RequestNotFoundError
405RequestMethodNotAllowedError
406RequestMethodNotAcceptableError
409RequestConflictError
415RequestUnsupportedMediaTypeError
429RequestTooManyRequestsError
Legacy errors (used by the endpoints files, profiles and contacts)
400RequestBadRequestLegacyError500RequestInternalServerLegacyError
406 297 | RequestMethodNotAcceptableLegacyError
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 | --------------------------------------------------------------------------------