g360-cli 1.7.1 → 1.10.0

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.
Files changed (54) hide show
  1. package/README.md +83 -8
  2. package/package.json +16 -6
  3. package/py/pyproject.toml +4 -4
  4. package/py/requirements.txt +4 -0
  5. package/py/src/g360_core/__init__.py +67 -4
  6. package/py/src/g360_core/__pycache__/__init__.cpython-312.pyc +0 -0
  7. package/py/src/g360_core/__pycache__/__init__.cpython-314.pyc +0 -0
  8. package/py/src/g360_core/__pycache__/batch_processor.cpython-312.pyc +0 -0
  9. package/py/src/g360_core/__pycache__/batch_processor.cpython-314.pyc +0 -0
  10. package/py/src/g360_core/__pycache__/commercial_engine.cpython-314.pyc +0 -0
  11. package/py/src/g360_core/__pycache__/logger.cpython-312.pyc +0 -0
  12. package/py/src/g360_core/__pycache__/logger.cpython-314.pyc +0 -0
  13. package/py/src/g360_core/__pycache__/pipeline.cpython-312.pyc +0 -0
  14. package/py/src/g360_core/__pycache__/pipeline.cpython-314.pyc +0 -0
  15. package/py/src/g360_core/__pycache__/processor.cpython-312.pyc +0 -0
  16. package/py/src/g360_core/__pycache__/processor.cpython-314.pyc +0 -0
  17. package/py/src/g360_core/__pycache__/processor_segmentacion.cpython-312.pyc +0 -0
  18. package/py/src/g360_core/__pycache__/processor_segmentacion.cpython-314.pyc +0 -0
  19. package/py/src/g360_core/__pycache__/processor_sku.cpython-312.pyc +0 -0
  20. package/py/src/g360_core/__pycache__/processor_sku.cpython-314.pyc +0 -0
  21. package/py/src/g360_core/__pycache__/scanner.cpython-312.pyc +0 -0
  22. package/py/src/g360_core/__pycache__/scanner.cpython-314.pyc +0 -0
  23. package/py/src/g360_core/__pycache__/utils.cpython-312.pyc +0 -0
  24. package/py/src/g360_core/__pycache__/utils.cpython-314.pyc +0 -0
  25. package/py/src/g360_core/batch_processor.py +120 -0
  26. package/py/src/g360_core/commercial_engine.py +305 -0
  27. package/py/src/g360_core/logger.py +40 -0
  28. package/py/src/g360_core/pipeline.py +578 -0
  29. package/py/src/g360_core/processor.py +634 -0
  30. package/py/src/g360_core/processor_segmentacion.py +859 -0
  31. package/py/src/g360_core/processor_sku.py +427 -0
  32. package/py/src/g360_core/scanner.py +218 -0
  33. package/py/src/g360_core/utils.py +435 -0
  34. package/src/cli.js +35 -2
  35. package/src/commands/addon.js +188 -0
  36. package/src/commands/ingest.js +187 -0
  37. package/src/commands/scan.js +90 -0
  38. package/src/commands/validate.js +150 -0
  39. package/src/lib/python_runner.js +89 -0
  40. package/py/src/g360_core/flet/__init__.py +0 -3
  41. package/py/src/g360_core/flet/ingestion_panel.py +0 -218
  42. package/py/src/g360_core/ingestion.py +0 -480
  43. package/src/assets/engine/g360-data-validator.js +0 -44
  44. package/src/assets/engine/g360-engine.js +0 -12
  45. package/src/assets/engine/g360-field-mapper.js +0 -35
  46. package/src/assets/engine/g360-skill-audit.mjs +0 -37
  47. package/src/assets/engine/g360-skill-meta-evaluator.mjs +0 -33
  48. package/src/lib/assets.js +0 -38
  49. package/src/lib/checksum.js +0 -27
  50. package/src/lib/config.js +0 -23
  51. package/src/lib/offline.js +0 -33
  52. package/src/lib/presenter.js +0 -24
  53. package/src/lib/rollback.js +0 -49
  54. package/src/lib/theme.js +0 -30
