Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Python reconciliation tests

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
cache-dependency-path: python_jupyter/requirements.txt
- name: Install test dependencies
run: python -m pip install "pandas>=2.2,<3.0" "pytest>=8.0,<9.0"
- name: Validate reconciliation logic
run: python -m pytest -q
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
*.xls
data/raw/
data/private/
__pycache__/
.pytest_cache/
.ipynb_checkpoints/

# Connections and credentials
.env
Expand All @@ -19,6 +22,7 @@ data/private/
*secret*
*connection*.txt
*odbc*.txt
config_db*.py

# Local system files
.DS_Store
Expand Down
67 changes: 63 additions & 4 deletions README.es.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# Conciliación financiera de facturación con ACL Analytics y Oracle SQL
# Controles financieros: de ACL y Oracle SQL a Python/Jupyter

[English](README.md) | [Español](README.es.md)

Caso profesional de control de datos basado en la automatización de conciliaciones de facturación. La solución combina Oracle SQL para extraer y preparar información operativa, ACL Analytics para ejecutar reglas de control repetibles y Excel para la revisión de resultados y gestión de excepciones.
Caso profesional de control de datos basado en la automatización de conciliaciones de facturación. La solución original combina Oracle SQL, ACL Analytics y Excel; su evolución actual migra controles seleccionados a **Python, Jupyter Notebook y Pandas**, manteniendo Oracle como fuente y haciendo explícitas las validaciones, justificaciones y excepciones.

Todo el código, los datos, los identificadores y las capturas públicas son sintéticos o fueron sanitizados. Se excluyen deliberadamente scripts productivos, conexiones, información de estudiantes y objetos internos de base de datos.

> **Estado real del trabajo:** la migración ACL → Python/Jupyter está en curso en un entorno profesional. El ejemplo público reproduce el enfoque técnico con datos ficticios. La persistencia histórica de resultados en Oracle se encuentra en diseño conjunto con BI y no se presenta como productiva.

## Problema de negocio

La facturación masiva combina inscripciones, aranceles, becas, descuentos, bajas y ajustes especiales. La información puede diferir entre reportes operativos y fuentes de base de datos. Una comparación manual es lenta, difícil de repetir y puede omitir excepciones de baja frecuencia.
Expand Down Expand Up @@ -34,6 +36,39 @@ flowchart LR

Oracle SQL y ACL cumplen responsabilidades diferentes: SQL construye el universo de datos cerca de la fuente; ACL estandariza campos, combina extracciones, evalúa reglas, resume resultados y exporta el reporte de control.

## Evolución actual: ACL → Python/Jupyter

```mermaid
flowchart LR
A["Fuentes autorizadas Oracle"] --> B["SQL parametrizado"]
B --> C["DataFrames de Pandas"]
C --> D["Normalización y agregación"]
D --> E["Conciliación bidireccional por cuenta"]
E --> F["Justificaciones por reglas de negocio"]
F --> G["Pendientes + controles de líneas e importes"]
G --> H["Dashboard HTML en Jupyter"]
H -. "en diseño con BI" .-> I["Histórico de resultados en Oracle"]
```

El nuevo patrón conserva el conocimiento de negocio de los controles existentes y mejora su trazabilidad técnica:

- conexión autorizada a Oracle con `oracledb` y SQLAlchemy;
- extracción SQL a DataFrames sin guardar credenciales en el notebook;
- normalización de identificadores, nulos, fechas y tipos;
- agregaciones y conciliación por cuenta mediante `groupby()` y `merge(..., how="outer")`;
- detección de diferencias en ambas direcciones;
- justificaciones automáticas sin ocultar la diferencia original;
- validación conjunta de cantidad de líneas, importes y casos pendientes;
- salida resumida como dashboard HTML dentro de Jupyter.

El notebook público [`python_jupyter/notebooks/01_synthetic_billing_reconciliation.ipynb`](python_jupyter/notebooks/01_synthetic_billing_reconciliation.ipynb) ejecuta ese flujo de punta a punta con datos sintéticos. La lógica reutilizable está separada en [`python_jupyter/src/control_reconciliation.py`](python_jupyter/src/control_reconciliation.py) y cuenta con pruebas automatizadas.

### Decisión de control demostrada

El ejemplo tiene 10 líneas esperadas y 10 líneas facturadas en total. Sin embargo, existe una cuenta esperada sin facturación y otra facturada sin registro esperado. Por eso no alcanza con comparar totales generales: el `outer merge` por cuenta evita que dos diferencias opuestas se compensen y produzcan un falso resultado correcto.

Los casos justificados se conservan como `JUSTIFIED`; los que no cierran cantidad e importe permanecen como `PENDING_REVIEW`. Esta separación permite explicar qué resolvió una regla automática y qué requiere análisis humano.

## KPI principal: cobertura financiera mensual del control

