This commit is contained in:
csj
2026-09-18 10:35:38 +08:00
parent 45ccfd69be
commit 967eaaec8a
13 changed files with 787 additions and 463 deletions
+58 -3
View File
@@ -12,7 +12,7 @@ from openpyxl import load_workbook
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
import test_expense_template as fixtures
from expense_preview import PreviewCalculator, UnsupportedFormula, builtin_rmb, prepare_preview
from expense_template import Template, encoded, set_cell, tag
from expense_template import Template, encoded, export_custom_expense, set_cell, tag
class ExpensePreviewTests(unittest.TestCase):
@@ -36,10 +36,48 @@ class ExpensePreviewTests(unittest.TestCase):
self.assertEqual(formulas.active['H29'].value, '=IF(F27>C27,F27-C27,0)')
self.assertEqual(result['previewUncalculatedCells'], [])
self.assertEqual(sheet['D29'].value, '伍佰肆拾肆元叁角整')
self.assertEqual(result['expensePreview'], 'xlsx-layout-v2')
self.assertEqual(result['expensePreview'], 'xlsx-layout-v4')
values.close()
formulas.close()
def test_original_preview_preserves_signature_borders_fonts_and_geometry(self):
signatures = ['部门长', '剧组出纳', '制片主任', '剧组会计', '执行制片人']
payee = dict(recipient='测试收款人', bankName='测试银行', accountNumber='001234567890')
source = self.fixture.root / 'signature-template.xlsx'
output = self.fixture.root / 'signature-preview.xlsx'
export_custom_expense(self.fixture.state, source, self.fixture.template, self.fixture.mapping,
{}, signatures, payee, {})
prepare_preview(source)
changed = dict(matches=[self.fixture.match('preview-changed', '交通', '447')])
export_custom_expense(changed, output, source, self.fixture.mapping,
{}, signatures, payee, {})
result = prepare_preview(output)
original, preview = Template(source), Template(output)
self.assertEqual(result['previewLayout']['version'], 4)
self.assertEqual(original.parts['xl/styles.xml'], preview.parts['xl/styles.xml'])
_, _, before = original.sheet('个人报销单')
_, _, after = preview.sheet('个人报销单')
for name in ['cols', 'mergeCells', 'sheetFormatPr', 'pageMargins', 'pageSetup']:
self.assertEqual(encoded(before.find(tag(name))), encoded(after.find(tag(name))), name)
for reference, label in zip(['A30', 'D30', 'A31', 'D31', 'A32'], signatures):
old = before.find('.//' + tag('c') + f'[@r="{reference}"]')
new = after.find('.//' + tag('c') + f'[@r="{reference}"]')
self.assertEqual(encoded(old), encoded(new), reference)
self.assertEqual(''.join(node.text or '' for node in new.iter(tag('t'))), label + '')
old_cells = {cell.get('r'): cell.get('s') for cell in before.iter(tag('c'))}
new_cells = {cell.get('r'): cell.get('s') for cell in after.iter(tag('c'))}
self.assertEqual(old_cells, new_cells)
for row in [30, 31, 32]:
old = before.find(tag('sheetData') + '/' + tag('row') + f'[@r="{row}"]')
new = after.find(tag('sheetData') + '/' + tag('row') + f'[@r="{row}"]')
self.assertEqual(old.attrib, new.attrib)
folder = os.environ.get('RECEIPT_PREVIEW_FIXTURE_DIR')
if folder:
folder = Path(folder)
folder.mkdir(parents=True, exist_ok=True)
(folder / 'template.xlsx').write_bytes(source.read_bytes())
(folder / 'preview.xlsx').write_bytes(output.read_bytes())
def test_builtin_uppercase_amount_formula(self):
for amount, expected in [('3491.86', '叁仟肆佰玖拾壹元捌角陆分'), ('10001.01', '壹万零壹元零壹分'),
('100000001', '壹亿零壹元整'), ('100010000', '壹亿零壹万元整'),
@@ -105,8 +143,9 @@ class ExpensePreviewTests(unittest.TestCase):
process = subprocess.run(command, input=json.dumps(request) + '\n', text=True, capture_output=True, timeout=60)
self.assertEqual(process.returncode, 0, process.stderr + process.stdout)
result = next(json.loads(line)['result'] for line in process.stdout.splitlines() if json.loads(line).get('event') == 'result')
self.assertEqual(result['expensePreview'], 'xlsx-layout-v2')
self.assertEqual(result['expensePreview'], 'xlsx-layout-v4')
self.assertTrue(result['previewLayout']['sheets'])
self.assertNotIn('templatePreviewLayout', result)
self.assertEqual(result['expenseRowCount'], 2)
workbook = load_workbook(output, data_only=True)
self.assertEqual(workbook.active['F27'].value, 544.3)
@@ -114,6 +153,22 @@ class ExpensePreviewTests(unittest.TestCase):
self.assertIn(workbook.active['C23'].value, ('', None))
workbook.close()
def test_engine_process_template_preview_is_read_only_without_expense_selection(self):
from spreadsheet_layout import read_layout
alternate, _ = self.fixture.alternate()
binary = os.environ.get('RECEIPT_ENGINE_BINARY')
command = [binary] if binary else [sys.executable, str(Path(__file__).resolve().parents[1] / 'engine.py')]
for source in [self.fixture.template, alternate]:
before = source.read_bytes()
request = dict(operation='preview-expense-template', templatePath=str(source))
process = subprocess.run(command, input=json.dumps(request) + '\n', text=True,
capture_output=True, timeout=60)
self.assertEqual(process.returncode, 0, process.stderr + process.stdout)
result = next(json.loads(line)['result'] for line in process.stdout.splitlines()
if json.loads(line).get('event') == 'result')
self.assertEqual(result, read_layout(source))
self.assertEqual(source.read_bytes(), before)
if __name__ == '__main__':
unittest.main()