import pdfplumber
import json
import re
import sys
import os
from datetime import datetime


OUTPUT_SUFFIX = "_order.json"


# ============================================================
# BASIC FUNCTIONS
# ============================================================

def clean_text(value):

    if value is None:
        return ""

    value = str(value)

    value = value.replace("\r", " ")
    value = value.replace("\n", " ")

    value = re.sub(
        r"\s+",
        " ",
        value
    )

    return value.strip()


def clean_number(value):

    if value is None:
        return ""

    value = str(value)

    value = re.sub(
        r"\s+",
        "",
        value
    )

    value = value.replace(
        ",",
        ""
    )

    return value


def to_number(value):

    value = clean_number(value)

    if not value:
        return 0

    value = value.replace(
        "%",
        ""
    )

    value = re.sub(
        r"[^\d.\-]",
        "",
        value
    )

    if not value:
        return 0

    try:

        number = float(value)

        if number.is_integer():

            return int(number)

        return number

    except Exception:

        return 0


# ============================================================
# DATE FORMAT
# ============================================================

def format_date(value):

    if not value:
        return ""

    value = clean_text(value)

    # Remove time
    value = re.sub(
        r"\s+\d{1,2}:\d{2}(?::\d{2})?(?:\s*[AP]M)?$",
        "",
        value,
        flags=re.IGNORECASE
    ).strip()

    # --------------------------------------------------------
    # YYYY-MM-DD
    # YYYY/MM/DD
    # --------------------------------------------------------

    match = re.search(
        r"\b(\d{4})[-/](\d{1,2})[-/](\d{1,2})\b",
        value
    )

    if match:

        try:

            dt = datetime(
                int(match.group(1)),
                int(match.group(2)),
                int(match.group(3))
            )

            return dt.strftime(
                "%d/%m/%Y"
            )

        except Exception:
            pass

    # --------------------------------------------------------
    # DD-MM-YYYY
    # DD/MM/YYYY
    # --------------------------------------------------------

    match = re.search(
        r"\b(\d{1,2})[-/](\d{1,2})[-/](\d{4})\b",
        value
    )

    if match:

        try:

            dt = datetime(
                int(match.group(3)),
                int(match.group(2)),
                int(match.group(1))
            )

            return dt.strftime(
                "%d/%m/%Y"
            )

        except Exception:
            pass

    # --------------------------------------------------------
    # Aug 27, 2026
    # August 27, 2026
    # --------------------------------------------------------

    match = re.search(
        r"\b([A-Za-z]{3,9})\s+"
        r"(\d{1,2}),?\s+"
        r"(\d{4})\b",
        value
    )

    if match:

        date_text = "{} {} {}".format(
            match.group(1),
            match.group(2),
            match.group(3)
        )

        for fmt in (
            "%b %d %Y",
            "%B %d %Y"
        ):

            try:

                dt = datetime.strptime(
                    date_text,
                    fmt
                )

                return dt.strftime(
                    "%d/%m/%Y"
                )

            except Exception:
                pass

    # --------------------------------------------------------
    # 27 Aug 2026
    # 27 August 2026
    # --------------------------------------------------------

    match = re.search(
        r"\b(\d{1,2})\s+"
        r"([A-Za-z]{3,9})\s+"
        r"(\d{4})\b",
        value
    )

    if match:

        date_text = "{} {} {}".format(
            match.group(1),
            match.group(2),
            match.group(3)
        )

        for fmt in (
            "%d %b %Y",
            "%d %B %Y"
        ):

            try:

                dt = datetime.strptime(
                    date_text,
                    fmt
                )

                return dt.strftime(
                    "%d/%m/%Y"
                )

            except Exception:
                pass

    return ""


# ============================================================
# GET COMPLETE PDF TEXT
# ============================================================

def get_pdf_text(pdf_file):

    pages_text = []

    with pdfplumber.open(
        pdf_file
    ) as pdf:

        for page in pdf.pages:

            text = page.extract_text(
                x_tolerance=2,
                y_tolerance=3
            )

            if text:

                pages_text.append(
                    text
                )

    return "\n".join(
        pages_text
    )


# ============================================================
# GET FIRST PAGE TEXT
# ============================================================

def get_first_page_text(pdf_file):

    with pdfplumber.open(
        pdf_file
    ) as pdf:

        if not pdf.pages:

            return ""

        text = pdf.pages[0].extract_text(
            x_tolerance=2,
            y_tolerance=3
        )

        return text or ""