El resultado aproximado del **98%** es una métrica mensual de cobertura financiera. No es precisión de un pronóstico ni significa que el 98% de las facturas estén libres de errores.
Expand Down Expand Up @@ -113,20 +148,32 @@ Las reglas típicas clasifican casos como:
|-- acl/
| `-- financial_control_coverage.acl
|-- data/
| `-- synthetic_billing_control.csv
| |-- synthetic_billing_control.csv
| |-- synthetic_billed_scope.csv
| `-- synthetic_expected_scope.csv
|-- docs/
| |-- DATA_DICTIONARY.md
| `-- images/
| |-- financial-control-coverage.png
| `-- financial-control-coverage.svg
|-- pictures/
| `-- capturas sanitizadas
|-- python_jupyter/
| |-- notebooks/
| | `-- 01_synthetic_billing_reconciliation.ipynb
| |-- src/
| | `-- control_reconciliation.py
| |-- .env.example
| |-- oracle_connection.example.py
| `-- requirements.txt
|-- sql/
| |-- 00_create_synthetic_tables.sql
| |-- 01_extract_control_scope.sql
| |-- 02_reconcile_expected_actual.sql
| |-- 03_financial_control_coverage.sql
| `-- 04_data_quality_checks.sql
|-- tests/
| `-- test_python_reconciliation.py
|-- README.md
`-- README.es.md
```
Expand All @@ -138,6 +185,7 @@ Las reglas típicas clasifican casos como:
- Clasificación repetible de diferencias antes de la gestión de cobranzas.
- Análisis manual concentrado en una población menor de excepciones.
- Controles reutilizables por mes y período académico con mantenimiento reducido.
- Migración progresiva de controles seleccionados desde ACL hacia Python/Jupyter, con conciliaciones en Pandas y salidas de revisión más trazables.

## Cómo reproducir el ejemplo sintético

Expand All @@ -146,6 +194,16 @@ Las reglas típicas clasifican casos como:
3. Revisar el mismo universo en `data/synthetic_billing_control.csv`.
4. Si se dispone de ACL Analytics, adaptar el script representativo a una tabla importada con los mismos campos.

Para ejecutar la demostración Python/Jupyter:

```powershell
python -m pip install -r python_jupyter/requirements.txt
python -m pytest
jupyter lab python_jupyter/notebooks/01_synthetic_billing_reconciliation.ipynb
```

El archivo `oracle_connection.example.py` es únicamente una plantilla segura: usa variables de entorno y nombres de vistas ficticios. No es una copia de la conexión productiva.

El ejemplo SQL es autocontenido y utiliza únicamente tablas ficticias del portfolio. ACL Analytics es software comercial; por eso el script se ofrece como patrón legible y no como una prueba automatizada de CI.

## Confidencialidad y seguridad
Expand All @@ -155,7 +213,8 @@ El ejemplo SQL es autocontenido y utiliza únicamente tablas ficticias del portf
- Los valores monetarios e identificadores son sintéticos.
- Los scripts públicos reproducen el patrón técnico, no la implementación productiva.
- Las capturas se conservan únicamente cuando contienen información ficticia o sanitizada.
- Los notebooks productivos no se publican: pueden contener nombres internos, consultas, resultados o referencias de configuración aun cuando la contraseña esté en otro archivo.

## Resumen profesional

> Desarrollé controles automatizados de facturación utilizando ACL Analytics y Oracle SQL. SQL extraía y combinaba información de inscripciones, facturación, becas, descuentos y ajustes; ACL estandarizaba campos, aplicaba reglas de negocio, conciliaba importes esperados contra reales y clasificaba excepciones. Cada mes, los controles alcanzaban aproximadamente el 98% del valor total facturado dentro del alcance definido, permitiendo concentrar la revisión manual en los conceptos excepcionales restantes. Los controles con mayor intensidad de datos procesaban datasets que alcanzaban aproximadamente 5 GB y generaban reportes de control repetibles en Excel antes de la gestión de cobranzas.
> Desarrollé controles automatizados de facturación con ACL Analytics y Oracle SQL, y actualmente migro controles seleccionados a Python y Jupyter Notebook. Con Pandas extraigo y normalizo datos de Oracle, concilio universos por cuenta en ambas direcciones, aplico justificaciones de negocio y valido tanto cantidades de líneas como importes. Los controles existentes alcanzan mensualmente aproximadamente el 98% del valor facturado dentro del alcance definido; los de mayor intensidad procesaron datasets de hasta aproximadamente 5 GB. La evolución a Python busca conservar ese conocimiento de control y mejorar la trazabilidad, la reutilización y la futura persistencia histórica junto con BI.
67 changes: 63 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# Financial Billing Reconciliation with ACL Analytics and Oracle SQL
# Financial Controls: From ACL and Oracle SQL to Python/Jupyter

[English](README.md) | [Español](README.es.md)

A professional data-control case study based on automated billing reconciliation work. The solution combines Oracle SQL for extracting and shaping operational data, ACL Analytics for repeatable control rules, and Excel outputs for business review and exception management.
A professional data-control case study based on automated billing reconciliation work. The original solution combines Oracle SQL, ACL Analytics, and Excel; its current evolution migrates selected controls to **Python, Jupyter Notebook, and Pandas**, while retaining Oracle as the source and making validations, justifications, and exceptions explicit.

All public code, data, identifiers, and screenshots are synthetic or sanitized. Production scripts, connection details, student information, and internal database objects are intentionally excluded.

> **Current work status:** the ACL-to-Python/Jupyter migration is underway in a professional environment. The public example reproduces the technical approach with fictitious data. Historical result persistence in Oracle is being designed collaboratively with BI and is not presented as production-ready.

## Business problem

High-volume billing combines enrollment, tuition, scholarships, discounts, cancellations, and special adjustments. These inputs can disagree across operational reports and database sources. Manual comparison is slow, difficult to repeat, and likely to miss low-frequency exceptions.
Expand Down Expand Up @@ -34,6 +36,39 @@ flowchart LR

Oracle SQL and ACL have different responsibilities: SQL builds the required data scope close to the source, while ACL standardizes fields, combines extracts, evaluates business rules, summarizes results, and exports the control report.

## Current evolution: ACL → Python/Jupyter

```mermaid
flowchart LR
A["Authorized Oracle sources"] --> B["Parameterized SQL"]
B --> C["Pandas DataFrames"]
C --> D["Normalization and aggregation"]
D --> E["Bidirectional account reconciliation"]
E --> F["Business-rule justifications"]
F --> G["Pending items + line and amount checks"]
G --> H["Jupyter HTML dashboard"]
H -. "in design with BI" .-> I["Oracle result history"]
```

The new pattern preserves the business knowledge embedded in the existing controls while improving technical traceability:

- authorized Oracle access through `oracledb` and SQLAlchemy;
- SQL extraction into DataFrames without storing credentials in notebooks;
- identifier, null, date, and type normalization;
- account-level aggregation and reconciliation with `groupby()` and `merge(..., how="outer")`;
- difference detection in both directions;
- automatic justifications that do not hide the original difference;
- combined validation of line counts, amounts, and pending cases;
- a compact HTML dashboard inside Jupyter.

The public notebook [`python_jupyter/notebooks/01_synthetic_billing_reconciliation.ipynb`](python_jupyter/notebooks/01_synthetic_billing_reconciliation.ipynb) runs the pattern end to end with synthetic data. Reusable logic lives in [`python_jupyter/src/control_reconciliation.py`](python_jupyter/src/control_reconciliation.py) and is covered by automated tests.

### Demonstrated control decision

The fixture contains 10 expected lines and 10 billed lines in total. Even so, one expected account has no billing and one unexpected account was billed. Comparing only grand totals would therefore produce a false pass; the account-level outer merge prevents opposite differences from cancelling each other out.

Justified cases remain visible as `JUSTIFIED`, while cases that do not close both count and amount checks stay as `PENDING_REVIEW`. This distinction makes it possible to explain what an automated rule resolved and what still requires human analysis.

## Main KPI: monthly financial control coverage

The approximately **98%** result is a monthly financial-coverage metric, not prediction accuracy and not a claim that 98% of invoices are error-free.
Expand Down Expand Up @@ -113,20 +148,32 @@ Typical rules classify cases such as:
|-- acl/
| `-- financial_control_coverage.acl
|-- data/
| `-- synthetic_billing_control.csv
| |-- synthetic_billing_control.csv
| |-- synthetic_billed_scope.csv
| `-- synthetic_expected_scope.csv
|-- docs/
| |-- DATA_DICTIONARY.md
| `-- images/
| |-- financial-control-coverage.png
| `-- financial-control-coverage.svg
|-- pictures/
| `-- sanitized screenshots
|-- python_jupyter/
| |-- notebooks/
| | `-- 01_synthetic_billing_reconciliation.ipynb
| |-- src/
| | `-- control_reconciliation.py
| |-- .env.example
| |-- oracle_connection.example.py
| `-- requirements.txt
|-- sql/
| |-- 00_create_synthetic_tables.sql
| |-- 01_extract_control_scope.sql
| |-- 02_reconcile_expected_actual.sql
| |-- 03_financial_control_coverage.sql
| `-- 04_data_quality_checks.sql
|-- tests/
| `-- test_python_reconciliation.py
|-- README.md
`-- README.es.md
```
Expand All @@ -138,6 +185,7 @@ Typical rules classify cases such as:
- Repeatable classification of differences before downstream collection activities.
- Manual analysis focused on a smaller exception population instead of the complete billing universe.
- Reusable monthly and academic-period controls with reduced maintenance.
- Progressive migration of selected controls from ACL to Python/Jupyter, with Pandas reconciliations and more traceable review outputs.

