日本工资明细转换工具
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

config.py 8.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. # -*- coding: utf-8 -*-
  2. # Excel 转换工具配置文件
  3. import os
  4. import json
  5. import sys
  6. import logging
  7. # 模板文件路径
  8. TEMPLATE_PATH = "template.xlsx"
  9. # 配置文件路径
  10. # 检测是否为打包环境
  11. if getattr(sys, 'frozen', False):
  12. # PyInstaller打包后的环境
  13. CONFIG_DIR = os.path.dirname(sys.executable)
  14. else:
  15. # 开发环境
  16. CONFIG_DIR = os.path.dirname(os.path.abspath(__file__))
  17. EMPLOYEE_CONFIG_PATH = os.path.join(CONFIG_DIR, "employee_info.json")
  18. COMPANY_CONFIG_PATH = os.path.join(CONFIG_DIR, "company_options.json") # 保留兼容性
  19. BANK_CONFIG_PATH = os.path.join(CONFIG_DIR, "bank_options.json") # 保留兼容性
  20. # 设置日志记录
  21. logging.basicConfig(
  22. level=logging.DEBUG,
  23. format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
  24. handlers=[
  25. logging.FileHandler(os.path.join(CONFIG_DIR, "config.log")),
  26. logging.StreamHandler()
  27. ]
  28. )
  29. logger = logging.getLogger("config")
  30. # 单元格映射关系(源文件位置 -> 目标文件位置)
  31. # 格式:{(源文件sheet索引, 行, 列): (目标文件sheet索引, 行, 列)}
  32. # 注意:源文件行索引为0表示从C3开始的行
  33. CELL_MAPPINGS = {
  34. # 第一个Sheet的映射关系
  35. (0, 0, 3): (0, 2, 5), # C3 -> E2 氏名
  36. (0, 0, 4): (0, 3, 5), # D3 -> E3 労働日数
  37. (0, 0, 2): (0, 3, 3), # B3 -> C3 ID
  38. (0, 0, 5): (0, 6, 4), # E3 -> D6 基本給
  39. (0, 0, 6): (0, 7, 4), # F3 -> D7 職務給
  40. (0, 0, 7): (0, 8, 4), # G3 -> D8 資格手当
  41. (0, 0, 8): (0, 9, 4), # H3 -> D9 住宅手当
  42. (0, 0, 9): (0, 10, 4), # I3 -> D10 能力手当
  43. (0, 0, 10): (0, 11, 4), # J3 -> D11 通勤手当
  44. (0, 0, 11): (0, 12, 4), # K3 -> D12 残業(固定)
  45. (0, 0, 12): (0, 13, 4), # L3 -> D13 週末・祝日出勤手当
  46. (0, 0, 13): (0, 14, 4), # M3 -> D14 時間外EC操作
  47. (0, 0, 15): (0, 17, 4), # O3 -> D17
  48. (0, 0, 16): (0, 18, 4), # P3 -> D18
  49. (0, 0, 17): (0, 19, 4), # Q3 -> D19
  50. (0, 0, 18): (0, 20, 4), # R3 -> D20
  51. (0, 0, 19): (0, 21, 4), # S3 -> D21
  52. (0, 0, 20): (0, 22, 4), # T3 -> D22
  53. (0, 0, 25): (0, 25, 5), # Y3 -> E25
  54. }
  55. # 自定义值的单元格位置
  56. CUSTOM_CELLS = {
  57. "company": (0, 2, 3), # C2 - 所属公司
  58. "bank": (0, 29, 2), # B29 - 转账银行
  59. "other": (0, 2, 6), # F2 - 其他信息
  60. }
  61. # 文件名提取信息
  62. FILENAME_CONFIG = {
  63. "name_cell": (0, 3, 3), # Sheet1 的 C3 - 员工姓名
  64. "year_month_cell": (1, 4, 2), # Sheet2 的 B4 - 年月信息
  65. }
  66. # 第二个Sheet的命名格式
  67. SHEET2_NAME_FORMAT = "{month}月勤怠一覧"
  68. # 输出文件名格式
  69. OUTPUT_FILENAME_FORMAT = "{year}年{month}月份給料明細書-{name}.xlsx"
  70. # 默认选项列表(仅在配置文件不存在时使用)
  71. DEFAULT_COMPANY_OPTIONS = []
  72. # 银行信息默认结构 - 扩展为字典格式,包含更多字段
  73. DEFAULT_BANK_OPTIONS = [
  74. # 银行信息示例:
  75. # {
  76. # "employee_name": "田中太郎", # 员工姓名
  77. # "bank_name": "三井住友銀行", # 银行名称
  78. # "branch_account": "新宿通支店661 普通 8324403", # 支店和账号
  79. # "account_holder": "田中太郎" # 账户持有人
  80. # }
  81. ]
  82. # 员工信息默认结构 - 合并公司和银行信息
  83. DEFAULT_EMPLOYEE_INFO = [
  84. # 员工信息示例:
  85. # {
  86. # "employee_name": "田中太郎", # 员工姓名
  87. # "bank_name": "三井住友銀行", # 银行名称
  88. # "branch_account": "新宿通支店661 普通 8324403", # 支店和账号
  89. # "account_holder": "田中太郎", # 账户持有人
  90. # "company_name": "SPD株式会社" # 所属公司
  91. # }
  92. ]
  93. # 加载员工信息
  94. def load_employee_info():
  95. # 检查是否存在员工信息配置
  96. logger.debug("尝试从 {0} 加载员工信息".format(EMPLOYEE_CONFIG_PATH))
  97. if os.path.exists(EMPLOYEE_CONFIG_PATH):
  98. try:
  99. with open(EMPLOYEE_CONFIG_PATH, 'r', encoding='utf-8') as f:
  100. employee_info = json.load(f)
  101. logger.info("成功加载 {0} 条员工信息".format(len(employee_info)))
  102. return employee_info
  103. except Exception as e:
  104. logger.error("加载员工信息出错: {0}".format(str(e)))
  105. return DEFAULT_EMPLOYEE_INFO
  106. else:
  107. # 如果不存在,尝试从旧的配置中导入
  108. logger.warning("员工信息配置文件不存在,尝试从旧配置导入")
  109. employee_info = []
  110. company_options = []
  111. bank_options = []
  112. # 加载旧的公司配置
  113. if os.path.exists(COMPANY_CONFIG_PATH):
  114. try:
  115. with open(COMPANY_CONFIG_PATH, 'r', encoding='utf-8') as f:
  116. company_options = json.load(f)
  117. logger.info("从旧配置加载了 {0} 个公司选项".format(len(company_options)))
  118. except Exception as e:
  119. logger.error("加载公司配置出错: {0}".format(str(e)))
  120. # 加载旧的银行配置
  121. if os.path.exists(BANK_CONFIG_PATH):
  122. try:
  123. with open(BANK_CONFIG_PATH, 'r', encoding='utf-8') as f:
  124. bank_options = json.load(f)
  125. # 兼容旧版本:如果是字符串列表,转换为字典
  126. if bank_options and isinstance(bank_options[0], str):
  127. new_bank_options = []
  128. for bank_name in bank_options:
  129. new_bank_options.append({
  130. "employee_name": "",
  131. "bank_name": bank_name,
  132. "branch_account": "",
  133. "account_holder": ""
  134. })
  135. bank_options = new_bank_options
  136. logger.info("从旧配置加载了 {0} 个银行选项".format(len(bank_options)))
  137. except Exception as e:
  138. logger.error("加载银行配置出错: {0}".format(str(e)))
  139. # 合并旧配置
  140. if bank_options and isinstance(bank_options[0], dict):
  141. for bank_option in bank_options:
  142. employee_name = bank_option.get("employee_name", "")
  143. if employee_name: # 如果有员工姓名,则添加为一条记录
  144. employee_info.append({
  145. "employee_name": employee_name,
  146. "bank_name": bank_option.get("bank_name", ""),
  147. "branch_account": bank_option.get("branch_account", ""),
  148. "account_holder": bank_option.get("account_holder", ""),
  149. "company_name": company_options[0] if company_options else ""
  150. })
  151. # 保存新的员工信息
  152. save_options(EMPLOYEE_CONFIG_PATH, employee_info)
  153. logger.info("创建了新的员工信息配置,包含 {0} 条记录".format(len(employee_info)))
  154. return employee_info
  155. # 保存选项
  156. def save_options(config_path, options):
  157. try:
  158. # 确保目录存在
  159. config_dir = os.path.dirname(config_path)
  160. if not os.path.exists(config_dir):
  161. os.makedirs(config_dir)
  162. logger.info("创建目录: {0}".format(config_dir))
  163. # 保存前记录当前工作目录和文件路径
  164. logger.debug("当前工作目录: {0}".format(os.getcwd()))
  165. logger.debug("保存配置到: {0}".format(config_path))
  166. # 保存配置
  167. with open(config_path, 'w', encoding='utf-8') as f:
  168. json.dump(options, f, ensure_ascii=False, indent=4)
  169. # 验证保存是否成功
  170. if os.path.exists(config_path):
  171. logger.info("成功保存配置到 {0}".format(config_path))
  172. return True
  173. else:
  174. logger.error("保存失败,文件不存在: {0}".format(config_path))
  175. return False
  176. except Exception as e:
  177. logger.error("保存配置出错: {0}".format(str(e)), exc_info=True)
  178. return False
  179. # 加载选项
  180. try:
  181. logger.info("开始加载配置...")
  182. EMPLOYEE_INFO = load_employee_info()
  183. COMPANY_OPTIONS = [info.get("company_name") for info in EMPLOYEE_INFO if info.get("company_name")]
  184. COMPANY_OPTIONS = list(set(COMPANY_OPTIONS)) # 去重
  185. BANK_OPTIONS = [info for info in EMPLOYEE_INFO] # 兼容性:旧代码可能仍使用BANK_OPTIONS
  186. logger.info("加载了 {0} 条员工信息, {1} 个公司选项".format(len(EMPLOYEE_INFO), len(COMPANY_OPTIONS)))
  187. except Exception as e:
  188. logger.critical("配置加载失败: {0}".format(str(e)), exc_info=True)
  189. EMPLOYEE_INFO = DEFAULT_EMPLOYEE_INFO
  190. COMPANY_OPTIONS = []
  191. BANK_OPTIONS = []