diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..14fac91 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index 831e788..954badc 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -34,7 +34,9 @@ Consulte também [`SDK_CONTRACT.md`](SDK_CONTRACT.md) (mapeamento HTTP → exce `BulkRequirement.create` pode responder **HTTP 200** com slots com erro em `atomic:results`. O SDK **não** lança exceção nesse caso. ```python -response = client.notarial.bulk_requirements.create( +from clicksign.resources.notarial.bulk_requirement import BulkRequirement + +response = BulkRequirement.create( envelope_id, block=lambda ops: ops.add_agree( signer_id=signer.id, @@ -110,11 +112,11 @@ Receita completa: [`examples/03-webhooks.md`](examples/03-webhooks.md). ```python # correto -client.notarial.documents.create(envelope.id, filename="a.pdf", content_base64="...") -client.notarial.signers.create(envelope.id, name="...", email="...") +Document.create(envelope.id, filename="a.pdf", content_base64="...") +Signer.create(envelope.id, name="...", email="...") # incorreto — envelope_id vira atributo do JSON por engano -client.notarial.documents.create(envelope_id=envelope.id, filename="a.pdf") +Document.create(envelope_id=envelope.id, filename="a.pdf") ``` --- diff --git a/docs/WORKFLOW.md b/docs/WORKFLOW.md index 042a294..1ed8f81 100644 --- a/docs/WORKFLOW.md +++ b/docs/WORKFLOW.md @@ -8,23 +8,8 @@ Guia do ciclo de vida de um envelope na API 3.0 (Envelope): criação, documento ## Configuração inicial -Recomendado para código novo: `ClicksignClient` (dependências explícitas). Alternativa legada: `configure()` + classes de resource. - ```python import os - -from clicksign import ClicksignClient - -client = ClicksignClient( - api_key=os.environ["CLICKSIGN_API_KEY"], - environment="sandbox", # ou "production" - # base_url="https://..." # opcional: sobrescreve environment -) -``` - -Equivalente global: - -```python import clicksign from clicksign.resources.notarial.envelope import Envelope from clicksign.resources.notarial.document import Document @@ -41,13 +26,11 @@ clicksign.configure(api_key=os.environ["CLICKSIGN_API_KEY"], environment="sandbo O envelope é o contêiner do processo. Começa em `draft`. ```python -envelope = client.notarial.envelopes.create( +envelope = Envelope.create( name="Contrato de prestação de serviços", locale="pt-BR", auto_close=True, remind_interval=3, - default_subject="Documentos aguardando sua assinatura", - default_message="Por favor, revise e assine os documentos em anexo.", ) print(envelope.id) # uuid do envelope @@ -69,7 +52,7 @@ import base64 pdf_bytes = open("contrato.pdf", "rb").read() pdf_base64 = "data:application/pdf;base64," + base64.b64encode(pdf_bytes).decode() -document = client.notarial.documents.create( +document = Document.create( envelope.id, filename="contrato.pdf", content_base64=pdf_base64, @@ -79,18 +62,6 @@ print(document.id) print(document.status) # draft ``` -Via classe (mesmo contrato): - -```python -from clicksign.resources.notarial.document import Document - -document = Document.create( - envelope.id, - filename="contrato.pdf", - content_base64=pdf_base64, -) -``` - **Outros modos de criação** (mutuamente exclusivos na API — consulte [`SPEC.md`](SPEC.md)): ```python @@ -106,7 +77,7 @@ Document.create(envelope.id, filename="contrato.docx", template_key="uuid-do-tem ## 3. Adicionar o signatário ```python -signer = client.notarial.signers.create( +signer = Signer.create( envelope.id, name="Maria Silva", email="maria.silva@example.com", @@ -126,23 +97,13 @@ print(signer.envelope_id) Os requisitos definem **o que** o signatário deve fazer em cada documento. -Relacionamentos padrão documento + signatário: - -```python -rels = { - "document": {"data": {"type": "documents", "id": document.id}}, - "signer": {"data": {"type": "signers", "id": signer.id}}, -} -``` - ### 4.1 Concordância (`agree`) ```python -from clicksign.resources.notarial.requirement import Requirement - agree = Requirement.create( envelope.id, - relationships=rels, + signer_id=signer.id, + document_id=document.id, action="agree", role="sign", ) @@ -155,7 +116,8 @@ print(agree.id, agree.action) ```python evidence = Requirement.create( envelope.id, - relationships=rels, + signer_id=signer.id, + document_id=document.id, action="provide_evidence", auth="email", ) @@ -179,8 +141,6 @@ print(envelope.status) # running Alternativa explícita (POST `/activate`): ```python -from clicksign.resources.notarial.envelope import Envelope - envelope = Envelope.activate(envelope.id) ``` @@ -215,51 +175,29 @@ signer.notify( ```python import base64 import os - -from clicksign import ClicksignClient +import clicksign +from clicksign.resources.notarial.envelope import Envelope +from clicksign.resources.notarial.document import Document +from clicksign.resources.notarial.signer import Signer from clicksign.resources.notarial.requirement import Requirement -client = ClicksignClient( - api_key=os.environ["CLICKSIGN_API_KEY"], - environment="sandbox", -) +clicksign.configure(api_key=os.environ["CLICKSIGN_API_KEY"], environment="sandbox") # 1. Envelope -envelope = client.notarial.envelopes.create( - name="Contrato ACME", - locale="pt-BR", - auto_close=True, -) +envelope = Envelope.create(name="Contrato ACME", locale="pt-BR", auto_close=True) # 2. Documento pdf_b64 = "data:application/pdf;base64," + base64.b64encode( open("contrato.pdf", "rb").read() ).decode() -document = client.notarial.documents.create( - envelope.id, - filename="contrato.pdf", - content_base64=pdf_b64, -) +document = Document.create(envelope.id, filename="contrato.pdf", content_base64=pdf_b64) # 3. Signatário -signer = client.notarial.signers.create( - envelope.id, - name="Maria Silva", - email="maria@example.com", -) +signer = Signer.create(envelope.id, name="Maria Silva", email="maria@example.com") # 4. Requisitos -rels = { - "document": {"data": {"type": "documents", "id": document.id}}, - "signer": {"data": {"type": "signers", "id": signer.id}}, -} -Requirement.create(envelope.id, relationships=rels, action="agree", role="sign") -Requirement.create( - envelope.id, - relationships=rels, - action="provide_evidence", - auth="email", -) +Requirement.create(envelope.id, signer_id=signer.id, document_id=document.id, action="agree", role="sign") +Requirement.create(envelope.id, signer_id=signer.id, document_id=document.id, action="provide_evidence", auth="email") # 5. Ativar envelope.update(status="running") @@ -277,15 +215,13 @@ print(f"Envelope {envelope.id} ativo e signatário notificado.") Uma única requisição HTTP para vários requisitos (`BulkRequirement`): ```python -response = client.notarial.bulk_requirements.create( +from clicksign.resources.notarial.bulk_requirement import BulkRequirement + +response = BulkRequirement.create( envelope.id, block=lambda ops: ( ops.add_agree(signer_id=signer.id, document_id=document.id, role="sign"), - ops.add_provide_evidence( - signer_id=signer.id, - document_id=document.id, - auth="email", - ), + ops.add_provide_evidence(signer_id=signer.id, document_id=document.id, auth="email"), ), ) @@ -305,9 +241,6 @@ Detalhes: [examples/02-bulk-requirements.md](examples/02-bulk-requirements.md). ### Eventos e estado (API) ```python -from clicksign.resources.notarial.document import Document -from clicksign.resources.notarial.envelope import Envelope - for event in Envelope.list_events(envelope.id): print(event.name, event.created) @@ -317,7 +250,7 @@ for event in Document.list_events(document.id, envelope_id=envelope.id): for s in Envelope.list_signers(envelope.id): print(s.name, s.email) -envelope = client.notarial.envelopes.retrieve(envelope.id) +envelope = Envelope.retrieve(envelope.id) print(envelope.status) # running, closed, cancelled, ... ``` diff --git a/docs/examples/02-bulk-requirements.md b/docs/examples/02-bulk-requirements.md index 1f5ccdd..8e06769 100644 --- a/docs/examples/02-bulk-requirements.md +++ b/docs/examples/02-bulk-requirements.md @@ -8,14 +8,10 @@ Cria ou remove vários requisitos de assinatura em **uma** requisição (`POST / ```python import os - -from clicksign import ClicksignClient +import clicksign from clicksign.resources.notarial.bulk_requirement import BulkRequirement -client = ClicksignClient( - api_key=os.environ["CLICKSIGN_API_KEY"], - environment="sandbox", -) +clicksign.configure(api_key=os.environ["CLICKSIGN_API_KEY"], environment="sandbox") # envelope, document e signer já criados (status draft) response = BulkRequirement.create( @@ -36,19 +32,6 @@ response = BulkRequirement.create( ) ``` -Via facade (mesmo contrato): - -```python -response = client.notarial.bulk_requirements.create( - envelope.id, - block=lambda ops: ops.add_agree( - signer_id=signer.id, - document_id=document.id, - role="sign", - ), -) -``` - --- ## Tratar sucesso parcial (`atomic:results`) diff --git a/pyproject.toml b/pyproject.toml index 9f4d8a6..2c93639 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,7 @@ build-backend = "hatchling.build" name = "clicksign" dynamic = ["version"] description = "Official Python SDK for the Clicksign e-signature API (v3)" +license = "MIT" requires-python = ">=3.10" dependencies = [] diff --git a/src/clicksign/resources/notarial/requirement.py b/src/clicksign/resources/notarial/requirement.py index b1805cc..3690cc1 100644 --- a/src/clicksign/resources/notarial/requirement.py +++ b/src/clicksign/resources/notarial/requirement.py @@ -54,14 +54,21 @@ def filter(cls, **kwargs: Unpack[RequirementFilterParams]) -> QueryProxy[Require def create( # type: ignore[override] cls, envelope_id: str, + signer_id: str | None = None, + document_id: str | None = None, relationships: dict[str, Any] | None = None, **attrs: Unpack[RequirementCreateParams], ) -> Requirement: from ...json_api.serializer import serialize_create + rels: dict[str, Any] = dict(relationships or {}) + if signer_id: + rels["signer"] = {"data": {"type": "signers", "id": signer_id}} + if document_id: + rels["document"] = {"data": {"type": "documents", "id": document_id}} client = cls._get_client() path = f"/envelopes/{envelope_id}/requirements" - body = serialize_create(cls._get_resource_type(), dict(attrs), relationships or None) + body = serialize_create(cls._get_resource_type(), dict(attrs), rels or None) response = client.post(path, body) instances, _ = cls._parse_response(response) inst = instances[0]