2026-10-22 08:55:54 -05:00
|
|
|
import observaciones as obs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _item(numero, line_total=100.0, quantity=10.0, error=None):
|
|
|
|
|
item = {"guia": {"tipo": "09", "serie": "T001", "numero": numero}, "line_total": line_total, "quantity": quantity}
|
|
|
|
|
if error:
|
|
|
|
|
item["error"] = error
|
|
|
|
|
return item
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _resultado(ok, guias, sap_price=100.0, label="LineTotal (FL*) vs total_linea"):
|
|
|
|
|
return {"ok": ok, "guias_lt": guias, "sap_price": sap_price, "label": label}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FACTURA = {"lineas": [{"total_linea": 624.38, "descripcion": "Servicio A"}]}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_formato():
|
|
|
|
|
assert obs.moneda(1250) == "S/ 1,250.00"
|
|
|
|
|
assert obs.moneda(None) == "S/ 0.00"
|
|
|
|
|
assert obs.texto_observaciones(1) == "1 observación"
|
|
|
|
|
assert obs.texto_observaciones(3) == "3 observaciones"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_lineas_conformes_no_generan_observaciones():
|
|
|
|
|
assert obs.desde_validacion(FACTURA, [_resultado(True, [_item("1")])]) == []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_importe_distinto_muestra_ambos_importes():
|
|
|
|
|
(o,) = obs.desde_validacion(FACTURA, [_resultado(False, [_item("1"), _item("2")], sap_price=674.38)])
|
|
|
|
|
|
|
|
|
|
assert o.codigo == "precio_no_coincide"
|
|
|
|
|
assert dict(o.filas) == {"Guías": "T001-1, T001-2", "Importe en su factura": "S/ 624.38", "Importe esperado": "S/ 674.38"}
|
|
|
|
|
assert "Línea" not in dict(o.filas) # con una sola línea no hace falta numerarla
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_con_varias_lineas_se_indica_cual():
|
|
|
|
|
factura = {"lineas": [{"total_linea": 10}, {"total_linea": 20}]}
|
|
|
|
|
resultados = [_resultado(True, [_item("1")]), _resultado(False, [_item("2")], sap_price=25.0)]
|
|
|
|
|
|
|
|
|
|
(o,) = obs.desde_validacion(factura, resultados)
|
|
|
|
|
|
|
|
|
|
assert dict(o.filas)["Línea"] == "2"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_guia_sin_datos_en_sap_es_guia_no_encontrada_y_no_importe():
|
|
|
|
|
guias = [_item("1", 0.0, 0.0), _item("2", 0.0, 0.0)]
|
|
|
|
|
observaciones = obs.desde_validacion(FACTURA, [_resultado(False, guias, 0.0, "Sin guías SAP")])
|
|
|
|
|
|
|
|
|
|
assert [o.codigo for o in observaciones] == ["guia_no_encontrada", "guia_no_encontrada"]
|
|
|
|
|
assert [o.titulo for o in observaciones] == ["No encontramos la guía T001-1", "No encontramos la guía T001-2"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_una_guia_encontrada_y_otra_no_da_ambas_observaciones():
|
|
|
|
|
guias = [_item("1", 0.0, 0.0), _item("2", 40.0, 5.0)]
|
|
|
|
|
observaciones = obs.desde_validacion(FACTURA, [_resultado(False, guias, sap_price=40.0)])
|
|
|
|
|
|
|
|
|
|
assert [o.codigo for o in observaciones] == ["guia_no_encontrada", "precio_no_coincide"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_una_guia_con_error_de_consulta_no_se_reporta_como_no_encontrada():
|
|
|
|
|
guias = [_item("1", 0.0, 0.0, error="timeout")]
|
|
|
|
|
assert obs.desde_validacion(FACTURA, [_resultado(False, guias, 0.0)]) == [
|
|
|
|
|
obs.precio_no_coincide(1, 624.38, 0.0, ["T001-1"], False)
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_lineas_sin_guia_o_con_cantidad_cero():
|
|
|
|
|
factura = {"lineas": [{"descripcion": "Flete a Lima", "total_linea": 5}, {"total_linea": 7}]}
|
|
|
|
|
resultados = [_resultado(False, [], label="Sin guía de referencia"), _resultado(False, [], label="Cantidad cero, sin validar")]
|
|
|
|
|
|
|
|
|
|
sin_guia, cero = obs.desde_validacion(factura, resultados)
|
|
|
|
|
|
|
|
|
|
assert sin_guia.codigo == "linea_sin_guia" and dict(sin_guia.filas) == {"Línea": "1", "Descripción": "Flete a Lima"}
|
|
|
|
|
assert cero.codigo == "cantidad_cero" and dict(cero.filas) == {"Línea": "2"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_motivos_de_correo_sin_comprobante():
|
|
|
|
|
assert obs.sin_pdf_xml(2).filas == (("Archivos recibidos", "2"),)
|
|
|
|
|
assert obs.falta_xml(1).codigo == "falta_xml"
|
|
|
|
|
assert obs.sin_factura(["Guía de Remisión"]).filas == (("Documentos recibidos", "Guía de Remisión"),)
|
|
|
|
|
assert obs.sin_factura([]).filas == (("Documentos recibidos", "ninguno reconocible"),)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_todas_las_observaciones_tienen_titulo_y_como_corregir():
|
|
|
|
|
muestras = [
|
|
|
|
|
obs.precio_no_coincide(1, 1, 2, ["T001-1"], True), obs.guia_no_encontrada("T001-1"),
|
2026-10-22 12:08:38 -05:00
|
|
|
obs.linea_sin_guia(1, ""), obs.cantidad_cero(1), obs.factura_ya_registrada("F002-1"), obs.sin_pdf_xml(0), obs.falta_xml(1), obs.sin_factura([]),
|
2026-10-22 08:55:54 -05:00
|
|
|
]
|
|
|
|
|
for o in muestras:
|
|
|
|
|
assert o.titulo and o.como_corregir and o.codigo
|
|
|
|
|
assert len({o.codigo for o in muestras}) == len(muestras)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_a_dict_es_serializable():
|
|
|
|
|
d = obs.guia_no_encontrada("T001-9").a_dict()
|
|
|
|
|
assert d == {"codigo": "guia_no_encontrada", "titulo": "No encontramos la guía T001-9",
|
|
|
|
|
"filas": [{"etiqueta": "Guía", "valor": "T001-9"}], "como_corregir": d["como_corregir"]}
|
2026-10-22 12:08:38 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_factura_ya_registrada():
|
|
|
|
|
o = obs.factura_ya_registrada("F002-486")
|
|
|
|
|
assert o.codigo == "factura_ya_registrada" and o.filas == (("Comprobante", "F002-486"),)
|
|
|
|
|
assert "No necesita enviarla otra vez" in o.como_corregir
|
|
|
|
|
|