Merge remote-tracking branch 'origin/feat/general-cart-and-payment' into ui-design
This commit is contained in:
commit
be3c25bbf5
9
.claude/settings.json
Normal file
9
.claude/settings.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(git fetch *)",
|
||||
"Bash(git switch *)",
|
||||
"Bash(grep -n 'is_spring_slot\\\\|protected $fillable\\\\|protected $casts\\\\|'\\\\''settings'\\\\''' app/Models/Machine/Machine.php)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -10,7 +10,7 @@ plugins {
|
||||
// 避免不同尺寸機台間版本號產生衝突。
|
||||
// =========================================================================
|
||||
def verMinor = 1
|
||||
def verPatch = 87
|
||||
def verPatch = 91
|
||||
|
||||
|
||||
|
||||
|
||||
@ -115,11 +115,15 @@ public class MyApp extends Application {
|
||||
// 購買流程模式:basic(多品項購物車)/ employee_card(員工卡)/ pickup_sheet(取貨單),
|
||||
// 由雲端 B014 ShoppingMode 下發,取代舊 project flavor 分家。預設 basic。
|
||||
private String shoppingMode = "basic";
|
||||
// 上傳 log 的寫檔門檻等級。預設 INFO:debug.txt 只留 INFO/WARNING/ERROR/UPDATE,過濾掉 DEBUG/IGNORE 雜訊。
|
||||
// 工程師現場除錯硬體時可設為 DEBUG,讓 hex/協議細節重新寫檔(logcat 一律照印,不受此值影響)。
|
||||
private String logLevel = "INFO";
|
||||
// 機台顯示語系(由雲端 B014 LangSet 下發)。標準標籤(zh-TW/en/...),有序,第 0 個為預設。
|
||||
private java.util.List<String> machineLanguages = new java.util.ArrayList<>();
|
||||
private String defaultLanguage = "";
|
||||
// 商品名稱多語 map(B012 t060v01_i18n),key=商品ID,value=底線代碼→名稱 的 JSONObject。
|
||||
private JSONObject productNameI18nBlob = new JSONObject();
|
||||
private JSONObject productSpecI18nBlob = new JSONObject();
|
||||
private GreenInvoiceStructure greenInvoice;
|
||||
private GreenpaySettingStructure greenpaySetting;
|
||||
private GreenpayMerchantStructure greenpayMerchant;
|
||||
@ -387,6 +391,7 @@ public class MyApp extends Application {
|
||||
MyApp.getInstance().getDevSet().setMemberSystem(jsonObject.optBoolean("MemberSystem", false));
|
||||
MyApp.getInstance().getDevSet().setAmbientTemp(jsonObject.optBoolean("AmbientTemp", false));
|
||||
MyApp.getInstance().setShoppingMode(jsonObject.optString("ShoppingMode", "basic"));
|
||||
MyApp.getInstance().setLogLevel(jsonObject.optString("LogLevel", "INFO"));
|
||||
// 機台顯示語系(B014 LangSet)
|
||||
java.util.List<String> langs = new java.util.ArrayList<>();
|
||||
JSONArray langArr = jsonObject.optJSONArray("MachineLanguages");
|
||||
@ -408,6 +413,11 @@ public class MyApp extends Application {
|
||||
MyApp.getInstance().setProductNameI18nBlob(memoDao.getOnlyOne("ProductNameI18n").getData());
|
||||
}
|
||||
|
||||
// 商品規格多語 blob(B012 t060v03_i18n),離線重啟時供 fallback
|
||||
if (memoDao.existOnlyOne("ProductSpecI18n")) {
|
||||
MyApp.getInstance().setProductSpecI18nBlob(memoDao.getOnlyOne("ProductSpecI18n").getData());
|
||||
}
|
||||
|
||||
if (memoDao.existOnlyOne(MyApp.getInstance().getCashSetStructure().getClass().getSimpleName())) {
|
||||
JSONObject jsonObject = memoDao.getOnlyOne(MyApp.getInstance().getCashSetStructure().getClass().getSimpleName()).getData();
|
||||
logs.debug("initialSystemSet:" + jsonObject.toString());
|
||||
@ -470,6 +480,7 @@ public class MyApp extends Application {
|
||||
systemSetByJsonObject.put("MemberSystem", MyApp.getInstance().getDevSet().isMemberSystem());
|
||||
systemSetByJsonObject.put("AmbientTemp", MyApp.getInstance().getDevSet().isAmbientTemp());
|
||||
systemSetByJsonObject.put("ShoppingMode", MyApp.getInstance().getShoppingMode());
|
||||
systemSetByJsonObject.put("LogLevel", MyApp.getInstance().getLogLevel());
|
||||
systemSetByJsonObject.put("MachineLanguages", new JSONArray(MyApp.getInstance().getMachineLanguages()));
|
||||
systemSetByJsonObject.put("DefaultLanguage", MyApp.getInstance().getDefaultLanguage());
|
||||
|
||||
@ -535,6 +546,20 @@ public class MyApp extends Application {
|
||||
}
|
||||
}
|
||||
|
||||
// 持久化商品規格多語 blob(不動 products 表 schema,存於 MemoDao),供離線重啟 fallback
|
||||
public static void saveProductSpecI18n(Context context) {
|
||||
MemoDao memoDao = new MemoDao(context);
|
||||
try {
|
||||
memoDao.insertOnlyOne(new MemoStructure(
|
||||
"ProductSpecI18n",
|
||||
MyApp.getInstance().getProductSpecI18nBlob(),
|
||||
new JSONObject()));
|
||||
memoDao.mapUpdate();
|
||||
} catch (Exception e) {
|
||||
new Logs(MyApp.class).warning(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateLog(Context context) {
|
||||
OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(UploadLogOneTimeWorker.class)
|
||||
.setInitialDelay(5, TimeUnit.SECONDS)
|
||||
@ -816,7 +841,7 @@ public class MyApp extends Application {
|
||||
int i = 0;
|
||||
for (SlotStructure slotData: getSlotList(field)) {
|
||||
i += 1;
|
||||
logs.info("AAAAAAAAAAAAAAAAAAAAAAAAAAAA0"+i);
|
||||
logs.debug("slot index:" + i);
|
||||
if (slotData.getProduct().getProductID().equals(String.valueOf(pid))) {
|
||||
if(slotData.getCount() > 0) {
|
||||
return slotData;
|
||||
@ -1016,6 +1041,14 @@ public class MyApp extends Application {
|
||||
this.shoppingMode = (shoppingMode == null || shoppingMode.isEmpty()) ? "basic" : shoppingMode;
|
||||
}
|
||||
|
||||
public String getLogLevel() {
|
||||
return logLevel;
|
||||
}
|
||||
|
||||
public void setLogLevel(String logLevel) {
|
||||
this.logLevel = (logLevel == null || logLevel.isEmpty()) ? "INFO" : logLevel.toUpperCase(java.util.Locale.US);
|
||||
}
|
||||
|
||||
public java.util.List<String> getMachineLanguages() {
|
||||
return machineLanguages;
|
||||
}
|
||||
@ -1047,6 +1080,21 @@ public class MyApp extends Application {
|
||||
return (obj == null) ? null : obj.toString();
|
||||
}
|
||||
|
||||
public void setProductSpecI18nBlob(JSONObject blob) {
|
||||
this.productSpecI18nBlob = (blob == null) ? new JSONObject() : blob;
|
||||
}
|
||||
|
||||
public JSONObject getProductSpecI18nBlob() {
|
||||
return productSpecI18nBlob;
|
||||
}
|
||||
|
||||
// 取某商品的規格多語 JSON 字串(底線代碼 key),無則回 null。供 ProductStructure 離線時 fallback。
|
||||
public String getProductSpecI18nJson(String productId) {
|
||||
if (productId == null || productSpecI18nBlob == null) return null;
|
||||
JSONObject obj = productSpecI18nBlob.optJSONObject(productId);
|
||||
return (obj == null) ? null : obj.toString();
|
||||
}
|
||||
|
||||
|
||||
public ReportAccessCodeStructure getReportAccessCodeStructure() {
|
||||
return reportAccessCodeStructure;
|
||||
|
||||
@ -155,7 +155,7 @@ public class DevNFCPayController extends HandlerMainScheduler {
|
||||
byte[] buffer = comPort2.readListen(30, 200);
|
||||
|
||||
if (buffer.length > 0) {
|
||||
getLogs().info("NFC 收到資料: " + PortTools.showHex(buffer));
|
||||
getLogs().debug("NFC 收到資料: " + PortTools.showHex(buffer));
|
||||
String rxData = new String(buffer, StandardCharsets.UTF_8);
|
||||
|
||||
if (rxData.contains(String.valueOf((char) 0x03))) { // ETX
|
||||
|
||||
@ -56,7 +56,7 @@ public class DevXinYuanController extends DevController {
|
||||
try {
|
||||
this.rs232Dev = new Rs232Dev(MyApp.getInstance().getComPort1ByXinYuan());
|
||||
devXinYuan.setRun(true);
|
||||
logs.debug("下位機連接");
|
||||
logs.info("下位機連接");
|
||||
} catch (LogsUnsupportedOperationException | LogsIOException e) {
|
||||
// getLogs().error(ErrorCode.DEV_ERROR, "@下位機連接錯誤", handlerMain);
|
||||
getLogs().warning(e);
|
||||
@ -123,9 +123,9 @@ public class DevXinYuanController extends DevController {
|
||||
Byte key = Byte.valueOf(vmcCmd[0]);
|
||||
|
||||
if (!vmcMap.containsKey(key)) {
|
||||
getLogs().info("XinYuan vmcCmd:" + PortTools.showHex(vmcCmd));
|
||||
getLogs().info("XinYuan vmcData:" + PortTools.showHex(vmcData));
|
||||
getLogs().info("XinYuan vmcXor:" + PortTools.showHex(vmcXor));
|
||||
getLogs().debug("XinYuan vmcCmd:" + PortTools.showHex(vmcCmd));
|
||||
getLogs().debug("XinYuan vmcData:" + PortTools.showHex(vmcData));
|
||||
getLogs().debug("XinYuan vmcXor:" + PortTools.showHex(vmcXor));
|
||||
getLogs().info("XinYuan vmc not found");
|
||||
continue;
|
||||
}
|
||||
@ -133,9 +133,9 @@ public class DevXinYuanController extends DevController {
|
||||
DevXinYuan.VMC vmc = vmcMap.get(key);
|
||||
|
||||
if (vmc != DevXinYuan.VMC.VMC_41_POLL) {
|
||||
getLogs().info("XinYuan vmcCmd:" + PortTools.showHex(vmcCmd));
|
||||
getLogs().info("XinYuan vmcData:" + PortTools.showHex(vmcData));
|
||||
getLogs().info("XinYuan vmcXor:" + PortTools.showHex(vmcXor));
|
||||
getLogs().debug("XinYuan vmcCmd:" + PortTools.showHex(vmcCmd));
|
||||
getLogs().debug("XinYuan vmcData:" + PortTools.showHex(vmcData));
|
||||
getLogs().debug("XinYuan vmcXor:" + PortTools.showHex(vmcXor));
|
||||
getLogs().info("XinYuan vmc:" + vmc.name());
|
||||
}
|
||||
|
||||
@ -143,12 +143,12 @@ public class DevXinYuanController extends DevController {
|
||||
case VMC_41_POLL:
|
||||
if (getCountBySendBuffer() > 0) {
|
||||
byte[] cmdBytes = getSendBufferByFirst();
|
||||
getLogs().info("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"+bytesToHex(cmdBytes));
|
||||
getLogs().debug("XinYuan cmd hex:" + bytesToHex(cmdBytes));
|
||||
|
||||
if (cmdBytes.length == (4+cmdBytes[3]+1)) {
|
||||
for (int sendCount=1; sendCount <2; sendCount++) {
|
||||
rs232Dev.send(cmdBytes);
|
||||
getLogs().info("XinYuan Send:" + PortTools.showHex(cmdBytes));
|
||||
getLogs().debug("XinYuan Send:" + PortTools.showHex(cmdBytes));
|
||||
|
||||
byte[] vmcBuffer = rs232Dev.readListen(0, 1);
|
||||
if (vmcBuffer.length > 2) {
|
||||
@ -247,7 +247,7 @@ public class DevXinYuanController extends DevController {
|
||||
break;
|
||||
case VMC_52_CHECK_VMC_STATUS:
|
||||
//溫度:vmcData[7]
|
||||
getLogs().info("下位機訊號"+PortTools.showHex(vmcData));
|
||||
getLogs().debug("下位機訊號"+PortTools.showHex(vmcData));
|
||||
if (PortTools.showHex(vmcData).isEmpty()) {
|
||||
getLogs().info("下位機失連,重啟APP");
|
||||
|
||||
|
||||
@ -267,7 +267,7 @@ public class DevXinYuan {
|
||||
proBytes = PortTools.concatByteArrays(proBytes, XORByte);
|
||||
|
||||
if (isShow) {
|
||||
logs.info("XinYuan ipcCmd:" + cmd.name() + " " + PortTools.showHex(proBytes));
|
||||
logs.debug("XinYuan ipcCmd:" + cmd.name() + " " + PortTools.showHex(proBytes));
|
||||
}
|
||||
|
||||
return proBytes;
|
||||
@ -371,18 +371,18 @@ public class DevXinYuan {
|
||||
}
|
||||
|
||||
public CMD readCMD(byte[] buffer) throws LogsEmptyException {
|
||||
logs.info(PortTools.showHex(buffer));
|
||||
logs.debug(PortTools.showHex(buffer));
|
||||
|
||||
for (CMD cmd : CMD.values()) {
|
||||
if (cmd.getBytes().length >= 2 && buffer.length >= 2) {
|
||||
if (cmd.getBytes()[0] == buffer[0]) {
|
||||
if (cmd.equals(CMD.CMD_FAFB_STX)) {
|
||||
if (cmd.getBytes()[1] == buffer[1]) {
|
||||
logs.info("CMD:" + cmd.name() + " " + PortTools.showHex(cmd.getBytes()) + " buffer:" + PortTools.showHex(new byte[]{buffer[0], buffer[1]}));
|
||||
logs.debug("CMD:" + cmd.name() + " " + PortTools.showHex(cmd.getBytes()) + " buffer:" + PortTools.showHex(new byte[]{buffer[0], buffer[1]}));
|
||||
return cmd;
|
||||
}
|
||||
} else {
|
||||
logs.info("CMD:" + cmd.name() + " " + PortTools.showHex(cmd.getBytes()) + " buffer:" + PortTools.showHex(new byte[]{buffer[0], buffer[1]}));
|
||||
logs.debug("CMD:" + cmd.name() + " " + PortTools.showHex(cmd.getBytes()) + " buffer:" + PortTools.showHex(new byte[]{buffer[0], buffer[1]}));
|
||||
return cmd;
|
||||
}
|
||||
}
|
||||
@ -393,18 +393,18 @@ public class DevXinYuan {
|
||||
}
|
||||
|
||||
public VMC readVMC(byte[] buffer) throws LogsEmptyException {
|
||||
logs.info(PortTools.showHex(buffer));
|
||||
logs.debug(PortTools.showHex(buffer));
|
||||
|
||||
for (VMC vmc : VMC.values()) {
|
||||
if (vmc.getBytes().length >= 2 && buffer.length >= 2) {
|
||||
if (vmc.getBytes()[0] == buffer[0]) {
|
||||
if (vmc.equals(VMC.VMC_FAFB_STX)) {
|
||||
if (vmc.getBytes()[1] == buffer[1]) {
|
||||
logs.info("VMC:" + vmc.name() + " " + PortTools.showHex(vmc.getBytes()) + " buffer:" + PortTools.showHex(new byte[]{buffer[0], buffer[1]}));
|
||||
logs.debug("VMC:" + vmc.name() + " " + PortTools.showHex(vmc.getBytes()) + " buffer:" + PortTools.showHex(new byte[]{buffer[0], buffer[1]}));
|
||||
return vmc;
|
||||
}
|
||||
} else {
|
||||
logs.info("VMC:" + vmc.name() + " " + PortTools.showHex(vmc.getBytes()) + " buffer:" + PortTools.showHex(new byte[]{buffer[0], buffer[1]}));
|
||||
logs.debug("VMC:" + vmc.name() + " " + PortTools.showHex(vmc.getBytes()) + " buffer:" + PortTools.showHex(new byte[]{buffer[0], buffer[1]}));
|
||||
return vmc;
|
||||
}
|
||||
}
|
||||
@ -416,7 +416,7 @@ public class DevXinYuan {
|
||||
|
||||
public byte readPackNo(byte[] buffer) {
|
||||
byte packNo = buffer[0];
|
||||
logs.info("Read packNo:" + PortTools.showHex(packNo));
|
||||
logs.debug("Read packNo:" + PortTools.showHex(packNo));
|
||||
return packNo;
|
||||
}
|
||||
|
||||
@ -424,7 +424,7 @@ public class DevXinYuan {
|
||||
for (BUY_STATUS_04 buyStatus : BUY_STATUS_04.values()) {
|
||||
if (buyStatus.getBytes().length == 1 && buffer.length > 0) {
|
||||
if (buyStatus.getBytes()[0] == buffer[1]) {
|
||||
logs.info(BUY_STATUS_04.class.getSimpleName()+":" + buyStatus.name() + " " + PortTools.showHex(buyStatus.getBytes()) + " buffer:" + PortTools.showHex(new byte[]{buffer[0], buffer[1]}));
|
||||
logs.debug(BUY_STATUS_04.class.getSimpleName()+":" + buyStatus.name() + " " + PortTools.showHex(buyStatus.getBytes()) + " buffer:" + PortTools.showHex(new byte[]{buffer[0], buffer[1]}));
|
||||
return buyStatus;
|
||||
}
|
||||
}
|
||||
@ -437,7 +437,7 @@ public class DevXinYuan {
|
||||
for (SLOT_STATUS_02 slotStatus : SLOT_STATUS_02.values()) {
|
||||
if (slotStatus.getBytes().length == 1 && buffer.length > 0) {
|
||||
if (slotStatus.getBytes()[0] == buffer[1]) {
|
||||
logs.info(SLOT_STATUS_02.class.getSimpleName()+":" + slotStatus.name() + " " + PortTools.showHex(slotStatus.getBytes()) + " buffer:" + PortTools.showHex(new byte[]{buffer[0], buffer[1]}));
|
||||
logs.debug(SLOT_STATUS_02.class.getSimpleName()+":" + slotStatus.name() + " " + PortTools.showHex(slotStatus.getBytes()) + " buffer:" + PortTools.showHex(new byte[]{buffer[0], buffer[1]}));
|
||||
return slotStatus;
|
||||
}
|
||||
}
|
||||
@ -451,7 +451,7 @@ public class DevXinYuan {
|
||||
for (GIVE_MONEY_26 giveMoney : GIVE_MONEY_26.values()) {
|
||||
if (giveMoney.getBytes().length == 1 && buffer.length > 0) {
|
||||
if (giveMoney.getBytes()[0] == buffer[1]) {
|
||||
logs.info(GIVE_MONEY_26.class.getSimpleName()+":" + giveMoney.name() + " " + PortTools.showHex(giveMoney.getBytes()) + " buffer:" + PortTools.showHex(new byte[]{buffer[0], buffer[1]}));
|
||||
logs.debug(GIVE_MONEY_26.class.getSimpleName()+":" + giveMoney.name() + " " + PortTools.showHex(giveMoney.getBytes()) + " buffer:" + PortTools.showHex(new byte[]{buffer[0], buffer[1]}));
|
||||
return giveMoney;
|
||||
}
|
||||
}
|
||||
@ -475,10 +475,10 @@ public class DevXinYuan {
|
||||
byte XORByte = buffer[buffer.length -1];
|
||||
proBytes = PortTools.concatByteArrays(proBytes, cmdLenData);
|
||||
byte XORByte2 = getXOR(proBytes);
|
||||
logs.info("Length:"+length+" XOR1:" + PortTools.showHex(new byte[] {XORByte}) + " XOR2:" + PortTools.showHex(new byte[] {XORByte2}));
|
||||
logs.debug("Length:"+length+" XOR1:" + PortTools.showHex(new byte[] {XORByte}) + " XOR2:" + PortTools.showHex(new byte[] {XORByte2}));
|
||||
if (buffer.length > (2+length)) {
|
||||
byte[] dataBytes = PortTools.catByteArrays(buffer, 2, length);
|
||||
logs.info("data:"+PortTools.showHex(dataBytes));
|
||||
logs.debug("data:"+PortTools.showHex(dataBytes));
|
||||
return dataBytes;
|
||||
} else {
|
||||
throw new LogsEmptyException(logs, "data array length insufficient");
|
||||
|
||||
@ -18,6 +18,10 @@ import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
public class Logs {
|
||||
// 單筆寫檔上限:超過即截斷,避免整包 JSON/payload dump 把 debug.txt 撐爆、淹沒有用事件。
|
||||
// 只影響「寫入 debug.txt」那份;logcat(Log.d/i/...)維持完整不截斷。
|
||||
private static final int MAX_LINE = 1000;
|
||||
|
||||
private String message;
|
||||
private String className;
|
||||
private String methodName;
|
||||
@ -46,6 +50,10 @@ public class Logs {
|
||||
}
|
||||
|
||||
String dateTime = Tools.getTimeStampByNow("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
// 招 1:超長行截斷(止血整包 JSON/payload dump),保留事件本身與原始長度資訊。
|
||||
if (data.length() > MAX_LINE) {
|
||||
data = data.substring(0, MAX_LINE) + " …[truncated " + data.length() + " chars]";
|
||||
}
|
||||
data = dateTime + " " + data;
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream(txtDebug, true)) { // try-with-resources 自動 close()
|
||||
@ -61,10 +69,20 @@ public class Logs {
|
||||
return this.className+"."+this.methodName+"("+this.lineNumber+")";
|
||||
}
|
||||
|
||||
// 招 2:是否把 DEBUG/IGNORE 寫進 debug.txt。預設(門檻 INFO)不寫,過濾掉開發雜訊;
|
||||
// 雲端/現場把 LogLevel 設為 DEBUG 時才寫檔。MyApp 尚未就緒時一律不寫,維持乾淨預設。
|
||||
private static boolean writeDebugToFile() {
|
||||
try {
|
||||
return "DEBUG".equals(com.unibuy.smartdevice.MyApp.getInstance().getLogLevel());
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void debug(String message) {
|
||||
this.message = message;
|
||||
Log.d(this.className, "DEBUG ["+getClassName()+"]"+this.message);
|
||||
writeFile("DEBUG ["+getClassName()+"]"+this.message);
|
||||
if (writeDebugToFile()) writeFile("DEBUG ["+getClassName()+"]"+this.message);
|
||||
}
|
||||
|
||||
public void info(String message) {
|
||||
@ -87,7 +105,7 @@ public class Logs {
|
||||
public void ignore(String message) {
|
||||
this.message = message;
|
||||
Log.w(this.className, "IGNORE ["+getClassName()+"/"+getInfo()+"]"+this.message);
|
||||
writeFile("IGNORE ["+getClassName()+"/"+getInfo()+"]"+this.message);
|
||||
if (writeDebugToFile()) writeFile("IGNORE ["+getClassName()+"/"+getInfo()+"]"+this.message);
|
||||
}
|
||||
|
||||
public void warning(Exception e) {
|
||||
|
||||
@ -493,7 +493,6 @@ public class HttpAPI {
|
||||
public void response(int responseCode, String receive, HandlerMain handlerMain) {
|
||||
if (responseCode == 200) {
|
||||
try {
|
||||
getLogs().info("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||
MyApp.getInstance().getMachine().setMachineID("");
|
||||
|
||||
MyApp.getInstance().getEsunpay().setStoreID("");
|
||||
@ -656,9 +655,7 @@ public class HttpAPI {
|
||||
MyApp.getInstance().getGreenpayMerchant().getAtomeMerchantId()
|
||||
}));
|
||||
|
||||
getLogs().info("aaaaaaaaaaaaaaaaaaaaaaaaaaaaa0");
|
||||
SettingActivity.updateProgressUI(10, "下載中...");
|
||||
getLogs().info("aaaaaaaaaaaaaaaaaaaaaaaaaaaaa10");
|
||||
settingsDao.close();
|
||||
} catch (JSONException e) {
|
||||
getLogs().warning(e);
|
||||
@ -720,7 +717,7 @@ public class HttpAPI {
|
||||
* t060v06 "https://unibuy.com.tw/Unibuy/dist/img/products/53317506.png",
|
||||
*/
|
||||
|
||||
getLogs().info("AAAAAAAAAAAAAAAAAAAAAAA" + productData);
|
||||
getLogs().debug("productData:" + productData);
|
||||
|
||||
if (productData.has("t060v00") &&
|
||||
productData.has("t060v06") &&
|
||||
@ -783,7 +780,6 @@ public class HttpAPI {
|
||||
productsDao.close();
|
||||
|
||||
SettingActivity.updateProgressUI(10, "下載中...");
|
||||
getLogs().info("aaaaaaaaaaaaaaaaaaaaaaaaaaaaa30");
|
||||
|
||||
handlerMain.start(getClass().getSimpleName(), Option.DOWNLOAD_PRODUCT_RESPONSE.getOption(),
|
||||
"成功載入" + MyApp.getInstance().getProductList().size() + "筆商品資料");
|
||||
@ -1053,9 +1049,8 @@ public class HttpAPI {
|
||||
JSONObject receiveByJsonObject = new JSONObject(receive);
|
||||
String workApi = receiveByJsonObject.getString("workApi");
|
||||
int status = receiveByJsonObject.getInt("status");
|
||||
getLogs().info("bbbbbbbbbbbbbbbbbbbbbb status" + status);
|
||||
String message = receiveByJsonObject.getString("message");
|
||||
getLogs().info("bbbbbbbbbbbbbbbbbbbbbb message" + message);
|
||||
getLogs().debug("reportFlow status:" + status + " message:" + message);
|
||||
|
||||
if (status == 0) {
|
||||
handlerMain.start(getClass().getSimpleName(), Option.REPORT_FLOW_RESPONSE.getOption(),
|
||||
@ -2142,7 +2137,7 @@ public class HttpAPI {
|
||||
*/
|
||||
jsonObject2.put("data", jsonArray);
|
||||
|
||||
getLogs().info("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=" + jsonObject2.toString());
|
||||
getLogs().debug("B009 supplementary:" + jsonObject2.toString());
|
||||
|
||||
formPutRunMain.start(domainNameByUnibuy + "products/supplementary/B009", jsonObject2.toString(), null, null,
|
||||
HttpConnect.ContentType.JSON);
|
||||
@ -2228,7 +2223,7 @@ public class HttpAPI {
|
||||
int slotid = Integer.parseInt(deliveryData.getString("tid"));
|
||||
int slotnum = Integer.parseInt(deliveryData.getString("num"));
|
||||
|
||||
getLogs().info("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + slotid + " " + slotnum);
|
||||
getLogs().info("dispatch slotid:" + slotid + " num:" + slotnum);
|
||||
|
||||
slotid = slotid;
|
||||
|
||||
|
||||
@ -174,11 +174,11 @@ public class HttpTapPay {
|
||||
int payType = MyApp.getInstance().getReportFlowInfoData().getFlowType();
|
||||
GreenpaySettingStructure greenpaySetting = MyApp.getInstance().getGreenpaySetting();
|
||||
String greenpayMerchantId = MyApp.getInstance().getMachine().getMachineID();
|
||||
logs.info("aaaaaaaaaaaaaaaaaaaaaa1"+ MyApp.getInstance().getGreenpayMerchant().getLineMerchantId());
|
||||
logs.info("aaaaaaaaaaaaaaaaaaaaaa2"+ MyApp.getInstance().getGreenpayMerchant().getJkoMerchantId());
|
||||
logs.info("aaaaaaaaaaaaaaaaaaaaaa3"+ MyApp.getInstance().getGreenpayMerchant().getPiMerchantId());
|
||||
logs.info("aaaaaaaaaaaaaaaaaaaaaa4"+ MyApp.getInstance().getGreenpayMerchant().getPsMerchantId());
|
||||
logs.info("aaaaaaaaaaaaaaaaaaaaaa5"+ MyApp.getInstance().getGreenpayMerchant().getEasyMerchantId());
|
||||
logs.debug("merchantId line:" + MyApp.getInstance().getGreenpayMerchant().getLineMerchantId()
|
||||
+ " jko:" + MyApp.getInstance().getGreenpayMerchant().getJkoMerchantId()
|
||||
+ " pi:" + MyApp.getInstance().getGreenpayMerchant().getPiMerchantId()
|
||||
+ " ps:" + MyApp.getInstance().getGreenpayMerchant().getPsMerchantId()
|
||||
+ " easy:" + MyApp.getInstance().getGreenpayMerchant().getEasyMerchantId());
|
||||
switch (payType) {
|
||||
case 30:
|
||||
if (MyApp.getInstance().getGreenpayMerchant() != null && !MyApp.getInstance().getGreenpayMerchant().getLineMerchantId().isEmpty()) {
|
||||
|
||||
@ -1169,6 +1169,8 @@ public class StarCloudAPI {
|
||||
MyApp.getInstance().getProductList().clear();
|
||||
// 商品名稱多語 blob:商品ID → t060v01_i18n(底線代碼 key),存於 MemoDao 持久化(不動 products 表)
|
||||
JSONObject nameI18nBlob = new JSONObject();
|
||||
// 商品規格多語 blob:商品ID → t060v03_i18n(純文字含換行)
|
||||
JSONObject specI18nBlob = new JSONObject();
|
||||
for (int i = 0; i < products.length(); i++) {
|
||||
JSONObject p = products.getJSONObject(i);
|
||||
|
||||
@ -1195,11 +1197,23 @@ public class StarCloudAPI {
|
||||
try { nameI18nBlob.put(pid, nameI18n); } catch (JSONException ignore) {}
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:商品規格多語 map(B012 t060v03_i18n,純文字含換行)
|
||||
JSONObject specI18n = p.optJSONObject("t060v03_i18n");
|
||||
if (specI18n != null) {
|
||||
product.setSpecI18nJson(specI18n.toString());
|
||||
String pid = p.optString("t060v00", "");
|
||||
if (!pid.isEmpty()) {
|
||||
try { specI18nBlob.put(pid, specI18n); } catch (JSONException ignore) {}
|
||||
}
|
||||
}
|
||||
MyApp.getInstance().getProductList().add(product);
|
||||
}
|
||||
// 持久化商品多語 blob,供離線重啟時 fallback
|
||||
MyApp.getInstance().setProductNameI18nBlob(nameI18nBlob);
|
||||
MyApp.saveProductNameI18n(handlerMain.getContext());
|
||||
MyApp.getInstance().setProductSpecI18nBlob(specI18nBlob);
|
||||
MyApp.saveProductSpecI18n(handlerMain.getContext());
|
||||
|
||||
// 將商品同步至資料庫
|
||||
ProductsDao productsDao = new ProductsDao(handlerMain.getContext());
|
||||
|
||||
@ -171,6 +171,47 @@ public class ProductStructure {
|
||||
}
|
||||
}
|
||||
|
||||
// B012 t060v03_i18n 原始 JSON 字串(底線代碼 key,如 {"zh_TW":..,"en":..})。
|
||||
// 內容為純文字含換行(\n),顯示時以 setText 原樣呈現(保留換行/空格/縮排)。
|
||||
// 由 B012 解析時設入;離線從 DB 重新載入商品時此欄為空,改向 MyApp 持久化 blob 取。
|
||||
private String specI18nJson;
|
||||
|
||||
public String getSpecI18nJson() {
|
||||
return specI18nJson;
|
||||
}
|
||||
|
||||
public void setSpecI18nJson(String specI18nJson) {
|
||||
this.specI18nJson = specI18nJson;
|
||||
}
|
||||
|
||||
// 依目前語言取商品規格/描述。順序:對應語系 → zh_TW → 空字串(無描述)。
|
||||
public String getLocalizedSpec(Context context) {
|
||||
String canonical = LanguageHelper.getSavedLanguage(context);
|
||||
String backendLocale = LanguageHelper.toBackendLocale(canonical);
|
||||
|
||||
String fromMap = lookupSpecI18n(backendLocale);
|
||||
if (notBlank(fromMap)) return fromMap;
|
||||
|
||||
if (!"zh_TW".equals(backendLocale)) {
|
||||
String zh = lookupSpecI18n("zh_TW");
|
||||
if (notBlank(zh)) return zh;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private String lookupSpecI18n(String backendLocale) {
|
||||
String json = specI18nJson;
|
||||
if (json == null || json.isEmpty()) {
|
||||
json = com.unibuy.smartdevice.MyApp.getInstance().getProductSpecI18nJson(productID);
|
||||
}
|
||||
if (json == null || json.isEmpty()) return null;
|
||||
try {
|
||||
return new org.json.JSONObject(json).optString(backendLocale, null);
|
||||
} catch (org.json.JSONException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean notBlank(String s) {
|
||||
return s != null && !s.trim().isEmpty();
|
||||
}
|
||||
|
||||
@ -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{" +
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -114,8 +114,11 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter<RecyclerVmcBu
|
||||
holder.binding.imageProductPicture.setFocusable(false);
|
||||
holder.binding.imageProductPicture.setFocusableInTouchMode(false);
|
||||
holder.binding.imageProductPicture.setOnClickListener(new imageProductPictureOnClickListener(position));
|
||||
// 整張卡片皆可點開商品詳情(不限圖片),避免點到名稱/價格區沒反應
|
||||
holder.binding.getRoot().setOnClickListener(new imageProductPictureOnClickListener(position));
|
||||
} else {
|
||||
holder.binding.imageProductPicture.setOnClickListener(null);
|
||||
holder.binding.getRoot().setOnClickListener(null);
|
||||
}
|
||||
|
||||
// 商品卡右下角「+」快速加購:僅購物車功能開啟時顯示;無購物車時隱藏(只能點商品開詳情頁)
|
||||
|
||||
@ -387,7 +387,7 @@ public class BuyDialog extends DialogAbstract {
|
||||
public void gotoDialog() {
|
||||
closeDialogCountdown.shutdown();
|
||||
int flowType = MyApp.getInstance().getReportFlowInfoData().getFlowType();
|
||||
getLogs().info("aaaaaaaaaaaaaaaaaaaaaaaa"+flowType);
|
||||
getLogs().debug("buy flowType:" + flowType);
|
||||
// if (flowType == 5 || flowType == 7 || flowType == 30) {
|
||||
if (flowType == 5 || flowType == 7) {
|
||||
changeCheckout();
|
||||
|
||||
@ -100,25 +100,20 @@ public class CheckoutEsunPayDialog extends DialogAbstract {
|
||||
case TRANSACTION_SUCCESSFUL:
|
||||
if (isTransaction) {
|
||||
MyApp.getInstance().getReportFlowInfoData().setFlowStatusType(1);
|
||||
try {
|
||||
httpAPI.reportFlowInfo(MyApp.getInstance().getReportFlowInfoData());
|
||||
} catch (LogsSettingEmptyException | LogsParseException e) {
|
||||
// exception(context, e.getErrorCode(), e.getLocalizedMessage());
|
||||
getLogs().warning(e);
|
||||
}
|
||||
// [MQTT Finalize 重構] 支付成功不再呼叫舊 B600(reportFlowInfo);
|
||||
// 直接進出貨流程,由 DispatchDialog 以 MQTT Finalize 統一上報結案(含發票/扣庫存)。
|
||||
// payment_type=3(掃碼支付)已於 BuyDialog 設定。
|
||||
isTransaction = false;
|
||||
changeDispatch();
|
||||
}
|
||||
break;
|
||||
case TRANSACTION_FAILED:
|
||||
if (isTransaction) {
|
||||
MyApp.getInstance().getReportFlowInfoData().setFlowStatusType(0);
|
||||
try {
|
||||
httpAPI.reportFlowInfo(MyApp.getInstance().getReportFlowInfoData());
|
||||
} catch (LogsSettingEmptyException | LogsParseException e) {
|
||||
// exception(context, e.getErrorCode(), e.getLocalizedMessage());
|
||||
getLogs().warning(e);
|
||||
}
|
||||
// [MQTT Finalize 重構] 交易失敗不再經 B600;直接提示並返回購買頁,不進出貨。
|
||||
isTransaction = false;
|
||||
start(getSrcClassName(), "@" + getContext().getString(R.string.transaction_failure));
|
||||
changeBuy();
|
||||
}
|
||||
break;
|
||||
case CHANGE_DISPATCH:
|
||||
|
||||
@ -101,25 +101,20 @@ public class CheckoutLinePayDialog extends DialogAbstract {
|
||||
case TRANSACTION_SUCCESSFUL:
|
||||
if (isTransaction) {
|
||||
MyApp.getInstance().getReportFlowInfoData().setFlowStatusType(1);
|
||||
try {
|
||||
httpAPI.reportFlowInfo(MyApp.getInstance().getReportFlowInfoData());
|
||||
} catch (LogsSettingEmptyException | LogsParseException e) {
|
||||
// exception(context, e.getErrorCode(), e.getLocalizedMessage());
|
||||
getLogs().warning(e);
|
||||
}
|
||||
// [MQTT Finalize 重構] 支付成功不再呼叫舊 B600(reportFlowInfo);
|
||||
// 直接進出貨流程,由 DispatchDialog 以 MQTT Finalize 統一上報結案(含發票/扣庫存)。
|
||||
// payment_type=70(LINE Pay 官方)已於 BuyDialog 設定。
|
||||
isTransaction = false;
|
||||
changeDispatch();
|
||||
}
|
||||
break;
|
||||
case TRANSACTION_FAILED:
|
||||
if (isTransaction) {
|
||||
MyApp.getInstance().getReportFlowInfoData().setFlowStatusType(0);
|
||||
try {
|
||||
httpAPI.reportFlowInfo(MyApp.getInstance().getReportFlowInfoData());
|
||||
} catch (LogsSettingEmptyException | LogsParseException e) {
|
||||
// exception(context, e.getErrorCode(), e.getLocalizedMessage());
|
||||
getLogs().warning(e);
|
||||
}
|
||||
// [MQTT Finalize 重構] 交易失敗不再經 B600;直接提示並返回購買頁,不進出貨。
|
||||
isTransaction = false;
|
||||
start(getSrcClassName(), "@" + getContext().getString(R.string.transaction_failure));
|
||||
changeBuy();
|
||||
}
|
||||
break;
|
||||
case CHANGE_DISPATCH:
|
||||
|
||||
@ -109,11 +109,10 @@ public class CheckoutNfcCardDialog extends DialogAbstract {
|
||||
if (message.equals("0000")) {
|
||||
MyApp.getInstance().getReportFlowInfoData().setFlowStatusType(1);
|
||||
binding.textStatus.setText(DevNFCPay.recordMessage(message));
|
||||
try {
|
||||
httpAPI.reportFlowInfo(MyApp.getInstance().getReportFlowInfoData());
|
||||
} catch (LogsSettingEmptyException | LogsParseException e) {
|
||||
getLogs().warning(e);
|
||||
}
|
||||
// [MQTT Finalize 重構] 支付成功不再呼叫舊 B600(reportFlowInfo);
|
||||
// 直接進出貨流程,由 DispatchDialog 以 MQTT Finalize 統一上報結案(含發票/扣庫存)。
|
||||
// payment_type=1(信用卡)已於 BuyDialog 設定。
|
||||
changeDispatch();
|
||||
} else {
|
||||
MyApp.getInstance().getReportFlowInfoData().setFlowStatusType(0);
|
||||
binding.textStatus.setText(DevNFCPay.recordMessage(message));
|
||||
|
||||
@ -108,11 +108,13 @@ public class CheckoutNfcPhoneDialog extends DialogAbstract {
|
||||
if (message.equals("0000")) {
|
||||
MyApp.getInstance().getReportFlowInfoData().setFlowStatusType(1);
|
||||
binding.textStatus.setText(DevNFCPay.recordMessage(message));
|
||||
try {
|
||||
httpAPI.reportFlowInfo(MyApp.getInstance().getReportFlowInfoData());
|
||||
} catch (LogsSettingEmptyException | LogsParseException e) {
|
||||
getLogs().warning(e);
|
||||
}
|
||||
// NfcPhone(手機支付):刷卡機 NCCC 電文無法區分手機錢包/實體卡,
|
||||
// 依使用者所選付款鈕歸類為 payment_type=10(手機支付)。
|
||||
// 路由鍵 flowType=4 僅用於 BuyDialog 開啟本 Dialog,這裡覆寫為實際上報值。
|
||||
MyApp.getInstance().getReportFlowInfoData().setFlowType(10);
|
||||
// [MQTT Finalize 重構] 支付成功不再呼叫舊 B600(reportFlowInfo);
|
||||
// 直接進出貨流程,由 DispatchDialog 以 MQTT Finalize 統一上報結案(含發票/扣庫存)。
|
||||
changeDispatch();
|
||||
} else {
|
||||
MyApp.getInstance().getReportFlowInfoData().setFlowStatusType(0);
|
||||
binding.textStatus.setText(DevNFCPay.recordMessage(message));
|
||||
|
||||
@ -108,11 +108,10 @@ public class CheckoutNfcRechargeDialog extends DialogAbstract {
|
||||
if (message.equals("0000")) {
|
||||
MyApp.getInstance().getReportFlowInfoData().setFlowStatusType(1);
|
||||
binding.textStatus.setText(DevNFCPay.recordMessage(message));
|
||||
try {
|
||||
httpAPI.reportFlowInfo(MyApp.getInstance().getReportFlowInfoData());
|
||||
} catch (LogsSettingEmptyException | LogsParseException e) {
|
||||
getLogs().warning(e);
|
||||
}
|
||||
// [MQTT Finalize 重構] 支付成功不再呼叫舊 B600(reportFlowInfo);
|
||||
// 直接進出貨流程,由 DispatchDialog 以 MQTT Finalize 統一上報結案(含發票/扣庫存)。
|
||||
// payment_type=2(悠遊卡/一卡通)已於 BuyDialog 設定。
|
||||
changeDispatch();
|
||||
} else {
|
||||
MyApp.getInstance().getReportFlowInfoData().setFlowStatusType(0);
|
||||
binding.textStatus.setText(DevNFCPay.recordMessage(message));
|
||||
|
||||
@ -123,22 +123,19 @@ public class CheckoutTapPayDialog extends DialogAbstract {
|
||||
if (isTransaction) {
|
||||
isTransaction = false; // 🚨 馬上關閉,避免送出第二筆
|
||||
MyApp.getInstance().getReportFlowInfoData().setFlowStatusType(1);
|
||||
try {
|
||||
httpAPI.reportFlowInfo(MyApp.getInstance().getReportFlowInfoData());
|
||||
} catch (LogsSettingEmptyException | LogsParseException e) {
|
||||
getLogs().warning(e);
|
||||
}
|
||||
// [MQTT Finalize 重構] 支付成功不再呼叫舊 B600(reportFlowInfo);
|
||||
// 直接進出貨流程,由 DispatchDialog 以 MQTT Finalize 統一上報結案(含發票/扣庫存)。
|
||||
// payment_type=30~34(TapPay 子項)已於本 Dialog 選擇時設定。
|
||||
changeDispatch();
|
||||
}
|
||||
break;
|
||||
case TRANSACTION_FAILED:
|
||||
if (isTransaction) {
|
||||
isTransaction = false; // 🚨 馬上關閉
|
||||
MyApp.getInstance().getReportFlowInfoData().setFlowStatusType(0);
|
||||
try {
|
||||
httpAPI.reportFlowInfo(MyApp.getInstance().getReportFlowInfoData());
|
||||
} catch (LogsSettingEmptyException | LogsParseException e) {
|
||||
getLogs().warning(e);
|
||||
}
|
||||
// [MQTT Finalize 重構] 交易失敗不再經 B600;直接提示並返回購買頁,不進出貨。
|
||||
start(getSrcClassName(), "@" + getContext().getString(R.string.transaction_failure));
|
||||
changeBuy();
|
||||
}
|
||||
break;
|
||||
case TRANSACTION_AGAIN:
|
||||
|
||||
@ -28,10 +28,15 @@ public class DialogProductDetail extends DialogAbstract {
|
||||
|
||||
private DialogProductDetailBinding binding;
|
||||
private final SlotStructure slot;
|
||||
// 建構子傳入的原始 Context(真正的 FontendActivity)。
|
||||
// 不可用 onCreate 的 getContext(),那是 AlertDialog 包裝過的 ContextThemeWrapper,
|
||||
// instanceof FontendActivity 會是 false,導致購物車頁/結帳入口取不到 SaleFlowHandler。
|
||||
private final Context hostContext;
|
||||
private int quantity = 1;
|
||||
|
||||
public DialogProductDetail(Context context, HandlerMain srcHandlerMain, SlotStructure slot) {
|
||||
super(context, srcHandlerMain);
|
||||
this.hostContext = context;
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
@ -72,6 +77,15 @@ public class DialogProductDetail extends DialogAbstract {
|
||||
binding.slotNumber.setText("貨道" + slot.getSlot());
|
||||
binding.inventoryNumber.setText("庫存" + slot.getCount());
|
||||
|
||||
// 商品規格/描述(多語,純文字含換行;後台空白則隱藏整塊)。
|
||||
// setText 原樣呈現換行與空格/縮排;版面已設 gravity=start,故縮排不會被置中打亂。
|
||||
String spec = slot.getProduct().getLocalizedSpec(context);
|
||||
if (spec == null || spec.trim().isEmpty()) {
|
||||
binding.textProductDescription.setVisibility(android.view.View.GONE);
|
||||
} else {
|
||||
binding.textProductDescription.setText(spec);
|
||||
}
|
||||
|
||||
// 依購物車開關決定按鈕呈現
|
||||
boolean cartOn = MyApp.getInstance().getDevSet().isShoppingCar();
|
||||
if (!cartOn) {
|
||||
@ -119,14 +133,16 @@ public class DialogProductDetail extends DialogAbstract {
|
||||
binding.btnBuyNow.setOnClickListener(v -> {
|
||||
if (cartOn) {
|
||||
addSelectedQuantityToCart();
|
||||
// 先切到購物車品項頁(於詳情頁視窗後方),再關閉詳情頁顯示出來,
|
||||
// 避免「關閉對話框」與「fragment 切換」時序互相干擾導致沒跳頁。
|
||||
openCartList();
|
||||
cancel();
|
||||
openCartList(context);
|
||||
} else {
|
||||
MyApp.getInstance().getBuyList().clear();
|
||||
MyApp.getInstance().getBuyList().add(
|
||||
new BuyStructure(slot.getField(), slot.getSlot(), slot.getProduct(), quantity));
|
||||
cancel();
|
||||
requestBuy(context);
|
||||
requestBuy();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -139,9 +155,9 @@ public class DialogProductDetail extends DialogAbstract {
|
||||
}
|
||||
|
||||
/** 前往購物車列表頁(沿用各 flavor 的購物車入口)。 */
|
||||
private void openCartList(Context context) {
|
||||
if (context instanceof FontendActivity) {
|
||||
SaleFlowHandler handler = ((FontendActivity) context).getSaleFlowHandler();
|
||||
private void openCartList() {
|
||||
if (hostContext instanceof FontendActivity) {
|
||||
SaleFlowHandler handler = ((FontendActivity) hostContext).getSaleFlowHandler();
|
||||
if (handler != null) {
|
||||
handler.onShoppingCartRequested();
|
||||
}
|
||||
@ -155,14 +171,14 @@ public class DialogProductDetail extends DialogAbstract {
|
||||
}
|
||||
|
||||
/** 沿用各 flavor 的結帳入口(basic→BuyDialog);非 FontendActivity 時直接開 BuyDialog。 */
|
||||
private void requestBuy(Context context) {
|
||||
if (context instanceof FontendActivity) {
|
||||
SaleFlowHandler handler = ((FontendActivity) context).getSaleFlowHandler();
|
||||
private void requestBuy() {
|
||||
if (hostContext instanceof FontendActivity) {
|
||||
SaleFlowHandler handler = ((FontendActivity) hostContext).getSaleFlowHandler();
|
||||
if (handler != null) {
|
||||
handler.onBuyRequested();
|
||||
return;
|
||||
}
|
||||
}
|
||||
new BuyDialog(context, getSrcHandlerMain()).show();
|
||||
new BuyDialog(hostContext, getSrcHandlerMain()).show();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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());
|
||||
@ -494,7 +498,7 @@ public class DispatchDialog extends DialogAbstract {
|
||||
public void readBufferOnScheduler(DevController devController, HandlerMain handlerMain) {
|
||||
if (isCheckSlotByVMC)
|
||||
return;
|
||||
getLogs().info("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA006");
|
||||
getLogs().debug("dispatch flow 006");
|
||||
isCheckSlotByVMC = true; // 進來立即設定true,可以判斷掉重複出貨
|
||||
|
||||
boolean isCMD = false;
|
||||
@ -669,7 +673,7 @@ public class DispatchDialog extends DialogAbstract {
|
||||
getSrcHandlerMain().send(getClass().getSimpleName(), Option.NEXT_PROCESS_GOODS.getOption(),
|
||||
"next process goods");
|
||||
} else {
|
||||
getLogs().info("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA002");
|
||||
getLogs().debug("dispatch flow 002");
|
||||
MyApp.getInstance().getDevXinYuanController()
|
||||
.getReadBufferByNow(new DevGetProductByVMC(handlerMain, slot), 5000L);
|
||||
}
|
||||
@ -682,7 +686,7 @@ public class DispatchDialog extends DialogAbstract {
|
||||
getCtx().getString(R.string.product_delivery_issues_contact_the_counter_window));
|
||||
}
|
||||
} else {
|
||||
getLogs().info("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA003");
|
||||
getLogs().debug("dispatch flow 003");
|
||||
MyApp.getInstance().getDevXinYuanController()
|
||||
.getReadBufferByNow(new DevGetProductByVMC(handlerMain, slot), 5000L);
|
||||
MyApp.getInstance().getDevXinYuanController()
|
||||
|
||||
@ -138,8 +138,7 @@
|
||||
android:ellipsize="end"
|
||||
android:gravity="start"
|
||||
android:lineSpacingExtra="4dp"
|
||||
android:maxLines="3"
|
||||
android:text="無"
|
||||
android:maxLines="8"
|
||||
android:textColor="#757575"
|
||||
android:textSize="28sp" />
|
||||
</LinearLayout>
|
||||
|
||||
@ -9,10 +9,12 @@
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerBuy"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="@color/background"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<!-- pickup_sheet(原 Cmuh)模式專用:掃描單據按鈕;其餘模式隱藏(由 VmcFragment 依 shopping_mode 切換可見性)。 -->
|
||||
|
||||
Loading…
Reference in New Issue
Block a user