Skip to main content

sinpapel

v0.7.0 — Versioned state machines, immutable audit trail, and pluggable electronic signatures for Django.

PyPI Python Django License: GPL v3 Tests

🇪🇸 Leer en Español


Why sinpapel?

Building paperless processes in Django usually means stitching together a state-machine library, an audit framework, a signing layer, and a forms toolkit. sinpapel ships them as one coherent package: declarative versioned workflows, immutable history, pluggable e-signature backends, schema-based metadata capture, transition predicates, SLA timers, and custom domain signals — designed to be adopted incrementally in any Django 5+ project.

Features

  • Workflow Engine — versioned state machines via VersionFlujo + ConfiguracionTransicion, with permission groups, document-requirement gates, and a WorkflowEngine service. Convenience methods (available_transitions, can_transition_to, transition, preview_transition) are injected onto every @workflow_enabled model.
  • Document Requirements — both a coarse per-state flag (Estado.expediente_obligatorio) and fine-grained per-type rules (RequisitoEstadoDocumento: document type + minimum completion percentage) are enforced on every transition; system-generated documents (auto_carga=True) do not block.
  • Transition Predicates — Python paths, restricted JSON Logic, and Django-ORM-backed predicates, ordered per transition.
  • Structured Metadata CaptureMetadatosCapturables mixin with schema-declared CampoMetadato fields, validated at save.
  • Dynamic Forms & SerializersMetaFormFactory builds Django Forms from metadata schema; DRF Serializer mode also supported.
  • Pluggable Signing BackendsSignatureBackend strategy interface plus reference backends: FakeBackend (tests), ManualBackend (default), and FielBackend (FIEL/SAT, RSA-SHA256 + X.509).
  • Immutable Audit TrailTrazable mixin, SeguimientoWorkflow history, RegistroFirma, plus django-simple-history integration.
  • SLA Timers & Preview TransitionsSLAEngine with notify / escalate / reject / flag actions; preview_transition() returns an impact report (blocking reasons, missing documents, failed predicates) without mutating state — available both as WorkflowEngine.preview_transition() and as a method on the instance.
  • Custom Domain Signalspredicate_failed, sla_breached, sla_action_executed, transition_preview_requested for observability and side-effect wiring.

Installation

pip install sinpapel

Requires Python 3.10+ and Django 5.0+.

Add to INSTALLED_APPS:

INSTALLED_APPS = [
    # ...
    "simple_history",
    "sinpapel",
    "my_app",  # your app that defines workflow-enabled models
]

Run migrations:

python manage.py migrate sinpapel

Quick Start

Declare a workflow-enabled model:

from decimal import Decimal

from django.db import models
from sinpapel import workflow_enabled
from sinpapel.mixins import CampoMetadato, MetadatosCapturables, Trazable


@workflow_enabled(state_field="estado", workflow_key="solicitud")
class Solicitud(MetadatosCapturables, Trazable):
    folio = models.CharField(max_length=20, unique=True)
    estado = models.ForeignKey("sinpapel.Estado", on_delete=models.PROTECT)

    SCHEMA_METADATOS = [
        CampoMetadato("monto", Decimal, requerido=True),
        CampoMetadato("rfc", str, requerido=True),
    ]

    def resolve_workflow_version(self):
        from sinpapel.models import VersionFlujo
        return VersionFlujo.objects.get(nombre="solicitudes", activo=True)

Drive a state transition through the methods injected on the instance:

# Preview before committing (no mutation, returns an impact report)
preview = solicitud.preview_transition("APROBADA", user=request.user)
if not preview["permitido"]:
    # razones_bloqueo aggregates permission, predicate and document failures;
    # documentos_faltantes lists missing per-type requirements, e.g.
    # {"tipo": "requisito_documento", "tipo_documento": "INE",
    #  "porcentaje_requerido": 100, "porcentaje_actual": 0, "mensaje": "..."}
    raise ValueError(preview["razones_bloqueo"][0]["mensaje"])

# Execute the transition (validates, creates audit row, fires signals).
# Raises PermissionError if validation (groups, predicates, documents) fails.
solicitud.transition("APROBADA", user=request.user, comentarios="Cumple requisitos")