# ============================================================
# PO NUMBER
# ============================================================

def get_po_number(text):

    patterns = [

        r"\bPO\s*No\s*[:\-]?\s*"
        r"([A-Z0-9][A-Z0-9\-\/]*)",

        r"\bPO\s*Number\s*[:\-]?\s*"
        r"([A-Z0-9][A-Z0-9\-\/]*)",

        r"\bP\.O\.?\s*No\s*[:\-]?\s*"
        r"([A-Z0-9][A-Z0-9\-\/]*)",

        r"\bPO\s*#\s*[:\-]?\s*"
        r"([A-Z0-9][A-Z0-9\-\/]*)"
    ]

    for pattern in patterns:

        match = re.search(
            pattern,
            text,
            re.IGNORECASE
        )

        if match:

            return clean_text(
                match.group(1)
            )

    return ""


# ============================================================
# PO DATE
# ============================================================

def get_po_date(text):

    # --------------------------------------------------------
    # Line based
    # --------------------------------------------------------

    for line in text.splitlines():

        line = clean_text(
            line
        )

        if not line:

            continue

        # IMPORTANT:
        # Must start with PO Date.
        # This prevents PO Release Date matching.

        match = re.match(
            r"^PO\s+Date\s*:?\s*(.*)$",
            line,
            re.IGNORECASE
        )

        if match:

            value = clean_text(
                match.group(1)
            )

            result = format_date(
                value
            )

            if result:

                return result

    # --------------------------------------------------------
    # Global fallback
    # --------------------------------------------------------

    match = re.search(

        r"\bPO\s+Date\s*:?\s*"
        r"("
        r"\d{4}[-/]\d{1,2}[-/]\d{1,2}"
        r"|"
        r"\d{1,2}[-/]\d{1,2}[-/]\d{4}"
        r"|"
        r"[A-Za-z]{3,9}\s+\d{1,2},?\s+\d{4}"
        r"|"
        r"\d{1,2}\s+[A-Za-z]{3,9}\s+\d{4}"
        r")",

        text,

        re.IGNORECASE
    )

    if match:

        return format_date(
            match.group(1)
        )

    return ""


# ============================================================
# DELIVERY DATE
# ============================================================

def get_delivery_date(text):

    for line in text.splitlines():

        line = clean_text(
            line
        )

        if not line:

            continue

        match = re.match(
            r"^Expected\s+Delivery\s+Date\s*:?\s*(.*)$",
            line,
            re.IGNORECASE
        )

        if match:

            value = clean_text(
                match.group(1)
            )

            result = format_date(
                value
            )

            if result:

                return result

    # Fallback

    match = re.search(

        r"Expected\s+Delivery\s+Date\s*:?\s*"
        r"("
        r"\d{4}[-/]\d{1,2}[-/]\d{1,2}"
        r"|"
        r"\d{1,2}[-/]\d{1,2}[-/]\d{4}"
        r"|"
        r"[A-Za-z]{3,9}\s+\d{1,2},?\s+\d{4}"
        r"|"
        r"\d{1,2}\s+[A-Za-z]{3,9}\s+\d{4}"
        r")",

        text,

        re.IGNORECASE
    )

    if match:

        return format_date(
            match.group(1)
        )

    return ""


# ============================================================
# ADDRESS EXTRACTION - TEXT BASED
#
# IMPORTANT:
#
# Billing Address
# ...
# Tem Code -1
#
# Shipping Address
# ...
# Tem Code -1
#
# Address is captured ONLY until Tem Code -1.
# ============================================================

