Ctrl K

PCE TW GUI MODULE — 銷項開立取號流程與邏輯

對象:產品/顧問/開發/測試
目的:說明「開立銷項發票」時發票號碼如何決定、如何對發票本、成功後回寫哪些欄位
實作:PCE_LIB_AR_BookAllocate.js(入口 resolveInvoiceNumberForIssue)
操作摘要:使用說明 §5.5 · 藍圖 §9.5
開立後主檔上傳狀態(與取號無關、依表單 vacUpload):使用說明 §3.2.1
單元測試:如何自己加 test case
文件日期:2026-09-24
HTML:HTML 版

1. 發生時機

取號發生在開立流程鎖定來源交易之後、建立 PCE 銷項主檔之前。

  1. 使用者在 PCE GUI Portal → 銷項 → 開立發票/折讓單 查詢並勾選 Invoice/Credit Memo。
  2. 按 單張/合併開立,進入憑證開立表單,確認格式、加值中心、發票號碼、發票本分配條件。
  3. 送出後,PCE_LIB_AR_Issue.js 呼叫 bookAllocateLib.resolveInvoiceNumberForIssue(...)。
  4. 取號失敗:丟出錯誤訊息,不建立銷項主檔。
  5. 取號成功:建立主檔/明細,回寫來源交易發票號碼與開立狀態;主檔 狀態=已開立,上傳狀態依表單「是否透過加值中心上傳」為 待上傳 或 不上傳(見 §3.2.1)。格式 36 的主檔 Name 與發票號碼皆為 SEUI+時間戳後 10 碼(共 14 字)。

發票開立(只選 Invoice)格式下拉不含 33/34。開立折讓單(只選 Credit Memo)只顯示 33/34。發票本管理不含 33/34/36。

2. 開立條件從哪裡來

條件來源備註
賣方統編 taxId子公司對應 PCE 營業人不可覆寫
格式 record id docFormatId表單「格式代號」自動取號必填
格式代碼 docFormatCode該格式的代碼值(31/32/35/36…)決定走哪一條路徑
加值中心上傳 vacUpload表單「是否透過加值中心上傳」格式 35、33 可選;其餘鎖成「不上傳」
發票日期 docDate表單「發票日期」自動取號必填;並換算雙月期別
發票號碼表單「發票號碼」僅紙本/35 不上傳可填
部門/類別/自定義配號表單「發票本分配條件」;選填三欄都要與發票本完全相同。空白只對空白。不沿用交易部門/類別

畫面欄位行為(開立 Suitelet Bootstrap 表單,格式看 option data-code):

格式與上傳發票號碼欄
36停用、清空、非必填;開立時由系統產生 SEUI 號碼
35 且加值中心=上傳停用、清空、非必填
35 且加值中心=不上傳,或紙本(31/32 等)可填、必填
33/34(開立折讓單)可手填;留空則系統產生 PCE+時間戳後 13 碼;不找發票本

3. 路徑總覽

由 resolveInvoiceNumberForIssue 依格式與是否手填分流。

flowchart TD
  start[resolveInvoiceNumberForIssue] --> fmt{格式代碼?}
  fmt -->|36| skipFill{有手填號碼?}
  skipFill -->|有| err36[拒絕: 請勿手填]
  skipFill -->|無| ok36[成功: SEUI+時間戳 10 碼 / 不找本]
  fmt -->|35 且上傳| autoGate{有手填?}
  autoGate -->|有| err35[拒絕: 不可手動填寫]
  autoGate -->|無| auto[allocateInvoiceNumber 自動取號]
  fmt -->|其餘紙本或 35 不上傳| paper{有手填?}
  paper -->|無| errReq[拒絕: 請手填發票號碼]
  paper -->|有| manual[applyManualInvoiceNumber 手填反查]

流程圖原文

