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 | Nonetype syntax anddataclass(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-levelservicespackage (which collided with any application that had its ownservices/), leftclientandexceptionsout of the distribution entirely, and imported from asrcpackage that was never packaged.0.1.0a2moves everything underlibciss, sofrom src import ...becomesfrom 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_synccan make this endpoint hang; the source carries aFIXMEabout 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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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.