Bläddra i källkod

first commit

master
lizhuang 2 veckor sedan
incheckning
ec2f18dd68
13 ändrade filer med 1825 tillägg och 0 borttagningar
  1. 141
    0
      .gitignore
  2. 38
    0
      ExcelConverter.spec
  3. 115
    0
      README.md
  4. 1051
    0
      app.py
  5. 86
    0
      bank_options.json
  6. 32
    0
      build.py
  7. 1
    0
      company_options.json
  8. 160
    0
      config.py
  9. 156
    0
      employee_info.json
  10. 36
    0
      main.py
  11. 5
    0
      requirements.txt
  12. 4
    0
      run.bat
  13. Binär
      template.xlsx

+ 141
- 0
.gitignore Visa fil

@@ -0,0 +1,141 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/
__pycache__/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# vuepress v2.x temp and cache directory
.temp
.cache

# vitepress build output
**/.vitepress/dist

# vitepress cache directory
**/.vitepress/cache

.venv/

build/

# Docusaurus cache and generated files
.docusaurus

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

+ 38
- 0
ExcelConverter.spec Visa fil

@@ -0,0 +1,38 @@
# -*- mode: python ; coding: utf-8 -*-


a = Analysis(
['app.py'],
pathex=[],
binaries=[],
datas=[('employee_info.json', '.'), ('company_options.json', '.'), ('bank_options.json', '.'), ('template.xlsx', '.'), ('config.py', '.')],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)

exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='ExcelConverter',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)

+ 115
- 0
README.md Visa fil

@@ -0,0 +1,115 @@
# 工资明细转换工具

这是一个用于批量转换Excel工资明细表的图形界面工具。

## 功能特点

1. 批量导入Excel文件并根据模板进行转换
2. 自定义设置每个文件的公司、银行和其他信息
3. 按照指定的单元格映射规则转换数据
4. 自动生成包含员工姓名和日期的输出文件名
5. 支持复制和保留原始Excel的格式和样式

## 环境要求

- Python 3.7 或更高版本
- 依赖包:pandas, openpyxl, xlrd, xlwt

## 快速开始

1. 确保已安装Python环境(3.7或更高版本)
2. 使用uv创建虚拟环境:
```bash
uv venv
```
3. 激活虚拟环境:
```bash
# Windows
.venv\Scripts\activate
# macOS/Linux
source .venv/bin/activate
```
4. 安装依赖包:
```bash
uv pip install -r requirements.txt
```
5. 运行程序:
```bash
# 方法1: 直接运行Python脚本
python main.py
# 方法2: 使用批处理文件 (Windows)
run.bat
```

## 使用方法

1. 在界面上点击"选择Excel文件"按钮选择要转换的Excel文件
2. 双击列表中的每个文件,设置公司信息(C2)、银行信息(B30)和其他信息(F2)
3. 点击"选择导出位置"按钮选择输出文件保存位置
4. 点击"开始转换"按钮进行批量转换
5. 转换完成后会显示成功转换的文件数量

## 文件命名规则

输出文件名格式为:`YYYY年M月份給料明細書-姓名.xls`
- 年份(YYYY):从输入文件第二个Sheet页的B4单元格中提取
- 月份(M):从输入文件第二个Sheet页的B4单元格中提取
- 姓名:从输入文件第一个Sheet页的C3单元格中提取

## 配置指南

### 修改模板路径
在 `config.py` 文件中修改 `TEMPLATE_PATH` 变量。

### 修改单元格映射关系
在 `config.py` 文件中的 `CELL_MAPPINGS` 字典中定义映射关系:
```python
{
(源文件sheet索引, 行, 列): (目标文件sheet索引, 行, 列)
}
```

### 修改公司和银行列表
编辑 `config.py` 文件中的 `COMPANY_OPTIONS` 和 `BANK_OPTIONS` 列表即可添加或删除选项。

## 常见问题解决

### 程序启动失败
- 检查 Python 版本是否兼容 (3.7+)
- 确认所有依赖已正确安装
- 检查模板文件是否存在于指定路径

### 转换后的文件格式不正确
- 确认模板文件格式无误
- 检查输入文件是否符合要求的格式
- 确认映射关系配置正确

### 无法识别日期
如果程序无法正确识别日期格式,可能需要在 `app.py` 的 `process_file` 方法中添加更多的日期解析逻辑。