## Reproducing the synthetic example

Expand All @@ -146,6 +194,16 @@ Typical rules classify cases such as:
3. Review the same input in `data/synthetic_billing_control.csv`.
4. If ACL Analytics is available, adapt the representative script to an imported table with the same fields.

To run the Python/Jupyter demonstration:

```powershell
python -m pip install -r python_jupyter/requirements.txt
python -m pytest
jupyter lab python_jupyter/notebooks/01_synthetic_billing_reconciliation.ipynb
```

The `oracle_connection.example.py` file is a secure template only: it relies on environment variables and fictitious view names. It is not a copy of the production connection.

The SQL example is self-contained and uses only synthetic portfolio tables. ACL Analytics is commercial software, so the ACL script is provided as a readable implementation pattern rather than an automated CI test.

## Confidentiality and security
Expand All @@ -155,7 +213,8 @@ The SQL example is self-contained and uses only synthetic portfolio tables. ACL
- Monetary values and identifiers are synthetic.
- Public scripts reproduce the technical pattern, not the production implementation.
- Screenshots are retained only when they contain sanitized or fictitious information.
- Production notebooks are not published: they may expose internal names, queries, results, or configuration references even when passwords live elsewhere.

## Professional summary

> I developed automated billing controls using ACL Analytics and Oracle SQL. SQL extracted and combined enrollment, billing, scholarship, discount, and adjustment data, while ACL standardized fields, applied business rules, reconciled expected versus actual amounts, and classified exceptions. Each month, the controls covered approximately 98% of total billed value within the defined scope, allowing manual review to focus on the remaining exceptional concepts. The most data-intensive controls processed datasets reaching approximately 5 GB and produced repeatable Excel control reports before collection activities.
> I developed automated billing controls using ACL Analytics and Oracle SQL, and I am currently migrating selected controls to Python and Jupyter Notebook. With Pandas, I extract and normalize Oracle data, reconcile account-level universes in both directions, apply business justifications, and validate both line counts and monetary amounts. The existing controls cover approximately 98% of monthly billed value within the defined scope, and the most data-intensive cases processed datasets of up to approximately 5 GB. The Python evolution preserves that control knowledge while improving traceability, reuse, and future historical persistence in collaboration with BI.
8 changes: 8 additions & 0 deletions data/synthetic_billed_scope.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
billing_period,account_ref,billed_line_count,billed_amount,justification_type
2026-01,ACC-1001,2,20000,NONE
2026-01,ACC-1002,3,30000,NONE
2026-01,ACC-1003,1,12000,NONE
2026-01,ACC-1004,1,9000,NONE
2026-01,ACC-1004,1,9000,MANUAL_ADJUSTMENT
2026-01,ACC-1005,1,10000,CASH_PAYMENT
2026-01,ACC-1007,1,2500,NONE
7 changes: 7 additions & 0 deletions data/synthetic_expected_scope.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
billing_period,account_ref,expected_line_count,expected_amount
2026-01,ACC-1001,2,20000
2026-01,ACC-1002,3,30000
2026-01,ACC-1003,1,12000
2026-01,ACC-1004,2,18000
2026-01,ACC-1005,1,10000
2026-01,ACC-1006,1,8000
21 changes: 21 additions & 0 deletions docs/CAPTURAS_RECOMENDADAS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Capturas recomendadas para la migración Python/Jupyter