The same logic is also reachable through the WorkflowEngine service directly (WorkflowEngine().preview_transition(solicitud, "APROBADA", user) / .cambiar_estado(...)) when you need it outside a model instance.

Subscribe to a custom signal:

from django.dispatch import receiver
from sinpapel.signals import sla_breached

@receiver(sla_breached)
def on_sla_breach(sender, instance, sla, **kwargs):
    notify_team(instance, sla)

Full end-to-end examples, schema seeding, predicate cookbook, signing backend setup, and admin integration live in docs/usage/en.md.

What's Inside

Subsystem Module Docs
Workflow Engine sinpapel.services.workflow_engine USAGE §State Transitions
Predicates sinpapel.services.predicate_engine USAGE §Transition Predicates
Metadata sinpapel.mixins USAGE §Metadata
Forms Factory sinpapel.forms USAGE §Forms
Signing sinpapel.signing USAGE §Signing
Audit Trail sinpapel.models + sinpapel.mixins.Trazable USAGE §Audit
SLA Engine sinpapel.services.sla_engine USAGE §SLA
Custom Signals sinpapel.signals USAGE §Signals
Schema Export/Import sinpapel.schemas + management commands USAGE §Schema

Configuration

Optional Django settings:

# settings.py
# Dotted path to the signature backend (default: ManualBackend).
SINPAPEL_SIGNATURE_BACKEND = "sinpapel.signing.backends.fiel.FielBackend"
SINPAPEL_ALLOW_SERVER_SIGNING = False  # gate FIEL server-side signing (legal review)
SINPAPEL_EMIT_PREVIEW_EVENTS = False   # set True to fire transition_preview_requested signal

See USAGE §Settings for the full reference.

Compatibility

Python Django
3.10, 3.11, 3.12, 3.13 5.0, 5.1

CI runs the test suite across the full matrix.

Documentation

Versioning & Stability

sinpapel follows Semantic Versioning. The current release is v0.7.0 (Beta). Public APIs (WorkflowEngine, PredicateEngine, SLAEngine, signals, model fields, schema JSON v0.2) are stable in the 0.x series; breaking changes will bump the minor version and be flagged in docs/development/changelog.md until 1.0.0. Upgrading to 0.7.0: transition() / cambiar_estado() no longer accept the monto_aprobado parameter (removed in 0.7.0) — carry domain data via metadata (MetadatosCapturables) or condiciones / comentarios instead. Since 0.6.0, transitions also enforce any RequisitoEstadoDocumento rules that were previously configured but never evaluated — review existing flows before upgrading.

Contributing

Pull requests are welcome. Please read docs/development/contributing.md for development setup, commit conventions, and the Developer Certificate of Origin (DCO) sign-off requirement.

License

Copyright (C) 2024-2026 Julio Adrián.

sinpapel is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

sinpapel is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sinpapel-0.7.0.post1.tar.gz (102.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sinpapel-0.7.0.post1-py3-none-any.whl (83.7 kB view details)

Uploaded Python 3

File details

Details for the file sinpapel-0.7.0.post1.tar.gz.

File metadata

  • Download URL: sinpapel-0.7.0.post1.tar.gz
  • Upload date:
  • Size: 102.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for sinpapel-0.7.0.post1.tar.gz
Algorithm Hash digest
SHA256 c597e6be0d3858a1d3c087135a48cae97df9b34c0345c30b2eff797bbbb63de0
MD5 a67dc54d8f4ca52d082a64e2126be9ff
BLAKE2b-256 ae53749fffa7d4dd19f5b54f6ef9224a89bebd9f09111a5f1f345062a44bcb24

See more details on using hashes here.

File details

Details for the file sinpapel-0.7.0.post1-py3-none-any.whl.

File metadata

  • Download URL: sinpapel-0.7.0.post1-py3-none-any.whl
  • Upload date:
  • Size: 83.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for sinpapel-0.7.0.post1-py3-none-any.whl
Algorithm Hash digest
SHA256 95a5e7c153e4602fa89ae07f2346489e8f0bc1f18827a1dabce1a67c48b18bbf
MD5 d3c2cf9802d048b88256b010a3a5270f
BLAKE2b-256 8b142e11b843cfd0240c38db870784904acb40e8911c4dd6a7ec6e0364845592

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page