用 SharePoint 實現實用的請假管理


“請假管理”應用,應該算是 SharePoint 的“Hello World!”、川菜里面的魚香肉絲、粵菜里面的蛋炒飯 。。。吧?

怎么樣才能做出簡易、實用的請假管理,一直都是都是一個問題。完全 code free 不寫代碼是搞不出來的,完全寫代碼實現的話又何必用 SharePoint?簡潔、輕快的解決方案才是我們追求的。

 

問題

通常的“請假管理” SharePoint 實現存在這樣幾個問題:

  • 權限。
    要么就是所有人都可以看見你的請假單,要么就是只有你自己可以看到,要了命了。參見 這里 的描述。常見的解決方案,要么就是直接忽略這個問題,或者用視圖來過濾篩選,但這不是根本的辦法。
  • 預先指定審批人員。
    而實用的要求,其實是動態的指定審批人員。而且,往往並不是先由上級職能經理審批,而是先由所在項目的項目經理審批、職能經理一般只要項目經理沒意見都會同意的。
  • 年假天數約束。
    假別里面如果是年假,則應該有天數的限制,且每年重置。軟件應當自動對剩余年假天數做核對,避免需要人工再去查年假天數。
  • 孤立。
    沒有和 SharePoint 其它應用配合,比如項目、日歷。

 

目標

實用的“請假管理”應該什么樣子呢?

1、所有人都可以提出請假申請。如下圖所示:

image

2、自動查找合適的審批人。在項目中則項目經理就是審批人(直接去項目列表中找“項目經理”字段對應的用戶),否則就是職能部門經理(如下圖所示)。

image

image

3、不能申請超出可用年假天數的年假。

image

4、但是,一旦提交申請,就只有本人、審批人、管理員可以看到。而且審批人有“批准”權限。

注意:SharePoint 只允許每個列表擁有 8000 個獨立權限的列表項。所以,后面要配合列表的信息管理策略將完成的請假單轉移到別的地方。

image

5、申請人發起請假流程。

image

6、審批人審批請假申請。

image

7、審批完成后不能再修改申請。

image

配置信息管理策略將“聲明為記錄”的項在1個月后移動到其它的文檔庫,避免擁有獨立權限的項目超過 SharePoint 對每個列表 8000 條的限制。

8、如果同意了年假申請,那么,自動從當年年假中扣除。

image

9、申請同意后自動加入請假日歷。

該日歷可以和其它日歷合並在內網門戶上顯示。

image

 

實現

說實話,實現起來並不簡單。但是,通過努力,可以保持解決方案的干凈、輕快,與整個架構體系融合在一起。

1、所有人都可以提出請假申請。
直接斷開“請假單”列表的權限繼承,為所有用戶設置“參與討論”權限級別即可。
具體操作參見這里 中斷列表或庫的權限繼承


2、自動查找合適的審批人。
開發自定義字段,加入“請假單”列表。
此自定義字段將獲取申請人所在項目的項目經理或者申請人的上級職能經理。
創建自定義字段類型的文章在這里 創建自定義 SharePoint 2010 字段類型
獲取當前用戶的上級職能經理。需要用到 UserProfileManager 對象。

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、不能申請超出可用年假天數的年假。
這里需要用 SharePoint Designer 修改“請假單”列表的“NewForm.aspx”文件,利用 JavaScript 腳本調用 SharePoint 的 Client Object Model 獲取剩余年假並顯示在界面上。
首先,需要引入幾個 js 庫:jQuery 和 jQuery.SPServices。jQuery 已經放入 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>

然后,在選擇假別的下拉框內容改變時,讀取可用年假天數。 (我當時為什么要用 lj 做變量名?我也很奇怪。)

$('select[title="假別"]').change(function(){
var lj=$(this).val();
if(lj=='年假'){
ExecuteOrDelayUntilScriptLoaded(get_annual_leave_days, "sp.js"); }
else{
$("nobr:contains('請假天數')").children().each(function(){
$(this).html("*");
});
}
});