格式與上傳路徑發票號碼發票本
36(銷項免用統一發票)系統產生SEUI+時間戳後 10 碼(共 14 字);不可手填不找、不回寫
35 且加值中心=上傳自動取號系統配號(字軌 2+流水 8)依條件找本;只取 internal id 最小 且已勾選傳至加值中心
35 且加值中心=不上傳,或紙本(31/32 等)手填必填(例 CA00000075)依號碼反查;只驗已用;不要求接續 last_no

判斷函式:

  • skipsInvoiceNumber:代碼=36
  • requiresAutoInvoiceNumber:代碼=35 且 vacUpload 為 true/T
  • requiresManualInvoiceNumber:有代碼、且不是上兩種
  • isAllowanceDocFormat:代碼=33 或 34

4. 折讓證明單號碼(33/34)

函式:resolveUniqueAllowanceNumber(入口 resolveInvoiceNumberForIssue,格式 33/34 且表單未填號碼時)。

4.1 產生規則

  • 前綴來自該營業人 PCE 營業人系統設定 → 折讓單前綴(custrecord_pce_bizsys_alw_prefix)。英數最多 3 字,使用時轉大寫。空白則不加前綴。
  • 其後接時間戳數字字串的末 13 碼(不足左補 0)。有前綴時合計最多 16 字;無前綴時為 13 字。
  • 與格式 36 的 SEUI 類似,但折讓寫入 custrecord_pce_ar_allowance_no,不寫 custrecord_pce_ar_invoice_no。

4.2 唯一性(查重+重試)

  1. 以 findUsedInvoiceNumber 查銷項主檔與 Invoice/Credit Memo 的發票號、折讓號欄位(與手填折讓相同)。
  2. 若已使用:在同一毫秒時間基礎上,將 13 碼後綴視為整數 +1(mod 10¹³) 再組下一組候選號,最多重試 100 次(MAX_ALLOWANCE_NUMBER_RETRIES)。
  3. 仍無可用號碼 → ALLOWANCE_NO_GENERATE_FAILED(請稍後重試或改手填)。

手填折讓號仍走原邏輯:大寫、最多 16 碼、查重後成功;不經發票本。

5. 自動取號(35+上傳)

函式:allocateInvoiceNumber。

4.1 必要條件

缺一不可:

條件失敗代碼訊息重點
賣方統編SELLER_TAX_ID_REQUIRED無法判定賣方統一編號
格式 record idDOC_FORMAT_REQUIRED請選擇格式代號
可由日期算出期別DOC_DATE_PERIOD_REQUIRED無法依發票日期判定期別
發票日期可解析為日曆日DOC_DATE_INVALID發票日期格式無效

期別:發票日期 → 民國年月 YYYMM → 財政部雙月期別(奇數月進到下一個偶數月,例 11501/11502 皆為 11502)。

4.2 搜尋硬條件(searchCandidateBooks)

只找同時符合:

  • 未停用(isinactive = F)
  • 統編=賣方統編
  • 期別=上一步雙月期別
  • 格式=開立格式
  • 狀態 0-未使用 或 1-使用中(不含 2 用盡、9 停用)
  • custrecord_pce_ar_book_ns_platform=T(是否透過 NS傳至加值中心已勾選)

紙本/35 不上傳的手填反查不要求此勾選。

4.3 配號篩選(記憶體)

部門、類別、自定義配號都走 matchesExactAllocation:開立選了什麼,發票本就要是什麼。空白只對空白,不會改用其他組合的發票本。

開立選擇會用的發票本
部門 A、類別 B、配號空白部門 A、類別 B、配號空白
部門 A、類別空白、配號空白部門 A、類別空白、配號空白
部門 A、類別 B、配號 CC部門 A、類別 B、配號 CC
三欄皆空白三欄皆空白

三欄都要通過。不沿用 NetSuite 交易上的部門/類別。多本完全相同時,仍取 numeric internal id 最小者。 另外再確認 ns_platform 為 T/true(搜尋漏網時的第二道過濾)。

沒有任何一本通過 → NO_ELIGIBLE_BOOK(訊息含統編、期別、部門、類別、自定義配號,並提示至發票本管理確認)。

4.4 多本只留一本

