Python GCP Function BORB PDF - TypeError: object of type 'float' has no len()

60 views Asked by At

I have been trying to generate an invoice pdf in a gcp function that receives the following input:

end_client_invoices15jjltjkq38p {'oldValue': {}, 'updateMask': {}, 'value': {'createTime': '2023-04-10T13:05:38.934111Z', 'fields': {'buyerDetails': {'mapValue': {'fields': {'buyer_address_city_postal_code_country': {'stringValue': 'Town A AB500 Country'}, 'buyer_address_street': {'stringValue': 'Street 12 '}, 'buyer_email': {'stringValue': '[email protected]'}, 'buyer_id': {'stringValue': 'tqNPjTbvbQQxAUwjp6uVqnrybLh1'}, 'buyer_name': {'stringValue': 'William Surname'}, 'buyer_phone': {'stringValue': '+34 666 111 222'}}}}, 'invoice_number': {'integerValue': '4'}, 'product': {'mapValue': {'fields': {'VAT': {'doubleValue': 0.16}, 'amount': {'doubleValue': 2.0}, 'description': {'stringValue': 'description'}, 'price_per_unit_without_VAT': {'doubleValue': 3.5}, 'quantity': {'doubleValue': 1.0}}}}, 'sellerDetails': {'mapValue': {'fields': {'seller_address_city_postal_code_country': {'stringValue': 'Town AD123 Andorra'}, 'seller_address_street': {'stringValue': '30 Street'}, 'seller_email': {'stringValue': '[email protected]'}, 'seller_name': {'stringValue': 'Name'}, 'seller_phone': {'stringValue': '+376 125 142'}, 'store_uid': {'stringValue': 'Kz7gUu2kokgKdmwTXcSB'}}}}}, 'name': 'projects/project-prod/databases/(default)/documents/invoices_end_client/3CCievchkZHsIVDGQ5oR', 'updateTime': '2023-04-10T13:05:38.934111Z'}}
from borb.pdf.canvas.layout.page_layout.multi_column_layout import SingleColumnLayout
from borb.pdf.canvas.layout.page_layout.page_layout import PageLayout
from borb.pdf.canvas.layout.text.paragraph import Paragraph
from borb.pdf.document.document import Document
from borb.pdf.page.page import Page
from borb.pdf.pdf import PDF
from borb.pdf.canvas.layout.page_layout.multi_column_layout import SingleColumnLayout
from decimal import Decimal
from borb.pdf.canvas.layout.table.fixed_column_width_table import (
    FixedColumnWidthTable as Table,
)
from borb.pdf.canvas.layout.layout_element import Alignment
from borb.pdf.canvas.layout.image.image import Image
from borb.pdf.page.page_size import PageSize
from datetime import datetime
import random
from borb.pdf.pdf import PDF
from borb.pdf.canvas.color.color import HexColor, X11Color
from borb.pdf.canvas.layout.table.fixed_column_width_table import (
    FixedColumnWidthTable as Table,
)
from borb.pdf.canvas.layout.table.table import TableCell
from borb.pdf import Page
import json
from google.cloud import storage
from datetime import datetime
import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore
from pprint import pformat
import logging
import re
import pandas as pd

# Global Variables
export_time = datetime.now().strftime("%d_%m_%Y")
invoice_date_month = datetime.now().strftime("%m_%Y")

logging.basicConfig(format='%(message)s')