def get_addresses_from_first_page(pdf_file):

    text = get_first_page_text(
        pdf_file
    )

    if not text:

        return "", ""

    # --------------------------------------------------------
    # Normalize PDF text
    # --------------------------------------------------------

    text = text.replace(
        "\r",
        "\n"
    )

    # --------------------------------------------------------
    # Normalize spaces around Tem Code
    #
    # Handles:
    #
    # Tem Code -1
    # Tem Code: -1
    # Tem Code - 1
    # Tem Code
    # -1
    # --------------------------------------------------------

    # We intentionally keep newlines here.
    lines = []

    for line in text.splitlines():

        line = clean_text(
            line
        )

        if line:

            lines.append(
                line
            )

    # --------------------------------------------------------
    # Find Billing Address
    # --------------------------------------------------------

    billing_index = -1
    shipping_index = -1

    for i, line in enumerate(lines):

        if re.search(
            r"\bBilling\s+Address\b",
            line,
            re.IGNORECASE
        ):

            billing_index = i

            break

    for i, line in enumerate(lines):

        if re.search(
            r"\bShipping\s+Address\b",
            line,
            re.IGNORECASE
        ):

            shipping_index = i

            break

    # --------------------------------------------------------
    # DEBUG
    # --------------------------------------------------------

    print(
        "Billing Address line:",
        billing_index
    )

    print(
        "Shipping Address line:",
        shipping_index
    )

    # --------------------------------------------------------
    # Function to get one block
    # --------------------------------------------------------

    def extract_block(
        start_index,
        next_section_index
    ):

        if start_index < 0:

            return ""

        start = start_index + 1

        end = len(lines)

        if next_section_index > start_index:

            end = next_section_index

        block_lines = []

        i = start

        while i < end:

            line = lines[i]

            # ------------------------------------------------
            # STOP AT TEM CODE
            # ------------------------------------------------

            if re.search(
                r"\bTem\s*Code\b",
                line,
                re.IGNORECASE
            ):

                break

            # ------------------------------------------------
            # STOP AT PAN
            # ------------------------------------------------

            if re.match(
                r"^PAN\s*:?",
                line,
                re.IGNORECASE
            ):

                break

            # ------------------------------------------------
            # STOP AT CONTACT
            # ------------------------------------------------

            if re.match(
                r"^Contact\s*:?",
                line,
                re.IGNORECASE
            ):

                break

            # ------------------------------------------------
            # Stop at another address heading
            # ------------------------------------------------

            if re.search(
                r"\b(Billing|Shipping)\s+Address\b",
                line,
                re.IGNORECASE
            ):

                break

            block_lines.append(
                line
            )

            i += 1

        # ----------------------------------------------------
        # Remove unwanted lines
        # ----------------------------------------------------

        cleaned = []

        for line in block_lines:

            line = clean_text(
                line
            )

            if not line:

                continue

            if re.match(
                r"^(Billing|Shipping)\s+Address\b",
                line,
                re.IGNORECASE
            ):

                continue

            if re.match(
                r"^Tem\s*Code\b",
                line,
                re.IGNORECASE
            ):

                continue

            if re.match(
                r"^PAN\s*:?",
                line,
                re.IGNORECASE
            ):

                continue

            if re.match(
                r"^Contact\s*:?",
                line,
                re.IGNORECASE
            ):

                continue

            cleaned.append(
                line
            )

        return " ".join(
            cleaned
        )

    # --------------------------------------------------------
    # BILLING
    # --------------------------------------------------------

    bill_to_address = extract_block(
        billing_index,
        shipping_index
    )

    # --------------------------------------------------------
    # SHIPPING
    # --------------------------------------------------------

    ship_to_address = extract_block(
        shipping_index,
        -1
    )

    return (
        bill_to_address,
        ship_to_address
    )


# ============================================================
# HEADER TOTALS
#
# Handles:
#
# Total Amount (INR): 111952.52
# Total Amount (INR) 86786.28
#
# GST Compensation Cess: 0.00
# GST Compensation Cess 0.00
# ============================================================

def get_header_totals(text):

    result = {

        "po_total_amount":
            0,

        "po_gst_compensation_cess":
            0,

        "po_gst_additional_cess":
            0,

        "po_total_tax":
            0,

        "po_grand_total":
            0
    }

    # --------------------------------------------------------
    # Normalize whitespace
    # --------------------------------------------------------

    text = re.sub(
        r"\s+",
        " ",
        text
    )

    # --------------------------------------------------------
    # Total Amount
    # --------------------------------------------------------

    match = re.search(

        r"Total\s+Amount"
        r"\s*\(\s*INR\s*\)"
        r"\s*:?\s*"
        r"([\d,]+(?:\.\d+)?)",

        text,

        re.IGNORECASE
    )

    if match:

        result[
            "po_total_amount"
        ] = to_number(
            match.group(1)
        )

    # --------------------------------------------------------
    # GST Compensation Cess
    # --------------------------------------------------------

    match = re.search(

        r"GST\s+Compensation\s+Cess"
        r"\s*:?\s*"
        r"([\d,]+(?:\.\d+)?)",

        text,

        re.IGNORECASE
    )

    if match:

        result[
            "po_gst_compensation_cess"
        ] = to_number(
            match.group(1)
        )

    # --------------------------------------------------------
    # GST Additional Cess
    # --------------------------------------------------------

    match = re.search(

        r"GST\s+Additional\s+Cess"
        r"\s*:?\s*"
        r"([\d,]+(?:\.\d+)?)",

        text,

        re.IGNORECASE
    )

    if match:

        result[
            "po_gst_additional_cess"
        ] = to_number(
            match.group(1)
        )

    # --------------------------------------------------------
    # Total Tax
    # --------------------------------------------------------

    match = re.search(

        r"Total\s+Tax"
        r"\s*\(\s*INR\s*\)"
        r"\s*:?\s*"
        r"([\d,]+(?:\.\d+)?)",

        text,

        re.IGNORECASE
    )

    if match:

        result[
            "po_total_tax"
        ] = to_number(
            match.group(1)
        )

    # --------------------------------------------------------
    # Grand Total
    # --------------------------------------------------------

    match = re.search(

        r"Grand\s+Total"
        r"\s*\(\s*INR\s*\)"
        r"\s*:?\s*"
        r"([\d,]+(?:\.\d+)?)",

        text,

        re.IGNORECASE
    )

    if match:

        result[
            "po_grand_total"
        ] = to_number(
            match.group(1)
        )

    return result


