Files
playwrite/db/materials_to_be_deleted_dao.py
Misaka_Company 2096a51f55 feat: add database-driven material validation with multi-mode support
This commit enhances the material validation functionality to support
database-driven workflows alongside the existing Excel-based approach.

## New Features

### DAO Layer
- Add ProductionContractDataDAO for querying production contract data
- Add MaterialsToBeDeletedDAO with full CRUD operations
- Enhance DiscreteMaterialPlanDAO with query_all(), query_by_source_numbers(),
  and get_unique_material_names() methods

### Validation Modes
Support for 4 validation modes in MaterialValidationTab:
1. Database Full - Query all materials from DiscreteMaterialPlanData
2. Database Filtered - Query by ProductionID.txt file
3. Excel Existing - Validate from existing Excel file
4. Excel Full - Complete workflow with ERP extraction

### Configuration
- Add ValidationConfig dataclass with data_source, batch_size, match_mode,
  enable_crud_operations, and default_manager fields
- Update ConfigLoader to support validation configuration
- Add validation settings section in SettingsTab GUI

### Query Chain
Implementation of full query chain:
ProductionID.txt (总排号) → productionContractData (生产订单号) →
DiscreteMaterialPlanData (SourceNumber) → MaterialName →
MaterialsToBeDeleted comparison

## Backward Compatibility
All existing Excel-based validation methods remain unchanged, ensuring
no breaking changes for existing workflows.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-06 13:08:31 +08:00

346 lines
10 KiB
Python

