Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

libciss

Python client for CISS applications. Currently supports CISS Control, with helpers for authentication and pré-carga / separação (warehouse pre-load picking).

Status: 0.1.0a2 (alpha) — the API surface may change.

Requirements

  • Python 3.10+ (uses X | None type syntax and dataclass(slots=True))
  • requests>=2.31.0

Installation

pip install libciss

Everything is imported from the single top-level libciss package:

from libciss import CISSClient

Upgrading from 0.1.0a1: that release was unusable — it shipped its modules under a top-level services package (which collided with any application that had its own services/), left client and exceptions out of the distribution entirely, and imported from a src package that was never packaged. 0.1.0a2 moves everything under libciss, so from src import ... becomes from libciss import ....

Quick start

from datetime import datetime

from libciss import CISSClient
from libciss.services.cisscontrol.models import PreCargaSeparacaoSubmission

client = CISSClient("https://your-ciss-host.example.com")

# Authenticate — stores the bearer token on the shared session
user = client.cisscontrol.auth.login("username", "password")
print(user.id_empresa, user.razao_social)

# List pre-loads
preloads = client.cisscontrol.precarga_separacao.get_all(
    id_empresa=user.id_empresa,
    num_dias_sync=30,
)

# Fetch the line items of one pre-load
items = client.cisscontrol.precarga_separacao.items(
    id_empresa=user.id_empresa,
    id_precarga=preloads[0].id_pre_carga,
    id_usuario=user.id_user,
)

# Report a separated item back to CISS
item = items[0]
submission = PreCargaSeparacaoSubmission(
    id_usuario=user.id_user,
    id_empresa=item.id_empresa,
    id_documento=item.id_documento,
    id_pre_carga=item.id_pre_carga,
    id_produto=item.id_produto,
    id_subproduto=item.id_subproduto,
    id_local_retirada=item.id_local_retirada,
    ds_local_retirada=item.ds_local_retirada,
    num_sequencia=item.num_sequencia,
    qtd_produto=item.qtd_produto,
    fg_separado="S",
    qtd_contada=item.qtd_produto,
    dt_hr_inicio=datetime.now(),
)

response = client.cisscontrol.precarga_separacao.submit([submission])

client.cisscontrol.auth.logout()

Architecture

One CISSClient owns a single requests.Session. Services never hold their own transport — they call back into client.request(...), so the token set at login is automatically applied to every later call.

CISSClient(base_url)
├── session          # requests.Session (shared headers / cookies)
├── request(...)     # low-level HTTP helper, raises on 4xx/5xx
└── cisscontrol      # CISSControl
      ├── auth                 # AuthenticationService
      │     ├── login / logout
      │     ├── token / authenticated
      │     └── user → LoginResponse | None
      └── precarga_separacao   # PreCargaSeparacaoService
            ├── get_all(...)  → list[PreCargaSeparacao]
            ├── items(...)    → list[PreCargaSeparacaoItem]
            └── submit(...)   → requests.Response

API reference

CISSClient

Entry point for all services.

CISSClient(base_url: str)
Attribute / method Description
base_url API root URL; a trailing / is stripped
session Shared requests.Session — holds the Authorization header after login
cisscontrol CISSControl service namespace
request(method, endpoint, **kwargs) Sends method to base_url + endpoint, calls raise_for_status(), returns the Response

endpoint is concatenated onto base_url verbatim, so it must start with /. Any extra keyword argument is forwarded to requests.Session.request (json=, params=, timeout=, …).

Session-level configuration goes on client.session:

client.session.verify = "/path/to/ca-bundle.pem"
client.session.proxies = {"https": "http://proxy.internal:3128"}

Note that requests.Session has no timeout attribute — setting one has no effect. To bound a request, pass timeout through request(), or mount your own adapter:

client.request("GET", "/ciss-control/precarga_separacao", params={...}, timeout=30)

The service methods below do not currently expose a timeout parameter.


Authentication — client.cisscontrol.auth

login(username, password) -> LoginResponse

POST /ciss-control/login

Sends {"username": ..., "password": ...} as JSON. On success it writes the returned tokenBearer into session.headers["Authorization"] and caches the parsed payload on auth.user.

The token is stored exactly as the API returns it — the library does not prepend a Bearer prefix. razao_social is right-stripped of trailing whitespace; all other fields are passed through unchanged.

logout() -> None

Removes the Authorization header and clears auth.user. Client-side only — no request is sent, so the token stays valid server-side until it expires.

Properties

