mirror of
https://devops.liangqichi.top/qichi.liang/Orbitin.git
synced 2026-02-10 07:41:29 +08:00
fix: 修复月份选择器问题,确保12月正确显示
- 修复跨年月份计算逻辑(1月时正确计算为去年12月) - 改进_get_month_list()方法,生成正确的近12个月列表 - 增加Combobox宽度以完整显示月份值如'2025-12' - 优化手动剔除次月多统计的船对话框
This commit is contained in:
30
README.md
30
README.md
@@ -75,6 +75,9 @@ python3 main.py --unaccounted 118 --month 2025-12
|
|||||||
# 去除未统计数据
|
# 去除未统计数据
|
||||||
python3 main.py --remove-unaccounted --month 2025-12
|
python3 main.py --remove-unaccounted --month 2025-12
|
||||||
|
|
||||||
|
# 手动剔除次月多统计的船
|
||||||
|
python3 main.py --cross-exclude --source-date 2025-12-31 --target-date 2026-01-01 --ship-name "学友洋山" --teu 100
|
||||||
|
|
||||||
# 配置测试(验证所有连接)
|
# 配置测试(验证所有连接)
|
||||||
python3 main.py config-test
|
python3 main.py config-test
|
||||||
```
|
```
|
||||||
@@ -98,6 +101,7 @@ python3 src/gui.py
|
|||||||
- **去除多余统计数据**:用于删除多余统计的箱量(对称功能)
|
- **去除多余统计数据**:用于删除多余统计的箱量(对称功能)
|
||||||
- **月底智能调整**:月底最后一天自动弹出剔除对话框
|
- **月底智能调整**:月底最后一天自动弹出剔除对话框
|
||||||
- **数据自动转移**:月底剔除的数据自动转移到次月1号
|
- **数据自动转移**:月底剔除的数据自动转移到次月1号
|
||||||
|
- **手动剔除次月多统计的船**:用于处理上月底余留数据未及时剔除的情况(例如:2号打开工具整理1号数据,但上月底余留数据没有剔除)
|
||||||
|
|
||||||
### 配置管理
|
### 配置管理
|
||||||
- **管理月份页面ID映射**:配置各月份的Confluence页面ID
|
- **管理月份页面ID映射**:配置各月份的Confluence页面ID
|
||||||
@@ -128,6 +132,32 @@ python3 src/gui.py
|
|||||||
- 默认不弹出调整对话框
|
- 默认不弹出调整对话框
|
||||||
- 但GUI侧边栏保留了手动添加/剔除TEU的功能入口
|
- 但GUI侧边栏保留了手动添加/剔除TEU的功能入口
|
||||||
|
|
||||||
|
### 手动剔除次月多统计的船
|
||||||
|
用于处理上月底余留数据未及时剔除的情况:
|
||||||
|
|
||||||
|
**使用场景**:
|
||||||
|
- 用户在2号打开工具,整理的是1号的数据
|
||||||
|
- 上月底余留的数据没有剔除,导致没有算在1号的日报中
|
||||||
|
- 需要手动从次月(当前月)中剔除上月底余留的数据
|
||||||
|
|
||||||
|
**功能特点**:
|
||||||
|
- **GUI操作**:在左侧控制面板点击"剔除次月多统计"按钮
|
||||||
|
- **CLI操作**:使用 `--cross-exclude` 参数
|
||||||
|
- **灵活配置**:支持指定源日期(上月底)、目标日期(次月)、船名、TEU、20尺/40尺箱量
|
||||||
|
- **数据记录**:调整记录存储在数据库中,便于追踪和审计
|
||||||
|
|
||||||
|
**使用示例**:
|
||||||
|
```bash
|
||||||
|
# CLI方式
|
||||||
|
python3 main.py --cross-exclude --source-date 2025-12-31 --target-date 2026-01-01 --ship-name "学友洋山" --teu 100
|
||||||
|
|
||||||
|
# GUI方式
|
||||||
|
1. 打开GUI界面
|
||||||
|
2. 在左侧控制面板点击"剔除次月多统计"按钮
|
||||||
|
3. 填写源日期、目标日期、船名、TEU等信息
|
||||||
|
4. 点击"确定"保存
|
||||||
|
```
|
||||||
|
|
||||||
### 二次靠泊合并
|
### 二次靠泊合并
|
||||||
解析时会自动合并同一天的二次靠泊记录:
|
解析时会自动合并同一天的二次靠泊记录:
|
||||||
- 夜班 学友洋山: 273TEU
|
- 夜班 学友洋山: 273TEU
|
||||||
|
|||||||
113
main.py
113
main.py
@@ -219,6 +219,40 @@ def remove_unaccounted(year_month: str, teu_to_reduce: int = None):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def add_cross_month_exclusion(source_date: str, target_date: str, ship_name: str, teu: int,
|
||||||
|
twenty_feet: int = 0, forty_feet: int = 0, reason: str = ''):
|
||||||
|
"""
|
||||||
|
添加跨月剔除调整(手动剔除次月多统计的船)
|
||||||
|
|
||||||
|
参数:
|
||||||
|
source_date: 源日期(上月底日期)
|
||||||
|
target_date: 目标日期(次月日期)
|
||||||
|
ship_name: 船名
|
||||||
|
teu: TEU数量
|
||||||
|
twenty_feet: 20尺箱量
|
||||||
|
forty_feet: 40尺箱量
|
||||||
|
reason: 调整原因
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
db = DailyLogsDatabase()
|
||||||
|
success = db.insert_cross_month_exclusion(
|
||||||
|
source_date=source_date,
|
||||||
|
target_date=target_date,
|
||||||
|
ship_name=ship_name,
|
||||||
|
teu=teu,
|
||||||
|
twenty_feet=twenty_feet,
|
||||||
|
forty_feet=forty_feet,
|
||||||
|
reason=reason
|
||||||
|
)
|
||||||
|
if success:
|
||||||
|
logger.info(f"已添加跨月剔除调整: {source_date} -> {target_date} {ship_name} {teu}TEU")
|
||||||
|
else:
|
||||||
|
logger.error("添加跨月剔除调整失败")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"添加跨月剔除调整失败: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
def show_stats(date: str):
|
def show_stats(date: str):
|
||||||
"""
|
"""
|
||||||
显示指定日期的统计
|
显示指定日期的统计
|
||||||
@@ -322,6 +356,16 @@ def main():
|
|||||||
--unaccounted, -u TEU 添加未统计数据(需同时指定月份)
|
--unaccounted, -u TEU 添加未统计数据(需同时指定月份)
|
||||||
--remove-unaccounted, -r [TEU] 去除未统计数据(需同时指定月份)。如果指定TEU值,则减少该数量;如果不指定,则删除整个记录
|
--remove-unaccounted, -r [TEU] 去除未统计数据(需同时指定月份)。如果指定TEU值,则减少该数量;如果不指定,则删除整个记录
|
||||||
--month, -m YEAR-MONTH 指定月份(与 -u 或 -r 配合使用)
|
--month, -m YEAR-MONTH 指定月份(与 -u 或 -r 配合使用)
|
||||||
|
--cross-exclude, -c 手动剔除次月多统计的船(需指定源日期、目标日期、船名和TEU)
|
||||||
|
|
||||||
|
跨月剔除参数:
|
||||||
|
--source-date DATE 源日期(上月底日期),格式: YYYY-MM-DD
|
||||||
|
--target-date DATE 目标日期(次月日期),格式: YYYY-MM-DD
|
||||||
|
--ship-name NAME 船名
|
||||||
|
--teu TEU TEU数量
|
||||||
|
--twenty-feet COUNT 20尺箱量(可选,默认0)
|
||||||
|
--forty-feet COUNT 40尺箱量(可选,默认0)
|
||||||
|
--reason REASON 调整原因(可选)
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
python3 main.py fetch
|
python3 main.py fetch
|
||||||
@@ -331,6 +375,7 @@ def main():
|
|||||||
python3 main.py --unaccounted 118 --month 2025-12
|
python3 main.py --unaccounted 118 --month 2025-12
|
||||||
python3 main.py --remove-unaccounted --month 2025-12 # 删除整个记录
|
python3 main.py --remove-unaccounted --month 2025-12 # 删除整个记录
|
||||||
python3 main.py --remove-unaccounted 118 --month 2025-12 # 减少118TEU
|
python3 main.py --remove-unaccounted 118 --month 2025-12 # 减少118TEU
|
||||||
|
python3 main.py --cross-exclude --source-date 2025-12-31 --target-date 2026-01-01 --ship-name "学友洋山" --teu 100
|
||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -367,6 +412,53 @@ def main():
|
|||||||
metavar='YEAR-MONTH',
|
metavar='YEAR-MONTH',
|
||||||
help='指定月份(与 --unaccounted 或 --remove-unaccounted 配合使用)'
|
help='指定月份(与 --unaccounted 或 --remove-unaccounted 配合使用)'
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--cross-exclude',
|
||||||
|
'-c',
|
||||||
|
action='store_true',
|
||||||
|
help='手动剔除次月多统计的船'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--source-date',
|
||||||
|
metavar='DATE',
|
||||||
|
help='源日期(上月底日期),格式: YYYY-MM-DD'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--target-date',
|
||||||
|
metavar='DATE',
|
||||||
|
help='目标日期(次月日期),格式: YYYY-MM-DD'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--ship-name',
|
||||||
|
metavar='NAME',
|
||||||
|
help='船名'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--teu',
|
||||||
|
metavar='TEU',
|
||||||
|
type=int,
|
||||||
|
help='TEU数量'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--twenty-feet',
|
||||||
|
metavar='COUNT',
|
||||||
|
type=int,
|
||||||
|
default=0,
|
||||||
|
help='20尺箱量(可选,默认0)'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--forty-feet',
|
||||||
|
metavar='COUNT',
|
||||||
|
type=int,
|
||||||
|
default=0,
|
||||||
|
help='40尺箱量(可选,默认0)'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--reason',
|
||||||
|
metavar='REASON',
|
||||||
|
default='手动剔除次月多统计的船',
|
||||||
|
help='调整原因(可选,默认: "手动剔除次月多统计的船")'
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -398,6 +490,27 @@ def main():
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# 跨月剔除功能
|
||||||
|
if args.cross_exclude:
|
||||||
|
if not all([args.source_date, args.target_date, args.ship_name, args.teu]):
|
||||||
|
logger.error("跨月剔除功能需要指定以下参数: --source-date, --target-date, --ship-name, --teu")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
add_cross_month_exclusion(
|
||||||
|
source_date=args.source_date,
|
||||||
|
target_date=args.target_date,
|
||||||
|
ship_name=args.ship_name,
|
||||||
|
teu=args.teu,
|
||||||
|
twenty_feet=args.twenty_feet,
|
||||||
|
forty_feet=args.forty_feet,
|
||||||
|
reason=args.reason
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"跨月剔除失败: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
return
|
||||||
|
|
||||||
# 执行功能
|
# 执行功能
|
||||||
try:
|
try:
|
||||||
if args.function == 'report' and args.date:
|
if args.function == 'report' and args.date:
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ class DailyLogsDatabase(DatabaseBase):
|
|||||||
cursor.execute('''
|
cursor.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS manual_adjustments (
|
CREATE TABLE IF NOT EXISTS manual_adjustments (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
date TEXT NOT NULL, -- 调整适用的日期
|
date TEXT NOT NULL, -- 调整适用的日期(目标日期)
|
||||||
ship_name TEXT NOT NULL, -- 船名
|
ship_name TEXT NOT NULL, -- 船名
|
||||||
teu INTEGER NOT NULL, -- TEU数量
|
teu INTEGER NOT NULL, -- TEU数量
|
||||||
twenty_feet INTEGER DEFAULT 0, -- 20尺箱量
|
twenty_feet INTEGER DEFAULT 0, -- 20尺箱量
|
||||||
@@ -115,6 +115,23 @@ class DailyLogsDatabase(DatabaseBase):
|
|||||||
)
|
)
|
||||||
''')
|
''')
|
||||||
|
|
||||||
|
# 检查是否需要添加新字段
|
||||||
|
cursor.execute("PRAGMA table_info(manual_adjustments)")
|
||||||
|
columns = [col[1] for col in cursor.fetchall()]
|
||||||
|
|
||||||
|
# 添加缺失的字段
|
||||||
|
if 'source_date' not in columns:
|
||||||
|
cursor.execute('ALTER TABLE manual_adjustments ADD COLUMN source_date TEXT')
|
||||||
|
logger.info("已添加 source_date 字段到 manual_adjustments 表")
|
||||||
|
|
||||||
|
if 'reason' not in columns:
|
||||||
|
cursor.execute('ALTER TABLE manual_adjustments ADD COLUMN reason TEXT')
|
||||||
|
logger.info("已添加 reason 字段到 manual_adjustments 表")
|
||||||
|
|
||||||
|
if 'status' not in columns:
|
||||||
|
cursor.execute('ALTER TABLE manual_adjustments ADD COLUMN status TEXT DEFAULT "pending"')
|
||||||
|
logger.info("已添加 status 字段到 manual_adjustments 表")
|
||||||
|
|
||||||
# 创建索引
|
# 创建索引
|
||||||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_manual_date ON manual_adjustments(date)')
|
cursor.execute('CREATE INDEX IF NOT EXISTS idx_manual_date ON manual_adjustments(date)')
|
||||||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_manual_type ON manual_adjustments(adjustment_type)')
|
cursor.execute('CREATE INDEX IF NOT EXISTS idx_manual_type ON manual_adjustments(adjustment_type)')
|
||||||
@@ -409,18 +426,23 @@ class DailyLogsDatabase(DatabaseBase):
|
|||||||
|
|
||||||
def insert_manual_adjustment(self, date: str, ship_name: str, teu: int,
|
def insert_manual_adjustment(self, date: str, ship_name: str, teu: int,
|
||||||
twenty_feet: int = 0, forty_feet: int = 0,
|
twenty_feet: int = 0, forty_feet: int = 0,
|
||||||
adjustment_type: str = 'add', note: str = '') -> bool:
|
adjustment_type: str = 'add', note: str = '',
|
||||||
|
source_date: str = None, reason: str = '',
|
||||||
|
status: str = 'pending') -> bool:
|
||||||
"""
|
"""
|
||||||
插入手动调整数据
|
插入手动调整数据
|
||||||
|
|
||||||
参数:
|
参数:
|
||||||
date: 日期字符串
|
date: 日期字符串(目标日期)
|
||||||
ship_name: 船名
|
ship_name: 船名
|
||||||
teu: TEU数量
|
teu: TEU数量
|
||||||
twenty_feet: 20尺箱量
|
twenty_feet: 20尺箱量
|
||||||
forty_feet: 40尺箱量
|
forty_feet: 40尺箱量
|
||||||
adjustment_type: 调整类型 'add' 或 'exclude'
|
adjustment_type: 调整类型 'add' 或 'exclude'
|
||||||
note: 备注
|
note: 备注
|
||||||
|
source_date: 源日期(上月底日期,可选)
|
||||||
|
reason: 调整原因
|
||||||
|
status: 调整状态:'pending', 'processed'
|
||||||
|
|
||||||
返回:
|
返回:
|
||||||
是否成功
|
是否成功
|
||||||
@@ -428,10 +450,12 @@ class DailyLogsDatabase(DatabaseBase):
|
|||||||
try:
|
try:
|
||||||
query = '''
|
query = '''
|
||||||
INSERT INTO manual_adjustments
|
INSERT INTO manual_adjustments
|
||||||
(date, ship_name, teu, twenty_feet, forty_feet, adjustment_type, note, created_at)
|
(date, source_date, ship_name, teu, twenty_feet, forty_feet,
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
adjustment_type, note, reason, status, created_at)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
||||||
'''
|
'''
|
||||||
params = (date, ship_name, teu, twenty_feet, forty_feet, adjustment_type, note)
|
params = (date, source_date, ship_name, teu, twenty_feet, forty_feet,
|
||||||
|
adjustment_type, note, reason, status)
|
||||||
self.execute_update(query, params)
|
self.execute_update(query, params)
|
||||||
logger.info(f"插入手动调整数据: {date} {ship_name} {teu}TEU ({adjustment_type})")
|
logger.info(f"插入手动调整数据: {date} {ship_name} {teu}TEU ({adjustment_type})")
|
||||||
return True
|
return True
|
||||||
@@ -473,6 +497,143 @@ class DailyLogsDatabase(DatabaseBase):
|
|||||||
'''
|
'''
|
||||||
return self.execute_query(query, (date, adjustment_type))
|
return self.execute_query(query, (date, adjustment_type))
|
||||||
|
|
||||||
|
def get_cross_month_adjustments(self, source_date: str = None, target_date: str = None,
|
||||||
|
status: str = None) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
获取跨月调整数据
|
||||||
|
|
||||||
|
参数:
|
||||||
|
source_date: 源日期(上月底日期)
|
||||||
|
target_date: 目标日期(次月日期)
|
||||||
|
status: 调整状态
|
||||||
|
|
||||||
|
返回:
|
||||||
|
跨月调整数据列表
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
conditions = []
|
||||||
|
params = []
|
||||||
|
|
||||||
|
if source_date:
|
||||||
|
conditions.append("source_date = ?")
|
||||||
|
params.append(source_date)
|
||||||
|
|
||||||
|
if target_date:
|
||||||
|
conditions.append("date = ?")
|
||||||
|
params.append(target_date)
|
||||||
|
|
||||||
|
if status:
|
||||||
|
conditions.append("status = ?")
|
||||||
|
params.append(status)
|
||||||
|
|
||||||
|
where_clause = " AND ".join(conditions) if conditions else "1=1"
|
||||||
|
query = f'''
|
||||||
|
SELECT * FROM manual_adjustments
|
||||||
|
WHERE {where_clause} ORDER BY created_at DESC
|
||||||
|
'''
|
||||||
|
|
||||||
|
return self.execute_query(query, tuple(params))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取跨月调整数据失败: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_pending_cross_month_adjustments(self) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
获取待处理的跨月调整数据
|
||||||
|
|
||||||
|
返回:
|
||||||
|
待处理的跨月调整数据列表
|
||||||
|
"""
|
||||||
|
return self.get_cross_month_adjustments(status='pending')
|
||||||
|
|
||||||
|
def update_adjustment_status(self, adjustment_id: int, status: str) -> bool:
|
||||||
|
"""
|
||||||
|
更新调整状态
|
||||||
|
|
||||||
|
参数:
|
||||||
|
adjustment_id: 调整记录ID
|
||||||
|
status: 新状态
|
||||||
|
|
||||||
|
返回:
|
||||||
|
是否成功
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
query = 'UPDATE manual_adjustments SET status = ? WHERE id = ?'
|
||||||
|
result = self.execute_update(query, (status, adjustment_id))
|
||||||
|
if result > 0:
|
||||||
|
logger.info(f"更新调整状态: ID={adjustment_id} -> {status}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.warning(f"未找到调整记录: ID={adjustment_id}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"更新调整状态失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def insert_cross_month_exclusion(self, source_date: str, target_date: str,
|
||||||
|
ship_name: str, teu: int,
|
||||||
|
twenty_feet: int = 0, forty_feet: int = 0,
|
||||||
|
reason: str = '') -> bool:
|
||||||
|
"""
|
||||||
|
插入跨月剔除调整(手动剔除次月多统计的船)
|
||||||
|
|
||||||
|
参数:
|
||||||
|
source_date: 源日期(上月底日期)
|
||||||
|
target_date: 目标日期(次月日期)
|
||||||
|
ship_name: 船名
|
||||||
|
teu: TEU数量
|
||||||
|
twenty_feet: 20尺箱量
|
||||||
|
forty_feet: 40尺箱量
|
||||||
|
reason: 调整原因
|
||||||
|
|
||||||
|
返回:
|
||||||
|
是否成功
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 1. 插入剔除记录(从月底最后一天扣除)
|
||||||
|
exclude_success = self.insert_manual_adjustment(
|
||||||
|
date=source_date,
|
||||||
|
source_date=source_date,
|
||||||
|
ship_name=ship_name,
|
||||||
|
teu=teu,
|
||||||
|
twenty_feet=twenty_feet,
|
||||||
|
forty_feet=forty_feet,
|
||||||
|
adjustment_type='exclude',
|
||||||
|
note=f"手动剔除次月多统计的船,目标日期: {target_date}",
|
||||||
|
reason=reason,
|
||||||
|
status='pending'
|
||||||
|
)
|
||||||
|
|
||||||
|
if not exclude_success:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 2. 自动将相同数据添加到次月1号
|
||||||
|
add_success = self.insert_manual_adjustment(
|
||||||
|
date=target_date,
|
||||||
|
source_date=source_date,
|
||||||
|
ship_name=ship_name,
|
||||||
|
teu=teu,
|
||||||
|
twenty_feet=twenty_feet,
|
||||||
|
forty_feet=forty_feet,
|
||||||
|
adjustment_type='add',
|
||||||
|
note=f"从{source_date}转移的数据: {reason}",
|
||||||
|
reason=reason,
|
||||||
|
status='pending'
|
||||||
|
)
|
||||||
|
|
||||||
|
if add_success:
|
||||||
|
logger.info(f"插入跨月剔除调整: {source_date} -> {target_date} {ship_name} {teu}TEU")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.error(f"插入跨月剔除调整失败: 添加数据到次月1号失败")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"插入跨月剔除调整失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
def delete_manual_adjustment(self, adjustment_id: int) -> bool:
|
def delete_manual_adjustment(self, adjustment_id: int) -> bool:
|
||||||
"""
|
"""
|
||||||
删除指定ID的手动调整数据
|
删除指定ID的手动调整数据
|
||||||
|
|||||||
515
src/gui.py
515
src/gui.py
@@ -103,55 +103,16 @@ class OrbitInGUI:
|
|||||||
# 分隔线
|
# 分隔线
|
||||||
ttk.Separator(left_frame, orient=tk.HORIZONTAL).pack(fill=tk.X, pady=10)
|
ttk.Separator(left_frame, orient=tk.HORIZONTAL).pack(fill=tk.X, pady=10)
|
||||||
|
|
||||||
# 未统计数据
|
# 手动剔除次月多统计的船
|
||||||
ttk.Label(left_frame, text="添加未统计数据:").pack(anchor=tk.W, pady=(10, 5))
|
ttk.Label(left_frame, text="手动剔除次月多统计的船:").pack(anchor=tk.W, pady=(10, 5))
|
||||||
|
|
||||||
unaccounted_frame = ttk.Frame(left_frame)
|
btn_cross_month_exclude = ttk.Button(
|
||||||
unaccounted_frame.pack(fill=tk.X, pady=5)
|
|
||||||
|
|
||||||
ttk.Label(unaccounted_frame, text="月份:").pack(side=tk.LEFT)
|
|
||||||
self.month_var = tk.StringVar(value=datetime.now().strftime('%Y-%m'))
|
|
||||||
month_entry = ttk.Entry(unaccounted_frame, textvariable=self.month_var, width=8)
|
|
||||||
month_entry.pack(side=tk.LEFT, padx=(5, 10))
|
|
||||||
|
|
||||||
ttk.Label(unaccounted_frame, text="TEU:").pack(side=tk.LEFT)
|
|
||||||
self.teu_var = tk.StringVar()
|
|
||||||
teu_entry = ttk.Entry(unaccounted_frame, textvariable=self.teu_var, width=8)
|
|
||||||
teu_entry.pack(side=tk.LEFT, padx=(5, 0))
|
|
||||||
|
|
||||||
btn_unaccounted = ttk.Button(
|
|
||||||
left_frame,
|
left_frame,
|
||||||
text="添加",
|
text="剔除次月多统计",
|
||||||
command=self.add_unaccounted,
|
command=self.show_cross_month_exclude_dialog,
|
||||||
width=20
|
width=20
|
||||||
)
|
)
|
||||||
btn_unaccounted.pack(pady=5)
|
btn_cross_month_exclude.pack(pady=5)
|
||||||
|
|
||||||
# 分隔线
|
|
||||||
ttk.Separator(left_frame, orient=tk.HORIZONTAL).pack(fill=tk.X, pady=5)
|
|
||||||
|
|
||||||
# 去除多余统计数据
|
|
||||||
ttk.Label(left_frame, text="去除多余统计数据:").pack(anchor=tk.W, pady=(10, 5))
|
|
||||||
|
|
||||||
remove_frame = ttk.Frame(left_frame)
|
|
||||||
remove_frame.pack(fill=tk.X, pady=5)
|
|
||||||
|
|
||||||
ttk.Label(remove_frame, text="月份:").pack(side=tk.LEFT)
|
|
||||||
month_entry2 = ttk.Entry(remove_frame, textvariable=self.month_var, width=8)
|
|
||||||
month_entry2.pack(side=tk.LEFT, padx=(5, 10))
|
|
||||||
|
|
||||||
ttk.Label(remove_frame, text="TEU:").pack(side=tk.LEFT)
|
|
||||||
self.remove_teu_var = tk.StringVar()
|
|
||||||
remove_teu_entry = ttk.Entry(remove_frame, textvariable=self.remove_teu_var, width=8)
|
|
||||||
remove_teu_entry.pack(side=tk.LEFT, padx=(5, 0))
|
|
||||||
|
|
||||||
btn_remove_unaccounted = ttk.Button(
|
|
||||||
left_frame,
|
|
||||||
text="去除",
|
|
||||||
command=self.remove_unaccounted,
|
|
||||||
width=20
|
|
||||||
)
|
|
||||||
btn_remove_unaccounted.pack(pady=5)
|
|
||||||
|
|
||||||
# 分隔线
|
# 分隔线
|
||||||
ttk.Separator(left_frame, orient=tk.HORIZONTAL).pack(fill=tk.X, pady=10)
|
ttk.Separator(left_frame, orient=tk.HORIZONTAL).pack(fill=tk.X, pady=10)
|
||||||
@@ -646,123 +607,6 @@ class OrbitInGUI:
|
|||||||
self.date_var.set(yesterday)
|
self.date_var.set(yesterday)
|
||||||
self.generate_report()
|
self.generate_report()
|
||||||
|
|
||||||
def add_unaccounted(self):
|
|
||||||
"""添加未统计数据"""
|
|
||||||
year_month = self.month_var.get().strip()
|
|
||||||
teu = self.teu_var.get().strip()
|
|
||||||
|
|
||||||
if not year_month or not teu:
|
|
||||||
self.log_message("错误: 请输入月份和 TEU", is_error=True)
|
|
||||||
self.logger.error("未输入月份和 TEU")
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
teu = int(teu)
|
|
||||||
except ValueError:
|
|
||||||
self.log_message("错误: TEU 必须是数字", is_error=True)
|
|
||||||
self.logger.error(f"TEU 不是数字: {teu}")
|
|
||||||
return
|
|
||||||
|
|
||||||
self.set_status("正在添加...")
|
|
||||||
self.log_message(f"添加 {year_month} 月未统计数据: {teu}TEU")
|
|
||||||
self.logger.info(f"添加 {year_month} 月未统计数据: {teu}TEU")
|
|
||||||
|
|
||||||
try:
|
|
||||||
db = DailyLogsDatabase()
|
|
||||||
result = db.insert_unaccounted(year_month, teu, '')
|
|
||||||
|
|
||||||
if result:
|
|
||||||
self.log_message("添加成功!")
|
|
||||||
self.logger.info(f"未统计数据添加成功: {year_month} {teu}TEU")
|
|
||||||
# 刷新日报显示
|
|
||||||
self.generate_today_report()
|
|
||||||
else:
|
|
||||||
self.log_message("添加失败!", is_error=True)
|
|
||||||
self.logger.error(f"未统计数据添加失败: {year_month} {teu}TEU")
|
|
||||||
|
|
||||||
self.set_status("完成")
|
|
||||||
|
|
||||||
except DatabaseConnectionError as e:
|
|
||||||
self.log_message(f"数据库连接错误: {e}", is_error=True)
|
|
||||||
self.logger.error(f"数据库连接错误: {e}")
|
|
||||||
self.set_status("错误")
|
|
||||||
except Exception as e:
|
|
||||||
self.log_message(f"未知错误: {e}", is_error=True)
|
|
||||||
self.logger.error(f"未知错误: {e}", exc_info=True)
|
|
||||||
self.set_status("错误")
|
|
||||||
|
|
||||||
def remove_unaccounted(self):
|
|
||||||
"""去除未统计数据"""
|
|
||||||
year_month = self.month_var.get().strip()
|
|
||||||
teu_str = self.remove_teu_var.get().strip()
|
|
||||||
|
|
||||||
if not year_month:
|
|
||||||
self.log_message("错误: 请输入月份", is_error=True)
|
|
||||||
self.logger.error("未输入月份")
|
|
||||||
return
|
|
||||||
|
|
||||||
# 确认对话框
|
|
||||||
if teu_str:
|
|
||||||
try:
|
|
||||||
teu_to_reduce = int(teu_str)
|
|
||||||
confirm_msg = f"确定要减少 {year_month} 月的 {teu_to_reduce}TEU 未统计数据吗?"
|
|
||||||
operation_type = "减少"
|
|
||||||
except ValueError:
|
|
||||||
self.log_message("错误: TEU 必须是数字", is_error=True)
|
|
||||||
self.logger.error(f"TEU 不是数字: {teu_str}")
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
confirm_msg = f"确定要删除 {year_month} 月的未统计数据吗?"
|
|
||||||
operation_type = "删除"
|
|
||||||
teu_to_reduce = None
|
|
||||||
|
|
||||||
if not messagebox.askyesno("确认", confirm_msg):
|
|
||||||
self.log_message("操作已取消")
|
|
||||||
self.logger.info("用户取消操作")
|
|
||||||
return
|
|
||||||
|
|
||||||
self.set_status(f"正在{operation_type}...")
|
|
||||||
if teu_to_reduce:
|
|
||||||
self.log_message(f"{operation_type} {year_month} 月未统计数据: {teu_to_reduce}TEU")
|
|
||||||
self.logger.info(f"{operation_type} {year_month} 月未统计数据: {teu_to_reduce}TEU")
|
|
||||||
else:
|
|
||||||
self.log_message(f"{operation_type} {year_month} 月未统计数据")
|
|
||||||
self.logger.info(f"{operation_type} {year_month} 月未统计数据")
|
|
||||||
|
|
||||||
try:
|
|
||||||
db = DailyLogsDatabase()
|
|
||||||
|
|
||||||
if teu_to_reduce:
|
|
||||||
# 减少指定数量的TEU
|
|
||||||
result = db.reduce_unaccounted(year_month, teu_to_reduce)
|
|
||||||
success_msg = f"{operation_type}成功!"
|
|
||||||
error_msg = f"{operation_type}失败!"
|
|
||||||
else:
|
|
||||||
# 删除整个记录
|
|
||||||
result = db.delete_unaccounted(year_month)
|
|
||||||
success_msg = f"{operation_type}成功!"
|
|
||||||
error_msg = f"{operation_type}失败!"
|
|
||||||
|
|
||||||
if result:
|
|
||||||
self.log_message(success_msg)
|
|
||||||
self.logger.info(f"未统计数据{operation_type}成功: {year_month}")
|
|
||||||
# 刷新日报显示
|
|
||||||
self.generate_today_report()
|
|
||||||
else:
|
|
||||||
self.log_message(error_msg, is_error=True)
|
|
||||||
self.logger.error(f"未统计数据{operation_type}失败: {year_month}")
|
|
||||||
|
|
||||||
self.set_status("完成")
|
|
||||||
|
|
||||||
except DatabaseConnectionError as e:
|
|
||||||
self.log_message(f"数据库连接错误: {e}", is_error=True)
|
|
||||||
self.logger.error(f"数据库连接错误: {e}")
|
|
||||||
self.set_status("错误")
|
|
||||||
except Exception as e:
|
|
||||||
self.log_message(f"未知错误: {e}", is_error=True)
|
|
||||||
self.logger.error(f"未知错误: {e}", exc_info=True)
|
|
||||||
self.set_status("错误")
|
|
||||||
|
|
||||||
def auto_fetch_data(self):
|
def auto_fetch_data(self):
|
||||||
"""自动获取新数据(GUI启动时调用)"""
|
"""自动获取新数据(GUI启动时调用)"""
|
||||||
self.set_status("正在自动获取新数据...")
|
self.set_status("正在自动获取新数据...")
|
||||||
@@ -943,6 +787,11 @@ class OrbitInGUI:
|
|||||||
dialog = ConfluencePagesDialog(self.root, self)
|
dialog = ConfluencePagesDialog(self.root, self)
|
||||||
self.root.wait_window(dialog)
|
self.root.wait_window(dialog)
|
||||||
|
|
||||||
|
def show_cross_month_exclude_dialog(self):
|
||||||
|
"""显示手动剔除次月多统计的船对话框"""
|
||||||
|
dialog = CrossMonthExcludeDialog(self.root, self)
|
||||||
|
self.root.wait_window(dialog)
|
||||||
|
|
||||||
|
|
||||||
class AddDataDialog(tk.Toplevel):
|
class AddDataDialog(tk.Toplevel):
|
||||||
"""添加数据对话框"""
|
"""添加数据对话框"""
|
||||||
@@ -1469,6 +1318,348 @@ class ConfluencePageEditDialog(tk.Toplevel):
|
|||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
|
|
||||||
|
class CrossMonthExcludeDialog(tk.Toplevel):
|
||||||
|
"""手动剔除次月多统计的船对话框"""
|
||||||
|
|
||||||
|
def __init__(self, parent, gui):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.title("手动剔除次月多统计的船")
|
||||||
|
self.gui = gui
|
||||||
|
self.result = None
|
||||||
|
|
||||||
|
# 设置对话框大小和位置
|
||||||
|
self.geometry("850x750")
|
||||||
|
self.resizable(True, True)
|
||||||
|
|
||||||
|
# 使对话框模态
|
||||||
|
self.transient(parent)
|
||||||
|
self.grab_set()
|
||||||
|
|
||||||
|
# 计算当前月份和上月
|
||||||
|
now = datetime.now()
|
||||||
|
current_year = now.year
|
||||||
|
current_month = now.month
|
||||||
|
|
||||||
|
# 计算上个月(正确处理跨年)
|
||||||
|
if current_month == 1:
|
||||||
|
last_month = 12
|
||||||
|
last_year = current_year - 1
|
||||||
|
else:
|
||||||
|
last_month = current_month - 1
|
||||||
|
last_year = current_year
|
||||||
|
|
||||||
|
# 获取月份列表
|
||||||
|
month_list = self._get_month_list()
|
||||||
|
print(f"DEBUG: 月份列表: {month_list}")
|
||||||
|
|
||||||
|
# 初始化月份选择
|
||||||
|
self.source_month_var = tk.StringVar(value=f"{last_year}-{last_month:02d}") # 默认上个月
|
||||||
|
self.target_month_var = tk.StringVar(value=f"{current_year}-{current_month:02d}") # 默认当前月
|
||||||
|
|
||||||
|
print(f"DEBUG: 源月份默认值: {self.source_month_var.get()}")
|
||||||
|
print(f"DEBUG: 目标月份默认值: {self.target_month_var.get()}")
|
||||||
|
|
||||||
|
# 创建输入字段
|
||||||
|
frame = ttk.Frame(self, padding="20")
|
||||||
|
frame.pack(fill=tk.BOTH, expand=True)
|
||||||
|
|
||||||
|
# 说明文本
|
||||||
|
ttk.Label(frame, text="用于处理上月底余留数据未及时剔除的情况。\n"
|
||||||
|
"例如:本月1号整理数据时,发现上月余留数据未剔除。\n"
|
||||||
|
"提示:选择船后可手动修改TEU值(支持跨日船部分剔除)。",
|
||||||
|
wraplength=800).grid(row=0, column=0, columnspan=3, sticky=tk.W, pady=(0, 15))
|
||||||
|
|
||||||
|
# 源月份选择
|
||||||
|
ttk.Label(frame, text="源月份(被剔除数据的月份):").grid(row=1, column=0, sticky=tk.W, pady=5)
|
||||||
|
self.source_month_combo = ttk.Combobox(frame, textvariable=self.source_month_var,
|
||||||
|
values=self._get_month_list(), width=12)
|
||||||
|
self.source_month_combo.grid(row=1, column=1, sticky=tk.W, pady=5)
|
||||||
|
self.source_month_combo.bind('<<ComboboxSelected>>', self.on_source_month_changed)
|
||||||
|
|
||||||
|
# 目标月份选择
|
||||||
|
ttk.Label(frame, text="目标月份(数据转移到的月份):").grid(row=2, column=0, sticky=tk.W, pady=5)
|
||||||
|
self.target_month_combo = ttk.Combobox(frame, textvariable=self.target_month_var,
|
||||||
|
values=self._get_month_list(), width=12)
|
||||||
|
self.target_month_combo.grid(row=2, column=1, sticky=tk.W, pady=5)
|
||||||
|
|
||||||
|
# 分隔线
|
||||||
|
ttk.Separator(frame, orient=tk.HORIZONTAL).grid(row=3, column=0, columnspan=3, sticky=tk.EW, pady=10)
|
||||||
|
|
||||||
|
# 源月份船次列表
|
||||||
|
ttk.Label(frame, text="源月份船次列表:").grid(row=4, column=0, sticky=tk.W, pady=5)
|
||||||
|
|
||||||
|
# 创建Treeview显示船次
|
||||||
|
columns = ('ship_name', 'teu', 'twenty_feet', 'forty_feet', 'shift')
|
||||||
|
self.tree = ttk.Treeview(frame, columns=columns, show='headings', height=8)
|
||||||
|
|
||||||
|
# 设置列标题
|
||||||
|
self.tree.heading('ship_name', text='船名')
|
||||||
|
self.tree.heading('teu', text='TEU')
|
||||||
|
self.tree.heading('twenty_feet', text='20尺')
|
||||||
|
self.tree.heading('forty_feet', text='40尺')
|
||||||
|
self.tree.heading('shift', text='班次')
|
||||||
|
|
||||||
|
# 设置列宽度
|
||||||
|
self.tree.column('ship_name', width=120)
|
||||||
|
self.tree.column('teu', width=60)
|
||||||
|
self.tree.column('twenty_feet', width=60)
|
||||||
|
self.tree.column('forty_feet', width=60)
|
||||||
|
self.tree.column('shift', width=80)
|
||||||
|
|
||||||
|
# 添加滚动条
|
||||||
|
scrollbar = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=self.tree.yview)
|
||||||
|
self.tree.configure(yscroll=scrollbar.set)
|
||||||
|
|
||||||
|
self.tree.grid(row=5, column=0, columnspan=2, sticky=tk.NSEW, pady=5)
|
||||||
|
scrollbar.grid(row=5, column=2, sticky=tk.NS, pady=5)
|
||||||
|
|
||||||
|
# 加载船次数据
|
||||||
|
self.load_ships()
|
||||||
|
|
||||||
|
# 分隔线
|
||||||
|
ttk.Separator(frame, orient=tk.HORIZONTAL).grid(row=6, column=0, columnspan=3, sticky=tk.EW, pady=10)
|
||||||
|
|
||||||
|
# 手动输入区域(用于输入不在列表中的船)
|
||||||
|
ttk.Label(frame, text="手动输入:").grid(row=7, column=0, sticky=tk.W, pady=5)
|
||||||
|
|
||||||
|
# 船名
|
||||||
|
ttk.Label(frame, text="船名:").grid(row=8, column=0, sticky=tk.W, pady=5)
|
||||||
|
self.ship_var = tk.StringVar()
|
||||||
|
ship_entry = ttk.Entry(frame, textvariable=self.ship_var, width=20)
|
||||||
|
ship_entry.grid(row=8, column=1, sticky=tk.W, pady=5)
|
||||||
|
|
||||||
|
# TEU
|
||||||
|
ttk.Label(frame, text="TEU:").grid(row=9, column=0, sticky=tk.W, pady=5)
|
||||||
|
self.teu_var = tk.StringVar()
|
||||||
|
teu_entry = ttk.Entry(frame, textvariable=self.teu_var, width=10)
|
||||||
|
teu_entry.grid(row=9, column=1, sticky=tk.W, pady=5)
|
||||||
|
|
||||||
|
# 20尺箱量
|
||||||
|
ttk.Label(frame, text="20尺箱量:").grid(row=10, column=0, sticky=tk.W, pady=5)
|
||||||
|
self.twenty_var = tk.StringVar(value="0")
|
||||||
|
twenty_entry = ttk.Entry(frame, textvariable=self.twenty_var, width=10)
|
||||||
|
twenty_entry.grid(row=10, column=1, sticky=tk.W, pady=5)
|
||||||
|
|
||||||
|
# 40尺箱量
|
||||||
|
ttk.Label(frame, text="40尺箱量:").grid(row=11, column=0, sticky=tk.W, pady=5)
|
||||||
|
self.forty_var = tk.StringVar(value="0")
|
||||||
|
forty_entry = ttk.Entry(frame, textvariable=self.forty_var, width=10)
|
||||||
|
forty_entry.grid(row=11, column=1, sticky=tk.W, pady=5)
|
||||||
|
|
||||||
|
# 调整原因
|
||||||
|
ttk.Label(frame, text="调整原因:").grid(row=12, column=0, sticky=tk.W, pady=5)
|
||||||
|
self.reason_var = tk.StringVar(value="手动剔除次月多统计的船")
|
||||||
|
reason_entry = ttk.Entry(frame, textvariable=self.reason_var, width=30)
|
||||||
|
reason_entry.grid(row=12, column=1, sticky=tk.W, pady=5)
|
||||||
|
|
||||||
|
# 按钮
|
||||||
|
button_frame = ttk.Frame(frame)
|
||||||
|
button_frame.grid(row=13, column=0, columnspan=3, pady=20)
|
||||||
|
|
||||||
|
ttk.Button(button_frame, text="确定", command=self.on_ok).pack(side=tk.LEFT, padx=10)
|
||||||
|
ttk.Button(button_frame, text="取消", command=self.on_cancel).pack(side=tk.LEFT, padx=10)
|
||||||
|
|
||||||
|
# 绑定回车键
|
||||||
|
self.bind('<Return>', lambda e: self.on_ok())
|
||||||
|
self.bind('<Escape>', lambda e: self.on_cancel())
|
||||||
|
|
||||||
|
# 绑定Treeview选择事件
|
||||||
|
self.tree.bind('<<TreeviewSelect>>', self.on_ship_selected)
|
||||||
|
|
||||||
|
# 焦点设置
|
||||||
|
ship_entry.focus_set()
|
||||||
|
|
||||||
|
def _get_month_list(self):
|
||||||
|
"""获取可选月份列表(近12个月,从当前月往前推)"""
|
||||||
|
months = []
|
||||||
|
now = datetime.now()
|
||||||
|
year = now.year
|
||||||
|
month = now.month
|
||||||
|
|
||||||
|
# 生成近12个月
|
||||||
|
for i in range(12):
|
||||||
|
if month - i <= 0:
|
||||||
|
# 跨年
|
||||||
|
m = month - i + 12
|
||||||
|
y = year - 1
|
||||||
|
else:
|
||||||
|
m = month - i
|
||||||
|
y = year
|
||||||
|
months.append(f"{y}-{m:02d}")
|
||||||
|
|
||||||
|
print(f"DEBUG: _get_month_list 返回: {months}")
|
||||||
|
return months
|
||||||
|
|
||||||
|
def on_source_month_changed(self, event):
|
||||||
|
"""当源月份改变时,重新加载船次列表"""
|
||||||
|
self.load_ships()
|
||||||
|
|
||||||
|
def get_source_date(self):
|
||||||
|
"""获取源月份的最后一天日期"""
|
||||||
|
month_str = self.source_month_var.get()
|
||||||
|
year, month = map(int, month_str.split('-'))
|
||||||
|
# 月底最后一天
|
||||||
|
if month == 12:
|
||||||
|
next_month = datetime(year + 1, 1, 1)
|
||||||
|
else:
|
||||||
|
next_month = datetime(year, month + 1, 1)
|
||||||
|
last_day = next_month - timedelta(days=1)
|
||||||
|
return last_day.strftime('%Y-%m-%d')
|
||||||
|
|
||||||
|
def get_target_date(self):
|
||||||
|
"""获取目标月份的第一天日期"""
|
||||||
|
month_str = self.target_month_var.get()
|
||||||
|
year, month = map(int, month_str.split('-'))
|
||||||
|
return datetime(year, month, 1).strftime('%Y-%m-%d')
|
||||||
|
|
||||||
|
def load_ships(self):
|
||||||
|
"""加载源月份的船次数据"""
|
||||||
|
source_date = self.get_source_date()
|
||||||
|
try:
|
||||||
|
db = DailyLogsDatabase()
|
||||||
|
logs = db.query_by_date(source_date)
|
||||||
|
|
||||||
|
# 清空现有数据
|
||||||
|
for item in self.tree.get_children():
|
||||||
|
self.tree.delete(item)
|
||||||
|
|
||||||
|
if not logs:
|
||||||
|
self.tree.insert('', tk.END, values=('无数据', '', '', '', ''))
|
||||||
|
return
|
||||||
|
|
||||||
|
# 按船名汇总数据
|
||||||
|
ships = {}
|
||||||
|
for log in logs:
|
||||||
|
ship_name = log['ship_name']
|
||||||
|
if ship_name not in ships:
|
||||||
|
ships[ship_name] = {
|
||||||
|
'teu': 0,
|
||||||
|
'twenty_feet': 0,
|
||||||
|
'forty_feet': 0,
|
||||||
|
'shifts': set()
|
||||||
|
}
|
||||||
|
|
||||||
|
if log.get('teu'):
|
||||||
|
ships[ship_name]['teu'] += log['teu']
|
||||||
|
if log.get('twenty_feet'):
|
||||||
|
ships[ship_name]['twenty_feet'] += log['twenty_feet']
|
||||||
|
if log.get('forty_feet'):
|
||||||
|
ships[ship_name]['forty_feet'] += log['forty_feet']
|
||||||
|
if log.get('shift'):
|
||||||
|
ships[ship_name]['shifts'].add(log['shift'])
|
||||||
|
|
||||||
|
# 插入到Treeview
|
||||||
|
for ship_name, data in ships.items():
|
||||||
|
shifts_str = ', '.join(sorted(data['shifts']))
|
||||||
|
self.tree.insert('', tk.END, values=(
|
||||||
|
ship_name,
|
||||||
|
data['teu'],
|
||||||
|
data['twenty_feet'],
|
||||||
|
data['forty_feet'],
|
||||||
|
shifts_str
|
||||||
|
))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.gui.log_message(f"加载船次数据失败: {e}", is_error=True)
|
||||||
|
self.tree.insert('', tk.END, values=('加载失败', '', '', '', ''))
|
||||||
|
|
||||||
|
def on_ship_selected(self, event):
|
||||||
|
"""当选择船次时,自动填充数据"""
|
||||||
|
selection = self.tree.selection()
|
||||||
|
if not selection:
|
||||||
|
return
|
||||||
|
|
||||||
|
item = self.tree.item(selection[0])
|
||||||
|
values = item['values']
|
||||||
|
|
||||||
|
if values[0] in ('无数据', '加载失败'):
|
||||||
|
return
|
||||||
|
|
||||||
|
# 自动填充数据
|
||||||
|
self.ship_var.set(values[0])
|
||||||
|
self.teu_var.set(str(values[1]))
|
||||||
|
self.twenty_var.set(str(values[2]))
|
||||||
|
self.forty_var.set(str(values[3]))
|
||||||
|
|
||||||
|
def on_ok(self):
|
||||||
|
"""确定按钮处理"""
|
||||||
|
try:
|
||||||
|
# 获取输入
|
||||||
|
source_date = self.get_source_date()
|
||||||
|
target_date = self.get_target_date()
|
||||||
|
ship_name = self.ship_var.get().strip()
|
||||||
|
teu_str = self.teu_var.get().strip()
|
||||||
|
twenty_str = self.twenty_var.get().strip()
|
||||||
|
forty_str = self.forty_var.get().strip()
|
||||||
|
reason = self.reason_var.get().strip()
|
||||||
|
|
||||||
|
if not ship_name:
|
||||||
|
messagebox.showerror("错误", "请输入船名")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not teu_str:
|
||||||
|
messagebox.showerror("错误", "请输入TEU")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 验证数字
|
||||||
|
teu = int(teu_str)
|
||||||
|
twenty_feet = int(twenty_str) if twenty_str else 0
|
||||||
|
forty_feet = int(forty_str) if forty_str else 0
|
||||||
|
|
||||||
|
if teu <= 0:
|
||||||
|
messagebox.showerror("错误", "TEU必须大于0")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 确认对话框
|
||||||
|
confirm_msg = (f"确定要将数据从源月份({source_date})转移到目标月份({target_date})吗?\n\n"
|
||||||
|
f"船名: {ship_name}\n"
|
||||||
|
f"TEU: {teu}\n"
|
||||||
|
f"20尺箱量: {twenty_feet}\n"
|
||||||
|
f"40尺箱量: {forty_feet}\n"
|
||||||
|
f"原因: {reason}")
|
||||||
|
|
||||||
|
if not messagebox.askyesno("确认操作", confirm_msg):
|
||||||
|
return
|
||||||
|
|
||||||
|
# 保存到数据库
|
||||||
|
try:
|
||||||
|
db = DailyLogsDatabase()
|
||||||
|
success = db.insert_cross_month_exclusion(
|
||||||
|
source_date=source_date,
|
||||||
|
target_date=target_date,
|
||||||
|
ship_name=ship_name,
|
||||||
|
teu=teu,
|
||||||
|
twenty_feet=twenty_feet,
|
||||||
|
forty_feet=forty_feet,
|
||||||
|
reason=reason
|
||||||
|
)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
self.gui.log_message(f"已添加跨月剔除调整: {source_date} -> {target_date} {ship_name} {teu}TEU")
|
||||||
|
self.gui.logger.info(f"已添加跨月剔除调整: {source_date} -> {target_date} {ship_name} {teu}TEU")
|
||||||
|
# 刷新日报显示
|
||||||
|
self.gui.generate_today_report()
|
||||||
|
self.result = True
|
||||||
|
self.destroy()
|
||||||
|
else:
|
||||||
|
messagebox.showerror("错误", "保存失败")
|
||||||
|
self.gui.log_message("保存跨月剔除调整失败", is_error=True)
|
||||||
|
self.gui.logger.error("保存跨月剔除调整失败")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror("错误", f"保存失败: {e}")
|
||||||
|
self.gui.log_message(f"保存跨月剔除调整失败: {e}", is_error=True)
|
||||||
|
self.gui.logger.error(f"保存跨月剔除调整失败: {e}")
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
messagebox.showerror("错误", "请输入有效的数字")
|
||||||
|
|
||||||
|
def on_cancel(self):
|
||||||
|
"""取消按钮处理"""
|
||||||
|
self.result = None
|
||||||
|
self.destroy()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""主函数"""
|
"""主函数"""
|
||||||
root = tk.Tk()
|
root = tk.Tk()
|
||||||
|
|||||||
Reference in New Issue
Block a user