# ============================================================
# ITEM NUMBER
# ============================================================

def normalize_item_no(value):

    if value is None:

        return ""

    value = str(value)

    value = re.sub(
        r"\s+",
        "",
        value
    )

    return value


def is_item_number(value):

    value = normalize_item_no(
        value
    )

    return bool(
        re.fullmatch(
            r"\d{1,3}",
            value
        )
    )


# ============================================================
# TABLE EXTRACTION
# ============================================================

def get_tables(pdf_file):

    all_rows = []

    with pdfplumber.open(
        pdf_file
    ) as pdf:

        print(
            "Total PDF pages:",
            len(pdf.pages)
        )

        for page_no, page in enumerate(
            pdf.pages,
            start=1
        ):

            print(
                "Reading table page:",
                page_no
            )

            tables = page.extract_tables({

                "vertical_strategy":
                    "lines",

                "horizontal_strategy":
                    "lines",

                "snap_tolerance":
                    3,

                "join_tolerance":
                    3,

                "intersection_tolerance":
                    3,

                "edge_min_length":
                    3
            })

            if not tables:

                continue

            for table in tables:

                for row in table:

                    if not row:

                        continue

                    row = [
                        clean_text(x)
                        for x in row
                    ]

                    if not any(row):

                        continue

                    joined = " ".join(
                        row
                    ).lower()

                    # Ignore headers
                    if (
                        "item code" in joined
                        or "item desc" in joined
                        or "taxable value" in joined
                        or "unit base cost" in joined
                        or "hsn code" in joined
                    ):

                        continue

                    # ------------------------------------------------
                    # ITEM
                    # ------------------------------------------------

                    if is_item_number(
                        row[0]
                    ):

                        all_rows.append(
                            row
                        )

                        continue

                    # ------------------------------------------------
                    # Continuation
                    # ------------------------------------------------

                    if (
                        all_rows
                        and not row[0]
                        and len(row) > 2
                    ):

                        description = clean_text(
                            row[2]
                        )

                        if description:

                            old = clean_text(
                                all_rows[-1][2]
                            )

                            all_rows[-1][2] = (
                                old
                                + " "
                                + description
                            )

    return all_rows


# ============================================================
# CREATE ITEM ARRAY
# ============================================================