# Function
def generate_pdf_invoice(data, context):
    """ Triggered by a change to a Firestore document.
    Args:
        data (dict): The event payload.
        context (google.cloud.functions.Context): Metadata for the event.
    """

    logging.warning(list(data["value"]["fields"]["product"]["mapValue"]["fields"]["amount"].values())[0])
    logging.warning(data)
    # Build Table
    table_001 = Table(number_of_rows=4, number_of_columns=4)

    table_001.add(
        Paragraph(
            "Raó Social", font="Helvetica-Bold", horizontal_alignment=Alignment.LEFT
        )
    )
    table_001.add(
        Paragraph(
            "Company, SL", font="Helvetica-Bold"
        )
    )

    table_001.add(
        Paragraph("Data", font="Helvetica-Bold", horizontal_alignment=Alignment.RIGHT)
    )
    now = datetime.now()
    table_001.add(Paragraph("%d/%d/%d" % (now.day, now.month, now.year)))

    table_001.add(
        Paragraph("Carrer", font="Helvetica-Bold", horizontal_alignment=Alignment.LEFT)
    )
    table_001.add(
        Paragraph("Street A,Bloc X")
    )
    table_001.add(
        Paragraph(
            "Factura #", font="Helvetica-Bold", horizontal_alignment=Alignment.RIGHT,
        )
    )
    table_001.add(
        Paragraph(str(data["value"]["fields"]["invoice_number"]["integerValue"]))
    )

    table_001.add(
        Paragraph(
            "Població", font="Helvetica-Bold", horizontal_alignment=Alignment.LEFT
        )
    )

    table_001.add(
        Paragraph("Town")
    )

    table_001.add(
        Paragraph(
            "Venciment", font="Helvetica-Bold", horizontal_alignment=Alignment.LEFT
        )
    )
    table_001.add(Paragraph("%d/%d/%d" % (now.day, now.month, now.year)))

    table_001.add(
        Paragraph("Email", font="Helvetica-Bold", horizontal_alignment=Alignment.RIGHT)
    )
    table_001.add(
        Paragraph(
            "[email protected]"
        )
    )

    table_001.set_padding_on_all_cells(Decimal(2), Decimal(2), Decimal(2), Decimal(2))
    table_001.no_borders()

    #Billing and shipping information
    table_002 = Table(number_of_columns=1, number_of_rows=6)
    table_002.add(
        Paragraph(
            "Facturat a",
            background_color=HexColor("263238"),
            font_color=X11Color("White"),
        )
    )
    # BILLING
    table_002.add(
        Paragraph(remove_emoji(data["value"]["fields"]["buyerDetails"]["mapValue"]["fields"]["buyer_name"]["stringValue"]))
    )  # BILLING
    table_002.add(
        Paragraph(data["value"]["fields"]["buyerDetails"]["mapValue"]["fields"]["buyer_address_street"]["stringValue"])
    )  # BILLING
    table_002.add(
        Paragraph(
            data["value"]["fields"]["buyerDetails"]["mapValue"]["fields"]["buyer_address_city_postal_code_country"]["stringValue"]
        )
    )  # BILLING
    table_002.add(
        Paragraph(data["value"]["fields"]["buyerDetails"]["mapValue"]["fields"]["buyer_phone"]["stringValue"])
    )  # BILLING
    table_002.add(
        Paragraph(data["value"]["fields"]["buyerDetails"]["mapValue"]["fields"]["buyer_email"]["stringValue"])
    )  # BILLING
    table_002.set_padding_on_all_cells(Decimal(2), Decimal(2), Decimal(2), Decimal(2))
    table_002.no_borders()

    # Build_itemized_description_table
    table_003 = Table(number_of_rows=7, number_of_columns=4)
    for h in ["Descripció", "Quantitat", "Preu per unitat", "Import"]:
        table_003.add(
            TableCell(
                Paragraph(h, font_color=X11Color("White")),
                background_color=HexColor("000000"),
            )
        )

    odd_color = HexColor("BBBBBB")
    even_color = HexColor("FFFFFF")
    for row_number, item in enumerate(
        [
            (
                data["value"]["fields"]["product"]["mapValue"]["fields"]["description"]["stringValue"],
                data["value"]["fields"]["product"]["mapValue"]["fields"]["quantity"]["doubleValue"],
                list(data["value"]["fields"]["product"]["mapValue"]["fields"]["price_per_unit_without_VAT"].values())[0],
                list(data["value"]["fields"]["product"]["mapValue"]["fields"]["price_per_unit_without_VAT"].values())[0]*data["value"]["fields"]["product"]["mapValue"]["fields"]["quantity"]["doubleValue"]
            ),
        ]
    ):
        c = even_color if row_number % 2 == 0 else odd_color
        table_003.add(TableCell(Paragraph(item[0]), background_color=c))
        table_003.add(TableCell(Paragraph(str(item[1])), background_color=c))
        table_003.add(TableCell(Paragraph("$ " + str(item[2])), background_color=c))
        table_003.add(TableCell(Paragraph("$ " + str(item[3])), background_color=c))

    # Optionally add some empty rows to have a fixed number of rows for styling purposes
    for row_number in range(3, 5):
        c = even_color if row_number % 2 == 0 else odd_color
        for _ in range(0, 4):
            table_003.add(TableCell(Paragraph(" "), background_color=c))

    table_003.add(
        TableCell(
            Paragraph(
                "Subtotal", font="Helvetica-Bold", horizontal_alignment=Alignment.RIGHT,
            ),
            col_span=3,
        )
    )
    table_003.add(
        TableCell(
            Paragraph(
                float(data["value"]["fields"]["product"]["mapValue"]["fields"]["price_per_unit_without_VAT"]["doubleValue"])*float(data["value"]["fields"]["product"]["mapValue"]["fields"]["quantity"]["doubleValue"])
            )
        )
    )
    table_003.add(
        TableCell(
            Paragraph(
                "IGI", font="Helvetica-Bold", horizontal_alignment=Alignment.RIGHT
            ),
            col_span=3,
        )
    )
    table_003.add(
        TableCell(
            Paragraph(
                "{0:.2f}".format(
                    float(data["value"]["fields"]["product"]["mapValue"]["fields"]["price_per_unit_without_VAT"]["doubleValue"]) * float(data["value"]["fields"]["product"]["mapValue"]["fields"]["quantity"]["doubleValue"]) * 4.5 / 100
                )
            )
        )
    )
    table_003.add(
        TableCell(
            Paragraph(
                "Total", font="Helvetica-Bold", horizontal_alignment=Alignment.RIGHT
            ),
            col_span=3,
        )
    )
    table_003.add(
        TableCell(
            Paragraph(
                "{0:.2f}".format(
                    float(data["value"]["fields"]["product"]["mapValue"]["fields"]["price_per_unit_without_VAT"]["doubleValue"])*float(data["value"]["fields"]["product"]["mapValue"]["fields"]["quantity"]["doubleValue"]) * (1 + 4.5 / 100)
                )            
            )
        )
    )
    table_003.set_padding_on_all_cells(Decimal(2), Decimal(2), Decimal(2), Decimal(2))
    table_003.no_borders()


    # Create document
    pdf = Document()
    # Add page
    page = Page()

    pdf.add_page(page)

    page_layout = SingleColumnLayout(page)
    page_layout.vertical_margin = page.get_page_info().get_height() * Decimal(0.02)

    page_layout.add(
        Image(
            "https://firebasestorage.googleapis.com/v0/b/mes-k-bo-dev.appspot.com/o/stores%2F2322e310-1743-11ed-87c2-9f7ebec4c7dc?alt=media&token=71ebb1be-a9ae-4f49-b3e1-b46619cdfb4a",
            width=Decimal(128),
            height=Decimal(128),
        )
    )


    # Invoice information table
    page_layout.add(table_001)
    # Empty paragraph for spacing
    page_layout.add(Paragraph(" "))
    # Billing and shipping information table
    page_layout.add(table_002)
    # Empty paragraph for spacing
    page_layout.add(Paragraph(" "))
    # Itemized description
    page_layout.add(table_003)

