diff --git a/central/alembic/versions/0025_tenant_external_cnpj.py b/central/alembic/versions/0025_tenant_external_cnpj.py new file mode 100644 index 0000000..fc46451 --- /dev/null +++ b/central/alembic/versions/0025_tenant_external_cnpj.py @@ -0,0 +1,20 @@ +"""Tenant.external_cnpj (digits) mirrored from GTI Gerenciador for the client selector search. + +Revision ID: 0025_tenant_external_cnpj +Revises: 0024_tenant_gerenciador_mirror +""" +from alembic import op +import sqlalchemy as sa + +revision = "0025_tenant_external_cnpj" +down_revision = "0024_tenant_gerenciador_mirror" +branch_labels = None +depends_on = None + + +def upgrade(): + op.add_column("tenants", sa.Column("external_cnpj", sa.String(20), nullable=True)) + + +def downgrade(): + op.drop_column("tenants", "external_cnpj") diff --git a/central/app/client_sync.py b/central/app/client_sync.py index e5970c0..a0121e1 100644 --- a/central/app/client_sync.py +++ b/central/app/client_sync.py @@ -74,7 +74,7 @@ def sync_from_gerenciador(session, clients: list[dict]) -> SyncResult: if row is None: name = wanted_name if wanted_name not in taken_names else f"{wanted_name} ({code})" - row = Tenant(name=name, source=SOURCE, external_client_id=external_id, external_code=code, active=active, synced_at=now) + row = Tenant(name=name, source=SOURCE, external_client_id=external_id, external_code=code, external_cnpj=client.get("cnpj"), active=active, synced_at=now) session.add(row) taken_names.add(name) created += 1 @@ -89,6 +89,9 @@ def sync_from_gerenciador(session, clients: list[dict]) -> SyncResult: if row.external_code != code: row.external_code = code changed = True + if row.external_cnpj != client.get("cnpj"): + row.external_cnpj = client.get("cnpj") + changed = True if row.active != active: row.active = active changed = True diff --git a/central/app/models.py b/central/app/models.py index 24f81f0..1306b5d 100644 --- a/central/app/models.py +++ b/central/app/models.py @@ -229,6 +229,7 @@ class Tenant(Base): source: Mapped[str] = mapped_column(String(16), default="LOCAL", server_default="LOCAL") # LOCAL | GTI_GERENCIADOR external_client_id: Mapped[int | None] = mapped_column(Integer, nullable=True, index=True) external_code: Mapped[str | None] = mapped_column(String(16), nullable=True) + external_cnpj: Mapped[str | None] = mapped_column(String(20), nullable=True) # digits only, for the client selector search active: Mapped[bool] = mapped_column(Boolean, default=True, server_default="true") synced_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) diff --git a/central/app/web.py b/central/app/web.py index a5e96f8..f0a3f3e 100644 --- a/central/app/web.py +++ b/central/app/web.py @@ -208,6 +208,11 @@ def router(session_factory): if search: query = query.where(Tenant.name.ilike(f"%{search}%")) return session.scalars(query.order_by(Tenant.name)).all() + def _clients_json(tenants): + import json + # "<" escaped so the payload can never close the @@ -30,13 +34,72 @@
+ {% endif %} diff --git a/central/tests/test_agent_distribution.py b/central/tests/test_agent_distribution.py index 22a527e..db030dd 100644 --- a/central/tests/test_agent_distribution.py +++ b/central/tests/test_agent_distribution.py @@ -162,7 +162,7 @@ def test_full_installation_flow_creates_a_working_agent(hub): release_id = publish_release(factory) login(client) - created = client.post("/agents/new", data={"csrf_token": csrf(client), "tenant_id": tenant_id(factory), + created = client.post("/agents/new", data={"csrf_token": csrf(client), "client_id": tenant_id(factory), "display_name": "Servidor Principal", "expected_hostname": "SRV-UNIAO", "release_id": release_id}, follow_redirects=False) assert created.status_code == 303 @@ -221,7 +221,7 @@ def test_direct_authenticated_download_does_not_need_a_link(hub): client, factory = hub release_id = publish_release(factory) login(client) - created = client.post("/agents/new", data={"csrf_token": csrf(client), "tenant_id": tenant_id(factory), + created = client.post("/agents/new", data={"csrf_token": csrf(client), "client_id": tenant_id(factory), "display_name": "Loja 2", "release_id": release_id}, follow_redirects=False) installation_uuid = created.headers["location"].rsplit("/", 1)[-1] downloaded = client.get(f"/agents/pending/{installation_uuid}/download") @@ -303,7 +303,7 @@ def test_revoked_installation_link_rejected(hub): client, factory = hub release_id = publish_release(factory) login(client) - created = client.post("/agents/new", data={"csrf_token": csrf(client), "tenant_id": tenant_id(factory), + created = client.post("/agents/new", data={"csrf_token": csrf(client), "client_id": tenant_id(factory), "display_name": "Teste", "release_id": release_id}, follow_redirects=False) uid = created.headers["location"].rsplit("/", 1)[-1] linked = client.post(f"/agents/pending/{uid}/link", data={"csrf_token": csrf(client)}, follow_redirects=False) @@ -322,7 +322,7 @@ def test_release_revoked_blocks_download(hub): client, factory = hub release_id = publish_release(factory) login(client) - created = client.post("/agents/new", data={"csrf_token": csrf(client), "tenant_id": tenant_id(factory), + created = client.post("/agents/new", data={"csrf_token": csrf(client), "client_id": tenant_id(factory), "display_name": "Teste", "release_id": release_id}, follow_redirects=False) uid = created.headers["location"].rsplit("/", 1)[-1] linked = client.post(f"/agents/pending/{uid}/link", data={"csrf_token": csrf(client)}, follow_redirects=False) diff --git a/central/tests/test_install_code_enrollment.py b/central/tests/test_install_code_enrollment.py index 91aca4a..f6f49da 100644 --- a/central/tests/test_install_code_enrollment.py +++ b/central/tests/test_install_code_enrollment.py @@ -340,7 +340,7 @@ def test_panel_flow_code_generate_download_and_enroll_with_exe(hub, tmp_path): client, factory = hub release_id = publish_exe_release(factory, tmp_path) login(client) - created = client.post("/agents/new", data={"csrf_token": csrf(client), "tenant_id": tenant_id(factory), + created = client.post("/agents/new", data={"csrf_token": csrf(client), "client_id": tenant_id(factory), "display_name": "Servidor EXE", "release_id": release_id}, follow_redirects=False) assert created.status_code == 303 installation_uuid = created.headers["location"].rsplit("/", 1)[-1] @@ -391,7 +391,7 @@ def test_rotation_generates_a_code_that_invalidates_the_previous(hub, tmp_path): client, factory = hub release_id = publish_exe_release(factory, tmp_path) login(client) - created = client.post("/agents/new", data={"csrf_token": csrf(client), "tenant_id": tenant_id(factory), + created = client.post("/agents/new", data={"csrf_token": csrf(client), "client_id": tenant_id(factory), "display_name": "Rotação", "release_id": release_id}, follow_redirects=False) installation_uuid = created.headers["location"].rsplit("/", 1)[-1] first = client.post(f"/agents/pending/{installation_uuid}/code", data={"csrf_token": csrf(client)}, follow_redirects=True) @@ -414,7 +414,7 @@ def test_revoke_clears_code_and_is_audited(hub, tmp_path): client, factory = hub release_id = publish_exe_release(factory, tmp_path) login(client) - created = client.post("/agents/new", data={"csrf_token": csrf(client), "tenant_id": tenant_id(factory), + created = client.post("/agents/new", data={"csrf_token": csrf(client), "client_id": tenant_id(factory), "display_name": "Revoga", "release_id": release_id}, follow_redirects=False) installation_uuid = created.headers["location"].rsplit("/", 1)[-1] client.post(f"/agents/pending/{installation_uuid}/code", data={"csrf_token": csrf(client)}) diff --git a/central/tests/test_new_installation_clients.py b/central/tests/test_new_installation_clients.py index 1221a53..48f22c5 100644 --- a/central/tests/test_new_installation_clients.py +++ b/central/tests/test_new_installation_clients.py @@ -23,8 +23,8 @@ def hub(tmp_path, monkeypatch): factory = setup_db() seed(factory) # tenant "Acme" (LOCAL, no source) + admin "operator"/"a-secure-password" with factory() as session: - session.add(Tenant(name="Mercado Ativo", source="GTI_GERENCIADOR", external_client_id=1, active=True)) - session.add(Tenant(name="Loja Inativa", source="GTI_GERENCIADOR", external_client_id=2, active=False)) + session.add(Tenant(name="Mercado Ativo", source="GTI_GERENCIADOR", external_client_id=1, external_code="000001", external_cnpj="11222333000181", active=True)) + session.add(Tenant(name="Loja