python 为每个唯一文档使用相同的行数据创建PDF

ndh0cuux  于 2023-01-01  发布在  Python
关注(0)|答案(2)|浏览(79)

我想创建一个PDF格式的设备信息唯一的发票。

给定输入

使用代码中的print(result)从控制台输出复制:

ID: INV/2022/00003
Partner: [8, 'TEST Customer']
Payment term: [13, 'Siding']
Products: Check House
Siding equipment:
Category: Siding, Name: NEW TEST, Install date: 2022-12-22, Material: False, Brand: False, Style: False, Color: False, Shake siding: False

Window equipment:
Category: Window, Name: aw, Install date: 2022-12-23, Brand: Jeldwen, Window Material: Aluminum, Serial numbers: 2164

Door equipment:
Roof equipment:
Category: Roof, Name: NEW TEST, Install date: 2022-12-23, Roof Brand: False, Roof Style: False, Roof Color: False, Impact Resistant: False

ID: INV/2022/00002
Partner: [3, 'keith.mikel@e5sol.com']
Payment term: False
Products: NEW House
Siding equipment:
Category: Siding, Name: Sing, Install date: 2022-12-23, Material: Vinyl, Brand: False, Style: False, Color: False, Shake siding: False
Category: Siding, Name: as, Install date: 2022-12-23, Material: False, Brand: False, Style: False, Color: False, Shake siding: False
Category: Siding, Name: sa, Install date: 2022-12-23, Material: False, Brand: False, Style: False, Color: False, Shake siding: False

Window equipment:

Door equipment:
Roof equipment:
Category: Roof, Name: test, Install date: 2022-12-23, Roof Brand: GAF / Certainteed, Roof Style: Architectural, Roof Color: Moire Black, Impact Resistant: Moire Black

Process finished with exit code 0

代码

创建的PDF应提取与给定产品/发票相关的设备数据:

from reportlab.pdfgen import canvas

for ID, data in results.items():
    # Create a canvas object for the current invoice
    c = canvas.Canvas(f"{data['products']}.pdf")
    # Set the font and font size
    c.setFont("Helvetica", 16)
    c.drawString(10, 800, f"Housing Maintenance Report")
    # Write the ID to the PDF
    c.drawString(10, 780, f"Invoice ID: {ID}")
    # Write the partner information to the PDF
    c.drawString(10, 760, f"Customer: {data['partner']}")
    # Write the payment term to the PDF
    c.drawString(10, 740, f"Report Type: {data['payment_term']}")
    # Write the heading for the products section to the PDF
    c.drawString(10, 720, "House Address:")
    # Write the list of products to the PDF
    c.drawString(25, 700, ", ".join(data['products']))
    # Write the heading for the equipment section to the PDF
    c.drawString(10, 680, "Equipment:")
    # Set the starting line number
    line_number = 660

    # Write the siding equipment information to the PDF
    if siding_equipment:
        c.drawString(25, 660, "Siding equipment:")
        for equipment in siding_equipment:
            # Decrement the line number by 20 after each piece of equipment
            line_number -= 20
            c.drawString(50, line_number, f"Category: {equipment[0][1]}")
            line_number -= 20
            c.drawString(75, line_number, f"Name: {equipment[1]}")
            line_number -= 20
            c.drawString(75, line_number, f"Install date: {equipment[2]}")
            line_number -= 20
            c.drawString(75, line_number, f"Material: {equipment[3]}")
            line_number -= 20
            c.drawString(75, line_number, f"Brand: {equipment[4]}")
            line_number -= 20
            c.drawString(75, line_number, f"Style: {equipment[5]}")
            line_number -= 20
            c.drawString(75, line_number, f"Color: {equipment[6]}")
            line_number -= 20
            c.drawString(75, line_number, f"Shake siding: {equipment[7]}")
            line_number -= 20

    # Write the siding equipment information to the PDF
    if roof_equipment:
        c.drawString(25, line_number, "Roof equipment:")
        for equipment in roof_equipment:
            # Decrement the line number by 20 after each piece of equipment
            line_number -= 20
            c.drawString(50, line_number, f"Category: {equipment[0][1]}")
            line_number -= 20
            c.drawString(75, line_number, f"Name: {equipment[1]}")
            line_number -= 20
            c.drawString(75, line_number, f"Install date: {equipment[2]}")
            line_number -= 20
            c.drawString(75, line_number, f"Brand: {equipment[3]}")
            line_number -= 20
            c.drawString(75, line_number, f"Style: {equipment[4]}")
            line_number -= 20
            c.drawString(75, line_number, f"Color: {equipment[5]}")
            line_number -= 20
            c.drawString(75, line_number, f"Impact Resistant: {equipment[6]}")
            line_number -= 20

            c.save()

问题

现在,当创建PDF时,设备条目将在所有创建的PDF中复制。
示例:

  • 正在输入2个项目NEW houseTest House
  • 创建2份PDF文档。
  • 两个文档顶部的标题数据都是正确的
  • 但是,壁板设备和屋顶设备的设备数据在两个文档中重复

我不能让它创建pdf与设备是唯一的,为他们每个人。

问题

1.这和压痕有关吗?
1.需要改变什么?

获取未付发票的代码

Outstanding = models.execute_kw(db, uid, password, 'account.move', 'search_read', [[['payment_state', '=', 'paid'], ['x_studio_report_sent', '=', False]]],
                                      {'fields': ['name', 'partner_id', 'invoice_payment_term_id','invoice_line_ids']})

results = {}

for invoice in Outstanding:
    ID = invoice['name']
    partner = invoice['partner_id']
    payment_term = invoice['invoice_payment_term_id']
    invoice_lines = invoice['invoice_line_ids']
    line_items = models.execute_kw(db, uid, password, 'account.move.line', 'search', [[['id', 'in', invoice_lines]]])
    line_item_details = models.execute_kw(db, uid, password, 'account.move.line', 'read', [line_items], {'fields': ['product_id']})
    products = []
    for line_item in line_item_details:
        product_id = line_item['product_id']
        products.append(product_id[1])
    results[ID] = {'partner': partner, 'payment_term': payment_term, 'products': products}

分配给发票上使用的产品的设备的代码

for ID, data in results.items():
print(f'ID: {ID}')
print(f'Partner: {data["partner"]}')
print(f'Payment term: {data["payment_term"]}')
print(f'Products: {", ".join(data["products"])}')
models = xmlrpc.client.ServerProxy('{}/xmlrpc/2/object'.format(url))
models.execute_kw(db, uid, password, 'maintenance.equipment', 'check_access_rights', ['read'],
                  {'raise_exception': False})
product_names = data['products']

# Execute the search_read method to fetch the product templates
Maintenance = models.execute_kw(db, uid, password, 'maintenance.equipment', 'search_read',
                                [[['x_studio_many2one_field_0NvLy', '=', product_names]]],
                                {'fields': ['category_id', 'name', 'assign_date',
                                            'x_studio_siding_material', 'x_studio_siding_brand',
                                            'x_studio_siding_style', 'x_studio_siding_color',
                                            'x_studio_shake_siding',
                                            'x_studio_window_serial_numbers', 'x_studio_selection_field_ZfZO8',
                                            'x_studio_window_brand',
                                            'x_studio_door_brand', 'x_studio_door_code',
                                            'x_studio_roof_brand_1', 'x_studio_roof_style', 'x_studio_roof_color',
                                            'x_studio_impact_resistant']})

window_equipment = []
siding_equipment = []
door_equipment = []
roof_equipment = []

siding_equipment = [
    [equipment['category_id'], equipment['name'], equipment['assign_date'], equipment['x_studio_siding_material'],
     equipment['x_studio_siding_brand'], equipment['x_studio_siding_style'], equipment['x_studio_siding_color'],
     equipment['x_studio_shake_siding']] for equipment in Maintenance if equipment['category_id'] == [2, 'Siding']]

window_equipment = [
    [equipment['category_id'], equipment['name'], equipment['assign_date'], equipment['x_studio_window_brand'],
     equipment['x_studio_selection_field_ZfZO8'], equipment['x_studio_window_serial_numbers']] for equipment in
    Maintenance if equipment['category_id'] == [3, 'Window']]

door_equipment = [
    [equipment['category_id'], equipment['name'], equipment['assign_date'], equipment['x_studio_door_brand'],
     equipment['x_studio_door_code']] for equipment in Maintenance if equipment['category_id'] == [4, 'Door']]

roof_equipment = [
    [equipment['category_id'], equipment['name'], equipment['assign_date'], equipment['x_studio_roof_brand_1'],
     equipment['x_studio_roof_style'], equipment['x_studio_roof_color'], equipment['x_studio_impact_resistant']] for
    equipment in Maintenance if equipment['category_id'] == [1, 'Roof']]

