feat(project): show presign counts as a statistics column

Group presign counts by project and team, include totals and unassigned rows, and reuse count drill-down. Remove the standalone footer while retaining presignTotal compatibility and the four formal business types.

Related to issue #13. Independent source review completed; no tests or builds run under user self-test arrangement.
This commit is contained in:
zlwu committed 2026-11-04 14:53:07 +08:00
1 parent ce8babcb7b
commit 79ca01ac0d
5 files changed
+66 -9

No files matched your search

+6 -5
View File
@@ -131,10 +131,11 @@ def records(db, tenant, user, biz_type, page=1, pageSize=20, **filters):
def statistics(db, tenant, user, groupBy='project', **filters):
if groupBy not in ('project', 'team'):
raise HTTPException(400, '分组方式不支持')
totals = dict.fromkeys(COUNT_TYPES, 0)
unassigned = dict.fromkeys(COUNT_TYPES, 0)
statistics_types = ('presign', *COUNT_TYPES)
totals = dict.fromkeys(statistics_types, 0)
unassigned = dict.fromkeys(statistics_types, 0)
counts = {}
for kind in COUNT_TYPES:
for kind in statistics_types:
model = BUSINESS[kind][0]
counts[kind] = filtered_query(db, tenant, user, kind, filters).with_entities(
model.project_id, func.count(model.id)).group_by(model.project_id).all()
@@ -159,10 +160,10 @@ def statistics(db, tenant, user, groupBy='project', **filters):
key = team_id if groupBy == 'team' else project_id
item = items.setdefault(key, dict(id=key,
name=team_name if groupBy == 'team' else (info.project_name if info else None),
teamName=team_name, **dict.fromkeys(COUNT_TYPES, 0)))
teamName=team_name, **dict.fromkeys(statistics_types, 0)))
item[kind] += count
return dict(items=list(items.values()), totals=totals, unassignedTotals=unassigned,
presignTotal=filtered_query(db, tenant, user, 'presign', filters).count())
presignTotal=totals['presign'])
def linked_records(db, tenant, user, kind, row_id, lock=False, current_read=False):
+48
View File
@@ -70,6 +70,54 @@ class ProjectManagementTest(unittest.TestCase):
self.assertEqual(self.db.get(BizPresignOrder, 's').project_id, 'p')
self.assertEqual(len(self.svc.assignment_history(self.db, 't', self.user, 'cell_storage', 'a')['list']), 1)
def test_presign_statistics_column_groups_totals_and_drill_down(self):
from datetime import datetime
from app.models.models import BizCollectionProtocol, BizPresignOrder
self.db.add_all([
self.Team(id='g2', tenant_no='t', team_name='Presign only', leader_user_id='u'),
self.Project(id='p2', tenant_no='t', project_no='P2', project_name='Presign only',
team_id='g2', leader_user_id='u', biz_types='cell_storage', status='active'),
BizCollectionProtocol(id='protocol', tenant_no='t', protocol_no='CP', project_id='p',
create_time=datetime(2026, 11, 4)),
])
for row_id, project_id, status, tenant, deleted, day in (
('active', 'p', 'active', 't', 0, 4),
('converted', 'p', 'converted', 't', 0, 4),
('draft', 'p2', 'draft', 't', 0, 4),
('void', None, 'void', 't', 0, 4),
('foreign', 'p', 'active', 'foreign', 0, 4),
('deleted', 'p', 'active', 't', 1, 4),
('old', 'p', 'active', 't', 0, 1),
):
self.db.add(BizPresignOrder(
id=row_id, tenant_no=tenant, protocol_no=row_id, project_id=project_id,
order_status=status, del_flag=deleted, create_by='u',
create_time=datetime(2026, 11, day),
converted_protocol_id='protocol' if status == 'converted' else None,
))
self.db.commit()
filters = dict(startDate='2026-11-04', endDate='2026-11-04')
for group_by, expected in (('project', {'p': 2, 'p2': 1}), ('team', {'g': 2, 'g2': 1})):
with self.subTest(group_by=group_by):
result = self.svc.statistics(self.db, 't', self.user, groupBy=group_by, **filters)
self.assertEqual(result['totals']['presign'], 4)
self.assertEqual(result['presignTotal'], 4)
self.assertEqual(result['unassignedTotals']['presign'], 1)
self.assertEqual(result['totals']['cell_storage'], 1)
self.assertEqual({r['id']: r['presign'] for r in result['items']}, expected)
for row in result['items']:
drill = {('teamId' if group_by == 'team' else 'projectId'): row['id']}
self.assertEqual(self.svc.records(self.db, 't', self.user, 'presign', **drill, **filters)['total'], row['presign'])
for selection in (dict(teamId='g'), dict(projectId='p')):
result = self.svc.statistics(self.db, 't', self.user, **selection, **filters)
self.assertEqual(result['totals']['presign'], 2)
self.assertEqual(result['unassignedTotals']['presign'], 0)
self.assertEqual(self.svc.records(self.db, 't', self.user, 'presign', unassigned=True, **filters)['total'], 1)
denied = self.svc.statistics(self.db, 't', dict(user_id='outsider', role='admin'), **filters)
self.assertEqual(denied['totals']['presign'], 0)
self.assertEqual(denied['items'], [])
self.assertNotIn('presign', self.svc.COUNT_TYPES)
def test_team_child_ids_survive_edits(self):
from app.models.models import SysUser
self.db.add(SysUser(id='u', tenant_no='t', user_no='u', nick_name='User'))