package/README.md CHANGED
@@ -55,6 +55,8 @@ CLI tool para el ecosistema G360 que permite inicializar proyectos con estructur
55
55
  - **Inicialización rápida** - Crea proyectos G360 con estructura estándar
56
56
  - **Gestión de assets** - Trae componentes, skills y plantillas embebidas
57
57
  - **Ingesta ERP** - Normaliza `.xls/.xlsx` de SAP, StarSoft, Spring con `g360 bring ingestion`
58
+ - **Motor de clasificación comercial** - `commercial_engine` clasifica documentos en VENTA/DEVOLUCION/AJUSTE con subtipos (PRECIO_LINEA, CARGO_FIJO, SIN_BASE)
59
+ - **Precio efectivo** - PRECIO_BASE, RECARGO_UNITARIO y PRECIO_EFECTIVO separan precio físico de ajustes financieros FAE
58
60
  - **Paquete Python** - `g360-core` en PyPI para pipelines de datos independientes
59
61
  - **Auditoría** - Verifica compliance de proyectos G360
60
62
  - **Limpieza** - Elimina assets embebidos antes de deployment
@@ -309,6 +311,35 @@ El paquete pip acompañante `g360-core` se publica en PyPI:
309
311
  pip install g360-core
310
312
  ```
311
313
 
314
+ ### `g360-core` — Módulos principales
315
+
316
+ #### `commercial_engine.py`
317
+
318
+ Motor de lógica de negocio para clasificación documental. Única fuente de verdad para reglas comerciales.
319
+
320
+ | Función | Propósito |
321
+ |---------|-----------|
322
+ | `classify_base()` | Clasificación primaria: VENTA, DEVOLUCION, AJUSTE |
323
+ | `build_invoice_index()` | Índice de facturas para cruce de referencias |
324
+ | `resolve_document_relationships()` | Asigna SUBTIPO_AJUSTE (PRECIO_LINEA, CARGO_FIJO, SIN_BASE) |
325
+ | `calculate_prices()` | PRECIO_BASE, RECARGO_UNITARIO, PRECIO_EFECTIVO |
326
+ | `parse_referencia()` | Descompone REFERENCIA "F01/204-56287" en tipo/serie/número |
327
+
328
+ **Clasificación de documentos:**
329
+ ```
330
+ TPO_DOC CANTIDAD CANTIDAD_FAE → CATEGORIA_OP SUBTIPO_AJUSTE
331
+ F01/BDI ≠0 = VENTA —
332
+ NCR ≠0 = DEVOLUCION —
333
+ NCR 0 ≠0 AJUSTE PRECIO_LINEA / SIN_BASE
334
+ NDB 0 ≠0 AJUSTE CARGO_FIJO / SIN_BASE
335
+ ```
336
+
337
+ #### `batch_processor.py`
338
+
339
+ | Función | Propósito |
340
+ |---------|-----------|
341
+ | `read_erp_file()` | Punto único de lectura: .xls (xlrd), .xlsx (openpyxl), .csv. `dtype=str` preserva ceros a la izquierda |
342
+
312
343
  ---
313
344
 
314
345
  ### `g360 list`
@@ -588,12 +619,15 @@ mi-app/
588
619
  └── skill.json
589
620
  ```
590
621
 
591
- **Normalización de datos:** La ingesta aplica 16 transformaciones automáticas:
622
+ **Normalización de datos:** La ingesta aplica transformaciones automáticas:
592
623
  - Parseo de referencias (`F01/201-243065` → tipo, serie, periodo, número)
593
624
  - Separación de sucursales (nombre + dirección)
594
625
  - Clasificación de documentos (RUC 11 dígitos / DNI 8 dígitos)
595
626
  - Normalización monetaria con auto-detección de formato SAP/Spring