## 项目结构

```
excel_converter/
├── app.py # 主应用程序代码
├── config.py # 配置文件
├── main.py # 入口点
├── requirements.txt # 依赖列表
├── run.bat # Windows 运行脚本
├── README.md # 项目说明
├── template.xlsx # Excel模板文件
└── test_data/ # 测试数据目录
```

## 维护和扩展

### 添加新功能
1. 尽量将配置参数放在 `config.py` 文件中
2. 保持用户界面简洁直观
3. 添加适当的错误处理和用户反馈

### 代码维护
- 定期更新依赖包版本
- 如果更改了核心功能,请更新文档
- 考虑添加单元测试以确保功能正常

+ 1051
- 0
app.py
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


+ 86
- 0
bank_options.json Visa fil

@@ -0,0 +1,86 @@
[
{
"employee_name": "杜云",
"bank_name": "ゆうちょ銀行",
"branch_account": "一三八(普通)2192640",
"account_holder": "杜云 ト ウン"
},
{
"employee_name": "範慶博",
"bank_name": "三井住友銀行",
"branch_account": "新宿通支店661(普通)8324403",
"account_holder": "範慶博 ハン ケイハク"
},
{
"employee_name": "龚丽犀",
"bank_name": "三井住友銀行",
"branch_account": "武蔵関支店(普通)6992252",
"account_holder": "龚丽犀 キヨウ レイサイ"
},
{
"employee_name": "劉紅軍",
"bank_name": "三菱UFJ銀行",
"branch_account": "西川口支店(289)(普通)1409435",
"account_holder": "劉紅軍 リュウ コウグン"
},
{
"employee_name": "欧阳冠英",
"bank_name": "楽天銀行",
"branch_account": "〇五八(普通)9357044",
"account_holder": "欧阳 冠英 オウヤン グアンイン"
},
{
"employee_name": "孙文岱",
"bank_name": "ゆうちょ銀行",
"branch_account": "一三八店(普通)1280916",
"account_holder": "孙文岱 ソン ブンタイ"
},
{
"employee_name": "王磊",
"bank_name": "三井住友銀行",
"branch_account": "町屋支店(228)(普通)6919966",
"account_holder": "王磊 オウ ライ"
},
{
"employee_name": "魏强",
"bank_name": "ゆうちょ銀行",
"branch_account": "〇一八(普通)8654986",
"account_holder": "魏强 ギ キョウ"
},
{
"employee_name": "吴桐宇",
"bank_name": "三菱UFJ銀行",
"branch_account": "心斎橋支店(031)(普通)0391875",
"account_holder": "吴桐宇 ゴ トンウ"
},
{
"employee_name": "朱文娟",
"bank_name": "楽天銀行",
"branch_account": "フーガ支店(220)(普通)1943194",
"account_holder": "朱文娟 シュ ブンケン"
},
{
"employee_name": "查干",
"bank_name": "りそな銀行",
"branch_account": "竹ノ塚支店(605)(普通) 4978660",
"account_holder": "査干 チャ カン"
},
{
"employee_name": "徐芮",
"bank_name": "三菱東京UFJ銀行",
"branch_account": "(普通)0314135",
"account_holder": "徐芮 ジョ リ"
},
{
"employee_name": "郑博",
"bank_name": "三井住友銀行",
"branch_account": "白山支店(228)(普通)6836322",
"account_holder": "鄭博 テイ ハク"
},
{
"employee_name": "周喻",
"bank_name": "三菱UFJ銀行",
"branch_account": "日暮里之店(180)(普通)0281831",
"account_holder": "周喻 シュウ ユ"
}
]

+ 32
- 0
build.py Visa fil

@@ -0,0 +1,32 @@
import os
import subprocess
import sys

def build_executable():
# Ensure we're in the project root directory
os.chdir(os.path.dirname(os.path.abspath(__file__)))
# Install required packages using uv
print("Installing required packages...")
subprocess.run(["uv", "pip", "install", "-r", "requirements.txt"], check=True)
# Build the executable using PyInstaller
print("Building executable...")
subprocess.run([
"pyinstaller",
"--name=ExcelConverter",
"--onefile",
"--windowed",
"--add-data=employee_info.json;.",
"--add-data=company_options.json;.",
"--add-data=bank_options.json;.",
"--add-data=template.xlsx;.",
"--add-data=config.py;.",
"app.py"
], check=True)
print("Build completed successfully!")
print("The executable can be found in the 'dist' directory.")