get_annual_leave_days 方法將讀取當前用戶所剩余年假天數。下面函數中變量命名方法並不統一,這是這些代碼來自多個不同時期的不同項目的印記啊!軟件開發是個手藝活兒。
"_x5269__x4f59__x5e74__x5047__x59" 是字段“剩余年假天數”的 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('請假天數')").children().each(function(){
$(this).html("(剩余 "+days+" 天)*");
});
}
}
function onFail(sender, args) {
alert('獲取年假天數時出錯:' + args.get_message());
}

更多的技術細節可以參考 ECMAScript 對象模型系列,這個系列講解很細致了。另外,JS 腳本調用 SharePoint 的 JSCOM 時是異步操作,回調次數多了代碼會很亂,這篇 使用Jscex增強SharePoint 2010 JavaScript Client Object Model (JSOM) 提供了一優化代碼的解決方案可供參考。


4、但是,一旦提交申請,就只有本人、審批人、管理員可以看到。而且審批人有“批准”權限。
為實現這個功能,需要處理列表的 Create 事件。
先斷開現有的繼承權限。

item.BreakRoleInheritance(false);

然后,綁定新的權限。
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;
}
}

對某個用戶執行綁定角色的操作。

bind_role(item, user, web.RoleDefinitions["參與討論"]);

 

5、申請人發起請假流程。
用 SharePoint Designer 建立請假流程。
image


6、審批人審批請假申請。
審批即可。可以用 InfoPath Designer 從 SPD 中打開美化一下任務界面。
如果需要對任務也實施和“請假單”相同的權限控制,可以參考對“請假單”的處理一樣進行。


7、審批完成后不能再修改申請。
需要開發處理工作流事件的代碼,聲明當前項目為記錄。

SPWorkflow wf = new SPWorkflow(web, properties.InstanceId);
SPList list = web.Lists[wf.ListId];
if (list.Title == "請假單")
{
SPListItem item = list.Items.GetItemById(wf.ItemId);
title = item.Title;
if (!Records.IsRecord(item))
{
     Records.DeclareItemAsRecord(item);
     }
}

還需要配置信息管理策略,自動備份已經完成流程的“請假單”到歸檔庫中。
image


8、如果同意了年假申請,那么,自動從當年年假中扣除。
同樣在工作流事件代碼中處理,扣除年假天數。做減法即可。由於申請人本人沒有直接修改年假天數的權限(這是當然的),所以需要提升權限才能操作。而這也意味着要服務器場解決方案,“全程沙盒方案”夢想破滅。


9、申請同意后自動加入請假日歷。
在 SPD 工作流中處理。
image

10、簽入代碼和 SPD 文件(導出來備份,或者直接 stsadm –o backup 備份網站集),寫好部署操作手冊。完工。


也許有人會覺得,“這還不如我寫代碼快!”。呃,自己開發這個進度估算可能樂觀了點兒,參見 【譯】為什么估算很難?!從舊金山到洛杉磯的旅行,這篇文章解釋了為什么看上去簡單的功能做起來工作量會大大超出原來的估算。

 

2012-07-05 補充:

在“實現”的第7步,做“信息管理策略”的時候,需要先開啟網站的“內容管理器”(Content Organizer)feature,然后去管理中心配置好發送鏈接才能用。具體操作參見 這個 鏈接。這無疑在本來就復雜的方案之上又加了一塊磚。不過,這樣做仍然是“清爽”的,沒有對整體架構造成任何污染。

 

2012-07-08 補充:

在“實現”的第4步,“ 但是,一旦提交申請,就只有本人、審批人、管理員可以看到。”,也可以采用另外一種做法,就是為每個項目、人都建立一個目錄,只斷開此目錄的權限並設置好僅本人和審批人可見。則需要獨立權限的列表項數量就是“項目數”ד人數”,如果該數量小於 8000,則可以嘗試。

 

2012-10-29 補充:

這里糾正我的一個誤解,列表可以擁有的獨立權限的列表項數量是可以設置的。在 CA 的應用程序管理中可以設置“List Unique Permissions Threshold”的數量,默認是 50000 條,而非 8000。

List Unique Permissions Threshold 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM