feat(project): show business fields in statistics details

Reuse assignment business columns and keyword searches in the four statistics drill-down lists. Add a statistics-permission read endpoint sharing tenant-scoped detail queries without granting assignment access.

Related to issue #13. Independent static review completed. User self-test arrangement: regression cases authored, no tests or builds executed.
This commit is contained in:
zlwu committed 2026-11-04 14:40:04 +08:00
1 parent f04e24186f
commit fa5efa9629
7 files changed
+106 -8

No files matched your search

@@ -59,6 +59,65 @@ class ProjectAssignmentRecordsTest(unittest.TestCase):
self.db.commit()
self.assertEqual([r['id'] for r in self.records()['list']], ['own'])
def test_statistics_detail_permission_is_separate_from_assignment(self):
self.user['role'] = 'admin'
self.grant('project:statistics:list')
self.db.add_all([
BizCollectionProtocol(id='own', tenant_no='t', protocol_no='OWN', create_by='u'),
BizCollectionProtocol(id='other', tenant_no='t', protocol_no='OTHER', create_by='other'),
BizCollectionProtocol(id='foreign', tenant_no='foreign', protocol_no='FOREIGN', create_by='u'),
BizCollectionProtocol(id='deleted', tenant_no='t', protocol_no='DELETED', create_by='u', del_flag=1),
BizProtocolPartyCustomer(id='p', tenant_no='t', collection_protocol_id='own', mother_name='Mother', mother_phone='13800000000'),
BizProtocolSample(id='s', tenant_no='t', collection_protocol_id='own', sample_code='SAMPLE'),
])
self.db.commit()
result = svc.statistics_records(self.db, 't', self.user, 'cell_storage', keyword='SAMPLE')
self.assertEqual(result['total'], 1)
self.assertEqual(result['list'][0]['motherName'], 'Mother')
self.assertEqual(result['list'][0]['motherPhone'], '13800000000')
self.assertEqual(result['list'][0]['sampleCodes'], 'SAMPLE')
self.assertEqual(svc.statistics_records(self.db, 't', self.user, 'cell_storage')['total'], 1)
with self.assertRaises(HTTPException) as denied:
self.records()
self.assertEqual(denied.exception.status_code, 403)
self.db.get(SysRole, 'project:statistics:list').status = '0'
self.db.commit()
self.grant('project:assignment:edit')
self.assertEqual(self.records()['total'], 1)
with self.assertRaises(HTTPException) as denied:
svc.statistics_records(self.db, 't', self.user, 'cell_storage')
self.assertEqual(denied.exception.status_code, 403)
def test_four_statistics_details_share_fields_and_match_summary_counts(self):
cases = (
('cell_storage', BizCollectionProtocol, dict(protocol_no='C'), 'sampleCodes'),
('research_donation', BizResearchDonation, dict(donation_no='D'), 'motherName'),
('pilot_service', BizPilotApplication, dict(application_no='P', channel_service_order_id='', biz_type='pilot_service', application_name='Pilot'), 'applicationName'),
('basic_experiment', BizPilotApplication, dict(application_no='B', channel_service_order_id='', biz_type='basic_experiment', project_title='Study'), 'projectTitle'),
)
for kind, model, fields, detail_key in cases:
self.db.add_all([
model(id=kind + '-assigned', tenant_no='t', project_id='project', create_time=datetime(2026, 11, 4), **fields),
model(id=kind + '-empty', tenant_no='t', create_time=datetime(2026, 11, 4), **fields),
model(id=kind + '-old', tenant_no='t', create_time=datetime(2026, 11, 1), **fields),
])
self.db.commit()
filters = dict(startDate='2026-11-04', endDate='2026-11-04')
summary = project_management.statistics(self.db, 't', self.user, **filters)
for kind, _, _, detail_key in cases:
with self.subTest(kind=kind):
details = svc.statistics_records(self.db, 't', self.user, kind, **filters)
self.assertEqual(details, self.records(kind, **filters))
self.assertEqual(details['total'], summary['totals'][kind])
self.assertEqual(details['total'], 2)
self.assertIn(detail_key, details['list'][0])
unassigned = svc.statistics_records(self.db, 't', self.user, kind, unassigned=True, **filters)
self.assertEqual(unassigned['total'], summary['unassignedTotals'][kind])
self.assertEqual(unassigned['list'][0]['id'], kind + '-empty')
assigned = svc.statistics_records(self.db, 't', self.user, kind, projectId='project', **filters)
self.assertEqual(assigned['total'], 1)
self.assertEqual(assigned['list'][0]['id'], kind + '-assigned')
def test_children_do_not_duplicate_counts_or_leak_foreign_deleted_data(self):
self.db.add_all([
BizCollectionProtocol(id='a', tenant_no='t', protocol_no='A', protocol_status='sxz'),