通過篩選後,只取 numeric internal id 最小者(pickBookWithSmallestId)。 這本後面任何驗證失敗,不會改試 id 較大的本。

4.5 對這本做最後驗證

  1. 日期:發票日期 ≥ 該本 last_issue_dt(只比日曆日;同日可接受;尚未取號則不限)。 失敗:DOC_DATE_BEFORE_LAST_ISSUE,例 發票本 501:發票日期(2026/02/15)不可早於最新取號日期(2026/03/01)。
  2. 下一號 computeNextSequenceNo:
    • last_no 空白 → 下一號=起號
    • 否則下一號=last_no + 1(皆先補成 8 碼再加減)
    • 下一號超過訖號 → BOOK_EXHAUSTED:發票本 501:已無可用發票號碼。
  3. 回寫樂觀鎖 commitBookAllocation:再 load 該本,重算下一號必須仍是剛才那一號;日期再驗一次。 已被其他開立取走 → BOOK_NUMBER_CONFLICT:發票本號碼已被其他開立流程取用,請重試。 只對同一本、同一預計號碼重試最多 3 次(MAX_ALLOC_RETRIES),仍不換本。

4.6 成功時寫什麼

  • 發票號碼=字軌 2 碼英文大寫+流水 8 碼(例 CA+00000075 → CA00000075)
  • 發票本 custrecord_pce_ar_book_last_no=本次流水
  • 發票本 custrecord_pce_ar_book_last_issue_dt=本次發票日期
  • 狀態:0 → 1;若本次已是訖號 → 2-使用完畢
  • 銷項主檔 custrecord_pce_ar_invoice_no、來源 Invoice custbody_pce_ar_invoice_no(Credit Memo 開立折讓單則回寫 custbody_pce_ar_allowance_no/custbody_pce_ar_orig_invoice_no,不寫發票號碼)

銷項主檔不連結發票本欄位;本 id 只在取號結果與稽核 log 中。

5. 手填號碼(紙本/35 不上傳)

函式:applyManualInvoiceNumber。 不用 last_no、也不用最新取號日判斷能不能用。只確認「這個號碼還沒被用過,且能唯一對到一本」。

5.1 解析

  • 去空白、去 -、轉大寫
  • 須為字軌 2 碼英文+1~8 位數字(不足 8 碼補零)
  • 例:ca-75 → CA00000075
  • 失敗:INVALID_INVOICE_NUMBER

5.2 已用檢查 findUsedInvoiceNumber

任一處已有同一號碼(不分大小寫正規化後)就拒絕 INVOICE_NO_ALREADY_USED:

  • PCE 銷項主檔 custrecord_pce_ar_invoice_no 或 custrecord_pce_ar_allowance_no(未停用)
  • Invoice/Credit Memo 主列 custbody_pce_ar_invoice_no 或 custbody_pce_ar_allowance_no

5.3 反查發票本

searchBooksByTrack → 再以流水落在起訖範圍篩選:

  • 依字軌搜尋未停用發票本(不限狀態 0/1,也不要求勾選傳至加值中心)
  • 有帶統編/期別/格式時一併縮小範圍(期別可由發票日期推雙月)
  • 表單有填部門/類別/配號時,才套用配號篩選;三欄皆空白則不套

結果:

涵蓋本數代碼處理
0BOOK_NOT_FOUND請確認字軌、起訖號與統編/期別
1—用這本
2 本以上BOOK_AMBIGUOUS請再指定統編、期別或格式代號

5.4 成功時回寫 commitManualBookUsage

  • 流水必須仍在該本起訖範圍內,否則 INVOICE_NO_OUT_OF_RANGE
  • last_no=max(既有 last_no, 本次流水)(避免之後自動取號倒退)
  • last_issue_dt=本次日期(即使比舊的早也會覆寫)
  • 狀態:未使用可改使用中;若 last 已達訖號可改使用完畢

