單據編碼是ERP系統中必備的功能,用於生成各種單據的流水號,常常借助於日期時間等字符來生成壹個唯壹的單據號碼。
從軟件的角度來說,就是為生成數據表的主鍵值(參考編號),從用戶的角度來說,就是給業務單據制定編碼規範。之後做到見名知意。
舉個例子:比如銷售訂單號是SO201508190001,采購訂單號碼是PO201508190001。
1 基礎單據編碼 Document serialization basic?
單據編碼主表,用於存放單據及其編碼規則。
CREATE?TABLE?[dbo].[DocumentSerialization]([SeriesCode]?[NVARCHAR](8)?NOT?NULL,
[Description]?[NVARCHAR](40)?NOT?NULL,
[Suspended]?[NVARCHAR](1)?NULL,
[SerialLength]?[DECIMAL](2,?0)?NULL,
[PrefixLength]?[DECIMAL](2,?0)?NULL,
[Prefix]?[NVARCHAR](12)?NULL,
[NextSeqNo]?[DECIMAL](10,?0)?NULL,
[AllowOverride]?[NVARCHAR](1)?NULL,
[CreatedDate]?[DATETIME]?NULL,
[CreatedBy]?[NVARCHAR](10)?NULL,
[RevisedDate]?[DATETIME]?NULL,
[RevisedBy]?[NVARCHAR](10)?NULL,
[WithReset]?[NVARCHAR](1)?NULL,
[PrevResetDate]?[DATETIME]?NULL,
[PrefixDefault]?[NVARCHAR](12)?NULL,
CONSTRAINT?[PK_DocumentSerialization]?PRIMARY?KEY?CLUSTERED?(
[SeriesCode]?ASC
)WITH?(PAD_INDEX?=?OFF,?STATISTICS_NORECOMPUTE?=?OFF,?IGNORE_DUP_KEY?=?OFF,?ALLOW_ROW_LOCKS?=?ON,?ALLOW_PAGE_LOCKS?=?ON)?ON?[PRIMARY]
)?ON?[PRIMARY]
GO
舉例說明,這些字段值的含義。
處理銷售訂單功能SLSOSO,它的單據編碼總長度是12,序號前綴長度是6,序號前綴規則是SO@Y@M,@Y表示兩位數的年,@M表示兩位數的月份,下壹個單據編碼流水號是4,所以當產生處理銷售訂單的單據編碼時,它是SO1508000004。
2 宏處理 Macro
有時候我們需要根據情況選擇壹種或多種序列號生成方式,在生成序號的時候彈出窗體,讓我們選擇要哪壹種前綴編碼方案,比如采購訂單的編碼規則,有時候是PO201508180001,有時候是OE201508180001,它們的前綴(Prefix)是不壹樣的。為達到這種目的,我們給DocumentSerialization增加子表。
CREATE?TABLE?[dbo].[DocumentSerializationDetail](
[Index]?[int]?NOT?NULL,
[SeriesCode]?[nvarchar]?(8)?COLLATE?SQL_Latin1_General_CP1_CI_AS?NOT?NULL?,
[Prefix]?[varchar]?(12)?COLLATE?SQL_Latin1_General_CP1_CI_AS?NOT?NULL,
[TextPattern]?[varchar]?(12)?COLLATE?SQL_Latin1_General_CP1_CI_AS?NOT?NULL?CONSTRAINT?,
[NextSeqNo]?[decimal]?(10,?0)?NULL,
[CreatedDate]?[datetime]?NULL,
[CreatedBy]?[varchar]?(10)?COLLATE?SQL_Latin1_General_CP1_CI_AS?NULL,
[RevisedDate]?[datetime]?NULL,
[RevisedBy]?[varchar]?(10)?COLLATE?SQL_Latin1_General_CP1_CI_AS?NULL,
[Suspended]?[varchar]?(1)?COLLATE?SQL_Latin1_General_CP1_CI_AS?NULL
)?ON?[PRIMARY]
GO
ALTER?TABLE?[dbo].[DocumentSerializationDetail]?ADD?CONSTRAINT?[PK_DocumentSerializationDetail]?PRIMARY?KEY?CLUSTERED?([Index],?[SeriesCode])?ON?[PRIMARY]
GO
ALTER?TABLE?[dbo].[DocumentSerializationDetail]?ADD?CONSTRAINT?[FK_DocumentSerializationDetail_DocumentSerialization]?FOREIGN?KEY?([SeriesCode])?REFERENCES?[dbo].[DocumentSerialization]?([SeriesCode])
GO
參考下面的數據例子來理解這個表的含義:
使用問號作為占位符,在運行時彈出窗體讓用戶選擇哪壹種單據編碼方案,用戶選擇PO,則生成PO201508前綴的采購訂單編碼,如用戶選擇OE,則生成OE201508前綴的采購訂單編碼。
為了加深對占位符號的理解,舉例說明以下幾種情況。
1? 前綴定義值是 ?ABC,用戶選擇XY,則返回前綴結果XYABC。?
2? 前綴定義ABC,用戶選擇XY,返回結果前墜XY_ABC,對於不多余的占位符號用下劃線替代。?
3? 前綴定義@D@M@YABC,當前日期是2015年8月19日,則生成的前綴值是150819ABC。
3 基於用戶的需要編碼方案 User-based document serialization
有時候不同的用戶有不同的單據編碼規則,我們需要依照用戶來創建編碼規則。先創建數據庫。
CREATE?TABLE?[dbo].[DocumentSerializationUser](
[Index]?[int]?NOT?NULL,
[SeriesCode]?[nvarchar]?(8)?COLLATE?SQL_Latin1_General_CP1_CI_AS?NOT?NULL?,
[UserId]?nvarchar(10)?NOT?NULL?COLLATE?SQL_Latin1_General_CP1_CI_AS?NOT?NULL?,
[Prefix]?[varchar]?(12)?COLLATE?SQL_Latin1_General_CP1_CI_AS?NOT?NULL,
[TextPattern]?[varchar]?(12)?COLLATE?SQL_Latin1_General_CP1_CI_AS?NOT?NULL?CONSTRAINT?,
[NextSeqNo]?[decimal]?(10,?0)?NULL,
[CreatedDate]?[datetime]?NULL,
[CreatedBy]?[varchar]?(10)?COLLATE?SQL_Latin1_General_CP1_CI_AS?NULL,
[RevisedDate]?[datetime]?NULL,
[RevisedBy]?[varchar]?(10)?COLLATE?SQL_Latin1_General_CP1_CI_AS?NULL,
[Suspended]?[varchar]?(1)?COLLATE?SQL_Latin1_General_CP1_CI_AS?NULL
)?ON?[PRIMARY]
GO
這個表也是序號編碼DocumentSerialization的子表,主鍵增加了用戶編碼字段UserId,記錄每個用戶要編碼規則。
在系統中,優先使用基於用戶的編碼規則,其次是宏替換處理,最後才是應用基礎的編碼規則。
4 並發處理 Concurrency
當兩個並發用戶同時創建或保存壹張同樣的業務單據時,系統會返回兩個相同的單據編碼,產生了並發問題。
A 方案
打開業務功能時,立即為當前單據創建單據編碼,比如產生單據編碼SO15080004,在單據保存時,發現這張單據編碼被其它的用戶使用過,則重新產生壹個新的業務單據編碼SO15080005,如有發現此編碼仍然被占用,依此向下搜尋,直到找到可以保存的單據編碼。
這種方案的優點是總是可以保存單據,缺點是界面中看到的單據編碼,不壹定是最終保存的單據編碼。
B 方案
打開業務功能時,不產生單據編碼,只有在單據保存時才產生單據編碼。避免了單據並發沖突。
這種方案優點是沒有並發沖突,缺點是只有單據保存之後才可以看到單據編碼。
5 編碼規則程序設計 Document serialization programming
在單據保存時,調用接口產生編碼規則,參考下面的程序片段。
EcnEntity?ecn.....if?(ecn.IsNew?&&?seriesCode?!=?string.Empty)
{
IDocumentSerializationManager?serializationManager?=ProxyInstance<IDocumentSerializationManager>(); ecn.EcnNo?=?serializationManager.GetNextSerialNo(sessionId,?seriesCode,?ecn.EcnNo,?ecn);}
如果業務單據的實體保存時發生異常,則需要重置用戶編碼,清除產生的序號編碼。
catch{
adapter.Rollback(); if?(ecn.IsNew?&&?string.CompareOrdinal(ecn.EcnNo,?currentRefNo)?!=?0) { try {ecn.EcnNo?=?currentRefNo;
serializationManager.ResetNextSequenceNo(seriesCode);
} catch { } } throw;}
6 固定編碼規則? Fixed document serialization
以上實現了基於流水號的單據編碼規則,如果單據的編碼規則相對固定,則以上方法行不通。請先閱讀下面的需求說明:
接到客戶訂單,訂立合同編號:HT201508003;接著做合同評審,產生壹個合同評審單號PS201508003;合同評審通過以後,再到ERP系統中做銷售單,銷售單號是XSD201508003;如果壹個合同分三個銷售訂單下單,則會分別產生XSD201508003-01,XSD201508003-02,XSD201508003-03 三個銷售訂單號。繼續為銷售訂單發貨,銷售訂單XSD201508003所產生的發貨單號應該是XSFH201508003,如果銷售訂單XSD201508003分三次發貨,則依次產生的三張銷售發貨單號是XSFH201508003-01,XSFH201508003-02,XSFH201508003-03。
單據編號201508003從銷售合同到銷售發貨都是同壹個單據號,只是編碼前綴不同。
這種編碼方案要求壹個單據號碼貫穿整個流程,單據編號從起始點業務單據傳遞到最終業務單據,僅僅是前綴不同。
要實現這種固定格式的單據編碼,需要對流轉的每個單據進行編程處理,業務單據也應該有固定的下推流程,做不到通用性,但是優點是很明顯的,壹個號碼貫穿整個業務單據,非常清晰明了。
參考:博客園