def create_final_po_items(rows):

    final_po_items = {}

    for row in rows:

        if len(row) < 8:

            continue

        # ----------------------------------------------------
        # SL NO
        # ----------------------------------------------------

        sl_no = normalize_item_no(
            row[0]
        )

        if not is_item_number(
            sl_no
        ):

            continue

        # ----------------------------------------------------
        # SKU
        # ----------------------------------------------------

        sku_code = ""

        if len(row) > 1:

            sku_code = clean_text(
                row[1]
            ).upper()

        # ----------------------------------------------------
        # DESCRIPTION
        # ----------------------------------------------------

        description = ""

        if len(row) > 2:

            description = clean_text(
                row[2]
            ).upper()

        # ----------------------------------------------------
        # HSN
        # ----------------------------------------------------

        hsn = ""

        if len(row) > 3:

            hsn = clean_text(
                row[3]
            )

        # ----------------------------------------------------
        # QTY
        # ----------------------------------------------------

        qty = 0

        if len(row) > 4:

            qty = to_number(
                row[4]
            )

        # ----------------------------------------------------
        # MRP
        # ----------------------------------------------------

        mrp = 0

        if len(row) > 5:

            mrp = to_number(
                row[5]
            )

        # ----------------------------------------------------
        # RATE
        # ----------------------------------------------------

        rate = 0

        if len(row) > 6:

            rate = to_number(
                row[6]
            )

        # ----------------------------------------------------
        # TAXABLE VALUE
        # ----------------------------------------------------

        taxable_value = 0

        if len(row) > 7:

            taxable_value = to_number(
                row[7]
            )

        # ----------------------------------------------------
        # TAX RATE
        # ----------------------------------------------------

        cgst_rate = 0
        sgst_rate = 0
        igst_rate = 0

        if len(row) > 8:

            cgst_rate = to_number(
                row[8]
            )

        if len(row) > 10:

            sgst_rate = to_number(
                row[10]
            )

        if len(row) > 12:

            igst_rate = to_number(
                row[12]
            )

        if cgst_rate or sgst_rate:

            po_tax_rate = (
                cgst_rate
                + sgst_rate
            )

        else:

            po_tax_rate = igst_rate

        # ----------------------------------------------------
        # TOTAL
        # ----------------------------------------------------

        total = 0

        if row:

            total = to_number(
                row[-1]
            )

        # ----------------------------------------------------
        # ITEM
        # ----------------------------------------------------

        final_po_items[sl_no] = {

            "po_item_description":
                description,

            "po_item_sku_code":
                sku_code,

            "po_item_hsn_code":
                hsn,

            "qty":
                qty,

            "po_item_mrp":
                mrp,

            "rate":
                rate,

            "taxable_value":
                taxable_value,

            "po_tax_rate":
                po_tax_rate,

            "tot_amt":
                total,

            "po_item_barcode":
                ""
        }

    return final_po_items


# ============================================================
# ORDER QTY
# ============================================================

def get_order_qty(items):

    order_qty = 0

    for item in items.values():

        order_qty += to_number(
            item.get(
                "qty",
                0
            )
        )

    return order_qty


# ============================================================
# ORDER VALUE
# ============================================================

def get_order_value(items):

    total_po_value = 0

    for item in items.values():

        total_po_value += to_number(
            item.get(
                "tot_amt",
                0
            )
        )

    return round(
        total_po_value,
        2
    )


# ============================================================
# CGST / SGST
# ============================================================

def check_cgst_sgst(text):

    has_cgst = re.search(
        r"\bCGST\b",
        text,
        re.IGNORECASE
    )

    has_sgst = re.search(
        r"\bSGST\b",
        text,
        re.IGNORECASE
    )

    if has_cgst and has_sgst:

        return 1

    return 0


# ============================================================
# FINAL DATA
# ============================================================

def create_final_data(
    pdf_file,
    items
):

    text = get_pdf_text(
        pdf_file
    )

    # --------------------------------------------------------
    # HEADER
    # --------------------------------------------------------

    po_number = get_po_number(
        text
    )

    po_date = get_po_date(
        text
    )

    delivery_date = get_delivery_date(
        text
    )

    ship_date = delivery_date

    # --------------------------------------------------------
    # ADDRESSES
    # --------------------------------------------------------

    (
        bill_to_address,
        ship_to_address
    ) = get_addresses_from_first_page(
        pdf_file
    )

    # --------------------------------------------------------
    # TOTALS
    # --------------------------------------------------------

    header_totals = get_header_totals(
        text
    )

    # --------------------------------------------------------
    # ORDER QTY
    # --------------------------------------------------------

    order_qty = get_order_qty(
        items
    )

    # --------------------------------------------------------
    # ORDER VALUE
    # --------------------------------------------------------

    order_value = get_order_value(
        items
    )

    # --------------------------------------------------------
    # FINAL ARRAY
    # --------------------------------------------------------

    final_data = {

        "po_number":
            po_number,

        "po_date":
            po_date,

        "delivery_date":
            delivery_date,

        "ship_date":
            ship_date,

        "bill_to_address":
            bill_to_address,

        "ship_to_address":
            ship_to_address,

        "po_total_amount":
            header_totals[
                "po_total_amount"
            ],

        "po_gst_compensation_cess":
            header_totals[
                "po_gst_compensation_cess"
            ],

        "po_gst_additional_cess":
            header_totals[
                "po_gst_additional_cess"
            ],

        "po_total_tax":
            header_totals[
                "po_total_tax"
            ],

        "po_grand_total":
            header_totals[
                "po_grand_total"
            ],

        "order_value":
            order_value,

        "order_qty":
            order_qty,

        "items":
            items,

        "is_cgst_sgst_exist":
            check_cgst_sgst(
                text
            )
    }

    return final_data