if __name__ == "__main__":
build_executable()

+ 1
- 0
company_options.json Visa fil

@@ -0,0 +1 @@
[]

+ 160
- 0
config.py Visa fil

@@ -0,0 +1,160 @@
# Excel 转换工具配置文件
import os
import json

# 模板文件路径
TEMPLATE_PATH = "template.xlsx"

# 配置文件路径
CONFIG_DIR = os.path.dirname(os.path.abspath(__file__))
EMPLOYEE_CONFIG_PATH = os.path.join(CONFIG_DIR, "employee_info.json")
COMPANY_CONFIG_PATH = os.path.join(CONFIG_DIR, "company_options.json") # 保留兼容性
BANK_CONFIG_PATH = os.path.join(CONFIG_DIR, "bank_options.json") # 保留兼容性

# 单元格映射关系(源文件位置 -> 目标文件位置)
# 格式:{(源文件sheet索引, 行, 列): (目标文件sheet索引, 行, 列)}
CELL_MAPPINGS = {
# 第一个Sheet的映射关系
(0, 3, 3): (0, 2, 5), # C3 -> E2 氏名
(0, 3, 4): (0, 3, 5), # D3 -> E3 労働日数
(0, 3, 2): (0, 3, 3), # B3 -> C3 ID
(0, 3, 5): (0, 6, 4), # E3 -> D6 基本給
(0, 3, 6): (0, 7, 4), # F3 -> D7 職務給
(0, 3, 7): (0, 8, 4), # G3 -> D8 資格手当
(0, 3, 8): (0, 9, 4), # H3 -> D9 住宅手当
(0, 3, 9): (0, 10, 4), # I3 -> D10 能力手当
(0, 3, 10): (0, 11, 4), # J3 -> D11 通勤手当
(0, 3, 11): (0, 12, 4), # K3 -> D12 残業(固定)
(0, 3, 12): (0, 13, 4), # L3 -> D13
(0, 3, 13): (0, 14, 4), # M3 -> D14
(0, 3, 15): (0, 17, 4), # O3 -> D17
(0, 3, 16): (0, 18, 4), # P3 -> D18
(0, 3, 17): (0, 19, 4), # Q3 -> D19
(0, 3, 18): (0, 20, 4), # R3 -> D20
(0, 3, 19): (0, 21, 4), # S3 -> D21
(0, 3, 20): (0, 22, 4), # T3 -> D22
(0, 3, 25): (0, 25, 5), # Y3 -> E25
}

# 自定义值的单元格位置
CUSTOM_CELLS = {
"company": (0, 2, 3), # C2 - 所属公司
"bank": (0, 29, 2), # B29 - 转账银行
"other": (0, 2, 6), # F2 - 其他信息
}

# 文件名提取信息
FILENAME_CONFIG = {
"name_cell": (0, 3, 3), # Sheet1 的 C3 - 员工姓名
"year_month_cell": (1, 4, 2), # Sheet2 的 B4 - 年月信息
}

# 第二个Sheet的命名格式
SHEET2_NAME_FORMAT = "{month}月勤怠一覧"

# 输出文件名格式
OUTPUT_FILENAME_FORMAT = "{year}年{month}月份給料明細書-{name}.xlsx"

# 默认选项列表(仅在配置文件不存在时使用)
DEFAULT_COMPANY_OPTIONS = []

# 银行信息默认结构 - 扩展为字典格式,包含更多字段
DEFAULT_BANK_OPTIONS = [
# 银行信息示例:
# {
# "employee_name": "田中太郎", # 员工姓名
# "bank_name": "三井住友銀行", # 银行名称
# "branch_account": "新宿通支店661 普通 8324403", # 支店和账号
# "account_holder": "田中太郎" # 账户持有人
# }
]

# 员工信息默认结构 - 合并公司和银行信息
DEFAULT_EMPLOYEE_INFO = [
# 员工信息示例:
# {
# "employee_name": "田中太郎", # 员工姓名
# "bank_name": "三井住友銀行", # 银行名称
# "branch_account": "新宿通支店661 普通 8324403", # 支店和账号
# "account_holder": "田中太郎", # 账户持有人
# "company_name": "SPD株式会社" # 所属公司
# }
]

