I have the following code that reads an existing Excel file, change PDF files, and send emails. Among those things sending emails fuction doesn't work. How can I solve it?
here is How macro works
-
- Unit to set the path to save Excel file
-
- Load original file
-
- Load email info
-
- Converting Excel to PDF
-
- Send Emails with PDF Files
number 5 doesn't works :(
import tkinter as tk
from tkinter import *
from tkinter import filedialog
from tkinter import messagebox
import pandas as pd
import win32com.client.gencache
import os
from PyPDF2 import PdfReader, PdfWriter
# 현재 월을 가져오는 라이브러리
import datetime
import time
# 이메일 전송을 위한 PKG
from smtplib import SMTP_SSL
from os.path import basename
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import COMMASPACE, formatdate
from email.header import Header
# 현재 날짜와 시간 가져오기
current_date = datetime.datetime.now()
# 현재의 년도 가져오기
current_year = current_date.year
# 현재의 월 가져오기
current_month = current_date.month
def AutoPay(RFolder, RFile, MData, mTitle, mMain, mYN):
global current_date, current_year, current_month
if mYN == True :
# 내 메일 정보 가져오기
dfmail = pd.read_excel(MData)
# 메일 필요 정보 초기화하기
# 세션 생성
# s = smtplib.SMTP('smtp.gmail.com', 587) # gmail 이메일 서버
s = SMTP_SSL('smtp.daum.net', 465) # 다음 이메일 서버
# s = smtplib.SMTP_SSL('smtp.naver.com', 587) # 네이버 이메일 서버
# TLS 보안 시작
# s.starttls()
# 로그인 인증
s.login(dfmail['mail'][0], dfmail['m_pw'][0])
# 필요 데이터 읽어오기 (판다스)
dfR = pd.read_excel(RFile, sheet_name = 1)
dfM = pd.read_excel(RFile, sheet_name = 'Master')
dfP = pd.read_excel(RFile, sheet_name = 'Private')
dfR = dfR.merge(dfP, on = '순번')
# None 값 0으로 대체하기
dfR = dfR.fillna(0)
# 필요 데이터 읽어오기 (pywin32, Format)
excel = win32com.client.gencache.EnsureDispatch("Excel.Application")
excel.Visible = False
wb = excel.Workbooks.Open(RFile)
ws = wb.Sheets('Format')
# 저장할 폴더 지정
FPath = os.path.join(RFolder, f'{current_year}년_{current_month}월_급여명세서')
if not os.path.exists(FPath):
os.makedirs(FPath)
def open_folder_in_explorer(folder_path):
try:
os.startfile(folder_path)
except Exception as e:
print("폴더를 열 수 없습니다:", e)
# 파일 탐색기에서 폴더 열기
open_folder_in_explorer(FPath)
# 인원별 반복문
for index, row in dfR.iterrows():
# 첫번째 인원 사명, 이름 정보 입력하기
ws.Cells(2,2).Value = '지엔아이티'
ws.Cells(2,4).Value = row['이름_x']
# 포멧 양식의 값 초기화 하기
ws.Range("A5:D20").Value = ''
# 지급 / 공제 값 입력하기
pnum = 5
cnum = 5
for cName in dfR:
Sep = dfM['구분'].where(dfM['항목'] == cName).dropna()
# 구분자가 값이 없을때
if Sep.empty:
if cName == '비고':
ws.Cells(26,1).Value = row[cName]
# 구분이 지급일 경우
elif Sep.iloc[0] =="지급" and row[cName] > 0:
ws.Cells(pnum,1).Value = cName
ws.Cells(pnum,2).Value = row[cName]
pnum += 1
# 구분이 공제일 경우
elif Sep.iloc[0] =="공제" and row[cName] > 0:
ws.Cells(cnum,3).Value = cName
ws.Cells(cnum,4).Value = row[cName]
cnum += 1
# PDF 파일로 파일 저장
FName = '지엔아이티_' + str(row['이름_x']) + '_' + str(current_year) +'년_' + str(current_month) + '월_' + '급여명세서.pdf'
pwFName = '지엔아이티_' + str(row['이름_x']) + '_' + str(current_year) +'년_' + str(current_month) + '월_' + '급여명세서_잠금파일.pdf'
ws.ExportAsFixedFormat(0, os.path.join(FPath,FName) )
try:
with open(os.path.join(FPath, FName), "rb") as in_file:
input_pdf = PdfReader(in_file)
output_pdf = PdfWriter()
for page in input_pdf.pages:
output_pdf.add_page(page)
output_pdf.encrypt(str(row['PW']))
with open(os.path.join(FPath, pwFName), "wb") as out_file:
output_pdf.write(out_file)
except Exception as e:
messagebox.showerror("오류", "PDF 파일 암호화 과정 중 오류가 발생했습니다. 관리자에게 문의하세요.")
print("PDF 파일 암호화 중 오류 발생:", e)
if mYN == True :
# 이메일 발송
msg = MIMEMultipart()
msg['Subject'] = mTitle
msg['From'] = dfmail['mail'][0]
msg['To'] = row['e-mail']
msg.attach(MIMEText(mMain))
f= os.path.join(FPath,pwFName)
fil = open(f, "rb")
part = MIMEApplication(
fil.read(),
Name=basename(f)
)
# After the file is closed
# 파일 이름을 UTF-8로 인코딩하여 설정
part.add_header('Content-Disposition', 'attachment', filename = Header(basename(f), 'utf-8').encode())
msg.attach(part)
# 메일 보내기
s.sendmail(dfmail['mail'][0], row['e-mail'], msg.as_string())
if mYN == True :
# 이메일 세션 종료
s.quit()
try:
# Excel 파일 닫기
wb.Close(False)
# Excel 인스턴스 종료
excel.Quit()
# 시간 지연 추가
time.sleep(1) # 1초 대기
except Exception as e:
print("Excel 관련 오류 발생:", e)
# 폴더경로 바꾸는 버튼을 눌렀을때 업데이트
def onClick(i):
folder_selected = filedialog.askdirectory()
foldName = folder_selected.replace('/','\\')
if folder_selected: # 사용자가 폴더를 선택한 경우에만 업데이트
lbPath[i].config(text=folder_selected) # 선택한 폴더 경로 업데이트
df.Detail[i] = foldName
writer = pd.ExcelWriter('Master.xlsx', engine='xlsxwriter')
df.to_excel(writer, sheet_name='Folder', index=False)
dff.to_excel(writer, sheet_name='File',index=False)
writer.close()
return
# 파일경로와 이름 바꾸는 버튼을 눌렀을때 업데이트
def onClickf(i):
folder_selected = filedialog.askopenfile()
fileName = folder_selected.name.replace('/','\\')
lbPathf[i].config(text=fileName)
dff.Detail[i] = fileName
writer = pd.ExcelWriter('Master.xlsx', engine='xlsxwriter')
df.to_excel(writer, sheet_name='Folder', index=False)
dff.to_excel(writer, sheet_name='File',index=False)
writer.close()
return
# GUI 구성
win = Tk()
win.geometry("580x490")
win.title('자동 개별 급여 명세표 발송기')
df= pd.read_excel("Master/Master.xlsx",sheet_name="Folder", engine = "openpyxl")
dff= pd.read_excel("Master/Master.xlsx",sheet_name="File", engine = "openpyxl")
# 프로그램 Process 안내문구
frame0 = Frame(win, pady=5)
frame0.pack()
Label(frame0, text = '\n[ 프로그램 Process ]\n\n 1. 개별 파일 만들기 → 2. (수정 시) 파일 재생성 or 이메일 전송 → 3. 프로그램 종료', width = 70, anchor = 'w').pack()
# 폴더 경로 설정 GUI
frame1 = Frame(win, pady=5)
frame1.pack() # 만약, GUI 내에 파일 저장 경로를 보여지게 하고 싶다면 주석처리 해제
lbName = []
lbPath = []
btnPath = []
for i in df.index:
lbName.append(Label(frame1, text=df.Item[i], width=10))
lbName[i].grid(row=i, column=0, sticky=W)
lbPath.append(Label(frame1, text=df.Detail[i], width=50, anchor = 'w'))
lbPath[i].grid(row=i, column=1, sticky=W)
btnPath.append(Button(frame1, text="Change Path", width=10,command=lambda i=i: onClick(i)))
btnPath[i].grid(row=i, column=2, sticky=W)
# 파일 경로 설정 GUI
frame2 = Frame(win, pady=5)
frame2.pack() # 만약, GUI 내에 파일 저장 경로를 보여지게 하고 싶다면 주석처리 해제
lbNamef = []
lbPathf = []
btnPathf = []
for i in dff.index:
lbNamef.append(Label(frame2, text=dff.Item[i], width=10))
lbNamef[i].grid(row=i, column=0, sticky=W)
lbPathf.append(Label(frame2, text=dff.Detail[i], width=50, anchor = 'w'))
lbPathf[i].grid(row=i, column=1, sticky=W)
btnPathf.append(Button(frame2, text="Change Path", width=10,command=lambda i=i: onClickf(i)))
btnPathf[i].grid(row=i, column=2, sticky=W)
def check_excel_file(path):
"""파일이 유효한 엑셀 파일인지 확인합니다."""
if not path.lower().endswith('.xlsx'):
messagebox.showerror("오류", "유효한 엑셀 파일이 아닙니다. 엑셀 파일을 선택하세요.\n(Master File에서 파일 경로 수정 가능)")
return False
try:
pd.read_excel(path, engine="openpyxl") # 엑셀 파일인지 확인
return True
except Exception as e:
messagebox.showerror("오류", f"파일을 열 수 없습니다: {e}")
return False
# 메일 제목 Label
frame3 = Frame(win, pady=7)
frame3.pack()
Label(frame3, text = '메일 제목', width = 70, anchor = 'w').pack()
# 메일 제목 TextBox
frame4 = Frame(win)
frame4.pack()
txtMT = Text(frame4, width = 70, height = 1, padx = 5, pady=5)
txtMT.insert(INSERT, str(current_month) + '월 급여 내역 송부의 건')
txtMT.pack()
# 메일 본문 Label
frame5 = Frame(win, pady=7)
frame5.pack()
Label(frame5, text = '메일 본문', width = 70, anchor = 'w').pack()
# 메일 본문 TextBox
frame6 = Frame(win)
frame6.pack()
txtMM = Text(frame6, width = 70, height = 5, padx = 5, pady=5)
# 현재 날짜와 시간 가져오기
current_date = datetime.datetime.now()
txtMM.insert(INSERT, '안녕하세요. (주)지엔아이티입니다.\n' + str(current_month) + '월 급여 내역을 유첨 파일과 같이 송부 드립니다.\n명세서 파일 비밀번호는 본인의 주민등록번호 뒤 7자리입니다.\n감사합니다.')
txtMM.pack()
# 안내문 or 공지문
frame7 = Frame(win, pady=2)
frame7.pack()
Label(frame7, text = "(본문과 제목의 '월'은 매달 자동으로 입력되므로 수정하지 않으셔도 됩니다.)", width = 70, anchor = 'w').pack()
label = tk.Label(frame7, text="< 진행 상황 >", height = 2, padx = 5, pady = 5)
label.pack(side=tk.BOTTOM)
def send_email():
# 파일 경로가 유효한 엑셀 파일인지 확인
if not all(check_excel_file(path['text']) for path in lbPathf):
return
# 작업 중 텍스트 표시
label.config(text="작업 중...", fg="red", font="Helvetica")
win.update()
# 메일 본문 및 메일 제목 가져오기
mail_subject = txtMT.get(1.0, END).strip() # 메일 제목 가져오기
mail_body = txtMM.get(1.0, END).strip() # 메일 본문 가져오기
# 메일 본문이나 메일 제목이 비어있는지 확인
if not mail_subject or not mail_body:
messagebox.showerror("오류", "메일 제목 또는 메일 본문이 비어있습니다. 메일을 발송할 수 없습니다.")
return
# 이메일 발송 전 확인 창 띄우기
confirmation = messagebox.askyesno("이메일 발송 확인", "정말 이대로 이메일을 발송하시겠습니까?\n(예를 누를시, 취소가 불가능합니다!)")
if not confirmation:
return # 사용자가 "아니오"를 선택한 경우 작업 취소
# 이메일 발송 로직 추가
# AutoPay 함수 호출 또는 다른 이메일 발송 로직 작성
AutoPay(lbPath[0]['text'], lbPathf[0]['text'], lbPathf[1]['text'], mail_subject, mail_body, True)
# 메일 전송 완료 메시지를 표시
messagebox.showinfo("메일 전송 완료", "메일이 성공적으로 전송되었습니다.")
# 작업 완료 텍스트 표시
label.config(text="전송 완료", pady=7, fg="green", font="Helvetica")
# 버튼 숨기기
btnSM.grid_forget()
btnMF.grid_forget() # 재생성 버튼 숨기기
btnMFRe.grid_forget() # 메일 전송 버튼 숨기기
# 종료 버튼 생성
btn_exit = Button(frame7, text="프로그램 닫기", width = 20, pady=10, height=1, command=win.destroy)
btn_exit.pack(side=tk.BOTTOM)
def create_file():
# 파일 경로가 유효한 엑셀 파일인지 확인
if not all(check_excel_file(path['text']) for path in lbPathf):
return
# 작업 중 텍스트 표시
label.config(text="작업 중...", fg="red", font="Helvetica")
win.update()
AutoPay(lbPath[0]['text'], lbPathf[0]['text'], lbPathf[1]['text'], txtMT.get(1.0,END), txtMM.get(1.0,END), False)
# 메일 전송 완료 메시지를 표시
messagebox.showinfo("파일 생성 완료", "파일이 성공적으로 생성되었습니다.")
# 작업 완료 텍스트 표시
label.config(text="작업 완료", fg="green", font="Helvetica")
# 버튼 숨기기
btnMF.grid_forget()
# "재생성" 버튼 생성
btnMFRe.grid(row=0, column=0, sticky=W)
# "개별 메일 보내기" 버튼 보이기
btnSM.grid(row=0, column=1, sticky=W)
# 실행 버튼 GUI
frame7 = Frame(win, pady=5) # Row of buttons
frame7.pack()
# Keywork Change 버튼
btnMF = Button(frame7, text="개별 파일 만들기", width = 30, pady=5, command=create_file)
btnMF.grid(row=0, column=0, sticky=W)
btnSM = Button(frame7, text="개별 메일 보내기 (확정)", width = 30, pady=5, height=2, command=send_email, fg="red")
# 재생성 버튼 (초기에는 숨김)
btnMFRe = Button(frame7, text="개별 파일 재생성하기 \n (수정되었다면 덮어쓰기됩니다.)", width=30, height=2, pady=5, command=create_file)
win.mainloop()
Here is error code.
`Exception in Tkinter callback Traceback (most recent call last):
ws.ExportAsFixedFormat(0, os.path.join(FPath,FName) )
File "C:\Users\ZNIT\AppData\Local\Temp\gen_py\3.12\00020813-0000-0000-C000-000000000046x0x1x9\_Worksheet.py", line 114, in ExportAsFixedFormat
return self._oleobj_.InvokeTypes(3175, LCID, 1, (24, 0), ((3, 1), (12, 17), (12, 17), (12, 17), (12, 17), (12, 17), (12, 17), (12, 17), (12, 17), (12, 17)),Type
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
pywintypes.com_error: (-2147352567, '예외가 발생했습니다.', (0, 'Microsoft Excel', '문서가 저장되지 않았습니다.', 'xlmain11.chm', 0, -2147018887), None)`
Have a good day
What I tried
- Check the absolute path
- Shut down Excel
- and so on...
What I expected
- Send Email successfuly