Mejoras de sesion: estabilizacion, IA, Nexus Yonke y marketplace B2B
- F0 blindaje: Redis/Celery, pg_hba sin trust LAN, secretos a env 600, backups, webhook auth, jobs muertos - F1 caja: bugs POS, reembolsos al corte, apartados, CFDI, topes de descuento en cotizacion/apartado - F2/F7 IA de catalogo: autocategorizacion, dedup de SKUs con embeddings, OCR de facturas, descripciones, claves SAT, limpieza de fitment - F3 IA en el flujo: vision Yonke, preguntas MeLi, venta cruzada, anomalias, sinonimos, busqueda semantica (rerank/embeddings) - F4/F8 escala y cartera: /metrics+Prometheus+Grafana, pool, score de credito, churn, metricas multi-tenant - Nexus Yonke (M1-M6): modelo de datos 1-de-1, alta por donador, vitrina publica, MeLi usado, consignacion, garantia por dias, reportes, etiquetas - Marketplace B2B entre yonkes (catalogo + pedidos cross-tenant) - Seguridad: auth en /api/admin/* (35 endpoints), alertas por tenant - Migraciones v4.27/v4.28; timers expire-demos y rollup-metrics
This commit is contained in:
@@ -91,9 +91,10 @@ class TestRequest:
|
||||
|
||||
|
||||
class TestCreateOrganization:
|
||||
@mock.patch("pos.services.facturapi_service.update_organization_legal")
|
||||
@mock.patch("pos.services.facturapi_service.find_organization_by_rfc")
|
||||
@mock.patch("pos.services.facturapi_service._request")
|
||||
def test_creates_organization_and_generates_live_key(self, mock_request, mock_find, user_key):
|
||||
def test_creates_organization_and_generates_live_key(self, mock_request, mock_find, mock_legal, user_key):
|
||||
mock_find.return_value = None
|
||||
mock_request.side_effect = [
|
||||
{"id": "org_123"}, # POST /organizations
|
||||
@@ -102,28 +103,31 @@ class TestCreateOrganization:
|
||||
|
||||
result = facturapi_service.create_organization({"rfc": "ABC010101AAA", "razon_social": "Test SA"})
|
||||
|
||||
assert result == {"org_id": "org_123", "api_key": "sk_live_abc"}
|
||||
assert result == {"org_id": "org_123", "api_key": "sk_live_abc", "legal_updated": True}
|
||||
assert mock_request.call_count == 2
|
||||
mock_legal.assert_called_once()
|
||||
|
||||
@mock.patch("pos.services.facturapi_service.update_organization_legal")
|
||||
@mock.patch("pos.services.facturapi_service.find_organization_by_rfc")
|
||||
@mock.patch("pos.services.facturapi_service._request")
|
||||
def test_reuses_existing_organization_by_rfc(self, mock_request, mock_find, user_key):
|
||||
def test_reuses_existing_organization_by_rfc(self, mock_request, mock_find, mock_legal, user_key):
|
||||
mock_find.return_value = {"id": "org_existing"}
|
||||
mock_request.return_value = {"key": "sk_live_existing"}
|
||||
|
||||
result = facturapi_service.create_organization({"rfc": "ABC010101AAA", "razon_social": "Test SA"})
|
||||
|
||||
assert result["org_id"] == "org_existing"
|
||||
assert result["legal_updated"] is True
|
||||
mock_request.assert_called_once()
|
||||
|
||||
|
||||
class TestUploadCsd:
|
||||
@mock.patch("pos.services.facturapi_service.requests.post")
|
||||
def test_uploads_csd(self, mock_post):
|
||||
@mock.patch("pos.services.facturapi_service.requests.put")
|
||||
def test_uploads_csd(self, mock_put):
|
||||
mock_response = mock.Mock()
|
||||
mock_response.ok = True
|
||||
mock_response.json.return_value = {"certificate": {"has_certificate": True}}
|
||||
mock_post.return_value = mock_response
|
||||
mock_put.return_value = mock_response
|
||||
|
||||
config = {"facturapi_key": "sk_test", "facturapi_org_id": "org_1"}
|
||||
cer_b64 = base64.b64encode(b"fake-cer").decode("ascii")
|
||||
@@ -132,9 +136,14 @@ class TestUploadCsd:
|
||||
result = facturapi_service.upload_csd(config, cer_b64, key_b64, "password")
|
||||
|
||||
assert result["certificate"]["has_certificate"] is True
|
||||
mock_post.assert_called_once()
|
||||
_, kwargs = mock_post.call_args
|
||||
mock_put.assert_called_once()
|
||||
args, kwargs = mock_put.call_args
|
||||
assert args[0].endswith("/organizations/org_1/certificate")
|
||||
assert kwargs["auth"] == ("sk_test", "")
|
||||
assert set(kwargs["files"]) == {"cer", "key", "password"}
|
||||
assert kwargs["files"]["cer"][1] == b"fake-cer"
|
||||
assert kwargs["files"]["key"][1] == b"fake-key"
|
||||
assert kwargs["files"]["password"][1] == "password"
|
||||
|
||||
|
||||
class TestCreateInvoice:
|
||||
@@ -199,6 +208,19 @@ class TestGetOrgStatus:
|
||||
assert result["has_csd"] is True
|
||||
assert result["has_org_id"] is True
|
||||
|
||||
@mock.patch("pos.services.facturapi_service.get_organization")
|
||||
def test_handles_organization_without_certificate(self, mock_get_org):
|
||||
mock_get_org.return_value = {
|
||||
"legal": {"name": "Test SA", "tax_id": "ABC010101AAA"},
|
||||
"certificate": None,
|
||||
}
|
||||
|
||||
config = {"facturapi_key": "sk_test", "cfdi_facturapi_org_id": "org_1"}
|
||||
result = facturapi_service.get_org_status(config)
|
||||
|
||||
assert result["has_csd"] is False
|
||||
assert result["configured"] is True
|
||||
|
||||
def test_returns_error_without_key(self):
|
||||
with mock.patch.object(facturapi_service, "USER_KEY", ""):
|
||||
result = facturapi_service.get_org_status({})
|
||||
@@ -211,6 +233,17 @@ class TestGetOrgStatus:
|
||||
assert result["has_org_id"] is False
|
||||
assert "organization" in result["error"].lower()
|
||||
|
||||
@mock.patch("pos.services.facturapi_service.get_organization")
|
||||
def test_reports_error_without_raising_when_fetch_fails(self, mock_get_org):
|
||||
mock_get_org.side_effect = facturapi_service.FacturapiError("boom", status_code=500)
|
||||
|
||||
config = {"facturapi_key": "sk_live_x", "cfdi_facturapi_org_id": "org_1"}
|
||||
with mock.patch.object(facturapi_service, "USER_KEY", ""):
|
||||
result = facturapi_service.get_org_status(config)
|
||||
|
||||
assert result["has_csd"] is False
|
||||
assert "boom" in result["error"]
|
||||
|
||||
|
||||
class TestCreateOrUpdateCustomer:
|
||||
@mock.patch("pos.services.facturapi_service._request")
|
||||
|
||||
Reference in New Issue
Block a user