And I am getting this error which I can't figure out how to solve:

  File "/workspace/main.py", line 260, in generate_pdf_invoice 
    page_layout.add(table_003)
  File "/layers/google.python.pip/pip/lib/python3.9/site-packages/borb/pdf/canvas/layout/page_layout/multi_column_layout.py", line 201, in add
    layout_rect = layout_element.layout(
  File "/layers/google.python.pip/pip/lib/python3.9/site-packages/borb/pdf/canvas/layout/layout_element.py", line 307, in layout 
    return self.calculate_layout_box_and_do_layout(page, bounding_box)
  File "/layers/google.python.pip/pip/lib/python3.9/site-packages/borb/pdf/canvas/layout/layout_element.py", line 320, in calculate_layout_box_and_do_layout
    layout_box = self._calculate_layout_box(page, bounding_box)
  File "/layers/google.python.pip/pip/lib/python3.9/site-packages/borb/pdf/canvas/layout/layout_element.py", line 230, in _calculate_layout_box
    returned_layout_box = self._calculate_layout_box_without_padding(
  File "/layers/google.python.pip/pip/lib/python3.9/site-packages/borb/pdf/canvas/layout/layout_element.py", line 258, in _calculate_layout_box_without_padding
    layout_rect = self._do_layout_without_padding(page, bounding_box)
  File "/layers/google.python.pip/pip/lib/python3.9/site-packages/borb/pdf/canvas/layout/table/fixed_column_width_table.py", line 131, in _do_layout_without_padding
    t.layout(page, Rectangle(x, y, w, h))
  File "/layers/google.python.pip/pip/lib/python3.9/site-packages/borb/pdf/canvas/layout/table/table.py", line 151, in layout
    returned_layout_box: Rectangle = self._layout_element.layout(
  File "/layers/google.python.pip/pip/lib/python3.9/site-packages/borb/pdf/canvas/layout/layout_element.py", line 307, in layout
    return self.calculate_layout_box_and_do_layout(page, bounding_box)
  File "/layers/google.python.pip/pip/lib/python3.9/site-packages/borb/pdf/canvas/layout/layout_element.py", line 320, in calculate_layout_box_and_do_layout
    layout_box = self._calculate_layout_box(page, bounding_box)
  File "/layers/google.python.pip/pip/lib/python3.9/site-packages/borb/pdf/canvas/layout/layout_element.py", line 230, in _calculate_layout_box
    returned_layout_box = self._calculate_layout_box_without_padding(
  File "/layers/google.python.pip/pip/lib/python3.9/site-packages/borb/pdf/canvas/layout/layout_element.py", line 258, in _calculate_layout_box_without_padding
    layout_rect = self._do_layout_without_padding(page, bounding_box)
  File "/layers/google.python.pip/pip/lib/python3.9/site-packages/borb/pdf/canvas/layout/text/paragraph.py", line 230, in _do_layout_without_padding
    if len(self._text) == 0:
TypeError: object of type 'float' has no len()

Could anyone help me? Thousand thanks!

Several variations of the code but could not get it to work, mainly trying more configuration of the paragraph without success

1

There are 1 answers

0
Joris Schellekens On

disclaimer: I am the author of borb

One of your Paragraph objects is being constructed with a float rather than a str. At some point, the Paragraph attempts to perform layout, for which it needs to determine its width.

And thus it calls len on what it expects to be str, but is in fact float.

This is where it goes wrong:

    table_003.add(
        TableCell(
            Paragraph(
                float(data["value"]["fields"]["product"]["mapValue"]["fields"]["price_per_unit_without_VAT"]["doubleValue"])*float(data["value"]["fields"]["product"]["mapValue"]["fields"]["quantity"]["doubleValue"])
            )
        )
    )

You are explicitly constructing Paragraph with a float there.