Property Type Description
token str | None Current Authorization header value, or None
authenticated bool True when a token is set
user LoginResponse | None Payload of the last successful login

authenticated only reports whether a token is present — it does not validate or check expiry. An expired token still reads as True, and the call then fails with requests.HTTPError (401).

requires_login decorator

libciss.services.cisscontrol.auth.requires_login guards service methods. It reads self.client.cisscontrol.auth.authenticated and raises NotAuthenticatedError when no token is set, so it can only decorate methods of a service that holds a client attribute.

from libciss.services.cisscontrol.auth import requires_login

class MyService:
    def __init__(self, client):
        self.client = client

    @requires_login
    def do_something(self):
        return self.client.request("GET", "/ciss-control/something")

Pré-carga separação — client.cisscontrol.precarga_separacao

Every method is decorated with @requires_login and raises NotAuthenticatedError when called before login().

get_all(*, id_empresa, ...) -> list[PreCargaSeparacao]

GET /ciss-control/precarga_separacao

Keyword-only. All filter parameters are always sent, empty strings included.

Parameter Type Default API query key
id_empresa int required idEmpresa
num_dias_sync int 99 numDiasSync
ds_separacao str "" dsSeparacao
ds_precarga str "" dsPreCarga
ds_ponto_retirada str "" dsPontoRetirada
dt_inicio str "" dtInicio
ds_pre_separacao str "" dsPreSeparacao
dt_fim str "" dtFim

Returns [] on HTTP 204, otherwise one PreCargaSeparacao per element of the JSON array.

Date filters (dt_inicio / dt_fim) are typed str and forwarded verbatim — the library does not format date objects for you.

A large num_dias_sync can make this endpoint hang; the source carries a FIXME about it. Prefer a bounded window (e.g. num_dias_sync=30).

items(*, id_empresa, id_precarga, id_usuario, ...) -> list[PreCargaSeparacaoItem]

GET /ciss-control/precarga_separacao/itens

Parameter Type Default API query key
id_empresa int required idEmpresa
id_precarga int required idPreCarga
id_usuario int required idUsuario
ds_separacao str "" dsSeparacao
ds_precarga str "" dsPreCarga
ds_ponto_retirada str "" dsPontoRetirada
dt_inicio str "" dtInicio
ds_pre_separacao str "" dsPreSeparacao
dt_fim str "" dtFim

Unlike get_all, this method has no 204 guard — an empty response body raises requests.exceptions.JSONDecodeError rather than returning [].

submit(items) -> requests.Response

POST /ciss-control/precarga_separacao

Takes a positional list[PreCargaSeparacaoSubmission] and posts [item.to_dict() for item in items] as a JSON array. Returns the raw requests.Response — the body is not parsed, so inspect response.status_code / response.json() yourself.


Data models

<<<<<<< HEAD Models live in libciss.services.cisscontrol.models and use snake_case in Python, mapped to the API’s camelCase.

Models live in libciss.services.cisscontrol.models. They are @dataclass(slots=True), use snake_case in Python, and map to the API's camelCase. Response models expose from_dict; the request model exposes to_dict.

Being slotted dataclasses, they take positional or keyword arguments, compare by value, and reject attributes that are not declared fields.

135ef23cd6ec3d4a8f50ff4eda7518ff653b2de5

LoginResponse

Built by auth.login().

Field Type API field
token_bearer str tokenBearer
id_user int idUser
id_grupo int idGrupo
username str username
enabled bool enabled
id_empresa int idEmpresa
cnpj_empresa str cnpjEmpresa
razao_social str razaoSocial (right-stripped)
permissions list[str] permissionsList

PreCargaSeparacao

Field Type API field
id_empresa int idEmpresa
id_pre_carga int idPreCarga
descricao str descricao
dt_movimento date dtMovimento, parsed with date.fromisoformat

from_dict requires every key above; a missing one raises KeyError, and a dtMovimento that is not ISO-8601 raises ValueError.

PreCargaSeparacaoItem

Field Type API field
id_pre_carga int idPreCarga
id_empresa int idEmpresa
id_documento int idDocumento
id_produto int idProduto
id_subproduto int idSubproduto
ds_local_retirada str dsLocalRetirada
num_sequencia int numSequencia
id_lote str | None idLote — optional, None when absent
qtd_produto float qtdProduto
fg_separado str fgSeparado
qtd_contada_divergente bool qtdContadaDivergente
id_local_retirada int idLocalRetirada

id_lote is the only tolerated-missing key; all others raise KeyError when absent.

PreCargaSeparacaoSubmission

Request DTO for submit(). Every field is required — there are no defaults, so all thirteen must be supplied.

