LoadViewer Integration - V1
V
Manage your Api Keys.
V1 Details
- Description
- Initial release supporting data in CSV formats.
- Release Date
- 2025-04-01
- Status
- Active
- Upload Settings (ERP → LoadViewer)
- details
Understanding data flow from your ERP to LoadViewer
General guidelines specific to this version of the API.
1. Data Format
LoadViewer expects data from your ERP in a structured CSV format with two special prefix columns:
- Column 1: Section type —
S
(Shipment),B
(Container),C
(Cargo) - Column 2: Row type —
H
(Header),D
(Detail)
Structure Example
S,H,Reference,Hanging Allowed,Use Multiple Containers
S,D,SANDBOX-01,True,False
B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm
B,D,5867,2352,2393,27200,0
C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper
C,D,#008000,700,500,300,91.5,300,0,0,True,1,0,0,0,0,0,0,,0,,0,,
Explanation
S,H,Reference,Hanging Allowed,Use Multiple Containers
: Shipment-level headersS,D,SANDBOX-01,True,False
: Shipment-level data rowsB,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm
: Container headersB,D,5867,2352,2393,27200,0
: Container data rows (can be multiple)C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper
: Cargo headersC,D,#008000,700,500,300,91.5,300,0,0,True,1,0,0,0,0,0,0,,0,,0,,
: Cargo data rows (can be multiple)
Expected Fields
We have used the FIXED, Mandatory, and Optional identifiers for the fields. Rules for data values in each of these fields are as follows.
For FIXED fields no other data is accepted. Match the exact upper and lower case. Do not include extra spaces or characters in any such FIXED fields.
For Mandatory fields, the field cannot be blank. Use 0 for numeric fields and meaningful text for string (character) mandatory fields.
For Optional fields, you may leave them blank for text fields and use 0 for numeric values.
-
Shipment(S) Header(H) Row - Only one such row per CSV allowed.
S
: FIXED. Always provideS
here. Since this belongs to the Shipment Section.H
: FIXED. Always provideH
here. Since this is a Header row of the Section.Reference
: FIXEDHanging Allowed
: FIXEDUse Multiple Containers
: FIXED
-
Shipment(S) Details(D) Row - Only one such row per CSV allowed.
- Section =
S
: FIXED. Always provideS
here. Since this belongs to the Shipment Section. - Row Type =
D
: FIXED. Always provideD
here. Since this is a Data row of the Section. -
Reference =
SANDBOX-01
: Mandatory, String: MaxLength[50]. Provide a unique shipment/job reference.Values starting with
SANDBOX
are for testing purposes and are not chargeable.
When using in a production environment, provide a unique reference—such as an Order ID, Proforma Invoice Number, or Job Code. The value should not exceed 50 characters in total. -
Hanging Allowed =
True
: Mandatory. Allowed values –True
orFalse
.This value tells LoadViewer whether placing any cargo over other cargo requires 100% base support (set
False
) or 70% base support is sufficient (setTrue
). -
Use Multiple Containers =
False
: Mandatory. Allowed values –True
orFalse
.This is useful to control your coin usage for this shipment. When you want the complete cargo to be loaded using the best containers and no unplanned (shut out) cargo should be left in the packing list, set
True
. If you want only one container to be planned, setFalse
, and your plan may have Shut-Out (unpacked) cargo. During the planning phase—when you are checking the loadability of an SKU before accepting/revising an order—useFalse
. In the execution (consolidation) phase—while creating a CLP (Container Load Plan) to estimate the number of FCL containers required and how much can be shipped LCL—supply theTrue
value here.
- Section =
-
Container(B) Header(H) Row - Only one such row per CSV allowed.
B
: FIXED. Always provideB
here. Since this belongs to the Container (Bin for you to relate to the Abbreviation B) Section.H
: FIXED. Always provideH
here. Since this is a Header row of the Section.Length mm
: FIXED.Width mm
: FIXED.Height mm
: FIXED.Max Weight Kg
: FIXEDBack Margin mm
: FIXED
-
Container(B) Details(D) Row - Multilple rows per CSV allowed.
- Section =
B
: FIXED. Always provideB
here. Since this belongs to the Container Section. - Row Type =
D
: FIXED. Always provideD
here. Since this is a Data row of the Section. -
Length mm =
5867
: Mandatory, Number: Range [250-30000]. Decimals and commas not allowed. Internal Length (Depth) of the Container in Millimeters (mm). -
Width mm =
2352
: Mandatory, Number: Range [250-3000]. Decimals and commas not allowed. Internal Width of the Container in Millimeters (mm). -
Height mm =
2393
: Mandatory, Number: Range [250-3000]. Decimals and commas not allowed. Internal Height of the Container in Millimeters (mm). -
Max Weight Kg =
27200
: Mandatory, Decimal (up to three digits after the decimal). Range [0.001, 50000]. Commas not allowed.Maximum Weight that can be stuffed in the Container in Kg (Kilogram).
-
Back Margin mm =
0
: Mandatory, Number: Range [0-200]. Decimals not allowed.Sometimes you may wish LoadViewer not to plan the area near the door of the Container. That area can be used by your loading team for placing latches for the safety of unloading staff or for other purposes. A maximum of 20 CM (or 200 mm) can be reserved for such uses.
- Section =
-
Cargo(C) Header(H) Row - Only one such row per CSV allowed.
C
: FIXED. Always provideC
here. Since this belongs to the Cargo Section.H
: FIXED. Always provideH
here. Since this is a Header row of the Section.Color
: FIXED.Length mm
,Width mm
andHeight mm
: FIXED.Weight Kg
: FIXED.Cartons
: FIXED.Additional Cartons
: FIXED.Placement
: FIXED.Vertical Rotation Allowed
: FIXED.SKU per Carton
: FIXED.Empty Pallet Height mm
: FIXED.Tray Cap mm
: FIXED.Tray Height mm
: FIXED.Air Space Lenght mm
,Air Space Width mm
andAir Space Height mm
: FIXED.Name of Set
: FIXED.Cartons Ratio in the Set
: FIXED.Description
: FIXED.Destination
: FIXED.PO
: FIXED.Shipper
: FIXED.
-
Cargo(C) Details(D) Row - Multilple rows per CSV allowed.
- Section =
C
: FIXED. Always provideC
here. Since this belongs to the Cargo Section. - Row Type =
D
: FIXED. Always provideD
here. Since this is a Data row of the Section. -
Color =
#008000
: Mandatory, String: MaxLength [9], Hex color code for your cargo. -
Length mm =
700
, Width mm =500
and Height mm =300
: Mandatory, Number: Range [10–3000]. Decimals and commas are not allowed. External dimensions of the cargo in millimeters (mm): Length, Width, and Height respectively. -
Weight Kg =
91.5
: Mandatory, Decimal (up to three digits after the decimal). Range [0.001–1000]. Commas are not allowed.Weight of the carton or pallet in kilograms (kg).
-
Cartons =
300
: Mandatory, Number: Range [1–30000]. Decimals and commas are not allowed. Number of desired cartons (or pallets) you want to load into containers. -
Additional Cartons =
0
: Mandatory, Number: Range [0–30000]. Decimals and commas are not allowed. LoadViewer will try to place number of additional cargo (cartons or pallets) defined here in any remaining space that cannot be filled by the desired quantities specified in the Cartons section. -
Placement =
0
: Mandatory, Number: Range [0–4]. Defines how the item should be vertically placed inside the container.This setting determines the vertical placement of the item, mainly for palletized cargo or goods with specific stacking requirements.
-
Vertical Rotation Allowed =
True
: Mandatory. Allowed values:True
orFalse
. Indicates whether the item can be rotated vertically during load planning.When vertical rotation is
True
, LoadViewer explores all six possible orientations to optimize space inside the container.
💡 Image showing all six possible rotations : -
SKU per Carton =
1
: Mandatory, Number: Range[1–10000]. Decimals and commas are not allowed. Represents how many units of the SKU are packed inside one carton or pallet. -
Empty Pallet Height mm =
0
: Mandatory, Number: Range [90–200] or 0. Use0
for cartons, and any value between90
to200
mm for empty pallet height (i.e., pallet feet only). -
Tray Cap mm =
0
: Mandatory, Number: Range [0–200]. Represents the top cap height. This does not reserve any extra space above Height mm but adds a line near the top edge of the cargo for visual reference. -
Tray Height mm =
0
: Mandatory, Number: Range [0–200]. Represents the tray height in mm. This does not reserve any extra space below Height mm but adds a line near the bottom edge of the cargo for visual reference. -
Air Space Length mm =
0
, Air Space Width mm =0
, and Air Space Height mm =0
: Mandatory, Number: Range [0–20]. This can be used to allow LoadViewer to keep margins around your cargo up to 2 cm (20 mm). - Name of Set : Optional, String: MaxLength [1], Range [B–Y]. You can define up to 24 sets per shipment. Values A and Z are reserved. Leave it blank if your cargo does not belong to any set.
-
Cartons Ratio in the Set =
0
: Mandatory, Number: Range [0–100]. Works in conjunction with Name of Set. When Name of Set is defined, this must be greater than0
. - Description : Optional, String: MaxLength [60]. You can enter SKU number, item description, or any meaningful value for LoadViewer reports. For ERP integration, this can also serve as a cargo ID to link with ERP entities.
-
Destination =
0
: Mandatory, Number: Range [0–200]. Use0
when all cargo is for a single destination. This indicates load/unload priority. Lower values are placed closer to the container door for earlier unloading. Higher values are placed deeper inside the container for last unloading. This improves cargo handling efficiency during transit. -
PO : Optional, String: MaxLength [20]. You can enter the Purchase Order number to appear on LoadViewer reports.
LoadViewer uses this to group SKUs of the same PO in the same container, if sizes and quantities allow.
💡 Practical Use Case – PO (Purchase Order): - Shipper : Optional, String: MaxLength [20]. You can define your department or division name for use in LoadViewer reports. LoadViewer uses this to group SKUs of the same shipper in a single container, if sizes and quantities allow. If multiple departments are involved in a PO, LoadViewer prioritizes departments with higher volume cargo to be placed deeper in the container.
- Section =
Notes:
- Separator must be a comma (,)
- All header and detail rows must strictly follow the
S/B/C
+H/D
format. - All header rows are case sensitive and must strictly follow the same format as mentioned in the Headers sections above.
- Always provide a
S,H
row followed by at least oneS,D
row - The order of sections can be any. But our example mostly reflects: Shipment → Container(s) → Cargo(s)
Tip: Download SANDBOX-01 and modify the sandbox CSV for accuracy and editing in your text editor / Excel / OpenOffice Calc or other editor of your choice.
2. Authentication
The V
How it works:
- Each user is issued a unique API Key and associated Email.
- Use these credentials to obtain a JWT token using a secure POST request to our token endpoint.
Manage keys
API Key Manager
Business Email Domain Required
API keys are not available for accounts using free email domains (e.g., Gmail, Hotmail, Yahoo).
If you already have a business email, please log in with that account.
Or, update your email to a business address like your_name@yourcompany.com
.
Token Url:
POST https://www.loadviewer.com/api/auth/jwt/token
Request Headers:
Content-Type: application/json
Request Body:
{
"email": "your.email@yourcompany.com",
"apikey": "YOUR_API_KEY_HERE"
}
{
"token": "eyJh...pljBlI"
}
{
"status":"error",
"message": "Your domain is not allowed",
"code":401,
"data": null,
"errors":
[
{
"errorCode":"401",
"errorMessage":"Your domain is not Allowed"
}
],
"meta": null
}
Key Rotation & Security:
- Keep your API key secure. Never expose it in frontend code or public repositories.
- You can regenerate (rotate) your API key at any time. Once rotated, the old key becomes invalid immediately.
- Always use HTTPS to encrypt the API key and token during transmission.
-
Cache the token on the client side and reuse it for future requests. When the token expires, you'll receive:
Code/Status = 410, Message = "Token has expired. Please call /api/auth/jwt/token to retrieve a new one." - Monitor usage and manage rate limits using the counters associated with your API key.
If a request is made without a valid key, the API will respond with HTTP 401 Unauthorized
.
Example: Get Token in Different Languages
YOUR_Email
and YOUR_API_KEY_HERE
variables in code sections with your actual email and the API Key generated in
Api Key Manager.
' === Constants ===
Const LoadViewerTokenURL As String = "https://www.loadviewer.com/api/auth/jwt/token"
Const LoadViewerAPIEmail As String = "YOUR_Email"
Const LoadViewerAPIKey As String = "YOUR_API_KEY_HERE"
' === Get JWT Token ===
' Returns the token string to be passed in to Authorization Header for every call to
' LoadViewer endpoints like https://www.loadviewer.com/api/integration/v1/upload
Function getJwtToken() As String
On Error GoTo ErrHandler
Dim http As Object
Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
Dim jsonBody As String
jsonBody = "{""email"":""" & LoadViewerAPIEmail & """,""apikey"":""" & LoadViewerAPIKey & """}"
With http
.SetTimeouts 3000, 3000, 5000, 5000 ' 3 sec connect/send, 5 sec receive/resolve
.Open "POST", LoadViewerTokenURL, False
.SetRequestHeader "Content-Type", "application/json"
.Send jsonBody
End With
If http.Status = 200 Then
getJwtToken = extractTokenFromResponse(http.ResponseText)
Else
getJwtToken = "ERROR: " & http.Status & " - " & http.ResponseText
End If
Exit Function
ErrHandler:
getJwtToken = "Exception: " & Err.Description
End Function
' === Extract "token" field from JSON response ===
Function extractTokenFromResponse(jsonText As String) As String
Dim tokenStart As Long, tokenEnd As Long
tokenStart = InStr(jsonText, """token"":""")
If tokenStart = 0 Then
extractTokenFromResponse = ""
Exit Function
End If
tokenStart = tokenStart + 9
tokenEnd = InStr(tokenStart, jsonText, """")
extractTokenFromResponse = Mid(jsonText, tokenStart, tokenEnd - tokenStart)
End Function
import requests
import json
url = 'https://www.loadviewer.com/api/auth/jwt/token'
payload = {
'email': 'YOUR_Email',
'apikey': 'YOUR_API_KEY_HERE'
}
headers = {'Content-Type': 'application/json'}
response = requests.post(url, data=json.dumps(payload), headers=headers)
if response.status_code == 200:
response_data = response.json()
token = response_data['token']
print(f'Token: {token}')
else:
print(f'Error: {response.status_code} - {response.text}')
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
class Program
{
static async Task Main()
{
var url = "https://www.loadviewer.com/api/auth/jwt/token";
var email = "YOUR_Email";
var apiKey = "YOUR_API_KEY_HERE";
var payload = new
{
email = email,
apikey = apiKey
};
var json = System.Text.Json.JsonSerializer.Serialize(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
using var client = new HttpClient();
var response = await client.PostAsync(url, content);
var responseBody = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
var token = JObject.Parse(responseBody)["token"]?.ToString();
Console.WriteLine("Token: " + token);
}
else
{
Console.WriteLine($"Error: {(int)response.StatusCode} - {responseBody}");
}
}
}
// Using Fetch API
const url = 'https://www.loadviewer.com/api/auth/jwt/token';
const payload = {
email: 'YOUR_Email',
apikey: 'YOUR_API_KEY_HERE'
};
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
.then(response => response.json())
.then(data => {
if (data.token) {
console.log('Token:', data.token);
} else {
console.error('Authentication failed:', data);
}
})
.catch(error => {
console.error('Error:', error);
});
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
public class AuthExample {
public static void main(String[] args) {
String tokenUrl = "https://www.loadviewer.com/api/auth/jwt/token";
String email = "YOUR_Email";
String apiKey = "YOUR_API_KEY_HERE";
String jsonInputString = String.format("{\"email\":\"%s\",\"apikey\":\"%s\"}", email, apiKey);
try {
URL url = new URL(tokenUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
conn.setDoOutput(true);
try (OutputStream os = conn.getOutputStream()) {
byte[] input = jsonInputString.getBytes("utf-8");
os.write(input, 0, input.length);
}
int status = conn.getResponseCode();
Scanner scanner;
if (status == 200) {
scanner = new Scanner(conn.getInputStream(), "utf-8");
String responseBody = scanner.useDelimiter("\\A").next();
System.out.println("Token: " + responseBody);
} else {
scanner = new Scanner(conn.getErrorStream(), "utf-8");
String errorResponse = scanner.useDelimiter("\\A").next();
System.out.println("Error: " + status + " - " + errorResponse);
}
scanner.close();
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
open System
open System.Net.Http
open System.Text
open System.Threading.Tasks
let getTokenAsync () =
async {
use client = new HttpClient()
let url = "https://www.loadviewer.com/api/auth/jwt/token"
let email = "YOUR_Email"
let apiKey = "YOUR_API_KEY_HERE"
let payload = sprintf """{ "email": "%s", "apikey": "%s" }""" email apiKey
let content = new StringContent(payload, Encoding.UTF8, "application/json")
try
let! response = client.PostAsync(url, content) |> Async.AwaitTask
if response.IsSuccessStatusCode then
let! body = response.Content.ReadAsStringAsync() |> Async.AwaitTask
printfn "Token: %s" body
else
let! error = response.Content.ReadAsStringAsync() |> Async.AwaitTask
printfn "Error: %d - %s" (int response.StatusCode) error
with ex ->
printfn "Exception: %s" ex.Message
}
[<EntryPoint>]
let main _ =
getTokenAsync () |> Async.RunSynchronously
0
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
url := "https://www.loadviewer.com/api/auth/jwt/token"
payload := map[string]string{
"email": "YOUR_Email",
"apikey": "YOUR_API_KEY_HERE",
}
jsonData, err := json.Marshal(payload)
if err != nil {
fmt.Println("Error encoding JSON:", err)
return
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
if err != nil {
fmt.Println("Error creating request:", err)
return
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response:", err)
return
}
if resp.StatusCode == http.StatusOK {
var result map[string]interface{}
if err := json.Unmarshal(body, &result); err != nil {
fmt.Println("Error decoding JSON:", err)
return
}
fmt.Println("Token:", result["token"])
} else {
fmt.Printf("Error: %d - %s\n", resp.StatusCode, string(body))
}
}
library(httr)
library(jsonlite)
url <- "https://www.loadviewer.com/api/auth/jwt/token"
payload <- list(
email = "YOUR_Email",
apikey = "YOUR_API_KEY_HERE"
)
response <- POST(
url,
add_headers(`Content-Type` = "application/json"),
body = toJSON(payload, auto_unbox = TRUE)
)
if (status_code(response) == 200) {
token <- content(response, as = "parsed")$token
cat("Token:", token, "\n")
} else {
cat("Error:", status_code(response), "-", content(response, as = "text"), "\n")
}
<?php
$tokenUrl = 'https://www.loadviewer.com/api/auth/jwt/token';
$email = 'YOUR_Email';
$apiKey = 'YOUR_API_KEY_HERE';
$data = json_encode(['email' => $email, 'apikey' => $apiKey]);
$ch = curl_init($tokenUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
$responseData = json_decode($response, true);
echo "Token: " . $responseData['token'];
} else {
echo "Error: $httpCode - $response";
}
?>
import 'dart:convert';
import 'package:http/http.dart' as http;
void main() async {
var url = Uri.parse('https://www.loadviewer.com/api/auth/jwt/token');
var headers = {'Content-Type': 'application/json'};
var body = jsonEncode({
'email': 'YOUR_Email',
'apikey': 'YOUR_API_KEY_HERE'
});
var response = await http.post(url, headers: headers, body: body);
if (response.statusCode == 200) {
var responseData = jsonDecode(response.body);
print('Token: \${responseData['token']}');
} else {
print('Error: \${response.statusCode} - \${response.body}');
}
}
Token Fetch Errors
These errors may occur while requesting a JWT token using your email and API key. They usually relate to key configuration or mismatch.
Status / code | ResponseText / message | Solution |
---|---|---|
400 | Missing API Key | The apiKey field is missing in the request body. Ensure both email and apiKey are provided. |
401 | Your domain is not Allowed | Your email domain is not permitted. Please use a business email (not a free domain like gmail.com). Contact support if unsure. |
401 | API Key mismatch | The API Key provided does not match our records. Double-check the key and try again. |
403 | API Key is not generated | Generate an API Key first using the API Key Manager before making requests. |
403 | API Key is cleared, please regenerate | Your existing API Key was cleared manually. Please regenerate it from the API Key Manager. |
403 | API Key is disabled | The API Key has been disabled. Enable it again from the API Key Manager to proceed. |
403 | API Key is Expired | The API Key has expired. Please generate a new one from the API Key Manager. |
https://www.loadviewer.com/api/auth/jwt/token
endpoint.
You may refer to the Errors
section for guidance on sending and receiving data with other API endpoints.
3. Error handling
API Error Handling Guide
Every API response includes a standard structure for handling errors:
LoadViewer response messages are in JSON format. But if your application has limitied capabilities parsing JSON you may get the response in Plain text.a
To get the Plain Text responses please add Accept
header in the http request headers.
Dim http As Object
Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
With http
.Open "POST", LoadViewerUploadURL, False
.SetRequestHeader "Content-Type", "text/csv"
.SetRequestHeader "Authorization", "Bearer " & jwtToken
.SetRequestHeader "Accept", "text/plain" ' → Hint: Comment this line to get JSON format response. The token endpoint shall always return the json format in case of success response.
.Send csvData
End With
{
"status":"success",
"message":"Shipment Number 'SANDBOX-01' is accepted. 1 out of 1 Container(s) added. 1 out of 1 cargoes added. 1 Cargo added with new Colors.",
"code":202,
"data":
{
"shipmentNumber":"SANDBOX-01",
"shipmentStatus":"Draft"
},
"errors":null,
"meta":
{
"coinsCharged":0,
"coinsBalance":0
}
}
{
"status":"error",
"message": "Shipment Number (Field 'Reference') 'SANDBOX-01' is used by other Shipment.",
"code":409,
"data":
{
"shipmentNumber":"SANDBOX-01"
},
"errors":
[
{
"errorCode":"409",
"errorMessage":"Shipment Number (Field 'Reference') 'SANDBOX-01' is used by other Shipment."
}
],
"meta":
{
"coinsBalance":0
}
}
Status =202
ResponseText =Shipment Number 'SANDBOX-01' is accepted.
Status =409
ResponseText =Shipment Number (Field 'Reference') 'SANDBOX-01' is used by other Shipment.
Note: Always check the Status / code
and ResponseText / message
for handling logic on the ERP side. Refer to the suggested Solution
below for each error.
1. Errors related to Upload of CSV data to LoadViewer
These errors occur due to missing headers, invalid, expired, or tampered JWT tokens.
Status / code | ResponseText / message | Solution |
---|---|---|
400 | Missing 'email' in request body. | For token request email and APIKey is not present in the request header. |
400 | Invalid JSON format. | For token request email and APIKey is supplied in proper format in the request header. |
400 | One or more errors occurred while parsing the CSV. | CSV file has errors in column names or data type or out of range etc. Specific details are available in errors section of the JSON. |
400 | Field 'Reference' is missing in Shipment Row. | Mandatory field for shipment is not supplied. Check your CSV file. |
400 | Shipment must contain at least one container and one cargo item. | Your CSV data is missing the Row with 'C' in first column and 'H' in second column. |
409 | Shipment Number (Field 'Reference') '[XXXXXX]' is used by other Shipment. | Check your CSV file and change the Shipment Reference value to not previously used on LoadViewer. |
401 | Missing or invalid JWT token. | Your request is missing a token or has an invalid format. Please obtain a valid token using your credentials. |
401 | Invalid token. Ensure you are sending a valid Bearer token. | Your request is missing a token or has an invalid format. Please obtain a valid token using your credentials. |
403 | Your domain is not allowed. | Your email domain is not allowed. LoadViewer expects a real domain other then gmail.com or hotmail.com etc. Please contact support for more details. |
403 | Insufficent permissions to perform Upload Action | The user has no permission to the resource. |
410 | Token has expired. Please call /api/auth/jwt/token to retrieve a new one. | The token has expired. Please re-authenticate to obtain a new token. |
429 | Too Many Requests | You are exceeding the allowed request rate. Implement retry logic with exponential backoff. |
2. Internal & System Errors
These are unexpected system-side issues that may occur temporarily due to backend failures.
Status / code | ResponseText / message | Solution |
---|---|---|
500 | Internal Server Error | Retry the request after a few seconds. If the issue persists, contact LoadViewer support with request details. |
503 | Service Unavailable | The system is temporarily overloaded or under maintenance. Retry after a short delay. |
4. Testing
🧪 API Testing Guide
Why Testing is Important
Testing ensures that:
- Your ERP generates a valid file format
- Communication with LoadViewer APIs is secure and successful
- You avoid unnecessary coin consumption during testing.
- Real-time errors are handled before going live
Your Two-Stage Testing Strategy
-
Test the File Creation
Use the manual file upload section in your dashboard to upload a CSV file created by your ERP or a text editor.
- Make sure the
Reference
does not start withSANDBOX
in manual upload mode. - Manual uploads are not charged coins unless published.
- If the upload is successful, your file structure is correct.
- Repeat this until your ERP consistently generates acceptable files.
- Make sure the
-
Test the API Communication
Use your tool of choice (Postman, Swagger, curl, script) to post the file to LoadViewer’s API endpoint.
- Use
Reference
that starts withSANDBOX
. - No coins are consumed for such files.
Reference
starting withSANDBOX
can be provided in mixed case (Upper, Lower, camelCase or else). - File is accepted and validated but not published.
- This lets you test end-to-end without impacting live shipments.
E.g.,
SANDBOX-01
is a valid test reference for your dummy data. - Use
Tools & Skills Required
Skill | Description |
---|---|
Basic HTTP | Understanding Headers, POST, GET, Status Codes |
JSON/CSV | Correct formatting for request body and payload |
Tool usage | Postman, curl, Swagger, or scripting languages |
ERP Integration Testing
- Ensure your ERP can generate shipment files in valid CSV format
-
Configure the ERP to send a POST request to:
https://www.loadviewer.com/api/integration/v1/upload
- Include
ApiKey
in the request headers - Use a Shipment
Reference
starting withSANDBOX
for test uploads - Enable logging of request payloads and API responses within the ERP
- Test various combinations of SKUs and file sizes before going live
For detailed integration steps, refer to the upcoming Implementation Guide.
Test Code to Upload your CSV data
Note: The example uses integration version V1
.
' ========================================
' Module: modLoadViewerCSV
' Purpose:
' 1. Generate LoadViewer-compatible CSV from hardcoded or database values
' 2. Upload CSV to LoadViewer using HTTP POST (WinHttp)
' Includes helper functions:
' - removeComma: for all string fields
' - formatDecimal3: for weights with decimals
' ========================================
' === Constants ===
Const LoadViewerTokenURL As String = "https://www.loadviewer.com/api/auth/jwt/token"
Const LoadViewerUploadURL As String = "https://www.loadviewer.com/api/integration/v1/upload"
Const LoadViewerAPIEmail As String = "YOUR_Email"
Const LoadViewerAPIKey As String = "YOUR_API_KEY_HERE"
' Optional: You can fetch the above values from file or DB:
' - From INI file in App.Path
' - From encrypted DB table
' - From registry or environment variable
'
' === Module level variable to cache the retrieved token ===
Public LoadViewerToken As String
' === Dynamically generates LoadViewer CSV from ADODB recordsets ===
'For csvGenerate() to work properly you may need to add Reference to
'Microsoft ActiveX Data Object 2.8 Library
'(C:\Program Files (x86)\Common Files\System\ado\msado28.tlb)
'OR any other version for ADODB.Recordset available to you.
Function csvGenerate() As String
Dim csv As String
Dim rsShipment As ADODB.Recordset
Dim rsContainer As ADODB.Recordset
Dim rsCargo As ADODB.Recordset
Set rsShipment = New ADODB.Recordset
Set rsContainer = New ADODB.Recordset
Set rsCargo = New ADODB.Recordset
' --- Define Shipment fields ---
rsShipment.Fields.Append "Reference", adVarChar, 50
rsShipment.Fields.Append "HangingAllowed", adBoolean
rsShipment.Fields.Append "UseMultipleContainers", adBoolean
rsShipment.Open
rsShipment.AddNew
rsShipment("Reference") = "SANDBOX-01"
rsShipment("HangingAllowed") = True
rsShipment("UseMultipleContainers") = False
rsShipment.Update
' --- Define Container fields ---
rsContainer.Fields.Append "Length", adInteger
rsContainer.Fields.Append "Width", adInteger
rsContainer.Fields.Append "Height", adInteger
rsContainer.Fields.Append "MaxWeight", adDouble
rsContainer.Fields.Append "BackMargin", adInteger
rsContainer.Open
rsContainer.AddNew
rsContainer("Length") = 5867
rsContainer("Width") = 2352
rsContainer("Height") = 2393
rsContainer("MaxWeight") = 27200
rsContainer("BackMargin") = 0
rsContainer.Update
' --- Define Cargo fields ---
rsCargo.Fields.Append "Color", adVarChar, 10
rsCargo.Fields.Append "Length", adInteger
rsCargo.Fields.Append "Width", adInteger
rsCargo.Fields.Append "Height", adInteger
rsCargo.Fields.Append "Weight", adDouble
rsCargo.Fields.Append "Cartons", adInteger
rsCargo.Fields.Append "AdditionalCartons", adInteger
rsCargo.Fields.Append "Placement", adInteger
rsCargo.Fields.Append "VerticalRotationAllowed", adBoolean
rsCargo.Fields.Append "SKUPerCarton", adInteger
rsCargo.Fields.Append "EmptyPalletHeight", adInteger
rsCargo.Fields.Append "TrayCap", adInteger
rsCargo.Fields.Append "TrayHeight", adInteger
rsCargo.Fields.Append "AirSpaceLength", adInteger
rsCargo.Fields.Append "AirSpaceWidth", adInteger
rsCargo.Fields.Append "AirSpaceHeight", adInteger
rsCargo.Fields.Append "NameOfSet", adVarChar, 1
rsCargo.Fields.Append "CartonRatio", adInteger
rsCargo.Fields.Append "Description", adVarChar, 60
rsCargo.Fields.Append "Destination", adInteger
rsCargo.Fields.Append "PO", adVarChar, 20
rsCargo.Fields.Append "Shipper", adVarChar, 20
rsCargo.Open
rsCargo.AddNew
rsCargo("Color") = "#008000"
rsCargo("Length") = 700
rsCargo("Width") = 500
rsCargo("Height") = 300
rsCargo("Weight") = 91.5 ' ? "91.5"
rsCargo("Cartons") = 300
rsCargo("AdditionalCartons") = 0
rsCargo("Placement") = 0
rsCargo("VerticalRotationAllowed") = True
rsCargo("SKUPerCarton") = 1
rsCargo("EmptyPalletHeight") = 0
rsCargo("TrayCap") = 0
rsCargo("TrayHeight") = 0
rsCargo("AirSpaceLength") = 0
rsCargo("AirSpaceWidth") = 0
rsCargo("AirSpaceHeight") = 0
rsCargo("NameOfSet") = ""
rsCargo("CartonRatio") = 0
rsCargo("Description") = ""
rsCargo("Destination") = 0
rsCargo("PO") = ""
rsCargo("Shipper") = ""
rsCargo.Update
' --- Begin CSV output ---
csv = "S,H,Reference,Hanging Allowed,Use Multiple Containers" & vbCrLf
csv = csv & "S,D," & removeComma(rsShipment("Reference")) & "," & rsShipment("HangingAllowed") & "," & rsShipment("UseMultipleContainers") & vbCrLf
csv = csv & "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm" & vbCrLf
Do Until rsContainer.EOF
csv = csv & "B,D," & rsContainer("Length") & "," & rsContainer("Width") & "," & rsContainer("Height") & "," & formatDecimal3(rsContainer("MaxWeight")) & "," & rsContainer("BackMargin") & vbCrLf
rsContainer.MoveNext
Loop
csv = csv & "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper" & vbCrLf
Do Until rsCargo.EOF
csv = csv & "C,D," & removeComma(rsCargo("Color")) & "," & rsCargo("Length") & "," & rsCargo("Width") & "," & rsCargo("Height") & "," & formatDecimal3(rsCargo("Weight")) & "," & rsCargo("Cartons") & "," & rsCargo("AdditionalCartons") & "," & rsCargo("Placement") & "," & rsCargo("VerticalRotationAllowed") & "," & rsCargo("SKUPerCarton") & "," & rsCargo("EmptyPalletHeight") & "," & rsCargo("TrayCap") & "," & rsCargo("TrayHeight") & "," & rsCargo("AirSpaceLength") & "," & rsCargo("AirSpaceWidth") & "," & rsCargo("AirSpaceHeight") & "," & removeComma(rsCargo("NameOfSet")) & "," & rsCargo("CartonRatio") & "," & removeComma(rsCargo("Description")) & "," & rsCargo("Destination") & "," & removeComma(rsCargo("PO")) & "," & removeComma(rsCargo("Shipper")) & vbCrLf
rsCargo.MoveNext
Loop
csvGenerate = csv
End Function
' === Generate CSV string for SANDBOX-01 ===
Function csvCreate() As String
Dim csv As String
csv = "S,H,Reference,Hanging Allowed,Use Multiple Containers" & vbCrLf
csv = csv & "S,D,SANDBOX-01,True,False" & vbCrLf
csv = csv & "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm" & vbCrLf
csv = csv & "B,D,5867,2352,2393,27200,0" & vbCrLf
csv = csv & "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper" & vbCrLf
csv = csv & "C,D,#008000,700,500,300,91.5,300,0,0,True,1,0,0,0,0,0,0,,0,,0,," & vbCrLf
csvCreate = csv
End Function
' === Get JWT Token ===
Function getJwtToken() As String
On Error GoTo ErrHandler
Dim http As Object
Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
Dim tokenURL As String
tokenURL = LoadViewerTokenURL ' Example: "https://www.loadviewer.com/api/auth/jwt/token"
Dim jsonBody As String
jsonBody = "{""email"":""" & LoadViewerAPIEmail & """,""apikey"":""" & LoadViewerAPIKey & """}"
With http
.SetTimeouts 3000, 3000, 5000, 5000 ' 3 sec connect/send, 5 sec receive/resolve
.Open "POST", tokenURL, False
.SetRequestHeader "Content-Type", "application/json"
.Send jsonBody
End With
If http.Status = 200 Then
getJwtToken = extractTokenFromResponse(http.ResponseText) ' you Should extract the token from returned JSON with token
LoadViewerToken = getJwtToken ' you should cache it for later use to avoid netwroks calls
Else
getJwtToken = "ERROR: " & http.Status & " - " & http.ResponseText
End If
Exit Function
ErrHandler:
getJwtToken = "Exception: " & Err.Description
End Function
' === Extract "token" field from JSON response ===
Function extractTokenFromResponse(jsonText As String) As String
Dim tokenStart As Long, tokenEnd As Long
tokenStart = InStr(jsonText, """token"":""")
If tokenStart = 0 Then
extractTokenFromResponse = ""
Exit Function
End If
tokenStart = tokenStart + 9
tokenEnd = InStr(tokenStart, jsonText, """")
extractTokenFromResponse = Mid(jsonText, tokenStart, tokenEnd - tokenStart)
End Function
' === Upload CSV using Bearer Token ===
Function UploadTOLoadViewer(csvData As String, jwtToken As String) As String
Dim retry As Integer
retry = 3
UploadTOLoadViewer:
Dim http As Object
Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
On Error GoTo ErrHandler
With http
.Open "POST", LoadViewerUploadURL, False
.SetRequestHeader "Content-Type", "text/csv"
.SetRequestHeader "Authorization", "Bearer " & jwtToken
'.SetRequestHeader "Accept", "text/plain" '->Hint: Comment this line to get JSON format response
.Send csvData
End With
If http.Status = 200 Or http.Status = 202 Then 'Success (200) or Accepted (202)
UploadTOLoadViewer = http.Status & " - " & http.ResponseText
ElseIf http.Status = 410 And retry > 0 Then
getJwtToken
jwtToken = LoadViewerToken
retry = retry - 1
GoTo UploadTOLoadViewer
Else
UploadTOLoadViewer = "ERROR: " & http.Status & " - " & http.ResponseText
End If
Exit Function
ErrHandler:
UploadTOLoadViewer = "Exception: " & Err.Description
End Function
' === Cleans text for CSV: removes commas and replaces line breaks ===
' === Use this for all adVarChar (string) fields before writing to CSV ===
'
' Replaces:
' , ? (removed)
' vbCrLf, vbCr, vbLf ? " " (space)
Function removeComma(value As Variant) As String
If IsNull(value) Then
removeComma = ""
Exit Function
End If
Dim s As String
s = Trim(CStr(value))
s = Replace(s, ",", "") ' Remove commas
s = Replace(s, vbCrLf, " ") ' Replace Windows line breaks with space
s = Replace(s, vbCr, " ") ' Replace carriage return
s = Replace(s, vbLf, " ") ' Replace line feed
removeComma = s
End Function
' === Converts numeric value to string with up to 3 decimal places ===
' === Trailing zeros are removed. Use only for Weight-related fields ===
'
' Examples:
' Convert 91.5 ? "91.5"
' Convert 91.56789 ? "91.568"
' Keep 52 ? "52" (no .000 added)
Function formatDecimal3(value As Variant) As String
If IsNull(value) Or Not IsNumeric(value) Then
formatDecimal3 = "0"
Exit Function
End If
Dim s As String
s = Trim(CStr(value))
If InStr(s, ".") > 0 Then
' Format with 3 decimals, remove trailing zeroes
formatDecimal3 = Trim(Str(Val(FormatNumber(value, 3, , , vbFalse))))
Else
' Keep as integer string
formatDecimal3 = Trim(Str(Val(value)))
End If
End Function
' === OPTIONAL TEST CALL ===
' Make calls Examples
' testUpload '->SaveToDisk=No, CSVFromADO=No
' testUpload True '->SaveToDisk=Yes, CSVFromADO=No
' testUpload False, True '->SaveToDisk=No, CSVFromADO=Yes
' testUpload True, True '->SaveToDisk=Yes, CSVFromADO=Yes
'===============================================================
Sub testUpload(Optional saveToDisk As Boolean = False, Optional useADORecordset As Boolean = False)
Dim csv As String
If (useADORecordset = False) Then
csv = csvCreate() '--Using Hard Coded values
Else
csv = csvGenerate() '-->Using ADO
End If
If saveToDisk Then
filePath = App.Path & "\SANDBOX-VB6.csv"
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Dim fileOut As Object
Set fileOut = fso.CreateTextFile(filePath, True)
fileOut.Write csv
fileOut.Close
End If
Dim token As String
If LoadViewerToken = "" Then
token = Trim(getJwtToken())
Else
token = LoadViewerToken
End If
If token = "" Or Left(token, 5) = "ERROR" Or Left(token, 9) = "Exception" Then
MsgBox "Token Error or Server Not Reachable: " & vbCrLf & token, vbCritical
Exit Sub
End If
Dim response As String
response = UploadTOLoadViewer(csv, token)
MsgBox response
End Sub
# ========================================
# Module: loadviewer_csv.py
# Purpose:
# 1. Generate LoadViewer-compatible CSV from hardcoded or DB values
# 2. Upload CSV to LoadViewer using HTTP POST
# Includes helper functions:
# - remove_comma: for all string fields
# - format_decimal_3: for weights with decimals
# ========================================
import json
from io import StringIO
import requests
# === Configuration Constants ===
# Replace with your actual credentials and endpoints
LoadViewerTokenURL = "https://www.loadviewer.com/api/auth/jwt/token"
LoadViewerUploadURL = "https://www.loadviewer.com/api/upload"
LoadViewerAPIEmail = "YOUR_Email"
LoadViewerAPIKey = "YOUR_API_KEY_HERE"
# === Global Token Cache ===
loadviewer_token = None
# === Helper Functions ===
def remove_comma(value):
"""
Cleans text for CSV: removes commas and replaces line breaks.
Use this for all string fields before writing to CSV.
"""
if value is None:
return ""
s = str(value).strip()
s = s.replace(",", "")
s = s.replace("\r\n", " ").replace("\r", " ").replace("\n", " ")
return s
def format_decimal_3(value):
"""
Converts numeric value to string with up to 3 decimal places.
Trailing zeros are removed. Use only for weight-related fields.
"""
try:
num = float(value)
formatted = f"{num:.3f}".rstrip("0").rstrip(".")
return formatted
except (TypeError, ValueError):
return "0"
def extract_token_from_response(json_text):
"""
Extracts the 'token' field from JSON response.
"""
try:
data = json.loads(json_text)
return data.get("token", "")
except json.JSONDecodeError:
return ""
# === CSV Generation Functions ===
def csv_create():
"""
Generate CSV string for SANDBOX-01 using hardcoded values.
"""
csv = ""
csv += "S,H,Reference,Hanging Allowed,Use Multiple Containers\n"
csv += "S,D,SANDBOX-01,True,False\n"
csv += "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n"
csv += "B,D,5867,2352,2393,27200,0\n"
csv += ("C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,"
"Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,"
"Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,"
"Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,"
"Description,Destination,PO,Shipper\n")
csv += ("C,D,#008000,700,500,300,91.5,300,0,0,True,1,0,0,0,0,0,0,,0,,0,,\n")
return csv
def csv_generate():
"""
Dynamically generates LoadViewer CSV from simulated recordsets.
"""
# --- Sample Recordsets simulated with Python dictionaries/lists ---
# Shipment record (only one row)
rs_shipment = {
"Reference": "SANDBOX-01",
"HangingAllowed": True,
"UseMultipleContainers": False
}
# Container recordset (list of dicts)
rs_container = [{
"Length": 5867,
"Width": 2352,
"Height": 2393,
"MaxWeight": 27200,
"BackMargin": 0
}]
# Cargo recordset (list of dicts)
rs_cargo = [{
"Color": "#008000",
"Length": 700,
"Width": 500,
"Height": 300,
"Weight": 91.5,
"Cartons": 300,
"AdditionalCartons": 0,
"Placement": 0,
"VerticalRotationAllowed": True,
"SKUPerCarton": 1,
"EmptyPalletHeight": 0,
"TrayCap": 0,
"TrayHeight": 0,
"AirSpaceLength": 0,
"AirSpaceWidth": 0,
"AirSpaceHeight": 0,
"NameOfSet": "",
"CartonRatio": 0,
"Description": "",
"Destination": 0,
"PO": "",
"Shipper": ""
}]
# --- CSV construction ---
output = StringIO()
output.write("S,H,Reference,Hanging Allowed,Use Multiple Containers\n")
output.write(f"S,D,{remove_comma(rs_shipment['Reference'])},{rs_shipment['HangingAllowed']},{rs_shipment['UseMultipleContainers']}\n")
output.write("B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n")
for container in rs_container:
output.write(f"B,D,{container['Length']},{container['Width']},{container['Height']},{format_decimal_3(container['MaxWeight'])},{container['BackMargin']}\n")
output.write("C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n")
for cargo in rs_cargo:
output.write("C,D,")
output.write(",".join([
remove_comma(cargo["Color"]),
str(cargo["Length"]),
str(cargo["Width"]),
str(cargo["Height"]),
format_decimal_3(cargo["Weight"]),
str(cargo["Cartons"]),
str(cargo["AdditionalCartons"]),
str(cargo["Placement"]),
str(cargo["VerticalRotationAllowed"]),
str(cargo["SKUPerCarton"]),
str(cargo["EmptyPalletHeight"]),
str(cargo["TrayCap"]),
str(cargo["TrayHeight"]),
str(cargo["AirSpaceLength"]),
str(cargo["AirSpaceWidth"]),
str(cargo["AirSpaceHeight"]),
remove_comma(cargo["NameOfSet"]),
str(cargo["CartonRatio"]),
remove_comma(cargo["Description"]),
str(cargo["Destination"]),
remove_comma(cargo["PO"]),
remove_comma(cargo["Shipper"])
]))
output.write("\n")
return output.getvalue()
# === Extract "token" field from JSON response ===
def extract_token_from_response(json_text: str) -> str:
try:
data = json.loads(json_text)
return data.get("token", "")
except json.JSONDecodeError:
return ""
# === Get JWT Token ===
def get_jwt_token() -> str:
global loadviewer_token
try:
# Return cached token if available
if loadviewer_token:
return loadviewer_token
json_body = {
"email": LoadViewerAPIEmail,
"apikey": LoadViewerAPIKey
}
response = requests.post(
LoadViewerTokenURL,
json=json_body,
timeout=(3, 5) # connect, read timeout
)
if response.status_code == 200:
token = extract_token_from_response(response.text)
loadviewer_token = token # Cache it
return token
else:
return f"ERROR: {response.status_code} - {response.text}"
except Exception as e:
return f"Exception: {str(e)}"
# === Upload CSV using Bearer Token ===
def upload_to_loadviewer(csv_data: str, jwt_token: str) -> str:
retries = 3
global loadviewer_token # reuse cached token
while retries > 0:
try:
headers = {
"Content-Type": "text/csv",
"Authorization": f"Bearer {jwt_token}"
# "Accept": "text/plain" # Optional: Uncomment if plain text is preferred
}
response = requests.post(
LoadViewerUploadURL,
data=csv_data.encode("utf-8"), # Ensure encoding
headers=headers,
timeout=(3, 5) # connect, read
)
if response.status_code in (200, 202): # Success
return f"{response.status_code} - {response.text}"
elif response.status_code == 410 and retries > 1:
# Token expired, refresh it
jwt_token = get_jwt_token()
loadviewer_token = jwt_token
retries -= 1
continue
else:
return f"ERROR: {response.status_code} - {response.text}"
except Exception as e:
return f"Exception: {str(e)}"
return "ERROR: Max retries exceeded"
#=== test call ====
def test_upload(save_to_disk=False, use_ado_recordset=False):
"""
Test function to generate CSV data and upload it to LoadViewer.
Can save the CSV to disk and/or use ADO recordset simulation.
"""
# Generate CSV data
if not use_ado_recordset:
csv_data = csv_create() # Using hardcoded values
else:
csv_data = csv_generate() # Using simulated ADO recordsets
# Save to disk if required
if save_to_disk:
file_path = "SANDBOX-Python.csv"
with open(file_path, "w", newline="") as file_out:
file_out.write(csv_data)
# Retrieve JWT token
global loadviewer_token
if not loadviewer_token:
token = get_jwt_token()
else:
token = loadviewer_token
if not token or token.startswith(("ERROR", "Exception")):
print(f"Token Error or Server Not Reachable: {token}")
return
# Upload CSV data
response = upload_to_loadviewer(csv_data, token)
print(response)
// ========================================
// Module: modLoadViewerCSV
// Purpose:
// 1. Generate LoadViewer-compatible CSV from hardcoded or database values
// 2. Upload CSV to LoadViewer using HTTP POST (WinHttp equivalent in C# is HttpClient)
// Includes helper functions:
// - removeComma: for all string fields
// - formatDecimal3: for weights with decimals
// ========================================
using System;
using System.Data.OleDb; // For ADODB recordset equivalent
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
public class LoadViewerCSV
{
// === Constants ===
private const string LoadViewerTokenURL = "https://www.loadviewer.com/api/auth/jwt/token";
private const string LoadViewerUploadURL = "https://www.loadviewer.com/api/integration/v1/upload";
private const string LoadViewerAPIEmail = "YOUR_Email";
private const string LoadViewerAPIKey = "YOUR_API_KEY_HERE";
// Optional: You can fetch the above values from file or DB:
// - From INI file in App.Path (use System.Configuration in C#)
// - From encrypted DB table
// - From registry or environment variable (use Microsoft.Win32.Registry in C#)
//
// === Module level variable to cache the retrieved token ===
public static string LoadViewerToken { get; private set; }
// === Dynamically generates LoadViewer CSV from ADODB recordsets ===
// For csvGenerate() to work properly, you may need to add System.Data.OleDb
public static string CsvGenerate()
{
string csv = "";
OleDbDataReader rsShipment = null;
OleDbDataReader rsContainer = null;
OleDbDataReader rsCargo = null;
OleDbConnection connection = null;
try
{
// --- Define Shipment fields ---
string shipmentQuery = "SELECT 'SANDBOX-01' as Reference, true as HangingAllowed, false as UseMultipleContainers";
string containerQuery = "SELECT 5867 as Length, 2352 as Width, 2393 as Height, 27200 as MaxWeight, 0 as BackMargin";
string cargoQuery = "SELECT '#008000' as Color, 700 as Length, 500 as Width, 300 as Height, 91.5 as Weight, 300 as Cartons, 0 as AdditionalCartons, 0 as Placement, true as VerticalRotationAllowed, 1 as SKUPerCarton, 0 as EmptyPalletHeight, 0 as TrayCap, 0 as TrayHeight, 0 as AirSpaceLength, 0 as AirSpaceWidth, 0 as AirSpaceHeight, '' as NameOfSet, 0 as CartonRatio, '' as Description, 0 as Destination, '' as PO, '' as Shipper";
// Create a dummy connection and reader just to simulate vb6 behaviour
connection = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=.;Extended Properties=dBASE IV;");
connection.Open();
OleDbCommand shipmentCommand = new OleDbCommand(shipmentQuery, connection);
rsShipment = shipmentCommand.ExecuteReader();
OleDbCommand containerCommand = new OleDbCommand(containerQuery, connection);
rsContainer = containerCommand.ExecuteReader();
OleDbCommand cargoCommand = new OleDbCommand(cargoQuery, connection);
rsCargo = cargoCommand.ExecuteReader();
// --- Begin CSV output ---
csv = "S,H,Reference,Hanging Allowed,Use Multiple Containers" + Environment.NewLine;
if (rsShipment.Read())
{
csv += "S,D," + RemoveComma(rsShipment["Reference"].ToString()) + "," + rsShipment["HangingAllowed"] + "," + rsShipment["UseMultipleContainers"] + Environment.NewLine;
}
csv += "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm" + Environment.NewLine;
if (rsContainer.Read())
{
csv += "B,D," + rsContainer["Length"] + "," + rsContainer["Width"] + "," + rsContainer["Height"] + "," + FormatDecimal3(rsContainer["MaxWeight"]) + "," + rsContainer["BackMargin"] + Environment.NewLine;
}
csv += "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper" + Environment.NewLine;
if (rsCargo.Read())
{
csv += "C,D," + RemoveComma(rsCargo["Color"].ToString()) + "," + rsCargo["Length"] + "," + rsCargo["Width"] + "," + rsCargo["Height"] + "," + FormatDecimal3(rsCargo["Weight"]) + "," + rsCargo["Cartons"] + "," + rsCargo["AdditionalCartons"] + "," + rsCargo["Placement"] + "," + rsCargo["VerticalRotationAllowed"] + "," + rsCargo["SKUPerCarton"] + "," + rsCargo["EmptyPalletHeight"] + "," + rsCargo["TrayCap"] + "," + rsCargo["TrayHeight"] + "," + rsCargo["AirSpaceLength"] + "," + rsCargo["AirSpaceWidth"] + "," + rsCargo["AirSpaceHeight"] + "," + RemoveComma(rsCargo["NameOfSet"].ToString()) + "," + rsCargo["CartonRatio"] + "," + RemoveComma(rsCargo["Description"].ToString()) + "," + rsCargo["Destination"] + "," + RemoveComma(rsCargo["PO"].ToString()) + "," + RemoveComma(rsCargo["Shipper"].ToString()) + Environment.NewLine;
}
}
catch (Exception ex)
{
return "Exception: " + ex.Message;
}
finally
{
if (rsShipment != null) rsShipment.Close();
if (rsContainer != null) rsContainer.Close();
if (rsCargo != null) rsCargo.Close();
if (connection != null) connection.Close();
}
return csv;
}
// === Generate CSV string for SANDBOX-01 ===
public static string CsvCreate()
{
string csv = "S,H,Reference,Hanging Allowed,Use Multiple Containers" + Environment.NewLine;
csv += "S,D,SANDBOX-01,True,False" + Environment.NewLine;
csv += "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm" + Environment.NewLine;
csv += "B,D,5867,2352,2393,27200,0" + Environment.NewLine;
csv += "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper" + Environment.NewLine;
csv += "C,D,#008000,700,500,300,91.5,300,0,0,True,1,0,0,0,0,0,0,,0,,0,," + Environment.NewLine;
return csv;
}
// === Get JWT Token ===
public static async Task<string> GetJwtTokenAsync()
{
try
{
using (HttpClient client = new HttpClient())
{
client.Timeout = TimeSpan.FromSeconds(5); // Adjust timeouts
var jsonBody = JsonSerializer.Serialize(new { email = LoadViewerAPIEmail, apikey = LoadViewerAPIKey });
var content = new StringContent(jsonBody, System.Text.Encoding.UTF8, "application/json");
var response = await client.PostAsync(LoadViewerTokenURL, content);
if (response.IsSuccessStatusCode)
{
string responseText = await response.Content.ReadAsStringAsync();
string token = ExtractTokenFromResponse(responseText);
LoadViewerToken = token; // Cache the token
return token;
}
else
{
return $"ERROR: {(int)response.StatusCode} - {await response.Content.ReadAsStringAsync()}";
}
}
}
catch (Exception ex)
{
return $"Exception: {ex.Message}";
}
}
// === Extract "token" field from JSON response ===
private static string ExtractTokenFromResponse(string jsonText)
{
try
{
using (JsonDocument doc = JsonDocument.Parse(jsonText))
{
if (doc.RootElement.TryGetProperty("token", out JsonElement tokenElement))
{
return tokenElement.GetString();
}
}
}
catch { }
return "";
}
// === Upload CSV using Bearer Token ===
public static async Task<string> UploadToLoadViewerAsync(string csvData, string jwtToken)
{
int retry = 3;
while (retry > 0)
{
try
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", jwtToken);
var content = new StringContent(csvData, System.Text.Encoding.UTF8, "text/csv");
var response = await client.PostAsync(LoadViewerUploadURL, content);
if (response.StatusCode == System.Net.HttpStatusCode.OK || response.StatusCode == System.Net.HttpStatusCode.Accepted)
{
return $"{(int)response.StatusCode} - {await response.Content.ReadAsStringAsync()}";
}
else if (response.StatusCode == System.Net.HttpStatusCode.Gone) // 410
{
jwtToken = await GetJwtTokenAsync(); // Refresh token
if (jwtToken.StartsWith("ERROR") || jwtToken.StartsWith("Exception"))
{
return jwtToken; // Return error from token retrieval
}
retry--;
continue; // Retry upload
}
else
{
return $"ERROR: {(int)response.StatusCode} - {await response.Content.ReadAsStringAsync()}";
}
}
}
catch (Exception ex)
{
return $"Exception: {ex.Message}";
}
}
return "Upload failed after retries.";
}
// === Cleans text for CSV: removes commas and replaces line breaks ===
// === Use this for all adVarChar (string) fields before writing to CSV ===
//
// Replaces:
// , ? (removed)
// vbCrLf, vbCr, vbLf ? " " (space)
public static string RemoveComma(string value)
{
if (string.IsNullOrEmpty(value))
{
return "";
}
string s = value.Trim();
s = s.Replace(",", "");
s = s.Replace(Environment.NewLine, " ");
s = s.Replace("\r", " ");
s = s.Replace("\n", " ");
return s;
}
// === Converts numeric value to string with up to 3 decimal places ===
// === Trailing zeros are removed. Use only for Weight-related fields ===
//
// Examples:
// Convert 91.5 ? "91.5"
// Convert 91.56789 ? "91.568"
// Keep 52 ? "52" (no .000 added)
public static string FormatDecimal3(double value)
{
return value.ToString("0.###");
}
// === OPTIONAL TEST CALL ===
// Make calls Examples
// TestUploadAsync() //->SaveToDisk=No, CSVFromADO=No
// TestUploadAsync(true) //->SaveToDisk=Yes, CSVFromADO=No
// TestUploadAsync(false, true) //->SaveToDisk=No, CSVFromADO=Yes
// TestUploadAsync(true, true) //->SaveToDisk=Yes, CSVFromADO=Yes
//===============================================================
public static async Task TestUploadAsync(bool saveToDisk = false, bool useADORecordset = false)
{
string csv;
if (!useADORecordset)
{
csv = CsvCreate(); //--Using Hard Coded values
}
else
{
csv = CsvGenerate(); //-->Using ADO
}
if (saveToDisk)
{
string filePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SANDBOX-VB6.csv");
System.IO.File.WriteAllText(filePath, csv);
}
string token;
if (string.IsNullOrEmpty(LoadViewerToken))
{
token = await GetJwtTokenAsync();
}
else
{
token = LoadViewerToken;
}
if (string.IsNullOrEmpty(token) || token.StartsWith("ERROR") || token.StartsWith("Exception"))
{
Console.WriteLine("Token Error or Server Not Reachable: " + Environment.NewLine + token);
return;
}
string response = await UploadToLoadViewerAsync(csv, token);
Console.WriteLine(response);
}
}
// ========================================
// Module: modLoadViewerCSV
// Purpose:
// 1. Generate LoadViewer-compatible CSV from hardcoded or database values
// 2. Upload CSV to LoadViewer using HTTP POST (fetch API)
// Includes helper functions:
// - removeComma: for all string fields
// - formatDecimal3: for weights with decimals
// ========================================
// === Constants ===
const LoadViewerTokenURL = "https://www.loadviewer.com/api/auth/jwt/token";
const LoadViewerUploadURL = "https://www.loadviewer.com/api/integration/v1/upload";
const LoadViewerAPIEmail = "YOUR_Email";
const LoadViewerAPIKey = "YOUR_API_KEY_HERE";
// Optional: You can fetch the above values from file or DB:
// - From local storage, or from a server config file
// - From encrypted DB table (Node.js with a database library)
// - From environment variable (process.env in Node.js)
//
// === Module level variable to cache the retrieved token ===
let LoadViewerToken = "";
// === Dynamically generates LoadViewer CSV from hardcoded data ===
function csvGenerate() {
let csv = "";
const shipmentData = {
Reference: "SANDBOX-01",
HangingAllowed: true,
UseMultipleContainers: false,
};
const containerData = {
Length: 5867,
Width: 2352,
Height: 2393,
MaxWeight: 27200,
BackMargin: 0,
};
const cargoData = {
Color: "#008000",
Length: 700,
Width: 500,
Height: 300,
Weight: 91.5,
Cartons: 300,
AdditionalCartons: 0,
Placement: 0,
VerticalRotationAllowed: true,
SKUPerCarton: 1,
EmptyPalletHeight: 0,
TrayCap: 0,
TrayHeight: 0,
AirSpaceLength: 0,
AirSpaceWidth: 0,
AirSpaceHeight: 0,
NameOfSet: "",
CartonRatio: 0,
Description: "",
Destination: 0,
PO: "",
Shipper: "",
};
// --- Begin CSV output ---
csv = "S,H,Reference,Hanging Allowed,Use Multiple Containers\n";
csv += `S,D,${removeComma(shipmentData.Reference)},${shipmentData.HangingAllowed},${shipmentData.UseMultipleContainers}\n`;
csv += "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n";
csv += `B,D,${containerData.Length},${containerData.Width},${containerData.Height},${formatDecimal3(containerData.MaxWeight)},${containerData.BackMargin}\n`;
csv += "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n";
csv += `C,D,${removeComma(cargoData.Color)},${cargoData.Length},${cargoData.Width},${cargoData.Height},${formatDecimal3(cargoData.Weight)},${cargoData.Cartons},${cargoData.AdditionalCartons},${cargoData.Placement},${cargoData.VerticalRotationAllowed},${cargoData.SKUPerCarton},${cargoData.EmptyPalletHeight},${cargoData.TrayCap},${cargoData.TrayHeight},${cargoData.AirSpaceLength},${cargoData.AirSpaceWidth},${cargoData.AirSpaceHeight},${removeComma(cargoData.NameOfSet)},${cargoData.CartonRatio},${removeComma(cargoData.Description)},${cargoData.Destination},${removeComma(cargoData.PO)},${removeComma(cargoData.Shipper)}\n`;
return csv;
}
// === Generate CSV string for SANDBOX-01 ===
function csvCreate() {
let csv = "S,H,Reference,Hanging Allowed,Use Multiple Containers\n";
csv += "S,D,SANDBOX-01,True,False\n";
csv += "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n";
csv += "B,D,5867,2352,2393,27200,0\n";
csv += "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n";
csv += "C,D,#008000,700,500,300,91.5,300,0,0,True,1,0,0,0,0,0,0,,0,,0,,\n";
return csv;
}
// === Get JWT Token ===
async function getJwtToken() {
try {
const response = await fetch(LoadViewerTokenURL, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ email: LoadViewerAPIEmail, apikey: LoadViewerAPIKey }),
});
if (response.ok) {
const json = await response.json();
const token = json.token;
LoadViewerToken = token; // Cache the token
return token;
} else {
return `ERROR: ${response.status} - ${await response.text()}`;
}
} catch (error) {
return `Exception: ${error.message}`;
}
}
// === Upload CSV using Bearer Token ===
async function UploadToLoadViewer(csvData, jwtToken) {
let retry = 3;
while (retry > 0) {
try {
const response = await fetch(LoadViewerUploadURL, {
method: "POST",
headers: {
"Content-Type": "text/csv",
Authorization: `Bearer ${jwtToken}`,
},
body: csvData,
});
if (response.status === 200 || response.status === 202) {
return `${response.status} - ${await response.text()}`;
} else if (response.status === 410) {
jwtToken = await getJwtToken();
if (jwtToken.startsWith("ERROR") || jwtToken.startsWith("Exception")) {
return jwtToken;
}
retry--;
continue;
} else {
return `ERROR: ${response.status} - ${await response.text()}`;
}
} catch (error) {
return `Exception: ${error.message}`;
}
}
return "Upload failed after retries.";
}
// === Cleans text for CSV: removes commas and replaces line breaks ===
// === Use this for all string fields before writing to CSV ===
//
// Replaces:
// , ? (removed)
// \r\n, \r, \n ? " " (space)
function removeComma(value) {
if (value === null || value === undefined) {
return "";
}
let s = String(value).trim();
s = s.replace(/,/g, "");
s = s.replace(/\r\n/g, " ");
s = s.replace(/\r/g, " ");
s = s.replace(/\n/g, " ");
return s;
}
// === Converts numeric value to string with up to 3 decimal places ===
// === Trailing zeros are removed. Use only for Weight-related fields ===
//
// Examples:
// Convert 91.5 ? "91.5"
// Convert 91.56789 ? "91.568"
// Keep 52 ? "52" (no .000 added)
function formatDecimal3(value) {
if (value === null || value === undefined || isNaN(value)) {
return "0";
}
const strValue = value.toString();
if (strValue.indexOf('.') === -1) {
return strValue; // Return integer value as is
}
const rounded = parseFloat(value.toFixed(3));
return rounded.toString();
}
// === OPTIONAL TEST CALL ===
// Make calls Examples
// testUpload() //-> CSVFromHardCoded
//===============================================================
async function testUpload() {
const csv = csvCreate(); //--Using Hard Coded values
let token;
if (!LoadViewerToken) {
token = await getJwtToken();
} else {
token = LoadViewerToken;
}
if (!token || token.startsWith("ERROR") || token.startsWith("Exception")) {
console.error("Token Error or Server Not Reachable: \n" + token);
return;
}
const response = await UploadToLoadViewer(csv, token);
console.log(response);
}
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
public class LoadViewerCSV {
// === Constants ===
private static final String LoadViewerTokenURL = "https://www.loadviewer.com/api/auth/jwt/token";
private static final String LoadViewerUploadURL = "https://www.loadviewer.com/api/integration/v1/upload";
private static final String LoadViewerAPIEmail = "YOUR_Email";
private static final String LoadViewerAPIKey = "YOUR_API_KEY_HERE";
// Optional: You can fetch the above values from file or DB:
// - From properties file, or from a database config table
// - From encrypted DB table
// - From environment variable (System.getenv() in Java)
// === Module level variable to cache the retrieved token ===
private static String LoadViewerToken = "";
// === Dynamically generates LoadViewer CSV from hardcoded data ===
public static String csvGenerate() {
StringBuilder csv = new StringBuilder();
Map<String, Object> shipmentData=new HashMap<>(); shipmentData.put("Reference", "SANDBOX-01");
shipmentData.put("HangingAllowed", true);
shipmentData.put("UseMultipleContainers", false);
Map<String, Object> containerData = new HashMap<>(); containerData.put("Length", 5867);
containerData.put("Width", 2352);
containerData.put("Height", 2393);
containerData.put("MaxWeight", 27200.0);
containerData.put("BackMargin", 0);
Map<String, Object> cargoData = new HashMap<>(); cargoData.put("Color", "#008000");
cargoData.put("Length", 700);
cargoData.put("Width", 500);
cargoData.put("Height", 300);
cargoData.put("Weight", 91.5);
cargoData.put("Cartons", 300);
cargoData.put("AdditionalCartons", 0);
cargoData.put("Placement", 0);
cargoData.put("VerticalRotationAllowed", true);
cargoData.put("SKUPerCarton", 1);
cargoData.put("EmptyPalletHeight", 0);
cargoData.put("TrayCap", 0);
cargoData.put("TrayHeight", 0);
cargoData.put("AirSpaceLength", 0);
cargoData.put("AirSpaceWidth", 0);
cargoData.put("AirSpaceHeight", 0);
cargoData.put("NameOfSet", "");
cargoData.put("CartonRatio", 0);
cargoData.put("Description", "");
cargoData.put("Destination", 0);
cargoData.put("PO", "");
cargoData.put("Shipper", "");
// --- Begin CSV output ---
csv.append("S,H,Reference,Hanging Allowed,Use Multiple Containers\n");
csv.append(String.format("S,D,%s,%s,%s\n", removeComma(shipmentData.get("Reference").toString()),
shipmentData.get("HangingAllowed"), shipmentData.get("UseMultipleContainers")));
csv.append("B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n");
csv.append(String.format("B,D,%s,%s,%s,%s,%s\n", containerData.get("Length"), containerData.get("Width"),
containerData.get("Height"), formatDecimal3((Double) containerData.get("MaxWeight")),
containerData.get("BackMargin")));
csv.append("C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n");
csv.append(String.format("C,D,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
removeComma(cargoData.get("Color").toString()), cargoData.get("Length"), cargoData.get("Width"),
cargoData.get("Height"), formatDecimal3((Double) cargoData.get("Weight")), cargoData.get("Cartons"),
cargoData.get("AdditionalCartons"), cargoData.get("Placement"), cargoData.get("VerticalRotationAllowed"),
cargoData.get("SKUPerCarton"), cargoData.get("EmptyPalletHeight"), cargoData.get("TrayCap"),
cargoData.get("TrayHeight"), cargoData.get("AirSpaceLength"), cargoData.get("AirSpaceWidth"),
cargoData.get("AirSpaceHeight"), removeComma(cargoData.get("NameOfSet").toString()),
cargoData.get("CartonRatio"), removeComma(cargoData.get("Description").toString()),
cargoData.get("Destination"), removeComma(cargoData.get("PO").toString()),
removeComma(cargoData.get("Shipper").toString())));
return csv.toString();
}
// === Generate CSV string for SANDBOX-01 ===
public static String csvCreate() {
StringBuilder csv = new StringBuilder();
csv.append("S,H,Reference,Hanging Allowed,Use Multiple Containers\n");
csv.append("S,D,SANDBOX-01,True,False\n");
csv.append("B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n");
csv.append("B,D,5867,2352,2393,27200,0\n");
csv.append("C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n");
csv.append("C,D,#008000,700,500,300,91.5,300,0,0,True,1,0,0,0,0,0,0,,0,,0,,\n");
return csv.toString();
}
// === Get JWT Token ===
public static String getJwtToken() throws IOException, InterruptedException {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(LoadViewerTokenURL))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(String.format("{\"email\":\"%s\",\"apikey\":\"%s\"}",
LoadViewerAPIEmail, LoadViewerAPIKey)))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
String json = response.body();
String token = json.substring(json.indexOf("\"token\":\"") + 9, json.indexOf("\"", json.indexOf("\"token\":\"") + 9));
LoadViewerToken = token; // Cache the token
return token;
} else {
return "ERROR: " + response.statusCode() + " - " + response.body();
}
}
// === Upload CSV using Bearer Token ===
public static String UploadToLoadViewer(String csvData, String jwtToken) throws IOException, InterruptedException {
int retry = 3;
while (retry > 0) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(LoadViewerUploadURL))
.header("Content-Type", "text/csv")
.header("Authorization", "Bearer " + jwtToken)
.POST(HttpRequest.BodyPublishers.ofString(csvData))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200 || response.statusCode() == 202) {
return response.statusCode() + " - " + response.body();
} else if (response.statusCode() == 410) {
jwtToken = getJwtToken();
if (jwtToken.startsWith("ERROR")) {
return jwtToken;
}
retry--;
continue;
} else {
return "ERROR: " + response.statusCode() + " - " + response.body();
}
}
return "Upload failed after retries.";
}
// === Cleans text for CSV: removes commas and replaces line breaks ===
// === Use this for all string fields before writing to CSV ===
//
// Replaces:
// , ? (removed)
// \r\n, \r, \n ? " " (space)
public static String removeComma(String value) {
if (value == null) {
return "";
}
String s = value.trim();
s = s.replace(",", "");
s = s.replace("\r\n", " ");
s = s.replace("\r", " ");
s = s.replace("\n", " ");
return s;
}
// === Converts numeric value to string with up to 3 decimal places ===
// === Trailing zeros are removed. Use only for Weight-related fields ===
//
// Examples:
// Convert 91.5 ? "91.5"
// Convert 91.56789 ? "91.568"
// Keep 52 ? "52" (no .000 added)
public static String formatDecimal3(Double value) {
DecimalFormat df = new DecimalFormat("0.###");
return df.format(value);
}
// === OPTIONAL TEST CALL ===
// Make calls Examples
// testUpload() //-> CSVFromHardCoded
// ===============================================================
public static void testUpload() throws IOException, InterruptedException {
String csv = csvCreate(); // --Using Hard Coded values
String token;
if (LoadViewerToken.isEmpty()) {
token = getJwtToken();
} else {
token = LoadViewerToken;
}
if (token.startsWith("ERROR")) {
System.err.println("Token Error or Server Not Reachable: \n" + token);
return;
}
String response = UploadToLoadViewer(csv, token);
System.out.println(response);
}
}
open System
open System.Net.Http
open System.Text.Json
open System.Threading.Tasks
// ========================================
// Module: modLoadViewerCSV
// Purpose:
// 1. Generate LoadViewer-compatible CSV from hardcoded or database values
// 2. Upload CSV to LoadViewer using HTTP POST (HttpClient)
// Includes helper functions:
// - removeComma: for all string fields
// - formatDecimal3: for weights with decimals
// ========================================
// === Constants ===
let LoadViewerTokenURL = "https://www.loadviewer.com/api/auth/jwt/token"
let LoadViewerUploadURL = "https://www.loadviewer.com/api/integration/v1/upload"
let LoadViewerAPIEmail = "YOUR_Email"
let LoadViewerAPIKey = "YOUR_API_KEY_HERE"
// Optional: You can fetch the above values from file or DB:
// - From config files, or from a database config table
// - From encrypted DB table
// - From environment variables (Environment.GetEnvironmentVariable in F#)
// === Module level variable to cache the retrieved token ===
let mutable LoadViewerToken = ""
// === Dynamically generates LoadViewer CSV from hardcoded data ===
let csvGenerate () =
let shipmentData =
[ "Reference", "SANDBOX-01"; "HangingAllowed", true; "UseMultipleContainers", false ] |> Map.ofList
let containerData =
[ "Length", 5867; "Width", 2352; "Height", 2393; "MaxWeight", 27200.0; "BackMargin", 0 ] |> Map.ofList
let cargoData =
[ "Color", "#008000"; "Length", 700; "Width", 500; "Height", 300; "Weight", 91.5; "Cartons", 300;
"AdditionalCartons", 0; "Placement", 0; "VerticalRotationAllowed", true; "SKUPerCarton", 1;
"EmptyPalletHeight", 0; "TrayCap", 0; "TrayHeight", 0; "AirSpaceLength", 0; "AirSpaceWidth", 0;
"AirSpaceHeight", 0; "NameOfSet", ""; "CartonRatio", 0; "Description", ""; "Destination", 0;
"PO", ""; "Shipper", "" ] |> Map.ofList
let csv = StringBuilder()
csv.Append("S,H,Reference,Hanging Allowed,Use Multiple Containers\n") |> ignore
csv.Append(sprintf "S,D,%s,%b,%b\n" (removeComma (shipmentData.["Reference"].ToString())) shipmentData.["HangingAllowed"] shipmentData.["UseMultipleContainers"]) |> ignore
csv.Append("B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n") |> ignore
csv.Append(sprintf "B,D,%i,%i,%i,%s,%i\n" containerData.["Length"] containerData.["Width"] containerData.["Height"] (formatDecimal3 (containerData.["MaxWeight"] :> float)) containerData.["BackMargin"]) |> ignore
csv.Append("C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n") |> ignore
csv.Append(sprintf "C,D,%s,%i,%i,%i,%s,%i,%i,%i,%b,%i,%i,%i,%i,%i,%i,%i,%s,%i,%s,%i,%s,%s\n"
(removeComma (cargoData.["Color"].ToString())) cargoData.["Length"] cargoData.["Width"] cargoData.["Height"]
(formatDecimal3 (cargoData.["Weight"] :> float)) cargoData.["Cartons"] cargoData.["AdditionalCartons"] cargoData.["Placement"]
cargoData.["VerticalRotationAllowed"] cargoData.["SKUPerCarton"] cargoData.["EmptyPalletHeight"] cargoData.["TrayCap"]
cargoData.["TrayHeight"] cargoData.["AirSpaceLength"] cargoData.["AirSpaceWidth"] cargoData.["AirSpaceHeight"]
(removeComma (cargoData.["NameOfSet"].ToString())) cargoData.["CartonRatio"] (removeComma (cargoData.["Description"].ToString()))
cargoData.["Destination"] (removeComma (cargoData.["PO"].ToString())) (removeComma (cargoData.["Shipper"].ToString()))) |> ignore
csv.ToString()
// === Generate CSV string for SANDBOX-01 ===
let csvCreate () =
let csv = StringBuilder()
csv.Append("S,H,Reference,Hanging Allowed,Use Multiple Containers\n") |> ignore
csv.Append("S,D,SANDBOX-01,True,False\n") |> ignore
csv.Append("B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n") |> ignore
csv.Append("B,D,5867,2352,2393,27200,0\n") |> ignore
csv.Append("C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n") |> ignore
csv.Append("C,D,#008000,700,500,300,91.5,300,0,0,True,1,0,0,0,0,0,0,,0,,0,,\n") |> ignore
csv.ToString()
// === Get JWT Token ===
let getJwtToken () =
task {
try
use client = new HttpClient()
client.Timeout <- TimeSpan.FromSeconds(5.0)
let jsonBody = JsonSerializer.Serialize([ "email", LoadViewerAPIEmail; "apikey", LoadViewerAPIKey ] |> Map.ofList)
let content = new StringContent(jsonBody, System.Text.Encoding.UTF8, "application/json")
let! response = client.PostAsync(LoadViewerTokenURL, content)
if response.IsSuccessStatusCode then
let! responseText = response.Content.ReadAsStringAsync()
let json = JsonDocument.Parse(responseText)
let token = json.RootElement.GetProperty("token").GetString()
LoadViewerToken <- token
return token
else
return sprintf "ERROR: %i - %s" (int response.StatusCode) (response.Content.ReadAsStringAsync().Result)
with ex ->
return sprintf "Exception: %s" ex.Message
}
// === Upload CSV using Bearer Token ===
let UploadToLoadViewer (csvData: string) (jwtToken: string) =
task {
let mutable retry = 3
while retry > 0 do
try
use client = new HttpClient()
client.DefaultRequestHeaders.Authorization <- System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", jwtToken)
let content = new StringContent(csvData, System.Text.Encoding.UTF8, "text/csv")
let! response = client.PostAsync(LoadViewerUploadURL, content)
if response.StatusCode = System.Net.HttpStatusCode.OK || response.StatusCode = System.Net.HttpStatusCode.Accepted then
return sprintf "%i - %s" (int response.StatusCode) (response.Content.ReadAsStringAsync().Result)
elif response.StatusCode = System.Net.HttpStatusCode.Gone then
let! newToken = getJwtToken ()
if newToken.StartsWith("ERROR") || newToken.StartsWith("Exception") then
return newToken
jwtToken <- newToken
retry <- retry - 1
else
return sprintf "ERROR: %i - %s" (int response.StatusCode) (response.Content.ReadAsStringAsync().Result)
with ex ->
return sprintf "Exception: %s" ex.Message
return "Upload failed after retries."
}
// === Cleans text for CSV: removes commas and replaces line breaks ===
// === Use this for all string fields before writing to CSV ===
//
// Replaces:
// , ? (removed)
// \r\n, \r, \n ? " " (space)
let removeComma (value: string) =
if String.IsNullOrEmpty(value) then ""
else
value.Trim().Replace(",", "").Replace("\r\n", " ").Replace("\r", " ").Replace("\n", " ")
// === Converts numeric value to string with up to 3 decimal places ===
// === Trailing zeros are removed. Use only for Weight-related fields ===
//
// Examples:
// Convert 91.5 ? "91.5"
// Convert 91.56789 ? "91.568"
// Keep 52 ? "52" (no .000 added)
let formatDecimal3 (value: float) =
sprintf "%.3f" value |> float |> string
// === OPTIONAL TEST CALL ===
// Make calls Examples
// testUpload () //-> CSVFromHardCoded
//===============================================================
let testUpload () =
task {
let csv = csvCreate ()
let! token = if String.IsNullOrEmpty LoadViewerToken then getJwtToken () else async.Return LoadViewerToken |> Async.AwaitTask
if token.StartsWith("ERROR") || token.StartsWith("Exception") then
printfn "Token Error or Server Not Reachable: \n%s" token
else
let! response = UploadToLoadViewer csv token
printfn "%s" response
}
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"strconv"
"strings"
)
// ========================================
// Module: modLoadViewerCSV
// Purpose:
// 1. Generate LoadViewer-compatible CSV from hardcoded or database values
// 2. Upload CSV to LoadViewer using HTTP POST (http.Client)
// Includes helper functions:
// - removeComma: for all string fields
// - formatDecimal3: for weights with decimals
// ========================================
// === Constants ===
const (
LoadViewerTokenURL = "https://www.loadviewer.com/api/auth/jwt/token"
LoadViewerUploadURL = "https://www.loadviewer.com/api/integration/v1/upload"
LoadViewerAPIEmail = "YOUR_Email"
LoadViewerAPIKey = "YOUR_API_KEY_HERE"
)
// Optional: You can fetch the above values from file or DB:
// - From config files, or from a database config table
// - From encrypted DB table
// - From environment variables (os.Getenv in Go)
// === Module level variable to cache the retrieved token ===
var LoadViewerToken string
// === Dynamically generates LoadViewer CSV from hardcoded data ===
func csvGenerate() string {
var csv strings.Builder
shipmentData := map[string]interface{}{
"Reference": "SANDBOX-01",
"HangingAllowed": true,
"UseMultipleContainers": false,
}
containerData := map[string]interface{}{
"Length": 5867,
"Width": 2352,
"Height": 2393,
"MaxWeight": 27200.0,
"BackMargin": 0,
}
cargoData := map[string]interface{}{
"Color": "#008000",
"Length": 700,
"Width": 500,
"Height": 300,
"Weight": 91.5,
"Cartons": 300,
"AdditionalCartons": 0,
"Placement": 0,
"VerticalRotationAllowed": true,
"SKUPerCarton": 1,
"EmptyPalletHeight": 0,
"TrayCap": 0,
"TrayHeight": 0,
"AirSpaceLength": 0,
"AirSpaceWidth": 0,
"AirSpaceHeight": 0,
"NameOfSet": "",
"CartonRatio": 0,
"Description": "",
"Destination": 0,
"PO": "",
"Shipper": "",
}
// --- Begin CSV output ---
csv.WriteString("S,H,Reference,Hanging Allowed,Use Multiple Containers\n")
csv.WriteString(fmt.Sprintf("S,D,%s,%t,%t\n", removeComma(shipmentData["Reference"].(string)), shipmentData["HangingAllowed"].(bool), shipmentData["UseMultipleContainers"].(bool)))
csv.WriteString("B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n")
csv.WriteString(fmt.Sprintf("B,D,%d,%d,%d,%s,%d\n", containerData["Length"].(int), containerData["Width"].(int), containerData["Height"].(int), formatDecimal3(containerData["MaxWeight"].(float64)), containerData["BackMargin"].(int)))
csv.WriteString("C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n")
csv.WriteString(fmt.Sprintf("C,D,%s,%d,%d,%d,%s,%d,%d,%d,%t,%d,%d,%d,%d,%d,%d,%d,%s,%d,%s,%d,%s,%s\n",
removeComma(cargoData["Color"].(string)), cargoData["Length"].(int), cargoData["Width"].(int), cargoData["Height"].(int),
formatDecimal3(cargoData["Weight"].(float64)), cargoData["Cartons"].(int), cargoData["AdditionalCartons"].(int), cargoData["Placement"].(int),
cargoData["VerticalRotationAllowed"].(bool), cargoData["SKUPerCarton"].(int), cargoData["EmptyPalletHeight"].(int), cargoData["TrayCap"].(int),
cargoData["TrayHeight"].(int), cargoData["AirSpaceLength"].(int), cargoData["AirSpaceWidth"].(int), cargoData["AirSpaceHeight"].(int),
removeComma(cargoData["NameOfSet"].(string)), cargoData["CartonRatio"].(int), removeComma(cargoData["Description"].(string)),
cargoData["Destination"].(int), removeComma(cargoData["PO"].(string)), removeComma(cargoData["Shipper"].(string))))
return csv.String()
}
// === Generate CSV string for SANDBOX-01 ===
func csvCreate() string {
var csv strings.Builder
csv.WriteString("S,H,Reference,Hanging Allowed,Use Multiple Containers\n")
csv.WriteString("S,D,SANDBOX-01,True,False\n")
csv.WriteString("B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n")
csv.WriteString("B,D,5867,2352,2393,27200,0\n")
csv.WriteString("C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n")
csv.WriteString("C,D,#008000,700,500,300,91.5,300,0,0,True,1,0,0,0,0,0,0,,0,,0,,\n")
return csv.String()
}
// === Get JWT Token ===
func getJwtToken() (string, error) {
payload := map[string]string{
"email": LoadViewerAPIEmail,
"apikey": LoadViewerAPIKey,
}
jsonPayload, err := json.Marshal(payload)
if err != nil {
return "", err
}
resp, err := http.Post(LoadViewerTokenURL, "application/json", bytes.NewBuffer(jsonPayload))
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
if resp.StatusCode == http.StatusOK {
var response map[string]string
if err := json.Unmarshal(body, &response); err != nil {
return "", err
}
LoadViewerToken = response["token"]
return response["token"], nil
}
return fmt.Sprintf("ERROR: %d - %s", resp.StatusCode, string(body)), nil
}
// === Upload CSV using Bearer Token ===
func UploadToLoadViewer(csvData, jwtToken string) (string, error) {
retry := 3
for retry > 0 {
req, err := http.NewRequest("POST", LoadViewerUploadURL, bytes.NewBuffer([]byte(csvData)))
if err != nil {
return "", err
}
req.Header.Set("Content-Type", "text/csv")
req.Header.Set("Authorization", "Bearer "+jwtToken)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusAccepted {
return fmt.Sprintf("%d - %s", resp.StatusCode, string(body)), nil
} else if resp.StatusCode == http.StatusGone {
token, err := getJwtToken()
if err != nil {
return "", err
}
if strings.HasPrefix(token, "ERROR") {
return token, nil
}
jwtToken = token
retry--
} else {
return fmt.Sprintf("ERROR: %d - %s", resp.StatusCode, string(body)), nil
}
}
return "Upload failed after retries.", nil
}
// === Cleans text for CSV: removes commas and replaces line breaks ===
// === Use this for all string fields before writing to CSV ===
//
// Replaces:
// , ? (removed)
// \r\n, \r, \n ? " " (space)
func removeComma(value string) string {
s := strings.TrimSpace(value)
s = strings.ReplaceAll(s, ",", "")
s = strings.ReplaceAll(s, "\r\n", " ")
s = strings.ReplaceAll(s, "\r", " ")
s = strings.ReplaceAll(s, "\n", " ")
return s
}
// === Converts numeric value to string with up to 3 decimal places ===
// === Trailing zeros are removed. Use only for Weight-related fields ===
//
// Examples:
// Convert 91.5 ? "91.5"
// Convert 91.56789 ? "91.568"
// Keep 52 ? "52" (no .000 added)
func formatDecimal3(value float64) string {
return strconv.FormatFloat(value, 'f', 3, 64)
}
// === OPTIONAL TEST CALL ===
// Make calls Examples
// testUpload() //-> CSVFromHardCoded
// ===============================================================
func testUpload() {
csv := csvCreate()
token := LoadViewerToken
if token == "" {
tok, err := getJwtToken()
if err != nil {
log.Println(err)
return
}
token = tok
}
if strings.HasPrefix(token, "ERROR") {
log.Println("Token Error or Server Not Reachable: \n" + token)
return
}
resp, err := UploadToLoadViewer(csv, token)
if err != nil {
log.Println(err)
return
}
log.Println(resp)
}
func main() {
// testUpload()
}
# ========================================
# Module: modLoadViewerCSV
# Purpose:
# 1. Generate LoadViewer-compatible CSV from hardcoded or database values
# 2. Upload CSV to LoadViewer using HTTP POST (httr package)
# Includes helper functions:
# - removeComma: for all string fields
# - formatDecimal3: for weights with decimals
# ========================================
# === Constants ===
LoadViewerTokenURL <- "https://www.loadviewer.com/api/auth/jwt/token"
LoadViewerUploadURL <- "https://www.loadviewer.com/api/integration/v1/upload"
LoadViewerAPIEmail <- "YOUR_Email"
LoadViewerAPIKey <- "YOUR_API_KEY_HERE"
# Optional: You can fetch the above values from file or DB:
# - From config files, or from a database config table
# - From encrypted DB table
# - From environment variables (Sys.getenv in R)
# === Module level variable to cache the retrieved token ===
LoadViewerToken <- ""
# === Dynamically generates LoadViewer CSV from hardcoded data ===
csvGenerate <- function() {
shipmentData <- list(
Reference = "SANDBOX-01",
HangingAllowed = TRUE,
UseMultipleContainers = FALSE
)
containerData <- list(
Length = 5867,
Width = 2352,
Height = 2393,
MaxWeight = 27200,
BackMargin = 0
)
cargoData <- list(
Color = "#008000",
Length = 700,
Width = 500,
Height = 300,
Weight = 91.5,
Cartons = 300,
AdditionalCartons = 0,
Placement = 0,
VerticalRotationAllowed = TRUE,
SKUPerCarton = 1,
EmptyPalletHeight = 0,
TrayCap = 0,
TrayHeight = 0,
AirSpaceLength = 0,
AirSpaceWidth = 0,
AirSpaceHeight = 0,
NameOfSet = "",
CartonRatio = 0,
Description = "",
Destination = 0,
PO = "",
Shipper = ""
)
# --- Begin CSV output ---
csv <- "S,H,Reference,Hanging Allowed,Use Multiple Containers\n"
csv <- paste0(csv, "S,D,", removeComma(shipmentData$Reference), ",", shipmentData$HangingAllowed, ",", shipmentData$UseMultipleContainers, "\n")
csv <- paste0(csv, "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n")
csv <- paste0(csv, "B,D,", containerData$Length, ",", containerData$Width, ",", containerData$Height, ",", formatDecimal3(containerData$MaxWeight), ",", containerData$BackMargin, "\n")
csv <- paste0(csv, "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n")
csv <- paste0(csv, "C,D,", removeComma(cargoData$Color), ",", cargoData$Length, ",", cargoData$Width, ",", cargoData$Height, ",", formatDecimal3(cargoData$Weight), ",", cargoData$Cartons, ",", cargoData$AdditionalCartons, ",", cargoData$Placement, ",", cargoData$VerticalRotationAllowed, ",", cargoData$SKUPerCarton, ",", cargoData$EmptyPalletHeight, ",", cargoData$TrayCap, ",", cargoData$TrayHeight, ",", cargoData$AirSpaceLength, ",", cargoData$AirSpaceWidth, ",", cargoData$AirSpaceHeight, ",", removeComma(cargoData$NameOfSet), ",", cargoData$CartonRatio, ",", removeComma(cargoData$Description), ",", cargoData$Destination, ",", removeComma(cargoData$PO), ",", removeComma(cargoData$Shipper), "\n")
return(csv)
}
# === Generate CSV string for SANDBOX-01 ===
csvCreate <- function() {
csv <- "S,H,Reference,Hanging Allowed,Use Multiple Containers\n"
csv <- paste0(csv, "S,D,SANDBOX-01,TRUE,FALSE\n")
csv <- paste0(csv, "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n")
csv <- paste0(csv, "B,D,5867,2352,2393,27200,0\n")
csv <- paste0(csv, "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n")
csv <- paste0(csv, "C,D,#008000,700,500,300,91.5,300,0,0,TRUE,1,0,0,0,0,0,0,,0,,0,,\n")
return(csv)
}
# === Get JWT Token ===
getJwtToken <- function() {
tryCatch({
response <- httr::POST(
LoadViewerTokenURL,
httr::content_type_json(),
body = list(email = LoadViewerAPIEmail, apikey = LoadViewerAPIKey)
)
if (httr::status_code(response) == 200) {
json <- httr::content(response, "parsed")
token <- json$token
LoadViewerToken <<- token # Cache the token
return(token)
} else {
return(paste0("ERROR: ", httr::status_code(response), " - ", httr::content(response, "text")))
}
}, error = function(e) {
return(paste0("Exception: ", e$message))
})
}
# === Upload CSV using Bearer Token ===
UploadToLoadViewer <- function(csvData, jwtToken) {
retry <- 3
while (retry > 0) {
tryCatch({
response <- httr::POST(
LoadViewerUploadURL,
httr::content_type("text/csv"),
httr::add_headers(Authorization = paste0("Bearer ", jwtToken)),
body = csvData
)
if (httr::status_code(response) %in% c(200, 202)) {
return(paste0(httr::status_code(response), " - ", httr::content(response, "text")))
} else if (httr::status_code(response) == 410) {
jwtToken <- getJwtToken()
if (startsWith(jwtToken, "ERROR") || startsWith(jwtToken, "Exception")) {
return(jwtToken)
}
retry <- retry - 1
} else {
return(paste0("ERROR: ", httr::status_code(response), " - ", httr::content(response, "text")))
}
}, error = function(e) {
return(paste0("Exception: ", e$message))
})
}
return("Upload failed after retries.")
}
# === Cleans text for CSV: removes commas and replaces line breaks ===
# === Use this for all string fields before writing to CSV ===
#
# Replaces:
# , ? (removed)
# \r\n, \r, \n ? " " (space)
removeComma <- function(value) {
if (is.null(value)) {
return("")
}
s <- trimws(value)
s <- gsub(",", "", s)
s <- gsub("[\r\n]", " ", s)
return(s)
}
# === Converts numeric value to string with up to 3 decimal places ===
# === Trailing zeros are removed. Use only for Weight-related fields ===
#
# Examples:
# Convert 91.5 ? "91.5"
# Convert 91.56789 ? "91.568"
# Keep 52 ? "52" (no .000 added)
formatDecimal3 <- function(value) {
if (is.null(value) || !is.numeric(value)) {
return("0")
}
return(format(round(value, 3), nsmall = 3, scientific = FALSE))
}
# === OPTIONAL TEST CALL ===
# Make calls Examples
# testUpload() //-> CSVFromHardCoded
#===============================================================
testUpload <- function() {
csv <- csvCreate()
token <- LoadViewerToken
if (token == "") {
token <- getJwtToken()
}
if (startsWith(token, "ERROR") || startsWith(token, "Exception")) {
print(paste0("Token Error or Server Not Reachable: \n", token))
return()
}
response <- UploadToLoadViewer(csv, token)
print(response)
}
<?php
// ========================================
// Module: modLoadViewerCSV
// Purpose:
// 1. Generate LoadViewer-compatible CSV from hardcoded or database values
// 2. Upload CSV to LoadViewer using HTTP POST (cURL)
// Includes helper functions:
// - removeComma: for all string fields
// - formatDecimal3: for weights with decimals
// ========================================
// === Constants ===
const LoadViewerTokenURL = 'https://www.loadviewer.com/api/auth/jwt/token';
const LoadViewerUploadURL = 'https://www.loadviewer.com/api/integration/v1/upload';
const LoadViewerAPIEmail = 'YOUR_Email';
const LoadViewerAPIKey = 'YOUR_API_KEY_HERE';
// Optional: You can fetch the above values from file or DB:
// - From config files, or from a database config table
// - From encrypted DB table
// - From environment variables (getenv in PHP)
// === Module level variable to cache the retrieved token ===
$LoadViewerToken = '';
// === Dynamically generates LoadViewer CSV from hardcoded data ===
function csvGenerate() {
$shipmentData = [
'Reference' => 'SANDBOX-01',
'HangingAllowed' => true,
'UseMultipleContainers' => false,
];
$containerData = [
'Length' => 5867,
'Width' => 2352,
'Height' => 2393,
'MaxWeight' => 27200,
'BackMargin' => 0,
];
$cargoData = [
'Color' => '#008000',
'Length' => 700,
'Width' => 500,
'Height' => 300,
'Weight' => 91.5,
'Cartons' => 300,
'AdditionalCartons' => 0,
'Placement' => 0,
'VerticalRotationAllowed' => true,
'SKUPerCarton' => 1,
'EmptyPalletHeight' => 0,
'TrayCap' => 0,
'TrayHeight' => 0,
'AirSpaceLength' => 0,
'AirSpaceWidth' => 0,
'AirSpaceHeight' => 0,
'NameOfSet' => '',
'CartonRatio' => 0,
'Description' => '',
'Destination' => 0,
'PO' => '',
'Shipper' => '',
];
// --- Begin CSV output ---
$csv = 'S,H,Reference,Hanging Allowed,Use Multiple Containers\n';
$csv .= 'S,D,' . removeComma($shipmentData['Reference']) . ',' . ($shipmentData['HangingAllowed'] ? 'TRUE' : 'FALSE') . ',' . ($shipmentData['UseMultipleContainers'] ? 'FALSE' : 'TRUE') . '\n';
$csv .= 'B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n';
$csv .= 'B,D,' . $containerData['Length'] . ',' . $containerData['Width'] . ',' . $containerData['Height'] . ',' . formatDecimal3($containerData['MaxWeight']) . ',' . $containerData['BackMargin'] . '\n';
$csv .= 'C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n';
$csv .= 'C,D,' . removeComma($cargoData['Color']) . ',' . $cargoData['Length'] . ',' . $cargoData['Width'] . ',' . $cargoData['Height'] . ',' . formatDecimal3($cargoData['Weight']) . ',' . $cargoData['Cartons'] . ',' . $cargoData['AdditionalCartons'] . ',' . $cargoData['Placement'] . ',' . ($cargoData['VerticalRotationAllowed'] ? 'TRUE' : 'FALSE') . ',' . $cargoData['SKUPerCarton'] . ',' . $cargoData['EmptyPalletHeight'] . ',' . $cargoData['TrayCap'] . ',' . $cargoData['TrayHeight'] . ',' . $cargoData['AirSpaceLength'] . ',' . $cargoData['AirSpaceWidth'] . ',' . $cargoData['AirSpaceHeight'] . ',' . removeComma($cargoData['NameOfSet']) . ',' . $cargoData['CartonRatio'] . ',' . removeComma($cargoData['Description']) . ',' . $cargoData['Destination'] . ',' . removeComma($cargoData['PO']) . ',' . removeComma($cargoData['Shipper']) . '\n';
return $csv;
}
// === Generate CSV string for SANDBOX-01 ===
function csvCreate() {
$csv = 'S,H,Reference,Hanging Allowed,Use Multiple Containers\n';
$csv .= 'S,D,SANDBOX-01,TRUE,FALSE\n';
$csv .= 'B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n';
$csv .= 'B,D,5867,2352,2393,27200,0\n';
$csv .= 'C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n';
$csv .= 'C,D,#008000,700,500,300,91.5,300,0,0,TRUE,1,0,0,0,0,0,0,,0,,0,,\n';
return $csv;
}
// === Get JWT Token ===
function getJwtToken() {
global $LoadViewerToken;
$postData = json_encode(['email' => LoadViewerAPIEmail, 'apikey' => LoadViewerAPIKey]);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, LoadViewerTokenURL);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode == 200) {
$json = json_decode($response, true);
$LoadViewerToken = $json['token'];
return $json['token'];
} else {
return 'ERROR: ' . $httpCode . ' - ' . $response;
}
}
// === Upload CSV using Bearer Token ===
function UploadToLoadViewer($csvData, $jwtToken) {
$retry = 3;
while ($retry > 0) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, LoadViewerUploadURL);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $csvData);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: text/csv', 'Authorization: Bearer ' . $jwtToken]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode == 200 || $httpCode == 202) {
return $httpCode . ' - ' . $response;
} else if ($httpCode == 410) {
$jwtToken = getJwtToken();
if (strpos($jwtToken, 'ERROR') === 0) {
return $jwtToken;
}
$retry--;
} else {
return 'ERROR: ' . $httpCode . ' - ' . $response;
}
}
return 'Upload failed after retries.';
}
// === Cleans text for CSV: removes commas and replaces line breaks ===
// === Use this for all string fields before writing to CSV ===
//
// Replaces:
// , ? (removed)
// \r\n, \r, \n ? ' ' (space)
function removeComma($value) {
if ($value === null) {
return '';
}
$s = trim($value);
$s = str_replace(',', '', $s);
$s = preg_replace('/[\r\n]+/', ' ', $s);
return $s;
}
// === Converts numeric value to string with up to 3 decimal places ===
// === Trailing zeros are removed. Use only for Weight-related fields ===
//
// Examples:
// Convert 91.5 ? '91.5'
// Convert 91.56789 ? '91.568'
// Keep 52 ? '52' (no .000 added)
function formatDecimal3($value) {
if ($value === null || !is_numeric($value)) {
return '0';
}
return rtrim(sprintf('%.3f', $value), '0.');
}
// === OPTIONAL TEST CALL ===
// Make calls Examples
// testUpload() //-> CSVFromHardCoded
//===============================================================
function testUpload() {
global $LoadViewerToken;
$csv = csvCreate();
$token = $LoadViewerToken;
if (empty($token)) {
$token = getJwtToken();
}
if (strpos($token, 'ERROR') === 0) {
echo 'Token Error or Server Not Reachable: \n' . $token;
return;
}
$response = UploadToLoadViewer($csv, $token);
echo $response;
}
?>
import 'dart:convert';
import 'package:http/http.dart' as http;
// ========================================
// Module: modLoadViewerCSV
// Purpose:
// 1. Generate LoadViewer-compatible CSV from hardcoded or database values
// 2. Upload CSV to LoadViewer using HTTP POST (http package)
// Includes helper functions:
// - removeComma: for all string fields
// - formatDecimal3: for weights with decimals
// ========================================
// === Constants ===
const String LoadViewerTokenURL = "https://www.loadviewer.com/api/auth/jwt/token";
const String LoadViewerUploadURL = "https://www.loadviewer.com/api/integration/v1/upload";
const String LoadViewerAPIEmail = "YOUR_Email";
const String LoadViewerAPIKey = "YOUR_API_KEY_HERE";
// Optional: You can fetch the above values from file or DB:
// - From config files, or from a database config table
// - From encrypted DB table
// - From environment variables (Platform.environment in Dart)
// === Module level variable to cache the retrieved token ===
String LoadViewerToken = "";
// === Dynamically generates LoadViewer CSV from hardcoded data ===
String csvGenerate() {
Map<String, dynamic> shipmentData={
"Reference": "SANDBOX-01",
"HangingAllowed": true,
"UseMultipleContainers": false,
};
Map<String, dynamic> containerData = {
"Length": 5867,
"Width": 2352,
"Height": 2393,
"MaxWeight": 27200.0,
"BackMargin": 0,
};
Map<String, dynamic> cargoData = {
"Color": "#008000",
"Length": 700,
"Width": 500,
"Height": 300,
"Weight": 91.5,
"Cartons": 300,
"AdditionalCartons": 0,
"Placement": 0,
"VerticalRotationAllowed": true,
"SKUPerCarton": 1,
"EmptyPalletHeight": 0,
"TrayCap": 0,
"TrayHeight": 0,
"AirSpaceLength": 0,
"AirSpaceWidth": 0,
"AirSpaceHeight": 0,
"NameOfSet": "",
"CartonRatio": 0,
"Description": "",
"Destination": 0,
"PO": "",
"Shipper": "",
};
// --- Begin CSV output ---
String csv = "S,H,Reference,Hanging Allowed,Use Multiple Containers\n";
csv += "S,D,<span class="math-inline">\{removeComma\(shipmentData\["Reference"\]\)\},</span>{shipmentData["HangingAllowed"]},<span class="math-inline" >\{shipmentData\["UseMultipleContainers"\]\}\\n";
csv \+\= "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\\n";
csv \+\= "B,D,</span>{containerData["Length"]},<span class="math-inline" >\{containerData\["Width"\]\},</span>{containerData["Height"]},<span class="math-inline" >\{formatDecimal3\(containerData\["MaxWeight"\]\)\},</span>{containerData["BackMargin"]}\n";
csv += "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n";
csv += "C,D,<span class="math-inline">\{removeComma\(cargoData\["Color"\]\)\},</`>{cargoData["Length"]},<span class="math-inline" >\{cargoData\["Width"\]\},</span>{cargoData["Height"]},<span class="math-inline" >\{formatDecimal3\(cargoData\["Weight"\]\)\},</span>{cargoData["Cartons"]},<span class="math-inline" >\{cargoData\["AdditionalCartons"\]\},</span>{cargoData["Placement"]},<span class="math-inline" >\{cargoData\["VerticalRotationAllowed"\]\},</span>{cargoData["SKUPerCarton"]},<span class="math-inline" >\{cargoData\["EmptyPalletHeight"\]\},</span>{cargoData["TrayCap"]},<span class="math-inline" >\{cargoData\["TrayHeight"\]\},</span>{cargoData["AirSpaceLength"]},<span class="math-inline" >\{cargoData\["AirSpaceWidth"\]\},</span>{cargoData["AirSpaceHeight"]},<span class="math-inline" >\{removeComma\(cargoData\["NameOfSet"\]\)\},</span>{cargoData["CartonRatio"]},<span class="math-inline" >\{removeComma\(cargoData\["Description"\]\)\},</span>{cargoData["Destination"]},<span class="math-inline" >\{removeComma\(cargoData\["PO"\]\)\},</span>{removeComma(cargoData["Shipper"])}\n";
return csv;
}
// === Generate CSV string for SANDBOX-01 ===
String csvCreate() {
String csv = "S,H,Reference,Hanging Allowed,Use Multiple Containers\n";
csv += "S,D,SANDBOX-01,TRUE,FALSE\n";
csv += "B,H,Length mm,Width mm,Height mm,Max Weight Kg,Back Margin mm\n";
csv += "B,D,5867,2352,2393,27200,0\n";
csv += "C,H,Color,Length mm,Width mm,Height mm,Weight Kg,Cartons,Additional Cartons,Placement,Vertical Rotation Allowed,SKU per Carton,Empty Pallet Height mm,Tray Cap mm,Tray Height mm,Air Space Lenght mm,Air Space Width mm,Air Space Height mm,Name of Set,Cartons Ratio in the Set,Description,Destination,PO,Shipper\n";
csv += "C,D,#008000,700,500,300,91.5,300,0,0,TRUE,1,0,0,0,0,0,0,,0,,0,,\n";
return csv;
}
// === Get JWT Token ===
Future<String> getJwtToken() async {
try {
final response = await http.post(
Uri.parse(LoadViewerTokenURL),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({"email": LoadViewerAPIEmail, "apikey": LoadViewerAPIKey}),
);
if (response.statusCode == 200) {
final json = jsonDecode(response.body);
LoadViewerToken = json['token'];
return json['token'];
} else {
return "ERROR: ${response.statusCode} - ${response.body}";
}
} catch (e) {
return "Exception: $e";
}
}
// === Upload CSV using Bearer Token ===
Future<String> UploadToLoadViewer(String csvData, String jwtToken) async {
int retry = 3;
while (retry > 0) {
try {
final response = await http.post(
Uri.parse(LoadViewerUploadURL),
headers: {'Content-Type': 'text/csv', 'Authorization': 'Bearer <span class="math-inline">jwtToken'\},
body\: csvData,
\);
if \(response\.statusCode \=\= 200 \|\| response\.statusCode \=\= 202\) \{
return "</span>{response.statusCode} - ${response.body}";
} else if (response.statusCode == 410) {
jwtToken = await getJwtToken();
if (jwtToken.startsWith("ERROR")) {
return jwtToken;
}
retry--;
} else {
return "ERROR: ${response.statusCode} - ${response.body}";
}
} catch (e) {
return "Exception: <span class="math-inline">e";
\}
\}
return "Upload failed after retries\.";
\}
// \=\=\= Cleans text for CSV\: removes commas and replaces line breaks \=\=\=
// \=\=\= Use this for all string fields before writing to CSV \=\=\=
//
// Replaces\:
// , ? \(removed\)
// \\r\\n, \\r, \\n ? " " \(space\)
String removeComma\(String value\) \{
if \(value \=\= null\) \{
return "";
\}
String s \= value\.trim\(\);
s \= s\.replaceAll\(",", ""\);
s \= s\.replaceAll\(RegExp\(r"\[\\r\\n\]\+"\), " "\);
return s;
\}
// \=\=\= Converts numeric value to string with up to 3 decimal places \=\=\=
// \=\=\= Trailing zeros are removed\. Use only for Weight\-related fields \=\=\=
//
// Examples\:
// Convert 91\.5 ? "91\.5"
// Convert 91\.56789 ? "91\.568"
// Keep 52 ? "52" \(no \.000 added\)
String formatDecimal3\(double value\) \{
if \(value \=\= null \|\| value\.isNaN\) \{
return "0";
\}
return value\.toStringAsFixed\(3\)\.replaceAll\(RegExp\(r"0\*</span>"), '').replaceAll(RegExp(r"\.$"), '');
}
// === OPTIONAL TEST CALL ===
// Make calls Examples
// testUpload() //-> CSVFromHardCoded
//===============================================================
Future<void> testUpload() async {
String csv = csvCreate();
String token = LoadViewerToken;
if (token.isEmpty) {
token = await getJwtToken();
}
if (token.startsWith("ERROR")) {
print("Token Error or Server Not Reachable: \n$token");
return;
}
String response = await UploadToLoadViewer(csv, token);
print(response);
}
Important Reminders
- Requests without API Key will return HTTP
400
or401
- Requests with expired taken shall return HTTP
410
you should replace your cached token with new one from LoadViewer. - Use
SANDBOX-*
Shipment References to avoid real charges, these shipments are not published. - Accepted Shipment always retrun Success with code 202 with Draft status. SANDBOX shipments and other valid shipment that cannot be published are Saved with Draft Status.
- Accepted and Published Shipment always retrun Success with code 200 with Coins Charges and Coin Balance after charge.
- Use the manual file upload method as your first validation step
- Use the Shipments page to delete the SHIPMENTS to try again.
- Always verify
success
and noerror
structure in responses