[FEAT] 電子發票改由後台開立:機台只隨 finalize 上報發票輸入

1. TransactionFinalizePayload 新增 InvoicePayload 物件(status/amount/carrier_id/business_tax_id/love_code 等),讓 finalize 一併帶上發票開立輸入。
2. TransactionFinalizeBuilder:有發票需求(invoiceInfo 非空)時,從 InvoiceData 組發票輸入並固定 status=pending 帶進 finalize;機台不再判定開立結果。
3. DispatchDialog:移除機台直連綠界開票,改為進出貨流程僅上報;發票由後台收到 finalize 後建 pending 並去綠界開立(避免機台網路不確定性與等待)。
4. ReportInvoiceInfoStructure:保留 relateNumber 欄位(冪等鍵相關)。
5. 開票邏輯不放在即將淘汰的 HttpAPI(先前暫放 StarCloudAPI 的機台開票方法亦已移除)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
sky121113 2026-06-15 16:18:11 +08:00
parent 7875db4c7d
commit ff65559b4c
4 changed files with 114 additions and 0 deletions

View File

@ -8,6 +8,9 @@ public class ReportInvoiceInfoStructure {
String invoiceDate;
String randomNumber;
String loveCode;
// 綠界 RelateNumber冪等鍵= machineID + flow_id DispatchDialog 開票前產生
// 開票請求與 finalize 上報共用同一值後台據此查詢/補開避免重複開立
String relateNumber = "";
public ReportInvoiceInfoStructure(String reportFlowId, String rtnCode, String rtnMsg, String invoiceNo, String invoiceDate, String randomNumber, String loveCode) {
this.reportFlowId = reportFlowId;
@ -75,6 +78,14 @@ public class ReportInvoiceInfoStructure {
this.loveCode = loveCode;
}
public String getRelateNumber() {
return relateNumber;
}
public void setRelateNumber(String relateNumber) {
this.relateNumber = relateNumber;
}
@Override
public String toString() {
return "ReportInvoiceInfoStructure{" +

View File

@ -35,6 +35,14 @@ public class TransactionFinalizePayload {
/** 每個貨道的出貨結果列表 */
private List<DispenseRecord> dispense;
/**
* 電子發票開立結果選填
* 機台在進入出貨流程時平行向綠界開立結果折進此物件隨 finalize 一起上報
* 若開票尚未回應則 status=pending後台依 relate_number 去綠界查詢/補開
* 無發票需求未帶發票資訊時此欄為 null後台不建立發票
*/
private InvoicePayload invoice;
// =========== Getters & Setters ===========
public String getAction() {
@ -82,6 +90,74 @@ public class TransactionFinalizePayload {
this.dispense = dispense;
}
public InvoicePayload getInvoice() {
return invoice;
}
public void setInvoice(InvoicePayload invoice) {
this.invoice = invoice;
}
// =========== 內部類別電子發票開立結果 ===========
public static class InvoicePayload {
/** 開立狀態issued已開/ pending已送出未回應/ failed綠界回失敗 */
private String status;
/** 綠界 RelateNumber= machineID + flow_id後台查詢/補開用 */
private String relate_number;
/** 發票號碼pending/failed 時為空) */
private String invoice_no;
/** 綠界回應碼1=成功) */
private String rtn_code;
/** 綠界回應訊息 */
private String rtn_msg;
/** 發票開立日期 */
private String invoice_date;
/** 隨機碼 */
private String random_number;
/** 愛心碼(捐贈時) */
private String love_code;
/** 載具號碼 */
private String carrier_id;
/** 統一編號B2B 統編,有打統編時) */
private String business_tax_id;
/** 發票金額 */
private int amount;
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getRelate_number() { return relate_number; }
public void setRelate_number(String relate_number) { this.relate_number = relate_number; }
public String getInvoice_no() { return invoice_no; }
public void setInvoice_no(String invoice_no) { this.invoice_no = invoice_no; }
public String getRtn_code() { return rtn_code; }
public void setRtn_code(String rtn_code) { this.rtn_code = rtn_code; }
public String getRtn_msg() { return rtn_msg; }
public void setRtn_msg(String rtn_msg) { this.rtn_msg = rtn_msg; }
public String getInvoice_date() { return invoice_date; }
public void setInvoice_date(String invoice_date) { this.invoice_date = invoice_date; }
public String getRandom_number() { return random_number; }
public void setRandom_number(String random_number) { this.random_number = random_number; }
public String getLove_code() { return love_code; }
public void setLove_code(String love_code) { this.love_code = love_code; }
public String getCarrier_id() { return carrier_id; }
public void setCarrier_id(String carrier_id) { this.carrier_id = carrier_id; }
public String getBusiness_tax_id() { return business_tax_id; }
public void setBusiness_tax_id(String business_tax_id) { this.business_tax_id = business_tax_id; }
public int getAmount() { return amount; }
public void setAmount(int amount) { this.amount = amount; }
}
// =========== 內部類別訂單資訊 ===========
public static class OrderPayload {

View File

@ -5,6 +5,7 @@ import android.util.Log;
import com.unibuy.smartdevice.MyApp;
import com.unibuy.smartdevice.structure.BuyStructure;
import com.unibuy.smartdevice.structure.InvoiceStructure;
import com.unibuy.smartdevice.structure.ReportFlowInfoStructure;
import com.unibuy.smartdevice.structure.mqtt.TransactionFinalizePayload;
@ -59,6 +60,10 @@ public class TransactionFinalizeBuilder {
this.flowId = flowId;
}
public String getFlowId() {
return flowId;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
@ -133,6 +138,24 @@ public class TransactionFinalizeBuilder {
// --- 出貨記錄 ---
payload.setDispense(new ArrayList<>(dispenseRecords));
// --- 電子發票改由後台開立---
// 機台不開票只把開立輸入載具/愛心碼/統編/金額帶上去狀態固定 pending
// 後台建 pending 發票後派 Job 去綠界開立無發票需求invoiceInfo 為空時不帶後台不建立發票
if (flowInfo.getInvoiceInfo() != null && !flowInfo.getInvoiceInfo().isEmpty()) {
TransactionFinalizePayload.InvoicePayload invoicePayload =
new TransactionFinalizePayload.InvoicePayload();
invoicePayload.setStatus("pending");
invoicePayload.setAmount(flowInfo.getTotalPrice());
InvoiceStructure invoiceData = MyApp.getInstance().getInvoiceData();
if (invoiceData != null) {
invoicePayload.setCarrier_id(invoiceData.getCarrier());
invoicePayload.setBusiness_tax_id(invoiceData.getCustomerIdentifier());
invoicePayload.setLove_code(invoiceData.getLoveCode());
}
payload.setInvoice(invoicePayload);
}
Log.i(TAG, "TransactionFinalizePayload built: flow_id=" + flowId
+ ", order_no=" + order.getOrder_no()
+ ", items=" + order.getItems().size()

View File

@ -262,6 +262,10 @@ public class DispatchDialog extends DialogAbstract {
MyApp.getInstance().getReportFlowInfoData().setFlowType(42);
}
// [MQTT Finalize 重構] 電子發票改由後台開立機台不再直連綠界
// 僅把發票輸入載具/愛心碼/統編/金額 finalize invoice 物件上報TransactionFinalizeBuilder 組裝
// 後台收到 finalize 後建 pending 發票並派 Job 去綠界開立避免機台網路不確定性與等待
MyApp.getInstance().setReportFreeGiftCodeStructure(new ReportFreeGiftCodeStructure("", 0, "", 0, 0, ""));
getLogs().info("BuyList size:" + MyApp.getInstance().getBuyList().size());