Spaces:
Running on Zero
Running on Zero
File size: 9,022 Bytes
31f24ea b40c854 31f24ea b40c854 31f24ea b40c854 31f24ea b40c854 31f24ea b40c854 31f24ea b40c854 31f24ea b40c854 31f24ea b40c854 31f24ea b40c854 31f24ea b40c854 31f24ea b40c854 31f24ea b40c854 31f24ea b40c854 31f24ea b40c854 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | import openpyxl
import pdfplumber
import re
from io import BytesIO
# 🚀 Dedicated parsers import
from parser_welspun import extract_welspun_items, map_items_to_excel_dynamic
from parser_bkt import extract_bkt_items
from parser_polycab import extract_polycab_items, map_polycab_items_to_excel_dynamic
from parser_vapi_welspun import extract_vapi_welspun_items, map_vapi_welspun_items_to_excel_dynamic
from pdf_engine import apply_rule_filter, extract_header_value
from google_sheet_sync import load_template_from_sheet
from supporting_engine import extract_data_from_supporting_file
def process_invoices_backend(selected_shipper, shipper_info, main_inv_file, gst_inv_file, deec_decl_file):
"""
Gradio ke file objects ko process karke Excel file ki bytes return karta hai.
"""
if main_inv_file is None:
return None, "⚠️ Mukhya invoice file missing hai!"
try:
rules = shipper_info.get("mapping_rules", {})
item_table_rules = shipper_info.get("item_table_rules", {})
assigned_parser = shipper_info.get("item_table_rule_name", "parser_welspun").strip().lower()
igst_cfg = shipper_info.get("igst_config", {})
lut_kws = igst_cfg.get("lut_keywords", "")
paid_kws = igst_cfg.get("paid_keywords", "")
wb = load_template_from_sheet(selected_shipper)
if wb is None:
wb = openpyxl.Workbook()
ws = wb["INV"] if "INV" in wb.sheetnames else wb.active
first_inv_no = "INV"
overall_item_sr = 1
excel_write_row = 2
# Gradio file object ka path ya bytes handle karna
file_bytes = main_inv_file.read() if hasattr(main_inv_file, "read") else open(main_inv_file, "rb").read()
file_name = getattr(main_inv_file, "name", "invoice.pdf")
pdf_text = ""
pdf_lines = []
if file_name.lower().endswith(".pdf"):
with pdfplumber.open(BytesIO(file_bytes)) as pdf:
for page in pdf.pages:
t = page.extract_text()
if t:
pdf_text += t + "\n"
pdf_lines.extend(t.split("\n"))
else:
excel_text, _ = extract_data_from_supporting_file(main_inv_file)
if excel_text:
pdf_text = excel_text
pdf_lines = excel_text.split("\n")
gst_text, _ = extract_data_from_supporting_file(gst_inv_file) if gst_inv_file else ("", None)
deec_text, _ = extract_data_from_supporting_file(deec_decl_file) if deec_decl_file else ("", None)
current_inv_number = "INV_1"
current_inv_date = ""
inv_data_dict = {}
summary_row = 2
for field, r_info in rules.items():
kw = r_info.get("keyword", "").strip()
if kw.startswith("'") and len(kw) > 1:
kw = kw[1:].strip()
pos = r_info.get("position", "Right (आगे)")
target_cell = r_info.get("cell", "").strip().upper()
mode = r_info.get("match_mode", "Exact Word")
stop_kw = r_info.get("stop_kw", "").strip()
flt = r_info.get("filter", "None")
fallback_val = r_info.get("fallback", "").strip()
doc_source = r_info.get("logic", "Main Invoice")
extracted_logic = r_info.get("extracted_logic", "").strip()
found_val = None
target_lines, target_full_text = pdf_lines, pdf_text
if "gst" in doc_source.lower() and gst_inv_file:
target_lines = gst_text.split("\n")
target_full_text = gst_text
elif "deec" in doc_source.lower() and deec_decl_file:
target_lines = deec_text.split("\n")
target_full_text = deec_text
if extracted_logic:
try:
clean_code = extracted_logic.replace("```python", "").replace("```", "").strip()
clean_code = clean_code.replace(r"\n", "\n")
local_vars = {"text": target_full_text, "lines": target_lines, "re": re}
exec(clean_code, {}, local_vars)
found_val = local_vars.get("value", None)
except Exception:
found_val = None
if not found_val or not str(found_val).strip():
found_val = extract_header_value(target_lines, target_full_text, kw, pos, mode, stop_kw, flt, field_label=field, pdf_bytes=file_bytes)
if not found_val or not str(found_val).strip():
if fallback_val:
found_val = fallback_val
inv_data_dict[field.lower()] = found_val
if target_cell and "dynamic" not in target_cell.lower():
try:
if "\n" in str(found_val):
col_letters = re.findall(r'[A-Za-z]+', target_cell)[0].upper()
start_row_num = int(re.findall(r'\d+', target_cell)[0]) if re.findall(r'\d+', target_cell) else summary_row
for idx, line_val in enumerate(str(found_val).split("\n")):
ws[f"{col_letters}{start_row_num + idx}"] = line_val.strip()
else:
cell_to_write = f"{target_cell}{summary_row}" if target_cell.isalpha() else target_cell
ws[cell_to_write] = found_val
except Exception:
pass
if "inv. no" in field.lower() or "invoice no" in field.lower():
if found_val:
current_inv_number = found_val
first_inv_no = found_val
if "date" in field.lower() or "dt" in field.lower():
d_match = re.search(r'\b\d{2}[./-]\d{2}[./-]\d{4}\b', str(found_val))
if d_match:
current_inv_date = d_match.group(0).replace(".", "/").replace("-", "/")
elif found_val and not str(found_val).lower().startswith("inv"):
current_inv_date = found_val
ws[f"AH{summary_row}"] = 1
ws[f"AI{summary_row}"] = current_inv_number
if current_inv_date:
ws[f"AJ{summary_row}"] = current_inv_date
resolved_item_rules = {}
for i_name, i_info in item_table_rules.items():
i_type = i_info.get("type", "")
i_rule = i_info.get("rule", "")
if i_rule.startswith("'") and len(i_rule) > 1:
i_rule = i_rule[1:].strip()
i_col = i_info.get("col", "K")
actual_rule_val = i_rule
if i_type == "Header Field Mapping" and i_rule.lower() in inv_data_dict:
actual_rule_val = inv_data_dict[i_rule.lower()]
resolved_item_rules[i_name] = {
"col": i_col,
"type": i_type if i_type != "Header Field Mapping" else "Constant Text",
"rule": actual_rule_val
}
if assigned_parser == "parser_bkt":
parsed_items = extract_bkt_items(pdf_lines)
elif assigned_parser == "parser_polycab":
parsed_items = extract_polycab_items(pdf_lines, pdf_text=pdf_text)
elif assigned_parser == "parser_vapi_welspun":
parsed_items = extract_vapi_welspun_items(pdf_lines, pdf_text=pdf_text)
else:
parsed_items = extract_welspun_items(pdf_lines, pdf_text=pdf_text)
if assigned_parser == "parser_polycab":
ws, overall_item_sr, excel_write_row = map_polycab_items_to_excel_dynamic(
ws, parsed_items, resolved_item_rules, 1, overall_item_sr, excel_write_row, current_inv_number, current_inv_date, pdf_text, lut_kws, paid_kws, assigned_parser
)
elif assigned_parser == "parser_vapi_welspun":
ws, overall_item_sr, excel_write_row = map_vapi_welspun_items_to_excel_dynamic(
ws, parsed_items, resolved_item_rules, 1, overall_item_sr, excel_write_row, current_inv_number, current_inv_date, pdf_text, lut_kws, paid_kws, assigned_parser
)
else:
ws, overall_item_sr, excel_write_row = map_items_to_excel_dynamic(
ws, parsed_items, resolved_item_rules, 1, overall_item_sr, excel_write_row, current_inv_number, current_inv_date, pdf_text, lut_kws, paid_kws, assigned_parser
)
output = BytesIO()
wb.save(output)
short_shipper = selected_shipper.split(" ")[0].lower()
clean_inv = re.sub(r'[\\/*?:"<>|]', "", first_inv_no)
final_filename = f"{clean_inv}_{short_shipper}_MultiDoc.xlsx"
return output.getvalue(), final_filename
except Exception as e:
return None, str(e) |