API Integration Guide
This section describes how to create Custom Templates that create, read, update and delete data using the API, the focus is on the javascript parw.
This is work in progress
Prequsite
-
All examples are written inside a
<script>...</script>tag in the header of the document -
Setup the base information for the system token and the customer as constants
1
2
const CSRF_TOKEN = 'csrf';
const baseUrl = '/api/v1/';
Naming convention
The convention for objects fetched with jquery is to prefixed them with $
The convention for variables is to start with lover case and use camel-case for the
Exampler
1
2
3
const numberOfItems = 5;
function updateOrderStatus();
const $confirmButton = $("#confirm-button");
Flow
The flow in the the template is as follow.
- initialize
- listen for events
- Handle events
- Update the db
- Render updates on the template
Listen for events
This is handled with jquery, this is where data is collected and passed on to the handlers
example:
1
2
3
4
5
$(document).on('click', '#confirm-button', function () {
const $confirmButton = $("#confirm-button");
const orderId = $("order-id").val();
confirmAndCloseOrderHandler({$confirmButton, orderId});
});
Handle events
Handlers are postfixed with ‘xxxHandler’, it’s role is to orkestrate what happens when an event occurs
Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// collectData
const $order = $("#order");
// validate data
if ($orderStatus.data("status") == "closed"){
renderWarning("the order is alreaddy closed");
$("orderClosed).show();
return;
}
// update the db
try {
await updateOrder({orderId, orderStatus.val()});
} catch error {
renderError("Outch the developre is on vacation, in Iran ");
}
}
Up date the db
- url
- payload
create the url
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
async function updateOrder( totalWeight ) {
const dbValue = quantityToDbValueHelper(totalWeight);
const url =
`${baseUrl}slug/orders/production/${orderId}/inbound/lots/${inboundLotId}`;
const payload = {
quantity: dbValue
};
try {
await apiPut(url, payload);
} catch (err) {
throw err;
}
}
API helpers
- apiGet(url, payload: = {})
- apiPost(url, payload)
- apiPut(url, payload)
- apiDelete(url)
example
1
2
3
4
5
6
7
8
9
function apiGet(url, payload = {}) {
return request(url, {
method: "GET",
params: { pds: 1, ...payload }
});
}
Helpers
Write helpers to keep the code DRY
Common provided helpers for validation and convetion of values.
examples
1
2
3
4
5
6
7
8
9
10
11
function calculateTotalWeight(items = []) {
return items.reduce(
(acc, item) => {
const weight = Number(item.record_ref) || 0;
acc.totalWeight += weight;
return acc;
},
{ totalWeight: 0 }
);
}
Some common used helpers are provided including a library
TO BE DONE
Function provided:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Formats a numeric quantity using locale-specific number formatting
* and appends the given unit of measure.
*
* Example:
* formatQuantityWithUnit(3.5) // "3.500 kg" (da-DK)
*/
function formatQuantityWithUnit(num) {
const languageCode = getLanguageCodeHelper();
return (
new Intl.NumberFormat(languageCode, {
minimumFractionDigits: 3,
maximumFractionDigits: 3
}).format(Number(num)) +
' ' +
defaultUnitOfMeasure
);
}
1
2
3
4
5
6
7
8
9
10
11
function getLocaleSeparators(locale = getLanguageCodeHelper()) {
const parts = new Intl.NumberFormat(locale).formatToParts(12345.6);
const decimal =
parts.find(p => p.type === 'decimal')?.value || '.';
const group = decimal === ',' ? '.' : ',';
return { group, decimal };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
* Parses a locale-formatted numeric string into a JavaScript Number.
*
* - Accepts strings containing thousand separators, decimal separators,
* and optional unit text (e.g. "1.000,5 kg").
* - Validates grouping and decimal placement according to the current locale.
* - Returns NaN if the format is invalid.
*
* Examples (da-DK):
* parseNumberHelper("1.000,5 kg") → 1000.5
* parseNumberHelper("4,4 kg") → 4.4
* parseNumberHelper("12.34.5") → NaN
*
* @param {string|number} value - The input value to parse.
* @returns {number} Parsed numeric value or NaN if invalid.
*/
function parseNumberHelper(value) {
if (typeof value === 'number') {
return value < 0 ? NaN : value;
}
const raw = String(value).trim();
if (!raw) return NaN;
if (raw[0] === '-') return NaN;
const cleaned = raw.replace(/[^\d.,-]/g, '');
if (!cleaned) return NaN;
const { decimal } = getLocaleSeparators();
const decimalCount = cleaned.split(decimal).length - 1;
if (decimalCount > 1) return NaN;
const lastSeparator = cleaned.match(/[.,](?!.*[.,])/);
if (lastSeparator && lastSeparator[0] !== decimal) return NaN;
const decIndex = cleaned.lastIndexOf(decimal);
if (decIndex !== -1) {
const afterDecimal = cleaned.slice(decIndex + 1);
if (/[.,]/.test(afterDecimal)) return NaN;
}
const integerPart =
decIndex === -1 ? cleaned : cleaned.slice(0, decIndex);
if (integerPart.includes('.') || integerPart.includes(',')) {
const groupSep = decimal === ',' ? '.' : ',';
const groups = integerPart.split(groupSep);
if (!groups.slice(1).every(g => g.length === 3)) return NaN;
}
const normalized = cleaned
.replace(new RegExp(`\\${decimal}`, 'g'), '.')
.replace(/[.,](?=.*[.,])/g, '');
const num = Number(normalized);
// ❗ reject negative after parsing (safety)
return Number.isNaN(num) || num < 0 ? NaN : num;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
function quantityToDbValueHelper(value) {
if (typeof value === 'number') {
return Math.round(value / defaultUomConversion);
}
const raw = String(value).trim();
if (!raw) return NaN;
// keep digits + separators only
let cleaned = raw.replace(/[^\d.,]/g, '');
if (!cleaned) return NaN;
const { decimal, group } = getLocaleSeparators();
const decimalCount = cleaned.split(decimal).length - 1;
if (decimalCount > 1) return NaN;
const lastSep = cleaned.match(/[.,](?!.*[.,])/);
if (lastSep && lastSep[0] !== decimal) return NaN;
const decIndex = cleaned.lastIndexOf(decimal);
if (decIndex !== -1) {
const afterDecimal = cleaned.slice(decIndex + 1);
if (/[.,]/.test(afterDecimal)) return NaN;
}
const integerPart = decIndex === -1 ? cleaned : cleaned.slice(0, decIndex);
if (integerPart.includes(group)) {
const groups = integerPart.split(group);
if (!groups.slice(1).every(g => g.length === 3)) return NaN;
}
if (group) {
cleaned = cleaned.replace(new RegExp(`\\${group}`, 'g'), '');
}
if (decimal !== '.') {
cleaned = cleaned.replace(decimal, '.');
}
const num = Number(cleaned);
if (Number.isNaN(num)) return NaN;
return Math.round(num / defaultUomConversion);
}
## Render methods
1
2
3
function renderTotalBoxesForLot(lotId, boxes) {
$(`#total-boxes_${lotId}`).val(boxes);
}
## HTML ellements
Dialog for confirmations and alerts.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div class="modal fade" id="apiErrorModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="staticBackdropLabel">Error!</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="alert alert-danger" role="alert">
Ett tekniskt fel uppstod vid kontakt med servern.
</div>
</div>
<div class="modal-footer">
<button id="close-api-eror-modal" type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>