How Tos /businessnxtapi/howto section Guides and best practices for integrating with the Business NXT GraphQL API, including creating orders and managing documents. 2024-09-24T15:57:29+02:00 # How Tos Guides and best practices for integrating with the Business NXT GraphQL API, including creating orders and managing documents. We recognize that having learned GraphQL API does not mean you are able to write integrations without further help. Although tools such as GraphiQL, Postman, and Insomnia allow you to browse the schema and discover the Business NXT model and operations with it, writing code to handle your data requires further knowledge. In this section, we provide a set of recommended practices as well as a series of guides to help you perform some common business operations. - [Best practices for Business NXT API](bestpractices.md) - [How to create and finish an order](order.md) - [How to add an attachment to an order](orderattachments.md) - [How to invoice an order](invoice_order.md) - [How to create and update a batch](batch.md) - [How to add a document to a voucher](voucherdocument.md) - [How to read text from the Text table](texts.md) Best practices /businessnxtapi/howto/bestpractices page GraphQL best practices guide for naming queries, parameterizing with variables, fetching only needed data, ensuring input order, batch inserting, handling transactions, and executing long-running operations asynchronously. 2025-04-15T09:48:42+02:00 # Best practices GraphQL best practices guide for naming queries, parameterizing with variables, fetching only needed data, ensuring input order, batch inserting, handling transactions, and executing long-running operations asynchronously. This page contains a series of recommendations for creating GraphQL queries and mutations. ## Name the queries Although it's possible to create anonymous queries (and mutations) it is recommended that you give each operation a relevant name. The main benefits is that it indicates the purpose of each operation in a clear manner. > [!TIP] > > Give each query a name. ```graphql { title = "Recommended ✅" } query read_gla { useCompany (no: 123456789) { generalLedgerAccount { items { accountNo name } } } } ``` ```graphql { title = "Not recommended ❌" } query { useCompany (no: 123456789) { generalLedgerAccount { items { accountNo name } } } } ``` ## Parameterize the queries You probably have to run the same query with different input values (such as filters or inputs for create and update operations). At the least, you need to provide either a company number or a customer number or run any query. You should avoid hardcoding such input values and use variables to provide them. The main advange is that this makes a query reusable. > [!TIP]+ Best practice > > Use variables to parameterize queries. ```graphql { title = "Recommended ✅" } query read_gla($cid : Int!) { useCompany (no: $cid) { generalLedgerAccount { items { accountNo name } } } } Variables: { "cid" : 123456789 } ``` ```graphql { title = "Not recommended ❌" } query read_gla { useCompany (no: 123456789) { generalLedgerAccount { items { accountNo name } } } } ``` ## Read only the data you need A major advantage of GraphQL compared to other API standards (such as REST or SOAP) is that it solves the problem of data overfetching (fetching more data than what you need) and underfetching (fetching less data than what you need). This is because GraphQL is a data-oriented API. In GraphQL, you can fetch exactly what you need, no more, no less. Even when you fetch data from the same table, sometimes you might need more fields, sometimes less. Reading only the data you need improves the performance by reducing unnecessary operation on the server and data transfer over the network. > [!TIP]+ Best practice > > Always fetch only the data that you need for the current operation. Example: you need to display the general ledger account numbers and names. ```graphql { title = "Recommended ✅" } query read_gla($cid : Int!) { useCompany (no: $cid) { generalLedgerAccount { items { accountNo name } } } } ``` ```graphql { title = "Not recommended ❌" } query read_gla($cid: Int) { useCompany (no:$cid) { generalLedgerAccount { items { accountNo name name2 shortName accountGroup accountSubType taxCode changedDate changedTime createdDate createdTime joinup_Currency { isoCode name } } } } } ``` ## Input values have a specific order When you insert a new record or update an existing one, the order of the given input fields is important. Various business logic operations occur in the backend based on the order of these fields. Therefore, you must be aware of the order of fields for various tables and use it accordingly. Otherwise, the result of an insert or update operation will be (at least partially) incorrect. > [!TIP]+ Best practice > > When you specify inputs, do the following: > > - fields must be specified in the correct order > - if a field should have a specific value, provide it as an input (e.g. `customerNo : 42`) > - if a field should remain unassigned, do not list it in the input > - if a field should have a system given value, specify `null` for its value (e.g. `voucherNo : null`) > - if a field should have a system given value but in a given interval and you want to specify the interval, use the `_suggest_interval` suffixed fields (e.g. `customerNo_suggest_interval: {from: 10000, to: 20000}`). If you do not want to specify the interval, use the regular input field and give the value `null` (e.g. `customerNo : null`). > - do not explicitly request a suggested value for primary key fields (the system does that implicitly) ## Do not fetch total count when you don't need it When you fetch a list of records, you can also fetch the total count of records that match the given filter. Although there are cases when you need this information, there are also cases when it's not need. For instance, when you need to fetch all the records from a table, you don't really need the total count. You do a repetitive fetch of the records (page by page) until there are no more records to fetch. Fetching of the total count translates to a `SELECT Count(*) FROM table`. On large tables (with millions or tens of millions of records), this operation can be very slow, an can take up 20-30 seconds to execute. > [!TIP]+ Best practice > > Do not fetch the total count when you don't need it. Avoid fetching the total count when you fetch all the records from a table. ## Do not pass entire input objects as variables Variables are parsed and deserialized into objects on the server. This results in loosing the order of the fields as given in the input variables. However, as explained above, the order of the fields is important. The outcome is an incorrect result of the insert or update operation. > [!TIP]+ Best practice > > When you specify inputs, do not pass entire input objects as variables. ```graphql { title = "Recommended ✅" } mutation create_order($cid : Int!, $cno: Int, $ourref : String, $yourref : String) { useCompany(no : $cid) { order_create(values: [{ customerNo : $cno, orderType : 2, orderPreferences : 0, information5 : "1", information6 : null, transactionType : 1, ourReference : $ourref, yourReference : $yourref }]) { affectedRows items { orderNo } } } } { "cid":12345678, "order": { "customerNo":12345, "ourReference":"", "yourReference":"Ref Test" } } ``` ```graphql { title = "Not recommended ❌" } mutation create_order($cid : Int!, $order: Order_Input!) { useCompany(no : $cid) { order_create(values: [$order]) { affectedRows items { orderNo } } } } { "cid":12345678, "order": { "customerNo":12345, "orderType":2, "orderPreferences":0, "information5":"1", "information6":null, "transactionType":1, "ourReference":"", "yourReference":"Ref Test" } } ``` ## Batch multiple inserts into a single request If you need to insert multiple records into the same table, doing so one record at a time will incur performance penalties, the more significant the more records you have. This will in incorrect > [!TIP]+ Best practice > > Insert multiple records into a table with in a single request. ```graphql { title = "Recommended ✅" } mutation create_voucher($cid: Int, $batchId: Int!) { useCompany(no: $cid) { voucher_create(values: [ { batchNo: $batchId voucherNo : null voucherDate: null amountDomestic: 1300 creditAccountType: 2 creditAccountNo: 50000 }, { batchNo: $batchId voucherNo : null voucherDate: null amountDomestic: 600 debitAccountType: 3 debitAccountNo: 4300 }, { batchNo: $batchId voucherNo : null voucherDate: null amountDomestic: 700 debitAccountType: 3 debitAccountNo: 1930 }]) { affectedRows items { voucherNo } } } } ``` ```graphql { title = "Not recommended ❌" } mutation create_voucher($cid: Int, $batchId: Int!) { useCompany(no: $cid) { voucher_create(values: [{ batchNo: $batchId voucherNo : null voucherDate: null amountDomestic: 1300 creditAccountType: 2 creditAccountNo: 50000 }]) { affectedRows items { voucherNo } } } } mutation create_voucher($cid: Int, $batchId: Int!) { useCompany(no: $cid) { voucher_create(values: [{ batchNo: $batchId voucherNo : null voucherDate: null amountDomestic: 600 debitAccountType: 3 debitAccountNo: 4300 }]) { affectedRows items { voucherNo } } } } mutation create_voucher($cid: Int, $batchId: Int!) { useCompany(no: $cid) { voucher_create(values: [{ batchNo: $batchId voucherNo : null voucherDate: null amountDomestic: 700 debitAccountType: 3 debitAccountNo: 1930 }]) { affectedRows items { voucherNo } } } } ``` ## Implement transactions in your application Business NXT GraphQL does not support transactional operations. If a request succeeds partially and has failed operations, the successful operations will be persisted. There is no mechanism to request the system to roll back the operations when something failed. This must be done in the client application. > [!TIP]+ Best practice > > Ensure that operations that must succeed entirely to be persisted are handled transactionally in your application. Let us consider the example of creating an order. This is done in two steps: 1. The order is added to the `Order` table and its `orderNo` (at least) is retrieved. 2. The order lines are added to the `OrderLines` table for the newly created order. In your application, you must handle the following cases: 1. Creating the order fails. In this case, the order lines should not be inserted. If the operation succeeds partially, you must verify the returned error and decide whether you can continue, or the order should be deleted from the table and the operation retried. 2. Creating one or more order lines failed. Typically, you do not want partial/incomplete orders in the system. Therefore, in this case, you must delete the successfully added lines (from the `OrderLines` table) and the order (from the `Order` table). ## Long running operations should be async If a GraphQL request takes more than 30 seconds to execute, you will get an HTTP timeout, even though the operation will continue to execute on the server. To avoid this problem that implies loosing the result of the operation, Business NXT provides the ability to execute the operations asynchronously. You should identify potentially long running operations and execute them async. You will receive an operation ID immediately, and you will use this to poll for the result. > [!TIP]+ Best practice > > Execute long running operations asynchronously. ```graphql { title = "Recommended ✅" } mutation update_batches_async($args: Dictionary) { asyncQuery(query:""" mutation update_batches($cid: Int) { useCompany (no:$cid) { batch_processings { updateBatch { succeeded voucherJournalNo } } } } """, args: $args) { operationId status } } Variables: { "args" : { "cid" : 123456789 } } ``` ```graphql { title = "Not recommended ❌" } mutation update_batches($cid: Int) { useCompany (no:$cid) { batch_processings { updateBatch { succeeded voucherJournalNo } } } } Variables: { "cid" : 123456789 } ``` ## Use batched requests judiciously You can send multiple requests in a single HTTP request. This is called **batched requests**. It is possible to batch both queries and mutations together in any order. They are executed individually, one by one, in the order they where provided. When batching multiple request, the results will only be returned after all the requests have been executed. A large number of requests could make the overall operation exceed the HTTP timeout interval. In this case, the operation will timeout and the result will be lost. You should use batched requests judiciously, making sure their overall execution time will not trigger an operation timeout! > [!TIP]+ Best practice > > Prefer to use an insert with multiple records over batching multiple inserts, or, similarly, an update with multiple records over batching multiple updates. ```graphql { title = "Recommended ✅" } mutation multi_update($cid : Int!, $ono : Int!) { useCompany(no : $cid) { orderLine_update( filters: [ {_and:[ {orderNo : {_eq : $ono}} {lineNo : {_eq : 1}}]}, {_and:[ {orderNo : {_eq : $ono}} {lineNo : {_eq : 2}}]} ] values: [ { priceInCurrency : 199.99, invoiceNow : 1.0 }, { priceInCurrency : 59.99, invoiceNow : 1.0 }, ]) { affectedRows items { orderNo lineNo quantity priceInCurrency invoiceNow } } } } ``` ```graphql { title = "Not recommended ❌" } mutation one_update($cid : Int!, $ono : Int!) { useCompany(no : $cid) { orderLine_update( filter: {_and:[ {orderNo : {_eq : $ono}} {lineNo : {_eq : 1}}]}, value: { priceInCurrency : 199.99, invoiceNow : 1.0 }) { affectedRows items { orderNo lineNo quantity priceInCurrency invoiceNow } } } } mutation another_update($cid : Int!, $ono : Int!) { useCompany(no : $cid) { orderLine_update( filter: {_and:[ {orderNo : {_eq : $ono}} {lineNo : {_eq : 2}}]}, value: { priceInCurrency : 59.99, invoiceNow : 1.0 }) { affectedRows items { orderNo lineNo quantity priceInCurrency invoiceNow } } } } ``` How to create and finish an order /businessnxtapi/howto/order page Guide to creating, adding lines, and finishing an order using GraphQL mutations. Includes field requirements, example queries, and responses for each step. 2025-04-15T09:48:42+02:00 # How to create and finish an order Guide to creating, adding lines, and finishing an order using GraphQL mutations. Includes field requirements, example queries, and responses for each step. You need to perform the following GraphQL requests, in this order: ## 1. Creating a new order head The minimum information you must provide is: | Field | Type | Description | | ----- | ---- | ----------- | | `orderDate` | int | The date of the order. An integer in the form `yyyymmdd`. For instance, 23 March 2022, is 20220323. | | `customerNo` | int | The number identifying the customer in Business NXT. (Do not confuse this with the Visma.net customer number!) | Use a mutation with the `order_create` field to add a new order: ```graphql { title = "Query" } mutation create_order($cid : Int!, $cno : Int!, $date : Int) { useCompany(no : $cid) { order_create(values:[{ orderDate : $date customerNo : $cno }]) { affectedRows items { orderNo orderDate } } } } ``` ```graphql { title = "Result" } { "data": { "useCompany": { "order_create": { "affectedRows": 1, "items": [ { "orderNo": 439, "orderDate": 20220401 } ] } } } } ``` From the result, you must use the `orderNo` to add order lines and execute processings such as finishing the order. ## 2. Adding order lines The minimum information you must provide for each order line is: | Field | Type | Description | | ----- | ---- | ----------- | | `orderNo` | int | The order number, from the previous mutation. | | `productNo` | int | The number identifying the product in the system. | | `quantity` | int | The quantity of items that you want to add. | Use a mutation with the `orderLine_create` field to add one or more order lines: ```graphql { title = "Query" } mutation create_order_line($cid : Int!, $ono : Int!, $pno1 : String, $pno2 : String) { useCompany(no : $cid) { orderLine_create(values:[ { orderNo : $ono productNo : $pno1 quantity : 1 }, { orderNo : $ono productNo : $pno2 quantity : 2 }, ]) { affectedRows items { lineNo } } } } ``` ```graphql { title = "Result" } { "data": { "useCompany": { "orderLine_create": { "affectedRows": 2, "items": [ { "lineNo": 1 }, { "lineNo": 2 } ] } } } } ``` ### Alternative: Create an order and its order lines with a single request You can create both an order and its order lines with a single request, using the `@export` directive. This [directive](../features/directives.md#the-export-directive) allows you to capture the value of an evaluated field into a variable that can be used later in the query. ```graphql { title = "Query" } mutation create_order_and_line($cid : Int!, $cno : Int!, $date : Int $pno1 : String, $pno2 : String, $ono : Int = 0) { useCompany(no : $cid) { # create the order first order_create(values:[{ orderDate : $date customerNo : $cno }]) { affectedRows items { # capture the value of the orderNo field # into the ono variable orderNo @export(as: "ono") orderDate } } # create the order lines orderLine_create(values:[ { orderNo : $ono productNo : $pno1 quantity : 1 }, { orderNo : $ono productNo : $pno2 quantity : 2 }, ]) { affectedRows items { orderNo lineNo } } } } ``` ```graphql { title = "Result" } { "data": { "useCompany": { "order_create": { "affectedRows": 1, "items": [ { "orderNo": 440, "orderDate": 20221122 } ] }, "orderLine_create": { "affectedRows": 2, "items": [ { "orderNo": 440, "lineNo": 1 }, { "orderNo": 440, "lineNo": 2 } ] } } } } ``` ## 3. Finishing an order The minimum information you need to finish an order is the order number. Use a mutation with the `order_processings` field to execute a processing on an order. To finish the order, use the `finish` field: ```graphql { title = "Query" } mutation finish_order($cid : Int!, $ono : Int!) { useCompany(no : $cid) { order_processings { finish( filter: {orderNo:{_eq : $ono}} ) { succeeded items { handledOrderLine { lineNo finishedNow } } } } } } ``` ```graphql { title = "Result" } { "data": { "useCompany": { "order_processings": { "finish": { "succeeded": true, "items": [ { "handledOrderLine": [ { "lineNo": 1, "finishedNow": 1 }, { "lineNo": 2, "finishedNow": 2 } ] } ] } } } } } ``` ## 3. Finishing parameters The `finish` processing has a parameter called `finishType` that is an integer defining what the processing should do. The implicit value is 0 which means the whole order should be finished. This is what we have seen in the previous example, where the argument was missing, therefore its default value was used. The possible values for `finishType` are: | Value | Description | | ----- | ----------- | | 0 | Finish the whole order. | | 1 | Finish the articles specified by their barcode. | | 2 | Finish the articles specified by their product number. | | 3 | Finish the articles specified by their `TransactionInformation1` field. | Alternatively, you can use the `finishTypeAsEnum` field, that enables you to use a enumeration value, instead of a numerical one. The possible values are: | Name | Value | | ---- | ----- | | WholeOrder | 0 | | Barcode | 1 | | ProductNumber | 2 | | TransactionInformation1 | 3 | When `finishType` has any other value than 0, then you need to provide the additional arguments using the `group` parameter. This is a dictionary of key-value pairs, where the key is the bar code, product number, or the `TransactionInformation1` value of a product, and value is the quantity to finish (remove from the order). ```graphql { title = "Query" } mutation finish_order($cid : Int!, $ono : Int!) { useCompany(no : $cid) { order_processings { finish( args : { finishTypeAsEnum : ProductNumber group : [ {key: "HW1", quantity: 2}, {key: "AGG4", quantity: 5} ] }, filter: {orderNo:{_eq : $ono}} ) { succeeded items { handledOrderLine { lineNo finishedNow } } } } } } ``` ```graphql { title = "Result" } { "data": { "useCompany": { "order_processings": { "finish": { "succeeded": true, "items": [ { "handledOrderLine": [ { "lineNo": 1, "finishedNow": 2 }, { "lineNo": 2, "finishedNow": 5 } ] } ] } } } } } ``` How to add an attachment to an order /businessnxtapi/howto/orderattachments page Instructions on adding attachments to orders using GraphQL, including setting specific document types and handling file size limitations. 2024-09-24T15:57:29+02:00 # How to add an attachment to an order Instructions on adding attachments to orders using GraphQL, including setting specific document types and handling file size limitations. You can add an attachment to an order by runnning the `AddAttachment` processing on the `Order` table. The following query adds an attachment to an existing order: ```graphql mutation upload_invoice_attachment( $cid: Int!, $orderNo: Int!, $fileName: String!, $description: String!, $data: String!) { useCompany(no: $cid) { order_processings { addAttachment( filter: { orderNo: { _eq: $orderNo } }, args: { description: $description, fileName: $fileName, fileBytes: $data, sendWithInvoicesAndCreditNotes : 1 } ) { succeeded } } } } ``` > [!NOTE] > > The content of the attachment must be provided as a base64 encoded string. The precence of the `sendWithInvoicesAndCreditNotes : 1` argument will set the order attachment processing to `Invoices/credit notes`. You can see this in the front-end, when you look at the `Order attachment processing` column of the `Order attachment` table. ![Order attachment processing](../order_attachment_processing.png) If you want to set any of the other available values, such as `Pick lists` or `Packing slips`, then you must use the `sendWithDocumentTypes` parameter instead. The following table shows the available values for this parameter: | Value | Description | | ----- | ----------- | | 1 | Invoices/credit notes | | 2 | Consignment notes | | 4 | Packing slips | | 8 | Pick lists | | 16 | Order confirmations | | 32 | Quotations | | 64 | Purchase orders | | 128 | Inquiries | | 256 | Production orders | | 512 | Order prints | | 1024 | Approval requests | The following example shows how to set the `sendWithDocumentTypes` parameter to `Packing slips`: ```graphql mutation upload_packing_slip_attachment( $cid: Int!, $orderNo: Int!, $fileName: String!, $description: String!, $data: String!) { useCompany(no: $cid) { order_processings { addAttachment( filter: { orderNo: { _eq: $orderNo } }, args: { description: $description, fileName: $fileName, fileBytes: $data, sendWithDocumentTypes : 4 } ) { succeeded } } } } ``` > [!NOTE] > > Beware there is a limit to the raw size of a request. Currently, this is set at 15MB. This limit may be prone to future changes. How to create and update a batch /businessnxtapi/howto/batch page Guide on creating and updating a batch using GraphQL. Includes steps for batch creation, determining voucher number, voucher creation, and batch update. 2025-04-15T09:48:42+02:00 # How to create and update a batch Guide on creating and updating a batch using GraphQL. Includes steps for batch creation, determining voucher number, voucher creation, and batch update. You need to perform the following GraphQL requests, in this order: ## 1. Creating a new batch The minimum information you need to provide is: | Field | Type | Description | | ----- | ---- | ----------- | | `valueDate` | int | The value date as an integer in the form `yyyymmdd`. For instance, 23 March 2022, is 20220323. | | `voucherSeriesNo` | int | The number of the voucher series. | Use a mutation with the `batch_create` field to create a new batch: ```graphql { title = "Query" } mutation create_batch($cid : Int!, $vsn : Int!, $valuedt : Int!) { useCompany(no : $cid) { batch_create(values:[ { voucherSeriesNo : $vsn valueDate : $valuedt } ]) { affectedRows items { batchNo valueDate } } } } ``` ```graphql { title = "Result" } { "data": { "useCompany": { "batch_create": { "affectedRows": 1, "items": [ { "batchNo": 1002, "valueDate": 20220415 } ] } } } } ``` You must read and store the `batchNo` in order to use it for creating a voucher. ## 2. Determining the next voucher no When creating a voucher, you need to provide a voucher number. You can determine this value by reading the `nextVoucherNo` value from the `VoucherSeries` table. To do so, you must provide the `voucherSeriesNo` which should be the same used for creating the batch. ```graphql { title = "Query" } query read_voucher_series($cid : Int!, $vsn : Int!) { useCompany(no : $cid) { voucherSeries(filter : { voucherSeriesNo : {_eq: $vsn} } ) { totalCount items { voucherSeriesNo name lastVoucherNo nextVoucherNo } } } } ``` ```graphql { title = "Result" } { "data": { "useCompany": { "voucherSeries": { "totalCount": 1, "items": [ { "voucherSeriesNo": 6, "name": "Diverse bilag", "lastVoucherNo": 69999, "nextVoucherNo": 60003 } ] } } } } ``` ## 3. Creating a new voucher The minimum information you need to provide in order to create a new voucher is: | Field | Type | Description | | ----- | ---- | ----------- | | `batchNo` | int | The number of the batch. Use the `batchNo` returned from the first mutation. | | `voucherNo` | int | The number of the voucher. Use the `nextVoucherNo` value read previously. | | `debitAccountNo` | int | The debit account number. | | `creditAccountNo` | int | The credit account number. | | `amountDomestic` | decimal | The amount value. | | `voucherDate` | int | The date of the voucher. | | `valueDate` | int | The value date of the voucher. | Use a mutation with the `voucher_create` field to create a new voucher: ```graphql { title = "Query" } mutation create_voucher($cid : Int!, $bno : Int!, $cno : Int!, $vno : Int!, $vdt : Int!, $valuedt : Int!) { useCompany(no : $cid) { voucher_create(values: [ { batchNo : $bno voucherNo : $vno debitAccountNo : 1930 creditAccountNo: $cno amountDomestic : 100 voucherDate : $vdt valueDate : $valuedt } ]) { affectedRows items { batchNo voucherNo voucherDate valueDate } } } } ``` ```graphql { title = "Result" } { "data": { "useCompany": { "voucher_create": { "affectedRows": 1, "items": [ { "batchNo": 1002, "voucherNo": 60003, "voucherDate": 20220323, "valueDate": 20220415 } ] } } } } ``` ### Alternative: Create a new voucher with suggested voucher number Instead of determining the next voucher number in the series you can request that the system figures out the next value for it. You can also do that for other fields, such as voucher date. This is done with the [Suggest Feature](/businessnxtapi/schema/mutations/inserts). ```graphql { title = "Query" } mutation create_voucher($cid : Int!, $bno : Int!, $cno : Int!, $valuedt : Int!) { useCompany(no : $cid) { voucher_create(values: [ { batchNo : $bno voucherNo : null debitAccountNo : 1930 creditAccountNo: $cno amountDomestic : 100 voucherDate : null valueDate : $valuedt } ]) { affectedRows items { batchNo voucherNo voucherDate valueDate } } } } ``` ```graphql { title = "Result" } { "data": { "useCompany": { "voucher_create": { "affectedRows": 1, "items": [ { "batchNo": 1002, "voucherNo": 60003, "voucherDate": 20220323, "valueDate": 20220415 } ] } } } } ``` ### Alternative: Create the batch and the voucher in a single request This is possible using the [@export](../features/directives.md#the-export-directive) directive. ```graphql { title = "Query" } mutation create_batch($cid : Int!, $vsn : Int!, $valuedt : Int!, $cno : Int!, $bno : Int! = 0) { useCompany(no : $cid) { batch_create(values:[ { voucherSeriesNo : $vsn valueDate : $valuedt }] ) { affectedRows items { batchNo @export(as: "bno") valueDate } } voucher_create(values: [ { batchNo : $bno voucherNo : null debitAccountNo : 1930 creditAccountNo: $cno amountDomestic : 100 voucherDate : null valueDate : $valuedt } ]) { affectedRows items { batchNo voucherNo voucherDate valueDate } } } } ``` ```graphql { title = "Result" } { "data": { "useCompany": { "batch_create": { "affectedRows": 1, "items": [ { "batchNo": 1002, "valueDate": 20220415 } ] }, "voucher_create": { "affectedRows": 1, "items": [ { "batchNo": 1002, "voucherNo": 60003, "voucherDate": 20220323, "valueDate": 20220415 } ] } } } } ``` ## 4. Updating a batch The minimum information to update a batch is the batch number. Use a mutation with the `batch_processings` field to execute processings on the batch table. Use the `updateBatch` field to update the batch: ```graphql { title = "Query" } mutation update_batch($cid : Int!, $bno : Int!) { useCompany(no : $cid) { batch_processings { updateBatch(filter : { batchNo : {_eq : $bno} } ) { succeeded voucherJournalNo } } } } ``` ```graphql { title = "Result" } { "data": { "useCompany": { "batch_processings": { "updateBatch": { "succeeded": true, "voucherJournalNo": 193 } } } } ``` How to invoice an order /businessnxtapi/howto/invoice_order page Guide on invoicing orders using GraphQL mutation. Includes example query, result, and parameter descriptions for generating invoices and credit note reports. 2025-04-15T09:48:42+02:00 # How to invoice an order Guide on invoicing orders using GraphQL mutation. Includes example query, result, and parameter descriptions for generating invoices and credit note reports. To invoice an order, you must first finish the order. You can run the `invoicesAndCreditNotes` report on the `Order` table in order to invoice an order, as shown in the following example: ```graphql { title = "Query" } mutation invoice_order($cid : Int!, $ono : [Int]!) { useCompany(no : $cid) { order_reports { invoicesAndCreditNotes( filter:{orderNo :{_in : $ono}}, returnDocuments : true, printDestination : PRINT_TO_PDF, approval : true ) { succeeded documents { name content attachments { name content } } } } } } ``` ```graphql { title = "Result" } { "data": { "useCompany": { "order_reports": { "invoicesAndCreditNotes": { "succeeded": true, "documents": [ { "name": "1000013.pdf", "content": "JVBERi0xLjQKJdD...", "attachments": [] } ] } } } } } ``` The parameters have the following meaning: | Parameter | Description | | --------- | ----------- | | `filter` | The filter to apply to the report. In this case, we are filtering by the order number (one or more orders). | | `returnDocuments` | If set to `true`, the report will return the invoice document. | | `printDestination` | The destination where the invoice will be printed. In this case, we are printing to a PDF file. | | `approval` | It updates the tables when set to `true` (the default value). Set to `false` to only preview results. | > [!TIP] > > The content of the returned documents (and attachments) is base64-encoded. You must decode it to get the original file. How to add a document to a voucher /businessnxtapi/howto/voucherdocument page Guide on adding a document to a voucher and transferring it to file service using GraphQL mutations. Includes example queries and notes on limitations. 2024-09-24T15:57:29+02:00 # How to add a document to a voucher Guide on adding a document to a voucher and transferring it to file service using GraphQL mutations. Includes example queries and notes on limitations. You can add a document to a voucher by running the the `AddNewDocument` processing on the `Voucher` table. The following query adds a document to an existing voucher: ```graphql mutation upload_voucher_document( $cid: Int!, $batchNo : Int!, $voucherNo: Int!, $fileName: String!, $description: String!, $data: String!) { useCompany(no: $cid) { voucher_processings { addNewDocument( filter: {_and:[ {batchNo : {_eq : $batchNo}}, {voucherNo : {_eq : $voucherNo}} ]}, args: { fileName : $fileName, description : $description, fileBytes : $data }) { succeeded } } } } ``` > [!NOTE] > > The content of the document must be provided as a base64 encoded string. > [!NOTE] > > Beware there is a limit to the raw size of a request. Currently, this is set at 15MB. This limit may be prone to future changes. ## Transfering a document to the file service Documents attached using the `Voucher.AddNewDocument` processing are stored in the database. If you want to transfer the document to the file service, you can use the `UploadToFileService` processing on the `IncomingAccountingDocumentAttachment` table, as shown in the following example: ```graphql mutation move_attachment_to_fileservice( $cid: Int, $fileName: String, $tok: String) { useCompany(no: $cid) { incomingAccountingDocumentAttachment_processings { uploadToFileService( filter: {fileName: {_eq: $fileName}}, args: { connectToken: $tok } ) { succeeded } } } } ``` The argument `connectToken` requires a valid Visma Connect access token. This is the same token you use to authenticate your requests to the Business NXT API. You can execute these two processings sequentially, in a single request. ```graphql mutation upload_voucher_document( $cid: Int!, $batchNo : Int!, $voucherNo: Int!, $fileName: String!, $description: String!, $data: String!, $tok: String) { useCompany(no: $cid) { voucher_processings { addNewDocument( filter: {_and:[ {batchNo : {_eq : $batchNo}}, {voucherNo : {_eq : $voucherNo}} ]}, args: { fileName : $fileName, description : $description, fileBytes : $data }) { succeeded } } incomingAccountingDocumentAttachment_processings { uploadToFileService( filter: {fileName: {_eq: $fileName}}, args: { connectToken: $tok } ) { succeeded } } } } ``` > [!NOTE] > > In the future, the `Voucher.AddNewDocument` processing may be updated to transfer the document to the file service directly without needing to execute the second processing explicitly. How to read text from the Text table /businessnxtapi/howto/texts page Learn how to query and filter text values from the Text table for various fields, including payment and delivery methods, using GraphQL. 2025-04-15T09:48:42+02:00 # How to read text from the Text table Learn how to query and filter text values from the Text table for various fields, including payment and delivery methods, using GraphQL. ## Overview The `Text` table contains text values for various fields from many tables. Examples are the text values for the payment method and delivery method of an order. It is a common need to retrieve these texts. The following image shows possible values for the payment method for an order: ![Payment methods](../text_paymentmethods.png) These values can be retrieved with a query as follows: ```graphql { title = "Query" } query read_payment_methods($cid :Int!) { useCompany(no :$cid) { text(filter:{_and : [ {textType : {_eq : 7}}, {languageNo : {_eq : 47}} ]}) { totalCount items { textNo text } } } } ``` ```graphql { title = "Result" } { "data": { "useCompany": { "text": { "totalCount": 10, "items": [ { "textNo": 1, "text": "Kontant kasse" }, { "textNo": 2, "text": "Bankkort" }, { "textNo": 3, "text": "Visa" }, { "textNo": 4, "text": "Master-/Eurocard" }, { "textNo": 5, "text": "Am-ex" }, { "textNo": 8, "text": "Tilgodelapp" }, { "textNo": 9, "text": "Gavekort" }, { "textNo": 97, "text": "Diverse" }, { "textNo": 98, "text": "Differanse dagsoppgjør" }, { "textNo": 99, "text": "Avbrutt" } ] } } } } ``` ## Filtering These text values are available in multiple languages. Therefore, you need to filter by language and text type, as shown in the previous example. The possible values for languages are the following: | LanguageNo | Language | | ---------- | -------- | | 44 | English | | 45 | Danish | | 46 | Swedish | | 47 | Norwegian | The possible values for the texttype field are presented in the following table: | Text type | Description (Identifier) | | --------- | ----------- | | 1 | FreeText | | 2 | ReminderText | | 3 | DocumentName | | 4 | DeliveryTerms | | 5 | DeliveryMethod | | 6 | PaymentTerms | | 7 | PaymentMethod | | 8 | InformationCategory | | 9 | District | | 10 | Trade | | 11 | OrderPriceGroup | | 12 | CustomerPriceGroup1 | | 13 | ProductPriceGroup1 | | 14 | EmployeePriceGroup | | 15 | PayrollRateNo | | 16 | Unit | | 17 | TaxAndAccountingGroup | | 18 | AccountSet | | 19 | ProductType1 | | 20 | TransactionGroup1 | | 21 | ProductPriceGroup2 | | 22 | BudgetLineType | | 23 | StockCountGroup | | 24 | AssociateGrouping1 | | 25 | AssociateGrouping2 | | 26 | AssociateGrouping3 | | 27 | AssociateGrouping4 | | 28 | AssociateGrouping5 | | 29 | AssociateGrouping6 | | 30 | GeneralLedgerAccountGrouping1 | | 31 | GeneralLedgerAccountGrouping2 | | 32 | GeneralLedgerAccountGrouping3 | | 33 | GeneralLedgerAccountGrouping4 | | 34 | GeneralLedgerAccountGrouping5 | | 35 | GeneralLedgerAccountGrouping6 | | 36 | OrgUnitGrouping1 | | 37 | OrgUnitGrouping2 | | 38 | OrgUnitGrouping3 | | 39 | OrgUnitGrouping4 | | 40 | OrgUnitGrouping5 | | 41 | OrgUnitGrouping6 | | 42 | ProductGrouping1 | | 43 | ProductGrouping2 | | 44 | ProductGrouping3 | | 45 | ProductGrouping4 | | 46 | ProductGrouping5 | | 47 | ProductGrouping6 | | 48 | OrderGrouping1 | | 49 | OrderGrouping2 | | 50 | OrderGrouping3 | | 51 | OrderGrouping4 | | 52 | OrderGrouping5 | | 53 | OrderGrouping6 | | 54 | ProductTransactionControlStatus | | 55 | AccountingTransactionControlStatus | | 56 | ReportHeading | | 57 | SumLine | | 58 | ProductType2 | | 59 | TransactionGroup2 | | 60 | OrgUnitStatus | | 61 | CapitalAssetGrouping1 | | 62 | CapitalAssetGrouping2 | | 63 | CapitalAssetGrouping3 | | 64 | CapitalAssetGrouping4 | | 65 | CapitalAssetGrouping5 | | 66 | CapitalAssetGrouping6 | | 67 | PaymentPriority | | 68 | DeliveryPriority | | 69 | AppointmentPriority | | 70 | DayPriority | | 71 | DocumentGroup | | 72 | ProductPriceGroup3 | | 73 | CustomerPriceGroup2 | | 74 | AssociateGrouping7 | | 75 | AssociateGrouping8 | | 76 | AssociateGrouping9 | | 77 | AssociateGrouping10 | | 78 | AssociateGrouping11 | | 79 | AssociateGrouping12 | | 80 | GeneralLedgerAccountGrouping7 | | 81 | GeneralLedgerAccountGrouping8 | | 82 | GeneralLedgerAccountGrouping9 | | 83 | GeneralLedgerAccountGrouping10 | | 84 | GeneralLedgerAccountGrouping11 | | 85 | GeneralLedgerAccountGrouping12 | | 86 | CapitalAssetGrouping7 | | 87 | CapitalAssetGrouping8 | | 88 | CapitalAssetGrouping9 | | 89 | CapitalAssetGrouping10 | | 90 | CapitalAssetGrouping11 | | 91 | CapitalAssetGrouping12 | | 92 | OrgUnitGrouping7 | | 93 | OrgUnitGrouping8 | | 94 | OrgUnitGrouping9 | | 95 | OrgUnitGrouping10 | | 96 | OrgUnitGrouping11 | | 97 | OrgUnitGrouping12 | | 98 | ProductGrouping7 | | 99 | ProductGrouping8 | | 100 | ProductGrouping9 | | 101 | ProductGrouping10 | | 102 | ProductGrouping11 | | 103 | ProductGrouping12 | | 104 | OrderGrouping7 | | 105 | OrderGrouping8 | | 106 | OrderGrouping9 | | 107 | OrderGrouping10 | | 108 | OrderGrouping11 | | 109 | OrderGrouping12 | | 110 | TransactionGroup3 | | 111 | TransactionGroup4 | | 112 | ProductType3 | | 113 | ProductType4 | | 114 | AppointmentGrouping1 | | 115 | AppointmentGrouping2 | | 116 | AppointmentGrouping3 | | 117 | AppointmentGrouping4 | | 118 | AppointmentGrouping5 | | 119 | AppointmentGrouping6 | | 120 | AppointmentGrouping7 | | 121 | AppointmentGrouping8 | | 122 | AppointmentGrouping9 | | 123 | AppointmentGrouping10 | | 124 | AppointmentGrouping11 | | 125 | AppointmentGrouping12 | | 126 | PriceType | | 127 | CreateDocument | | 128 | CrmTexts | | 129 | EftCurrencyCode | | 130 | EftTaxCode | | 131 | EftDeclarationCode | | 132 | EftPaymentMethod | | 133 | PriceRefundGrouping1 | | 134 | PriceRefundGrouping2 | | 135 | EuGoodsStatisticsNo | | 137 | AssociateInformationGroup1 | | 138 | AssociateInformationGroup2 | | 139 | AssociateInformationGroup3 | | 140 | AssociateInformationGroup4 | | 141 | AssociateInformationGroup5 | | 142 | AssociateInformationGroup6 | | 143 | AssociateInformationGroup7 | | 144 | AssociateInformationGroup8 | | 145 | ProductCategory | | 146 | CustomerPriceGroup3 | | 147 | ProxyType | | 148 | RoleType | | 149 | MessageType | | 150 | ExternalConfigurationGrouping1 | | 151 | ExternalConfigurationGrouping2 | | 152 | ExternalConfigurationGrouping3 | | 153 | ExternalConfigurationGrouping4 | | 154 | InterestRateGroup | | 155 | SmsProvider | | 156 | FreeInformationType1 | | 157 | FreeInformationGrouping1 | | 158 | FreeInformationGrouping2 | | 159 | FreeInformationGrouping3 | | 160 | FreeInformationGrouping4 | | 161 | ShipmentGrouping1 | | 162 | ShipmentGrouping2 | | 163 | VoucherGroup1 | | 164 | VoucherGroup2 | | 165 | VoucherTypeText | | 166 | AlternativeProductGrouping1 | | 167 | AlternativeProductGrouping2 | | 168 | StructureGrouping1 | | 169 | StructureGrouping2 | | 170 | StructureGrouping3 | | 171 | StructureGrouping4 | | 172 | StructureGrouping5 | | 173 | StructureGrouping6 | | 174 | StructureGrouping7 | | 175 | StructureGrouping8 | | 176 | StructureGrouping9 | | 177 | StructureGrouping10 | | 178 | StructureGrouping11 | | 179 | StructureGrouping12 | | 180 | FreeInformationCategory | | 181 | ExternalExportGrouping | | 182 | TimeScheduleBalanceGroup | | 183 | TaxTerm | | 184 | BankFormat | | 185 | AppointmentDescription | | 186 | RemittanceCodeForRemittanceAgreements | | 187 | RegistrationTypeForPaymentAgreements | | 188 | CommentCodeForPaymentAgreements | | 189 | FreeInformation1Type2 | | 190 | FreeInformation1Type3 | | 191 | FreeInformation2Type2 | | 192 | FreeInformation2Type3 | | 193 | FreeInformation3Type2 | | 194 | FreeInformation3Type3 | | 195 | FreeInformationGrouping5 | | 196 | FreeInformationGrouping6 | | 197 | FreeInformationGrouping7 | | 198 | FreeInformationGrouping8 | | 199 | FreeInformationGrouping9 | | 200 | FreeInformationGrouping10 | | 201 | FreeInformationGrouping11 | | 202 | FreeInformationGrouping12 | | 203 | EftFormType | | 204 | DeliveryAlternativeGrouping1 | | 205 | DeliveryAlternativeGrouping2 | | 206 | GiroType | | 207 | OssTaxTerm | | 208 | EmailTemplateGroup | | 211 | ExemptReason | | 212 | InvoiceNote | ## Joining values from the text table The values from the `Text` table are usually needed when reading records from other tables. An example was previously given: reading the payment method and the delivery method of an order. A direct joining mechanism is not available in the API (such as a `joinup_` \ `joindown_` relation). However, it is possible to read them in a single query with the help of the [@export directive](../features/directives.md#the-export-directive). The following example shows how to read the payment and delivery method of a particular order: ```graphql { title = "Query" } query read_texts($cid : Int!, $orderNo : Int, $dm : Long = 0, $pm : Long = 0) { useCompany(no : $cid) { order(filter :{orderNo :{_eq : $orderNo}}) { items { orderNo deliveryMethod @export(as :"dm") paymentMethod @export(as : "pm") } } deliveryMethodName : text(filter: { languageNo :{_eq : 47} # Norwegian textType : {_eq : 5} # delivery method textNo : {_eq : $dm} }) { items { text } } paymentMethodName : text(filter: { languageNo :{_eq : 47} # Norwegian textType : {_eq : 7} # payment method textNo : {_eq : $pm} }) { items { text } } } } ``` ```graphql { title = "Result" } { "data": { "useCompany": { "order": { "items": [ { "orderNo": 1, "deliveryMethod": 4, "paymentMethod": 3 } ] }, "deliveryMethodName": { "items": [ { "text": "Tollpost" } ] }, "paymentMethodName": { "items": [ { "text": "Visa" } ] } } } } ``` What you have to do is the following: - read the numerical value of the text type you want to retrieve (such as `paymentMethod` and `deliveryMethod` in this example) - export this numerical value to a query variable using the `@export` directive - make another read, this time from the `Text` table, and filter using the desired language, the appropriate text type (such as 5 for `DeliveryMethod` and 7 for `PaymentMethod`), and the text number stored in the (previously read) query variable - you can make as many reads as you want from the `Text` table in a single query, provided you diferentiate them using [aliases](../features/aliases.md)