2012年11月8日

[IIS] URL Rewrite 設定備忘

備忘:
我有一個網站,網站內容是純 html、js 組成的,但 js 會呼叫 services 做事,
我們希望 services 可以被 hosted 在別的 server 上,但我們同時也希望,
js 裡面在寫呼叫 services 的事,不需要管 services 的 server 在哪裡。
於是我們在這個網站上,設了一個反向 Proxy 做 URLRewrite ,
把特定的路徑 request 都導向另一台 Server 。

^s/(.*) 表示 s 這個目錄所以有網址都要 rewrite 。
("^"是指一開始,因為後面馬上接 "/",所以就是 "s" 這個目錄,

圖二中 {R:1} 表示上面表示式中 "(.*)" match 到的字串,{R:0}則表示含 "s/"。
(圖一)
(圖二)

參考資料:http://www.microsoft.com/taiwan/technet/iis/expand/URLRewrite.aspx



2012年10月9日

[EasyFlow] 增加TipTop XML 傳來之欄位


原因:
   TipTop 的請購單,備註是放在單身中,USER 反映簽核時不夠明顯,也無法輸入太多字

改善:
1.在 TipTop APMT420 中的自訂欄位,新增一欄備註( pmkud01),設定此欄位要傳到 EasyFlow.

2.打開此表單的 EasyFlow XML檔 (路徑 EFNET \ WS \ EFNETService \ TTXMLDoc \ Draw \ apmt420.xml )
       在<Grid name="gr5261" width="52" height="7">的最後插入
     
<Label text="備註" posY="0" posX="69" gridWidth="8" sizePolicy="dynamic"/>
<FormField name="pmk_file.pmkud01" colName="pmkud01" sqlType="VARCHAR(255)" fieldId="999" sqlTabName="pmk_file"
      tabIndex="999">
<ButtonEdit width="10" action="controlp" image="zoom" posY="0" posX="81" gridWidth="12" scroll="1"
      sample="MMMMMM000000" comment="備註[pmkud01]"/>
</FormField>


3. 修改此EasyFlow表單的HTML(路徑 EFNET \ src \ TEI \ TEIAPMT420)
         在TABLE的最下面新增

<tr>
<td width="90" id="pmkud01_1" nowrap="true" align="right" class="normalFormCaption">備註</td><td nowrap="true" width="100">
<table style="margin:0px;padding:0px;" border="0" cellspacing="0" cellpadding="0">
<tr>
<td><span id="pmkud01_2" class="normalSpan"></span></td>
</tr>
</table>
</td>
</tr>

成果:



[EasyFlow] DropDownList 下拉式選單的加工

加工一:在SQL Command加上其他較複雜的SQL語法

原因:
      因為在 EasyFlow 表單設計中的DropDownList選單中的 SQL Command 無法用 SQL 的
      ORDER BY  or HAVING 之類的語法, 所以必須手動加在 CS 檔中.
作法:  
      找到 ” #region 重新取得 select selectXXXXX下拉選項 ”,修改 string tSQL 後面的  SQL 語法



#region 重新取得selectcategory2下拉選項 
[AjaxPro.AjaxMethod(AjaxPro.HttpSessionStateRequirement.ReadWrite)]  
public string ajaxGetselectcategory2Item(string pselectcategory1Val)  
{  
 string strReturn = ""; string tSQL = @"select distinct * from LMIT where FormCode='表單名稱' 
        and ItemNo='1' and Category=N'" +  pselectcategory1Val + "' order by 00";
 strReturn = createEasyFlowDataTable(tSQL, "Details", "Details");  
 return strReturn;  
}  
#endregion 重新取得selectcategory2下拉選項 

加工二:DropDownList 第一個選項是空白

作法:   
  要刪除這個空白的話,在if (!IsPostBack)中,找到這個下拉式選單,註解掉this.selectXXXXX.Items.Insert(0, new ListItem("", ""));
  在JS檔中的FunOnChange_ selectXXXXX 註解掉CreateOption("","請選擇", selectXXXXX DDL);即可

2012年10月8日

[EasyFlow] 自製 TipTop 放行單



原因:
         在 TipTop AXM520 中,沒有信用查核放行的單子可拋轉至 EasyFlow 簽核,且因 EasyFlow 版本為 .net ,鼎新將 CS 檔包成 DLL 檔了,沒有 Code ,故無法在出貨單拋轉至 EasyFlow 簽核完成時回寫 TipTop .
             
 故現行公司的做法是:
         需要放行時由相關人員填寫紙本的信用查核放行單,依核決權限簽核後,交由會計至下面欄位勾選允許放行.

流程改善:
1. 當需要放行時,負責的人員至 EasyFlow 開立信用查詢放行單,依核決權限簽核,
         當簽核完成後,更新 TipTop DB OGA903 欄位(改為Y)





2.    防呆:
a)送出前檢查: 須為出貨單 / 確認碼不為X:作廢 / 狀況碼不為9:作廢 / 信用額度查核放行不為Y / Gridview裡面的單號須為同一客戶且幣別相同
b)簽核完成後檢查:流程中是否有會計部門的員工
        c)輸入出貨單單號自動帶出該訂單之金額
3.    <!--[endif]-->檢查後帶出相關資料:
a)客戶編號 / 客戶名稱 / 授信條件 / 付款條件 / 幣別 / 本幣金額
b)計算出原幣金額(單價*數量*匯率)
c)檢查通過才會出現送出的按鈕,檢查不通過會跳出視窗題是哪邊錯誤


#region 簽核流程結束時要處理的事情
protected override void AfterApprove()
{
if (this.BlnCaseClosed && this.AryFlowProperty.SerialSignResult == "2")
{
   //先判斷流程中是否有財會部的人
   string sql_EFdept = @"select * from resdd as D
        where resdd001='表單TABLE' and resdd002='"+this.StrSheetNo+"'
              and exists (
                select '"+"x"+"' from  resak X where X.resak001 =
                    D.resdd008 and (X.resak015 = 'FN3000')
             )";
DataSet set_EFdept = this.m_objDB.CreateRecordset(" use EFNETDB;" +
                   sql_EFdept, this.Session["strProcID"].ToString());
  DataTable temp_EFdept = set_EFdept.Tables[0];
  if (temp_EFdept.Rows.Count == 0)
  {
    //把ERROR存成TXT檔
  }
  else
  {
    StringBuilder builder = new StringBuilder();
    builder.Append(" use EFNETDB;");
    builder.Append(" select saleno ");
    builder.Append(" FROM 表單TABLEb ");
    //表單代號,表單編號
    builder.AppendFormat(" WHERE 表單TABLEb001='{0}' ", this.formID);
    builder.AppendFormat(" AND 表單TABLEb002='{0}' ", this.StrSheetNo);
    DataSet set_saleno=this.m_objDB.CreateRecordset(builder.ToString(),this.Session["strProcID"].ToString());
    DataTable temp_saleno = set_saleno.Tables[0];
    foreach (DataRow saleno_read in temp_saleno.Rows)
    {
       string sql_OGAFILE = @"UPDATE OPENQUERY(ORACLE,'
               select oga903 from TT.oga_file where oga01=''" + saleno_read["saleno"] + "''') SET OGA903='Y'";                      
this.m_objDB.ExecuteSQL(sql_OGAFILE.ToString(),this.Session["strProcID"].ToString());
     }
   }
 }





#region 按下檢查時要做的事情

protected void butcheck_Click(object sender, EventArgs e)
{
    //先清空textbox
    
    
    base.BtnCreateToolSendForm.Attributes.Add("style", "display:none");//隱藏送出的按鈕
    int rows_count = MasterObj.DetailObjs[0].NewRows.Count;//取得grid有幾列資料
    System.Text.StringBuilder sb = new System.Text.StringBuilder();
    string customer_1 = "";//客戶名稱
    string customer_no = "";//客戶編號
    string terms_payment = "";//付款條件
    int customer_i = 1;
    string currency_1 = "";//幣別
    double local_currency = 0.0;//本幣未稅金額NTD
    double org_currency = 0.0;//原幣未稅金額
    double credit = 0.0; //授信額度
    double rate = 0.0; //匯率
    string txt_saleno = "";//銷貨單號
    bool bPass = true;
    if (rows_count == 0)
    {
         MessageBox.Show("請先輸入訂單編號");
    }
    else
    {
        foreach (DscRow dscRow in MasterObj.DetailObjs[0].NewRows)
        {
            string sqlstring_TT = @"Select * From OPENQUERY(ORACLE, '
            select oga01 as 銷貨單號,oga03 as 客戶編號,oga032 as 客戶名稱,oga903 as 是否放行,oga55 as 狀況碼,
                    ogaconf as 核准碼,oga09 as 單據別,oag02 as 付款條件,oga24 as 匯率,
                    oga50 as 出貨金額,OCCG03 as 授信額度,oga23 as  幣別,oga50 as 原幣未稅金額,(
                              select sum(omb16) from TT.omb_file  
                                    where TT.oga_file.oga01=omb31
                            ) as 本幣未稅金額
            from TT.oga_file join TT.oag_file on oga32=oag01
                 join TT.occg_file on OCCG01=oga03
            where oga01=''" + dscRow["saleno"].Value.ToString() + "''')";

           DataSet set_TT = this.m_objDB.CreateRecordset(" use EFNETDB;" +sqlstring_TT,this.Session["strProcID"].ToString());
           DataTable dtTemp_TT = set_TT.Tables[0];
           if (dtTemp_TT.Rows.Count == 0)
           {
               sb.Append("查無'" + dscRow["saleno"].Value.ToString() + "'的訂單資料,請檢查訂單號碼是否錯誤\r\n");
                bPass = false;
          }
          else
          {
              foreach (DataRow s_read in dtTemp_TT.Rows)
              {
                if (s_read["單據別"].ToString().Trim() == "1")
                {
                  sb.Append("單號:" + s_read["銷貨單號"] + "是出貨通知單喔~請再確認\r\n");
                  bPass = false;         
                }
                else if (s_read["核准碼"].ToString().Trim()=="X")
                {
                  sb.Append("單號:" + s_read["銷貨單號"] + "核准碼是作廢'X',請再確認");
                  bPass = false;
                }
                else if (s_read["是否放行"].ToString().Trim()!= "N")
                {
                  sb.Append("單號:" + s_read["銷貨單號"] + "已放行,請再確認.\r\n");
                  bPass = false;
                }
                else
                {
                  if (customer_i == 1)
                {
                customer_1 = s_read["客戶名稱"].ToString();
                currency_1 = s_read["幣別"].ToString();
                double x = 0.0;
                bool flag_1 = double.TryParse(s_read["匯率"].ToString(), out x);
                bool flag_2 = double.TryParse(s_read["原幣未稅金額"].ToString(), out x);
                bool flag_3 = double.TryParse(s_read["授信額度"].ToString(), out x);
                if (flag_1 && flag_2 && flag_3)
                {
                   rate = double.Parse(s_read["匯率"].ToString());
                   org_currency = double.Parse(s_read["原幣未稅金額"].ToString());
                   credit = double.Parse(s_read["授信額度"].ToString());
                }
                customer_no = s_read["客戶編號"].ToString();
                terms_payment = s_read["付款條件"].ToString();
                local_currency = rate * org_currency;
                txt_saleno = s_read["銷貨單號"].ToString();
              }
              else
              {
               if (customer_1 != s_read["客戶名稱"].ToString())               {
                 sb.Append("單號:" + s_read["銷貨單號"] + "公司名稱與其他不同");
                 bPass = false;
               }
               if (currency_1 != s_read["幣別"].ToString())
               {
                 sb.Append("單號:" + s_read["銷貨單號"] + "幣別與其他不同.\r\n");
                 bPass = false;
               }
               if (txt_saleno == s_read["銷貨單號"].ToString())
               {
                 sb.Append("單號:" + s_read["銷貨單號"] + "重複.\r\n");
                 bPass = false;
               }
               org_currency = org_currency + (double.Parse(s_read["原幣未稅金額"].ToString()));
               local_currency = local_currency + (rate * (double.Parse(s_read["原幣未稅金額"].ToString())));
                            }
                        }
                    }
                }
                customer_i++;
            }
        }
string ErrorMsg = sb.ToString();
if (bPass && butcheck.ToolTip != "0")
{
  //Lucas 觸發 refresh 重新解悉流程
  string str_java = "document.getElementById('" +base.BtnCreateToolRefresh.ClientID + "').click();";
  Page.ClientScript.RegisterStartupScript(typeof(string), "jva", str_java, true);
  base.BtnCreateToolSendForm.Attributes.Add("style", "display");
  butcheck.ToolTip = "0";
  butcheck.Text = "已檢查";
  currencymoney.Text = org_currency.ToString();//原幣未稅金額
  orgmoney.Text = local_currency.ToString();
  customerno.Text = customer_no;
  overdue3.Text = terms_payment;
  credit3.Text = credit.ToString();
  }
  else
  {
     base.BtnCreateToolSendForm.Attributes.Add("style", "display:none");
     butcheck.ToolTip = "";
     MessageBox.Show("錯誤訊息如下:\r\n" + ErrorMsg);
  }
} 
#endregion
}

2012年9月20日

[.Net] 如何用 C# 將 LibreOffice 的 ods 轉成 excel 的 xls

首先,當然是要安裝 LibreOffice,
接下來要安裝 LiberOffice SDK ,坦白說,這個步驟我找好久,
下圖是下載的位置:


安裝完成之後,開啟 dotNet C# 專案,將 C:\Program files\LibreOffice 3.6\sdk\cli 中的 Dll 全部加進參考。


接下來就可以開始寫程式了:
public static string FilePath2Url(string fname)
{
    return string.Format("file:///{0}", fname.Replace("\\", "/"));
}

internal static XComponent openCalcSheet(string fname)
{
    string url = FilePath2Url(fname);
    XComponentContext oStrap = uno.util.Bootstrap.bootstrap();
    XMultiServiceFactory oServMan = (XMultiServiceFactory)oStrap.getServiceManager();
    XComponentLoader desktop = (XComponentLoader)oServMan.createInstance("com.sun.star.frame.Desktop");
    PropertyValue[] loadProps = new PropertyValue[1];
    loadProps[0] = new PropertyValue();
    loadProps[0].Name = "Hidden";
    loadProps[0].Value = new uno.Any(true);
    XComponent document = desktop.loadComponentFromURL(url, "_blank", 0, loadProps);
    return document;
}

public static XSpreadsheetDocument Open(string fname)
{
    return (XSpreadsheetDocument)openCalcSheet(fname);
}

public static void Save(XSpreadsheetDocument xDoc, string fname)
{
    string url = FilePath2Url(fname);
    PropertyValue[] propVals = new PropertyValue[2];
    /* 另存成 MS Excel 97 的格式,
       格式可以參考:http://www.oooforum.org/forum/viewtopic.phtml?t=3549  (找不到官方的清單 Orz,看官知道在哪的,請通知一聲,感恩ㄚ)
       原理可以參考:http://wiki.openoffice.org/wiki/Documentation/DevGuide/OfficeDev/Integrating_Import_and_Export_Filters
     */
    propVals[0] = new PropertyValue { Name = "FilterName", Value = new uno.Any("MS Excel 97") };
    propVals[1] = new PropertyValue { Name = "Overwrite", Value = new uno.Any(true) };
    ((XStorable)xDoc).storeToURL(url, propVals);
}

public static void Convert2Xls(string fname, string new_fname)
{
    Save(Open(fname), new_fname);
}
於是只要呼叫 Convert2Xls(ods檔案位置, xls輸出檔案位置) 就可以將 ods 轉成 xls 囉。

範例程式:將選到的 ODS 另存成 XLS 並透過 oledb 讀取資料顯示在 DataGridView 上

參考資料:
.http://www.oooforum.org/forum/viewtopic.phtml?t=3549
.Integrating Import and Export Filters
.use openoffice uno cli with c sharp to create a spreadsheet
.c-cpp.r3dcode.com


2012年9月17日

[Refactoring] Demo video


原本這程式會分別出兩張報表,新的需求是要併成一張,
看到程式的第一個念頭就是「重構」,
請記住!「重構」不是「重寫」,
我們是懷著讓原本程式更容易閱讀的心情再做「重構」的。

影片不是很清楚,程式不是很容易閱讀,純粹紀錄「重構」的感覺,
如果你也進行過「重構」工作,我想你會懂我的。




2012年8月21日

[Javascript] 實作熱鍵(HotKey)共用程式,以Konami(上上下下左右左右BA)為例

/*
不曉得有沒有人想過像 Konami 那種,按 "上上下下左右左右BA" 後,
會觸發某些事件的功能,如何撰寫,以下是我的實作,除了特定的 Konami 按鍵外,
也允許自訂自己的按鍵事件序列的測試方法,
整個程式的概念,是註冊 document 的 keyup 事件,並紀錄下最近 10 次的按鍵內容,
並提供一個 RegisterTest 方法,由註冊者自行決定目前按下的按鍵是否符合觸發的條件,
若符合要觸發什麼事件; RegisterTest 會先把這兩個 function 紀錄下來,
等到畫面上的 kepup 事件產生時,逐一呼叫 testFun , testFun 回傳 true ,
就呼叫 triggerFun 執行當初註冊的觸發事件。
這個程式需要 jQuery ,
另外這個陽春的版本不提供組合鍵的處理,當然稍微修改一下也可以支援組合鍵。
*/
var HotKeyControler = {
    MAX_QUEUE: 10, /* 保留最後 10 個鍵盤事件 */
    testFuns: new Object(),
    triggerFuns: new Object(),
    KeyQueue: new Array,
    Keyup: function(event) {
        HotKeyControler.KeyQueue.push(event.keyCode);
        if (HotKeyControler.KeyQueue.length > HotKeyControler.MAX_QUEUE) {
            HotKeyControler.KeyQueue.shift();
        }
        for (var key in HotKeyControler.testFuns) {
            if (HotKeyControler.testFuns[key](event,  HotKeyControler.KeyQueue)) {
                HotKeyControler.triggerFuns[key]();
            }
        }
    },
   /*  函數名稱:註冊熱鍵
    *  參數說明:(唯一鍵值, 測試function, 觸發function)
    *  說   明:提供註冊熱鍵的方法,提供目前畫面被按下的按鍵佇列,供AP自行判斷是否觸發執行事件
    */
    RegisterTest: function(key, testFun, triggerFun) {
        if (typeof(testFun)=='function' && typeof(triggerFun)=='function') {
            HotKeyControler.testFuns[key] = testFun;
            HotKeyControler.triggerFuns[key] = triggerFun;
        }
    }
}

$(document).ready(function(){    
    $(document).unbind("keyup",HotKeyControler.Keyup);
    $(document).bind("keyup",HotKeyControler.Keyup);
});

/* DEMO 註冊當按下 Konami(上上下下左右左右BA)時要觸發的事件 */
HotKeyControler.RegisterTest("Konami"
    ,function(event,code) { /* 上上下下左右左右BA */
        if (code.length!=10) return false;
        if (code[0]==38 && code[1]==38 && code[2]==40 && code[3]==40 &&
            code[4]==37 && code[5]==39 && code[6]==37 && code[7]==39 &&
            code[8]==66 && code[9]==65) {
            return true;
        }
        return false;
    }
    ,function() {
        alert('上上下下左右左右BA!!!');
    }
);