# Siding equipment
print('Siding equipment:')
for entry in siding_equipment:
    print(
        f'Category: {entry[0][1]}, Name: {entry[1]}, Install date: {entry[2]}, Material: {entry[3]}, Brand: {entry[4]}, Style: {entry[5]}, Color: {entry[6]}, Shake siding: {entry[7]}')
print()

# Window equipment
print('Window equipment:')
for entry in window_equipment:
    print(
        f'Category: {entry[0][1]}, Name: {entry[1]}, Install date: {entry[2]}, Brand: {entry[3]}, Window Material: {entry[4]}, Serial numbers: {entry[5]}')
print()

# Door equipment
print('Door equipment:')
for entry in door_equipment:
    print(
        f'Category: {entry[0][1]}, Name: {entry[1]}, Install date: {entry[2]}, Door Brand: {entry[3]}, Door Code: {entry[3]}')
    print()

# Roof equipment
print('Roof equipment:')
for entry in roof_equipment:
    print(
        f'Category: {entry[0][1]}, Name: {entry[1]}, Install date: {entry[2]}, Roof Brand: {entry[3]}, Roof Style: {entry[4]}, Roof Color: {entry[5]}, Impact Resistant: {entry[5]}')
    print()
v1l68za4

v1l68za41#

通过重构提高可读性

很难看清到底发生了什么,因为行太多了,尤其是循环体很长。
我们可以通过重构代码来折叠或分组一些逻辑,为了减少循环体中的代码行,可以使用一种名为extract-methodextract-function的重构技术。
提取的函数:

  • 第一个月
  • write_siding_equipment(c, line_number, equipment)
  • write_roof_equipment(c, line_number, equipment)

这样代码就更容易阅读和维护了。

from reportlab.pdfgen import canvas

def write_header(c, id, data):
    c.drawString(10, 800, f"Housing Maintenance Report")
    c.drawString(10, 780, f"Invoice ID: {id}")  # Write the ID to the PDF
    c.drawString(10, 760, f"Customer: {data['partner']}")  # Write the partner information to the PDF
    c.drawString(10, 740, f"Report Type: {data['payment_term']}")  # Write the payment term to the PDF
    c.drawString(10, 720, "House Address:")  # Write the heading for the products section to the PDF
    c.drawString(25, 700, ", ".join(data['products']))  # Write the list of products to the PDF
    c.drawString(10, 680, "Equipment:")  # Write the heading for the equipment section to the PDF

def write_siding_equipment(c, line_number, equipment):
    line_number -= 20  # Decrement the line number by 20 after each piece of equipment
    c.drawString(50, line_number, f"Category: {equipment[0][1]}")
    line_number -= 20
    c.drawString(75, line_number, f"Name: {equipment[1]}")
    line_number -= 20
    c.drawString(75, line_number, f"Install date: {equipment[2]}")
    line_number -= 20
    c.drawString(75, line_number, f"Material: {equipment[3]}")
    line_number -= 20
    c.drawString(75, line_number, f"Brand: {equipment[4]}")
    line_number -= 20
    c.drawString(75, line_number, f"Style: {equipment[5]}")
    line_number -= 20
    c.drawString(75, line_number, f"Color: {equipment[6]}")
    line_number -= 20
    c.drawString(75, line_number, f"Shake siding: {equipment[7]}")
    line_number -= 20

def write_roof_equipment(c, line_number, equipment):
    line_number -= 20  # Decrement the line number by 20 after each piece of equipment
    c.drawString(50, line_number, f"Category: {equipment[0][1]}")
    line_number -= 20
    c.drawString(75, line_number, f"Name: {equipment[1]}")
    line_number -= 20
    c.drawString(75, line_number, f"Install date: {equipment[2]}")
    line_number -= 20
    c.drawString(75, line_number, f"Brand: {equipment[3]}")
    line_number -= 20
    c.drawString(75, line_number, f"Style: {equipment[4]}")
    line_number -= 20
    c.drawString(75, line_number, f"Color: {equipment[5]}")
    line_number -= 20
    c.drawString(75, line_number, f"Impact Resistant: {equipment[6]}")
    line_number -= 20

