为基础资料维护页面增加导入导出

This commit is contained in:
2026-10-09 21:13:43 +08:00
parent a62803ccf0
commit a8af1f542b
20 changed files with 2134 additions and 15 deletions
@@ -11,6 +11,7 @@ MENU_PROMOTION_PATCH_PATH = SQL_ROOT / 'patches/20260727_promote_business_menus_
BUSINESS_MENU_ICON_PATCH_PATH = SQL_ROOT / 'patches/20260728_add_business_menu_icons_mysql.sql'
TEST_BATCH_MYSQL_PATCH_PATH = SQL_ROOT / 'patches/20260922_test_data_batch_mysql.sql'
SHIPMENT_RECEIPT_PATCH_PATH = SQL_ROOT / 'patches/20261008_shipment_receipt_mysql.sql'
MASTER_EXCEL_PATCH_PATH = SQL_ROOT / 'patches/20261009_master_excel_mysql.sql'
SYS_MENU_VALUE_COUNT = 20
SYS_MENU_PERMS_INDEX = 13
SYS_MENU_ICON_INDEX = 14
@@ -798,7 +799,27 @@ LEGACY_PATCH_BUSINESS_MENU_FIELDS = LEGACY_EXPECTED_BUSINESS_MENU_FIELDS | {
# 同样在菜单旧快照生成后加入收货权限,避免改变历史迁移的权限清单。
EXPECTED_BUTTONS[1119] = ('收货核对', 510, 6, 'business:shipment:receive')
EXPECTED_BUSINESS_MENU_FIELDS[1119] = (510, '收货核对', '', '', 6, 'business:shipment:receive', '')
EXPECTED_BUTTON_PERMISSIONS |= {'business:shipment:receive'}
MASTER_EXCEL_BUTTONS = {
1120: ('菌种导入', 502, 4, 'master:strain:import'),
1121: ('菌种导出', 502, 5, 'master:strain:export'),
1122: ('客户导入', 503, 4, 'master:customer:import'),
1123: ('客户导出', 503, 5, 'master:customer:export'),
1124: ('现场导入', 504, 6, 'master:site:import'),
1125: ('现场导出', 504, 7, 'master:site:export'),
1126: ('产线导入', 505, 4, 'master:line:import'),
1127: ('产线导出', 505, 5, 'master:line:export'),
1128: ('仓库导入', 506, 6, 'master:warehouse:import'),
1129: ('仓库导出', 506, 7, 'master:warehouse:export'),
1130: ('托盘导入', 507, 6, 'master:pallet:import'),
1131: ('托盘导出', 507, 7, 'master:pallet:export'),
1132: ('隧道导入', 520, 4, 'master:tunnel:import'),
1133: ('隧道导出', 520, 5, 'master:tunnel:export'),
}
for menu_id, spec in MASTER_EXCEL_BUTTONS.items():
EXPECTED_BUTTONS[menu_id] = spec
name, parent_id, order_num, permission = spec
EXPECTED_BUSINESS_MENU_FIELDS[menu_id] = (parent_id, name, '', '', order_num, permission, '')
EXPECTED_BUTTON_PERMISSIONS |= {'business:shipment:receive', *(spec[3] for spec in MASTER_EXCEL_BUTTONS.values())}
EXPECTED_PERMISSIONS = EXPECTED_PAGE_PERMISSIONS | EXPECTED_BUTTON_PERMISSIONS
EXPECTED_TOP_LEVEL_MENU_POSITIONS = {
121: ('基础资料维护', 0, 1),
@@ -842,6 +863,7 @@ BUSINESS_MENU_IDS = (
| {1103}
| set(range(1104, 1109))
| set(range(1109, 1119))
| set(range(1120, 1134))
)
TEST_BATCH_MENU_IDS = {519, 1109, 1110, 1111, 1112, 1113, 1114, 1115}
MYSQL_MENU_STABLE_COLUMNS = (
@@ -1508,6 +1530,45 @@ class ShipmentReceiptSqlContractTest(unittest.TestCase):
self.assertNotIn(1119, LEGACY_EXPECTED_BUSINESS_MENU_FIELDS)
class MasterExcelMenuPatchContractTest(unittest.TestCase):
def patch_sql(self) -> str:
self.assertTrue(MASTER_EXCEL_PATCH_PATH.is_file(), '缺少基础资料导入导出菜单迁移脚本')
return MASTER_EXCEL_PATCH_PATH.read_text(encoding='utf-8')
def test_patch_only_inserts_master_excel_menus_without_role_grants(self) -> None:
raw = self.patch_sql()
stripped = _strip_sql_comments(raw).lower()
inserts = re.findall(r'\binsert\s+into\s+(\w+)', stripped)
self.assertEqual(['sys_menu'], inserts)
self.assertNotRegex(stripped, r'\b(?:alter\s+table|delete\s+from|replace\s+into|truncate\s+table|update\s+\w+)\b')
self.assertNotRegex(stripped, r'\b(?:insert\s+into|update|delete\s+from)\s+sys_role_menu\b')
self.assertIn('不写 sys_role_menu', raw)
guard = stripped.split('start transaction', maxsplit=1)[0]
self.assertIn('menu_id between 1120 and 1133', guard)
self.assertIn('perms in (', guard)
self.assertIn('from sys_role_menu where menu_id between 1120 and 1133', guard)
self.assertIn("signal sqlstate '45000'", guard)
self.assertIn('resignal', guard)
self.assertLess(stripped.index("signal sqlstate '45000'"), stripped.index('start transaction'))
rows = _insert_rows(raw, 'sys_menu')
self.assertEqual(list(MASTER_EXCEL_BUTTONS), [int(row[0]) for row in rows])
for row in rows:
menu_id = int(row[0])
name, parent_id, order_num, permission = MASTER_EXCEL_BUTTONS[menu_id]
self.assertEqual(SYS_MENU_VALUE_COUNT, len(row), menu_id)
self.assertEqual([str(menu_id), name, str(parent_id), str(order_num)], row[:4])
self.assertEqual(permission, row[SYS_MENU_PERMS_INDEX])
self.assertEqual(
('', '', '', '', '1', '0', 'F', '0', '0'),
tuple(row[index] for index in (4, 5, 6, 7, 8, 9, 10, 11, 12)),
)
def test_patch_does_not_change_legacy_menu_snapshot(self) -> None:
self.assertTrue(MASTER_EXCEL_BUTTONS.keys().isdisjoint(LEGACY_EXPECTED_BUSINESS_MENU_FIELDS))
self.assertTrue({spec[3] for spec in MASTER_EXCEL_BUTTONS.values()}.isdisjoint(LEGACY_EXPECTED_PERMISSIONS))
class SqlContractTest(unittest.TestCase):
def test_test_batch_initializers_and_patches_match_contract(self) -> None:
scripts = (
@@ -1615,10 +1676,10 @@ class SqlContractTest(unittest.TestCase):
def test_initializers_define_fixed_business_menus_and_permissions(self) -> None:
self.assertEqual(19, len(EXPECTED_PAGE_PERMISSIONS))
self.assertEqual(47, len(EXPECTED_BUTTON_PERMISSIONS))
self.assertEqual(61, len(EXPECTED_BUTTON_PERMISSIONS))
self.assertFalse(EXPECTED_PAGE_PERMISSIONS & EXPECTED_BUTTON_PERMISSIONS)
self.assertEqual(66, len(EXPECTED_PERMISSIONS))
self.assertEqual(69, len(EXPECTED_BUSINESS_MENU_FIELDS))
self.assertEqual(80, len(EXPECTED_PERMISSIONS))
self.assertEqual(83, len(EXPECTED_BUSINESS_MENU_FIELDS))
for filename in INITIALIZERS:
with self.subTest(filename=filename):
sql = (SQL_ROOT / filename).read_text(encoding='utf-8')