Inventory & Products | Products | Stock | Set Products Stock

Setting Product Stock Data

This document outlines the procedure for making API calls to update product stock data via https://easycms.fi/public_api/set_products_stock/.

Important Notes

  • All parameters marked as MANDATORY are required
  • This endpoint handles both stock additions and deductions
  • A stockdiary entry is automatically created for every stock modification
  • The endpoint intelligently handles creating new stock entries or updating existing ones

Product Stock Data Update

Update product stock quantities with full audit trail support.

Endpoint and Method

  • Endpoint: https://easycms.fi/public_api/set_products_stock/
  • Method: POST only

Parameters | Payload

  • TOKEN (api_key): Your unique API key for authentication. How to Create API Credentials.
  • username: Your login username.
  • password: Your login password.
  • account: Your specific account ID.

Required Parameters (MANDATORY)

Parameter Type Description
pid integer Product ID - The product to update stock for
location_id integer Location ID - The warehouse/location
shelf_id integer Shelf ID - The shelf within the location
stock decimal Stock quantity change (positive for additions, negative for deductions)
reason integer Reason code for the stock movement (see Reason Codes table)

Optional Parameters

Parameter Type Description
best_before_date date Best before date (YYYY-MM-DD format)
price decimal Sell price at time of movement (for stockdiary reference)
price_buy decimal Buy price at time of movement (for stockdiary reference)
supplier_id integer Supplier ID (for stockdiary reference)
reference string Reference text (order ID, invoice ID, etc.)



Reason Codes

The following reason codes are supported for stock movements:

Stock Additions (IN)

Code Constant Description
1 IN_PURCHASE Stock received from purchase order
2 IN_REFUND Stock returned from customer refund
3 IN_MOVEMENT Stock moved in from another location
4 IN_SALE_CORRECTION Sale cancelled, stock returned

Stock Deductions (OUT)

Code Constant Description
-1 OUT_SALE Stock sold to customer
-2 OUT_REFUND Stock refunded to supplier
-3 OUT_BREAK Stock broken/damaged
-4 OUT_MOVEMENT Stock moved to another location
-5 OUT_SAMPLE Stock used as sample
-6 OUT_FREE Stock given for free
-7 OUT_USED Stock used internally
-8 OUT_SUBTRACT Stock adjustment (deduction)

Note: The stock parameter should be positive for additions and negative for deductions. The reason code helps categorize the movement for reporting purposes.



Call Examples in Different Languages


# Add stock from purchase (IN_PURCHASE = 1)
curl -X POST 'https://easycms.fi/public_api/set_products_stock' \
-H 'Authorization1: TOKEN' \
-H 'Content-Type: application/json' \
-d '{
  "username": "USERNAME",
  "password": "PASSWORD",
  "account": "ACCOUNT_ID",
  "pid": 12345,
  "location_id": 1,
  "shelf_id": 5,
  "stock": 100,
  "reason": 1,
  "best_before_date": "2025-12-31",
  "price_buy": 15.50,
  "supplier_id": 10,
  "reference": "PO-2024-001"
}'

# Deduct stock from sale (OUT_SALE = -1)
curl -X POST 'https://easycms.fi/public_api/set_products_stock' \
-H 'Authorization1: TOKEN' \
-H 'Content-Type: application/json' \
-d '{
  "username": "USERNAME",
  "password": "PASSWORD",
  "account": "ACCOUNT_ID",
  "pid": 12345,
  "location_id": 1,
  "shelf_id": 5,
  "stock": -5,
  "reason": -1,
  "price": 25.00,
  "reference": "ORD-2024-1234"
}'