# 加载员工信息
def load_employee_info():
# 检查是否存在员工信息配置
if os.path.exists(EMPLOYEE_CONFIG_PATH):
try:
with open(EMPLOYEE_CONFIG_PATH, 'r', encoding='utf-8') as f:
employee_info = json.load(f)
except Exception as e:
print(f"加载员工信息出错: {e}")
employee_info = DEFAULT_EMPLOYEE_INFO
else:
# 如果不存在,尝试从旧的配置中导入
employee_info = []
company_options = []
bank_options = []
# 加载旧的公司配置
if os.path.exists(COMPANY_CONFIG_PATH):
try:
with open(COMPANY_CONFIG_PATH, 'r', encoding='utf-8') as f:
company_options = json.load(f)
except Exception as e:
print(f"加载公司配置出错: {e}")
# 加载旧的银行配置
if os.path.exists(BANK_CONFIG_PATH):
try:
with open(BANK_CONFIG_PATH, 'r', encoding='utf-8') as f:
bank_options = json.load(f)
# 兼容旧版本:如果是字符串列表,转换为字典
if bank_options and isinstance(bank_options[0], str):
new_bank_options = []
for bank_name in bank_options:
new_bank_options.append({
"employee_name": "",
"bank_name": bank_name,
"branch_account": "",
"account_holder": ""
})
bank_options = new_bank_options
except Exception as e:
print(f"加载银行配置出错: {e}")
# 合并旧配置
if bank_options and isinstance(bank_options[0], dict):
for bank_option in bank_options:
employee_name = bank_option.get("employee_name", "")
if employee_name: # 如果有员工姓名,则添加为一条记录
employee_info.append({
"employee_name": employee_name,
"bank_name": bank_option.get("bank_name", ""),
"branch_account": bank_option.get("branch_account", ""),
"account_holder": bank_option.get("account_holder", ""),
"company_name": company_options[0] if company_options else ""
})
# 保存新的员工信息
save_options(EMPLOYEE_CONFIG_PATH, employee_info)
return employee_info

# 保存选项
def save_options(config_path, options):
try:
with open(config_path, 'w', encoding='utf-8') as f:
json.dump(options, f, ensure_ascii=False, indent=4)
return True
except Exception as e:
print(f"保存配置出错: {e}")
return False

# 加载选项
EMPLOYEE_INFO = load_employee_info()
COMPANY_OPTIONS = [info.get("company_name") for info in EMPLOYEE_INFO if info.get("company_name")]
COMPANY_OPTIONS = list(set(COMPANY_OPTIONS)) # 去重
BANK_OPTIONS = [info for info in EMPLOYEE_INFO] # 兼容性:旧代码可能仍使用BANK_OPTIONS

+ 156
- 0
employee_info.json Visa fil

