I can't send email with pywin32 & excel

34 Views Asked by At

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

    1. Unit to set the path to save Excel file
    1. Load original file
    1. Load email info
    1. Converting Excel to PDF
    1. 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
0

There are 0 best solutions below