se agrega integración con SAP HANA, manejo de errores en el demonio y actualización de documentación

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
GeorgeandClaude Sonnet 5 committed 2026-09-02 07:40:26 -05:00
1 parent fcc80d3c6e
commit e6cde62e72
14 files changed
+1711 -237

No files matched your search

+34 -20
View File
@@ -134,55 +134,69 @@ def _extract_factura_details(text: str, data: dict) -> None:
if table_start_index != -1:
items_lines = lines[table_start_index + 1:]
current_item = None
pre_item_buffer = []
for line in items_lines:
line = line.strip()
if not line: continue
if re.search(r"^(?:SUB|TOTAL|IGV|SON:|OP\.|RES|CUO|DET|BANCO|Representación|Podrá|Autorizado|FORMA|CUOTA|GRAVADO|I\.G\.V\.|DESEMBOLSO|OBSERVACION)", line, re.IGNORECASE):
break
amounts = re.findall(r"(?<![A-Z0-9-])(\d+(?:,\d{3})*(?:\.\d{2,}))(?![A-Z0-9-])", line)
amounts = re.findall(r"(?<![A-Z0-9-])(\d+(?:,\d{3})*(?:\.\d{2,}))(?:(?=\s)|(?=$)|(?=[A-Z][a-z]))", line)
if len(amounts) >= 2:
if current_item: data["items"].append(current_item)
qty = amounts[0]
total = amounts[-1]
p_unit = amounts[-2]
if len(amounts) >= 4:
p_unit = amounts[1]
p_unit = amounts[-2].replace(",", "")
total = amounts[-1].replace(",", "")
desc_line = line
for amt in [qty, p_unit, total]:
desc_line = desc_line.replace(amt, "", 1)
seen = set()
for amt in amounts:
if amt not in seen:
desc_line = desc_line.replace(amt, "", 1)
seen.add(amt)
desc_parts = [p.strip() for p in desc_line.split() if p.strip()]
codigo = None
for i in range(min(3, len(desc_parts))):
if re.search(r"[A-Z].*\d|\d.*[A-Z]|[A-Z]{2,}-\d+", desc_parts[i]):
codigo = desc_parts[i]
desc_parts.pop(i)
cod = None
for j in range(min(3, len(desc_parts))):
if re.search(r"[A-Z].*\d|\d.*[A-Z]|[A-Z]{2,}-\d+", desc_parts[j]):
cod = desc_parts[j]
desc_parts.pop(j)
break
if desc_parts and desc_parts[0].isdigit() and len(desc_parts[0]) <= 2:
desc_parts.pop(0)
descripcion = " ".join(desc_parts)
current_item = {
"codigo": codigo,
"codigo": cod,
"cantidad": qty,
"descripcion": descripcion,
"precio_unitario": p_unit.replace(",", ""),
"total": total.replace(",", ""),
"precio_unitario": p_unit,
"total": total,
"guias_remision": [],
"guia_transportista": None,
"bolsas": None,
"peso": None,
"unidad_peso": None,
}
if pre_item_buffer:
current_item["descripcion"] = " ".join(pre_item_buffer) + " " + descripcion
pre_item_buffer = []
elif current_item and len(line) > 5:
current_item["descripcion"] += " " + line
elif not current_item and len(line) > 5:
pre_item_buffer.append(line)
if current_item: data["items"].append(current_item)
# Extraer guías de cada ítem usando los mismos patrones que sunat_schema
GUIAS_PDF_PATTERNS = [
(r"(?:GRE\s+REMITENTE|GRR|G\.R\.R)\s*[:.]?\s*([TVE][A-Z0-9]{3,4})\s*-?\s*(\d+)", "09"),
(r"\b(?:GRT|G\.R\.T)\s*[:.]?\s*([TVE][A-Z0-9]{3,4})\s*-?\s*(\d+)", "31"),
]
for item in data["items"]:
desc = item["descripcion"]
guias_remision_matches = re.findall(r"\b(?:GRR\w*|GR\b|G\.R\.R\.?|GUIA REMISI.N)\s*[:/\s]*((?:[TVE][A-Z0-9]{3})-\d+)", desc, re.IGNORECASE)
item["guias_remision"] = [m.strip() for m in guias_remision_matches] if guias_remision_matches else []
guia_transportista_match = re.search(r"\b(?:GRT\w*|GR\b|G\.R\.T\.?|GUIA TRANSPORTISTA)\s*[:/\s]*((?:[TVE][A-Z0-9]{3})-\d+)", desc, re.IGNORECASE)
if guia_transportista_match:
item["guia_transportista"] = guia_transportista_match.group(1).strip()
for patron, tipo in GUIAS_PDF_PATTERNS:
for m in re.finditer(patron, desc, re.IGNORECASE):
numero = m.group(2).lstrip("0") or "0"
if tipo == "09":
item["guias_remision"].append(f"{m.group(1).upper()}-{numero}")
else:
item["guia_transportista"] = f"{m.group(1).upper()}-{numero}"
bolsas_match = re.search(r"(\d+)\s*(?:BOLSAS|BLS|BOLS|SACOS)\b", desc, re.IGNORECASE)
if bolsas_match:
item["bolsas"] = bolsas_match.group(1)