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.0a3(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
├── pre_carga # PreCargaService — conferência
│ ├── get_all(...) → list[PreCarga]
│ ├── items(...) → list[PreCargaItem]
│ ├── submit(...) → requests.Response
│ └── set_status(...) → requests.Response
└── precarga_separacao # PreCargaSeparacaoService — separação
├── get_all(...) → list[PreCargaSeparacao]
├── items(...) → list[PreCargaSeparacaoItem]
└── submit(...) → requests.Response
pre_carga and precarga_separacao are different CISS modules, not two views of one. pre_carga covers conferência — checking a picked order against its invoice. precarga_separacao covers separação — the warehouse picking itself. Their payloads share almost no fields.
How conferência works
Conferência is the checking step: a conferente walks a pré-carga that has already been picked, counts what is physically there, and reports the counted quantities back so the order can be released.
Everything in this section is derived from real traffic captured off the reference Flutter app (Dart/3.9) against a CISS backend on versao-backend: 8.14.2. Where the captures do not settle a question, this says so rather than guessing.
The flow
1. get_all(id_empresa, id_conferente)
└── the pré-cargas assigned to this conferente → list[PreCarga]
2. items(id_empresa, id_usuario, id_precarga)
└── the lines of one pré-carga, expected quantities → list[PreCargaItem]
every line comes back with qtd_contada == 0.0
3. ...the conferente counts, you set item.qtd_contada...
4. submit(pre_carga, itens)
└── the header + every counted line, in one request → 201 + persisted items
5. set_status(id_precarga, status_code, id_usuario_conferente)
└── closes / moves the pré-carga → 200 + echo of the request
6. get_all(...) again
└── refreshed queue
Steps 4 and 5 are two separate requests. Submitting the items does not change the status, and the library does not chain them for you — a crash between the two leaves the items recorded and the status untouched.
Step by step
| # | Call | Sends | Gets back |
|---|---|---|---|
| 1 | get_all |
idEmpresa, numDiasSync, dsPreCarga, dsPontoRetirada, idConferente as query keys |
Array of headers, each with clientes[] and cliForList[] |
| 2 | items |
idEmpresa, idUsuario, idPreCarga, dsPreCarga, dsPontoRetirada, isValidaEmpLog as query keys |
Array of lines — 9 fields, expected quantities only |
| 4 | submit |
{"preCargaList": [header], "itens": [...]} |
201 + the persisted lines, minus idLote / corredor / gondola / prateleira |
| 5 | set_status |
{"idPreCarga", "statusCode", "idUsuarioConferente"} |
200 + the same three fields echoed verbatim |
The counted quantity is yours to fill in
items() returns what the system expects. There is no qtdContada in the response — that value is the whole point of the conferência, so CISS has nothing to tell you about it yet.
The library models this by returning lines with qtd_contada == 0.0:
itens = client.cisscontrol.pre_carga.items(
id_empresa=user.id_empresa,
id_usuario=user.id_user,
id_precarga=pre_carga.id_pre_carga,
)
for item in itens:
item.qtd_contada = count_physically(item) # ← your job
client.cisscontrol.pre_carga.submit(pre_carga, itens)
Submitting a line you never touched reports that you counted zero of it. That is deliberate — the alternative default, seeding qtd_contada from qtd_produto, would silently claim a clean count for a pré-carga nobody checked.
Seven of the sixteen fields are also absent from the response and are filled in locally: idEmpresa and idUsuario come from the arguments you passed (CISS takes them as query parameters and does not echo them), while idLote, corredor, gondola and prateleira default to None. Where a real idLote is meant to come from is unresolved — see the open questions.
How a line is identified
A conferência line is identified by the sale it came from, not by the product:
idEmpresaVenda + idPlanilhaVenda + numSequenciaVenda
The capture proves this matters — idProduto 134640 appears twice in the same pré-carga, once under idPlanilhaVenda 21675444 and once under 21689087, with different quantities (90.0 and 30.0). Deduplicating by product would silently merge two legitimate lines.
Quantities
qtdProduto is what the system expects; qtdContada is what the conferente actually counted. Nothing is validated client-side, and a divergence is resolved server-side.
Worth knowing: in the captured session all seven lines matched exactly (2/2, 5/5, 1/1, 5/5, 90/90, 30/30, 30/30). A divergent count was never observed, so how the backend reacts to one is untested here.
Statuses
This is the part the capture does not settle. What it shows, literally:
| Where | Field | Value |
|---|---|---|
Header sent in preCargaList |
idStatus |
1 |
Header sent in preCargaList |
idConferente |
null |
set_status body |
statusCode |
0 |
| A different pré-carga in the later listing | idStatus |
0 |
| That same pré-carga | idConferente |
617 |
Only statusCode: 0 was ever sent, so the set of valid codes and their meanings is unknown. idStatus and statusCode are also not demonstrably the same scale — they are different field names in different payloads, and nothing in the capture links them. The library therefore passes status codes through untouched and neither validates nor enumerates them.
Open questions
Genuinely unresolved — do not treat any of these as documented behaviour.
| Question | Why it is open |
|---|---|
What do the statusCode values mean? |
Only 0 was ever sent |
Are idStatus and statusCode the same scale? |
Different fields, different payloads, no observed link |
What does isValidaEmpLog do? |
Only false was ever sent |
Where do idLote, corredor, gondola and prateleira come from? |
items never returns them, yet submit has a slot for each. Only null was ever sent |
Why does items say idUsuario where get_all says idConferente? |
Both were 617 in every capture, so nothing distinguishes them |
| Does submitting remove a pré-carga from the queue? | The submitted 43797 is absent from the later listing — but that listing filtered on idConferente=617 and the submitted header carried idConferente: null, so the filter alone explains it |
Does preCargaList accept more than one header? |
Only ever sent with one |
Is submit idempotent? |
Never sent twice |
Is idPedidoPreCarga ever non-null? |
Only observed as null |
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"}
Every request is bounded by a default (connect, read) timeout of (5.0, 30.0) seconds, exposed as libciss.client.DEFAULT_TIMEOUT. The read half is short deliberately: CISS Control tends to stall rather than answer when a company has no pre-load.
Change it per client, or per call:
client = CISSClient("https://ciss.example.com", timeout=(5, 60))
client.timeout = None # wait forever
client.request("GET", "/ciss-control/precarga_separacao", timeout=10) # this call only
A request that exceeds it raises requests.Timeout. Note that setting client.session.timeout does not work — requests.Session has no such attribute; use the constructor argument above.
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")
Conferência — client.cisscontrol.pre_carga
Every method is decorated with @requires_login and raises NotAuthenticatedError when called before login().
The flow is: get_all to find the pré-carga, items to fetch its lines, submit to send the counted quantities, set_status to close it.
user = client.cisscontrol.auth.login("username", "password")
pre_cargas = client.cisscontrol.pre_carga.get_all(
id_empresa=user.id_empresa,
id_conferente=user.id_user,
)
pre_carga = pre_cargas[0]
itens = client.cisscontrol.pre_carga.items(
id_empresa=user.id_empresa,
id_usuario=user.id_user,
id_precarga=pre_carga.id_pre_carga,
)
# Every line arrives with qtd_contada == 0.0 — record what you counted
for item in itens:
item.qtd_contada = item.qtd_produto
client.cisscontrol.pre_carga.submit(pre_carga, itens)
client.cisscontrol.pre_carga.set_status(
id_precarga=pre_carga.id_pre_carga,
status_code=0,
id_usuario_conferente=user.id_user,
)
get_all(*, id_empresa, id_conferente, ...) -> list[PreCarga]
GET /ciss-control/pre_carga
| Parameter | Query key | Default |
|---|---|---|
id_empresa |
idEmpresa |
required |
id_conferente |
idConferente |
required |
num_dias_sync |
numDiasSync |
99 |
ds_precarga |
dsPreCarga |
"" |
ds_ponto_retirada |
dsPontoRetirada |
"" |
All keys are sent on every call, empty strings included. Returns [] on HTTP 204 or an empty/non-array body.
items(*, id_empresa, id_usuario, id_precarga, ...) -> list[PreCargaItem]
GET /ciss-control/pre_carga/itens
| Parameter | Query key | Default |
|---|---|---|
id_empresa |
idEmpresa |
required |
id_usuario |
idUsuario |
required |
id_precarga |
idPreCarga |
required |
ds_precarga |
dsPreCarga |
"" |
ds_ponto_retirada |
dsPontoRetirada |
"" |
is_valida_emp_log |
isValidaEmpLog |
False |
The source of the itens you later hand to submit. Returns [] on HTTP 204 or an empty/non-array body.
id_empresa and id_usuario are copied onto every returned line — CISS takes them as query parameters and does not repeat them in the body. Note that this endpoint calls the user idUsuario while get_all calls the same person idConferente.
is_valida_emp_log is serialized as the string "true" / "false". Letting requests stringify the bool would send "False", which is not what the reference app sends. Its purpose is unknown and only False has been observed.
Every returned line has qtd_contada == 0.0 — the response has no counted quantity, because producing one is the point of the conferência. Overwrite it before submitting, or you will report a count of zero. See The counted quantity is yours to fill in.
submit(pre_carga, itens) -> requests.Response
POST /ciss-control/pre_carga
Posts {"preCargaList": [pre_carga.to_dict()], "itens": [i.to_dict() for i in itens]}. The wire format takes a list of headers, but only one pré-carga per call has ever been observed, so the method takes a single PreCarga and wraps it.
itens normally comes from items() with qtd_contada filled in.
Returns the raw requests.Response — 201 on success, carrying the persisted items as its body. The body is not parsed, so read response.json() yourself if you need it.
This does not close the pré-carga; call set_status afterwards.
set_status(*, id_precarga, status_code, id_usuario_conferente) -> requests.Response
POST /ciss-control/pre_carga/status
Posts {"idPreCarga": ..., "statusCode": ..., "idUsuarioConferente": ...}. Status codes are passed through as CISS defines them — the library neither validates nor enumerates them. CISS echoes the request body back, so there is nothing to parse.
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 or an empty/non-array body, 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 stall. It no longer hangs indefinitely — the default read timeout caps it — but a stalled call still costs you that timeout, so 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 |
Returns [] on HTTP 204 or an empty/non-array body, matching get_all.
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
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, request models expose to_dict, and PreCarga exposes both because CISS hands the same header back to you on submit.
Being slotted dataclasses, they take positional or keyword arguments, compare by value, and reject attributes that are not declared fields.
from_dict requires the keys that identify a record and tolerates the merely descriptive ones, because CISS omits those on some pre-loads and losing a description is not a reason to fail a whole batch. Each model below lists what it tolerates.
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 |
A missing or null razaoSocial becomes "" and a missing permissionsList becomes []. The remaining keys are required.
PreCarga
Returned by pre_carga.get_all() and accepted back by pre_carga.submit().
| Field | Type | API field |
|---|---|---|
id_empresa |
int |
idEmpresa |
id_pre_carga |
int |
idPreCarga |
descr_pre_carga |
str |
descrPreCarga (right-stripped) |
id_status |
int |
idStatus |
dt_alteracao |
datetime | None |
dtAlteracao |
num_nota |
int |
numNota |
id_orcamento |
int |
idOrcamento |
id_pedido_pre_carga |
int | None |
idPedidoPreCarga |
id_conferente |
int | None |
idConferente |
emp_alias |
str |
empAlias — GET only |
clientes |
list[Cliente] |
clientes — GET only |
cli_for_list |
list[CliFor] |
cliForList — GET only |
idEmpresa, idPreCarga, idStatus, numNota and idOrcamento are required — a missing one raises KeyError. A missing or null descrPreCarga / empAlias becomes "", the two nested arrays become [], and the remaining keys become None.
to_dict() emits only the nine non-GET-only keys, which is exactly what CISS accepts in preCargaList. The intended use is to take an object from get_all(), adjust id_status / id_conferente, and pass it straight to submit().
dtAlteracao keeps its fractional seconds rather than being truncated to a date, because it round-trips verbatim. CISS is inconsistent about the width — "2026-08-11T17:14:40.161" and "2026-07-20T19:33:12.966555" both occur — so parsing accepts either and serialization emits milliseconds, reproducing what the app sends.
Cliente
Element of PreCarga.clientes.
| Field | Type | API field |
|---|---|---|
id_clifor |
int |
idClifor |
nome_cliente |
str |
nomeCliente (right-stripped) |
idClifor is required; a missing or null nomeCliente becomes "".
CliFor
Element of PreCarga.cli_for_list. Overlaps with Cliente — CISS returns the same party under both keys with different field names — but adds the activity flag and the last-change timestamp.
| Field | Type | API field |
|---|---|---|
id_fornecedor |
int |
idFornecedor |
nome_fornecedor |
str |
nomeFornecedor (right-stripped) |
flag_inativo |
str |
flagInativo ("F" / "T"), not coerced to bool |
dt_alteracao |
datetime | None |
dtAlteracao |
idFornecedor is required; the rest fall back to "" / None.
PreCargaItem
Returned by pre_carga.items() and accepted back by pre_carga.submit(). A line is identified by the sale it came from — idEmpresaVenda / idPlanilhaVenda / numSequenciaVenda — not by the product, so the same idProduto legitimately appears more than once in one pré-carga.
| Field | Type | API field | Default |
|---|---|---|---|
id_usuario |
int |
idUsuario |
required |
id_empresa |
int |
idEmpresa |
required |
id_pre_carga |
int |
idPreCarga |
required |
id_empresa_venda |
int |
idEmpresaVenda |
required |
id_planilha_venda |
int |
idPlanilhaVenda |
required |
num_sequencia_venda |
int |
numSequenciaVenda |
required |
id_produto |
int |
idProduto |
required |
id_subproduto |
int |
idSubproduto |
required |
qtd_produto |
float |
qtdProduto |
required |
qtd_contada |
float |
qtdContada |
required |
ds_local_retirada |
str |
dsLocalRetirada |
required |
id_lote |
str | None |
idLote |
None |
visualiza_qtd_pre_carga |
bool |
visualizaQtdPreCarga |
True |
corredor |
str | None |
corredor |
None |
gondola |
str | None |
gondola |
None |
prateleira |
str | None |
prateleira |
None |
Nothing is validated: a qtd_contada differing from qtd_produto is accepted and resolved server-side.
visualizaQtdPreCarga is serialized as the string "true" / "false", not as a JSON boolean. That is what the app sends, even though CISS answers with a real boolean. Sending an actual boolean is untested.
from_dict(data, *, id_empresa, id_usuario)
Used by pre_carga.items(). The /itens response carries only nine of the sixteen fields, so from_dict takes the two it omits as keyword arguments and defaults the rest:
| Field | Where it comes from |
|---|---|
id_empresa, id_usuario |
The keyword arguments — CISS takes them as query parameters and does not echo them back |
qtd_contada |
0.0 — nothing has been counted yet |
id_lote, corredor, gondola, prateleira |
None — absent from the response |
idPreCarga, idEmpresaVenda, idPlanilhaVenda, numSequenciaVenda, idProduto, idSubproduto and qtdProduto are required — a missing one raises KeyError. A missing or null dsLocalRetirada becomes "", and a missing visualizaQtdPreCarga becomes True.
Unlike to_dict, this reads visualizaQtdPreCarga as a real boolean, because that is how the endpoint returns it.
PreCargaSeparacao
| Field | Type | API field |
|---|---|---|
id_empresa |
int |
idEmpresa |
id_pre_carga |
int |
idPreCarga |
descricao |
str |
descricao |
dt_movimento |
date |
dtMovimento, coerced to a date |
idEmpresa, idPreCarga and dtMovimento are required — a missing one raises KeyError. A missing or null descricao becomes "", and it is right-stripped since CISS pads it to a fixed width.
dtMovimento accepts "2026-08-05", a full "2026-08-05T00:00:00" timestamp, or an already-decoded date/datetime; CISS returns all of these for the same field. Anything else 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 |
Required: idPreCarga, idEmpresa, idDocumento, idProduto, idSubproduto, numSequencia and qtdProduto — these identify the line, so a missing one raises KeyError. The rest fall back when missing or null:
| Key | Fallback |
|---|---|
dsLocalRetirada |
"" |
idLote |
None |
fgSeparado |
"" |
qtdContadaDivergente |
False |
idLocalRetirada |
0 |
Because of that fallback, id_local_retirada == 0 means "CISS did not say", not pick-up location number zero. qtd_produto is coerced with float() and id_local_retirada with int(), since CISS sometimes sends them as strings.
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/pre_carga |
pre_carga.get_all |
GET |
/ciss-control/pre_carga/itens |
pre_carga.items |
POST |
/ciss-control/pre_carga |
pre_carga.submit |
POST |
/ciss-control/pre_carga/status |
pre_carga.set_status |
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 can stall on wide sync windows |
Bounded by the read timeout now, but a stalled call still costs you those seconds — keep num_dias_sync small |
logout() is client-side only |
The token remains valid on the server |
| Transport errors are not wrapped | Callers catch requests exceptions (HTTPError, Timeout) alongside CISSClientError |
Empty query filters are sent as key= |
The reference app sends bare key with no =. Both decode to an empty string server-side, but this is the one place the library is not byte-identical to the captured traffic |
pre_carga.submit / set_status return unparsed responses |
The 201 body (the persisted items) and the status echo are yours to read |
pre_carga.items returns qtd_contada == 0.0 |
Submitting an untouched line reports a count of zero. Deliberate, but it is a footgun if you forget |
pre_carga.items never returns idLote / corredor / gondola / prateleira |
They default to None on submit; where real values come from is unknown |
| 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 |
| Conferência | Checking a picked order against its invoice |
| Conferente | The person doing the conferência (id_conferente) |
| Nota | Invoice (num_nota) |
| Orçamento | Quote (id_orcamento, 0 when there is none) |
| Planilha de venda | Sale sheet a line item came from (id_planilha_venda) |
| Clifor | Cliente/fornecedor — customer or supplier, one record for both |
| Corredor / gôndola / prateleira | Aisle / gondola / shelf, the item's physical location |
| 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.0a6
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.0a6.tar.gz | 40.9 kB | Without provenance | Details for libciss-0.1.0a6.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| libciss-0.1.0a6-py3-none-any.whl | Python 3 | none | any | Details for libciss-0.1.0a6-py3-none-any.whl |
Total release size: 67.2 kB
Release files / libciss-0.1.0a6.tar.gz
| Download URL | libciss-0.1.0a6.tar.gz |
|---|---|
| Size | 40.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cb9981907088e5b0cebc5ae7228deabe6d065ad9f2d219aa909c25276971de00
|
|
BLAKE2b-256 checksum How to use checksums |
d41b74378374fb99a27dc7380caff9c669a96b104fb6a1e6995e683d94c2757e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / libciss-0.1.0a6-py3-none-any.whl
| Download URL | libciss-0.1.0a6-py3-none-any.whl |
|---|---|
| Size | 26.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
84ff7881cee30fde631862c69251b78924d82aa5e50d54bea1697d17523c5c37
|
|
BLAKE2b-256 checksum How to use checksums |
5cb85c15bdff10ac331da86f1ead5ce5fcd81d1f5ce4191e6b49e7a147467f6a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Security for libciss 0.1.0a6
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.