import logging import os import adjuntos import documentos # ============================================================================ # completar_guias # ============================================================================ def test_completar_guias_desde_pdf_normaliza_strings(): xml_doc = { "ruc_emisor": "20537929520", "tipo": "01", "serie": "F002", "numero": "486", "lineas": [{"guias_remision": []}, {"guias_remision": [{"tipo": "09", "serie": "T001", "numero": "5"}]}], } pdf_cache = {"20537929520-01-F002-486.pdf": {"items": [ {"guias_remision": ["T001-00123"], "guia_transportista": "V002-0011"}, {"guias_remision": ["T001-999"]}, ]}} copiadas = documentos.completar_guias([xml_doc], pdf_cache) assert copiadas == 1 assert xml_doc["lineas"][0]["guias_remision"] == [{"tipo": "09", "serie": "T001", "numero": "123"}] assert xml_doc["lineas"][0]["guia_transportista"] == {"tipo": "31", "serie": "V002", "numero": "11"} assert xml_doc["lineas"][1]["guias_remision"][0]["numero"] == "5" # la que ya tenía no se toca def test_completar_guias_sin_pdf_no_hace_nada(): doc = {"ruc_emisor": "1", "tipo": "01", "serie": "F", "numero": "1", "lineas": [{"guias_remision": []}]} assert documentos.completar_guias([doc], {}) == 0 assert doc["lineas"][0]["guias_remision"] == [] # ============================================================================ # validar_guias # ============================================================================ def test_validar_guias_advierte_solo_las_invalidas(caplog): doc = {"serie": "F002", "numero": "1", "lineas": [{ "guias_remision": [{"tipo": "09", "serie": "T001", "numero": "1"}, {"tipo": "09", "serie": "T001"}], "guia_transportista": {"tipo": "31", "serie": "", "numero": "2"}, }]} with caplog.at_level(logging.WARNING, logger="pipeline"): documentos.validar_guias([doc]) mensajes = [r.getMessage() for r in caplog.records] assert len(mensajes) == 2 assert any("guias_remision[1] inválida" in m for m in mensajes) assert any("guia_transportista inválida" in m for m in mensajes) assert all(r.levelno == logging.WARNING for r in caplog.records) def test_validar_guias_sin_problemas_no_registra(caplog): doc = {"serie": "F", "numero": "1", "lineas": [{"guias_remision": [{"tipo": "09", "serie": "T001", "numero": "1"}]}]} with caplog.at_level(logging.WARNING, logger="pipeline"): documentos.validar_guias([doc]) assert caplog.records == [] # ============================================================================ # copiar_a_red # ============================================================================ def _comprobante(tmp_path, fecha="2026-03-05"): xml = tmp_path / "a.xml" pdf = tmp_path / "a.pdf" xml.write_text("") pdf.write_bytes(b"%PDF") return {"ruc_emisor": "20111", "tipo": "01", "serie": "F002", "numero": "7", "fecha_emision": fecha, "ruta_xml": str(xml), "ruta_pdf": str(pdf)} def test_copiar_a_red_arma_la_estructura(tmp_path, monkeypatch): red = tmp_path / "red" monkeypatch.setenv("RUTA_RED_FACTURACION", str(red)) monkeypatch.setenv("RUC_EMPRESA", "20999") data = _comprobante(tmp_path) documentos.copiar_a_red(data) destino = red / "2026" / "3" / "5" / "01" / "20999" / "20111" # mes y día sin ceros assert (destino / "20999-01-F002-7.xml").exists() assert (destino / "20999-01-F002-7_FormatoImpreso.pdf").exists() assert data["ruta_red_xml"].endswith("\\20999-01-F002-7.xml") # estilo UNC con backslash assert data["ruta_red_pdf"].endswith("\\20999-01-F002-7_FormatoImpreso.pdf") def test_copiar_a_red_deshabilitada_si_no_hay_ruta(tmp_path, monkeypatch): monkeypatch.setenv("RUTA_RED_FACTURACION", "") data = _comprobante(tmp_path) documentos.copiar_a_red(data) assert "ruta_red_xml" not in data and "ruta_red_pdf" not in data def test_copiar_a_red_fecha_invalida_no_falla(tmp_path, monkeypatch): red = tmp_path / "red" monkeypatch.setenv("RUTA_RED_FACTURACION", str(red)) monkeypatch.setenv("RUC_EMPRESA", "20999") data = _comprobante(tmp_path, fecha="no-es-fecha") documentos.copiar_a_red(data) assert not red.exists() and "ruta_red_xml" not in data # ============================================================================ # adjuntos.filtrar_y_limpiar # ============================================================================ def test_filtrar_conserva_pdf_xml_y_borra_el_resto(tmp_path): for nombre in ("a.pdf", "B.XML", "c.png"): (tmp_path / nombre).write_text("x") email = {"adjuntos": ["a.pdf", "B.XML", "c.png"]} assert adjuntos.filtrar_y_limpiar(email, str(tmp_path)) is True assert email["adjuntos"] == ["a.pdf", "B.XML"] assert sorted(os.listdir(tmp_path)) == ["B.XML", "a.pdf"] def test_filtrar_sin_pdf_ni_xml_borra_todo(tmp_path): for nombre in ("c.png", "d.docx"): (tmp_path / nombre).write_text("x") email = {"adjuntos": ["c.png", "d.docx"]} assert adjuntos.filtrar_y_limpiar(email, str(tmp_path)) is False assert os.listdir(tmp_path) == []