# main loop
for id, data in results.items():
    c = canvas.Canvas(f"{data['products']}.pdf")  # Create a canvas object for the current invoice
    c.setFont("Helvetica", 16)  # Set the font and font size
    write_header(c, id, data)  # <-- refactored: extract-method
    line_number = 660  # Set the starting line number

    # Write the siding equipment information to the PDF
    if siding_equipment:
        c.drawString(25, 660, "Siding equipment:")
        for equipment in siding_equipment:
            write_siding_equipment(c, line_number, equipment)  # <-- refactored: extract-method
    
    # Write the siding equipment information to the PDF
    if roof_equipment:
        c.drawString(25, line_number, "Roof equipment:")
        for equipment in roof_equipment:
            write_roof_equipment(c, line_number, equipment)  # <-- refactored: extract-method
            c.save()  # <-- WARNING: might save multiple PDFs

问题可见

现在您可以看到:

  • 2个if语句具有相同的注解
# Write the siding equipment information to the PDF
  • 两个设备项目(壁板和屋顶)都在同一画布c上背对背打印。line_number不会重置,但会增加。
  • roof_equipment的嵌套循环内调用用于保存PDF的最终c.save,这可能导致PDF重复
bfnvny8b

bfnvny8b2#

我应该为这份工作付多少钱呢,花3个小时在Python的bug修复和软件开发上--考虑到一个ERP顾问的日常费用很容易就超过1000美元。
差不多170行代码之后

数据准备

定义数据检索和准备的方法:

def map_equipment(equipment):
    # used by all types of equipment
       # equipment['category_id'], 
       # equipment['name'], 
       # equipment['assign_date'], 

    # remove key-values that are irrelevant for respective category
    # see: https://stackoverflow.com/questions/11277432/how-can-i-remove-a-key-from-a-python-dictionary
    if equipment['category_id'] != [1, 'Roof']]:
        equipment.pop('x_studio_roof_brand_1', None)
        equipment.pop('x_studio_roof_style', None)
        equipment.pop('x_studio_roof_color', None)
        equipment.pop('x_studio_impact_resistant', None)
    if equipment['category_id'] != [2, 'Siding']:
        equipment.pop('x_studio_siding_material', None)
        equipment.pop('x_studio_siding_brand', None) 
        equipment.pop('x_studio_siding_style', None)
        equipment.pop('x_studio_siding_color', None)
        equipment.pop('x_studio_shake_siding', None)
    if equipment['category_id'] != [3, 'Window']:
        equipment.pop('x_studio_window_brand', None)
        equipment.pop('x_studio_selection_field_ZfZO8', None)
        equipment.pop('x_studio_window_serial_numbers', None)
    if equipment['category_id'] != [4, 'Door']:
        equipment.pop('x_studio_door_brand', None)
        equipment.pop('x_studio_door_code', None)

    print(equipment)
    return equipment
    
    
fetch_equipment_items(product_id):
    models = xmlrpc.client.ServerProxy('{}/xmlrpc/2/object'.format(url))
    models.execute_kw(db, uid, password, 'maintenance.equipment', 'check_access_rights', ['read'], {'raise_exception': False})
    # ambiguous naming: 'product template' or 'maintenance.equipment' or product
    equipment_items = models.execute_kw(db, uid, password, 'maintenance.equipment', 'search_read',
                                [[['x_studio_many2one_field_0NvLy', '=', product_id]]],
                                {'fields': [
                                'category_id', 
                                'name', 
                                'assign_date',
                                'x_studio_siding_material',
                                'x_studio_siding_brand',
                                'x_studio_siding_style',
                                'x_studio_siding_color',
                                'x_studio_shake_siding',
                                'x_studio_window_serial_numbers', 
                                'x_studio_selection_field_ZfZO8',
                                'x_studio_window_brand',
                                'x_studio_door_brand', 
                                'x_studio_door_code',
                                'x_studio_roof_brand_1', 
                                'x_studio_roof_style', 
                                'x_studio_roof_color',
                                'x_studio_impact_resistant'
                                ]}
    )
    # equipment_items for each product_id
    return equipment_items
    
    
def fetch_products(line_ids):
    items   = models.execute_kw(db, uid, password, 'account.move.line', 'search', [[['id', 'in', line_ids]]])
    lines =  models.execute_kw(db, uid, password, 'account.move.line', 'read', [items], {'fields': ['product_id']})
    
    products = []
    for line in lines:
        product = line['product_id']
        equipment_items = fetch_equipment_items(line['product_id'])
        equipments = [map_equipment(ei) for ei in equipment_items] 
        products.append({'id': product, 'equipments': equipments})
    return products

def fetch_outstanding():
    return models.execute_kw(db, uid, password, 'account.move', 'search_read',
        [[['payment_state', '=', 'paid'], ['x_studio_report_sent', '=', False]]],
        {'fields': ['name', 'partner_id', 'invoice_payment_term_id', 'invoice_line_ids']}
    )

