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>