張柯 劉曉光 李正雄 李金旗 賈靜
摘要:以涵蓋企業(yè)所有員工薪酬信息的Excel文件為輸入,結(jié)合電子郵件,利用C#編程語言,設(shè)計開發(fā)企業(yè)工資條自動分發(fā)程序,解決了開發(fā)過程中的一些關(guān)鍵問題,實現(xiàn)了精準(zhǔn)便捷的分發(fā)功能,大大提升財務(wù)人員工作效率,對同類軟件的開發(fā)具有一定的參考作用。
關(guān)鍵詞:C#;Excel;工資條;電子郵件;自動分發(fā)
中圖分類號:TP391.13 ? ? ?文獻(xiàn)標(biāo)識碼:A
文章編號:1009-3044(2020)28-0077-04
Abstract: With the input of an Excel file covering all employee salary information of the enterprise, and combined with E-mail,the enterprise salary bill dispatching program based on C# was designed, which solved some key points in the program development, achieved accurate and convenient dispatching, and greatly improved the efficiency of financial staff. Meanwhile, it provided the idea for further development of a similar system.
Key words: C#; Excel; salary bill; E-mail; dispatching
1 背景簡介
出于方便、及時、私密性的考慮,現(xiàn)如今多數(shù)企業(yè)選擇銀行代發(fā)的方式來支付員工薪酬。通過銀行網(wǎng)上代付業(yè)務(wù),可從企業(yè)結(jié)算賬戶直接向員工個人結(jié)算賬戶(借記卡、存折)自助發(fā)放工資[1]。
受部分企業(yè)薪酬保密制度的制約,為使員工及時了解當(dāng)月薪酬的詳細(xì)組成,無論是采用手工打印、人工分發(fā)工資條的方式,還是利用網(wǎng)絡(luò)實現(xiàn)遠(yuǎn)程逐一發(fā)放的方式,效率都很低下,且容易出現(xiàn)人為失誤導(dǎo)致泄密。當(dāng)企業(yè)人數(shù)較多時,采用上述方式,若因工作失誤發(fā)生遺漏問題,會大大增加核對的時間成本和人工成本,不利于企業(yè)降本增效。
財務(wù)管理中經(jīng)常會用到微軟Office辦公軟件套裝中的Excel電子表格軟件,而包含企業(yè)所有員工的工資條文件正是由集中部署ERP系統(tǒng)導(dǎo)出的Excel(.xlsx)文件,那么如何避免上述分發(fā)工資條方式的弊端,使工資條文件在導(dǎo)出后能夠及時地、準(zhǔn)確無誤地分發(fā)到每位員工手中,是我們要思考的問題[2-4]。
2 設(shè)計思路
以存儲當(dāng)月企業(yè)所有員工薪酬信息的工資條文件作為輸入(下文以“總工資條.xlsx”文件為例,如圖1所示,數(shù)據(jù)為測試數(shù)據(jù)),以每位員工獲取自己的薪酬信息作為輸出,考慮到時效性、準(zhǔn)確性要求,設(shè)計編寫工資條自動分發(fā)程序,對輸入文件進(jìn)行篩選,從中提取員工姓名、郵箱、薪酬組成等信息,以電子郵箱為媒介,達(dá)到短時間內(nèi)精準(zhǔn)分發(fā)的效果。
2.1 編程語言選擇
C#是一種安全的、穩(wěn)定的、簡單的,由C和C++衍生出來的面向?qū)ο蟮木幊陶Z言。它在繼承C和C++強(qiáng)大功能的同時去掉了一些復(fù)雜特性,它綜合了VB簡單的可視化操作和C++的高運(yùn)行效率,使得其在開發(fā)運(yùn)行在.NET平臺上的應(yīng)用程序時極為方便[5-6]。
該工資條自動分發(fā)程序采用C#編程語言,操作簡單,可讀性好,編程環(huán)境采用Visual Studio 2019。
2.2 功能設(shè)計
參考電子郵箱附件發(fā)送的模式,該程序主流程如圖2所示,其主要功能如下[7]:
1)處理發(fā)件人信息,可一鍵配置,可手動輸入;
2)處理輸入文件信息,提取所需數(shù)據(jù)保存成附件;
3)發(fā)送郵件;
4)顯示發(fā)送成功或失敗的信息;
5)顯示處理進(jìn)度。
2.3 界面設(shè)計
程序主界面如圖3所示,出于方便操作的考慮,設(shè)計時將主要功能以按鈕的形式體現(xiàn),通過位置的合理擺放將主界面劃分為四個區(qū)域,即發(fā)件人信息配置區(qū)、輸入文件讀取區(qū)、發(fā)送成功顯示區(qū)(上顯示窗口)和發(fā)送失敗顯示區(qū)(下顯示窗口)。
用戶只需四步即可完成每月工資條分發(fā):
1)單擊“刷新”按鈕,瀏覽并選擇“發(fā)件信息設(shè)置.xml”文件,可在主界面自動配置好用戶信息,如圖4所示。
2)單擊“瀏覽”按鈕,瀏覽并選擇“總工資條.xlsx”文件,當(dāng)主界面底部進(jìn)度條加載完畢后,表明已經(jīng)在后臺處理好輸入文件,此時在輸入文件的同級目錄下,會生成“tempDir”文件夾來存儲臨時文件,文件名格式為“姓名_郵箱.xls”,即一個個待發(fā)送的附件,如圖5所示。
3)單擊“發(fā)送”按鈕,后臺自動遍歷“tempDir”文件夾中的所有附件,提取郵箱信息并逐一發(fā)送,已發(fā)送成功的郵件,將會在主界面的上顯示窗口輸出信息,發(fā)送失敗的郵件,將會在主界面的下顯示窗口輸出信息,如圖6所示。若不存在該郵箱,會收到系統(tǒng)退信,如圖7所示,該信息不會在下顯示窗口輸出。此時在輸入文件同級目錄下,會生成以年月命名的文件夾(如“2020-07”)來存儲已發(fā)送成功的附件,會生成“errorDir”文件夾來存儲發(fā)送失敗的附件,會刪除臨時文件夾“tempDir”。
4)根據(jù)主界面的下顯示窗口中的輸出信息,可判斷出郵件發(fā)送失敗的原因,通過檢查“errorDir”文件夾中的附件來修正錯誤(如發(fā)現(xiàn)郵箱有誤,可直接修改附件名稱中的郵箱信息),錯誤修正后單擊“選擇errorDir發(fā)送失敗郵件” 按鈕,瀏覽并選擇“errorDir”文件夾,后臺自動遍歷“errorDir”文件夾中的所有附件,提取郵箱信息并逐一發(fā)送,已發(fā)送成功的附件會保存在以年月命名的文件夾內(nèi),發(fā)送失敗的郵件會保存在“errorDir”文件夾內(nèi)。
3 具體實現(xiàn)
3.1 發(fā)件人信息的配置與讀取
用戶可直接在主界面填寫發(fā)件人、發(fā)件服務(wù)器、授權(quán)碼等信息,或者在“發(fā)件信息設(shè)置.xml”文件中提前配置,后經(jīng)程序自動讀取,避免了在主界面填寫時人為錯誤導(dǎo)致郵件發(fā)送失敗。讀取配置文件的關(guān)鍵代碼如下:
string path = "";
if (dialog.ShowDialog() == DialogResult.OK)
{
path = dialog.FileName;
//讀取路徑下的配置文件并將其中信息顯示在相應(yīng)位置
XmlDocument document = new XmlDocument();
document.Load(path);
XmlNode xmlNode = document.DocumentElement;//獲取根節(jié)點(diǎn)
XmlNodeList xmlNodeList = xmlNode.ChildNodes;//獲取子節(jié)點(diǎn)
for (int i = 0; i < xmlNodeList.Count; i++)
{
string name = xmlNodeList[i].Name.ToString();
switch (name)
{
case "addresser":
textBox發(fā)件人.Text = xmlNodeList[i].InnerText;
textBox發(fā)件人.BackColor = SystemColors.Window;
break;
case "server":
textBox發(fā)件服務(wù)器.Text = xmlNodeList[i].InnerText;
textBox發(fā)件服務(wù)器.BackColor = SystemColors.Window;
break;
case "authorization":
textBox授權(quán)碼.Text = xmlNodeList[i].InnerText;
textBox授權(quán)碼.BackColor = SystemColors.Window;
break;
case "#comment":
break;
default:
MessageBox.Show("請檢查配置文件是否損壞");
break;
}
}
}
3.2 處理輸入文件
“總工資條.xlsx”文件作為程序的輸入文件,其中包含了企業(yè)所有員工的薪酬信息。因為需要對Excel文件進(jìn)行讀取、復(fù)制、保存、關(guān)閉等一系列操作,考慮使用Microsoft.Office.Interop.Excel程序集較為方便,且容易實現(xiàn)。
首先,需要添加對Microsoft.Office.Interop.Excel的引用。在解決方案資源管理器中找到該項目,右鍵菜單選擇“管理NuGet程序包(N)...”,搜索Excel關(guān)鍵字,找到并選中Microsoft.Office.Interop.Excel進(jìn)行安裝。安裝完畢后需在命名空間中添加引用。
利用app. Workbooks的Open()方法打開輸入文件,此時是在該文檔上進(jìn)行修改,因此任何的改動都會生效,這里只對該文件進(jìn)行內(nèi)容識別、復(fù)制等操作,不會對其數(shù)據(jù)和格式進(jìn)行改動。該函數(shù)返回一個Workbook對象,即要操作的Excel文檔對象。關(guān)鍵代碼如下:
_Application app;
……
app = new Microsoft.Office.Interop.Excel.Application();
app.SheetsInNewWorkbook = 1;//設(shè)定新建工作簿當(dāng)中默認(rèn)工作表
Workbook oldWorkbook = app.Workbooks.Open(path);//打開輸入文件
Worksheet oldWorksheet = oldWorkbook.Worksheets[1];//獲取輸入文件第一個工作表
int iMax = 1;//最大行
int jMax = 1;//最大列
string column = "";
for (int i = 0; i < letter.Length; i++)
{
if (oldWorksheet.Range[$"{letter[i]}1"].Value == null)
{
break;
}
column = letter[i].ToString();
jMax = i + 1;
}
int row = 1;
while (?。╫ldWorksheet.Range[$"A{row}"].Value == null))
{
iMax = row;
row++;
}
中間變量iMax和jMax分別代表輸入文件的最大行數(shù)和最大列數(shù),以此確定姓名、郵箱的所在列及附件個數(shù),為后續(xù)提取信息生成附件做準(zhǔn)備。
循環(huán)遍歷輸入文件,每次利用Worksheet. Range.Copy()方法復(fù)制輸入文件中指定范圍的信息到新建Excel文件中的指定范圍,將該新建的Excel文件以“姓名_郵箱.xls”的格式命名,作為附件待后續(xù)郵件發(fā)送時使用。關(guān)鍵代碼如下:
……
Workbook newWorkbook = app.Workbooks.Add();//新建一個workbook對象
Worksheet newWorksheet = newWorkbook.Worksheets[1];//獲新建工作簿當(dāng)中第一個工作表
oldWorksheet.Range[$"A1:{column}1"].Copy(newWorksheet.Range["A1"]);oldWorksheet.Range[$"A{i}:{column}{i}"].Copy(newWorksheet.Range["A2"]);
……
name = oldWorksheet.Range[$"B{i}"].Text.Trim();//提取姓名
email = oldWorksheet.Range[$"{column}{i}"].Text.Trim();//提取郵箱地址
string savePath = tempDir + "\\" + name + "_" + email + ".xls";
newWorkbook.SaveAs(savePath, XlFileFormat.xlExcel7);//保存成.xls文件
當(dāng)輸入文件數(shù)據(jù)量過多時,遍歷一次需要新建很多個workbook對象,當(dāng)操作完畢后選擇釋放對象,保證內(nèi)存不受影響;當(dāng)輸入文件遍歷完成后,釋放輸入文件對象,關(guān)鍵代碼如下:
……
System.Runtime.InteropServices.Marshal.ReleaseComObject(newWorksheet);
System.Runtime.InteropServices.Marshal.ReleaseComObject(newWorkbook);
……
System.Runtime.InteropServices.Marshal.ReleaseComObject(oldWorksheet);
System.Runtime.InteropServices.Marshal.ReleaseComObject(oldWorkbook);
輸入文件處理完畢后,需要及時清除Excel進(jìn)程,引入public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int ID)關(guān)閉進(jìn)程,關(guān)鍵代碼如下:
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int ID);
……
//釋放Excel資源,殺死相關(guān)進(jìn)程
IntPtr t = new IntPtr(app.Hwnd);
int k = 0;
GetWindowThreadProcessId(t, out k);
System.Diagnostics.Process p = System.Diagnostics.Process.GetProcessById(k);
p.Kill();
3.3 郵件發(fā)送
使用SmtpClient類的send(MailMessage message)方法實現(xiàn)帶附件的發(fā)送。獲取“tempDir”文件夾中的所有附件,分別讀取附件名稱,提取其中的姓名和郵箱信息,為后續(xù)郵件發(fā)送及窗口信息顯示做準(zhǔn)備,主界面上下顯示窗口分別用richTextBox1、richTextBox2來表示,發(fā)送成功信息將顯示在richTextBox1中,發(fā)送失敗信息及原因?qū)@示在richTextBox2中。關(guān)鍵代碼如下:
richTextBox1.Clear();
richTextBox2.Clear();
……
using (SmtpClient client = new SmtpClient(textBox發(fā)件服務(wù)器.Text))
{
client.EnableSsl = true;
client.UseDefaultCredentials = false;
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.Credentials = new NetworkCredential(textBox發(fā)件人.Text, textBox授權(quán)碼.Text);
string[] files = Directory.GetFiles(tempDir);//獲取臨時文件夾中所有的文件名
int countSuccess = 0;//記錄發(fā)送成功的次數(shù)
int countFail = 0;//記錄發(fā)送失敗的次數(shù)
foreach (string item in files)
{
string file = Path.GetFileNameWithoutExtension(item);
string fileName = file.Substring(0, file.IndexOf("_"));
string fileEmail = file.Substring(file.IndexOf("_") + 1, file.Length - file.IndexOf("_") - 1);
using (MailMessage msg = new MailMessage(new MailAddress(textBox發(fā)件人.Text), new MailAddress(fileEmail)))
{
msg.Subject = textBox主題.Text;
msg.SubjectEncoding = Encoding.UTF8;
msg.Body = textBox內(nèi)容.Text;
msg.BodyEncoding = Encoding.UTF8;
msg.Priority = MailPriority.High;
string sfile = item;//添加附件
msg.Attachments.Add(new Attachment(sfile));
try
{
client.Send(msg);
countSuccess++;
richTextBox1.Text += countSuccess + "." + fileName + ":郵件已發(fā)送" + "\r\n";
msg.Dispose();
File.Copy(item, saveDir + "\\" + file + ".xls", true);//將發(fā)送成功的附件從“tempDir”復(fù)制到“saveDir”
}
catch (Exception ex)
{
countFail++;
richTextBox2.Text += countFail + "." + fileName + ":郵件發(fā)送失敗" + "\r\n";
richTextBox2.Text += "失敗原因:" + ex.Message + "\r\n";
File.Copy(item, errorDir + "\\" + file + ".xls", true);//將發(fā)送失敗的附件從“tempDir”復(fù)制到“errorDir”
}
}
}
}
3.4 失敗郵件的發(fā)送
郵件發(fā)送失敗后的附件將保存在“errorDir”文件夾內(nèi),這一部分的代碼與3.3小節(jié)類似,不同之處在于所遍歷的文件夾不同,這里不再贅述。
4 結(jié)論及展望
通過對輸入Excel文件的簡單處理,提取其中每位企業(yè)員工的薪酬信息作為附件,采用發(fā)送郵件的方式實現(xiàn)了企業(yè)工資條的自動分發(fā),目前已完成11個月的實際工資條分發(fā)工作,程序運(yùn)行準(zhǔn)確無誤。工資條郵件的附件內(nèi)容采用Excel文件格式,清晰美觀,方便查看。使用本程序,操作簡單、可視性強(qiáng)、便于統(tǒng)計,避免了人工操作可能導(dǎo)致的失誤,大大提高了工作效率,使企業(yè)財務(wù)人員可以方便快捷正確地完成每月工資條分發(fā)工作。此外,通過對本程序的簡單修改,還可以用于養(yǎng)老保險數(shù)據(jù)、考勤記錄、考試成績等的自動分發(fā),實現(xiàn)更廣泛的用途[8]。
參考文獻(xiàn):
[1] 王志軍.兩種方法批量生成工資條[J].電腦知識與技術(shù)(經(jīng)驗技巧),2019(5):34-35.
[2] 曾慧.Excel制作工資條方法探析——針對不同基礎(chǔ)人群[J].遼寧高職學(xué)報,2020,22(5):84-87.
[3] 鄧祖芬.Excel工資條制作方法探析[J].信息與電腦(理論版),2018(19):28-30.
[4] 鄒傳樹.運(yùn)用Excel_VBA編程實現(xiàn)一鍵批量發(fā)送工資條[J].電腦知識與技術(shù),2019,15(28):58-59.
[5] Watson K,Hammer J V,Reid J D,等.C#入門經(jīng)典[M]. 6版.北京:清華大學(xué)出版社,2014.
[6] 王小科,徐薇.C#從入門到精通[M].2版.北京:清華大學(xué)出版社,2010.
[7] 吳波.工資條郵件群發(fā)系統(tǒng)的分析和研究[D].成都:電子科技大學(xué),2013.
[8] 張君.采用C#實現(xiàn)工資條自動分發(fā)[J].電腦編程技巧與維護(hù),2011(14):43-44,52.
【通聯(lián)編輯:謝媛媛】