# ============================================================
# SAVE
# ============================================================

def save_json(
    data,
    output_file
):

    with open(
        output_file,
        "w",
        encoding="utf-8"
    ) as f:

        json.dump(
            data,
            f,
            indent=4,
            ensure_ascii=False
        )


# ============================================================
# MAIN
# ============================================================

def main():

    if len(sys.argv) < 2:

        print(
            "Usage:"
        )

        print(
            "python extract_invoice.py a.pdf"
        )

        sys.exit(1)

    pdf_file = sys.argv[1]

    if not os.path.isfile(
        pdf_file
    ):

        print(
            "ERROR: PDF not found:",
            pdf_file
        )

        sys.exit(1)

    try:

        # ----------------------------------------------------
        # TABLE
        # ----------------------------------------------------

        rows = get_tables(
            pdf_file
        )

        print()
        print(
            "Rows found:",
            len(rows)
        )

        # ----------------------------------------------------
        # ITEMS
        # ----------------------------------------------------

        items = create_final_po_items(
            rows
        )

        print(
            "Items found:",
            len(items)
        )

        # ----------------------------------------------------
        # FINAL
        # ----------------------------------------------------

        final_data = create_final_data(
            pdf_file,
            items
        )

        # ----------------------------------------------------
        # OUTPUT
        # ----------------------------------------------------

        directory = os.path.dirname(
            os.path.abspath(
                pdf_file
            )
        )

        filename = os.path.splitext(
            os.path.basename(
                pdf_file
            )
        )[0]

        output_file = os.path.join(
            directory,
            filename + OUTPUT_SUFFIX
        )

        # ----------------------------------------------------
        # SAVE
        # ----------------------------------------------------

        save_json(
            final_data,
            output_file
        )

        # ----------------------------------------------------
        # DISPLAY
        # ----------------------------------------------------

        print()
        print("=" * 100)
        print("FINAL RESULT")
        print("=" * 100)

        print(
            "PO Number             :",
            final_data["po_number"]
        )

        print(
            "PO Date               :",
            final_data["po_date"]
        )

        print(
            "Delivery Date         :",
            final_data["delivery_date"]
        )

        print(
            "Ship Date             :",
            final_data["ship_date"]
        )

        print(
            "Bill To               :",
            final_data["bill_to_address"]
        )

        print(
            "Ship To               :",
            final_data["ship_to_address"]
        )

        print(
            "Total Amount          :",
            final_data["po_total_amount"]
        )

        print(
            "GST Compensation Cess :",
            final_data[
                "po_gst_compensation_cess"
            ]
        )

        print(
            "GST Additional Cess   :",
            final_data[
                "po_gst_additional_cess"
            ]
        )

        print(
            "Total Tax             :",
            final_data[
                "po_total_tax"
            ]
        )

        print(
            "Grand Total           :",
            final_data[
                "po_grand_total"
            ]
        )

        print(
            "Order Value           :",
            final_data[
                "order_value"
            ]
        )

        print(
            "Order Qty             :",
            final_data[
                "order_qty"
            ]
        )

        print(
            "CGST / SGST           :",
            final_data[
                "is_cgst_sgst_exist"
            ]
        )

        print(
            "Output File           :",
            output_file
        )

        print("=" * 100)

        # ----------------------------------------------------
        # ITEMS
        # ----------------------------------------------------

        print()
        print(
            "ITEM DETAILS"
        )

        print(
            "-" * 180
        )

        for sl_no, item in items.items():

            print(
                "{} | SKU={} | HSN={} | QTY={} | MRP={} | RATE={} | TAXABLE={} | TAX={} | TOTAL={}".format(

                    sl_no,

                    item[
                        "po_item_sku_code"
                    ],

                    item[
                        "po_item_hsn_code"
                    ],

                    item[
                        "qty"
                    ],

                    item[
                        "po_item_mrp"
                    ],

                    item[
                        "rate"
                    ],

                    item[
                        "taxable_value"
                    ],

                    item[
                        "po_tax_rate"
                    ],

                    item[
                        "tot_amt"
                    ]
                )
            )

    except Exception as e:

        print()
        print(
            "ERROR:",
            str(e)
        )

        sys.exit(1)


# ============================================================
# START
# ============================================================

if __name__ == "__main__":

    main()