596
627
  - Cantidad + Cantidad FAE → cantidad_total + tipo_transaccion
628
+ - **Clasificación comercial**: VENTA / DEVOLUCION / AJUSTE con subtipos PRECIO_LINEA, CARGO_FIJO, SIN_BASE
629
+ - **Precio efectivo**: PRECIO_BASE (físico) y RECARGO_UNITARIO (financiero) separados
630
+ - Cruce de NC/NDB contra facturas referenciadas para determinar ajustes de precio por línea
597
631
  - Purga de filas total/general/acumulado
598
632
 
599
633
  ### python-flet-migrate
@@ -806,7 +840,7 @@ g360-cli/
806
840
  │ └── config/ # Configuraciones
807
841
  ├── py/ # Paquete Python publicable en PyPI
808
842
  │ ├── pyproject.toml # g360-core
809
- │ └── src/g360_core/ # ingestion.py + flet/ingestion_panel.py
843
+ │ └── src/g360_core/ # commercial_engine.py, pipeline.py, processor.py, batch_processor.py, utils.py
810
844
  ├── package.json
811
845
  ├── README.md
812
846
  └── LICENSE
@@ -820,7 +854,7 @@ g360-cli/
820
854
  |---------|-------------|
821
855
  | `npm run build` | Build portable con pkg (g360.exe) |
822
856
  | `npm run build:portable` | Especificar target node18-win-x64 |
823
- | `npm test` | Ejecutar tests con Vitest (51 tests, 7 suites) |
857
+ | `npm test` | Ejecutar tests con Vitest (53 tests, 9 suites) |
824
858
  | `npm run prepublishOnly` | Validación antes de publicar en npm |
825
859
 
826
860
  ---
@@ -828,16 +862,16 @@ g360-cli/
828
862
  ## Testing
829
863
 
830
864
  ```bash
831
- npm test # Vitest — 51 tests, 8 suites
865
+ npm test # Vitest — 53 tests, 9 suites
832
866
  npm run test:watch # Modo watch
833
867
  npm run test:ui # UI interactiva
834
868
  npm run test:coverage
835
869
  ```
836
870
 
837
- **Cobertura actual (v1.7.0):**
838
- - `commands/`: init, bring, list, audit, set-skill
839
- - `lib/`: manifest, validator, asset-validator
840
- - **51 passing / 1 timeout** (init.test.js requiere import pesado de inquirer)
871
+ **Cobertura actual (v1.10.0):**
872
+ - `commands/`: init, bring, list, audit, set-skill, addon
873
+ - `lib/`: manifest, validator, asset-validator, python_runner
874
+ - **53 passing / 1 timeout** (init.test.js requiere import pesado de inquirer)
841
875
 
842
876
  ---
843
877
 
@@ -877,6 +911,47 @@ Este proyecto forma parte de la familia de microherramientas **G360** para apoyo
877
911
 
878
912
  ---
879
913
 
914
+ ### `g360 addon`
915
+
916
+ Gestión de addons y paquetes de desarrollo.
917
+
918
+ ```bash
919
+ g360 addon <comando> [paquete] [opciones]
920
+ ```
921
+
922
+ **Comandos:**
923
+ - `install <package>` - Instala un addon (core o dev-tool)
924
+ - `list` - Lista addons instalados
925
+ - `remove <package>` - Desinstala un addon
926
+
927
+ **Opciones:**
928
+ | Opción | Descripción |
929
+ |--------|-------------|
930
+ | `-p, --path <ruta>` | Ruta destino (default: `.`) |
931
+ | `--dry-run` | Previsualizar cambios |
932
+ | `--force` | Forzar reinstalación/remoción |
933
+
934
+ **Ejemplos:**
935
+ ```bash
936
+ # Instalar paquete de diseño (core)
937
+ g360 addon install @google/design.md
938
+
939
+ # Instalar kit de testing (dev)
940
+ g360 addon install @g360/testing
941
+
942
+ # Listar addons
943
+ g360 addon list
944
+
945
+ # Remover addon
946
+ g360 addon remove @google/design.md
947
+ ```
948
+
949
+ **Registro de addons:**
950
+ - 🏛️ Core Dev: Van a `dependencies` (producción)
951
+ - 🛠️ Dev Tools: Van a `devDependencies` (desarrollo)
952
+
953
+ ---
954
+
880
955
  ## Integración con OpenCode