Field Type JSON key
id_usuario int idUsuario
id_empresa int idEmpresa
id_documento int idDocumento
id_pre_carga int idPreCarga
id_produto int idProduto
id_subproduto int idSubproduto
id_local_retirada int idLocalRetirada
ds_local_retirada str dsLocalRetirada
num_sequencia int numSequencia
qtd_produto float qtdProduto
fg_separado str fgSeparado
qtd_contada float qtdContada
dt_hr_inicio datetime.datetime dtHrInicio, serialized with .isoformat()

to_dict() calls .isoformat() on dt_hr_inicio, so it must be a datetime — passing a string raises AttributeError. Nothing is validated beyond that: fg_separado is sent as-is ("S" / "N"), and a qtd_contada that differs from qtd_produto is accepted and resolved server-side.


Exceptions

Exception
└── CISSClientError
    └── AuthenticationError
        └── NotAuthenticatedError

Defined in libciss.exceptions.

Exception Raised when
CISSClientError Base class for library errors — catch this to catch them all
AuthenticationError Auth-related failures
NotAuthenticatedError A @requires_login method is called with no token set

Transport and HTTP failures are not wrapped: client.request() calls raise_for_status(), so 4xx/5xx surface as requests.HTTPError, and connection/timeout problems surface as the corresponding requests.RequestException subclasses.

import requests

from libciss.exceptions import NotAuthenticatedError

try:
    preloads = client.cisscontrol.precarga_separacao.get_all(id_empresa=1)
except NotAuthenticatedError:
    client.cisscontrol.auth.login("user", "pass")
except requests.HTTPError as e:
    print(e.response.status_code, e.response.text)

A 401 from an expired token arrives as requests.HTTPError, not NotAuthenticatedError — the local check only sees that a token exists. To recover, call login() again.


Endpoint map

HTTP Path Client method
POST /ciss-control/login auth.login
GET /ciss-control/precarga_separacao precarga_separacao.get_all
GET /ciss-control/precarga_separacao/itens precarga_separacao.items
POST /ciss-control/precarga_separacao precarga_separacao.submit

Known issues

These are current limitations of the code, not of the documentation.

Issue Impact
get_all may hang on wide sync windows (FIXME in precarga_separacao.py:50) Keep num_dias_sync small
items does not handle HTTP 204 Empty result raises JSONDecodeError instead of returning []
logout() is client-side only The token remains valid on the server
No timeouts are set on any request A stalled server can block indefinitely
No test suite Changes are unverified

Glossary

Portuguese terms used by the CISS API, kept untranslated in the model field names.

Term Meaning
Empresa Company / store (id_empresa, CNPJ, razão social)
Pré-carga Pre-load batch for warehouse picking
Separação Product picking / separation
Local de retirada Pick-up location
Documento Related document ID
Produto / Subproduto Product and subproduct IDs
Lote Lot / batch code (optional)
Qtd produto Expected quantity on the pre-load
Qtd contada Quantity actually counted during separation
fgSeparado Separation flag, an API string ("S" / "N")
numDiasSync Days of history to sync (default 99)

License

MIT © Redson

Metadata

Release files for libciss 0.1.0a2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for libciss 0.1.0a2
File Size Uploaded Provenance Actions
libciss-0.1.0a2.tar.gz 15.5 kB Without provenance Details for libciss-0.1.0a2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for libciss 0.1.0a2
File Interpreter ABI Platform Actions
libciss-0.1.0a2-py3-none-any.whl Python 3 none any Details for libciss-0.1.0a2-py3-none-any.whl

Total release size: 29.7 kB

Release files / libciss-0.1.0a2.tar.gz

Download URL libciss-0.1.0a2.tar.gz
Size 15.5 kB
Tags Source
SHA-256 checksum
How to use checksums
2fc24ccea62458df71bd55fd5d267efcfe4b68f12548ec92f66575b209be1da1
BLAKE2b-256 checksum
How to use checksums
2fb4589dbaab7e318167f2f5320ade7c613be5ac951adeef5047dd238a842efb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Release files / libciss-0.1.0a2-py3-none-any.whl

Download URL libciss-0.1.0a2-py3-none-any.whl
Size 14.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f1b8e1d83fde8354f43e2ca2b5962220ac46fed9f63904b1a5e6331c7f7f3c17
BLAKE2b-256 checksum
How to use checksums
8d084b3c643c972b78ec5cd537d0620f22716f0fa092486a7e6573d9b4accbb4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Security for libciss 0.1.0a2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page