def prepare_invoices():
    invoices = {}
    for invoice in fetch_outstanding():
        ID =            invoice['name']
        partner =       invoice['partner_id']
        payment_term =  invoice['invoice_payment_term_id']
        products =      fetch_products(invoice['invoice_line_ids']) 
        invoices[ID] = {'id': ID, 'partner': partner, 'payment_term': payment_term, 'products': products}
    return invoices

准备发票和调试打印:

# prepare and debug-print invoices
invoices = prepare_invoices()
print(invoices.values())

报告创建

定义方法:

def next(y):
    return y - 20  # Decrement the line number by 20 after each piece
    
    
def write_header(c, y=800, invoice):
    c.drawString(x0, y, f"Housing Maintenance Report")
    c.drawString(x0, next(y), f"Invoice ID: {invoice['id']}")  # Write the ID to the PDF
    c.drawString(x0, next(y), f"Customer: {invoice['partner']}")  # Write the partner information to the PDF
    c.drawString(x0, next(y), f"Report Type: {invoice['payment_term']}")  # Write the payment term to the PDF
    c.drawString(x0, next(y), "House Address:")  # Write the heading for the products section to the PDF
    c.drawString(x1, next(y), ", ".join([p.id for p in invoice['products']]))  # Write the list of products to the PDF
    c.drawString(x0, next(y), "Equipment:")  # Write the heading for the equipment section to the PDF
    return next(y)

def map_title(equipment):
    # equipment['category_id'] is one of those:
    # [1, 'Roof']]
    # [2, 'Siding']
    # [3, 'Window']
    # [4, 'Door']
    return equipment['category_id'][1] + " equipment:"


def write_specific_fields(x, y, equipment):
    if equipment['category_id'] == [1, 'Roof']]:
        c.drawString(x, next(x), f"Brand: {equipment['x_studio_roof_brand_1']}")
        c.drawString(x, next(x), f"Style: {equipment['x_studio_roof_style']}")
        c.drawString(x, next(x), f"Color: {equipment['x_studio_roof_color']}")
        c.drawString(x, next(x), f"Impact Resistant: {roof[x_studio_impact_resistant]}")
    if equipment['category_id'] == [2, 'Siding']:
        c.drawString(x, next(y), f"Brand: {equipment['x_studio_siding_brand']}")
        c.drawString(x, next(y), f"Style: {equipment['x_studio_siding_style']}")
        c.drawString(x, next(y), f"Color: {equipment['x_studio_siding_color']}")
        c.drawString(x, next(y), f"Material: {equipment['x_studio_siding_material']}")
        c.drawString(x, next(y), f"Shake siding: {equipment['x_studio_shake_siding']}")
    if equipment['category_id'] == [3, 'Window']:
        c.drawString(x, next(y), f"Brand: {equipment['x_studio_window_brand']}")
        c.drawString(x, next(y), f"Field: {equipment['x_studio_selection_field_ZfZO8']}")
        c.drawString(x, next(y), f"Serials: {equipment['x_studio_window_serial_numbers']}")
    if equipment['category_id'] == [4, 'Door']:
        c.drawString(x, next(y), f"Brand: {equipment['x_studio_door_brand']}")
        c.drawString(x, next(y), f"Code: {equipment['x_studio_door_code']}")
    return next(y)

def write_equipment(c, y, equipment):
    c.drawString(x2, next(y), map_title(equipment))
    c.drawString(x2, next(y), f"Category: {equipment['category_id']}")
    c.drawString(x3, next(y), f"Name: {equipment['name']}")
    c.drawString(x3, next(y), f"Install date: {equipment['assign_date']}")
    return write_specific_fields(x3, next(y), equipment)

使用方法:

from reportlab.pdfgen import canvas

x0 = 10
x1 = 25
x2 = 25
x3 = 75

# report creation
for invoice in invoices.values():
    pdf_filename = f"{invoice['products']}.pdf"
    c = canvas.Canvas(pdf_filename)
    c.setFont("Helvetica", 16)
    y = 800  # Set the starting line number
    y = write_header(c, y, invoice)  # <-- refactored: extract-method

    # Write the equipment information to the PDF
    for e in invoice['equipments']:
        y = write_equipment(c, y, e)  # <-- refactored: extract-method

由于StackOverflow不是代码编写服务,因此我留给您一些东西:c.save().

相关问题