單據編碼是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
舉例說明,這些字段值的含義。
SeriesCode | Description | Suspended | SerialLength | PrefixLength | Prefix | NextSeqNo |
SLSOSC | Sales Order Cancellation | N | 12 | 6 | SC@Y@M | 55 |
SLSOSO | Sales Order Entry | N | 12 | 6 | SO@Y@M | 4 |
SLSOSQ | Sales Quotations Processing | N | 12 | 6 | SQ@Y@M | 2 |
處理銷售訂單功能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
參考下面的數據例子來理解這個表的含義:
SeriesCode | Prefix | TextPattern | NextSeqNo |
SLSOSO | ??@Y@M | PO | 9 |
SLSOSO | ??@Y@M | OE | 17 |
使用問號作為占位符,在運行時彈出窗體讓用戶選擇哪一種單據編碼方案,用戶選擇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。
銷售合同 |
合同評審 |
銷售訂單 |
銷售發貨 |
備注 |
HT201508003 |
PS201508003 |
一張銷售訂單 XSD201508003 三張銷售訂單 XSD201508003-01 XSD201508003-02 XSD201508003-03 |
一張銷售發貨單 XSFH201508003 二張銷售發貨單 XSFH201508003-01 XSFH201508003-02 |
單據編號201508003從銷售合同到銷售發貨都是同一個單據號,只是編碼前綴不同。 |
這種編碼方案要求一個單據號碼貫穿整個流程,單據編號從起始點業務單據傳遞到最終業務單據,僅僅是前綴不同。
要實現這種固定格式的單據編碼,需要對流轉的每個單據進行編程處理,業務單據也應該有固定的下推流程,做不到通用性,但是優點是很明顯的,一個號碼貫穿整個業務單據,非常清晰明了。