881
956
 
882
957
  g360-cli incluye integración con **OpenCode** para desarrollo asistido por IA. Esta integración permite que los agentes de IA tengan acceso a los recursos de g360-cli durante el desarrollo.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "g360-cli",
3
- "version": "1.7.1",
4
- "description": "CLI tool for bootstrapping G360 projects with standardized structure, assets, and identity",
3
+ "version": "1.10.0",
4
+ "description": "CLI tool for bootstrapping G360 projects with standardized structure, assets, identity, and ERP data processing",
5
5
  "type": "module",
6
6
  "main": "src/cli.js",
7
7
  "bin": {
@@ -14,19 +14,25 @@
14
14
  "test:ui": "vitest --ui",
15
15
  "test:coverage": "vitest --coverage",
16
16
  "build": "npm run build:portable",
17
- "build:portable": "pkg . --targets node18-win-x64 --output dist/g360.exe"
17
+ "build:portable": "pkg . --targets node18-win-x64 --output dist/g360.exe",
18
+ "postinstall": "echo '✅ g360-cli instalado. Asegúrate de tener Python 3.11+ y pip instalados.'"
18
19
  },
19
20
  "pkg": {
20
21
  "assets": [
21
- "src/assets/**/*"
22
+ "src/assets/**/*",
23
+ "py/**/*"
22
24
  ],
23
- "outputPath": "dist"
25
+ "outputPath": "dist",
26
+ "scripts": [],
27
+ "targets": "node18-win-x64"
24
28
  },
25
29
  "keywords": [
26
30
  "g360",
27
31
  "cli",
28
32
  "scaffolding",
29
- "generator"
33
+ "generator",
34
+ "erp",
35
+ "data-processing"
30
36
  ],
31
37
  "author": "Carlos Cusi",
32
38
  "license": "MIT",
@@ -46,5 +52,9 @@
46
52
  },
47
53
  "engines": {
48
54
  "node": ">=18.0.0"
55
+ },
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "https://github.com/ccusi/g360-cli.git"
49
59
  }
50
60
  }
package/py/pyproject.toml CHANGED
@@ -18,10 +18,10 @@ classifiers = [
18
18
  ]
19
19
 
20
20
  dependencies = [
21
- "pandas>=2.0.0",
22
- "numpy>=1.24.0",
23
- "openpyxl>=3.1.0",
24
- "xlrd>=2.0.0",
21
+ "pandas>=2.2.3",
22
+ "numpy>=1.26.0",
23
+ "openpyxl>=3.1.5",
24
+ "xlrd>=2.0.1",
25
25
  ]
26
26
 
27
27
  [project.optional-dependencies]