Las capturas son opcionales: el repositorio ya contiene un notebook reproducible, código modular y pruebas. Si se agregan evidencias visuales, conviene que refuercen el razonamiento sin exponer el entorno productivo.

## Capturas seguras

1. **Resultado del notebook sintético**: tabla final con `RECONCILED`, `JUSTIFIED` y `PENDING_REVIEW`.
2. **Dashboard HTML sintético**: tarjetas de cantidades, importes y pendientes generadas por el notebook público.
3. **Pruebas automatizadas**: terminal mostrando `python -m pytest` exitoso.
4. **Arquitectura conceptual**: Oracle → SQLAlchemy/oracledb → Pandas → conciliación → dashboard → histórico en diseño.

## No publicar

- celdas de conexión, aunque importen la contraseña desde otro archivo;
- nombres reales de servidores, DSN, esquemas, tablas o vistas;
- identificadores o información de estudiantes;
- resultados, importes o cantidades de una ejecución productiva;
- rutas locales, usuarios, correos o nombres de archivos internos;
- notebooks originales con outputs guardados.

Antes de subir cualquier captura real, recortarla y revisar visualmente cada celda, encabezado, tooltip y resultado. La alternativa más segura es ejecutar el notebook sintético incluido y capturar únicamente esa ejecución.
Loading
Loading