import copy import tempfile import unittest import zipfile from pathlib import Path from test_domain import material, workspace from domain import auto_match, enrich from exports import export_expense, export_ppt, export_travel from PIL import Image from openpyxl import load_workbook from pptx import Presentation class ExportTests(unittest.TestCase): def setUp(self): self.temporary = tempfile.TemporaryDirectory() self.root = Path(self.temporary.name) image = self.root / 'receipt.png' Image.new('RGB', (400, 200), 'white').save(image) self.state = enrich(auto_match(workspace([material('i', 'invoice', '100.00')], [material('p', 'payment', '100.00')]))) for item in self.state['invoices'] + self.state['payments']: item['previewPath'] = str(image) self.template = Path(__file__).parents[1] / 'personal-expense-template.xlsx' def tearDown(self): self.temporary.cleanup() def test_simple_ppt_with_photo_placeholder(self): destination = self.root / 'test.pptx' export_ppt(self.state, destination, True) deck = Presentation(destination) self.assertEqual(len(deck.slides), 3) self.assertEqual(deck.slide_width, 1280 * 12700) self.assertTrue(any('实物照片' in shape.text for shape in deck.slides[2].shapes if shape.has_text_frame)) def test_travel_category_excludes_photo(self): self.state['matches'][0]['category'] = '交通' destination = self.root / 'test.pptx' export_ppt(self.state, destination, False) self.assertEqual(len(Presentation(destination).slides), 2) def test_complex_payment_pagination(self): self.state['matches'][0]['payments'] *= 5 destination = self.root / 'test.pptx' export_ppt(self.state, destination, True) self.assertEqual(len(Presentation(destination).slides), 5) def test_expense_template_values_and_formulas(self): destination = self.root / 'expense.xlsx' match = self.state['matches'][0] export_expense(self.state, destination, self.template, {match['id']: '=不是公式'}, ['经办人']) workbook = load_workbook(destination) self.assertEqual(workbook.sheetnames, ['个人报销单']) sheet = workbook.active self.assertEqual(sheet['C9'].value, '=不是公式') self.assertEqual(sheet['C9'].data_type, 's') self.assertEqual(sheet['H9'].value, 100) self.assertEqual(sheet['G9'].value, 1) self.assertEqual(sheet['A30'].value, '经办人:') self.assertEqual(sheet['D30'].value, '') original = load_workbook(self.template)['1个人报销单'] formulas = {cell.coordinate: cell.value for row in original for cell in row if cell.data_type == 'f'} self.assertTrue(formulas) for coordinate, value in formulas.items(): self.assertEqual(sheet[coordinate].value, value) self.assertEqual(str(sheet.print_area).split('!')[-1], str(original.print_area).split('!')[-1]) def test_expense_pagination(self): self.state['matches'] = [copy.deepcopy(self.state['matches'][0]) for _ in range(14)] destination = self.root / 'expense.xlsx' export_expense(self.state, destination, self.template, {}, []) workbook = load_workbook(destination) self.assertEqual(workbook.sheetnames, ['个人报销单-1', '个人报销单-2']) self.assertEqual(workbook.worksheets[1]['H9'].value, 100) self.assertEqual(workbook.worksheets[1]['H10'].value, '') self.assertEqual(workbook.worksheets[1]['A30'].value, '部门长:') def test_travel_only_verified_invoices(self): travel = self.state['invoices'][0]['ocr']['travel'] travel.update(type='train', travelerName='张三', departure='北京南', destination='上海虹桥', departureTime='2026-06-24 15:00', transportNumber='G21') destination = self.root / 'travel.xlsx' export_travel(self.state, destination) sheet = load_workbook(destination).active self.assertEqual(sheet.max_row, 2) self.assertEqual(sheet['C2'].value, '张三') self.assertEqual(sheet['G2'].value, 'G21') def test_no_travel_raises(self): with self.assertRaises(ValueError): export_travel(self.state, self.root / 'travel.xlsx') if __name__ == '__main__': unittest.main()