Files
Autoparts-DB/scripts/scrape_rached_catalogs.py
consultoria-as f42910f4f6
Some checks failed
CI / lint-and-test (3.11) (push) Has been cancelled
CI / lint-and-test (3.13) (push) Has been cancelled
feat: cashier/counter reports, service-order & remission flows, Rached migration utils
- Add "Mis cortes de caja" report for cashiers/counters with sales detail.
- Cash register history scoped to own cuts for non-admin roles; new /register/<id>/sales endpoint.
- Remove dashboard from cashier menu; add Reports to cashier/counter.
- Service orders: assign mechanic, budget field, invoice flag, counter/cashier can add items/remissions, convert to remission.
- Remission notes module (UI, CSS, courier, counter remissions).
- Customer hard-delete and vehicle/customer linkage in workshop.
- POS: always show search results, compact payment grid, credit validation, tier pricing (5%/10%), ticket with customer/folio.
- Inventory: CSV template with sku_secondary, alias import.
- Rached migration scripts and DB migrations.
- Version-bump cached JS/CSS query strings.

Excludes local Rached session tokens/captures (rached_*.json / rached_*.txt).
2026-07-02 12:51:56 +00:00

176 lines
6.4 KiB
Python

#!/usr/bin/env python3
"""Scrape Rached legacy catalog endpoints discovered via UI."""
import json
import os
import time
import requests
from pathlib import Path
BASE_API = 'http://appapi.flechasyventiladores-rached.com'
OUT_DIR = Path(os.environ.get('OUT_DIR', Path(__file__).resolve().parent.parent / 'data' / 'rached_import'))
USER = 'IVAN@flechasyventiladores-rached.com'
PASS = 'Nexus01'
HEADERS = {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json',
'Origin': 'http://app.flechasyventiladores-rached.com',
'Referer': 'http://app.flechasyventiladores-rached.com/',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
}
def login():
url = f'{BASE_API}/api/seguridad/login'
payload = {
'grant_type': 'password',
'client_secret': 'JiRE9iL3pqRnqcFp6wDeYH0tYu97QSpkrwVKAvEP',
'client_id': 2,
'username': USER,
'password': PASS,
}
resp = requests.post(url, headers=HEADERS, json=payload)
resp.raise_for_status()
data = resp.json()
if data.get('mensaje'):
raise RuntimeError(f'Login failed: {data["mensaje"]}')
return data['datos']['token']
def api_headers(token):
return {**HEADERS, 'Authorization': f'Bearer {token}'}
def fetch_get(token, path, params=None):
url = f'{BASE_API}{path}'
resp = requests.get(url, headers=api_headers(token), params=params, timeout=30)
resp.raise_for_status()
return resp.json()
def fetch_post(token, path, payload=None, params=None):
url = f'{BASE_API}{path}'
resp = requests.post(url, headers=api_headers(token), json=payload or {}, params=params, timeout=30)
resp.raise_for_status()
return resp.json()
def save(name, data):
path = OUT_DIR / f'{name}.json'
path.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding='utf-8')
return path
def fetch_list_all(token, path, method='GET', payload=None):
"""Fetch a list endpoint that may return either a list or Laravel pagination."""
all_items = []
page = 1
while True:
try:
if method == 'GET':
data = fetch_get(token, f'{path}?page={page}')
else:
data = fetch_post(token, f'{path}?page={page}', payload)
except Exception as e:
print(f' {path} page {page} error: {e}')
break
datos = data.get('datos', {})
if isinstance(datos, list):
# Non-paginated full list
all_items.extend(datos)
print(f' {path}: +{len(datos)} (total {len(all_items)})')
break
items = datos.get('data', [])
all_items.extend(items)
print(f' {path} page {page}: +{len(items)} (total {len(all_items)}/{datos.get("total", "?")})')
if datos.get('current_page', 1) >= datos.get('last_page', 1):
break
page += 1
time.sleep(0.6)
return all_items
def main():
OUT_DIR.mkdir(parents=True, exist_ok=True)
log_path = OUT_DIR / 'scrape_catalogs.log'
def log(msg):
print(msg)
with open(log_path, 'a', encoding='utf-8') as f:
f.write(msg + '\n')
log('Logging in...')
token = login()
log('Logged in.')
# Simple catalog endpoints (GET, single page or paginated)
simple_gets = {
'catalog_sucursales': '/api/catalogos/sucursales',
'catalog_viasentrega': '/api/catalogos/viasentrega/listado',
'catalog_order_statuses': '/api/catalogos/ordenesservicioestatus/listado',
'catalog_item_statuses': '/api/catalogos/ordenesserviciodetalleestatus/listado',
'catalog_directas': '/api/catalogos/ordenesDirectas',
'catalog_telefonostipos': '/api/catalogos/telefonostipos/listado',
'catalog_direccionestipos': '/api/catalogos/direccionestipos/listado',
}
for name, path in simple_gets.items():
try:
data = fetch_get(token, path)
save(name, data)
count = len(data.get('datos', []) if isinstance(data.get('datos'), list) else [])
log(f'{name}: {count} items')
except Exception as e:
log(f'{name} error: {e}')
time.sleep(0.6)
# List endpoints (GET or POST)
list_gets = {
'catalog_articulos': '/api/catalogos/articulos/listado',
'catalog_mecanicos': '/api/catalogos/mecanicos/listado',
'catalog_motociclistas': '/api/catalogos/motociclistas/listado',
}
for name, path in list_gets.items():
items = fetch_list_all(token, path, method='GET')
save(name, items)
log(f'{name}: {len(items)} items')
# Client list
try:
client_items = fetch_list_all(token, '/api/catalogos/clientes/listado', method='POST',
payload={'taller': '', 'nombreCompleto': '', 'idClienteEstatus': '-1'})
save('catalog_clientes', client_items)
log(f'catalog_clientes: {len(client_items)} items')
# Fetch full client details for clients referenced in orders
orders_path = OUT_DIR / 'orders_list.json'
if orders_path.exists():
order_ids = {o['idCliente'] for o in json.loads(orders_path.read_text(encoding='utf-8')) if o.get('idCliente')}
log(f'Fetching details for {len(order_ids)} unique order clients...')
client_details = {}
for idx, cid in enumerate(sorted(order_ids), 1):
detail_path = OUT_DIR / 'client_details' / f'client_{cid}.json'
if detail_path.exists():
client_details[cid] = json.loads(detail_path.read_text(encoding='utf-8'))
continue
try:
data = fetch_get(token, f'/api/catalogos/clientes/{cid}')
client_details[cid] = data.get('datos')
detail_path.parent.mkdir(parents=True, exist_ok=True)
detail_path.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding='utf-8')
except Exception as e:
log(f' client {cid} detail error: {e}')
if idx % 30 == 0:
time.sleep(1)
else:
time.sleep(0.2)
save('catalog_client_details', client_details)
log(f'catalog_client_details: {len(client_details)} items')
except Exception as e:
log(f'catalog_clientes error: {e}')
log('Done.')
if __name__ == '__main__':
main()