Files
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

69 lines
3.0 KiB
Python

"""
Dry-run del IncomingPayment: para una o más órdenes de una tienda descarga
detalle y transacciones de Shopify (GET), toma la Invoice de SAP si la orden
ya está integrada (GET) o una sintética si no, y muestra el JSON de pago que
se enviaría. NO escribe en SAP ni en la base de datos.
Uso: python -m tools.probar_pago <store_code> <shopify_order_id> [...]
"""
import json
import sys
from decimal import Decimal
from pathlib import Path
from core.config import cargar_config_tienda
from core.db import db_session
from core.sap import SAPServiceLayerClient
from core.shopify_client import ShopifyClient
from domain.payment import PaymentConfigError, construir_incoming_payment, transacciones_efectivas
STORES_DIR = Path(__file__).resolve().parent.parent / "config" / "stores"
def _invoice(sap: SAPServiceLayerClient, store_code: str, order_id: int, detalle: dict) -> tuple[dict, str]:
with db_session() as conn:
row = conn.execute(
"""
SELECT d.sap_doc_entry FROM order_documents d JOIN orders o ON o.sys_id = d.order_sys_id
WHERE o.store_code = ? AND o.shopify_order_id = ? AND d.doc_type = 'INVOICE' AND d.status = 'created'
""",
(store_code, order_id),
).fetchone()
if row:
return sap.forward("GET", f"Invoices({row['sap_doc_entry']})").json(), f"Invoice real DocEntry {row['sap_doc_entry']}"
sintetica = {
"DocEntry": 0,
"CardCode": "C99998",
"CardName": "(Invoice sintética)",
"DocDate": detalle["created_at"][:10],
"DocumentLines": [{"Quantity": 1, "PriceAfterVAT": float(detalle["total_price"])}],
}
return sintetica, "Invoice sintética (orden aún sin Invoice en SAP)"
def main() -> None:
store_code, ids = sys.argv[1], [int(x) for x in sys.argv[2:]]
cfg = cargar_config_tienda(STORES_DIR / f"{store_code}.json")
shopify = ShopifyClient.from_store_config(cfg["shopify"])
sap = SAPServiceLayerClient.from_store_config(cfg["sap_connection"])
for order_id in ids:
print(f"\n===== Orden {order_id} =====")
detalle = shopify.obtener_orden(order_id)
transacciones = shopify.obtener_transacciones(order_id)
invoice, origen = _invoice(sap, store_code, order_id, detalle)
total = sum(Decimal(str(l["Quantity"])) * Decimal(str(l["PriceAfterVAT"])) for l in invoice["DocumentLines"])
print(f"{detalle.get('name')} | payment_gateway_names={detalle.get('payment_gateway_names')} | total_price={detalle.get('total_price')}")
print(f"{len(transacciones)} transacciones -> {len(transacciones_efectivas(transacciones))} efectivas | {origen} | total Invoice={total}")
try:
payload = construir_incoming_payment(detalle, transacciones, cfg, invoice, invoice["DocEntry"], float(total))
print(json.dumps(payload, indent=2, ensure_ascii=False))
except PaymentConfigError as exc:
print(f"ERROR (no se crearía el pago): {exc}")
if __name__ == "__main__":
main()