Files
shopify_run_run/tools/cargar_entrada_mercancia.py
T
georgeandClaude Sonnet 5.5 901dd8763d Integración Shopify → SAP Business One (snobbycl y on-chile)
Pipeline de integración de órdenes de Shopify a SAP Service Layer:
Invoice de reserva, pago con tarjeta o transferencia por transacción,
solicitud de traslado, traslado de stock y entrega.

- Invoice igual a lo cobrado; fulfillments limitados a lo facturado
- Estados terminales integrated, timeout, ignored y cancelled
- Idempotencia contra SAP: adopta documentos existentes en vez de duplicar
- Reintentos de red en el cliente de Shopify
- Secretos fuera de los JSON de tienda (${VARIABLE} resuelta desde .env)
- Herramientas de consulta, integración puntual y carga de pruebas
- Documentación en docs/ (flujo, casos especiales, mapeo, configuración)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 19:22:40 -05:00

83 lines
3.0 KiB
Python

"""
Utilidad puntual (no forma parte del pipeline recurrente): crea una Entrada
de Mercancía (InventoryGenEntries) en SAP con todos los SKU del grupo de
artículos indicado, para dejar el stock inicial en un almacén.
Uso:
.venv/bin/python tools/cargar_entrada_mercancia.py config/stores/snobbycl.json \
--warehouse 8801 --price 5000 --account 1-1-080-40-000 --quantity 1000
Agregar --apply para ejecutar el POST real (por defecto es dry-run).
"""
import argparse
import json
import sys
from pathlib import Path
from typing import List
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from core.config import cargar_config_tienda
from core.errors import SAPServiceLayerError
from core.sap import SAPServiceLayerClient
ITEMS_GROUP_CODE = 108
def obtener_skus_grupo(sap: SAPServiceLayerClient, group_code: int) -> List[str]:
resp = sap.forward(
"GET",
"Items",
params={"$select": "ItemCode", "$filter": f"ItemsGroupCode eq {group_code}"},
extra_headers={"B1S-PageSize": "1000"},
)
data = resp.json()
return [i["ItemCode"] for i in data.get("value", [])]
def main(store_config_path: str, warehouse: str, price: float, account: str, quantity: float, dry_run: bool) -> None:
store_config = cargar_config_tienda(store_config_path)
sap = SAPServiceLayerClient.from_store_config(store_config["sap_connection"])
skus = obtener_skus_grupo(sap, ITEMS_GROUP_CODE)
print(f"SKUs encontrados en grupo {ITEMS_GROUP_CODE}: {len(skus)}")
document_lines = [
{
"ItemCode": sku,
"WarehouseCode": warehouse,
"Quantity": quantity,
"UnitPrice": price,
"AccountCode": account,
}
for sku in skus
]
payload = {"DocumentLines": document_lines}
if dry_run:
print(json.dumps(payload["DocumentLines"][:3], indent=2, ensure_ascii=False))
print(f"... ({len(document_lines)} líneas en total, almacén={warehouse}, precio={price}, cuenta={account}, cantidad={quantity})")
print("(dry-run: no se envió nada a SAP, correr con --apply para crear el documento)")
return
try:
resp = sap.forward("POST", "InventoryGenEntries", json_data=payload)
data = resp.json()
print(f"Entrada de mercancía creada. DocEntry={data.get('DocEntry')} DocNum={data.get('DocNum')}")
except SAPServiceLayerError as exc:
print(f"ERROR creando la entrada de mercancía: {exc.detail}")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("store_config")
parser.add_argument("--warehouse", required=True)
parser.add_argument("--price", type=float, required=True)
parser.add_argument("--account", required=True)
parser.add_argument("--quantity", type=float, required=True)
parser.add_argument("--apply", action="store_true", help="Ejecuta el POST real (por defecto es dry-run)")
args = parser.parse_args()
main(args.store_config, args.warehouse, args.price, args.account, args.quantity, dry_run=not args.apply)