This commit is contained in:
csj
2026-09-16 16:06:21 +08:00
parent b2c7b28e22
commit 7fea2063aa
18 changed files with 815 additions and 36 deletions
+44 -17
View File
@@ -183,7 +183,37 @@ def export_travel(state, destination):
workbook.save(destination)
def export_expense(state, destination, template, purposes, signatures):
def expense_rows(matches, purposes, category_purposes=None):
grouped = {}
for match in matches:
category = match.get('category') or '其他'
row = grouped.setdefault(category, dict(category=category, count=0, amount=Decimal(0), types=[], purposes=[]))
row['count'] += len(match['invoices'])
purpose = purposes.get(match['id'], '').strip()
if purpose and purpose not in row['purposes']:
row['purposes'].append(purpose)
for invoice in match['invoices']:
content = text(invoice)
if '专用发票' in content:
invoice_type = '专票'
elif 'invoice' in content.lower():
invoice_type = 'Invoice'
elif '押金' in content and '收据' in content:
invoice_type = '押金收据'
else:
invoice_type = '普票'
if invoice_type not in row['types']:
row['types'].append(invoice_type)
row['amount'] += max((abs(Decimal(value.replace(',', ''))) for value in invoice['ocr']['amounts']), default=Decimal(0))
for row in grouped.values():
if category_purposes is not None:
row['purpose'] = category_purposes.get(row['category'], '').strip() or row['category']
else:
row['purpose'] = ''.join(row['purposes']) or row['category']
return list(grouped.values())
def export_expense(state, destination, template, purposes, signatures, payee=None, category_purposes=None):
from lxml import etree as ET
namespace = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'
relationships = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'
@@ -207,7 +237,10 @@ def export_expense(state, destination, template, purposes, signatures):
names.clear()
sheets.clear()
signatures = signatures or DEFAULT_SIGNATURES
page_count = math.ceil(len(state['matches']) / 13)
rows = expense_rows(state['matches'], purposes, category_purposes)
if not rows:
raise ValueError('请先勾选要导出的已核对材料')
page_count = math.ceil(len(rows) / 13)
def set_cell(document, reference, value):
cell = document.find('.//' + tag('c') + '[@r="' + reference + '"]')
@@ -229,25 +262,18 @@ def export_expense(state, destination, template, purposes, signatures):
for page in range(page_count):
document = ET.fromstring(sheet_bytes)
profile = payee or {}
for reference, field in [('C23', 'recipient'), ('C24', 'bankName'), ('C25', 'accountNumber'), ('H6', 'preparer')]:
value = str(profile.get(field, '') or '').strip()
if field == 'preparer' and not value:
value = str(profile.get('recipient', '') or '').strip()
set_cell(document, reference, value)
for row_number in range(9, 22):
for column in ['B', 'C', 'G', 'H', 'I']:
set_cell(document, column + str(row_number), '')
set_cell(document, 'A' + str(row_number), row_number - 8)
for index, match in enumerate(state['matches'][page * 13:(page + 1) * 13]):
invoice_type = '普票'
for invoice in match['invoices']:
content = text(invoice)
if '专用发票' in content:
invoice_type = '专票'
break
if 'invoice' in content.lower():
invoice_type = 'Invoice'
break
if '押金' in content and '收据' in content:
invoice_type = '押金收据'
break
total = sum(max((abs(Decimal(value.replace(',', ''))) for value in item['ocr']['amounts']), default=Decimal(0)) for item in match['invoices'])
for column, value in [('B', invoice_type), ('C', purposes.get(match['id'], '').strip()), ('G', len(match['invoices'])), ('H', total)]:
for index, row in enumerate(rows[page * 13:(page + 1) * 13]):
for column, value in [('B', '/'.join(row['types'])), ('C', row['purpose']), ('G', row['count']), ('H', row['amount'])]:
set_cell(document, column + str(index + 9), value)
for index, cell in enumerate(['A30', 'D30', 'A31', 'D31', 'A32', 'D32']):
value = signatures[index].strip() if index < len(signatures) else ''
@@ -277,3 +303,4 @@ def export_expense(state, destination, template, purposes, signatures):
with zipfile.ZipFile(destination, 'w', zipfile.ZIP_DEFLATED) as output:
for name, content in parts.items():
output.writestr(name, content)
return dict(expenseGrouping='category-v1', expenseRowCount=len(rows))