@@ -0,0 +1,156 @@
[
{
"employee_name": "杜云",
"bank_name": "ゆうちょ銀行",
"branch_account": "一三八(普通)2192640",
"account_holder": "杜云 ト ウン",
"company_name": "SPD株式会社"
},
{
"employee_name": "範慶博",
"company_name": "開元株式会社",
"bank_name": "三井住友銀行",
"branch_account": "新宿通支店661(普通)8324403",
"account_holder": "範慶博 ハン ケイハク"
},
{
"employee_name": "龚丽犀",
"bank_name": "三井住友銀行",
"branch_account": "武蔵関支店(普通)6992252",
"account_holder": "龚丽犀 キヨウ レイサイ",
"company_name": "SPD株式会社"
},
{
"employee_name": "劉紅軍",
"bank_name": "三菱UFJ銀行",
"branch_account": "西川口支店(289)(普通)1409435",
"account_holder": "劉紅軍 リュウ コウグン",
"company_name": "SPD株式会社"
},
{
"employee_name": "欧阳冠英",
"bank_name": "楽天銀行",
"branch_account": "〇五八(普通)9357044",
"account_holder": "欧阳 冠英 オウヤン グアンイン",
"company_name": "SPD株式会社"
},
{
"employee_name": "孙文岱",
"bank_name": "ゆうちょ銀行",
"branch_account": "一三八店(普通)1280916",
"account_holder": "孙文岱 ソン ブンタイ",
"company_name": "SPD株式会社"
},
{
"employee_name": "王磊",
"bank_name": "三井住友銀行",
"branch_account": "町屋支店(228)(普通)6919966",
"account_holder": "王磊 オウ ライ",
"company_name": "SPD株式会社"
},
{
"employee_name": "魏强",
"bank_name": "ゆうちょ銀行",
"branch_account": "〇一八(普通)8654986",
"account_holder": "魏强 ギ キョウ",
"company_name": "SPD株式会社"
},
{
"employee_name": "吴桐宇",
"bank_name": "三菱UFJ銀行",
"branch_account": "心斎橋支店(031)(普通)0391875",
"account_holder": "吴桐宇 ゴ トンウ",
"company_name": "SPD株式会社"
},
{
"employee_name": "朱文娟",
"bank_name": "楽天銀行",
"branch_account": "フーガ支店(220)(普通)1943194",
"account_holder": "朱文娟 シュ ブンケン",
"company_name": "SPD株式会社"
},
{
"employee_name": "查干",
"company_name": "嘉年華株式会社",
"bank_name": "りそな銀行",
"branch_account": "竹ノ塚支店(605)(普通) 4978660",
"account_holder": "査干 チャ カン"
},
{
"employee_name": "徐芮",
"bank_name": "三菱東京UFJ銀行",
"branch_account": "(普通)0314135",
"account_holder": "徐芮 ジョ リ",
"company_name": "SPD株式会社"
},
{
"employee_name": "郑博",
"bank_name": "三井住友銀行",
"branch_account": "白山支店(228)(普通)6836322",
"account_holder": "鄭博 テイ ハク",
"company_name": "SPD株式会社"
},
{
"employee_name": "周喻",
"company_name": "嘉年華株式会社",
"bank_name": "三菱UFJ銀行",
"branch_account": "日暮里之店(180)(普通)0281831",
"account_holder": "周喻 シュウ ユ"
},
{
"employee_name": "呉恵宜",
"company_name": "嘉年華株式会社",
"bank_name": "",
"branch_account": "",
"account_holder": ""
},
{
"employee_name": "西本真衣",
"company_name": "嘉年華株式会社",
"bank_name": "",
"branch_account": "",
"account_holder": ""
},
{
"employee_name": "柳顺子",
"company_name": "嘉年華株式会社",
"bank_name": "",
"branch_account": "",
"account_holder": ""
},
{
"employee_name": "宓存行",
"company_name": "嘉年華株式会社",
"bank_name": "",
"branch_account": "",
"account_holder": ""
},
{
"employee_name": "盖雪梅",
"company_name": "嘉年華株式会社",
"bank_name": "",
"branch_account": "",
"account_holder": ""
},
{
"employee_name": "王玥倩",
"company_name": "嘉年華株式会社",
"bank_name": "",
"branch_account": "",
"account_holder": ""
},
{
"employee_name": "周锋",
"company_name": "嘉年華株式会社",
"bank_name": "",
"branch_account": "",
"account_holder": ""
},
{
"employee_name": "西本智子",
"company_name": "SPD株式会社",
"bank_name": "",
"branch_account": "",
"account_holder": ""
}
]

+ 36
- 0
main.py Visa fil

@@ -0,0 +1,36 @@
import os
import sys
import tkinter as tk
from app import ExcelConverterApp
import openpyxl
from copy import copy

def main():
"""
Main entry point for the Excel Converter application.
"""
# Create the main window
root = tk.Tk()
root.title("日本工资明细转换工具")
# Set window icon if available
if os.path.exists("icon.ico"):
root.iconbitmap("icon.ico")
# Create and run the application
app = ExcelConverterApp(root)
# Center window on screen
window_width = 800
window_height = 600
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
x = (screen_width - window_width) // 2
y = (screen_height - window_height) // 2
root.geometry(f"{window_width}x{window_height}+{x}+{y}")
# Run the application
root.mainloop()

if __name__ == "__main__":
main()

+ 5
- 0
requirements.txt Visa fil

@@ -0,0 +1,5 @@
pandas>=2.0.0
openpyxl>=3.1.0
xlrd>=2.0.0
xlwings>=0.30.0
pyinstaller>=6.0.0

+ 4
- 0
run.bat Visa fil

@@ -0,0 +1,4 @@
@echo off
echo 启动工资明细转换工具...
python app.py
pause

Binär
template.xlsx Visa fil


Laddar…
Avbryt
Spara