吉特倉儲管系統(開源WMS)--分享兩月如何做到10W+的專案

賀臣發表於2016-10-12

 

  在此文開篇之處先特別申明,此文在有些人的眼中會有廣告的嫌疑,但是本人不想將其作為一個廣告宣傳的文章,在此提到軟體內容部分請大家予以諒解和包含,作為時間不算短的程式設計師給大家分享一些自己開發吉特倉儲管理軟體相關的經驗和坑,當然還有一些自己從中獲利的方式,不能說給大家指條明路吧,算是作為程式開發人的相互經驗交流。

  此文字來想寫在國慶假期之前的,但是那段時間公司事情剛好很忙,所以沒有來得及寫此文。當時要搞Solr搜尋引擎,因為自己不熟悉java程式所以在弄得過程中有些費力,而且自己本身也不是一心苦心專研程式技術的人,但我絕對是一位苦心專研程式的人,雖然最終也沒有什麼大的成果。去年公司沒有幹之後,一直到今年現在我一直在想為何我們公司會做不下去,就連一個小公司都做不下去,其實我們也很努力,我們也很拼命,創業之初一週基本上四天會睡在辦公室,每日每夜的做專案,那個時候是有客戶的,但是到了最後發現每個專案都不賺錢,為什麼時間太長了,不能快速解決客戶問題[這只是其中一個很小的原因],當時確定了公司的方向就是要做倉儲物流製造方面的軟體產品。雖然我知道這個行業競爭很大,很多人說現在的市場有很多成熟的產品,這個不說我也自然明白,每個人都有自己的想法,也有自己針對的市場方向,我們在此不多說。

 

  一. 深入一個行業

    最開始做開發的時候涉及到過好多行業,汽車零配件,金融單位,遊戲行業,房地產行業,一直做著感覺最終除了會寫程式碼好像啥也不會,一個行業的套路你都還沒有弄清楚感覺你就落伍了(主要是幹個幾年感覺工資跟不上了,只能跳槽增加工資咯,其實我跳槽沒有一次是加過工資的,因為我膽小,只要認為自己過得去就行了,最重要的是覺得自己乾的值)。不是每個技術員都可以開公司的,但是很多技術員都給別人做過私活,感覺工作之餘賺點小錢還是很不錯的,在此之前我也幹過感覺挺牛逼的.其實後來我也做的少,我也去問過很多人他們基本一致的答案:私活真的是賺的辛苦錢,每天沒日沒夜的,真不如上班。 這個的確是如此的,我已人格保證, 所以現在剛出道的程式設計師們如果手上沒有什麼貨的話,真的不要輕易去接什麼私活。

    1. 你跳槽那麼多次,多少年之後你發現你比別人強在哪裡?

    這個問題好容易戳人的心窩子,特別是做過外包的同學感觸肯定很深,今天這個專案需要我那我不顧一切撲上去,下一個專案需要我被迫撲上去。國慶回家的時候,火車上三個湖南小夥,聽聊天得知是幹程式的,其中一人說我做過某某軍工企業的什麼軟體,我做過某某銀行的什麼軟體,我還做了某個***企業什麼的軟體, 其實我只是聽聽,我基本知道這個小夥是幹外包專案的,然後還有一個攜程的小夥,他們聊了很多我一直不說話,但是他們都談到了一個問題就是這些年下來感覺除了會寫程式碼,好像就幹不了其他的,如果換了一個行業或者一個專案組又好像從頭開始了。

    其實這就是問題的所在,因為你在短期內不斷的在切換你不熟知的領域,你還沒有足夠的時間來深入瞭解這個行業的特性,也就使得你不能夠在這個行業成為專家,所以你最終的結果就是可替代性很強,程式設計師現在大把大把的,上海地鐵上隨便抓一大把,替換你分分鐘的事情,就說攜程招人和離職的速度來看就足以說明這個問題。在此之前我也有過類似的感受,感覺到頭來啥啥都不是,我等天之愚鈍之人不可能做到技術牛逼之人,發揮不了自己的智商。那就只能在某個小的領域專心做事,做精做強。

    之前我特別羨慕一個朋友,他是做銷售的,其實他所做的事情遠不是一個銷售人員能夠達到的了,他之前是做貨架,自動門等銷售的,一直做了十來年吧,後面我經常跟著他一起去跑客戶,我說讓他帶我我見世面,到客戶那邊之後他所做的事情遠不是銷售給客戶介紹產品,推薦價格。到了客戶倉庫他會給客戶做專業的倉庫規劃測量,他隨身帶著捲尺,測距儀,黃膠帶等等一系列東西,儼然就是一個工程師的範,事情做得非常的專業到位,但是我見他很少直接給客戶推薦某個產品,然後還會給客戶給出規劃的圖紙設計圖。當時我很是驚呆,這個銷售的確做的到位,後面我問他是不是學這個行業的,他說他從來沒有學過這些,只是在這個行業一直做就慢慢的專業了。再後來我有認識了另外一個做同樣銷售的銷售員,他也就是一個普通的銷售員了,我跟他去看客戶現場他也就只知道拿著產品說明書給客戶介紹產品了,差距自然不說,後面這位就沒有幹這一行了,我問他為什麼,他說這個行業賺不到錢。那個朋友現在上海兒女雙全,有車有房,相信比一般的程式設計師要好很多的。後面他說自己做貨架銷售,在他們公司做了一個業績全國第一,好像是這樣的。總之給我的感覺他做這個事"專業",他也給過我一個建議:“好好的幹一行” 

 

    2. 你為什麼成為不了專家?

    前不久上海嘉定那邊一個工廠的客戶老闆打電話我,說你好像很久沒有到我們公司來坐坐了,你啥時候有空到我們公司來坐坐啊,我們聊聊人生。其實這位老闆年長我很多,為人很精明。我問他是不是軟體出問題了,他說不是的,他說像找你聊聊倉庫生產方面的事情。當他電話我的時候我第一個反應就是軟體出問題了,後面聊下來他覺得流程比軟體更重要吧,他是想讓我幫他們想想如何優化生產流程以及倉庫作業。其實我不太想搭理這個客戶,具體原因不說了.但是客戶能夠給我打電話我還是挺高興的,其實他大可以去找別人或者其他的供應商來處理這個事情。他說事情我一點就通,他們說的生產倉庫問題我很快就能夠知道他們說的什麼問題,並且給出合理的建議,他們覺得我很專業。 其實我不能自詡自己是專業,當時決定做了這個方面的事情之後,我的確花了很大的精力來了解這個行業的知識,包括現場實地考察。

    什麼是專家, 我個人覺得就是在這個行業有資深經驗, 專 就是專業, 家 不好怎麼解釋,反正畫家,書法家等比較有威望吧,最關鍵的是能夠在關鍵時刻關鍵點起到關鍵作用, 當然不能說我們國家某某部門某某專家了,這種話不要多說。專家能夠刻畫到點子上,戳中要點一針見血,找出良方,這就是專家。專家除了苦學那就是經驗積累,遇到的問題越多後面你能夠解決問題的機率就越大,久而久之你就是專家了。其實現在很多做技術的今天學.NET,明天又是Java,再來說PHP(拍黃片)是世界上最好的語言, 久而久之其實他也就是一個普通寫程式碼的,幹了10年還是隻能寫淺顯的程式碼。

 

  二. 專案開發你為何這麼慢

    我自己一句有一句調侃自己的話:"每個人都說時間就是金錢,你的時間真的值錢麼,如果在特定的時間內你沒有創造價值那麼你的時間就不值錢" 。 好像有點過分,但對於我自己來說是有道理的

    1. 為何一個簡單的進銷存你都要做三個月

    這是我當年遇到的很現實的問題,先不說價錢問題。別人一個月就可以上線執行,為何你一個簡單的進銷存要做三個月,我們仔細羅列這些功能:(1) 系統設定 (2) 使用者管理 (3) 部門管理 (4) 角色管理 (4)產品管理 (6) 價格管理 (7) 客戶管理 (8) 供應商管理 .....我實在不想說下去了,簡單的進銷存確實不止這些功能,在怎麼簡單你得讓客戶流程能夠跑起來才行啊。

    再從技術方面考慮: 這得有多少張表啊,資料庫操作好多程式碼啊,還要設計前段UI,做的互動性好還要大量的JS,想想這些工作量3個月真的一點不多。唉,為什麼我就沒有一套可以複用的東西呢?

    對的,因為你沒有可以複用的東西,所以你慢,你專案進度不能快也就在情理之中了

    

    2.怎麼就沒有公共元件

    等你確定好做一個行業軟體的開發之後,技術體系基本確定之後,開發還是慢啊。我要彈出框,系統規劃中沒有這個元件,我要從網上找。  好多個頁面要選擇使用者資訊,彈出頁面,第一個頁面湊合著做吧,忍了,又來一個這樣的頁面需求,又忍了, 越來越多,忍無可忍,這種業務元件你為何不封裝成公共的呢。

; (function ($) {
    $.fn.ProductDialog = function (options) {
        var defaultOption = {
            data: {},
            Mult: true,
            EventName: "dblclick",
            callBack: undefined
        };
        defaultOption = $.extend(defaultOption, options);

        var DataServer={
            Server: function () {
                var config = (function () {
                    var URL_GetList = "/Storage/ProductAjax/GetList";
                    return {
                        URL_GetList: URL_GetList
                    };
                })();

                //資料操作服務
                var dataServer = (function ($, config) {
                    //查詢分頁列表
                    var GetList=function(data,callback){
                        $.gitAjax({
                            url: config.URL_GetList,
                            data: data,
                            type: "post",
                            dataType: "json",
                            success: function (result) {
                                if(callback!=undefined && typeof callback=="function"){
                                    callback(result);
                                }
                            }
                        });
                    }

                    return {
                        GetList: GetList
                    }

                })($, config);
                return dataServer;
            },
            PageClick:function(PageIndex,PageSize){
                $.jBox.tip("正在努力載入資料...","loading");
                var Server=DataServer.Server();
                var search={};
                search["PageIndex"]=PageIndex;
                search["PageSize"]=PageSize;
                Server.GetList(search,function(result){
                    DataServer.SetTable(result);
                    $.jBox.closeTip();
                });
            },
            SetTable:function(result){
                current.find("#tabInfo").DataTable({
                    destroy: true,
                    data:result.Result,
                    paging:false,
                    searching:false,
                    scrollX: false,
                    bAutoWidth:false,
                    bInfo:false,
                    ordering:false,
                    columns: [
                        { data: 'SnNum' ,render:function(data, type, full, meta){
                            return "<input type='checkbox' name='product_item' value='"+data+"' data-full='"+JSON.stringify(full)+"'/>";
                        }},
                        { data: 'BarCode'},
                        { data: 'ProductName',createdCell:function(td, cellData, rowData, row, col){
                            if(!git.IsEmpty(cellData) && cellData.length>10){
                                $(td).popover({container:"body",content:cellData,trigger:"hover",placement:"bottom",delay:{show:500}});
                            } 
                        },render:function(data,type,full,meat){
                            return git.GetStrSub(data,10);
                        }},
                        { data: 'Size',render:function(data,type,full,meta){
                            return git.GetStrSub(data,10);
                        },createdCell:function(td, cellData, rowData, row, col){
                            if(!git.IsEmpty(cellData) && cellData.length>10){
                                $(td).popover({container:"body",content:cellData,trigger:"hover",placement:"bottom",delay:{show:500}});
                            } 
                        }},
                        { data: 'CateName'},
                        { data: 'UnitName'},
                        { data: 'MaxNum'},
                        { data: 'MinNum'},
                        { data: 'AvgPrice'},
                        { data: 'Display',render:function(data,type,full,meta){
                            return git.GetStrSub(data,10);
                        },createdCell:function(td, cellData, rowData, row, col){
                            if(!git.IsEmpty(cellData) && cellData.length>10){
                                $(td).popover({container:"body",content:cellData,trigger:"hover",placement:"bottom",delay:{show:500}});
                            } 
                        }}
                    ],
                    aoColumnDefs:[
                        { "sWidth": "15px",  "aTargets": [0] }
                    ],
                    oLanguage:{
                        sEmptyTable:"沒有查詢到任何資料"
                    }
                });
                var pageInfo=result.PageInfo;
                if(pageInfo!=undefined){
                    current.find("#myMinPager").minpager({ pagenumber: pageInfo.PageIndex, recordCount: pageInfo.RowCount, pageSize: pageInfo.PageSize, buttonClickCallback: DataServer.PageClick });
                }

                DataServer.BindEvent();
            },
            BindEvent:function(){
                if(defaultOption.Mult){
                    current.find("#tabInfo").find("input[name='item_all']").click(function(event) {
                        var flag=$(this).attr("checked");
                        if(flag){
                            current.find("#tabInfo").find("input[name='product_item']").attr("checked",true);
                        }else{
                            current.find("#tabInfo").find("input[name='product_item']").attr("checked",false);
                        }
                    });
                }
                else{
                    current.find("#tabInfo").find("input[name='item_all']").hide();
                    current.find("#tabInfo").find("input[name='product_item']").click(function(event) {
                        current.find("#tabInfo").find("input[name='product_item']").attr('checked', false);
                        $(this).attr("checked",true);
                    });
                }
                //搜尋
                current.find(".search").find('button[data-command="Search"]').click(function(event) {
                    DataServer.PageClick(1,10);
                });
            },
            GetSelect:function(){
                var list=[];
                current.find("#tabInfo").find("input[name='product_item']").each(function(i,item){
                    var flag=$(item).attr("checked");
                    if(flag){
                        var value=$(item).attr("data-full");
                        list.push(JSON.parse(value));
                    }
                });
                return list;
            }
        }

        var submit = function (v, h, f) {
            if (v == 1) {
                var list=DataServer.GetSelect();
                if(list==undefined || list.length==0){
                    $.jBox.tip("請選擇產品","warn");
                    return false;
                }
                if (defaultOption.callBack != undefined && typeof (defaultOption.callBack) == "function") {
                    if(defaultOption.Mult){
                        defaultOption.callBack(list);
                    }else{
                        defaultOption.callBack(list[0]);
                    }
                }
            }
        };
        
        $(this).bind(defaultOption.EventName, function () {
            $.jBox.open("get:/Storage/Product/Dialog", "選擇產品", 850, 600, {
                buttons: { "選擇": 1, "關閉": 2 }, submit: submit, loaded: function (h) {
                    current=h;
                    DataServer.PageClick(1,10);
                }
            });
        });
    };
})(jQuery);
選擇產品的公共元件

    

     類似於這種的元件,在專案中有大量使用,如果能夠做到全域性公共並且方便使用這就是極佳的。最終形成元件也是有發展歷程的: 開始只是一個簡單的額對話方塊顯示錶格並且選中產品,慢慢的又有更多的需求,比如要能夠搜尋,要能夠分頁,要支援多選以及單選等等,雙幾行能夠自動選擇等一系列問題。其實你做好了公共元件在後期的開發中也就省事了。

 

    3. 程式碼的套路

    10個人就有10種程式碼風格,這種相互改程式碼會讓人崩潰,人員的離職,沒有文件和註釋的說明,欄位和資料庫的對應不起來,各種讓人吐的問題。不要太多的花樣,定義好套路,所有的程式碼必須遵循這種套路你會快很多,最關鍵的是找bug的速度回快很多。

/**
*
*財務管理-應收實收
*
**/

var FinanceBillManager = {
    Server: function () {
        var config = (function () {
            var URL_Add = "/Finance/BillAjax/AddRec";
            var URL_GetList = "/Finance/BillManagerAjax/GetList";
            var URL_GetDetail = "/Finance/BillManagerAjax/GetDetail";
            var URL_Delete = "/Finance/BillManagerAjax/Delete";
            var URL_Cancel = "/Finance/BillManagerAjax/Cancel";
            var URL_Audite = "/Finance/BillManagerAjax/Audite";
            var URL_ToExcel = "/Finance/BillManagerAjax/ToExcel";

            var URL_AddPay = "/Finance/BillAjax/AddPay";
            var URL_PayDetail = "/Finance/BillAjax/AddRec";

            return {
                URL_GetList: URL_GetList,
                URL_Add: URL_Add,
                URL_Delete: URL_Delete,
                URL_Cancel: URL_Cancel,
                URL_Audite: URL_Audite,
                URL_ToExcel: URL_ToExcel,

                URL_AddPay: URL_AddPay,
                URL_PayDetail: URL_PayDetail,
            };
        })();

        //資料操作服務
        var dataServer = (function ($, config) {

            //查詢分頁列表
            var Add=function(data,callback){
                $.gitAjax({
                    url: config.URL_Add,
                    data: data,
                    type: "post",
                    dataType: "json",
                    success: function (result) {
                        if(callback!=undefined && typeof callback=="function"){
                            callback(result);
                        }
                    }
                });
            }

            var GetList=function(data,callback){
                $.gitAjax({
                    url: config.URL_GetList,
                    data: data,
                    type: "post",
                    dataType: "json",
                    success: function (result) {
                        if(callback!=undefined && typeof callback=="function"){
                            callback(result);
                        }
                    }
                });
            }

            var Delete=function(data,callback){
                $.gitAjax({
                    url: config.URL_Delete,
                    data: data,
                    type: "post",
                    dataType: "json",
                    success: function (result) {
                        if(callback!=undefined && typeof callback=="function"){
                            callback(result);
                        }
                    }
                });
            }

            var Cancel=function(data,callback){
                $.gitAjax({
                    url: config.URL_Cancel,
                    data: data,
                    type: "post",
                    dataType: "json",
                    success: function (result) {
                        if(callback!=undefined && typeof callback=="function"){
                            callback(result);
                        }
                    }
                });
            }

            var Audite=function(data,callback){
                $.gitAjax({
                    url: config.URL_Audite,
                    data: data,
                    type: "post",
                    dataType: "json",
                    success: function (result) {
                        if(callback!=undefined && typeof callback=="function"){
                            callback(result);
                        }
                    }
                });
            }

            var ToExcel=function(data,callback){
                $.gitAjax({
                    url: config.URL_ToExcel,
                    data: data,
                    type: "post",
                    dataType: "json",
                    success: function (result) {
                        if(callback!=undefined && typeof callback=="function"){
                            callback(result);
                        }
                    }
                });
            }


            var AddPay=function(data,callback){
                $.gitAjax({
                    url: config.URL_AddPay,
                    data: data,
                    type: "post",
                    dataType: "json",
                    success: function (result) {
                        if(callback!=undefined && typeof callback=="function"){
                            callback(result);
                        }
                    }
                });
            }

            return {
                Add: Add,
                GetList: GetList,
                Delete: Delete,
                Cancel: Cancel,
                Audite: Audite,
                ToExcel: ToExcel,
                AddPay: AddPay,
            }

        })($, config);
        return dataServer;
    },
    PageClick:function(PageIndex,PageSize){
        $.jBox.tip("正在努力載入資料...","loading");
        var Server=FinanceBillManager.Server();
        var search=FinanceBillManager.GetSearch();
        search["PageIndex"]=PageIndex;
        search["PageSize"]=PageSize;
        Server.GetList(search,function(result){
            FinanceBillManager.SetTable(result);
            $.jBox.closeTip();
        });
    },
    Refresh:function(){
        var PageSize=$("#mypager").pager("GetPageSize");
        var PageIndex=$("#mypager").pager("GetCurrent");
        $.jBox.tip("正在努力載入資料...","loading");
        var Server=FinanceBillManager.Server();
        var search=FinanceBillManager.GetSearch();
        search["PageIndex"]=PageIndex;
        search["PageSize"]=PageSize;
        Server.GetList(search,function(result){
            FinanceBillManager.SetTable(result);
            $.jBox.closeTip();
        });
    },
    SetTable:function(result){
        $("#tabList").DataTable({
            destroy: true,
            data:result.Result,
            paging:false,
            searching:false,
            scrollX: false,
            bAutoWidth:false,
            bInfo:false,
            ordering:false,
            columns: [
                { data: 'SnNum' ,render:function(data, type, full, meta){
                    return "<input type='checkbox' name='bill_item' value='"+data+"' data-full='"+JSON.stringify(full)+"'/>";
                }},
                { data: 'BillNum'},
                { data: 'Title'},
                { data: 'CateName'},
                { data: 'FromName'},
                { data: 'Amount',render:function(data,type,full,meta){
                    return git.ToDecimal(data,2);
                }},
                { data: 'RealPayAmount',render:function(data,type,full,meta){
                    return git.ToDecimal(data,2);
                }},
                { data: 'LeavAmount',render:function(data,type,full,meta){

                    if(parseFloat(data)>0){
                        return '<span class="label label-warning">'+git.ToDecimal(data,2)+'</span>';
                    }
                    else{
                        return "0.00";
                    }
                }},
                { data: 'Status',render:function(data,type,full,meta){
                    if(data==EFinanceStatusJson.Pass){
                        return '<span class="label label-success">'+git.GetEnumDesc(EFinanceStatus,data)+'</span>';
                    }else if(data==EFinanceStatusJson.NotPass){
                        return '<span class="label label-warning">'+git.GetEnumDesc(EFinanceStatus,data)+'</span>';
                    }else{
                        return git.GetEnumDesc(EFinanceStatus,data);
                    }
                }},
                { data: 'CreateTime',render:function(data,type,full,meta){
                    return git.JsonToDateTime(data);
                }},
                { data:"ID",render:function(data,type,full,meta){
                    var html="";
                    if(full.Status==EFinanceStatusJson.Wait || full.Status==EFinanceStatusJson.NotPass){
                        html+='<a class="edit" href="javascript:void(0)">編輯</a>&nbsp;';
                    }
                    if(full.Status==EFinanceStatusJson.Wait || full.Status==EFinanceStatusJson.NotPass){
                        html+='<a class="audite" href="javascript:void(0)">稽核</a>&nbsp;';
                    }
                    html+='<a class="view" href="javascript:void(0)">檢視</a>&nbsp;';

                    if(full.Status==EFinanceStatusJson.Wait || full.Status==EFinanceStatusJson.NotPass){
                        html+='<a class="delete" href="javascript:void(0)">刪除</a>&nbsp;';
                    }
                    
                    if(full.Status==EFinanceStatusJson.Pass || full.Status==EFinanceStatusJson.PayPart){
                        html+='<a class="pay" href="javascript:void(0)">收款</a>&nbsp;';
                    }
                    return html;
                }}
            ],
            aoColumnDefs:[
                { "sWidth": "15px",  "aTargets": [0] }
            ],
            oLanguage:{
                sEmptyTable:"沒有查詢到任何資料"
            }
        });

        var pageInfo=result.PageInfo;
        if(pageInfo!=undefined){
            $("#mypager").pager({ pagenumber: pageInfo.PageIndex, recordCount: pageInfo.RowCount, pageSize: pageInfo.PageSize, buttonClickCallback: FinanceBillManager.PageClick });
        }

        //繫結編輯 刪除事件
        FinanceBillManager.BindEvent();
    },
    BindEvent:function(){

        $("#tabList").find("tbody td").popover({
            container:"body",
        });

        $("#tabList").find("a.edit").click(function(){
            var SN=$(this).parent().parent().find("input[name='bill_item']").val();
            FinanceBillManager.Detail(SN,"Edit");
        });

        //稽核
        $("#tabList").find("a.audite").click(function(){
            var SN=$(this).parent().parent().find("input[name='bill_item']").val();
            FinanceBillManager.Detail(SN,"Audite");
        });

        //檢視
        $("#tabList").find("a.view").click(function(){
            var SN=$(this).parent().parent().find("input[name='bill_item']").val();
            FinanceBillManager.Detail(SN,"View");
        });

        $("#tabList").find("a.delete").click(function(){
            var SN=$(this).parent().parent().find("input[name='bill_item']").val();
            var submit=function(v,h,f){
                if(v=="ok"){
                    var list=[];
                    list.push(SN);
                    var param={};
                    param["list"]=JSON.stringify(list);
                    var Server=FinanceBillManager.Server();
                    Server.Delete(param,function(result){
                        $.jBox.tip(result.Message,"success");
                        var pageSize=$("#mypager").pager("GetPageSize");
                        FinanceBillManager.PageClick(1,pageSize);
                    });
                }
            }
            $.jBox.confirm("確定要刪除嗎?", "提示", submit);
        });


        //付款
        $("#tabList").find("a.pay").click(function(){
            var SN=$(this).parent().parent().find("input[name='bill_item']").val();
            FinanceBillManager.Pay("",SN,"Add");
        });
    },
    SelectAll:function(item){
        var flag=$(item).attr("checked");
        if(flag){
            $("#tabList").find("input[name='bill_item']").attr("checked",true);
        }else{
            $("#tabList").find("input[name='bill_item']").attr("checked",false);
        }
    },
    GetSelect:function(){
        var list=[];
        $("#tabList").find("input[name='bill_item']").each(function(i,item){
            var flag=$(item).attr("checked");
            if(flag){
                var value=$(item).val();
                list.push(value);
            }
        });
        return list;
    },
    GetSearch:function(){
        var searchBar=$("div[data-condition='search']");

        var BillNum=searchBar.find("input[name='BillNum']").val();
        var CateNum=searchBar.find("select[name='CateNum']").val();
        var FromName=searchBar.find("input[name='FromName']").val();
        var Title=searchBar.find("input[name='Title']").val();
        var ContractNum=searchBar.find("input[name='ContractNum']").val();
        var Status=$("div[data-group='Status']").find("button.disabled").attr("value");
        var BeginTime=searchBar.find("input[name='BeginTime']").val();
        var EndTime=searchBar.find("input[name='EndTime']").val();
        var search={};
        search["BillNum"]=BillNum;
        search["CateNum"]=CateNum;
        search["FromName"]=FromName;
        search["Title"]=Title;
        search["ContractNum"]=ContractNum;
        search["Status"]=Status;
        search["BeginTime"]=BeginTime;
        search["EndTime"]=EndTime;

        return search;
    },
    Detail:function(SnNum,Command){
        var currentSnNum=SnNum;
        var submit=function(v,h,f){
            if(v=="1"){
                var BillNum=h.find('input[name="BillNum"]').val();
                var SnNum=h.find('input[name="SnNum"]').val();
                var CateNum=h.find('select[name="CateNum"]').val();
                var FromName=h.find('input[name="FromName"]').val();
                var ToName=h.find('input[name="ToName"]').val();
                var Amount=h.find('input[name="Amount"]').val();
                var PrePayCount=h.find('input[name="PrePayCount"]').val();
                var Remark=h.find('input[name="Remark"]').val();
                var Title=h.find('input[name="Title"]').val();
                var Remark=h.find('input[name="Remark"]').val();
                var LastTime=h.find('input[name="LastTime"]').val();
                var Title=h.find('input[name="Title"]').val();

                var param={};
                param["BillNum"]=BillNum;
                param["SnNum"]=SnNum;
                param["CateNum"]=CateNum;
                param["FromName"]=FromName;
                param["ToName"]=ToName;
                param["Amount"]=Amount;
                param["PrePayCount"]=PrePayCount;
                param["Status"]=1; //稽核通過
                param["Remark"]=Remark;
                param["LastTime"]=LastTime;
                param["Title"]=Title;

                var Server=FinanceBillManager.Server();
                Server.Add(param,function(result){
                    $.jBox.tip(result.Message,"success");
                    var pageSize=$("#mypager").pager("GetPageSize");
                    FinanceBillManager.PageClick(1,pageSize);
                }); 
            }else if(v=="2"){
                
            }else if(v=="3"){
                var param={};
                param["SnNum"]=currentSnNum;
                param["Status"]=2; //稽核通過
                var Server=FinanceBillManager.Server();
                Server.Audite(param,function(result){
                    $.jBox.tip(result.Message,"success");
                    var pageSize=$("#mypager").pager("GetPageSize");
                    FinanceBillManager.PageClick(1,pageSize);
                }); 
            }else if(v=="4"){
                var param={};
                param["SnNum"]=currentSnNum;
                param["Status"]=3; //稽核通過
                var Server=FinanceBillManager.Server();
                Server.Audite(param,function(result){
                    $.jBox.tip(result.Message,"success");
                    var pageSize=$("#mypager").pager("GetPageSize");
                    FinanceBillManager.PageClick(1,pageSize);
                }); 
            }
        }
        
        // 1確定 2 關閉  3 稽核通過 4 稽核不通過
        if(Command=="View"){
            $.jBox.open("get:/Finance/Bill/AddRec?SnNum="+SnNum, "檢視應收", 670, 350, { buttons: {"關閉":2 } ,submit:submit,loaded:function(h){
                h.find('input,select').attr('disabled',true);
            }}); 
        }else if(Command=="Audite"){
            $.jBox.open("get:/Finance/Bill/AddRec?SnNum="+SnNum, "稽核應收", 670, 350, { buttons: {"稽核通過": 3, "稽核不通過": 4,"關閉":2 } ,submit:submit,loaded:function(h){
                h.find('input,select').attr('disabled',true);
            }}); 
        }else if(Command=="Add"){
            $.jBox.open("get:/Finance/Bill/AddRec", "新增應收", 670, 350, { buttons: {"確定": 1, "關閉":2 } ,submit:submit,loaded:function(h){
                
            }}); 
        }else if(Command=="Edit"){
            $.jBox.open("get:/Finance/Bill/AddRec?SnNum="+SnNum, "編輯應收", 670, 350, { buttons: {"確定": 1, "關閉":2 } ,submit:submit,loaded:function(h){
                
            }}); 
        }
    },
    Pay:function(SnNum,BillSnNum,Command){
        var currentSnNum=SnNum;
        var submit=function(v,h,f){
            if(v=="1"){
                var PayNum=h.find('input[name="PayNum"]').val();
                var SnNum=h.find('input[name="SnNum"]').val();
                var PayType=h.find('select[name="PayType"]').val();
                var BankName=h.find('input[name="BankName"]').val();
                var Amount=h.find('input[name="Amount"]').val();
                var PayTime=h.find('input[name="PayTime"]').val();
                var Remark=h.find('input[name="Remark"]').val();
                var BillSnNum=h.find('input[name="BillSnNum"]').val();

                var param={};
                param["PayNum"]=PayNum;
                param["SnNum"]=SnNum;
                param["PayType"]=PayType;
                param["BankName"]=BankName;
                param["Amount"]=Amount;
                param["PayTime"]=PayTime;
                param["Remark"]=Remark;
                param["BillSnNum"]=BillSnNum;

                var Server=FinanceBillManager.Server();
                Server.AddPay(param,function(result){
                    $.jBox.tip(result.Message,"success");
                    var pageSize=$("#mypager").pager("GetPageSize");
                    FinanceBillManager.PageClick(1,pageSize);
                }); 
            }else if(v=="2"){
                
            }
        }
        
        // 1確定 2 關閉
        if(Command=="Add"){
            $.jBox.open("get:/Finance/Bill/AddPay?BillSnNum="+BillSnNum, "新增實收", 670, 300, { buttons: {"確定": 1, "關閉":2 } ,submit:submit,loaded:function(h){
                
            }}); 
        }else if(Command=="Edit"){
            $.jBox.open("get:/Finance/Bill/AddPay?BillSnNum="+BillSnNum+"&SnNum="+SnNum, "編輯實收", 670, 300, { buttons: {"確定": 1, "關閉":2 } ,submit:submit,loaded:function(h){
                
            }}); 
        }
    },
    ToolBar:function(){
        //工具欄按鈕點選事件
        $("div.toolbar").find("a.btn").click(function(){
            var command=$(this).attr("data-command");

            if(command=="Add"){
                FinanceBillManager.Detail(undefined,"Add");
            }else if(command=="Edit"){
                var list=FinanceBillManager.GetSelect();
                if(list.length==0){
                    $.jBox.tip("請選擇要編輯的項","warn");
                    return false;
                }
                var SN=list[0];
                FinanceBillManager.Detail(SN,"Edit");
            }else if(command=="Delete"){
                var submit=function(v,h,f){
                    if(v=="ok"){
                        var list=FinanceBillManager.GetSelect();
                        if(list.length==0){
                            $.jBox.tip("請選擇要刪除的項","warn");
                            return false;
                        }
                        var param={};
                        param["list"]=JSON.stringify(list);
                        var Server=FinanceBillManager.Server();
                        Server.Delete(param,function(result){
                            $.jBox.tip(result.Message,"success");
                            var pageSize=$("#mypager").pager("GetPageSize");
                            FinanceBillManager.PageClick(1,pageSize);
                        });
                    }
                }
                $.jBox.confirm("確定要刪除嗎?", "提示", submit);

            }else if(command=="Excel"){
                var Server=FinanceBillManager.Server();
                var search=FinanceBillManager.GetSearch();
                Server.ToExcel(search,function(result){
                    
                    if(result.Code==1000){
                        var path = unescape(result.Message);
                        window.location.href = path;
                    }else{
                        $.jBox.info(result.Message, "提示");
                    }
                });
            }else if(command=="Refresh"){
                FinanceBillManager.Refresh();
            }

        });

        //搜尋 高階搜尋按鈕
        var searchBar=$("div[data-condition='search']");
        searchBar.find("a[data-command='search']").click(function(){
            FinanceBillManager.PageClick(1,10);
        });
        searchBar.find("button[data-command='Advanced']").click(function(){
            var display=$("div.formsearch").css("display");
            if(display=="block"){
                $("div.formsearch").hide(300);
            }else{
                $("div.formsearch").show(300);
            }
        });
        searchBar.find("button[data-command='Clear']").click(function(){
            //清空搜尋條件
            searchBar.find("input[name='BillNum']").val("");
            searchBar.find("select[name='CateNum']").val("");
            searchBar.find("input[name='FromName']").val("");
            searchBar.find("input[name='Title']").val("");
            searchBar.find("input[name='ContractNum']").val("");
            searchBar.find("input[name='BeginTime']").val("");
            searchBar.find("input[name='EndTime']").val("");
        });


        //狀態按鈕處理
        $("div[data-group='Status']").find("button").click(function(){
            $("div[data-group='Status']").find("button").removeClass("disabled");
            $(this).addClass("disabled");
            FinanceBillManager.PageClick(1,10);
        });
        
        //載入預設資料
        FinanceBillManager.PageClick(1,10);
    }
}
吉特倉儲管系統-JS套路

    在吉特倉儲管理系統中,經過多次的改版之後,最終確定了JS的程式碼風格:

    (1) 不一定要遵循世間的套路什麼駝峰命名,帕斯卡命名,定好符合自己的規則

    (2) 前後端資料傳輸Key名稱儘量和Model或者資料庫欄位一致,雖然違反了一定傳統的命名規則

    (3) 每個模組定義一個物件,所有的操作方法都封裝到物件中

    (4) 儘量做到UI分離,多使用事件驅動

ITopClient client = new TopClientDefault();
            string list = WebUtil.GetFormValue<string>("list");
            string CompanyID = this.CompanyID;
            Dictionary<string, string> dic = new Dictionary<string, string>();
            dic.Add("CompanyID", CompanyID);
            dic.Add("List", list);
            string result = client.Execute(DepartApiName.DepartApiName_Delete, dic);
            return Content(result);
統計的介面訪問方式

    呼叫API使用而程式碼示例

    (1) 所有的程式碼都遵循如上操作方式,不得標新立異再做一套

    (2) 請求API全部是使用POST的方式 (根據自己的情況來,有些人說這種做法不好,我覺得挺好)

    (3) 統一的資料返回格式JSON,也便於做攔截處理,程式碼要結構化

    程式碼套路多,這裡不多說 有興趣去GitHub上下載開源版本的程式碼下來觀摩

 

  三. 八九月我激動過度

    從未如此為專案激動,想著有一天軟體能夠被客戶所能夠接受,八九月的確有點出乎我的意料之外,吉特倉儲管理系統已經不溫不火的做了四年了,改版也不知道多少,雖然也有些客戶在使用,但那終究不是我滿意的結果,不是對客戶不滿意而是我在這些客戶身上沒有獲取真正的價值,因為很多專案都是靠時間堆積起來的。八月九月兩個月客戶諮詢的客戶有點多,最終選擇了幾個客戶合作, 專案金額超10W。 對於我現在一個兼職開發者(為生活所迫)來說的確是一件可喜可賀的事情,不要拿這個跟某某公司來對比,我也從來不想超過哪個公司,我只想認真的做好一個自己想做的軟體。

    10W+ 這遠超過我工資收入,當然忙我也找了一些小夥伴幫忙,但是現在的確要比之前的處理速度要快很多。

    8月初有個山西的客戶聯絡我了,讓我幫其弄一個倉庫系統,說是給他們縣城的小型加工廠使用,也就是雲倉儲。其實正中我下懷, 費用也就2W 我沒有多要價,也想著拿新版本倉儲系統試水, 20來天專案上線使用,客戶爽快付款90%,聽他說之前有個團隊給他們弄了幾個月還不是他想要的。

  

    1. 專案大底開發速度高了很多

      這幾個客戶開發效率自己明顯推進速度高了很多,除了前期自己積累了大量的公共組建,現有成熟的軟體之外,也積累了大量的客戶犯錯案例

    2. 正確的引導客戶

      大多數客戶其實不清楚自己要做什麼東西的,如果對方是程式設計師我估計你會很溝通。首先給客戶一個演示版本,讓客戶看是不是他想要的,如果不是那就不用談了,避免浪費雙方的時間

    3. 合理的建議

      在某種情況下你要決定的相信你的專業能力要比客戶強,給客戶實際的可見的演示軟體,還要給客戶說具體的案例,特別是案例中會出現的問題以及解決方式,合理的處理建議會讓客戶相信你的專業度

    4. 多為客戶考慮

      多為客戶考慮,有時候客戶考慮的功能很少,你要合理的給客戶新增一些業務功能,同時也要刪減一些不必要的需求,你的目的就是為他解決這個問題

    5. 不要一味的談技術

      可能客戶會處於禮貌的聽你說你的技術如何的牛逼,但是客戶絕大時候是聽不懂在說什麼的。 我會問客戶的倉庫規劃情況,銷售情況,列印情況,作業包裝情況等,側面找方式闡述你的解決方案

 

  四. 軟體版權保護

 

    1. 軟體著作權登記證照

    不得不說有些人真的很可惡,吉特倉儲管理系統(GitWMS)之前有一個版本開源之後,自己下載悶聲去做客戶就不說什麼了,但是有些人卻公開出售原始碼,之前還和某平臺鬧過一次。 這裡不多說了,其實當時已經在申請軟體著作權登記,這個登記證照終於下來了。

    

    雖然很多人說這個東西沒鳥用,有時候這個東西是必須的,最起碼在給別人說這個東西是我自己的時候有底氣一點,看程式碼別人不承認是你的東西啊。

 

   2. 如何看待開源問題

    我從內心裡面承認開源給我帶來了很多的讓我意向不到的好處,但是絕大部分來下載此程式碼的人都是程式設計師或者軟體公司,他們問我出售價格以及使用技術,其實我更想聽到合作意向,但是這種聲音少之又少,同時還有更多的人或者企業拿到之後默默的去修改程式碼,去掉版權資訊然後去做自己的客戶,做自己的私單,然後有問題了就來像我諮詢。其實我內心是高興和糾結的,還有人說我是花了10塊錢買的程式碼,留的聯絡方式是你的說可以提供技術支援,10塊錢一頓飯都不夠,而且錢不在我口袋。

    (1). 開源的目的

    我真正開源的目的是想讓更多人瞭解我的專案,供大家一個可以參考學習的案例,最初我也沒有想過有人可以拿著去賣錢或者直接修改給客戶去使用,就算申明不可以給客戶使用有些人還是會默默的修改不露聲色的去給客戶使用,就好比說不能兜售原始碼但是還是有人默默的在兜售原始碼。

    (2). 你為什麼不全部開源

    我開源了所有的業務程式碼,當然我也可以負全部責任的告訴你,新版本的是沒有開源的(不要問我為什麼),以及底層的一些操作基本沒有公開原始碼,我相信程式碼已經流露在外面了。 公開的程式碼是2.0版本的程式碼,其中刪除了一些非倉庫業務的功能,其餘功能模組程式碼完全公開了,保證是一個可以正常執行,流程可以走通的,資料庫結構全部公開的程式,沒有任何的保留。

    總是有人問我質問我,你這些都開源了為什麼還有底層的一些東西不開源,你就得將所有的程式碼全部開源,要不然你開源幹嘛。對於這種質問我很無奈, 打個不恰當的比喻,.NET現在也要開源了,為什麼他不開源他依託的Windows系統, 我想我也是這麼想的。希望大家能夠讀懂我的意思,有時候想做一個好事反而遭來辱罵聲其實心裡很不爽。

 

   3. 開源帶來更多的分享者以及潛在客戶

    其實我並不是那麼在乎有人拿著我東西去做其他的客戶,只希望他們能夠保留我的版權標題等基本資訊,但是很多人做不到。自從開源之後我的確有很多開發者對此表示了興趣,仔細我的人也很多,其實認真去看的人不多。每個人心裡都有這一個心理,開源的東西就是好東西,但是有多少人真正去看過開源的程式碼,更有甚者那些優秀的開源專案你下載之後當作安心石的程式碼你能夠真的看得懂麼。"如果開源你看不懂,那和不開源有什麼區別" ,開源只是為那些懂的人而開源的。  當然也有很多人下載程式碼之後給我提了好多的意見,這些意見都是非常寶貴的,我在此非常的感謝這些人,分享各位的想法和經驗,我才能做得更好。

 

  五. 學習的參考物件

     在部落格園也算混了很久了,其中牛人比較多。至於哪些人是牛人我不多說,在開發吉特倉儲管理系統的時候,我也參考過他們的模式。

    吉日嘎拉: http://www.cnblogs.com/jirigala/   當年他所開發的 通用許可權管理系統

    魏瓊東: http://www.cnblogs.com/eastjade/   AgileEAS.NET SOA 中介軟體平臺,醫療系統開發

    路過秋天: http://www.cnblogs.com/cyq1162/  QBlog 部落格

    李天平: http://www.cnblogs.com/ltp/category/44293.html 程式碼生成器

    麥舒: http://www.cnblogs.com/ansiboy/category/322450.html ALinq 當年也算雄霸一方

    當然還有更多的優秀作品, 我參考過他們的很多運營模式。其中吉日嘎啦和魏瓊東的相對是偏重業務行業性的, 最起碼給我的感覺是這樣的,我沒有深入的去了解過他們的程式碼。其實我個人的側重點也是如此,我更想希望側重於偏重行業性質的軟體開發。

    之前收入一直很低,其實老婆給我了很大的支援,我也非常害怕她說我為什麼不找個正經的工作去幹,天天折騰這些東西。我給她玩笑的說,我說我用吉特倉儲管理系統可以賺到100W, 然後我也就笑笑,她也就笑笑。我說知道你們不會相信我的,我說賺不到100W我賺50W也可以,再不行30W也可以。

    我一直堅信自己能夠做到,當然過去的幾年我也曾一度懷疑自己,雖然現在這個也很遙遠,但是我希望自己依舊堅持。

     

    參考了很多產品,大體上可以分為兩種: (1) 以技術型為主,也就是技術開發框架或者元件, 麥舒的就是典型的案例  (2) 以業務型為主,老魏的醫療系統絕對的偏向行業性質

    且不論這兩者之間各有什麼優缺點,但堅信一點只要你堅持去做持續改進都能夠做出好東西來。

 

    此文有點囉嗦,有不正之處望請諒解,有心在業餘時間發展個人倉儲系統,如果對此感興趣的朋友可以下載開源版本的程式碼研究並和本人交流,希望能夠給到更多更好的建議。

 


作者:情緣
出處:http://www.cnblogs.com/qingyuan/
關於作者:從事倉庫,生產軟體方面的開發,在專案管理以及企業經營方面尋求發展之路
版權宣告:本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結。
聯絡方式: 個人QQ  821865130 ; 倉儲技術QQ群 88718955,142050808 ;
吉特倉儲管理系統 開源地址: https://github.com/hechenqingyuan/gitwms

 

相關文章