@@ -0,0 +1,4 @@
1
+ pandas>=2.2.3
2
+ numpy>=1.26.0
3
+ openpyxl>=3.1.5
4
+ xlrd>=2.0.1
@@ -1,7 +1,70 @@
1
- from g360_core.ingestion import estabilizar_excel_crudo, _MAPA_TPO_DOC, _MAPA_MESES
1
+ """
2
+ g360_core - Módulo principal de procesamiento ERP para g360-cli.
3
+
4
+ Proporciona:
5
+ - Estabilización de datos crudos de Excel/CSV
6
+ - Pipeline de ingesta con normalización
7
+ - Scanner de directorios
8
+ - Protección de ceros a la izquierda en IDs
9
+ - Batch processing
10
+ """
11
+
12
+ from .commercial_engine import (classify_base, parse_referencia, resolve_document_relationships, calculate_prices, CAT_VENTA, CAT_DEVOLUCION, CAT_AJUSTE, SUBTIPO_PRECIO_LINEA, SUBTIPO_PRECIO_PARCIAL, SUBTIPO_CARGO_FIJO, SUBTIPO_SIN_BASE)
13
+ from .pipeline import ingest_to_master, batch_ingest
14
+ from .scanner import (
15
+ ERPScanner,
16
+ ERPFileInfo,
17
+ find_erp_files_in_dir,
18
+ process_single_file,
19
+ batch_process_files,
20
+ merge_processed_data,
21
+ )
22
+ from .processor import InsightProcessor
23
+ from .utils import (
24
+ clean_erp_headers,
25
+ normalize_ids,
26
+ build_doc_completo,
27
+ build_entity_labels,
28
+ validate_columns,
29
+ parse_excel_date,
30
+ ID_COLS_PRESERVE_ZEROS,
31
+ NC_PREFIXES,
32
+ )
33
+ from .batch_processor import read_erp_file
2
34
 
3
35
  __all__ = [
4
- "estabilizar_excel_crudo",
5
- "_MAPA_TPO_DOC",
6
- "_MAPA_MESES",
36
+ # Commercial Engine
37
+ "classify_base",
38
+ "parse_referencia",
39
+ "resolve_document_relationships",
40
+ "calculate_prices",
41
+ "CAT_VENTA",
42
+ "CAT_DEVOLUCION",
43
+ "CAT_AJUSTE",
44
+ "SUBTIPO_PRECIO_LINEA",
45
+ "SUBTIPO_PRECIO_PARCIAL",
46
+ "SUBTIPO_CARGO_FIJO",
47
+ "SUBTIPO_SIN_BASE",
48
+ # Ingestion
49
+ "ingest_to_master",
50
+ "batch_ingest",
51
+ # Scanner
52
+ "ERPScanner",
53
+ "ERPFileInfo",
54
+ "find_erp_files_in_dir",
55
+ "process_single_file",
56
+ "batch_process_files",
57
+ "merge_processed_data",
58
+ # Processor
59
+ "InsightProcessor",
60
+ # Utils
61
+ "clean_erp_headers",
62
+ "normalize_ids",
63
+ "build_doc_completo",
64
+ "build_entity_labels",
65
+ "validate_columns",
66
+ "parse_excel_date",
67
+ "ID_COLS_PRESERVE_ZEROS",
68
+ "NC_PREFIXES",
69
+ "read_erp_file",
7
70
  ]
@@ -0,0 +1,120 @@
1
+ """
2
+ Procesador por lotes para archivos ERP grandes.
3
+
4
+ Lee archivos grandes y los procesa en un solo paso usando InsightProcessor.
5
+ El chunking original causaba procesamiento duplicado; ahora se procesa
6
+ todo el DataFrame de una vez para garantizar consistencia.
7
+ """
8
+ import pandas as pd
9
+ from pathlib import Path
10
+ from typing import Optional, Callable
11
+ import gc
12
+ from .processor import InsightProcessor
13
+
14
+
15
+ class BatchProcessor:
16
+ """Procesa archivos ERP grandes con feedback de progreso."""
17
+
18
+ def __init__(self, progress_callback: Optional[Callable] = None):
19
+ self.progress_callback = progress_callback
20
+
21
+ def process_large_file(self, df: pd.DataFrame, filename: str) -> InsightProcessor:
22
+ """
23
+ Procesa un DataFrame completo en un solo paso.
24
+
25
+ Args:
26
+ df: DataFrame con datos crudos
27
+ filename: Nombre del archivo para logs
28
+
29
+ Returns:
30
+ InsightProcessor con todos los datos procesados
31
+ """
32
+ if self.progress_callback:
33
+ self.progress_callback(0.1, f"Procesando {len(df):,} registros...")
34
+
35
+ processor = InsightProcessor(df)
36
+ gc.collect()
37
+
38
+ if self.progress_callback:
39
+ self.progress_callback(1.0, "Procesamiento completado")
40
+
41
+ return processor
42
+
43
+ def read_with_progress(self, path: str, file_ext: str,
44
+ on_progress: Callable[[float, str], None]) -> pd.DataFrame:
45
+ """
46
+ Lee archivo con indicador de progreso.
47
+
48
+ Args:
49
+ path: Ruta del archivo
50
+ file_ext: Extension del archivo (.csv, .xls, .xlsx)
51
+ on_progress: Callback de progreso (0-1, mensaje)
52
+
53
+ Returns:
54
+ DataFrame leido
55
+ """
56
+ on_progress(0.05, "Leyendo archivo...")
57
+ df = read_erp_file(path, file_ext)
58
+ on_progress(0.2, f"Archivo leido: {len(df):,} filas")
59
+ return df
60
+
61
+
62
+ def read_erp_file(path: str, file_ext: str = None) -> pd.DataFrame:
63
+ """
64
+ Funcion unificada de lectura de archivos ERP.
65
+
66
+ Es el unico punto de entrada para leer archivos .xls, .xlsx y .csv
67
+ en toda la aplicacion. Usa engines explicitos para evitar problemas
68
+ de compatibilidad con pandas/xlrd/openpyxl.
69
+
70
+ PROTECCIÓN DE CEROS A LA IZQUIERDA:
71
+ Las columnas críticas de IDs se leen obligatoriamente como strings (dtype=str)
72
+ para preservar ceros iniciales (ej: "01240" no se convierte a 1240).
73
+
74
+ Args:
75
+ path: Ruta absoluta al archivo.
76
+ file_ext: Extension (incluye el punto). Si es None se infiere.
77
+
78
+ Returns:
79
+ DataFrame con columnas críticas como strings para preservar ceros.
80
+
81
+ Raises:
82
+ ValueError: Si la extension no es soportada o hay error de lectura.
83
+ """
84
+ if file_ext is None:
85
+ file_ext = Path(path).suffix.lower()
86
+ else:
87
+ file_ext = file_ext.lower()
88
+
89
+ # Columnas críticas que deben preservar ceros a la izquierda
90
+ critical_cols = [
91
+ "ID_ARTICULO", "ID_CLIENTE", "ID_VENDEDOR", "ID_LINEA",
92
+ "ID_GRUPO", "ID_TIPO", "ID_FAMILIA", "COD_SUCURSAL",
93
+ "NRO_DOC", "SERIE_DOC", "DOC_CLIENTE"
94
+ ]
95
+
96
+ if file_ext == '.csv':
97
+ # Para CSV, leer todo como string inicialmente
98
+ df = pd.read_csv(
99
+ path, dtype=str, encoding='utf-8-sig',
100
+ sep=None, engine='python', low_memory=False,
101
+ )
102
+ elif file_ext in ('.xls', '.xlsx'):
103
+ engine = "xlrd" if file_ext == ".xls" else "openpyxl"
104
+ try:
105
+ # Leer Excel con dtype=str para forzar texto en TODAS las columnas
106
+ # Esto previene que pandas convierta "01240" -> 1240 automáticamente
107
+ df = pd.read_excel(path, dtype=str, engine=engine)
108
+ except Exception as ex:
109
+ raise ValueError(
110
+ f"Error leyendo archivo Excel ({file_ext}) con motor '{engine}': {ex}"
111
+ )
112
+ else:
113
+ raise ValueError(f"Extension '{file_ext}' no soportada. Use .xls, .xlsx, .csv")
114
+
115
+ # Limpieza post-lectura: eliminar espacios en blanco de columnas críticas
116
+ for col in critical_cols:
117
+ if col in df.columns:
118
+ df[col] = df[col].astype(str).str.strip()
119
+
120
+ return df