Léim ar ábhar
ShopentlyLearn

Coigeartaigh stoc

Coigeartaigh stoc. Paraiméadair: STOCK_ID, LOCATION_ID. Samplaí cóid: cURL, PHP, Python.

Paraiméadair

  • SHOPENTLY_SHOP_URL — URL na hacmhainne.
  • SHOPENTLY_ACCESS_TOKEN — Dintiúr Bearer gearrthéarmach.
  • STOCK_ID — Aitheantóir taifid stoic.
  • LOCATION_ID — Aitheantóir suímh stórais.
  • NEW_ON_HAND — Cainníocht fhisiciúil stoic.
  • ADJUSTMENT_REASON — Cúis taifeadta don athrú.
  • IDEMPOTENCY_KEY — Aitheantóir uathúil d'oibríocht loighciúil scríbhneoireachta amháin.

PHP 8.3 (64-bit, ext-curl, JSON) · Python 3.12 + requests · cURL 8 + jq 1.7.

Sampla iomlán

cURL

#!/bin/sh
# POSIX shell, curl 8+, jq 1.7. Credentials stay in environment variables.
set -eu
: "${SHOPENTLY_SHOP_URL:?}"
base=${SHOPENTLY_SHOP_URL%/}
case "$base" in https://*) ;; *) echo 'Use an HTTPS origin' >&2; exit 1;; esac
authority=${base#https://}
case "$authority" in ''|*/*|*\?*|*\#*|*@*) echo 'Use an HTTPS origin' >&2; exit 1;; esac
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT HUP INT TERM
chmod 700 "$work"
encode() { printf '%s' "$1" | jq -sRr @uri; }
api() {
    method=$1; path=$2; body=${3-}; key=${4-}
    scheme=Bearer; credential=${SHOPENTLY_ACCESS_TOKEN-}
    if [ "$path" = /api/v1/auth/token ]; then scheme=ApiKey; credential=${SHOPENTLY_API_KEY-}; fi
    [ -n "$credential" ] || { echo 'Missing credential' >&2; return 1; }
    set -- --disable --globoff --silent --show-error --proto '=https' --connect-timeout 5 --max-time 30 \
        --request "$method" --header "Authorization: $scheme $credential" \
        --dump-header "$work/headers" --output "$work/body" --write-out '%{http_code}'
    if [ -n "$key" ]; then set -- "$@" --header "Idempotency-Key: $key"; fi
    if [ -n "$body" ]; then set -- "$@" --header 'Content-Type: application/json' --data-raw "$body"; fi
    status=$(curl "$@" --url "$base$path") || return 1
    case "$status" in
        2??) if [ -s "$work/body" ]; then jq -e . "$work/body"; else printf 'null\n'; fi ;;
        *) printf 'HTTP %s\n' "$status" >&2
           jq -c '.error | {code, fields}' "$work/body" >&2 2>/dev/null || true
           return 1 ;;
    esac
}

: "${STOCK_ID:?}" "${LOCATION_ID:?}" "${NEW_ON_HAND:?}" "${ADJUSTMENT_REASON:?}" "${IDEMPOTENCY_KEY:?}"
case "$NEW_ON_HAND" in ''|*[!0-9]*) echo 'Use a nonnegative integer' >&2; exit 1;; esac
path="/api/v1/inventory/stock/$(encode "$STOCK_ID")"
stock=$(api GET "$path")
printf '%s' "$stock" | jq -e --arg id "$LOCATION_ID" 'any(.data.allocations[]; .location_id == $id)' >/dev/null
body=$(printf '%s' "$stock" | jq -c --arg id "$LOCATION_ID" --argjson quantity "$NEW_ON_HAND" --arg reason "$ADJUSTMENT_REASON" '{
    expected_version: .data.version, reason: $reason, locations: [.data.allocations[] |
    {location_id, on_hand: (if .location_id == $id then $quantity else .on_hand end)}]}')
api PATCH "$path" "$body" "$IDEMPOTENCY_KEY"

PHP

<?php
declare(strict_types=1);
// PHP 8.3+, 64-bit, ext-curl. Credentials stay in environment variables.
function env(string $name): string {
    $value = getenv($name);
    if ($value === false || $value === '' || strpbrk($value, "\r\n") !== false) {
        throw new RuntimeException('Invalid environment variable: ' . $name);
    }
    return $value;
}
$base = rtrim(env('SHOPENTLY_SHOP_URL'), '/');
$origin = parse_url($base);
if (!$origin || ($origin['scheme'] ?? '') !== 'https' || empty($origin['host']) ||
    isset($origin['user']) || isset($origin['pass']) || isset($origin['path']) ||
    isset($origin['query']) || isset($origin['fragment']) || PHP_INT_SIZE !== 8) {
    throw new RuntimeException('Use a 64-bit PHP runtime and an HTTPS origin');
}
class ApiError extends RuntimeException {
    public function __construct(public int $status, public array $headers, public array $error) {
        parent::__construct('HTTP ' . $status . '; code=' . ($error['code'] ?? '') .
            '; request_id=' . ($headers['x-request-id'] ?? ''));
    }
}
function api(string $method, string $path, array $query = [], ?array $body = null, ?string $key = null, bool $apiKey = false): ?array {
    global $base;
    $credential = env($apiKey ? 'SHOPENTLY_API_KEY' : 'SHOPENTLY_ACCESS_TOKEN');
    $headers = ['Authorization: ' . ($apiKey ? 'ApiKey ' : 'Bearer ') . $credential];
    if ($key !== null) $headers[] = 'Idempotency-Key: ' . $key;
    if ($body !== null) $headers[] = 'Content-Type: application/json';
    $url = $base . $path . ($query ? '?' . http_build_query($query, '', '&', PHP_QUERY_RFC3986) : '');
    $responseHeaders = [];
    $h = curl_init($url);
    curl_setopt_array($h, [
        CURLOPT_CUSTOMREQUEST => $method, CURLOPT_HTTPHEADER => $headers,
        CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => false,
        CURLOPT_PROTOCOLS => CURLPROTO_HTTPS, CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2, CURLOPT_CONNECTTIMEOUT => 5, CURLOPT_TIMEOUT => 30,
        CURLOPT_HEADERFUNCTION => static function ($h, string $line) use (&$responseHeaders): int {
            if (str_contains($line, ':')) {
                [$name, $value] = explode(':', $line, 2);
                $responseHeaders[strtolower(trim($name))] = trim($value);
            }
            return strlen($line);
        },
    ]);
    if ($body !== null) {
        $json = $apiKey && $body === [] ? '{}' : json_encode($body, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
        curl_setopt($h, CURLOPT_POSTFIELDS, $json);
    }
    $raw = curl_exec($h);
    $status = curl_getinfo($h, CURLINFO_RESPONSE_CODE);
    $error = curl_error($h);
    curl_close($h);
    if ($raw === false) throw new RuntimeException('Transport error: ' . $error);
    if ($status < 200 || $status >= 300) {
        $data = json_decode($raw, true);
        throw new ApiError($status, $responseHeaders, is_array($data) ? ($data['error'] ?? []) : []);
    }
    return $raw === '' ? null : json_decode($raw, true, 512, JSON_THROW_ON_ERROR | JSON_BIGINT_AS_STRING);
}
function output(mixed $value): void {
    echo json_encode($value, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE), PHP_EOL;
}
$path = '/api/v1/inventory/stock/' . rawurlencode(env('STOCK_ID'));
$stock = api('GET', $path)['data'];
$locationId = env('LOCATION_ID');
if (!in_array($locationId, array_column($stock['allocations'], 'location_id'), true)) throw new RuntimeException('Choose an assigned location');
$quantity = filter_var(env('NEW_ON_HAND'), FILTER_VALIDATE_INT);
if ($quantity === false || $quantity < 0) throw new RuntimeException('Use a nonnegative integer');
$body = ['expected_version' => $stock['version'], 'reason' => env('ADJUSTMENT_REASON'),
    'locations' => array_map(fn($a) => ['location_id' => $a['location_id'],
        'on_hand' => $a['location_id'] === $locationId ? $quantity : $a['on_hand']], $stock['allocations'])];
output(api('PATCH', $path, body: $body, key: env('IDEMPOTENCY_KEY')));

Python

"""Run with Python 3.12 and requests. Credentials stay in environment variables."""
import json
import os
import random
import time
from urllib.parse import quote, urlencode, urlsplit
import requests

def env(name):
    value = os.environ[name]
    if not value or "\r" in value or "\n" in value:
        raise ValueError("Invalid environment variable: " + name)
    return value

base = env("SHOPENTLY_SHOP_URL").rstrip("/")
origin = urlsplit(base)
if origin.scheme != "https" or not origin.hostname or origin.username or origin.password or origin.path or origin.query or origin.fragment:
    raise ValueError("SHOPENTLY_SHOP_URL must be an HTTPS origin")

class ApiError(RuntimeError):
    def __init__(self, response):
        self.status = response.status_code
        self.request_id = response.headers.get("X-Request-ID", "")
        self.retry_after = response.headers.get("Retry-After", "")
        try:
            self.error = response.json().get("error", {})
        except (ValueError, AttributeError):
            self.error = {}
        super().__init__(f"HTTP {self.status}; code={self.error.get('code', '')}; request_id={self.request_id}")

def api(method, path, *, query=None, body=None, key=None, api_key=False):
    credential = env("SHOPENTLY_API_KEY" if api_key else "SHOPENTLY_ACCESS_TOKEN")
    headers = {"Authorization": ("ApiKey " if api_key else "Bearer ") + credential}
    if key:
        headers["Idempotency-Key"] = key
    if body is not None:
        headers["Content-Type"] = "application/json"
    response = requests.request(method, base + path, params=query, headers=headers,
        data=json.dumps(body, ensure_ascii=False, allow_nan=False).encode() if body is not None else None,
        timeout=(5, 30), allow_redirects=False)
    if not 200 <= response.status_code < 300:
        raise ApiError(response)
    return response.json() if response.content else None

def segment(value):
    return quote(value, safe="")

path = "/api/v1/inventory/stock/" + segment(env("STOCK_ID"))
stock = api("GET", path)["data"]
location_id = env("LOCATION_ID")
if location_id not in {a["location_id"] for a in stock["allocations"]}:
    raise ValueError("Choose a location already assigned to this stock record")
quantity = int(env("NEW_ON_HAND"))
if quantity < 0:
    raise ValueError("NEW_ON_HAND must be nonnegative")
body = {"expected_version": stock["version"], "reason": env("ADJUSTMENT_REASON"),
        "locations": [{"location_id": a["location_id"],
                       "on_hand": quantity if a["location_id"] == location_id else a["on_hand"]}
                      for a in stock["allocations"]]}
print(json.dumps(api("PATCH", path, body=body, key=env("IDEMPOTENCY_KEY"))))

Conas a oibríonn sé

Déanann taifid stoic idirdhealú idir an chainníocht fhisiciúil on_hand, áirithintí orduithe, áirithintí láimhe agus an chainníocht ar díol. Úsáid an t-aitheantóir stoic agus an leagan a fhaightear; is spriocanna ar leith iad stoc táirge agus stoc athraithigh.

Ciallaíonn sellable_limit de -1 neamhtheoranta, agus ciallaíonn 0 nach bhfuil díol ar fáil. Taispeántar infhaighteacht neamhtheoranta mar null in available. Ná caith le null mar nialas. Tá oibríochtaí agus ceadanna ar leith ag coigeartuithe stoic, teorainneacha díolacháin agus áirithintí láimhe.