"""
Data Access Object for MaterialsToBeDeleted table.
This module provides CRUD operations for the MaterialsToBeDeleted table,
which tracks materials that need to be deleted by their managers.
"""
from typing import List, Dict, Any, Tuple, Optional
from db.connection import get_connection
class MaterialsToBeDeletedDAO:
"""Data Access Object for MaterialsToBeDeleted table CRUD operations"""
def __init__(self):
self.db = None
def __enter__(self):
"""Enter context manager and establish database connection"""
self.db = get_connection()
self.db.connect()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""Exit context manager and close database connection"""
if self.db:
self.db.disconnect()
def close(self):
"""Close database connection"""
if self.db:
self.db.disconnect()
# ==================== CREATE ====================
def insert_material(
self, material_name: str, manager_name: str
) -> bool:
"""
Insert a single material record.
Args:
material_name: Material name
manager_name: Manager name
Returns:
True if successful, False otherwise
"""
sql = """
INSERT INTO [dbo].[MaterialsToBeDeleted] ([MaterialName], [ManagerName])
VALUES (?, ?)
"""
try:
with get_connection() as db:
db.execute_update(sql, (material_name, manager_name))
return True
except Exception as e:
print(f"Error inserting material: {e}")
return False
def insert_batch(self, materials: List[Tuple[str, str]]) -> int:
"""
Insert multiple material records in batch.
Args:
materials: List of tuples (material_name, manager_name)
Returns:
Number of records inserted
"""
if not materials:
return 0
sql = """
INSERT INTO [dbo].[MaterialsToBeDeleted] ([MaterialName], [ManagerName])
VALUES (?, ?)
"""
inserted_count = 0
try:
with get_connection() as db:
for material_name, manager_name in materials:
db.execute_update(sql, (material_name, manager_name))
inserted_count += 1
return inserted_count
except Exception as e:
print(f"Error inserting batch materials: {e}")
return inserted_count
# ==================== READ ====================
def get_all_materials(self) -> List[Dict[str, Any]]:
"""
Get all material records.
Returns:
List of all materials with MaterialName and ManagerName
"""
sql = """
SELECT [MaterialName], [ManagerName]
FROM [dbo].[MaterialsToBeDeleted]
WHERE [MaterialName] IS NOT NULL
ORDER BY [ManagerName], [MaterialName]
"""
with get_connection() as db:
return db.execute_query(sql)
def get_materials_by_manager(self, manager_name: str) -> List[Dict[str, Any]]:
"""
Get all materials for a specific manager.
Args:
manager_name: Manager name
Returns:
List of materials for the specified manager
"""
sql = """
SELECT [MaterialName], [ManagerName]
FROM [dbo].[MaterialsToBeDeleted]
WHERE [ManagerName] = ? AND [MaterialName] IS NOT NULL
ORDER BY [MaterialName]
"""
with get_connection() as db:
return db.execute_query(sql, (manager_name,))
def get_managers(self) -> List[str]:
"""
Get list of unique manager names.
Returns:
List of unique manager names
"""
sql = """
SELECT DISTINCT [ManagerName]
FROM [dbo].[MaterialsToBeDeleted]
WHERE [ManagerName] IS NOT NULL
ORDER BY [ManagerName]
"""
with get_connection() as db:
results = db.execute_query(sql)
return [r['ManagerName'] for r in results if r.get('ManagerName')]
def get_material_names_by_manager(self, manager_name: str) -> List[str]:
"""
Get material names for a specific manager.
Args:
manager_name: Manager name
Returns:
List of material names for the specified manager
"""
results = self.get_materials_by_manager(manager_name)
return [r['MaterialName'] for r in results if r.get('MaterialName')]
# ==================== UPDATE ====================
def update_manager(
self,
material_name: str,
old_manager: str,
new_manager: str
) -> bool:
"""
Update manager for a specific material.
Args:
material_name: Material name
old_manager: Current manager name
new_manager: New manager name
Returns:
True if successful, False otherwise
"""
sql = """
UPDATE [dbo].[MaterialsToBeDeleted]
SET [ManagerName] = ?
WHERE [MaterialName] = ? AND [ManagerName] = ?
"""
try:
with get_connection() as db:
affected = db.execute_update(sql, (new_manager, material_name, old_manager))
return affected > 0
except Exception as e:
print(f"Error updating manager: {e}")
return False
# ==================== DELETE ====================
def delete_material(
self,
material_name: str,
manager_name: str
) -> bool:
"""
Delete a specific material record.
Args:
material_name: Material name
manager_name: Manager name
Returns:
True if successful, False otherwise
"""
sql = """
DELETE FROM [dbo].[MaterialsToBeDeleted]
WHERE [MaterialName] = ? AND [ManagerName] = ?
"""
try:
with get_connection() as db:
affected = db.execute_update(sql, (material_name, manager_name))
return affected > 0
except Exception as e:
print(f"Error deleting material: {e}")
return False
def delete_by_manager(self, manager_name: str) -> int:
"""
Delete all materials for a specific manager.
Args:
manager_name: Manager name
Returns:
Number of records deleted
"""
sql = """
DELETE FROM [dbo].[MaterialsToBeDeleted]
WHERE [ManagerName] = ?
"""
try:
with get_connection() as db:
return db.execute_update(sql, (manager_name,))
except Exception as e:
print(f"Error deleting by manager: {e}")
return 0
def delete_all_materials(self) -> int:
"""
Delete all material records.
Returns:
Number of records deleted
"""
sql = "DELETE FROM [dbo].[MaterialsToBeDeleted]"
try:
with get_connection() as db:
return db.execute_update(sql)
except Exception as e:
print(f"Error deleting all materials: {e}")
return 0
# ==================== UTILITIES ====================
def material_exists(self, material_name: str) -> bool:
"""
Check if a material exists in the table.
Args:
material_name: Material name
Returns:
True if material exists, False otherwise
"""
sql = """
SELECT COUNT(*) as count
FROM [dbo].[MaterialsToBeDeleted]
WHERE [MaterialName] = ?
"""
with get_connection() as db:
result = db.execute_query(sql, (material_name,))
return result[0]['count'] > 0 if result else False
def count_by_manager(self, manager_name: str) -> int:
"""
Count materials for a specific manager.
Args:
manager_name: Manager name
Returns:
Number of materials for the manager
"""
sql = """
SELECT COUNT(*) as count
FROM [dbo].[MaterialsToBeDeleted]
WHERE [ManagerName] = ?
"""
with get_connection() as db:
result = db.execute_query(sql, (manager_name,))
return result[0]['count'] if result else 0
def get_statistics(self) -> Dict[str, Any]:
"""
Get comprehensive statistics about the data.
Returns:
Dictionary with statistics including total materials,
unique managers, and materials per manager
"""
sql = """
SELECT
COUNT(*) as total_materials,
COUNT(DISTINCT ManagerName) as unique_managers
FROM [dbo].[MaterialsToBeDeleted]
WHERE [MaterialName] IS NOT NULL
"""
with get_connection() as db:
result = db.execute_query(sql)
stats = result[0] if result else {}
# Get materials per manager
manager_sql = """
SELECT [ManagerName], COUNT(*) as count
FROM [dbo].[MaterialsToBeDeleted]
WHERE [ManagerName] IS NOT NULL
GROUP BY [ManagerName]
ORDER BY count DESC
"""
manager_results = db.execute_query(manager_sql)
stats['materials_per_manager'] = [
{r['ManagerName']: r['count']} for r in manager_results
]
return stats
def search_materials(self, keyword: str) -> List[Dict[str, Any]]:
"""
Search materials by keyword (partial match).
Args:
keyword: Keyword to search for
Returns:
List of matching materials
"""
sql = """
SELECT [MaterialName], [ManagerName]
FROM [dbo].[MaterialsToBeDeleted]
WHERE [MaterialName] LIKE ?
ORDER BY [ManagerName], [MaterialName]
"""
with get_connection() as db:
return db.execute_query(sql, (f'%{keyword}%',))