Update src/streamlit_app.py
Browse files- src/streamlit_app.py +86 -88
src/streamlit_app.py
CHANGED
|
@@ -38,11 +38,6 @@ import pandas as pd
|
|
| 38 |
from PIL import Image
|
| 39 |
from huggingface_hub import login
|
| 40 |
|
| 41 |
-
# π ADD CALLBACK FUNCTION AT TOP LEVEL
|
| 42 |
-
def trigger_rerun():
|
| 43 |
-
# Forces immediate rerun after any edit β ensures edited_df is fresh
|
| 44 |
-
pass
|
| 45 |
-
|
| 46 |
# ---------------------------
|
| 47 |
# UI: main
|
| 48 |
# ---------------------------
|
|
@@ -178,7 +173,7 @@ def run_inference_on_image(image: Image.Image, processor, model, device, decoder
|
|
| 178 |
pixel_values=pixel_values,
|
| 179 |
decoder_input_ids=decoder_input_ids,
|
| 180 |
max_length=1536,
|
| 181 |
-
num_beams=
|
| 182 |
early_stopping=False,
|
| 183 |
)
|
| 184 |
|
|
@@ -681,15 +676,15 @@ if not st.session_state.is_processing_batch and len(st.session_state.batch_resul
|
|
| 681 |
# RESULTS VIEW β Show selector + editable form
|
| 682 |
# ---------------------------
|
| 683 |
elif len(st.session_state.batch_results) > 0:
|
| 684 |
-
|
| 685 |
-
|
| 686 |
-
|
| 687 |
if st.button("π¦ Download All Results (Excel)", key="download_all"):
|
| 688 |
-
|
| 689 |
all_rows = []
|
| 690 |
for file_hash, result in st.session_state.batch_results.items():
|
| 691 |
rows = flatten_invoice_to_rows(result["edited_data"])
|
| 692 |
-
|
| 693 |
for r in rows:
|
| 694 |
r["Source File"] = result.get("file_name", file_hash)
|
| 695 |
all_rows.extend(rows)
|
|
@@ -699,13 +694,13 @@ elif len(st.session_state.batch_results) > 0:
|
|
| 699 |
else:
|
| 700 |
full_df = pd.DataFrame(all_rows)
|
| 701 |
|
| 702 |
-
|
| 703 |
cols = list(full_df.columns)
|
| 704 |
if "Source File" in cols:
|
| 705 |
cols = ["Source File"] + [c for c in cols if c != "Source File"]
|
| 706 |
full_df = full_df[cols]
|
| 707 |
|
| 708 |
-
|
| 709 |
buffer = BytesIO()
|
| 710 |
dl_filename = "all_extracted_invoices.xlsx"
|
| 711 |
tried_xlsx = False
|
|
@@ -726,7 +721,7 @@ elif len(st.session_state.batch_results) > 0:
|
|
| 726 |
dl_filename = "all_extracted_invoices.csv"
|
| 727 |
mime = "text/csv"
|
| 728 |
|
| 729 |
-
|
| 730 |
import base64
|
| 731 |
import streamlit.components.v1 as components
|
| 732 |
b64 = base64.b64encode(file_bytes).decode()
|
|
@@ -768,8 +763,9 @@ elif len(st.session_state.batch_results) > 0:
|
|
| 768 |
# Get current file data
|
| 769 |
current = st.session_state.batch_results[selected_hash]
|
| 770 |
image = current["image"]
|
| 771 |
-
|
| 772 |
-
|
|
|
|
| 773 |
|
| 774 |
# Layout
|
| 775 |
left_col, right_col = st.columns([1, 1])
|
|
@@ -806,6 +802,7 @@ elif len(st.session_state.batch_results) > 0:
|
|
| 806 |
st.rerun()
|
| 807 |
except Exception as e:
|
| 808 |
st.error(f"Re-run failed: {e}")
|
|
|
|
| 809 |
tabs = st.tabs(["Invoice Details", "Sender/Recipient info", "Bank Details", "Line Items"])
|
| 810 |
|
| 811 |
st.markdown(
|
|
@@ -830,48 +827,53 @@ elif len(st.session_state.batch_results) > 0:
|
|
| 830 |
""",
|
| 831 |
unsafe_allow_html=True,
|
| 832 |
)
|
|
|
|
| 833 |
# ---------- Invoice Details ----------
|
|
|
|
| 834 |
with tabs[0]:
|
| 835 |
with st.container():
|
| 836 |
-
|
| 837 |
-
|
| 838 |
-
|
|
|
|
| 839 |
curr_options = ['USD', 'EUR', 'GBP', 'INR', 'Other']
|
| 840 |
curr_value = form_data.get('Currency', 'USD')
|
| 841 |
curr_index = curr_options.index(curr_value) if curr_value in curr_options else (len(curr_options) - 1)
|
| 842 |
-
|
| 843 |
-
if
|
| 844 |
-
|
| 845 |
-
|
| 846 |
-
|
| 847 |
-
|
| 848 |
-
|
| 849 |
-
|
| 850 |
|
| 851 |
# ---------- Sender / Recipient ----------
|
| 852 |
with tabs[1]:
|
| 853 |
-
# Get FLAT top-level keys (NOT nested dictionaries)
|
| 854 |
sender_name = form_data.get('Sender Name', '')
|
| 855 |
sender_address = form_data.get('Sender Address', '')
|
| 856 |
recipient_name = form_data.get('Recipient Name', '')
|
| 857 |
recipient_address = form_data.get('Recipient Address', '')
|
| 858 |
|
| 859 |
with st.container():
|
| 860 |
-
|
| 861 |
-
|
| 862 |
-
|
| 863 |
-
|
| 864 |
|
| 865 |
if st.button("β Swap", help="Swap sender and recipient information", key=f"swap_{selected_hash}"):
|
| 866 |
-
# Swap
|
| 867 |
-
|
| 868 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 869 |
st.rerun()
|
| 870 |
|
| 871 |
-
# ---------- Bank Details ----------
|
| 872 |
-
# ---------- Bank Details ---------- (FIXED)
|
| 873 |
with tabs[2]:
|
| 874 |
-
# Get bank details from nested dictionary
|
| 875 |
bank_details = form_data.get("Bank Details", {})
|
| 876 |
if not isinstance(bank_details, dict):
|
| 877 |
bank_details = {}
|
|
@@ -885,31 +887,20 @@ elif len(st.session_state.batch_results) > 0:
|
|
| 885 |
bank_branch = bank_details.get('bank_branch', '')
|
| 886 |
|
| 887 |
with st.container():
|
| 888 |
-
|
| 889 |
-
|
| 890 |
-
|
| 891 |
-
|
| 892 |
-
|
| 893 |
-
|
| 894 |
-
|
| 895 |
-
|
| 896 |
-
# Update the nested Bank Details dictionary
|
| 897 |
-
form_data.setdefault("Bank Details", {})
|
| 898 |
-
form_data["Bank Details"]["bank_name"] = bank_name
|
| 899 |
-
form_data["Bank Details"]["bank_acc_no"] = bank_acc_no
|
| 900 |
-
form_data["Bank Details"]["bank_acc_name"] = bank_acc_name
|
| 901 |
-
form_data["Bank Details"]["bank_iban"] = bank_iban
|
| 902 |
-
form_data["Bank Details"]["bank_swift"] = bank_swift
|
| 903 |
-
form_data["Bank Details"]["bank_routing"] = bank_routing
|
| 904 |
-
form_data["Bank Details"]["bank_branch"] = bank_branch
|
| 905 |
|
| 906 |
# ---------- Line Items ----------
|
| 907 |
-
# ---------- Line Items ----------
|
| 908 |
with tabs[3]:
|
| 909 |
editor_key = f"item_editor_{selected_hash}"
|
| 910 |
item_rows = form_data.get('Itemized Data', []) or []
|
| 911 |
|
| 912 |
-
# --- Normalize item keys produced by the model
|
| 913 |
def normalize_item_keys(item):
|
| 914 |
if not isinstance(item, dict):
|
| 915 |
return {
|
|
@@ -938,17 +929,13 @@ elif len(st.session_state.batch_results) > 0:
|
|
| 938 |
"line_total": "Line Total",
|
| 939 |
}
|
| 940 |
new = {}
|
| 941 |
-
# map keys
|
| 942 |
for k, v in item.items():
|
| 943 |
key = mapping.get(k, mapping.get(str(k).lower(), k))
|
| 944 |
-
# normalize to our canonical set where possible
|
| 945 |
if key in ["Description", "Quantity", "Unit Price", "Amount", "Tax", "Line Total"]:
|
| 946 |
new[key] = v
|
| 947 |
else:
|
| 948 |
-
# store unknowns too so user can see them if needed
|
| 949 |
new[k] = v
|
| 950 |
|
| 951 |
-
# ensure canonical columns exist (avoid missing columns in DataFrame)
|
| 952 |
for kk in ["Description", "Quantity", "Unit Price", "Amount", "Tax", "Line Total"]:
|
| 953 |
if kk not in new:
|
| 954 |
new[kk] = ""
|
|
@@ -956,11 +943,8 @@ elif len(st.session_state.batch_results) > 0:
|
|
| 956 |
return new
|
| 957 |
|
| 958 |
normalized_items = [normalize_item_keys(it) for it in item_rows]
|
| 959 |
-
|
| 960 |
-
# Create DataFrame from normalized items (these columns will be visible)
|
| 961 |
df = pd.DataFrame(normalized_items)
|
| 962 |
|
| 963 |
-
# Ensure columns exist and are named user-friendly
|
| 964 |
for col in ["Description", "Quantity", "Unit Price", "Amount", "Tax", "Line Total"]:
|
| 965 |
if col not in df.columns:
|
| 966 |
df[col] = ""
|
|
@@ -975,38 +959,57 @@ elif len(st.session_state.batch_results) > 0:
|
|
| 975 |
if len(edited_df) == 0:
|
| 976 |
st.info("No line items found in the invoice.")
|
| 977 |
|
| 978 |
-
|
| 979 |
-
# Save button (per file)
|
| 980 |
if st.button("πΎ Save Edits for This File", key=f"save_{selected_hash}"):
|
| 981 |
-
#
|
| 982 |
-
|
| 983 |
-
|
| 984 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 985 |
st.success(f"β
Edits saved for {current['file_name']}")
|
| 986 |
|
| 987 |
# Download buttons (per file)
|
| 988 |
st.markdown("---")
|
| 989 |
col_a, col_b, col_c = st.columns([1, 1, 1])
|
| 990 |
-
|
| 991 |
-
#jsonl_str = json.dumps(data, ensure_ascii=False, indent=2)
|
| 992 |
-
#st.download_button(
|
| 993 |
-
# "π₯ Download JSON",
|
| 994 |
-
#jsonl_str.encode("utf-8"),
|
| 995 |
-
#file_name=f"{Path(current['file_name']).stem}_extracted.json",
|
| 996 |
-
#mime="application/json",
|
| 997 |
-
#key=f"dl_json_{selected_hash}"
|
| 998 |
-
#)
|
| 999 |
with col_b:
|
| 1000 |
-
#
|
| 1001 |
-
rows = flatten_invoice_to_rows(
|
| 1002 |
full_df = pd.DataFrame(rows)
|
| 1003 |
|
| 1004 |
# Optional: Reorder columns for better readability
|
| 1005 |
desired_col_order = [
|
| 1006 |
"Invoice Number", "Invoice Date", "Due Date", "Currency",
|
| 1007 |
-
"Sender Name", "Sender Address", "Recipient Name", "Recipient Address",
|
| 1008 |
"Subtotal", "Tax Percentage", "Total Tax", "Total Amount",
|
| 1009 |
-
"
|
|
|
|
| 1010 |
"Item Description", "Item Quantity", "Item Unit Price", "Item Amount", "Item Tax", "Item Line Total"
|
| 1011 |
]
|
| 1012 |
# Keep only columns that exist
|
|
@@ -1025,11 +1028,6 @@ elif len(st.session_state.batch_results) > 0:
|
|
| 1025 |
mime="text/csv",
|
| 1026 |
key=f"dl_csv_{selected_hash}"
|
| 1027 |
)
|
| 1028 |
-
# Global Download All β produce a single Excel file (concatenated rows) and trigger direct download
|
| 1029 |
-
|
| 1030 |
-
|
| 1031 |
-
# ---------------------------
|
| 1032 |
-
# PROCESSING STATE
|
| 1033 |
|
| 1034 |
# ---------------------------
|
| 1035 |
# PROCESSING STATE β Show progress
|
|
|
|
| 38 |
from PIL import Image
|
| 39 |
from huggingface_hub import login
|
| 40 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
# ---------------------------
|
| 42 |
# UI: main
|
| 43 |
# ---------------------------
|
|
|
|
| 173 |
pixel_values=pixel_values,
|
| 174 |
decoder_input_ids=decoder_input_ids,
|
| 175 |
max_length=1536,
|
| 176 |
+
num_beams=4,
|
| 177 |
early_stopping=False,
|
| 178 |
)
|
| 179 |
|
|
|
|
| 676 |
# RESULTS VIEW β Show selector + editable form
|
| 677 |
# ---------------------------
|
| 678 |
elif len(st.session_state.batch_results) > 0:
|
| 679 |
+
# ---------------------------
|
| 680 |
+
# Global Download All β produce a single Excel file (concatenated rows) and trigger direct download
|
| 681 |
+
# ---------------------------
|
| 682 |
if st.button("π¦ Download All Results (Excel)", key="download_all"):
|
| 683 |
+
# Collect rows from all invoices and concatenate into one DataFrame
|
| 684 |
all_rows = []
|
| 685 |
for file_hash, result in st.session_state.batch_results.items():
|
| 686 |
rows = flatten_invoice_to_rows(result["edited_data"])
|
| 687 |
+
# Annotate rows with source file name so user can identify which invoice each row came from
|
| 688 |
for r in rows:
|
| 689 |
r["Source File"] = result.get("file_name", file_hash)
|
| 690 |
all_rows.extend(rows)
|
|
|
|
| 694 |
else:
|
| 695 |
full_df = pd.DataFrame(all_rows)
|
| 696 |
|
| 697 |
+
# Reorder columns to put Source File first
|
| 698 |
cols = list(full_df.columns)
|
| 699 |
if "Source File" in cols:
|
| 700 |
cols = ["Source File"] + [c for c in cols if c != "Source File"]
|
| 701 |
full_df = full_df[cols]
|
| 702 |
|
| 703 |
+
# Try to write XLSX (preferred). If engine not available, fall back to CSV.
|
| 704 |
buffer = BytesIO()
|
| 705 |
dl_filename = "all_extracted_invoices.xlsx"
|
| 706 |
tried_xlsx = False
|
|
|
|
| 721 |
dl_filename = "all_extracted_invoices.csv"
|
| 722 |
mime = "text/csv"
|
| 723 |
|
| 724 |
+
# Trigger immediate download via a data URI and small HTML snippet
|
| 725 |
import base64
|
| 726 |
import streamlit.components.v1 as components
|
| 727 |
b64 = base64.b64encode(file_bytes).decode()
|
|
|
|
| 763 |
# Get current file data
|
| 764 |
current = st.session_state.batch_results[selected_hash]
|
| 765 |
image = current["image"]
|
| 766 |
+
|
| 767 |
+
# β
FIX: Don't create a copy here - just reference the stored data
|
| 768 |
+
form_data = current["edited_data"]
|
| 769 |
|
| 770 |
# Layout
|
| 771 |
left_col, right_col = st.columns([1, 1])
|
|
|
|
| 802 |
st.rerun()
|
| 803 |
except Exception as e:
|
| 804 |
st.error(f"Re-run failed: {e}")
|
| 805 |
+
|
| 806 |
tabs = st.tabs(["Invoice Details", "Sender/Recipient info", "Bank Details", "Line Items"])
|
| 807 |
|
| 808 |
st.markdown(
|
|
|
|
| 827 |
""",
|
| 828 |
unsafe_allow_html=True,
|
| 829 |
)
|
| 830 |
+
|
| 831 |
# ---------- Invoice Details ----------
|
| 832 |
+
# β
FIX: Read values directly from widgets without assigning back to form_data
|
| 833 |
with tabs[0]:
|
| 834 |
with st.container():
|
| 835 |
+
st.text_input("Invoice Number", value=form_data.get('Invoice Number', ''), key=f"invoice_number_{selected_hash}")
|
| 836 |
+
st.text_input("Invoice Date", value=str(form_data.get('Invoice Date', '')).strip(), key=f"invoice_date_text_{selected_hash}")
|
| 837 |
+
st.text_input("Due Date", value=str(form_data.get('Due Date', '')).strip(), key=f"due_date_text_{selected_hash}")
|
| 838 |
+
|
| 839 |
curr_options = ['USD', 'EUR', 'GBP', 'INR', 'Other']
|
| 840 |
curr_value = form_data.get('Currency', 'USD')
|
| 841 |
curr_index = curr_options.index(curr_value) if curr_value in curr_options else (len(curr_options) - 1)
|
| 842 |
+
st.selectbox("Currency", options=curr_options, index=curr_index, key=f"currency_select_{selected_hash}")
|
| 843 |
+
if st.session_state.get(f"currency_select_{selected_hash}") == 'Other':
|
| 844 |
+
st.text_input("Specify Currency", value=form_data.get('Currency', ''), key=f"custom_currency_{selected_hash}")
|
| 845 |
+
|
| 846 |
+
safe_number_input("Subtotal", form_data.get('Subtotal', 0.0), f"subtotal_{selected_hash}")
|
| 847 |
+
safe_number_input("Tax Percentage", form_data.get('Tax Percentage', 0.0), f"tax_pct_{selected_hash}")
|
| 848 |
+
safe_number_input("Total Tax", form_data.get('Total Tax', 0.0), f"total_tax_{selected_hash}")
|
| 849 |
+
safe_number_input("Total Amount", form_data.get('Total Amount', 0.0), f"total_amount_{selected_hash}")
|
| 850 |
|
| 851 |
# ---------- Sender / Recipient ----------
|
| 852 |
with tabs[1]:
|
|
|
|
| 853 |
sender_name = form_data.get('Sender Name', '')
|
| 854 |
sender_address = form_data.get('Sender Address', '')
|
| 855 |
recipient_name = form_data.get('Recipient Name', '')
|
| 856 |
recipient_address = form_data.get('Recipient Address', '')
|
| 857 |
|
| 858 |
with st.container():
|
| 859 |
+
st.text_input("Sender Name*", value=sender_name, key=f"sender_name_{selected_hash}")
|
| 860 |
+
st.text_area("Sender Address*", value=sender_address, key=f"sender_address_{selected_hash}")
|
| 861 |
+
st.text_input("Recipient Name*", value=recipient_name, key=f"recipient_name_{selected_hash}")
|
| 862 |
+
st.text_area("Recipient Address*", value=recipient_address, key=f"recipient_address_{selected_hash}")
|
| 863 |
|
| 864 |
if st.button("β Swap", help="Swap sender and recipient information", key=f"swap_{selected_hash}"):
|
| 865 |
+
# Swap in session_state widget values
|
| 866 |
+
temp_name = st.session_state.get(f"sender_name_{selected_hash}", "")
|
| 867 |
+
temp_addr = st.session_state.get(f"sender_address_{selected_hash}", "")
|
| 868 |
+
|
| 869 |
+
st.session_state[f"sender_name_{selected_hash}"] = st.session_state.get(f"recipient_name_{selected_hash}", "")
|
| 870 |
+
st.session_state[f"sender_address_{selected_hash}"] = st.session_state.get(f"recipient_address_{selected_hash}", "")
|
| 871 |
+
st.session_state[f"recipient_name_{selected_hash}"] = temp_name
|
| 872 |
+
st.session_state[f"recipient_address_{selected_hash}"] = temp_addr
|
| 873 |
st.rerun()
|
| 874 |
|
| 875 |
+
# ---------- Bank Details ----------
|
|
|
|
| 876 |
with tabs[2]:
|
|
|
|
| 877 |
bank_details = form_data.get("Bank Details", {})
|
| 878 |
if not isinstance(bank_details, dict):
|
| 879 |
bank_details = {}
|
|
|
|
| 887 |
bank_branch = bank_details.get('bank_branch', '')
|
| 888 |
|
| 889 |
with st.container():
|
| 890 |
+
st.text_input("Bank Name", value=bank_name, key=f"bank_name_{selected_hash}")
|
| 891 |
+
st.text_input("Account Number", value=bank_acc_no, key=f"bank_acc_no_{selected_hash}")
|
| 892 |
+
st.text_input("Bank Account Name", value=bank_acc_name, key=f"bank_acc_name_{selected_hash}")
|
| 893 |
+
st.text_input("IBAN", value=bank_iban, key=f"iban_{selected_hash}")
|
| 894 |
+
st.text_input("SWIFT Code", value=bank_swift, key=f"swift_code_{selected_hash}")
|
| 895 |
+
st.text_input("Routing Number", value=bank_routing, key=f"routing_{selected_hash}")
|
| 896 |
+
st.text_input("Branch", value=bank_branch, key=f"branch_{selected_hash}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 897 |
|
| 898 |
# ---------- Line Items ----------
|
|
|
|
| 899 |
with tabs[3]:
|
| 900 |
editor_key = f"item_editor_{selected_hash}"
|
| 901 |
item_rows = form_data.get('Itemized Data', []) or []
|
| 902 |
|
| 903 |
+
# --- Normalize item keys produced by the model ---
|
| 904 |
def normalize_item_keys(item):
|
| 905 |
if not isinstance(item, dict):
|
| 906 |
return {
|
|
|
|
| 929 |
"line_total": "Line Total",
|
| 930 |
}
|
| 931 |
new = {}
|
|
|
|
| 932 |
for k, v in item.items():
|
| 933 |
key = mapping.get(k, mapping.get(str(k).lower(), k))
|
|
|
|
| 934 |
if key in ["Description", "Quantity", "Unit Price", "Amount", "Tax", "Line Total"]:
|
| 935 |
new[key] = v
|
| 936 |
else:
|
|
|
|
| 937 |
new[k] = v
|
| 938 |
|
|
|
|
| 939 |
for kk in ["Description", "Quantity", "Unit Price", "Amount", "Tax", "Line Total"]:
|
| 940 |
if kk not in new:
|
| 941 |
new[kk] = ""
|
|
|
|
| 943 |
return new
|
| 944 |
|
| 945 |
normalized_items = [normalize_item_keys(it) for it in item_rows]
|
|
|
|
|
|
|
| 946 |
df = pd.DataFrame(normalized_items)
|
| 947 |
|
|
|
|
| 948 |
for col in ["Description", "Quantity", "Unit Price", "Amount", "Tax", "Line Total"]:
|
| 949 |
if col not in df.columns:
|
| 950 |
df[col] = ""
|
|
|
|
| 959 |
if len(edited_df) == 0:
|
| 960 |
st.info("No line items found in the invoice.")
|
| 961 |
|
| 962 |
+
# β
FIX: Save button now collects values from session_state widgets
|
|
|
|
| 963 |
if st.button("πΎ Save Edits for This File", key=f"save_{selected_hash}"):
|
| 964 |
+
# Collect all values from session_state
|
| 965 |
+
updated_data = {
|
| 966 |
+
'Invoice Number': st.session_state.get(f"invoice_number_{selected_hash}", ""),
|
| 967 |
+
'Invoice Date': st.session_state.get(f"invoice_date_text_{selected_hash}", ""),
|
| 968 |
+
'Due Date': st.session_state.get(f"due_date_text_{selected_hash}", ""),
|
| 969 |
+
'Currency': st.session_state.get(f"custom_currency_{selected_hash}", "") if st.session_state.get(f"currency_select_{selected_hash}") == 'Other' else st.session_state.get(f"currency_select_{selected_hash}", "USD"),
|
| 970 |
+
'Subtotal': st.session_state.get(f"subtotal_{selected_hash}", 0.0),
|
| 971 |
+
'Tax Percentage': st.session_state.get(f"tax_pct_{selected_hash}", 0.0),
|
| 972 |
+
'Total Tax': st.session_state.get(f"total_tax_{selected_hash}", 0.0),
|
| 973 |
+
'Total Amount': st.session_state.get(f"total_amount_{selected_hash}", 0.0),
|
| 974 |
+
'Sender Name': st.session_state.get(f"sender_name_{selected_hash}", ""),
|
| 975 |
+
'Sender Address': st.session_state.get(f"sender_address_{selected_hash}", ""),
|
| 976 |
+
'Recipient Name': st.session_state.get(f"recipient_name_{selected_hash}", ""),
|
| 977 |
+
'Recipient Address': st.session_state.get(f"recipient_address_{selected_hash}", ""),
|
| 978 |
+
'Bank Details': {
|
| 979 |
+
'bank_name': st.session_state.get(f"bank_name_{selected_hash}", ""),
|
| 980 |
+
'bank_acc_no': st.session_state.get(f"bank_acc_no_{selected_hash}", ""),
|
| 981 |
+
'bank_acc_name': st.session_state.get(f"bank_acc_name_{selected_hash}", ""),
|
| 982 |
+
'bank_iban': st.session_state.get(f"iban_{selected_hash}", ""),
|
| 983 |
+
'bank_swift': st.session_state.get(f"swift_code_{selected_hash}", ""),
|
| 984 |
+
'bank_routing': st.session_state.get(f"routing_{selected_hash}", ""),
|
| 985 |
+
'bank_branch': st.session_state.get(f"branch_{selected_hash}", "")
|
| 986 |
+
},
|
| 987 |
+
'Itemized Data': edited_df.to_dict('records')
|
| 988 |
+
}
|
| 989 |
+
|
| 990 |
+
# Also set convenience fields
|
| 991 |
+
updated_data['Sender'] = {"Name": updated_data['Sender Name'], "Address": updated_data['Sender Address']}
|
| 992 |
+
updated_data['Recipient'] = {"Name": updated_data['Recipient Name'], "Address": updated_data['Recipient Address']}
|
| 993 |
+
|
| 994 |
+
# Update session state
|
| 995 |
+
st.session_state.batch_results[selected_hash]["edited_data"] = updated_data
|
| 996 |
st.success(f"β
Edits saved for {current['file_name']}")
|
| 997 |
|
| 998 |
# Download buttons (per file)
|
| 999 |
st.markdown("---")
|
| 1000 |
col_a, col_b, col_c = st.columns([1, 1, 1])
|
| 1001 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1002 |
with col_b:
|
| 1003 |
+
# Use the saved edited_data (not the temporary form_data)
|
| 1004 |
+
rows = flatten_invoice_to_rows(st.session_state.batch_results[selected_hash]["edited_data"])
|
| 1005 |
full_df = pd.DataFrame(rows)
|
| 1006 |
|
| 1007 |
# Optional: Reorder columns for better readability
|
| 1008 |
desired_col_order = [
|
| 1009 |
"Invoice Number", "Invoice Date", "Due Date", "Currency",
|
|
|
|
| 1010 |
"Subtotal", "Tax Percentage", "Total Tax", "Total Amount",
|
| 1011 |
+
"Sender Name", "Sender Address", "Recipient Name", "Recipient Address",
|
| 1012 |
+
"bank_name", "bank_acc_no", "bank_acc_name", "bank_iban", "bank_swift", "bank_routing", "bank_branch",
|
| 1013 |
"Item Description", "Item Quantity", "Item Unit Price", "Item Amount", "Item Tax", "Item Line Total"
|
| 1014 |
]
|
| 1015 |
# Keep only columns that exist
|
|
|
|
| 1028 |
mime="text/csv",
|
| 1029 |
key=f"dl_csv_{selected_hash}"
|
| 1030 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1031 |
|
| 1032 |
# ---------------------------
|
| 1033 |
# PROCESSING STATE β Show progress
|