“請(qǐng)假管理”應(yīng)用,應(yīng)該算是 SharePoint 的“Hello World!”、川菜里面的魚香肉絲、粵菜里面的蛋炒飯 。。。吧?
怎么樣才能做出簡(jiǎn)易、實(shí)用的請(qǐng)假管理,一直都是都是一個(gè)問題。完全 code free 不寫代碼是搞不出來(lái)的,完全寫代碼實(shí)現(xiàn)的話又何必用 SharePoint?簡(jiǎn)潔、輕快的解決方案才是我們追求的。
問題
通常的“請(qǐng)假管理” SharePoint 實(shí)現(xiàn)存在這樣幾個(gè)問題:
權(quán)限。
要么就是所有人都可以看見你的請(qǐng)假單,要么就是只有你自己可以看到,要了命了。參見 這里 的描述。常見的解決方案,要么就是直接忽略這個(gè)問題,或者用視圖來(lái)過濾篩選,但這不是根本的辦法。
預(yù)先指定審批人員。
而實(shí)用的要求,其實(shí)是動(dòng)態(tài)的指定審批人員。而且,往往并不是先由上級(jí)職能經(jīng)理審批,而是先由所在項(xiàng)目的項(xiàng)目經(jīng)理審批、職能經(jīng)理一般只要項(xiàng)目經(jīng)理沒意見都會(huì)同意的。
年假天數(shù)約束。
假別里面如果是年假,則應(yīng)該有天數(shù)的限制,且每年重置。軟件應(yīng)當(dāng)自動(dòng)對(duì)剩余年假天數(shù)做核對(duì),避免需要人工再去查年假天數(shù)。
孤立。
沒有和 SharePoint 其它應(yīng)用配合,比如項(xiàng)目、日歷。
目標(biāo)
實(shí)用的“請(qǐng)假管理”應(yīng)該什么樣子呢?
1、所有人都可以提出請(qǐng)假申請(qǐng)。如下圖所示:
2、自動(dòng)查找合適的審批人。在項(xiàng)目中則項(xiàng)目經(jīng)理就是審批人(直接去項(xiàng)目列表中找“項(xiàng)目經(jīng)理”字段對(duì)應(yīng)的用戶),否則就是職能部門經(jīng)理(如下圖所示)。
3、不能申請(qǐng)超出可用年假天數(shù)的年假。
4、但是,一旦提交申請(qǐng),就只有本人、審批人、管理員可以看到。而且審批人有“批準(zhǔn)”權(quán)限。
注意:SharePoint 只允許每個(gè)列表?yè)碛?8000 個(gè)獨(dú)立權(quán)限的列表項(xiàng)。所以,后面要配合列表的信息管理策略將完成的請(qǐng)假單轉(zhuǎn)移到別的地方。
5、申請(qǐng)人發(fā)起請(qǐng)假流程。
6、審批人審批請(qǐng)假申請(qǐng)。
7、審批完成后不能再修改申請(qǐng)。
配置信息管理策略將“聲明為記錄”的項(xiàng)在1個(gè)月后移動(dòng)到其它的文檔庫(kù),避免擁有獨(dú)立權(quán)限的項(xiàng)目超過 SharePoint 對(duì)每個(gè)列表 8000 條的限制。
8、如果同意了年假申請(qǐng),那么,自動(dòng)從當(dāng)年年假中扣除。
9、申請(qǐng)同意后自動(dòng)加入請(qǐng)假日歷。
該日歷可以和其它日歷合并在內(nèi)網(wǎng)門戶上顯示。
實(shí)現(xiàn)
說(shuō)實(shí)話,實(shí)現(xiàn)起來(lái)并不簡(jiǎn)單。但是,通過努力,可以保持解決方案的干凈、輕快,與整個(gè)架構(gòu)體系融合在一起。
1、所有人都可以提出請(qǐng)假申請(qǐng)。
直接斷開“請(qǐng)假單”列表的權(quán)限繼承,為所有用戶設(shè)置“參與討論”權(quán)限級(jí)別即可。
具體操作參見這里 中斷列表或庫(kù)的權(quán)限繼承。
2、自動(dòng)查找合適的審批人。
開發(fā)自定義字段,加入“請(qǐng)假單”列表。
此自定義字段將獲取申請(qǐng)人所在項(xiàng)目的項(xiàng)目經(jīng)理或者申請(qǐng)人的上級(jí)職能經(jīng)理。
創(chuàng)建自定義字段類型的文章在這里 創(chuàng)建自定義 SharePoint 2010 字段類型。
獲取當(dāng)前用戶的上級(jí)職能經(jīng)理。需要用到 UserProfileManager 對(duì)象。
UserProfileManager upm = new UserProfileManager(SPServiceContext.Current);
if (upm.UserExists(user.LoginName))
{
UserProfile u = upm.GetUserProfile(user.LoginName);
UserProfile[] managers = u.GetManagers();
if (managers != null)
{
foreach(UserProfile manager in managers){
SPUser u_manager = web.SiteUsers[manager[PropertyConstants.AccountName].Value.ToString()];
// 其它自定義代碼。
}
}
}
3、不能申請(qǐng)超出可用年假天數(shù)的年假。
這里需要用 SharePoint Designer 修改“請(qǐng)假單”列表的“NewForm.aspx”文件,利用 JavaScript 腳本調(diào)用 SharePoint 的 Client Object Model 獲取剩余年假并顯示在界面上。
首先,需要引入幾個(gè) js 庫(kù):jQuery 和 jQuery.SPServices。jQuery 已經(jīng)放入 masterpage。
<SharePoint:ScriptLink Name="SP.js" runat="server" OnDemand="true" Localizable="false" /> <script src="http://www.cnblogs.com/DocLib/spservices/jquery.SPServices-0.7.1a.min.js" type="text/javascript"></script>
然后,在選擇假別的下拉框內(nèi)容改變時(shí),讀取可用年假天數(shù)。 (我當(dāng)時(shí)為什么要用 lj 做變量名?我也很奇怪。)
$('select[title="假別"]').change(function(){ var lj=$(this).val(); if(lj=='年假'){ ExecuteOrDelayUntilScriptLoaded(get_annual_leave_days, "sp.js"); } else{ $("nobr:contains('請(qǐng)假天數(shù)')").children().each(function(){ $(this).html("*"); }); } });
get_annual_leave_days 方法將讀取當(dāng)前用戶所剩余年假天數(shù)。下面函數(shù)中變量命名方法并不統(tǒng)一,這是這些代碼來(lái)自多個(gè)不同時(shí)期的不同項(xiàng)目的印記。≤浖_發(fā)是個(gè)手藝活兒。
"_x5269__x4f59__x5e74__x5047__x59" 是字段“剩余年假天數(shù)”的 InnerName。
var _ctx = null; var _items = null; function get_annual_leave_days(){ _ctx = new SP.ClientContext.get_current(); var web = _ctx.get_web(); var lists = web.get_lists(); var list_annual_leave = lists.getByTitle("年假匯總"); var currentDate = new Date(); var year = currentDate.getFullYear(); var currentUserID = $().SPServices.SPGetCurrentUser({ fieldName: "ID", debug: false }); var camlQuery = new SP.CamlQuery(); var strCaml = "<View>" + "<Query>" + "<Where>"+ "<And>"+ "<Eq>"+ "<FieldRef Name='_x4eba__x5458_' LookupId='TRUE' />"+ "<Value Type='Lookup'>"+currentUserID+"</Value>"+ "</Eq>"+ "<Eq>"+ "<FieldRef Name='_x5e74__x4efd_' />"+ "<Value Type='Integer'>"+year+"</Value>"+ "</Eq>"+ "</And>"+ "</Where>"+ "</Query>" + "</View>"; camlQuery.set_viewXml(strCaml); this._items = list_annual_leave.getItems(camlQuery); _ctx.load(_items); _ctx.executeQueryAsync(Function.createDelegate(this, this.onSuccess), Function.createDelegate(this, this.onFail)); } function onSuccess(sender, args) { var listItemEnumerator = this._items.getEnumerator(); while(listItemEnumerator.moveNext()) { var oListItem = listItemEnumerator.get_current(); var days = oListItem.get_item("_x5269__x4f59__x5e74__x5047__x59"); $("nobr:contains('請(qǐng)假天數(shù)')").children().each(function(){ $(this).html("(剩余 "+days+" 天)*"); }); } } function onFail(sender, args) { alert('獲取年假天數(shù)時(shí)出錯(cuò):' + args.get_message()); }
更多的技術(shù)細(xì)節(jié)可以參考 ECMAScript 對(duì)象模型系列,這個(gè)系列講解很細(xì)致了。另外,JS 腳本調(diào)用 SharePoint 的 JSCOM 時(shí)是異步操作,回調(diào)次數(shù)多了代碼會(huì)很亂,這篇 使用Jscex增強(qiáng)SharePoint 2010 JavaScript Client Object Model (JSOM) 提供了一優(yōu)化代碼的解決方案可供參考。
4、但是,一旦提交申請(qǐng),就只有本人、審批人、管理員可以看到。而且審批人有“批準(zhǔn)”權(quán)限。
為實(shí)現(xiàn)這個(gè)功能,需要處理列表的 Create 事件。
先斷開現(xiàn)有的繼承權(quán)限。
item.BreakRoleInheritance(false);
然后,綁定新的權(quán)限。
protected void bind_role(SPListItem item, SPPrincipal principal, SPRoleDefinition definition) { try { SPRoleAssignment assignment = new SPRoleAssignment(principal); assignment.RoleDefinitionBindings.Add(definition); item.RoleAssignments.Add(assignment); } catch (Exception ex) { throw ex; } }
對(duì)某個(gè)用戶執(zhí)行綁定角色的操作。
bind_role(item, user, web.RoleDefinitions["參與討論"]);
5、申請(qǐng)人發(fā)起請(qǐng)假流程。
用 SharePoint Designer 建立請(qǐng)假流程。
6、審批人審批請(qǐng)假申請(qǐng)。
審批即可?梢杂 InfoPath Designer 從 SPD 中打開美化一下任務(wù)界面。
如果需要對(duì)任務(wù)也實(shí)施和“請(qǐng)假單”相同的權(quán)限控制,可以參考對(duì)“請(qǐng)假單”的處理一樣進(jìn)行。
7、審批完成后不能再修改申請(qǐng)。
需要開發(fā)處理工作流事件的代碼,聲明當(dāng)前項(xiàng)目為記錄。
SPWorkflow wf = new SPWorkflow(web, properties.InstanceId); SPList list = web.Lists[wf.ListId]; if (list.Title == "請(qǐng)假單") { SPListItem item = list.Items.GetItemById(wf.ItemId); title = item.Title; if (!Records.IsRecord(item)) { Records.DeclareItemAsRecord(item); } }
還需要配置信息管理策略,自動(dòng)備份已經(jīng)完成流程的“請(qǐng)假單”到歸檔庫(kù)中。
8、如果同意了年假申請(qǐng),那么,自動(dòng)從當(dāng)年年假中扣除。
同樣在工作流事件代碼中處理,扣除年假天數(shù)。做減法即可。由于申請(qǐng)人本人沒有直接修改年假天數(shù)的權(quán)限(這是當(dāng)然的),所以需要提升權(quán)限才能操作。而這也意味著要服務(wù)器場(chǎng)解決方案,“全程沙盒方案”夢(mèng)想破滅。
9、申請(qǐng)同意后自動(dòng)加入請(qǐng)假日歷。
在 SPD 工作流中處理。
10、簽入代碼和 SPD 文件(導(dǎo)出來(lái)備份,或者直接 stsadm –o backup 備份網(wǎng)站集),寫好部署操作手冊(cè)。完工。