relatorios-sivwin 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- relatorios_sivwin-0.2.0/PKG-INFO +109 -0
- relatorios_sivwin-0.2.0/README.md +90 -0
- relatorios_sivwin-0.2.0/pyproject.toml +35 -0
- relatorios_sivwin-0.2.0/setup.cfg +4 -0
- relatorios_sivwin-0.2.0/src/relatorios_sivwin/__init__.py +16 -0
- relatorios_sivwin-0.2.0/src/relatorios_sivwin/constants.py +19 -0
- relatorios_sivwin-0.2.0/src/relatorios_sivwin/contracts.py +48 -0
- relatorios_sivwin-0.2.0/src/relatorios_sivwin/core.py +18 -0
- relatorios_sivwin-0.2.0/src/relatorios_sivwin/dbapi.py +54 -0
- relatorios_sivwin-0.2.0/src/relatorios_sivwin/queries/__init__.py +9 -0
- relatorios_sivwin-0.2.0/src/relatorios_sivwin/queries/_shared.py +152 -0
- relatorios_sivwin-0.2.0/src/relatorios_sivwin/queries/gerais.py +127 -0
- relatorios_sivwin-0.2.0/src/relatorios_sivwin/queries/servicos.py +277 -0
- relatorios_sivwin-0.2.0/src/relatorios_sivwin.egg-info/PKG-INFO +109 -0
- relatorios_sivwin-0.2.0/src/relatorios_sivwin.egg-info/SOURCES.txt +19 -0
- relatorios_sivwin-0.2.0/src/relatorios_sivwin.egg-info/dependency_links.txt +1 -0
- relatorios_sivwin-0.2.0/src/relatorios_sivwin.egg-info/requires.txt +5 -0
- relatorios_sivwin-0.2.0/src/relatorios_sivwin.egg-info/top_level.txt +1 -0
- relatorios_sivwin-0.2.0/tests/test_public_api.py +26 -0
- relatorios_sivwin-0.2.0/tests/test_queries.py +24 -0
- relatorios_sivwin-0.2.0/tests/test_sql_query.py +37 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: relatorios-sivwin
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Consultas SQL reutilizaveis para o ecossistema SivWin/Otimiza.
|
|
5
|
+
Keywords: sql,django,sivwin,otimiza,relatorios
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Topic :: Database
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: build; extra == "dev"
|
|
17
|
+
Requires-Dist: pytest; extra == "dev"
|
|
18
|
+
Requires-Dist: twine; extra == "dev"
|
|
19
|
+
|
|
20
|
+
# Relatorios SivWin
|
|
21
|
+
|
|
22
|
+
Biblioteca Python para centralizar consultas SQL do ecossistema SivWin/Otimiza.
|
|
23
|
+
|
|
24
|
+
## Instalacao
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install relatorios-sivwin
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
O nome publicado usa hifen, mas o import Python usa underscore:
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from relatorios_sivwin import RelatoriosSivWin
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Objetivo
|
|
37
|
+
|
|
38
|
+
- Concentrar consultas SQL em um unico lugar.
|
|
39
|
+
- Reduzir duplicacao de regras SQL entre projetos.
|
|
40
|
+
- Facilitar o uso das mesmas consultas em Django, scripts e outros servicos.
|
|
41
|
+
|
|
42
|
+
## Estrutura
|
|
43
|
+
|
|
44
|
+
- `relatorios_sivwin.SQLQuery`: contrato padrao para consultas parametrizadas.
|
|
45
|
+
- `relatorios_sivwin.RelatoriosSivWin`: hub principal de acesso aos dominios.
|
|
46
|
+
- `relatorios_sivwin.RelatoriosGeraisQueries`: consultas gerais, como declaracoes e ARTs.
|
|
47
|
+
- `relatorios_sivwin.RelatoriosServicosQueries`: consultas do dominio de servicos e inspecoes.
|
|
48
|
+
- `relatorios_sivwin.dbapi`: helpers opcionais para executar uma `SQLQuery` com um cursor ja aberto.
|
|
49
|
+
|
|
50
|
+
## Exemplo Rapido
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from relatorios_sivwin import RelatoriosSivWin
|
|
54
|
+
|
|
55
|
+
relatorios = RelatoriosSivWin()
|
|
56
|
+
consulta = relatorios.gerais.relatorio_art(120000, 120999)
|
|
57
|
+
|
|
58
|
+
print(consulta.sql)
|
|
59
|
+
print(consulta.params)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Por padrao, a representacao SQL bruta usa `otimiza` como banco:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
relatorios = RelatoriosSivWin(database="sivwin_homolog")
|
|
66
|
+
consulta = relatorios.servicos.relatorio_completo("2026-02-01", "2026-02-28")
|
|
67
|
+
|
|
68
|
+
print(consulta.to_sql_raw())
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Uso Com Django
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from django.db import connections
|
|
75
|
+
from relatorios_sivwin import RelatoriosSivWin, fetch_all
|
|
76
|
+
|
|
77
|
+
relatorios = RelatoriosSivWin()
|
|
78
|
+
consulta = relatorios.servicos.relatorio_completo("2026-02-01", "2026-02-28")
|
|
79
|
+
|
|
80
|
+
with connections["otimiza"].cursor() as cursor:
|
|
81
|
+
dados = fetch_all(cursor, consulta)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Nesse formato:
|
|
85
|
+
|
|
86
|
+
- o Django continua responsavel pela conexao;
|
|
87
|
+
- o pacote so define a consulta;
|
|
88
|
+
- o mesmo objeto pode ser reutilizado em outros contextos.
|
|
89
|
+
|
|
90
|
+
## Uso Fora Do Django
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from relatorios_sivwin import RelatoriosSivWin
|
|
94
|
+
|
|
95
|
+
relatorios = RelatoriosSivWin()
|
|
96
|
+
consulta = relatorios.gerais.relatorio_declaracoes(123456)
|
|
97
|
+
|
|
98
|
+
cursor.execute(consulta.sql, consulta.params)
|
|
99
|
+
linhas = cursor.fetchall()
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Principios Do Pacote
|
|
103
|
+
|
|
104
|
+
- Sem acoplamento com conexao, `.env` ou credenciais.
|
|
105
|
+
- Sem dependencias pesadas para gerar SQL.
|
|
106
|
+
- Metodos de dominio retornam `SQLQuery`.
|
|
107
|
+
- Queries sempre parametrizadas, sem interpolacao direta de valores.
|
|
108
|
+
- Cada relatorio relevante tem sua propria SQL, escrita de forma explicita.
|
|
109
|
+
- Reaproveitamento existe apenas onde ele realmente simplifica, como constantes SQL e bloco de contato.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Relatorios SivWin
|
|
2
|
+
|
|
3
|
+
Biblioteca Python para centralizar consultas SQL do ecossistema SivWin/Otimiza.
|
|
4
|
+
|
|
5
|
+
## Instalacao
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install relatorios-sivwin
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
O nome publicado usa hifen, mas o import Python usa underscore:
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from relatorios_sivwin import RelatoriosSivWin
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Objetivo
|
|
18
|
+
|
|
19
|
+
- Concentrar consultas SQL em um unico lugar.
|
|
20
|
+
- Reduzir duplicacao de regras SQL entre projetos.
|
|
21
|
+
- Facilitar o uso das mesmas consultas em Django, scripts e outros servicos.
|
|
22
|
+
|
|
23
|
+
## Estrutura
|
|
24
|
+
|
|
25
|
+
- `relatorios_sivwin.SQLQuery`: contrato padrao para consultas parametrizadas.
|
|
26
|
+
- `relatorios_sivwin.RelatoriosSivWin`: hub principal de acesso aos dominios.
|
|
27
|
+
- `relatorios_sivwin.RelatoriosGeraisQueries`: consultas gerais, como declaracoes e ARTs.
|
|
28
|
+
- `relatorios_sivwin.RelatoriosServicosQueries`: consultas do dominio de servicos e inspecoes.
|
|
29
|
+
- `relatorios_sivwin.dbapi`: helpers opcionais para executar uma `SQLQuery` com um cursor ja aberto.
|
|
30
|
+
|
|
31
|
+
## Exemplo Rapido
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from relatorios_sivwin import RelatoriosSivWin
|
|
35
|
+
|
|
36
|
+
relatorios = RelatoriosSivWin()
|
|
37
|
+
consulta = relatorios.gerais.relatorio_art(120000, 120999)
|
|
38
|
+
|
|
39
|
+
print(consulta.sql)
|
|
40
|
+
print(consulta.params)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Por padrao, a representacao SQL bruta usa `otimiza` como banco:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
relatorios = RelatoriosSivWin(database="sivwin_homolog")
|
|
47
|
+
consulta = relatorios.servicos.relatorio_completo("2026-02-01", "2026-02-28")
|
|
48
|
+
|
|
49
|
+
print(consulta.to_sql_raw())
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Uso Com Django
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from django.db import connections
|
|
56
|
+
from relatorios_sivwin import RelatoriosSivWin, fetch_all
|
|
57
|
+
|
|
58
|
+
relatorios = RelatoriosSivWin()
|
|
59
|
+
consulta = relatorios.servicos.relatorio_completo("2026-02-01", "2026-02-28")
|
|
60
|
+
|
|
61
|
+
with connections["otimiza"].cursor() as cursor:
|
|
62
|
+
dados = fetch_all(cursor, consulta)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Nesse formato:
|
|
66
|
+
|
|
67
|
+
- o Django continua responsavel pela conexao;
|
|
68
|
+
- o pacote so define a consulta;
|
|
69
|
+
- o mesmo objeto pode ser reutilizado em outros contextos.
|
|
70
|
+
|
|
71
|
+
## Uso Fora Do Django
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from relatorios_sivwin import RelatoriosSivWin
|
|
75
|
+
|
|
76
|
+
relatorios = RelatoriosSivWin()
|
|
77
|
+
consulta = relatorios.gerais.relatorio_declaracoes(123456)
|
|
78
|
+
|
|
79
|
+
cursor.execute(consulta.sql, consulta.params)
|
|
80
|
+
linhas = cursor.fetchall()
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Principios Do Pacote
|
|
84
|
+
|
|
85
|
+
- Sem acoplamento com conexao, `.env` ou credenciais.
|
|
86
|
+
- Sem dependencias pesadas para gerar SQL.
|
|
87
|
+
- Metodos de dominio retornam `SQLQuery`.
|
|
88
|
+
- Queries sempre parametrizadas, sem interpolacao direta de valores.
|
|
89
|
+
- Cada relatorio relevante tem sua propria SQL, escrita de forma explicita.
|
|
90
|
+
- Reaproveitamento existe apenas onde ele realmente simplifica, como constantes SQL e bloco de contato.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "relatorios-sivwin"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Consultas SQL reutilizaveis para o ecossistema SivWin/Otimiza."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
keywords = ["sql", "django", "sivwin", "otimiza", "relatorios"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
15
|
+
"Programming Language :: Python :: 3.10",
|
|
16
|
+
"Programming Language :: Python :: 3.11",
|
|
17
|
+
"Programming Language :: Python :: 3.12",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Topic :: Database",
|
|
20
|
+
]
|
|
21
|
+
dependencies = []
|
|
22
|
+
|
|
23
|
+
[project.optional-dependencies]
|
|
24
|
+
dev = [
|
|
25
|
+
"build",
|
|
26
|
+
"pytest",
|
|
27
|
+
"twine",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[tool.setuptools.packages.find]
|
|
31
|
+
where = ["src"]
|
|
32
|
+
include = ["relatorios_sivwin", "relatorios_sivwin.*"]
|
|
33
|
+
|
|
34
|
+
[tool.pytest.ini_options]
|
|
35
|
+
pythonpath = ["src"]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""API publica do pacote ``relatorios_sivwin``."""
|
|
2
|
+
|
|
3
|
+
from .contracts import SQLQuery
|
|
4
|
+
from .core import RelatoriosSivWin
|
|
5
|
+
from .dbapi import execute_sql_query, fetch_all, fetch_one
|
|
6
|
+
from .queries import RelatoriosGeraisQueries, RelatoriosServicosQueries
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"RelatoriosSivWin",
|
|
10
|
+
"RelatoriosGeraisQueries",
|
|
11
|
+
"RelatoriosServicosQueries",
|
|
12
|
+
"SQLQuery",
|
|
13
|
+
"execute_sql_query",
|
|
14
|
+
"fetch_all",
|
|
15
|
+
"fetch_one",
|
|
16
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Compatibilidade para constantes SQL historicamente publicas."""
|
|
2
|
+
|
|
3
|
+
from .queries._shared import (
|
|
4
|
+
CASE_SITUACAO_DECLARACAO,
|
|
5
|
+
CASE_SITUACAO_INSPECAO,
|
|
6
|
+
CASE_TIPO_INSPECAO,
|
|
7
|
+
CASE_TIPO_SERVICO,
|
|
8
|
+
_campos_contato,
|
|
9
|
+
sql_base,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"CASE_SITUACAO_DECLARACAO",
|
|
14
|
+
"CASE_SITUACAO_INSPECAO",
|
|
15
|
+
"CASE_TIPO_INSPECAO",
|
|
16
|
+
"CASE_TIPO_SERVICO",
|
|
17
|
+
"_campos_contato",
|
|
18
|
+
"sql_base",
|
|
19
|
+
]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Tipos compartilhados para representacao de consultas SQL."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from textwrap import dedent
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True, slots=True)
|
|
11
|
+
class SQLQuery:
|
|
12
|
+
"""Representacao ordenada de uma consulta SQL pronta para execucao.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
sql: A string da consulta SQL, com placeholders (%s) para os parametros.
|
|
16
|
+
name: Um nome opcional para a consulta, util para log e depuracao.
|
|
17
|
+
params: Uma tupla de parametros a serem interpolados na consulta.
|
|
18
|
+
database: Nome do banco usado ao renderizar a consulta para inspecao.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
sql: str
|
|
22
|
+
name: str = ""
|
|
23
|
+
params: tuple[Any, ...] = field(default_factory=tuple)
|
|
24
|
+
database: str = "otimiza"
|
|
25
|
+
|
|
26
|
+
def __post_init__(self) -> None:
|
|
27
|
+
"""Normaliza o SQL e garante que os parametros sejam uma tupla."""
|
|
28
|
+
object.__setattr__(self, "sql", dedent(self.sql).strip())
|
|
29
|
+
object.__setattr__(self, "params", tuple(self.params))
|
|
30
|
+
|
|
31
|
+
def to_sql_raw(self, database: str | None = None) -> str:
|
|
32
|
+
"""Retorna a consulta com os parametros interpolados para inspecao."""
|
|
33
|
+
selected_database = self.database if database is None else database
|
|
34
|
+
sql = self.sql
|
|
35
|
+
|
|
36
|
+
for param in self.params:
|
|
37
|
+
if param is None:
|
|
38
|
+
value = "NULL"
|
|
39
|
+
elif isinstance(param, str):
|
|
40
|
+
value = "'" + param.replace("'", "''") + "'"
|
|
41
|
+
elif isinstance(param, bool):
|
|
42
|
+
value = "1" if param else "0"
|
|
43
|
+
else:
|
|
44
|
+
value = str(param)
|
|
45
|
+
|
|
46
|
+
sql = sql.replace("%s", value, 1)
|
|
47
|
+
|
|
48
|
+
return f"USE [{selected_database}];\n\n{sql}"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Objeto principal de acesso aos relatorios SivWin."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .queries import RelatoriosGeraisQueries, RelatoriosServicosQueries
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RelatoriosSivWin:
|
|
9
|
+
"""Centraliza o acesso aos dominios de consulta do pacote."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, database: str = "otimiza") -> None:
|
|
12
|
+
"""Inicializa os modulos publicos do pacote."""
|
|
13
|
+
self.database = database
|
|
14
|
+
self.gerais = RelatoriosGeraisQueries(database=database)
|
|
15
|
+
self.servicos = RelatoriosServicosQueries(database=database)
|
|
16
|
+
|
|
17
|
+
self.relatorios_gerais = self.gerais
|
|
18
|
+
self.relatorios_servicos = self.servicos
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Helpers opcionais para executar consultas com cursores ja abertos."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from .contracts import SQLQuery
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def execute_sql_query(cursor: Any, query: SQLQuery) -> None:
|
|
11
|
+
"""Executa uma ``SQLQuery`` usando um cursor existente.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
cursor: Cursor DB-API compativel com ``execute(sql, params)``.
|
|
15
|
+
query: Consulta parametrizada a ser executada.
|
|
16
|
+
"""
|
|
17
|
+
cursor.execute(query.sql, query.params)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def fetch_all(cursor: Any, query: SQLQuery) -> list[dict[str, Any]]:
|
|
21
|
+
"""Executa uma consulta e retorna todas as linhas como dicionario.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
cursor: Cursor DB-API ja associado a uma conexao aberta.
|
|
25
|
+
query: Consulta parametrizada a ser executada.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Lista de dicionarios no formato ``{nome_coluna: valor}``.
|
|
29
|
+
"""
|
|
30
|
+
execute_sql_query(cursor, query)
|
|
31
|
+
|
|
32
|
+
columns = [column[0] for column in cursor.description]
|
|
33
|
+
return [dict(zip(columns, linha)) for linha in cursor.fetchall()]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def fetch_one(cursor: Any, query: SQLQuery) -> dict[str, Any] | None:
|
|
37
|
+
"""Executa uma consulta e retorna a primeira linha como dicionario.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
cursor: Cursor DB-API ja associado a uma conexao aberta.
|
|
41
|
+
query: Consulta parametrizada a ser executada.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Um dicionario com a primeira linha retornada ou ``None`` quando nao ha
|
|
45
|
+
resultados.
|
|
46
|
+
"""
|
|
47
|
+
execute_sql_query(cursor, query)
|
|
48
|
+
|
|
49
|
+
linha = cursor.fetchone()
|
|
50
|
+
if linha is None:
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
columns = [column[0] for column in cursor.description]
|
|
54
|
+
return dict(zip(columns, linha))
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Constantes e pequenos utilitarios SQL compartilhados pelo pacote."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from textwrap import dedent
|
|
5
|
+
|
|
6
|
+
CASE_SITUACAO_INSPECAO = """
|
|
7
|
+
CASE i.SituacaoVeiculo
|
|
8
|
+
WHEN 0 THEN 'Ativo'
|
|
9
|
+
WHEN 1 THEN 'Aprovado'
|
|
10
|
+
WHEN 2 THEN 'Reprovado'
|
|
11
|
+
WHEN 3 THEN 'Cancelada'
|
|
12
|
+
WHEN 4 THEN 'Vencida'
|
|
13
|
+
WHEN 5 THEN 'Em correcao'
|
|
14
|
+
WHEN 6 THEN 'Corrigida'
|
|
15
|
+
WHEN 7 THEN 'Reprovada pelo SISCSV'
|
|
16
|
+
ELSE 'Outros'
|
|
17
|
+
END
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
CASE_TIPO_SERVICO = """
|
|
21
|
+
CASE s.TipoServico
|
|
22
|
+
WHEN 'SR' THEN s.TipoCsvSerproNome
|
|
23
|
+
WHEN 'LG' THEN s.TipoLaudoNome
|
|
24
|
+
WHEN 'LS' THEN s.ProjetoSisLitIdentificacao
|
|
25
|
+
WHEN 'LD' THEN 'Laudo CSV Legado'
|
|
26
|
+
END
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
CASE_TIPO_INSPECAO = """
|
|
30
|
+
CASE s.TipoServico
|
|
31
|
+
WHEN 'SR' THEN 'Laudo CSV'
|
|
32
|
+
WHEN 'LD' THEN 'Laudo CSV Legado'
|
|
33
|
+
WHEN 'LG' THEN 'Laudo Geral'
|
|
34
|
+
WHEN 'LS' THEN 'Laudo SISLIT'
|
|
35
|
+
END
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
CASE_SITUACAO_DECLARACAO = """
|
|
39
|
+
CASE i.SituacaoVeiculo
|
|
40
|
+
WHEN 1 THEN 'APROVADO'
|
|
41
|
+
WHEN 2 THEN 'REPROVADO'
|
|
42
|
+
END
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def _campos_contato(alias: str, nome: str) -> str:
|
|
46
|
+
"""Retorna um bloco padrao de colunas de contato.
|
|
47
|
+
|
|
48
|
+
O bloco atende os cenarios recorrentes de proprietario, contratante e condutor. Os aliases seguem o padrao ``<nome><Campo>``.
|
|
49
|
+
Por exemplo, para o proprietario, ``proprietarioNome`` e ``proprietarioCidade``. Para o condutor, por exemplo
|
|
50
|
+
"""
|
|
51
|
+
documento_alias = f"{nome}CPF" if nome == "condutor" else f"{nome}CPFCNPJ"
|
|
52
|
+
|
|
53
|
+
colunas = [
|
|
54
|
+
f"{alias}.Nome_RazaoSocial AS [{nome}Nome]",
|
|
55
|
+
f"{alias}.UF AS [{nome}UF]",
|
|
56
|
+
f"{alias}.Municipio AS [{nome}Cidade]",
|
|
57
|
+
f"{alias}.Bairro AS [{nome}Bairro]",
|
|
58
|
+
f"{alias}.CEP AS [{nome}CEP]",
|
|
59
|
+
f"{alias}.Logradouro AS [{nome}Endereco]",
|
|
60
|
+
f"{alias}.Numero AS [{nome}Numero]",
|
|
61
|
+
f"{alias}.Complemento AS [{nome}Complemento]",
|
|
62
|
+
f"{alias}.CPF_CNPJ AS [{documento_alias}]",
|
|
63
|
+
f"CONCAT({alias}.Telefone1DDD, {alias}.Telefone1) AS [{nome}Telefone]",
|
|
64
|
+
f"CONCAT({alias}.Telefone2DDD, {alias}.Telefone2) AS [{nome}Celular]",
|
|
65
|
+
f"{alias}.Email AS [{nome}Email]",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
return dedent(",\n".join(colunas)).strip()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def sql_base() -> str:
|
|
72
|
+
"""Retorna o SQL base do relatorio completo de servicos."""
|
|
73
|
+
return f"""
|
|
74
|
+
SELECT
|
|
75
|
+
s.ServicoNumero AS [OS],
|
|
76
|
+
tc.Numero AS [RI],
|
|
77
|
+
i.Id AS [idInspecao],
|
|
78
|
+
CONVERT(CHAR, s.AberturaDataHora, 103) AS [dataAberturaOS],
|
|
79
|
+
i.AberturaDataHora AS [dataAberturaInspecao],
|
|
80
|
+
s.TipoServico AS [codigoServico],
|
|
81
|
+
{CASE_TIPO_SERVICO} AS [tipoServico],
|
|
82
|
+
{CASE_TIPO_INSPECAO} AS [tipoInspecao],
|
|
83
|
+
CONVERT(VARCHAR(20), i.NumeroCsvSerpro) AS [numeroCSV],
|
|
84
|
+
esc.Codigo AS [numeroEscopo],
|
|
85
|
+
esc.Nome AS [nomeEscopo],
|
|
86
|
+
i.InspecaoEmissaoDataHora AS [dataConclusaoInspecao],
|
|
87
|
+
i.ConcluidoDataHora AS [dataFinalizacaoInspecao],
|
|
88
|
+
i.CsvCorrigidoDataHora AS [dataCorrecaoInspecao],
|
|
89
|
+
i.InspecaoVencimentoData AS [dataVencimentoDocumento],
|
|
90
|
+
nf.Numero AS [NF],
|
|
91
|
+
s.ValorBrutoServico AS [valorBruto],
|
|
92
|
+
s.ValorServico AS [valorLiquido],
|
|
93
|
+
fdt.Nome AS [formaPagamento],
|
|
94
|
+
i.CsvPdfArquivoNome AS [csvArquivo],
|
|
95
|
+
i.SisLitNomeArquivoLaudo AS [sislitArquivo],
|
|
96
|
+
tp.Portaria AS [portariaInmetro],
|
|
97
|
+
vw_esc.Descricao AS [descricaoEscopo],
|
|
98
|
+
{CASE_SITUACAO_INSPECAO} AS [situacaoInspecao],
|
|
99
|
+
s.Placa AS [placa],
|
|
100
|
+
s.Chassi AS [chassi],
|
|
101
|
+
charac.EspecieVeiculo AS [especieVeiculo],
|
|
102
|
+
charac.TipoVeiculo AS [tipoVeiculo],
|
|
103
|
+
charac.MarcaModelo AS [marcaModelo],
|
|
104
|
+
charac.AnoFabricacao AS [anoFabricacao],
|
|
105
|
+
charac.AnoModelo AS [anoModelo],
|
|
106
|
+
charac.Carrocaria AS [carrocaria],
|
|
107
|
+
g.SeloNumero AS [numeroSelo],
|
|
108
|
+
p.ConfirmadoCpf AS [cpfInspetor],
|
|
109
|
+
p.ConfirmadoNome AS [nomeInspetor],
|
|
110
|
+
i.ConfirmadoCpf AS [cpfEngenheiro],
|
|
111
|
+
i.ConfirmadoNome AS [nomeEngenheiro],
|
|
112
|
+
ind.Nome_RazaoSocial AS [indicacaoNome],
|
|
113
|
+
{_campos_contato("prop", "proprietario")},
|
|
114
|
+
{_campos_contato("cont", "contratante")},
|
|
115
|
+
{_campos_contato("cond", "condutor")}
|
|
116
|
+
FROM dbo.Servicos AS s
|
|
117
|
+
INNER JOIN dbo.Inspecoes AS i
|
|
118
|
+
ON s.Id = i.ServicoId
|
|
119
|
+
LEFT JOIN dbo.TabelaCertificado AS tc
|
|
120
|
+
ON tc.ServicoId = s.Id
|
|
121
|
+
LEFT JOIN dbo.SivWin_CaracteristicasSerpro AS charac
|
|
122
|
+
ON s.CaracteristicaAtualSerproId = charac.Id
|
|
123
|
+
LEFT JOIN dbo.TabelaPortarias AS tp
|
|
124
|
+
ON s.PortariaId = tp.Id
|
|
125
|
+
LEFT JOIN dbo.NotasFiscais AS nf
|
|
126
|
+
ON s.NotaFiscalId = nf.Id
|
|
127
|
+
LEFT JOIN dbo.Financeiro_Lancamentos AS fl
|
|
128
|
+
ON s.ServicoNumero = fl.NumeroPedido
|
|
129
|
+
AND fl.SistemaRemoto = 'SIVWIN'
|
|
130
|
+
LEFT JOIN dbo.Financeiro_DocumentosTipos AS fdt
|
|
131
|
+
ON fdt.Id = fl.DocumentoTipo
|
|
132
|
+
LEFT JOIN dbo.Principal_Indicacoes AS ind
|
|
133
|
+
ON s.IndicacaoId = ind.Id
|
|
134
|
+
LEFT JOIN dbo.Gnv AS g
|
|
135
|
+
ON g.ServicoId = s.Id
|
|
136
|
+
LEFT JOIN dbo.Processos AS p
|
|
137
|
+
ON p.Id = i.ProcessoId
|
|
138
|
+
INNER JOIN dbo.Principal_Contatos AS prop
|
|
139
|
+
ON s.proprietarioId = prop.Id
|
|
140
|
+
INNER JOIN dbo.Principal_Contatos AS cont
|
|
141
|
+
ON s.ClienteId = cont.Id
|
|
142
|
+
INNER JOIN dbo.Principal_Contatos AS cond
|
|
143
|
+
ON s.CondutorId = cond.Id
|
|
144
|
+
LEFT JOIN dbo.SivWin_ProcedimentosEscopoServicoSerpro AS pess
|
|
145
|
+
ON s.Id = pess.ServicoId
|
|
146
|
+
LEFT JOIN dbo.SivWin_ProcedimentosEscoposSerpro AS pes
|
|
147
|
+
ON pess.ProcedimentoEscopoId = pes.Id
|
|
148
|
+
LEFT JOIN dbo.SivWin_TabelaEscoposSerpro AS esc
|
|
149
|
+
ON pes.EscopoDenatranId = esc.Id
|
|
150
|
+
LEFT JOIN dbo.RelatoriosSiv_SivWin_InspecoesPorEscopoInmetro_View AS vw_esc
|
|
151
|
+
ON s.ServicoNumero = vw_esc.ServicoNumero
|
|
152
|
+
"""
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""Consultas SQL de relatorios gerais do ecossistema SivWin."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from ..contracts import SQLQuery
|
|
6
|
+
from ._shared import (
|
|
7
|
+
CASE_SITUACAO_DECLARACAO,
|
|
8
|
+
CASE_TIPO_INSPECAO,
|
|
9
|
+
CASE_TIPO_SERVICO,
|
|
10
|
+
_campos_contato,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RelatoriosGeraisQueries:
|
|
15
|
+
"""Agrupa relatorios gerais nao concentrados em um unico subdominio."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, database: str = "otimiza") -> None:
|
|
18
|
+
self.database = database
|
|
19
|
+
|
|
20
|
+
def relatorio_declaracoes(self, os_numero: int) -> SQLQuery:
|
|
21
|
+
"""Gera a consulta usada na emissao da declaracao por OS."""
|
|
22
|
+
sql = f"""
|
|
23
|
+
SELECT
|
|
24
|
+
s.ServicoNumero AS [OS],
|
|
25
|
+
tc.Numero AS [RI],
|
|
26
|
+
CONVERT(VARCHAR(10), s.AberturaDataHora, 103) AS [dataAberturaOS],
|
|
27
|
+
CONVERT(VARCHAR(10), i.AberturaDataHora, 103) AS [dataAberturaInspecao],
|
|
28
|
+
i.NumeroCsvSerpro AS [numeroCSV],
|
|
29
|
+
esc.Codigo AS [numeroEscopo],
|
|
30
|
+
esc.Nome AS [escopo],
|
|
31
|
+
{CASE_TIPO_INSPECAO} AS [tipoInspecao],
|
|
32
|
+
{CASE_TIPO_SERVICO} AS [tipoLaudo],
|
|
33
|
+
tp.Portaria AS [portariaInmetro],
|
|
34
|
+
{CASE_SITUACAO_DECLARACAO} AS [situacaoInspecao],
|
|
35
|
+
s.ValorBrutoServico AS [valorBruto],
|
|
36
|
+
nf.Numero AS [NF],
|
|
37
|
+
fdt.Nome AS [formaPagamento],
|
|
38
|
+
s.Placa AS [placa],
|
|
39
|
+
s.Chassi AS [chassi],
|
|
40
|
+
charac.Carrocaria AS [carrocaria],
|
|
41
|
+
charac.MarcaModelo AS [marcaModelo],
|
|
42
|
+
charac.EspecieVeiculo AS [especieVeiculo],
|
|
43
|
+
charac.TipoVeiculo AS [tipoVeiculo],
|
|
44
|
+
charac.AnoFabricacao AS [anoFabricacao],
|
|
45
|
+
charac.AnoModelo AS [anoModelo],
|
|
46
|
+
{_campos_contato("prop", "proprietario")},
|
|
47
|
+
{_campos_contato("cont", "contratante")},
|
|
48
|
+
{_campos_contato("cond", "condutor")}
|
|
49
|
+
FROM dbo.Servicos AS s
|
|
50
|
+
INNER JOIN dbo.Inspecoes AS i
|
|
51
|
+
ON s.Id = i.ServicoId
|
|
52
|
+
LEFT JOIN dbo.TabelaCertificado AS tc
|
|
53
|
+
ON tc.ServicoId = s.Id
|
|
54
|
+
LEFT JOIN dbo.SivWin_CaracteristicasSerpro AS charac
|
|
55
|
+
ON s.CaracteristicaAtualSerproId = charac.Id
|
|
56
|
+
LEFT JOIN dbo.TabelaPortarias AS tp
|
|
57
|
+
ON s.PortariaId = tp.Id
|
|
58
|
+
LEFT JOIN dbo.NotasFiscais AS nf
|
|
59
|
+
ON s.NotaFiscalId = nf.Id
|
|
60
|
+
LEFT JOIN dbo.Financeiro_Lancamentos AS fl
|
|
61
|
+
ON s.ServicoNumero = fl.NumeroPedido
|
|
62
|
+
AND fl.SistemaRemoto = 'SIVWIN'
|
|
63
|
+
LEFT JOIN dbo.Financeiro_DocumentosTipos AS fdt
|
|
64
|
+
ON fdt.Id = fl.DocumentoTipo
|
|
65
|
+
INNER JOIN dbo.Principal_Contatos AS prop
|
|
66
|
+
ON s.proprietarioId = prop.Id
|
|
67
|
+
INNER JOIN dbo.Principal_Contatos AS cont
|
|
68
|
+
ON s.ClienteId = cont.Id
|
|
69
|
+
INNER JOIN dbo.Principal_Contatos AS cond
|
|
70
|
+
ON s.CondutorId = cond.Id
|
|
71
|
+
LEFT JOIN dbo.SivWin_ProcedimentosEscopoServicoSerpro AS pess
|
|
72
|
+
ON s.Id = pess.ServicoId
|
|
73
|
+
LEFT JOIN dbo.SivWin_ProcedimentosEscoposSerpro AS pes
|
|
74
|
+
ON pess.ProcedimentoEscopoId = pes.Id
|
|
75
|
+
LEFT JOIN dbo.SivWin_TabelaEscoposSerpro AS esc
|
|
76
|
+
ON pes.EscopoDenatranId = esc.Id
|
|
77
|
+
WHERE s.ServicoNumero = %s
|
|
78
|
+
ORDER BY OS ASC
|
|
79
|
+
"""
|
|
80
|
+
return SQLQuery(
|
|
81
|
+
sql=sql,
|
|
82
|
+
params=(os_numero,),
|
|
83
|
+
name="relatorio_declaracoes",
|
|
84
|
+
database=self.database,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def relatorio_art(self, os_numero_inicio: int, os_numero_fim: int) -> SQLQuery:
|
|
88
|
+
"""Gera a consulta do relatorio para emissao de ART por faixa de OS."""
|
|
89
|
+
sql = f"""
|
|
90
|
+
WITH cte_art AS (
|
|
91
|
+
SELECT
|
|
92
|
+
s.ServicoNumero AS [OS],
|
|
93
|
+
tc.Numero AS [RI],
|
|
94
|
+
CONVERT(CHAR, s.AberturaDataHora, 103) AS [dataAberturaOS],
|
|
95
|
+
{CASE_TIPO_SERVICO} AS [tipoLaudo],
|
|
96
|
+
CONVERT(VARCHAR(20), i.NumeroCsvSerpro) AS [numeroCSV],
|
|
97
|
+
i.InspecaoEmissaoDataHora AS [dataConclusaoInspecao],
|
|
98
|
+
s.Placa AS [placa],
|
|
99
|
+
s.Chassi AS [chassi],
|
|
100
|
+
ROW_NUMBER() OVER (
|
|
101
|
+
PARTITION BY s.ServicoNumero
|
|
102
|
+
ORDER BY i.InspecaoEmissaoDataHora ASC, i.Id ASC
|
|
103
|
+
) AS rn
|
|
104
|
+
FROM dbo.Servicos AS s
|
|
105
|
+
INNER JOIN dbo.Inspecoes AS i
|
|
106
|
+
ON s.Id = i.ServicoId
|
|
107
|
+
LEFT JOIN dbo.TabelaCertificado AS tc
|
|
108
|
+
ON tc.ServicoId = s.Id
|
|
109
|
+
WHERE
|
|
110
|
+
s.ServicoNumero BETWEEN %s AND %s
|
|
111
|
+
AND s.TipoServico = %s
|
|
112
|
+
AND i.SituacaoVeiculo IN (1, 2, 6)
|
|
113
|
+
)
|
|
114
|
+
SELECT
|
|
115
|
+
OS, RI, dataAberturaOS, tipoLaudo, numeroCSV, dataConclusaoInspecao, placa, chassi
|
|
116
|
+
FROM cte_art
|
|
117
|
+
WHERE rn = 1
|
|
118
|
+
ORDER BY OS ASC
|
|
119
|
+
"""
|
|
120
|
+
params = (os_numero_inicio, os_numero_fim, "SR")
|
|
121
|
+
return SQLQuery(
|
|
122
|
+
sql=sql,
|
|
123
|
+
params=params,
|
|
124
|
+
name="relatorio_art",
|
|
125
|
+
database=self.database,
|
|
126
|
+
)
|
|
127
|
+
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"""Consultas SQL do dominio de servicos e inspecoes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from ..contracts import SQLQuery
|
|
6
|
+
from ._shared import sql_base
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RelatoriosServicosQueries:
|
|
10
|
+
"""Agrupa consultas relacionadas ao ciclo de vida dos servicos."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, database: str = "otimiza") -> None:
|
|
13
|
+
self.database = database
|
|
14
|
+
self._sql_base = sql_base
|
|
15
|
+
|
|
16
|
+
def relatorio_completo(self, start: str, end: str) -> SQLQuery:
|
|
17
|
+
"""Gera o relatorio completo de servicos abertos no periodo.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
start: Data inicial no formato ``YYYY-MM-DD``.
|
|
21
|
+
end: Data final no formato ``YYYY-MM-DD``.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Consulta parametrizada com servicos cuja OS foi aberta entre
|
|
25
|
+
``startT00:00:00`` e ``endT23:59:59``.
|
|
26
|
+
"""
|
|
27
|
+
sql = f"""
|
|
28
|
+
{self._sql_base()}
|
|
29
|
+
WHERE
|
|
30
|
+
s.AberturaDataHora BETWEEN %s AND %s
|
|
31
|
+
ORDER BY OS ASC
|
|
32
|
+
"""
|
|
33
|
+
params = (f"{start}T00:00:00", f"{end}T23:59:59")
|
|
34
|
+
return SQLQuery(
|
|
35
|
+
sql=sql,
|
|
36
|
+
params=params,
|
|
37
|
+
name="relatorio_completo",
|
|
38
|
+
database=self.database,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def cis_emitidos(self, start: str, end: str) -> SQLQuery:
|
|
42
|
+
"""Gera a consulta de CIs/RIs emitidos para servicos CSV abertos no periodo.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
start: Data inicial no formato ``YYYY-MM-DD``.
|
|
46
|
+
end: Data final no formato ``YYYY-MM-DD``.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Consulta parametrizada com o ultimo registro por OS que possui RI,
|
|
50
|
+
numero CSV e situacao aprovada ou corrigida.
|
|
51
|
+
"""
|
|
52
|
+
sql = f"""
|
|
53
|
+
WITH base AS (
|
|
54
|
+
{self._sql_base()}
|
|
55
|
+
WHERE
|
|
56
|
+
tc.Numero IS NOT NULL
|
|
57
|
+
AND s.TipoServico = %s
|
|
58
|
+
AND i.SituacaoVeiculo IN (1, 6)
|
|
59
|
+
AND i.NumeroCsvSerpro IS NOT NULL
|
|
60
|
+
AND s.AberturaDataHora BETWEEN %s AND %s
|
|
61
|
+
)
|
|
62
|
+
SELECT *
|
|
63
|
+
FROM (
|
|
64
|
+
SELECT TOP 1 WITH TIES *
|
|
65
|
+
FROM base
|
|
66
|
+
ORDER BY ROW_NUMBER() OVER (
|
|
67
|
+
PARTITION BY OS
|
|
68
|
+
ORDER BY
|
|
69
|
+
dataConclusaoInspecao DESC,
|
|
70
|
+
RI DESC
|
|
71
|
+
)
|
|
72
|
+
) AS consulta
|
|
73
|
+
ORDER BY OS ASC
|
|
74
|
+
"""
|
|
75
|
+
params = ("SR", f"{start}T00:00:00", f"{end}T23:59:59")
|
|
76
|
+
return SQLQuery(
|
|
77
|
+
sql=sql,
|
|
78
|
+
params=params,
|
|
79
|
+
name="cis_emitidos",
|
|
80
|
+
database=self.database,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
def servicos_aprovados(self, start: str, end: str) -> SQLQuery:
|
|
84
|
+
"""Gera a consulta de servicos aprovados pela conclusao da inspecao.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
start: Data inicial no formato ``YYYY-MM-DD``.
|
|
88
|
+
end: Data final no formato ``YYYY-MM-DD``.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Consulta parametrizada com o ultimo registro por OS cuja inspecao
|
|
92
|
+
foi emitida/concluida entre ``startT00:00:00`` e ``endT23:59:59``
|
|
93
|
+
e esteja aprovada ou corrigida.
|
|
94
|
+
"""
|
|
95
|
+
sql = f"""
|
|
96
|
+
WITH base AS (
|
|
97
|
+
{self._sql_base()}
|
|
98
|
+
WHERE
|
|
99
|
+
i.InspecaoEmissaoDataHora BETWEEN %s AND %s
|
|
100
|
+
AND i.SituacaoVeiculo IN (1, 6)
|
|
101
|
+
)
|
|
102
|
+
SELECT *
|
|
103
|
+
FROM (
|
|
104
|
+
SELECT TOP 1 WITH TIES *
|
|
105
|
+
FROM base
|
|
106
|
+
ORDER BY ROW_NUMBER() OVER (
|
|
107
|
+
PARTITION BY OS
|
|
108
|
+
ORDER BY
|
|
109
|
+
dataConclusaoInspecao DESC,
|
|
110
|
+
RI DESC
|
|
111
|
+
)
|
|
112
|
+
) AS consulta
|
|
113
|
+
ORDER BY OS ASC
|
|
114
|
+
"""
|
|
115
|
+
params = (f"{start}T00:00:00", f"{end}T23:59:59")
|
|
116
|
+
return SQLQuery(
|
|
117
|
+
sql=sql,
|
|
118
|
+
params=params,
|
|
119
|
+
name="servicos_aprovados",
|
|
120
|
+
database=self.database,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
def servicos_reprovados(self, start: str, end: str) -> SQLQuery:
|
|
124
|
+
"""Gera a consulta de servicos reprovados abertos no periodo.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
start: Data inicial no formato ``YYYY-MM-DD``.
|
|
128
|
+
end: Data final no formato ``YYYY-MM-DD``.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
Consulta parametrizada com o ultimo registro reprovado por OS cuja
|
|
132
|
+
abertura ocorreu entre ``startT00:00:00`` e ``endT23:59:59``.
|
|
133
|
+
"""
|
|
134
|
+
sql = f"""
|
|
135
|
+
WITH base AS (
|
|
136
|
+
{self._sql_base()}
|
|
137
|
+
WHERE
|
|
138
|
+
s.AberturaDataHora BETWEEN %s AND %s
|
|
139
|
+
AND i.SituacaoVeiculo IN (2)
|
|
140
|
+
)
|
|
141
|
+
SELECT *
|
|
142
|
+
FROM (
|
|
143
|
+
SELECT TOP 1 WITH TIES *
|
|
144
|
+
FROM base
|
|
145
|
+
ORDER BY ROW_NUMBER() OVER (
|
|
146
|
+
PARTITION BY OS
|
|
147
|
+
ORDER BY
|
|
148
|
+
dataConclusaoInspecao DESC,
|
|
149
|
+
RI DESC
|
|
150
|
+
)
|
|
151
|
+
) AS consulta
|
|
152
|
+
ORDER BY OS ASC
|
|
153
|
+
"""
|
|
154
|
+
params = (f"{start}T00:00:00", f"{end}T23:59:59")
|
|
155
|
+
return SQLQuery(
|
|
156
|
+
sql=sql,
|
|
157
|
+
params=params,
|
|
158
|
+
name="servicos_reprovados",
|
|
159
|
+
database=self.database,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
def servicos_inmetro(self, start: str, end: str) -> SQLQuery:
|
|
163
|
+
"""Gera a consulta de servicos INMETRO/CSV abertos no periodo.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
start: Data inicial no formato ``YYYY-MM-DD``.
|
|
167
|
+
end: Data final no formato ``YYYY-MM-DD``.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Consulta parametrizada com o ultimo registro por OS de servicos do
|
|
171
|
+
tipo ``SR`` abertos no intervalo informado.
|
|
172
|
+
"""
|
|
173
|
+
sql = f"""
|
|
174
|
+
WITH base AS (
|
|
175
|
+
{self._sql_base()}
|
|
176
|
+
WHERE
|
|
177
|
+
s.AberturaDataHora BETWEEN %s AND %s
|
|
178
|
+
AND s.TipoServico IN ('SR')
|
|
179
|
+
)
|
|
180
|
+
SELECT *
|
|
181
|
+
FROM (
|
|
182
|
+
SELECT TOP 1 WITH TIES *
|
|
183
|
+
FROM base
|
|
184
|
+
ORDER BY ROW_NUMBER() OVER (
|
|
185
|
+
PARTITION BY OS
|
|
186
|
+
ORDER BY
|
|
187
|
+
dataConclusaoInspecao DESC,
|
|
188
|
+
RI DESC
|
|
189
|
+
)
|
|
190
|
+
) AS consulta
|
|
191
|
+
ORDER BY OS ASC
|
|
192
|
+
"""
|
|
193
|
+
params = (f"{start}T00:00:00", f"{end}T23:59:59")
|
|
194
|
+
return SQLQuery(
|
|
195
|
+
sql=sql,
|
|
196
|
+
params=params,
|
|
197
|
+
name="servicos_inmetro",
|
|
198
|
+
database=self.database,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
def servicos_sislit(self, start: str, end: str) -> SQLQuery:
|
|
202
|
+
"""Gera a consulta de servicos SISLIT abertos no periodo.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
start: Data inicial no formato ``YYYY-MM-DD``.
|
|
206
|
+
end: Data final no formato ``YYYY-MM-DD``.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
Consulta parametrizada com o ultimo registro por OS de servicos do
|
|
210
|
+
tipo ``LS`` abertos no intervalo informado.
|
|
211
|
+
"""
|
|
212
|
+
sql = f"""
|
|
213
|
+
WITH base AS (
|
|
214
|
+
{self._sql_base()}
|
|
215
|
+
WHERE
|
|
216
|
+
s.AberturaDataHora BETWEEN %s AND %s
|
|
217
|
+
AND s.TipoServico IN ('LS')
|
|
218
|
+
)
|
|
219
|
+
SELECT *
|
|
220
|
+
FROM (
|
|
221
|
+
SELECT TOP 1 WITH TIES *
|
|
222
|
+
FROM base
|
|
223
|
+
ORDER BY ROW_NUMBER() OVER (
|
|
224
|
+
PARTITION BY OS
|
|
225
|
+
ORDER BY
|
|
226
|
+
dataConclusaoInspecao DESC,
|
|
227
|
+
RI DESC
|
|
228
|
+
)
|
|
229
|
+
) AS consulta
|
|
230
|
+
ORDER BY OS ASC
|
|
231
|
+
"""
|
|
232
|
+
params = (f"{start}T00:00:00", f"{end}T23:59:59")
|
|
233
|
+
return SQLQuery(
|
|
234
|
+
sql=sql,
|
|
235
|
+
params=params,
|
|
236
|
+
name="servicos_sislit",
|
|
237
|
+
database=self.database,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
def servicos_laudo_geral(self, start: str, end: str) -> SQLQuery:
|
|
241
|
+
"""Gera a consulta de servicos de Laudo Geral abertos no periodo.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
start: Data inicial no formato ``YYYY-MM-DD``.
|
|
245
|
+
end: Data final no formato ``YYYY-MM-DD``.
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
Consulta parametrizada com o ultimo registro por OS de servicos do
|
|
249
|
+
tipo ``LG`` abertos no intervalo informado.
|
|
250
|
+
"""
|
|
251
|
+
sql = f"""
|
|
252
|
+
WITH base AS (
|
|
253
|
+
{self._sql_base()}
|
|
254
|
+
WHERE
|
|
255
|
+
s.AberturaDataHora BETWEEN %s AND %s
|
|
256
|
+
AND s.TipoServico IN ('LG')
|
|
257
|
+
)
|
|
258
|
+
SELECT *
|
|
259
|
+
FROM (
|
|
260
|
+
SELECT TOP 1 WITH TIES *
|
|
261
|
+
FROM base
|
|
262
|
+
ORDER BY ROW_NUMBER() OVER (
|
|
263
|
+
PARTITION BY OS
|
|
264
|
+
ORDER BY
|
|
265
|
+
dataConclusaoInspecao DESC,
|
|
266
|
+
RI DESC
|
|
267
|
+
)
|
|
268
|
+
) AS consulta
|
|
269
|
+
ORDER BY OS ASC
|
|
270
|
+
"""
|
|
271
|
+
params = (f"{start}T00:00:00", f"{end}T23:59:59")
|
|
272
|
+
return SQLQuery(
|
|
273
|
+
sql=sql,
|
|
274
|
+
params=params,
|
|
275
|
+
name="servicos_laudo_geral",
|
|
276
|
+
database=self.database,
|
|
277
|
+
)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: relatorios-sivwin
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Consultas SQL reutilizaveis para o ecossistema SivWin/Otimiza.
|
|
5
|
+
Keywords: sql,django,sivwin,otimiza,relatorios
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Topic :: Database
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: build; extra == "dev"
|
|
17
|
+
Requires-Dist: pytest; extra == "dev"
|
|
18
|
+
Requires-Dist: twine; extra == "dev"
|
|
19
|
+
|
|
20
|
+
# Relatorios SivWin
|
|
21
|
+
|
|
22
|
+
Biblioteca Python para centralizar consultas SQL do ecossistema SivWin/Otimiza.
|
|
23
|
+
|
|
24
|
+
## Instalacao
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install relatorios-sivwin
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
O nome publicado usa hifen, mas o import Python usa underscore:
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from relatorios_sivwin import RelatoriosSivWin
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Objetivo
|
|
37
|
+
|
|
38
|
+
- Concentrar consultas SQL em um unico lugar.
|
|
39
|
+
- Reduzir duplicacao de regras SQL entre projetos.
|
|
40
|
+
- Facilitar o uso das mesmas consultas em Django, scripts e outros servicos.
|
|
41
|
+
|
|
42
|
+
## Estrutura
|
|
43
|
+
|
|
44
|
+
- `relatorios_sivwin.SQLQuery`: contrato padrao para consultas parametrizadas.
|
|
45
|
+
- `relatorios_sivwin.RelatoriosSivWin`: hub principal de acesso aos dominios.
|
|
46
|
+
- `relatorios_sivwin.RelatoriosGeraisQueries`: consultas gerais, como declaracoes e ARTs.
|
|
47
|
+
- `relatorios_sivwin.RelatoriosServicosQueries`: consultas do dominio de servicos e inspecoes.
|
|
48
|
+
- `relatorios_sivwin.dbapi`: helpers opcionais para executar uma `SQLQuery` com um cursor ja aberto.
|
|
49
|
+
|
|
50
|
+
## Exemplo Rapido
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from relatorios_sivwin import RelatoriosSivWin
|
|
54
|
+
|
|
55
|
+
relatorios = RelatoriosSivWin()
|
|
56
|
+
consulta = relatorios.gerais.relatorio_art(120000, 120999)
|
|
57
|
+
|
|
58
|
+
print(consulta.sql)
|
|
59
|
+
print(consulta.params)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Por padrao, a representacao SQL bruta usa `otimiza` como banco:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
relatorios = RelatoriosSivWin(database="sivwin_homolog")
|
|
66
|
+
consulta = relatorios.servicos.relatorio_completo("2026-02-01", "2026-02-28")
|
|
67
|
+
|
|
68
|
+
print(consulta.to_sql_raw())
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Uso Com Django
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from django.db import connections
|
|
75
|
+
from relatorios_sivwin import RelatoriosSivWin, fetch_all
|
|
76
|
+
|
|
77
|
+
relatorios = RelatoriosSivWin()
|
|
78
|
+
consulta = relatorios.servicos.relatorio_completo("2026-02-01", "2026-02-28")
|
|
79
|
+
|
|
80
|
+
with connections["otimiza"].cursor() as cursor:
|
|
81
|
+
dados = fetch_all(cursor, consulta)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Nesse formato:
|
|
85
|
+
|
|
86
|
+
- o Django continua responsavel pela conexao;
|
|
87
|
+
- o pacote so define a consulta;
|
|
88
|
+
- o mesmo objeto pode ser reutilizado em outros contextos.
|
|
89
|
+
|
|
90
|
+
## Uso Fora Do Django
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from relatorios_sivwin import RelatoriosSivWin
|
|
94
|
+
|
|
95
|
+
relatorios = RelatoriosSivWin()
|
|
96
|
+
consulta = relatorios.gerais.relatorio_declaracoes(123456)
|
|
97
|
+
|
|
98
|
+
cursor.execute(consulta.sql, consulta.params)
|
|
99
|
+
linhas = cursor.fetchall()
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Principios Do Pacote
|
|
103
|
+
|
|
104
|
+
- Sem acoplamento com conexao, `.env` ou credenciais.
|
|
105
|
+
- Sem dependencias pesadas para gerar SQL.
|
|
106
|
+
- Metodos de dominio retornam `SQLQuery`.
|
|
107
|
+
- Queries sempre parametrizadas, sem interpolacao direta de valores.
|
|
108
|
+
- Cada relatorio relevante tem sua propria SQL, escrita de forma explicita.
|
|
109
|
+
- Reaproveitamento existe apenas onde ele realmente simplifica, como constantes SQL e bloco de contato.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/relatorios_sivwin/__init__.py
|
|
4
|
+
src/relatorios_sivwin/constants.py
|
|
5
|
+
src/relatorios_sivwin/contracts.py
|
|
6
|
+
src/relatorios_sivwin/core.py
|
|
7
|
+
src/relatorios_sivwin/dbapi.py
|
|
8
|
+
src/relatorios_sivwin.egg-info/PKG-INFO
|
|
9
|
+
src/relatorios_sivwin.egg-info/SOURCES.txt
|
|
10
|
+
src/relatorios_sivwin.egg-info/dependency_links.txt
|
|
11
|
+
src/relatorios_sivwin.egg-info/requires.txt
|
|
12
|
+
src/relatorios_sivwin.egg-info/top_level.txt
|
|
13
|
+
src/relatorios_sivwin/queries/__init__.py
|
|
14
|
+
src/relatorios_sivwin/queries/_shared.py
|
|
15
|
+
src/relatorios_sivwin/queries/gerais.py
|
|
16
|
+
src/relatorios_sivwin/queries/servicos.py
|
|
17
|
+
tests/test_public_api.py
|
|
18
|
+
tests/test_queries.py
|
|
19
|
+
tests/test_sql_query.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
relatorios_sivwin
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from relatorios_sivwin import (
|
|
2
|
+
RelatoriosGeraisQueries,
|
|
3
|
+
RelatoriosServicosQueries,
|
|
4
|
+
RelatoriosSivWin,
|
|
5
|
+
SQLQuery,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_public_api_exports_main_objects() -> None:
|
|
10
|
+
relatorios = RelatoriosSivWin()
|
|
11
|
+
|
|
12
|
+
assert relatorios.database == "otimiza"
|
|
13
|
+
assert isinstance(relatorios.gerais, RelatoriosGeraisQueries)
|
|
14
|
+
assert isinstance(relatorios.servicos, RelatoriosServicosQueries)
|
|
15
|
+
|
|
16
|
+
assert relatorios.relatorios_gerais is relatorios.gerais
|
|
17
|
+
assert relatorios.relatorios_servicos is relatorios.servicos
|
|
18
|
+
assert SQLQuery(sql="SELECT 1").sql == "SELECT 1"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_public_api_accepts_custom_database() -> None:
|
|
22
|
+
relatorios = RelatoriosSivWin(database="sivwin_homolog")
|
|
23
|
+
|
|
24
|
+
assert relatorios.database == "sivwin_homolog"
|
|
25
|
+
assert relatorios.gerais.database == "sivwin_homolog"
|
|
26
|
+
assert relatorios.servicos.database == "sivwin_homolog"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from relatorios_sivwin import RelatoriosSivWin, SQLQuery
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_servicos_query_returns_parameterized_sql_query() -> None:
|
|
5
|
+
relatorios = RelatoriosSivWin(database="sivwin_homolog")
|
|
6
|
+
|
|
7
|
+
query = relatorios.servicos.relatorio_completo("2026-03-01", "2026-03-31")
|
|
8
|
+
|
|
9
|
+
assert isinstance(query, SQLQuery)
|
|
10
|
+
assert query.name == "relatorio_completo"
|
|
11
|
+
assert query.database == "sivwin_homolog"
|
|
12
|
+
assert query.params == ("2026-03-01T00:00:00", "2026-03-31T23:59:59")
|
|
13
|
+
assert "s.AberturaDataHora BETWEEN %s AND %s" in query.sql
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_gerais_query_returns_parameterized_sql_query() -> None:
|
|
17
|
+
relatorios = RelatoriosSivWin()
|
|
18
|
+
|
|
19
|
+
query = relatorios.gerais.relatorio_art(120000, 120999)
|
|
20
|
+
|
|
21
|
+
assert isinstance(query, SQLQuery)
|
|
22
|
+
assert query.name == "relatorio_art"
|
|
23
|
+
assert query.params == (120000, 120999, "SR")
|
|
24
|
+
assert "s.ServicoNumero BETWEEN %s AND %s" in query.sql
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from relatorios_sivwin import SQLQuery
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_sql_query_normalizes_sql_and_params() -> None:
|
|
5
|
+
query = SQLQuery(
|
|
6
|
+
sql="""
|
|
7
|
+
SELECT *
|
|
8
|
+
FROM dbo.Servicos
|
|
9
|
+
WHERE ServicoNumero = %s
|
|
10
|
+
""",
|
|
11
|
+
params=[123],
|
|
12
|
+
name="servico_por_os",
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
assert query.name == "servico_por_os"
|
|
16
|
+
assert query.database == "otimiza"
|
|
17
|
+
assert query.sql.startswith("SELECT *")
|
|
18
|
+
assert query.params == (123,)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_sql_query_to_sql_raw_quotes_values_for_inspection() -> None:
|
|
22
|
+
query = SQLQuery(
|
|
23
|
+
sql="SELECT * FROM exemplo WHERE nome = %s AND ativo = %s AND valor IS %s",
|
|
24
|
+
params=("D'Agua", True, None),
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
assert query.to_sql_raw("teste") == (
|
|
28
|
+
"USE [teste];\n\n"
|
|
29
|
+
"SELECT * FROM exemplo WHERE nome = 'D''Agua' AND ativo = 1 "
|
|
30
|
+
"AND valor IS NULL"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_sql_query_to_sql_raw_uses_query_database_by_default() -> None:
|
|
35
|
+
query = SQLQuery(sql="SELECT 1", database="sivwin_homolog")
|
|
36
|
+
|
|
37
|
+
assert query.to_sql_raw() == "USE [sivwin_homolog];\n\nSELECT 1"
|