$curl = curl_init();
curl_setopt_array($curl, array(
  CURLOPT_URL => "https://easycms.fi/public_api/set_products_stock",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST => true,
  CURLOPT_POSTFIELDS => json_encode([
    'username' => 'USERNAME', 
    'password' => 'PASSWORD', 
    'account' => 'ACCOUNT_ID',
    'pid' => 12345,
    'location_id' => 1,
    'shelf_id' => 5,
    'stock' => 100,           // positive for addition
    'reason' => 1,            // IN_PURCHASE
    'best_before_date' => '2025-12-31',
    'price_buy' => 15.50,
    'supplier_id' => 10,
    'reference' => 'PO-2024-001'
  ]),
  CURLOPT_HTTPHEADER => array(
    "Authorization1: TOKEN",
    "Content-Type: application/json"
  ),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;

import requests
import json

url = "https://easycms.fi/public_api/set_products_stock"
headers = {
    "Authorization1": "TOKEN",
    "Content-Type": "application/json"
}
payload = {
    'username': 'USERNAME', 
    'password': 'PASSWORD', 
    'account': 'ACCOUNT_ID',
    'pid': 12345,
    'location_id': 1,
    'shelf_id': 5,
    'stock': 100,           # positive for addition
    'reason': 1,            # IN_PURCHASE
    'best_before_date': '2025-12-31',
    'price_buy': 15.50,
    'supplier_id': 10,
    'reference': 'PO-2024-001'
}
response = requests.post(url, headers=headers, data=json.dumps(payload))
print(response.text)

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

HttpClient client = HttpClient.newHttpClient();
String jsonPayload = """
    {
        "username": "USERNAME",
        "password": "PASSWORD",
        "account": "ACCOUNT_ID",
        "pid": 12345,
        "location_id": 1,
        "shelf_id": 5,
        "stock": 100,
        "reason": 1,
        "best_before_date": "2025-12-31",
        "price_buy": 15.50,
        "supplier_id": 10,
        "reference": "PO-2024-001"
    }
    """;
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://easycms.fi/public_api/set_products_stock"))
    .headers("Authorization1", "TOKEN", "Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
    .build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());

const https = require('https');

const payload = JSON.stringify({
  username: 'USERNAME', 
  password: 'PASSWORD', 
  account: 'ACCOUNT_ID',
  pid: 12345,
  location_id: 1,
  shelf_id: 5,
  stock: 100,
  reason: 1,
  best_before_date: '2025-12-31',
  price_buy: 15.50,
  supplier_id: 10,
  reference: 'PO-2024-001'
});

const options = {
  hostname: 'easycms.fi',
  path: '/public_api/set_products_stock',
  method: 'POST',
  headers: {
    'Authorization1': 'TOKEN',
    'Content-Type': 'application/json',
    'Content-Length': Buffer.byteLength(payload)
  }
};

const req = https.request(options, (res) => {
  let data = '';
  res.on('data', (chunk) => { data += chunk; });
  res.on('end', () => { console.log(data); });
});
req.on('error', (e) => { console.error(e); });
req.write(payload);
req.end();

import okhttp3.OkHttpClient
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody

fun main() {
    val client = OkHttpClient()

    val jsonPayload = """
        {
            "username": "USERNAME",
            "password": "PASSWORD",
            "account": "ACCOUNT_ID",
            "pid": 12345,
            "location_id": 1,
            "shelf_id": 5,
            "stock": 100,
            "reason": 1,
            "best_before_date": "2025-12-31",
            "price_buy": 15.50,
            "supplier_id": 10,
            "reference": "PO-2024-001"
        }
    """.trimIndent()

    val requestBody = jsonPayload.toRequestBody("application/json".toMediaType())

    val request = Request.Builder()
        .url("https://easycms.fi/public_api/set_products_stock")
        .post(requestBody)
        .addHeader("Authorization1", "TOKEN")
        .build()

    client.newCall(request).execute().use { response ->
        if (!response.isSuccessful) throw IOException("Unexpected code $response")
        println(response.body?.string())
    }
}




Handling Endpoint Results

When you make a request to the endpoint, you receive a JSON response containing various keys and values. Here's an explanation of the response keys and their meanings:


{
  "status": "success",
  "message": "Stock updated successfully",
  "data": {
    "pid": 12345,
    "location_id": 1,
    "shelf_id": 5,
    "previous_stock": 100.0,
    "stock_change": 50.0,
    "new_stock": 150.0,
    "reason": 1,
    "reason_text": "IN_PURCHASE",
    "stockdiary_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "best_before_date": "2025-12-31"
  }
}
    

- `status`: The operation status ("success" or "error")
- `message`: Human-readable message describing the result
- `data`: Object containing the operation details:
  - `pid`: The product ID that was updated
  - `location_id`: The location ID
  - `shelf_id`: The shelf ID
  - `previous_stock`: The stock quantity before this update
  - `stock_change`: The amount that was added (positive) or deducted (negative)
  - `new_stock`: The current stock quantity after this update
  - `reason`: The numeric reason code used
  - `reason_text`: The human-readable reason text
  - `stockdiary_id`: The UUID of the created stockdiary entry
  - `best_before_date`: The best before date (if provided)
    

When creating a new stock entry (no previous stock existed):

{
  "status": "success",
  "message": "Stock entry created successfully",
  "data": {
    "pid": 12345,
    "location_id": 2,
    "shelf_id": 3,
    "previous_stock": 0,
    "stock_change": 100.0,
    "new_stock": 100.0,
    "reason": 1,
    "reason_text": "IN_PURCHASE",
    "stockdiary_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "best_before_date": "2025-12-31",
    "is_new_entry": true
  }
}
      

Error Handling

Here are the possible error messages and their meanings:

Error Code HTTP Status Description
MISSING_PID 400 The pid parameter is required
MISSING_LOCATION_ID 400 The location_id parameter is required
MISSING_SHELF_ID 400 The shelf_id parameter is required
MISSING_STOCK 400 The stock parameter is required
MISSING_REASON 400 The reason parameter is required
INVALID_PID 400 The pid must be a valid integer
INVALID_LOCATION_ID 400 The location_id must be a valid integer
INVALID_SHELF_ID 400 The shelf_id must be a valid integer
INVALID_STOCK 400 The stock must be a valid number
INVALID_REASON 400 The reason code is not valid
PRODUCT_NOT_FOUND 404 The product with the specified pid does not exist
LOCATION_NOT_FOUND 404 The specified location does not exist
SHELF_NOT_FOUND 404 The specified shelf does not exist
STOCK_ENTRY_NOT_FOUND 400 No stock entry exists for this location/shelf (for deductions)
INSUFFICIENT_STOCK 400 Not enough stock to complete the deduction
CANNOT_DEDUCT_FROM_NONEXISTENT 400 Cannot deduct stock from a location that has no stock
UN-AUTHORIZED 401 Incorrect username or password
this_account_does_not_exist_or_your_credentials_do_not_match_this_account 401 The account doesn't exist or mismatched credentials

Error Response Example

{
  "status": "error",
  "error_code": "INSUFFICIENT_STOCK",
  "message": "Insufficient stock. Current stock: 50, Requested deduction: 100"
}

Business Logic

Stock Addition Flow

  1. Validate all required parameters
  2. Check if product, location, and shelf exist
  3. If stock entry exists: Add to current stock
  4. If stock entry does not exist: Create new entry with the provided quantity
  5. Create stockdiary entry with reason code
  6. Return success response with updated stock details

Stock Deduction Flow

  1. Validate all required parameters
  2. Check if product, location, and shelf exist
  3. Verify stock entry exists for this location/shelf
  4. Verify sufficient stock is available
  5. Deduct from current stock
  6. Create stockdiary entry with reason code (negative quantity)
  7. Return success response with updated stock details

Stockdiary Entry

Every stock modification creates an entry in tbl_stockdiary with:

  • pid: Product ID
  • location_id: Location ID
  • shelf_id: Shelf ID
  • stock: Quantity change (positive or negative)
  • reason: Reason code
  • datenew: Current timestamp
  • price: Sell price (if provided)
  • price_buy: Buy price (if provided)
  • supplier_id: Supplier ID (if provided)
  • appuser: API user reference
  • best_before_date: Best before date (if provided)

Best Practices

  1. Always provide a meaningful reason code - This helps with inventory reporting and auditing
  2. Use the reference parameter - Include order IDs, invoice numbers, or other references for traceability
  3. Check stock before deductions - Use get_products_stock to verify available stock
  4. Handle insufficient stock errors - Implement proper error handling for the INSUFFICIENT_STOCK error
  5. Track best before dates - Provide best_before_date for perishable items
  6. Include pricing information - Provide price and price_buy for accurate stock valuation reports

Common Use Cases

Receiving Stock from Purchase Order

POST /set_products_stock
{
  "pid": 12345,
  "location_id": 1,
  "shelf_id": 5,
  "stock": 100,
  "reason": 1,
  "price_buy": 15.50,
  "supplier_id": 10,
  "reference": "PO-2024-001"
}

Processing a Sale

POST /set_products_stock
{
  "pid": 12345,
  "location_id": 1,
  "shelf_id": 5,
  "stock": -5,
  "reason": -1,
  "price": 25.00,
  "reference": "ORD-2024-1234"
}

Handling Damaged Goods

POST /set_products_stock
{
  "pid": 12345,
  "location_id": 1,
  "shelf_id": 5,
  "stock": -10,
  "reason": -3,
  "reference": "DAMAGE-REPORT-2024-056"
}

Moving Stock Between Locations

First, deduct from source location:

POST /set_products_stock
{
  "pid": 12345,
  "location_id": 1,
  "shelf_id": 5,
  "stock": -50,
  "reason": -4,
  "reference": "TRANSFER-2024-001-OUT"
}

Then, add to destination location:

POST /set_products_stock
{
  "pid": 12345,
  "location_id": 2,
  "shelf_id": 3,
  "stock": 50,
  "reason": 3,
  "reference": "TRANSFER-2024-001-IN"
}