6. 格式 36

  • 不找發票本、不寫 last_no/last_issue_dt
  • 系統產生號碼:SEUI(Sales Exempt from Uniform Invoices)+時間戳後 10 碼,共 14 字(例 SEUI4567890123)
  • 主檔 Name、externalid、custrecord_pce_ar_invoice_no 與來源交易 custbody_pce_ar_invoice_no 皆寫入同一組號碼
  • 若表單仍帶入手填號碼 → MANUAL_INVOICE_NO_NOT_ALLOWED
  • 仍建立銷項主檔/明細,並回寫交易開立狀態

7. 發票本狀態與號碼計算

發票本狀態(customrecord_pce_ar_book_status):

代碼意義自動取號搜尋手填反查
0未使用可可(不限狀態)
1使用中可可
2使用完畢否可(只要號碼落在起訖內且未使用)
9停用否可(未停用紀錄;停用列 isinactive 另當別論)

下一號:

若 last_no 空白 → begin_no
否則 → last_no + 1
若結果 > end_no → 已用盡

日期比對只看年月日,不含時分秒。

8. 錯誤代碼一覽

代碼路徑含義
MANUAL_INVOICE_NO_NOT_ALLOWED36 或 35+上傳卻手填此路徑不可手填
INVOICE_NO_REQUIRED紙本沒填/解析空白須手填
INVALID_INVOICE_NUMBER手填格式錯/組合字軌失敗須字軌 2+流水
SELLER_TAX_ID_REQUIRED自動無賣方統編
DOC_FORMAT_REQUIRED自動無格式
DOC_DATE_PERIOD_REQUIRED自動無法算期別
DOC_DATE_INVALID自動日期無效
NO_ELIGIBLE_BOOK自動搜尋+配號+傳至加值中心勾選後沒有本
DOC_DATE_BEFORE_LAST_ISSUE自動(或回寫再驗)最小 id 本日期擋下;不換本
BOOK_EXHAUSTED自動最小 id 本沒號了;不換本
BOOK_NUMBER_CONFLICT自動回寫下一號已被搶;同一本重試 3 次
BOOK_ALLOCATE_FAILED自動回寫失敗且無更具體代碼
INVOICE_NO_ALREADY_USED手填主檔或 NS 交易已有同一號
BOOK_NOT_FOUND手填沒有本涵蓋該字軌+流水
BOOK_AMBIGUOUS手填多本同時涵蓋
INVOICE_NO_OUT_OF_RANGE手填回寫load 後發現不在起訖內
BOOK_COMMIT_INVALID回寫缺本 id 或流水
ALLOWANCE_NO_INVALID33/34 手填或自動超過 16 碼
ALLOWANCE_NO_GENERATE_FAILED33/34 自動查重重試用盡仍無唯一號碼

開立畫面會直接顯示 error.message。自動取號失敗訊息通常以 發票本 {id}: 開頭。

9. 相關程式與物件

項目位置
取號核心ar/libraries/PCE_LIB_AR_BookAllocate.js
開立呼叫點ar/libraries/PCE_LIB_AR_Issue.js(取號後才 create 銷項主檔)
開立 Suiteletar/suitelets/PCE_SL_AR_Issue.js
表單欄位ar/libraries/PCE_LIB_AR_IssueForm.js
開立畫面ar/libraries/PCE_LIB_AR_IssueHtml.js(Bootstrap 5 全頁)
發票本紀錄customrecord_pce_ar_invoice_book
單元測試test/ar-book-allocate.test.js、test/ar-book-allocate-scenarios.test.js
如何加測試銷項取號單元測試指南

10. 刻意不做的事

  • 自動取號不會在多本間 fallback(不用「使用中優先/字軌排序/起號排序」)。
  • 自動取號不會使用未勾選「是否透過 NS傳至加值中心」的本。
  • 手填不會要求號碼接續 last_no,也不會因日期早於 last_issue_dt 拒絕。
  • 銷項主檔沒有發票本 List/Record 欄位。
  • 取號本身不寫 MIG 匯出檔;一般發票準備主檔上的 10 碼號碼,格式 36 為 14 字 SEUI 號碼。